CELL.CPP (143702B)
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/CELL.CPP 4 3/14/97 1: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 : CELL.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : April 29, 1994 * 28 * * 29 * Last Update : October 6, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * 34 * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * 35 * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. * 36 * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. * 37 * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. * 38 * CellClass::CellClass -- Constructor for cell objects. * 39 * CellClass::Cell_Building -- Return with building at specified cell. * 40 * CellClass::Cell_Color -- Determine what radar color to use for this cell. * 41 * CellClass::Cell_Coord -- Returns the coordinate of this cell. * 42 * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * 43 * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit. * 44 * CellClass::Cell_Object -- Returns with clickable object in cell. * 45 * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * 46 * CellClass::Cell_Terrain -- Determines terrain object in cell. * 47 * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * 48 * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. * 49 * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * 50 * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * 51 * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * 52 * CellClass::Draw_It -- Draws the cell imagery at the location specified. * 53 * CellClass::Flag_Place -- Places a house flag down on the cell. * 54 * CellClass::Flag_Remove -- Removes the house flag from the cell. * 55 * CellClass::Goodie_Check -- Performs crate discovery logic. * 56 * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. * 57 * CellClass::Incoming -- Causes objects in cell to "run for cover". * 58 * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. * 59 * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. * 60 * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel * 61 * CellClass::Occupy_Down -- Flag occupation of specified cell. * 62 * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * 63 * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (over* 64 * CellClass::Overlap_Unit -- Marks cell as being overlapped by unit. * 65 * CellClass::Overlap_Up -- Removes overlap flag for the cell. * 66 * CellClass::Read -- Reads a particular cell value from a save game file. * 67 * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * 68 * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * 69 * CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. * 70 * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * 71 * CellClass::Reserve_Cell -- Marks a cell as being occupied by the specified unit ID. * 72 * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * 73 * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE * 74 * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. * 75 * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smooth. * 76 * CellClass::Wall_Update -- Updates the imagery for wall objects in cell. * 77 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 78 79 #include "function.h" 80 #include "vortex.h" 81 82 /* 83 ** New sidebar for GlyphX multiplayer. ST - 8/2/2019 2:50PM 84 */ 85 #include "SidebarGlyphx.h" 86 87 88 /*********************************************************************************************** 89 * CellClass::CellClass -- Constructor for cell objects. * 90 * * 91 * A cell object is constructed into an empty state. It contains no specific objects, * 92 * templates, or overlays. * 93 * * 94 * INPUT: none * 95 * * 96 * OUTPUT: none * 97 * * 98 * WARNINGS: none * 99 * * 100 * HISTORY: * 101 * 08/09/1994 JLB : Created. * 102 * 02/20/1996 JLB : Uses initializer list. * 103 *=============================================================================================*/ 104 CellClass::CellClass(void) : 105 ID(Map.ID(this)), 106 IsPlot(false), 107 IsCursorHere(false), 108 IsMapped(false), 109 IsVisible(false), 110 IsWaypoint(false), 111 IsRadarCursor(false), 112 IsFlagged(false), 113 IsToShroud(false), 114 Jammed(0), 115 Trigger(NULL), 116 TType(TEMPLATE_NONE), 117 TIcon(0), 118 Overlay(OVERLAY_NONE), 119 OverlayData(0), 120 Smudge(SMUDGE_NONE), 121 SmudgeData(0), 122 Owner(HOUSE_NONE), 123 InfType(HOUSE_NONE), 124 OccupierPtr(0), 125 Land(LAND_CLEAR), 126 OverrideLand(LAND_NONE), 127 IsMappedByPlayerMask(0), 128 IsVisibleByPlayerMask(0), 129 CTFFlag(NULL) 130 { 131 for (int zone = MZONE_FIRST; zone < MZONE_COUNT; zone++) { 132 Zones[zone] = 0; 133 } 134 Flag.Composite = 0; 135 for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { 136 Overlapper[index] = 0; 137 } 138 } 139 140 141 /*********************************************************************************************** 142 * CellClass::Cell_Color -- Determine what radar color to use for this cell. * 143 * * 144 * Use this routine to determine what radar color to render a radar * 145 * pixel with. This routine is called many many times to render the * 146 * radar map, so it must be fast. * 147 * * 148 * INPUT: none * 149 * * 150 * OUTPUT: Returns with the color to display the radar pixel with. * 151 * * 152 * WARNINGS: none * 153 * * 154 * HISTORY: * 155 * 03/01/1994 JLB : Created. * 156 * 04/30/1994 JLB : Converted to member function. * 157 * 05/31/1994 JLB : Takes into account any stealth characteristics of object. * 158 *=============================================================================================*/ 159 int CellClass::Cell_Color(bool override) const 160 { 161 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 162 163 BuildingClass * object = Cell_Building(); 164 if (object && !object->Class->IsInvisible) { 165 return(ColorRemaps[object->House->RemapColor].Bar); 166 } 167 168 if (override) { 169 return(TBLACK); 170 } 171 if (LastTheater == THEATER_SNOW) { 172 return(::SnowColor[Land_Type()]); 173 } else { 174 return(::GroundColor[Land_Type()]); 175 } 176 } 177 178 179 /*********************************************************************************************** 180 * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * 181 * * 182 * Returns an object located in the cell. If there is a * 183 * building present, it returns a pointer to that, otherwise it returns * 184 * a pointer to one of the units there. If nothing is present in the * 185 * specified cell, then it returns NULL. * 186 * * 187 * INPUT: x,y -- Coordinate offset (from upper left corner) to use as an aid in selecting * 188 * the desired object within the cell. * 189 * * 190 * OUTPUT: Returns a pointer to a building or unit located in cell. If * 191 * nothing present, just returns NULL. * 192 * * 193 * WARNINGS: none * 194 * * 195 * HISTORY: * 196 * 08/05/1992 JLB : Created. * 197 * 04/30/1994 JLB : Converted to member function. * 198 *=============================================================================================*/ 199 TechnoClass * CellClass::Cell_Techno(int x, int y) const 200 { 201 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 202 203 ObjectClass * object; 204 COORDINATE click; // Coordinate of click relative to cell corner. 205 TechnoClass * close = NULL; 206 long distance = 0; // Recorded closest distance. 207 208 /* 209 ** Create a coordinate value that represent the pixel location within the cell. This is 210 ** actually the lower significant bits (leptons) of a regular coordinate value. 211 */ 212 click = XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y)); 213 214 if (Cell_Occupier()) { 215 object = Cell_Occupier(); 216 while (object && object->IsActive) { 217 if (object->Is_Techno()) { 218 COORDINATE coord = Coord_Fraction(object->Center_Coord()); 219 long dist = Distance(coord, click); 220 if (!close || dist < distance) { 221 close = (TechnoClass *)object; 222 distance = dist; 223 } 224 } 225 object = object->Next; 226 } 227 } 228 return(close); 229 } 230 231 232 /*************************************************************************** 233 * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * 234 * * 235 * INPUT: RTTIType the RTTI type we are searching for * 236 * * 237 * OUTPUT: none * 238 * * 239 * WARNINGS: none * 240 * * 241 * HISTORY: * 242 * 03/17/1995 PWG : Created. * 243 * 06/12/1995 JLB : Returns object class pointer. * 244 *=========================================================================*/ 245 ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const 246 { 247 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 248 assert(rtti != RTTI_NONE); 249 250 ObjectClass * object = Cell_Occupier(); 251 252 while (object != NULL && object->IsActive) { 253 if (object->What_Am_I() == rtti) { 254 return(object); 255 } 256 object = object->Next; 257 } 258 return(NULL); 259 } 260 261 262 /*********************************************************************************************** 263 * CellClass::Cell_Building -- Return with building at specified cell. * 264 * * 265 * Given a cell, determine if there is a building associated * 266 * and return with a pointer to this building. * 267 * * 268 * INPUT: none * 269 * * 270 * OUTPUT: Returns with a pointer to the building associated with the * 271 * cell. If there is no building associated, then NULL is * 272 * returned. * 273 * * 274 * WARNINGS: none * 275 * * 276 * HISTORY: * 277 * 08/05/1992 JLB : Created. * 278 * 04/30/1994 JLB : Converted to member function. * 279 *=============================================================================================*/ 280 BuildingClass * CellClass::Cell_Building(void) const 281 { 282 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 283 284 return((BuildingClass *)Cell_Find_Object(RTTI_BUILDING)); 285 } 286 287 288 /*********************************************************************************************** 289 * CellClass::Cell_Terrain -- Determines terrain object in cell. * 290 * * 291 * This routine is used to determine the terrain object (if any) that * 292 * overlaps this cell. * 293 * * 294 * INPUT: none * 295 * * 296 * OUTPUT: Returns with a pointer to the terrain object that overlaps * 297 * this cell. If there is no terrain object present, then NULL * 298 * is returned. * 299 * * 300 * WARNINGS: none * 301 * * 302 * HISTORY: * 303 * 05/18/1994 JLB : Created. * 304 *=============================================================================================*/ 305 TerrainClass * CellClass::Cell_Terrain(void) const 306 { 307 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 308 309 return((TerrainClass *)Cell_Find_Object(RTTI_TERRAIN)); 310 } 311 312 313 /*********************************************************************************************** 314 * CellClass::Cell_Object -- Returns with clickable object in cell. * 315 * * 316 * This routine is used to determine which object is to be selected * 317 * by a player click upon the cell. Not all objects that overlap the * 318 * cell are selectable by the player. This routine sorts out which * 319 * is which and returns with the appropriate object pointer. * 320 * * 321 * INPUT: x,y -- Coordinate (from upper left corner of cell) to use as a guide when * 322 * selecting the object within the cell. This plays a role in those cases * 323 * where several objects (such as infantry) exist within the same cell. * 324 * * 325 * OUTPUT: Returns with pointer to the object clickable within the * 326 * cell. NULL is returned if there is no clickable object * 327 * present. * 328 * * 329 * WARNINGS: none * 330 * * 331 * HISTORY: * 332 * 05/13/1994 JLB : Created. * 333 *=============================================================================================*/ 334 ObjectClass * CellClass::Cell_Object(int x, int y) const 335 { 336 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 337 338 ObjectClass * ptr; 339 340 /* 341 ** Hack so that aircraft landed on helipads can still be selected if directly 342 ** clicked on. 343 */ 344 ptr = (ObjectClass *)Cell_Find_Object(RTTI_AIRCRAFT); 345 if (ptr) { 346 return(ptr); 347 } 348 349 ptr = Cell_Techno(x, y); 350 if (ptr) { 351 return(ptr); 352 } 353 ptr = Cell_Terrain(); 354 if (ptr) return(ptr); 355 return(ptr); 356 } 357 358 359 /*********************************************************************************************** 360 * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * 361 * * 362 * This is a low level routine that marks all objects that overlap this * 363 * cell to be redrawn. It is necessary to call this routine whenever * 364 * the underlying icon has to be redrawn. * 365 * * 366 * INPUT: forced -- Should this redraw be forced even if flags * 367 * indicate that it would be redundant? * 368 * * 369 * OUTPUT: none * 370 * * 371 * WARNINGS: none * 372 * * 373 * HISTORY: * 374 * 05/18/1994 JLB : Created. * 375 * 06/20/1994 JLB : Simplified to use object pointers. * 376 * 12/24/1994 JLB : Only checks if cell is in view and not flagged already. * 377 *=============================================================================================*/ 378 void CellClass::Redraw_Objects(bool forced) 379 { 380 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 381 382 CELL cell = Cell_Number(); 383 384 if (Map.In_View(cell) && (forced || !Map.Is_Cell_Flagged(cell))) { 385 386 /* 387 ** Flag the icon to be redrawn. 388 */ 389 Map.Flag_Cell(cell); 390 391 /* 392 ** Flag the main object in the cell to be redrawn. 393 */ 394 if (Cell_Occupier() != NULL) { 395 ObjectClass * optr = Cell_Occupier(); 396 while (optr != NULL && optr->IsActive) { 397 398 #ifdef SORTDRAW 399 if (optr->Is_Techno() && ((TechnoClass *)optr)->Visual_Character() != VISUAL_NORMAL) { 400 optr->Mark(MARK_CHANGE); 401 } 402 #else 403 optr->Mark(MARK_CHANGE); 404 #endif 405 if (optr->Next != NULL && !optr->Next->IsActive) { 406 optr->Next = NULL; 407 } 408 optr = optr->Next; 409 } 410 } 411 412 #ifdef SORTDRAW 413 /* 414 ** Flag any overlapping object in this cell to be redrawn. 415 */ 416 for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { 417 if (Overlapper[index]) { 418 assert(Overlapper[index]->IsActive); 419 if (Overlapper[index]->Is_Techno() && ((TechnoClass *)Overlapper[index])->Visual_Character() != VISUAL_NORMAL) { 420 Overlapper[index]->Mark(MARK_CHANGE); 421 } 422 } 423 } 424 #else 425 /* 426 ** Flag any overlapping object in this cell to be redrawn. 427 */ 428 for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { 429 if (Overlapper[index] != NULL) { 430 if (!Overlapper[index]->IsActive) { 431 Overlapper[index] = NULL; 432 } else { 433 Overlapper[index]->Mark(MARK_CHANGE); 434 } 435 } 436 } 437 #endif 438 } 439 } 440 441 442 /*********************************************************************************************** 443 * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. * 444 * * 445 * This determines if the cell can become a proper foundation for * 446 * building placement. * 447 * * 448 * INPUT: loco -- The locomotion of the object trying to consider if this cell is * 449 * generally clear. Buildings use the value of SPEED_NONE. * 450 * * 451 * OUTPUT: bool; Is this cell generally clear (usually for building purposes)? * 452 * * 453 * WARNINGS: none * 454 * * 455 * HISTORY: * 456 * 05/18/1994 JLB : Created. * 457 * 06/25/1996 JLB : Handles different locomotion types. * 458 * 10/05/1996 JLB : Checks for crushable walls and crushable object. * 459 *=============================================================================================*/ 460 bool CellClass::Is_Clear_To_Build(SpeedType loco) const 461 { 462 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 463 464 /* 465 ** During scenario initialization, passability is always guaranteed. 466 */ 467 if (ScenarioInit) return(true); 468 469 /* 470 ** If there is an object there, then don't allow building. 471 */ 472 if (Cell_Object() != NULL) { 473 return(false); 474 } 475 476 /* 477 ** Prevents a building from being placed over a flag object. 478 */ 479 #ifdef FIXIT_FLAG_CHECK 480 if (IsFlagged) { 481 return(false); 482 } 483 #endif 484 485 /* 486 ** Walls are always considered to block the terrain for general passability 487 ** purposes. In normal game mode, all overlays are not buildable. 488 */ 489 if (Overlay != OVERLAY_NONE && (Overlay == OVERLAY_FLAG_SPOT || !Debug_Map || OverlayTypeClass::As_Reference(Overlay).IsWall)) { 490 return(false); 491 } 492 493 /* 494 ** Building over a bib is not allowed. 495 */ 496 if (Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Smudge).IsBib /* && Owner != HOUSE_NONE*/) { 497 return(false); 498 } 499 500 /* 501 ** Building on certain kinds of terrain is prohibited -- bridges in particular. 502 ** If the locomotion type is SPEED_NONE, then this check is presumed to be 503 ** for the purposes of building. 504 */ 505 if (loco == SPEED_NONE) { 506 if (Is_Bridge_Here()) { 507 return(false); 508 } 509 510 return(::Ground[Land_Type()].Build); 511 512 } else { 513 514 if (::Ground[Land_Type()].Cost[loco] == fixed(0)) { 515 // if (::Ground[Land_Type()].Cost[SPEED_TRACK] == fixed(0)) { 516 return(false); 517 } 518 return(true); 519 } 520 } 521 522 523 /*********************************************************************************************** 524 * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * 525 * * 526 * This routine recalculates the ground type in the cell. The speeds the find path * 527 * algorithm and other determinations of the cell type. * 528 * * 529 * INPUT: none * 530 * * 531 * OUTPUT: none * 532 * * 533 * WARNINGS: none * 534 * * 535 * HISTORY: * 536 * 05/29/1994 JLB : Created. * 537 * 06/20/1994 JLB : Knows about template pointer in cell object. * 538 *=============================================================================================*/ 539 void CellClass::Recalc_Attributes(void) 540 { 541 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 542 543 /* 544 ** Special override for interior terrain set so that a non-template or a clear template 545 ** is equivalent to impassable rock. 546 */ 547 if (LastTheater == THEATER_INTERIOR) { 548 if (TType == TEMPLATE_NONE || TType == TEMPLATE_CLEAR1) { 549 Land = LAND_ROCK; 550 return; 551 } 552 } 553 554 /* 555 ** Check for wall effects. 556 */ 557 if (Overlay != OVERLAY_NONE) { 558 Land = OverlayTypeClass::As_Reference(Overlay).Land; 559 if (Land != LAND_CLEAR) return; 560 } 561 562 /* 563 ** If there is a template associated with this cell, then fetch the 564 ** land type given the template type and icon number. 565 */ 566 if (TType != TEMPLATE_NONE && TType != 255) { 567 TemplateTypeClass const * ttype = &TemplateTypeClass::As_Reference(TType); 568 Land = ttype->Land_Type(TIcon); 569 return; 570 } 571 572 /* 573 ** No template is the same as clear terrain. 574 */ 575 Land = LAND_CLEAR; 576 } 577 578 579 /*********************************************************************************************** 580 * CellClass::Occupy_Down -- Flag occupation of specified cell. * 581 * * 582 * This routine is used to mark the cell as being occupied by the specified object. * 583 * * 584 * INPUT: object -- The object that is to occupy the cell * 585 * * 586 * OUTPUT: none * 587 * * 588 * WARNINGS: none * 589 * * 590 * HISTORY: * 591 * 07/18/1994 JLB : Created. * 592 * 11/29/1994 JLB : Simplified. * 593 *=============================================================================================*/ 594 void CellClass::Occupy_Down(ObjectClass * object) 595 { 596 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 597 assert(object != NULL && object->IsActive); 598 599 ObjectClass * optr; 600 601 if (object == NULL) return; 602 603 /* 604 ** Always add buildings to the end of the occupation chain. This is necessary because 605 ** the occupation chain is a single list even though buildings occupy more than one 606 ** cell. If more than one building is allowed to occupy the same cell, then this chain 607 ** logic will fail. 608 */ 609 if (object->What_Am_I() == RTTI_BUILDING && Cell_Occupier()) { 610 optr = Cell_Occupier(); 611 while (optr->Next != NULL) { 612 assert(optr != object); 613 assert(optr->What_Am_I() != RTTI_BUILDING); 614 optr = optr->Next; 615 } 616 optr->Next = object; 617 object->Next = 0; 618 } else { 619 object->Next = Cell_Occupier(); 620 OccupierPtr = object; 621 } 622 Map.Radar_Pixel(Cell_Number()); 623 624 /* 625 ** If being placed down on a visible square, then flag this 626 ** techno object as being revealed to the player. 627 */ 628 // Changes for client/server multiplayer. ST - 8/2/2019 2:51PM 629 //if (IsMapped || Session.Type != GAME_NORMAL) { 630 // object->Revealed(PlayerPtr); 631 //} 632 if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { 633 if (IsMapped || Session.Type != GAME_NORMAL) { 634 object->Revealed(PlayerPtr); 635 } 636 } else { 637 638 for (int i = 0; i < Session.Players.Count(); i++) { 639 HousesType house_type = Session.Players[i]->Player.ID; 640 if (Is_Visible(house_type)) { 641 HouseClass *house = HouseClass::As_Pointer(house_type); 642 object->Revealed(house); 643 } 644 } 645 } 646 647 /* 648 ** Special occupy bit set. 649 */ 650 switch (object->What_Am_I()) { 651 case RTTI_BUILDING: 652 Flag.Occupy.Building = true; 653 break; 654 655 case RTTI_VESSEL: 656 case RTTI_AIRCRAFT: 657 case RTTI_UNIT: 658 Flag.Occupy.Vehicle = true; 659 break; 660 661 case RTTI_TERRAIN: 662 Flag.Occupy.Monolith = true; 663 break; 664 665 default: 666 break; 667 } 668 } 669 670 671 /*********************************************************************************************** 672 * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * 673 * * 674 * This routine will lift the object from the cell and free the cell to be occupied by * 675 * another object. Only if the cell was previously marked with the object specified, will * 676 * the object be lifted off. This routine is the counterpart to Occupy_Down(). * 677 * * 678 * INPUT: object -- The object that is being lifted off. * 679 * * 680 * OUTPUT: none * 681 * * 682 * WARNINGS: none * 683 * * 684 * HISTORY: * 685 * 07/18/1994 JLB : Created. * 686 * 11/29/1994 JLB : Fixed to handle next pointer in previous object. * 687 *=============================================================================================*/ 688 void CellClass::Occupy_Up(ObjectClass * object) 689 { 690 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 691 assert(object != NULL && object->IsActive); 692 693 if (object == NULL) return; 694 695 ObjectClass * optr = Cell_Occupier(); // Working pointer to the objects in the chain. 696 697 if (optr == object) { 698 OccupierPtr = object->Next; 699 object->Next = 0; 700 } else { 701 bool found = false; 702 while (optr != NULL) { 703 if (optr->Next == object) { 704 optr->Next = object->Next; 705 object->Next = 0; 706 found = true; 707 break; 708 } 709 optr = optr->Next; 710 } 711 // assert(found); 712 } 713 Map.Radar_Pixel(Cell_Number()); 714 715 /* 716 ** Special occupy bit clear. 717 */ 718 switch (object->What_Am_I()) { 719 case RTTI_BUILDING: 720 Flag.Occupy.Building = false; 721 break; 722 723 case RTTI_VESSEL: 724 case RTTI_AIRCRAFT: 725 case RTTI_UNIT: 726 Flag.Occupy.Vehicle = false; 727 break; 728 729 case RTTI_TERRAIN: 730 Flag.Occupy.Monolith = false; 731 break; 732 733 default: 734 break; 735 } 736 } 737 738 739 /*********************************************************************************************** 740 * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla* 741 * * 742 * Most game objects can often have their graphic imagery spill into more than one cell * 743 * even though they are considered to "occupy" only one cell. All cells overlapped are * 744 * flagged by this routine. Using this information it is possible to keep the tactical map * 745 * display correct. * 746 * * 747 * INPUT: object -- The object to mark as overlapping this cell. * 748 * * 749 * OUTPUT: none * 750 * * 751 * WARNINGS: none * 752 * * 753 * HISTORY: * 754 * 07/18/1994 JLB : Created. * 755 * 07/04/1995 JLB : Ensures that buildings are always marked down. * 756 *=============================================================================================*/ 757 void CellClass::Overlap_Down(ObjectClass * object) 758 { 759 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 760 assert(object != NULL && object->IsActive); 761 762 ObjectClass ** ptr = 0; 763 764 if (!object) return; 765 766 int index; 767 for (index = 0; index < ARRAY_SIZE(Overlapper); index++) { 768 if (Overlapper[index] == object) return; 769 if (!Overlapper[index]) ptr = &Overlapper[index]; 770 } 771 772 /* 773 ** Buildings must ALWAYS succeed in marking the cell as overlapped. Bump somebody 774 ** else out in this case. 775 */ 776 if (!ptr && object->What_Am_I() == RTTI_BUILDING) { 777 for (index = 0; index < ARRAY_SIZE(Overlapper); index++) { 778 switch (Overlapper[index]->What_Am_I()) { 779 case RTTI_BUILDING: 780 case RTTI_TERRAIN: 781 break; 782 783 default: 784 Overlapper[index] = object; 785 index = ARRAY_SIZE(Overlapper); 786 break; 787 } 788 } 789 } 790 if (ptr) *ptr = object; 791 792 /* 793 ** If being placed down on a visible square, then flag this 794 ** techno object as being revealed to the player. 795 */ 796 if (IsMapped) { 797 object->Revealed(PlayerPtr); 798 } 799 } 800 801 802 /*********************************************************************************************** 803 * CellClass::Overlap_Up -- Removes overlap flag for the cell. * 804 * * 805 * This is the counterpart to Overlap_Down and is used to remove the overlap flag for the * 806 * specified unit on the cell. * 807 * * 808 * INPUT: object -- The object to remove the overlap flag for. * 809 * * 810 * OUTPUT: none * 811 * * 812 * WARNINGS: none * 813 * * 814 * HISTORY: * 815 * 07/18/1994 JLB : Created. * 816 *=============================================================================================*/ 817 void CellClass::Overlap_Up(ObjectClass * object) 818 { 819 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 820 assert(object != NULL && object->IsActive); 821 822 for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { 823 if (Overlapper[index] == object) { 824 Overlapper[index] = 0; 825 break; 826 } 827 } 828 } 829 830 831 /*********************************************************************************************** 832 * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * 833 * * 834 * This routine will determine if a unit is occupying the cell and if so, return a pointer * 835 * to it. If there is no unit occupying the cell, then NULL is returned. * 836 * * 837 * INPUT: none * 838 * * 839 * OUTPUT: Returns with pointer to unit occupying cell, else NULL. * 840 * * 841 * WARNINGS: none * 842 * * 843 * HISTORY: * 844 * 07/18/1994 JLB : Created. * 845 *=============================================================================================*/ 846 UnitClass * CellClass::Cell_Unit(void) const 847 { 848 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 849 850 return((UnitClass*)Cell_Find_Object(RTTI_UNIT)); 851 } 852 853 854 /*********************************************************************************************** 855 * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. * 856 * * 857 * Call this routine to query and return a pointer to a vessel located in the cell. If * 858 * there is no vessel present, then this routine will return NULL. * 859 * * 860 * INPUT: none * 861 * * 862 * OUTPUT: Returns with a pointer to the vessel class object if one is present. * 863 * * 864 * WARNINGS: none * 865 * * 866 * HISTORY: * 867 * 05/20/1996 JLB : Created. * 868 *=============================================================================================*/ 869 VesselClass * CellClass::Cell_Vessel(void) const 870 { 871 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 872 873 return((VesselClass*)Cell_Find_Object(RTTI_VESSEL)); 874 } 875 876 877 /*********************************************************************************************** 878 * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit occupying the cell. * 879 * * 880 * This routine examines the cell and returns a pointer to the first infantry unit * 881 * that occupies it. If there is no infantry unit in the cell, then NULL is returned. * 882 * * 883 * INPUT: none * 884 * * 885 * OUTPUT: Returns with pointer to infantry unit occupying the cell or NULL if none are * 886 * present. * 887 * * 888 * WARNINGS: none * 889 * * 890 * HISTORY: * 891 * 12/21/1994 JLB : Created. * 892 *=============================================================================================*/ 893 InfantryClass * CellClass::Cell_Infantry(void) const 894 { 895 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 896 897 return((InfantryClass*)Cell_Find_Object(RTTI_INFANTRY)); 898 } 899 900 901 #ifdef SORTDRAW 902 static bool _Calc_Partial_Window(int cellx, int celly, int & drawx, int & drawy) 903 { 904 int & px = WindowList[WINDOW_PARTIAL][WINDOWX]; 905 int & py = WindowList[WINDOW_PARTIAL][WINDOWY]; 906 int & pw = WindowList[WINDOW_PARTIAL][WINDOWWIDTH]; 907 int & ph = WindowList[WINDOW_PARTIAL][WINDOWHEIGHT]; 908 int & tx = WindowList[WINDOW_TACTICAL][WINDOWX]; 909 int & ty = WindowList[WINDOW_TACTICAL][WINDOWY]; 910 int & tw = WindowList[WINDOW_TACTICAL][WINDOWWIDTH]; 911 int & th = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT]; 912 913 px = cellx + tx; 914 py = celly + ty; 915 pw = CELL_PIXEL_W; 916 ph = CELL_PIXEL_H; 917 918 if (px < tx) { 919 pw -= tx - px; 920 px = tx; 921 } 922 if (pw < 1) return(false); 923 924 if (py < ty) { 925 ph -= ty - py; 926 py = ty; 927 } 928 if (ph < 1) return(false); 929 930 if (px + pw > tx + tw) { 931 pw -= (px + pw) - (tx + tw); 932 } 933 if (pw < 1) return(false); 934 935 if (py + ph > ty + th) { 936 ph -= (py + ph) - (ty + th); 937 } 938 if (ph < 1) return(false); 939 940 drawx = drawx - (px-tx); 941 drawy = drawy - (py-ty); 942 return(true); 943 } 944 945 946 static int _ocompare(const void * left, const void * right) 947 { 948 COORDINATE lcoord = (*((ObjectClass **)left))->Sort_Y(); 949 COORDINATE rcoord = (*((ObjectClass **)right))->Sort_Y(); 950 if (lcoord < rcoord) return(-1); 951 if (lcoord > rcoord) return(1); 952 return(0); 953 } 954 #endif 955 956 957 /*********************************************************************************************** 958 * CellClass::Get_Template_Info -- Get some info about a template for external use * 959 * * 960 * * 961 * * 962 * * 963 * INPUT: Ref to info required * 964 * * 965 * OUTPUT: True if image info available * 966 * * 967 * * 968 * WARNINGS: none * 969 * * 970 * HISTORY: * 971 * 1/10/2019 5:57PM ST : Created. * 972 *=============================================================================================*/ 973 bool CellClass::Get_Template_Info(char *template_name, int &icon, void *&image_data) 974 { 975 TemplateTypeClass const *ttype = NULL; 976 977 if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) { // Not sure why it's checking for 255 here since that's a valid tile type. ST - 6/4/2019 978 ttype = &TemplateTypeClass::As_Reference(TType); 979 icon = TIcon; 980 } 981 else { 982 ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); 983 icon = Clear_Icon(); 984 } 985 986 if (ttype) { 987 988 strcpy(template_name, ttype->IniName); 989 image_data = (void*)ttype->ImageData; 990 991 return true; 992 } 993 994 return false; 995 } 996 997 998 999 /*********************************************************************************************** 1000 * CellClass::Draw_It -- Draws the cell imagery at the location specified. * 1001 * * 1002 * This is the gruntwork cell rendering code. It draws the cell at the screen location * 1003 * specified. This routine doesn't draw any overlapping or occupying units. It only * 1004 * deals with the ground (cell) layer -- icon level. * 1005 * * 1006 * INPUT: x,y -- The screen coordinates to render the cell imagery at. * 1007 * * 1008 * OUTPUT: none * 1009 * * 1010 * WARNINGS: none * 1011 * * 1012 * HISTORY: * 1013 * 07/18/1994 JLB : Created. * 1014 * 08/21/1994 JLB : Revised for simple template objects. * 1015 * 11/01/1994 BRR : Updated placement cursor; draws actual object * 1016 * 11/14/1994 BRR : Added remapping code to show passable areas * 1017 * 12/02/1994 BRR : Added trigger display * 1018 * 12/11/1994 JLB : Mixes up clear terrain through pseudo-random table. * 1019 * 04/25/1995 JLB : Smudges drawn BELOW overlays. * 1020 * 07/22/1996 JLB : Objects added to draw process. * 1021 *=============================================================================================*/ 1022 void CellClass::Draw_It(int x, int y, bool objects) const 1023 { 1024 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 1025 1026 if (!objects) { 1027 BStart(BENCH_CELL); 1028 1029 TemplateTypeClass const * ttype = 0; 1030 int icon; // The icon number to use from the template set. 1031 CELL cell = Cell_Number(); 1032 void * remap = NULL; 1033 #ifdef SCENARIO_EDITOR 1034 TemplateTypeClass * tptr; 1035 // TriggerClass * trig; 1036 int i; 1037 char waypt[3]; 1038 #endif 1039 1040 CellCount++; 1041 1042 /* 1043 ** Fetch a pointer to the template type associated with this cell. 1044 */ 1045 if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) { 1046 ttype = &TemplateTypeClass::As_Reference(TType); 1047 icon = TIcon; 1048 } else { 1049 ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); 1050 icon = Clear_Icon(); 1051 } 1052 1053 #ifdef CHEAT_KEYS 1054 /* 1055 ** Draw the stamp of the template. 1056 */ 1057 if (Debug_Icon) { 1058 LogicPage->Fill_Rect(Map.TacPixelX+x, Map.TacPixelY+y, Map.TacPixelX+x+ICON_PIXEL_W-1, Map.TacPixelY+y+ICON_PIXEL_H-1, Sim_Random_Pick(1, 254)); 1059 FontXSpacing -= 2; 1060 Fancy_Text_Print("%02X%02X\r%d%d%d\r%d %d", Map.TacPixelX+x+(ICON_PIXEL_W>>1), Map.TacPixelY+y, &GreyScheme, TBLACK, TPF_EFNT|TPF_CENTER|TPF_BRIGHT_COLOR|TPF_FULLSHADOW, 1061 Cell_Y(cell), Cell_X(cell), 1062 //(CurrentObject.Count() && CurrentObject[0]->Is_Techno()) ? ((TechnoClass *)CurrentObject[0])->House->Which_Zone(cell) : -1, 1063 Zones[MZONE_NORMAL],Zones[MZONE_CRUSHER],Zones[MZONE_DESTROYER], 1064 Overlay, OverlayData 1065 ); 1066 FontXSpacing += 2; 1067 } else { 1068 #endif 1069 1070 #ifdef SCENARIO_EDITOR 1071 /* 1072 ** Set up the remap table for this icon. 1073 */ 1074 if (Debug_Map && Debug_Passable) { 1075 if (::Ground[Land].Cost[0] == 0 || (Cell_Occupier() != NULL && 1076 Cell_Occupier()->What_Am_I() != RTTI_INFANTRY)) { // impassable 1077 remap = DisplayClass::FadingRed; 1078 } else { 1079 if (::Ground[Land].Cost[0] > fixed(1, 3)) { // pretty passable 1080 remap = DisplayClass::FadingGreen; 1081 } else { 1082 remap = DisplayClass::FadingYellow; // moderately passable 1083 } 1084 } 1085 } 1086 #endif 1087 1088 /* 1089 ** This is the underlying terrain icon. 1090 */ 1091 if (ttype->Get_Image_Data()) { 1092 LogicPage->Draw_Stamp(ttype->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL); 1093 if (remap) { 1094 LogicPage->Remap(x+Map.TacPixelX, y+Map.TacPixelY, ICON_PIXEL_W, ICON_PIXEL_H, remap); 1095 } 1096 } 1097 1098 #ifdef SCENARIO_EDITOR 1099 /* 1100 ** Draw the map editor's "current" cell. This is the cell that can be 1101 ** assigned attributes such as tag labels. 1102 ** This must be draw before the placement cursor, but after drawing the 1103 ** objects in the cell. 1104 */ 1105 if (Debug_Map && CurrentCell == Cell_Number()) { 1106 LogicPage->Draw_Rect(x+Map.TacPixelX, y+Map.TacPixelY, Map.TacPixelX + x + CELL_PIXEL_W - 1, Map.TacPixelY + y + CELL_PIXEL_H - 1, YELLOW); 1107 } 1108 #endif 1109 1110 /* 1111 ** Redraw any smudge. 1112 */ 1113 if (Smudge != SMUDGE_NONE) { 1114 SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData); 1115 } 1116 1117 /* 1118 ** Draw the overlay object. 1119 */ 1120 if (Overlay != OVERLAY_NONE) { 1121 OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); 1122 IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific 1123 CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, DisplayClass::UnitShadow); 1124 IsTheaterShape = false; 1125 } 1126 1127 #ifdef SCENARIO_EDITOR 1128 if (Debug_Map) { 1129 /* 1130 ** Draw the cell's Trigger mnemonic, if it has a trigger 1131 */ 1132 if (Trigger.Is_Valid()) { 1133 Fancy_Text_Print(Trigger->Class->IniName, x+Map.TacPixelX, y+Map.TacPixelY, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_EFNT|TPF_FULLSHADOW); 1134 } 1135 1136 /* 1137 ** Draw the cell's Waypoint designation if there is one. 1138 */ 1139 if (IsWaypoint) { 1140 for (i = 0; i < WAYPT_HOME; i++) { 1141 if (Scen.Waypoint[i] == Cell_Number()) { 1142 if (i < 26) { 1143 waypt[0] = 'A' + i; 1144 waypt[1] = 0; 1145 } else { 1146 waypt[0] = 'A' + (i/26)-1; 1147 waypt[1] = 'A' + (i % 26); 1148 waypt[2] = 0; 1149 } 1150 Fancy_Text_Print(waypt, Map.TacPixelX + x + CELL_PIXEL_W / 2, 1151 Map.TacPixelY + y + (CELL_PIXEL_H / 2) - 3, 1152 &ColorRemaps[PCOLOR_RED], TBLACK, 1153 TPF_EFNT | TPF_CENTER|TPF_FULLSHADOW); 1154 break; 1155 } 1156 } 1157 if (Scen.Waypoint[WAYPT_HOME] == Cell_Number()) { 1158 Fancy_Text_Print("Home", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, 1159 &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW); 1160 } 1161 if (Scen.Waypoint[WAYPT_REINF] == Cell_Number()) { 1162 Fancy_Text_Print("Reinf", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, 1163 &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW); 1164 } 1165 } 1166 } 1167 #endif 1168 1169 /* 1170 ** Draw the placement cursor: 1171 ** - First, draw the hash-mark cursor, so it will appear underneath 1172 ** any cursor being drawn 1173 ** - If the PendingObject is a template, overlay, or smudge, draw it 1174 ** - Otherwise, it's up to the Display.Refresh_Map() routine to draw it 1175 */ 1176 if (IsCursorHere) { 1177 SpeedType loco = SPEED_NONE; 1178 if (Map.PendingObjectPtr) { 1179 if (Map.PendingObjectPtr->What_Am_I() == RTTI_BUILDING) { 1180 BuildingClass * obj = (BuildingClass *)(Map.PendingObjectPtr); 1181 loco = obj->Class->Speed; 1182 // if (*obj == STRUCT_SUB_PEN || *obj == STRUCT_SHIP_YARD || 1183 // *obj == STRUCT_FAKE_PEN || *obj == STRUCT_FAKE_YARD) loco = SPEED_FLOAT; 1184 } 1185 } 1186 1187 /* 1188 ** Draw the hash-mark cursor: 1189 */ 1190 if (Map.ProximityCheck && Is_Clear_To_Build(loco)) { 1191 LogicPage->Draw_Stamp(DisplayClass::TransIconset, 0, x, y, NULL, WINDOW_TACTICAL); 1192 } else { 1193 LogicPage->Draw_Stamp(DisplayClass::TransIconset, 2, x, y, NULL, WINDOW_TACTICAL); 1194 } 1195 1196 #ifdef SCENARIO_EDITOR 1197 if (Debug_Map && Map.PendingObject) { 1198 1199 switch (Map.PendingObject->What_Am_I()) { 1200 1201 /* 1202 ** Draw a template: 1203 ** - Compute the icon offset of this cell for this template, using 1204 ** ZoneCell+ZoneOffset to get the upper-left corner of the placement 1205 ** cursor 1206 ** - Draw the icon 1207 */ 1208 case RTTI_TEMPLATETYPE: 1209 tptr = (TemplateTypeClass *)Map.PendingObject; 1210 if (tptr->Get_Image_Data()) { 1211 icon = (Cell_X(cell) - Cell_X(Map.ZoneCell + Map.ZoneOffset)) + 1212 (Cell_Y(cell) - Cell_Y(Map.ZoneCell + Map.ZoneOffset)) * 1213 tptr->Width; 1214 LogicPage->Draw_Stamp(tptr->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL); 1215 } 1216 break; 1217 1218 /* 1219 ** Draw an overlay; just use the existing 'OverlayData' even though 1220 ** it means nothing. 1221 */ 1222 case RTTI_OVERLAYTYPE: 1223 OverlayTypeClass::As_Reference(((OverlayTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, OverlayData); 1224 break; 1225 1226 /* 1227 ** Draw a smudge 1228 */ 1229 case RTTI_SMUDGETYPE: 1230 SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, 0); 1231 break; 1232 1233 default: 1234 break; 1235 } 1236 } 1237 #endif 1238 } 1239 1240 #ifdef CHEAT_KEYS 1241 } 1242 #endif 1243 BEnd(BENCH_CELL); 1244 } 1245 1246 #ifdef SORTDRAW 1247 if (objects) { 1248 BStart(BENCH_OBJECTS); 1249 1250 /* 1251 ** Build a list of objects to draw into a working buffer. There is a 1252 ** big presumption here -- it is presumed that if the cell is to be 1253 ** redrawn, then all objects in the cell should properly be flagged to 1254 ** be redrawn as well. Normally, this isn't a problem, but for subs 1255 ** the IsToDisplay flag MUST REMAIN SET. This is because there is a 1256 ** hack overpass after the cells are redrawn so that subs can be 1257 ** redrawn separately. 1258 */ 1259 static DynamicVectorClass<ObjectClass*> optr(20 + ARRAY_SIZE(Overlapper)); 1260 optr.Delete_All(); 1261 ObjectClass * object = Cell_Occupier(); 1262 while (object != NULL) { 1263 if (!object->IsActive) break; 1264 optr.Add(object); 1265 object->IsToDisplay = true; 1266 object = object->Next; 1267 } 1268 for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { 1269 object = Overlapper[index]; 1270 if (object != NULL && object->IsActive) { 1271 object->IsToDisplay = true; 1272 optr.Add(object); 1273 } 1274 } 1275 1276 /* 1277 ** Sort the object list so that objects will be drawn from 1278 ** back to front. 1279 */ 1280 switch (optr.Count()) { 1281 1282 /* 1283 ** If there are zero or one object, then sorting is 1284 ** unnecessary. 1285 */ 1286 case 0: 1287 case 1: 1288 break; 1289 1290 /* 1291 ** Two objects can be sorted with a single compare and swap. 1292 */ 1293 case 2: 1294 if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) { 1295 swap(optr[0], optr[1]); 1296 } 1297 break; 1298 1299 /* 1300 ** Three objects can be sorted with three compares and swaps. 1301 */ 1302 case 3: 1303 if (optr[0]->Sort_Y() > optr[2]->Sort_Y()) { 1304 swap(optr[0], optr[2]); 1305 } 1306 if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) { 1307 swap(optr[0], optr[1]); 1308 } 1309 if (optr[1]->Sort_Y() > optr[2]->Sort_Y()) { 1310 swap(optr[1], optr[2]); 1311 } 1312 break; 1313 1314 /* 1315 ** Large number of objects can be effeciently sorted by using 1316 ** a quicksort. 1317 */ 1318 default: 1319 qsort(&optr[0], optr.Count(), sizeof(ObjectClass*), _ocompare); 1320 break; 1321 } 1322 1323 /* 1324 ** Draw any objects that happen to be in or overlapping this cell. 1325 */ 1326 for (int index = 0; index < optr.Count(); index++) { 1327 object = optr[index]; 1328 int xx,yy; 1329 if (object->IsToDisplay && (!object->Is_Techno() || ((TechnoClass *)object)->Visual_Character() == VISUAL_NORMAL) && Map.Coord_To_Pixel(object->Render_Coord(), xx, yy)) { 1330 if (_Calc_Partial_Window(x, y, xx, yy)) { 1331 object->Draw_It(xx, yy, WINDOW_PARTIAL); 1332 if (Debug_Map) { 1333 object->IsToDisplay = true; 1334 } else { 1335 object->IsToDisplay = false; 1336 } 1337 } 1338 object->IsToDisplay = false; 1339 } 1340 } 1341 BEnd(BENCH_OBJECTS); 1342 } 1343 #endif 1344 1345 } 1346 1347 1348 /*********************************************************************************************** 1349 * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * 1350 * * 1351 * This routine examines the cells around the current one and from this, determines what * 1352 * concrete icon shape to use (if any). The cell data is adjusted and the cell is marked * 1353 * for redraw if the icon changed. * 1354 * * 1355 * INPUT: none * 1356 * * 1357 * OUTPUT: none * 1358 * * 1359 * WARNINGS: none * 1360 * * 1361 * HISTORY: * 1362 * 08/01/1994 JLB : Created. * 1363 *=============================================================================================*/ 1364 void CellClass::Concrete_Calc(void) 1365 { 1366 #ifdef OBSOLETE 1367 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 1368 1369 static FacingType _even[5] = {FACING_N, FACING_S, FACING_SW, FACING_W, FACING_NW}; 1370 static FacingType _odd[5] = {FACING_N, FACING_NE, FACING_E, FACING_SE, FACING_S}; 1371 FacingType * ptr; // Working pointer into adjacent cell list. 1372 int index; // Constructed bit index. 1373 int icon; // Icon number. 1374 bool isodd; // Is this for the odd column? 1375 1376 #define OF_N 0x01 1377 #define OF_NE 0x02 1378 #define OF_E 0x04 1379 #define OF_SE 0x08 1380 #define OF_S 0x10 1381 1382 #define EF_N 0x01 1383 #define EF_NW 0x10 1384 #define EF_W 0x08 1385 #define EF_SW 0x04 1386 #define EF_S 0x02 1387 1388 /* 1389 ** Determine if the even or odd row logic is necessary. 1390 */ 1391 isodd = ((Cell_Number() & 0x01) != 0); 1392 1393 /* 1394 ** Fetch correct pointer depending on whether this is for an 1395 ** odd or even row. 1396 */ 1397 ptr = (isodd) ? _odd : _even; 1398 1399 /* 1400 ** Build an index according to the presence of concrete in the special 1401 ** adjacent cells. This is a short list of adjacent cell flags since 1402 ** only 5 adjacent cells need to be examined. The choice of which 5 1403 ** depends on whether this is for an even or odd column. 1404 */ 1405 index = 0; 1406 for (int i = 0; i < (sizeof(_even)/sizeof(_even[0])); i++) { 1407 CellClass & cellptr = Adjacent_Cell(*ptr++); 1408 1409 if (cellptr.Overlay == OVERLAY_CONCRETE) { 1410 index |= (1<<i); 1411 } 1412 } 1413 1414 /* 1415 ** Special logic occurs for cells that are concrete filled. 1416 */ 1417 if (Overlay == OVERLAY_CONCRETE) { 1418 1419 /* 1420 ** Process the index value and place the appropriate concrete icon 1421 ** in the cell. 1422 */ 1423 if (isodd) { 1424 switch (index) { 1425 case OF_NE: 1426 case OF_N|OF_NE: 1427 case OF_E|OF_N: 1428 case OF_E|OF_NE: 1429 case OF_N|OF_NE|OF_E: 1430 case OF_S|OF_N|OF_NE: 1431 icon = C_RIGHT_UP; // right - up 1432 break; 1433 1434 case OF_SE: 1435 case OF_E|OF_SE: 1436 case OF_S|OF_SE: 1437 case OF_S|OF_E: 1438 case OF_S|OF_SE|OF_E: 1439 case OF_S|OF_SE|OF_N: 1440 icon = C_RIGHT_DOWN; // right - down 1441 break; 1442 1443 case OF_SE|OF_NE: 1444 case OF_SE|OF_NE|OF_N: 1445 case OF_SE|OF_NE|OF_S: 1446 case OF_SE|OF_NE|OF_S|OF_N: 1447 case OF_SE|OF_E|OF_N: 1448 case OF_SE|OF_E|OF_NE|OF_N: 1449 case OF_S|OF_E|OF_N: 1450 case OF_S|OF_E|OF_NE: 1451 case OF_S|OF_E|OF_NE|OF_N: 1452 case OF_S|OF_SE|OF_E|OF_N: 1453 case OF_S|OF_SE|OF_E|OF_NE|OF_N: 1454 case OF_S|OF_SE|OF_E|OF_NE: 1455 icon = C_RIGHT_UPDOWN; // right - up - down 1456 break; 1457 1458 default: 1459 icon = C_RIGHT; // right 1460 break; 1461 } 1462 } else { 1463 switch (index) { 1464 case EF_NW: 1465 case EF_NW|EF_N: 1466 case EF_W|EF_N: 1467 case EF_NW|EF_W|EF_N: 1468 case EF_NW|EF_W: 1469 case EF_NW|EF_S|EF_N: 1470 icon = C_LEFT_UP; // left - up 1471 break; 1472 1473 case EF_SW: 1474 case EF_SW|EF_S: 1475 case EF_W|EF_S: 1476 case EF_W|EF_SW|EF_S: 1477 case EF_W|EF_SW: 1478 case EF_SW|EF_S|EF_N: 1479 icon = C_LEFT_DOWN; // left - down 1480 break; 1481 1482 case EF_NW|EF_SW: 1483 case EF_NW|EF_SW|EF_N: 1484 case EF_NW|EF_SW|EF_S: 1485 case EF_NW|EF_SW|EF_S|EF_N: 1486 case EF_W|EF_S|EF_N: 1487 case EF_W|EF_SW|EF_N: 1488 case EF_W|EF_SW|EF_S|EF_N: 1489 case EF_NW|EF_W|EF_S: 1490 case EF_NW|EF_W|EF_S|EF_N: 1491 case EF_NW|EF_W|EF_SW|EF_S|EF_N: 1492 case EF_NW|EF_W|EF_SW|EF_N: 1493 case EF_NW|EF_W|EF_SW|EF_S: 1494 icon = C_LEFT_UPDOWN; // left - up - down 1495 break; 1496 1497 default: 1498 icon = C_LEFT; // left 1499 break; 1500 } 1501 } 1502 1503 } else { 1504 1505 // Presume that no concrete piece is needed. 1506 icon = C_NONE; 1507 if (isodd) { 1508 index &= ~(OF_NE|OF_SE); // Ignore diagonals. 1509 switch (index) { 1510 case OF_N|OF_E: 1511 icon = C_UP_RIGHT; // up right 1512 break; 1513 1514 case OF_E|OF_S: 1515 icon = C_DOWN_RIGHT; // down right 1516 break; 1517 1518 case OF_N|OF_E|OF_S: 1519 icon = C_UPDOWN_RIGHT; // up/down right 1520 break; 1521 1522 default: 1523 break; 1524 } 1525 } else { 1526 index &= ~(EF_NW|EF_SW); // Ignore diagonals. 1527 switch (index) { 1528 case EF_N|EF_W: 1529 icon = C_UP_LEFT; // up left 1530 break; 1531 1532 case EF_W|EF_S: 1533 icon = C_DOWN_LEFT; // down left 1534 break; 1535 1536 case EF_N|EF_W|EF_S: 1537 icon = C_UPDOWN_LEFT; // up/down left 1538 break; 1539 1540 default: 1541 break; 1542 } 1543 } 1544 1545 /* 1546 ** If any kind of fixup piece is needed, then add concrete 1547 ** to this location RECURSIVELY! 1548 */ 1549 if (icon != C_NONE) { 1550 OverlayTypeClass::As_Reference(OVERLAY_CONCRETE).Create_And_Place(Cell_Number()); 1551 icon = C_NONE; 1552 } 1553 1554 } 1555 1556 /* 1557 ** Update the icon on the map. 1558 */ 1559 if (icon != C_NONE && OverlayData != icon) { 1560 OverlayData = icon; 1561 //Array[cell].Base = 0; 1562 Redraw_Objects(); 1563 } 1564 #endif 1565 } 1566 1567 1568 /*********************************************************************************************** 1569 * CellClass::Wall_Update -- Updates the imagery for wall objects in cell. * 1570 * * 1571 * This routine will examine the cell and the adjacent cells to determine what the wall * 1572 * should look like with the cell. It will then update the wall's imagery value and flag * 1573 * the cell to be redrawn if necessary. This routine should be called whenever the wall * 1574 * or an adjacent wall is created or destroyed. * 1575 * * 1576 * INPUT: none * 1577 * * 1578 * OUTPUT: none * 1579 * * 1580 * WARNINGS: none * 1581 * * 1582 * HISTORY: * 1583 * 09/19/1994 JLB : Created. * 1584 * 09/19/1994 BWG : Updated to handle partially-damaged walls. * 1585 *=============================================================================================*/ 1586 void CellClass::Wall_Update(void) 1587 { 1588 if (Overlay == OVERLAY_NONE) { 1589 return; 1590 } 1591 1592 OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay); 1593 if (!wall.IsWall) { 1594 return; 1595 } 1596 1597 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 1598 1599 static FacingType _offsets[5] = {FACING_N, FACING_E, FACING_S, FACING_W, FACING_NONE}; 1600 1601 for (unsigned index = 0; index < (sizeof(_offsets)/sizeof(_offsets[0])); index++) { 1602 CellClass * newcell = Adjacent_Cell(_offsets[index]); 1603 1604 if (newcell && newcell->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(newcell->Overlay).IsWall) { 1605 int icon = 0; 1606 1607 /* 1608 ** Build the icon number according to walls located in the adjacent 1609 ** cells. 1610 */ 1611 for (unsigned i = 0; i < 4; i++) { 1612 CellClass * adjcell = newcell->Adjacent_Cell(_offsets[i]); 1613 if (adjcell && adjcell->Overlay == newcell->Overlay) { 1614 icon |= 1 << i; 1615 } 1616 } 1617 newcell->OverlayData = (newcell->OverlayData & 0xFFF0) | icon; 1618 1619 /* 1620 ** Handle special cases for the incomplete damaged wall sets. If a damage stage 1621 ** is calculated, but there is no artwork for it, then consider the wall to be 1622 ** completely destroyed. 1623 */ 1624 if (newcell->Overlay == OVERLAY_BRICK_WALL && newcell->OverlayData == 48) { 1625 newcell->Overlay = OVERLAY_NONE; 1626 newcell->OverlayData = 0; 1627 Detach_This_From_All(::As_Target(newcell->Cell_Number()), true); 1628 } 1629 if (newcell->Overlay == OVERLAY_SANDBAG_WALL && newcell->OverlayData == 16) { 1630 newcell->Overlay = OVERLAY_NONE; 1631 newcell->OverlayData = 0; 1632 Detach_This_From_All(::As_Target(newcell->Cell_Number()), true); 1633 } 1634 if (newcell->Overlay == OVERLAY_CYCLONE_WALL && newcell->OverlayData == 32) { 1635 newcell->Overlay = OVERLAY_NONE; 1636 newcell->OverlayData = 0; 1637 Detach_This_From_All(::As_Target(newcell->Cell_Number()), true); 1638 } 1639 if (newcell->Overlay == OVERLAY_FENCE && (newcell->OverlayData == 16 || newcell->OverlayData == 32)) { 1640 newcell->Overlay = OVERLAY_NONE; 1641 newcell->OverlayData = 0; 1642 Detach_This_From_All(::As_Target(newcell->Cell_Number()), true); 1643 } 1644 if (newcell->Overlay == OVERLAY_BARBWIRE_WALL && newcell->OverlayData == 16) { 1645 newcell->Overlay = OVERLAY_NONE; 1646 newcell->OverlayData = 0; 1647 Detach_This_From_All(::As_Target(newcell->Cell_Number()), true); 1648 } 1649 1650 newcell->Recalc_Attributes(); 1651 newcell->Redraw_Objects(); 1652 } 1653 } 1654 } 1655 1656 1657 /*********************************************************************************************** 1658 * CellClass::Cell_Coord -- Returns the coordinate of this cell. * 1659 * * 1660 * This support function will determine the coordinate of this cell and return it. * 1661 * * 1662 * INPUT: none * 1663 * * 1664 * OUTPUT: Returns with coordinate value of cell. * 1665 * * 1666 * WARNINGS: none * 1667 * * 1668 * HISTORY: * 1669 * 09/19/1994 JLB : Created. * 1670 *=============================================================================================*/ 1671 COORDINATE CellClass::Cell_Coord(void) const 1672 { 1673 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 1674 1675 return(::Cell_Coord(Cell_Number())); 1676 } 1677 1678 1679 /*********************************************************************************************** 1680 * CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. * 1681 * * 1682 * This routine will lower the tiberium level in the cell. It is used by the harvesting * 1683 * process as well as by combat damage to the tiberium fields. * 1684 * * 1685 * INPUT: levels -- The number of levels to reduce the tiberium. * 1686 * * 1687 * OUTPUT: bool; Was the tiberium level reduced by at least one level? * 1688 * * 1689 * WARNINGS: none * 1690 * * 1691 * HISTORY: * 1692 * 09/19/1994 JLB : Created. * 1693 *=============================================================================================*/ 1694 int CellClass::Reduce_Tiberium(int levels) 1695 { 1696 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 1697 1698 int reducer = 0; 1699 1700 if (levels > 0 && Land == LAND_TIBERIUM) { 1701 if (OverlayData+1 > levels) { 1702 OverlayData -= levels; 1703 reducer = levels; 1704 } else { 1705 Overlay = OVERLAY_NONE; 1706 reducer = OverlayData; 1707 OverlayData = 0; 1708 Recalc_Attributes(); 1709 } 1710 } 1711 return(reducer); 1712 } 1713 1714 1715 /*********************************************************************************************** 1716 * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * 1717 * * 1718 * This routine will change the wall shape used for a wall if it's damaged. * 1719 * * 1720 * INPUT: damage -- The number of damage points the wall was hit with. If this value is * 1721 * -1, then the entire wall at this cell will be destroyed. * 1722 * * 1723 * OUTPUT: bool; Was the wall destroyed? * 1724 * * 1725 * WARNINGS: none * 1726 * * 1727 * HISTORY: * 1728 * 03/15/1995 BWG : Created. * 1729 * 03/19/1995 JLB : Updates cell information if wall was destroyed. * 1730 * 10/06/1996 JLB : Updates zone as necessary. * 1731 *=============================================================================================*/ 1732 int CellClass::Reduce_Wall(int damage) 1733 { 1734 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 1735 1736 if (Overlay != OVERLAY_NONE) { 1737 bool destroyed = false; 1738 OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay); 1739 1740 if (wall.IsWall) { 1741 1742 /* 1743 ** If the damage was great enough to ensure wall destruction, reduce the wall by one 1744 ** level (no more). Otherwise determine wall reduction based on a percentage chance 1745 ** proportional to the damage received and the wall's strength. 1746 */ 1747 if (damage == -1 || damage >= wall.DamagePoints) { 1748 destroyed = true; 1749 } else { 1750 destroyed = Random_Pick(0, wall.DamagePoints) < damage; 1751 } 1752 1753 /* 1754 ** If the wall is destroyed, destroy it and check for any adjustments to 1755 ** adjacent walls. 1756 */ 1757 if (destroyed) { 1758 OverlayData+=16; 1759 if (damage == -1 || 1760 (OverlayData>>4) >= wall.DamageLevels || 1761 ((OverlayData>>4) == wall.DamageLevels-1 && (OverlayData & 0xF)==0) ) { 1762 1763 Owner = HOUSE_NONE; 1764 Overlay = OVERLAY_NONE; 1765 OverlayData = 0; 1766 Recalc_Attributes(); 1767 Redraw_Objects(); 1768 CellClass * ncell = Adjacent_Cell(FACING_N); 1769 if (ncell) ncell->Wall_Update(); 1770 CellClass * wcell = Adjacent_Cell(FACING_W); 1771 if (wcell) wcell->Wall_Update(); 1772 CellClass * scell = Adjacent_Cell(FACING_S); 1773 if (scell) scell->Wall_Update(); 1774 CellClass * ecell = Adjacent_Cell(FACING_E); 1775 if (ecell) ecell->Wall_Update(); 1776 Detach_This_From_All(As_Target()); 1777 1778 /* 1779 ** The zone calculation changes now for non-crushable zone sensitive 1780 ** travellers. 1781 */ 1782 if (wall.IsCrushable) { 1783 Map.Zone_Reset(MZONEF_NORMAL); 1784 } else { 1785 Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL); 1786 } 1787 return(true); 1788 } 1789 } 1790 } 1791 } 1792 return(false); 1793 } 1794 1795 1796 /*********************************************************************************************** 1797 * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE * 1798 * * 1799 * INPUT: * 1800 * coord COORDINATE to compute index for * 1801 * * 1802 * OUTPUT: * 1803 * index into StoppingCoord that's closest to this coord * 1804 * * 1805 * WARNINGS: * 1806 * none. * 1807 * * 1808 * HISTORY: * 1809 * 11/21/1994 BR : Created. * 1810 * 12/10/1994 JLB : Uses alternate sub-position algorithm. * 1811 *=============================================================================================*/ 1812 int CellClass::Spot_Index(COORDINATE coord) 1813 { 1814 COORDINATE rel = Coord_Fraction(coord); // Sub coordinate value within cell. 1815 1816 /* 1817 ** If the coordinate is close enough to the center of the cell, then return 1818 ** the center position index. 1819 */ 1820 if (Distance(rel, (COORDINATE)0x00800080L) < 60) { 1821 return(0); 1822 } 1823 1824 /* 1825 ** Since the center cell position has been eliminated, a simple comparison 1826 ** as related to the center of the cell can be used to determine the sub 1827 ** position. Take advantage of the fact that the sub positions are organized 1828 ** from left to right, top to bottom. 1829 */ 1830 int index = 0; 1831 if (Coord_X(rel) > 0x80) index |= 0x01; 1832 if (Coord_Y(rel) > 0x80) index |= 0x02; 1833 return(index+1); 1834 } 1835 1836 1837 /*********************************************************************************************** 1838 * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * 1839 * * 1840 * Similar to the CellClass::Free_Spot; this routine finds the spot in * 1841 * the cell closest to the given coordinate, and returns the COORDINATE of * 1842 * that spot if it's available, NULL if it's not. * 1843 * * 1844 * INPUT: * 1845 * coord coordinate to check (only sub cell position examined) * 1846 * * 1847 * any -- If only the closest spot is desired regardless of whether it is free or * 1848 * not, then this parameter will be true. * 1849 * * 1850 * OUTPUT: * 1851 * COORDINATE of free spot, NULL if none. The coordinate return value does not alter the cell * 1852 * coordinate data portions of the coordinate passed in. Only the lower sub-cell * 1853 * data is altered. * 1854 * * 1855 * WARNINGS: * 1856 * none. * 1857 * * 1858 * HISTORY: * 1859 * 11/08/1994 BR : Created. * 1860 * 12/10/1994 JLB : Picks best of closest stopping positions. * 1861 * 12/21/1994 JLB : Adds a mix-up factor if center location is occupied. * 1862 *=============================================================================================*/ 1863 COORDINATE CellClass::Closest_Free_Spot(COORDINATE coord, bool any) const 1864 { 1865 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 1866 1867 int spot_index = Spot_Index(coord); 1868 1869 /* 1870 ** This precalculated sequence table records the closest spots to any given spot. Sequential 1871 ** examination of these spots for availability ensures that the closest available one is 1872 ** discovered first. 1873 */ 1874 static unsigned char _sequence[5][4] = { 1875 {1,2,3,4}, 1876 {0,2,3,4}, 1877 {0,1,4,3}, 1878 {0,1,4,2}, 1879 {0,2,3,1} 1880 }; 1881 1882 /* 1883 ** In the case of the center coordinate being requested, but is occupied, then all other 1884 ** sublocations are equidistant. Instead of picking a static sequence of examination, the 1885 ** order is mixed up by way of this table. 1886 */ 1887 static unsigned char _alternate[4][4] = { 1888 {1,2,3,4}, 1889 {2,3,4,1}, 1890 {3,4,1,2}, 1891 {4,1,2,3}, 1892 }; 1893 coord = Coord_Whole(coord); 1894 1895 /* 1896 ** Cells occupied by buildings or vehicles don't have any free spots. 1897 */ 1898 if (!any && (Flag.Occupy.Vehicle || Flag.Occupy.Monolith)) { 1899 return(NULL); 1900 } 1901 1902 /* 1903 ** If just the nearest position is desired regardless of whether occupied or not, 1904 ** then just return with the stopping coordinate value. 1905 */ 1906 if (any || Is_Spot_Free(spot_index)) { 1907 return(Coord_Add(coord, StoppingCoordAbs[spot_index])); 1908 } 1909 1910 /* 1911 ** Scan through all available sub-locations in the cell in order to determine 1912 ** the closest one to the coordinate requested. Use precalculated table so that 1913 ** when the first free position is found, bail. 1914 */ 1915 unsigned char * sequence; 1916 if (spot_index == 0) { 1917 sequence = &_alternate[Random_Pick(0, 3)][0]; 1918 } else { 1919 sequence = &_sequence[spot_index][0]; 1920 } 1921 for (int index = 0; index < 4; index++) { 1922 int pos = *sequence++; 1923 1924 if (Is_Spot_Free(pos)) { 1925 return(Coord_Add(coord, StoppingCoordAbs[pos])); 1926 } 1927 } 1928 1929 /* 1930 ** No free spot could be found so return a NULL coordinate. 1931 */ 1932 return(0x00000000L); 1933 } 1934 1935 1936 /*********************************************************************************************** 1937 * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * 1938 * * 1939 * This support routine will determine what the clear icon number would be for the cell. * 1940 * The icon number is determined by converting the cell number into an index into a * 1941 * lookup table. This yields what appears to be a randomized map without the necessity of * 1942 * generating and recording randomized map numbers. * 1943 * * 1944 * INPUT: none * 1945 * * 1946 * OUTPUT: Returns with the icon number for clear terrain if it were displayed at the * 1947 * cell. * 1948 * * 1949 * WARNINGS: none * 1950 * * 1951 * HISTORY: * 1952 * 12/26/1994 JLB : Created. * 1953 * 06/09/1995 JLB : Uses 16 entry scramble algorithm. * 1954 *=============================================================================================*/ 1955 int CellClass::Clear_Icon(void) const 1956 { 1957 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 1958 1959 CELL cell = Cell_Number(); 1960 return((Cell_X(cell) & 0x03) | ((Cell_Y(cell) & 0x03) << 2)); 1961 // return((cell & 0x03) | ((unsigned(cell)>>5) & 0x0C)); 1962 } 1963 1964 1965 /*********************************************************************************************** 1966 * CellClass::Incoming -- Causes objects in cell to "run for cover". * 1967 * * 1968 * This routine is called whenever a great, but slow moving, threat is presented to the * 1969 * occupants of a cell. The occupants will, in most cases, stop what they are doing and * 1970 * try to get out of the way. * 1971 * * 1972 * INPUT: threat -- The coordinate source of the threat. * 1973 * * 1974 * forced -- If this threat is so major that the occupants should stop what * 1975 * they are doing, then this parameter should be set to true. * 1976 * * 1977 * nokidding -- Override the scatter to also affect human controlled objects. * 1978 * * 1979 * OUTPUT: none * 1980 * * 1981 * WARNINGS: none * 1982 * * 1983 * HISTORY: * 1984 * 01/10/1995 JLB : Created. * 1985 * 08/02/1996 JLB : Added the "nokidding" parameter. * 1986 *=============================================================================================*/ 1987 void CellClass::Incoming(COORDINATE threat, bool forced, bool nokidding) 1988 { 1989 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 1990 1991 ObjectClass * object = NULL; 1992 1993 object = Cell_Occupier(); 1994 while (object != NULL) { 1995 1996 /* 1997 ** Special check to make sure that friendly units never scatter. 1998 */ 1999 if (nokidding || Rule.IsScatter || (object->Is_Techno() && ((TechnoClass *)object)->House->IQ >= Rule.IQScatter)) { 2000 object->Scatter(threat, forced, nokidding); 2001 } 2002 object = object->Next; 2003 } 2004 } 2005 2006 2007 /*********************************************************************************************** 2008 * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * 2009 * * 2010 * Use this routine to return a reference to the adjacent cell in the direction specified. * 2011 * * 2012 * INPUT: face -- The direction to use when determining the adjacent cell. * 2013 * * 2014 * OUTPUT: Returns with a reference to the adjacent cell. * 2015 * * 2016 * WARNINGS: If the facing value is invalid, then a reference to the same cell is returned. * 2017 * * 2018 * HISTORY: * 2019 * 03/19/1995 JLB : Created. * 2020 *=============================================================================================*/ 2021 CellClass const * CellClass::Adjacent_Cell(FacingType face) const 2022 { 2023 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 2024 2025 if (face == FACING_NONE) { 2026 return(this); 2027 } 2028 2029 if ((unsigned)face >= FACING_COUNT) { 2030 return(NULL); 2031 } 2032 2033 CELL newcell = ::Adjacent_Cell(Cell_Number(), face); 2034 if ((unsigned)newcell >= MAP_CELL_TOTAL) { 2035 return(NULL); 2036 } 2037 2038 return &Map[newcell]; 2039 } 2040 2041 2042 /*************************************************************************** 2043 * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * 2044 * * 2045 * INPUT: * 2046 * * 2047 * OUTPUT: * 2048 * * 2049 * WARNINGS: * 2050 * * 2051 * HISTORY: * 2052 * 04/24/1995 PWG : Created. * 2053 *=========================================================================*/ 2054 void CellClass::Adjust_Threat(HousesType house, int threat_value) 2055 { 2056 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 2057 2058 int region = Map.Cell_Region(Cell_Number()); 2059 2060 for (HousesType lp = HOUSE_FIRST; lp < HOUSE_COUNT; lp ++) { 2061 if (lp == house) continue; 2062 2063 HouseClass * house_ptr = HouseClass::As_Pointer(lp); 2064 if (house_ptr && (!house_ptr->IsHuman || !house_ptr->Is_Ally(house))) { 2065 house_ptr->Adjust_Threat(region, threat_value); 2066 } 2067 } 2068 if (Debug_Threat) { 2069 Map.Flag_To_Redraw(true); 2070 } 2071 } 2072 2073 2074 /*********************************************************************************************** 2075 * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smoothing purposes. * 2076 * * 2077 * This routine will adjust the level of the Tiberium in the cell so that it will * 2078 * smoothly blend with the adjacent Tiberium. This routine should only be called for * 2079 * new Tiberium cells. Existing cells that contain Tiberium follow a different growth * 2080 * pattern. * 2081 * * 2082 * INPUT: pregame -- Is this a pregame call? Such a call will mixup the Tiberium overlay * 2083 * used. * 2084 * * 2085 * OUTPUT: Returns with the added Tiberium value that is now available for harvesting. * 2086 * * 2087 * WARNINGS: The return value is only valid for the initial placement. Tiberium growth will * 2088 * increase the net worth of the existing Tiberium. * 2089 * * 2090 * HISTORY: * 2091 * 05/16/1995 JLB : Created. * 2092 * 02/20/1996 JLB : Takes into account the ore type. * 2093 *=============================================================================================*/ 2094 long CellClass::Tiberium_Adjust(bool pregame) 2095 { 2096 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 2097 if (Overlay != OVERLAY_NONE) { 2098 if (OverlayTypeClass::As_Reference(Overlay).Land == LAND_TIBERIUM) { 2099 static int _adj[9] = {0,1,3,4,6,7,8,10,11}; 2100 static int _adjgem[9] = {0,0,0,1,1,1,2,2,2}; 2101 int count = 0; 2102 2103 /* 2104 ** Mixup the Tiberium overlays so that they don't look the same. 2105 ** Since the type of ore is known, also record the nominal 2106 ** value per step of that ore type. 2107 */ 2108 bool gems = false; 2109 int value = 0; 2110 if (pregame) { 2111 switch (Overlay) { 2112 case OVERLAY_GOLD1: 2113 case OVERLAY_GOLD2: 2114 case OVERLAY_GOLD3: 2115 case OVERLAY_GOLD4: 2116 value = Rule.GoldValue; 2117 Overlay = Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4); 2118 break; 2119 2120 case OVERLAY_GEMS1: 2121 case OVERLAY_GEMS2: 2122 case OVERLAY_GEMS3: 2123 case OVERLAY_GEMS4: 2124 gems = true; 2125 value = Rule.GemValue*4; 2126 Overlay = Random_Pick(OVERLAY_GEMS1, OVERLAY_GEMS4); 2127 break; 2128 2129 default: 2130 break; 2131 } 2132 } 2133 2134 /* 2135 ** Add up all adjacent cells that contain tiberium. 2136 ** (Skip those cells which aren't on the map) 2137 */ 2138 for (FacingType face = FACING_FIRST; face < FACING_COUNT; face++) { 2139 if ((unsigned)::Adjacent_Cell(Cell_Number(), face) >= MAP_CELL_TOTAL) continue; 2140 CellClass * adj = Adjacent_Cell(face); 2141 2142 if (adj && adj->Overlay != OVERLAY_NONE && 2143 OverlayTypeClass::As_Reference(adj->Overlay).Land == LAND_TIBERIUM) { 2144 count++; 2145 } 2146 } 2147 2148 if (gems) { 2149 OverlayData = _adjgem[count]; 2150 OverlayData = min(OverlayData, 2); 2151 } else { 2152 OverlayData = _adj[count]; 2153 } 2154 return((OverlayData+1) * value); 2155 } 2156 } 2157 return(0); 2158 } 2159 2160 extern bool MPSuperWeaponDisable; 2161 2162 /*********************************************************************************************** 2163 * CellClass::Goodie_Check -- Performs crate discovery logic. * 2164 * * 2165 * Call this routine whenever an object enters a cell. It will check for the existence * 2166 * of a crate and generate any "goodie" it might contain. * 2167 * * 2168 * INPUT: object -- Pointer to the object that is triggering this crate. * 2169 * * 2170 * OUTPUT: Can the object continue to enter this cell? A false return value means that the * 2171 * cell is now occupied and must not be entered. * 2172 * * 2173 * WARNINGS: none * 2174 * * 2175 * HISTORY: * 2176 * 05/22/1995 JLB : Created. * 2177 * 07/08/1995 JLB : Added a bunch of goodies to the crates. * 2178 * 06/17/1996 JLB : Revamped for Red Alert * 2179 *=============================================================================================*/ 2180 bool CellClass::Goodie_Check(FootClass * object) 2181 { 2182 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 2183 2184 if (object != NULL && Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Overlay).IsCrate) { 2185 bool force_mcv = false; 2186 int force_money = 0; 2187 int damage; 2188 COORDINATE coord; 2189 2190 /* 2191 ** Determine the total number of shares for all the crate powerups. This is used as 2192 ** the base pool to determine the odds from. 2193 */ 2194 int total_shares = 0; 2195 for (int index = CRATE_FIRST; index < CRATE_COUNT; index++) { 2196 total_shares += CrateShares[index]; 2197 } 2198 2199 /* 2200 ** Pick a random crate powerup according to the shares allotted to each powerup. 2201 ** In solo play, the bonus item is dependant upon the rules control. 2202 */ 2203 CrateType powerup; 2204 if (Session.Type == GAME_NORMAL) { 2205 2206 /* 2207 ** Solo play has money amount determined by rules.ini file. 2208 */ 2209 force_money = Rule.SoloCrateMoney; 2210 2211 if (Overlay == OVERLAY_STEEL_CRATE) { 2212 powerup = Rule.SilverCrate; 2213 } 2214 2215 if (Overlay == OVERLAY_WOOD_CRATE) { 2216 powerup = Rule.WoodCrate; 2217 } 2218 2219 if (Overlay == OVERLAY_WATER_CRATE) { 2220 //Mono_Printf("%d-%s.\n", __LINE__, __FILE__); 2221 powerup = Rule.WaterCrate; 2222 } 2223 2224 } else { 2225 int pick = Random_Pick(1, total_shares); 2226 2227 int share_count = 0; 2228 for (powerup = CRATE_FIRST; powerup < CRATE_COUNT; powerup++) { 2229 share_count += CrateShares[powerup]; 2230 if (pick <= share_count) break; 2231 } 2232 assert(powerup != CRATE_COUNT); 2233 2234 /* 2235 ** Depending on what was picked, there might be an alternate goodie if the selected 2236 ** goodie would have no effect. 2237 */ 2238 switch (powerup) { 2239 case CRATE_UNIT: 2240 if (object->House->CurUnits > 50) powerup = CRATE_MONEY; 2241 break; 2242 2243 case CRATE_SQUAD: 2244 if (object->House->CurInfantry > 100) powerup = CRATE_MONEY; 2245 break; 2246 2247 case CRATE_DARKNESS: 2248 if (object->House->IsGPSActive) powerup = CRATE_MONEY; 2249 break; 2250 2251 case CRATE_ARMOR: 2252 if (object->ArmorBias != 1) powerup = CRATE_MONEY; 2253 break; 2254 2255 case CRATE_SPEED: 2256 if (object->SpeedBias != 1 || object->What_Am_I() == RTTI_AIRCRAFT) powerup = CRATE_MONEY; 2257 break; 2258 2259 case CRATE_FIREPOWER: 2260 if (object->FirepowerBias != 1 || !object->Is_Weapon_Equipped()) powerup = CRATE_MONEY; 2261 break; 2262 2263 case CRATE_REVEAL: 2264 if (object->House->IsVisionary) { 2265 if (object->House->IsGPSActive) { 2266 powerup = CRATE_MONEY; 2267 } else { 2268 powerup = CRATE_DARKNESS; 2269 } 2270 } 2271 break; 2272 2273 case CRATE_CLOAK: 2274 if (object->IsCloakable) powerup = CRATE_MONEY; 2275 break; 2276 2277 // case CRATE_HEAL_BASE: 2278 // if (object->House->BScan == 0) powerup = CRATE_UNIT; 2279 2280 case CRATE_MONEY: 2281 break; 2282 2283 case CRATE_ICBM: 2284 case CRATE_PARA_BOMB: 2285 case CRATE_SONAR: 2286 if (Session.Type != GAME_NORMAL) { 2287 if (MPSuperWeaponDisable) { 2288 powerup = CRATE_MONEY; 2289 } 2290 } 2291 break; 2292 2293 case CRATE_TIMEQUAKE: 2294 /* 2295 ** For the time quake crate, scan through and count up all the 2296 ** units (and infantry and ships and aircraft) and if either 2297 ** side has very few, allow the time quake. Otherwise, 2298 ** change the crate to money or something. Only do this for 2299 ** multiplay - for solo play, they get what they get. First, 2300 ** check for time - the chance for getting a time quake crate 2301 ** should be very very low when they first start the mission, 2302 ** but as time goes on the chance goes up. 2303 */ 2304 if (Session.Type != GAME_NORMAL) { 2305 int i,ucount; 2306 int minunits = 1000; 2307 bool found = false; 2308 unsigned long minutes = (Score.ElapsedTime / TIMER_MINUTE); 2309 if (minutes > 100) minutes = 100; 2310 if (Random_Pick(0,100-(int)minutes) == 0) { 2311 for (i=0; i < (Session.Players.Count() + Session.Options.AIPlayers); i++) { 2312 ucount = 0; 2313 HouseClass * hptr = Houses.Ptr(i + HOUSE_MULTI1); 2314 if (hptr != NULL && !hptr->IsDefeated) { 2315 int j; 2316 for( j=0; j < UNIT_COUNT; j++) { 2317 ucount += hptr->QuantityU(j); 2318 } 2319 for( j=0; j < INFANTRY_COUNT; j++) { 2320 ucount += hptr->QuantityI(j); 2321 } 2322 for( j=0; j < AIRCRAFT_COUNT; j++) { 2323 ucount += hptr->QuantityA(j); 2324 } 2325 for( j=0; j < VESSEL_COUNT; j++) { 2326 ucount += hptr->QuantityV(j); 2327 } 2328 int bcount = 0; 2329 for( j=0; j < STRUCT_COUNT; j++) { 2330 bcount += hptr->QuantityB(j); 2331 } 2332 ucount += bcount/2; // weight buildings less 2333 minunits = min(minunits, ucount); 2334 } 2335 } 2336 if (Random_Pick(0, minunits) == minunits) { 2337 found = true; 2338 } 2339 } 2340 2341 if (!found) { 2342 powerup = CRATE_MONEY; 2343 } 2344 } 2345 break; 2346 } 2347 /* 2348 ** Possibly force it to be an MCV if there is 2349 ** sufficient money and no buildings left. 2350 */ 2351 if ( object->House->BScan == 0 && 2352 object->House->Available_Money() > ( (BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost) * object->House->CostBias) && 2353 Session.Options.Bases && 2354 !(object->House->UScan & UNITF_MCV)) { 2355 powerup = CRATE_UNIT; 2356 force_mcv = true; 2357 } 2358 2359 /* 2360 ** If the powerup is money but there is insufficient money to build a refinery but there is a construction 2361 ** yard available, then force the money to be enough to rebuild the refinery. 2362 */ 2363 if (powerup == CRATE_MONEY && (object->House->BScan & (STRUCTF_CONST|STRUCTF_REFINERY)) == STRUCTF_CONST && 2364 object->House->Available_Money() < BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias) { 2365 2366 force_money = BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias; 2367 } 2368 2369 /* 2370 ** Special override for water crates so that illegal goodies items 2371 ** won't appear. 2372 */ 2373 if (Overlay == OVERLAY_WATER_CRATE) { 2374 switch (powerup) { 2375 case CRATE_UNIT: 2376 case CRATE_SQUAD: 2377 powerup = CRATE_MONEY; 2378 break; 2379 2380 default: 2381 break; 2382 } 2383 } 2384 } 2385 2386 /* 2387 ** Keep track of the number of each type of crate found 2388 */ 2389 if (Session.Type == GAME_INTERNET) { 2390 object->House->TotalCrates->Increment_Unit_Total(powerup); 2391 } 2392 2393 /* 2394 ** Remove the crate from the map. 2395 */ 2396 Map.Remove_Crate(Cell_Number()); 2397 // Map[Cell_Number()].Overlay = OVERLAY_NONE; 2398 2399 if (Session.Type != GAME_NORMAL && Rule.IsMPCrates) { 2400 Map.Place_Random_Crate(); 2401 } 2402 2403 /* 2404 ** Generate any corresponding animation associated with this crate powerup. 2405 */ 2406 if (CrateAnims[powerup] != ANIM_NONE) { 2407 new AnimClass(CrateAnims[powerup], Cell_Coord()); 2408 } 2409 2410 /* 2411 ** Create the effect requested. 2412 */ 2413 bool tospeak = false; 2414 switch (powerup) { 2415 case CRATE_TIMEQUAKE: 2416 TimeQuake = true; 2417 break; 2418 2419 /* 2420 ** Give the player money. 2421 */ 2422 case CRATE_MONEY: 2423 crate_money: 2424 if (force_money > 0) { 2425 object->House->Refund_Money(force_money); 2426 } else { 2427 object->House->Refund_Money(Random_Pick(CrateData[powerup], CrateData[powerup]+900)); 2428 } 2429 break; 2430 2431 /* 2432 ** Shroud the world in blackness. 2433 */ 2434 case CRATE_DARKNESS: 2435 /* 2436 ** Updated for client/server multiplayer. ST - 8/12/2019 11:38AM 2437 */ 2438 if (object->House->IsHuman) { 2439 Map.Shroud_The_Map(object->House); 2440 } 2441 break; 2442 2443 /* 2444 ** Reveal the entire map. 2445 */ 2446 case CRATE_REVEAL: 2447 /* 2448 ** Updated for client/server multiplayer. ST - 8/12/2019 11:38AM 2449 */ 2450 object->House->IsVisionary = true; 2451 if (object->House->IsHuman) { 2452 for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { 2453 Map.Map_Cell(cell, object->House); 2454 } 2455 Map.Flag_To_Redraw(true); 2456 } 2457 break; 2458 2459 /* 2460 ** Try to create a unit where the crate was. 2461 */ 2462 case CRATE_UNIT: { 2463 UnitTypeClass const * utp = NULL; 2464 2465 /* 2466 ** Give the player an MCV if he has no base left but does have more than enough 2467 ** money to rebuild a new base. Of course, if he already has an MCV, then don't 2468 ** give him another one. 2469 */ 2470 if (force_mcv) { 2471 utp = &UnitTypeClass::As_Reference(UNIT_MCV); 2472 } 2473 2474 /* 2475 ** If the player has a base and a refinery, but no harvester, then give him 2476 ** a free one. 2477 */ 2478 if (utp == NULL && (object->House->BScan & STRUCTF_REFINERY) && !(object->House->UScan & UNITF_HARVESTER)) { 2479 utp = &UnitTypeClass::As_Reference(UNIT_HARVESTER); 2480 } 2481 2482 /* 2483 ** Check for special unit type override value. 2484 */ 2485 if (Rule.UnitCrateType != UNIT_NONE) { 2486 utp = &UnitTypeClass::As_Reference(Rule.UnitCrateType); 2487 } 2488 2489 /* 2490 ** If no unit type has been determined, then pick one at random. 2491 */ 2492 while (utp == NULL) { 2493 #ifdef FIXIT_ANTS 2494 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2495 UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_RA_COUNT-1 -3)); 2496 #else 2497 UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1 -3)); 2498 #endif 2499 #else 2500 UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1)); 2501 #endif 2502 if (utype != UNIT_MCV || Session.Options.Bases) { 2503 utp = &UnitTypeClass::As_Reference(utype); 2504 if (utp->IsCrateGoodie && (utp->Ownable & (1 << HouseClass::As_Pointer(object->Owner())->ActLike))) { 2505 break; 2506 } 2507 utp = NULL; 2508 } 2509 } 2510 2511 if (utp != NULL) { 2512 UnitClass * goodie_unit = (UnitClass *)utp->Create_One_Of(object->House); 2513 if (goodie_unit != NULL) { 2514 if (goodie_unit->Unlimbo(Cell_Coord())) { 2515 return(false); 2516 } 2517 2518 /* 2519 ** Try to place the object into a nearby cell if something is preventing 2520 ** placement at the crate location. 2521 */ 2522 CELL cell = Map.Nearby_Location(Cell_Number(), goodie_unit->Class->Speed); 2523 if (goodie_unit->Unlimbo(::Cell_Coord(cell))) { 2524 return(false); 2525 } 2526 delete goodie_unit; 2527 goto crate_money; 2528 } 2529 } 2530 } 2531 break; 2532 2533 /* 2534 ** Create a squad of miscellaneous composition. 2535 */ 2536 case CRATE_SQUAD: 2537 for (int index = 0; index < 5; index++) { 2538 static InfantryType _inf[] = { 2539 INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1, 2540 INFANTRY_E2, 2541 INFANTRY_E3, 2542 INFANTRY_RENOVATOR 2543 }; 2544 if (!InfantryTypeClass::As_Reference(_inf[Random_Pick(0, ARRAY_SIZE(_inf)-1)]).Create_And_Place(Cell_Number(), object->Owner())) { 2545 if (index == 0) { 2546 goto crate_money; 2547 } 2548 } 2549 } 2550 return(false); 2551 2552 /* 2553 ** A one para-bomb mission. 2554 */ 2555 case CRATE_PARA_BOMB: 2556 if (object->House->SuperWeapon[SPC_PARA_BOMB].Enable(true)) { 2557 // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM 2558 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 2559 if (object->House->IsHuman) { 2560 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, object->House); 2561 } 2562 } else { 2563 if (object->IsOwnedByPlayer) { 2564 Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); 2565 Map.Column[1].Flag_To_Redraw(); 2566 } 2567 } 2568 } 2569 break; 2570 2571 /* 2572 ** A one time sonar pulse 2573 */ 2574 case CRATE_SONAR: 2575 if (object->House->SuperWeapon[SPC_SONAR_PULSE].Enable(true)) { 2576 // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM 2577 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 2578 if (object->House->IsHuman) { 2579 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SONAR_PULSE, object->House); 2580 } 2581 } else { 2582 if (object->IsOwnedByPlayer) { 2583 Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE); 2584 Map.Column[1].Flag_To_Redraw(); 2585 } 2586 } 2587 } 2588 break; 2589 2590 /* 2591 ** A group of explosions are triggered around the crate. 2592 */ 2593 case CRATE_EXPLOSION: 2594 if (object != NULL) { 2595 int d = CrateData[powerup]; 2596 object->Take_Damage(d, 0, WARHEAD_HE, 0, true); 2597 } 2598 for (int index = 0; index < 5; index++) { 2599 COORDINATE frag_coord = Coord_Scatter(Cell_Coord(), Random_Pick(0, 0x0200)); 2600 new AnimClass(ANIM_FBALL1, frag_coord); 2601 damage = CrateData[powerup]; 2602 Explosion_Damage(frag_coord, damage, NULL, WARHEAD_HE); 2603 } 2604 break; 2605 2606 /* 2607 ** A napalm blast is triggered. 2608 */ 2609 case CRATE_NAPALM: 2610 coord = Coord_Mid(Cell_Coord(), object->Center_Coord()); 2611 new AnimClass(ANIM_NAPALM3, coord); 2612 if (object != NULL) { 2613 int d = CrateData[powerup]; 2614 object->Take_Damage(d, 0, WARHEAD_FIRE, 0, true); 2615 } 2616 damage = CrateData[powerup]; 2617 Explosion_Damage(coord, damage, NULL, WARHEAD_FIRE); 2618 break; 2619 2620 /* 2621 ** All objects within a certain range will gain the ability to cloak. 2622 */ 2623 case CRATE_CLOAK: 2624 for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { 2625 ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; 2626 2627 if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) { 2628 ((TechnoClass *)obj)->IsCloakable = true; 2629 } 2630 } 2631 break; 2632 2633 /* 2634 ** All of the player's objects heal up. 2635 */ 2636 case CRATE_HEAL_BASE: 2637 if (object->IsOwnedByPlayer) { 2638 Sound_Effect(VOC_HEAL, object->Center_Coord()); 2639 } 2640 for (int index = 0; index < Logic.Count(); index++) { 2641 ObjectClass * obj = Logic[index]; 2642 2643 if (obj && object->Is_Techno() && object->House->Class->House == obj->Owner()) { 2644 obj->Strength = obj->Class_Of().MaxStrength; 2645 } 2646 } 2647 break; 2648 2649 2650 case CRATE_ICBM: 2651 if (object->House->SuperWeapon[SPC_NUCLEAR_BOMB].Enable(true)) { 2652 // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM 2653 if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { 2654 if (object->House->IsHuman) { 2655 Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, object->House); 2656 } 2657 } else { 2658 if (object->IsOwnedByPlayer) { 2659 Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); 2660 Map.Column[1].Flag_To_Redraw(); 2661 } 2662 } 2663 } 2664 break; 2665 2666 case CRATE_ARMOR: 2667 for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { 2668 ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; 2669 2670 if (obj != NULL && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->ArmorBias == 1) { 2671 fixed val = ((TechnoClass *)obj)->ArmorBias * Inverse(fixed(CrateData[powerup], 256)); 2672 ((TechnoClass *)obj)->ArmorBias = val; 2673 if (obj->Owner() == PlayerPtr->Class->House) tospeak = true; 2674 } 2675 } 2676 if (tospeak) Speak(VOX_UPGRADE_ARMOR); 2677 break; 2678 2679 case CRATE_SPEED: 2680 for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { 2681 ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; 2682 2683 if (obj && obj->Is_Foot() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((FootClass *)obj)->SpeedBias == 1 && obj->What_Am_I() != RTTI_AIRCRAFT) { 2684 FootClass * foot = (FootClass *)obj; 2685 2686 fixed val = foot->SpeedBias * fixed(CrateData[powerup], 256); 2687 foot->SpeedBias = val; 2688 if (foot->IsOwnedByPlayer) tospeak = true; 2689 } 2690 } 2691 if (tospeak) Speak(VOX_UPGRADE_SPEED); 2692 break; 2693 2694 case CRATE_FIREPOWER: 2695 for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { 2696 ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; 2697 2698 if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->FirepowerBias == 1) { 2699 2700 fixed val = ((TechnoClass *)obj)->FirepowerBias * fixed(CrateData[powerup], 256); 2701 ((TechnoClass *)obj)->FirepowerBias = val; 2702 if (obj->Owner() == PlayerPtr->Class->House) tospeak = true; 2703 } 2704 } 2705 if (tospeak) Speak(VOX_UPGRADE_FIREPOWER); 2706 break; 2707 2708 case CRATE_INVULN: 2709 for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { 2710 ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; 2711 2712 if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) { 2713 ((TechnoClass *)obj)->IronCurtainCountDown = (TICKS_PER_MINUTE * fixed(CrateData[powerup], 256)); 2714 obj->Mark(MARK_CHANGE); 2715 } 2716 } 2717 break; 2718 2719 /* 2720 ** A chronal vortex appears targetted at the triggering object. 2721 */ 2722 case CRATE_VORTEX: 2723 if ( !ChronalVortex.Is_Active()) { 2724 ChronalVortex.Appear ( Cell_Coord() ); 2725 ChronalVortex.Set_Target ( (ObjectClass*) object ); 2726 Sound_Effect(VOC_TESLA_ZAP, object->Center_Coord()); 2727 } 2728 break; 2729 2730 default: 2731 break; 2732 } 2733 } 2734 return(true); 2735 } 2736 2737 2738 /*********************************************************************************************** 2739 * CellClass::Flag_Place -- Places a house flag down on the cell. * 2740 * * 2741 * This routine will place the house flag at this cell location. * 2742 * * 2743 * INPUT: house -- The house that is having its flag placed here. * 2744 * * 2745 * OUTPUT: Was the flag successfully placed here? * 2746 * * 2747 * WARNINGS: Failure to place means that the cell is impassable for some reason. * 2748 * * 2749 * HISTORY: * 2750 * 05/23/1995 JLB : Created. * 2751 *=============================================================================================*/ 2752 bool CellClass::Flag_Place(HousesType house) 2753 { 2754 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 2755 2756 if (!IsFlagged && Is_Clear_To_Move(SPEED_TRACK, false, false)) { 2757 IsFlagged = true; 2758 Owner = house; 2759 Flag_Update(); 2760 Redraw_Objects(); 2761 return(true); 2762 } 2763 return(false); 2764 } 2765 2766 2767 /*********************************************************************************************** 2768 * CellClass::Flag_Remove -- Removes the house flag from the cell. * 2769 * * 2770 * This routine will free the cell of any house flag that may be located there. * 2771 * * 2772 * INPUT: none * 2773 * * 2774 * OUTPUT: Was there a flag here that was removed? * 2775 * * 2776 * WARNINGS: none * 2777 * * 2778 * HISTORY: * 2779 * 05/23/1995 JLB : Created. * 2780 *=============================================================================================*/ 2781 bool CellClass::Flag_Remove(void) 2782 { 2783 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 2784 2785 if (IsFlagged) { 2786 IsFlagged = false; 2787 Owner = HOUSE_NONE; 2788 Flag_Update(); 2789 Redraw_Objects(); 2790 return(true); 2791 } 2792 return(false); 2793 } 2794 2795 2796 void CellClass::Flag_Update(void) 2797 { 2798 if (IsFlagged && !CTFFlag) { 2799 Flag_Create(); 2800 } else if (!IsFlagged && CTFFlag) { 2801 Flag_Destroy(); 2802 } 2803 } 2804 2805 2806 void CellClass::Flag_Create(void) 2807 { 2808 if (!CTFFlag) { 2809 CTFFlag = new AnimClass(ANIM_FLAG, Cell_Coord()); 2810 if (CTFFlag == NULL) { 2811 for (int i = 0; i < Anims.Count(); ++i) { 2812 AnimClass* anim = Anims.Ptr(i); 2813 if (*anim != ANIM_FLAG) { 2814 delete anim; 2815 break; 2816 } 2817 } 2818 CTFFlag = new AnimClass(ANIM_FLAG, Cell_Coord()); 2819 } 2820 assert(CTFFlag != NULL); 2821 CTFFlag->Set_Owner(Owner); 2822 } 2823 } 2824 2825 2826 void CellClass::Flag_Destroy(void) 2827 { 2828 delete CTFFlag; 2829 CTFFlag = NULL; 2830 } 2831 2832 2833 /*********************************************************************************************** 2834 * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * 2835 * * 2836 * This routine is called when some event would cause a momentary disruption in the * 2837 * cloaking device. All objects that are cloaked in the cell will have their cloaking * 2838 * device shimmer. * 2839 * * 2840 * INPUT: none * 2841 * * 2842 * OUTPUT: none * 2843 * * 2844 * WARNINGS: none * 2845 * * 2846 * HISTORY: * 2847 * 07/29/1995 JLB : Created. * 2848 *=============================================================================================*/ 2849 void CellClass::Shimmer(void) 2850 { 2851 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 2852 2853 ObjectClass * object = Cell_Occupier(); 2854 2855 while (object) { 2856 object->Do_Shimmer(); 2857 object = object->Next; 2858 } 2859 } 2860 2861 2862 /*********************************************************************************************** 2863 * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel * 2864 * * 2865 * This routine is called when determining general passability for purposes of zone * 2866 * calculation. Only blockages that cannot be circumvented are considered to make a cell * 2867 * impassable. All other obstructions can either be destroyed or are temporary. * 2868 * * 2869 * INPUT: loco -- The locomotion type to use when determining passablility. * 2870 * * 2871 * ignoreinfantry -- Should infantry in the cell be ignored for movement purposes? * 2872 * * 2873 * ignorevehicles -- If vehicles should be ignored, then this flag will be true. * 2874 * * 2875 * zone -- If specified, the zone must match this value or else movement is * 2876 * presumed disallowed. * 2877 * * 2878 * check -- This specifies the zone type that this check applies to. * 2879 * * 2880 * OUTPUT: Is the cell generally passable to ground targeting? * 2881 * * 2882 * WARNINGS: none * 2883 * * 2884 * HISTORY: * 2885 * 09/25/1995 JLB : Created. * 2886 * 06/25/1996 JLB : Uses tracked vehicles as a basis for zone check. * 2887 * 10/05/1996 JLB : Allows checking for crushable blockages. * 2888 *=============================================================================================*/ 2889 bool CellClass::Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone, MZoneType check) const 2890 { 2891 assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); 2892 2893 /* 2894 ** Flying objects always consider every cell passable since they can fly over everything. 2895 */ 2896 if (loco == SPEED_WINGED) { 2897 return(true); 2898 } 2899 2900 /* 2901 ** If a zone was specified, then see if the cell is in a legal 2902 ** zone to allow movement. 2903 */ 2904 if (zone != -1) { 2905 if (zone != Zones[check]) { 2906 return(false); 2907 } 2908 } 2909 2910 /* 2911 ** Check the occupy bits for passable legality. If ignore infantry is true, then 2912 ** don't consider infnatry. 2913 */ 2914 int composite = Flag.Composite; 2915 if (ignoreinfantry) { 2916 composite &= 0xE0; // Drop the infantry occupation bits. 2917 } 2918 if (ignorevehicles) { 2919 composite &= 0x5F; // Drop the vehicle/building bit. 2920 } 2921 if (composite != 0) { 2922 return(false); 2923 } 2924 2925 /* 2926 ** Fetch the land type of the cell -- to be modified and used later. 2927 */ 2928 LandType land = Land_Type(); 2929 2930 /* 2931 ** Walls are always considered to block the terrain for general passability 2932 ** purposes unless this is a wall crushing check or if the checking object 2933 ** can destroy walls. 2934 */ 2935 OverlayTypeClass const * overlay = NULL; 2936 if (Overlay != OVERLAY_NONE) { 2937 overlay = &OverlayTypeClass::As_Reference(Overlay); 2938 } 2939 if (overlay != NULL && overlay->IsWall) { 2940 if (check != MZONE_DESTROYER && (check != MZONE_CRUSHER || !overlay->IsCrushable)) { 2941 return(false); 2942 } 2943 2944 /* 2945 ** Crushing objects consider crushable walls as clear rather than the 2946 ** typical LAND_WALL setting. 2947 */ 2948 land = LAND_CLEAR; 2949 } 2950 2951 /* 2952 ** See if the ground type is impassable to this locomotion type and if 2953 ** so, return the error condition. 2954 */ 2955 if (::Ground[land].Cost[loco] == 0) { 2956 return(false); 2957 } 2958 2959 /* 2960 ** All checks passed, so this cell must be passable. 2961 */ 2962 return(true); 2963 } 2964 2965 2966 /*********************************************************************************************** 2967 * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. * 2968 * * 2969 * This routine will examine this cell and if there is a bridge here, it will return * 2970 * true. * 2971 * * 2972 * INPUT: none * 2973 * * 2974 * OUTPUT: bool; Is there a bridge located in this cell? * 2975 * * 2976 * WARNINGS: none * 2977 * * 2978 * HISTORY: * 2979 * 07/30/1996 JLB : Created. * 2980 *=============================================================================================*/ 2981 bool CellClass::Is_Bridge_Here(void) const 2982 { 2983 switch (TType) { 2984 case TEMPLATE_BRIDGE1: 2985 case TEMPLATE_BRIDGE1H: 2986 case TEMPLATE_BRIDGE1D: 2987 case TEMPLATE_BRIDGE2: 2988 case TEMPLATE_BRIDGE2H: 2989 case TEMPLATE_BRIDGE2D: 2990 case TEMPLATE_BRIDGE_1A: 2991 case TEMPLATE_BRIDGE_1B: 2992 case TEMPLATE_BRIDGE_2A: 2993 case TEMPLATE_BRIDGE_2B: 2994 case TEMPLATE_BRIDGE_3A: 2995 case TEMPLATE_BRIDGE_3B: 2996 case TEMPLATE_BRIDGE_3C: 2997 case TEMPLATE_BRIDGE_3D: 2998 case TEMPLATE_BRIDGE_3E: 2999 case TEMPLATE_BRIDGE_3F: 3000 return(true); 3001 } 3002 return(false); 3003 } 3004 3005 3006 /*********************************************************************************************** 3007 * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. * 3008 * * 3009 * This checks the cell to see if Tiberium can grow at least one level in it. Tiberium can * 3010 * grow only if there is Tiberium already present. It can only grow to a certain level * 3011 * and then all further growth is suspended. * 3012 * * 3013 * INPUT: none * 3014 * * 3015 * OUTPUT: bool; Can Tiberium grow in this cell? * 3016 * * 3017 * WARNINGS: none * 3018 * * 3019 * HISTORY: * 3020 * 08/14/1996 JLB : Created. * 3021 *=============================================================================================*/ 3022 bool CellClass::Can_Tiberium_Grow(void) const 3023 { 3024 if (!Rule.IsTGrowth) return(false); 3025 3026 if (Session.Type != GAME_NORMAL) { 3027 if(!Session.Options.Tiberium) return(false); 3028 } 3029 3030 if (Land_Type() != LAND_TIBERIUM) return(false); 3031 3032 if (OverlayData >= 11) return(false); 3033 3034 if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false); 3035 3036 return(true); 3037 } 3038 3039 3040 /*********************************************************************************************** 3041 * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. * 3042 * * 3043 * This routine will examine the cell and determine if there is sufficient Tiberium * 3044 * present that Tiberium spores will spread to adjacent cells. If the Tiberium level is * 3045 * too low, spreading will not occur. * 3046 * * 3047 * INPUT: none * 3048 * * 3049 * OUTPUT: bool; Can Tiberium spread from this cell into adjacent cells? * 3050 * * 3051 * WARNINGS: This routine does not check to see if, in fact, there are any adjacent cells * 3052 * available to spread to. * 3053 * * 3054 * HISTORY: * 3055 * 08/14/1996 JLB : Created. * 3056 *=============================================================================================*/ 3057 bool CellClass::Can_Tiberium_Spread(void) const 3058 { 3059 if (!Rule.IsTSpread) return(false); 3060 3061 if (Session.Type != GAME_NORMAL) { 3062 if(!Session.Options.Tiberium) return(false); 3063 } 3064 3065 if (Land_Type() != LAND_TIBERIUM) return(false); 3066 3067 if (OverlayData <= 6) return(false); 3068 3069 if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false); 3070 3071 return(true); 3072 } 3073 3074 3075 /*********************************************************************************************** 3076 * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. * 3077 * * 3078 * This routine will cause the tiberium to grow in the cell. * 3079 * * 3080 * INPUT: none * 3081 * * 3082 * OUTPUT: bool; Did Tiberium grow in the cell? * 3083 * * 3084 * WARNINGS: none * 3085 * * 3086 * HISTORY: * 3087 * 08/14/1996 JLB : Created. * 3088 *=============================================================================================*/ 3089 bool CellClass::Grow_Tiberium(void) 3090 { 3091 if (Can_Tiberium_Grow()) { 3092 OverlayData++; 3093 Redraw_Objects(); 3094 return(true); 3095 } 3096 return(false); 3097 } 3098 3099 3100 /*********************************************************************************************** 3101 * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. * 3102 * * 3103 * This routine will cause the Tiberium to spread from this cell into an adjacent (random) * 3104 * cell. * 3105 * * 3106 * INPUT: none * 3107 * * 3108 * OUTPUT: bool; Did the Tiberium spread? * 3109 * * 3110 * WARNINGS: If there are no adjacent cells that the tiberium can spread to, then this * 3111 * routine will fail. * 3112 * * 3113 * HISTORY: * 3114 * 08/14/1996 JLB : Created. * 3115 *=============================================================================================*/ 3116 bool CellClass::Spread_Tiberium(bool forced) 3117 { 3118 if (!forced) { 3119 if (!Can_Tiberium_Spread()) return(false); 3120 } 3121 FacingType offset = Random_Pick(FACING_N, FACING_NW); 3122 for (FacingType index = FACING_N; index < FACING_COUNT; index++) { 3123 CellClass * newcell = Adjacent_Cell(index+offset); 3124 3125 if (newcell != NULL && newcell->Can_Tiberium_Germinate()) { 3126 new OverlayClass(Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4), newcell->Cell_Number()); 3127 newcell->OverlayData = 0; 3128 return(true); 3129 } 3130 } 3131 return(false); 3132 } 3133 3134 3135 /*********************************************************************************************** 3136 * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. * 3137 * * 3138 * This routine will examine the cell and determine if Tiberium can start growth in it. * 3139 * * 3140 * INPUT: none * 3141 * * 3142 * OUTPUT: bool; Can Tiberium grow in this cell? * 3143 * * 3144 * WARNINGS: none * 3145 * * 3146 * HISTORY: * 3147 * 08/14/1996 JLB : Created. * 3148 *=============================================================================================*/ 3149 bool CellClass::Can_Tiberium_Germinate(void) const 3150 { 3151 if (!Map.In_Radar(Cell_Number())) return(false); 3152 3153 if (Is_Bridge_Here()) return(false); 3154 3155 /* 3156 ** Don't allow Tiberium to grow on a cell with a building unless that building is 3157 ** invisible. In such a case, the Tiberium must grow or else the location of the 3158 ** building will be revealed. 3159 */ 3160 BuildingClass const * building = Cell_Building(); 3161 if (building != NULL && !building->Class->IsInvisible) return(false); 3162 3163 if (!Ground[Land_Type()].Build) return(false); 3164 3165 if (Overlay != OVERLAY_NONE) return(false); 3166 3167 return(true); 3168 } 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 /* 3180 ** Additions to CellClass to track visibility per-player. ST - 8/2/2019 2:59PM 3181 ** 3182 ** 3183 ** 3184 ** 3185 ** 3186 */ 3187 3188 /*********************************************************************************************** 3189 * CellClass::Set_Mapped -- Set the cell mapped for the given player * 3190 * * 3191 * * 3192 * HISTORY: * 3193 * 3/5/2019 3:09PM - ST * 3194 *=============================================================================================*/ 3195 void CellClass::Set_Mapped(HousesType house, bool set) 3196 { 3197 int shift = (int) house; 3198 if (set) { 3199 IsMappedByPlayerMask |= (1 << shift); 3200 } else { 3201 IsMappedByPlayerMask &= ~(1 << shift); 3202 } 3203 } 3204 3205 3206 /*********************************************************************************************** 3207 * CellClass::Set_Mapped -- Set the cell mapped for the given player * 3208 * * 3209 * * 3210 * HISTORY: * 3211 * 3/5/2019 3:09PM - ST * 3212 *=============================================================================================*/ 3213 void CellClass::Set_Mapped(HouseClass *player, bool set) 3214 { 3215 if (player && player->Class) { 3216 Set_Mapped(player->Class->House, set); 3217 if (Session.Type == GAME_NORMAL && player->IsHuman) { 3218 IsMapped = set; // Also set the regular flag in single player 3219 } 3220 } 3221 } 3222 3223 /*********************************************************************************************** 3224 * CellClass::Is_Mapped -- Is the cell mapped for the given player * 3225 * * 3226 * * 3227 * HISTORY: * 3228 * 3/5/2019 3:13PM - ST * 3229 *=============================================================================================*/ 3230 bool CellClass::Is_Mapped(HousesType house) const 3231 { 3232 int shift = (int) house; 3233 return (IsMappedByPlayerMask & (1 << shift)) ? true : false; 3234 } 3235 3236 /*********************************************************************************************** 3237 * CellClass::Is_Mapped -- Is the cell mapped for the given player * 3238 * * 3239 * * 3240 * HISTORY: * 3241 * 3/5/2019 3:13PM - ST * 3242 *=============================================================================================*/ 3243 bool CellClass::Is_Mapped(HouseClass *player) const 3244 { 3245 if (player && player->Class) { 3246 return Is_Mapped(player->Class->House); 3247 } 3248 return false; 3249 } 3250 3251 /*********************************************************************************************** 3252 * CellClass::Set_Visible -- Set the cell visible for the given player * 3253 * * 3254 * * 3255 * HISTORY: * 3256 * 3/5/2019 3:16PM - ST * 3257 *=============================================================================================*/ 3258 void CellClass::Set_Visible(HousesType house, bool set) 3259 { 3260 int shift = (int) house; 3261 if (set) { 3262 IsVisibleByPlayerMask |= (1 << shift); 3263 } else { 3264 IsVisibleByPlayerMask &= ~(1 << shift); 3265 } 3266 } 3267 3268 3269 /*********************************************************************************************** 3270 * CellClass::Set_Visible -- Set the cell visible for the given player * 3271 * * 3272 * * 3273 * HISTORY: * 3274 * 3/5/2019 3:16PM - ST * 3275 *=============================================================================================*/ 3276 void CellClass::Set_Visible(HouseClass *player, bool set) 3277 { 3278 if (player && player->Class) { 3279 Set_Visible(player->Class->House, set); 3280 if (Session.Type == GAME_NORMAL && player->IsHuman) { 3281 IsVisible = set; // Also set the regular flag in single player. This is needed for rendering 3282 } 3283 } 3284 } 3285 3286 /*********************************************************************************************** 3287 * CellClass::Is_Visible -- Is the cell visible for the given player * 3288 * * 3289 * * 3290 * HISTORY: * 3291 * 3/5/2019 3:16PM - ST * 3292 *=============================================================================================*/ 3293 bool CellClass::Is_Visible(HousesType house) const 3294 { 3295 int shift = (int) house; 3296 return (IsVisibleByPlayerMask & (1 << shift)) ? true : false; 3297 } 3298 3299 /*********************************************************************************************** 3300 * CellClass::Is_Visible -- Is the cell visible for the given player * 3301 * * 3302 * * 3303 * HISTORY: * 3304 * 3/5/2019 3:16PM - ST * 3305 *=============================================================================================*/ 3306 bool CellClass::Is_Visible(HouseClass *player) const 3307 { 3308 if (player && player->Class) { 3309 return Is_Visible(player->Class->House); 3310 } 3311 return false; 3312 } 3313 3314 bool CellClass::Is_Jamming(HousesType house) const 3315 { 3316 int shift = (int)house; 3317 return (Jammed & (1 << shift)) ? true : false; 3318 } 3319 3320 bool CellClass::Is_Jamming(HouseClass *player) const 3321 { 3322 if (player && player->Class) { 3323 return Is_Jamming(player->Class->House); 3324 } 3325 return false; 3326 } 3327 3328 void CellClass::Override_Land_Type(LandType type) 3329 { 3330 OverrideLand = type; 3331 }