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