BUILDING.CPP (224993B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: F:\projects\c&c\vcs\code\building.cpv 2.13 02 Aug 1995 17:00:14 JOE_BOSTIC $ */ 17 /*********************************************************************************************** 18 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : BUILDING.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : September 10, 1993 * 28 * * 29 * Last Update : August 20, 1995 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * 34 * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * 35 * BuildingClass::As_Target -- Convert the building into a target value. * 36 * BuildingClass::Assign_Target -- Assigns a target to the building. * 37 * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * 38 * BuildingClass::BuildingClass -- Constructor for buildings. * 39 * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * 40 * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * 41 * BuildingClass::Can_Fire -- Determines if this building can fire. * 42 * BuildingClass::Captured -- Captures the building. * 43 * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * 44 * BuildingClass::Click_With -- Handles clicking on the map while the building is selected. * 45 * BuildingClass::Crew_Type -- This determines the crew that this object generates. * 46 * BuildingClass::Death_Announcement -- Announce the death of this building. * 47 * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * 48 * BuildingClass::Detach -- Handles target removal from the game system. * 49 * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * 50 * BuildingClass::Draw_It -- Displays the building at the location specified. * 51 * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * 52 * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * 53 * BuildingClass::Exit_Object -- Initiates an object to leave the building. * 54 * BuildingClass::Fire_At -- Fires weapon at specified target. * 55 * BuildingClass::Fire_Coord -- Calculates the coordinate that projectile would appear. * 56 * BuildingClass::Fire_Direction -- Fetches the direction of firing. * 57 * BuildingClass::Fire_Out -- Handles when attached animation expires. * 58 * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * 59 * BuildingClass::Grand_Opening -- Handles construction completed special operations. * 60 * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * 61 * BuildingClass::Init -- Initialize the building system to an empty null state. * 62 * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * 63 * BuildingClass::Look -- Reveal map around building. * 64 * BuildingClass::Mark -- Building interface to map rendering system. * 65 * BuildingClass::Mission_Attack -- Handles attack mission for building. * 66 * BuildingClass::Mission_Construction -- Handles mission construction. * 67 * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * 68 * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * 69 * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * 70 * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * 71 * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * 72 * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * 73 * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * 74 * BuildingClass::Power_Output -- Fetches the current power output from this building. * 75 * BuildingClass::Read_INI -- Reads buildings from INI file. * 76 * BuildingClass::Receive_Message -- Handle an incoming message to the building. * 77 * BuildingClass::Refund_Amount -- Fetches the refund amount if building is sold. * 78 * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * 79 * BuildingClass::Repair -- Initiates or terminates the repair process. * 80 * BuildingClass::Revealed -- Reveals the building to the specified house. * 81 * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * 82 * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * 83 * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * 84 * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * 85 * BuildingClass::Unlimbo -- Removes a building from limbo state. * 86 * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * 87 * BuildingClass::Update_Specials -- removes computer specials for lost bld * 88 * BuildingClass::What_Action -- Determines action to perform if click on specified object. * 89 * BuildingClass::What_Action -- Determines what action will occur. * 90 * BuildingClass::Write_INI -- Writes all building data to an INI file. * 91 * BuildingClass::delete -- Deallocates building object. * 92 * BuildingClass::new -- Allocates a building object from building pool. * 93 * BuildingClass::~BuildingClass -- Destructor for building type objects. * 94 * BuildingClass::Validate -- validates building pointer * 95 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 96 97 #include "function.h" 98 99 /* 100 ** New sidebar for GlyphX multiplayer. ST - 3/26/2019 12:24PM 101 */ 102 #include "SidebarGlyphx.h" 103 104 105 enum SAMState { 106 SAM_NONE=-1, // Used for non SAM site buildings. 107 SAM_UNDERGROUND, // Launcher is underground and awaiting orders. 108 SAM_RISING, // Doors open and launcher rises to normal locked down position. 109 SAM_READY, // Launcher can be facing any direction tracking targets. 110 SAM_FIRING, // Stationary while missile is being fired. 111 SAM_READY2, // Launcher can be facing any direction tracking targets. 112 SAM_FIRING2, // Stationary while missile is being fired. 113 SAM_LOCKING, // Rotating to locked position in preparation for lowering. 114 SAM_LOWERING, // Launcher is lowering into the ground. 115 }; 116 117 118 /*************************************************************************** 119 ** Center of building offset table. 120 */ 121 COORDINATE const BuildingClass::CenterOffset[BSIZE_COUNT] = { 122 0x00800080L, 123 0x008000FFL, 124 0x00FF0080L, 125 0x00FF00FFL, 126 0x018000FFL, 127 0x00FF0180L, 128 0x01800180L, 129 130 0x00FF0200L, 131 132 0x02800280L, 133 }; 134 135 /* 136 ** This contains the value of the Virtual Function Table Pointer 137 */ 138 void * BuildingClass::VTable; 139 140 141 /*********************************************************************************************** 142 * BuildingClass::Validate -- validates building pointer * 143 * * 144 * INPUT: * 145 * none. * 146 * * 147 * OUTPUT: * 148 * 1 = ok, 0 = error * 149 * * 150 * WARNINGS: * 151 * none. * 152 * * 153 * HISTORY: * 154 * 08/09/1995 BRR : Created. * 155 *=============================================================================================*/ 156 #ifdef CHEAT_KEYS 157 int BuildingClass::Validate(void) const 158 { 159 int num; 160 161 num = Buildings.ID(this); 162 if (num < 0 || num >= BUILDING_MAX) { 163 Validate_Error("BUILDING"); 164 return (0); 165 } 166 else 167 return (1); 168 } 169 #else 170 #define Validate() 171 #endif 172 173 174 /*********************************************************************************************** 175 * BuildingClass::Receive_Message -- Handle an incoming message to the building. * 176 * * 177 * This routine handles an incoming message to the building. Messages regulate the * 178 * various cooperative ventures between buildings and units. This might include such * 179 * actions as coordinating the construction yard animation with the actual building's * 180 * construction animation. * 181 * * 182 * INPUT: from -- The originator of the message received. * 183 * * 184 * message -- The radio message received. * 185 * * 186 * param -- Reference to an optional parameter that might be used to return * 187 * extra information to the message originator. * 188 * * 189 * OUTPUT: Returns with the response to the message (typically, this is just RADIO_OK). * 190 * * 191 * WARNINGS: none * 192 * * 193 * HISTORY: * 194 * 06/09/1994 JLB : Created. * 195 * 06/26/1995 JLB : Forces refinery load anim to start immediately. * 196 * 08/13/1995 JLB : Uses ScenarioInit for special loose "CAN_LOAD" check. * 197 *=============================================================================================*/ 198 RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) 199 { 200 Validate(); 201 switch (message) { 202 203 /* 204 ** This message is received as a request to attach/load/dock with this building. 205 ** Verify that this is allowed and return the appropriate response. 206 */ 207 case RADIO_HELLO: 208 //Refineries can't be interupted while they're processing a harvester - LLL April 22, 2020 209 if (Mission == MISSION_HARVEST) { 210 return(RADIO_NEGATIVE); 211 } 212 break; 213 214 case RADIO_CAN_LOAD: 215 TechnoClass::Receive_Message(from, message, param); 216 if (BState == BSTATE_CONSTRUCTION || (!ScenarioInit && Class->Type != STRUCT_REFINERY && In_Radio_Contact())) return(RADIO_NEGATIVE); 217 switch (Class->Type) { 218 case STRUCT_AIRSTRIP: 219 if (from->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass const *)from) == AIRCRAFT_CARGO) { 220 return(RADIO_ROGER); 221 } 222 break; 223 224 case STRUCT_HELIPAD: 225 if (from->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass const *)from)->Class->IsFixedWing) { 226 return(RADIO_ROGER); 227 } 228 break; 229 230 case STRUCT_REPAIR: 231 if (/*from->Health_Ratio() < 0x0100 &&*/ from->What_Am_I() == RTTI_UNIT || from->What_Am_I() == RTTI_AIRCRAFT) { 232 return(RADIO_ROGER); 233 } 234 return(RADIO_NEGATIVE); 235 236 case STRUCT_REFINERY: 237 if (from->What_Am_I() == RTTI_UNIT && 238 *((UnitClass *)from) == UNIT_HARVESTER && 239 (ScenarioInit || !Is_Something_Attached())) { 240 241 return((Contact_With_Whom() != from) ? RADIO_ROGER : RADIO_NEGATIVE); 242 } 243 break; 244 245 default: 246 break; 247 } 248 return(RADIO_NEGATIVE); 249 250 /* 251 ** This message is received when the object has attached itself to this 252 ** building. 253 */ 254 case RADIO_IM_IN: 255 if (Mission == MISSION_DECONSTRUCTION) { 256 return(RADIO_NEGATIVE); 257 } 258 switch (Class->Type) { 259 case STRUCT_REPAIR: 260 IsReadyToCommence = true; 261 Assign_Mission(MISSION_REPAIR); 262 from->Assign_Mission(MISSION_SLEEP); 263 return(RADIO_ROGER); 264 265 case STRUCT_HELIPAD: 266 Assign_Mission(MISSION_REPAIR); 267 from->Assign_Mission(MISSION_SLEEP); 268 return(RADIO_ROGER); 269 270 case STRUCT_REFINERY: 271 ScenarioInit++; 272 Begin_Mode(BSTATE_ACTIVE); 273 ScenarioInit--; 274 Mark(MARK_CHANGE); 275 Assign_Mission(MISSION_HARVEST); 276 return(RADIO_ATTACH); 277 } 278 break; 279 280 /* 281 ** Docking maneuver maintenance message. See if new order should be given to the 282 ** unit trying to dock. 283 */ 284 case RADIO_DOCKING: 285 TechnoClass::Receive_Message(from, message, param); 286 287 /* 288 ** When in radio contact for loading, the refinery starts 289 ** flashing the lights. 290 */ 291 //Fix for refinery animation bug when mission is harvest - LLL April 22, 2020 292 if (*this == STRUCT_REFINERY && BState != BSTATE_FULL && Mission != MISSION_HARVEST) { 293 Begin_Mode(BSTATE_FULL); 294 } 295 296 /* 297 ** If this building is already in radio contact, then it might 298 ** be able to satisfy the request to load by bumping off any 299 ** preoccupying task. 300 */ 301 if (*this == STRUCT_REPAIR) { 302 if (Contact_With_Whom() != from) { 303 if (Transmit_Message(RADIO_ON_DEPOT) == RADIO_ROGER) { 304 if (Transmit_Message(RADIO_NEED_REPAIR) == RADIO_NEGATIVE) { 305 Transmit_Message(RADIO_RUN_AWAY); 306 Transmit_Message(RADIO_OVER_OUT); 307 return(RADIO_ROGER); 308 } 309 } 310 } 311 } 312 313 /* 314 ** Establish contact with the object if this building isn't already in contact 315 ** with another. 316 */ 317 if (!In_Radio_Contact()) { 318 Transmit_Message(RADIO_HELLO, from); 319 } 320 321 if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { 322 if (*this == STRUCT_HELIPAD) { 323 param = As_Target(); 324 } else { 325 if (*this == STRUCT_REPAIR) { 326 Transmit_Message(RADIO_TETHER); 327 param = ::As_Target(Coord_Cell(Center_Coord())); 328 } else { 329 param = ::As_Target(Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_SW))); 330 } 331 } 332 333 /* 334 ** Tell the harvester to move to the docking pad of the refinery. 335 */ 336 if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) { 337 338 /* 339 ** Since the harvester is already there, tell it to begin the backup 340 ** procedure now. If it can't, then tell it to get outta here. 341 */ 342 Transmit_Message(RADIO_TETHER); 343 if (*this == STRUCT_REFINERY && Transmit_Message(RADIO_BACKUP_NOW, from) != RADIO_ROGER) { 344 from->Scatter(NULL, true); 345 } 346 } 347 } 348 return(RADIO_ROGER); 349 350 /* 351 ** If a transport or harvester is requesting permission to head toward, dock 352 ** and load/unload, check to make sure that this is allowed given the current 353 ** state of the building. 354 */ 355 case RADIO_ARE_REFINERY: 356 if (Is_Something_Attached() || In_Radio_Contact() || IsInLimbo || House->Class->House != from->Owner() || (*this != STRUCT_REFINERY/* && *this != STRUCT_REPAIR*/)) { 357 return(RADIO_NEGATIVE); 358 } 359 return(RADIO_ROGER); 360 361 /* 362 ** Someone is telling us that it is starting construction. This should only 363 ** occur if this is a construction yard and a building was just placed on 364 ** the map. 365 */ 366 case RADIO_BUILDING: 367 Assign_Mission(MISSION_REPAIR); 368 TechnoClass::Receive_Message(from, message, param); 369 return(RADIO_ROGER); 370 371 /* 372 ** Someone is telling us that they have finished construction. This should 373 ** only occur if this is a construction yard and the building that was being 374 ** constructed has finished. In this case, stop the construction yard 375 ** animation. 376 */ 377 case RADIO_COMPLETE: 378 if (Mission != MISSION_DECONSTRUCTION) { 379 Assign_Mission(MISSION_GUARD); 380 } 381 TechnoClass::Receive_Message(from, message, param); 382 return(RADIO_ROGER); 383 384 /* 385 ** This message may occur unexpectedly if the unit in contact with this 386 ** building is suddenly destroyed. Handle any cleanup necessary. For example, 387 ** a construction yard should stop its construction animation in this case. 388 */ 389 case RADIO_OVER_OUT: 390 Begin_Mode(BSTATE_IDLE); 391 TechnoClass::Receive_Message(from, message, param); 392 return(RADIO_ROGER); 393 394 /* 395 ** This message is received when an object has completely left 396 ** building. Sometimes special cleanup action is required when 397 ** this event occurs. 398 */ 399 case RADIO_UNLOADED: 400 if (*this == STRUCT_REPAIR) { 401 if (Distance(from) < 0x0180) { 402 return(RADIO_ROGER); 403 } 404 } 405 406 //Turn off the refinery lights - LLL April 22, 2020 407 if (*this == STRUCT_REFINERY) { 408 Begin_Mode(BSTATE_IDLE); 409 } 410 411 TechnoClass::Receive_Message(from, message, param); 412 if (*this == STRUCT_WEAP || *this == STRUCT_AIRSTRIP || *this == STRUCT_REPAIR) return(RADIO_RUN_AWAY); 413 return(RADIO_ROGER); 414 } 415 416 /* 417 ** Pass along the message to the default message handler in the radio itself. 418 */ 419 return(TechnoClass::Receive_Message(from, message, param)); 420 } 421 422 423 #ifdef CHEAT_KEYS 424 /*********************************************************************************************** 425 * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * 426 * * 427 * This utility function will output the current status of the building class to the * 428 * monochrome screen. It is through this data that bugs may be fixed or detected. * 429 * * 430 * INPUT: none * 431 * * 432 * OUTPUT: none * 433 * * 434 * WARNINGS: none * 435 * * 436 * HISTORY: * 437 * 05/31/1994 JLB : Created. * 438 *=============================================================================================*/ 439 void BuildingClass::Debug_Dump(MonoClass *mono) const 440 { 441 Validate(); 442 mono->Set_Cursor(0, 0); 443 mono->Print( 444 "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂÄÄÄÄÄÄÄÂRadio:ÂCoord:ÄÄÂÄÄÄÄÄÄÄÄÂSt:Ä¿\n" 445 "³ ³ ³ ³ ³ ³ ³ ³ ³\n" 446 "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂÄÄÄÁÄÂTurret:ÂÄÄÄÄÄÁÂÄBuilding:ÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n" 447 "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n" 448 "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n" 449 "³Owned.........³ ³ ³Last Message: ³\n" 450 "³Discovered....³ ³ ÃTimer:ÂArm:ÂÄÄÄÄÄÄÂTiberium:ÂFlash:ÂStage:ÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\n" 451 "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ \n" 452 "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÙ \n" 453 "³Locked on Map.³ ³ ³ \n" 454 "³Is A Loaner...³ ³ ³ \n" 455 "³ ³ ³ ³ \n" 456 "³ ³ ³ ³ \n" 457 "³ ³ ³ ³ \n" 458 "³Repairing.....³ ³ ³ \n" 459 "³ ³ ³ ³ \n" 460 "³ ³ ³ ³ \n" 461 "³Recoiling.....³ ³ ³ \n" 462 "³To Display....³ ³ ³ \n" 463 "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n"); 464 mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName); 465 mono->Set_Cursor(35, 3);mono->Printf("%02X:%02X", PrimaryFacing.Current(), PrimaryFacing.Desired()); 466 mono->Set_Cursor(50, 3); 467 if (Factory) { 468 mono->Printf(Factory->Get_Object()->Class_Of().IniName); 469 mono->Printf(" "); 470 mono->Printf("%d%%", Factory->Completion()); 471 } else { 472 mono->Printf("(empty)"); 473 } 474 475 mono->Text_Print("X", 16 + (IsRepairing?2:0), 14); 476 // mono->Set_Cursor(44, 3);mono->Printf("%d", SAM); 477 mono->Set_Cursor(34, 1);mono->Printf("%04X", TarCom); 478 mono->Set_Cursor(28, 7);mono->Printf("%2d", Arm); 479 480 TechnoClass::Debug_Dump(mono); 481 } 482 #endif 483 484 485 /*********************************************************************************************** 486 * BuildingClass::Draw_It -- Displays the building at the location specified. * 487 * * 488 * This is the low level graphic routine that displays the building at the location * 489 * specified. * 490 * * 491 * INPUT: x,y -- The coordinate to draw the building at. * 492 * * 493 * window -- The clipping window to use. * 494 * * 495 * OUTPUT: none * 496 * * 497 * WARNINGS: none * 498 * * 499 * HISTORY: * 500 * 06/20/1994 JLB : Created. * 501 * 06/27/1994 JLB : Takes a clipping window parameter. * 502 * 07/06/1995 JLB : Handles damaged silos correctly. * 503 *=============================================================================================*/ 504 void BuildingClass::Draw_It(int x, int y, WindowNumberType window) 505 { 506 Validate(); 507 void const * shapefile; // Pointer to loaded shape file. 508 int shapenum; 509 510 shapenum = Fetch_Stage(); 511 512 /* 513 ** The shape file to use for rendering depends on whether the building 514 ** is undergoing construction or not. 515 */ 516 if (BState == BSTATE_CONSTRUCTION) { 517 shapefile = Class->Get_Buildup_Data(); 518 519 /* 520 ** If the building is deconstructing, then the display frame progresses 521 ** from the end to the beginning. Reverse the shape number accordingly. 522 */ 523 if (Mission == MISSION_DECONSTRUCTION) { 524 shapenum = (Class->Anims[BState].Start+Class->Anims[BState].Count-1)-shapenum; 525 } 526 527 } else { 528 529 shapefile = Class->Get_Image_Data(); 530 531 /* 532 ** The obelisk has a stage value than can be overridden by 533 ** its current state. 534 */ 535 if (*this == STRUCT_OBELISK) { 536 if (IsCharged) { 537 shapenum = 3; 538 } else { 539 if (IsCharging) { 540 shapenum = Fetch_Stage(); 541 } else { 542 shapenum = 0; 543 } 544 } 545 } 546 547 /* 548 ** Buildings that contain a turret handle their shape determination 549 ** differently than normal buildings. They need to take into consideration 550 ** the direction the turret is facing. 551 */ 552 if (Class->IsTurretEquipped) { 553 shapenum = UnitClass::BodyShape[Facing_To_32(PrimaryFacing.Current())]; 554 555 if (*this == STRUCT_SAM) { 556 557 /* 558 ** SAM sites that are free to rotate fetch their animation frame 559 ** from the building's turret facing. All other animation stages 560 ** fetch their frame from the embedded animation sequencer. 561 */ 562 if (Status == SAM_READY || Status == SAM_FIRING || Status == SAM_READY2 || Status == SAM_FIRING2 || Status == SAM_LOCKING) { 563 shapenum += 16; 564 } else { 565 shapenum = Fetch_Stage(); 566 } 567 } else { 568 if (IsInRecoilState) { 569 shapenum += 32; 570 } 571 } 572 if (Health_Ratio() < 0x0080) { 573 shapenum += 64; 574 } 575 } else { 576 577 /* 578 ** If it has only one point of strength left, it is shown in the 579 ** worst state possible. 580 */ 581 if (Strength <= 1) { 582 shapenum = Get_Build_Frame_Count(shapefile)-1; 583 } else { 584 585 if (*this == STRUCT_WEAP) { 586 shapenum = 0; 587 if (Health_Ratio() < 0x0080) { 588 shapenum = 1; 589 } 590 591 } else { 592 593 /* 594 ** Special render stage for silos. The stage is dependant on the current 595 ** Tiberium collected as it relates to Tiberium capacity. 596 */ 597 if (*this == STRUCT_STORAGE) { 598 int level = 0; 599 if (House->Capacity) { 600 level = (House->Tiberium * 5) / House->Capacity; 601 } 602 // int level = Fixed_To_Cardinal(4, Cardinal_To_Fixed(House->Capacity, House->Tiberium)); 603 604 shapenum += Bound(level, 0, 4); 605 if (Health_Ratio() < 0x0080) { 606 shapenum += 5; 607 } 608 609 } else { 610 611 if (Health_Ratio() < 0x0080) { 612 613 /* 614 ** Special damage stage for pump. 615 */ 616 if (!Class->IsSimpleDamage) { 617 int last1 = Class->Anims[BSTATE_IDLE].Start + Class->Anims[BSTATE_IDLE].Count; 618 int last2 = Class->Anims[BSTATE_ACTIVE].Start + Class->Anims[BSTATE_ACTIVE].Count; 619 int largest = MAX(last1, last2); 620 last2 = Class->Anims[BSTATE_AUX1].Start + Class->Anims[BSTATE_AUX1].Count; 621 largest = MAX(largest, last2); 622 last2 = Class->Anims[BSTATE_AUX2].Start + Class->Anims[BSTATE_AUX2].Count; 623 largest = MAX(largest, last2); 624 625 shapenum += largest; 626 } else { 627 628 /* 629 ** Presume that the damage stage is the end frame. 630 */ 631 shapenum = Get_Build_Frame_Count(shapefile) - 2; 632 } 633 } 634 } 635 } 636 } 637 } 638 } 639 640 /* 641 ** Actually draw the building shape. 642 */ 643 IsTheaterShape = Class->IsTheater; 644 Techno_Draw_Object(shapefile, shapenum, x, y, window); 645 IsTheaterShape = false; 646 647 /* 648 ** Patch for adding overlay onto weapon factory. Only add the overlay if 649 ** the building has more than 1 hp. Also, if the building's in radio 650 ** contact, he must be unloading a constructed vehicle, so draw that 651 ** vehicle before drawing the overlay. 652 */ 653 if (BState != BSTATE_CONSTRUCTION) { 654 655 /* 656 ** A Tethered object is always rendered AFTER the building. 657 */ 658 if (*this == STRUCT_WEAP && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) { 659 TechnoClass * contact = Contact_With_Whom(); 660 661 assert(contact->IsActive); 662 int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord()))); 663 int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord()))); 664 contact->Draw_It(xxx, yyy, window); 665 contact->IsToDisplay = false; 666 } 667 668 /* 669 ** Draw the weapon factory custom overlay graphic. 670 */ 671 if (*this == STRUCT_WEAP && Strength > 1) { 672 shapenum = Door_Stage(); 673 if (Health_Ratio() < 0x0080) shapenum += 10; 674 // Added override shape file name. ST - 6/20/2019 1:35PM 675 //Techno_Draw_Object(WarFactoryOverlay, shapenum, x, y, window); 676 Techno_Draw_Object_Virtual(WarFactoryOverlay, shapenum, x, y, window, "WEAP2"); 677 } 678 679 /* 680 ** Draw any repair feedback graphic required. 681 */ 682 if (IsRepairing && IsWrenchVisible) { 683 CC_Draw_Shape(ObjectTypeClass::SelectShapes, SELECT_WRENCH, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); 684 } 685 } 686 687 TechnoClass::Draw_It(x, y, window); 688 } 689 690 691 /*********************************************************************************************** 692 * BuildingClass::Mark -- Building interface to map rendering system. * 693 * * 694 * This routine is used to mark the map cells so that when it renders * 695 * the underlying icons will also be updated as necessary. * 696 * * 697 * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * 698 * MARK_UP -- Building is removed. * 699 * MARK_CHANGE -- Building changes shape. * 700 * MARK_DOWN -- Building is added. * 701 * * 702 * OUTPUT: bool; Did the mark operation succeed? Failure could be the result of marking down * 703 * when the building is already marked down, or visa versa. * 704 * * 705 * WARNINGS: none * 706 * * 707 * HISTORY: * 708 * 03/31/1994 JLB : Created. * 709 * 04/15/1994 JLB : Converted to member function. * 710 * 04/16/1994 JLB : Added health bar tracking. * 711 * 12/23/1994 JLB : Calls low level check before proceeding. * 712 * 01/27/1995 JLB : Special road spacer template added. * 713 *=============================================================================================*/ 714 bool BuildingClass::Mark(MarkType mark) 715 { 716 Validate(); 717 if (TechnoClass::Mark(mark)) { 718 short const *offset = Overlap_List(); 719 short const *occupy = Occupy_List(); 720 CELL cell = Coord_Cell(Coord); 721 SmudgeType bib; 722 723 switch (mark) { 724 case MARK_UP: 725 Map.Pick_Up(cell, this); 726 if (Class->Bib_And_Offset(bib, cell)) { 727 SmudgeClass * smudge = new SmudgeClass(bib); 728 if (smudge) { 729 smudge->Disown(cell); 730 delete smudge; 731 } 732 } 733 break; 734 735 case MARK_DOWN: 736 737 /* 738 ** Special wall logic is handled here. A building that is really a wall 739 ** gets converted into an overlay wall type when it is placed down. The 740 ** actual building object itself is destroyed. 741 */ 742 if (Class->IsWall) { 743 switch (Class->Type) { 744 case STRUCT_BRICK_WALL: 745 new OverlayClass(OVERLAY_BRICK_WALL, cell, House->Class->House); 746 break; 747 748 case STRUCT_BARBWIRE_WALL: 749 new OverlayClass(OVERLAY_BARBWIRE_WALL, cell, House->Class->House); 750 break; 751 752 case STRUCT_SANDBAG_WALL: 753 new OverlayClass(OVERLAY_SANDBAG_WALL, cell, House->Class->House); 754 break; 755 756 case STRUCT_WOOD_WALL: 757 new OverlayClass(OVERLAY_WOOD_WALL, cell, House->Class->House); 758 break; 759 760 case STRUCT_CYCLONE_WALL: 761 new OverlayClass(OVERLAY_CYCLONE_WALL, cell, House->Class->House); 762 break; 763 } 764 Transmit_Message(RADIO_OVER_OUT); 765 Delete_This(); 766 767 } else { 768 769 if (Can_Enter_Cell(cell) == MOVE_OK) { 770 771 /* 772 ** Determine if a bib is required for this building. If one is, then 773 ** create and place it. 774 */ 775 CELL newcell = cell; 776 if (Class->Bib_And_Offset(bib, newcell)) { 777 new SmudgeClass(bib, Cell_Coord(newcell), House->Class->House); 778 } 779 780 Map.Place_Down(cell, this); 781 } else { 782 return(false); 783 } 784 } 785 break; 786 787 default: 788 Map.Refresh_Cells(cell, offset); 789 Map.Refresh_Cells(cell, occupy); 790 break; 791 } 792 return(true); 793 } 794 return(false); 795 } 796 797 798 /*********************************************************************************************** 799 * BuildingClass::Fire_At -- Fires weapon at specified target. * 800 * * 801 * This routine does the actual firing of a projectile from the * 802 * building toward the specified target. Prior to calling this * 803 * routine, the building must have rotated into position and acquired * 804 * a suitable target. * 805 * * 806 * INPUT: target -- The target to fire upon. * 807 * * 808 * which -- Which weapon to use for firing. 0=primary, 1=secondary. * 809 * * 810 * OUTPUT: Returns with a pointer to the projectile just launched. This * 811 * may come in handy if additional adjustments to the projectile * 812 * are required. * 813 * * 814 * WARNINGS: none * 815 * * 816 * HISTORY: * 817 * 04/16/1994 JLB : Created. * 818 *=============================================================================================*/ 819 BulletClass * BuildingClass::Fire_At(TARGET target, int which) 820 { 821 Validate(); 822 BulletClass * bullet; // Projectile. 823 WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; 824 825 bullet = TechnoClass::Fire_At(target, which); 826 if (bullet) { 827 if (*this == STRUCT_SAM) { 828 AnimClass *anim = new AnimClass((AnimType)(ANIM_SAM_N + Dir_Facing(PrimaryFacing.Current())), Center_Coord()); 829 if (anim) { 830 anim->Attach_To(this); 831 } 832 833 // MBL 04.17.2020 834 Sound_Effect(weapon->Sound, Coord); 835 836 } else { 837 838 /* 839 ** Flash the muzzle, play sound, and perform any firing animation. 840 */ 841 Sound_Effect(weapon->Sound, Coord); 842 843 AnimClass* anim = NULL; 844 if (weapon->Fires == BULLET_BULLET) { 845 anim = new AnimClass((AnimType)(ANIM_GUN_N + Dir_Facing(PrimaryFacing.Current())), Fire_Coord(which)); 846 } else { 847 switch (weapon->Fires) { 848 case BULLET_SPREADFIRE: 849 break; 850 case BULLET_LASER: 851 IsCharging = false; 852 IsCharged = false; 853 Set_Stage(0); 854 Set_Rate(0); 855 break; 856 default: 857 anim = new AnimClass(ANIM_MUZZLE_FLASH, Fire_Coord(which)); 858 break; 859 } 860 } 861 if (anim != NULL) { 862 anim->Attach_To(this); 863 } 864 Mark(MARK_CHANGE); 865 } 866 } 867 868 return(bullet); 869 } 870 871 872 /*********************************************************************************************** 873 * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * 874 * * 875 * This function is to handle the AI logic for the building. The graphic logic (facing, * 876 * firing, and animation) is handled elsewhere. * 877 * * 878 * INPUT: none * 879 * * 880 * OUTPUT: none * 881 * * 882 * WARNINGS: none * 883 * * 884 * HISTORY: * 885 * 05/31/1994 JLB : Created. * 886 * 12/26/1994 JLB : Handles production. * 887 * 06/11/1995 JLB : Revamped. * 888 *=============================================================================================*/ 889 void BuildingClass::AI(void) 890 { 891 Validate(); 892 893 /* 894 ** Process building animation state changes. Transition to a following state 895 ** if there is one specified and the current animation sequence has expired. 896 ** This process must occur before mission AI since the mission AI relies on 897 ** the bstate change to occur immediately before the MissionClass::AI. 898 */ 899 bool stagechange = Graphic_Logic(); 900 bool toloop = false; 901 902 /* 903 ** Always refresh the SAM site if it has an animation change. 904 */ 905 if (*this == STRUCT_SAM && stagechange) Mark(MARK_CHANGE); 906 907 if ((!Class->IsTurretEquipped && *this != STRUCT_OBELISK) || Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION) { 908 if (stagechange) { 909 910 /* 911 ** Check for animation end or if special case of MCV deconstructing when it is allowed 912 ** to convert back into an MCV. 913 */ 914 BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); 915 916 /* 917 ** When the last frame of the current animation sequence is reached, flag that 918 ** a new mission may be started. This must occur before the animation actually 919 ** loops so that if a mission change does occur, it will have a chance to change 920 ** the building graphic before the last frame is replaced by the first frame of 921 ** the loop. 922 */ 923 if (Fetch_Stage() == ctrl->Start+ctrl->Count-1 || (Special.IsMCVDeploy && *this == STRUCT_CONST && Mission == MISSION_DECONSTRUCTION && Fetch_Stage() == (42-19))) { 924 IsReadyToCommence = true; 925 } 926 927 /* 928 ** If the animation advances beyond the last frame, then start the animation 929 ** sequence over from the beginning. 930 */ 931 if (Fetch_Stage() >= ctrl->Start+ctrl->Count) { 932 toloop = true; 933 } 934 Mark(MARK_CHANGE); 935 } else { 936 if (BState == BSTATE_NONE || Fetch_Rate() == 0) { 937 IsReadyToCommence = true; 938 } 939 } 940 } 941 942 /* 943 ** If there is a door that is animating, then it might cause this building 944 ** to be redrawn. Check for and flag to redraw as necessary. 945 */ 946 if (Time_To_Redraw()) { 947 Clear_Redraw_Flag(); 948 Mark(MARK_CHANGE); 949 } 950 951 /* 952 ** The animation sequence has looped. Restart it and flag this loop condition. 953 ** This is used to tell the mission system that the animation has completed. It 954 ** also signals that now is a good time to act on any pending mission. 955 */ 956 if (toloop) { 957 BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); 958 if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { 959 Set_Rate(Options.Normalize_Delay(ctrl->Rate)); 960 } else { 961 Set_Rate(ctrl->Rate); 962 } 963 Set_Stage(ctrl->Start); 964 Mark(MARK_CHANGE); 965 } 966 967 /* 968 ** If now is a good time to act on a new mission, then do so. This process occurs 969 ** here because some outside event may have requested a mission change for the building. 970 ** Such outside requests (player input) must be initiated BEFORE the normal AI process. 971 */ 972 if (IsReadyToCommence && BState != BSTATE_CONSTRUCTION) { 973 974 /* 975 ** Clear the commencement flag ONLY if something actually occured. By acting 976 ** this way, a building can set the IsReadyToCommence flag before it goes 977 ** to "sleep" knowing that it will wake up as soon as a new mission comes 978 ** along. 979 */ 980 if (Commence()) { 981 IsReadyToCommence = false; 982 } 983 } 984 985 /* 986 ** Proceed with normal logic processing. This is where the mission processing 987 ** occurs. This call must be located after the animation sequence makes the 988 ** transition to the next frame (see above) in order for the mission logic to 989 ** act at the exact moment of graphic transition BEFORE it has a chance to 990 ** be displayed. 991 */ 992 TechnoClass::AI(); 993 994 /* 995 ** If now is a good time to act on a new mission, then do so. This occurs here because 996 ** some AI event may have requested a mission change (usually from another mission 997 ** state machine). This must occur here before it has a chance to render. 998 */ 999 if (IsReadyToCommence) { 1000 1001 /* 1002 ** Clear the commencement flag ONLY if something actually occured. By acting 1003 ** this way, a building can set the IsReadyToCommence flag before it goes 1004 ** to "sleep" knowing that it will wake up as soon as a new mission comes 1005 ** along. 1006 */ 1007 if (Commence()) { 1008 IsReadyToCommence = false; 1009 } 1010 } 1011 1012 /* 1013 ** If a change of animation was requested, then make the change 1014 ** now. The building animation system acts independantly but subordinate 1015 ** to the mission state machine system. By performing the animation change-up 1016 ** here, the mission AI system is ensured of immediate visual affect when it 1017 ** decides to change the animation state of the building. 1018 */ 1019 if (QueueBState != BSTATE_NONE) { 1020 if (BState != QueueBState) { 1021 BState = QueueBState; 1022 BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); 1023 if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { 1024 Set_Rate(Options.Normalize_Delay(ctrl->Rate)); 1025 } else { 1026 Set_Rate(ctrl->Rate); 1027 } 1028 Set_Stage(ctrl->Start); 1029 } 1030 QueueBState = BSTATE_NONE; 1031 } 1032 1033 /* 1034 ** If the building's strength has changed, then update the power 1035 ** accordingly. 1036 */ 1037 if (Strength != LastStrength) { 1038 int oldpower = Power_Output(); 1039 LastStrength = Strength; 1040 int newpower = Power_Output(); 1041 House->Adjust_Power(newpower - oldpower); 1042 } 1043 1044 /* 1045 ** Check to see if the destruction countdown timer is active. If so, then decrement it. 1046 ** When this timer reaches zero, the building is removed from the map. All the explosions 1047 ** are presumed to be in progress at this time. 1048 */ 1049 if (!Strength) { 1050 if (CountDown.Expired()) { 1051 Limbo(); 1052 Drop_Debris(WhomToRepay); 1053 Delete_This(); 1054 } 1055 return; 1056 } 1057 1058 /* 1059 ** Obelisk charging logic. 1060 */ 1061 if (*this == STRUCT_OBELISK && BState != BSTATE_CONSTRUCTION) { 1062 if (Target_Legal(TarCom) && House->Power_Fraction() >= 0x0100) { 1063 if (!IsCharged) { 1064 if (IsCharging) { 1065 if (stagechange) { 1066 Mark(MARK_CHANGE); 1067 if (Fetch_Stage() >= 4) { 1068 IsCharged = true; 1069 IsCharging = false; 1070 Set_Rate(0); 1071 } 1072 } 1073 } else { 1074 IsCharged = false; 1075 IsCharging = true; 1076 Set_Stage(0); 1077 Set_Rate(OBELISK_ANIMATION_RATE); 1078 Sound_Effect(VOC_LASER_POWER, Coord); 1079 } 1080 } 1081 } else { 1082 if (IsCharging || IsCharged) { 1083 Mark(MARK_CHANGE); 1084 IsCharging = false; 1085 IsCharged = false; 1086 Set_Stage(0); 1087 Set_Rate(0); 1088 } 1089 } 1090 } 1091 1092 /* 1093 ** Handle any repair process that may be going on. 1094 */ 1095 if (IsRepairing) { 1096 if ((Frame % 15) == 0) { 1097 IsWrenchVisible = (IsWrenchVisible == false); 1098 Mark(MARK_CHANGE); 1099 int cost = Class->Repair_Cost(); 1100 int step = Class->Repair_Step(); 1101 1102 /* 1103 ** Check for and expend any necessary monies to continue the repair. 1104 */ 1105 if (House->Available_Money() >= cost) { 1106 House->Spend_Money(cost); 1107 Strength += step; 1108 1109 if (Strength >= Class->MaxStrength) { 1110 Strength = Class->MaxStrength; 1111 IsRepairing = false; 1112 } 1113 } else { 1114 IsRepairing = false; 1115 } 1116 } 1117 } 1118 1119 /* 1120 ** Handle any production tied to this building. Only computer controlled buildings have 1121 ** production attached to the building itself. The player uses the sidebar interface for 1122 ** all production control. 1123 */ 1124 if (Factory && Factory->Has_Completed() && PlacementDelay.Expired()) { 1125 1126 switch (Exit_Object(Factory->Get_Object())) { 1127 1128 /* 1129 ** If the object could not leave the factory, then either request 1130 ** a transport, place the (what must be a) building using another method, or 1131 ** abort the production and refund money. 1132 */ 1133 case 0: 1134 Factory->Abandon(); 1135 delete Factory; 1136 Factory = 0; 1137 break; 1138 1139 case 1: 1140 PlacementDelay = TICKS_PER_SECOND*3; 1141 break; 1142 1143 case 2: 1144 Factory->Completed(); 1145 delete Factory; 1146 Factory = 0; 1147 break; 1148 1149 } 1150 } 1151 1152 /* 1153 ** For computer controlled buildings, determine what should be produced and start 1154 ** production accordingly. 1155 */ 1156 if (!House->IsHuman && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { 1157 1158 /* 1159 ** Possibly start repair process if the building is below half strength. 1160 */ 1161 int ratio = 0x0040; 1162 if (Scenario > 6) ratio = 0x0080; 1163 if (Scenario > 10) ratio = 0x00C0; 1164 if (Class->IsRepairable && Health_Ratio() <= (unsigned) ratio) { 1165 if (House->Available_Money() >= REPAIR_THRESHHOLD) { 1166 Repair(1); 1167 } else { 1168 if (IsTickedOff && (int)Scenario > 2 && Random_Pick(0, 50) < (int)Scenario && !Trigger) { 1169 if (GameToPlay != GAME_NORMAL || Scenario != 15 || PlayerPtr->ActLike != HOUSE_GOOD || *this != STRUCT_TEMPLE) { 1170 Sell_Back(1); 1171 } 1172 } 1173 } 1174 } 1175 1176 /* 1177 ** Buildings that produce other objects have special factory logic handled here. 1178 */ 1179 if (Class->ToBuild != RTTI_NONE) { 1180 1181 if (Factory) { 1182 1183 /* 1184 ** If production has halted, then just abort production and make the 1185 ** funds available for something else. 1186 */ 1187 if (PlacementDelay.Expired() && !Factory->Is_Building()) { 1188 Factory->Abandon(); 1189 delete Factory; 1190 Factory = 0; 1191 } 1192 1193 } else { 1194 1195 /* 1196 ** Only look to start production if there is at least a small amount of 1197 ** money available. In cases where there is no practical money left, then 1198 ** production can never complete -- don't bother starting it. 1199 */ 1200 if (House->IsStarted && House->Available_Money() > 10) { 1201 TechnoTypeClass const * techno = House->Suggest_New_Object(Class->ToBuild); 1202 1203 /* 1204 ** If a suitable object type was selected for production, then start 1205 ** producing it now. 1206 */ 1207 if (techno) { 1208 Factory = new FactoryClass; 1209 if (Factory) { 1210 if (!Factory->Set(*techno, *House)) { 1211 delete Factory; 1212 Factory = 0; 1213 } else { 1214 #ifdef USE_RA_AI 1215 House->Production_Begun(Factory->Get_Object()); // Added for RA AI in TD. ST - 7/26/2019 9:46AM 1216 #endif 1217 Factory->Start(); 1218 } 1219 } 1220 } 1221 } 1222 } 1223 } 1224 } 1225 1226 /* 1227 ** Check for demolition timeout. When timeout has expired, the building explodes. 1228 */ 1229 if (IsGoingToBlow && CountDown.Expired()) { 1230 1231 /* 1232 ** Maybe trigger an achivement. ST - 11/14/2019 1:53PM 1233 */ 1234 TechnoTypeClass const *object_type = Techno_Type_Class(); 1235 if (object_type) { 1236 TechnoClass *saboteur = As_Techno(WhomToRepay); 1237 if (saboteur && saboteur->IsActive && saboteur->House && saboteur->House->IsHuman) { 1238 On_Achievement_Event(saboteur->House, "BUILDING_DESTROYED_C4", object_type->IniName); 1239 } 1240 } 1241 1242 SabotagedType = Class->Type; 1243 int damage = 5000; 1244 Take_Damage(damage, 0, WARHEAD_FIRE, As_Techno(WhomToRepay)); 1245 Mark(MARK_CHANGE); 1246 } 1247 1248 /* 1249 ** If the building was in a recoil state (as it would be just as it fires), then 1250 ** restore the building. 1251 */ 1252 if (IsInRecoilState) { 1253 IsInRecoilState = false; 1254 Mark(MARK_CHANGE); 1255 } 1256 1257 /* 1258 ** Turret equiped buildings must handle turret rotation logic here. This entails 1259 ** rotating the turret to the desired facing as well as figuring out what that 1260 ** desired facing should be. 1261 */ 1262 if (Class->IsTurretEquipped && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { 1263 1264 /* 1265 ** Rotate turret to match desired facing. 1266 */ 1267 if (PrimaryFacing.Is_Rotating()) { 1268 if (*this == STRUCT_SAM) { 1269 if (PrimaryFacing.Rotation_Adjust(15)) { 1270 Mark(MARK_CHANGE); 1271 } 1272 } else { 1273 if (PrimaryFacing.Rotation_Adjust(12)) { 1274 Mark(MARK_CHANGE); 1275 } 1276 } 1277 } 1278 } 1279 } 1280 1281 1282 /*********************************************************************************************** 1283 * BuildingClass::Unlimbo -- Removes a building from limbo state. * 1284 * * 1285 * Use this routine to transform a building that has been held in limbo * 1286 * state, into one that really exists on the map. Once a building as * 1287 * been unlimboed, then it becomes a normal object in the game world. * 1288 * * 1289 * INPUT: pos -- The position to place the building on the map. * 1290 * * 1291 * dir (optional) -- not used for this class * 1292 * * 1293 * OUTPUT: bool; Was the unlimbo successful? * 1294 * * 1295 * WARNINGS: The unlimbo operation might not be successful if the * 1296 * building could not be placed at the location specified. * 1297 * * 1298 * HISTORY: * 1299 * 04/16/1994 JLB : Created. * 1300 * 06/07/1994 JLB : Matches virtual function format for base class. * 1301 * 05/09/1995 JLB : Handles wall placement. * 1302 * 06/18/1995 JLB : Checks for wall legality before placing down. * 1303 *=============================================================================================*/ 1304 bool BuildingClass::Unlimbo(COORDINATE coord, DirType dir) 1305 { 1306 Validate(); 1307 #ifdef OBSOLETE 1308 if (*this == STRUCT_ROAD) { 1309 if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { 1310 ObjectClass * o = OverlayTypeClass::As_Reference(OVERLAY_ROAD).Create_One_Of(House); 1311 if (o && o->Unlimbo(coord)) { 1312 Transmit_Message(RADIO_OVER_OUT); 1313 Delete_This(); 1314 return(true); 1315 } 1316 } 1317 return(false); 1318 } 1319 #endif 1320 1321 /* 1322 ** If this is a wall type building, then it never gets unlimboed. Instead, it gets 1323 ** converted to an overlay type. 1324 */ 1325 if (Class->IsWall) { 1326 if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { 1327 OverlayType otype = OVERLAY_NONE; 1328 switch (Class->Type) { 1329 case STRUCT_SANDBAG_WALL: 1330 otype = OVERLAY_SANDBAG_WALL; 1331 break; 1332 1333 case STRUCT_CYCLONE_WALL: 1334 otype = OVERLAY_CYCLONE_WALL; 1335 break; 1336 1337 case STRUCT_BRICK_WALL: 1338 otype = OVERLAY_BRICK_WALL; 1339 break; 1340 1341 case STRUCT_BARBWIRE_WALL: 1342 otype = OVERLAY_BARBWIRE_WALL; 1343 break; 1344 1345 case STRUCT_WOOD_WALL: 1346 otype = OVERLAY_WOOD_WALL; 1347 break; 1348 } 1349 if (otype != OVERLAY_NONE) { 1350 ObjectClass * o = OverlayTypeClass::As_Reference(otype).Create_One_Of(House); 1351 if (o && o->Unlimbo(coord)) { 1352 Map[Coord_Cell(coord)].Owner = House->Class->House; 1353 Transmit_Message(RADIO_OVER_OUT); 1354 Delete_This(); 1355 return(true); 1356 } 1357 } 1358 } 1359 return(false); 1360 } 1361 1362 /* 1363 ** Normal building unlimbo process. 1364 */ 1365 if (TechnoClass::Unlimbo(coord, dir)) { 1366 1367 /* 1368 ** Ensure that the owning house knows about the 1369 ** new object. 1370 */ 1371 House->BScan |= (1L << Class->Type); 1372 House->ActiveBScan |= (1L << Class->Type); 1373 1374 #ifdef USE_RA_AI 1375 // 1376 // Added for RA AI in TD. ST - 7/26/2019 9:25AM 1377 // 1378 House->Recalc_Center(); 1379 #endif 1380 1381 /* 1382 ** Update the total factory type, assuming this building has a factory. 1383 */ 1384 switch (Class->ToBuild) { 1385 case RTTI_AIRCRAFTTYPE: 1386 House->AircraftFactories++; 1387 break; 1388 1389 case RTTI_INFANTRYTYPE: 1390 House->InfantryFactories++; 1391 break; 1392 1393 case RTTI_UNITTYPE: 1394 House->UnitFactories++; 1395 break; 1396 1397 case RTTI_BUILDINGTYPE: 1398 House->BuildingFactories++; 1399 break; 1400 1401 default: 1402 break; 1403 } 1404 1405 /* 1406 ** Possibly the sidebar will be affected by this addition. 1407 */ 1408 House->IsRecalcNeeded = true; 1409 LastStrength = 0; 1410 1411 // Changed for new multiplayer. ST - 4/3/2019 11:20AM 1412 //if ((!IsDiscoveredByPlayer && Map[Coord_Cell(coord)].IsVisible) || GameToPlay != GAME_NORMAL) { 1413 // Revealed(PlayerPtr); 1414 //} 1415 if (!Is_Discovered_By_Player(House) && Map[Coord_Cell(coord)].Is_Visible(House) || GameToPlay != GAME_NORMAL) { 1416 if (House->IsHuman) { 1417 Revealed(House); 1418 } 1419 } 1420 1421 if (!House->IsHuman) { 1422 Revealed(House); 1423 } 1424 1425 //Changed for multiplayer ST - 3/13/2019 5:31PM 1426 if (Is_Owned_By_Player()) { 1427 //if (IsOwnedByPlayer) { 1428 Map.PowerClass::IsToRedraw = true; 1429 Map.Flag_To_Redraw(false); 1430 } 1431 return(true); 1432 } 1433 return(false); 1434 } 1435 1436 1437 /*********************************************************************************************** 1438 * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * 1439 * * 1440 * This routine will inflict damage points upon the specified building. * 1441 * It will handle the damage animation and building destruction. Use * 1442 * this routine whenever a building is attacked. * 1443 * * 1444 * INPUT: damage -- Amount of damage to inflict. * 1445 * * 1446 * distance -- The distance from the damage center point to the object's center point.* 1447 * * 1448 * warhead -- The kind of damage to inflict. * 1449 * * 1450 * source -- The source of the damage. This is used to change targeting. * 1451 * * 1452 * OUTPUT: true/false; Was the building destroyed? * 1453 * * 1454 * WARNINGS: none * 1455 * * 1456 * HISTORY: * 1457 * 07/21/1991 : Created. * 1458 * 04/15/1994 JLB : Converted to member function. * 1459 * 04/16/1994 JLB : Added warhead modifier to damage. * 1460 * 06/03/1994 JLB : Added source of damage as target value. * 1461 * 06/20/1994 JLB : Source is a base class pointer. * 1462 * 11/22/1994 JLB : Shares base damage handler for techno objects. * 1463 * 07/15/1995 JLB : Power ratio gets adjusted. * 1464 *=============================================================================================*/ 1465 ResultType BuildingClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) 1466 { 1467 Validate(); 1468 ResultType res = RESULT_NONE; 1469 int shakes; 1470 1471 if (this != source) { 1472 if (source) Base_Is_Attacked(source); 1473 1474 short const *offset = Occupy_List(); 1475 1476 /* 1477 ** SPECIAL CASE: 1478 ** SAM sites that are closed will take half damage, but never less than one point. 1479 */ 1480 if (*this == STRUCT_SAM && Status == SAM_UNDERGROUND) { 1481 damage /= 2; 1482 damage++; 1483 } 1484 1485 /* 1486 ** Damage from an ion cannon against the Temple of Nod does more damage than 1487 ** usual. 1488 */ 1489 if (GameToPlay == GAME_NORMAL && *this == STRUCT_TEMPLE && warhead == WARHEAD_PB) { 1490 damage += damage/2; 1491 } 1492 1493 /* 1494 ** Perform the low level damage assessment. 1495 */ 1496 res = TechnoClass::Take_Damage(damage, distance, warhead, source); 1497 1498 switch (res) { 1499 case RESULT_DESTROYED: 1500 1501 /* 1502 ** Destroy all attached objects. 1503 */ 1504 while (Attached_Object()) { 1505 FootClass * obj = Detach_Object(); 1506 1507 Detach_All(true); 1508 delete obj; 1509 } 1510 1511 Sound_Effect(VOC_XPLOBIG4, Coord); 1512 while (*offset != REFRESH_EOL) { 1513 CELL cell = Coord_Cell(Coord) + *offset++; 1514 1515 /* 1516 ** If the building is destroyed, then lots of 1517 ** explosions occur. 1518 */ 1519 new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Cell_Coord(cell)); 1520 if (Random_Pick(0, 1) == 0) { 1521 new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0080), Random_Pick(0, 7), Random_Pick(1, 3)); 1522 if (Random_Pick(0, 1) == 0) { 1523 new AnimClass(ANIM_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 7), Random_Pick(1, 3)); 1524 } 1525 } 1526 //Start_Profiler(); 1527 new AnimClass(ANIM_FBALL1, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0,3)); 1528 } 1529 1530 shakes = Class->Cost_Of() / 400; 1531 if (shakes) { 1532 Shake_The_Screen(shakes, Owner()); 1533 if (source && Owner() != source->Owner()) { 1534 Shake_The_Screen(shakes, source->Owner()); 1535 } 1536 } 1537 Sound_Effect(VOC_CRUMBLE, Coord); 1538 if (Mission == MISSION_DECONSTRUCTION) { 1539 CountDown = 0; 1540 Set_Rate(0); 1541 } else { 1542 CountDown = 8; 1543 } 1544 1545 /* 1546 ** A destuction of the Temple by an ion cannon requires a global 1547 ** remembering of this fact. The finale uses this information to 1548 ** play the correct movie. 1549 */ 1550 if (*this == STRUCT_TEMPLE && warhead == WARHEAD_PB) { 1551 TempleIoned = true; 1552 1553 /* 1554 ** Maybe trigger an achivement if the structure is owned by an AI house in campaign mode. ST - 11/14/2019 1:53PM 1555 */ 1556 if (GameToPlay == GAME_NORMAL && !House->IsHuman && source && source->House && source->House->IsHuman) { 1557 TechnoTypeClass const *object_type = Techno_Type_Class(); 1558 if (object_type) { 1559 On_Achievement_Event(source->House, "ION_DESTROYS_TEMPLE", object_type->IniName); 1560 } 1561 } 1562 1563 } else { 1564 TempleIoned = false; 1565 } 1566 1567 if (House) { 1568 House->Check_Pertinent_Structures(); 1569 } 1570 break; 1571 1572 case RESULT_HALF: 1573 if (*this == STRUCT_PUMP) { 1574 AnimClass *anim = new AnimClass(ANIM_OILFIELD_BURN, Coord_Add(Coord, 0x00400130L), 1); 1575 if (anim) { 1576 anim->Attach_To(this); 1577 } 1578 } 1579 // Fall into next case. 1580 1581 case RESULT_MAJOR: 1582 Sound_Effect(VOC_XPLOBIG4, Coord); 1583 while (*offset != REFRESH_EOL) { 1584 CELL cell = Coord_Cell(Coord) + *offset++; 1585 AnimClass * anim = NULL; 1586 1587 /* 1588 ** Show pieces of fire to indicate that a significant change in 1589 ** damage level has occurred. 1590 */ 1591 if (warhead == WARHEAD_FIRE) { 1592 switch (Random_Pick(0, 13)) { 1593 case 0: 1594 case 1: 1595 case 2: 1596 case 3: 1597 case 4: 1598 anim = new AnimClass(ANIM_ON_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); 1599 break; 1600 1601 case 5: 1602 case 6: 1603 case 7: 1604 anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); 1605 break; 1606 1607 case 8: 1608 anim = new AnimClass(ANIM_ON_FIRE_BIG, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, 1); 1609 break; 1610 1611 case 9: 1612 case 10: 1613 case 11: 1614 case 12: 1615 case 13: 1616 break; 1617 } 1618 } else { 1619 if (Random_Pick(0, 1) == 0) { 1620 anim = new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), Random_Pick(0, 7), Random_Pick(1, 3)); 1621 } 1622 } 1623 1624 /* 1625 ** If the animation was created, then attach it to the building. 1626 */ 1627 if (anim) { 1628 anim->Attach_To(this); 1629 } 1630 } 1631 break; 1632 1633 case RESULT_NONE: 1634 break; 1635 } 1636 1637 if (source && res != RESULT_NONE) { 1638 1639 /* 1640 ** If any damage occurred, then inform the house of this fact. If it is the player's 1641 ** house, it might announce this fact. 1642 */ 1643 House->Attacked(this); 1644 1645 /* 1646 ** Save the type of the house that's doing the damage, so if the building burns 1647 ** to death credit can still be given for the kill 1648 */ 1649 WhoLastHurtMe = source->Owner(); 1650 1651 /* 1652 ** When certain buildings are hit, they "snap out of it" and 1653 ** return fire if they are able and allowed. 1654 */ 1655 if (*this != STRUCT_SAM && 1656 !House->Is_Ally(source) && 1657 Class->Primary != WEAPON_NONE && 1658 (!Target_Legal(TarCom) || !In_Range(TarCom))) { 1659 1660 if (source->What_Am_I() != RTTI_AIRCRAFT && (!House->IsHuman || Special.IsSmartDefense)) { 1661 Assign_Target(source->As_Target()); 1662 } else { 1663 1664 /* 1665 ** Generate a random rotation effect since there is nothing else that this 1666 ** building can do. 1667 */ 1668 if (!PrimaryFacing.Is_Rotating()) { 1669 PrimaryFacing.Set_Desired(Random_Pick(DIR_N, DIR_MAX)); 1670 } 1671 } 1672 } 1673 } 1674 } 1675 1676 return(res); 1677 } 1678 1679 1680 /*********************************************************************************************** 1681 * BuildingClass::Look -- Reveal map around building. * 1682 * * 1683 * Given a building, reveal the cells around the building in accordance * 1684 * with the building's sighting range. * 1685 * * 1686 * INPUT: none * 1687 * * 1688 * OUTPUT: none * 1689 * * 1690 * WARNINGS: This is a very slow routine. Call only when necessary. * 1691 * * 1692 * HISTORY: * 1693 * 08/05/1992 JLB : Created. * 1694 * 04/15/1994 JLB : Converted to member function. * 1695 *=============================================================================================*/ 1696 void BuildingClass::Look(bool) 1697 { 1698 /* 1699 ** Changed this function to reveal for the appropriate players in GlyphX multiplayer. ST - 4/17/2019 11:50AM 1700 */ 1701 Validate(); 1702 1703 if (Class->SightRange) { 1704 Map.Sight_From(House, Coord_Cell(Center_Coord()), Class->SightRange, false); 1705 } 1706 1707 #if (0) 1708 if (GameToPlay != GAME_GLYPHX_MULTIPLAYER) { 1709 1710 if (Is_Owned_By_Player(PlayerPtr) || Is_Discovered_By_Player(PlayerPtr)) { 1711 Map.Sight_From(PlayerPtr, Coord_Cell(Center_Coord()), Class->SightRange, false); 1712 } 1713 } else { 1714 1715 for (int i = 0; i < MPlayerCount; i++) { 1716 HousesType house_type = MPlayerHouses[i]; 1717 HouseClass *house = HouseClass::As_Pointer(house_type); 1718 1719 if (Is_Owned_By_Player(house) || Is_Discovered_By_Player(house)) { 1720 Map.Sight_From(house, Coord_Cell(Center_Coord()), Class->SightRange, false); 1721 } 1722 } 1723 } 1724 #endif 1725 } 1726 1727 1728 #if (0) // For reference. ST - 4/17/2019 11:38AM 1729 void BuildingClass::Look(bool) 1730 { 1731 Validate(); 1732 if (IsOwnedByPlayer || IsDiscoveredByPlayer) { 1733 Map.Sight_From(PlayerPtr, Coord_Cell(Center_Coord()), Class->SightRange, false); 1734 } 1735 } 1736 #endif 1737 1738 1739 /*********************************************************************************************** 1740 * BuildingClass::new -- Allocates a building object from building pool. * 1741 * * 1742 * This routine will allocate a building slot from the building alloc * 1743 * system. * 1744 * * 1745 * INPUT: none * 1746 * * 1747 * OUTPUT: Returns with a pointer to the allocated building. If NULL is * 1748 * returned, then this indicates a failure to allocate. * 1749 * * 1750 * WARNINGS: none * 1751 * * 1752 * HISTORY: * 1753 * 04/11/1994 JLB : Created. * 1754 * 04/21/1994 JLB : Converted to operator new. * 1755 * 05/17/1994 JLB : Revamped allocation scheme * 1756 * 07/29/1994 JLB : Simplified. * 1757 *=============================================================================================*/ 1758 void * BuildingClass::operator new(size_t ) 1759 { 1760 void * ptr = Buildings.Allocate(); 1761 if (ptr) { 1762 ((BuildingClass *)ptr)->Set_Active(); 1763 } 1764 return(ptr); 1765 } 1766 1767 1768 /*********************************************************************************************** 1769 * BuildingClass::delete -- Deallocates building object. * 1770 * * 1771 * This is the memory deallocation operation for a building object. * 1772 * Since buildings are allocated out of a fixed memory block, all that * 1773 * is needed is to flag the unit as inactive. * 1774 * * 1775 * INPUT: ptr -- Pointer to building to deallocate. * 1776 * * 1777 * OUTPUT: none * 1778 * * 1779 * WARNINGS: none * 1780 * * 1781 * HISTORY: * 1782 * 04/21/1994 JLB : Created. * 1783 *=============================================================================================*/ 1784 void BuildingClass::operator delete(void *ptr) 1785 { 1786 if (ptr) { 1787 ((BuildingClass *)ptr)->IsActive = false; 1788 } 1789 Buildings.Free((BuildingClass *)ptr); 1790 1791 //Map.Validate(); 1792 } 1793 1794 1795 /*********************************************************************************************** 1796 * BuildingClass::BuildingClass -- Constructor for buildings. * 1797 * * 1798 * This routine inserts a building into the object tracking system. * 1799 * It is placed into a limbo state unless a location is provided for * 1800 * it to unlimbo at. * 1801 * * 1802 * INPUT: type -- The structure type to make this object. * 1803 * * 1804 * house -- The owner of this building. * 1805 * * 1806 * pos -- The position to unlimbo the building. If -1 is * 1807 * specified, then the building remains in a limbo * 1808 * state. * 1809 * * 1810 * OUTPUT: none * 1811 * * 1812 * WARNINGS: none * 1813 * * 1814 * HISTORY: * 1815 * 04/21/1994 JLB : Created. * 1816 * 08/07/1995 JLB : Fixed act like value to match expected value. * 1817 *=============================================================================================*/ 1818 BuildingClass::BuildingClass(StructType type, HousesType house) : 1819 Class(&BuildingTypeClass::As_Reference(type)), 1820 TechnoClass(house) 1821 { 1822 PlacementDelay = 0; 1823 LastStrength = 0; 1824 ActLike = House->ActLike; 1825 BState = BSTATE_NONE; 1826 CountDown.Set(0); 1827 Factory = 0; 1828 #ifndef USE_RA_AI 1829 House->CurBuildings++; // Removed for RA AI in TD 1830 #endif 1831 WhomToRepay = TARGET_NONE; 1832 IsCaptured = false; 1833 IsCharged = false; 1834 IsCharging = false; 1835 IsSurvivorless = false; 1836 IsGoingToBlow = false; 1837 IsReadyToCommence = false; 1838 IsRepairing = false; 1839 IsSecondShot = !Class->IsTwoShooter; 1840 IsWrenchVisible = false; 1841 QueueBState = BSTATE_NONE; 1842 Strength = Class->MaxStrength; 1843 WhoLastHurtMe = house; 1844 Ammo = Class->MaxAmmo; 1845 1846 /* 1847 ** Make sure that newly built house specific building types will act like 1848 ** the house they are supposed to act like, regardless of who the current 1849 ** owner may happen to be. 1850 */ 1851 if ((type == STRUCT_AIRSTRIP || type == STRUCT_HAND) && house != HOUSE_BAD) { 1852 ActLike = HOUSE_BAD; 1853 IsCaptured = true; 1854 } 1855 if ((type == STRUCT_WEAP || type == STRUCT_BARRACKS) && house != HOUSE_GOOD) { 1856 ActLike = HOUSE_GOOD; 1857 IsCaptured = true; 1858 } 1859 1860 if (GameToPlay == GAME_INTERNET){ 1861 House->BuildingTotals->Increment_Unit_Total( (int) type); 1862 } 1863 1864 #ifdef USE_RA_AI 1865 // 1866 // Added for RA AI in TD. ST - 7/26/2019 9:12AM 1867 // 1868 House->Tracking_Add(this); 1869 #endif // USE_RA_AI 1870 } 1871 1872 1873 /*********************************************************************************************** 1874 * BuildingClass::~BuildingClass -- Destructor for building type objects. * 1875 * * 1876 * This destructor for building objects will put the building in limbo if possible. * 1877 * * 1878 * INPUT: none * 1879 * * 1880 * OUTPUT: none * 1881 * * 1882 * WARNINGS: none * 1883 * * 1884 * HISTORY: * 1885 * 01/18/1995 JLB : Created. * 1886 *=============================================================================================*/ 1887 BuildingClass::~BuildingClass(void) 1888 { 1889 if (GameActive && Class) { 1890 if (House) { 1891 #ifndef USE_RA_AI 1892 House->CurBuildings--; 1893 #else 1894 // 1895 // Added for RA AI in TD. ST - 7/26/2019 9:12AM 1896 // 1897 House->Tracking_Remove(this); 1898 #endif 1899 } 1900 Limbo(); 1901 } 1902 } 1903 1904 1905 /*********************************************************************************************** 1906 * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * 1907 * * 1908 * This routine is called when a building is destroyed. It handles * 1909 * placing the rubble down. * 1910 * * 1911 * INPUT: none * 1912 * * 1913 * OUTPUT: none * 1914 * * 1915 * WARNINGS: none * 1916 * * 1917 * HISTORY: * 1918 * 05/14/1994 JLB : Created. * 1919 * 06/13/1995 JLB : Added smoke and normal infantry survivor possibility. * 1920 * 07/16/1995 JLB : Survival rate depends on if captured or sabotaged. * 1921 *=============================================================================================*/ 1922 void BuildingClass::Drop_Debris(TARGET source) 1923 { 1924 Validate(); 1925 CELL const *offset; 1926 CELL cell; 1927 1928 /* 1929 ** Special case for Chan to run from destroyed technology 1930 ** building. 1931 */ 1932 if (GameToPlay == GAME_NORMAL && *this == STRUCT_MISSION && PlayerPtr->ActLike == HOUSE_BAD && Scenario == 10) { 1933 InfantryClass * i = new InfantryClass(INFANTRY_CHAN, House->Class->House); 1934 1935 ScenarioInit++; 1936 if (i->Unlimbo(Center_Coord(), DIR_N)) { 1937 i->Trigger = TriggerClass::As_Pointer("win"); 1938 i->Strength = Random_Pick(5, (int)i->Class->MaxStrength); 1939 ScenarioInit--; 1940 i->Scatter(0, true); 1941 ScenarioInit++; 1942 i->Assign_Mission(MISSION_GUARD_AREA); 1943 } else { 1944 delete i; 1945 PlayerPtr->Flag_To_Win(); 1946 } 1947 ScenarioInit--; 1948 } 1949 1950 1951 /* 1952 ** Generate random survivors from the destroyed building. 1953 */ 1954 cell = Coord_Cell(Coord); 1955 offset = Occupy_List(); 1956 int odds = 2; 1957 if (Target_Legal(WhomToRepay)) odds -= 1; 1958 if (IsCaptured) odds += 6; 1959 while (*offset != REFRESH_EOL) { 1960 CELL newcell; 1961 1962 newcell = cell + *offset++; 1963 1964 /* 1965 ** Infantry could run out of a destroyed building. 1966 */ 1967 if (!House->IsToDie && !IsSurvivorless) { 1968 InfantryClass * i = NULL; 1969 1970 if (Random_Pick(0, odds) == 1) { 1971 i = new InfantryClass(Crew_Type(), House->Class->House); 1972 if (i) { 1973 if (Class->Get_Buildup_Data() != NULL && i->Class->IsNominal) i->IsTechnician = true; 1974 ScenarioInit++; 1975 if (i->Unlimbo(Cell_Coord(newcell), DIR_N)) { 1976 i->Strength = Random_Pick(5, (int)i->Class->MaxStrength); 1977 i->Scatter(0, true); 1978 if (source != TARGET_NONE && !House->Is_Ally(As_Object(source))) { 1979 i->Assign_Mission(MISSION_ATTACK); 1980 i->Assign_Target(source); 1981 } else { 1982 if (House->IsHuman) { 1983 i->Assign_Mission(MISSION_GUARD); 1984 } else { 1985 i->Assign_Mission(MISSION_HUNT); 1986 } 1987 } 1988 } else { 1989 delete i; 1990 } 1991 ScenarioInit--; 1992 } 1993 } 1994 } 1995 1996 /* 1997 ** Possibly add some smoke rising from the ashes of the building. 1998 */ 1999 switch (Random_Pick(0, 5)) { 2000 case 0: 2001 case 1: 2002 case 2: 2003 new AnimClass(ANIM_SMOKE_M, Coord_Scatter(Cell_Coord(newcell), 0x0050, false), Random_Pick(0, 5), Random_Pick(1, 2)); 2004 break; 2005 2006 default: 2007 break; 2008 } 2009 2010 /* 2011 ** The building always scars the ground in some fashion. 2012 */ 2013 if (Random_Pick(0, 3) == 0) { 2014 new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(newcell)); 2015 } else { 2016 new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Coord_Scatter(Cell_Coord(newcell), 0x0080, false)); 2017 } 2018 } 2019 } 2020 2021 2022 /*********************************************************************************************** 2023 * BuildingClass::Active_Click_With -- Handles clicking on the map while the building is selected.* 2024 * * 2025 * This interface routine handles when the player clicks on the map while this building * 2026 * is currently selected. This is used to assign an override target to a turret or * 2027 * guard tower. * 2028 * * 2029 * INPUT: target -- The target that was clicked upon. * 2030 * * 2031 * OUTPUT: none * 2032 * * 2033 * WARNINGS: none * 2034 * * 2035 * HISTORY: * 2036 * 05/28/1994 JLB : Created. * 2037 *=============================================================================================*/ 2038 void BuildingClass::Active_Click_With(ActionType action, ObjectClass * object) 2039 { 2040 Validate(); 2041 if (action == ACTION_ATTACK) { 2042 Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); 2043 } 2044 2045 if (action == ACTION_TOGGLE_PRIMARY && Class->IsFactory) { 2046 OutList.Add(EventClass(EventClass::PRIMARY, As_Target())); 2047 } 2048 } 2049 2050 2051 /*********************************************************************************************** 2052 * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * 2053 * * 2054 * This routine really only serves one purpose -- to allow targeting of the ground for * 2055 * buildings that are euipped with weapons. * 2056 * * 2057 * INPUT: action -- The requested action to perform. * 2058 * * 2059 * cell -- The cell location to perform the action upon. * 2060 * * 2061 * OUTPUT: none * 2062 * * 2063 * WARNINGS: none * 2064 * * 2065 * HISTORY: * 2066 * 07/04/1995 JLB : Created. * 2067 *=============================================================================================*/ 2068 void BuildingClass::Active_Click_With(ActionType action, CELL cell) 2069 { 2070 Validate(); 2071 if (action == ACTION_ATTACK) { 2072 Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); 2073 } 2074 2075 if (action == ACTION_MOVE && *this == STRUCT_CONST) { 2076 OutList.Add(EventClass(EventClass::ARCHIVE, As_Target(), ::As_Target(cell))); 2077 OutList.Add(EventClass(EventClass::SELL, As_Target())); 2078 2079 COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); 2080 OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord, 1 << PlayerPtr->Class->House)); 2081 } 2082 } 2083 2084 2085 /*********************************************************************************************** 2086 * BuildingClass::Assign_Target -- Assigns a target to the building. * 2087 * * 2088 * Assigning of a target to a building makes sense if the building is one that can attack. * 2089 * This routine would be used to assign the attack target to a turret or guard tower. * 2090 * * 2091 * INPUT: target -- The target that was clicked on while this building was selected. * 2092 * * 2093 * OUTPUT: none * 2094 * * 2095 * WARNINGS: none * 2096 * * 2097 * HISTORY: * 2098 * 05/28/1994 JLB : Created. * 2099 * 11/02/1994 JLB : Checks for range before assigning target. * 2100 *=============================================================================================*/ 2101 void BuildingClass::Assign_Target(TARGET target) 2102 { 2103 Validate(); 2104 if (*this != STRUCT_SAM && !In_Range(target, 0)) { 2105 target = TARGET_NONE; 2106 } 2107 2108 TechnoClass::Assign_Target(target); 2109 } 2110 2111 2112 /*********************************************************************************************** 2113 * BuildingClass::Init -- Initialize the building system to an empty null state. * 2114 * * 2115 * This routine initializes the building system in preparation for a scenario load. * 2116 * * 2117 * INPUT: none * 2118 * * 2119 * OUTPUT: none * 2120 * * 2121 * WARNINGS: none * 2122 * * 2123 * HISTORY: * 2124 * 09/19/1994 JLB : Created. * 2125 *=============================================================================================*/ 2126 void BuildingClass::Init(void) 2127 { 2128 BuildingClass *ptr; 2129 2130 Buildings.Free_All(); 2131 2132 ptr = new BuildingClass(); 2133 VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; 2134 delete ptr; 2135 } 2136 2137 2138 /*********************************************************************************************** 2139 * BuildingClass::Exit_Object -- Initiates an object to leave the building. * 2140 * * 2141 * This function is used to cause an object to exit the building. It is called when a * 2142 * factory produces a vehicle or other mobile object and that object needs to exit the * 2143 * building to join the ranks of a regular unit. Typically, the object is placed down on * 2144 * the map such that it overlaps the building and then it is given a movement order so that * 2145 * it will move to an adjacent free cell. * 2146 * * 2147 * INPUT: base -- Pointer to the object that is to exit the building. * 2148 * * 2149 * OUTPUT: Returns the success rating for the exit attempt; * 2150 * 0 = complete failure (refund money please) * 2151 * 1 = temporarily prevented (try again later please) * 2152 * 2 = successful * 2153 * * 2154 * WARNINGS: The building is placed in radio contact with the object. The object is in a * 2155 * teathered condition. This condition will be automatically broken when the * 2156 * object reaches the adjacent square. * 2157 * * 2158 * HISTORY: * 2159 * 11/28/1994 JLB : Created. * 2160 * 04/10/1995 JLB : Handles building production by computer. * 2161 * 06/17/1995 JLB : Handles refinery exit. * 2162 *=============================================================================================*/ 2163 int BuildingClass::Exit_Object(TechnoClass * base) 2164 { 2165 Validate(); 2166 if (!base) return(0); 2167 2168 TechnoTypeClass const * ttype = (TechnoTypeClass const *)&base->Class_Of(); 2169 2170 /* 2171 ** A unit exiting a building is always considered to be "locked". That means, it 2172 ** will be considered as to have legally entered the visible map domain. 2173 */ 2174 base->IsLocked = true; 2175 2176 /* 2177 ** Find a good cell to unload the object to. The object, probably a vehicle 2178 ** will drive/walk to the adjacent free cell. 2179 */ 2180 switch (base->What_Am_I()) { 2181 CELL cell; 2182 2183 case RTTI_AIRCRAFT: 2184 if (!In_Radio_Contact()) { 2185 AircraftClass *air = (AircraftClass *)base; 2186 2187 air->Altitude = 0; 2188 ScenarioInit++; 2189 if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { 2190 Transmit_Message(RADIO_HELLO, air); 2191 Transmit_Message(RADIO_TETHER); 2192 ScenarioInit--; 2193 return(2); 2194 } 2195 ScenarioInit--; 2196 } else { 2197 AircraftClass *air = (AircraftClass *)base; 2198 2199 if (Cell_X(Coord_Cell(Center_Coord())) - Map.MapCellX < Map.MapCellWidth/2) { 2200 cell = XY_Cell(Map.MapCellX-1, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); 2201 } else { 2202 cell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); 2203 } 2204 ScenarioInit++; 2205 if (air->Unlimbo(Cell_Coord(cell), DIR_N)) { 2206 air->Assign_Destination(::As_Target(Nearby_Location(air))); 2207 air->Assign_Mission(MISSION_MOVE); 2208 ScenarioInit--; 2209 return(2); 2210 } 2211 ScenarioInit--; 2212 } 2213 break; 2214 2215 case RTTI_INFANTRY: 2216 case RTTI_UNIT: 2217 switch (Class->Type) { 2218 case STRUCT_REFINERY: 2219 if (base->What_Am_I() == RTTI_UNIT) { 2220 CELL cell = Coord_Cell(Center_Coord()); 2221 UnitClass * unit = (UnitClass *)base; 2222 2223 cell = Adjacent_Cell(cell, FACING_SW); 2224 ScenarioInit++; 2225 if (unit->Unlimbo(Coord_Add(unit->Coord, 0x00550060L), DIR_SW_X2)) { 2226 unit->PrimaryFacing = DIR_SW_X2; 2227 Transmit_Message(RADIO_HELLO, unit); 2228 Transmit_Message(RADIO_TETHER); 2229 unit->Assign_Mission(MISSION_HARVEST); 2230 unit->Force_Track(DriveClass::OUT_OF_REFINERY, Cell_Coord(cell)); 2231 unit->Set_Speed(128); 2232 } 2233 ScenarioInit--; 2234 } else { 2235 base->Scatter(true); 2236 } 2237 break; 2238 2239 case STRUCT_AIRSTRIP: 2240 if (Create_Special_Reinforcement(House, &AircraftTypeClass::As_Reference(AIRCRAFT_CARGO), ttype, TMISSION_UNLOAD, As_Target())) { 2241 delete base; 2242 return(2); 2243 } 2244 return(0); 2245 2246 case STRUCT_WEAP: 2247 ScenarioInit++; 2248 if (base->Unlimbo(Coord_Add(Coord, Class->ExitPoint), DIR_SW)) { 2249 // base->Assign_Mission(MISSION_MOVE); 2250 // base->Assign_Destination(::As_Target(As_Cell(Coord)+MAP_CELL_W*2)); 2251 base->Mark(MARK_UP); 2252 base->Coord = Coord_Add(Coord, Class->ExitPoint); 2253 base->Mark(MARK_DOWN); 2254 Transmit_Message(RADIO_HELLO, base); 2255 Transmit_Message(RADIO_TETHER); 2256 Assign_Mission(MISSION_UNLOAD); 2257 ScenarioInit--; 2258 return(2); 2259 } 2260 ScenarioInit--; 2261 break; 2262 2263 case STRUCT_BARRACKS: 2264 case STRUCT_HAND: 2265 CELL cell; 2266 bool found = false; 2267 2268 cell = Find_Exit_Cell(base); 2269 if (cell) found = true; 2270 2271 #ifdef OBSOLETE 2272 CELL const *ptr; 2273 bool found = false; 2274 2275 ptr = Class->ExitList; 2276 while (*ptr != REFRESH_EOL) { 2277 cell = Coord_Cell(Coord) + *ptr++; 2278 if (base->Can_Enter_Cell(cell) == MOVE_OK) { 2279 found = true; 2280 break; 2281 } 2282 } 2283 #endif 2284 2285 if (found) { 2286 DirType dir = Direction(cell); 2287 COORDINATE start = Coord_Add(Coord, Class->ExitPoint); 2288 2289 ScenarioInit++; 2290 if (base->Unlimbo(start, dir)) { 2291 2292 base->Assign_Mission(MISSION_MOVE); 2293 base->Assign_Destination(::As_Target(cell)); 2294 2295 /* 2296 ** Establish radio contact so unload coordination can occur. This 2297 ** radio contact should always succeed. 2298 */ 2299 if (Transmit_Message(RADIO_HELLO, base) == RADIO_ROGER) { 2300 Transmit_Message(RADIO_UNLOAD); 2301 } 2302 ScenarioInit--; 2303 return(2); 2304 } 2305 ScenarioInit--; 2306 } 2307 break; 2308 } 2309 break; 2310 2311 case RTTI_BUILDING: 2312 2313 if (!House->IsHuman) { 2314 /* 2315 ** Find the next available spot to place this newly created building. If the 2316 ** building could be placed at the desired location, fine. If not, then this 2317 ** routine will return failure. The calling routine will probably abandon this 2318 ** building in preference to building another. 2319 */ 2320 2321 BaseNodeClass * node = Base.Next_Buildable(((BuildingClass *)base)->Class->Type); 2322 // Replaced with RA AI functioality. ST - 7/25/2019 4:14PM 2323 #ifndef USE_RA_AI 2324 if (node) { 2325 if (Flush_For_Placement(base, Coord_Cell(node->Coord))) { 2326 return(1); 2327 } 2328 if (base->Unlimbo(node->Coord)) { 2329 return(2); 2330 } 2331 } 2332 #else 2333 if (GameToPlay == GAME_NORMAL) { 2334 if (node) { 2335 if (Flush_For_Placement(base, Coord_Cell(node->Coord))) { 2336 return(1); 2337 } 2338 if (base->Unlimbo(node->Coord)) { 2339 return(2); 2340 } 2341 } 2342 } else { 2343 2344 COORDINATE coord = 0; 2345 if (node) { 2346 coord = node->Coord; 2347 } else { 2348 2349 /* 2350 ** Find a suitable new spot to place. 2351 */ 2352 coord = House->Find_Build_Location((BuildingClass *)base); 2353 } 2354 2355 if (coord) { 2356 if (Flush_For_Placement(base, Coord_Cell(coord))) { 2357 return(1); 2358 } 2359 if (base->Unlimbo(coord)) { 2360 if (node && ((BuildingClass *)base)->Class->Type == House->BuildStructure) { 2361 House->BuildStructure = STRUCT_NONE; 2362 } 2363 return(2); 2364 } 2365 } 2366 } 2367 #endif 2368 } 2369 break; 2370 } 2371 2372 /* 2373 ** Failure to exit the object results in a false return value. 2374 */ 2375 return(0); 2376 } 2377 2378 2379 2380 2381 2382 /*********************************************************************************************** 2383 * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * 2384 * * 2385 * This routine will tell the sidebar of objects that can be built. The function is called * 2386 * whenever a building matures. * 2387 * * 2388 * INPUT: none * 2389 * * 2390 * OUTPUT: none * 2391 * * 2392 * WARNINGS: none * 2393 * * 2394 * HISTORY: * 2395 * 11/11/1994 JLB : Created. * 2396 * 12/23/1994 JLB : Only updates for PLAYER buildings. * 2397 *=============================================================================================*/ 2398 void BuildingClass::Update_Buildables(void) 2399 { 2400 Validate(); 2401 2402 /* 2403 ** Only do this for real human players. ST - 3/22/2019 1:38PM 2404 */ 2405 if (House != PlayerPtr) { 2406 if (GameToPlay != GAME_GLYPHX_MULTIPLAYER || House->IsHuman == false) { 2407 return; 2408 } 2409 } 2410 2411 bool buildable_via_capture = (IsCaptured && ActLike != House->ActLike) ? true : false; 2412 2413 if (!IsInLimbo && Is_Discovered_By_Player()) { 2414 switch (Class->ToBuild) { 2415 StructType i; 2416 UnitType u; 2417 InfantryType f; 2418 AircraftType a; 2419 2420 case RTTI_BUILDINGTYPE: 2421 for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) { 2422 if (PlayerPtr->Can_Build(i, ActLike)) { 2423 // if (BuildingTypeClass::As_Reference(i).Who_Can_Build_Me(true, true, ActLike)) { 2424 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 2425 Sidebar_Glyphx_Add(RTTI_BUILDINGTYPE, i, House, buildable_via_capture); 2426 } else { 2427 Map.Add(RTTI_BUILDINGTYPE, i, buildable_via_capture); 2428 } 2429 // } 2430 } 2431 } 2432 break; 2433 2434 case RTTI_UNITTYPE: 2435 for (u = UNIT_FIRST; u < UNIT_COUNT; u++) { 2436 if (PlayerPtr->Can_Build(u, ActLike)) { 2437 // if (UnitTypeClass::As_Reference(u).Who_Can_Build_Me(true, true, ActLike)) { 2438 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 2439 Sidebar_Glyphx_Add(RTTI_UNITTYPE, u, House, buildable_via_capture); 2440 } else { 2441 Map.Add(RTTI_UNITTYPE, u, buildable_via_capture); 2442 } 2443 // } 2444 } 2445 } 2446 break; 2447 2448 case RTTI_INFANTRYTYPE: 2449 for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) { 2450 if (PlayerPtr->Can_Build(f, ActLike)) { 2451 // if (InfantryTypeClass::As_Reference(f).Who_Can_Build_Me(true, true, ActLike)) { 2452 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 2453 Sidebar_Glyphx_Add(RTTI_INFANTRYTYPE, f, House, buildable_via_capture); 2454 } else { 2455 Map.Add(RTTI_INFANTRYTYPE, f, buildable_via_capture); 2456 } 2457 // } 2458 } 2459 } 2460 break; 2461 2462 case RTTI_AIRCRAFTTYPE: 2463 for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) { 2464 if (PlayerPtr->Can_Build(a, ActLike)) { 2465 // if (AircraftTypeClass::As_Reference(a).Who_Can_Build_Me(true, true, ActLike)) { 2466 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) { 2467 Sidebar_Glyphx_Add(RTTI_AIRCRAFTTYPE, a, House, buildable_via_capture); 2468 } else { 2469 Map.Add(RTTI_AIRCRAFTTYPE, a, buildable_via_capture); 2470 } 2471 // } 2472 } 2473 } 2474 break; 2475 } 2476 } 2477 } 2478 2479 #if (0) //ST - 3/22/2019 1:33PM 2480 /*********************************************************************************************** 2481 * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * 2482 * * 2483 * This routine will tell the sidebar of objects that can be built. The function is called * 2484 * whenever a building matures. * 2485 * * 2486 * INPUT: none * 2487 * * 2488 * OUTPUT: none * 2489 * * 2490 * WARNINGS: none * 2491 * * 2492 * HISTORY: * 2493 * 11/11/1994 JLB : Created. * 2494 * 12/23/1994 JLB : Only updates for PLAYER buildings. * 2495 *=============================================================================================*/ 2496 void BuildingClass::Update_Buildables(void) 2497 { 2498 Validate(); 2499 2500 if (House == PlayerPtr && !IsInLimbo && IsDiscoveredByPlayer) { 2501 switch (Class->ToBuild) { 2502 StructType i; 2503 UnitType u; 2504 InfantryType f; 2505 AircraftType a; 2506 2507 case RTTI_BUILDINGTYPE: 2508 for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) { 2509 if (PlayerPtr->Can_Build(i, ActLike)) { 2510 // if (BuildingTypeClass::As_Reference(i).Who_Can_Build_Me(true, true, ActLike)) { 2511 Map.Add(RTTI_BUILDINGTYPE, i); 2512 // } 2513 } 2514 } 2515 break; 2516 2517 case RTTI_UNITTYPE: 2518 for (u = UNIT_FIRST; u < UNIT_COUNT; u++) { 2519 if (PlayerPtr->Can_Build(u, ActLike)) { 2520 // if (UnitTypeClass::As_Reference(u).Who_Can_Build_Me(true, true, ActLike)) { 2521 Map.Add(RTTI_UNITTYPE, u); 2522 // } 2523 } 2524 } 2525 break; 2526 2527 case RTTI_INFANTRYTYPE: 2528 for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) { 2529 if (PlayerPtr->Can_Build(f, ActLike)) { 2530 // if (InfantryTypeClass::As_Reference(f).Who_Can_Build_Me(true, true, ActLike)) { 2531 Map.Add(RTTI_INFANTRYTYPE, f); 2532 // } 2533 } 2534 } 2535 break; 2536 2537 case RTTI_AIRCRAFTTYPE: 2538 for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) { 2539 if (PlayerPtr->Can_Build(a, ActLike)) { 2540 // if (AircraftTypeClass::As_Reference(a).Who_Can_Build_Me(true, true, ActLike)) { 2541 Map.Add(RTTI_AIRCRAFTTYPE, a); 2542 // } 2543 } 2544 } 2545 break; 2546 } 2547 } 2548 } 2549 #endif 2550 2551 /*********************************************************************************************** 2552 * BuildingClass::Fire_Out -- Handles when attached animation expires. * 2553 * * 2554 * This routine is used to perform any fixups necessary when the attached animation has * 2555 * terminated. This occurs when the fire & smoke animation that a SAM site produces stops. * 2556 * At that point, normal reload procedures can commence. * 2557 * * 2558 * INPUT: none * 2559 * * 2560 * OUTPUT: none * 2561 * * 2562 * WARNINGS: none * 2563 * * 2564 * HISTORY: * 2565 * 11/30/1994 JLB : Created. * 2566 *=============================================================================================*/ 2567 void BuildingClass::Fire_Out(void) 2568 { 2569 Validate(); 2570 // SAM = SAM_READY; 2571 // IsFiring = false; 2572 } 2573 2574 2575 /*********************************************************************************************** 2576 * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * 2577 * * 2578 * This routine will handle the power adjustments for the associated house when the * 2579 * building goes into limbo. This means that its power drain or production is subtracted * 2580 * from the house accumulated totals. * 2581 * * 2582 * INPUT: none * 2583 * * 2584 * OUTPUT: bool; Was the building limboed? * 2585 * * 2586 * WARNINGS: none * 2587 * * 2588 * HISTORY: * 2589 * 12/24/1994 JLB : Created. * 2590 *=============================================================================================*/ 2591 bool BuildingClass::Limbo(void) 2592 { 2593 Validate(); 2594 // HouseClass *housep; 2595 // RTTIType bld_type; 2596 2597 if (!IsInLimbo) { 2598 2599 /* 2600 ** Update the total factory type, assuming this building has a factory. 2601 */ 2602 switch (Class->ToBuild) { 2603 case RTTI_AIRCRAFTTYPE: 2604 House->AircraftFactories--; 2605 break; 2606 2607 case RTTI_INFANTRYTYPE: 2608 House->InfantryFactories--; 2609 break; 2610 2611 case RTTI_UNITTYPE: 2612 House->UnitFactories--; 2613 break; 2614 2615 case RTTI_BUILDINGTYPE: 2616 House->BuildingFactories--; 2617 break; 2618 2619 default: 2620 break; 2621 } 2622 2623 House->IsRecalcNeeded = true; 2624 2625 #ifdef USE_RA_AI 2626 // 2627 // Added for RA AI in TD. ST - 7/26/2019 9:25AM 2628 // 2629 House->Recalc_Center(); 2630 #endif 2631 /* 2632 ** Update the power status of the owner's house. 2633 */ 2634 House->Adjust_Power(-Power_Output()); 2635 House->Adjust_Drain(-Class->Drain); 2636 House->Adjust_Capacity(-(int)Class->Capacity, true); 2637 if (House == PlayerPtr) { 2638 Map.PowerClass::IsToRedraw = true; 2639 Map.Flag_To_Redraw(false); 2640 } 2641 2642 #ifdef OBSOLETE 2643 /* 2644 ** If this building is building something, the FactoryClass doing the 2645 ** building must be deleted, as well as the object being built. 2646 ** - If this building is controlled by Computer AI, this building's Factory 2647 ** pointer will point to the factory doing the building. 2648 ** - Otherwise, the owner House's Factory indices will indicate what's 2649 ** being built. (The player's sidebar also contains indices to indicate 2650 ** what's being built, but those just echo the house's indices.) 2651 */ 2652 if (Factory) { 2653 Factory->Abandon(); 2654 delete Factory; 2655 Factory = 0; 2656 } 2657 2658 /* 2659 ** If the owner HouseClass is building something, and this building can 2660 ** build that thing, we may be the last building for that house that can 2661 ** build that thing; if so, abandon production of it. 2662 */ 2663 housep = HouseClass::As_Pointer(Owner()); 2664 if (housep) { 2665 FactoryClass * factory = 0; 2666 bld_type = RTTI_NONE; 2667 if (housep->AircraftFactory != -1 && Class->ToBuild == RTTI_AIRCRAFTTYPE) { 2668 bld_type = RTTI_AIRCRAFTTYPE; 2669 factory = Factories.Raw_Ptr(housep->AircraftFactory); 2670 } 2671 if (housep->InfantryFactory != -1 && Class->ToBuild==RTTI_INFANTRYTYPE) { 2672 bld_type = RTTI_INFANTRYTYPE; 2673 factory = Factories.Raw_Ptr(housep->InfantryFactory); 2674 } 2675 if (housep->UnitFactory != -1 && Class->ToBuild==RTTI_UNITTYPE) { 2676 bld_type = RTTI_UNITTYPE; 2677 factory = Factories.Raw_Ptr(housep->UnitFactory); 2678 } 2679 if (housep->BuildingFactory != -1 && Class->ToBuild==RTTI_BUILDINGTYPE) { 2680 bld_type = RTTI_BUILDINGTYPE; 2681 factory = Factories.Raw_Ptr(housep->BuildingFactory); 2682 } 2683 if (housep->SpecialFactory != -1 && Class->ToBuild==RTTI_SPECIAL) { 2684 bld_type = RTTI_SPECIAL; 2685 } 2686 2687 if (bld_type != RTTI_NONE) { 2688 if (factory) { 2689 TechnoClass * object = factory->Get_Object(); 2690 IsInLimbo = true; 2691 if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, housep->Class->House)) { 2692 housep->Abandon_Production(bld_type); 2693 } 2694 IsInLimbo = false; 2695 } 2696 } 2697 } 2698 #endif 2699 2700 /* 2701 ** This could be a building that builds. If so, then the sidebar may need adjustment. 2702 ** Set IsInLimbo to true to "fool" the sidebar into knowing that this building 2703 ** isn't available. Set it back to false so the rest of the Limbo code works. 2704 ** Otherwise, the sidebar won't properly remove non-available buildables. 2705 */ 2706 if (IsOwnedByPlayer && !ScenarioInit) { 2707 IsInLimbo = true; 2708 Map.Recalc(); 2709 IsInLimbo = false; 2710 } 2711 #ifdef NEVER 2712 if (!House->IsHuman) { 2713 Update_Specials(); 2714 } 2715 #endif 2716 2717 } 2718 return(TechnoClass::Limbo()); 2719 } 2720 2721 2722 /*********************************************************************************************** 2723 * BuildingClass::Fire_Coord -- Calculates the coordinate that projectile would appear. * 2724 * * 2725 * This routine is used to determine where a projectile would appear if this building * 2726 * were to fire. The location usually depends on the current rotation setting of the * 2727 * turret or else on the direction of the target that will be fired upon. * 2728 * * 2729 * INPUT: none * 2730 * * 2731 * OUTPUT: Returns with coordinate that the projectile should appear at if the building * 2732 * were to fire. * 2733 * * 2734 * WARNINGS: none * 2735 * * 2736 * HISTORY: * 2737 * 12/24/1994 JLB : Created. * 2738 *=============================================================================================*/ 2739 FireDataType BuildingClass::Fire_Data(int ) const 2740 { 2741 Validate(); 2742 COORDINATE coord = Center_Coord(); 2743 int dist = 0; 2744 2745 /* 2746 ** Make adjustments to the firing coordinate to account for turret 2747 ** position. This depends on building type and turret facing. 2748 */ 2749 switch (Class->Type) { 2750 default: 2751 case STRUCT_GTOWER: 2752 case STRUCT_ATOWER: 2753 coord = Coord_Move(coord, DIR_N, 0x0030); 2754 dist = 0x0040; 2755 break; 2756 case STRUCT_OBELISK: 2757 coord = Coord_Move(coord, DIR_N, 0x00A8); 2758 coord = Coord_Move(coord, DIR_W, 0x0018); 2759 break; 2760 2761 case STRUCT_SAM: 2762 case STRUCT_TURRET: 2763 coord = Coord_Move(coord, DIR_N, 0x0030); 2764 dist = 0x0080; 2765 break; 2766 } 2767 2768 return{coord,dist}; 2769 } 2770 2771 2772 COORDINATE BuildingClass::Fire_Coord(int ) const 2773 { 2774 Validate(); 2775 COORDINATE coord = Center_Coord(); // Center of firing building. 2776 2777 /* 2778 ** Make adjustments to the firing coordinate to account for turret 2779 ** position. This depends on building type and turret facing. 2780 */ 2781 switch (Class->Type) { 2782 default: 2783 case STRUCT_GTOWER: 2784 case STRUCT_ATOWER: 2785 coord = Coord_Move(coord, DIR_N, 0x0030); 2786 if (Target_Legal(TarCom)) { 2787 coord = Coord_Move(coord, ::Direction(coord, As_Coord(TarCom)), 0x0040); 2788 } 2789 break; 2790 case STRUCT_OBELISK: 2791 coord = Coord_Move(coord, DIR_N, 0x00A8); 2792 // coord = Coord_Move(coord, DIR_N, 0x0058); 2793 coord = Coord_Move(coord, DIR_W, 0x0018); 2794 break; 2795 2796 case STRUCT_SAM: 2797 case STRUCT_TURRET: 2798 coord = Coord_Move(coord, DIR_N, 0x0030); 2799 coord = Coord_Move(coord, PrimaryFacing.Current(), 0x0080); 2800 break; 2801 } 2802 2803 return(coord); 2804 } 2805 2806 2807 /*********************************************************************************************** 2808 * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * 2809 * * 2810 * This routine intercepts the Greatest_Threat function so that it can add the ability * 2811 * to search for ground targets, if this isn't a SAM site. * 2812 * * 2813 * INPUT: threat -- The base threat control value. Typically, it might be THREAT_RANGE * 2814 * or THREAT_NORMAL. * 2815 * * 2816 * OUTPUT: Returns with a suitable target. If none could be found, then TARGET_NONE is * 2817 * returned instead. * 2818 * * 2819 * WARNINGS: none * 2820 * * 2821 * HISTORY: * 2822 * 01/01/1995 JLB : Created. * 2823 *=============================================================================================*/ 2824 TARGET BuildingClass::Greatest_Threat(ThreatType threat) const 2825 { 2826 Validate(); 2827 if (*this != STRUCT_SAM) { 2828 threat = threat | (THREAT_INFANTRY|THREAT_BOATS|THREAT_VEHICLES|THREAT_RANGE); 2829 2830 if (!House->IsHuman) { 2831 threat = threat | THREAT_BUILDINGS; 2832 } 2833 // threat = threat & ~THREAT_AIR; 2834 } else { 2835 threat = threat | THREAT_AREA; 2836 } 2837 2838 if (Class->Primary != WEAPON_NONE && BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).IsAntiAircraft) { 2839 threat = threat | THREAT_AIR; 2840 } 2841 return(TechnoClass::Greatest_Threat(threat)); 2842 } 2843 2844 2845 /*********************************************************************************************** 2846 * BuildingClass::Grand_Opening -- Handles construction completed special operations. * 2847 * * 2848 * This routine is called when construction has finished. Typically, this enables * 2849 * new production options for factories. * 2850 * * 2851 * INPUT: none * 2852 * * 2853 * OUTPUT: none * 2854 * * 2855 * WARNINGS: none * 2856 * * 2857 * HISTORY: * 2858 * 01/08/1995 JLB : Created. * 2859 * 06/13/1995 JLB : Added helipad. * 2860 *=============================================================================================*/ 2861 void BuildingClass::Grand_Opening(bool captured) 2862 { 2863 Validate(); 2864 2865 /* 2866 ** Adjust the owning house according to the power, drain, and Tiberium capacity that 2867 ** this building has. 2868 */ 2869 House->Adjust_Drain(Class->Drain); 2870 House->Adjust_Capacity(Class->Capacity); 2871 House->IsRecalcNeeded = true; 2872 2873 /* SPECIAL CASE: 2874 ** Refineries get a free harvester. Add a harvester to the reinforcement list 2875 ** at this time. 2876 */ 2877 if (*this == STRUCT_REFINERY && !ScenarioInit && !captured && !Debug_Map && (!House->IsHuman || PurchasePrice == 0 || PurchasePrice > Class->Raw_Cost())) { 2878 CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_SW)); 2879 // if (!Map[cell].Cell_Unit()) { 2880 UnitClass * unit = new UnitClass(UNIT_HARVESTER, House->Class->House); 2881 if (unit) { 2882 2883 /* 2884 ** Try to place down the harvesters. If it could not be placed, then try 2885 ** to place it in a nearby location. 2886 */ 2887 if (!unit->Unlimbo(Cell_Coord(cell), DIR_SW)) { 2888 cell = Nearby_Location(unit); 2889 2890 /* 2891 ** If the harvester could still not be placed, then refund the money 2892 ** to the owner and then bail. 2893 */ 2894 if (!unit->Unlimbo(Cell_Coord(cell), DIR_SW)) { 2895 House->Refund_Money(unit->Class->Cost_Of()); 2896 delete unit; 2897 } 2898 } 2899 } else { 2900 2901 /* 2902 ** If the harvester could not be created in the first place, then give 2903 ** the full refund price to the owning player. 2904 */ 2905 House->Refund_Money(UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost_Of()); 2906 } 2907 } 2908 // } 2909 2910 /* 2911 ** Helicopter pads get a free attack helicopter. 2912 */ 2913 if (*this == STRUCT_HELIPAD && !captured && (!House->IsHuman || PurchasePrice == 0 || PurchasePrice > Class->Raw_Cost())) { 2914 ScenarioInit++; 2915 AircraftClass * air = 0; 2916 if (House->ActLike == HOUSE_GOOD) { 2917 air = new AircraftClass(AIRCRAFT_ORCA, House->Class->House); 2918 } else { 2919 air = new AircraftClass(AIRCRAFT_HELICOPTER, House->Class->House); 2920 } 2921 if (air) { 2922 air->Altitude = 0; 2923 if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { 2924 air->Assign_Mission(MISSION_GUARD); 2925 air->Transmit_Message(RADIO_HELLO, this); 2926 Transmit_Message(RADIO_TETHER); 2927 } 2928 } 2929 ScenarioInit--; 2930 } 2931 } 2932 2933 2934 /*********************************************************************************************** 2935 * BuildingClass::Repair -- Initiates or terminates the repair process. * 2936 * * 2937 * This routine will start, stop, or toggle the repair process. When a building repairs, it * 2938 * occurs incrementally over time. * 2939 * * 2940 * INPUT: control -- Determines how to control the repair process. * 2941 * 0: Turns repair process off (if it was on). * 2942 * 1: Turns repair process on (if it was off). * 2943 * -1:Toggles repair process to other state. * 2944 * * 2945 * OUTPUT: none * 2946 * * 2947 * WARNINGS: none * 2948 * * 2949 * HISTORY: * 2950 * 01/08/1995 JLB : Created. * 2951 *=============================================================================================*/ 2952 void BuildingClass::Repair(int control) 2953 { 2954 Validate(); 2955 switch (control) { 2956 case -1: 2957 IsRepairing = (IsRepairing == false); 2958 break; 2959 2960 case 1: 2961 if (IsRepairing) return; 2962 IsRepairing = true; 2963 break; 2964 2965 case 0: 2966 if (!IsRepairing) return; 2967 IsRepairing = false; 2968 break; 2969 } 2970 2971 /* 2972 ** At this point, we know that the repair state has changed. Perform 2973 ** appropriate action. 2974 */ 2975 VocType sound = VOC_NONE; 2976 if (IsRepairing) { 2977 if (Strength == Class->MaxStrength) { 2978 sound = VOC_SCOLD; 2979 } else { 2980 sound = VOC_BUTTON; 2981 Clicked_As_Target(PlayerPtr->Class->House); // 2019/09/20 JAS - Added record of who clicked on the object 2982 IsWrenchVisible = true; 2983 } 2984 } else { 2985 sound = VOC_BUTTON; 2986 } 2987 //Changed for multiplayer ST - 3/13/2019 5:31PM 2988 if (Is_Owned_By_Player()) { 2989 //if (IsOwnedByPlayer) { 2990 Sound_Effect(sound, Coord); 2991 } 2992 } 2993 2994 2995 /*********************************************************************************************** 2996 * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * 2997 * * 2998 * This routine will initiate or stop the sell back process for a building. It is called * 2999 * when the player clicks on a building when the sell mode is active. * 3000 * * 3001 * INPUT: control -- The action to perform. 0 = turn deconstruction off, 1 = deconstruct, * 3002 * -1 = toggle deconstruction state. * 3003 * * 3004 * OUTPUT: none * 3005 * * 3006 * WARNINGS: none * 3007 * * 3008 * HISTORY: * 3009 * 06/25/1995 JLB : Created. * 3010 *=============================================================================================*/ 3011 void BuildingClass::Sell_Back(int control) 3012 { 3013 Validate(); 3014 if (Class->Get_Buildup_Data()) { 3015 bool decon = false; 3016 switch (control) { 3017 case -1: 3018 decon = (Mission != MISSION_DECONSTRUCTION); 3019 break; 3020 3021 case 1: 3022 if (Mission == MISSION_DECONSTRUCTION) return; 3023 decon = true; 3024 break; 3025 3026 case 0: 3027 if (Mission != MISSION_DECONSTRUCTION) return; 3028 decon = false; 3029 break; 3030 } 3031 3032 /* 3033 ** At this point, we know that the repair state has changed. Perform 3034 ** appropriate action. 3035 */ 3036 if (decon) { 3037 // Transmit_Message(RADIO_RUN_AWAY); 3038 // Transmit_Message(RADIO_OVER_OUT); 3039 Assign_Mission(MISSION_DECONSTRUCTION); 3040 //Changed for multiplayer ST - 3/13/2019 5:31PM 3041 if (Is_Owned_By_Player()) { 3042 //if (IsOwnedByPlayer) { 3043 Clicked_As_Target(PlayerPtr->Class->House); // 2019/09/20 JAS - Added record of who clicked on the object 3044 } 3045 } 3046 //Changed for multiplayer ST - 3/13/2019 5:31PM 3047 if (Is_Owned_By_Player()) { 3048 //if (IsOwnedByPlayer) { 3049 Sound_Effect(VOC_BUTTON); 3050 } 3051 } 3052 } 3053 3054 3055 /*********************************************************************************************** 3056 * BuildingClass::What_Action -- Determines action to perform if click on specified object. * 3057 * * 3058 * This routine will determine what action to perform if the mouse was clicked on the * 3059 * object specified. This determination is used to control the mouse imagery and the * 3060 * function process when the mouse button is pressed. * 3061 * * 3062 * INPUT: object -- Pointer to the object that, if clicked on, will control what action * 3063 * is to be performed. * 3064 * * 3065 * OUTPUT: Returns with the ActionType that will occur if the mouse is clicked over the * 3066 * object specified while the building is currently selected. * 3067 * * 3068 * WARNINGS: none * 3069 * * 3070 * HISTORY: * 3071 * 01/18/1995 JLB : Created. * 3072 *=============================================================================================*/ 3073 ActionType BuildingClass::What_Action(ObjectClass * object) const 3074 { 3075 Validate(); 3076 ActionType action = TechnoClass::What_Action(object); 3077 3078 if (action == ACTION_SELF) { 3079 if (Class->IsFactory && PlayerPtr == House) { 3080 switch (Class->ToBuild) { 3081 case RTTI_AIRCRAFTTYPE: 3082 if (House->AircraftFactories < 2) { 3083 action = ACTION_NONE; 3084 } 3085 else { 3086 action = ACTION_TOGGLE_PRIMARY; 3087 } 3088 break; 3089 3090 case RTTI_INFANTRYTYPE: 3091 if (House->InfantryFactories < 2) { 3092 action = ACTION_NONE; 3093 } 3094 else { 3095 action = ACTION_TOGGLE_PRIMARY; 3096 } 3097 break; 3098 3099 case RTTI_UNITTYPE: 3100 if (House->UnitFactories < 2) { 3101 action = ACTION_NONE; 3102 } 3103 else { 3104 action = ACTION_TOGGLE_PRIMARY; 3105 } 3106 break; 3107 3108 default: 3109 action = ACTION_NONE; 3110 break; 3111 } 3112 3113 } else { 3114 action = ACTION_NONE; 3115 } 3116 } 3117 3118 /* 3119 ** Don't allow targeting of SAM sites, even if the CTRL key 3120 ** is held down. 3121 */ 3122 if (action == ACTION_ATTACK && *this == STRUCT_SAM) { 3123 action = ACTION_NONE; 3124 } 3125 3126 if (action == ACTION_MOVE) { 3127 action = ACTION_NONE; 3128 } 3129 3130 return(action); 3131 } 3132 3133 3134 /*********************************************************************************************** 3135 * BuildingClass::What_Action -- Determines what action will occur. * 3136 * * 3137 * This routine examines the cell specified and returns with the action that will be * 3138 * performed if that cell were clicked upon while the building is selected. * 3139 * * 3140 * INPUT: cell -- The cell to examine. * 3141 * * 3142 * OUTPUT: Returns the ActionType that indicates what should occur if the mouse is clicked * 3143 * on this cell. * 3144 * * 3145 * WARNINGS: none * 3146 * * 3147 * HISTORY: * 3148 * 01/18/1995 JLB : Created. * 3149 *=============================================================================================*/ 3150 ActionType BuildingClass::What_Action(CELL cell) const 3151 { 3152 Validate(); 3153 ActionType action = TechnoClass::What_Action(cell); 3154 3155 if (action == ACTION_MOVE && (*this != STRUCT_CONST || !Special.IsMCVDeploy)) { 3156 action = ACTION_NONE; 3157 } 3158 3159 /* 3160 ** Don't allow targeting of SAM sites, even if the CTRL key 3161 ** is held down. 3162 */ 3163 if (action == ACTION_ATTACK && *this == STRUCT_SAM) { 3164 action = ACTION_NONE; 3165 } 3166 3167 return(action); 3168 } 3169 3170 3171 /*********************************************************************************************** 3172 * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * 3173 * * 3174 * This routine will start the building animating. This animation will loop indefinately * 3175 * until explicitly stopped. * 3176 * * 3177 * INPUT: bstate -- The animation state to initiate. * 3178 * * 3179 * OUTPUT: none * 3180 * * 3181 * WARNINGS: The buliding graphic state will reflect the first stage of this animation the * 3182 * very next time it is rendered. * 3183 * * 3184 * HISTORY: * 3185 * 06/25/1995 JLB : Created. * 3186 * 07/02/1995 JLB : Uses normalize animation rate where applicable. * 3187 *=============================================================================================*/ 3188 void BuildingClass::Begin_Mode(BStateType bstate) 3189 { 3190 Validate(); 3191 QueueBState = bstate; 3192 if (BState == BSTATE_NONE || bstate == BSTATE_CONSTRUCTION || ScenarioInit) { 3193 BState = bstate; 3194 QueueBState = BSTATE_NONE; 3195 BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); 3196 3197 int rate = ctrl->Rate; 3198 if (Class->IsRegulated && bstate != BSTATE_CONSTRUCTION) { 3199 rate = Options.Normalize_Delay(rate); 3200 } 3201 Set_Rate(rate); 3202 Set_Stage(ctrl->Start); 3203 } 3204 } 3205 3206 3207 /*********************************************************************************************** 3208 * BuildingClass::Read_INI -- Reads buildings from INI file. * 3209 * * 3210 * This is the basic scenario initialization of building function. It * 3211 * is called when reading the scenario startup INI file and it handles * 3212 * creation of all specified buildings. * 3213 * * 3214 * INI entry format: * 3215 * Housename, Typename, Strength, Cell, Facing, Triggername * 3216 * * 3217 * INPUT: buffer -- Pointer to the loaded INI file data. * 3218 * * 3219 * OUTPUT: none * 3220 * * 3221 * WARNINGS: none * 3222 * * 3223 * HISTORY: * 3224 * 05/24/1994 JLB : Created. * 3225 *=============================================================================================*/ 3226 void BuildingClass::Read_INI(char *buffer) 3227 { 3228 BuildingClass *b; // Working unit pointer. 3229 char *tbuffer; // Accumulation buffer of unit IDs. 3230 HousesType bhouse; // Building house. 3231 StructType classid; // Building type. 3232 int len; // Size of data in buffer. 3233 CELL cell; // Cell of building. 3234 char buf[128]; 3235 char *trigname; // building's trigger's name 3236 3237 len = strlen(buffer) + 2; 3238 tbuffer = buffer + len; 3239 3240 /* 3241 ** Read the entire building INI section into HIDBUF 3242 */ 3243 WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); 3244 3245 while (*tbuffer != '\0') { 3246 3247 /* 3248 ** Get a building entry. 3249 */ 3250 WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); 3251 3252 /* 3253 ** 1st token: house name. 3254 */ 3255 bhouse = HouseTypeClass::From_Name(strtok(buf, ",")); 3256 3257 /* 3258 ** 2nd token: building name. 3259 */ 3260 classid = BuildingTypeClass::From_Name(strtok(NULL, ",")); 3261 3262 if (bhouse != HOUSE_NONE && classid != STRUCT_NONE) { 3263 int strength; 3264 DirType facing; 3265 3266 /* 3267 ** 3rd token: strength. 3268 */ 3269 strength = atoi(strtok(NULL, ",")); 3270 3271 /* 3272 ** 4th token: cell #. 3273 */ 3274 cell = atoi(strtok(NULL, ",")); 3275 3276 /* 3277 ** 5th token: facing. 3278 */ 3279 facing = (DirType)atoi(strtok(NULL, ",")); 3280 3281 /* 3282 ** 6th token: triggername (can be NULL). 3283 */ 3284 trigname = strtok(NULL,","); 3285 3286 if (HouseClass::As_Pointer(bhouse) != NULL) { 3287 b = new BuildingClass(classid, bhouse); 3288 if (b) { 3289 if (b->Unlimbo(Cell_Coord(cell), facing)) { 3290 strength = MIN(strength, 0x100); 3291 strength = Fixed_To_Cardinal(b->Class->MaxStrength, strength); 3292 b->Strength = strength; 3293 b->IsALemon = false; 3294 b->Trigger = TriggerClass::As_Pointer(trigname); 3295 if (b->Trigger) { 3296 b->Trigger->AttachCount++; 3297 } 3298 } else { 3299 3300 /* 3301 ** If the building could not be unlimboed on the map, then this indicates 3302 ** a serious error. Delete the building. 3303 */ 3304 delete b; 3305 } 3306 } 3307 } 3308 } 3309 tbuffer += strlen(tbuffer)+1; 3310 } 3311 } 3312 3313 3314 /*********************************************************************************************** 3315 * BuildingClass::Write_INI -- Writes all building data to an INI file. * 3316 * * 3317 * This routine is used to write the buildings into an INI file. It is necessary for the * 3318 * scenario editor save game option. * 3319 * * 3320 * INI entry format: * 3321 * Housename, Typename, Strength, Cell, Facing, Triggername * 3322 * * 3323 * INPUT: buffer -- The buffer that holds the INI data. * 3324 * * 3325 * OUTPUT: none * 3326 * * 3327 * WARNINGS: none * 3328 * * 3329 * HISTORY: * 3330 * 05/28/1994 JLB : Created. * 3331 *=============================================================================================*/ 3332 void BuildingClass::Write_INI(char *buffer) 3333 { 3334 int index; 3335 char uname[10]; 3336 char buf[127]; 3337 char *tbuffer; // Accumulation buffer of unit IDs. 3338 3339 /* 3340 ** First, clear out all existing building data from the ini file. 3341 */ 3342 tbuffer = buffer + strlen(buffer) + 2; 3343 WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); 3344 while (*tbuffer != '\0') { 3345 WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); 3346 tbuffer += strlen(tbuffer)+1; 3347 } 3348 3349 /* 3350 ** Write the data out. 3351 */ 3352 for (index = 0; index < Buildings.Count(); index++) { 3353 BuildingClass * building; 3354 3355 building = Buildings.Ptr(index); 3356 if (!building->IsInLimbo) { 3357 3358 sprintf(uname, "%03d", index); 3359 sprintf(buf, "%s,%s,%d,%u,%d,%s", 3360 building->House->Class->IniName, 3361 building->Class->IniName, 3362 building->Health_Ratio(), 3363 Coord_Cell(building->Coord), 3364 building->PrimaryFacing.Current(), 3365 building->Trigger ? building->Trigger->Get_Name() : "None" 3366 ); 3367 WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); 3368 } 3369 } 3370 } 3371 3372 3373 /*********************************************************************************************** 3374 * BuildingClass::As_Target -- Convert the building into a target value. * 3375 * * 3376 * Use this routine to take the building and convert it into a target number. * 3377 * * 3378 * INPUT: none * 3379 * * 3380 * OUTPUT: Returns the target number for this building. * 3381 * * 3382 * WARNINGS: none * 3383 * * 3384 * HISTORY: * 3385 * 03/10/1995 JLB : Created. * 3386 *=============================================================================================*/ 3387 TARGET BuildingClass::As_Target(void) const 3388 { 3389 Validate(); 3390 return(Build_Target(KIND_BUILDING, Buildings.ID(this))); 3391 } 3392 3393 3394 /*********************************************************************************************** 3395 * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * 3396 * * 3397 * This routine is used to set the center coordinate for this building. * 3398 * * 3399 * INPUT: none * 3400 * * 3401 * OUTPUT: Returns with the coordinate for the center location for the building. * 3402 * * 3403 * WARNINGS: none * 3404 * * 3405 * HISTORY: * 3406 * 03/10/1995 JLB : Created. * 3407 *=============================================================================================*/ 3408 COORDINATE BuildingClass::Center_Coord(void) const 3409 { 3410 Validate(); 3411 return(Coord_Add(Coord, CenterOffset[Class->Size])); 3412 } 3413 3414 static bool Occupy_List_Contains(const short * list, short cell) 3415 { 3416 while (*list != REFRESH_EOL) { 3417 if (*list == cell) { 3418 return true; 3419 } 3420 list++; 3421 } 3422 return false; 3423 } 3424 3425 COORDINATE BuildingClass::Target_Coord(void) const 3426 { 3427 static constexpr int _num_facings = 3; 3428 static const FacingType _facings[_num_facings] = { FACING_S, FACING_E, FACING_SE }; 3429 static const COORDINATE _offsets[_num_facings] = { 0x00800000, 0x00000080, 0x00800080 }; 3430 3431 Validate(); 3432 COORDINATE offset = CenterOffset[Class->Size]; 3433 3434 const short * list = Occupy_List(); 3435 CELL cell = Coord_Cell(offset); 3436 if (!Occupy_List_Contains(list, cell)) { 3437 for (int i = 0; i < _num_facings; ++i) { 3438 CELL adjcell = Adjacent_Cell(cell, _facings[i]); 3439 if (Occupy_List_Contains(list, adjcell)) { 3440 offset = Coord_Add(offset, _offsets[i]) & 0xFF80FF80; 3441 break; 3442 } 3443 } 3444 } 3445 return(Coord_Add(Coord, offset)); 3446 } 3447 3448 3449 COORDINATE BuildingClass::Docking_Coord(void) const 3450 { 3451 Validate(); 3452 if (*this == STRUCT_HELIPAD) { 3453 return(Coord_Add(Coord, XYP_COORD(24, 18))); 3454 } 3455 if (*this == STRUCT_AIRSTRIP) { 3456 return(Coord_Add(Coord, XYP_COORD(18, 30))); 3457 } 3458 return(TechnoClass::Docking_Coord()); 3459 } 3460 3461 3462 /*********************************************************************************************** 3463 * BuildingClass::Can_Fire -- Determines if this building can fire. * 3464 * * 3465 * Use this routine to see if the building can fire its weapon. * 3466 * * 3467 * * 3468 * INPUT: target -- The target that firing upon is desired. * 3469 * * 3470 * which -- Which weapon to use when firing. 0=primary, 1=secondary. * 3471 * * 3472 * OUTPUT: Returns with the fire possibility code. If firing is allowed, then FIRE_OK is * 3473 * returned. Other cases will result in appropriate fire code value that indicates * 3474 * why firing is not allowed. * 3475 * * 3476 * WARNINGS: none * 3477 * * 3478 * HISTORY: * 3479 * 05/03/1995 JLB : Created. * 3480 *=============================================================================================*/ 3481 FireErrorType BuildingClass::Can_Fire(TARGET target, int which) const 3482 { 3483 Validate(); 3484 FireErrorType canfire = TechnoClass::Can_Fire(target, which); 3485 3486 if (canfire == FIRE_OK) { 3487 3488 /* 3489 ** Double check to make sure that the facing is roughly toward 3490 ** the target. If the difference is too great, then firing is 3491 ** temporarily postponed. 3492 */ 3493 if (Class->IsTurretEquipped) { 3494 /* 3495 ** If the turret is rotating then firing must be delayed. 3496 */ 3497 if (PrimaryFacing.Is_Rotating()) { 3498 return(FIRE_ROTATING); 3499 } 3500 3501 int diff = PrimaryFacing.Difference(Direction(TarCom)); 3502 if (ABS(diff) > 8) { 3503 return(FIRE_FACING); 3504 } 3505 } 3506 3507 /* 3508 ** Advanced guard towers need power to fire. 3509 */ 3510 if (*this == STRUCT_ATOWER && House->Power_Fraction() < 0x0100) { 3511 return(FIRE_BUSY); 3512 } 3513 3514 /* 3515 ** If an obelisk can fire, check the state of charge. If it isn't charging 3516 ** up, start it charging up and return FIRE_BUSY. If it is charging but 3517 ** isn't done yet, return FIRE_BUSY. If it's done charging, stop the 3518 ** charging process, clear the stage timer, and return FIRE_OK. 3519 */ 3520 if (Class->Primary == WEAPON_OBELISK_LASER && !IsCharged) { 3521 return(FIRE_BUSY); 3522 } 3523 } 3524 return(canfire); 3525 } 3526 3527 3528 /*********************************************************************************************** 3529 * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * 3530 * * 3531 * This routine will change the primary factory state of this building. The primary * 3532 * factory is the one that units will be produced from (by default). * 3533 * * 3534 * INPUT: none * 3535 * * 3536 * OUTPUT: Is this building NOW the primary factory? * 3537 * * 3538 * WARNINGS: none * 3539 * * 3540 * HISTORY: * 3541 * 05/03/1995 JLB : Created. * 3542 *=============================================================================================*/ 3543 bool BuildingClass::Toggle_Primary(void) 3544 { 3545 Validate(); 3546 if (IsLeader) { 3547 IsLeader = false; 3548 } else { 3549 for (int index = 0; index < Buildings.Count(); index++) { 3550 BuildingClass * building = Buildings.Ptr(index); 3551 3552 if (!building->IsInLimbo && building->Owner() == Owner() && building->Class->ToBuild == Class->ToBuild) { 3553 building->IsLeader = false; 3554 } 3555 } 3556 IsLeader = true; 3557 // 3558 // MBL 07.22.2020 - Update so that each player in multiplayer will properly hear this when it applies to them 3559 // 3560 // if (House == PlayerPtr) { 3561 // Speak(VOX_PRIMARY_SELECTED); 3562 // } 3563 if ((HouseClass *)House->IsHuman) { 3564 Speak(VOX_PRIMARY_SELECTED, House); 3565 } 3566 } 3567 Mark(MARK_CHANGE); 3568 return(IsLeader); 3569 } 3570 3571 3572 /*********************************************************************************************** 3573 * BuildingClass::Captured -- Captures the building. * 3574 * * 3575 * This routine will change the owner of the building. It handles updating any related * 3576 * game systems as a result. Factories are the most prone to have great game related * 3577 * consequences when captured. This could also affect the sidebar and building ownership. * 3578 * * 3579 * INPUT: newowner -- Pointer to the house that is now the new owner. * 3580 * * 3581 * OUTPUT: Was the capture attempt successful? * 3582 * * 3583 * WARNINGS: Capturing could fail if the house is already owned by the one specified or * 3584 * the building isn't allowed to be captured. * 3585 * * 3586 * HISTORY: * 3587 * 05/03/1995 JLB : Created. * 3588 * 07/05/1995 JLB : Fixed production problem with capturing enemy buildings. * 3589 *=============================================================================================*/ 3590 bool BuildingClass::Captured(HouseClass * newowner) 3591 { 3592 Validate(); 3593 if (Can_Capture() && newowner != House) { 3594 switch (Owner()) { 3595 case HOUSE_GOOD: 3596 Speak(VOX_GDI_CAPTURED); 3597 break; 3598 3599 case HOUSE_BAD: 3600 Speak(VOX_NOD_CAPTURED); 3601 break; 3602 } 3603 3604 if (House == PlayerPtr) { 3605 Map.PowerClass::IsToRedraw = true; 3606 Map.Flag_To_Redraw(false); 3607 } 3608 3609 /* 3610 ** Maybe trigger an achivement. ST - 11/14/2019 1:53PM 3611 */ 3612 if (newowner->IsHuman) { 3613 TechnoTypeClass const *object_type = Techno_Type_Class(); 3614 if (object_type) { 3615 if (newowner->ActLike != House->ActLike) { 3616 On_Achievement_Event(newowner, "OPPOSING_BUILDING_CAPTURED", object_type->IniName); 3617 } else { 3618 On_Achievement_Event(newowner, "BUILDING_CAPTURED", object_type->IniName); 3619 } 3620 } 3621 } 3622 3623 /* 3624 ** Add this building to the list of buildings captured this game. For internet stats purposes 3625 */ 3626 if (GameToPlay == GAME_INTERNET){ 3627 newowner->CapturedBuildings->Increment_Unit_Total (Class->Type); 3628 } 3629 3630 House->Adjust_Power(-Power_Output()); 3631 LastStrength = 0; 3632 House->Adjust_Drain(-Class->Drain); 3633 int booty = House->Adjust_Capacity(-(int)Class->Capacity, true); 3634 3635 /* 3636 ** If there is something loaded, then it gets captured as well. 3637 */ 3638 TechnoClass * tech = Attached_Object(); 3639 if (tech) tech->Captured(newowner); 3640 3641 /* 3642 ** If something isn't technically attached, but is sitting on this 3643 ** building for another reason (e.g., helicopter on helipad), then it 3644 ** gets captured as well. 3645 */ 3646 tech = Contact_With_Whom(); 3647 if (tech) { 3648 if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && ::Distance(tech->Center_Coord(), Docking_Coord()) < 0x0040) { 3649 tech->Captured(newowner); 3650 } else { 3651 Transmit_Message(RADIO_RUN_AWAY); 3652 Transmit_Message(RADIO_OVER_OUT); 3653 } 3654 } 3655 3656 /* 3657 ** Decrement the factory counter for the original owner. 3658 */ 3659 switch (Class->ToBuild) { 3660 case RTTI_UNITTYPE: 3661 House->UnitFactories--; 3662 break; 3663 3664 case RTTI_INFANTRYTYPE: 3665 House->InfantryFactories--; 3666 break; 3667 3668 case RTTI_BUILDINGTYPE: 3669 House->BuildingFactories--; 3670 break; 3671 3672 case RTTI_AIRCRAFTTYPE: 3673 House->AircraftFactories--; 3674 break; 3675 3676 default: 3677 break; 3678 } 3679 3680 #ifdef NEVER 3681 if (IsOwnedByPlayer && !ScenarioInit) { 3682 Map.Recalc(); 3683 } 3684 if (!House->IsHuman) { 3685 Update_Specials(); 3686 } 3687 #endif 3688 3689 /* 3690 ** Flag that both owners now need to update their buildable lists. 3691 */ 3692 House->IsRecalcNeeded = true; 3693 newowner->IsRecalcNeeded = true; 3694 3695 HouseClass * oldowner = House; // Added for RA AI in TD. ST - 7/26/2019 9:25AM 3696 3697 IsCaptured = true; 3698 TechnoClass::Captured(newowner); 3699 3700 3701 #ifdef USE_RA_AI 3702 // 3703 // Added for RA AI in TD. ST - 7/26/2019 9:25AM 3704 // 3705 oldowner->Recalc_Center(); 3706 House->Recalc_Center(); 3707 #endif 3708 3709 SmudgeType bib; 3710 CELL cell = Coord_Cell(Coord); 3711 if (Class->Bib_And_Offset(bib, cell)) { 3712 SmudgeClass * smudge = new SmudgeClass(bib); 3713 if (smudge) { 3714 smudge->Disown(cell); 3715 delete smudge; 3716 } 3717 new SmudgeClass(bib, Cell_Coord(cell), House->Class->House); 3718 } 3719 3720 /* 3721 ** Increment the factory count for the new owner. 3722 */ 3723 switch (Class->ToBuild) { 3724 case RTTI_UNITTYPE: 3725 House->UnitFactories++; 3726 break; 3727 3728 case RTTI_INFANTRYTYPE: 3729 House->InfantryFactories++; 3730 break; 3731 3732 case RTTI_BUILDINGTYPE: 3733 House->BuildingFactories++; 3734 break; 3735 3736 case RTTI_AIRCRAFTTYPE: 3737 House->AircraftFactories++; 3738 break; 3739 3740 default: 3741 break; 3742 } 3743 3744 IsRepairing = false; 3745 Grand_Opening(true); 3746 House->Harvested(booty); 3747 3748 Mark(MARK_CHANGE); 3749 3750 /* 3751 ** Perform a look operation when catpured if it was the player 3752 ** that performed the capture. 3753 */ 3754 if (GameToPlay == GAME_GLYPHX_MULTIPLAYER && House->IsHuman) { 3755 Look(false); 3756 } else { 3757 if (House == PlayerPtr) { 3758 Look(false); 3759 } 3760 } 3761 3762 if (oldowner) { 3763 oldowner->Check_Pertinent_Structures(); 3764 } 3765 3766 return(true); 3767 } 3768 return(false); 3769 } 3770 3771 3772 /*********************************************************************************************** 3773 * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * 3774 * * 3775 * The coordinate value returned from this function should be used for sorting purposes. * 3776 * It has special offset adjustment applied so that vehicles don't overlap (as much). * 3777 * * 3778 * INPUT: none * 3779 * * 3780 * OUTPUT: Returns with a coordinate value suitable to be used for sorting. * 3781 * * 3782 * WARNINGS: none * 3783 * * 3784 * HISTORY: * 3785 * 05/23/1995 JLB : Created. * 3786 * 06/19/1995 JLB : Handles buildings that come with bibs built-in. * 3787 *=============================================================================================*/ 3788 COORDINATE BuildingClass::Sort_Y(void) const 3789 { 3790 Validate(); 3791 if (*this == STRUCT_REPAIR) { 3792 return(Coord); 3793 } 3794 if (*this == STRUCT_HELIPAD) { 3795 return(Center_Coord()); 3796 } 3797 if (*this == STRUCT_BARRACKS /*|| *this == STRUCT_POWER*/) { 3798 return(Center_Coord()); 3799 } 3800 if (*this == STRUCT_REFINERY) { 3801 return(Center_Coord()); 3802 } 3803 return(Coord_Add(Center_Coord(), XY_Coord(0, (Class->Height()*256)/3))); 3804 } 3805 3806 3807 /*********************************************************************************************** 3808 * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * 3809 * * 3810 * This routine will determine if the building can be placed down at the location * 3811 * specified. * 3812 * * 3813 * INPUT: cell -- The cell to examine. This is usually the cell of the upper left corner * 3814 * of the building if it were to be placed down. * 3815 * * 3816 * OUTPUT: Returns with the move legality value for placement at the location specified. This * 3817 * will either be MOVE_OK or MOVE_NO. * 3818 * * 3819 * WARNINGS: none * 3820 * * 3821 * HISTORY: * 3822 * 06/25/1995 JLB : Created. * 3823 *=============================================================================================*/ 3824 MoveType BuildingClass::Can_Enter_Cell(CELL cell, FacingType) const 3825 { 3826 Validate(); 3827 3828 if (*this == STRUCT_CONST && IsDown) { 3829 return(Map[cell].Is_Generally_Clear() ? MOVE_OK : MOVE_NO); 3830 } 3831 3832 return(Class->Legal_Placement(cell) ? MOVE_OK : MOVE_NO); 3833 } 3834 3835 3836 /*********************************************************************************************** 3837 * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * 3838 * * 3839 * Determines if the player can sell this building. Selling is possible if the building * 3840 * is not currently in construction or deconstruction animation. * 3841 * * 3842 * INPUT: none * 3843 * * 3844 * OUTPUT: Can the building be demolished at this time? * 3845 * * 3846 * WARNINGS: none * 3847 * * 3848 * HISTORY: * 3849 * 06/25/1995 JLB : Created. * 3850 * 07/01/1995 JLB : If there is no buildup data, then the building can't be sold. * 3851 * 07/17/1995 JLB : Cannot sell a refinery that has a harvester attached. * 3852 *=============================================================================================*/ 3853 bool BuildingClass::Can_Demolish(void) const 3854 { 3855 Validate(); 3856 // !Mission != sounds a bit fishy. ST - 2018 3857 //if (Class->Get_Buildup_Data() && BState != BSTATE_CONSTRUCTION && !Mission != MISSION_DECONSTRUCTION && Mission != MISSION_CONSTRUCTION) { 3858 if (Class->IsUnsellable) return(false); 3859 if (Class->Get_Buildup_Data() && BState != BSTATE_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION && Mission != MISSION_CONSTRUCTION) { 3860 if (*this == STRUCT_REFINERY && Is_Something_Attached()) return(false); 3861 return(true); 3862 } 3863 return(false); 3864 } 3865 3866 3867 bool BuildingClass::Can_Demolish_Unit(void) const 3868 { 3869 return(*this == STRUCT_REPAIR && In_Radio_Contact() && Distance(Contact_With_Whom()) < 0x0080); 3870 } 3871 3872 3873 bool BuildingClass::Can_Capture(void) const 3874 { 3875 bool can_capture = Class->IsCaptureable && Mission != MISSION_DECONSTRUCTION; 3876 3877 // Override capturable state if this building has a capture win trigger 3878 if (GameToPlay == GAME_NORMAL) { 3879 if (!House->IsHuman && Trigger != NULL && Trigger->Action == TriggerClass::ACTION_WINLOSE) { 3880 can_capture = true; 3881 } 3882 } 3883 3884 return(can_capture); 3885 } 3886 3887 3888 /*********************************************************************************************** 3889 * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * 3890 * * 3891 * Buildings that can attack are given this mission. They will wait until a suitable target * 3892 * comes within range and then launch into the attack mission. Buildings that have no * 3893 * weaponry will just sit in this routine forever. * 3894 * * 3895 * INPUT: none * 3896 * * 3897 * OUTPUT: Returns with the number of game frames to delay before this routine will be called * 3898 * again. * 3899 * * 3900 * WARNINGS: none * 3901 * * 3902 * HISTORY: * 3903 * 06/25/1995 JLB : Created. * 3904 *=============================================================================================*/ 3905 int BuildingClass::Mission_Guard(void) 3906 { 3907 Validate(); 3908 /* 3909 ** If this building has a weapon, then search for a target to attack. When 3910 ** a target is found, switch into attack mode to deal with the threat. 3911 */ 3912 if (Class->Primary != WEAPON_NONE) { 3913 3914 /* 3915 ** Weapon equipped buildings are ALWAYS ready to launch into another mission if 3916 ** they are sitting around in guard mode. 3917 */ 3918 IsReadyToCommence = true; 3919 3920 /* 3921 ** If there is no target available, then search for one. 3922 */ 3923 if (!Target_Legal(TarCom)) { 3924 ThreatType threat = THREAT_NORMAL; 3925 Assign_Target(Greatest_Threat(threat)); 3926 } 3927 3928 /* 3929 ** There is a valid target. Switch into attack mode right away. 3930 */ 3931 if (Target_Legal(TarCom)) { 3932 Assign_Mission(MISSION_ATTACK); 3933 return(1); 3934 } 3935 } else { 3936 3937 /* 3938 ** This is the very simple state machine that basically does 3939 ** nothing. This is the mode that non weapon equipped buildings 3940 ** are normally in. 3941 */ 3942 enum { 3943 INITIAL_ENTRY, 3944 IDLE 3945 }; 3946 switch (Status) { 3947 case INITIAL_ENTRY: 3948 Begin_Mode(BSTATE_IDLE); 3949 Status = IDLE; 3950 break; 3951 3952 case IDLE: 3953 /* 3954 ** Special case to break out of guard mode if this is a repair 3955 ** facility and there is a customer waiting at the grease pit. 3956 */ 3957 if (*this == STRUCT_REPAIR && 3958 In_Radio_Contact() && 3959 Contact_With_Whom()->Is_Techno() && 3960 ((TechnoClass *)Contact_With_Whom())->Mission == MISSION_ENTER && 3961 Distance(Contact_With_Whom()) < 0x0040 && 3962 Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { 3963 3964 Assign_Mission(MISSION_REPAIR); 3965 return(1); 3966 } 3967 break; 3968 } 3969 return(TICKS_PER_SECOND*5); 3970 } 3971 return(TICKS_PER_SECOND/2); 3972 } 3973 3974 3975 /*********************************************************************************************** 3976 * BuildingClass::Mission_Construction -- Handles mission construction. * 3977 * * 3978 * This routine will handle mission construction. When this mission is complete, the * 3979 * building will begin normal operation. * 3980 * * 3981 * INPUT: none * 3982 * * 3983 * OUTPUT: Returns with the number of game frames to delay before calling this routine * 3984 * again. * 3985 * * 3986 * WARNINGS: none * 3987 * * 3988 * HISTORY: * 3989 * 06/25/1995 JLB : Created. * 3990 *=============================================================================================*/ 3991 int BuildingClass::Mission_Construction(void) 3992 { 3993 Validate(); 3994 enum { 3995 INITIAL, 3996 DURING 3997 }; 3998 switch (Status) { 3999 case INITIAL: 4000 Begin_Mode(BSTATE_CONSTRUCTION); 4001 Transmit_Message(RADIO_BUILDING); 4002 //Changed for multiplayer ST - 3/13/2019 5:31PM 4003 if (Is_Owned_By_Player()) { 4004 //if (IsOwnedByPlayer) { 4005 Sound_Effect(VOC_CONSTRUCTION, Coord); 4006 } 4007 Status = DURING; 4008 break; 4009 4010 case DURING: 4011 if (IsReadyToCommence) { 4012 4013 /* 4014 ** When construction is complete, then transmit this 4015 ** to the construction yard so that it can stop its 4016 ** construction animation. 4017 */ 4018 Transmit_Message(RADIO_COMPLETE); // "I'm finished." 4019 Transmit_Message(RADIO_OVER_OUT); // "You're free." 4020 Begin_Mode(BSTATE_IDLE); 4021 // Construction yard already called this on reveal in normal game mode, so don't do twice 4022 if (*this != STRUCT_CONST || GameToPlay != GAME_NORMAL) { 4023 Grand_Opening(); 4024 } 4025 Assign_Mission(MISSION_GUARD); 4026 PrimaryFacing = Class->StartFace; 4027 } 4028 break; 4029 } 4030 return(1); 4031 } 4032 4033 4034 /*********************************************************************************************** 4035 * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * 4036 * * 4037 * This state machine is only used when the building is deconstructing as a result of * 4038 * selling. When this mission is finished, the building will no longer exist. * 4039 * * 4040 * INPUT: none * 4041 * * 4042 * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * 4043 * * 4044 * WARNINGS: none * 4045 * * 4046 * HISTORY: * 4047 * 06/25/1995 JLB : Created. * 4048 * 08/13/1995 JLB : Enable selling of units on a repair bay. * 4049 * 08/20/1995 JLB : Scatters infantry from scattered starting points. * 4050 *=============================================================================================*/ 4051 int BuildingClass::Mission_Deconstruction(void) 4052 { 4053 Validate(); 4054 /* 4055 ** Always force repair off. 4056 */ 4057 Repair(0); 4058 4059 enum { 4060 INITIAL, 4061 HOLDING, 4062 DURING 4063 }; 4064 switch (Status) { 4065 case INITIAL: 4066 4067 /* 4068 ** Special check for the repair bay which has the ability to sell 4069 ** whatever is on it. If there is something on the repair bay, then 4070 ** it will be sold. If there is nothing on the repair bay, then 4071 ** the repair bay itself will be sold. 4072 */ 4073 if (Can_Demolish_Unit() && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { 4074 TechnoClass * tech = Contact_With_Whom(); 4075 Transmit_Message(RADIO_OVER_OUT); 4076 tech->Sell_Back(1); 4077 // House->Refund_Money(tech->Refund_Amount()); 4078 // tech->Limbo(); 4079 IsReadyToCommence = true; 4080 Assign_Mission(MISSION_GUARD); 4081 return(1); 4082 } 4083 4084 IsReadyToCommence = false; 4085 Transmit_Message(RADIO_RUN_AWAY); 4086 Status = HOLDING; 4087 break; 4088 4089 case HOLDING: 4090 if (!IsTethered) { 4091 4092 /* 4093 ** The crew will evacuate from the building. The number of crew 4094 ** members leaving is equal to the unrecovered cost of the building 4095 ** divided by 100 (the typical cost of a minigunner infantryman). 4096 */ 4097 if (!Target_Legal(ArchiveTarget) || !Special.IsMCVDeploy || *this != STRUCT_CONST) { 4098 int divisor = 200; 4099 if (IsCaptured) divisor *= 2; 4100 int count = (Class->Raw_Cost()+(divisor-1)) / divisor; 4101 bool engine = false; 4102 count = Bound(count, 1, 5); 4103 4104 while (count) { 4105 4106 /* 4107 ** Ensure that the player only gets ONE engineer and not from a captured 4108 ** construction yard. 4109 */ 4110 InfantryType typ = Crew_Type(); 4111 while (typ == INFANTRY_E7 && engine) { 4112 typ = Crew_Type(); 4113 } 4114 if (typ == INFANTRY_E7) engine = true; 4115 4116 InfantryClass * infantry = new InfantryClass(typ, House->Class->House); 4117 if (infantry) { 4118 ScenarioInit++; 4119 COORDINATE coord = Coord_Add(Center_Coord(), XYP_COORD(0, -12)); 4120 coord = Map[Coord_Cell(coord)].Closest_Free_Spot(coord, false); 4121 4122 if (infantry->Unlimbo(coord, DIR_N)) { 4123 if (infantry->Class->IsNominal) infantry->IsTechnician = true; 4124 ScenarioInit--; 4125 infantry->Scatter(0, true); 4126 ScenarioInit++; 4127 infantry->Assign_Mission(MISSION_GUARD_AREA); 4128 } else { 4129 delete infantry; 4130 } 4131 ScenarioInit--; 4132 } 4133 count--; 4134 } 4135 } 4136 4137 // MBL 07.10.2020 - In 1v1, sometimes both players will hear this SFX, or neither player will hear it 4138 // Making it so all players hear it positionally in the map; Per thread discussion in https://jaas.ea.com/browse/TDRA-7245 4139 // 4140 #if 0 4141 //Changed for multiplayer ST - 3/13/2019 5:31PM 4142 if (Is_Owned_By_Player()) { 4143 //if (IsOwnedByPlayer) { 4144 Sound_Effect(VOC_CASHTURN, Coord); 4145 } 4146 #else 4147 Sound_Effect(VOC_CASHTURN, Coord); 4148 #endif 4149 4150 /* 4151 ** Destroy all attached objects. ST - 4/24/2020 9:38PM 4152 */ 4153 while (Attached_Object()) { 4154 FootClass * obj = Detach_Object(); 4155 4156 Detach_All(true); 4157 delete obj; 4158 } 4159 4160 Transmit_Message(RADIO_OVER_OUT); 4161 Status = DURING; 4162 Begin_Mode(BSTATE_CONSTRUCTION); 4163 IsReadyToCommence = false; 4164 IsSurvivorless = true; 4165 break; 4166 } 4167 Transmit_Message(RADIO_RUN_AWAY); 4168 break; 4169 4170 case DURING: 4171 if (IsReadyToCommence) { 4172 4173 /* 4174 ** Construction yards that deconstruct, really just revert back 4175 ** to an MCV. 4176 */ 4177 if (Target_Legal(ArchiveTarget) && Special.IsMCVDeploy && *this == STRUCT_CONST && House->IsHuman) { 4178 ScenarioInit++; 4179 UnitClass * unit = new UnitClass(UNIT_MCV, House->Class->House); 4180 ScenarioInit--; 4181 if (unit) { 4182 4183 /* 4184 ** Unlimbo the MCV onto the map. The MCV should start in the same 4185 ** health condition that the construction yard was in. 4186 */ 4187 int ratio = Health_Ratio(); 4188 int money = Refund_Amount(); 4189 TARGET arch = ArchiveTarget; 4190 COORDINATE place = Coord_Snap(Adjacent_Cell(Coord, DIR_SE)); 4191 4192 Delete_This(); 4193 4194 if (unit->Unlimbo(place, DIR_SW)) { 4195 unit->Strength = Fixed_To_Cardinal(unit->Class_Of().MaxStrength, ratio); 4196 4197 /* 4198 ** Lift the move destination from the building and assign 4199 ** it to the unit. 4200 */ 4201 if (Target_Legal(arch)) { 4202 unit->Assign_Destination(arch); 4203 unit->Assign_Mission(MISSION_MOVE); 4204 } 4205 } else { 4206 4207 /* 4208 ** If, for some strange reason, the MCV could not be placed on the 4209 ** map, then give the player some money to compensate. 4210 */ 4211 House->Refund_Money(money); 4212 } 4213 } else { 4214 House->Refund_Money(Refund_Amount()); 4215 Delete_This(); 4216 } 4217 4218 } else { 4219 4220 /* 4221 ** A sold building still counts as a kill, but it just isn't directly 4222 ** attributed to the enemy. 4223 */ 4224 WhoLastHurtMe = HOUSE_NONE; 4225 Record_The_Kill(NULL); 4226 4227 /* 4228 ** The player gets part of the money back for the sell. 4229 */ 4230 House->Refund_Money(Refund_Amount()); 4231 Limbo(); 4232 4233 if (House) { 4234 House->Check_Pertinent_Structures(); 4235 } 4236 4237 /* 4238 ** Finally, delete the building from the game. 4239 */ 4240 Delete_This(); 4241 } 4242 House->IsRecalcNeeded = true; 4243 } 4244 break; 4245 } 4246 return(1); 4247 } 4248 4249 4250 /*********************************************************************************************** 4251 * BuildingClass::Mission_Attack -- Handles attack mission for building. * 4252 * * 4253 * Buildings that can attack are processed by this attack mission state machine. * 4254 * * 4255 * INPUT: none * 4256 * * 4257 * OUTPUT: Returns with the number of game frames to delay before calling this routine * 4258 * again. * 4259 * * 4260 * WARNINGS: none * 4261 * * 4262 * HISTORY: * 4263 * 06/25/1995 JLB : Created. * 4264 *=============================================================================================*/ 4265 int BuildingClass::Mission_Attack(void) 4266 { 4267 Validate(); 4268 if (*this == STRUCT_SAM) { 4269 switch (Status) { 4270 4271 /* 4272 ** The launcher is underground and awaiting the acquisition of 4273 ** a target. 4274 */ 4275 case SAM_UNDERGROUND: 4276 IsReadyToCommence = true; 4277 if (Target_Legal(TarCom)) { 4278 Set_Rate(2); 4279 Set_Stage(0); 4280 Status = SAM_RISING; 4281 return(1); 4282 } else { 4283 Assign_Mission(MISSION_GUARD); 4284 } 4285 break; 4286 4287 /* 4288 ** The launcher is rising into the ready position so that it 4289 ** may rotate to face the target. 4290 */ 4291 case SAM_RISING: 4292 if (Fetch_Stage() == 15) { 4293 Set_Rate(0); 4294 PrimaryFacing = DIR_N; 4295 if (!Target_Legal(TarCom)) { 4296 Status = SAM_LOWERING; 4297 } else { 4298 Status = SAM_READY; 4299 } 4300 } 4301 return(1); 4302 4303 /* 4304 ** This is the target tracking state of the launcher. It will rotate 4305 ** to face the current TarCom of the launcher. 4306 */ 4307 case SAM_READY: 4308 if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { 4309 Assign_Target(TARGET_NONE); 4310 Status = SAM_LOCKING; 4311 return(TICKS_PER_SECOND); 4312 } else { 4313 if (!PrimaryFacing.Is_Rotating()) { 4314 DirType facing = Direction(TarCom); 4315 if (PrimaryFacing.Difference(facing)) { 4316 PrimaryFacing.Set_Desired(facing); 4317 } else { 4318 Status = SAM_FIRING; 4319 } 4320 } 4321 } 4322 return(1); 4323 4324 /* 4325 ** The launcher is in the process of firing. 4326 */ 4327 case SAM_FIRING: 4328 if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { 4329 Assign_Target(TARGET_NONE); 4330 Status = SAM_LOCKING; 4331 } else { 4332 FireErrorType error = Can_Fire(TarCom, 0); 4333 if (error == FIRE_ILLEGAL || error == FIRE_CANT || error == FIRE_RANGE) { 4334 Assign_Target(TARGET_NONE); 4335 Status = SAM_LOCKING; 4336 } else { 4337 if (error == FIRE_FACING) { 4338 Status = SAM_READY; 4339 } else { 4340 if (error == FIRE_OK) { 4341 Fire_At(TarCom, 0); 4342 Status = SAM_READY2; 4343 return(1); 4344 } 4345 } 4346 } 4347 } 4348 return(1); 4349 4350 case SAM_READY2: 4351 if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { 4352 Assign_Target(TARGET_NONE); 4353 Status = SAM_LOCKING; 4354 return(TICKS_PER_SECOND); 4355 } else { 4356 if (!PrimaryFacing.Is_Rotating()) { 4357 DirType facing = Direction(TarCom); 4358 if (PrimaryFacing.Difference(facing)) { 4359 PrimaryFacing.Set_Desired(facing); 4360 } else { 4361 Status = SAM_FIRING2; 4362 } 4363 } 4364 } 4365 return(1); 4366 4367 case SAM_FIRING2: 4368 if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { 4369 Assign_Target(TARGET_NONE); 4370 Status = SAM_LOCKING; 4371 } else { 4372 FireErrorType error = Can_Fire(TarCom, 0); 4373 if (error == FIRE_ILLEGAL || error == FIRE_CANT || error == FIRE_RANGE) { 4374 Assign_Target(TARGET_NONE); 4375 Status = SAM_LOCKING; 4376 } else { 4377 if (error == FIRE_FACING) { 4378 Status = SAM_READY2; 4379 } else { 4380 if (error == FIRE_OK) { 4381 Fire_At(TarCom, 0); 4382 Status = SAM_LOCKING; 4383 return(TICKS_PER_SECOND*3); 4384 } 4385 } 4386 } 4387 } 4388 return(1); 4389 4390 /* 4391 ** Rotating to face north in preparation for lowering to reload. 4392 */ 4393 case SAM_LOCKING: 4394 if (!PrimaryFacing.Is_Rotating()) { 4395 if (PrimaryFacing == DIR_N) { 4396 Set_Rate(2); 4397 Set_Stage(48); 4398 Status = SAM_LOWERING; 4399 } else { 4400 PrimaryFacing.Set_Desired(DIR_N); 4401 } 4402 } 4403 return(1); 4404 4405 /* 4406 ** Lowering into the ground in order to reload. 4407 */ 4408 case SAM_LOWERING: 4409 if (Fetch_Stage() >= 63) { 4410 Set_Rate(0); 4411 Set_Stage(0); 4412 Status = SAM_UNDERGROUND; 4413 return(TICKS_PER_SECOND); 4414 } else { 4415 if (Fetch_Rate() == 0) { 4416 Set_Rate(2); 4417 } 4418 } 4419 return(1); 4420 4421 default: 4422 break; 4423 } 4424 4425 } else { 4426 IsReadyToCommence = true; 4427 switch (Can_Fire(TarCom, 0)) { 4428 case FIRE_ILLEGAL: 4429 case FIRE_CANT: 4430 case FIRE_RANGE: 4431 case FIRE_AMMO: 4432 Assign_Target(TARGET_NONE); 4433 Assign_Mission(MISSION_GUARD); 4434 Commence(); 4435 break; 4436 4437 case FIRE_FACING: 4438 PrimaryFacing.Set_Desired(Direction(TarCom)); 4439 return(2); 4440 4441 case FIRE_REARM: 4442 case FIRE_BUSY: 4443 return(1); 4444 4445 case FIRE_CLOAKED: 4446 Do_Uncloak(); 4447 break; 4448 4449 case FIRE_OK: 4450 Fire_At(TarCom, 0); 4451 return(1); 4452 } 4453 } 4454 return(TICKS_PER_SECOND); 4455 } 4456 4457 4458 /*********************************************************************************************** 4459 * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * 4460 * * 4461 * This state machine handles the refinery when it unloads the harvester. * 4462 * * 4463 * INPUT: none * 4464 * * 4465 * OUTPUT: Returns with the number of game frames to delay before calling this routine * 4466 * again. * 4467 * * 4468 * WARNINGS: none * 4469 * * 4470 * HISTORY: * 4471 * 06/25/1995 JLB : Created. * 4472 *=============================================================================================*/ 4473 int BuildingClass::Mission_Harvest(void) 4474 { 4475 Validate(); 4476 enum { 4477 INITIAL, // Dock the Tiberium canister. 4478 WAIT_FOR_DOCK, // Waiting for docking to complete. 4479 MIDDLE, // Offload "bails" of tiberium. 4480 WAIT_FOR_UNDOCK, // Waiting for undocking to complete. 4481 EXITING // Cause the harvester to drive away. 4482 }; 4483 switch (Status) { 4484 case INITIAL: 4485 Begin_Mode(BSTATE_ACTIVE); 4486 Status = WAIT_FOR_DOCK; 4487 break; 4488 4489 case WAIT_FOR_DOCK: 4490 if (IsReadyToCommence) { 4491 IsReadyToCommence = false; 4492 Status = MIDDLE; 4493 Begin_Mode(BSTATE_AUX1); 4494 } 4495 break; 4496 4497 case MIDDLE: 4498 if (IsReadyToCommence) { 4499 IsReadyToCommence = false; 4500 4501 /* 4502 ** Force any bib squaters to scatter. 4503 */ 4504 bool old = Special.IsScatter; 4505 Special.IsScatter = true; 4506 Map[Adjacent_Cell(Coord_Cell(Center_Coord()), DIR_SW)].Incoming(0, true); 4507 Special.IsScatter = old; 4508 4509 FootClass * techno = Attached_Object(); 4510 if (techno) { 4511 int bail = techno->Offload_Tiberium_Bail(); 4512 4513 if (bail) { 4514 House->Harvested(bail); 4515 if (techno->Tiberium_Load()) { 4516 return(1); 4517 } 4518 } 4519 } 4520 Begin_Mode(BSTATE_AUX2); 4521 Status = WAIT_FOR_UNDOCK; 4522 } 4523 break; 4524 4525 case WAIT_FOR_UNDOCK: 4526 if (IsReadyToCommence) { 4527 4528 /* 4529 ** Detach harvester and go back into idle state. 4530 */ 4531 Exit_Object(Detach_Object()); 4532 Assign_Mission(MISSION_GUARD); 4533 } 4534 break; 4535 } 4536 return(1); 4537 } 4538 4539 4540 /*********************************************************************************************** 4541 * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * 4542 * * 4543 * This state machine is used when the building is active in some sort of repair or * 4544 * construction mode. The construction yard will animate. The repair facility will repair * 4545 * anything that it docked on it. * 4546 * * 4547 * INPUT: none * 4548 * * 4549 * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * 4550 * * 4551 * WARNINGS: none * 4552 * * 4553 * HISTORY: * 4554 * 06/25/1995 JLB : Created. * 4555 * 06/25/1995 JLB : Handles repair facility * 4556 * 07/29/1995 JLB : Repair rate is controlled by power rating. * 4557 *=============================================================================================*/ 4558 int BuildingClass::Mission_Repair(void) 4559 { 4560 Validate(); 4561 if (*this == STRUCT_CONST) { 4562 enum { 4563 INITIAL, 4564 DURING 4565 }; 4566 switch (Status) { 4567 case INITIAL: 4568 Begin_Mode(BSTATE_ACTIVE); 4569 Status = DURING; 4570 break; 4571 4572 case DURING: 4573 if (!In_Radio_Contact()) { 4574 Assign_Mission(MISSION_GUARD); 4575 } 4576 break; 4577 } 4578 return(1); 4579 } 4580 4581 if (*this == STRUCT_REPAIR) { 4582 enum { 4583 INITIAL, 4584 IDLE, 4585 DURING 4586 }; 4587 switch (Status) { 4588 case INITIAL: 4589 if (!In_Radio_Contact()) { 4590 Begin_Mode(BSTATE_IDLE); 4591 Assign_Mission(MISSION_GUARD); 4592 return(1); 4593 } 4594 IsReadyToCommence = false; 4595 if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Distance(Contact_With_Whom()) < 0x0010) { 4596 Status = IDLE; 4597 return(TICKS_PER_SECOND/4); 4598 } 4599 break; 4600 4601 case IDLE: 4602 if (!In_Radio_Contact()) { 4603 Assign_Mission(MISSION_GUARD); 4604 return(1); 4605 } 4606 4607 if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { 4608 if (Contact_With_Whom()->Health_Ratio() < 0x0100 && Transmit_Message(RADIO_REPAIR) == RADIO_ROGER) { 4609 4610 // MBL 07.06.2020 - Patch 3: Change to TD Legacy: Adding "Repairing" VO for units on repair bay 4611 // Per https://jaas.ea.com/browse/TDRA-7271 4612 if (IsOwnedByPlayer && House) Speak(VOX_REPAIRING, House); 4613 4614 Status = DURING; 4615 Begin_Mode(BSTATE_ACTIVE); 4616 IsReadyToCommence = false; 4617 } else { 4618 if (!House->IsHuman) { 4619 Transmit_Message(RADIO_RUN_AWAY); 4620 } 4621 } 4622 // } else { 4623 // Assign_Mission(MISSION_GUARD); 4624 // return(1); 4625 } 4626 break; 4627 4628 case DURING: 4629 if (!In_Radio_Contact()) { 4630 Begin_Mode(BSTATE_IDLE); 4631 Status = IDLE; 4632 return(1); 4633 } 4634 if (IsReadyToCommence && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { 4635 IsReadyToCommence = false; 4636 long param = Health_Ratio(); 4637 if (Transmit_Message(RADIO_REPAIR, param) != RADIO_ROGER) { 4638 #ifdef OBSOLETE 4639 if (House->Available_Money() < 10) { 4640 Transmit_Message(RADIO_RUN_AWAY); 4641 } 4642 #endif 4643 Begin_Mode(BSTATE_IDLE); 4644 Status = IDLE; 4645 #ifdef OBSOLETE 4646 } else { 4647 int time = Bound(Fixed_To_Cardinal(TICKS_PER_SECOND, House->Power_Fraction()), 0, TICKS_PER_SECOND); 4648 time = (TICKS_PER_SECOND + (TICKS_PER_SECOND/2)) - time; 4649 return(time); 4650 #endif 4651 } 4652 } 4653 break; 4654 } 4655 return(TICKS_PER_SECOND/2); 4656 } 4657 4658 if (*this == STRUCT_HELIPAD) { 4659 enum { 4660 INITIAL, 4661 DURING 4662 }; 4663 switch (Status) { 4664 case INITIAL: 4665 if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Transmit_Message(RADIO_PREPARED) == RADIO_NEGATIVE) { 4666 Begin_Mode(BSTATE_ACTIVE); 4667 Contact_With_Whom()->Assign_Mission(MISSION_SLEEP); 4668 Status = DURING; 4669 return(1); 4670 } 4671 Assign_Mission(MISSION_GUARD); 4672 break; 4673 4674 case DURING: 4675 if (IsReadyToCommence) { 4676 if (!In_Radio_Contact() || Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_NEGATIVE) { 4677 Assign_Mission(MISSION_GUARD); 4678 return(1); 4679 } 4680 4681 if (Transmit_Message(RADIO_PREPARED) == RADIO_ROGER) { 4682 Contact_With_Whom()->Assign_Mission(MISSION_GUARD); 4683 Assign_Mission(MISSION_GUARD); 4684 return(1); 4685 } 4686 4687 if (Transmit_Message(RADIO_RELOAD) != RADIO_ROGER) { 4688 Assign_Mission(MISSION_GUARD); 4689 Contact_With_Whom()->Assign_Mission(MISSION_GUARD); 4690 return(1); 4691 } else { 4692 int time = Bound(Fixed_To_Cardinal(TICKS_PER_SECOND, House->Power_Fraction()), 0, TICKS_PER_SECOND); 4693 time = (TICKS_PER_SECOND*3) - time; 4694 IsReadyToCommence = false; 4695 return(time); 4696 } 4697 } 4698 break; 4699 } 4700 return(3); 4701 } 4702 return(TICKS_PER_SECOND); 4703 } 4704 4705 4706 /*********************************************************************************************** 4707 * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * 4708 * * 4709 * This handles the Temple of Nod launching its nuclear missile. * 4710 * * 4711 * INPUT: none * 4712 * * 4713 * OUTPUT: Returns with the number of frames to delay before calling this routine again. * 4714 * * 4715 * WARNINGS: none * 4716 * * 4717 * HISTORY: * 4718 * 07/04/1995 JLB : Commented. * 4719 *=============================================================================================*/ 4720 int BuildingClass::Mission_Missile(void) 4721 { 4722 Validate(); 4723 enum { 4724 INITIAL, 4725 DOOR_OPENING, 4726 LAUNCH_UP, 4727 LAUNCH_DOWN, 4728 DONE_LAUNCH 4729 }; 4730 4731 if (*this == STRUCT_TEMPLE) { 4732 switch (Status) { 4733 4734 /* 4735 ** The initial case is responsible for starting the door 4736 ** opening on the building. 4737 */ 4738 case INITIAL: 4739 IsReadyToCommence = false; 4740 Begin_Mode(BSTATE_ACTIVE); 4741 Status = DOOR_OPENING; 4742 return(1); 4743 4744 /* 4745 ** This polls for the case when the door is actually open and 4746 ** then kicks off the missile smoke. 4747 */ 4748 case DOOR_OPENING: 4749 if (IsReadyToCommence) { 4750 Begin_Mode(BSTATE_IDLE); 4751 new AnimClass(ANIM_ATOM_DOOR, Center_Coord()); 4752 Status = LAUNCH_UP; 4753 return(14); 4754 } 4755 return(1); 4756 4757 /* 4758 ** Once the smoke has been going for a little while this 4759 ** actually handles launching the missile into the air. 4760 */ 4761 case LAUNCH_UP: 4762 { 4763 BulletClass *bullet = new BulletClass(BULLET_NUKE_UP); 4764 if (bullet) { 4765 COORDINATE launch = Coord_Move(Center_Coord(), (DirType)1, 0x1A0); 4766 bullet->Assign_Target(TARGET_NONE); 4767 bullet->Payback = NULL; 4768 bullet->Strength = 1; 4769 if (!bullet->Unlimbo(launch, DIR_N)) { 4770 delete bullet; 4771 bullet = NULL; 4772 } else { 4773 bullet->PrimaryFacing.Set_Current(DIR_N); 4774 Sound_Effect(VOC_NUKE_FIRE, launch); 4775 4776 // MBL 03.27.2020 This is never getting triggered for any player in multiplayer, so removing the check (https://jaas.ea.com/browse/TDRA-5458) 4777 // if (House == PlayerPtr) 4778 { 4779 Speak(VOX_NUKE_LAUNCHED); // "NUKLNCH1" - "Nuclear Weapon Launched" 4780 } 4781 } 4782 } 4783 4784 if (bullet) { 4785 Status = LAUNCH_DOWN; 4786 return(8 * TICKS_PER_SECOND); 4787 } 4788 } 4789 return(1); 4790 4791 /* 4792 ** Once the missile is in the air, this handles waiting for 4793 ** the missile to be off the screen and then launching one down 4794 ** over the target. 4795 */ 4796 case LAUNCH_DOWN: 4797 { 4798 BulletClass *bullet = new BulletClass(BULLET_NUKE_DOWN); 4799 if (bullet) { 4800 // Theme.Queue_Song(THEME_NONE); 4801 COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), 1)); 4802 bullet->Assign_Target(::As_Target(House->NukeDest)); 4803 4804 // MBL 05.20.2020 4805 // Fix for Nuke or Atom Bomb killing structures and units during animation sequence and not getting kills tracked 4806 // Per https://jaas.ea.com/browse/TDRA-6610 4807 // 4808 // bullet->Payback = NULL; 4809 bullet->Payback = this; 4810 4811 bullet->Strength = 1; 4812 if (!bullet->Unlimbo(start, DIR_S)) { 4813 delete bullet; 4814 } else { 4815 bullet->PrimaryFacing.Set_Current(DIR_S); 4816 } 4817 Speak(VOX_INCOMING_NUKE); // "Nuclear Warhead Approaching" - "NUKE1" 4818 Sound_Effect(VOC_NUKE_FIRE, start); 4819 } 4820 if (bullet) { 4821 Status = DONE_LAUNCH; 4822 return(7 * TICKS_PER_SECOND); 4823 } 4824 } 4825 return(1); 4826 4827 /* 4828 ** Once the missile is done launching this handles allowing 4829 ** the building to sit there with its door open. 4830 */ 4831 case DONE_LAUNCH: 4832 Assign_Mission(MISSION_GUARD); 4833 return(60); 4834 } 4835 } 4836 return(60); 4837 } 4838 4839 4840 /*********************************************************************************************** 4841 * BuildingClass::Revealed -- Reveals the building to the specified house. * 4842 * * 4843 * This routine will reveal the building to the specified house. It will handle updating * 4844 * the sidebar for player owned buildings. A player owned building that hasn't been * 4845 * revealed, is in a state of pseudo-limbo. It cannot be used for any of its special * 4846 * abilities even though it exists on the map for all other purposes. * 4847 * * 4848 * INPUT: house -- The house that this building is being revealed to. * 4849 * * 4850 * OUTPUT: Was this building revealed by this procedure? * 4851 * * 4852 * WARNINGS: none * 4853 * * 4854 * HISTORY: * 4855 * 06/25/1995 JLB : Created. * 4856 *=============================================================================================*/ 4857 bool BuildingClass::Revealed(HouseClass * house) 4858 { 4859 Validate(); 4860 if (TechnoClass::Revealed(house)) { 4861 4862 if (!ScenarioInit) { 4863 House->JustBuilt = Class->Type; 4864 } 4865 House->IsRecalcNeeded = true; 4866 4867 /* 4868 ** Perform any grand opening here so that in the scenarios where a player 4869 ** owned house is not yet revealed, it won't be reflected in the sidebar 4870 ** selection icons. 4871 */ 4872 /* 4873 ** Making a change here to avoid Grand_Opening happening multiple times in MP/skirmish. ST - 7/26/2019 11:26AM 4874 */ 4875 //if (!In_Radio_Contact() && (house == House || GameToPlay != GAME_NORMAL) && Mission != MISSION_CONSTRUCTION) { 4876 if (!In_Radio_Contact() && (house == House && GameToPlay == GAME_NORMAL) && Mission != MISSION_CONSTRUCTION) { 4877 Grand_Opening(); 4878 } 4879 4880 return(true); 4881 } 4882 return(false); 4883 } 4884 4885 4886 /*********************************************************************************************** 4887 * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * 4888 * * 4889 * This routine is called when the exact mode of the building isn't known. By examining * 4890 * the building's condition, this routine will assign an appropriate mission. * 4891 * * 4892 * INPUT: initial -- This this being called during scenario init? * 4893 * * 4894 * OUTPUT: none * 4895 * * 4896 * WARNINGS: none * 4897 * * 4898 * HISTORY: * 4899 * 06/25/1995 JLB : Created. * 4900 *=============================================================================================*/ 4901 void BuildingClass::Enter_Idle_Mode(bool initial) 4902 { 4903 Validate(); 4904 /* 4905 ** Assign an appropriate mission for the building. If the ScenarioInit flag is true, then 4906 ** this must be an initial building. Start such buildings in idle state. For other buildings 4907 ** it indicates that it is being placed during game play and thus it must start in 4908 ** the "construction" mission. 4909 */ 4910 MissionType mission = MISSION_GUARD; 4911 if (!initial || ScenarioInit || Debug_Map) { 4912 Begin_Mode(BSTATE_IDLE); 4913 mission = MISSION_GUARD; 4914 } else { 4915 Begin_Mode(BSTATE_CONSTRUCTION); 4916 mission = MISSION_CONSTRUCTION; 4917 } 4918 Assign_Mission(mission); 4919 } 4920 4921 4922 /*************************************************************************** 4923 * BuildingClass::Update_Specials -- removes computer specials * 4924 * * 4925 * * 4926 * * 4927 * INPUT: * 4928 * * 4929 * OUTPUT: * 4930 * * 4931 * WARNINGS: * 4932 * * 4933 * HISTORY: * 4934 * 06/21/1995 PWG : Created. * 4935 *=========================================================================*/ 4936 void BuildingClass::Update_Specials(void) 4937 { 4938 Validate(); 4939 } 4940 4941 4942 /*********************************************************************************************** 4943 * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * 4944 * * 4945 * This routine will determine the number of pips that should be filled in when rendering * 4946 * the building. * 4947 * * 4948 * INPUT: none * 4949 * * 4950 * OUTPUT: Returns the number of pips to display as filled in. * 4951 * * 4952 * WARNINGS: none * 4953 * * 4954 * HISTORY: * 4955 * 06/28/1995 JLB : Created. * 4956 *=============================================================================================*/ 4957 int BuildingClass::Pip_Count(void) const 4958 { 4959 Validate(); 4960 return(Fixed_To_Cardinal(Class->Max_Pips(), House->Tiberium_Fraction())); 4961 } 4962 4963 4964 /*********************************************************************************************** 4965 * BuildingClass::Death_Announcement -- Announce the death of this building. * 4966 * * 4967 * This routine is called when the building is destroyed by "unnatural" means. Typically * 4968 * as a result of combat. If the building is known to the player, then it should be * 4969 * announced. * 4970 * * 4971 * INPUT: source -- The object most directly responsible for the building's death. * 4972 * * 4973 * OUTPUT: none * 4974 * * 4975 * WARNINGS: none * 4976 * * 4977 * HISTORY: * 4978 * 07/04/1995 JLB : Created. * 4979 *=============================================================================================*/ 4980 void BuildingClass::Death_Announcement(TechnoClass const * ) const 4981 { 4982 Validate(); 4983 //Changed for multiplayer ST - 3/13/2019 5:31PM 4984 if (Is_Discovered_By_Player() || Is_Owned_By_Player()) { 4985 //if (IsDiscoveredByPlayer || IsOwnedByPlayer) { 4986 if (House != PlayerPtr && GameToPlay != GAME_NORMAL) { 4987 if (Options.IsDeathAnnounce) Speak(VOX_ENEMY_STRUCTURE); 4988 } else { 4989 if (House == PlayerPtr || Options.IsDeathAnnounce) { 4990 if (!Options.IsDeathAnnounce) { 4991 Speak(VOX_STRUCTURE_LOST); 4992 } else { 4993 switch (House->ActLike) { 4994 case HOUSE_GOOD: 4995 Speak(VOX_GDI_STRUCTURE); 4996 break; 4997 4998 case HOUSE_BAD: 4999 Speak(VOX_NOD_STRUCTURE); 5000 break; 5001 5002 default: 5003 break; 5004 } 5005 } 5006 } 5007 } 5008 } 5009 } 5010 5011 5012 /*********************************************************************************************** 5013 * BuildingClass::Fire_Direction -- Fetches the direction of firing. * 5014 * * 5015 * This routine will return with the default direction to use when firing from this * 5016 * building. This is the facing of the turret except for the case of non-turret equipped * 5017 * buildings that have a weapon (e.g., guard tower). * 5018 * * 5019 * INPUT: none * 5020 * * 5021 * OUTPUT: Returns with the default firing direction for this building. * 5022 * * 5023 * WARNINGS: none * 5024 * * 5025 * HISTORY: * 5026 * 07/04/1995 JLB : Created. * 5027 *=============================================================================================*/ 5028 DirType BuildingClass::Fire_Direction(void) const 5029 { 5030 Validate(); 5031 if (Class->IsTurretEquipped) { 5032 return(PrimaryFacing.Current()); 5033 } 5034 return(Direction(TarCom)); 5035 } 5036 5037 5038 /*********************************************************************************************** 5039 * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * 5040 * * 5041 * Use this routine to fetch the remap table to use. This override function is needed * 5042 * because the default remap table for techno objects presumes the object is a unit. * 5043 * Buildings aren't units. * 5044 * * 5045 * INPUT: none * 5046 * * 5047 * OUTPUT: Returns with the proper remap table to use for this building. * 5048 * * 5049 * WARNINGS: none * 5050 * * 5051 * HISTORY: * 5052 * 07/08/1995 JLB : Created. * 5053 *=============================================================================================*/ 5054 void const * BuildingClass::Remap_Table(void) 5055 { 5056 Validate(); 5057 return(House->Remap_Table(IsBlushing, false)); 5058 } 5059 5060 5061 /*********************************************************************************************** 5062 * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * 5063 * * 5064 * This is the unload mission for a building. This really only applies to the weapon's * 5065 * factory, since it needs the sophistication of an unload mission due to the door * 5066 * animation. * 5067 * * 5068 * INPUT: none * 5069 * * 5070 * OUTPUT: Returns with the number of game frames to delay before calling this routine * 5071 * again. * 5072 * * 5073 * WARNINGS: none * 5074 * * 5075 * HISTORY: * 5076 * 07/29/1995 JLB : Created. * 5077 *=============================================================================================*/ 5078 int BuildingClass::Mission_Unload(void) 5079 { 5080 Validate(); 5081 if (*this == STRUCT_WEAP) { 5082 COORDINATE coord = Adjacent_Cell(Center_Coord(), FACING_SW); 5083 CELL cell = Coord_Cell(coord); 5084 CellClass * cellptr = &Map[cell]; 5085 enum { 5086 INITIAL, 5087 CLEAR_BIB, 5088 OPEN, 5089 LEAVE, 5090 CLOSE 5091 }; 5092 UnitClass * unit; 5093 switch (Status) { 5094 case INITIAL: 5095 unit = (UnitClass *)Contact_With_Whom(); 5096 if (unit) { 5097 unit->Assign_Mission(MISSION_GUARD); 5098 unit->Commence(); 5099 } 5100 Open_Door(2, 11); 5101 Status = CLEAR_BIB; 5102 break; 5103 5104 /* 5105 ** Now that the occupants can peek out the door, they will tell 5106 ** everyone that could be blocking the way, that they should 5107 ** scatter away. 5108 */ 5109 case CLEAR_BIB: 5110 unit = (UnitClass *)Contact_With_Whom(); 5111 if (cellptr->Cell_Unit() || cellptr->Cell_Infantry()) { 5112 cellptr->Incoming(0, true, true); 5113 5114 /* 5115 ** Scatter everything around the weapon's factory door. 5116 */ 5117 for (FacingType f = FACING_FIRST; f < FACING_COUNT; f++) { 5118 CellClass * cptr = cellptr->Adjacent_Cell(f); 5119 if (!cptr) continue; 5120 UnitClass * cellunit = cptr->Cell_Unit(); 5121 if ((cellunit && cellunit != unit) || cptr->Cell_Infantry()) { 5122 cptr->Incoming(coord, true, true); 5123 } 5124 } 5125 } else { 5126 Status = OPEN; 5127 } 5128 break; 5129 5130 case OPEN: 5131 if (Is_Door_Open()) { 5132 unit = (UnitClass *)Contact_With_Whom(); 5133 if (unit) { 5134 unit->Assign_Mission(MISSION_MOVE); 5135 unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, Adjacent_Cell(Center_Coord(), FACING_SW)); 5136 unit->Set_Speed(128); 5137 Status = LEAVE; 5138 } else { 5139 Close_Door(2, 11); 5140 Status = CLOSE; 5141 } 5142 } 5143 break; 5144 5145 case LEAVE: 5146 if (!IsTethered) { 5147 Close_Door(2, 11); 5148 Status = CLOSE; 5149 } 5150 break; 5151 5152 case CLOSE: 5153 if (Is_Door_Closed()) { 5154 Enter_Idle_Mode(); 5155 } 5156 break; 5157 } 5158 return(TICKS_PER_SECOND/2); 5159 } 5160 Assign_Mission(MISSION_GUARD); 5161 return(TICKS_PER_SECOND); 5162 } 5163 5164 5165 /*********************************************************************************************** 5166 * BuildingClass::Power_Output -- Fetches the current power output from this building. * 5167 * * 5168 * This routine will return the current power output for this building. The power output * 5169 * is adjusted according to the damage level of the building. * 5170 * * 5171 * INPUT: none * 5172 * * 5173 * OUTPUT: Returns the current power output for this building. * 5174 * * 5175 * WARNINGS: none * 5176 * * 5177 * HISTORY: * 5178 * 07/29/1995 JLB : Created. * 5179 *=============================================================================================*/ 5180 int BuildingClass::Power_Output(void) const 5181 { 5182 Validate(); 5183 if (Class->Power) { 5184 return(Fixed_To_Cardinal(Class->Power, Cardinal_To_Fixed(Class->MaxStrength, LastStrength))); 5185 } 5186 return(0); 5187 } 5188 5189 5190 /*********************************************************************************************** 5191 * BuildingClass::Detach -- Handles target removal from the game system. * 5192 * * 5193 * This routine is called when the specified target is about to be removed from the game * 5194 * system. * 5195 * * 5196 * INPUT: target -- The target to be removed from this building's targeting computer. * 5197 * * 5198 * all -- Is the target about to be completely eliminated? * 5199 * * 5200 * OUTPUT: none * 5201 * * 5202 * WARNINGS: none * 5203 * * 5204 * HISTORY: * 5205 * 07/29/1995 JLB : Created. * 5206 *=============================================================================================*/ 5207 void BuildingClass::Detach(TARGET target, bool all) 5208 { 5209 Validate(); 5210 TechnoClass::Detach(target, all); 5211 if (target == WhomToRepay) { 5212 WhomToRepay = TARGET_NONE; 5213 } 5214 } 5215 5216 5217 /*********************************************************************************************** 5218 * BuildingClass::Refund_Amount -- Fetches the refund amount if building is sold. * 5219 * * 5220 * This routine will return the amount of money to be refunded to the building's owner * 5221 * if the building is sold. * 5222 * * 5223 * INPUT: none * 5224 * * 5225 * OUTPUT: Returns with the refund amount available for this building. * 5226 * * 5227 * WARNINGS: none * 5228 * * 5229 * HISTORY: * 5230 * 07/29/1995 JLB : Created. * 5231 *=============================================================================================*/ 5232 int BuildingClass::Refund_Amount(void) const 5233 { 5234 Validate(); 5235 int cost = TechnoClass::Refund_Amount(); 5236 5237 /* 5238 ** Add in any Tiberium that was stored within the building. 5239 */ 5240 if (IsV107 && Class->Capacity > 0) { 5241 cost += Fixed_To_Cardinal(Class->Capacity, Cardinal_To_Fixed(House->Capacity, House->Tiberium)); 5242 } 5243 return(cost); 5244 } 5245 5246 5247 /*********************************************************************************************** 5248 * BuildingClass::Crew_Type -- This determines the crew that this object generates. * 5249 * * 5250 * When selling very cheap buildings (such as the silo), a technician will pop out since * 5251 * generating minigunners would be overkill -- the player could use this loophole to * 5252 * gain an advantage. * 5253 * * 5254 * INPUT: none * 5255 * * 5256 * OUTPUT: Returns the infantry type that this building will generate as a survivor. * 5257 * * 5258 * WARNINGS: none * 5259 * * 5260 * HISTORY: * 5261 * 08/05/1995 JLB : Created. * 5262 *=============================================================================================*/ 5263 InfantryType BuildingClass::Crew_Type(void) const 5264 { 5265 Validate(); 5266 switch (Class->Type) { 5267 case STRUCT_STORAGE: 5268 if (Random_Pick(0, 1) == 0) { 5269 return(INFANTRY_C1); 5270 } else { 5271 return(INFANTRY_C7); 5272 } 5273 5274 case STRUCT_CONST: 5275 if (!IsCaptured && House->IsHuman && Random_Pick(0, 3) == 0) { 5276 return(INFANTRY_E7); 5277 } 5278 break; 5279 5280 default: 5281 break; 5282 } 5283 return(TechnoClass::Crew_Type()); 5284 } 5285 5286 5287 /*********************************************************************************************** 5288 * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * 5289 * * 5290 * When this routine is called, it indicates that the building is about to be destroyed * 5291 * or captured. In such a case any production it may be doing, must be abandoned. * 5292 * * 5293 * INPUT: all -- Is the object about the be completely destroyed? * 5294 * * 5295 * OUTPUT: none * 5296 * * 5297 * WARNINGS: none * 5298 * * 5299 * HISTORY: * 5300 * 08/05/1995 JLB : Created. * 5301 *=============================================================================================*/ 5302 void BuildingClass::Detach_All(bool all) 5303 { 5304 Validate(); 5305 /* 5306 ** If it is producing something, then it must be abandoned. 5307 */ 5308 if (Factory) { 5309 Factory->Abandon(); 5310 delete Factory; 5311 Factory = 0; 5312 } 5313 5314 /* 5315 ** If the owner HouseClass is building something, and this building can 5316 ** build that thing, we may be the last building for that house that can 5317 ** build that thing; if so, abandon production of it. 5318 */ 5319 if (House) { 5320 int fnum = -1; 5321 5322 switch (Class->ToBuild) { 5323 case RTTI_AIRCRAFTTYPE: 5324 fnum = House->AircraftFactory; 5325 break; 5326 5327 case RTTI_INFANTRYTYPE: 5328 fnum = House->InfantryFactory; 5329 break; 5330 5331 case RTTI_UNITTYPE: 5332 fnum = House->UnitFactory; 5333 break; 5334 5335 case RTTI_BUILDINGTYPE: 5336 fnum = House->BuildingFactory; 5337 break; 5338 5339 case RTTI_SPECIAL: 5340 fnum = House->SpecialFactory; 5341 break; 5342 5343 } 5344 5345 /* 5346 ** Convert the factory number into a real factory pointer. 5347 */ 5348 FactoryClass * factory = 0; 5349 if (fnum != -1) { 5350 factory = Factories.Raw_Ptr(fnum); 5351 } 5352 5353 /* 5354 ** If a factory was found, then temporarily disable this building and then 5355 ** detmermine if any object that is being produced can still be produced. If 5356 ** not, then the object being produced must be abandoned. 5357 */ 5358 if (factory) { 5359 TechnoClass * object = factory->Get_Object(); 5360 IsInLimbo = true; 5361 if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, House->Class->House)) { 5362 House->Abandon_Production(Class->ToBuild); 5363 } 5364 IsInLimbo = false; 5365 } 5366 } 5367 5368 TechnoClass::Detach_All(all); 5369 } 5370 5371 5372 /*********************************************************************************************** 5373 * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * 5374 * * 5375 * This routine is used to clear the way for placement of the specified object (usually * 5376 * a building). If there are friendly units blocking the placement area, they are told * 5377 * to scatter. Enemy blocking units are attacked. * 5378 * * 5379 * INPUT: techno -- Pointer to the object that is desired to be placed. * 5380 * * 5381 * cell -- The cell that placement wants to occur at. * 5382 * * 5383 * OUTPUT: none * 5384 * * 5385 * WARNINGS: none * 5386 * * 5387 * HISTORY: * 5388 * 08/06/1995 JLB : Created. * 5389 *=============================================================================================*/ 5390 bool BuildingClass::Flush_For_Placement(TechnoClass * techno, CELL cell) 5391 { 5392 Validate(); 5393 bool again = false; 5394 if (techno && cell > 0) { 5395 short const * list = techno->Class_Of().Occupy_List(true); 5396 5397 while (*list != REFRESH_EOL) { 5398 CELL newcell = cell + *list++; 5399 5400 if (Map.In_Radar(newcell)) { 5401 TechnoClass * occupier = Map[newcell].Cell_Techno(); 5402 if (occupier) { 5403 again = true; 5404 if (occupier->House->Is_Ally(this)) { 5405 Map[newcell].Incoming(0, true); 5406 } else { 5407 Base_Is_Attacked(occupier); 5408 } 5409 } 5410 } 5411 } 5412 } 5413 return(again); 5414 } 5415 5416 5417 void BuildingClass::Hidden(void) 5418 { 5419 // if (IsDiscoveredByPlayer && House->IsHuman) { 5420 // House->Adjust_Drain(-Class->Drain); 5421 // } 5422 TechnoClass::Hidden(); 5423 } 5424 5425 5426 CELL BuildingClass::Find_Exit_Cell(TechnoClass const * techno) const 5427 { 5428 CELL const *ptr; 5429 CELL origin = Coord_Cell(Coord); 5430 bool found = false; 5431 5432 ptr = Class->ExitList; 5433 if (ptr) { 5434 while (*ptr != REFRESH_EOL) { 5435 CELL cell = origin + *ptr++; 5436 if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { 5437 return(cell); 5438 } 5439 } 5440 } 5441 return(0); 5442 } 5443 5444 /*********************************************************************************************** 5445 * BuildingClass::Can_Player_Move -- Can this building be moved? * 5446 * * 5447 * This routine answers the question 'can this building be moved?' Typically, only the * 5448 * construction yard can be moved and it does this by undeploying back into a MCV. * 5449 * * 5450 * INPUT: none * 5451 * * 5452 * OUTPUT: Can the building move to a new location under player control? * 5453 * * 5454 * WARNINGS: none * 5455 * * 5456 * HISTORY: * 5457 * 10/04/1995 JLB : Created. * 5458 *=============================================================================================*/ 5459 bool BuildingClass::Can_Player_Move(void) const 5460 { 5461 Validate(); 5462 return(*this == STRUCT_CONST && (Mission == MISSION_GUARD) && Special.IsMCVDeploy); 5463 } 5464 5465 /*********************************************************************************************** 5466 * BuildingClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* 5467 * * 5468 * This routine is used by the building placement cursor logic to determine whether the * 5469 * at the current cursor position if the building would be adjacent to another friendly * 5470 * building. In cases where this is not true, then the building cannot be placed at all. * 5471 * This determination is returned by the function. * 5472 * * 5473 * INPUT: homecell -- The cell that the building would like to be placed down at. * 5474 * * 5475 * OUTPUT: bool; Can the pending building object be placed at the present cursor location * 5476 * checking only for proximity to friendly buildings? If this isn't for a * 5477 * building type object, then this routine always returns true. * 5478 * * 5479 * WARNINGS: none * 5480 * * 5481 * HISTORY: * 5482 * 06/06/1994 JLB : Created. * 5483 * 06/07/1994 JLB : Handles concrete check. * 5484 *=============================================================================================*/ 5485 bool BuildingClass::Passes_Proximity_Check(CELL homecell) 5486 { 5487 /* 5488 ** In editor mode, the proximity check always passes. 5489 */ 5490 if (Debug_Map || !House->IsHuman) { 5491 return(true); 5492 } 5493 5494 /* 5495 ** Scan through all cells that the building foundation would cover. If any adjacent 5496 ** cells to these are of friendly persuasion, then consider the proximity check to 5497 ** have been a success. 5498 */ 5499 short const * ptr = Occupy_List(true); 5500 while (*ptr != REFRESH_EOL) { 5501 CELL cell = homecell + *ptr++; 5502 5503 if (!Map.In_Radar(cell)) return(false); 5504 5505 for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { 5506 CELL newcell = Adjacent_Cell(cell, facing); 5507 5508 BuildingClass * base = Map[newcell].Cell_Building(); 5509 5510 /* 5511 ** The special cell ownership flag allows building adjacent 5512 ** to friendly walls and bibs even though there is no official 5513 ** building located there. 5514 */ 5515 if (Map[newcell].Owner == House->Class->House) { 5516 return(true); 5517 } 5518 5519 if (base && base->House->Class->House == House->Class->House) { 5520 return(true); 5521 } 5522 } 5523 } 5524 return(false); 5525 }