MAP.CPP (109350B)
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: /CounterStrike/MAP.CPP 3 3/14/97 5:15p Joe_b $ */ 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 : October 5, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * MapClass::Base_Region -- Finds the owner and base zone for specified cell. * 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::Destroy_Bridge_At -- Destroyes the bridge at location specified. * 38 * MapClass::Detach -- Remove specified object from map references. * 39 * MapClass::In_Radar -- Is specified cell in the radar map? * 40 * MapClass::Init -- clears all cells * 41 * MapClass::Intact_Bridge_Count -- Determine the number of intact bridges. * 42 * MapClass::Logic -- Handles map related logic functions. * 43 * MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. * 44 * MapClass::One_Time -- Performs special one time initializations for the map. * 45 * MapClass::Overlap_Down -- computes & marks object's overlap cells * 46 * MapClass::Overlap_Up -- Computes & clears object's overlap cells * 47 * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * 48 * MapClass::Pick_Up -- Removes specified object from the map. * 49 * MapClass::Place_Down -- Places the specified object onto the map. * 50 * MapClass::Place_Random_Crate -- Places a crate at random location on map. * 51 * MapClass::Read_Binary -- Reads the binary data from the straw specified. * 52 * MapClass::Remove_Crate -- Remove a crate from the specified cell. * 53 * MapClass::Set_Map_Dimensions -- Initialize the map. * 54 * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * 55 * MapClass::Validate -- validates every cell on the map * 56 * MapClass::Write_Binary -- Pipes the map template data to the destination specified. * 57 * MapClass::Zone_Reset -- Resets all zone numbers to match the map. * 58 * MapClass::Zone_Span -- Flood fills the specified zone from the cell origin. * 59 * MapClass::Pick_Random_Location -- Picks a random location on the map. * 60 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 61 62 #include "function.h" 63 64 #define MCW MAP_CELL_W 65 int const MapClass::RadiusOffset[] = { 66 /* 0 */ 0, 67 /* 1 */ (-MCW*1)-1,(-MCW*1)+0,(-MCW*1)+1,-1,1,(MCW*1)-1,(MCW*1)+0,(MCW*1)+1, 68 /* 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, 69 /* 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, 70 /* 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, 71 /* 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, 72 /* 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, 73 /* 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, 74 /* 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, 75 /* 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, 76 /* 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, 77 (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, 78 }; 79 80 int const MapClass::RadiusCount[11] = {1,9,21,37,61,89,121,161,205,253,309}; 81 82 83 CellClass * BlubCell; 84 85 /*********************************************************************************************** 86 * MapClass::One_Time -- Performs special one time initializations for the map. * 87 * * 88 * This routine is used by the game initialization function in order to perform any one * 89 * time initializations required for the map. This includes allocation of the map and * 90 * setting up its default dimensions. * 91 * * 92 * INPUT: none * 93 * * 94 * OUTPUT: none * 95 * * 96 * WARNINGS: This routine MUST be called once and only once. * 97 * * 98 * HISTORY: * 99 * 05/31/1994 JLB : Created. * 100 * 12/01/1994 BR : Added CellTriggers initialization * 101 *=============================================================================================*/ 102 void MapClass::One_Time(void) 103 { 104 GScreenClass::One_Time(); 105 106 XSize = MAP_CELL_W; 107 YSize = MAP_CELL_H; 108 Size = XSize * YSize; 109 110 /* 111 ** Allocate the cell array. 112 */ 113 Alloc_Cells(); 114 } 115 116 117 /*********************************************************************************************** 118 * MapClass::Init_Clear -- clears the map & buffers to a known state * 119 * * 120 * INPUT: * 121 * none. * 122 * * 123 * OUTPUT: * 124 * none. * 125 * * 126 * WARNINGS: * 127 * none. * 128 * * 129 * HISTORY: * 130 * 03/17/1995 BRR : Created. * 131 *=============================================================================================*/ 132 void MapClass::Init_Clear(void) 133 { 134 GScreenClass::Init_Clear(); 135 Init_Cells(); 136 TiberiumScan = 0; 137 TiberiumGrowthCount = 0; 138 TiberiumGrowthExcess = 0; 139 TiberiumSpreadCount = 0; 140 TiberiumSpreadExcess = 0; 141 for (int index = 0; index < ARRAY_SIZE(Crates); index++) { 142 Crates[index].Init(); 143 } 144 } 145 146 147 /*********************************************************************************************** 148 * MapClass::Alloc_Cells -- allocates the cell array * 149 * * 150 * This routine should be called at One_Time, and after loading the Map object from a save * 151 * game, but prior to loading the cell objects. * 152 * * 153 * INPUT: * 154 * none. * 155 * * 156 * OUTPUT: * 157 * none. * 158 * * 159 * WARNINGS: * 160 * none. * 161 * * 162 * HISTORY: * 163 * 03/17/1995 BRR : Created. * 164 *=============================================================================================*/ 165 void MapClass::Alloc_Cells(void) 166 { 167 /* 168 ** Assume that whatever the contents of the VectorClass are is garbage 169 ** (it may have been loaded from a save-game file), so zero it out first. 170 */ 171 new (&Array) VectorClass<CellClass>; 172 Array.Resize(Size); 173 } 174 175 176 /*********************************************************************************************** 177 * MapClass::Free_Cells -- frees the cell array * 178 * * 179 * This routine is used by the Load_Game routine to free the map's cell array before loading * 180 * the map object from disk; the array is then re-allocated & cleared before the cell objects * 181 * are loaded. * 182 * * 183 * INPUT: * 184 * none. * 185 * * 186 * OUTPUT: * 187 * none. * 188 * * 189 * WARNINGS: * 190 * none. * 191 * * 192 * HISTORY: * 193 * 03/17/1995 BRR : Created. * 194 *=============================================================================================*/ 195 void MapClass::Free_Cells(void) 196 { 197 Array.Clear(); 198 } 199 200 201 /*********************************************************************************************** 202 * MapClass::Init_Cells -- Initializes the cell array to a fresh state. * 203 * * 204 * This routine is used by Init_Clear to set the cells to a known state; it's also used by * 205 * the Load_Game routine to init all cells before loading a set of cells from disk, so it * 206 * needs to be called separately from the other Init_xxx() routines. * 207 * * 208 * INPUT: * 209 * none. * 210 * * 211 * OUTPUT: * 212 * none. * 213 * * 214 * WARNINGS: * 215 * none. * 216 * * 217 * HISTORY: * 218 * 03/17/1995 BRR : Created. * 219 *=============================================================================================*/ 220 void MapClass::Init_Cells(void) 221 { 222 TotalValue = 0; 223 for (int index = 0; index < MAP_CELL_TOTAL; index++) { 224 new (&Array[index]) CellClass; 225 } 226 } 227 228 229 /*********************************************************************************************** 230 * MapClass::Set_Map_Dimensions -- Set map dimensions. * 231 * * 232 * This routine is used to set the legal limits and position of the * 233 * map as it relates to the overall map array. Typically, this is * 234 * called by the scenario loading code. * 235 * * 236 * INPUT: x,y -- The X and Y coordinate of the "upper left" corner * 237 * of the map. * 238 * * 239 * w,h -- The width and height of the legal map. * 240 * * 241 * OUTPUT: none * 242 * * 243 * WARNINGS: none * 244 * * 245 * HISTORY: * 246 * 05/14/1994 JLB : Created. * 247 *=============================================================================================*/ 248 void MapClass::Set_Map_Dimensions(int x, int y, int w, int h) 249 { 250 MapCellX = x; 251 MapCellY = y; 252 MapCellWidth = w; 253 MapCellHeight = h; 254 } 255 256 257 /*********************************************************************************************** 258 * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * 259 * * 260 * This routine is used to reveal the cells around a specific location. * 261 * Typically, as a unit moves or is deployed, this routine will be * 262 * called. Since it deals with MANY cells, it needs to be extremely * 263 * fast. * 264 * * 265 * INPUT: cell -- The coordinate that the sighting originates from. * 266 * * 267 * sightrange-- The distance in cells that sighting extends. * 268 * * 269 * incremental-- Is this an incremental sighting. In other * 270 * words, has this function been called before where * 271 * the center coordinate is no more than one cell * 272 * distant from the last time? * 273 * * 274 * OUTPUT: none * 275 * * 276 * WARNINGS: none * 277 * * 278 * HISTORY: * 279 * 05/19/1992 JLB : Created. * 280 * 03/08/1994 JLB : Updated to use sight table and incremental flag. * 281 * 05/18/1994 JLB : Converted to member function. * 282 *=============================================================================================*/ 283 void MapClass::Sight_From(CELL cell, int sightrange, HouseClass * house, bool incremental) 284 { 285 int xx; // Center cell X coordinate (bounds checking). 286 int const * ptr; // Offset pointer. 287 int count; // Counter for number of offsets to process. 288 289 /* 290 ** Units that are off-map cannot sight. 291 */ 292 if (!In_Radar(cell)) return; 293 if (!sightrange || sightrange > 10) return; 294 295 /* 296 ** Determine logical cell coordinate for center scan point. 297 */ 298 xx = Cell_X(cell); 299 300 /* 301 ** Incremental scans only scan the outer rings. Full scans 302 ** scan all internal cells as well. 303 */ 304 count = RadiusCount[sightrange]; 305 ptr = &RadiusOffset[0]; 306 if (incremental) { 307 if (sightrange > 2) { 308 ptr += RadiusCount[sightrange-3]; 309 count -= RadiusCount[sightrange-3]; 310 } 311 } 312 313 /* 314 ** Process all offsets required for the desired scan. 315 */ 316 while (count--) { 317 CELL newcell; // New cell with offset. 318 int xdiff; // New cell's X coordinate distance from center. 319 320 newcell = cell + *ptr++; 321 322 /* 323 ** Determine if the map edge has been wrapped. If so, 324 ** then don't process the cell. 325 */ 326 if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; 327 xdiff = Cell_X(newcell) - xx; 328 xdiff = ABS(xdiff); 329 if (xdiff > sightrange) continue; 330 if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (sightrange * CELL_LEPTON_W)) continue; 331 332 /* 333 ** Map the cell. For incremental scans, then update 334 ** adjacent cells as well. For full scans, just update 335 ** the cell itself. 336 */ 337 //if (!(*this)[newcell].IsMapped) { // ST - 8/7/2019 10:31AM 338 Map.Map_Cell(newcell, house, true, true); 339 } 340 } 341 342 343 /*********************************************************************************************** 344 * MapClass::Shroud_From -- cloak a radius of cells * 345 * * 346 * This routine is used to shroud the cells around a specific location. * 347 * Typically, as a gap generator refreshes (when Encroach_Shadow() is called) this routine's* 348 * called. Since it deals with MANY cells, it needs to be extremely * 349 * fast. * 350 * * 351 * INPUT: cell -- The coordinate that the shrouding originates from. * 352 * * 353 * sightrange-- The distance in cells that sighting extends. * 354 * * 355 * OUTPUT: none * 356 * * 357 * WARNINGS: none * 358 * * 359 * HISTORY: * 360 * 11/10/1995 BWG : Created. * 361 * 08/09/2019 ST : Added house parameter * 362 *=============================================================================================*/ 363 void MapClass::Shroud_From(CELL cell, int sightrange, HouseClass *house) 364 { 365 int xx; // Center cell X coordinate (bounds checking). 366 int const * ptr; // Offset pointer. 367 int count; // Counter for number of offsets to process. 368 369 /* 370 ** Units that are off-map cannot sight. 371 */ 372 if (!In_Radar(cell)) return; 373 if (!sightrange || sightrange > Rule.GapShroudRadius) return; 374 375 /* 376 ** Determine logical cell coordinate for center scan point. 377 */ 378 xx = Cell_X(cell); 379 380 /* 381 ** Incremental scans only scan the outer rings. Full scans 382 ** scan all internal cells as well. 383 */ 384 count = RadiusCount[sightrange]; 385 ptr = &RadiusOffset[0]; 386 387 /* 388 ** Process all offsets required for the desired scan. 389 */ 390 while (count--) { 391 CELL newcell; // New cell with offset. 392 int xdiff; // New cell's X coordinate distance from center. 393 394 newcell = cell + *ptr++; 395 396 /* 397 ** Determine if the map edge has been wrapped. If so, 398 ** then don't process the cell. 399 */ 400 if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; 401 xdiff = Cell_X(newcell) - xx; 402 xdiff = ABS(xdiff); 403 if (xdiff > sightrange) continue; 404 if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (sightrange * CELL_LEPTON_W)) continue; 405 406 /* 407 ** Shroud the cell. 408 */ 409 Map.Shroud_Cell(newcell, house); 410 } 411 } 412 413 414 /*********************************************************************************************** 415 * MapClass::Jam_From -- Mark as jammed the cells within a specified radius. * 416 * * 417 * This routine is used to jam the cells around a specific location. * 418 * Typically, as a gap generator structure is created, this routine will be * 419 * called. Since it deals with MANY cells, it needs to be extremely * 420 * fast. * 421 * * 422 * INPUT: cell -- The coordinate that the jamming originates from. * 423 * * 424 * jamrange -- The distance in cells that jamming extends. * 425 * * 426 * * 427 * OUTPUT: none * 428 * * 429 * WARNINGS: none * 430 * * 431 * HISTORY: * 432 * 11/09/1995 BWG : Created. * 433 *=============================================================================================*/ 434 void MapClass::Jam_From(CELL cell, int jamrange, HouseClass * house) 435 { 436 int xx; // Center cell X coordinate (bounds checking). 437 int const * ptr; // Offset pointer. 438 int count; // Counter for number of offsets to process. 439 440 /* 441 ** Units that are off-map cannot jam. 442 */ 443 if (!jamrange || jamrange > Rule.GapShroudRadius) return; 444 445 /* 446 ** Determine logical cell coordinate for center scan point. 447 */ 448 xx = Cell_X(cell); 449 450 /* 451 ** Incremental scans only scan the outer rings. Full scans 452 ** scan all internal cells as well. 453 */ 454 count = RadiusCount[jamrange]; 455 ptr = &RadiusOffset[0]; 456 457 /* 458 ** Process all offsets required for the desired scan. 459 */ 460 while (count--) { 461 CELL newcell; // New cell with offset. 462 int xdiff; // New cell's X coordinate distance from center. 463 464 newcell = cell + *ptr++; 465 466 /* 467 ** Determine if the map edge has been wrapped. If so, 468 ** then don't process the cell. 469 */ 470 if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; 471 xdiff = Cell_X(newcell) - xx; 472 xdiff = ABS(xdiff); 473 if (xdiff > jamrange) continue; 474 if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (jamrange * CELL_LEPTON_W)) continue; 475 476 /* 477 ** Jam the cell. For incremental scans, then update 478 ** adjacent cells as well. For full scans, just update 479 ** the cell itself. 480 */ 481 Map.Jam_Cell(newcell, house/*KO, false*/); 482 } 483 484 /* 485 ** Updated for client/server multiplayer. ST - 8/12/2019 3:25PM 486 */ 487 if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { 488 if (!house->IsPlayerControl) { 489 Map.Constrained_Look(Cell_Coord(cell), Rule.GapShroudRadius * CELL_LEPTON_W, PlayerPtr); 490 } 491 492 } else { 493 494 for (int i = 0; i < Session.Players.Count(); i++) { 495 HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); 496 if (player_house->IsHuman && player_house != house) { 497 Map.Constrained_Look(Cell_Coord(cell), Rule.GapShroudRadius * CELL_LEPTON_W, player_house); 498 } 499 } 500 } 501 502 #ifdef OBSOLETE 503 /* 504 ** The objects on the map need to perform a manual look operation if they happen 505 ** to have their sight range overlap the gap radius. 506 */ 507 if (!house->IsPlayerControl) { 508 // if (house != PlayerPtr) { 509 510 for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { 511 ObjectClass * object = Map.Layer[LAYER_GROUND][index]; 512 if (object && object->Is_Techno()) { 513 TechnoClass * tech = ((TechnoClass *)object); 514 515 if (tech->IsDiscoveredByPlayer && 516 (tech->Distance(As_Target(cell)) / CELL_LEPTON_W) <= tech->Techno_Type_Class()->SightRange + Rule.GapShroudRadius && 517 (tech->House->IsPlayerControl || 518 (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr)))) { 519 520 object->Look(); 521 } 522 } 523 } 524 } 525 #endif 526 527 #ifdef OBSOLETE 528 /* 529 ** Here all the player's vehicles will perform a look if they're within 530 ** the shadow. 531 */ 532 for (int index = 0; index < Units.Count(); index++) { 533 UnitClass * unit = Units.Ptr(index); 534 if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { 535 int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; 536 if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { 537 unit->Look(); 538 } 539 } 540 } 541 for (index = 0; index < Infantry.Count(); index++) { 542 InfantryClass * unit = Infantry.Ptr(index); 543 if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { 544 int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; 545 if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { 546 unit->Look(); 547 } 548 } 549 } 550 for (index = 0; index < Vessels.Count(); index++) { 551 VesselClass * unit = Vessels.Ptr(index); 552 if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { 553 int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; 554 if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { 555 unit->Look(); 556 } 557 } 558 } 559 } 560 #endif 561 562 } 563 564 565 /*********************************************************************************************** 566 * MapClass::UnJam_From -- Remove jamming on the cells within a specified radius. * 567 * * 568 * This routine is used to jam the cells around a specific location. * 569 * Typically, as a gap generator structure is created, this routine will be * 570 * called. Since it deals with MANY cells, it needs to be extremely * 571 * fast. * 572 * * 573 * INPUT: cell -- The coordinate that the jamming originates from. * 574 * * 575 * jamrange -- The distance in cells that jamming extends. * 576 * * 577 * OUTPUT: none * 578 * * 579 * WARNINGS: none * 580 * * 581 * HISTORY: * 582 * 11/09/1995 BWG : Created. * 583 *=============================================================================================*/ 584 void MapClass::UnJam_From(CELL cell, int jamrange, HouseClass * house) 585 { 586 int xx; // Center cell X coordinate (bounds checking). 587 int const * ptr; // Offset pointer. 588 int count; // Counter for number of offsets to process. 589 590 /* 591 ** Units that are off-map cannot jam. 592 */ 593 if (!jamrange || jamrange > Rule.GapShroudRadius) return; 594 595 /* 596 ** Determine logical cell coordinate for center scan point. 597 */ 598 xx = Cell_X(cell); 599 600 /* 601 ** Incremental scans only scan the outer rings. Full scans 602 ** scan all internal cells as well. 603 */ 604 count = RadiusCount[jamrange]; 605 ptr = &RadiusOffset[0]; 606 607 /* 608 ** Process all offsets required for the desired scan. 609 */ 610 while (count--) { 611 CELL newcell; // New cell with offset. 612 int xdiff; // New cell's X coordinate distance from center. 613 614 newcell = cell + *ptr++; 615 616 /* 617 ** Determine if the map edge has been wrapped. If so, 618 ** then don't process the cell. 619 */ 620 if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; 621 xdiff = Cell_X(newcell) - xx; 622 xdiff = ABS(xdiff); 623 if (xdiff > jamrange) continue; 624 if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (jamrange * CELL_LEPTON_W)) continue; 625 626 /* 627 ** Jam the cell. For incremental scans, then update 628 ** adjacent cells as well. For full scans, just update 629 ** the cell itself. 630 */ 631 Map.UnJam_Cell(newcell, house); 632 } 633 } 634 635 636 /*********************************************************************************************** 637 * MapClass::In_Radar -- Is specified cell in the radar map? * 638 * * 639 * This determines if the specified cell can be within the navigable * 640 * bounds of the map. Technically, this means, any cell that can be * 641 * scanned by radar. If a cell returns false from this function, then * 642 * the player could never move to or pass over this cell. * 643 * * 644 * INPUT: cell -- The cell to examine. * 645 * * 646 * OUTPUT: bool; Is this cell possible to be displayed on radar? * 647 * * 648 * WARNINGS: none * 649 * * 650 * HISTORY: * 651 * 10/07/1992 JLB : Created. * 652 * 04/30/1994 JLB : Converted to member function. * 653 * 05/01/1994 JLB : Speeded up. * 654 *=============================================================================================*/ 655 bool MapClass::In_Radar(CELL cell) const 656 { 657 /* 658 ** If the cell value is WAY out of range, then it obviously can't be part of the game 659 ** playfield. 660 */ 661 if ((unsigned)cell > MAP_CELL_TOTAL) return(false); 662 663 /* 664 ** If the cell is off the left or right edge of the playfield, then return the "not in 665 ** radar" flag. 666 */ 667 if ((unsigned)(Cell_X(cell) - MapCellX) >= (unsigned)MapCellWidth) return(false); 668 669 /* 670 ** If the cell is off the top or bottom edge of the playfield, then return the "not in 671 ** radar" flag. 672 */ 673 if ((unsigned)(Cell_Y(cell) - MapCellY) >= (unsigned)MapCellHeight) return(false); 674 675 return(true); 676 } 677 678 679 /*********************************************************************************************** 680 * MapClass::Place_Down -- Places the specified object onto the map. * 681 * * 682 * This routine is used to place an object onto the map. It updates the "occupier" of the * 683 * cells that this object covers. The cells are determined from the Occupy_List function * 684 * provided by the object. Only one cell can have an occupier and this routine is the only * 685 * place that sets this condition. * 686 * * 687 * INPUT: cell -- The cell to base object occupation around. * 688 * * 689 * object -- The object to place onto the map. * 690 * * 691 * OUTPUT: none * 692 * * 693 * WARNINGS: none * 694 * * 695 * HISTORY: * 696 * 07/31/1994 JLB : Created. * 697 *=============================================================================================*/ 698 void MapClass::Place_Down(CELL cell, ObjectClass * object) 699 { 700 if (!object) return; 701 702 if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { 703 short xlist[32]; 704 List_Copy(object->Occupy_List(), ARRAY_SIZE(xlist), xlist); 705 short const * list = xlist; 706 while (*list != REFRESH_EOL) { 707 CELL newcell = cell + *list++; 708 if ((unsigned)newcell < MAP_CELL_TOTAL) { 709 (*this)[newcell].Occupy_Down(object); 710 (*this)[newcell].Recalc_Attributes(); 711 (*this)[newcell].Redraw_Objects(); 712 } 713 } 714 715 List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); 716 list = xlist; 717 while (*list != REFRESH_EOL) { 718 CELL newcell = cell + *list++; 719 if ((unsigned)newcell < MAP_CELL_TOTAL) { 720 (*this)[newcell].Overlap_Down(object); 721 (*this)[newcell].Redraw_Objects(); 722 } 723 } 724 } 725 } 726 727 728 /*********************************************************************************************** 729 * MapClass::Pick_Up -- Removes specified object from the map. * 730 * * 731 * The object specified is removed from the map by this routine. This will remove the * 732 * occupation flag for all the cells that the object covers. The cells that are covered * 733 * are determined from the Occupy_List function. * 734 * * 735 * INPUT: cell -- The cell that the object is centered about. * 736 * * 737 * object -- Pointer to the object that will be removed. * 738 * * 739 * OUTPUT: none * 740 * * 741 * WARNINGS: none * 742 * * 743 * HISTORY: * 744 * 07/31/1994 JLB : Created. * 745 *=============================================================================================*/ 746 void MapClass::Pick_Up(CELL cell, ObjectClass * object) 747 { 748 if (!object) return; 749 750 if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { 751 short xlist[32]; 752 List_Copy(object->Occupy_List(), ARRAY_SIZE(xlist), xlist); 753 short const * list = xlist; 754 while (*list != REFRESH_EOL) { 755 CELL newcell = cell + *list++; 756 if ((unsigned)newcell < MAP_CELL_TOTAL) { 757 (*this)[newcell].Occupy_Up(object); 758 (*this)[newcell].Recalc_Attributes(); 759 (*this)[newcell].Redraw_Objects(); 760 } 761 } 762 763 List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); 764 list = xlist; 765 while (*list != REFRESH_EOL) { 766 CELL newcell = cell + *list++; 767 if ((unsigned)newcell < MAP_CELL_TOTAL) { 768 (*this)[newcell].Overlap_Up(object); 769 (*this)[newcell].Redraw_Objects(); 770 } 771 } 772 } 773 } 774 775 776 /*********************************************************************************************** 777 * MapClass::Overlap_Down -- computes & marks object's overlap cells * 778 * * 779 * This routine is just like Place_Down, but it doesn't mark the cell's Occupier. * 780 * This routine is used to implement MARK_OVERLAP_DOWN, which is useful for changing * 781 * an object's render size, but not its logical size (ie when it's selected or an * 782 * animation is attached to it). * 783 * * 784 * INPUT: * 785 * cell -- The cell to base object overlap around. * 786 * object -- The object to place onto the map. * 787 * * 788 * OUTPUT: * 789 * none. * 790 * * 791 * WARNINGS: * 792 * none. * 793 * * 794 * HISTORY: * 795 * 07/12/1995 BRR : Created. * 796 *=============================================================================================*/ 797 void MapClass::Overlap_Down(CELL cell, ObjectClass * object) 798 { 799 if (!object) return; 800 801 if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { 802 short xlist[32]; 803 List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); 804 short const * list = xlist; 805 while (*list != REFRESH_EOL) { 806 CELL newcell = cell + *list++; 807 if ((unsigned)newcell < MAP_CELL_TOTAL) { 808 (*this)[newcell].Overlap_Down(object); 809 (*this)[newcell].Redraw_Objects(); 810 } 811 } 812 } 813 } 814 815 816 /*********************************************************************************************** 817 * MapClass::Overlap_Up -- Computes & clears object's overlap cells * 818 * * 819 * This routine is just like Pick_Up, but it doesn't mark the cell's Occupier. * 820 * This routine is used to implement MARK_OVERLAP_UP, which is useful for changing * 821 * an object's render size, but not its logical size (ie when it's selected or an * 822 * animation is attached to it). * 823 * * 824 * INPUT: * 825 * cell -- The cell to base object overlap around. * 826 * object -- The object to place onto the map. * 827 * * 828 * OUTPUT: * 829 * none. * 830 * * 831 * WARNINGS: * 832 * none. * 833 * * 834 * HISTORY: * 835 * 07/12/1995 BRR : Created. * 836 *=============================================================================================*/ 837 void MapClass::Overlap_Up(CELL cell, ObjectClass * object) 838 { 839 if (!object) return; 840 841 if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { 842 short xlist[32]; 843 List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); 844 short const * list = xlist; 845 while (*list != REFRESH_EOL) { 846 CELL newcell = cell + *list++; 847 if ((unsigned)newcell < MAP_CELL_TOTAL) { 848 (*this)[newcell].Overlap_Up(object); 849 (*this)[newcell].Redraw_Objects(); 850 } 851 } 852 } 853 } 854 855 856 /*********************************************************************************************** 857 * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * 858 * * 859 * This routine will clean up anything necessary with the presumption that the map has * 860 * been freshly created. Such things to clean up include various tiberium concentrations. * 861 * * 862 * INPUT: none * 863 * * 864 * OUTPUT: Returns the total credit value of the tiberium on the map. * 865 * * 866 * WARNINGS: none * 867 * * 868 * HISTORY: * 869 * 09/19/1994 JLB : Created. * 870 * 02/13/1995 JLB : Returns total tiberium worth. * 871 * 02/15/1995 JLB : Optimal scan. * 872 *=============================================================================================*/ 873 long MapClass::Overpass(void) 874 { 875 long value = 0; 876 877 /* 878 ** Smooth out Tiberium. Cells that are not surrounded by other tiberium 879 ** will be reduced in density. 880 */ 881 for (int y = 0; y < MapCellHeight; y++) { 882 for (int x = 0; x < MapCellWidth; x++) { 883 CELL cell = (MapCellY+y) * MAP_CELL_W + (MapCellX+x); 884 value += (*this)[cell].Tiberium_Adjust(true); 885 (*this)[cell].Recalc_Attributes(); 886 } 887 } 888 return(value); 889 } 890 891 892 /*********************************************************************************************** 893 * MapClass::Write_Binary -- Pipes the map template data to the destination specified. * 894 * * 895 * This stores the template data from the map to the output pipe specified. The template * 896 * data consists of the template type number and template icon number for every cell on * 897 * the map. The output is organized in such a way so as to get maximum compression. * 898 * * 899 * INPUT: pipe -- Reference to the output pipe that will receive the map template data. * 900 * * 901 * OUTPUT: Returns with the total number of bytes output to the pipe. * 902 * * 903 * WARNINGS: none * 904 * * 905 * HISTORY: * 906 * 07/03/1996 JLB : Created. * 907 *=============================================================================================*/ 908 int MapClass::Write_Binary(Pipe & pipe) 909 { 910 int total = 0; 911 912 LCWPipe comp(LCWPipe::COMPRESS); 913 comp.Put_To(&pipe); 914 915 CellClass * cellptr = &Array[0]; 916 for (int i = 0; i < MAP_CELL_TOTAL; i++) { 917 total += comp.Put(&cellptr->TType, sizeof(cellptr->TType)); 918 cellptr++; 919 } 920 921 cellptr = &Array[0]; 922 for (int i = 0; i < MAP_CELL_TOTAL; i++) { 923 total += comp.Put(&cellptr->TIcon, sizeof(cellptr->TIcon)); 924 cellptr++; 925 } 926 927 return(total); 928 } 929 930 931 /*********************************************************************************************** 932 * MapClass::Read_Binary -- Reads the binary data from the straw specified. * 933 * * 934 * This routine will retrieve the map template data from the straw specified. * 935 * * 936 * INPUT: straw -- Reference to the straw that will supply the map template data. * 937 * * 938 * OUTPUT: bool; Was the template data retrieved? * 939 * * 940 * WARNINGS: none * 941 * * 942 * HISTORY: * 943 * 07/03/1996 JLB : Created. * 944 *=============================================================================================*/ 945 bool MapClass::Read_Binary(Straw & straw) 946 { 947 LCWStraw decomp(LCWStraw::DECOMPRESS); 948 decomp.Get_From(&straw); 949 950 CELL cell; 951 CellClass * cellptr; 952 switch (NewINIFormat) { 953 default: 954 cellptr = &Array[0]; 955 for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { 956 decomp.Get(&cellptr->TType, sizeof(cellptr->TType)); 957 cellptr++; 958 } 959 cellptr = &Array[0]; 960 for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { 961 decomp.Get(&cellptr->TIcon, sizeof(cellptr->TIcon)); 962 cellptr->Recalc_Attributes(); 963 cellptr++; 964 } 965 break; 966 967 case 0: 968 case 1: 969 case 2: 970 cellptr = &Array[0]; 971 for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { 972 decomp.Get(&cellptr->TType, sizeof(cellptr->TType)); 973 decomp.Get(&cellptr->TIcon, sizeof(cellptr->TIcon)); 974 cellptr->Recalc_Attributes(); 975 cellptr++; 976 } 977 break; 978 } 979 return(true); 980 } 981 982 983 /*********************************************************************************************** 984 * MapClass::Logic -- Handles map related logic functions. * 985 * * 986 * Manages tiberium growth and spread. * 987 * * 988 * INPUT: none * 989 * * 990 * OUTPUT: none * 991 * * 992 * WARNINGS: none * 993 * * 994 * HISTORY: * 995 * 05/11/1995 JLB : Created. * 996 * 07/09/1995 JLB : Handles two directional scan. * 997 * 08/01/1995 JLB : Gives stronger weight to blossom trees. * 998 *=============================================================================================*/ 999 void MapClass::Logic(void) 1000 { 1001 if (Debug_Force_Crash) { *((int *)0) = 1; } 1002 /* 1003 ** Crate regeneration is handled here. 1004 */ 1005 if (Session.Type != GAME_NORMAL && Session.Options.Goodies) { 1006 1007 /* 1008 ** Find any crate that has expired and then regenerate it at a new 1009 ** spot. 1010 */ 1011 for (int index = 0; index < ARRAY_SIZE(Crates); index++) { 1012 if (Crates[index].Is_Expired()) { 1013 Crates[index].Remove_It(); 1014 Place_Random_Crate(); 1015 } 1016 } 1017 } 1018 1019 /* 1020 ** Bail early if there is no allowed growth or spread of Tiberium. 1021 */ 1022 if (!Rule.IsTGrowth && !Rule.IsTSpread) return; 1023 1024 /* 1025 ** Scan another block of the map in order to accumulate the potential 1026 ** Tiberium cells that can grow or spread. 1027 */ 1028 int subcount = MAP_CELL_TOTAL / (Rule.GrowthRate * TICKS_PER_MINUTE); 1029 1030 /* 1031 ** Use the Tiberium setting as a multiplier on growth rate. ST - 7/1/2020 3:05PM 1032 */ 1033 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 1034 if (Session.Options.Tiberium > 1) { 1035 subcount *= Session.Options.Tiberium; 1036 } 1037 } 1038 1039 subcount = max(subcount, 1); 1040 int index; 1041 for (index = TiberiumScan; index < MAP_CELL_TOTAL; index++) { 1042 CELL cell = index; 1043 if (In_Radar(cell)) { 1044 CellClass * ptr = &(*this)[cell]; 1045 1046 /* 1047 ** Tiberium cells can grow. 1048 */ 1049 if (ptr->Can_Tiberium_Grow()) { 1050 1051 /* 1052 ** Either replace an existing recorded cell value or add the new cell value to 1053 ** the list. 1054 */ 1055 if (Random_Pick(0, TiberiumGrowthExcess) <= TiberiumGrowthCount) { 1056 if (TiberiumGrowthCount < sizeof(TiberiumGrowth)/sizeof(TiberiumGrowth[0])) { 1057 TiberiumGrowth[TiberiumGrowthCount++] = cell; 1058 } else { 1059 TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)] = cell; 1060 } 1061 } 1062 TiberiumGrowthExcess++; 1063 } 1064 1065 /* 1066 ** Heavy Tiberium growth can spread. 1067 */ 1068 if (ptr->Can_Tiberium_Spread()) { 1069 /* 1070 ** Either replace an existing recorded cell value or add the new cell value to 1071 ** the list. 1072 */ 1073 if (Random_Pick(0, TiberiumSpreadExcess) <= TiberiumSpreadCount) { 1074 if (TiberiumSpreadCount < ARRAY_SIZE(TiberiumSpread)) { 1075 TiberiumSpread[TiberiumSpreadCount++] = cell; 1076 } else { 1077 TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)] = cell; 1078 } 1079 } 1080 TiberiumSpreadExcess++; 1081 } 1082 } 1083 1084 subcount--; 1085 if (subcount == 0) break; 1086 } 1087 TiberiumScan = index; 1088 1089 /* 1090 ** When the entire map has been processed, proceed with tiberium (ore) growth 1091 ** and spread action. 1092 */ 1093 if (TiberiumScan >= MAP_CELL_TOTAL) { 1094 TiberiumScan = 0; 1095 1096 /* 1097 ** Growth logic. 1098 */ 1099 if (TiberiumGrowthCount) { 1100 for (int i = 0; i < TiberiumGrowthCount; i++) { 1101 CELL cell = TiberiumGrowth[i]; 1102 CellClass * newcell = &(*this)[cell]; 1103 newcell->Grow_Tiberium(); 1104 } 1105 } 1106 TiberiumGrowthCount = 0; 1107 TiberiumGrowthExcess = 0; 1108 1109 /* 1110 ** Spread logic. 1111 */ 1112 if (TiberiumSpreadCount) { 1113 for (int i = 0; i < TiberiumSpreadCount; i++) { 1114 Map[TiberiumSpread[i]].Spread_Tiberium(); 1115 } 1116 } 1117 TiberiumSpreadCount = 0; 1118 TiberiumSpreadExcess = 0; 1119 } 1120 } 1121 1122 1123 /*********************************************************************************************** 1124 * MapClass::Cell_Region -- Determines the region from a specified cell number. * 1125 * * 1126 * Use this routine to determine what region a particular cell lies in. * 1127 * * 1128 * INPUT: cell -- The cell number to examine. * 1129 * * 1130 * OUTPUT: Returns with the region that the specified cell occupies. * 1131 * * 1132 * WARNINGS: none * 1133 * * 1134 * HISTORY: * 1135 * 03/15/1995 JLB : Created. * 1136 *=============================================================================================*/ 1137 int MapClass::Cell_Region(CELL cell) 1138 { 1139 return((Cell_X(cell) / REGION_WIDTH) + 1) + (((Cell_Y(cell) / REGION_HEIGHT) + 1) * MAP_REGION_WIDTH); 1140 } 1141 1142 1143 /*************************************************************************** 1144 * MapClass::Cell_Threat -- Gets a houses threat value for a cell * 1145 * * 1146 * INPUT: CELL cell - the cell number to check * 1147 * HouseType house - the house to check * 1148 * * 1149 * OUTPUT: * 1150 * * 1151 * WARNINGS: * 1152 * * 1153 * HISTORY: * 1154 * 04/25/1995 PWG : Created. * 1155 *=========================================================================*/ 1156 int MapClass::Cell_Threat(CELL cell, HousesType house) 1157 { 1158 int threat = HouseClass::As_Pointer(house)->Regions[Map.Cell_Region(Map[cell].Cell_Number())].Threat_Value(); 1159 //using function for IsVisible so we have different results for different players - JAS 2019/09/30 1160 if (!threat && Map[cell].Is_Visible(house)) { 1161 threat = 1; 1162 } 1163 return(threat); 1164 } 1165 1166 1167 /*********************************************************************************************** 1168 * MapClass::Place_Random_Crate -- Places a crate at random location on map. * 1169 * * 1170 * This routine will place a crate at a random location on the map. This routine will only * 1171 * make a limited number of attempts to place and if unsuccessful, it will not place any. * 1172 * * 1173 * INPUT: none * 1174 * * 1175 * OUTPUT: Was a crate successfully placed? * 1176 * * 1177 * WARNINGS: none * 1178 * * 1179 * HISTORY: * 1180 * 07/08/1995 JLB : Created. * 1181 *=============================================================================================*/ 1182 bool MapClass::Place_Random_Crate(void) 1183 { 1184 /* 1185 ** Find a crate index that is free for assignment. If there are 1186 ** no free slots, then return with failure to place crate. 1187 */ 1188 int crateindex = 0; 1189 for (crateindex = 0; crateindex < ARRAY_SIZE(Crates); crateindex++) { 1190 if (!Crates[crateindex].Is_Valid()) break; 1191 } 1192 if (crateindex == ARRAY_SIZE(Crates)) { 1193 return(false); 1194 } 1195 1196 /* 1197 ** Give a good effort to scan for and place a crate down on the map. 1198 */ 1199 for (int index = 0; index < 1000; index++) { 1200 CELL cell = Map.Pick_Random_Location(); 1201 1202 if (Crates[crateindex].Create_Crate(cell)) { 1203 return(true); 1204 } 1205 } 1206 return(false); 1207 } 1208 1209 1210 /*********************************************************************************************** 1211 * MapClass::Remove_Crate -- Remove a crate from the specified cell. * 1212 * * 1213 * This will examine the cell and remove any crates there. * 1214 * * 1215 * INPUT: cell -- The cell to examine for crates and remove from. * 1216 * * 1217 * OUTPUT: bool; Was a crate found at the location specified and was it removed? * 1218 * * 1219 * WARNINGS: none * 1220 * * 1221 * HISTORY: * 1222 * 08/26/1996 JLB : Created. * 1223 *=============================================================================================*/ 1224 bool MapClass::Remove_Crate(CELL cell) 1225 { 1226 if (Session.Type != GAME_NORMAL) { 1227 for (int index = 0; index < ARRAY_SIZE(Crates); index++) { 1228 if (Crates[index].Is_Here(cell)) { 1229 return(Crates[index].Remove_It()); 1230 } 1231 } 1232 } 1233 1234 // if (Session.Type == GAME_NORMAL) { 1235 CellClass * cellptr = &(*this)[cell]; 1236 if (cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsCrate) { 1237 cellptr->Overlay = OVERLAY_NONE; 1238 cellptr->OverlayData = 0; 1239 return(true); 1240 } 1241 // } else { 1242 // for (int index = 0; index < ARRAY_SIZE(Crates); index++) { 1243 // if (Crates[index].Is_Here(cell)) { 1244 // return(Crates[index].Remove_It()); 1245 // } 1246 // } 1247 // } 1248 return(false); 1249 } 1250 1251 1252 /*************************************************************************** 1253 * MapClass::Validate -- validates every cell on the map * 1254 * * 1255 * This is a debugging routine, designed to detect memory trashers that * 1256 * alter the map. This routine is slow, but thorough. * 1257 * * 1258 * INPUT: * 1259 * none. * 1260 * * 1261 * OUTPUT: * 1262 * true = map is OK, false = an error was found * 1263 * * 1264 * WARNINGS: * 1265 * none. * 1266 * * 1267 * HISTORY: * 1268 * 07/08/1995 BRR : Created. * 1269 *=========================================================================*/ 1270 int MapClass::Validate(void) 1271 { 1272 CELL cell; 1273 TemplateType ttype; 1274 unsigned char ticon; 1275 TemplateTypeClass const *tclass; 1276 unsigned char map[13*8]; 1277 OverlayType overlay; 1278 SmudgeType smudge; 1279 ObjectClass * obj; 1280 LandType land; 1281 int i; 1282 1283 BlubCell = &Array[797]; 1284 1285 if (BlubCell->Overlapper[1]) { 1286 obj = BlubCell->Overlapper[1]; 1287 if (obj) { 1288 if (obj->IsInLimbo) 1289 obj = obj; 1290 } 1291 } 1292 1293 /* 1294 ** Check every cell on the map, even those that aren't displayed, 1295 ** in the hopes of detecting a memory trasher. 1296 */ 1297 for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { 1298 /* 1299 ** Validate Template & Icon data 1300 */ 1301 ttype = (*this)[cell].TType; 1302 ticon = (*this)[cell].TIcon; 1303 if (ttype >= TEMPLATE_COUNT && ttype != TEMPLATE_NONE) 1304 return(false); 1305 1306 /* 1307 ** To validate the icon value, we have to get a copy of the template's 1308 ** "icon map"; this map will have 0xff's in spots where there is no 1309 ** icon. If the icon value is out of range or points to an invalid spot, 1310 ** return an error. 1311 */ 1312 if (ttype != TEMPLATE_NONE) { 1313 tclass = &TemplateTypeClass::As_Reference(ttype); 1314 ticon = (*this)[cell].TIcon; 1315 Mem_Copy(Get_Icon_Set_Map(tclass->Get_Image_Data()), map, tclass->Width * tclass->Height); 1316 if (ticon < 0 || ticon >= (tclass->Width * tclass->Height) || map[ticon]==0xff) { 1317 return (false); 1318 } 1319 } 1320 1321 /* 1322 ** Validate Overlay 1323 */ 1324 overlay = (*this)[cell].Overlay; 1325 if (overlay < OVERLAY_NONE || overlay >= OVERLAY_COUNT) { 1326 return(false); 1327 } 1328 1329 /* 1330 ** Validate Smudge 1331 */ 1332 smudge = (*this)[cell].Smudge; 1333 if (smudge < SMUDGE_NONE || smudge >= SMUDGE_COUNT) { 1334 return(false); 1335 } 1336 1337 /* 1338 ** Validate LandType 1339 */ 1340 land = (*this)[cell].Land_Type(); 1341 if (land < LAND_CLEAR || land >= LAND_COUNT) { 1342 return(false); 1343 } 1344 1345 /* 1346 ** Validate Occupier 1347 */ 1348 obj = (*this)[cell].Cell_Occupier(); 1349 if (obj) { 1350 if ( 1351 ((unsigned int)obj & 0xff000000) || 1352 ((unsigned int)obj->Next & 0xff000000) || 1353 // ((unsigned int)obj->Trigger & 0xff000000) || 1354 obj->IsInLimbo || 1355 ((unsigned int)Coord_Cell(obj->Coord) >= MAP_CELL_TOTAL)) { 1356 1357 return (false); 1358 } 1359 } 1360 1361 /* 1362 ** Validate Overlappers 1363 */ 1364 for (i = 0; i < ARRAY_SIZE((*this)[cell].CellClass::Overlapper); i++) { 1365 obj = (*this)[cell].Overlapper[i]; 1366 if (obj) { 1367 if ( 1368 ((unsigned int)obj & 0xff000000) || 1369 ((unsigned int)obj->Next & 0xff000000) || 1370 // ((unsigned int)obj->Trigger & 0xff000000) || 1371 obj->IsInLimbo || 1372 ((unsigned int)Coord_Cell(obj->Coord) >= MAP_CELL_TOTAL)) { 1373 1374 return (false); 1375 } 1376 } 1377 } 1378 } 1379 1380 return (true); 1381 } 1382 1383 1384 /*********************************************************************************************** 1385 * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * 1386 * * 1387 * This routine is used by the mouse input processing code to find a clickable object * 1388 * close to coordinate specified. This is for targeting as well as selection determination. * 1389 * * 1390 * INPUT: coord -- The coordinate to scan for close object from. * 1391 * * 1392 * OUTPUT: Returns with a pointer to an object that is nearby the specified coordinate. * 1393 * * 1394 * WARNINGS: There could be a cloaked object at the location, but it won't be considered * 1395 * if it is not owned by the player. * 1396 * * 1397 * HISTORY: * 1398 * 08/20/1995 JLB : Created. * 1399 *=============================================================================================*/ 1400 ObjectClass * MapClass::Close_Object(COORDINATE coord) const 1401 { 1402 ObjectClass * object = 0; 1403 int distance = 0; 1404 CELL cell = Coord_Cell(coord); 1405 1406 /* 1407 ** Scan through current and adjacent cells, looking for the 1408 ** closest object (within reason) to the specified coordinate. 1409 */ 1410 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)}; 1411 for (int index = 0; index < (sizeof(_offsets) / sizeof(_offsets[0])); index++) { 1412 1413 /* 1414 ** Examine the cell for close object. Make sure that the cell actually is a 1415 ** legal one. 1416 */ 1417 CELL newcell = cell + _offsets[index]; 1418 if (In_Radar(newcell)) { 1419 1420 /* 1421 ** Search through all objects that occupy this cell and then 1422 ** find the closest object. Check against any previously found object 1423 ** to ensure that it is actually closer. 1424 */ 1425 ObjectClass * o = Array[newcell].Cell_Occupier(); 1426 while (o != NULL) { 1427 1428 /* 1429 ** Special case check to ignore cloaked object if not allied with the player. 1430 */ 1431 // Change for client/server multiplayer. ST - 8/7/2019 10:35AM 1432 //if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) { 1433 if (!o->Is_Techno() || !((TechnoClass *)o)->Is_Cloaked(PlayerPtr)) { 1434 int d=-1; 1435 if (o->What_Am_I() == RTTI_BUILDING) { 1436 d = Distance(coord, Cell_Coord(newcell)); 1437 if (d > 0x00C0) d = -1; 1438 } else { 1439 d = Distance(coord, o->Center_Coord()); 1440 } 1441 if (d >= 0 && (!object || d < distance)) { 1442 distance = d; 1443 object = o; 1444 } 1445 } 1446 o = o->Next; 1447 } 1448 } 1449 } 1450 1451 /* 1452 ** Handle aircraft selection separately, since they aren't tracked in cells while flying 1453 */ 1454 for (int index = 0; index < Aircraft.Count(); index++) { 1455 AircraftClass * aircraft = Aircraft.Ptr(index); 1456 1457 if (aircraft->In_Which_Layer() != LAYER_GROUND) { 1458 if (!aircraft->Is_Cloaked(PlayerPtr)) { 1459 int d = Distance(coord, Coord_Add(aircraft->Center_Coord(), XY_Coord(0, -aircraft->Height))); 1460 if (d >= 0 && (!object || d < distance)) { 1461 distance = d; 1462 object = aircraft; 1463 } 1464 } 1465 } 1466 } 1467 1468 /* 1469 ** Only return the object if it is within 1/4 cell distance from the specified 1470 ** coordinate. 1471 */ 1472 if (object && distance > 0xC0) { 1473 object = 0; 1474 } 1475 return(object); 1476 } 1477 1478 1479 /*********************************************************************************************** 1480 * MapClass::Zone_Reset -- Resets all zone numbers to match the map. * 1481 * * 1482 * This routine will rescan the map and fill in the zone values for each of the cells. * 1483 * All cells that are contiguous are given the same zone number. * 1484 * * 1485 * INPUT: method -- The method to recalculate the zones upon. If 1 then recalc non * 1486 * crushable zone. If 2 then recalc crushable zone. If 3, then * 1487 * recalc both zones. * 1488 * * 1489 * OUTPUT: none * 1490 * * 1491 * WARNINGS: This is a time consuming routine. Call it as infrequently as possible. It must * 1492 * be called whenever something that would affect contiguousness occurs. Example: * 1493 * when a bridge is built or destroyed. * 1494 * * 1495 * HISTORY: * 1496 * 09/22/1995 JLB : Created. * 1497 *=============================================================================================*/ 1498 bool MapClass::Zone_Reset(int method) 1499 { 1500 /* 1501 ** Zero out all zones to a null state. 1502 */ 1503 for (int index = 0; index < MAP_CELL_TOTAL; index++) { 1504 if (method & MZONEF_NORMAL) { 1505 Array[index].Zones[MZONE_NORMAL] = 0; 1506 } 1507 if (method & MZONEF_CRUSHER) { 1508 Array[index].Zones[MZONE_CRUSHER] = 0; 1509 } 1510 if (method & MZONEF_DESTROYER) { 1511 Array[index].Zones[MZONE_DESTROYER] = 0; 1512 } 1513 if (method & MZONEF_WATER) { 1514 Array[index].Zones[MZONE_WATER] = 0; 1515 } 1516 } 1517 1518 /* 1519 ** Normal zone recalculation. 1520 */ 1521 if (method & MZONEF_NORMAL) { 1522 int zone = 1; // Starting zone number. 1523 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 1524 if (Zone_Span(cell, zone, MZONE_NORMAL)) { 1525 zone++; 1526 } 1527 } 1528 } 1529 1530 /* 1531 ** Crushable wall recalculation. 1532 */ 1533 if (method & MZONEF_CRUSHER) { 1534 int zone = 1; // Starting zone number. 1535 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 1536 if (Zone_Span(cell, zone, MZONE_CRUSHER)) { 1537 zone++; 1538 } 1539 } 1540 } 1541 1542 /* 1543 ** Wall destroyer zone recalculation. 1544 */ 1545 if (method & MZONEF_DESTROYER) { 1546 int zone = 1; // Starting zone number. 1547 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 1548 if (Zone_Span(cell, zone, MZONE_DESTROYER)) { 1549 zone++; 1550 } 1551 } 1552 } 1553 1554 /* 1555 ** Water based zone recalcuation. 1556 */ 1557 if (method & MZONEF_WATER) { 1558 int zone = 1; // Starting zone number. 1559 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 1560 if (Zone_Span(cell, zone, MZONE_WATER)) { 1561 zone++; 1562 } 1563 } 1564 } 1565 1566 return(false); 1567 } 1568 1569 1570 /*********************************************************************************************** 1571 * MapClass::Zone_Span -- Flood fills the specified zone from the cell origin. * 1572 * * 1573 * This routine is used to fill a zone value into the map. The map is "flood filled" from * 1574 * the cell specified. All adjacent (8 connected) and generally passable terrain cells are * 1575 * given the zone number specified. This routine checks for legality before filling * 1576 * occurs. The routine is safe to call even if the legality of the cell is unknown at the * 1577 * time of the call. * 1578 * * 1579 * INPUT: cell -- The cell to begin filling from. * 1580 * * 1581 * zone -- The zone number to assign to all adjacent cells. * 1582 * * 1583 * check -- The zone type to check against. * 1584 * * 1585 * OUTPUT: Returns with the number of cells marked by this routine. * 1586 * * 1587 * WARNINGS: This routine is slow and recursive. Only use when necessary. * 1588 * * 1589 * HISTORY: * 1590 * 09/25/1995 JLB : Created. * 1591 * 10/05/1996 JLB : Examines crushable walls. * 1592 *=============================================================================================*/ 1593 int MapClass::Zone_Span(CELL cell, int zone, MZoneType check) 1594 { 1595 int filled = 0; 1596 int xbegin = Cell_X(cell); 1597 int xend = xbegin; 1598 int y = Cell_Y(cell); 1599 1600 /* 1601 ** Perform some preliminary legality checks. If the cell specified 1602 ** is illegal, then no further processing is necessary. 1603 */ 1604 if (y < MapCellY || y >= MapCellY+MapCellHeight || xbegin < MapCellX || xbegin >= MapCellX+MapCellWidth) { 1605 return(0); 1606 } 1607 1608 /* 1609 ** Find the full extent of the current span by first scanning leftward 1610 ** until a boundary is reached. 1611 */ 1612 for (; xbegin >= MapCellX; xbegin--) { 1613 CellClass * cellptr = &(*this)[XY_Cell(xbegin, y)]; 1614 if (cellptr->Zones[check] != 0 || (!cellptr->Is_Clear_To_Move(check == MZONE_WATER ? SPEED_FLOAT : SPEED_TRACK, true, true, -1, check))) { 1615 1616 /* 1617 ** Special short circuit code to bail from this entire routine if 1618 ** it was called for a cell that is not a legal candidate for 1619 ** zone marking. This eliminates redundant processing and allows this 1620 ** routine to be called for illegal cells without causing an error. 1621 */ 1622 if (xbegin == Cell_X(cell)) return(0); 1623 1624 /* 1625 ** Otherwise break out of the left scan since a boundary was discovered. 1626 */ 1627 xbegin++; 1628 break; 1629 } 1630 } 1631 xbegin = max(xbegin, MapCellX); 1632 1633 /* 1634 ** Scan rightward until a boundary is reached. This will then define the 1635 ** extent of the current span. 1636 */ 1637 for (; xend < MapCellX+MapCellWidth; xend++) { 1638 CellClass * cellptr = &(*this)[XY_Cell(xend, y)]; 1639 if (cellptr->Zones[check] != 0 || (!cellptr->Is_Clear_To_Move(check == MZONE_WATER ? SPEED_FLOAT : SPEED_TRACK, true, true, -1, check))) { 1640 xend--; 1641 break; 1642 } 1643 } 1644 xend = min(xend, MapCellX+MapCellWidth-1); 1645 1646 /* 1647 ** At this point we know the bounds of the current span. Fill in the zone values 1648 ** for the entire span. 1649 */ 1650 for (int x = xbegin; x <= xend; x++) { 1651 (*this)[XY_Cell(x, y)].Zones[check] = zone; 1652 filled++; 1653 } 1654 1655 /* 1656 ** Now scan the upper and lower shadow rows. If any of these rows contain 1657 ** candidate cells, then recursively call the span process for them. Take 1658 ** note that the adjacent span scanning starts one cell wider on each 1659 ** end of the scan. This is necessary because diagonals are considered 1660 ** adjacent. 1661 */ 1662 for (int x = xbegin-1; x <= xend; x++) { 1663 filled += Zone_Span(XY_Cell(x, y-1), zone, check); 1664 filled += Zone_Span(XY_Cell(x, y+1), zone, check); 1665 } 1666 return(filled); 1667 } 1668 1669 1670 /*********************************************************************************************** 1671 * MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. * 1672 * * 1673 * This routine is used to find a location that probably will be ok to move to that is * 1674 * located as close as possible to the specified cell. The computer uses this when it has * 1675 * determined the ideal location for an object, but then needs to give a valid movement * 1676 * destination to a unit. * 1677 * * 1678 * INPUT: cell -- The cell that scanning should radiate out from. * 1679 * * 1680 * zone -- The zone that must be matched to find a legal location (value of -1 means * 1681 * any zone will do). * 1682 * * 1683 * * 1684 * check -- The type of zone to check against. Only valid if a zone value is given. * 1685 * * 1686 * checkflagged -- Whether the cell's flagged status is checked (used when dropping). * 1687 * * 1688 * OUTPUT: Returns with the cell that is generally clear (legal to move to) that is close * 1689 * to the specified cell. * 1690 * * 1691 * WARNINGS: none * 1692 * * 1693 * HISTORY: * 1694 * 10/05/1995 JLB : Created. * 1695 *=============================================================================================*/ 1696 CELL MapClass::Nearby_Location(CELL cell, SpeedType speed, int zone, MZoneType check, bool checkflagged, int locationmod) const 1697 { 1698 CELL topten[10]; 1699 int count = 0; 1700 int xx = Cell_X(cell); 1701 int yy = Cell_Y(cell); 1702 1703 /* 1704 ** Determine the limits of the scanning in the four directions so that 1705 ** it won't scan past the edge of the world. 1706 */ 1707 int left = MapCellX; 1708 int right = MapCellX + MapCellWidth - 1; 1709 int top = MapCellY; 1710 int bottom = MapCellY + MapCellHeight - 1; 1711 1712 /* 1713 ** Radiate outward from the specified location, looking for the closest 1714 ** location that is generally clear. 1715 */ 1716 for (int radius = 0; radius < MAP_CELL_W; radius++) { 1717 CELL newcell; 1718 CellClass const * cellptr; 1719 1720 /* 1721 ** Scan the top and bottom rows of the "box". 1722 */ 1723 for (int x = xx-radius; x <= xx+radius; x++) { 1724 if (x >= left && x <= right) { 1725 int y = yy-radius; 1726 if (y >= top) { 1727 newcell = XY_Cell(x, y); 1728 cellptr = &Map[newcell]; 1729 if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { 1730 topten[count++] = newcell; 1731 } 1732 } 1733 if (count == ARRAY_SIZE(topten)) break; 1734 1735 y = yy+radius; 1736 if (y <= bottom) { 1737 newcell = XY_Cell(x, y); 1738 cellptr = &Map[newcell]; 1739 if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { 1740 topten[count++] = newcell; 1741 } 1742 } 1743 if (count == ARRAY_SIZE(topten)) break; 1744 } 1745 } 1746 1747 if (count == ARRAY_SIZE(topten)) break; 1748 1749 /* 1750 ** Scan the left and right columns of the "box". 1751 */ 1752 for (int y = yy-radius; y <= yy+radius; y++) { 1753 if (y >= top && y <= bottom) { 1754 int x = xx-radius; 1755 if (x >= left) { 1756 newcell = XY_Cell(x, y); 1757 cellptr = &Map[newcell]; 1758 if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { 1759 topten[count++] = newcell; 1760 } 1761 } 1762 if (count == ARRAY_SIZE(topten)) break; 1763 1764 x = xx+radius; 1765 if (x <= right) { 1766 newcell = XY_Cell(x, y); 1767 cellptr = &Map[newcell]; 1768 if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { 1769 topten[count++] = newcell; 1770 } 1771 } 1772 if (count == ARRAY_SIZE(topten)) break; 1773 } 1774 } 1775 1776 if (count > 0) break; 1777 } 1778 1779 if (count > 0) { 1780 return(topten[(Frame+locationmod) % count]); 1781 } 1782 return(0); 1783 } 1784 1785 1786 /*********************************************************************************************** 1787 * MapClass::Base_Region -- Finds the owner and base zone for specified cell. * 1788 * * 1789 * This routine is used to determine what base the specified cell is close to and what * 1790 * zone of that base the cell lies in. This routine is particularly useful in letting the * 1791 * computer know when the player targets a destination near a computer's base. * 1792 * * 1793 * INPUT: cell -- The cell that is to be checked. * 1794 * * 1795 * house -- Reference to the house type number. This value will be set if a base * 1796 * was found nearby the specified cell. * 1797 * * 1798 * zone -- The zone that the cell is located in IF the cell is near a base. * 1799 * * 1800 * * 1801 * OUTPUT: Was a base near the specified cell found? If not, then the 'house' and 'zone' * 1802 * reference values are left in an undefined state and the return value will be * 1803 * false. * 1804 * * 1805 * WARNINGS: none * 1806 * * 1807 * HISTORY: * 1808 * 10/05/1995 JLB : Created. * 1809 *=============================================================================================*/ 1810 bool MapClass::Base_Region(CELL cell, HousesType & house, ZoneType & zone) const 1811 { 1812 if ((unsigned)cell < MAP_CELL_TOTAL && In_Radar(cell)) { 1813 for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { 1814 HouseClass * h = HouseClass::As_Pointer(house); 1815 1816 if (h && h->IsActive && !h->IsDefeated && h->Center) { 1817 zone = h->Which_Zone(cell); 1818 if (zone != ZONE_NONE) { 1819 return(true); 1820 } 1821 } 1822 } 1823 } 1824 return(false); 1825 } 1826 1827 1828 /*********************************************************************************************** 1829 * MapClass::Destroy_Bridge_At -- Destroyes the bridge at location specified. * 1830 * * 1831 * This routine will destroy the bridge at the location specified. * 1832 * * 1833 * INPUT: cell -- A cell that can uniquely identify the bridge. * 1834 * * 1835 * OUTPUT: bool; Was the bridge destroyed? * 1836 * * 1837 * WARNINGS: none * 1838 * * 1839 * HISTORY: * 1840 * 07/29/1996 JLB : Created. * 1841 *=============================================================================================*/ 1842 1843 // Need to inform the server of the cell change so it can communicate with the clients - SKY 1844 extern void On_Update_Map_Cell(int cell_x, int cell_y, const char* template_type_name); 1845 1846 struct CellUpdateStruct 1847 { 1848 const TemplateTypeClass* Type; 1849 CELL Cell; 1850 }; 1851 1852 static const int MAX_UPDATES = 8; 1853 1854 static void Add_Cell_Update(CellUpdateStruct* updates, int& count, TemplateType type, CELL cell) 1855 { 1856 new TemplateClass(type, cell); 1857 1858 assert(count < MAX_UPDATES); 1859 if (count < MAX_UPDATES) 1860 { 1861 updates[count].Type = TemplateTypes.Ptr((int)type); 1862 updates[count].Cell = cell; 1863 count++; 1864 } 1865 } 1866 1867 bool MapClass::Destroy_Bridge_At(CELL cell) 1868 { 1869 bool destroyed = false; 1870 if (In_Radar(cell) && !Special.IsCaptureTheFlag) { 1871 CellClass * cellptr = &(*this)[cell]; 1872 TemplateType ttype = cellptr->TType; 1873 1874 CellUpdateStruct cell_updates[MAX_UPDATES]; 1875 int update_count = 0; 1876 1877 if (ttype == TEMPLATE_BRIDGE1 || ttype == TEMPLATE_BRIDGE2) { 1878 int icon = cellptr->TIcon; 1879 int w = TemplateTypeClass::As_Reference(ttype).Width; 1880 int h = TemplateTypeClass::As_Reference(ttype).Height; 1881 1882 cell -= icon % w; 1883 cell -= MAP_CELL_W * (icon / w); 1884 1885 if (ttype == TEMPLATE_BRIDGE1) { 1886 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE1H, cell); 1887 } else { 1888 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE2H, cell); 1889 } 1890 1891 new AnimClass(ANIM_NAPALM3, Cell_Coord(cell + w/2 + (h/2)*MAP_CELL_W)); 1892 } 1893 1894 if (ttype == TEMPLATE_BRIDGE1H || ttype == TEMPLATE_BRIDGE2H) { 1895 int icon = cellptr->TIcon; 1896 int bridge_w = TemplateTypeClass::As_Reference(ttype).Width; 1897 int bridge_h = TemplateTypeClass::As_Reference(ttype).Height; 1898 1899 cell -= icon % bridge_w; 1900 cell -= MAP_CELL_W * (icon / bridge_w); 1901 1902 if (ttype == TEMPLATE_BRIDGE1H) { 1903 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE1D, cell); 1904 } else { 1905 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE2D, cell); 1906 } 1907 1908 Scen.BridgeCount--; 1909 Scen.IsBridgeChanged = true; 1910 new AnimClass(ANIM_NAPALM3, Cell_Coord(cell + bridge_w/2 + (bridge_h/2)*MAP_CELL_W)); 1911 Map.Zone_Reset(MZONEF_ALL); 1912 1913 /* 1914 ** Now, loop through all the bridge cells and find anyone standing 1915 ** on a destroyed part (which is now river), and nuke them. 1916 */ 1917 CELL cell2 = cell; 1918 for (int y = 0; y < bridge_h; y++) { 1919 for (int x = 0; x < bridge_w; x++) { 1920 CellClass * bridge_cell = &(*this)[cell2]; 1921 if (bridge_cell->TType == ttype) { 1922 1923 /* 1924 ** Any unit that is firing on the bridge at this location, will stop 1925 ** firing because the bridge has been destroyed. 1926 */ 1927 Detach_This_From_All(As_Target(cell2), true); 1928 1929 ObjectClass * obj = bridge_cell->Cell_Occupier(); 1930 while (obj != NULL) { 1931 ObjectClass * next = obj->Next; 1932 if (obj->Is_Techno()) { 1933 int damage = obj->Strength; 1934 obj->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); 1935 } 1936 obj = next; 1937 } 1938 } 1939 cell2++; 1940 } 1941 cell2 += MAP_CELL_W - bridge_w; 1942 } 1943 Shake_The_Screen(3); 1944 1945 destroyed = true; 1946 } else { 1947 /* 1948 ** All this code is for the multi-part bridges. 1949 */ 1950 if (ttype >= TEMPLATE_BRIDGE_1A && ttype <= TEMPLATE_BRIDGE_3E) { 1951 int icon = cellptr->TIcon; 1952 int w = TemplateTypeClass::As_Reference(ttype).Width; 1953 int h = TemplateTypeClass::As_Reference(ttype).Height; 1954 1955 cell -= icon % w; 1956 cell -= MAP_CELL_W * (icon / w); 1957 switch (ttype) { 1958 case TEMPLATE_BRIDGE_1A: 1959 case TEMPLATE_BRIDGE_1B: 1960 case TEMPLATE_BRIDGE_2A: 1961 case TEMPLATE_BRIDGE_2B: 1962 case TEMPLATE_BRIDGE_3A: 1963 case TEMPLATE_BRIDGE_3B: 1964 ttype++; 1965 Add_Cell_Update(cell_updates, update_count, ttype, cell); 1966 break; 1967 } 1968 1969 /* 1970 ** If we were a middle piece that just got blown up, update the 1971 ** adjoining pieces to make sure they're shaped properly. 1972 */ 1973 if (ttype == TEMPLATE_BRIDGE_3C) { 1974 // check the template below us, at x-1, y+1 1975 CELL cell2 = cell + (MAP_CELL_W - 1); 1976 CellClass * celptr = &(*this)[cell2]; 1977 if (celptr->TType == TEMPLATE_BRIDGE_3C) { 1978 // It was also destroyed. Update us and it. 1979 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell); 1980 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell2); 1981 } 1982 1983 // Now check the template above us, at x+1, y-1. 1984 cell2 = cell - (MAP_CELL_W - 1); 1985 celptr = &(*this)[cell2]; 1986 if (celptr->TType == TEMPLATE_BRIDGE_3C) { 1987 if (cellptr->TType == TEMPLATE_BRIDGE_3D) { 1988 // if we're already one-sided, turn us to all water 1989 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell); 1990 } else { 1991 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell); 1992 } 1993 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell2); 1994 } 1995 Map.Zone_Reset(MZONEF_ALL); 1996 } 1997 1998 /* 1999 ** If we're an end bridge piece, update the adjoining piece to 2000 ** be the proper shape. 2001 */ 2002 if (cellptr->TType == TEMPLATE_BRIDGE_1C) { 2003 Scen.BridgeCount--; 2004 Scen.IsBridgeChanged = true; 2005 2006 // Point to the template below us, x-1, y+2 2007 CELL cell2 = cell + (MAP_CELL_W * 2) - 1; 2008 switch ((*this)[cell2].TType) { 2009 case TEMPLATE_BRIDGE_3A: 2010 case TEMPLATE_BRIDGE_3B: 2011 case TEMPLATE_BRIDGE_3C: 2012 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell2); 2013 break; 2014 case TEMPLATE_BRIDGE_3D: 2015 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell2); 2016 break; 2017 } 2018 } else { 2019 if (cellptr->TType == TEMPLATE_BRIDGE_2C) { 2020 // Point to the template above us, x+2, y-1 2021 CELL cell2 = cell - (MAP_CELL_W - 2); 2022 switch ((*this)[cell2].TType) { 2023 case TEMPLATE_BRIDGE_3A: 2024 case TEMPLATE_BRIDGE_3B: 2025 case TEMPLATE_BRIDGE_3C: 2026 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell2); 2027 break; 2028 case TEMPLATE_BRIDGE_3E: 2029 Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell2); 2030 break; 2031 } 2032 } 2033 } 2034 if (cellptr->TType == TEMPLATE_BRIDGE_1C || 2035 cellptr->TType == TEMPLATE_BRIDGE_2C || 2036 (cellptr->TType >= TEMPLATE_BRIDGE_3C && cellptr->TType <= TEMPLATE_BRIDGE_3E)) { 2037 int x, y, tdata = 0; 2038 for (y = 0; y < h; y++) { 2039 for (x = 0; x < w; x++) { 2040 CellClass * ptr = &(*this)[(CELL)(cell + x)]; 2041 if (ptr->TType == cellptr->TType || ptr->Land_Type() == LAND_RIVER || ptr->Land_Type() == LAND_WATER) { 2042 Detach_This_From_All(As_Target((CELL)(cell+tdata)), true); 2043 2044 ObjectClass * obj = ptr->Cell_Occupier(); 2045 while (obj != NULL) { 2046 ObjectClass * next = obj->Next; 2047 if (obj->Is_Techno()) { 2048 int damage = obj->Strength; 2049 obj->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); 2050 } 2051 obj = next; 2052 } 2053 2054 } 2055 tdata++; 2056 } 2057 cell += MAP_CELL_W; 2058 } 2059 Map.Zone_Reset(MZONEF_ALL); 2060 destroyed = true; 2061 } 2062 Shake_The_Screen(3); 2063 } 2064 } 2065 2066 int cell_index = 0; 2067 char cell_name[_MAX_PATH] = { 0 }; 2068 char icon_number[32] = { 0 }; 2069 int icon = 0; 2070 void *image_data = 0; 2071 for (int i = 0; i < update_count; i++) { 2072 const TemplateTypeClass* type = cell_updates[i].Type; 2073 CELL cell = cell_updates[i].Cell; 2074 for (int y = 0; y < type->Height; y++) { 2075 for (int x = 0; x < type->Width; x++) { 2076 CELL newcell = cell + y * MAP_CELL_W + x; 2077 if (Map.In_Radar(newcell)) { 2078 CellClass * newcellptr = &Map[newcell]; 2079 if (newcellptr->Get_Template_Info(cell_name, icon, image_data)) { 2080 itoa(icon, icon_number, 10); 2081 strncat(cell_name, "_i", 32); 2082 strncat(cell_name, icon_number, 32); 2083 strncat(cell_name, ".tga", 32); 2084 On_Update_Map_Cell(Cell_X(newcell), Cell_Y(newcell), cell_name); 2085 } 2086 } 2087 } 2088 } 2089 } 2090 } 2091 return(destroyed); 2092 } 2093 2094 2095 /*********************************************************************************************** 2096 * MapClass::Detach -- Remove specified object from map references. * 2097 * * 2098 * This routine will take the object (represented by a target value) and remove all * 2099 * references to it from the map. Typically, this is used to remove trigger reference. * 2100 * * 2101 * INPUT: target -- The target object to remove from the map. * 2102 * * 2103 * OUTPUT: none * 2104 * * 2105 * WARNINGS: none * 2106 * * 2107 * HISTORY: * 2108 * 07/28/1996 JLB : Created. * 2109 *=============================================================================================*/ 2110 void MapClass::Detach(TARGET target, bool) 2111 { 2112 /* 2113 ** Remove this trigger from the map zone/line tracking list. 2114 */ 2115 if (Is_Target_Trigger(target)) { 2116 for (int index = 0; index < MapTriggers.Count(); index++) { 2117 if (MapTriggers[index] == As_Trigger(target)) { 2118 MapTriggers.Delete(index); 2119 break; 2120 } 2121 } 2122 2123 /* 2124 ** Loop through all cells; remove any reference to this trigger 2125 */ 2126 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 2127 if ((*this)[cell].Trigger == As_Trigger(target)) { 2128 (*this)[cell].Trigger = NULL; 2129 } 2130 } 2131 } 2132 } 2133 2134 2135 /*********************************************************************************************** 2136 * MapClass::Intact_Bridge_Count -- Determine the number of intact bridges. * 2137 * * 2138 * This will examine the entire map and return the number of bridges that are intact. An * 2139 * intact bridge is one that units can travel over. * 2140 * * 2141 * INPUT: none * 2142 * * 2143 * OUTPUT: Returns with the number of intact bridges on the map. * 2144 * * 2145 * WARNINGS: none * 2146 * * 2147 * HISTORY: * 2148 * 07/28/1996 JLB : Created. * 2149 *=============================================================================================*/ 2150 int MapClass::Intact_Bridge_Count(void) const 2151 { 2152 /* 2153 ** Count all non-destroyed bridges on the map. 2154 */ 2155 int count = 0; 2156 CellClass const * cellptr = &(*this)[(CELL)0]; 2157 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 2158 switch (cellptr->TType) { 2159 case TEMPLATE_BRIDGE1: 2160 case TEMPLATE_BRIDGE1H: 2161 case TEMPLATE_BRIDGE2: 2162 case TEMPLATE_BRIDGE2H: 2163 case TEMPLATE_BRIDGE_1A: 2164 case TEMPLATE_BRIDGE_1B: 2165 if (cellptr->TIcon == 6) { 2166 count++; 2167 } 2168 break; 2169 2170 default: 2171 break; 2172 } 2173 2174 cellptr++; 2175 } 2176 2177 return(count); 2178 } 2179 2180 2181 /*********************************************************************************************** 2182 * MapClass::Pick_Random_Location -- Picks a random location on the map. * 2183 * * 2184 * This routine will pick a random location on the map. It performs no legality checking * 2185 * other than forcing the cell to be on the map proper. * 2186 * * 2187 * INPUT: none * 2188 * * 2189 * OUTPUT: Returns with a cell that is within the map. * 2190 * * 2191 * WARNINGS: none * 2192 * * 2193 * HISTORY: * 2194 * 09/25/1996 JLB : Created. * 2195 *=============================================================================================*/ 2196 CELL MapClass::Pick_Random_Location(void) const 2197 { 2198 int x = Map.MapCellX + Random_Pick(0, Map.MapCellWidth-1); 2199 int y = Map.MapCellY + Random_Pick(0, Map.MapCellHeight-1); 2200 2201 return(XY_Cell(x, y)); 2202 } 2203 2204 2205 #if (1) 2206 2207 /*********************************************************************************************** 2208 * MapClass::Shroud_The_Map -- cover the whole map in darkness (usually from blackout crate) * 2209 * * 2210 * INPUT: House to shroud * 2211 * * 2212 * OUTPUT: None 2213 * * 2214 * WARNINGS: none * 2215 * * 2216 * HISTORY: * 2217 * 10/19/1996 BWG : Created. * 2218 * 08/12/2019 ST : Updated for client/server multiplayer * 2219 *=============================================================================================*/ 2220 void MapClass::Shroud_The_Map(HouseClass *house) 2221 { 2222 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 2223 CellClass * cellptr = &Map[cell]; 2224 if (cellptr->Is_Mapped(house) || cellptr->Is_Visible(house)) { 2225 2226 cellptr->Redraw_Objects(); 2227 2228 /* 2229 ** BG: remove "ring of darkness" around edge of map. 2230 */ 2231 int x = Cell_X(cell); 2232 int y = Cell_Y(cell); 2233 if (x >= Map.MapCellX && x < (Map.MapCellX + Map.MapCellWidth) && 2234 y >= Map.MapCellY && y < (Map.MapCellY + Map.MapCellHeight)) { 2235 cellptr->Set_Mapped(house, false); 2236 cellptr->Set_Visible(house, false); 2237 } 2238 } 2239 } 2240 for (int obj_index = 0; obj_index < DisplayClass::Layer[LAYER_GROUND].Count(); obj_index++) { 2241 ObjectClass * layer_object = DisplayClass::Layer[LAYER_GROUND][obj_index]; 2242 if (layer_object && layer_object->Is_Techno() && ((TechnoClass *)layer_object)->House == house) { 2243 layer_object->Look(); 2244 } 2245 } 2246 Flag_To_Redraw(true); 2247 } 2248 2249 2250 #else 2251 2252 // 2253 // Old code for posterity. ST - 8/12/2019 11:34AM 2254 // 2255 2256 /*********************************************************************************************** 2257 * MapClass::Shroud_The_Map -- cover the whole map in darkness (usually from blackout crate) * 2258 * * 2259 * INPUT: none * 2260 * * 2261 * OUTPUT: Returns with a cell that is within the map. * 2262 * * 2263 * WARNINGS: none * 2264 * * 2265 * HISTORY: * 2266 * 10/19/1996 BWG : Created. * 2267 *=============================================================================================*/ 2268 void MapClass::Shroud_The_Map(void) 2269 { 2270 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 2271 CellClass * cellptr = &Map[cell]; 2272 if (cellptr->IsMapped || cellptr->IsVisible) { 2273 cellptr->Redraw_Objects(); 2274 /* 2275 ** BG: remove "ring of darkness" around edge of map. 2276 */ 2277 int x = Cell_X(cell); 2278 int y = Cell_Y(cell); 2279 if (x >= Map.MapCellX && x < (Map.MapCellX + Map.MapCellWidth) && 2280 y >= Map.MapCellY && y < (Map.MapCellY + Map.MapCellHeight)) { 2281 cellptr->IsMapped = false; 2282 cellptr->IsVisible = false; 2283 } 2284 } 2285 } 2286 for (int obj_index = 0; obj_index < DisplayClass::Layer[LAYER_GROUND].Count(); obj_index++) { 2287 ObjectClass * layer_object = DisplayClass::Layer[LAYER_GROUND][obj_index]; 2288 if (layer_object && layer_object->Is_Techno() && ((TechnoClass *)layer_object)->House == PlayerPtr) { 2289 layer_object->Look(); 2290 } 2291 } 2292 Flag_To_Redraw(true); 2293 } 2294 #endif