1 #include "Buffer.h"
2 #include "Map_Edgepoints.h"
3 #include "Soldier_Control.h"
4 #include "PathAI.h"
5 #include "AI.h"
6 #include "Map_Information.h"
7 #include "Isometric_Utils.h"
8 #include "Debug.h"
9 #include "Random.h"
10 #include "Strategic.h"
11 #include "Animation_Control.h"
12 #include "Render_Fun.h"
13 #include "StrategicMap.h"
14 #include "Environment.h"
15 #include "TileDef.h"
16 #include "WorldMan.h"
17 #include "MemMan.h"
18 #include "FileMan.h"
19
20 #include "Message.h"
21
22 #include <vector>
23
24
25 //dynamic arrays that contain the valid gridno's for each edge
26 std::vector<INT16> gps1stNorthEdgepointArray;
27 std::vector<INT16> gps1stEastEdgepointArray;
28 std::vector<INT16> gps1stSouthEdgepointArray;
29 std::vector<INT16> gps1stWestEdgepointArray;
30 //contains the index value for the first array index of the second row of each edgepoint array.
31 //Because each edgepoint side has two rows, the outside most row is calculated first, then the inside row.
32 //For purposes of AI, it may become necessary to avoid this.
33 UINT16 gus1stNorthEdgepointMiddleIndex = 0;
34 UINT16 gus1stEastEdgepointMiddleIndex = 0;
35 UINT16 gus1stSouthEdgepointMiddleIndex = 0;
36 UINT16 gus1stWestEdgepointMiddleIndex = 0;
37
38 //dynamic arrays that contain the valid gridno's for each edge
39 std::vector<INT16> gps2ndNorthEdgepointArray;
40 std::vector<INT16> gps2ndEastEdgepointArray;
41 std::vector<INT16> gps2ndSouthEdgepointArray;
42 std::vector<INT16> gps2ndWestEdgepointArray;
43 //contains the index value for the first array index of the second row of each edgepoint array.
44 //Because each edgepoint side has two rows, the outside most row is calculated first, then the inside row.
45 //For purposes of AI, it may become necessary to avoid this.
46 UINT16 gus2ndNorthEdgepointMiddleIndex = 0;
47 UINT16 gus2ndEastEdgepointMiddleIndex = 0;
48 UINT16 gus2ndSouthEdgepointMiddleIndex = 0;
49 UINT16 gus2ndWestEdgepointMiddleIndex = 0;
50
51 BOOLEAN gfEdgepointsExist = FALSE;
52 BOOLEAN gfGeneratingMapEdgepoints = FALSE;
53
54 INT16 gsTLGridNo = 13286;
55 INT16 gsTRGridNo = 1043;
56 INT16 gsBLGridNo = 24878;
57 INT16 gsBRGridNo = 12635;
58
59 extern UINT8 gubTacticalDirection;
60
TrashMapEdgepoints()61 void TrashMapEdgepoints()
62 {
63 //Primary edgepoints
64 gps1stNorthEdgepointArray.clear();
65 gps1stEastEdgepointArray.clear();
66 gps1stSouthEdgepointArray.clear();
67 gps1stWestEdgepointArray.clear();
68 gus1stNorthEdgepointMiddleIndex = 0;
69 gus1stEastEdgepointMiddleIndex = 0;
70 gus1stSouthEdgepointMiddleIndex = 0;
71 gus1stWestEdgepointMiddleIndex = 0;
72 //Secondary edgepoints
73 gps2ndNorthEdgepointArray.clear();
74 gps2ndEastEdgepointArray.clear();
75 gps2ndSouthEdgepointArray.clear();
76 gps2ndWestEdgepointArray.clear();
77 gus2ndNorthEdgepointMiddleIndex = 0;
78 gus2ndEastEdgepointMiddleIndex = 0;
79 gus2ndSouthEdgepointMiddleIndex = 0;
80 gus2ndWestEdgepointMiddleIndex = 0;
81 }
82
83
84 static BOOLEAN VerifyEdgepoint(SOLDIERTYPE* pSoldier, INT16 sEdgepoint);
85
86
ValidateMapEdge(SOLDIERTYPE & s,UINT16 & middle_idx,std::vector<INT16> & edgepoints)87 static void ValidateMapEdge(SOLDIERTYPE& s, UINT16& middle_idx, std::vector<INT16>& edgepoints)
88 {
89 size_t dst = 0;
90 size_t middle = middle_idx;
91 size_t end = edgepoints.size();
92 Assert(end <= UINT16_MAX);
93 for (size_t i = 0; i != end; ++i)
94 {
95 if (VerifyEdgepoint(&s, edgepoints[i]))
96 {
97 edgepoints[dst] = edgepoints[i];
98 if (middle == i) middle = dst; // Adjust the middle index to the new one
99 ++dst;
100 }
101 else if (middle == i)
102 { // Increment the middle index because its edgepoint is no longer valid
103 ++middle;
104 }
105 }
106 middle_idx = static_cast<UINT16>(middle);
107 edgepoints.resize(dst);
108 }
109
110
111 /* This final step eliminates some edgepoints which actually don't path directly
112 * to the edge of the map. Cases would include an area that is close to the
113 * edge, but a fence blocks it from direct access to the edge of the map. */
ValidateEdgepoints()114 static void ValidateEdgepoints()
115 {
116 SOLDIERTYPE s;
117 s = SOLDIERTYPE{};
118 s.bTeam = ENEMY_TEAM;
119
120 ValidateMapEdge(s, gus1stNorthEdgepointMiddleIndex, gps1stNorthEdgepointArray);
121 ValidateMapEdge(s, gus1stEastEdgepointMiddleIndex, gps1stEastEdgepointArray);
122 ValidateMapEdge(s, gus1stSouthEdgepointMiddleIndex, gps1stSouthEdgepointArray);
123 ValidateMapEdge(s, gus1stWestEdgepointMiddleIndex, gps1stWestEdgepointArray);
124
125 ValidateMapEdge(s, gus2ndNorthEdgepointMiddleIndex, gps2ndNorthEdgepointArray);
126 ValidateMapEdge(s, gus2ndEastEdgepointMiddleIndex, gps2ndEastEdgepointArray);
127 ValidateMapEdge(s, gus2ndSouthEdgepointMiddleIndex, gps2ndSouthEdgepointArray);
128 ValidateMapEdge(s, gus2ndWestEdgepointMiddleIndex, gps2ndWestEdgepointArray);
129 }
130
131
CompactEdgepointArray(std::vector<INT16> & edgepoints,UINT16 & middle)132 static void CompactEdgepointArray(std::vector<INT16>& edgepoints, UINT16& middle)
133 {
134 size_t n = 0;
135 for (size_t i = 0; i < edgepoints.size(); i++)
136 {
137 if (edgepoints[ i ] == -1)
138 {
139 if (i < middle)
140 {
141 middle--;
142 }
143 }
144 else
145 {
146 if (n != i)
147 {
148 edgepoints[ n ] = edgepoints[ i ];
149 }
150 n++;
151 }
152 }
153 edgepoints.resize(n);
154 }
155
156
157 static BOOLEAN EdgepointsClose(SOLDIERTYPE* pSoldier, INT16 sEdgepoint1, INT16 sEdgepoint2);
158
159
InternallyClassifyEdgepoints(SOLDIERTYPE * pSoldier,INT16 sGridNo,std::vector<INT16> & edgepoints1,UINT16 & middle1,std::vector<INT16> & edgepoints2,UINT16 & middle2)160 static void InternallyClassifyEdgepoints(SOLDIERTYPE* pSoldier, INT16 sGridNo, std::vector<INT16>& edgepoints1, UINT16& middle1, std::vector<INT16>& edgepoints2, UINT16& middle2)
161 {
162 size_t i;
163 UINT16 us1stBenchmarkID, us2ndBenchmarkID;
164 us1stBenchmarkID = us2ndBenchmarkID = 0xffff;
165 for (i = 0; i < edgepoints1.size(); i++)
166 {
167 if (sGridNo == edgepoints1[ i ])
168 {
169 if (i < middle1)
170 { //in the first half of the array
171 us1stBenchmarkID = (UINT16)i;
172 //find the second benchmark
173 for (i = middle1; i < edgepoints1.size(); i++)
174 {
175 if (EdgepointsClose( pSoldier, edgepoints1[ us1stBenchmarkID ], edgepoints1[ i ] ))
176 {
177 us2ndBenchmarkID = (UINT16)i;
178 break;
179 }
180 }
181 }
182 else
183 { //in the second half of the array
184 us2ndBenchmarkID = (UINT16)i;
185 //find the first benchmark
186 for (i = 0; i < middle1; i++)
187 {
188 if (EdgepointsClose( pSoldier, edgepoints1[ us2ndBenchmarkID ], edgepoints1[ i ] ))
189 {
190 us1stBenchmarkID = (UINT16)i;
191 break;
192 }
193 }
194 }
195 break;
196 }
197 }
198 //Now we have found the two benchmarks, so go in both directions for each one to determine which entrypoints
199 //are going to be used in the primary array. All rejections will be positioned in the secondary array for
200 //use for isolated entry when tactically traversing.
201 if( us1stBenchmarkID != 0xffff )
202 {
203 for( i = us1stBenchmarkID; i > 0; i-- )
204 {
205 if (!EdgepointsClose( pSoldier, edgepoints1[ i ], edgepoints1[ i-1 ] ))
206 { //All edgepoints from index 0 to i-1 are rejected.
207 while( i )
208 {
209 i--;
210 edgepoints2.push_back(edgepoints1[ i ]);
211 middle2++;
212 edgepoints1[ i ] = -1;
213 }
214 break;
215 }
216 }
217 for (i = us1stBenchmarkID; (INT32)i < middle1 - 1; i++)
218 {
219 if (!EdgepointsClose( pSoldier, edgepoints1[ i ], edgepoints1[ i+1 ] ))
220 { //All edgepoints from index i+1 to 1st middle index are rejected.
221 while ((INT32)i < middle1 - 1)
222 {
223 i++;
224 edgepoints2.push_back(edgepoints1[ i ]);
225 middle2++;
226 edgepoints1[ i ] = -1;
227 }
228 break;
229 }
230 }
231 }
232 if( us2ndBenchmarkID != 0xffff )
233 {
234 for (i = us2ndBenchmarkID; i > middle1; i--)
235 {
236 if (!EdgepointsClose( pSoldier, edgepoints1[ i ], edgepoints1[ i-1 ] ))
237 { //All edgepoints from 1st middle index to i-1 are rejected.
238 while (i > middle1)
239 {
240 i--;
241 edgepoints2.push_back(edgepoints1[ i ]);
242 edgepoints1[ i ] = -1;
243 }
244 break;
245 }
246 }
247 for (i = us2ndBenchmarkID; i < edgepoints1.size() - 1; i++)
248 {
249 if (!EdgepointsClose( pSoldier, edgepoints1[ i ], edgepoints1[ i+1 ] ))
250 { //All edgepoints from index 0 to i-1 are rejected.
251 while (i < edgepoints1.size() - 1)
252 {
253 i++;
254 edgepoints2.push_back(edgepoints1[ i ]);
255 edgepoints1[ i ] = -1;
256 }
257 break;
258 }
259 }
260 }
261 //Now compact the primary array, because some edgepoints have been removed.
262 CompactEdgepointArray(edgepoints1, middle1);
263 }
264
265
ClassifyEdgepoints(void)266 static void ClassifyEdgepoints(void)
267 {
268 SOLDIERTYPE Soldier;
269 INT16 sGridNo = -1;
270
271 Soldier = SOLDIERTYPE{};
272 Soldier.bTeam = 1;
273
274 //north
275 if( gMapInformation.sNorthGridNo != -1 )
276 {
277 sGridNo = FindNearestEdgepointOnSpecifiedEdge( gMapInformation.sNorthGridNo, NORTH_EDGEPOINT_SEARCH );
278 if( sGridNo != NOWHERE )
279 {
280 InternallyClassifyEdgepoints( &Soldier, sGridNo,
281 gps1stNorthEdgepointArray, gus1stNorthEdgepointMiddleIndex,
282 gps2ndNorthEdgepointArray, gus2ndNorthEdgepointMiddleIndex );
283 }
284 }
285 //east
286 if( gMapInformation.sEastGridNo != -1 )
287 {
288 sGridNo = FindNearestEdgepointOnSpecifiedEdge( gMapInformation.sEastGridNo, EAST_EDGEPOINT_SEARCH );
289 if( sGridNo != NOWHERE )
290 {
291 InternallyClassifyEdgepoints( &Soldier, sGridNo,
292 gps1stEastEdgepointArray, gus1stEastEdgepointMiddleIndex,
293 gps2ndEastEdgepointArray, gus2ndEastEdgepointMiddleIndex );
294 }
295 }
296 //south
297 if( gMapInformation.sSouthGridNo != -1 )
298 {
299 sGridNo = FindNearestEdgepointOnSpecifiedEdge( gMapInformation.sSouthGridNo, SOUTH_EDGEPOINT_SEARCH );
300 if( sGridNo != NOWHERE )
301 {
302 InternallyClassifyEdgepoints( &Soldier, sGridNo,
303 gps1stSouthEdgepointArray, gus1stSouthEdgepointMiddleIndex,
304 gps2ndSouthEdgepointArray, gus2ndSouthEdgepointMiddleIndex );
305 }
306 }
307 //west
308 if( gMapInformation.sWestGridNo != -1 )
309 {
310 sGridNo = FindNearestEdgepointOnSpecifiedEdge( gMapInformation.sWestGridNo, WEST_EDGEPOINT_SEARCH );
311 if( sGridNo != NOWHERE )
312 {
313 InternallyClassifyEdgepoints( &Soldier, sGridNo,
314 gps1stWestEdgepointArray, gus1stWestEdgepointMiddleIndex,
315 gps2ndWestEdgepointArray, gus2ndWestEdgepointMiddleIndex );
316 }
317 }
318 }
319
GenerateMapEdgepoints()320 void GenerateMapEdgepoints()
321 {
322 INT16 sGridNo=-1;
323
324 //Get rid of the current edgepoint lists.
325 TrashMapEdgepoints();
326
327 gfGeneratingMapEdgepoints = TRUE;
328
329 if( gMapInformation.sNorthGridNo != -1 )
330 sGridNo = gMapInformation.sNorthGridNo;
331 else if( gMapInformation.sEastGridNo != -1 )
332 sGridNo = gMapInformation.sEastGridNo;
333 else if( gMapInformation.sSouthGridNo != -1 )
334 sGridNo = gMapInformation.sSouthGridNo;
335 else if( gMapInformation.sWestGridNo != -1 )
336 sGridNo = gMapInformation.sWestGridNo;
337 else if( gMapInformation.sCenterGridNo != -1 )
338 sGridNo = gMapInformation.sCenterGridNo;
339 else
340 return;
341
342 GlobalReachableTest( sGridNo );
343
344 //Calculate the north edges
345 if( gMapInformation.sNorthGridNo != -1 )
346 {
347 //1st row
348 sGridNo = gsTLGridNo;
349 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
350 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
351 {
352 gps1stNorthEdgepointArray.push_back(sGridNo);
353 }
354 while( sGridNo > gsTRGridNo )
355 {
356 sGridNo++;
357 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
358 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
359 {
360 gps1stNorthEdgepointArray.push_back(sGridNo);
361 }
362 sGridNo -= 160;
363 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
364 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
365 {
366 gps1stNorthEdgepointArray.push_back(sGridNo);
367 }
368 }
369 //2nd row
370 Assert(gps1stNorthEdgepointArray.size() <= UINT16_MAX);
371 gus1stNorthEdgepointMiddleIndex = static_cast<UINT16>(gps1stNorthEdgepointArray.size());
372 sGridNo = gsTLGridNo + 161;
373 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
374 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
375 {
376 gps1stNorthEdgepointArray.push_back(sGridNo);
377 }
378 while( sGridNo > gsTRGridNo + 161 )
379 {
380 sGridNo++;
381 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
382 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
383 {
384 gps1stNorthEdgepointArray.push_back(sGridNo);
385 }
386 sGridNo -= 160;
387 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
388 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
389 {
390 gps1stNorthEdgepointArray.push_back(sGridNo);
391 }
392 }
393 }
394 //Calculate the east edges
395 if( gMapInformation.sEastGridNo != -1 )
396 {
397 //1st row
398 sGridNo = gsTRGridNo;
399 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
400 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
401 {
402 gps1stEastEdgepointArray.push_back(sGridNo);
403 }
404 while( sGridNo < gsBRGridNo )
405 {
406 sGridNo += 160;
407 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
408 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
409 {
410 gps1stEastEdgepointArray.push_back(sGridNo);
411 }
412 sGridNo++;
413 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
414 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
415 {
416 gps1stEastEdgepointArray.push_back(sGridNo);
417 }
418 }
419 //2nd row
420 Assert(gps1stEastEdgepointArray.size() <= UINT16_MAX);
421 gus1stEastEdgepointMiddleIndex = static_cast<UINT16>(gps1stEastEdgepointArray.size());
422 sGridNo = gsTRGridNo + 159;
423 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
424 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
425 {
426 gps1stEastEdgepointArray.push_back(sGridNo);
427 }
428 while( sGridNo < gsBRGridNo + 159 )
429 {
430 sGridNo += 160;
431 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
432 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
433 {
434 gps1stEastEdgepointArray.push_back(sGridNo);
435 }
436 sGridNo++;
437 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
438 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
439 {
440 gps1stEastEdgepointArray.push_back(sGridNo);
441 }
442 }
443 }
444 //Calculate the south edges
445 if( gMapInformation.sSouthGridNo != -1 )
446 {
447 //1st row
448 sGridNo = gsBLGridNo;
449 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
450 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
451 {
452 gps1stSouthEdgepointArray.push_back(sGridNo);
453 }
454 while( sGridNo > gsBRGridNo )
455 {
456 sGridNo++;
457 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
458 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
459 {
460 gps1stSouthEdgepointArray.push_back(sGridNo);
461 }
462 sGridNo -= 160;
463 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
464 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
465 {
466 gps1stSouthEdgepointArray.push_back(sGridNo);
467 }
468 }
469 //2nd row
470 Assert(gps1stSouthEdgepointArray.size() <= UINT16_MAX);
471 gus1stSouthEdgepointMiddleIndex = static_cast<UINT16>(gps1stSouthEdgepointArray.size());
472 sGridNo = gsBLGridNo - 161;
473 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
474 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
475 {
476 gps1stSouthEdgepointArray.push_back(sGridNo);
477 }
478 while( sGridNo > gsBRGridNo - 161 )
479 {
480 sGridNo++;
481 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
482 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
483 {
484 gps1stSouthEdgepointArray.push_back(sGridNo);
485 }
486 sGridNo -= 160;
487 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
488 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
489 {
490 gps1stSouthEdgepointArray.push_back(sGridNo);
491 }
492 }
493 }
494 //Calculate the west edges
495 if( gMapInformation.sWestGridNo != -1 )
496 {
497 //1st row
498 sGridNo = gsTLGridNo;
499 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
500 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
501 {
502 gps1stWestEdgepointArray.push_back(sGridNo);
503 }
504 while( sGridNo < gsBLGridNo )
505 {
506 sGridNo++;
507 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
508 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
509 {
510 gps1stWestEdgepointArray.push_back(sGridNo);
511 }
512 sGridNo += 160;
513 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
514 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
515 {
516 gps1stWestEdgepointArray.push_back(sGridNo);
517 }
518 }
519 //2nd row
520 Assert(gps1stWestEdgepointArray.size() <= UINT16_MAX);
521 gus1stWestEdgepointMiddleIndex = static_cast<UINT16>(gps1stWestEdgepointArray.size());
522 sGridNo = gsTLGridNo - 159;
523 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
524 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
525 {
526 gps1stWestEdgepointArray.push_back(sGridNo);
527 }
528 while( sGridNo < gsBLGridNo - 159 )
529 {
530 sGridNo++;
531 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
532 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
533 {
534 gps1stWestEdgepointArray.push_back(sGridNo);
535 }
536 sGridNo += 160;
537 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
538 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
539 {
540 gps1stWestEdgepointArray.push_back(sGridNo);
541 }
542 }
543 }
544
545 //CHECK FOR ISOLATED EDGEPOINTS (but only if the entrypoint is ISOLATED!!!)
546 if( gMapInformation.sIsolatedGridNo != -1 && !(gpWorldLevelData[ gMapInformation.sIsolatedGridNo ].uiFlags & MAPELEMENT_REACHABLE) )
547 {
548 GlobalReachableTest( gMapInformation.sIsolatedGridNo );
549 if( gMapInformation.sNorthGridNo != -1 )
550 {
551 //1st row
552 sGridNo = gsTLGridNo;
553 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
554 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
555 {
556 gps2ndNorthEdgepointArray.push_back(sGridNo);
557 }
558 while( sGridNo > gsTRGridNo )
559 {
560 sGridNo++;
561 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
562 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
563 {
564 gps2ndNorthEdgepointArray.push_back(sGridNo);
565 }
566 sGridNo -= 160;
567 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
568 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
569 {
570 gps2ndNorthEdgepointArray.push_back(sGridNo);
571 }
572 }
573 //2nd row
574 Assert(gps2ndNorthEdgepointArray.size() <= UINT16_MAX);
575 gus2ndNorthEdgepointMiddleIndex = static_cast<UINT16>(gps2ndNorthEdgepointArray.size());
576 sGridNo = gsTLGridNo + 161;
577 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
578 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
579 {
580 gps2ndNorthEdgepointArray.push_back(sGridNo);
581 }
582 while( sGridNo > gsTRGridNo + 161 )
583 {
584 sGridNo++;
585 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
586 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
587 {
588 gps2ndNorthEdgepointArray.push_back(sGridNo);
589 }
590 sGridNo -= 160;
591 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
592 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
593 {
594 gps2ndNorthEdgepointArray.push_back(sGridNo);
595 }
596 }
597 }
598 //Calculate the east edges
599 if( gMapInformation.sEastGridNo != -1 )
600 {
601 //1st row
602 sGridNo = gsTRGridNo;
603 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
604 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
605 {
606 gps2ndEastEdgepointArray.push_back(sGridNo);
607 }
608 while( sGridNo < gsBRGridNo )
609 {
610 sGridNo += 160;
611 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
612 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
613 {
614 gps2ndEastEdgepointArray.push_back(sGridNo);
615 }
616 sGridNo++;
617 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
618 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
619 {
620 gps2ndEastEdgepointArray.push_back(sGridNo);
621 }
622 }
623 //2nd row
624 Assert(gps2ndEastEdgepointArray.size() <= UINT16_MAX);
625 gus2ndEastEdgepointMiddleIndex = static_cast<UINT16>(gps2ndEastEdgepointArray.size());
626 sGridNo = gsTRGridNo + 159;
627 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
628 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
629 {
630 gps2ndEastEdgepointArray.push_back(sGridNo);
631 }
632 while( sGridNo < gsBRGridNo + 159 )
633 {
634 sGridNo += 160;
635 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
636 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
637 {
638 gps2ndEastEdgepointArray.push_back(sGridNo);
639 }
640 sGridNo++;
641 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
642 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
643 {
644 gps2ndEastEdgepointArray.push_back(sGridNo);
645 }
646 }
647 }
648 //Calculate the south edges
649 if( gMapInformation.sSouthGridNo != -1 )
650 {
651 //1st row
652 sGridNo = gsBLGridNo;
653 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
654 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
655 {
656 gps2ndSouthEdgepointArray.push_back(sGridNo);
657 }
658 while( sGridNo > gsBRGridNo )
659 {
660 sGridNo++;
661 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
662 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
663 {
664 gps2ndSouthEdgepointArray.push_back(sGridNo);
665 }
666 sGridNo -= 160;
667 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
668 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
669 {
670 gps2ndSouthEdgepointArray.push_back(sGridNo);
671 }
672 }
673 //2nd row
674 Assert(gps2ndSouthEdgepointArray.size() <= UINT16_MAX);
675 gus2ndSouthEdgepointMiddleIndex = static_cast<UINT16>(gps2ndSouthEdgepointArray.size());
676 sGridNo = gsBLGridNo - 161;
677 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
678 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
679 {
680 gps2ndSouthEdgepointArray.push_back(sGridNo);
681 }
682 while( sGridNo > gsBRGridNo - 161 )
683 {
684 sGridNo++;
685 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
686 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
687 {
688 gps2ndSouthEdgepointArray.push_back(sGridNo);
689 }
690 sGridNo -= 160;
691 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
692 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
693 {
694 gps2ndSouthEdgepointArray.push_back(sGridNo);
695 }
696 }
697 }
698 //Calculate the west edges
699 if( gMapInformation.sWestGridNo != -1 )
700 {
701 //1st row
702 sGridNo = gsTLGridNo;
703 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
704 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
705 {
706 gps2ndWestEdgepointArray.push_back(sGridNo);
707 }
708 while( sGridNo < gsBLGridNo )
709 {
710 sGridNo++;
711 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
712 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
713 {
714 gps2ndWestEdgepointArray.push_back(sGridNo);
715 }
716 sGridNo += 160;
717 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
718 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
719 {
720 gps2ndWestEdgepointArray.push_back(sGridNo);
721 }
722 }
723 //2nd row
724 Assert(gps2ndWestEdgepointArray.size() <= UINT16_MAX);
725 gus2ndWestEdgepointMiddleIndex = static_cast<UINT16>(gps2ndWestEdgepointArray.size());
726 sGridNo = gsTLGridNo - 159;
727 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
728 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
729 {
730 gps2ndWestEdgepointArray.push_back(sGridNo);
731 }
732 while( sGridNo < gsBLGridNo - 159 )
733 {
734 sGridNo++;
735 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
736 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
737 {
738 gps2ndWestEdgepointArray.push_back(sGridNo);
739 }
740 sGridNo += 160;
741 if( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE &&
742 (!gubWorldRoomInfo[ sGridNo ] || gfBasement) )
743 {
744 gps2ndWestEdgepointArray.push_back(sGridNo);
745 }
746 }
747 }
748 }
749
750 //Eliminates any edgepoints not accessible to the edge of the world. This is done to the primary edgepoints
751 ValidateEdgepoints();
752 //Second step is to process the primary edgepoints and determine if any of the edgepoints aren't accessible from
753 //the associated entrypoint. These edgepoints that are rejected are placed in the secondary list.
754 if( gMapInformation.sIsolatedGridNo != -1 )
755 { //only if there is an isolated gridno in the map. There is a flaw in the design of this system. The classification
756 //process will automatically assign areas to be isolated if there is an obstacle between one normal edgepoint and another
757 //causing a 5 tile connection check to fail. So, all maps with isolated edgepoints will need to be checked manually to
758 //make sure there are no obstacles causing this to happen (except for obstacles between normal areas and the isolated area)
759
760 //Good thing most maps don't have isolated sections. This is one expensive function to call! Maybe 200MI!
761 ClassifyEdgepoints();
762 }
763
764 gfGeneratingMapEdgepoints = FALSE;
765 }
766
767
SaveMapEdgepoint(HWFILE const f,UINT16 const & middle,const std::vector<INT16> & edgepoints)768 static void SaveMapEdgepoint(HWFILE const f, UINT16 const& middle, const std::vector<INT16>& edgepoints)
769 {
770 Assert(edgepoints.size() <= UINT16_MAX);
771 UINT16 n = static_cast<UINT16>(edgepoints.size());
772 FileWrite(f, &n, sizeof(UINT16));
773 FileWrite(f, &middle, sizeof(UINT16));
774 if (n != 0) FileWrite(f, edgepoints.data(), sizeof(*edgepoints.data()) * n);
775 }
776
777
SaveMapEdgepoints(HWFILE const f)778 void SaveMapEdgepoints(HWFILE const f)
779 {
780 // 1st priority edgepoints -- for common entry -- tactical placement gui uses only these points.
781 SaveMapEdgepoint(f, gus1stNorthEdgepointMiddleIndex, gps1stNorthEdgepointArray);
782 SaveMapEdgepoint(f, gus1stEastEdgepointMiddleIndex, gps1stEastEdgepointArray);
783 SaveMapEdgepoint(f, gus1stSouthEdgepointMiddleIndex, gps1stSouthEdgepointArray);
784 SaveMapEdgepoint(f, gus1stWestEdgepointMiddleIndex, gps1stWestEdgepointArray);
785
786 // 2nd priority edgepoints -- for isolated areas. Okay to be zero
787 SaveMapEdgepoint(f, gus2ndNorthEdgepointMiddleIndex, gps2ndNorthEdgepointArray);
788 SaveMapEdgepoint(f, gus2ndEastEdgepointMiddleIndex, gps2ndEastEdgepointArray);
789 SaveMapEdgepoint(f, gus2ndSouthEdgepointMiddleIndex, gps2ndSouthEdgepointArray);
790 SaveMapEdgepoint(f, gus2ndWestEdgepointMiddleIndex, gps2ndWestEdgepointArray);
791 }
792
793
LoadMapEdgepoint(HWFILE const f,UINT16 & middle,std::vector<INT16> & edgepoints)794 static void LoadMapEdgepoint(HWFILE const f, UINT16& middle, std::vector<INT16>& edgepoints)
795 {
796 UINT16 n = 0;
797 FileRead(f, &n, sizeof(UINT16));
798 FileRead(f, &middle, sizeof(UINT16));
799 if (n != 0)
800 {
801 edgepoints.resize(n);
802 FileRead(f, edgepoints.data(), sizeof(*edgepoints.data()) * n);
803 }
804 }
805
806
LoadMapEdgepoints(HWFILE const f)807 bool LoadMapEdgepoints(HWFILE const f)
808 {
809 TrashMapEdgepoints();
810
811 LoadMapEdgepoint(f, gus1stNorthEdgepointMiddleIndex, gps1stNorthEdgepointArray);
812 LoadMapEdgepoint(f, gus1stEastEdgepointMiddleIndex, gps1stEastEdgepointArray);
813 LoadMapEdgepoint(f, gus1stSouthEdgepointMiddleIndex, gps1stSouthEdgepointArray);
814 LoadMapEdgepoint(f, gus1stWestEdgepointMiddleIndex, gps1stWestEdgepointArray);
815
816 if (gMapInformation.ubMapVersion < 17)
817 { /* To prevent invalidation of older maps, which only used one layer of
818 * edgepoints, and a UINT8 for containing the size, we will preserve that
819 * paradigm, then kill the loaded edgepoints and regenerate them. */
820 TrashMapEdgepoints();
821 return false;
822 }
823
824 LoadMapEdgepoint(f, gus2ndNorthEdgepointMiddleIndex, gps2ndNorthEdgepointArray);
825 LoadMapEdgepoint(f, gus2ndEastEdgepointMiddleIndex, gps2ndEastEdgepointArray);
826 LoadMapEdgepoint(f, gus2ndSouthEdgepointMiddleIndex, gps2ndSouthEdgepointArray);
827 LoadMapEdgepoint(f, gus2ndWestEdgepointMiddleIndex, gps2ndWestEdgepointArray);
828
829 if (gMapInformation.ubMapVersion < 22)
830 { // Regenerate them
831 TrashMapEdgepoints();
832 return false;
833 }
834
835 return true;
836 }
837
838
ChooseMapEdgepoint(UINT8 ubStrategicInsertionCode)839 UINT16 ChooseMapEdgepoint( UINT8 ubStrategicInsertionCode )
840 {
841 std::vector<INT16>* pEdgepoints = nullptr;
842
843 //First validate and get access to the correct array based on strategic direction.
844 //We will use the selected array to choose insertion gridno's.
845 switch( ubStrategicInsertionCode )
846 {
847 case INSERTION_CODE_NORTH:
848 pEdgepoints = &gps1stNorthEdgepointArray;
849 break;
850 case INSERTION_CODE_EAST:
851 pEdgepoints = &gps1stEastEdgepointArray;
852 break;
853 case INSERTION_CODE_SOUTH:
854 pEdgepoints = &gps1stSouthEdgepointArray;
855 break;
856 case INSERTION_CODE_WEST:
857 pEdgepoints = &gps1stWestEdgepointArray;
858 break;
859 default:
860 SLOGA("ChooseMapEdgepoints: Failed to pass a valid strategic insertion code." );
861 break;
862 }
863 if (!pEdgepoints || pEdgepoints->size() == 0)
864 {
865 return NOWHERE;
866 }
867 Assert(pEdgepoints->size() <= UINT32_MAX);
868 return (*pEdgepoints)[ Random( static_cast<UINT32>(pEdgepoints->size()) ) ];
869 }
870
871
ChooseMapEdgepoints(MAPEDGEPOINTINFO * const pMapEdgepointInfo,const UINT8 ubStrategicInsertionCode,UINT8 ubNumDesiredPoints)872 void ChooseMapEdgepoints(MAPEDGEPOINTINFO* const pMapEdgepointInfo, const UINT8 ubStrategicInsertionCode, UINT8 ubNumDesiredPoints)
873 {
874 AssertMsg(ubNumDesiredPoints > 0 && ubNumDesiredPoints <= 32, String("ChooseMapEdgepoints: Desired points = %d, valid range is 1-32", ubNumDesiredPoints));
875
876 /* First validate and get access to the correct array based on strategic
877 * direction. We will use the selected array to choose insertion gridno's. */
878 std::vector<INT16>* pEdgepoints = nullptr;
879 switch (ubStrategicInsertionCode)
880 {
881 case INSERTION_CODE_NORTH:
882 pEdgepoints = &gps1stNorthEdgepointArray;
883 break;
884
885 case INSERTION_CODE_EAST:
886 pEdgepoints = &gps1stEastEdgepointArray;
887 break;
888
889 case INSERTION_CODE_SOUTH:
890 pEdgepoints = &gps1stSouthEdgepointArray;
891 break;
892
893 case INSERTION_CODE_WEST:
894 pEdgepoints = &gps1stWestEdgepointArray;
895 break;
896
897 default:
898 SLOGA("ChooseMapEdgepoints: Failed to pass a valid strategic insertion code.");
899 break;
900 }
901 pMapEdgepointInfo->ubStrategicInsertionCode = ubStrategicInsertionCode;
902
903 if (!pEdgepoints || pEdgepoints->size() == 0)
904 {
905 pMapEdgepointInfo->ubNumPoints = 0;
906 return;
907 }
908
909 /* JA2 Gold: don't place people in the water. If any of the waypoints is on a
910 * water spot, we're going to have to remove it */
911 std::vector<INT16> usableEdgepoints;
912 for (INT16 edgepoint : *pEdgepoints)
913 {
914 const UINT8 terrain = GetTerrainType(edgepoint);
915 if (terrain == MED_WATER || terrain == DEEP_WATER) continue;
916
917 usableEdgepoints.push_back(edgepoint);
918 }
919
920 if (ubNumDesiredPoints >= usableEdgepoints.size())
921 { //We don't have enough points for everyone, return them all.
922 Assert(usableEdgepoints.size() <= UINT8_MAX);
923 pMapEdgepointInfo->ubNumPoints = static_cast<UINT8>(usableEdgepoints.size());
924 for (size_t i = 0; i < usableEdgepoints.size(); ++i)
925 {
926 pMapEdgepointInfo->sGridNo[i] = usableEdgepoints[i];
927 }
928 return;
929 }
930
931 // We have more points, so choose them randomly.
932 Assert(usableEdgepoints.size() <= UINT16_MAX);
933 UINT16 usSlots = static_cast<UINT16>(usableEdgepoints.size());
934 UINT16 usCurrSlot = 0;
935 pMapEdgepointInfo->ubNumPoints = ubNumDesiredPoints;
936 for (size_t i = 0; i < usableEdgepoints.size(); ++i)
937 {
938 if (Random(usSlots) < ubNumDesiredPoints)
939 {
940 pMapEdgepointInfo->sGridNo[usCurrSlot++] = usableEdgepoints[i];
941 --ubNumDesiredPoints;
942 }
943 --usSlots;
944 }
945 }
946
947
948 INT16 *gpReservedGridNos = NULL;
949 INT16 gsReservedIndex = 0;
950
BeginMapEdgepointSearch()951 void BeginMapEdgepointSearch()
952 {
953 INT16 sGridNo;
954
955 //Create the reserved list
956 AssertMsg( !gpReservedGridNos, "Attempting to BeginMapEdgepointSearch that has already been created." );
957 gpReservedGridNos = new INT16[20]{};
958 gsReservedIndex = 0;
959
960 if( gMapInformation.sNorthGridNo != -1 )
961 sGridNo = gMapInformation.sNorthGridNo;
962 else if( gMapInformation.sEastGridNo != -1 )
963 sGridNo = gMapInformation.sEastGridNo;
964 else if( gMapInformation.sSouthGridNo != -1 )
965 sGridNo = gMapInformation.sSouthGridNo;
966 else if( gMapInformation.sWestGridNo != -1 )
967 sGridNo = gMapInformation.sWestGridNo;
968 else
969 return;
970
971 GlobalReachableTest( sGridNo );
972
973 //Now, we have the path values calculated. Now, we can check for closest edgepoints.
974 }
975
EndMapEdgepointSearch()976 void EndMapEdgepointSearch()
977 {
978 AssertMsg( gpReservedGridNos, "Attempting to EndMapEdgepointSearch that has already been removed." );
979 delete[] gpReservedGridNos;
980 gpReservedGridNos = NULL;
981 gsReservedIndex = 0;
982 }
983
984
985 //THIS CODE ISN'T RECOMMENDED FOR TIME CRITICAL AREAS.
SearchForClosestPrimaryMapEdgepoint(INT16 sGridNo,UINT8 ubInsertionCode)986 INT16 SearchForClosestPrimaryMapEdgepoint( INT16 sGridNo, UINT8 ubInsertionCode )
987 {
988 INT32 i, iDirectionLoop;
989 std::vector<INT16>* pEdgepoints = nullptr;
990 INT16 sRadius, sDistance, sDirection, sOriginalGridNo;
991 BOOLEAN fReserved;
992
993 if( gsReservedIndex >= 20 )
994 { //Everything is reserved.
995 SLOGA("All closest map edgepoints have been reserved. We should only have 20 soldiers maximum...");
996 }
997 switch( ubInsertionCode )
998 {
999 case INSERTION_CODE_NORTH:
1000 pEdgepoints = &gps1stNorthEdgepointArray;
1001 AssertMsg(pEdgepoints->size() != 0, String("Sector %c%d level %d doesn't have any north mapedgepoints. LC:1", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ));
1002 break;
1003 case INSERTION_CODE_EAST:
1004 pEdgepoints = &gps1stEastEdgepointArray;
1005 AssertMsg(pEdgepoints->size() != 0, String("Sector %c%d level %d doesn't have any east mapedgepoints. LC:1", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ));
1006 break;
1007 case INSERTION_CODE_SOUTH:
1008 pEdgepoints = &gps1stSouthEdgepointArray;
1009 AssertMsg(pEdgepoints->size() != 0, String("Sector %c%d level %d doesn't have any south mapedgepoints. LC:1", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ));
1010 break;
1011 case INSERTION_CODE_WEST:
1012 pEdgepoints = &gps1stWestEdgepointArray;
1013 AssertMsg(pEdgepoints->size() != 0, String("Sector %c%d level %d doesn't have any west mapedgepoints. LC:1", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ));
1014 break;
1015 }
1016 if (!pEdgepoints || pEdgepoints->size() == 0)
1017 {
1018 return NOWHERE;
1019 }
1020
1021 //Check the initial gridno, to see if it is available and an edgepoint.
1022 fReserved = FALSE;
1023 for( i = 0; i < gsReservedIndex; i++ )
1024 {
1025 if( gpReservedGridNos[ i ] == sGridNo )
1026 {
1027 fReserved = TRUE;
1028 break;
1029 }
1030 }
1031 if( !fReserved )
1032 { //Not reserved, so see if we can find this gridno in the edgepoint array.
1033 for (INT16 edgepoint : *pEdgepoints)
1034 {
1035 if (edgepoint == sGridNo)
1036 { //Yes, the gridno is in the edgepoint array.
1037 gpReservedGridNos[ gsReservedIndex ] = sGridNo;
1038 gsReservedIndex++;
1039 return sGridNo;
1040 }
1041 }
1042 }
1043
1044 //spiral outwards, until we find an unreserved mapedgepoint.
1045 //
1046 // 09 08 07 06
1047 // 10 01 00 05
1048 // 11 02 03 04
1049 // 12 13 14 15 ..
1050 sRadius = 1;
1051 sDirection = WORLD_COLS;
1052 sOriginalGridNo = sGridNo;
1053 while( sRadius < (INT16)(gbWorldSectorZ ? 30 : 10) )
1054 {
1055 sGridNo = sOriginalGridNo + (-1 - WORLD_COLS)*sRadius; //start at the TOP-LEFT gridno
1056 for( iDirectionLoop = 0; iDirectionLoop < 4; iDirectionLoop++ )
1057 {
1058 switch( iDirectionLoop )
1059 {
1060 case 0: sDirection = WORLD_COLS; break;
1061 case 1: sDirection = 1; break;
1062 case 2: sDirection = -WORLD_COLS; break;
1063 case 3: sDirection = -1; break;
1064 }
1065 sDistance = sRadius * 2;
1066 while( sDistance-- )
1067 {
1068 sGridNo += sDirection;
1069 if( sGridNo < 0 || sGridNo >= WORLD_MAX )
1070 continue;
1071 //Check the gridno, to see if it is available and an edgepoint.
1072 fReserved = FALSE;
1073 for( i = 0; i < gsReservedIndex; i++ )
1074 {
1075 if( gpReservedGridNos[ i ] == sGridNo )
1076 {
1077 fReserved = TRUE;
1078 break;
1079 }
1080 }
1081 if( !fReserved )
1082 { //Not reserved, so see if we can find this gridno in the edgepoint array.
1083 for (INT16 edgepoint : *pEdgepoints)
1084 {
1085 if (edgepoint == sGridNo)
1086 { //Yes, the gridno is in the edgepoint array.
1087 gpReservedGridNos[ gsReservedIndex ] = sGridNo;
1088 gsReservedIndex++;
1089 return sGridNo;
1090 }
1091 }
1092 }
1093 }
1094 }
1095 sRadius++;
1096 }
1097 return NOWHERE ;
1098 }
1099
SearchForClosestSecondaryMapEdgepoint(INT16 sGridNo,UINT8 ubInsertionCode)1100 INT16 SearchForClosestSecondaryMapEdgepoint( INT16 sGridNo, UINT8 ubInsertionCode )
1101 {
1102 INT32 i, iDirectionLoop;
1103 std::vector<INT16>* pEdgepoints = nullptr;
1104 INT16 sRadius, sDistance, sDirection, sOriginalGridNo;
1105 BOOLEAN fReserved;
1106
1107 if( gsReservedIndex >= 20 )
1108 { //Everything is reserved.
1109 SLOGA("All closest map edgepoints have been reserved. We should only have 20 soldiers maximum...");
1110 }
1111 switch( ubInsertionCode )
1112 {
1113 case INSERTION_CODE_NORTH:
1114 pEdgepoints = &gps2ndNorthEdgepointArray;
1115 AssertMsg(pEdgepoints->size() != 0, String("Sector %c%d level %d doesn't have any isolated north mapedgepoints. KM:1", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ));
1116 break;
1117 case INSERTION_CODE_EAST:
1118 pEdgepoints = &gps2ndEastEdgepointArray;
1119 AssertMsg(pEdgepoints->size() != 0, String("Sector %c%d level %d doesn't have any isolated east mapedgepoints. KM:1", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ));
1120 break;
1121 case INSERTION_CODE_SOUTH:
1122 pEdgepoints = &gps2ndSouthEdgepointArray;
1123 AssertMsg(pEdgepoints->size() != 0, String("Sector %c%d level %d doesn't have any isolated south mapedgepoints. KM:1", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ));
1124 break;
1125 case INSERTION_CODE_WEST:
1126 pEdgepoints = &gps2ndWestEdgepointArray;
1127 AssertMsg(pEdgepoints->size() != 0, String("Sector %c%d level %d doesn't have any isolated west mapedgepoints. KM:1", gWorldSectorY + 'A' - 1, gWorldSectorX, gbWorldSectorZ));
1128 break;
1129 }
1130 if (!pEdgepoints || pEdgepoints->size() == 0)
1131 {
1132 return NOWHERE;
1133 }
1134
1135 //Check the initial gridno, to see if it is available and an edgepoint.
1136 fReserved = FALSE;
1137 for( i = 0; i < gsReservedIndex; i++ )
1138 {
1139 if( gpReservedGridNos[ i ] == sGridNo )
1140 {
1141 fReserved = TRUE;
1142 break;
1143 }
1144 }
1145 if( !fReserved )
1146 { //Not reserved, so see if we can find this gridno in the edgepoint array.
1147 for (INT16 edgepoint : *pEdgepoints)
1148 {
1149 if (edgepoint == sGridNo)
1150 { //Yes, the gridno is in the edgepoint array.
1151 gpReservedGridNos[ gsReservedIndex ] = sGridNo;
1152 gsReservedIndex++;
1153 return sGridNo;
1154 }
1155 }
1156 }
1157
1158 //spiral outwards, until we find an unreserved mapedgepoint.
1159 //
1160 // 09 08 07 06
1161 // 10 01 00 05
1162 // 11 02 03 04
1163 // 12 13 14 15 ..
1164 sRadius = 1;
1165 sDirection = WORLD_COLS;
1166 sOriginalGridNo = sGridNo;
1167 while( sRadius < (INT16)(gbWorldSectorZ ? 30 : 10) )
1168 {
1169 sGridNo = sOriginalGridNo + (-1 - WORLD_COLS)*sRadius; //start at the TOP-LEFT gridno
1170 for( iDirectionLoop = 0; iDirectionLoop < 4; iDirectionLoop++ )
1171 {
1172 switch( iDirectionLoop )
1173 {
1174 case 0: sDirection = WORLD_COLS; break;
1175 case 1: sDirection = 1; break;
1176 case 2: sDirection = -WORLD_COLS; break;
1177 case 3: sDirection = -1; break;
1178 }
1179 sDistance = sRadius * 2;
1180 while( sDistance-- )
1181 {
1182 sGridNo += sDirection;
1183 if( sGridNo < 0 || sGridNo >= WORLD_MAX )
1184 continue;
1185 //Check the gridno, to see if it is available and an edgepoint.
1186 fReserved = FALSE;
1187 for( i = 0; i < gsReservedIndex; i++ )
1188 {
1189 if( gpReservedGridNos[ i ] == sGridNo )
1190 {
1191 fReserved = TRUE;
1192 break;
1193 }
1194 }
1195 if( !fReserved )
1196 { //Not reserved, so see if we can find this gridno in the edgepoint array.
1197 for (INT16 edgepoint : *pEdgepoints)
1198 {
1199 if (edgepoint == sGridNo)
1200 { //Yes, the gridno is in the edgepoint array.
1201 gpReservedGridNos[ gsReservedIndex ] = sGridNo;
1202 gsReservedIndex++;
1203 return sGridNo;
1204 }
1205 }
1206 }
1207 }
1208 }
1209 sRadius++;
1210 }
1211 return NOWHERE ;
1212 }
1213
1214
1215 #define EDGE_OF_MAP_SEARCH 5
1216
1217
VerifyEdgepoint(SOLDIERTYPE * pSoldier,INT16 sEdgepoint)1218 static BOOLEAN VerifyEdgepoint(SOLDIERTYPE* pSoldier, INT16 sEdgepoint)
1219 {
1220 INT32 iSearchRange;
1221 INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
1222 INT16 sGridNo;
1223 INT8 bDirection;
1224
1225 pSoldier->sGridNo = sEdgepoint;
1226
1227 iSearchRange = EDGE_OF_MAP_SEARCH;
1228
1229 // determine maximum horizontal limits
1230 sMaxLeft = MIN( iSearchRange, (pSoldier->sGridNo % MAXCOL));
1231 sMaxRight = MIN( iSearchRange, MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1));
1232
1233 // determine maximum vertical limits
1234 sMaxUp = MIN( iSearchRange, (pSoldier->sGridNo / MAXROW));
1235 sMaxDown = MIN( iSearchRange, MAXROW - ((pSoldier->sGridNo / MAXROW) + 1));
1236
1237 // Call FindBestPath to set flags in all locations that we can
1238 // walk into within range. We have to set some things up first...
1239
1240 // set the distance limit of the square region
1241 gubNPCDistLimit = EDGE_OF_MAP_SEARCH;
1242
1243 // reset the "reachable" flags in the region we're looking at
1244 for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
1245 {
1246 for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
1247 {
1248 sGridNo = sEdgepoint + sXOffset + (MAXCOL * sYOffset);
1249 gpWorldLevelData[sGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE);
1250 }
1251 }
1252
1253 FindBestPath( pSoldier, NOWHERE, pSoldier->bLevel, WALKING, COPYREACHABLE, PATH_THROUGH_PEOPLE );
1254
1255 // Turn off the "reachable" flag for the current location
1256 // so we don't consider it
1257 //gpWorldLevelData[sEdgepoint].uiFlags &= ~(MAPELEMENT_REACHABLE);
1258
1259 // SET UP DOUBLE-LOOP TO STEP THROUGH POTENTIAL GRID #s
1260 for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
1261 {
1262 for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
1263 {
1264 // calculate the next potential gridno
1265 sGridNo = sEdgepoint + sXOffset + (MAXCOL * sYOffset);
1266
1267 if (!(gpWorldLevelData[sGridNo].uiFlags & MAPELEMENT_REACHABLE))
1268 {
1269 continue;
1270 }
1271
1272 if (GridNoOnEdgeOfMap( sGridNo, &bDirection ) )
1273 {
1274 // ok!
1275 return TRUE;
1276 }
1277 }
1278 }
1279
1280 // no spots right on edge of map within 5 tiles
1281 return FALSE;
1282 }
1283
1284
EdgepointsClose(SOLDIERTYPE * pSoldier,INT16 sEdgepoint1,INT16 sEdgepoint2)1285 static BOOLEAN EdgepointsClose(SOLDIERTYPE* pSoldier, INT16 sEdgepoint1, INT16 sEdgepoint2)
1286 {
1287 INT32 iSearchRange;
1288 INT16 sMaxLeft, sMaxRight, sMaxUp, sMaxDown, sXOffset, sYOffset;
1289 INT16 sGridNo;
1290
1291 pSoldier->sGridNo = sEdgepoint1;
1292
1293 if( gWorldSectorX == 14 && gWorldSectorY == 9 && !gbWorldSectorZ )
1294 { //BRUTAL CODE -- special case map.
1295 iSearchRange = 250;
1296 }
1297 else
1298 {
1299 iSearchRange = EDGE_OF_MAP_SEARCH;
1300 }
1301
1302 // determine maximum horizontal limits
1303 sMaxLeft = MIN( iSearchRange, (pSoldier->sGridNo % MAXCOL));
1304 sMaxRight = MIN( iSearchRange, MAXCOL - ((pSoldier->sGridNo % MAXCOL) + 1));
1305
1306 // determine maximum vertical limits
1307 sMaxUp = MIN( iSearchRange, (pSoldier->sGridNo / MAXROW));
1308 sMaxDown = MIN( iSearchRange, MAXROW - ((pSoldier->sGridNo / MAXROW) + 1));
1309
1310 // Call FindBestPath to set flags in all locations that we can
1311 // walk into within range. We have to set some things up first...
1312
1313 // set the distance limit of the square region
1314 gubNPCDistLimit = (UINT8)iSearchRange;
1315
1316 // reset the "reachable" flags in the region we're looking at
1317 for (sYOffset = -sMaxUp; sYOffset <= sMaxDown; sYOffset++)
1318 {
1319 for (sXOffset = -sMaxLeft; sXOffset <= sMaxRight; sXOffset++)
1320 {
1321 sGridNo = sEdgepoint1 + sXOffset + (MAXCOL * sYOffset);
1322 gpWorldLevelData[sGridNo].uiFlags &= ~(MAPELEMENT_REACHABLE);
1323 }
1324 }
1325
1326 if( FindBestPath( pSoldier, sEdgepoint2, pSoldier->bLevel, WALKING, COPYREACHABLE, PATH_THROUGH_PEOPLE ) )
1327 {
1328 return TRUE;
1329 }
1330 return FALSE;
1331 }
1332
CalcMapEdgepointClassInsertionCode(INT16 sGridNo)1333 UINT8 CalcMapEdgepointClassInsertionCode( INT16 sGridNo )
1334 {
1335 SOLDIERTYPE Soldier;
1336 std::vector<INT16>* pEdgepoints1 = nullptr;
1337 std::vector<INT16>* pEdgepoints2 = nullptr;
1338 INT16 sClosestSpot1 = NOWHERE, sClosestDist1 = 0x7FFF, sTempDist;
1339 INT16 sClosestSpot2 = NOWHERE, sClosestDist2 = 0x7FFF;
1340 BOOLEAN fPrimaryValid = FALSE, fSecondaryValid = FALSE;
1341
1342 Soldier = SOLDIERTYPE{};
1343 Soldier.bTeam = 1;
1344 Soldier.sGridNo = sGridNo;
1345
1346 if( gMapInformation.sIsolatedGridNo == -1 )
1347 { //If the map has no isolated area, then all edgepoints are primary.
1348 return INSERTION_CODE_PRIMARY_EDGEINDEX;
1349 }
1350
1351 switch( gubTacticalDirection )
1352 {
1353 case NORTH:
1354 pEdgepoints1 = &gps1stNorthEdgepointArray;
1355 pEdgepoints2 = &gps2ndNorthEdgepointArray;
1356 break;
1357 case EAST:
1358 pEdgepoints1 = &gps1stEastEdgepointArray;
1359 pEdgepoints2 = &gps2ndEastEdgepointArray;
1360 break;
1361 case SOUTH:
1362 pEdgepoints1 = &gps1stSouthEdgepointArray;
1363 pEdgepoints2 = &gps2ndSouthEdgepointArray;
1364 break;
1365 case WEST:
1366 pEdgepoints1 = &gps1stWestEdgepointArray;
1367 pEdgepoints2 = &gps2ndWestEdgepointArray;
1368 break;
1369 default:
1370 // WTF???
1371 return INSERTION_CODE_PRIMARY_EDGEINDEX;
1372 }
1373
1374 // Do a 2D search to find the closest map edgepoint and
1375 // try to create a path there
1376 for (INT16 edgepoint : *pEdgepoints1)
1377 {
1378 sTempDist = PythSpacesAway(sGridNo, edgepoint);
1379 if ( sTempDist < sClosestDist1 )
1380 {
1381 sClosestDist1 = sTempDist;
1382 sClosestSpot1 = edgepoint;
1383 }
1384 }
1385 for (INT16 edgepoint : *pEdgepoints2)
1386 {
1387 sTempDist = PythSpacesAway(sGridNo, edgepoint);
1388 if ( sTempDist < sClosestDist2 )
1389 {
1390 sClosestDist2 = sTempDist;
1391 sClosestSpot2 = edgepoint;
1392 }
1393 }
1394
1395 // set the distance limit of the square region
1396 gubNPCDistLimit = 15;
1397
1398 if( !sClosestDist1 || FindBestPath( &Soldier, sClosestSpot1, 0, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
1399 {
1400 fPrimaryValid = TRUE;
1401 }
1402 if( !sClosestDist2 || FindBestPath( &Soldier, sClosestSpot2, 0, WALKING, NO_COPYROUTE, PATH_THROUGH_PEOPLE ) )
1403 {
1404 fSecondaryValid = TRUE;
1405 }
1406
1407 if( fPrimaryValid == fSecondaryValid )
1408 {
1409 if( sClosestDist2 < sClosestDist1 )
1410 {
1411 return INSERTION_CODE_SECONDARY_EDGEINDEX;
1412 }
1413 return INSERTION_CODE_PRIMARY_EDGEINDEX;
1414 }
1415 if( fPrimaryValid )
1416 {
1417 return INSERTION_CODE_PRIMARY_EDGEINDEX;
1418 }
1419 return INSERTION_CODE_SECONDARY_EDGEINDEX;
1420 }
1421
1422
ShowMapEdgepoint(std::vector<INT16> & edgepoints,UINT16 const idx)1423 static bool ShowMapEdgepoint(std::vector<INT16>& edgepoints, UINT16 const idx)
1424 {
1425 INT32 n_illegal = 0;
1426 for (INT16 edgepoint : edgepoints)
1427 {
1428 if (edgepoint != -1)
1429 {
1430 AddTopmostToTail(edgepoint, idx);
1431 }
1432 else
1433 {
1434 ++n_illegal;
1435 }
1436 }
1437 return n_illegal;
1438 }
1439
1440
ShowMapEdgepoints()1441 void ShowMapEdgepoints()
1442 {
1443 INT32 n_illegal1 = 0;
1444 n_illegal1 += ShowMapEdgepoint(gps1stNorthEdgepointArray, FIRSTPOINTERS5);
1445 n_illegal1 += ShowMapEdgepoint(gps1stEastEdgepointArray, FIRSTPOINTERS5);
1446 n_illegal1 += ShowMapEdgepoint(gps1stSouthEdgepointArray, FIRSTPOINTERS5);
1447 n_illegal1 += ShowMapEdgepoint(gps1stWestEdgepointArray, FIRSTPOINTERS5);
1448
1449 INT32 n_illegal2 = 0;
1450 n_illegal2 += ShowMapEdgepoint(gps2ndNorthEdgepointArray, FIRSTPOINTERS6);
1451 n_illegal2 += ShowMapEdgepoint(gps2ndEastEdgepointArray, FIRSTPOINTERS6);
1452 n_illegal2 += ShowMapEdgepoint(gps2ndSouthEdgepointArray, FIRSTPOINTERS6);
1453 n_illegal2 += ShowMapEdgepoint(gps2ndWestEdgepointArray, FIRSTPOINTERS6);
1454
1455 if (n_illegal1 == 0 && n_illegal2 == 0)
1456 {
1457 SLOGD("Showing display of map edgepoints");
1458 }
1459 else
1460 {
1461 SLOGD("Showing display of map edgepoints (%d illegal primary, %d illegal secondary)", n_illegal1, n_illegal2);
1462 }
1463 SLOGD("N:%d:%d E:%d:%d S:%d:%d W:%d:%d",
1464 static_cast<int>(gps1stNorthEdgepointArray.size()), static_cast<int>(gps2ndNorthEdgepointArray.size()),
1465 static_cast<int>(gps1stEastEdgepointArray.size()), static_cast<int>(gps2ndEastEdgepointArray.size()),
1466 static_cast<int>(gps1stSouthEdgepointArray.size()), static_cast<int>(gps2ndSouthEdgepointArray.size()),
1467 static_cast<int>(gps1stWestEdgepointArray.size()), static_cast<int>(gps2ndWestEdgepointArray.size()));
1468 }
1469
1470
HideMapEdgepoint(std::vector<INT16> & edgepoints)1471 static void HideMapEdgepoint(std::vector<INT16>& edgepoints)
1472 {
1473 for (INT16 edgepoint : edgepoints)
1474 {
1475 if (edgepoint == -1) continue;
1476 RemoveAllTopmostsOfTypeRange(edgepoint, FIRSTPOINTERS, FIRSTPOINTERS);
1477 }
1478 }
1479
1480
HideMapEdgepoints()1481 void HideMapEdgepoints()
1482 {
1483 SLOGD("Removing display of map edgepoints");
1484
1485 HideMapEdgepoint(gps1stNorthEdgepointArray);
1486 HideMapEdgepoint(gps1stEastEdgepointArray);
1487 HideMapEdgepoint(gps1stSouthEdgepointArray);
1488 HideMapEdgepoint(gps1stWestEdgepointArray);
1489
1490 HideMapEdgepoint(gps2ndNorthEdgepointArray);
1491 HideMapEdgepoint(gps2ndEastEdgepointArray);
1492 HideMapEdgepoint(gps2ndSouthEdgepointArray);
1493 HideMapEdgepoint(gps2ndWestEdgepointArray);
1494 }
1495