MAP.CPP (76464B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: F:\projects\c&c\vcs\code\map.cpv 2.17 16 Oct 1995 16:52:22 JOE_BOSTIC $ */ 17 /*********************************************************************************************** 18 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : MAP.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : September 10, 1993 * 28 * * 29 * Last Update : August 20, 1995 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * MapClass::Cell_Distance -- Determines the distance between two cells. * 34 * MapClass::Cell_Region -- Determines the region from a specified cell number. * 35 * MapClass::Cell_Threat -- Gets a houses threat value for a cell * 36 * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * 37 * MapClass::In_Radar -- Is specified cell in the radar map? * 38 * MapClass::Init -- clears all cells * 39 * MapClass::Logic -- Handles map related logic functions. * 40 * MapClass::One_Time -- Performs special one time initializations for the map. * 41 * MapClass::Overlap_Down -- computes & marks object's overlap cells * 42 * MapClass::Overlap_Up -- Computes & clears object's overlap cells * 43 * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * 44 * MapClass::Pick_Up -- Removes specified object from the map. * 45 * MapClass::Place_Down -- Places the specified object onto the map. * 46 * MapClass::Place_Random_Crate -- Places a crate at random location on map. * 47 * MapClass::Read_Binary -- reads the map's binary image file * 48 * MapClass::Set_Map_Dimensions -- Initialize the map. * 49 * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * 50 * MapClass::Validate -- validates every cell on the map * 51 * MapClass::Write_Binary -- writes the map's binary image file * 52 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 53 54 #include "function.h" 55 56 #define MCW MAP_CELL_W 57 int const MapClass::RadiusOffset[] = { 58 /* 0 */ 0, 59 /* 1 */ (-MCW*1)-1,(-MCW*1)+0,(-MCW*1)+1,-1,1,(MCW*1)-1,(MCW*1)+0,(MCW*1)+1, 60 /* 2 */ (-MCW*2)-1,(-MCW*2)+0,(-MCW*2)+1,(-MCW*1)-2,(-MCW*1)+2,-2,2,(MCW*1)-2,(MCW*1)+2,(MCW*2)-1,(MCW*2)+0,(MCW*2)+1, 61 /* 3 */ (-MCW*3)-1,(-MCW*3)+0,(-MCW*3)+1,(-MCW*2)-2,(-MCW*2)+2,(-MCW*1)-3,(-MCW*1)+3,-3,3,(MCW*1)-3,(MCW*1)+3,(MCW*2)-2,(MCW*2)+2,(MCW*3)-1,(MCW*3)+0,(MCW*3)+1, 62 /* 4 */ (-MCW*4)-1,(-MCW*4)+0,(-MCW*4)+1,(-MCW*3)-3,(-MCW*3)-2,(-MCW*3)+2,(-MCW*3)+3,(-MCW*2)-3,(-MCW*2)+3,(-MCW*1)-4,(-MCW*1)+4,-4,4,(MCW*1)-4,(MCW*1)+4,(MCW*2)-3,(MCW*2)+3,(MCW*3)-3,(MCW*3)-2,(MCW*3)+2,(MCW*3)+3,(MCW*4)-1,(MCW*4)+0,(MCW*4)+1, 63 /* 5 */ (-MCW*5)-1,(-MCW*5)+0,(-MCW*5)+1,(-MCW*4)-3,(-MCW*4)-2,(-MCW*4)+2,(-MCW*4)+3,(-MCW*3)-4,(-MCW*3)+4,(-MCW*2)-4,(-MCW*2)+4,(-MCW*1)-5,(-MCW*1)+5,-5,5,(MCW*1)-5,(MCW*1)+5,(MCW*2)-4,(MCW*2)+4,(MCW*3)-4,(MCW*3)+4,(MCW*4)-3,(MCW*4)-2,(MCW*4)+2,(MCW*4)+3,(MCW*5)-1,(MCW*5)+0,(MCW*5)+1, 64 /* 6 */ (-MCW*6)-1,(-MCW*6)+0,(-MCW*6)+1,(-MCW*5)-3,(-MCW*5)-2,(-MCW*5)+2,(-MCW*5)+3,(-MCW*4)-4,(-MCW*4)+4,(-MCW*3)-5,(-MCW*3)+5,(-MCW*2)-5,(-MCW*2)+5,(-MCW*1)-6,(-MCW*1)+6,-6,6,(MCW*1)-6,(MCW*1)+6,(MCW*2)-5,(MCW*2)+5,(MCW*3)-5,(MCW*3)+5,(MCW*4)-4,(MCW*4)+4,(MCW*5)-3,(MCW*5)-2,(MCW*5)+2,(MCW*5)+3,(MCW*6)-1,(MCW*6)+0,(MCW*6)+1, 65 /* 7 */ (-MCW*7)-1,(-MCW*7)+0,(-MCW*7)+1,(-MCW*6)-3,(-MCW*6)-2,(-MCW*6)+2,(-MCW*6)+3,(-MCW*5)-5,(-MCW*5)-4,(-MCW*5)+4,(-MCW*5)+5,(-MCW*4)-5,(-MCW*4)+5,(-MCW*3)-6,(-MCW*3)+6,(-MCW*2)-6,(-MCW*2)+6,(-MCW*1)-7,(-MCW*1)+7,-7,7,(MCW*1)-7,(MCW*1)+7,(MCW*2)-6,(MCW*2)+6,(MCW*3)-6,(MCW*3)+6,(MCW*4)-5,(MCW*4)+5,(MCW*5)-5,(MCW*5)-4,(MCW*5)+4,(MCW*5)+5,(MCW*6)-3,(MCW*6)-2,(MCW*6)+2,(MCW*6)+3,(MCW*7)-1,(MCW*7)+0,(MCW*7)+1, 66 /* 8 */ (-MCW*8)-1,(-MCW*8)+0,(-MCW*8)+1,(-MCW*7)-3,(-MCW*7)-2,(-MCW*7)+2,(-MCW*7)+3,(-MCW*6)-5,(-MCW*6)-4,(-MCW*6)+4,(-MCW*6)+5,(-MCW*5)-6,(-MCW*5)+6,(-MCW*4)-6,(-MCW*4)+6,(-MCW*3)-7,(-MCW*3)+7,(-MCW*2)-7,(-MCW*2)+7,(-MCW*1)-8,(-MCW*1)+8,-8,8,(MCW*1)-8,(MCW*1)+8,(MCW*2)-7,(MCW*2)+7,(MCW*3)-7,(MCW*3)+7,(MCW*4)-6,(MCW*4)+6,(MCW*5)-6,(MCW*5)+6,(MCW*6)-5,(MCW*6)-4,(MCW*6)+4,(MCW*6)+5,(MCW*7)-3,(MCW*7)-2,(MCW*7)+2,(MCW*7)+3,(MCW*8)-1,(MCW*8)+0,(MCW*8)+1, 67 /* 9 */ (-MCW*9)-1,(-MCW*9)+0,(-MCW*9)+1,(-MCW*8)-3,(-MCW*8)-2,(-MCW*8)+2,(-MCW*8)+3,(-MCW*7)-5,(-MCW*7)-4,(-MCW*7)+4,(-MCW*7)+5,(-MCW*6)-6,(-MCW*6)+6,(-MCW*5)-7,(-MCW*5)+7,(-MCW*4)-7,(-MCW*4)+7,(-MCW*3)-8,(-MCW*3)+8,(-MCW*2)-8,(-MCW*2)+8,(-MCW*1)-9,(-MCW*1)+9,-9,9,(MCW*1)-9,(MCW*1)+9,(MCW*2)-8,(MCW*2)+8,(MCW*3)-8,(MCW*3)+8,(MCW*4)-7,(MCW*4)+7,(MCW*5)-7,(MCW*5)+7,(MCW*6)-6,(MCW*6)+6,(MCW*7)-5,(MCW*7)-4,(MCW*7)+4,(MCW*7)+5,(MCW*8)-3,(MCW*8)-2,(MCW*8)+2,(MCW*8)+3,(MCW*9)-1,(MCW*9)+0,(MCW*9)+1, 68 /* 10 */ (-MCW*10)-1,(-MCW*10)+0,(-MCW*10)+1,(-MCW*9)-3,(-MCW*9)-2,(-MCW*9)+2,(-MCW*9)+3,(-MCW*8)-5,(-MCW*8)-4,(-MCW*8)+4,(-MCW*8)+5,(-MCW*7)-7,(-MCW*7)-6,(-MCW*7)+6,(-MCW*7)+7,(-MCW*6)-7,(-MCW*6)+7,(-MCW*5)-8,(-MCW*5)+8,(-MCW*4)-8,(-MCW*4)+8,(-MCW*3)-9,(-MCW*3)+9,(-MCW*2)-9,(-MCW*2)+9,(-MCW*1)-10,(-MCW*1)+10,-10,10,(MCW*1)-10,(MCW*1)+10,(MCW*2)-9,(MCW*2)+9,(MCW*3)-9,(MCW*3)+9,(MCW*4)-8,(MCW*4)+8,(MCW*5)-8,(MCW*5)+8,(MCW*6)-7,(MCW*6)+7,(MCW*7)-7,(MCW*7)-6,(MCW*7)+6,(MCW*7)+7,(MCW*8)-5,(MCW*8)-4, 69 (MCW*8)+4,(MCW*8)+5,(MCW*9)-3,(MCW*9)-2,(MCW*9)+2,(MCW*9)+3,(MCW*10)-1,(MCW*10)+0,(MCW*10)+1, 70 }; 71 72 int const MapClass::RadiusCount[11] = {1,9,21,37,61,89,121,161,205,253,309}; 73 74 75 CellClass *BlubCell; 76 77 /*********************************************************************************************** 78 * MapClass::One_Time -- Performs special one time initializations for the map. * 79 * * 80 * This routine is used by the game initialization function in order to perform any one * 81 * time initializations required for the map. This includes allocation of the map and * 82 * setting up its default dimensions. * 83 * * 84 * INPUT: none * 85 * * 86 * OUTPUT: none * 87 * * 88 * WARNINGS: This routine MUST be called once and only once. * 89 * * 90 * HISTORY: * 91 * 05/31/1994 JLB : Created. * 92 * 12/01/1994 BR : Added CellTriggers initialization * 93 *=============================================================================================*/ 94 void MapClass::One_Time(void) 95 { 96 GScreenClass::One_Time(); 97 98 XSize = MAP_CELL_W; 99 YSize = MAP_CELL_H; 100 Size = XSize * YSize; 101 102 /* 103 ** Allocate the cell array. 104 */ 105 Alloc_Cells(); 106 107 /* 108 ** Init the CellTriggers array to the required size. 109 */ 110 CellTriggers.Resize(MAP_CELL_TOTAL); 111 } 112 113 //////////////////////////////////////////////////// 114 // Added this function to allow the editor to setup the map without setting up the entire system. - 06/18/2019 JAS 115 void MapClass::One_Time_Editor(void) 116 { 117 XSize = MAP_CELL_W; 118 YSize = MAP_CELL_H; 119 Size = XSize * YSize; 120 121 /* 122 ** Allocate the cell array. 123 */ 124 Alloc_Cells(); 125 126 /* 127 ** Init the CellTriggers array to the required size. 128 */ 129 CellTriggers.Resize(MAP_CELL_TOTAL); 130 } 131 // End of change. - 06/15/2019 JAS 132 //////////////////////////////////////////////////// 133 134 /*********************************************************************************************** 135 * MapClass::Init_Clear -- clears the map & buffers to a known state * 136 * * 137 * INPUT: * 138 * none. * 139 * * 140 * OUTPUT: * 141 * none. * 142 * * 143 * WARNINGS: * 144 * none. * 145 * * 146 * HISTORY: * 147 * 03/17/1995 BRR : Created. * 148 *=============================================================================================*/ 149 void MapClass::Init_Clear(void) 150 { 151 GScreenClass::Init_Clear(); 152 Init_Cells(); 153 TiberiumScan = 0; 154 IsForwardScan = true; 155 TiberiumGrowthCount = 0; 156 TiberiumSpreadCount = 0; 157 } 158 159 160 /*********************************************************************************************** 161 * MapClass::Alloc_Cells -- allocates the cell array * 162 * * 163 * This routine should be called at One_Time, and after loading the Map object from a save * 164 * game, but prior to loading the cell objects. * 165 * * 166 * INPUT: * 167 * none. * 168 * * 169 * OUTPUT: * 170 * none. * 171 * * 172 * WARNINGS: * 173 * none. * 174 * * 175 * HISTORY: * 176 * 03/17/1995 BRR : Created. * 177 *=============================================================================================*/ 178 void MapClass::Alloc_Cells(void) 179 { 180 /* 181 ** Assume that whatever the contents of the VectorClass are is garbage 182 ** (it may have been loaded from a save-game file), so zero it out first. 183 */ 184 Vector = 0; 185 VectorMax = 0; 186 IsAllocated = 0; 187 Resize(Size); 188 } 189 190 191 /*********************************************************************************************** 192 * MapClass::Free_Cells -- frees the cell array * 193 * * 194 * This routine is used by the Load_Game routine to free the map's cell array before loading * 195 * the map object from disk; the array is then re-allocated & cleared before the cell objects * 196 * are loaded. * 197 * * 198 * INPUT: * 199 * none. * 200 * * 201 * OUTPUT: * 202 * none. * 203 * * 204 * WARNINGS: * 205 * none. * 206 * * 207 * HISTORY: * 208 * 03/17/1995 BRR : Created. * 209 *=============================================================================================*/ 210 void MapClass::Free_Cells(void) 211 { 212 Clear(); 213 } 214 215 216 /*********************************************************************************************** 217 * MapClass::Init_Cells -- Initializes the cell array to a fresh state. * 218 * * 219 * This routine is used by Init_Clear to set the cells to a known state; it's also used by * 220 * the Load_Game routine to init all cells before loading a set of cells from disk, so it * 221 * needs to be called separately from the other Init_xxx() routines. * 222 * * 223 * INPUT: * 224 * none. * 225 * * 226 * OUTPUT: * 227 * none. * 228 * * 229 * WARNINGS: * 230 * none. * 231 * * 232 * HISTORY: * 233 * 03/17/1995 BRR : Created. * 234 *=============================================================================================*/ 235 void MapClass::Init_Cells(void) 236 { 237 TotalValue = 0; 238 #ifdef NEVER 239 Free_Cells(); 240 Alloc_Cells(); 241 #else 242 for (int index = 0; index < MAP_CELL_TOTAL; index++) { 243 Map[index] = CellClass(); 244 } 245 #endif 246 } 247 248 249 /*********************************************************************************************** 250 * MapClass::Set_Map_Dimensions -- Set map dimensions. * 251 * * 252 * This routine is used to set the legal limits and position of the * 253 * map as it relates to the overall map array. Typically, this is * 254 * called by the scenario loading code. * 255 * * 256 * INPUT: x,y -- The X and Y coordinate of the "upper left" corner * 257 * of the map. * 258 * * 259 * w,h -- The width and height of the legal map. * 260 * * 261 * OUTPUT: none * 262 * * 263 * WARNINGS: none * 264 * * 265 * HISTORY: * 266 * 05/14/1994 JLB : Created. * 267 *=============================================================================================*/ 268 void MapClass::Set_Map_Dimensions(int x, int y, int w, int h) 269 { 270 MapCellX = x; 271 MapCellY = y; 272 MapCellWidth = w; 273 MapCellHeight = h; 274 } 275 276 277 /*********************************************************************************************** 278 * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * 279 * * 280 * This routine is used to reveal the cells around a specific location. * 281 * Typically, as a unit moves or is deployed, this routine will be * 282 * called. Since it deals with MANY cells, it needs to be extremely * 283 * fast. * 284 * * 285 * INPUT: house -- Player to perform the visibility update for * 286 * * 287 * cell -- The coordinate that the sighting originates from. * 288 * * 289 * sightrange-- The distance in cells that sighting extends. * 290 * * 291 * incremental-- Is this an incremental sighting. In other * 292 * words, has this function been called before where * 293 * the center coordinate is no more than one cell * 294 * distant from the last time? * 295 * * 296 * OUTPUT: none * 297 * * 298 * WARNINGS: none * 299 * * 300 * HISTORY: * 301 * 05/19/1992 JLB : Created. * 302 * 03/08/1994 JLB : Updated to use sight table and incremental flag. * 303 * 05/18/1994 JLB : Converted to member function. * 304 * 03/06/2019 ST : Added HouseClass pointer parameter for new multiplayer code * 305 *=============================================================================================*/ 306 void MapClass::Sight_From(HouseClass *house, CELL cell, int sightrange, bool incremental) 307 { 308 int xx; // Center cell X coordinate (bounds checking). 309 int const *ptr; // Offset pointer. 310 int count; // Counter for number of offsets to process. 311 312 // Added. ST - 3/6/2019 1:50PM 313 if ((house == NULL || house->IsHuman == false) && !ShareAllyVisibility) { 314 return; 315 } 316 317 /* 318 ** Units that are off-map cannot sight. 319 */ 320 if (!In_Radar(cell)) return; 321 if (!sightrange || sightrange > 10) return; 322 323 /* 324 ** Determine logical cell coordinate for center scan point. 325 */ 326 xx = Cell_X(cell); 327 328 /* 329 ** Incremental scans only scan the outer rings. Full scans 330 ** scan all internal cells as well. 331 */ 332 count = RadiusCount[sightrange]; 333 ptr = &RadiusOffset[0]; 334 if (incremental) { 335 if (sightrange > 1) { 336 ptr += RadiusCount[sightrange-2]; 337 count -= RadiusCount[sightrange-2]; 338 } 339 } 340 341 /* 342 ** Process all offsets required for the desired scan. 343 */ 344 while (count--) { 345 CELL newcell; // New cell with offset. 346 int xdiff; // New cell's X coordinate distance from center. 347 348 newcell = cell + *ptr++; 349 350 /* 351 ** Determine if the map edge has been wrapped. If so, 352 ** then don't process the cell. 353 */ 354 if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; 355 xdiff = Cell_X(newcell) - xx; 356 xdiff = ABS(xdiff); 357 if (xdiff > sightrange) continue; 358 if (Distance(newcell, cell) > sightrange) continue; 359 360 /* 361 ** Map the cell. For incremental scans, then update 362 ** adjacent cells as well. For full scans, just update 363 ** the cell itself. 364 */ 365 // Pass the house through, instead of assuming it's the local player. ST - 3/6/2019 10:26AM 366 //Map.Map_Cell(newcell, PlayerPtr); 367 Map.Map_Cell(newcell, house, true); 368 } 369 } 370 371 372 /*********************************************************************************************** 373 * MapClass::Cell_Distance -- Determines the distance between two cells. * 374 * * 375 * This routine will return with the calculated "straight line" * 376 * distance between the two cells specified. It uses the dragon strike * 377 * method of distance calculation. * 378 * * 379 * INPUT: cell1 -- First cell. * 380 * * 381 * cell2 -- Second cell. * 382 * * 383 * OUTPUT: Returns with the "cell" distance between the two cells * 384 * specified. * 385 * * 386 * WARNINGS: none * 387 * * 388 * HISTORY: * 389 * 04/29/1994 JLB : Created. * 390 * 04/30/1994 JLB : Converted to member function. * 391 *=============================================================================================*/ 392 int MapClass::Cell_Distance(CELL cell1, CELL cell2) 393 { 394 register int x,y; // Difference on X and Y axis. 395 396 x = Cell_X(cell1) - Cell_X(cell2); 397 y = Cell_Y(cell1) - Cell_Y(cell2); 398 399 if (x < 0) x = -x; 400 if (y < 0) y = -y; 401 402 if (x > y) { 403 return(x + (y>>1)); 404 } 405 return(y + (x>>1)); 406 } 407 408 409 /*********************************************************************************************** 410 * MapClass::In_Radar -- Is specified cell in the radar map? * 411 * * 412 * This determines if the specified cell can be within the navigable * 413 * bounds of the map. Technically, this means, any cell that can be * 414 * scanned by radar. If a cell returns false from this function, then * 415 * the player could never move to or pass over this cell. * 416 * * 417 * INPUT: cell -- The cell to examine. * 418 * * 419 * OUTPUT: bool; Is this cell possible to be displayed on radar? * 420 * * 421 * WARNINGS: none * 422 * * 423 * HISTORY: * 424 * 10/07/1992 JLB : Created. * 425 * 04/30/1994 JLB : Converted to member function. * 426 * 05/01/1994 JLB : Speeded up. * 427 *=============================================================================================*/ 428 bool MapClass::In_Radar(CELL cell) const 429 { 430 if (cell & 0xF000) return(false); 431 return((unsigned)(Cell_X(cell) - MapCellX) < (unsigned)MapCellWidth && (unsigned)(Cell_Y(cell) - MapCellY) < (unsigned)MapCellHeight); 432 } 433 434 435 /*********************************************************************************************** 436 * MapClass::Place_Down -- Places the specified object onto the map. * 437 * * 438 * This routine is used to place an object onto the map. It updates the "occupier" of the * 439 * cells that this object covers. The cells are determined from the Occupy_List function * 440 * provided by the object. Only one cell can have an occupier and this routine is the only * 441 * place that sets this condition. * 442 * * 443 * INPUT: cell -- The cell to base object occupation around. * 444 * * 445 * object -- The object to place onto the map. * 446 * * 447 * OUTPUT: none * 448 * * 449 * WARNINGS: none * 450 * * 451 * HISTORY: * 452 * 07/31/1994 JLB : Created. * 453 *=============================================================================================*/ 454 void MapClass::Place_Down(CELL cell, ObjectClass * object) 455 { 456 if (!object) return; 457 458 short const *list = object->Occupy_List(); 459 while (*list != REFRESH_EOL) { 460 CELL newcell = cell + *list++; 461 if ((unsigned)newcell < MAP_CELL_TOTAL) { 462 (*this)[newcell].Occupy_Down(object); 463 (*this)[newcell].Recalc_Attributes(); 464 (*this)[newcell].Redraw_Objects(); 465 } 466 } 467 468 list = object->Overlap_List(); 469 while (*list != REFRESH_EOL) { 470 CELL newcell = cell + *list++; 471 if ((unsigned)newcell < MAP_CELL_TOTAL) { 472 (*this)[newcell].Overlap_Down(object); 473 (*this)[newcell].Redraw_Objects(); 474 } 475 } 476 } 477 478 479 /*********************************************************************************************** 480 * MapClass::Pick_Up -- Removes specified object from the map. * 481 * * 482 * The object specified is removed from the map by this routine. This will remove the * 483 * occupation flag for all the cells that the object covers. The cells that are covered * 484 * are determined from the Occupy_List function. * 485 * * 486 * INPUT: cell -- The cell that the object is centered about. * 487 * * 488 * object -- Pointer to the object that will be removed. * 489 * * 490 * OUTPUT: none * 491 * * 492 * WARNINGS: none * 493 * * 494 * HISTORY: * 495 * 07/31/1994 JLB : Created. * 496 *=============================================================================================*/ 497 void MapClass::Pick_Up(CELL cell, ObjectClass * object) 498 { 499 if (!object) return; 500 501 short const *list = object->Occupy_List(); 502 while (*list != REFRESH_EOL) { 503 CELL newcell = cell + *list++; 504 if ((unsigned)newcell < MAP_CELL_TOTAL) { 505 (*this)[newcell].Occupy_Up(object); 506 (*this)[newcell].Recalc_Attributes(); 507 (*this)[newcell].Redraw_Objects(); 508 } 509 } 510 511 list = object->Overlap_List(); 512 while (*list != REFRESH_EOL) { 513 CELL newcell = cell + *list++; 514 if ((unsigned)newcell < MAP_CELL_TOTAL) { 515 (*this)[newcell].Overlap_Up(object); 516 (*this)[newcell].Redraw_Objects(); 517 } 518 } 519 } 520 521 522 /*********************************************************************************************** 523 * MapClass::Overlap_Down -- computes & marks object's overlap cells * 524 * * 525 * This routine is just like Place_Down, but it doesn't mark the cell's Occupier. * 526 * This routine is used to implement MARK_OVERLAP_DOWN, which is useful for changing * 527 * an object's render size, but not its logical size (ie when it's selected or an * 528 * animation is attached to it). * 529 * * 530 * INPUT: * 531 * cell -- The cell to base object overlap around. * 532 * object -- The object to place onto the map. * 533 * * 534 * OUTPUT: * 535 * none. * 536 * * 537 * WARNINGS: * 538 * none. * 539 * * 540 * HISTORY: * 541 * 07/12/1995 BRR : Created. * 542 *=============================================================================================*/ 543 void MapClass::Overlap_Down(CELL cell, ObjectClass * object) 544 { 545 if (!object) return; 546 547 short const *list = object->Overlap_List(); 548 while (*list != REFRESH_EOL) { 549 CELL newcell = cell + *list++; 550 if ((unsigned)newcell < MAP_CELL_TOTAL) { 551 (*this)[newcell].Overlap_Down(object); 552 (*this)[newcell].Redraw_Objects(); 553 } 554 } 555 } 556 557 558 /*********************************************************************************************** 559 * MapClass::Overlap_Up -- Computes & clears object's overlap cells * 560 * * 561 * This routine is just like Pick_Up, but it doesn't mark the cell's Occupier. * 562 * This routine is used to implement MARK_OVERLAP_UP, which is useful for changing * 563 * an object's render size, but not its logical size (ie when it's selected or an * 564 * animation is attached to it). * 565 * * 566 * INPUT: * 567 * cell -- The cell to base object overlap around. * 568 * object -- The object to place onto the map. * 569 * * 570 * OUTPUT: * 571 * none. * 572 * * 573 * WARNINGS: * 574 * none. * 575 * * 576 * HISTORY: * 577 * 07/12/1995 BRR : Created. * 578 *=============================================================================================*/ 579 void MapClass::Overlap_Up(CELL cell, ObjectClass * object) 580 { 581 if (!object) return; 582 583 short const *list = object->Overlap_List(); 584 while (*list != REFRESH_EOL) { 585 CELL newcell = cell + *list++; 586 if ((unsigned)newcell < MAP_CELL_TOTAL) { 587 (*this)[newcell].Overlap_Up(object); 588 (*this)[newcell].Redraw_Objects(); 589 } 590 } 591 } 592 593 594 /*********************************************************************************************** 595 * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * 596 * * 597 * This routine will clean up anything necessary with the presumption that the map has * 598 * been freshly created. Such things to clean up include various tiberium concentrations. * 599 * * 600 * INPUT: none * 601 * * 602 * OUTPUT: Returns the total credit value of the tiberium on the map. * 603 * * 604 * WARNINGS: none * 605 * * 606 * HISTORY: * 607 * 09/19/1994 JLB : Created. * 608 * 02/13/1995 JLB : Returns total tiberium worth. * 609 * 02/15/1995 JLB : Optimal scan. * 610 *=============================================================================================*/ 611 long MapClass::Overpass(void) 612 { 613 long value = 0; 614 615 /* 616 ** Smooth out Tiberium. Cells that are not surrounded by other tiberium 617 ** will be reduced in density. 618 */ 619 for (int y = 0; y < MapCellHeight-1; y++) { 620 for (int x = 0; x < MapCellWidth; x++) { 621 value += (*this)[(MapCellY+y) * MAP_CELL_W + (MapCellX+x)].Tiberium_Adjust(true); 622 } 623 } 624 return(value); 625 } 626 627 628 /*********************************************************************************************** 629 * MapClass::Read_Binary -- reads the map's binary image file * 630 * * 631 * INPUT: * 632 * root root filename for scenario * 633 * crc ptr to CRC value to update * 634 * * 635 * OUTPUT: * 636 * 1 = success, 0 = failure * 637 * * 638 * WARNINGS: * 639 * none. * 640 * * 641 * HISTORY: * 642 * 11/14/1994 BR : Created. * 643 * 01/08/1995 JLB : Fixup any obsolete icons detected. * 644 *=============================================================================================*/ 645 #ifdef DEMO 646 bool MapClass::Read_Binary(char const * root, unsigned long *) 647 #else 648 bool MapClass::Read_Binary(char const * root, unsigned long *crc) 649 #endif 650 { 651 CCFileClass file; 652 char fname[_MAX_FNAME+_MAX_EXT]; 653 int i; 654 char *map; 655 void *rawmap; 656 void const * shape; 657 658 /* 659 ** Filename = INI name with BIN extension. 660 */ 661 sprintf(fname,"%s.BIN",root); 662 663 /* 664 ** Create object & open file. 665 */ 666 file.Set_Name(fname); 667 if (!file.Is_Available()) { 668 return(false); 669 } 670 file.Open(READ); 671 672 /* 673 ** Loop through all cells. 674 */ 675 CellClass * cellptr = &Map[0]; 676 for (i = 0; i < MAP_CELL_TOTAL; i++) { 677 struct { 678 TemplateType TType; // Template type. 679 unsigned char TIcon; // Template icon number. 680 } temp; 681 682 if (file.Read(&temp, sizeof(temp)) != sizeof(temp)) break; 683 if (temp.TType == (TemplateType)255) { 684 temp.TType = TEMPLATE_NONE; 685 } 686 687 /* 688 ** Verify that the template type actually contains the template number specified. If 689 ** an illegal icon was specified, then replace it with clear terrain. 690 */ 691 if (temp.TType != TEMPLATE_CLEAR1 && temp.TType != TEMPLATE_NONE) { 692 TemplateTypeClass const &ttype = TemplateTypeClass::As_Reference(temp.TType); 693 shape = ttype.Get_Image_Data(); 694 if (shape) { 695 rawmap = Get_Icon_Set_Map(shape); 696 if (rawmap) { 697 map = (char*)rawmap; 698 if ((temp.TIcon >= (ttype.Width * ttype.Height)) || (map[temp.TIcon] == -1)) { 699 temp.TIcon = 0; 700 temp.TType = TEMPLATE_NONE; 701 } 702 } 703 } 704 } 705 706 cellptr->TType = temp.TType; 707 cellptr->TIcon = temp.TIcon; 708 cellptr->Recalc_Attributes(); 709 710 #ifndef DEMO 711 Add_CRC(crc, (unsigned long)cellptr->TType); 712 Add_CRC(crc, (unsigned long)cellptr->TIcon); 713 #endif 714 715 cellptr++; 716 } 717 718 /* 719 ** Close the file. 720 */ 721 file.Close(); 722 723 return(i == MAP_CELL_TOTAL); 724 } 725 726 727 /*********************************************************************************************** 728 * MapClass::Read_BinaryRead_Binary_File -- reads the map's binary image file * 729 * * 730 * INPUT: * 731 * fname file path for scenario * 732 * crc ptr to CRC value to update * 733 * * 734 * OUTPUT: * 735 * 1 = success, 0 = failure * 736 * * 737 * WARNINGS: * 738 * none. * 739 * * 740 * HISTORY: * 741 * 10/28/2019 JAS : Created. * 742 *=============================================================================================*/ 743 bool MapClass::Read_Binary_File(char const * fname, unsigned long *crc) 744 { 745 CCFileClass file; 746 747 int i; 748 char *map; 749 void *rawmap; 750 void const * shape; 751 752 /* 753 ** Create object & open file. 754 */ 755 file.Set_Name(fname); 756 if (!file.Is_Available()) { 757 return(false); 758 } 759 file.Open(READ); 760 761 /* 762 ** Loop through all cells. 763 */ 764 CellClass * cellptr = &Map[0]; 765 for (i = 0; i < MAP_CELL_TOTAL; i++) { 766 struct { 767 TemplateType TType; // Template type. 768 unsigned char TIcon; // Template icon number. 769 } temp; 770 771 if (file.Read(&temp, sizeof(temp)) != sizeof(temp)) break; 772 if (temp.TType == (TemplateType)255) { 773 temp.TType = TEMPLATE_NONE; 774 } 775 776 /* 777 ** Verify that the template type actually contains the template number specified. If 778 ** an illegal icon was specified, then replace it with clear terrain. 779 */ 780 if (temp.TType != TEMPLATE_CLEAR1 && temp.TType != TEMPLATE_NONE) { 781 shape = TemplateTypeClass::As_Reference(temp.TType).Get_Image_Data(); 782 if (shape) { 783 rawmap = Get_Icon_Set_Map(shape); 784 if (rawmap) { 785 map = (char*)rawmap; 786 if (map[temp.TIcon] == -1) { 787 temp.TIcon = 0; 788 temp.TType = TEMPLATE_NONE; 789 } 790 } 791 } 792 } 793 794 cellptr->TType = temp.TType; 795 cellptr->TIcon = temp.TIcon; 796 cellptr->Recalc_Attributes(); 797 798 #ifndef DEMO 799 Add_CRC(crc, (unsigned long)cellptr->TType); 800 Add_CRC(crc, (unsigned long)cellptr->TIcon); 801 #endif 802 803 cellptr++; 804 } 805 806 /* 807 ** Close the file. 808 */ 809 file.Close(); 810 811 return(i == MAP_CELL_TOTAL); 812 } 813 814 815 816 /*********************************************************************************************** 817 * MapClass::Write_Binary -- writes the map's binary image file * 818 * * 819 * INPUT: * 820 * root root filename for scenario * 821 * * 822 * OUTPUT: * 823 * 1 = success, 0 = failure * 824 * * 825 * WARNINGS: * 826 * none. * 827 * * 828 * HISTORY: * 829 * 11/14/1994 BR : Created. * 830 *=============================================================================================*/ 831 bool MapClass::Write_Binary(char const * root) 832 { 833 CCFileClass *file; 834 char fname[_MAX_FNAME+_MAX_EXT]; 835 int i; 836 837 /* 838 ** Filename = INI name with BIN extension. 839 */ 840 sprintf(fname,"%s.BIN",root); 841 842 /* 843 ** Create object & open file. 844 */ 845 file = new CCFileClass(fname); 846 file->Open(WRITE); 847 848 /* 849 ** Loop through all cells. 850 */ 851 for (i = 0; i < MAP_CELL_TOTAL; i++) { 852 /* 853 ** Save TType. 854 */ 855 if (file->Write (&(Map[i].TType), sizeof(TemplateType)) != sizeof(TemplateType)) { 856 file->Close(); 857 delete file; 858 return(false); 859 } 860 861 /* 862 ** Save TIcon. 863 */ 864 if (file->Write (&(Map[i].TIcon), sizeof(unsigned char)) != sizeof(unsigned char)) { 865 file->Close(); 866 delete file; 867 return(false); 868 } 869 } 870 871 /* 872 ** Close the file. 873 */ 874 file->Close(); 875 delete file; 876 877 return(true); 878 } 879 880 881 /*********************************************************************************************** 882 * MapClass::Logic -- Handles map related logic functions. * 883 * * 884 * Manages tiberium growth and spread. * 885 * * 886 * INPUT: none * 887 * * 888 * OUTPUT: none * 889 * * 890 * WARNINGS: none * 891 * * 892 * HISTORY: * 893 * 05/11/1995 JLB : Created. * 894 * 07/09/1995 JLB : Handles two directional scan. * 895 * 08/01/1995 JLB : Gives stronger weight to blossom trees. * 896 *=============================================================================================*/ 897 void MapClass::Logic(void) 898 { 899 if (Debug_Force_Crash) { *((int *)0) = 1; } 900 901 /* 902 ** Bail early if there is no allowed growth or spread of Tiberium. 903 */ 904 if (!Special.IsTGrowth && !Special.IsTSpread) return; 905 906 /* 907 ** Scan another block of the map in order to accumulate the potential 908 ** Tiberium cells that can grow or spread. 909 */ 910 int subcount = 30; 911 int index; 912 for (index = TiberiumScan; index < MAP_CELL_TOTAL; index++) { 913 CELL cell = index; 914 if (!IsForwardScan) cell = (MAP_CELL_TOTAL-1) - index; 915 CellClass *ptr = &(*this)[cell]; 916 917 if (Special.IsTGrowth && ptr->Land_Type() == LAND_TIBERIUM && ptr->OverlayData < 11) { 918 if (TiberiumGrowthCount < sizeof(TiberiumGrowth)/sizeof(TiberiumGrowth[0])) { 919 TiberiumGrowth[TiberiumGrowthCount++] = cell; 920 } else { 921 TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)] = cell; 922 } 923 } 924 925 /* 926 ** Heavy Tiberium growth can spread. 927 */ 928 TerrainClass * terrain = ptr->Cell_Terrain(); 929 if (Special.IsTSpread && 930 (ptr->Land_Type() == LAND_TIBERIUM && ptr->OverlayData > 6) || 931 (terrain && terrain->Class->IsTiberiumSpawn)) { 932 933 int tries = 1; 934 if (terrain) tries = 3; 935 for (int i = 0; i < tries; i++) { 936 if (TiberiumSpreadCount < sizeof(TiberiumSpread)/sizeof(TiberiumSpread[0])) { 937 TiberiumSpread[TiberiumSpreadCount++] = cell; 938 } else { 939 TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)] = cell; 940 } 941 } 942 } 943 subcount--; 944 if (!subcount) break; 945 } 946 TiberiumScan = index; 947 948 if (TiberiumScan >= MAP_CELL_TOTAL) { 949 int tries = 1; 950 if (Special.IsTFast || GameToPlay != GAME_NORMAL) tries = 2; 951 952 /* 953 ** Use the Tiberium setting as a multiplier on growth rate. ST - 7/1/2020 3:05PM 954 */ 955 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 956 if (MPlayerTiberium > 1) { 957 tries += (MPlayerTiberium - 1) << 1; 958 } 959 } 960 TiberiumScan = 0; 961 IsForwardScan = (IsForwardScan == false); 962 963 /* 964 ** Growth logic. 965 */ 966 if (TiberiumGrowthCount) { 967 for (int i = 0; i < tries; i++) { 968 int pick = Random_Pick(0, TiberiumGrowthCount-1); 969 CELL cell = TiberiumGrowth[pick]; 970 CellClass * newcell = &(*this)[cell]; 971 if (newcell->Land_Type() == LAND_TIBERIUM && newcell->OverlayData < 12-1) { 972 newcell->OverlayData++; 973 } 974 TiberiumGrowth[pick] = TiberiumGrowth[TiberiumGrowthCount - 1]; 975 TiberiumGrowthCount--; 976 if (TiberiumGrowthCount <= 0) { 977 break; 978 } 979 } 980 } 981 TiberiumGrowthCount = 0; 982 983 /* 984 ** Spread logic. 985 */ 986 if (TiberiumSpreadCount) { 987 for (int i = 0; i < tries; i++) { 988 int pick = Random_Pick(0, TiberiumSpreadCount-1); 989 CELL cell = TiberiumSpread[pick]; 990 991 /* 992 ** Find a pseudo-random adjacent cell that doesn't contain any tiberium. 993 */ 994 if (Map.In_Radar(cell)) { 995 FacingType offset = Random_Pick(FACING_N, FACING_NW); 996 for (FacingType index = FACING_N; index < FACING_COUNT; index++) { 997 CellClass *newcell = (*this)[cell].Adjacent_Cell(index+offset); 998 999 if (newcell && newcell->Cell_Object() == NULL && newcell->Land_Type() == LAND_CLEAR && newcell->Overlay == OVERLAY_NONE) { 1000 bool found = false; 1001 1002 switch (newcell->TType) { 1003 case TEMPLATE_BRIDGE1: 1004 case TEMPLATE_BRIDGE2: 1005 case TEMPLATE_BRIDGE3: 1006 case TEMPLATE_BRIDGE4: 1007 break; 1008 1009 default: 1010 found = true; 1011 new OverlayClass(Random_Pick(OVERLAY_TIBERIUM1, OVERLAY_TIBERIUM12), newcell->Cell_Number()); 1012 newcell->OverlayData = 1; 1013 break; 1014 1015 } 1016 if (found) break; 1017 } 1018 } 1019 } 1020 TiberiumSpread[pick] = TiberiumSpread[TiberiumSpreadCount - 1]; 1021 TiberiumSpreadCount--; 1022 if (TiberiumSpreadCount <= 0) { 1023 break; 1024 } 1025 } 1026 } 1027 TiberiumSpreadCount = 0; 1028 } 1029 } 1030 1031 1032 /*********************************************************************************************** 1033 * MapClass::Cell_Region -- Determines the region from a specified cell number. * 1034 * * 1035 * Use this routine to determine what region a particular cell lies in. * 1036 * * 1037 * INPUT: cell -- The cell number to examine. * 1038 * * 1039 * OUTPUT: Returns with the region that the specified cell occupies. * 1040 * * 1041 * WARNINGS: none * 1042 * * 1043 * HISTORY: * 1044 * 03/15/1995 JLB : Created. * 1045 *=============================================================================================*/ 1046 int MapClass::Cell_Region(CELL cell) 1047 { 1048 return((Cell_X(cell) / REGION_WIDTH) + 1) + (((Cell_Y(cell) / REGION_HEIGHT) + 1) * MAP_REGION_WIDTH); 1049 } 1050 1051 1052 /*************************************************************************** 1053 * MapClass::Cell_Threat -- Gets a houses threat value for a cell * 1054 * * 1055 * INPUT: CELL cell - the cell number to check * 1056 * HouseType house - the house to check * 1057 * * 1058 * OUTPUT: * 1059 * * 1060 * WARNINGS: * 1061 * * 1062 * HISTORY: * 1063 * 04/25/1995 PWG : Created. * 1064 *=========================================================================*/ 1065 int MapClass::Cell_Threat(CELL cell, HousesType house) 1066 { 1067 int threat = HouseClass::As_Pointer(house)->Regions[Map.Cell_Region(Map[cell].Cell_Number())].Threat_Value(); 1068 //using function for IsVisible so we have different results for different players - JAS 2019/09/30 1069 if (!threat && Map[cell].Is_Visible(house)) { 1070 threat = 1; 1071 } 1072 return(threat); 1073 } 1074 1075 1076 /*********************************************************************************************** 1077 * MapClass::Place_Random_Crate -- Places a crate at random location on map. * 1078 * * 1079 * This routine will place a crate at a random location on the map. This routine will only * 1080 * make a limited number of attempts to place and if unsuccessful, it will not place any. * 1081 * * 1082 * INPUT: none * 1083 * * 1084 * OUTPUT: Was a crate successfully placed? * 1085 * * 1086 * WARNINGS: none * 1087 * * 1088 * HISTORY: * 1089 * 07/08/1995 JLB : Created. * 1090 *=============================================================================================*/ 1091 bool MapClass::Place_Random_Crate(void) 1092 { 1093 int old = ScenarioInit; 1094 ScenarioInit = 0; 1095 for (int index = 0; index < 100; index++) { 1096 int x = Random_Pick(0, MapCellWidth-1); 1097 int y = Random_Pick(0, MapCellHeight-1); 1098 CELL cell = XY_Cell(MapCellX+x, MapCellY+y); 1099 1100 CellClass * ptr = &(*this)[cell]; 1101 if (ptr->Is_Generally_Clear() && ptr->Overlay == OVERLAY_NONE) { 1102 ptr->Overlay = OVERLAY_WOOD_CRATE; 1103 ptr->OverlayData = 0; 1104 ptr->Redraw_Objects(); 1105 ScenarioInit = old; 1106 return(true); 1107 } 1108 } 1109 ScenarioInit = old; 1110 return(false); 1111 } 1112 1113 1114 /*************************************************************************** 1115 * MapClass::Validate -- validates every cell on the map * 1116 * * 1117 * This is a debugging routine, designed to detect memory trashers that * 1118 * alter the map. This routine is slow, but thorough. * 1119 * * 1120 * INPUT: * 1121 * none. * 1122 * * 1123 * OUTPUT: * 1124 * true = map is OK, false = an error was found * 1125 * * 1126 * WARNINGS: * 1127 * none. * 1128 * * 1129 * HISTORY: * 1130 * 07/08/1995 BRR : Created. * 1131 *=========================================================================*/ 1132 int MapClass::Validate(void) 1133 { 1134 CELL cell; 1135 TemplateType ttype; 1136 unsigned char ticon; 1137 //TemplateTypeClass const *tclass; 1138 //unsigned char map[13*8]; 1139 OverlayType overlay; 1140 SmudgeType smudge; 1141 ObjectClass *obj; 1142 LandType land; 1143 int i; 1144 1145 BlubCell = &((*this)[797]); 1146 1147 if (BlubCell->Overlapper[1]) { 1148 obj = BlubCell->Overlapper[1]; 1149 if (obj) { 1150 if (obj->IsInLimbo) 1151 obj = obj; 1152 } 1153 } 1154 1155 /*------------------------------------------------------------------------ 1156 Check every cell on the map, even those that aren't displayed, 1157 in the hopes of detecting a memory trasher. 1158 ------------------------------------------------------------------------*/ 1159 for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { 1160 /*..................................................................... 1161 Validate Template & Icon data 1162 .....................................................................*/ 1163 ttype = (*this)[cell].TType; 1164 ticon = (*this)[cell].TIcon; 1165 if (ttype >= TEMPLATE_COUNT && ttype != TEMPLATE_NONE) 1166 return(false); 1167 1168 /*..................................................................... 1169 To validate the icon value, we have to get a copy of the template's 1170 "icon map"; this map will have 0xff's in spots where there is no 1171 icon. If the icon value is out of range or points to an invalide spot, 1172 return an error. 1173 .....................................................................*/ 1174 #if (0) 1175 if (ttype != TEMPLATE_NONE) { 1176 tclass = &TemplateTypeClass::As_Reference(ttype); 1177 ticon = (*this)[cell].TIcon; 1178 Mem_Copy(Get_Icon_Set_Map(tclass->Get_Image_Data()), map, 1179 tclass->Width * tclass->Height); 1180 if (ticon < 0 || 1181 ticon >= (tclass->Width * tclass->Height) || 1182 map[ticon]==0xff) 1183 return (false); 1184 } 1185 #endif 1186 /*..................................................................... 1187 Validate Overlay 1188 .....................................................................*/ 1189 overlay = (*this)[cell].Overlay; 1190 if (overlay < OVERLAY_NONE || overlay >= OVERLAY_COUNT) 1191 return(false); 1192 1193 /*..................................................................... 1194 Validate Smudge 1195 .....................................................................*/ 1196 smudge = (*this)[cell].Smudge; 1197 if (smudge < SMUDGE_NONE || smudge >= SMUDGE_COUNT) 1198 return(false); 1199 1200 /*..................................................................... 1201 Validate LandType 1202 .....................................................................*/ 1203 land = (*this)[cell].Land_Type(); 1204 if (land < LAND_CLEAR || land >= LAND_COUNT) 1205 return(false); 1206 1207 /*..................................................................... 1208 Validate Occupier 1209 .....................................................................*/ 1210 obj = (*this)[cell].Cell_Occupier(); 1211 if (obj) { 1212 1213 volatile TARGET target = obj->As_Target(); // This will do some internal verification 1214 1215 if (!obj->IsActive) { 1216 return false; 1217 } 1218 1219 if (obj->IsInLimbo) { 1220 return false; 1221 } 1222 1223 if (((unsigned int)Coord_Cell(obj->Coord) > 4095)) { 1224 return (false); 1225 } 1226 } 1227 1228 /*..................................................................... 1229 Validate Overlappers 1230 .....................................................................*/ 1231 for (i = 0; i < 3; i++) { 1232 obj = (*this)[cell].Overlapper[i]; 1233 if (obj) { 1234 1235 volatile TARGET target = obj->As_Target(); // This will do some internal verification 1236 1237 if (!obj->IsActive) { 1238 return false; 1239 } 1240 1241 if (obj->IsInLimbo) { 1242 return false; 1243 } 1244 1245 if (((unsigned int)Coord_Cell(obj->Coord) > 4095)) { 1246 return (false); 1247 } 1248 } 1249 } 1250 } 1251 1252 return (true); 1253 } 1254 1255 1256 1257 /*********************************************************************************************** 1258 * MapClass::Clean -- Clean up dangling pointers caused by bugs in the originl code. * 1259 * * 1260 * Ideally, we'd fix the underlying cause of the overlappers not being cleared * 1261 * but we can afford the CPU time now * 1262 * * 1263 * INPUT: none * 1264 * * 1265 * OUTPUT: * 1266 * * 1267 * WARNINGS: none * 1268 * * 1269 * HISTORY: * 1270 * 4/14/2020 11:48AM ST : Created. * 1271 *=============================================================================================*/ 1272 void MapClass::Clean(void) 1273 { 1274 CELL cell; 1275 ObjectClass *obj; 1276 int i; 1277 #ifndef NDEBUG 1278 char debug_message[256]; 1279 #endif 1280 bool active_fail = false; 1281 bool limbo_fail = false; 1282 const char *type_text = NULL; 1283 const char *ini_name = NULL; 1284 AbstractClass abstract_object; 1285 unsigned long abstract_vtable = *(unsigned long*)&abstract_object; 1286 1287 /*------------------------------------------------------------------------ 1288 Check every cell on the map, even those that aren't displayed. 1289 ------------------------------------------------------------------------*/ 1290 for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { 1291 1292 /*..................................................................... 1293 Validate Occupier 1294 .....................................................................*/ 1295 (*this)[cell].Cell_Occupier(); 1296 1297 /*..................................................................... 1298 Validate Overlappers 1299 .....................................................................*/ 1300 for (i = 0; i < 3; i++) { 1301 obj = (*this)[cell].Overlapper[i]; 1302 if (obj) { 1303 1304 if (!obj->IsActive) { 1305 (*this)[cell].Overlapper[i] = NULL; 1306 active_fail = true; 1307 } 1308 1309 if (!active_fail && obj->IsInLimbo) { 1310 (*this)[cell].Overlapper[i] = NULL; 1311 limbo_fail = true; 1312 } 1313 1314 if (active_fail || limbo_fail) { 1315 #ifndef NDEBUG 1316 /* 1317 ** This object is likely deleted. 1318 */ 1319 if (abstract_vtable == *(unsigned long*)obj) { 1320 type_text = "Abstract"; 1321 ini_name = "UNKNOWN"; 1322 } else { 1323 1324 RTTIType type = obj->What_Am_I(); 1325 1326 switch (type) { 1327 default: 1328 type_text = "Unknown"; 1329 break; 1330 1331 case RTTI_INFANTRY: 1332 type_text = "Infantry"; 1333 break; 1334 1335 case RTTI_UNIT: 1336 type_text = "Unit"; 1337 break; 1338 1339 case RTTI_AIRCRAFT: 1340 type_text = "Aircraft"; 1341 break; 1342 1343 case RTTI_BUILDING: 1344 type_text = "Building"; 1345 break; 1346 1347 case RTTI_BULLET: 1348 type_text = "Bullet"; 1349 break; 1350 1351 case RTTI_ANIM: 1352 type_text = "Anim"; 1353 break; 1354 1355 case RTTI_SMUDGE: 1356 type_text = "Smudge"; 1357 break; 1358 1359 case RTTI_TERRAIN: 1360 type_text = "Terrain"; 1361 break; 1362 } 1363 1364 ini_name = obj->Class_Of().IniName; 1365 } 1366 1367 sprintf_s(debug_message, sizeof(debug_message) - 1, "Cleaned %s overlapper in cell %08X. Type=%s, IniName=%s", active_fail ? "inactive" : "limbo", cell, type_text, ini_name); 1368 GlyphX_Debug_Print(debug_message); 1369 #endif //NDEBUG 1370 } 1371 } 1372 } 1373 } 1374 } 1375 1376 1377 /*********************************************************************************************** 1378 * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * 1379 * * 1380 * This routine is used by the mouse input processing code to find a clickable object * 1381 * close to coordinate specified. This is for targeting as well as selection determination. * 1382 * * 1383 * INPUT: coord -- The coordinate to scan for close object from. * 1384 * * 1385 * OUTPUT: Returns with a pointer to an object that is nearby the specified coordinate. * 1386 * * 1387 * WARNINGS: There could be a cloaked object at the location, but it won't be considered * 1388 * if it is not owned by the player. * 1389 * * 1390 * HISTORY: * 1391 * 08/20/1995 JLB : Created. * 1392 *=============================================================================================*/ 1393 ObjectClass * MapClass::Close_Object(COORDINATE coord) const 1394 { 1395 ObjectClass * object = 0; 1396 int distance = 0; 1397 CELL cell = Coord_Cell(coord); 1398 1399 /* 1400 ** Scan through current and adjacent cells, looking for the 1401 ** closest object (within reason) to the specified coordinate. 1402 */ 1403 static int _offsets[] = {0, -1, 1, -MAP_CELL_W, MAP_CELL_W, MAP_CELL_W-1, MAP_CELL_W+1, -(MAP_CELL_W-1), -(MAP_CELL_W+1)}; 1404 for (int index = 0; index < (sizeof(_offsets) / sizeof(_offsets[0])); index++) { 1405 1406 /* 1407 ** Examine the cell for close object. Make sure that the cell actually is a 1408 ** legal one. 1409 */ 1410 CELL newcell = cell + _offsets[index]; 1411 if (In_Radar(newcell)) { 1412 1413 /* 1414 ** Search through all objects that occupy this cell and then 1415 ** find the closest object. Check against any previously found object 1416 ** to ensure that it is actually closer. 1417 */ 1418 ObjectClass * o = (*this)[newcell].Cell_Occupier(); 1419 while (o) { 1420 1421 /* 1422 ** Special case check to ignore cloaked object if not allied with the player. 1423 */ 1424 // Changed for multiplayer. ST - 3/13/2019 5:38PM 1425 if (!o->Is_Techno() || !((TechnoClass *)o)->Is_Cloaked(PlayerPtr)) { 1426 //if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) { 1427 int d=-1; 1428 if (o->What_Am_I() == RTTI_BUILDING) { 1429 d = Distance(coord, Cell_Coord(newcell)); 1430 if (d > 0x00C0) d = -1; 1431 } else { 1432 d = Distance(coord, o->Center_Coord()); 1433 } 1434 if (d >= 0 && (!object || d < distance)) { 1435 distance = d; 1436 object = o; 1437 } 1438 } 1439 o = o->Next; 1440 } 1441 } 1442 } 1443 1444 /* 1445 ** Handle aircraft selection separately, since they aren't tracked in cells while flying 1446 */ 1447 for (int index = 0; index < Aircraft.Count(); index++) { 1448 AircraftClass * aircraft = Aircraft.Ptr(index); 1449 1450 if (aircraft->In_Which_Layer() != LAYER_GROUND) { 1451 if (!aircraft->Is_Cloaked(PlayerPtr)) { 1452 int d = Distance(coord, Coord_Add(aircraft->Center_Coord(), XY_Coord(0, -Pixel_To_Lepton(aircraft->Altitude)))); 1453 if (d >= 0 && (!object || d < distance)) { 1454 distance = d; 1455 object = aircraft; 1456 } 1457 } 1458 } 1459 } 1460 1461 /* 1462 ** Only return the object if it is within 1/4 cell distance from the specified 1463 ** coordinate. 1464 */ 1465 if (object && distance > 0xC0) { 1466 object = 0; 1467 } 1468 return(object); 1469 } 1470 1471 1472 1473 1474 #ifdef USE_RA_AI 1475 1476 /* 1477 ** Nearby_Location pulled in from RA for AI. ST - 7/24/2019 5:54PM 1478 */ 1479 #ifndef ARRAY_SIZE 1480 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) 1481 #endif 1482 1483 /*********************************************************************************************** 1484 * MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. * 1485 * * 1486 * This routine is used to find a location that probably will be ok to move to that is * 1487 * located as close as possible to the specified cell. The computer uses this when it has * 1488 * determined the ideal location for an object, but then needs to give a valid movement * 1489 * destination to a unit. * 1490 * * 1491 * INPUT: cell -- The cell that scanning should radiate out from. * 1492 * * 1493 * zone -- The zone that must be matched to find a legal location (value of -1 means * 1494 * any zone will do). * 1495 * * 1496 * * 1497 * check -- The type of zone to check against. Only valid if a zone value is given. * 1498 * * 1499 * OUTPUT: Returns with the cell that is generally clear (legal to move to) that is close * 1500 * to the specified cell. * 1501 * * 1502 * WARNINGS: none * 1503 * * 1504 * HISTORY: * 1505 * 10/05/1995 JLB : Created. * 1506 *=============================================================================================*/ 1507 CELL MapClass::Nearby_Location(CELL cell) const //, SpeedType speed, int zone, MZoneType check) const 1508 { 1509 CELL topten[10]; 1510 int count = 0; 1511 int xx = Cell_X(cell); 1512 int yy = Cell_Y(cell); 1513 1514 /* 1515 ** Determine the limits of the scanning in the four directions so that 1516 ** it won't scan past the edge of the world. 1517 */ 1518 int left = MapCellX; 1519 int right = MapCellX + MapCellWidth - 1; 1520 int top = MapCellY; 1521 int bottom = MapCellY + MapCellHeight - 1; 1522 1523 /* 1524 ** Radiate outward from the specified location, looking for the closest 1525 ** location that is generally clear. 1526 */ 1527 for (int radius = 0; radius < MAP_CELL_W; radius++) { 1528 CELL newcell; 1529 CellClass const * cellptr; 1530 1531 /* 1532 ** Scan the top and bottom rows of the "box". 1533 */ 1534 for (int x = xx-radius; x <= xx+radius; x++) { 1535 if (x >= left && x <= right) { 1536 int y = yy-radius; 1537 if (y >= top) { 1538 newcell = XY_Cell(x, y); 1539 cellptr = &Map[newcell]; 1540 if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) { 1541 topten[count++] = newcell; 1542 } 1543 } 1544 if (count == ARRAY_SIZE(topten)) break; 1545 1546 y = yy+radius; 1547 if (y <= bottom) { 1548 newcell = XY_Cell(x, y); 1549 cellptr = &Map[newcell]; 1550 if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) { 1551 topten[count++] = newcell; 1552 } 1553 } 1554 if (count == ARRAY_SIZE(topten)) break; 1555 } 1556 } 1557 1558 if (count == ARRAY_SIZE(topten)) break; 1559 1560 /* 1561 ** Scan the left and right columns of the "box". 1562 */ 1563 for (int y = yy-radius; y <= yy+radius; y++) { 1564 if (y >= top && y <= bottom) { 1565 int x = xx-radius; 1566 if (x >= left) { 1567 newcell = XY_Cell(x, y); 1568 cellptr = &Map[newcell]; 1569 if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) { 1570 topten[count++] = newcell; 1571 } 1572 } 1573 if (count == ARRAY_SIZE(topten)) break; 1574 1575 x = xx+radius; 1576 if (x <= right) { 1577 newcell = XY_Cell(x, y); 1578 cellptr = &Map[newcell]; 1579 if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(false, false)) { 1580 topten[count++] = newcell; 1581 } 1582 } 1583 if (count == ARRAY_SIZE(topten)) break; 1584 } 1585 } 1586 1587 if (count > 0) break; 1588 } 1589 1590 if (count > 0) { 1591 return(topten[Frame % count]); 1592 } 1593 return(0); 1594 } 1595 1596 #endif // USE_RA_AI