TECHNO.CPP (318069B)
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/TECHNO.CPP 5 3/17/97 1:28a Steve_tall $ */ 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 : BASE.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : May 8, 1994 * 28 * * 29 * Last Update : November 1, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * TechnoClass::AI -- Handles AI processing for techno object. * 34 * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. * 35 * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. * 36 * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. * 37 * TechnoClass::Area_Modify -- Determine the area scan modifier for the cell. * 38 * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * 39 * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * 40 * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * 41 * TechnoClass::Can_Fire -- Determines if this techno object can fire. * 42 * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order* 43 * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * 44 * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * 45 * TechnoClass::Can_Teleport_Here -- Checks cell to see if a valid teleport destination. * 46 * TechnoClass::Captured -- Handles capturing this object. * 47 * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * 48 * TechnoClass::Cloaking_AI -- Perform the AI maintenance for a cloaking device. * 49 * TechnoClass::Combat_Damage -- Fetch the amount of damage infliced by the specified weapon.* 50 * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * 51 * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * 52 * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * 53 * TechnoClass::Detach -- Handles removal of target from tracking system. * 54 * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * 55 * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * 56 * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * 57 * TechnoClass::Draw_It -- Draws the health bar (if necessary). * 58 * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * 59 * TechnoClass::Electric_Zap -- Fires electric zap at the target specified. * 60 * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * 61 * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * 62 * TechnoClass::Evaluate_Just_Cell -- Evaluate a cell as a target by itself. * 63 * TechnoClass::Evaluate_Object -- Determines score value of specified object. * 64 * TechnoClass::Exit_Object -- Causes specified object to leave this object. * 65 * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * 66 * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * 67 * TechnoClass::Fire_At -- Fires projectile at target specified. * 68 * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * 69 * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * 70 * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * 71 * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * 72 * TechnoClass::Hidden -- Returns the object back into the hidden state. * 73 * TechnoClass::How_Many_Survivors -- Determine the number of survivors to escape. * 74 * TechnoClass::In_Range -- Determines if specified target is within weapon range. * 75 * TechnoClass::In_Range -- Determines if specified target is within weapon range. * 76 * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * 77 * TechnoClass::Is_Allowed_To_Recloak -- Can this object recloak? * 78 * TechnoClass::Is_Allowed_To_Retaliate -- Checks object to see if it can retaliate. * 79 * TechnoClass::Is_In_Same_Zone -- Determine if specified cell is in same zone as object. * 80 * TechnoClass::Is_Players_Army -- Determines if this object is part of the player's army. * 81 * TechnoClass::Is_Ready_To_Cloak -- Determines if this object is ready to begin cloaking. * 82 * TechnoClass::Is_Ready_To_Random_Animate -- Determines if the object should random animate.* 83 * TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? * 84 * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * 85 * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * 86 * TechnoClass::Look -- Performs a look around (map reveal) action. * 87 * TechnoClass::Mark -- Handles marking of techno objects. * 88 * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * 89 * TechnoClass::Owner -- Who is the owner of this object? * 90 * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects.* 91 * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * 92 * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * 93 * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * 94 * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * 95 * TechnoClass::Record_The_Kill -- Records the death of this object. * 96 * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * 97 * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * 98 * TechnoClass::Renovate -- Heal a building to maximum * 99 * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * 100 * TechnoClass::Response_Move -- Handles the voice response to a movement request. * 101 * TechnoClass::Response_Select -- Handles the voice response when selected. * 102 * TechnoClass::Revealed -- Handles revealing an object to the house specified. * 103 * TechnoClass::Risk -- Fetches the risk associated with this object. * 104 * TechnoClass::Select -- Selects object and checks to see if can be selected. * 105 * TechnoClass::Set_Mission -- Forced mission set (used by editor). * 106 * TechnoClass::Stun -- Prepares the object for removal from the game. * 107 * TechnoClass::Take_Damage -- Records damage assessed to this object. * 108 * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * 109 * TechnoClass::TechnoClass -- Constructor for techno type objects. * 110 * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * 111 * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * 112 * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * 113 * TechnoClass::Time_To_Build -- Determines the time it would take to build this. * 114 * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * 115 * TechnoClass::Value -- Fetches the target value for this object. * 116 * TechnoClass::Visual_Character -- Determine the visual character of the object. * 117 * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * 118 * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * 119 * TechnoClass::What_Action -- Determines what action to perform if object is selected. * 120 * TechnoClass::What_Weapon_Should_I_Use -- Determines what is the best weapon to use. * 121 * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * 122 * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * 123 * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * 124 * TechnoTypeClass::Is_Two_Shooter -- Determines if this object is a double shooter. * 125 * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * 126 * TechnoTypeClass::Read_INI -- Reads the techno type data from the INI database. * 127 * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * 128 * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * 129 * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * 130 * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * 131 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 132 133 #include "function.h" 134 135 136 /*************************************************************************** 137 ** Cloaking control values. 138 */ 139 #define MAX_UNCLOAK_STAGE 38 140 141 //Added for getting the input for special character keys from the client 142 // - 6/26/2019 JAS 143 extern bool DLL_Export_Get_Input_Key_State(KeyNumType key); 144 145 146 /*************************************************************************** 147 ** These are the pointers to the special shape data that the units may need. 148 */ 149 void const * TechnoTypeClass::WakeShapes = 0; 150 void const * TechnoTypeClass::TurretShapes = 0; 151 void const * TechnoTypeClass::SamShapes = 0; 152 void const * TechnoTypeClass::MGunShapes = 0; 153 154 //Xlat Tables for French and German 155 // For name overriding 156 //#define NEWNAMES 22 157 #ifdef GERMAN 158 const char* NewName[] = { 159 "Scout Ant", "Kundschafter-Ameise", 160 "Warrior Ant", "Krieger-Ameise", 161 "Fire Ant", "Feuer-Ameise", 162 "Queen Ant", "Ameisenk�nigin", 163 "ATS", "Angriffs-U-Boot", 164 "Tesla Tank", "Telsapanzer", 165 "Volkov", "Modell Volkov", 166 "Chitzkoi", "Roboterhund", 167 "Stavros", "Stavros", 168 "F-A Longbow", "Jagdhubschrauber Longbow", 169 "Civilian Specialist", "Wissenschaftler", 170 "Alloy Facility", "Legierungswerk", 171 NULL, 172 }; 173 174 #endif 175 #ifdef FRENCH 176 const char* NewName[] = { 177 "Scout Ant", "Fourmi de Reconnaissance", 178 "Warrior Ant", "Fourmi Guerri�re", 179 "Fire Ant", "Fourmi Lance-Flammes", 180 "Queen Ant", "Reine des Fourmis", 181 "ATS", "SMTA", 182 "Tesla Tank", "Tank Tesla", 183 "Volkov", "Volkov", 184 "Chitzkoi", "Cyber-Chien", 185 "Stavros", "Stavros", 186 "F-A Longbow", "HAA (H�licopt�re d'Assaut Avanc�)", 187 "Civilian Specialist", "Sp�cialiste Civil", 188 "Alloy Facility", "Usine M�tallurgique", 189 NULL, 190 }; 191 192 #endif 193 194 195 /*************************************************************************** 196 ** Which shape to use depending on which facing is controlled by these arrays. 197 */ 198 int const TechnoClass::BodyShape[32] = {0,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}; 199 200 201 /*********************************************************************************************** 202 * TechnoClass::Is_Players_Army -- Determines if this object is part of the player's army. * 203 * * 204 * The player's army is considered to be all those mobile units that can be selected * 205 * and controlled by the player (they may or may not have weapons in the traditional * 206 * sense). * 207 * * 208 * INPUT: none * 209 * * 210 * OUTPUT: bool; Is this object part of the player's selectable controllable army? * 211 * * 212 * WARNINGS: none * 213 * * 214 * HISTORY: * 215 * 09/23/1996 JLB : Created. * 216 *=============================================================================================*/ 217 bool TechnoClass::Is_Players_Army(void) const 218 { 219 /* 220 ** An object that is dead (or about to be) is not considered part of 221 ** the player's army. 222 */ 223 if (Strength <= 0) { 224 return(false); 225 } 226 227 /* 228 ** If the object is not yet on the map or is otherwise indisposed, then 229 ** don't consider it. 230 */ 231 if (IsInLimbo || !IsLocked) { 232 return(false); 233 } 234 235 /* 236 ** Buildings, although sometimes serving a combat purpose, are not part 237 ** of the player's army. 238 */ 239 if (What_Am_I() == RTTI_BUILDING) { 240 return(false); 241 } 242 243 /* 244 ** If not discoverd by the player, then don't consider it part of the 245 ** player's army (yet). 246 */ 247 if (!Is_Discovered_By_Player()) { 248 return(false); 249 } 250 251 /* 252 ** If not selectable, then not really part of the player's active army. 253 */ 254 if (!Techno_Type_Class()->IsSelectable) { 255 return(false); 256 } 257 258 /* 259 ** If not under player control, then it isn't part of the player's army. 260 */ 261 if (!House->IsPlayerControl) { 262 return(false); 263 } 264 265 return(true); 266 } 267 268 269 /*********************************************************************************************** 270 * TechnoClass::Can_Teleport_Here -- Checks cell to see if a valid teleport destination. * 271 * * 272 * Use this routine to examine the cell specified and if the cell could be a valid * 273 * destination for a teleport, then return true. * 274 * * 275 * INPUT: cell -- The cell to examine as a possible legal teleport destination. * 276 * * 277 * OUTPUT: bool; Is the specified cell a legal teleport destination? * 278 * * 279 * WARNINGS: none * 280 * * 281 * HISTORY: * 282 * 10/08/1996 JLB : Created. * 283 *=============================================================================================*/ 284 bool TechnoClass::Can_Teleport_Here(CELL cell) const 285 { 286 /* 287 ** Infantry are never allowed to teleport. 288 */ 289 if (What_Am_I() == RTTI_INFANTRY) { 290 return(false); 291 } 292 293 /* 294 ** The destination cell must be on the playfield. 295 */ 296 if (!Map.In_Radar(cell)) { 297 return(false); 298 } 299 300 /* 301 ** Teleporting directly onto the flag spot is not allowed. This is due to the 302 ** requirement that entering that location must proceed under normal channels. 303 */ 304 if (Map[cell].Overlay == OVERLAY_FLAG_SPOT) { 305 return(false); 306 } 307 308 /* 309 ** Determine if the object could enter the cell by normal means. If the 310 ** cell is impassable, then it can't be teleported there. 311 */ 312 TechnoTypeClass const * ttype = Techno_Type_Class(); 313 if (!Map[cell].Is_Clear_To_Move(ttype->Speed, true, true, -1, ttype->Speed == SPEED_FLOAT ? MZONE_WATER : MZONE_NORMAL)) { 314 return(false); 315 } 316 317 return(true); 318 } 319 320 321 /*********************************************************************************************** 322 * TechnoClass::What_Weapon_Should_I_Use -- Determines what is the best weapon to use. * 323 * * 324 * This routine will compare the weapons this object is equipped with verses the * 325 * candidate target object. The best weapon to use against the target will be returned. * 326 * Special emphasis is given to weapons that can fire on the target without requiring * 327 * this object to move within range. * 328 * * 329 * INPUT: target -- The candidate target to determine which weapon is best against. * 330 * * 331 * OUTPUT: Returns with an identifier the specifies what weapon to use against the target. * 332 * The return value will be "0" for the primary weapon and "1" for the secondary. * 333 * * 334 * WARNINGS: This routine is called very frequently. It should be as efficient as possible. * 335 * * 336 * HISTORY: * 337 * 08/12/1996 JLB : Created. * 338 *=============================================================================================*/ 339 int TechnoClass::What_Weapon_Should_I_Use(TARGET target) const 340 { 341 if (!Target_Legal(target)) return(0); 342 343 /* 344 ** Fetch the armor of the candidate target object. Presume that if the target 345 ** is not an object, then its armor is equivalent to wood. Who knows why? 346 */ 347 ArmorType armor = ARMOR_WOOD; 348 if (Is_Target_Object(target)) { 349 armor = As_Object(target)->Class_Of().Armor; 350 } 351 352 TechnoTypeClass const * ttype = Techno_Type_Class(); 353 354 /* 355 ** Get the value of the primary weapon verses the candidate target. Increase the 356 ** value of the weapon if it happens to be in range. 357 */ 358 int w1 = 0; 359 WeaponTypeClass const * wptr = ttype->PrimaryWeapon; 360 if (wptr != NULL && wptr->WarheadPtr != NULL) w1 = wptr->WarheadPtr->Modifier[armor] * 1000; 361 if (In_Range(target, 0)) w1 *= 2; 362 FireErrorType ok = Can_Fire(target, 0); 363 if (ok == FIRE_CANT || ok == FIRE_ILLEGAL) w1 = 0; 364 365 /* 366 ** Calculate a similar value for the secondary weapon. 367 */ 368 int w2 = 0; 369 wptr = ttype->SecondaryWeapon; 370 if (wptr != NULL && wptr->WarheadPtr != NULL) w2 = wptr->WarheadPtr->Modifier[armor] * 1000; 371 if (In_Range(target, 1)) w2 *= 2; 372 ok = Can_Fire(target, 1); 373 if (ok == FIRE_CANT || ok == FIRE_ILLEGAL) w2 = 0; 374 375 /* 376 ** Return with the weapon identifier that should be used to fire upon the 377 ** candidate target. 378 */ 379 if (w2 > w1) return(1); 380 return(0); 381 } 382 383 384 /*********************************************************************************************** 385 * TechnoClass::How_Many_Survivors -- Determine the number of survivors to escape. * 386 * * 387 * This routine will determine the number of survivors that should run from this object * 388 * when it is destroyed. * 389 * * 390 * INPUT: none * 391 * * 392 * OUTPUT: Returns with the maximum number of survivors that should run from this object * 393 * when the object gets destroyed. * 394 * * 395 * WARNINGS: none * 396 * * 397 * HISTORY: * 398 * 08/04/1996 JLB : Created. * 399 *=============================================================================================*/ 400 int TechnoClass::How_Many_Survivors(void) const 401 { 402 if (Techno_Type_Class()->IsCrew) { 403 return(1); 404 } 405 return(0); 406 } 407 408 409 /*********************************************************************************************** 410 * TechnoClass::Combat_Damage -- Fetch the amount of damage infliced by the specified weapon. * 411 * * 412 * This routine will examine the specified weapon of this object and determine how much * 413 * damage it could inflict -- best case. * 414 * * 415 * INPUT: which -- Which weapon to consider. If -1 is specified, then the average of both * 416 * weapon types (if present) is returned. * 417 * * 418 * OUTPUT: Returns with the combat damage that could be inflicted by the specified weapon. * 419 * * 420 * WARNINGS: This routine could return a negative number if a medic is scanned. * 421 * * 422 * HISTORY: * 423 * 09/16/1996 JLB : Created. * 424 *=============================================================================================*/ 425 int TechnoClass::Combat_Damage(int which) const 426 { 427 TechnoTypeClass const * ttype = Techno_Type_Class(); 428 429 int divisor = 0; 430 int value = 0; 431 432 if (which == 0 || which == -1) { 433 if (ttype->PrimaryWeapon != NULL) { 434 value += ttype->PrimaryWeapon->Attack; 435 divisor += 1; 436 } 437 } 438 439 if (which == 1 || which == -1) { 440 if (ttype->SecondaryWeapon != NULL) { 441 value += ttype->SecondaryWeapon->Attack; 442 divisor += 1; 443 } 444 } 445 446 if (divisor > 1) { 447 return(value / divisor); 448 } 449 return(value); 450 } 451 452 453 /*********************************************************************************************** 454 * TechnoClass::Is_Allowed_To_Recloak -- Can this object recloak? * 455 * * 456 * Determine is this object can recloak now and returns that info. Usually the answer is * 457 * yes, but it can be overridden be derived classes. * 458 * * 459 * INPUT: none * 460 * * 461 * OUTPUT: bool; Can this object recloak now? (presumes it has the ability to cloak) * 462 * * 463 * WARNINGS: none * 464 * * 465 * HISTORY: * 466 * 07/29/1996 JLB : Created. * 467 *=============================================================================================*/ 468 bool TechnoClass::Is_Allowed_To_Recloak(void) const 469 { 470 return(true); 471 } 472 473 474 /*********************************************************************************************** 475 * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * 476 * * 477 * This routine will determine the coordinate to use when this object fires. The coordinate * 478 * is the location where bullets appear (or fire effects appear) when the object fires * 479 * its weapon. * 480 * * 481 * INPUT: which -- Which weapon is the coordinate to be calculated for? 0 means primary * 482 * weapon, 1 means secondary weapon. * 483 * * 484 * OUTPUT: Returns with the coordinate that any bullets fired from the specified weapon * 485 * should appear. * 486 * * 487 * WARNINGS: none * 488 * * 489 * HISTORY: * 490 * 07/29/1996 JLB : Created. * 491 *=============================================================================================*/ 492 FireDataType TechnoClass::Fire_Data(int which) const 493 { 494 assert(IsActive); 495 496 TechnoTypeClass const * tclass = Techno_Type_Class(); 497 498 int dist = 0; 499 if (which == 0) { 500 dist = tclass->PrimaryOffset; 501 } else { 502 dist = tclass->SecondaryOffset; 503 } 504 505 COORDINATE coord = Coord_Move(Center_Coord(), DIR_N, tclass->VerticalOffset + Height); 506 coord = Coord_Move(coord, DIR_E, tclass->HorizontalOffset); 507 508 return{coord,dist}; 509 } 510 511 COORDINATE TechnoClass::Fire_Coord(int which) const 512 { 513 assert(IsActive); 514 515 DirType dir = Turret_Facing(); 516 TechnoTypeClass const * tclass = Techno_Type_Class(); 517 518 int dist = 0; 519 int lateral = 0; 520 if (which == 0) { 521 dist = tclass->PrimaryOffset; 522 lateral = tclass->PrimaryLateral; 523 } else { 524 dist = tclass->SecondaryOffset; 525 lateral = tclass->SecondaryLateral; 526 } 527 528 COORDINATE coord = Coord_Move(Center_Coord(), DIR_N, tclass->VerticalOffset + Height); 529 coord = Coord_Move(coord, DIR_E, tclass->HorizontalOffset); 530 if (IsSecondShot) { 531 coord = Coord_Move(coord, (DirType)(dir+DIR_E), lateral); 532 } else { 533 coord = Coord_Move(coord, (DirType)(dir+DIR_W), lateral); 534 } 535 coord = Coord_Move(coord, dir, dist); 536 537 return(coord); 538 } 539 540 541 #ifdef CHEAT_KEYS 542 /*********************************************************************************************** 543 * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * 544 * * 545 * This routine is used to dump the status of the object class to the monochrome screen. * 546 * This display can be used to track down or prevent bugs. * 547 * * 548 * INPUT: none * 549 * * 550 * OUTPUT: none * 551 * * 552 * WARNINGS: none * 553 * * 554 * HISTORY: * 555 * 06/02/1994 JLB : Created. * 556 *=============================================================================================*/ 557 void TechnoClass::Debug_Dump(MonoClass * mono) const 558 { 559 assert(IsActive); 560 mono->Set_Cursor(10, 7);mono->Printf("%2d", Fetch_Rate()); 561 mono->Set_Cursor(14, 7);mono->Printf("%2d", Fetch_Stage()); 562 mono->Set_Cursor(49, 1);mono->Printf("%3d", Ammo); 563 mono->Set_Cursor(71, 1);mono->Printf("$%4d", PurchasePrice); 564 mono->Set_Cursor(54, 1);mono->Printf("%3d", (long)Arm); 565 if (Is_Something_Attached()) { 566 mono->Set_Cursor(1, 5);mono->Printf("%08X", Attached_Object()); 567 } 568 if (Target_Legal(TarCom)) { 569 mono->Set_Cursor(29, 3);mono->Printf("%08X", TarCom); 570 } 571 if (Target_Legal(SuspendedTarCom)) { 572 mono->Set_Cursor(38, 3);mono->Printf("%08X", SuspendedTarCom); 573 } 574 if (Target_Legal(ArchiveTarget)) { 575 mono->Set_Cursor(69, 5);mono->Printf("%08X", ArchiveTarget); 576 } 577 mono->Set_Cursor(47, 3);mono->Printf("%02X:%02X", PrimaryFacing.Current(), PrimaryFacing.Desired()); 578 mono->Set_Cursor(64, 1);mono->Printf("%d(%d)", Cloak, CloakingDevice); 579 580 mono->Fill_Attrib(14, 15, 12, 1, IsUseless ? MonoClass::INVERSE : MonoClass::NORMAL); 581 mono->Fill_Attrib(14, 16, 12, 1, IsTickedOff ? MonoClass::INVERSE : MonoClass::NORMAL); 582 mono->Fill_Attrib(14, 17, 12, 1, IsCloakable ? MonoClass::INVERSE : MonoClass::NORMAL); 583 mono->Fill_Attrib(27, 13, 12, 1, IsLeader ? MonoClass::INVERSE : MonoClass::NORMAL); 584 mono->Fill_Attrib(27, 14, 12, 1, IsALoaner ? MonoClass::INVERSE : MonoClass::NORMAL); 585 mono->Fill_Attrib(27, 15, 12, 1, IsLocked ? MonoClass::INVERSE : MonoClass::NORMAL); 586 mono->Fill_Attrib(27, 16, 12, 1, IsInRecoilState ? MonoClass::INVERSE : MonoClass::NORMAL); 587 mono->Fill_Attrib(27, 17, 12, 1, IsTethered ? MonoClass::INVERSE : MonoClass::NORMAL); 588 mono->Fill_Attrib(40, 13, 12, 1, IsOwnedByPlayer ? MonoClass::INVERSE : MonoClass::NORMAL); 589 mono->Fill_Attrib(40, 14, 12, 1, IsDiscoveredByPlayer ? MonoClass::INVERSE : MonoClass::NORMAL); 590 mono->Fill_Attrib(40, 15, 12, 1, IsDiscoveredByComputer ? MonoClass::INVERSE : MonoClass::NORMAL); 591 mono->Fill_Attrib(40, 16, 12, 1, IsALemon ? MonoClass::INVERSE : MonoClass::NORMAL); 592 mono->Set_Cursor(47, 17);mono->Printf("%3d", (long)IronCurtainCountDown); 593 mono->Fill_Attrib(40, 17, 12, 1, IronCurtainCountDown > 0 ? MonoClass::INVERSE : MonoClass::NORMAL); 594 595 RadioClass::Debug_Dump(mono); 596 } 597 #endif 598 599 600 /*********************************************************************************************** 601 * TechnoClass::TechnoClass -- Default constructor for techno objects. * 602 * * 603 * This default constructor for techno objects is used to reset all internal variables to * 604 * their default state. * 605 * * 606 * INPUT: none * 607 * * 608 * OUTPUT: none * 609 * * 610 * WARNINGS: none * 611 * * 612 * HISTORY: * 613 * 12/09/1994 JLB : Created. * 614 *=============================================================================================*/ 615 TechnoClass::TechnoClass(RTTIType rtti, int id, HousesType house) : 616 RadioClass(rtti, id), 617 IsUseless(false), 618 IsTickedOff(false), 619 IsCloakable(false), 620 IsLeader(false), 621 IsALoaner(false), 622 IsLocked(false), 623 IsInRecoilState(false), 624 IsTethered(false), 625 IsOwnedByPlayer(false), 626 IsDiscoveredByPlayer(false), 627 IsDiscoveredByComputer(false), 628 IsALemon(false), 629 IsSecondShot(true), 630 ArmorBias(1), 631 FirepowerBias(1), 632 IdleTimer(0), 633 IronCurtainCountDown(0), 634 SpiedBy(0), 635 ArchiveTarget(TARGET_NONE), 636 House(HouseClass::As_Pointer(house)), 637 Cloak(UNCLOAKED), 638 TarCom(TARGET_NONE), 639 SuspendedTarCom(TARGET_NONE), 640 PrimaryFacing(DIR_N), 641 Arm(0), 642 Ammo(-1), 643 ElectricZapDelay(-1), 644 ElectricZapTarget(0), 645 ElectricZapWhich(0), 646 PurchasePrice(0) 647 { 648 //IsOwnedByPlayer = (PlayerPtr == House); 649 // Added for multiplayer changes. ST - 4/24/2019 10:40AM 650 IsDiscoveredByPlayerMask = 0; 651 if (Session.Type == GAME_NORMAL) { 652 IsOwnedByPlayer = (PlayerPtr == House); 653 } else { 654 IsOwnedByPlayer = House->IsHuman; 655 } 656 } 657 658 659 /*********************************************************************************************** 660 * TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? * 661 * * 662 * Use this routine to determine if this object should be visible on the player's radar * 663 * screen. This routine doesn't take into consideration mapped terrain or whether the * 664 * radar is active. It merely checks to see that if all else is functioning correctly, * 665 * is this unit invisible or visible on the radar map. Typically, cloaked vehicles will * 666 * not be visible. * 667 * * 668 * INPUT: none * 669 * * 670 * OUTPUT: bool; Is this object nominally visible on the player's radar screen? * 671 * * 672 * WARNINGS: none * 673 * * 674 * HISTORY: * 675 * 05/27/1996 JLB : Created. * 676 *=============================================================================================*/ 677 bool TechnoClass::Is_Visible_On_Radar(void) const 678 { 679 /* 680 ** Hack: MRJ is invisible to radar, unless it's allied with the player. 681 */ 682 if (What_Am_I() == RTTI_UNIT) { 683 if(*((UnitClass *)this) == UNIT_MRJ) { 684 if(!House->Is_Ally(PlayerPtr)) { 685 return(false); 686 } 687 } 688 } 689 if (!Is_Cloaked(PlayerPtr, true)) { 690 return(true); 691 } 692 return(false); 693 } 694 695 696 /*********************************************************************************************** 697 * TechnoClass::Revealed -- Handles revealing an object to the house specified. * 698 * * 699 * When a unit moves out from under the shroud or when it gets delivered into already * 700 * explored terrain, then it must be "revealed". Objects that are revealed may be * 701 * announced to the player. The discovered bit updated accordingly. * 702 * * 703 * INPUT: house -- The house that this object is revealed to. * 704 * * 705 * OUTPUT: none * 706 * * 707 * WARNINGS: none * 708 * * 709 * HISTORY: * 710 * 06/02/1994 JLB : Created. * 711 * 12/27/1994 JLB : Discovered trigger event processing. * 712 *=============================================================================================*/ 713 bool TechnoClass::Revealed(HouseClass * house) 714 { 715 assert(IsActive); 716 717 /* Replaced by client/server version from TD. ST - 8/7/2019 11:09AM 718 if (house == PlayerPtr && IsDiscoveredByPlayer) return(false); 719 if (house != PlayerPtr) { 720 if (IsDiscoveredByComputer) return(false); 721 IsDiscoveredByComputer = true; 722 } 723 */ 724 if (house == NULL) { 725 return false; 726 } 727 728 if (Is_Discovered_By_Player(house)) { 729 return false; 730 } 731 732 if (house->IsHuman == false) { 733 if (IsDiscoveredByComputer) { 734 return false; 735 } 736 IsDiscoveredByComputer = true; 737 } 738 739 if (RadioClass::Revealed(house)) { 740 741 /* 742 ** An enemy object that is discovered will go into hunt mode if 743 ** its current mission is to ambush. 744 */ 745 if (!house->IsHuman && Mission == MISSION_AMBUSH) { 746 Assign_Mission(MISSION_HUNT); 747 } 748 749 if (Session.Type == GAME_NORMAL) { 750 if (house == PlayerPtr) { 751 IsDiscoveredByPlayer = true; 752 753 if (!IsOwnedByPlayer) { 754 755 /* 756 ** If there is a trigger event associated with this object, then process 757 ** it for discovery purposes. 758 */ 759 if (!ScenarioInit && Trigger.Is_Valid()) { 760 Trigger->Spring(TEVENT_DISCOVERED, this); 761 } 762 763 /* 764 ** Alert the enemy house to presence of the friendly side. 765 */ 766 House->IsDiscovered = true; 767 768 if (house->IsHuman) { 769 Set_Discovered_By_Player(house); 770 } else { 771 IsDiscoveredByComputer = true; 772 } 773 774 } else { 775 776 if (house->IsHuman) { 777 Set_Discovered_By_Player(house); 778 } else { 779 IsDiscoveredByComputer = true; 780 } 781 782 /* 783 ** A newly revealed object will always perform a look operation. 784 */ 785 Look(); 786 } 787 } else { 788 IsDiscoveredByComputer = true; 789 } 790 791 } else { 792 793 if (house->IsHuman) { 794 Set_Discovered_By_Player(house); 795 } 796 797 Look(); 798 } 799 800 return(true); 801 } 802 return(false); 803 } 804 805 806 /*********************************************************************************************** 807 * TechnoClass::Hidden -- Returns the object back into the hidden state. * 808 * * 809 * This routine is called for every object that returns to the darkness shroud or when * 810 * it gets destroyed. This also occurs when an object enters another (such as infantry * 811 * entering a transport helicopter). * 812 * * 813 * INPUT: none * 814 * * 815 * OUTPUT: none * 816 * * 817 * WARNINGS: none * 818 * * 819 * HISTORY: * 820 * 06/02/1994 JLB : Created. * 821 *=============================================================================================*/ 822 void TechnoClass::Hidden(void) 823 { 824 assert(IsActive); 825 826 //if (!IsDiscoveredByPlayer) return; 827 if (!Is_Discovered_By_Player()) { // ST - 8/7/2019 11:17AM 828 return; 829 } 830 if (!House->IsHuman) { 831 Clear_Discovered_By_Players(); 832 } 833 } 834 835 836 /*********************************************************************************************** 837 * TechnoClass::Mark -- Handles marking of techno objects. * 838 * * 839 * On the Techno-level, marking handles transmission of the redraw command to any object * 840 * that it is 'connected' with. This only occurs during loading and unloading. * 841 * * 842 * INPUT: mark -- The marking method. This routine just passes this on to base classes. * 843 * * 844 * OUTPUT: none * 845 * * 846 * WARNINGS: none * 847 * * 848 * HISTORY: * 849 * 10/17/1994 JLB : Created. * 850 *=============================================================================================*/ 851 bool TechnoClass::Mark(MarkType mark) 852 { 853 assert(IsActive); 854 855 if (RadioClass::Mark(mark)) { 856 857 /* 858 ** When redrawing an object, if there is another object tethered to this one, 859 ** redraw it as well. 860 */ 861 if (IsTethered) { 862 Transmit_Message(RADIO_REDRAW); 863 } 864 return(true); 865 } 866 return(false); 867 } 868 869 870 /*********************************************************************************************** 871 * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * 872 * * 873 * This routine is used to handle inbound radio messages. It only handles those messages * 874 * that deal with techno objects. Typically, these include loading and unloading. * 875 * * 876 * INPUT: from -- Pointer to the originator of the radio message. * 877 * * 878 * message -- The inbound radio message. * 879 * * 880 * param -- Reference to optional parameter that might be used to transfer * 881 * more information than is possible with the simple radio message * 882 * type. * 883 * * 884 * OUTPUT: Returns with the radio response. * 885 * * 886 * WARNINGS: none * 887 * * 888 * HISTORY: * 889 * 10/17/1994 JLB : Created. * 890 * 06/17/1995 JLB : Handles tether contact messages. * 891 *=============================================================================================*/ 892 RadioMessageType TechnoClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) 893 { 894 assert(IsActive); 895 896 switch (message) { 897 898 /* 899 ** Just received instructions to attack the specified target. 900 */ 901 case RADIO_ATTACK_THIS: 902 if (Techno_Type_Class()->PrimaryWeapon != NULL) { 903 Assign_Target(param); 904 Assign_Mission(MISSION_ATTACK); 905 return(RADIO_ROGER); 906 } 907 break; 908 909 /* 910 ** Establish a tethered connection to the object in radio contact. 911 */ 912 case RADIO_TETHER: 913 if (!IsTethered) { 914 IsTethered = true; 915 Transmit_Message(RADIO_TETHER, from); 916 return(RADIO_ROGER); 917 } 918 break; 919 920 /* 921 ** Break the tethered connection to the object in radio contact. 922 */ 923 case RADIO_UNTETHER: 924 if (IsTethered) { 925 IsTethered = false; 926 Transmit_Message(RADIO_UNTETHER, from); 927 return(RADIO_ROGER); 928 } 929 break; 930 931 /* 932 ** A tethered unit has reached it's destination. All is 933 ** clear, so radio contact can be broken. 934 */ 935 case RADIO_UNLOADED: 936 Transmit_Message(RADIO_UNTETHER, from); 937 return(Transmit_Message(RADIO_OVER_OUT, from)); 938 939 /* 940 ** When this message is received, it means that the other object 941 ** has already turned its radio off. Turn this radio off as well. 942 */ 943 case RADIO_OVER_OUT: 944 Transmit_Message(RADIO_UNTETHER, from); 945 RadioClass::Receive_Message(from, message, param); 946 return(RADIO_ROGER); 947 948 /* 949 ** Request to be informed when unloaded. This message is transmitted 950 ** by the transport unit to the transported unit as it is about to be 951 ** unloaded. It is saying, "All is clear. Drive off now." 952 */ 953 case RADIO_UNLOAD: 954 case RADIO_BACKUP_NOW: 955 case RADIO_HOLD_STILL: 956 Transmit_Message(RADIO_TETHER, from); 957 RadioClass::Receive_Message(from, message, param); 958 return(RADIO_ROGER); 959 960 /* 961 ** Handle reloading one ammo point for this unit. 962 */ 963 case RADIO_RELOAD: 964 if (Ammo == Techno_Type_Class()->MaxAmmo) return(RADIO_NEGATIVE); 965 /*BG*/ Mark(MARK_CHANGE_REDRAW); 966 Ammo++; 967 return(RADIO_ROGER); 968 969 /* 970 ** Handle repair of this unit. 971 */ 972 case RADIO_REPAIR: 973 /* 974 ** If it's a mine layer, re-arm him if he's empty. This always takes precedence 975 ** over repair, since this operation is free. 976 */ 977 if (What_Am_I() == RTTI_UNIT && *((UnitClass *)this) == UNIT_MINELAYER && ((UnitClass *)this)->Ammo < ((UnitClass *)this)->Class->MaxAmmo) { 978 ((UnitClass *)this)->Ammo = ((UnitClass *)this)->Class->MaxAmmo; 979 return(RADIO_NEGATIVE); 980 } 981 982 /* 983 ** Determine if this unit can be repaired becaause it is under strength. If so, then 984 ** proceed with the repair process. 985 */ 986 if (Health_Ratio() < Rule.ConditionGreen) { 987 int cost = Techno_Type_Class()->Repair_Cost(); 988 cost = max(cost, 1); 989 int step = Techno_Type_Class()->Repair_Step(); 990 step = max(step, 1); 991 992 /* 993 ** If there is sufficient money to repair the unit one step, then do so. 994 ** Otherwise return with a "can't complete" radio response. 995 ** Special case: in single-player campaigns, also try to use the repair pad house's money. 996 */ 997 HouseClass* house = House; 998 if (Session.Type == GAME_NORMAL && house->Available_Money() < cost) { 999 house = HouseClass::As_Pointer(from->Owner()); 1000 } 1001 if (house != NULL && house->Available_Money() >= cost) { 1002 house->Spend_Money(cost); 1003 Strength += step; 1004 1005 /* 1006 ** Return with either an all ok or mission accomplished radio message. This 1007 ** lets the repairing object know if it should abort the repair control process 1008 ** or continue it. 1009 */ 1010 if (Health_Ratio() < Rule.ConditionGreen) { 1011 return(RADIO_ROGER); 1012 } else { 1013 Strength = Techno_Type_Class()->MaxStrength; 1014 return(RADIO_ALL_DONE); 1015 } 1016 } else { 1017 return(RADIO_CANT); 1018 } 1019 } 1020 return(RADIO_NEGATIVE); 1021 1022 default: 1023 break; 1024 } 1025 return(RadioClass::Receive_Message(from, message, param)); 1026 } 1027 1028 1029 /*********************************************************************************************** 1030 * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects. * 1031 * * 1032 * This routine handles marking a game object as not a loaner. It is set only if the unit * 1033 * is not player owned and is on the regular map. This is necessary so that enemy objects * 1034 * can exist off-map but as soon as they move onto the map, are flagged so that can never * 1035 * leave it again. * 1036 * * 1037 * INPUT: why -- Specifies the circumstances under which this routine was called. * 1038 * * 1039 * OUTPUT: none * 1040 * * 1041 * WARNINGS: none * 1042 * * 1043 * HISTORY: * 1044 * 10/17/1994 JLB : Created. * 1045 * 10/26/94 JLB : Handles scanner units. * 1046 * 12/27/1994 JLB : Checks for an processes any trigger in cell. * 1047 *=============================================================================================*/ 1048 void TechnoClass::Per_Cell_Process(PCPType why) 1049 { 1050 assert(IsActive); 1051 1052 if (why == PCP_END) { 1053 CELL cell = Coord_Cell(Center_Coord()); 1054 1055 /* 1056 ** When enemy units enter the proper map area from off map, they are 1057 ** flagged so that they won't travel back off the map again. 1058 */ 1059 if (!IsLocked && Map.In_Radar(cell)) { 1060 IsLocked = true; 1061 } 1062 1063 /* 1064 ** If this object somehow moves into mapped terrain, but is not yet 1065 ** discovered, then flag it to be discovered. 1066 */ 1067 // Change for client/server multiplayer ST - 8/7/2019 11:19AM 1068 //if (!IsDiscoveredByPlayer && Map[cell].IsVisible) { 1069 //Revealed(PlayerPtr); 1070 if (!Is_Discovered_By_Player() && Map[cell].Is_Visible(House)) { 1071 Revealed(House); 1072 } 1073 } 1074 } 1075 1076 1077 /*********************************************************************************************** 1078 * TechnoClass::Draw_It -- Draws the health bar (if necessary). * 1079 * * 1080 * This routine will draw the common elements for techno type objects. This element is * 1081 * the health bar. The main game object has already been rendered by the time this * 1082 * routine is called. * 1083 * * 1084 * INPUT: x,y -- The coordinate of the center of the unit. * 1085 * * 1086 * OUTPUT: none * 1087 * * 1088 * WARNINGS: none * 1089 * * 1090 * HISTORY: * 1091 * 10/17/1994 JLB : Created. * 1092 * 10/26/94 JLB : Knows about radar scanned cells. * 1093 * 12/13/1994 JLB : Clips health bar against map edge. * 1094 * 01/23/1995 JLB : Dynamic selected object rectangle. * 1095 *=============================================================================================*/ 1096 void TechnoClass::Draw_It(int x, int y, WindowNumberType window) const 1097 { 1098 assert(IsActive); 1099 1100 /* 1101 ** Tells the door logic that it has been drawn. 1102 */ 1103 ((TechnoClass *)this)->Clear_Redraw_Flag(); 1104 1105 /* 1106 ** Draw electric zap 1107 */ 1108 if ((ElectricZapDelay >= 0) && ElectricZapTarget) { 1109 Electric_Zap(ElectricZapTarget, ElectricZapWhich, window); 1110 } 1111 1112 int width, height; 1113 Class_Of().Dimensions(width, height); 1114 1115 const bool show_health_bar = (Strength > 0) && !Is_Cloaked(PlayerPtr) && (Is_Selected_By_Player() || 1116 ((Rule.HealthBarDisplayMode == RulesClass::HB_DAMAGED) && (Strength < Techno_Type_Class()->MaxStrength)) || 1117 (Rule.HealthBarDisplayMode == RulesClass::HB_ALWAYS)); 1118 1119 /* 1120 ** Draw the selected object graphic. 1121 */ 1122 int lx = width / 2; 1123 int ly = height / 2; 1124 int dx = width / 5; 1125 int dy = height / 5; 1126 int fudge = show_health_bar ? 4 : 0; 1127 if (What_Am_I() == RTTI_VESSEL) { 1128 lx = width / 2; 1129 } 1130 1131 //if (IsSelected) { 1132 if (Is_Selected_By_Player() || show_health_bar) { 1133 GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), 1134 WindowList[window][WINDOWX] + LogicPage->Get_XPos(), 1135 WindowList[window][WINDOWY] + LogicPage->Get_YPos(), 1136 WindowList[window][WINDOWWIDTH], 1137 WindowList[window][WINDOWHEIGHT]); 1138 1139 1140 /* 1141 ** The infantry select box should be a bit higher than normal. 1142 */ 1143 if (What_Am_I() == RTTI_INFANTRY) { 1144 y -= 6; 1145 } 1146 1147 if (What_Am_I() == RTTI_BUILDING && ((BuildingTypeClass const &)Class_Of()).Type == STRUCT_BARRACKS) { 1148 y -= 5; 1149 } 1150 1151 /* 1152 ** Fetch the dimensions of the object. These dimensions will be used to draw 1153 ** the selection box and the health bar. 1154 */ 1155 1156 if (show_health_bar) { 1157 fixed ratio = Health_Ratio(); 1158 int pwidth; // Pixel width of bar interior. 1159 int color; // The color to give the interior of the bargraph. 1160 1161 int xx = x-width/2; 1162 int yy = y-(height/2); 1163 1164 /* 1165 ** Draw the outline of the bargraph. 1166 */ 1167 draw_window.Remap(xx+1, yy+1, width-1, 3-1, Map.FadingShade); 1168 draw_window.Draw_Rect(xx, yy, xx+width-1, yy+3, BLACK); 1169 1170 /* 1171 ** Determine the width of the interior strength 1172 ** graph. 1173 */ 1174 pwidth = (width-2) * ratio; 1175 1176 pwidth = Bound(pwidth, 1, width-2); 1177 1178 color = LTGREEN; 1179 if (ratio <= Rule.ConditionYellow) { 1180 color = YELLOW; 1181 } 1182 if (ratio <= Rule.ConditionRed) { 1183 color = RED; 1184 } 1185 draw_window.Fill_Rect(xx+1, yy+1, xx+pwidth, yy+(3-1), color); 1186 } 1187 1188 /* 1189 ** Draw the selected object graphic. 1190 */ 1191 if (Is_Selected_By_Player()) { 1192 // Upper left corner. 1193 draw_window.Draw_Line(x - lx, fudge + y - ly, x - lx + dx, fudge + y - ly, WHITE); 1194 draw_window.Draw_Line(x - lx, fudge + y - ly, x - lx, fudge + y - ly + dy, WHITE); 1195 1196 // Upper right corner. 1197 draw_window.Draw_Line(x + lx, fudge + y - ly, x + lx - dx, fudge + y - ly, WHITE); 1198 draw_window.Draw_Line(x + lx, fudge + y - ly, x + lx, fudge + y - ly + dy, WHITE); 1199 1200 // Lower right corner. 1201 draw_window.Draw_Line(x + lx, y + ly, x + lx - dx, y + ly, WHITE); 1202 draw_window.Draw_Line(x + lx, y + ly, x + lx, y + ly - dy, WHITE); 1203 1204 // Lower left corner. 1205 draw_window.Draw_Line(x - lx, y + ly, x - lx + dx, y + ly, WHITE); 1206 draw_window.Draw_Line(x - lx, y + ly, x - lx, y + ly - dy, WHITE); 1207 } 1208 } 1209 1210 // MBL 04.21.2020 1211 bool selected = Is_Selected_By_Player() || Rule.ResourceBarDisplayMode == RulesClass::RB_ALWAYS; 1212 // if ((window == WINDOW_VIRTUAL) || (Is_Selected_By_Player() && (House->Is_Ally(PlayerPtr) || (Spied_By() & (1 << (PlayerPtr->Class->House)))))) 1213 if ((window == WINDOW_VIRTUAL) || (selected && (House->Is_Ally(PlayerPtr) || (Spied_By() & (1 << (PlayerPtr->Class->House)))))) 1214 { 1215 Draw_Pips((x-lx)+5, y+ly-3, window); 1216 } 1217 } 1218 1219 1220 /*********************************************************************************************** 1221 * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * 1222 * * 1223 * This routine handles the common operation between techno objects when they are * 1224 * unlimboed. This includes revealing the map. * 1225 * * 1226 * INPUT: coord -- The coordinate to unlimbo object at. * 1227 * * 1228 * dir (optional) -- initial facing direction for this object * 1229 * * 1230 * OUTPUT: bool; Was the unlimbo successful? * 1231 * * 1232 * WARNINGS: none * 1233 * * 1234 * HISTORY: * 1235 * 11/14/1994 JLB : Created. * 1236 *=============================================================================================*/ 1237 bool TechnoClass::Unlimbo(COORDINATE coord, DirType dir) 1238 { 1239 assert(IsActive); 1240 1241 if (RadioClass::Unlimbo(coord, dir)) { 1242 PrimaryFacing = dir; 1243 Enter_Idle_Mode(true); 1244 Commence(); 1245 1246 IsLocked = Map.In_Radar(Coord_Cell(coord)); 1247 return(true); 1248 } 1249 return(false); 1250 } 1251 1252 1253 /*********************************************************************************************** 1254 * TechnoClass::In_Range -- Determines if specified target is within weapon range. * 1255 * * 1256 * This routine is used to compare the distance to the specified target with the range * 1257 * of the weapon. If the target is outside of weapon range, then false is returned. * 1258 * * 1259 * INPUT: target -- The target to check if it is within weapon range. * 1260 * * 1261 * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * 1262 * * 1263 * OUTPUT: bool; Is the specified target within weapon range? * 1264 * * 1265 * WARNINGS: none * 1266 * * 1267 * HISTORY: * 1268 * 11/14/1994 JLB : Created. * 1269 *=============================================================================================*/ 1270 bool TechnoClass::In_Range(TARGET target, int which, bool reciprocal_check) const 1271 { 1272 assert(IsActive); 1273 1274 if (IsLocked && Target_Legal(target)) { 1275 int range = Weapon_Range(which); 1276 BuildingClass const * building = As_Building(target); 1277 if (building != NULL) { 1278 range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); 1279 } 1280 FireDataType data = Fire_Data(which); 1281 if (MAX(0, ::Distance(data.Center, As_Coord(target)) - data.Distance) <= range) { 1282 return(true); 1283 } 1284 1285 /* 1286 ** There's a longstanding bug where enemy units aligned north/south may have different perceptions of whether they 1287 ** are in range of each other due to the turrets offset on the unit. This check will say that if either considers the 1288 ** the other to be in range, then they both do. ST - 3/18/2020 10:44AM 1289 */ 1290 if (reciprocal_check && !building) { 1291 ObjectClass *target_object = As_Object(target); 1292 if (target_object) { 1293 RTTIType my_type = What_Am_I(); 1294 if (target_object->What_Am_I() == my_type) { 1295 if (range == target_object->Weapon_Range(which)) { 1296 TechnoClass *tech = As_Techno(target); 1297 if (tech->In_Range(As_Target(), which, false)) { 1298 return true; 1299 } 1300 } 1301 } 1302 } 1303 } 1304 } 1305 return(false); 1306 } 1307 1308 1309 /*********************************************************************************************** 1310 * TechnoClass::In_Range -- Determines if specified target is within weapon range. * 1311 * * 1312 * This routine will determine if the pointer to the target object passed into this * 1313 * routine is within weapon range. * 1314 * * 1315 * INPUT: target -- Pointer to the target object to check if within weapon range. * 1316 * * 1317 * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * 1318 * * 1319 * OUTPUT: bool; Is the target within weapon range? * 1320 * * 1321 * WARNINGS: none * 1322 * * 1323 * HISTORY: * 1324 * 11/14/1994 JLB : Created. * 1325 *=============================================================================================*/ 1326 bool TechnoClass::In_Range(ObjectClass const * target, int which, bool reciprocal_check) const 1327 { 1328 assert(IsActive); 1329 1330 if (IsLocked && target) { 1331 int range = Weapon_Range(which); 1332 if (target->What_Am_I() == RTTI_BUILDING) { 1333 BuildingClass const * building = (BuildingClass const *)target; 1334 range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); 1335 } 1336 FireDataType data = Fire_Data(which); 1337 if (MAX(0, ::Distance(data.Center, target->Center_Coord()) - data.Distance) <= range) { 1338 return(true); 1339 } 1340 1341 /* 1342 ** There's a longstanding bug where enemy units aligned north/south may have different perceptions of whether they 1343 ** are in range of each other due to the turrets offset on the unit. This check will say that if either considers the 1344 ** the other to be in range, then they both do. ST - 3/18/2020 10:44AM 1345 */ 1346 if (reciprocal_check) { 1347 RTTIType my_type = What_Am_I(); 1348 if (my_type != RTTI_BUILDING) { 1349 if (target->What_Am_I() == my_type) { 1350 if (range == target->Weapon_Range(which)) { 1351 if (((TechnoClass*)target)->In_Range(this, which, false)) { 1352 return true; 1353 } 1354 } 1355 } 1356 } 1357 } 1358 } 1359 return(false); 1360 } 1361 1362 1363 /*********************************************************************************************** 1364 * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * 1365 * * 1366 * Use this routine to determine if the specified coordinate is within weapon range. * 1367 * * 1368 * INPUT: coord -- The coordinate to examine against the object to determine range. * 1369 * * 1370 * which -- The weapon to consider when determining range. 0=primary, 1=secondary. * 1371 * * 1372 * OUTPUT: bool; Is the weapon within range? * 1373 * * 1374 * WARNINGS: none * 1375 * * 1376 * HISTORY: * 1377 * 03/16/1995 JLB : Created. * 1378 *=============================================================================================*/ 1379 bool TechnoClass::In_Range(COORDINATE coord, int which) const 1380 { 1381 assert(IsActive); 1382 1383 return(IsLocked && ::Distance(Fire_Coord(which), coord) <= Weapon_Range(which)); 1384 } 1385 1386 1387 /*********************************************************************************************** 1388 * TechnoClass::Area_Modify -- Determine the area scan modifier for the cell. * 1389 * * 1390 * This routine scans around the cell specified and if there are any friendly buildings * 1391 * nearby, the multiplier return value will be reduced. If there are no friendly buildings * 1392 * nearby, then the return value will be 1. It checks to see if the primary weapon is * 1393 * supposed to perform this scan and if so, the scan will be performed. Otherwise the * 1394 * default value is quickly returned. * 1395 * * 1396 * INPUT: cell -- The cell where the potential target lies. An area around this cell will * 1397 * be scanned for friendly buildings. * 1398 * * 1399 * OUTPUT: Returns with the multiplier to be multiplied by the potential target score value. * 1400 * For less opportune targets, the multiplier fraction will be less than one. For * 1401 * all other cases, it will return the default value of 1. * 1402 * * 1403 * WARNINGS: none * 1404 * * 1405 * HISTORY: * 1406 * 08/23/1996 JLB : Created. * 1407 *=============================================================================================*/ 1408 fixed TechnoClass::Area_Modify(CELL cell) const 1409 { 1410 // assert(Techno_Type_Class()->PrimaryWeapon != NULL); 1411 if (Techno_Type_Class()->PrimaryWeapon == NULL || !Techno_Type_Class()->PrimaryWeapon->IsSupressed) return(1); 1412 1413 int crange = Lepton_To_Cell(Rule.SupressRadius); 1414 fixed odds = 1; 1415 1416 for (int radius = 1; radius < crange; radius++) { 1417 1418 /* 1419 ** Scan the top and bottom rows of the "box". 1420 */ 1421 for (int x = -radius; x <= radius; x++) { 1422 CELL newcell; 1423 1424 if ((Cell_X(cell) + x) < Map.MapCellX) continue; 1425 if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue; 1426 1427 if ((Cell_Y(cell) - radius) >= Map.MapCellY) { 1428 newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius); 1429 BuildingClass const * building = Map[newcell].Cell_Building(); 1430 if (building != NULL && House->Is_Ally(building)) { 1431 odds /= 2; 1432 } 1433 } 1434 1435 if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) { 1436 newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius); 1437 BuildingClass const * building = Map[newcell].Cell_Building(); 1438 if (building != NULL && House->Is_Ally(building)) { 1439 odds /= 2; 1440 } 1441 } 1442 } 1443 1444 /* 1445 ** Scan the left and right columns of the "box". 1446 */ 1447 for (int y = -(radius-1); y < radius; y++) { 1448 CELL newcell; 1449 1450 if ((Cell_Y(cell) + y) < Map.MapCellY) continue; 1451 if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue; 1452 1453 if ((Cell_X(cell) - radius) >= Map.MapCellX) { 1454 newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y); 1455 BuildingClass const * building = Map[newcell].Cell_Building(); 1456 if (building != NULL && House->Is_Ally(building)) { 1457 odds /= 2; 1458 } 1459 } 1460 1461 if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) { 1462 newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y); 1463 BuildingClass const * building = Map[newcell].Cell_Building(); 1464 if (building != NULL && House->Is_Ally(building)) { 1465 odds /= 2; 1466 } 1467 } 1468 } 1469 } 1470 return(odds); 1471 } 1472 1473 1474 /*********************************************************************************************** 1475 * TechnoClass::Evaluate_Object -- Determines score value of specified object. * 1476 * * 1477 * This routine is used to determine the score value (value as a potential target) of the * 1478 * object specified. This routine will check the specified object for all the various * 1479 * legality checks that threat scanning requires. This is the main workhorse routine for * 1480 * target searching. * 1481 * * 1482 * INPUT: method -- The threat method requested. This is a combined bitflag value that * 1483 * not only specifies the kind of targets to consider, but how far away * 1484 * they are allowed to be. * 1485 * * 1486 * mask -- This is an RTTI mask to use for quickly eliminating object types. * 1487 * The mask is created outside of this routine because this routine is * 1488 * usually called from within a loop and this value is constant in that * 1489 * context. * 1490 * * 1491 * range -- The range at which potential target objects are rejected. * 1492 * 0 = must be within weapon range. * 1493 * >0 = must be within this lepton distance. * 1494 * <0 = range doesn't matter. * 1495 * * 1496 * object -- Pointer to the object itself. * 1497 * * 1498 * value -- Reference to the value variable that this routine will fill in. The * 1499 * higher the value the more likely this object will be selected as best. * 1500 * * 1501 * zone -- The zone restriction if any. A -1 means no zone check required. * 1502 * * 1503 * OUTPUT: Did the target pass all legality checks? If this value is returned true, then the * 1504 * value parameter will be filled in correctly. * 1505 * * 1506 * WARNINGS: This routine is time consuming. Don't call unless necessary. * 1507 * * 1508 * HISTORY: * 1509 * 06/30/1995 JLB : Created. * 1510 * 07/14/1995 JLB : Forces SAM site to not fire on landed aircraft. * 1511 * 09/22/1995 JLB : Zone checking enabled. * 1512 * 10/05/1995 JLB : Gives greater weight to designated enemy house targets. * 1513 * 02/16/1996 JLB : Added additional threat checks. * 1514 *=============================================================================================*/ 1515 bool TechnoClass::Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value, int zone) const 1516 { 1517 assert(IsActive); 1518 assert(object != NULL); 1519 1520 BStart(BENCH_EVAL_OBJECT); 1521 1522 /* 1523 ** An object in limbo can never be a valid target. 1524 */ 1525 if (object == NULL || object->IsInLimbo) { 1526 BEnd(BENCH_EVAL_OBJECT); 1527 return(false); 1528 } 1529 1530 /* 1531 ** If the object is cloaked, then it isn't a legal target. 1532 */ 1533 if (object->Is_Cloaked(this)) { 1534 BEnd(BENCH_EVAL_OBJECT); 1535 return(false); 1536 } 1537 1538 /* 1539 ** If the object is in a "harmless" state, then don't bother to consider it 1540 ** a threat. 1541 */ 1542 if (MissionControl[object->Mission].IsNoThreat) { 1543 BEnd(BENCH_EVAL_OBJECT); 1544 return(false); 1545 } 1546 1547 /* 1548 ** If the object is not within the desired zone, then ignore it, but only if 1549 ** zone checking is desired. 1550 */ 1551 COORDINATE objectcoord = object->Center_Coord(); 1552 if (zone != -1 && Map[objectcoord].Zones[Techno_Type_Class()->MZone] != zone) { 1553 BEnd(BENCH_EVAL_OBJECT); 1554 return(false); 1555 } 1556 1557 /* 1558 ** Friendly units are never considered a good target. Bail if this 1559 ** object is a friend. Unless we're a medic, of course. But then, 1560 ** only consider it a target if it's injured. 1561 */ 1562 bool is_ally = House->Is_Ally(object); 1563 bool is_medic = Combat_Damage() < 0; 1564 bool green_health = object->Health_Ratio() == Rule.ConditionGreen; 1565 if ((is_ally && (!is_medic || green_health)) || (!is_ally && is_medic)) { 1566 BEnd(BENCH_EVAL_OBJECT); 1567 return(false); 1568 } 1569 1570 /* 1571 ** If the object is further away than allowed, bail. 1572 */ 1573 int dist = Distance(object); 1574 if (range > 0 && dist > range) { 1575 BEnd(BENCH_EVAL_OBJECT); 1576 return(false); 1577 } 1578 1579 if (range == 0) { 1580 int primary = What_Weapon_Should_I_Use(object->As_Target()); 1581 if (!In_Range(object, primary)) { 1582 BEnd(BENCH_EVAL_OBJECT); 1583 return(false); 1584 } 1585 } 1586 1587 /* 1588 ** If the object is not visible, then bail. Human controlled units 1589 ** are always considered to be visible. 1590 */ 1591 if (!object->IsOwnedByPlayer && !object->IsDiscoveredByPlayer && Session.Type == GAME_NORMAL && object->What_Am_I() != RTTI_AIRCRAFT) { 1592 BEnd(BENCH_EVAL_OBJECT); 1593 return(false); 1594 } 1595 1596 /* 1597 ** Quickly eliminate all unit types that are not allowed according to the mask 1598 ** value. 1599 */ 1600 RTTIType otype = object->What_Am_I(); 1601 if (!((1 << otype) & mask)) { 1602 BEnd(BENCH_EVAL_OBJECT); 1603 return(false); // Mask failure. 1604 } 1605 1606 /* 1607 ** Determine if the target is theoretically allowed to be a target. If 1608 ** not, then bail. 1609 */ 1610 TechnoTypeClass const * tclass = object->Techno_Type_Class(); 1611 if (!tclass->IsLegalTarget) { 1612 BEnd(BENCH_EVAL_OBJECT); 1613 return(false); // Legality failure. 1614 } 1615 1616 /* 1617 ** Never consider a spy to be a valid target, unless you're a dog 1618 */ 1619 if (otype == RTTI_INFANTRY && ((InfantryTypeClass const *)tclass)->Type == INFANTRY_SPY) { 1620 if (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsDog) { 1621 // continue executing... 1622 } else { 1623 BEnd(BENCH_EVAL_OBJECT); 1624 return(false); 1625 } 1626 } 1627 1628 /* 1629 ** Special case so that SAM site doesn't fire on aircraft that are landed. 1630 */ 1631 if (otype == RTTI_AIRCRAFT && What_Am_I() == RTTI_BUILDING && *((BuildingClass *)this) == STRUCT_SAM) { 1632 if (((AircraftClass *)object)->Height == 0) { 1633 BEnd(BENCH_EVAL_OBJECT); 1634 return(false); 1635 } 1636 } 1637 1638 /* 1639 ** If only allowed to attack civilians, then eliminate all other types. 1640 */ 1641 if ((method & THREAT_CIVILIANS) && object->Owner() != HOUSE_NEUTRAL) { 1642 BEnd(BENCH_EVAL_OBJECT); 1643 return(false); 1644 } 1645 1646 /* 1647 ** If the scan is limited to capturable buildings only, then bail if the examined 1648 ** object isn't a capturable building. 1649 */ 1650 if ((method & THREAT_CAPTURE) && (otype != RTTI_BUILDING || !object->Can_Capture())) { 1651 BEnd(BENCH_EVAL_OBJECT); 1652 return(false); 1653 } 1654 1655 /* 1656 ** If we're a sub and the subject is a structure, bail if the structure 1657 ** is other than a sub pen or shipyard. 1658 */ 1659 if (otype == RTTI_BUILDING && What_Am_I() == RTTI_VESSEL && *(VesselClass *)this == VESSEL_SS) { 1660 StructType ostruc = *(BuildingClass *)object; 1661 if (ostruc != STRUCT_SUB_PEN && ostruc != STRUCT_SHIP_YARD) { 1662 BEnd(BENCH_EVAL_OBJECT); 1663 return(false); 1664 } 1665 } 1666 1667 /* 1668 ** SPECIAL CASE: Friendly units won't automatically fire on buildings 1669 ** if the building is not aggressive. That is, unless it is part of a team. A team 1670 ** is allowed to pick any target it so chooses. 1671 */ 1672 if ((!Is_Foot() || !((FootClass *)this)->Team.Is_Valid()) && 1673 (House->IsHuman || (House->IsPlayerControl && Session.Type == GAME_NORMAL)) && 1674 otype == RTTI_BUILDING && tclass->PrimaryWeapon == NULL) { 1675 #ifdef OBSOLETE 1676 if ((!Is_Foot() || ((FootClass *)this)->Team.Is_Valid()) && House->IsHuman && otype == RTTI_BUILDING && tclass->PrimaryWeapon == NULL) { 1677 #endif 1678 BEnd(BENCH_EVAL_OBJECT); 1679 return(false); 1680 } 1681 1682 /* 1683 ** Player-controlled demo trucks never automatically target. 1684 */ 1685 if ((House->IsHuman || (House->IsPlayerControl && Session.Type == GAME_NORMAL)) && What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_DEMOTRUCK) { 1686 BEnd(BENCH_EVAL_OBJECT); 1687 return(false); 1688 } 1689 1690 /* 1691 ** If the search is restricted to Tiberium processing objects, then 1692 ** perform the special qualification check now. 1693 */ 1694 if (method & THREAT_TIBERIUM) { 1695 switch (otype) { 1696 case RTTI_UNIT: 1697 if (!((UnitTypeClass const *)tclass)->IsToHarvest) { 1698 BEnd(BENCH_EVAL_OBJECT); 1699 return(false); 1700 } 1701 break; 1702 1703 case RTTI_BUILDING: 1704 if (!((BuildingTypeClass const *)tclass)->Capacity && Session.Type != GAME_NORMAL) { 1705 BEnd(BENCH_EVAL_OBJECT); 1706 return(false); 1707 } 1708 break; 1709 1710 default: 1711 BEnd(BENCH_EVAL_OBJECT); 1712 return(false); 1713 } 1714 } 1715 1716 /* 1717 ** If this target value is better than the previously recorded best 1718 ** target value then record this target for possible return as the 1719 ** best. 1720 */ 1721 int rawval = object->Value(); 1722 value = rawval + object->Crew.Kills; 1723 1724 /* 1725 ** If the candidate object is owned by the designated enemy of this house, then 1726 ** give it a higher value. This will tend to gravitate attacks toward the main 1727 ** antagonist of this house. 1728 */ 1729 if (House->Enemy != HOUSE_NONE && House->Enemy == object->House->Class->House) { 1730 value += 500; 1731 value *= 3; 1732 } 1733 1734 /* 1735 ** If the object is outside of the protective umbrella of the enemy base, then give it 1736 ** a target boost value. 1737 */ 1738 if (object->House->Which_Zone(object) == ZONE_NONE) { 1739 value *= 2; 1740 } 1741 1742 /* 1743 ** If fake buildings are considered to be a greater target option, then boost 1744 ** the fake building's value. 1745 */ 1746 if ((method & THREAT_FAKES) && otype == RTTI_BUILDING) { 1747 //switch (!((BuildingTypeClass const *)tclass)->Type) { 1748 //The '!' in front of the expression means we are switching on a bool instead of the type. Going to remove it so it compiles, but it might change behavior depending on what Watcom original made of this. 1749 //Might be better to remove this switch altogether 1750 //ST - 5/9/2019 1751 switch (((BuildingTypeClass const *)tclass)->Type) { 1752 case STRUCT_FAKECONST: 1753 case STRUCT_FAKEWEAP: 1754 case STRUCT_FAKE_YARD: 1755 case STRUCT_FAKE_PEN: 1756 case STRUCT_FAKE_RADAR: 1757 break; 1758 1759 /* 1760 ** Ignore all non-fake buildings. 1761 */ 1762 default: 1763 value = 0; 1764 break; 1765 } 1766 } 1767 1768 /* 1769 ** If power plants are to be considered a greater threat, then increase 1770 ** their value here. Buildings that produce no power are not considered 1771 ** a threat. 1772 */ 1773 if ((method & THREAT_POWER) && otype == RTTI_BUILDING) { 1774 if (((BuildingTypeClass const *)tclass)->Power > 0) { 1775 value += ((BuildingTypeClass const *)tclass)->Power * 1000; 1776 } else { 1777 value = 0; 1778 } 1779 } 1780 1781 /* 1782 ** If factories are to be considered a greater threat, then don't 1783 ** consider any non-factory building. 1784 */ 1785 if ((method & THREAT_FACTORIES) && otype == RTTI_BUILDING) { 1786 if (((BuildingTypeClass const *)tclass)->ToBuild == RTTI_NONE) { 1787 value = 0; 1788 } 1789 } 1790 1791 /* 1792 ** If base defensive structures are to be considered a greater threat, then 1793 ** don't consider an unarmed building to be a threat. 1794 */ 1795 if ((method & THREAT_BASE_DEFENSE) /*&& otype == RTTI_BUILDING*/) { 1796 if (tclass->PrimaryWeapon == NULL) { 1797 value = 0; 1798 } 1799 } 1800 1801 /* 1802 ** Possibly cause a reduction of the target's value if it is nearby friendly 1803 ** structures and the primary weapon of this object is flagged for 1804 ** friendly fire supression special check logic. 1805 */ 1806 fixed areamod = Area_Modify(Coord_Cell(object->Center_Coord())); 1807 if (areamod != 1) { 1808 value = areamod * value; 1809 } 1810 1811 /* 1812 ** Adjust the target value upward if it is in the 'nervous zone' of the 1813 ** owning base. This will tend to protect the base more thoroughly than 1814 ** an unmodified scan would. 1815 */ 1816 if (House->Which_Zone(object) != ZONE_NONE) { 1817 value *= Rule.NervousBias; 1818 } 1819 1820 /* 1821 ** Lessen threat as a factor of distance. 1822 */ 1823 if (value) { 1824 // if (rawval) { 1825 1826 value = (value * 32000) / ((dist/ICON_LEPTON_W)+1); 1827 // value = (value * 32000) / (((dist/ICON_LEPTON_W)*(dist/ICON_LEPTON_W))+1); 1828 1829 // if (value < MAP_CELL_W*2) value = dist/ICON_LEPTON_W; 1830 value = max(value, 1); 1831 BEnd(BENCH_EVAL_OBJECT); 1832 return(true); 1833 } 1834 value = 0; 1835 BEnd(BENCH_EVAL_OBJECT); 1836 return(false); 1837 } 1838 1839 1840 /*********************************************************************************************** 1841 * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * 1842 * * 1843 * This routine will examine the specified cell and return with the potential target * 1844 * object it contains and the value of it. Use this routine when searching for threats. * 1845 * * 1846 * INPUT: method -- The scan method to use for target searching. * 1847 * * 1848 * mask -- Prebuilt mask of object RTTI types acceptable for scanning. * 1849 * * 1850 * range -- Scan range limit to use for elimination purposes. This ensures that * 1851 * objects in the "corner" of a square scan get properly discarded. * 1852 * * 1853 * object -- Pointer to object pointer to be filled in with the object at this * 1854 * cell as a valid target. * 1855 * * 1856 * value -- Reference to the value of the object in this cell. It will be set * 1857 * according to the object's value. * 1858 * * 1859 * zone -- The zone restriction if any. A -1 means no zone check required. * 1860 * * 1861 * OUTPUT: Was a valid potential target found in this cell? * 1862 * * 1863 * WARNINGS: none * 1864 * * 1865 * HISTORY: * 1866 * 06/19/1995 JLB : Created. * 1867 * 09/22/1995 JLB : Zone checking enabled. * 1868 *=============================================================================================*/ 1869 bool TechnoClass::Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const * * object, int & value, int zone) const 1870 { 1871 assert(IsActive); 1872 1873 BStart(BENCH_EVAL_CELL); 1874 1875 *object = NULL; 1876 value = 0; 1877 1878 /* 1879 ** If the cell is not on the legal map, then always ignore it. 1880 */ 1881 if ((unsigned)cell > MAP_CELL_TOTAL) { 1882 BEnd(BENCH_EVAL_CELL); 1883 return(false); 1884 } 1885 if (!Map.In_Radar(cell)) { 1886 BEnd(BENCH_EVAL_CELL); 1887 return(false); 1888 } 1889 1890 /* 1891 ** Fetch the techno object from the cell. If there is no 1892 ** techno object there, then bail. 1893 */ 1894 CellClass * cellptr = &Map[cell]; 1895 1896 /* 1897 ** Don't consider for evaluation a cell that is not within the same zone. Only 1898 ** perform this check if zone checking is required. 1899 */ 1900 if (zone != -1 && cellptr->Zones[Techno_Type_Class()->MZone] != zone) { 1901 BEnd(BENCH_EVAL_CELL); 1902 return(false); 1903 } 1904 1905 TechnoClass const * tentative = (TechnoClass const *)cellptr->Cell_Occupier(); 1906 while (tentative != NULL) { 1907 if (tentative != this) { 1908 if (tentative->Is_Techno()) { 1909 if (Combat_Damage() < 0) { 1910 if (tentative->Health_Ratio() < Rule.ConditionGreen && House->Is_Ally(tentative)) break; 1911 } else { 1912 if (!House->Is_Ally(tentative)) break; 1913 } 1914 } 1915 } 1916 tentative = (TechnoClass const *)(ObjectClass *)tentative->Next; 1917 } 1918 1919 if (tentative == NULL) { 1920 BEnd(BENCH_EVAL_CELL); 1921 return(false); 1922 } 1923 *object = tentative; 1924 1925 bool result = Evaluate_Object(method, mask, range, tentative, value); 1926 1927 BEnd(BENCH_EVAL_CELL); 1928 return(result); 1929 } 1930 1931 1932 /*********************************************************************************************** 1933 * TechnoClass::Evaluate_Just_Cell -- Evaluate a cell as a target by itself. * 1934 * * 1935 * This will examine the cell (as if it contained no sentient objects) and determine a * 1936 * target value to assign to it. Typically, this is only useful for wall destroyable * 1937 * weapons when dealing with enemy walls. * 1938 * * 1939 * INPUT: cell -- The cell to examine and evaluate. * 1940 * * 1941 * OUTPUT: Returns with the target value to assign to this cell. * 1942 * * 1943 * WARNINGS: none * 1944 * * 1945 * HISTORY: * 1946 * 09/10/1996 JLB : Created. * 1947 *=============================================================================================*/ 1948 int TechnoClass::Evaluate_Just_Cell(CELL cell) const 1949 { 1950 BStart(BENCH_EVAL_WALL); 1951 1952 /* 1953 ** Ships don't scan for walls. 1954 */ 1955 if (What_Am_I() == RTTI_VESSEL) { 1956 return(0); 1957 } 1958 1959 /* 1960 ** First, only computer objects are allowed to automatically scan for walls. 1961 */ 1962 if (House->IsHuman) { 1963 BEnd(BENCH_EVAL_WALL); 1964 return(0); 1965 } 1966 1967 /* 1968 ** Even then, if the difficulty indicates that it shouldn't search for wall 1969 ** targets, then don't allow it to do so. 1970 */ 1971 if (!Rule.Diff[House->Difficulty].IsWallDestroyer) { 1972 BEnd(BENCH_EVAL_WALL); 1973 return(0); 1974 } 1975 1976 /* 1977 ** Determine if, in fact, a wall is located at this cell location. 1978 */ 1979 CellClass const * cellptr = &Map[cell]; 1980 if (cellptr->Overlay == OVERLAY_NONE || !OverlayTypeClass::As_Reference(cellptr->Overlay).IsWall) { 1981 BEnd(BENCH_EVAL_WALL); 1982 return(0); 1983 } 1984 1985 /* 1986 ** As a convenience to the target scanning logic, don't consider any wall to be 1987 ** a target if it isn't in range of the primary weapon. 1988 */ 1989 int primary = What_Weapon_Should_I_Use(::As_Target(cell)); 1990 if (!In_Range(Cell_Coord(cell), primary)) { 1991 BEnd(BENCH_EVAL_WALL); 1992 return(0); 1993 } 1994 1995 /* 1996 ** See if the object has a weapon that can damage walls. 1997 */ 1998 TechnoTypeClass const * ttype = (TechnoTypeClass const *)Techno_Type_Class(); 1999 if (ttype->PrimaryWeapon == NULL || ttype->PrimaryWeapon->WarheadPtr == NULL) { 2000 BEnd(BENCH_EVAL_WALL); 2001 return(0); 2002 } 2003 2004 /* 2005 ** If the weapon cannot deal with ground based targets, then don't consider 2006 ** this a valid cell target. 2007 */ 2008 if (ttype->PrimaryWeapon->Bullet != NULL && !ttype->PrimaryWeapon->Bullet->IsAntiGround) { 2009 BEnd(BENCH_EVAL_WALL); 2010 return(0); 2011 } 2012 2013 /* 2014 ** If the primary weapon cannot destroy a wall, then don't give the cell any 2015 ** value as a target. 2016 */ 2017 if (!ttype->PrimaryWeapon->WarheadPtr->IsWallDestroyer) { 2018 BEnd(BENCH_EVAL_WALL); 2019 return(0); 2020 } 2021 2022 /* 2023 ** If this is a friendly wall, then don't attack it. 2024 */ 2025 if (House->Is_Ally(cellptr->Owner)) { 2026 BEnd(BENCH_EVAL_WALL); 2027 return(0); 2028 } 2029 2030 /* 2031 ** Since a wall was found, then return a value adjusted according to the range the wall 2032 ** is from the object. The greater the range, the lesser the value returned. 2033 */ 2034 BEnd(BENCH_EVAL_WALL); 2035 return(Weapon_Range(0) - Distance(Cell_Coord(cell))); 2036 } 2037 2038 2039 bool TechnoClass::Is_Cloaked(HousesType house, bool check_invisible) const 2040 { 2041 const bool is_invisible = check_invisible && Techno_Type_Class()->IsInvisible; 2042 return !House->Is_Ally(house) && ((Cloak == CLOAKED) || is_invisible); 2043 } 2044 2045 2046 bool TechnoClass::Is_Cloaked(HouseClass const * house, bool check_invisible) const 2047 { 2048 const bool is_invisible = check_invisible && Techno_Type_Class()->IsInvisible; 2049 return !House->Is_Ally(house) && ((Cloak == CLOAKED) || is_invisible); 2050 } 2051 2052 2053 bool TechnoClass::Is_Cloaked(ObjectClass const * object, bool check_invisible) const 2054 { 2055 const bool is_invisible = check_invisible && Techno_Type_Class()->IsInvisible; 2056 return !House->Is_Ally(object) && ((Cloak == CLOAKED) || is_invisible); 2057 } 2058 2059 2060 /*********************************************************************************************** 2061 * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * 2062 * * 2063 * This routine will scan game objects looking for the best target. It is used by the * 2064 * general target searching processes. The type of target scan to perform is controlled * 2065 * by the method control parameter. * 2066 * * 2067 * INPUT: method -- The method control parameter is used to control the type of target * 2068 * scan performed. It consists of a series of bit flags (see ThreatType) * 2069 * that are combined to form the target scan desired. * 2070 * * 2071 * OUTPUT: Returns the target value of a suitable target. If no target was found then the * 2072 * value TARGET_NONE is returned. * 2073 * * 2074 * WARNINGS: none * 2075 * * 2076 * HISTORY: * 2077 * 11/14/1994 JLB : Created. * 2078 * 06/20/1995 JLB : Greatly optimized scan method. * 2079 * 09/22/1995 JLB : Takes into account the zone (if necessary). * 2080 * 05/30/1996 JLB : Tighter elimination mask checking. * 2081 *=============================================================================================*/ 2082 TARGET TechnoClass::Greatest_Threat(ThreatType method) const 2083 { 2084 assert(IsActive); 2085 2086 BStart(BENCH_GREATEST_THREAT); 2087 2088 ObjectClass const * bestobject = NULL; 2089 int bestval = -1; 2090 int zone = -1; 2091 2092 TargetScan++; 2093 2094 /* 2095 ** Determine the zone that the target must be in. For aircraft and gunboats, they 2096 ** ignore zones since they either can fly over any zone or are designed to fire into 2097 ** other zones. If scanning for targets that are within range, then zone checking need 2098 ** not be performed -- range checking is much more thorough and effective. 2099 */ 2100 if (!(method & THREAT_RANGE) && 2101 What_Am_I() != RTTI_VESSEL && 2102 What_Am_I() != RTTI_BUILDING && 2103 What_Am_I() != RTTI_AIRCRAFT) { 2104 2105 zone = Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]; 2106 } 2107 2108 /* 2109 ** Hack for dogs, 'cause they can only consider infantrymen to be a 2110 ** threat. Medics also. 2111 */ 2112 if (What_Am_I() == RTTI_INFANTRY) { 2113 if (((InfantryClass *)this)->Class->IsDog || Combat_Damage() < 0) { 2114 method = THREAT_INFANTRY | (method & (THREAT_RANGE | THREAT_AREA)); 2115 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2116 if(*(InfantryClass *)this == INFANTRY_MECHANIC) { 2117 method = (THREAT_VEHICLES | THREAT_AIR) | (method & (THREAT_RANGE | THREAT_AREA)); 2118 } 2119 #endif 2120 } 2121 } 2122 2123 /* 2124 ** Build a quick elimination mask. If the RTTI of the object doesn't 2125 ** qualify with this mask, then we KNOW that it shouldn't be considered. 2126 */ 2127 int mask = 0; 2128 if (method & THREAT_CIVILIANS) mask |= ((1 << RTTI_BUILDING) | (1 << RTTI_INFANTRY) | (1 << RTTI_UNIT)); 2129 if (method & THREAT_AIR) mask |= (1 << RTTI_AIRCRAFT); 2130 if (method & THREAT_CAPTURE) mask |= (1 << RTTI_BUILDING); 2131 if (method & (THREAT_CIVILIANS|THREAT_BUILDINGS|THREAT_FACTORIES|THREAT_POWER|THREAT_FAKES|THREAT_BASE_DEFENSE|THREAT_TIBERIUM)) mask |= (1 << RTTI_BUILDING); 2132 if (method & (THREAT_CIVILIANS|THREAT_INFANTRY|THREAT_BASE_DEFENSE)) mask |= (1 << RTTI_INFANTRY); 2133 if (method & THREAT_VEHICLES) mask |= (1 << RTTI_UNIT); 2134 if (method & THREAT_BASE_DEFENSE) mask |= (1 << RTTI_BUILDING); 2135 if (method & THREAT_BOATS) mask |= (1 << RTTI_VESSEL); 2136 2137 /* 2138 ** Limit area target scans use a method where the actual map cells are 2139 ** examined for occupants. The occupant is then examined in turn. The 2140 ** best target within the area is returned as a target. 2141 */ 2142 if (method & (THREAT_AREA|THREAT_RANGE)) { 2143 int range = Threat_Range((method & THREAT_RANGE) ? 0 : 1); 2144 2145 int crange = range / ICON_LEPTON_W; 2146 if (range == 0) { 2147 crange = max(Weapon_Range(0), Weapon_Range(1)) / ICON_LEPTON_W; 2148 crange++; 2149 } 2150 CELL cell = Coord_Cell(Fire_Coord(0)); 2151 2152 /* 2153 ** BG: Miserable hack to get the stupid doctor to actually do area 2154 ** guarding. 2155 */ 2156 if (Combat_Damage() < 0) { 2157 /*if (method & THREAT_AREA)*/ crange++; 2158 } 2159 2160 /* 2161 ** If aircraft are a legal target, then scan through all of them at this time. 2162 ** Scanning by cell is not possible for aircraft since they are not recorded 2163 ** at the cell level. 2164 */ 2165 if (method & THREAT_AIR) { 2166 for (int index = 0; index < Aircraft.Count(); index++) { 2167 TechnoClass * object = Aircraft.Ptr(index); 2168 2169 int value = 0; 2170 if (object->In_Which_Layer() != LAYER_GROUND && Evaluate_Object(method, mask, range, object, value)) { 2171 if (value > bestval) { 2172 bestobject = object; 2173 bestval = value; 2174 } 2175 } 2176 } 2177 } 2178 2179 /* 2180 ** When scanning the ground, always consider landed aircraft as a valid 2181 ** potential target. This is only true if vehicles are considered a 2182 ** valid target. A landed aircraft is considered a vehicle. 2183 */ 2184 if (method & THREAT_VEHICLES) { 2185 mask |= (1 << RTTI_AIRCRAFT); 2186 } 2187 2188 /* 2189 ** Radiate outward from the object's location, looking for the best 2190 ** target. 2191 */ 2192 CELL bestcell = -1; 2193 int bestcellvalue = 0; 2194 TechnoClass const * object; 2195 int value; 2196 // int rad = 1; 2197 2198 // BG: Medics need to be able to look in their own cell too. 2199 // if (Combat_Damage() < 0 || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass*)this)->Class->IsDog)) { 2200 // rad = 0; 2201 // } 2202 2203 for (int radius = 0; radius < crange; radius++) { 2204 2205 /* 2206 ** Scan the top and bottom rows of the "box". 2207 */ 2208 for (int x = -radius; x <= radius; x++) { 2209 CELL newcell; 2210 2211 if ((Cell_X(cell) + x) < Map.MapCellX) continue; 2212 if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue; 2213 2214 if ((Cell_Y(cell) - radius) >= Map.MapCellY) { 2215 newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius); 2216 if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { 2217 if (bestval < value) { 2218 bestobject = object; 2219 } 2220 } 2221 if (bestobject == NULL) { 2222 value = Evaluate_Just_Cell(newcell); 2223 if (bestcellvalue < value) { 2224 bestcellvalue = value; 2225 bestcell = newcell; 2226 } 2227 } 2228 } 2229 2230 if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) { 2231 newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius); 2232 if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { 2233 if (bestval < value) { 2234 bestobject = object; 2235 } 2236 } 2237 if (bestobject == NULL) { 2238 value = Evaluate_Just_Cell(newcell); 2239 if (bestcellvalue < value) { 2240 bestcellvalue = value; 2241 bestcell = newcell; 2242 } 2243 } 2244 } 2245 } 2246 2247 /* 2248 ** Scan the left and right columns of the "box". 2249 */ 2250 for (int y = -(radius-1); y < radius; y++) { 2251 CELL newcell; 2252 2253 if ((Cell_Y(cell) + y) < Map.MapCellY) continue; 2254 if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue; 2255 2256 if ((Cell_X(cell) - radius) >= Map.MapCellX) { 2257 newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y); 2258 if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { 2259 if (bestval < value) { 2260 bestobject = object; 2261 } 2262 } 2263 if (bestobject == NULL) { 2264 value = Evaluate_Just_Cell(newcell); 2265 if (bestcellvalue < value) { 2266 bestcellvalue = value; 2267 bestcell = newcell; 2268 } 2269 } 2270 } 2271 2272 if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) { 2273 newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y); 2274 if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { 2275 if (bestval < value) { 2276 bestobject = object; 2277 } 2278 } 2279 if (bestobject == NULL) { 2280 value = Evaluate_Just_Cell(newcell); 2281 if (bestcellvalue < value) { 2282 bestcellvalue = value; 2283 bestcell = newcell; 2284 } 2285 } 2286 } 2287 } 2288 2289 /* 2290 ** Bail early if a target has already been found and the range is at 2291 ** one of the breaking points (i.e., normal range or range * 2). 2292 */ 2293 if (bestobject != NULL) { 2294 if (radius == crange/4) { 2295 return(bestobject->As_Target()); 2296 } 2297 if (radius == crange/2) { 2298 return(bestobject->As_Target()); 2299 } 2300 } 2301 if (bestcell != -1) { 2302 return(::As_Target(bestcell)); 2303 } 2304 } 2305 2306 } else { 2307 /* 2308 ** A full map scan was requested. First scan through aircraft. The top map layer 2309 ** is NOT scanned since that layer will probably contain more bullets and animations 2310 ** than aircraft. 2311 */ 2312 if (mask & (1L << RTTI_AIRCRAFT)) { 2313 for (int index = 0; index < Aircraft.Count(); index++) { 2314 TechnoClass * object = Aircraft.Ptr(index); 2315 2316 int value = 0; 2317 if (Evaluate_Object(method, mask, -1, object, value)) { 2318 if (value > bestval) { 2319 bestobject = object; 2320 bestval = value; 2321 } 2322 } 2323 } 2324 } 2325 2326 /* 2327 ** When scanning the ground, always consider landed aircraft as a valid 2328 ** potential target. This is only true if vehicles are considered a 2329 ** valid target. A landed aircraft is considered a vehicle. 2330 */ 2331 if (method & THREAT_VEHICLES) { 2332 mask |= (1 << RTTI_AIRCRAFT); 2333 } 2334 2335 /* 2336 ** Now scan through the entire ground layer. This is painful, but what other 2337 ** choice is there? 2338 */ 2339 for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { 2340 ObjectClass const * object = Map.Layer[LAYER_GROUND][index]; 2341 2342 int value = 0; 2343 if (object->Is_Techno() && Evaluate_Object(method, mask, -1, (TechnoClass const *)object, value, zone)) { 2344 if (value > bestval) { 2345 bestobject = object; 2346 bestval = value; 2347 } 2348 } 2349 } 2350 } 2351 2352 BEnd(BENCH_GREATEST_THREAT); 2353 2354 /* 2355 ** If a good target object was found, then return with the target value 2356 ** of it. 2357 */ 2358 if (bestobject != NULL) { 2359 return(bestobject->As_Target()); 2360 } 2361 return(TARGET_NONE); 2362 } 2363 2364 2365 /*********************************************************************************************** 2366 * TechnoClass::Owner -- Who is the owner of this object? * 2367 * * 2368 * Use this routine to examine this object and return who the owner is. * 2369 * * 2370 * INPUT: none * 2371 * * 2372 * OUTPUT: Returns with the house number of the owner of this object. * 2373 * * 2374 * WARNINGS: none * 2375 * * 2376 * HISTORY: * 2377 * 12/09/1994 JLB : Created. * 2378 *=============================================================================================*/ 2379 HousesType TechnoClass::Owner(void) const 2380 { 2381 assert(IsActive); 2382 2383 return(House->Class->House); 2384 } 2385 2386 2387 /*********************************************************************************************** 2388 * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * 2389 * * 2390 * Use this routine to set the flash count for the object. This flash count is the number * 2391 * of times the object will "flash". Typically it is called as a result of the player * 2392 * clicking on this object in order to make it the target of a move or attack. * 2393 * * 2394 * INPUT: count -- The number of times the object should flash. * 2395 * * 2396 * OUTPUT: none * 2397 * * 2398 * WARNINGS: none * 2399 * * 2400 * HISTORY: * 2401 * 12/09/1994 JLB : Created. * 2402 *=============================================================================================*/ 2403 void TechnoClass::Clicked_As_Target(HousesType house, int count) // 2019/09/20 JAS - Added record of who clicked on the object 2404 { 2405 assert(IsActive); 2406 2407 FlashCount = count; 2408 2409 // 2019/09/20 JAS - Flashing info needs to exist per player 2410 if (house < HOUSE_COUNT) 2411 { 2412 FlashCountPerPlayer[house] = count; 2413 } 2414 else 2415 { 2416 //receiving HOUSE_COUNT means do it for every player 2417 for (int i = 0; i < HOUSE_COUNT; ++i) 2418 { 2419 FlashCountPerPlayer[i] = count; 2420 } 2421 } 2422 } 2423 2424 2425 /*********************************************************************************************** 2426 * TechnoClass::AI -- Handles AI processing for techno object. * 2427 * * 2428 * This routine handles AI processing for techno objects. Typically, this merely dispatches * 2429 * to the appropriate AI routines for the base classes. * 2430 * * 2431 * INPUT: none * 2432 * * 2433 * OUTPUT: none * 2434 * * 2435 * WARNINGS: Make sure that this routine is only called ONCE per game tick. * 2436 * * 2437 * HISTORY: * 2438 * 12/09/1994 JLB : Created. * 2439 *=============================================================================================*/ 2440 void TechnoClass::AI(void) 2441 { 2442 assert(IsActive); 2443 2444 /* 2445 ** Handle recoil recovery here. 2446 */ 2447 if (IsInRecoilState) { 2448 IsInRecoilState = false; 2449 Mark(MARK_CHANGE_REDRAW); 2450 } 2451 2452 /* 2453 ** If this building is being spied on by the player, need to redraw if selected 2454 ** since the money amount is rendering. 2455 */ 2456 if (Is_Selected_By_Player()) { 2457 if (What_Am_I() == RTTI_BUILDING) { 2458 int spiedby = Spied_By() & (1<<(PlayerPtr->Class->House)); 2459 if (spiedby) { 2460 if (((BuildingClass *)this)->Class->Capacity) { 2461 Mark(MARK_CHANGE_REDRAW); 2462 } 2463 } 2464 } 2465 } 2466 2467 CargoClass::AI(); 2468 RadioClass::AI(); 2469 2470 if (!IsActive || (Height > 0 && What_Am_I() != RTTI_AIRCRAFT)) return; 2471 2472 DoorClass::AI(); 2473 2474 /* 2475 ** If this is a vehicle that heals itself (e.g., Mammoth Tank), then it will perform 2476 ** the heal logic here. 2477 */ 2478 if (Techno_Type_Class()->IsSelfHealing && (Frame % (Rule.RepairRate * TICKS_PER_MINUTE)) == 0 && Health_Ratio() <= Rule.ConditionYellow) { 2479 Strength++; 2480 Mark(MARK_CHANGE); 2481 } 2482 2483 /* 2484 ** Cloaking device processing. 2485 */ 2486 Cloaking_AI(); 2487 2488 /* 2489 ** If for some strange reason, the computer is firing upon itself, then 2490 ** tell it not to. 2491 */ 2492 if (!House->IsHuman && As_Techno(TarCom) && As_Techno(TarCom)->House->Is_Ally(this) && Combat_Damage() >= 0) { 2493 //#ifdef FIXIT_CSII // checked - ajw 9/28/98 (commented out) 2494 //if(What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)this==INFANTRY_GENERAL && Session.Type==GAME_NORMAL && House->Class->House==HOUSE_UKRAINE) { 2495 //} else 2496 //#endif 2497 Assign_Target(TARGET_NONE); 2498 } 2499 2500 /* 2501 ** Perform a maintenance check to see that if somehow this object is trying to fire 2502 ** upon an object it can never hit (because it can't reach it), then abort the tarcom 2503 */ 2504 if (What_Am_I() != RTTI_AIRCRAFT && Target_Legal(TarCom) && (!Is_Foot() || !((FootClass *)this)->Team.Is_Valid()) && (!Is_Foot() || !Is_In_Same_Zone(As_Cell(TarCom)))) { 2505 int primary = What_Weapon_Should_I_Use(TarCom); 2506 if (!In_Range(TarCom, primary)) { 2507 Assign_Target(TARGET_NONE); 2508 } 2509 } 2510 2511 /* 2512 ** Update the animation timer system. If the animation stage 2513 ** changes, then flag the object to be redrawn as well as determine 2514 ** if the current animation process needs to change. 2515 */ 2516 if (What_Am_I() != RTTI_BUILDING) { 2517 if (StageClass::About_To_Change()) { 2518 Mark(MARK_CHANGE_REDRAW); 2519 } 2520 if (StageClass::Graphic_Logic() || Time_To_Redraw()) { 2521 Mark(MARK_CHANGE_REDRAW); 2522 } 2523 } 2524 2525 /* 2526 ** If the object is flashing and a change of flash state has occurred, then mark the 2527 ** object to be redrawn. 2528 */ 2529 if (FlasherClass::Process()) { 2530 Mark(MARK_CHANGE); 2531 } 2532 2533 /* 2534 ** Handle electric zap delay logic. 2535 */ 2536 if (ElectricZapDelay >= 0) { 2537 Map.Flag_To_Redraw(true); 2538 if (--ElectricZapDelay < 0) { 2539 ElectricZapTarget = 0; 2540 ElectricZapWhich = 0; 2541 } 2542 } 2543 } 2544 2545 2546 /*********************************************************************************************** 2547 * TechnoClass::Cloaking_AI -- Perform the AI maintenance for a cloaking device. * 2548 * * 2549 * This routine handles the cloaking device logic for this object. It will handle the * 2550 * transition effects as the object cloaks or decloaks. It will also try to start an * 2551 * object to cloak if possible. * 2552 * * 2553 * INPUT: none * 2554 * * 2555 * OUTPUT: none * 2556 * * 2557 * WARNINGS: none * 2558 * * 2559 * HISTORY: * 2560 * 09/09/1996 JLB : Created. * 2561 *=============================================================================================*/ 2562 void TechnoClass::Cloaking_AI(void) 2563 { 2564 /* 2565 ** Handle decision to re-cloak here. Process the cloaking/decloaking operation. 2566 */ 2567 if (IsCloakable) { 2568 2569 /* 2570 ** If this object is uncloaked, but it can be cloaked and it thinks that it 2571 ** is a good time do so, then begin cloaking. 2572 */ 2573 if (Cloak == UNCLOAKED) { 2574 #ifdef PREDATOR 2575 // Changed for multiplayer so we can visually see the different players in the original renderer. ST - 3/13/2019 5:40PM 2576 //if (IsOwnedByPlayer) Mark(MARK_CHANGE); 2577 if (Is_Owned_By_Player()) Mark(MARK_CHANGE); 2578 #endif 2579 CloakingDevice.Graphic_Logic(); 2580 if (Is_Ready_To_Cloak()) { 2581 if (Health_Ratio() > Rule.ConditionRed) { 2582 Do_Cloak(); 2583 } else { 2584 if (Percent_Chance(4)) { 2585 Do_Cloak(); 2586 } 2587 } 2588 } 2589 } else { 2590 2591 CloakingDevice.Graphic_Logic(); 2592 switch (Cloak) { 2593 2594 /* 2595 ** Handle the uncloaking process. Always mark to redraw 2596 ** the object and when cloaking is complete, stabilize into 2597 ** the normal uncloaked state. 2598 */ 2599 case UNCLOAKING: 2600 Mark(MARK_CHANGE); 2601 if (Visual_Character(true) == VISUAL_NORMAL) { 2602 CloakingDevice.Set_Rate(0); 2603 CloakingDevice.Set_Stage(0); // re-start the stage counter 2604 Cloak = UNCLOAKED; 2605 CloakDelay = Rule.CloakDelay * TICKS_PER_MINUTE; 2606 Mark(MARK_CHANGE); 2607 } 2608 break; 2609 2610 /* 2611 ** Handle the cloaking process. Always mark to redraw the object 2612 ** and when the cloaking process is complete, stabilize into the 2613 ** normal cloaked state. 2614 */ 2615 case CLOAKING: 2616 Mark(MARK_CHANGE); 2617 if(!CloakingDevice.Fetch_Rate()) { 2618 CloakingDevice.Set_Rate(1); 2619 } 2620 switch (Visual_Character(true)) { 2621 2622 /* 2623 ** If badly damaged, then it can never fully cloak. 2624 */ 2625 case VISUAL_DARKEN: 2626 if (Health_Ratio() <= Rule.ConditionRed && Percent_Chance(25)) { 2627 Cloak = UNCLOAKING; 2628 } 2629 break; 2630 2631 case VISUAL_HIDDEN: 2632 Cloak = CLOAKED; 2633 CloakingDevice.Set_Rate(0); 2634 CloakingDevice.Set_Stage(0); 2635 Mark(MARK_CHANGE); 2636 2637 Map[Center_Coord()].Redraw_Objects(true); 2638 Map.RadarClass::Flag_To_Redraw(true); 2639 2640 /* 2641 ** Special check to ensure that if the unit is carrying a captured 2642 ** flag, it will never fully cloak. 2643 */ 2644 if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) { 2645 Do_Shimmer(); 2646 } else { 2647 Detach_All(false); 2648 } 2649 2650 /* 2651 ** A computer controlled unit will try to scatter if possible so 2652 ** that it will be much harder to locate. 2653 */ 2654 if (What_Am_I() == RTTI_UNIT && !House->IsHuman) { 2655 Scatter(0, true); 2656 } 2657 break; 2658 } 2659 break; 2660 2661 /* 2662 ** A cloaked object will always be redrawn if it is owned by the 2663 ** player. This ensures that the shimmering effect will animate. 2664 */ 2665 case CLOAKED: 2666 #ifdef PREDATOR 2667 //if (IsOwnedByPlayer) { 2668 // Changed for multiplayer so we can visually see the different players in the original renderer. ST - 3/13/2019 5:40PM 2669 if (Is_Owned_By_Player()) { 2670 Mark(MARK_CHANGE); 2671 } 2672 #endif 2673 break; 2674 } 2675 } 2676 } 2677 } 2678 2679 2680 /*********************************************************************************************** 2681 * TechnoClass::Is_Ready_To_Cloak -- Determines if this object is ready to begin cloaking. * 2682 * * 2683 * This routine will examine this object and determine if it can and is ready and able * 2684 * to begin cloaking. It will also check to make sure it appears to be a good time to cloak * 2685 * as well. * 2686 * * 2687 * INPUT: none * 2688 * * 2689 * OUTPUT: bool; Is this unit ready and able to start cloaking? * 2690 * * 2691 * WARNINGS: none * 2692 * * 2693 * HISTORY: * 2694 * 09/09/1996 JLB : Created. * 2695 *=============================================================================================*/ 2696 bool TechnoClass::Is_Ready_To_Cloak(void) const 2697 { 2698 /* 2699 ** If it is already cloaked or in the process of cloaking, then it can't start cloaking. 2700 */ 2701 if (Cloak == CLOAKED || (Cloak == CLOAKING && CloakingDevice.Fetch_Rate())) { 2702 return(false); 2703 } 2704 2705 /* 2706 ** If the object cannot recloak, then it certainly is not allowed to start. 2707 */ 2708 if (!IsCloakable || !Is_Allowed_To_Recloak()) { 2709 return(false); 2710 } 2711 2712 /* 2713 ** If the object is currently rearming, then don't begin to recloak. 2714 */ 2715 if (Arm != 0) { 2716 return(false); 2717 } 2718 2719 /* 2720 ** If it seems like this object is about to fire on a target, then don't begin 2721 ** cloaking either. 2722 */ 2723 if (Target_Legal(TarCom) && In_Range(TarCom)) { 2724 return(false); 2725 } 2726 2727 /* 2728 ** Recloaking can only begin if the cloaking device is not already operating. 2729 */ 2730 if (CloakingDevice.Fetch_Stage() != 0) { 2731 return(false); 2732 } 2733 2734 /* 2735 ** If the arbitrary cloak delay value is still counting down, then don't 2736 ** allow recloaking just yet. 2737 */ 2738 if (CloakDelay != 0) { 2739 return(false); 2740 } 2741 2742 /* 2743 ** All tests passed, so this object is allowed to begin cloaking. 2744 */ 2745 return(true); 2746 } 2747 2748 2749 2750 /*********************************************************************************************** 2751 * TechnoClass::Select -- Selects object and checks to see if can be selected. * 2752 * * 2753 * This function checks to see if this techno object can be selected. If it can, then it * 2754 * is selected. * 2755 * * 2756 * INPUT: none * 2757 * * 2758 * OUTPUT: none * 2759 * * 2760 * WARNINGS: none * 2761 * * 2762 * HISTORY: * 2763 * 12/11/1994 JLB : Created. * 2764 *=============================================================================================*/ 2765 bool TechnoClass::Select(bool allow_mixed) 2766 { 2767 assert(IsActive); 2768 2769 //if (!IsDiscoveredByPlayer && !House->IsPlayerControl && !Debug_Unshroud) { // ST - 8/7/2019 11:24AM 2770 if (!Is_Discovered_By_Player() && !House->IsPlayerControl && !Debug_Unshroud) { 2771 return(false); 2772 } 2773 2774 if (RadioClass::Select(allow_mixed)) { 2775 2776 /* 2777 ** Speak a confirmation of selection. 2778 */ 2779 if (House->IsPlayerControl && AllowVoice) { 2780 Response_Select(); 2781 } 2782 return(true); 2783 } 2784 return(false); 2785 } 2786 2787 2788 /*********************************************************************************************** 2789 * TechnoClass::Can_Fire -- Determines if this techno object can fire. * 2790 * * 2791 * This performs a simple check to make sure that this techno object can fire. At this * 2792 * level, the only thing checked for is the rearming delay. * 2793 * * 2794 * INPUT: none * 2795 * * 2796 * OUTPUT: Returns with the fire legality control code. * 2797 * * 2798 * WARNINGS: none * 2799 * * 2800 * HISTORY: * 2801 * 12/23/1994 JLB : Created. * 2802 *=============================================================================================*/ 2803 FireErrorType TechnoClass::Can_Fire(TARGET target, int which) const 2804 { 2805 assert(IsActive); 2806 2807 /* 2808 ** Don't allow firing if the target is illegal. 2809 */ 2810 if (!Target_Legal(target)) { 2811 return(FIRE_ILLEGAL); 2812 } 2813 2814 ObjectClass * object = As_Object(target); 2815 2816 /* 2817 ** If an enemy object is completely cloaked, then you can't fire on it. 2818 */ 2819 if (object != NULL && object->Is_Techno() && ((TechnoClass *)object)->Is_Cloaked(this)) { 2820 return(FIRE_CANT); 2821 } 2822 2823 /* 2824 ** A falling object is too busy falling to fire. 2825 */ 2826 if (IsFalling) { 2827 return(FIRE_CANT); 2828 } 2829 2830 /* 2831 ** If there is no weapon, then firing is not allowed. 2832 */ 2833 WeaponTypeClass const * weapon = ((which == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon); 2834 if (weapon == NULL) { 2835 return(FIRE_CANT); 2836 } 2837 2838 /* 2839 ** Can only fire anti-aircraft weapons against aircraft unless the aircraft is 2840 ** sitting on the ground. 2841 */ 2842 if (object != NULL && object->What_Am_I() == RTTI_AIRCRAFT && 2843 !weapon->Bullet->IsAntiAircraft && 2844 ((AircraftClass *)object)->Height > 0) { 2845 2846 return(FIRE_CANT); 2847 } 2848 2849 /* 2850 ** If the object is on the ground, then don't allow firing if it can't fire upon ground objects. 2851 */ 2852 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2853 if (object != NULL && object->Height == 0 && (object->What_Am_I() != RTTI_VESSEL || (*((VesselClass*)object) != VESSEL_SS && *((VesselClass*)object) != VESSEL_MISSILESUB )) && 2854 #else 2855 if (object != NULL && object->Height == 0 && (object->What_Am_I() != RTTI_VESSEL || *((VesselClass*)object) != VESSEL_SS) && 2856 #endif 2857 !weapon->Bullet->IsAntiGround) { 2858 2859 return(FIRE_CANT); 2860 } 2861 if (Is_Target_Cell(target) && !weapon->Bullet->IsAntiGround) { 2862 return(FIRE_CANT); 2863 } 2864 2865 /* 2866 ** Don't allow firing if still rearming. 2867 */ 2868 if (Arm != 0) return(FIRE_REARM); 2869 2870 /* 2871 ** The target must be within range in order to allow firing. 2872 */ 2873 if (!In_Range(target, which)) { 2874 return(FIRE_RANGE); 2875 } 2876 2877 /* 2878 ** If there is no ammo left, then it can't fire. 2879 */ 2880 if (!Ammo) { 2881 return(FIRE_AMMO); 2882 } 2883 2884 /* 2885 ** If cloaked, then firing is disabled. 2886 */ 2887 if (Cloak != UNCLOAKED) { 2888 #ifdef FIXIT_CSII // checked - ajw 9/28/98 2889 // Special hack for John Archer's Hunt-The-Wumpus multiplayer mission... if 2890 // the object firing is a cloaked civilian, don't require uncloaking before 2891 // allowing firing. 2892 if (What_Am_I()==RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCivilian ) { 2893 return(FIRE_OK); 2894 } 2895 #endif 2896 return(FIRE_CLOAKED); 2897 } 2898 2899 return(FIRE_OK); 2900 } 2901 2902 2903 /*********************************************************************************************** 2904 * TechnoClass::Stun -- Prepares the object for removal from the game. * 2905 * * 2906 * This routine handles cleaning up this techno object from the game system so that when * 2907 * it is subsequently removed, it doesn't leave any loose ends. * 2908 * * 2909 * INPUT: none * 2910 * * 2911 * OUTPUT: none * 2912 * * 2913 * WARNINGS: none * 2914 * * 2915 * HISTORY: * 2916 * 12/23/1994 JLB : Created. * 2917 *=============================================================================================*/ 2918 void TechnoClass::Stun(void) 2919 { 2920 assert(IsActive); 2921 2922 Assign_Target(TARGET_NONE); 2923 Assign_Destination(TARGET_NONE); 2924 Transmit_Message(RADIO_OVER_OUT); 2925 Detach_All(); 2926 //Unselect(); 2927 //When an object is stunned it needs to be deselected from all players, not just the current PlayerPtr. 2928 // - 8/18/2019 JAS 2929 Unselect_All_Players(); 2930 } 2931 2932 2933 /*********************************************************************************************** 2934 * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * 2935 * * 2936 * Use this routine to set the targeting computer for this object. It checks to make sure * 2937 * that targeting of itself is prohibited. * 2938 * * 2939 * INPUT: target -- The target for this object to attack. * 2940 * * 2941 * OUTPUT: none * 2942 * * 2943 * WARNINGS: none * 2944 * * 2945 * HISTORY: * 2946 * 12/23/1994 JLB : Created. * 2947 *=============================================================================================*/ 2948 void TechnoClass::Assign_Target(TARGET target) 2949 { 2950 assert(IsActive); 2951 2952 if (target == TarCom) return; 2953 2954 if (!Target_Legal(target)) { 2955 target = TARGET_NONE; 2956 } else { 2957 2958 /* 2959 ** Prevent targeting of self. 2960 */ 2961 if (target == As_Target()) { 2962 target = ::As_Target(Coord_Cell(Coord)); 2963 } else { 2964 2965 /* 2966 ** Make sure that the target is not already dead. 2967 */ 2968 ObjectClass * object = As_Object(target); 2969 if (object != NULL && (object->IsActive == false || object->Strength == 0)) { 2970 target = TARGET_NONE; 2971 } 2972 } 2973 } 2974 2975 /* 2976 ** Set the unit's targeting computer. 2977 */ 2978 TarCom = target; 2979 } 2980 2981 2982 /*********************************************************************************************** 2983 * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * 2984 * * 2985 * This function calculates the delay between shots. It determines this from the standard * 2986 * rate of fire (ROF) of the base class and modifies it according to game speed and * 2987 * whether this is the first or second shot. All single shot attackers consider their * 2988 * shots to be "second" since the second shot is the one handled normally. The first shot * 2989 * usually gets assigned a much shorter delay time before the next shot can fire. * 2990 * * 2991 * INPUT: second -- bool; Is this the second of a two shot salvo? * 2992 * * 2993 * OUTPUT: Returns with the number of game frames to delay before the next shot may fire. * 2994 * * 2995 * WARNINGS: none * 2996 * * 2997 * HISTORY: * 2998 * 12/26/1994 JLB : Created. * 2999 *=============================================================================================*/ 3000 int TechnoClass::Rearm_Delay(bool second, int which) const 3001 { 3002 assert(IsActive); 3003 3004 if (What_Am_I() == RTTI_BUILDING && Ammo > 1) { 3005 return(1); 3006 } 3007 3008 WeaponTypeClass const * weapon = (which == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon; 3009 if (second && weapon != NULL) { 3010 return(weapon->ROF * House->ROFBias); 3011 } 3012 return(3); 3013 } 3014 3015 3016 /*********************************************************************************************** 3017 * TechnoClass::Electric_Zap -- Fires electric zap at the target specified. * 3018 * * 3019 * This routine is used to fire an electric zap at the target specified. * 3020 * * 3021 * INPUT: target -- The target to fire the zap at. * 3022 * * 3023 * which -- Which weapon is this zap associated with (0=primary, 1=secondary). * 3024 * * 3025 * window -- The clipping window to use when rendering. * 3026 * * 3027 * source_coord -- The coordinate that the zap is to originate from. This is an * 3028 * override value and if not specifide, the normal fire coordinate * 3029 * is used. * 3030 * * 3031 * remap -- Pointer to the zap animation remap override table. If not specified * 3032 * then the zap remains the normal blue white color. * 3033 * * 3034 * OUTPUT: bool; Does this object need to redraw? * 3035 * * 3036 * WARNINGS: none * 3037 * * 3038 * HISTORY: * 3039 * 09/30/1996 BWG : Created. * 3040 * 09/30/1996 JLB : Uses standard facing conversion and distance routines. * 3041 *=============================================================================================*/ 3042 bool TechnoClass::Electric_Zap(COORDINATE target_coord, int which, WindowNumberType window, COORDINATE source_coord, unsigned char * remap) const 3043 { 3044 //int x,y,x1,y1; 3045 //PG init variables 3046 int x = 0; 3047 int y = 0; 3048 int x1 = 0; 3049 int y1 = 0; 3050 COORDINATE source; 3051 3052 if (source_coord != 0) { 3053 source = source_coord; 3054 } else { 3055 source = Fire_Coord(which); 3056 } 3057 if (What_Am_I() == RTTI_BUILDING) { 3058 ((BuildingClass *)this)->IsCharging = false; 3059 } 3060 bool gonnadraw = false; 3061 3062 if (SpecialDialog == SDLG_NONE) { 3063 Map.Coord_To_Pixel(source, x, y); 3064 Map.Coord_To_Pixel(target_coord, x1, y1); 3065 x += Map.TacPixelX; 3066 x1 += Map.TacPixelX; 3067 y += Map.TacPixelY; 3068 y1 += Map.TacPixelY; 3069 gonnadraw = true; 3070 } 3071 3072 static int _shape[]={ 2, 3, 1, 0, 2, 3, 1, 0}; 3073 static int _xadd[8][8]={ 3074 { 0, 8, 8, 8, 0, 0, 0, 0}, 3075 { 0, 8, 8, 8, 0, 0, 0, 0}, 3076 { 0, 8, 8, 8, 0, 0, 0, 0}, 3077 { 0, 8, 8, 8, 0, 0, 0, 0}, 3078 { 0, 8, 8, 8, 0, 0, 0, 0}, 3079 {-8, 0, 0, 0,-8,-8,-8,-8}, 3080 {-8, 0, 0, 0,-8,-8,-8,-8}, 3081 {-8, 0, 0, 0,-8,-8,-8,-8} 3082 }; 3083 static int _yadd[8][8]={ 3084 {-8,-8,-8, 0, 0, 0,-8,-8}, 3085 {-8,-8,-8, 0, 0, 0,-8,-8}, 3086 { 0, 0, 0, 8, 8, 8, 0, 0}, 3087 { 0, 0, 0, 8, 8, 8, 0, 0}, 3088 { 0, 0, 0, 8, 8, 8, 0, 0}, 3089 { 0, 0, 0, 8, 8, 8, 0, 0}, 3090 { 0, 0, 0, 8, 8, 8, 0, 0}, 3091 {-8,-8,-8, 0, 0, 0,-8,-8} 3092 }; 3093 3094 int savex = x, savey = y; 3095 if (gonnadraw) { 3096 for (int shots = 0; shots < 3; shots++) { 3097 x = savex; 3098 y = savey; 3099 int lastfacing = 0; 3100 while (::Distance(x, y, x1, y1) > 8) { 3101 3102 /* 3103 ** Determine true (0..7) facing from current position to 3104 ** destination (actually the source coordinate of the zap). 3105 */ 3106 int facing = Dir_Facing(Desired_Facing8(x, y, x1, y1)); 3107 3108 /* 3109 ** If there's quite a bit of distance to go, 3110 ** we may vary the desired facing to give the 3111 ** bolt some randomness. 3112 */ 3113 if (::Distance(x, y, x1, y1) > 40) { 3114 switch (Sim_Random_Pick(1, 3 + ((shots==0) ? 3 : 0))) { 3115 case 1: 3116 facing++; 3117 break; 3118 3119 case 2: 3120 facing--; 3121 break; 3122 3123 default: 3124 break; 3125 } 3126 facing &= 7; 3127 } 3128 3129 /* 3130 ** Now that we have the direction of the bolt, 3131 ** draw it and move the x & y coords in the right 3132 ** direction for the next piece. 3133 */ 3134 // Electric zap coordinates are always tactical, so don't use the partial window if passed - SKY 3135 x += _xadd[facing][lastfacing]; 3136 y += _yadd[facing][lastfacing]; 3137 if (remap != NULL) { 3138 CC_Draw_Shape(this, "LITNING", LightningShapes, _shape[facing]+(shots ? 4 : 0), x, y, (window != WINDOW_PARTIAL) ? window : WINDOW_TACTICAL, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap); 3139 } else { 3140 CC_Draw_Shape(this, "LITNING", LightningShapes, _shape[facing]+(shots ? 4 : 0), x, y, (window != WINDOW_PARTIAL) ? window : WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL); 3141 } 3142 lastfacing = facing; 3143 } 3144 } 3145 } 3146 3147 return (gonnadraw); 3148 } 3149 3150 3151 /*********************************************************************************************** 3152 * TechnoClass::Fire_At -- Fires projectile at target specified. * 3153 * * 3154 * This is the main projectile firing code. Buildings, units, and infantry route fire * 3155 * requests through this function. * 3156 * * 3157 * INPUT: target -- The target that the projectile is to be fired at. * 3158 * * 3159 * which -- Which weapon to fire. * 3160 * * 3161 * OUTPUT: Returns with a pointer to the projectile object that was fired. If no projectile * 3162 * could be created or there was some other illegality detected, the return value * 3163 * will be NULL. * 3164 * * 3165 * WARNINGS: none * 3166 * * 3167 * HISTORY: * 3168 * 12/26/1994 JLB : Created. * 3169 * 07/03/1995 JLB : Moving platforms fire inaccurate projectiles. * 3170 * 02/22/1996 JLB : Handles camera "weapon" case. * 3171 *=============================================================================================*/ 3172 BulletClass * TechnoClass::Fire_At(TARGET target, int which) 3173 { 3174 assert(IsActive); 3175 3176 BulletClass * bullet; // Projectile. 3177 DirType dir; // The facing to impart upon the projectile. 3178 COORDINATE target_coord; // Coordinate of the target. 3179 COORDINATE fire_coord; // Coordinate of firing position. 3180 TechnoTypeClass const & tclass = *Techno_Type_Class(); 3181 ObjectClass * object; 3182 WeaponTypeClass const * weapon = (which == 0) ? tclass.PrimaryWeapon : tclass.SecondaryWeapon; 3183 3184 /* 3185 ** If this object doesn't have a weapon, then it is obvious that firing 3186 ** cannot ever succeed. Return with failure flag. 3187 */ 3188 if (weapon == NULL) return(NULL); 3189 3190 BulletTypeClass const & btype = *weapon->Bullet; 3191 3192 /* 3193 ** Perform a quick legality check to see if firing can occur. 3194 */ 3195 if (Debug_Map || !Target_Legal(target)) { 3196 return(NULL); 3197 } 3198 3199 /* 3200 ** Fetch the target coordinate for the target specified. 3201 */ 3202 object = As_Object(target); 3203 if (object != NULL) { 3204 target_coord = object->Target_Coord(); 3205 } else { 3206 target_coord = As_Coord(target); 3207 } 3208 3209 /* 3210 ** Get the location where the projectile should appear. 3211 */ 3212 fire_coord = Fire_Coord(which); 3213 3214 /* 3215 ** If the projectile is a homing type (such as a missile), then it will 3216 ** launch in the direction the turret is facing, NOT necessarily the same 3217 ** direction as the target. 3218 */ 3219 if (btype.ROT != 0 || btype.IsDropping) { 3220 dir = Fire_Direction(); 3221 if (btype.IsDropping) { 3222 fire_coord = Center_Coord(); 3223 } 3224 } else { 3225 dir = ::Direction(fire_coord, target_coord); 3226 } 3227 3228 /* 3229 ** Create the projectile. Then process any special operations that 3230 ** need to be performed according to the style of projectile 3231 ** created. 3232 */ 3233 int firepower = weapon->Attack; 3234 if (firepower > 0) { 3235 firepower = weapon->Attack * FirepowerBias * House->FirepowerBias; 3236 } 3237 3238 3239 /* 3240 ** Give the bullet a boost of speed if the weapon indicates that this is required. Only 3241 ** need to perform this check if the target is an aircraft. 3242 */ 3243 int firespeed = weapon->MaxSpeed; 3244 if (weapon->IsTurboBoosted && Is_Target_Aircraft(target)) { 3245 firespeed *= Rule.TurboBoost; 3246 } 3247 3248 bullet = new BulletClass(weapon->Bullet->Type, target, this, firepower, WarheadType(weapon->WarheadPtr->ID), firespeed); 3249 3250 if (bullet != NULL) { 3251 3252 /* 3253 ** If this is firing from a moving platform, then the projectile is inaccurate. 3254 */ 3255 if (Is_Foot() && ((FootClass const *)this)->IsDriving) { 3256 bullet->IsInaccurate = true; 3257 } 3258 3259 if (bullet->Unlimbo(fire_coord, dir)) { 3260 } else { 3261 delete bullet; 3262 } 3263 if (tclass.IsTurretEquipped) { 3264 IsInRecoilState = true; 3265 Mark(MARK_CHANGE_REDRAW); 3266 } 3267 3268 Arm = Rearm_Delay(IsSecondShot, which); 3269 if (tclass.Is_Two_Shooter()) { 3270 IsSecondShot = (IsSecondShot == false); 3271 } 3272 3273 /* 3274 ** Perform any animation effect for this weapon. 3275 */ 3276 AnimType a = weapon->Anim; 3277 switch (a) { 3278 case ANIM_GUN_N: 3279 a = AnimType(a + Dir_Facing(Fire_Direction())); 3280 break; 3281 3282 case ANIM_SAM_N: 3283 a = AnimType(ANIM_SAM_N + Dir_Facing(PrimaryFacing.Current())); 3284 break; 3285 } 3286 3287 /* 3288 ** Play any sound effect tied to this weapon type. 3289 */ 3290 Sound_Effect(weapon->Sound, Fire_Coord(which)); 3291 3292 /* 3293 ** If there is a special firing animation, then create and attach it 3294 ** now. 3295 */ 3296 if (a != ANIM_NONE) { 3297 AnimClass * anim = new AnimClass(a, Fire_Coord(which)); 3298 if (anim != NULL) { 3299 anim->Attach_To(this); 3300 } 3301 } 3302 3303 /* 3304 ** Electric zap animation. 3305 */ 3306 if (weapon->IsElectric) { 3307 ElectricZapDelay = 3; 3308 ElectricZapTarget = target_coord; 3309 ElectricZapWhich = which; 3310 #ifdef FIXIT_CSII // checked - ajw 9/28/98 3311 if(What_Am_I() != RTTI_INFANTRY) { 3312 Set_Stage(0); 3313 Set_Rate(0); 3314 } 3315 #else 3316 Set_Stage(0); 3317 Set_Rate(0); 3318 #endif 3319 if (Ammo <= 1 && What_Am_I() == RTTI_BUILDING) { 3320 ((BuildingClass *)this)->IsCharged = false; 3321 } 3322 3323 TechnoClass * tech = As_Techno(target); 3324 if (tech != NULL) { 3325 tech->Clicked_As_Target(PlayerPtr->Class->House, 4); // 2019/09/20 JAS - Added record of who clicked on the object 3326 } 3327 } 3328 3329 /* 3330 ** Reduce ammunition for this object. 3331 */ 3332 if (Ammo > 0) { 3333 Ammo--; 3334 } 3335 3336 /* 3337 ** Firing will in all likelihood, require the unit to be redrawn. Flag it to be 3338 ** redrawn here. 3339 */ 3340 Mark(MARK_CHANGE); 3341 3342 /* 3343 ** If a projectile was fired from a unit that is hidden in the darkness, 3344 ** reveal that unit and a little area around it. 3345 ** For multiplayer games, only reveal the unit if the target is the 3346 ** local player. 3347 */ 3348 #if (0) 3349 if ((!IsOwnedByPlayer && !IsDiscoveredByPlayer) || (!Map[Center_Coord()].IsMapped && (What_Am_I()!=RTTI_AIRCRAFT || !IsOwnedByPlayer)) ) { 3350 if (Session.Type == GAME_NORMAL) { 3351 Map.Sight_From(Coord_Cell(Center_Coord()), 2, PlayerPtr, false); 3352 } else { 3353 ObjectClass * obj = As_Object(target); 3354 if (obj != NULL) { 3355 HousesType tgt_owner = obj->Owner(); 3356 3357 if (PlayerPtr->Class->House == tgt_owner) { 3358 Map.Sight_From(Coord_Cell(Center_Coord()), 2, PlayerPtr, false); 3359 } 3360 } 3361 } 3362 } 3363 3364 #else 3365 3366 /* 3367 ** For client/server multiplayer, we need to reveal for any human player that is the target. ST - 3/13/2019 5:43PM 3368 */ 3369 ObjectClass *obj = As_Object(target); 3370 if (obj) { 3371 HousesType tgt_owner = obj->Owner(); 3372 3373 HouseClass *player = HouseClass::As_Pointer(tgt_owner); 3374 if ((player != NULL) && player->IsHuman) { 3375 if ((!Is_Owned_By_Player(player) && !Is_Discovered_By_Player(player)) || (!Map[Coord_Cell(Center_Coord())].Is_Mapped(House) && (What_Am_I()!=RTTI_AIRCRAFT || !Is_Owned_By_Player(player))) ) { 3376 Map.Sight_From(Coord_Cell(Center_Coord()), 1, player, false); 3377 } 3378 } 3379 } 3380 3381 /* 3382 ** For electric zaps, immediately perform bullet logic. 3383 */ 3384 if (weapon->IsElectric) { 3385 bullet->AI(); 3386 } 3387 3388 #endif 3389 } 3390 3391 return(bullet); 3392 } 3393 3394 3395 /*********************************************************************************************** 3396 * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * 3397 * * 3398 * This routine is called when the mission for an object needs to change as a result of * 3399 * player input. The basic operation would be to queue the event and let the action * 3400 * occur at the frame dictated by the queuing system. However, if a voice response is * 3401 * indicated, then perform it at this time. This will give a greater illusion of * 3402 * immediate response. * 3403 * * 3404 * INPUT: mission -- The mission order to assign to this object. * 3405 * * 3406 * target -- The target of this object. This will be used for combat and attack. * 3407 * * 3408 * destination -- The movement destination for this object. * 3409 * * 3410 * OUTPUT: none * 3411 * * 3412 * WARNINGS: none * 3413 * * 3414 * HISTORY: * 3415 * 05/22/1995 JLB : Created. * 3416 *=============================================================================================*/ 3417 void TechnoClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) 3418 { 3419 assert(IsActive); 3420 3421 if (AllowVoice) { 3422 if (mission == MISSION_ATTACK) { 3423 Response_Attack(); 3424 } else { 3425 Response_Move(); 3426 } 3427 } 3428 3429 if (FormMove) { 3430 SpeedType speed = FormSpeed; 3431 MPHType maxspeed = FormMaxSpeed; 3432 if (Is_Foot()) { 3433 const FootClass* foot = (const FootClass*)this; 3434 if (foot->Group < MAX_TEAMS) { 3435 TeamFormDataStruct& team_form_data = TeamFormData[foot->Owner()]; 3436 speed = team_form_data.TeamSpeed[foot->Group]; 3437 maxspeed = team_form_data.TeamMaxSpeed[foot->Group]; 3438 } 3439 } 3440 Queue_Mission(TargetClass(this), mission, target, destination, speed, maxspeed); 3441 } else { 3442 3443 /* 3444 ** Cooerce the movement mission into a queued movement mission if the ALT key was 3445 ** held down. 3446 */ 3447 // 3448 // MBL 04.14.2020 Original code KO, since is still active and can still hit 3449 // 3450 // if (mission == MISSION_MOVE && (Keyboard->Down(Options.KeyQueueMove1) || Keyboard->Down(Options.KeyQueueMove2))) { 3451 // mission = MISSION_QMOVE; 3452 // } 3453 3454 // 3455 // MBL 04.14.2020 - Apply the same logic as above, using what is assigned as hotkeys 3456 // 3457 if (PlayerPtr && House) { 3458 if (PlayerPtr == House) { 3459 if (mission == MISSION_MOVE && PlayerPtr->IsQueuedMovementToggle) { 3460 mission = MISSION_QMOVE; 3461 } 3462 } 3463 } 3464 3465 Queue_Mission(TargetClass(this), mission, target, destination); 3466 } 3467 } 3468 3469 3470 /*********************************************************************************************** 3471 * TechnoClass::What_Action -- Determines what action to perform if object is selected. * 3472 * * 3473 * This routine will examine the object specified and return with the action that will * 3474 * be performed if the mouse button were clicked over the object. * 3475 * * 3476 * INPUT: object -- The object that the mouse button might be clicked on. * 3477 * * 3478 * OUTPUT: Returns with the action that will be performed if the object was clicked on. * 3479 * * 3480 * WARNINGS: none * 3481 * * 3482 * HISTORY: * 3483 * 01/19/1995 JLB : Created. * 3484 * 03/21/1995 JLB : Special target control for trees. * 3485 *=============================================================================================*/ 3486 ActionType TechnoClass::What_Action(ObjectClass const * object) const 3487 { 3488 assert(IsActive); 3489 3490 if (object != NULL) { 3491 3492 /* 3493 ** Return the ACTION_SELF flag if clicking on itself. However, if this 3494 ** object cannot do anything special with itself, then just return with 3495 ** the no action flag. 3496 */ 3497 if (object == this && CurrentObject.Count() == 1 && House->IsPlayerControl) { 3498 return(ACTION_SELF); 3499 } 3500 3501 //bool altdown = (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)); 3502 //bool ctrldown = (Keyboard->Down(Options.KeyForceAttack1) || Keyboard->Down(Options.KeyForceAttack2)); 3503 //bool shiftdown = (Keyboard->Down(Options.KeySelect1) || Keyboard->Down(Options.KeySelect2)); 3504 //Added for getting the input for special character keys from the client 3505 // - 6/26/2019 JAS 3506 bool altdown = DLL_Export_Get_Input_Key_State(KN_LALT); 3507 bool ctrldown = DLL_Export_Get_Input_Key_State(KN_LCTRL); 3508 bool shiftdown = DLL_Export_Get_Input_Key_State(KN_LSHIFT); 3509 3510 /* 3511 ** Special guard area mission is possible if both the control and the 3512 ** alt keys are held down. 3513 */ 3514 if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() /*KO && Can_Player_Fire()*/) { 3515 // if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { 3516 return(ACTION_GUARD_AREA); 3517 } 3518 3519 /* 3520 ** Special override to force a move regardless of what is occupying the location. 3521 */ 3522 if (altdown) { 3523 if (House->IsPlayerControl && Can_Player_Move()) { 3524 return(ACTION_MOVE); 3525 } 3526 } 3527 3528 /* 3529 ** Override so that toggled select state can be performed while the <SHIFT> key 3530 ** is held down. 3531 */ 3532 bool is_a_loaner = object->Is_Techno() && ((TechnoClass*)object)->IsALoaner; 3533 if (shiftdown) { 3534 if (!is_a_loaner) { 3535 return(ACTION_TOGGLE_SELECT); 3536 } 3537 } 3538 3539 /* 3540 ** If the weapon is blatantly disallowed from firing on the object specified, then 3541 ** don't allow the attach check logic to proceed. 3542 */ 3543 TechnoTypeClass const * ttype = Techno_Type_Class(); 3544 if (Is_Weapon_Equipped() && 3545 ttype->PrimaryWeapon->Bullet != NULL && 3546 ttype->PrimaryWeapon->Bullet->IsSubSurface && 3547 Map[object->Target_Coord()].Land_Type() != LAND_WATER) { 3548 3549 // Do nothing. 3550 3551 } else { 3552 3553 /* 3554 ** If firing is possible and legal, then return this action potential. 3555 */ 3556 if (House->IsPlayerControl && (ctrldown || !House->Is_Ally(object)) && (ctrldown || object->Class_Of().IsLegalTarget || (Rule.IsTreeTarget && object->What_Am_I() == RTTI_TERRAIN))) { 3557 3558 if (Is_Weapon_Equipped() || 3559 (What_Am_I() == RTTI_INFANTRY && 3560 (((InfantryTypeClass const *)ttype)->IsBomber || 3561 ((InfantryTypeClass const *)ttype)->IsCapture) 3562 )) { 3563 // Check for anti-air capability 3564 int primary = What_Weapon_Should_I_Use(object->As_Target()); 3565 WeaponTypeClass const * weapon = (primary == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon; 3566 if ((object->What_Am_I() != RTTI_AIRCRAFT) || 3567 ((weapon != NULL) && weapon->Bullet->IsAntiAircraft) || 3568 (object->Is_Techno() && (((TechnoClass *)object)->Height == 0))) { 3569 if (Can_Player_Move() || In_Range(object, primary)) { 3570 if (In_Range(object, primary) || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCapture && object->What_Am_I() == RTTI_BUILDING && object->Can_Capture())) { 3571 return(ACTION_ATTACK); 3572 } else { 3573 if (!Can_Player_Move()) { 3574 return(ACTION_NONE); 3575 } else { 3576 return(ACTION_ATTACK); 3577 } 3578 } 3579 } 3580 } 3581 } 3582 } 3583 } 3584 3585 /* 3586 ** Possibly try to select the specified object, if that is warranted. 3587 */ 3588 if (!Is_Weapon_Equipped() || !House->IsPlayerControl || object->Owner() == Owner()) { 3589 if ((!is_a_loaner || !Is_Owned_By_Player()) && object->Class_Of().IsSelectable && (!object->Is_Selected_By_Player() || CurrentObject.Count())) { 3590 return(ACTION_SELECT); 3591 } 3592 return(ACTION_NONE); 3593 } 3594 } 3595 return(ACTION_NONE); 3596 } 3597 3598 3599 /*********************************************************************************************** 3600 * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * 3601 * * 3602 * Use this routine to determine what action will be performed if the specified cell * 3603 * is clicked on. Usually this action is either a ACTION_MOVE or ACTION_NOMOVE. The action * 3604 * nomove is used to perform special case checking for nearby cells if in fact the mouse * 3605 * is clicked over the cell. * 3606 * * 3607 * INPUT: cell -- The cell to check for being clicked over. * 3608 * * 3609 * OUTPUT: Returns with the action that will occur if the cell is clicked on. * 3610 * * 3611 * WARNINGS: none * 3612 * * 3613 * HISTORY: * 3614 * 01/19/1995 JLB : Created. * 3615 * 07/10/1995 JLB : Force fire for buildings is explicitly disabled. * 3616 *=============================================================================================*/ 3617 ActionType TechnoClass::What_Action(CELL cell) const 3618 { 3619 assert(IsActive); 3620 3621 CellClass const * cellptr = &Map[cell]; 3622 OverlayTypeClass const * optr = NULL; 3623 3624 //bool altdown = (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)); 3625 //bool ctrldown = (Keyboard->Down(Options.KeyForceAttack1) || Keyboard->Down(Options.KeyForceAttack2)); 3626 //bool shiftdown = (Keyboard->Down(Options.KeySelect1) || Keyboard->Down(Options.KeySelect2)); 3627 //Added for getting the input for special character keys from the client 3628 // - 6/26/2019 JAS 3629 bool altdown = DLL_Export_Get_Input_Key_State(KN_LALT); 3630 bool ctrldown = DLL_Export_Get_Input_Key_State(KN_LCTRL); 3631 bool shiftdown = DLL_Export_Get_Input_Key_State(KN_LSHIFT); 3632 3633 /* 3634 ** Disable recognizing the <CTRL> key forced fire option when dealing with buildings. 3635 */ 3636 if (What_Am_I() == RTTI_BUILDING) ctrldown = false; 3637 3638 /* 3639 ** Disable recognizing the <CTRL> key forced fire option when dealing with submarines. 3640 */ 3641 if(What_Am_I() == RTTI_VESSEL) { 3642 WeaponTypeClass const * weapon = ((VesselClass *)this)->Class->PrimaryWeapon; 3643 if (weapon && weapon->Bullet->IsSubSurface) ctrldown = false; 3644 } 3645 3646 3647 if (cellptr->Overlay != OVERLAY_NONE) { 3648 optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); 3649 } 3650 3651 /* 3652 ** Special guard area mission is possible if both the control and the 3653 ** alt keys are held down. 3654 */ 3655 if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { 3656 return(ACTION_GUARD_AREA); 3657 } 3658 3659 if (House->IsPlayerControl && Techno_Type_Class()->PrimaryWeapon != NULL && (ctrldown || (optr && optr->IsLegalTarget))) { 3660 WarheadTypeClass const * whead = Techno_Type_Class()->PrimaryWeapon->WarheadPtr; 3661 3662 // To be fixed for firing on ore by accounting for ore and ignoring the overlay in that case. 3663 3664 if (optr == NULL || (optr->IsWall && (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)))) { 3665 int primary = What_Weapon_Should_I_Use(::As_Target(cell)); 3666 if (Can_Player_Move() || In_Range(::As_Target(cell), primary)) { 3667 return(ACTION_ATTACK); 3668 } 3669 } 3670 } 3671 3672 if (House->IsPlayerControl && Can_Player_Move()) { 3673 3674 /* 3675 ** Special override to force a move regardless of what is occupying the location. 3676 */ 3677 if (shiftdown) { 3678 return(ACTION_MOVE); 3679 } 3680 3681 /* 3682 ** If the object can enter the cell specified, then allow 3683 ** movement to it. 3684 */ 3685 if (Can_Enter_Cell(cell) <= MOVE_CLOAK) { 3686 return(ACTION_MOVE); 3687 } 3688 return(ACTION_NOMOVE); 3689 } 3690 3691 return(ACTION_NONE); 3692 } 3693 3694 3695 /*********************************************************************************************** 3696 * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * 3697 * * 3698 * Use this routine to determine whether a movement order can be given to this object. * 3699 * * 3700 * INPUT: none * 3701 * * 3702 * OUTPUT: bool; Can this object be given a movement order by the player? * 3703 * * 3704 * WARNINGS: none * 3705 * * 3706 * HISTORY: * 3707 * 01/19/1995 JLB : Created. * 3708 *=============================================================================================*/ 3709 bool TechnoClass::Can_Player_Move(void) const 3710 { 3711 assert(IsActive); 3712 3713 return(House->IsPlayerControl); 3714 } 3715 3716 3717 /*********************************************************************************************** 3718 * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order. * 3719 * * 3720 * Call this routine to determine if this object can be given a fire order by the player. * 3721 * Such objects will affect the mouse cursor accordingly -- usually causes the targeting * 3722 * cursor to appear. * 3723 * * 3724 * INPUT: none * 3725 * * 3726 * OUTPUT: bool; Can this object be given firing orders by the player? * 3727 * * 3728 * WARNINGS: none * 3729 * * 3730 * HISTORY: * 3731 * 01/23/1995 JLB : Created. * 3732 *=============================================================================================*/ 3733 bool TechnoClass::Can_Player_Fire(void) const 3734 { 3735 assert(IsActive); 3736 3737 if (House->IsPlayerControl && Is_Techno() && Techno_Type_Class()->PrimaryWeapon != NULL) { 3738 return(true); 3739 } 3740 return(false); 3741 } 3742 3743 3744 /*********************************************************************************************** 3745 * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * 3746 * * 3747 * Use this routine to determine if this object is equipped with a combat weapon. Such * 3748 * determination is used by the AI system to gauge the threat potential of the object. * 3749 * * 3750 * INPUT: none * 3751 * * 3752 * OUTPUT: bool; Is this object equipped with a combat weapon? * 3753 * * 3754 * WARNINGS: none * 3755 * * 3756 * HISTORY: * 3757 * 01/23/1995 JLB : Created. * 3758 *=============================================================================================*/ 3759 bool TechnoClass::Is_Weapon_Equipped(void) const 3760 { 3761 assert(IsActive); 3762 3763 return(Techno_Type_Class()->PrimaryWeapon != NULL); 3764 } 3765 3766 3767 /*********************************************************************************************** 3768 * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * 3769 * * 3770 * Use this routine to determine if the specified object is a candidate for repair. In * 3771 * order to qualify, the object must be allowed to be repaired (in theory) and it must * 3772 * be below full strength. If these conditions are met, then it can be repaired. * 3773 * * 3774 * INPUT: none * 3775 * * 3776 * OUTPUT: bool; May this unit be repaired? A return value of false may mean that the object * 3777 * is not allowed to be repaired, or it might be full strength already. * 3778 * * 3779 * WARNINGS: none * 3780 * * 3781 * HISTORY: * 3782 * 01/23/1995 JLB : Created. * 3783 *=============================================================================================*/ 3784 bool TechnoClass::Can_Repair(void) const 3785 { 3786 assert(IsActive); 3787 3788 /* 3789 ** Temporary hack to disable repair cursor over non-buildings. 3790 */ 3791 if (What_Am_I() != RTTI_BUILDING) { 3792 return(false); 3793 } 3794 return(Techno_Type_Class()->IsRepairable && Strength != Class_Of().MaxStrength); 3795 } 3796 3797 3798 /*********************************************************************************************** 3799 * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * 3800 * * 3801 * Use this routine to determine the maximum range for the weapon indicated. * 3802 * * 3803 * INPUT: which -- Which weapon to use when determining the range. 0=primary, 1=secondary. * 3804 * * 3805 * OUTPUT: Returns with the range of the weapon (in leptons). * 3806 * * 3807 * WARNINGS: none * 3808 * * 3809 * HISTORY: * 3810 * 03/19/1995 JLB : Created. * 3811 *=============================================================================================*/ 3812 int TechnoClass::Weapon_Range(int which) const 3813 { 3814 assert(IsActive); 3815 assert((unsigned)which < 2); 3816 3817 WeaponTypeClass const * weapon = NULL; 3818 TechnoTypeClass const & ttype = *Techno_Type_Class(); 3819 3820 switch (which) { 3821 case 0: 3822 weapon = ttype.PrimaryWeapon; 3823 break; 3824 3825 case 1: 3826 weapon = ttype.SecondaryWeapon; 3827 break; 3828 } 3829 if (weapon != NULL) { 3830 return(weapon->Range); 3831 } 3832 return(0); 3833 } 3834 3835 3836 /*************************************************************************** 3837 * TechnoClass::Override_Mission -- temporarily overrides a units mission * 3838 * * 3839 * * 3840 * * 3841 * INPUT: MissionType mission - the mission we want to override * 3842 * TARGET tarcom - the new target we want to override * 3843 * TARGET navcom - the new navigation point to override* 3844 * * 3845 * OUTPUT: none * 3846 * * 3847 * WARNINGS: If a mission is already overridden, the current mission is * 3848 * just re-assigned. * 3849 * * 3850 * HISTORY: * 3851 * 04/28/1995 PWG : Created. * 3852 *=========================================================================*/ 3853 void TechnoClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) 3854 { 3855 assert(IsActive); 3856 3857 SuspendedTarCom = TarCom; 3858 RadioClass::Override_Mission(mission, tarcom, navcom); 3859 Assign_Target(tarcom); 3860 } 3861 3862 3863 /*************************************************************************** 3864 * TechnoClass::Restore_Mission -- Restores an overridden mission * 3865 * * 3866 * INPUT: none * 3867 * * 3868 * OUTPUT: none * 3869 * * 3870 * WARNINGS: none * 3871 * * 3872 * HISTORY: * 3873 * 04/28/1995 PWG : Created. * 3874 *=========================================================================*/ 3875 bool TechnoClass::Restore_Mission(void) 3876 { 3877 assert(IsActive); 3878 3879 if (RadioClass::Restore_Mission()) { 3880 Assign_Target(SuspendedTarCom); 3881 return(true); 3882 } 3883 return(false); 3884 } 3885 3886 3887 /*********************************************************************************************** 3888 * TechnoClass::Renovate -- Heal a building to maximum * 3889 * * 3890 * * 3891 * INPUT: none * 3892 * * 3893 * OUTPUT: none * 3894 * * 3895 * WARNINGS: none * 3896 * * 3897 * HISTORY: * 3898 * 04/15/1996 BWG : Created. * 3899 *=============================================================================================*/ 3900 void TechnoClass::Renovate(void) 3901 { 3902 assert(IsActive); 3903 3904 Mark(MARK_CHANGE); 3905 Strength = Techno_Type_Class()->MaxStrength; 3906 if (What_Am_I() == RTTI_BUILDING) { 3907 ((BuildingClass *)this)->Repair(0); 3908 } 3909 } 3910 3911 3912 /*********************************************************************************************** 3913 * TechnoClass::Captured -- Handles capturing this object. * 3914 * * 3915 * This routine is called when this object gets captured by the house specified. It handles * 3916 * removing this object from any targeting computers and then changes the ownership of * 3917 * the object to the new house. * 3918 * * 3919 * INPUT: newowner -- Pointer to the house that is now the new owner. * 3920 * * 3921 * OUTPUT: Was the object captured? Failure would mean that it is already under control of * 3922 * the house specified. * 3923 * * 3924 * WARNINGS: none * 3925 * * 3926 * HISTORY: * 3927 * 05/08/1995 JLB : Created. * 3928 * 09/29/1995 JLB : Keeps track of quantity records. * 3929 *=============================================================================================*/ 3930 bool TechnoClass::Captured(HouseClass * newowner) 3931 { 3932 assert(IsActive); 3933 3934 if (newowner != House) { 3935 3936 /* 3937 ** Capture attempt springs any "entered" trigger. The entered trigger 3938 ** occurs first since there may be a special trigger attached to this 3939 ** object that flags a capture as a win and a destroy as a loss. This 3940 ** order is necessary because the object is recorded as a kill as well. 3941 */ 3942 if (Trigger.Is_Valid()) { 3943 Trigger->Spring(TEVENT_PLAYER_ENTERED, this); 3944 } 3945 3946 /* 3947 ** Record this as a kill. 3948 */ 3949 Record_The_Kill(NULL); 3950 3951 /* 3952 ** Special kill record logic for capture process. 3953 */ 3954 House->Tracking_Remove(this); 3955 newowner->Tracking_Add(this); 3956 switch (What_Am_I()) { 3957 case RTTI_BUILDING: 3958 newowner->BuildingsKilled[Owner()]++; 3959 break; 3960 3961 case RTTI_AIRCRAFT: 3962 case RTTI_INFANTRY: 3963 case RTTI_UNIT: 3964 case RTTI_VESSEL: 3965 newowner->UnitsKilled[Owner()]++; 3966 break; 3967 3968 default: 3969 break; 3970 } 3971 House->WhoLastHurtMe = newowner->Class->House; 3972 3973 /* 3974 ** Remove from targeting computers. 3975 */ 3976 Detach_All(false); 3977 3978 /* 3979 ** Change ownership now. 3980 */ 3981 House = newowner; 3982 IsOwnedByPlayer = (House == PlayerPtr); 3983 3984 return(true); 3985 } 3986 return(false); 3987 } 3988 3989 3990 /*********************************************************************************************** 3991 * TechnoClass::Take_Damage -- Records damage assessed to this object. * 3992 * * 3993 * This routine is called when this object has taken damage. It handles recording whether * 3994 * this object has been destroyed. If it has, then mark the appropriate kill records as * 3995 * necessary. * 3996 * * 3997 * INPUT: * 3998 * * 3999 * OUTPUT: * 4000 * * 4001 * WARNINGS: * 4002 * * 4003 * HISTORY: * 4004 * 06/20/1995 JLB : Created. * 4005 *=============================================================================================*/ 4006 ResultType TechnoClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) 4007 { 4008 assert(IsActive); 4009 4010 ResultType result = RESULT_NONE; 4011 4012 /* 4013 ** If not a forced damage condition, adjust damage according to house override armor 4014 ** value. 4015 */ 4016 if (!forced && damage > 0) { 4017 damage = damage * ArmorBias * House->ArmorBias; 4018 } 4019 4020 if (IronCurtainCountDown == 0) { 4021 result = ObjectClass::Take_Damage(damage, distance, warhead, source, forced); 4022 } 4023 4024 switch (result) { 4025 case RESULT_DESTROYED: 4026 Transmit_Message(RADIO_OVER_OUT); 4027 Stun(); 4028 4029 /* 4030 ** If this object explodes with violent damage, then perform the explosion 4031 ** now and use the warhead type and full strength as the explosion values. 4032 */ 4033 if (Techno_Type_Class()->IsExploding) { 4034 4035 /* 4036 ** The warhead to use is based on the weapon this object is equipped with. 4037 */ 4038 WarheadType wh = WARHEAD_HE; 4039 if (Techno_Type_Class()->PrimaryWeapon != NULL) { 4040 wh = WarheadType(Techno_Type_Class()->PrimaryWeapon->WarheadPtr->ID); 4041 } 4042 4043 int damage = Techno_Type_Class()->MaxStrength; 4044 new AnimClass(Combat_Anim(damage, wh, Map[Center_Coord()].Land_Type()), Center_Coord()); 4045 int radius = damage * Rule.ExplosionSpread; 4046 // int radius = damage/2; 4047 Wide_Area_Damage(Center_Coord(), radius, damage, source, wh); 4048 } 4049 4050 if (this == (TechnoClass *)::As_Object(House->UnitToTeleport)) { 4051 House->UnitToTeleport = 0; 4052 if (!Scen.IsFadingColor) { 4053 Scen.IsFadingBW = false; 4054 Scen.IsFadingColor = true; 4055 Scen.FadeTimer = GRAYFADETIME; 4056 } 4057 if (Map.IsTargettingMode == SPC_CHRONO2) { 4058 KeyNumType input = KN_RMOUSE; 4059 Map.AI(input, 0, 0); 4060 } 4061 } 4062 4063 /* 4064 ** May trigger an achievement. ST - 11/14/2019 1:56PM 4065 */ 4066 if (Session.Type == GAME_NORMAL && !House->IsHuman && source && source->House && source->House->IsHuman) { 4067 TechnoTypeClass const *object_type = Techno_Type_Class(); 4068 if (object_type) { 4069 4070 RTTIType what = What_Am_I(); 4071 if (what == RTTI_AIRCRAFT || what == RTTI_INFANTRY || what == RTTI_UNIT || what == RTTI_VESSEL) { 4072 On_Achievement_Event(source->House, "UNIT_DESTROYED", object_type->IniName); 4073 } 4074 } 4075 } 4076 break; 4077 4078 /* 4079 ** If some damage was received and this object is cloaked, shimmer 4080 ** the cloak a bit. 4081 */ 4082 default: 4083 if (source != NULL && !House->Is_Ally(source)) { 4084 IsTickedOff = true; 4085 } 4086 Do_Shimmer(); 4087 break; 4088 4089 case RESULT_NONE: 4090 break; 4091 } 4092 return(result); 4093 } 4094 4095 4096 /*********************************************************************************************** 4097 * TechnoClass::Record_The_Kill -- Records the death of this object. * 4098 * * 4099 * This routine is used to record the death of this object. It will handle updating the * 4100 * owner house with the kill record as well as springing any trigger events associated with * 4101 * this object's death. * 4102 * * 4103 * INPUT: source -- Pointer to the source of this object's death (if there is a source). * 4104 * * 4105 * OUTPUT: none * 4106 * * 4107 * WARNINGS: none * 4108 * * 4109 * HISTORY: * 4110 * 07/08/1995 JLB : Created. * 4111 * 08/23/1995 JLB : Building loss is only counted if it received damage. * 4112 *=============================================================================================*/ 4113 void TechnoClass::Record_The_Kill(TechnoClass * source) 4114 { 4115 assert(IsActive); 4116 4117 int total_recorded = 0; 4118 4119 int points = Techno_Type_Class()->Points; 4120 4121 /* 4122 ** Handle any trigger event associated with this object. 4123 */ 4124 if (Trigger.Is_Valid() && source) Trigger->Spring(TEVENT_ATTACKED, this); 4125 4126 if (Trigger.Is_Valid() && source) Trigger->Spring(TEVENT_DISCOVERED, this); 4127 4128 if (Trigger.Is_Valid()) Trigger->Spring(TEVENT_DESTROYED, this); 4129 4130 if (source != NULL) { 4131 Crew.Made_A_Kill(); 4132 4133 House->WhoLastHurtMe = source->Owner(); 4134 4135 /* 4136 ** Add up the score for killing this unit 4137 */ 4138 source->House->PointTotal += points; 4139 } 4140 #ifdef FIXIT_CSII // checked - ajw 9/28/98 4141 // Hack check: if they were trying to teleport this unit when it died, take 4142 // the map mode out of teleportation mode. 4143 if(IsOwnedByPlayer && Map.IsTargettingMode == SPC_CHRONO2 && House->UnitToTeleport == As_Target()) { 4144 Map.IsTargettingMode = SPC_NONE; 4145 } 4146 #endif 4147 switch (What_Am_I()) { 4148 case RTTI_BUILDING: 4149 { 4150 StructType bldg = *(BuildingClass *)this; 4151 if (bldg != STRUCT_BARREL && bldg != STRUCT_BARREL3 && 4152 bldg != STRUCT_APMINE && bldg != STRUCT_AVMINE) { 4153 if (((BuildingClass *)this)->WhoLastHurtMe != HOUSE_NONE) { 4154 House->BuildingsLost++; 4155 } 4156 4157 if (source != NULL) { 4158 if (Session.Type == GAME_INTERNET) { 4159 source->House->DestroyedBuildings->Increment_Unit_Total( ((BuildingClass*)this)->Class->Type ); 4160 } 4161 source->House->BuildingsKilled[Owner()]++; 4162 } 4163 4164 /* 4165 ** If the map is displaying the multiplayer player names & their 4166 ** # of kills, tell it to redraw. 4167 */ 4168 if (Map.Is_Player_Names()) { 4169 Map.Player_Names(true); 4170 } 4171 } 4172 } 4173 break; 4174 4175 case RTTI_AIRCRAFT: 4176 if (source != NULL && Session.Type == GAME_INTERNET) { 4177 source->House->DestroyedAircraft->Increment_Unit_Total( ((AircraftClass*)this)->Class->Type ); 4178 total_recorded++; 4179 } 4180 //Fall through..... 4181 case RTTI_INFANTRY: 4182 if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { 4183 source->House->DestroyedInfantry->Increment_Unit_Total( ((InfantryClass*)this)->Class->Type ); 4184 total_recorded++; 4185 } 4186 //Fall through..... 4187 case RTTI_UNIT: 4188 if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { 4189 source->House->DestroyedUnits->Increment_Unit_Total( ((UnitClass*)this)->Class->Type ); 4190 total_recorded++; 4191 } 4192 //Fall through..... 4193 case RTTI_VESSEL: 4194 if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { 4195 source->House->DestroyedUnits->Increment_Unit_Total( ((VesselClass*)this)->Class->Type ); 4196 } 4197 4198 House->UnitsLost++; 4199 if (source != NULL) source->House->UnitsKilled[Owner()]++; 4200 4201 /* 4202 ** If the map is displaying the multiplayer player names & their 4203 ** # of kills, tell it to redraw. 4204 */ 4205 if (Map.Is_Player_Names()) { 4206 Map.Player_Names(true); 4207 } 4208 break; 4209 4210 default: 4211 break; 4212 } 4213 4214 /* 4215 ** Since we lost an object, we lose the associated points as well. 4216 */ 4217 House->PointTotal -= points; 4218 } 4219 4220 4221 /*********************************************************************************************** 4222 * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * 4223 * * 4224 * This routine is used to find a nearby location from center of this object. It can lean * 4225 * toward finding a location closest to an optional object. * 4226 * * 4227 * INPUT: object -- Optional object that the finding algorithm will try to find a close * 4228 * spot to. * 4229 * * 4230 * OUTPUT: Returns with the cell that is closest to this object. * 4231 * * 4232 * WARNINGS: none * 4233 * * 4234 * HISTORY: * 4235 * 07/06/1995 JLB : Created. * 4236 * 09/28/1995 JLB : Uses map scan function. * 4237 *=============================================================================================*/ 4238 CELL TechnoClass::Nearby_Location(TechnoClass const * techno, int locationmod) const 4239 { 4240 assert(IsActive); 4241 4242 SpeedType speed = Techno_Type_Class()->Speed; 4243 if (speed == SPEED_WINGED) { 4244 speed = SPEED_TRACK; 4245 } 4246 4247 CELL cell = 0; 4248 if (techno != NULL) { 4249 cell = Coord_Cell(techno->Center_Coord()); 4250 } else { 4251 cell = Coord_Cell(Center_Coord()); 4252 } 4253 4254 return(Map.Nearby_Location(cell, speed, Map[cell].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone, false, locationmod)); 4255 } 4256 4257 4258 /*********************************************************************************************** 4259 * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * 4260 * * 4261 * This routine will start the stealth tank to uncloak. * 4262 * * 4263 * INPUT: none * 4264 * * 4265 * OUTPUT: none * 4266 * * 4267 * WARNINGS: none * 4268 * * 4269 * HISTORY: * 4270 * 05/08/1995 JLB : Created. * 4271 *=============================================================================================*/ 4272 void TechnoClass::Do_Uncloak(void) 4273 { 4274 assert(IsActive); 4275 4276 if (IsCloakable && (Cloak == CLOAKED || Cloak == CLOAKING)) { 4277 if (Cloak == CLOAKED) { 4278 Map.RadarClass::Flag_To_Redraw(true); 4279 } 4280 Cloak = UNCLOAKING; 4281 CloakingDevice.Set_Stage(0); 4282 CloakingDevice.Set_Rate(1); 4283 #ifdef FIXIT_CSII // checked - ajw 9/28/98 4284 if(What_Am_I() == RTTI_VESSEL) { 4285 Sound_Effect(VOC_SUBSHOW, Coord); 4286 } else { 4287 Sound_Effect(VOC_IRON1, Coord); 4288 } 4289 #else 4290 Sound_Effect(VOC_SUBSHOW, Coord); 4291 #endif 4292 } 4293 } 4294 4295 4296 /*********************************************************************************************** 4297 * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * 4298 * * 4299 * This routine will start the object into its cloaking state. * 4300 * * 4301 * INPUT: none * 4302 * * 4303 * OUTPUT: none * 4304 * * 4305 * WARNINGS: none * 4306 * * 4307 * HISTORY: * 4308 * 07/08/1995 JLB : Created. * 4309 *=============================================================================================*/ 4310 void TechnoClass::Do_Cloak(void) 4311 { 4312 assert(IsActive); 4313 4314 if (IsCloakable && (Cloak == UNCLOAKED || Cloak == UNCLOAKING)) { 4315 Detach_All(false); 4316 4317 if (Cloak == UNCLOAKED) { 4318 Map.RadarClass::Flag_To_Redraw(true); 4319 } 4320 4321 Cloak = CLOAKING; 4322 CloakingDevice.Set_Stage(0); 4323 CloakingDevice.Set_Rate(1); 4324 #ifdef FIXIT_CSII // checked - ajw 9/28/98 4325 if(What_Am_I() == RTTI_VESSEL) { 4326 Sound_Effect(VOC_SUBSHOW, Coord); 4327 } else { 4328 Sound_Effect(VOC_IRON1, Coord); 4329 } 4330 #else 4331 Sound_Effect(VOC_SUBSHOW, Coord); 4332 #endif 4333 } 4334 } 4335 4336 4337 /*********************************************************************************************** 4338 * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * 4339 * * 4340 * This routine is called when this object should shimmer. If the object is cloaked, then * 4341 * a shimmering effect (partial decloak) occurs. For objects that are not cloaked, no * 4342 * effect occurs. * 4343 * * 4344 * INPUT: none * 4345 * * 4346 * OUTPUT: none * 4347 * * 4348 * WARNINGS: none * 4349 * * 4350 * HISTORY: * 4351 * 07/29/1995 JLB : Created. * 4352 *=============================================================================================*/ 4353 void TechnoClass::Do_Shimmer(void) 4354 { 4355 assert(IsActive); 4356 #if(0) 4357 if (IsCloakable && Cloak == CLOAKED) { 4358 Cloak = CLOAKING; 4359 CloakingDevice.Set_Stage(MAX_UNCLOAK_STAGE/2); 4360 CloakingDevice.Set_Rate(1); 4361 } 4362 #else 4363 Do_Uncloak(); 4364 #endif 4365 } 4366 4367 4368 /*********************************************************************************************** 4369 * TechnoClass::Visual_Character -- Determine the visual character of the object. * 4370 * * 4371 * This routine will determine how this object should be drawn. Typically, this is the * 4372 * unmodified visible state, but cloaked objects have a different character. * 4373 * * 4374 * INPUT: raw -- Should the check be based on the unmodified cloak condition of the * 4375 * object? If false, then an object owned by the player will never become * 4376 * completely invisible. * 4377 * * 4378 * OUTPUT: Returns with the visual character to use when displaying this object. * 4379 * * 4380 * WARNINGS: none * 4381 * * 4382 * HISTORY: * 4383 * 07/07/1995 JLB : Created. * 4384 * 05/27/1996 JLB : Knows about invisible objects. * 4385 *=============================================================================================*/ 4386 VisualType TechnoClass::Visual_Character(bool raw) const 4387 { 4388 assert(IsActive); 4389 4390 if (Techno_Type_Class()->IsInvisible) { 4391 if ((Session.Type != GAME_NORMAL) || Is_Owned_By_Player()) return(VISUAL_NORMAL); 4392 if (!Debug_Map) return(VISUAL_HIDDEN); 4393 } 4394 4395 /* 4396 ** When uncloaked or in map editor mode, always draw the object normally. 4397 */ 4398 if (Cloak == UNCLOAKED || Debug_Map) return(VISUAL_NORMAL); 4399 4400 /* 4401 ** A cloaked unit will not be visible at all unless it is owned 4402 ** by the player. 4403 */ 4404 if (Cloak == CLOAKED) { 4405 // Changed for multiplayer. Not needed except to test in the old renderer. ST - 3/13/2019 5:56PM 4406 if (!raw && Is_Owned_By_Player()) return(VISUAL_SHADOWY); 4407 //if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); 4408 return(VISUAL_HIDDEN); 4409 } 4410 4411 int stage = CloakingDevice.Fetch_Stage(); 4412 if (Cloak == UNCLOAKING) stage = MAX_UNCLOAK_STAGE - stage; 4413 if (stage <= 0) { 4414 return(VISUAL_NORMAL); 4415 } 4416 4417 stage = fixed(stage, MAX_UNCLOAK_STAGE) * 256; 4418 4419 if (stage < 0x0040) return(VISUAL_INDISTINCT); 4420 if (stage < 0x0080) return(VISUAL_DARKEN); 4421 if (stage < 0x00C0) return(VISUAL_SHADOWY); 4422 //if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); 4423 if (!raw && Is_Owned_By_Player()) return(VISUAL_SHADOWY); // Changed for multiplayer. Not needed except to test in the old renderer. ST - 3/13/2019 5:56PM 4424 if (stage < 0x00FF) return(VISUAL_RIPPLE); 4425 return(VISUAL_HIDDEN); 4426 } 4427 4428 4429 /*********************************************************************************************** 4430 * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * 4431 * * 4432 * This routine is used to draw the object. It will handle any remapping or cloaking * 4433 * effects required. This logic is isolated here since all techno object share the same * 4434 * render logic when it comes to remapping and cloaking. * 4435 * * 4436 * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. * 4437 * * 4438 * shapenum -- The shape number of the object in the file to use. * 4439 * * 4440 * x,y -- Center pixel coordinate to use for rendering this object. * 4441 * * 4442 * window -- The clipping window to use when rendering. * 4443 * * 4444 * rotation -- The rotation of the object. * 4445 * * 4446 * scale -- The scaling factor to use (24.8 fixed point). * 4447 * * 4448 * OUTPUT: none * 4449 * * 4450 * WARNINGS: none * 4451 * * 4452 * HISTORY: * 4453 * 07/08/1995 JLB : Created. * 4454 * 01/11/1996 JLB : Added rotation and scaling. * 4455 *=============================================================================================*/ 4456 void TechnoClass::Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation, int scale) const 4457 { 4458 assert(IsActive); 4459 4460 if (rotation != DIR_N || scale != 0x0100) { 4461 Disable_Uncompressed_Shapes(); 4462 } 4463 4464 if (shapefile != NULL) { 4465 VisualType visual = Visual_Character(); 4466 void const * remap = Remap_Table(); 4467 void const * shadow = Map.UnitShadow; 4468 4469 #ifdef PARTIAL 4470 /* 4471 ** Create a minimum shape rectangle if one hasn't already been 4472 ** calculated and the shape file matches the one that the 4473 ** class thinks it should be using. This check is necessary because 4474 ** the dimension rectangle pointer is referenced from the type class 4475 ** object on the presumption that the shapefile pointer passed to this 4476 ** routine matches. If it doesn't match, then the wrong rectangle information 4477 ** will be stored into the type class object. 4478 */ 4479 TechnoTypeClass * ttype = Techno_Type_Class(); 4480 if (shapefile == ttype->Get_Image_Data() && shapenum < Get_Build_Frame_Count(shapefile)-1) { 4481 if (ttype->DimensionData == NULL) { 4482 ttype->DimensionData = new Rect [Get_Build_Frame_Count(shapefile)]; 4483 } 4484 if (ttype->DimensionData != NULL && !ttype->DimensionData[shapenum].Is_Valid()) { 4485 ttype->DimensionData[shapenum] = Shape_Dimensions(shapefile, shapenum); 4486 } 4487 } 4488 #endif 4489 if (Height > 0) { 4490 shadow = Map.UnitShadowAir; 4491 } 4492 4493 y -= Lepton_To_Pixel(Height); 4494 4495 /* 4496 ** If they're viewing a spy, and the spy belongs to some other house, 4497 ** make it look like an infantryman from our house 4498 */ 4499 if (What_Am_I() == RTTI_INFANTRY) { 4500 if (!IsOwnedByPlayer) { 4501 if (*(InfantryClass *)this == INFANTRY_SPY) remap = PlayerPtr->Remap_Table(); 4502 } 4503 if (((InfantryClass *)this)->Class->IsRemapOverride) { 4504 remap = ((InfantryClass *)this)->Class->OverrideRemap; 4505 } 4506 } 4507 4508 /* 4509 ** Check for the special visual effect for the iron curtain 4510 */ 4511 if (IronCurtainCountDown > 0) { 4512 // remap = RemapEmber; 4513 remap = DisplayClass::FadingRed; 4514 } 4515 4516 #ifdef PREDATOR 4517 if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) { 4518 if (visual == VISUAL_SHADOWY) { 4519 CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade, rotation, scale); 4520 } else { 4521 CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); 4522 } 4523 if (visual == VISUAL_DARKEN) { 4524 CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade, rotation, scale); 4525 } 4526 } 4527 if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) { 4528 CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL, NULL, NULL, rotation, scale); 4529 } 4530 #else 4531 switch (visual) { 4532 case VISUAL_NORMAL: 4533 // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 4534 CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); 4535 break; 4536 4537 case VISUAL_INDISTINCT: 4538 case VISUAL_DARKEN: 4539 // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 4540 CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap, Map.FadingShade, rotation, scale); 4541 break; 4542 4543 case VISUAL_SHADOWY: 4544 case VISUAL_RIPPLE: 4545 // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 4546 CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); 4547 break; 4548 4549 case VISUAL_HIDDEN: 4550 // Server still needs to "render" hidden objects to the virtual window, so objects get created properly - SKY 4551 if (window == WINDOW_VIRTUAL) { 4552 CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); 4553 } 4554 break; 4555 } 4556 #endif 4557 } 4558 4559 Enable_Uncompressed_Shapes(); 4560 } 4561 4562 4563 4564 /*********************************************************************************************** 4565 * TechnoClass::Techno_Draw_Object_Virtual -- Draw object with virtual window support * 4566 * * 4567 * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. * 4568 * * 4569 * shapenum -- The shape number of the object in the file to use. * 4570 * * 4571 * x,y -- Center pixel coordinate to use for rendering this object. * 4572 * * 4573 * window -- The clipping window to use when rendering. * 4574 * * 4575 * rotation -- The rotation of the object. * 4576 * * 4577 * scale -- The scaling factor to use (24.8 fixed point). * 4578 * * 4579 * shape_name -- The name of the shapefile * 4580 * * 4581 * OUTPUT: none * 4582 * * 4583 * WARNINGS: none * 4584 * * 4585 * HISTORY: * 4586 * 8/1/2019 5:32PM - ST : Created. * 4587 *=============================================================================================*/ 4588 void TechnoClass::Techno_Draw_Object_Virtual(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation, int scale, const char *shape_name) const 4589 { 4590 assert(IsActive); 4591 4592 if (shape_name == NULL || *shape_name == 0) { 4593 /* 4594 ** If there's no override shape name, then call the regular draw 4595 */ 4596 Techno_Draw_Object(shapefile, shapenum, x, y, window, rotation, scale); 4597 return; 4598 } 4599 4600 if (shapefile != NULL) { 4601 VisualType visual = Visual_Character(); 4602 void const * remap = Remap_Table(); 4603 void const * shadow = Map.UnitShadow; 4604 4605 #ifdef PARTIAL 4606 /* 4607 ** Create a minimum shape rectangle if one hasn't already been 4608 ** calculated and the shape file matches the one that the 4609 ** class thinks it should be using. This check is necessary because 4610 ** the dimension rectangle pointer is referenced from the type class 4611 ** object on the presumption that the shapefile pointer passed to this 4612 ** routine matches. If it doesn't match, then the wrong rectangle information 4613 ** will be stored into the type class object. 4614 */ 4615 TechnoTypeClass * ttype = Techno_Type_Class(); 4616 if (shapefile == ttype->Get_Image_Data() && shapenum < Get_Build_Frame_Count(shapefile)-1) { 4617 if (ttype->DimensionData == NULL) { 4618 ttype->DimensionData = new Rect [Get_Build_Frame_Count(shapefile)]; 4619 } 4620 if (ttype->DimensionData != NULL && !ttype->DimensionData[shapenum].Is_Valid()) { 4621 ttype->DimensionData[shapenum] = Shape_Dimensions(shapefile, shapenum); 4622 } 4623 } 4624 #endif 4625 4626 if (Height > 0) { 4627 shadow = Map.UnitShadowAir; 4628 } 4629 4630 y -= Lepton_To_Pixel(Height); 4631 4632 /* 4633 ** If they're viewing a spy, and the spy belongs to some other house, 4634 ** make it look like an infantryman from our house 4635 */ 4636 if (What_Am_I() == RTTI_INFANTRY) { 4637 if (!IsOwnedByPlayer) { 4638 if (*(InfantryClass *)this == INFANTRY_SPY) remap = PlayerPtr->Remap_Table(); 4639 } 4640 if (((InfantryClass *)this)->Class->IsRemapOverride) { 4641 remap = ((InfantryClass *)this)->Class->OverrideRemap; 4642 } 4643 } 4644 4645 /* 4646 ** Check for the special visual effect for the iron curtain 4647 */ 4648 if (IronCurtainCountDown > 0) { 4649 // remap = RemapEmber; 4650 remap = DisplayClass::FadingRed; 4651 } 4652 4653 #ifdef PREDATOR 4654 if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) { 4655 if (visual == VISUAL_SHADOWY) { 4656 CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade, rotation, scale); 4657 } else { 4658 CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); 4659 } 4660 if (visual == VISUAL_DARKEN) { 4661 CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade, rotation, scale); 4662 } 4663 } 4664 if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) { 4665 CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL, NULL, NULL, rotation, scale); 4666 } 4667 #else 4668 switch (visual) { 4669 case VISUAL_NORMAL: 4670 // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 4671 CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); 4672 break; 4673 4674 case VISUAL_INDISTINCT: 4675 case VISUAL_DARKEN: 4676 // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 4677 CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap, Map.FadingShade, rotation, scale); 4678 break; 4679 4680 case VISUAL_SHADOWY: 4681 case VISUAL_RIPPLE: 4682 // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 4683 CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); 4684 break; 4685 4686 case VISUAL_HIDDEN: 4687 // Server still needs to "render" hidden objects to the virtual window, so objects get created properly - SKY 4688 if (window == WINDOW_VIRTUAL) { 4689 CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); 4690 } 4691 break; 4692 } 4693 #endif 4694 } 4695 } 4696 4697 4698 /*********************************************************************************************** 4699 * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * 4700 * * 4701 * This routine is used to fetch the appropriate remap table to use for this object. * 4702 * * 4703 * INPUT: none * 4704 * * 4705 * OUTPUT: Returns with a pointer to the remap table to use for this object. * 4706 * * 4707 * WARNINGS: none * 4708 * * 4709 * HISTORY: * 4710 * 07/08/1995 JLB : Created. * 4711 *=============================================================================================*/ 4712 void const * TechnoClass::Remap_Table(void) const 4713 { 4714 assert(IsActive); 4715 4716 if (Techno_Type_Class()->IsRemappable) { 4717 return(House->Remap_Table(IsBlushing, Techno_Type_Class()->Remap)); 4718 } 4719 return(ColorRemaps[PCOLOR_GOLD].RemapTable); 4720 } 4721 4722 4723 /*********************************************************************************************** 4724 * TechnoClass::Detach -- Handles removal of target from tracking system. * 4725 * * 4726 * This routine is called when the specified object is about to be removed from the game * 4727 * system. The target object is removed from any tracking computers that this object may * 4728 * have. * 4729 * * 4730 * INPUT: target -- The target object (as a target value) that is being removed from the * 4731 * game. * 4732 * * 4733 * all -- Is the target about to die? A false value might indicate that the * 4734 * object is merely cloaking. In such a case, radio contact will not * 4735 * be affected. * 4736 * * 4737 * OUTPUT: none * 4738 * * 4739 * WARNINGS: none * 4740 * * 4741 * HISTORY: * 4742 * 07/29/1995 JLB : Created. * 4743 *=============================================================================================*/ 4744 void TechnoClass::Detach(TARGET target, bool all) 4745 { 4746 assert(IsActive); 4747 RadioClass::Detach(target, all); 4748 4749 if (SuspendedMission != MISSION_NONE && SuspendedTarCom == target) { 4750 SuspendedMission = MISSION_NONE; 4751 SuspendedTarCom = TARGET_NONE; 4752 } 4753 4754 /* 4755 ** If the targeting computer is assigned to the target, then the targeting 4756 ** computer must be cleared. 4757 */ 4758 if (TarCom == target) { 4759 Assign_Target(TARGET_NONE); 4760 Restore_Mission(); 4761 } 4762 4763 /* 4764 ** If it is in radio contact with another object, then that radio contact 4765 ** must be broken. 4766 */ 4767 if (all && In_Radio_Contact() && Contact_With_Whom()->As_Target() == target) { 4768 Transmit_Message(RADIO_OVER_OUT); 4769 } 4770 } 4771 4772 4773 /*********************************************************************************************** 4774 * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * 4775 * * 4776 * This routine handles the destruction of any cargo this object may contain. Typical of * 4777 * this would be when a transport helicopter gets destroyed. * 4778 * * 4779 * INPUT: source -- The source of the destruction of the cargo. * 4780 * * 4781 * OUTPUT: none * 4782 * * 4783 * WARNINGS: none * 4784 * * 4785 * HISTORY: * 4786 * 07/29/1995 JLB : Created. * 4787 *=============================================================================================*/ 4788 void TechnoClass::Kill_Cargo(TechnoClass * source) 4789 { 4790 assert(IsActive); 4791 4792 while (Is_Something_Attached()) { 4793 FootClass * foot = Detach_Object(); 4794 if (foot != NULL) { 4795 foot->Record_The_Kill(source); 4796 delete foot; 4797 } 4798 } 4799 } 4800 4801 4802 /*********************************************************************************************** 4803 * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * 4804 * * 4805 * This routine is called when generating survivors to this object. This routine returns * 4806 * the type of survivor to generate. * 4807 * * 4808 * INPUT: none * 4809 * * 4810 * OUTPUT: Returns the infantry type of a survivor. * 4811 * * 4812 * WARNINGS: This routine is designed to be called repeatedly. Once for each survivor to * 4813 * generate. * 4814 * * 4815 * HISTORY: * 4816 * 07/29/1995 JLB : Created. * 4817 *=============================================================================================*/ 4818 InfantryType TechnoClass::Crew_Type(void) const 4819 { 4820 assert(IsActive); 4821 4822 /* 4823 ** If this object contains no crew, then there can be no 4824 ** crew inside, duh... return this news. 4825 */ 4826 if (!Techno_Type_Class()->IsCrew) { 4827 return(INFANTRY_NONE); 4828 } 4829 4830 /* 4831 ** The normal infantry survivor is the standard issue 4832 ** minigunner. Certain buildings, especially neutral ones, tend to have 4833 ** civilians exit them instead. 4834 */ 4835 InfantryType infantry = INFANTRY_E1; 4836 if (House->ActLike == HOUSE_NEUTRAL) { 4837 infantry = Random_Pick(INFANTRY_C1, INFANTRY_C9); 4838 } else { 4839 if (Techno_Type_Class()->PrimaryWeapon == NULL && Percent_Chance(15)) { 4840 if (Percent_Chance(50)) { 4841 infantry = INFANTRY_C1; 4842 } else { 4843 infantry = INFANTRY_C7; 4844 } 4845 } 4846 } 4847 return(infantry); 4848 } 4849 4850 4851 /*********************************************************************************************** 4852 * TechnoClass::Value -- Fetches the target value for this object. * 4853 * * 4854 * This routine is used to fetch the target value for this object. The greater the value * 4855 * returned, the better this object is as a target. * 4856 * * 4857 * INPUT: none * 4858 * * 4859 * OUTPUT: Returns with the target value for this object. * 4860 * * 4861 * WARNINGS: none * 4862 * * 4863 * HISTORY: * 4864 * 07/29/1995 JLB : Created. * 4865 * 08/16/1995 JLB : Adjusted for early mission lame-out. * 4866 *=============================================================================================*/ 4867 int TechnoClass::Value(void) const 4868 { 4869 assert(IsActive); 4870 4871 int value = 0; 4872 4873 /* 4874 ** In early missions, contents of transports are not figured 4875 ** into the total value. 4876 */ 4877 if (Rule.Diff[House->Difficulty].IsContentScan || House->IQ >= Rule.IQContentScan) { 4878 if (Is_Something_Attached()) { 4879 FootClass * object = Attached_Object(); 4880 4881 while (object != NULL) { 4882 value += object->Value(); 4883 object = (FootClass *)(ObjectClass *)object->Next; 4884 } 4885 } 4886 } 4887 4888 #ifdef TOFIX 4889 /* 4890 ** Increase the value of power producing object when there is power critical 4891 ** defensive structures. 4892 */ 4893 if (What_Am_I() == RTTI_BUILDING && ((BuildingClass *)this)->Class->Power) { 4894 if (House->BScan & (STRUCTF_ATOWER|STRUCTF_OBELISK)) { 4895 value += Techno_Type_Class()->Reward; 4896 } 4897 } 4898 #endif 4899 4900 return Risk() + Techno_Type_Class()->Reward + value; 4901 } 4902 4903 4904 /*********************************************************************************************** 4905 * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * 4906 * * 4907 * This routine will return the range to scan based on the control value specified. The * 4908 * value returned by this routine is typically used when scanning for enemies. * 4909 * * 4910 * INPUT: control -- The range control parameter. * 4911 * 0 = Use weapon range (zero is returned in this special case). * 4912 * -1 = Scan without range restrictions (-1 is returned in this case). * 4913 * 1 = Scan up to twice weapon range. * 4914 * * 4915 * OUTPUT: Returns with a range (or special value) that can be used in the threat scan * 4916 * process. If zero is returned, then always check threat against In_Range(). If * 4917 * -1 is returned, then no range limitation restriction exists. * 4918 * * 4919 * WARNINGS: none * 4920 * * 4921 * HISTORY: * 4922 * 07/29/1995 JLB : Created. * 4923 *=============================================================================================*/ 4924 int TechnoClass::Threat_Range(int control) const 4925 { 4926 assert(IsActive); 4927 4928 /* 4929 ** Threat range means nothing if scanning the whole map. In such a case, just 4930 ** return with the same control flag specified. 4931 */ 4932 if (control == -1) return(-1); 4933 4934 /* 4935 ** If simple guard range is requested, then return "0" since 4936 ** this is a special control value that is calculated as the object's 4937 ** weapon range. 4938 */ 4939 if (control == 0) { 4940 /* 4941 ** For normal guard mode or for area guard mode, use the override 4942 ** threat range value as specified by the object's type class. 4943 */ 4944 if (Techno_Type_Class()->ThreatRange != 0) { 4945 return(Techno_Type_Class()->ThreatRange); 4946 } 4947 return(0); 4948 } 4949 4950 /* 4951 ** Area guard range is specified, so figure twice the weapon range of the 4952 ** longest range weapon this object is equipped with. 4953 */ 4954 int range = Techno_Type_Class()->ThreatRange; 4955 if (range == 0) { 4956 range = max(Weapon_Range(0), Weapon_Range(1)); 4957 } 4958 4959 range *= 2; 4960 range = Bound(range, 0x0000, 0x0A00); 4961 4962 return(range); 4963 } 4964 4965 4966 /*********************************************************************************************** 4967 * TechnoClass::Is_In_Same_Zone -- Determine if specified cell is in same zone as object. * 4968 * * 4969 * This will examine the specified cell to determine if it is in the same zone as this * 4970 * object's location. * 4971 * * 4972 * INPUT: cell -- The cell that is to be checked against this object's current location. * 4973 * * 4974 * OUTPUT: bool; Is the specified cell in the same zone as this object is? * 4975 * * 4976 * WARNINGS: none * 4977 * * 4978 * HISTORY: * 4979 * 10/06/1996 JLB : Created. * 4980 *=============================================================================================*/ 4981 bool TechnoClass::Is_In_Same_Zone(CELL cell) const 4982 { 4983 MZoneType zone = Techno_Type_Class()->MZone; 4984 return(Map[cell].Zones[zone] == Map[Center_Coord()].Zones[zone]); 4985 } 4986 4987 4988 /*********************************************************************************************** 4989 * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * 4990 * * 4991 * This routine is called when the base is being attacked. It will pull units off of the * 4992 * field and send them back to defend the base. This routine will make taking an enemy * 4993 * base much more difficult. * 4994 * * 4995 * INPUT: enemy -- Pointer to the enemy object that did the damage on the base. * 4996 * * 4997 * OUTPUT: none * 4998 * * 4999 * WARNINGS: This routine can drastically affect the game play. The computer will probably * 5000 * call off its attacks as a result. * 5001 * * 5002 * HISTORY: * 5003 * 06/25/1995 JLB : Commented. * 5004 * 10/15/1996 JLB : Alternates between guard area and attack. * 5005 * 11/01/1996 JLB : Allow recruit of guard area units in multiplay. * 5006 *=============================================================================================*/ 5007 void TechnoClass::Base_Is_Attacked(TechnoClass const * enemy) 5008 { 5009 assert(IsActive); 5010 5011 FootClass * defender[6]; 5012 memset(defender, '\0', sizeof(defender)); 5013 5014 int value[ARRAY_SIZE(defender)]; 5015 memset(value, '\0', sizeof(value)); 5016 5017 int count = 0; 5018 int weakest = 0; 5019 int desired = enemy->Risk() * House->Control.TechLevel; 5020 int risktotal = 0; 5021 int zone = Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]; 5022 5023 /* 5024 ** Humans have to deal with their own base is attacked problems. 5025 */ 5026 if (enemy == NULL || House->Is_Ally(enemy) || House->IsHuman) { 5027 return; 5028 } 5029 5030 /* 5031 ** Don't overreact if this building can defend itself. 5032 */ 5033 if (Session.Type == GAME_NORMAL && Techno_Type_Class()->PrimaryWeapon != NULL) return; 5034 5035 /* 5036 ** If the enemy is not an infantry or a unit there is not much we can 5037 ** do about it. 5038 */ 5039 if (enemy->What_Am_I() != RTTI_INFANTRY && enemy->What_Am_I() != RTTI_UNIT) { 5040 return; 5041 } 5042 5043 /* 5044 ** If we are a certain type of building, such as a barrel or land mine, 5045 ** ignore the attack. 5046 */ 5047 if (Techno_Type_Class()->IsInsignificant) { 5048 return; 5049 } 5050 5051 /* 5052 ** If the threat has already been dealt with then we don't need to do 5053 ** any work. Check for that here. 5054 */ 5055 if (enemy->Is_Foot() && ((FootClass *)enemy)->BaseAttackTimer != 0) { 5056 return; 5057 } 5058 5059 /* 5060 ** We will need units to defend our base. We need to suspend teams until 5061 ** the situation has been dealt with. 5062 */ 5063 TeamClass::Suspend_Teams(Rule.SuspendPriority, House); 5064 5065 /* 5066 ** Loop through the infantry looking for those who are capable of going 5067 ** on a rescue mission. 5068 */ 5069 for (int index = 0; index < Infantry.Count() && desired > 0; index++) { 5070 InfantryClass * infantry = Infantry.Ptr(index); 5071 if (infantry != NULL && infantry->Owner() == Owner()) { 5072 5073 /* 5074 ** Never recruit sticky guard units to defend a base. 5075 */ 5076 if (!infantry->Is_Weapon_Equipped() || 5077 (!MissionControl[infantry->Mission].IsRecruitable && Session.Type == GAME_NORMAL)) continue; 5078 // (Mission != MISSION_GUARD_AREA || Session.Type == GAME_NORMAL)) continue; 5079 5080 /* 5081 ** Don't allow a response if it doesn't have a weapon that will affect the 5082 ** enemy object. 5083 */ 5084 if (infantry->Class->PrimaryWeapon->WarheadPtr->Modifier[enemy->Techno_Type_Class()->Armor] == 0) { 5085 continue; 5086 } 5087 5088 /* 5089 ** Don't try to help if the building is on another planet. 5090 */ 5091 if (Map[infantry->Center_Coord()].Zones[infantry->Class->MZone] != Map[Center_Coord()].Zones[infantry->Class->MZone]) continue; 5092 5093 /* 5094 ** Find the amount of threat that this unit can apply to the 5095 ** enemy. 5096 */ 5097 int threat = infantry->Rescue_Mission(enemy->As_Target()); 5098 5099 /* 5100 ** If it can't apply any threat then do just skip it and do not 5101 ** add it to the list. 5102 */ 5103 if (!threat) { 5104 continue; 5105 } 5106 5107 /* 5108 ** Greatly increase the threat value if this unit is already assigned to protect 5109 ** the target. 5110 */ 5111 if (ArchiveTarget == As_Target()) { 5112 threat *= 100; 5113 } 5114 5115 /* 5116 ** If the value returned is negative then this unit is already 5117 ** assigned to fighting the enemy, so subtract its value from 5118 ** the enemy's desired value. 5119 */ 5120 if (threat < 0) { 5121 desired += threat; 5122 continue; 5123 } 5124 5125 if (count < ARRAY_SIZE(defender)) { 5126 defender[count] = infantry; 5127 value[count] = threat; 5128 count++; 5129 continue; 5130 } 5131 5132 if (threat > weakest) { 5133 int newweakest = threat; 5134 5135 for (int lp = 0; lp < count; lp++) { 5136 if (value[lp] == weakest) { 5137 value[lp] = threat; 5138 defender[lp] = (FootClass *) infantry; 5139 continue; 5140 } 5141 if (value[lp] < newweakest) { 5142 newweakest = value[lp]; 5143 } 5144 } 5145 weakest = newweakest; 5146 } 5147 } 5148 } 5149 5150 /* 5151 ** Loop through the units looking for those who are capable of going 5152 ** on a rescue mission. 5153 */ 5154 for (int index = 0; index < Units.Count() && desired > 0; index++) { 5155 UnitClass * unit = Units.Ptr(index); 5156 if (unit != NULL && unit->Owner() == Owner()) { 5157 5158 /* 5159 ** Never recruit sticky guard units to defend a base. 5160 */ 5161 if (!unit->Is_Weapon_Equipped() || 5162 (!MissionControl[unit->Mission].IsRecruitable && Session.Type == GAME_NORMAL)) continue; 5163 5164 /* 5165 ** Don't allow a response if it doesn't have a weapon that will affect the 5166 ** enemy object. 5167 */ 5168 if (unit->Class->PrimaryWeapon->WarheadPtr->Modifier[enemy->Techno_Type_Class()->Armor] == 0) { 5169 continue; 5170 } 5171 5172 /* 5173 ** Don't try to help if the building is on another planet. 5174 */ 5175 if (Map[unit->Center_Coord()].Zones[unit->Class->MZone] != Map[Center_Coord()].Zones[unit->Class->MZone]) continue; 5176 5177 /* 5178 ** Find the amount of threat that this unit can apply to the 5179 ** enemy. 5180 */ 5181 int threat = unit->Rescue_Mission(enemy->As_Target()); 5182 5183 /* 5184 ** If it can't apply any threat then do just skip it and do not 5185 ** add it to the list. 5186 */ 5187 if (!threat) { 5188 continue; 5189 } 5190 5191 /* 5192 ** Greatly increase the threat value if this unit is already assigned to protect 5193 ** the target. 5194 */ 5195 if (threat > 0 && ArchiveTarget == As_Target()) { 5196 threat *= 10; 5197 } 5198 5199 /* 5200 ** If the value returned is negative then this unit is already 5201 ** assigned to fighting the enemy, so subtract its value from 5202 ** the enemy's desired value. 5203 */ 5204 if (threat < 0) { 5205 desired += threat; 5206 continue; 5207 } 5208 5209 if (count < ARRAY_SIZE(defender)) { 5210 defender[count] = unit; 5211 value[count] = threat; 5212 count++; 5213 continue; 5214 } 5215 if (threat > weakest) { 5216 int newweakest = threat; 5217 5218 for (int lp = 0; lp < count; lp ++) { 5219 if (value[lp] == weakest) { 5220 value[lp] = threat; 5221 defender[lp] = (FootClass *) unit; 5222 continue; 5223 } 5224 if (value[lp] < newweakest) { 5225 // if (value[count] < newweakest) { 5226 newweakest = value[lp]; 5227 } 5228 } 5229 weakest = newweakest; 5230 } 5231 } 5232 } 5233 5234 if (desired > 0) { 5235 5236 /* 5237 ** Sort the defenders by value, this doesn't take very long and will 5238 ** help the closest defenders to respond. 5239 */ 5240 for (int lp = 0; lp < count - 1; lp ++) { 5241 for (int lp2 = lp + 1; lp2 < count; lp2++) { 5242 if (value[lp] < value[lp2]) { 5243 5244 value[lp] ^= value[lp2]; 5245 value[lp2] ^= value[lp]; 5246 value[lp] ^= value[lp2]; 5247 5248 FootClass *temp; 5249 temp = defender[lp]; 5250 defender[lp] = defender[lp2]; 5251 defender[lp2] = temp; 5252 } 5253 } 5254 } 5255 5256 for (int lp = 0; lp < count; lp ++) { 5257 if (Percent_Chance(50)) { 5258 defender[lp]->Assign_Mission(MISSION_RESCUE); 5259 } else { 5260 defender[lp]->Assign_Mission(MISSION_GUARD_AREA); 5261 defender[lp]->ArchiveTarget = As_Target(); 5262 } 5263 defender[lp]->Assign_Target(enemy->As_Target()); 5264 risktotal += defender[lp]->Risk(); 5265 if (risktotal > desired) { 5266 break; 5267 } 5268 } 5269 } 5270 5271 if (risktotal > desired && enemy->Is_Foot()) { 5272 ((FootClass *)enemy)->BaseAttackTimer = TICKS_PER_MINUTE * Rule.BaseDefenseDelay; 5273 } 5274 } 5275 5276 5277 /*********************************************************************************************** 5278 * TechnoClass::Is_Allowed_To_Retaliate -- Checks object to see if it can retaliate. * 5279 * * 5280 * This routine is called when this object has suffered some damage and it needs to know * 5281 * if it should fight back. The object that caused the damage is specifed as a parameter. * 5282 * * 5283 * INPUT: source -- The points to the object that was the source of the damage applied * 5284 * to this object. * 5285 * * 5286 * OUTPUT: bool; Should retaliation occur? * 5287 * * 5288 * WARNINGS: none * 5289 * * 5290 * HISTORY: * 5291 * 10/19/1996 JLB : Created. * 5292 *=============================================================================================*/ 5293 bool TechnoClass::Is_Allowed_To_Retaliate(TechnoClass const * source) const 5294 { 5295 /* 5296 ** If there is no source of the damage, then retaliation cannot occur. 5297 */ 5298 if (source == NULL) return(false); 5299 5300 /* 5301 ** If the mission precludes retaliation, then don't retaliate. 5302 */ 5303 if (!MissionControl[Mission].IsRetaliate) return(false); 5304 5305 /* 5306 ** Fixed wing aircraft are not responsive enough to retaliate to damage recieved. 5307 */ 5308 if (What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)this)->Class->IsFixedWing) { 5309 return(false); 5310 } 5311 5312 /* 5313 ** If the source of the damage is an ally, then retaliation shouldn't 5314 ** occur either. 5315 */ 5316 if (House->Is_Ally(source)) return(false); 5317 5318 /* 5319 ** Only objects that have a damaging weapon are allowed to retaliate. 5320 */ 5321 if (Combat_Damage() <= 0 || !Is_Weapon_Equipped()) return(false); 5322 5323 /* 5324 ** If this is not equipped with a weapon that can attack the molester, then 5325 ** don't allow retaliation. 5326 */ 5327 TechnoTypeClass const * ttype = Techno_Type_Class(); 5328 if (ttype->PrimaryWeapon->WarheadPtr != NULL && 5329 ttype->PrimaryWeapon->WarheadPtr->Modifier[source->Techno_Type_Class()->Armor] == 0) { 5330 return(false); 5331 } 5332 5333 /* 5334 ** One can never retaliate against a dog because of their peculiar nature of attacking. 5335 ** Dogs must be attacked using normal target processing. 5336 */ 5337 if (source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) return(false); 5338 5339 /* 5340 ** Don't allow retaliation if it isn't equipped with a weapon that can deal with the threat. 5341 */ 5342 if (source->What_Am_I() == RTTI_AIRCRAFT && !ttype->PrimaryWeapon->Bullet->IsAntiAircraft) return(false); 5343 5344 /* 5345 ** Tanya is not allowed to retaliate against buildings in the normal sense while in guard mode. That 5346 ** is, unless it is owned by the computer. Normally, Tanya can't do anything substantial to a building 5347 ** except to blow it up. 5348 */ 5349 if ((House->IsHuman || (Session.Type == GAME_NORMAL && House->IsPlayerControl)) 5350 && source->What_Am_I() == RTTI_BUILDING && What_Am_I() == RTTI_INFANTRY && ((InfantryTypeClass const *)ttype)->IsBomber) { 5351 return(false); 5352 } 5353 5354 /* 5355 ** If a human house is not allowed to retaliate automatically, then don't 5356 */ 5357 if (House->IsHuman && !Rule.IsSmartDefense && (What_Am_I() != RTTI_INFANTRY || *((InfantryClass*)this) != INFANTRY_TANYA || source->What_Am_I() != RTTI_INFANTRY)) return(false); 5358 5359 /* 5360 ** If this object is part of a team that prevents retaliation then don't allow retaliation. 5361 */ 5362 if (Is_Foot() && ((FootClass *)this)->Team.Is_Valid() && ((FootClass *)this)->Team->Class->IsSuicide) { 5363 return(false); 5364 } 5365 5366 /* 5367 ** Compare potential threat of the current target and the potential new target. Don't retaliate 5368 ** if it is currently attacking the greater threat. 5369 */ 5370 if (!House->IsHuman && Percent_Chance(50)) { 5371 fixed source_val = 0; 5372 int primary = What_Weapon_Should_I_Use(source->As_Target()); 5373 WeaponTypeClass const * weapon = (primary == 0) ? source->Techno_Type_Class()->PrimaryWeapon : source->Techno_Type_Class()->SecondaryWeapon; 5374 if (weapon != NULL && weapon->WarheadPtr != NULL && In_Range(source, primary)) { 5375 source_val = weapon->WarheadPtr->Modifier[Techno_Type_Class()->Armor]; 5376 } 5377 5378 fixed current_val = 0; 5379 TechnoClass const * tech = As_Techno(TarCom); 5380 if (tech != NULL) { 5381 primary = What_Weapon_Should_I_Use(tech->As_Target()); 5382 weapon = (primary == 0) ? source->Techno_Type_Class()->PrimaryWeapon : source->Techno_Type_Class()->SecondaryWeapon; 5383 if (weapon != NULL && weapon->WarheadPtr != NULL && In_Range(tech, primary)) { 5384 current_val = weapon->WarheadPtr->Modifier[Techno_Type_Class()->Armor]; 5385 } 5386 } 5387 if (source_val <= current_val) return(false); 5388 } 5389 5390 /* 5391 ** If it is already busy attacking another target, then don't retaliate. 5392 */ 5393 // if (In_Range(TarCom)) return(false); 5394 5395 /* 5396 ** All checks passed, so return that retaliation is allowed. 5397 */ 5398 return(true); 5399 } 5400 5401 5402 /*********************************************************************************************** 5403 * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * 5404 * * 5405 * This routine will return the ownable bits for this object. The ownable bits represent * 5406 * the houses that are allowed to own this object. * 5407 * * 5408 * INPUT: none * 5409 * * 5410 * OUTPUT: Returns the ownable bits for this object. * 5411 * * 5412 * WARNINGS: none * 5413 * * 5414 * HISTORY: * 5415 * 07/29/1995 JLB : Created. * 5416 *=============================================================================================*/ 5417 int TechnoClass::Get_Ownable(void) const 5418 { 5419 assert(IsActive); 5420 5421 return ((TechnoTypeClass const &)Class_Of()).Get_Ownable(); 5422 // return ((TechnoTypeClass const &)Class_Of()).Ownable; 5423 } 5424 5425 5426 /*********************************************************************************************** 5427 * TechnoClass::Risk -- Fetches the risk associated with this object. * 5428 * * 5429 * This routine is called when the risk value for this object needs to be determined. * 5430 * * 5431 * INPUT: none * 5432 * * 5433 * OUTPUT: Returns with the risk value for this object. * 5434 * * 5435 * WARNINGS: none * 5436 * * 5437 * HISTORY: * 5438 * 07/29/1995 JLB : Created. * 5439 *=============================================================================================*/ 5440 int TechnoClass::Risk(void) const 5441 { 5442 assert(IsActive); 5443 5444 return(Techno_Type_Class()->Risk); 5445 } 5446 5447 5448 /*********************************************************************************************** 5449 * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * 5450 * * 5451 * This routine will return the current Tiberium load (expressed as a fixed point fraction) * 5452 * that this object currently contains. Typical implementor of this function would be * 5453 * the harvester. Any object that can return a non-zero value should derive from this * 5454 * function in order to return the appropriate value. * 5455 * * 5456 * INPUT: none * 5457 * * 5458 * OUTPUT: Returns with the current Tiberium load expressed as a fixed point number. * 5459 * 0x0000 = empty * 5460 * 0x0080 = half full * 5461 * 0x0100 = full * 5462 * * 5463 * WARNINGS: none * 5464 * * 5465 * HISTORY: * 5466 * 07/29/1995 JLB : Created. * 5467 *=============================================================================================*/ 5468 fixed TechnoClass::Tiberium_Load(void) const 5469 { 5470 assert(IsActive); 5471 5472 return(0); 5473 } 5474 5475 5476 /*********************************************************************************************** 5477 * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * 5478 * * 5479 * This routine is called when an object desires to load up on this object. The object * 5480 * desiring to load is specified. The cell that the loading object should move to is * 5481 * determined. The direction that this object should face is also calculated. This routine * 5482 * will be overridden by those objects that can actually load up passengers. * 5483 * * 5484 * INPUT: object -- The object that is desiring to load up. * 5485 * * 5486 * moveto -- Reference to the cell that the loading object should move to before * 5487 * the final load process occurs (this value will be filled in). * 5488 * * 5489 * OUTPUT: Returns with the direction that the transport object should face. * 5490 * * 5491 * WARNINGS: none * 5492 * * 5493 * HISTORY: * 5494 * 07/29/1995 JLB : Created. * 5495 *=============================================================================================*/ 5496 DirType TechnoClass::Desired_Load_Dir(ObjectClass * , CELL & moveto) const 5497 { 5498 assert(IsActive); 5499 5500 moveto = 0; 5501 return(DIR_N); 5502 } 5503 5504 5505 /*********************************************************************************************** 5506 * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * 5507 * * 5508 * This routine will return the number of pips to display on this object when the object * 5509 * is selected. The default condition is to return no pips at all. This routine is * 5510 * derived for those objects that can have pips. * 5511 * * 5512 * INPUT: none * 5513 * * 5514 * OUTPUT: Returns with the number of pips to display on this object when selected. * 5515 * * 5516 * WARNINGS: none * 5517 * * 5518 * HISTORY: * 5519 * 07/29/1995 JLB : Created. * 5520 *=============================================================================================*/ 5521 int TechnoClass::Pip_Count(void) const 5522 { 5523 assert(IsActive); 5524 5525 return(0); 5526 } 5527 5528 5529 /*********************************************************************************************** 5530 * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * 5531 * * 5532 * This routine will fetch the direction that a fired projectile will take. This is * 5533 * usually the facing of the object's weapon. This routine will be derived for the objects * 5534 * that have their weapon barrel facing a different direction than the body. * 5535 * * 5536 * INPUT: none * 5537 * * 5538 * OUTPUT: Returns with the direction a fired projectile will take. * 5539 * * 5540 * WARNINGS: none * 5541 * * 5542 * HISTORY: * 5543 * 07/29/1995 JLB : Created. * 5544 *=============================================================================================*/ 5545 DirType TechnoClass::Fire_Direction(void) const 5546 { 5547 assert(IsActive); 5548 5549 return(Turret_Facing()); 5550 } 5551 5552 5553 /*********************************************************************************************** 5554 * TechnoClass::Response_Select -- Handles the voice response when selected. * 5555 * * 5556 * This routine is called when a voice response to a select action is desired. This routine * 5557 * should be overridden for any object that actually has a voice response. * 5558 * * 5559 * INPUT: none * 5560 * * 5561 * OUTPUT: none * 5562 * * 5563 * WARNINGS: This routine can generate an audio response. * 5564 * * 5565 * HISTORY: * 5566 * 07/29/1995 JLB : Created. * 5567 *=============================================================================================*/ 5568 void TechnoClass::Response_Select(void) 5569 { 5570 assert(IsActive); 5571 } 5572 5573 5574 /*********************************************************************************************** 5575 * TechnoClass::Response_Move -- Handles the voice response to a movement request. * 5576 * * 5577 * This routine is called when a voice response to a movement order is desired. This * 5578 * routine should be overridden for any object that actually has a voice response. * 5579 * * 5580 * INPUT: none * 5581 * * 5582 * OUTPUT: none * 5583 * * 5584 * WARNINGS: This can generate an audio response. * 5585 * * 5586 * HISTORY: * 5587 * 07/29/1995 JLB : Created. * 5588 *=============================================================================================*/ 5589 void TechnoClass::Response_Move(void) 5590 { 5591 assert(IsActive); 5592 } 5593 5594 5595 /*********************************************************************************************** 5596 * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * 5597 * * 5598 * This routine is called when a voice response to an attack order is desired. This routine * 5599 * should be overridden for any object that actually have a voice response. * 5600 * * 5601 * INPUT: none * 5602 * * 5603 * OUTPUT: none * 5604 * * 5605 * WARNINGS: This can generate an audio response. * 5606 * * 5607 * HISTORY: * 5608 * 07/29/1995 JLB : Created. * 5609 *=============================================================================================*/ 5610 void TechnoClass::Response_Attack(void) 5611 { 5612 assert(IsActive); 5613 } 5614 5615 5616 /*********************************************************************************************** 5617 * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * 5618 * * 5619 * This routine will search for a nearby target and assign it to this object's TarCom. * 5620 * The method to use when scanning for a target is controlled by the parameter passed. * 5621 * * 5622 * INPUT: threat -- The threat control parameter used to control the range searched. The * 5623 * only values recognized are THREAT_RANGE and THREAT_AREA. * 5624 * * 5625 * OUTPUT: Was a suitable target acquired and assigned to the TarCom? * 5626 * * 5627 * WARNINGS: none * 5628 * * 5629 * HISTORY: * 5630 * 07/29/1995 JLB : Created. * 5631 *=============================================================================================*/ 5632 bool TechnoClass::Target_Something_Nearby(ThreatType threat) 5633 { 5634 assert(IsActive); 5635 threat = threat & (THREAT_RANGE|THREAT_AREA); 5636 5637 /* 5638 ** Determine that if there is an existing target it is still legal 5639 ** and within range. 5640 */ 5641 if (Target_Legal(TarCom)) { 5642 if ((threat & THREAT_RANGE)) { 5643 int primary = What_Weapon_Should_I_Use(TarCom); 5644 if (!In_Range(TarCom, primary)) { 5645 Assign_Target(TARGET_NONE); 5646 } 5647 } 5648 } 5649 5650 /* 5651 ** If there is no target, then try to find one and assign it as 5652 ** the target for this unit. 5653 */ 5654 if (!Target_Legal(TarCom)) { 5655 Assign_Target(Greatest_Threat(threat)); 5656 } 5657 5658 /* 5659 ** Return with answer to question: Does this unit now have a target? 5660 */ 5661 return(Target_Legal(TarCom)); 5662 } 5663 5664 5665 /*********************************************************************************************** 5666 * TechnoClass::Exit_Object -- Causes specified object to leave this object. * 5667 * * 5668 * This routine is called when there is an attached object that should detach and leave * 5669 * this object. Typical of this would be the refinery and APC. * 5670 * * 5671 * INPUT: object -- The object that is trying to leave this object. * 5672 * * 5673 * OUTPUT: Was the object successfully launched from this object? Failure might indicate that * 5674 * there is insufficient room to detach the specified object. * 5675 * * 5676 * WARNINGS: none * 5677 * * 5678 * HISTORY: * 5679 * 07/24/1995 JLB : Created. * 5680 *=============================================================================================*/ 5681 int TechnoClass::Exit_Object(TechnoClass *) 5682 { 5683 assert(IsActive); 5684 5685 return(0); 5686 } 5687 5688 5689 /*********************************************************************************************** 5690 * TechnoClass::Is_Ready_To_Random_Animate -- Determines if the object should random animate. * 5691 * * 5692 * This will examine this object to determine if it is time and ready to perform some * 5693 * kind of random animation. * 5694 * * 5695 * INPUT: none * 5696 * * 5697 * OUTPUT: bool; Is it time to perform an random animation? * 5698 * * 5699 * WARNINGS: none * 5700 * * 5701 * HISTORY: * 5702 * 10/19/1996 JLB : Created. * 5703 *=============================================================================================*/ 5704 bool TechnoClass::Is_Ready_To_Random_Animate(void) const 5705 { 5706 assert(IsActive); 5707 return(IdleTimer == 0); 5708 } 5709 5710 5711 /*********************************************************************************************** 5712 * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * 5713 * * 5714 * This routine is called when the object needs to have a new movement destination * 5715 * assigned. This routine must be overridden since at this level, movement is not allowed. * 5716 * * 5717 * INPUT: destination -- The destination to assign to this object. * 5718 * * 5719 * OUTPUT: none * 5720 * * 5721 * WARNINGS: none * 5722 * * 5723 * HISTORY: * 5724 * 07/24/1995 JLB : Created. * 5725 *=============================================================================================*/ 5726 void TechnoClass::Assign_Destination(TARGET ) 5727 { 5728 assert(IsActive); 5729 } 5730 5731 5732 /*********************************************************************************************** 5733 * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * 5734 * * 5735 * This routine is called when the object should intelligently revert to an idle state. * 5736 * Typically this routine is called after some mission has completed. This routine must * 5737 * be overridden by the various object types. It is located at this level merely to provide * 5738 * a virtual function entry point. * 5739 * * 5740 * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * 5741 * or is initially placed on the map? * 5742 * * 5743 * OUTPUT: none * 5744 * * 5745 * WARNINGS: none * 5746 * * 5747 * HISTORY: * 5748 * 07/24/1995 JLB : Created. * 5749 *=============================================================================================*/ 5750 void TechnoClass::Enter_Idle_Mode(bool ) 5751 { 5752 assert(IsActive); 5753 } 5754 5755 5756 /*********************************************************************************************** 5757 * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * 5758 * * 5759 * This routine is used to render the small transportation pip (occupant feedback graphic) * 5760 * used for transporter object. It will also display if the techno object is "primary" * 5761 * if necessary. * 5762 * * 5763 * INPUT: x,y -- The pixel coordinate for the center of the first pip. Subsequent pips * 5764 * are drawn rightward. * 5765 * * 5766 * window-- The window that pip clipping is relative to. * 5767 * * 5768 * OUTPUT: none * 5769 * * 5770 * WARNINGS: none * 5771 * * 5772 * HISTORY: * 5773 * 08/08/1995 JLB : Created. * 5774 * 10/06/1995 JLB : Displays the team group number. * 5775 * 09/10/1996 JLB : Medic hack for red pip. * 5776 *=============================================================================================*/ 5777 void TechnoClass::Draw_Pips(int x, int y, WindowNumberType window) const 5778 { 5779 assert(IsActive); 5780 5781 /* 5782 ** Transporter type objects have a different graphic representation for the pips. The 5783 ** pip color represents the type of occupant. 5784 */ 5785 bool carrying_passengers = (Techno_Type_Class()->Max_Passengers() > 0) && 5786 ((What_Am_I() != RTTI_AIRCRAFT) || (*(AircraftClass*)this != AIRCRAFT_BADGER) || (Mission != MISSION_HUNT)); 5787 if (carrying_passengers) { 5788 ObjectClass const * object = Attached_Object(); 5789 for (int index = 0; index < Class_Of().Max_Pips(); index++) { 5790 PipEnum pip = PIP_EMPTY; 5791 5792 if (object != NULL) { 5793 pip = PIP_FULL; 5794 if (object->What_Am_I() == RTTI_INFANTRY) { 5795 pip = ((InfantryClass *)object)->Class->Pip; 5796 } 5797 #ifdef FIXIT_CARRIER // checked - ajw 9/28/98 5798 if (What_Am_I() == RTTI_VESSEL && *(VesselClass *)this == VESSEL_CARRIER) { 5799 if (object->What_Am_I() == RTTI_AIRCRAFT) { 5800 AircraftClass *heli = (AircraftClass *)object; 5801 if (heli->Ammo != heli->Techno_Type_Class()->MaxAmmo) { 5802 pip = PIP_ENGINEER; 5803 if (!heli->Ammo) { 5804 pip = PIP_COMMANDO; 5805 } 5806 } 5807 } 5808 } 5809 #endif 5810 object = object->Next; 5811 } 5812 CC_Draw_Pip(this, Class_Of().PipShapes, pip, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); 5813 } 5814 5815 } else { 5816 5817 /* 5818 ** Display number of how many attached objects there are. This is also used 5819 ** to display the fullness rating for a harvester. 5820 */ 5821 int pips = Pip_Count(); 5822 5823 /* 5824 ** Check if it's a harvester, to show the right type of pips for the 5825 ** various minerals it could have harvested. 5826 */ 5827 if (What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_HARVESTER) { 5828 UnitClass * harv = (UnitClass *)this; 5829 5830 int iron = harv->Gems; 5831 int nickel = harv->Gold; 5832 int graypips = pips * fixed(iron, Rule.BailCount); 5833 int greenpips = pips * fixed(nickel, Rule.BailCount); 5834 5835 while (greenpips + graypips < pips) { 5836 int ironnickelmax = max(iron, nickel); 5837 if (iron > nickel) { 5838 graypips++; 5839 } else { 5840 greenpips++; 5841 } 5842 } 5843 5844 for (int index = 0; index < Class_Of().Max_Pips(); index++) { 5845 int shape = PIP_EMPTY; 5846 if (index < pips) { 5847 if (greenpips) { 5848 shape = PIP_FULL; 5849 greenpips--; 5850 } else { 5851 shape = PIP_COMMANDO; 5852 graypips--; 5853 } 5854 } 5855 CC_Draw_Pip(this, Class_Of().PipShapes, shape, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); 5856 } 5857 } 5858 #ifdef FIXIT_CSII // checked - ajw 9/28/98 5859 /* 5860 ** Check if it's a Chrono tank, to show the recharge gauge. 5861 */ 5862 else if (What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_CHRONOTANK) { 5863 for (int index = 0; index < 5; index++) { 5864 int shape = PIP_EMPTY; 5865 if (index < pips) { 5866 switch(index) { 5867 case 0: 5868 case 1: 5869 shape = PIP_COMMANDO; 5870 break; 5871 5872 case 2: 5873 case 3: 5874 shape = PIP_ENGINEER; 5875 break; 5876 5877 case 4: 5878 shape = PIP_FULL; 5879 default: 5880 break; 5881 } 5882 } 5883 CC_Draw_Pip(this, Class_Of().PipShapes, shape, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); 5884 } 5885 #endif 5886 } else { 5887 bool building = false; 5888 int pip = PIP_FULL; // green 5889 if(!IsOwnedByPlayer && What_Am_I() == RTTI_BUILDING) { 5890 if(*(BuildingClass *)this==STRUCT_POWER || *(BuildingClass *)this==STRUCT_ADVANCED_POWER) { 5891 building = true; 5892 if (House->Power_Fraction() < 1) { 5893 pip = PIP_ENGINEER; // gold 5894 if (House->Drain > (House->Power * 2) ) { 5895 pip = PIP_COMMANDO; 5896 } 5897 } 5898 } 5899 } 5900 5901 // Ally/spied power display is handled separately in the virtual window 5902 if (!building || (window != WINDOW_VIRTUAL)) { 5903 for (int index = 0; index < (building ? 5 : Class_Of().Max_Pips()); index++) { 5904 if (building) { 5905 CC_Draw_Pip(this, Class_Of().PipShapes, pip, x, y-index*3, window, SHAPE_CENTER|SHAPE_WIN_REL); 5906 } else { 5907 CC_Draw_Pip(this, Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); 5908 } 5909 } 5910 5911 //BG for (int index = 0; index < Class_Of().Max_Pips(); index++) { 5912 //BG CC_Draw_Shape(Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); 5913 //BG } 5914 } 5915 } 5916 } 5917 5918 /* 5919 ** Special hack to display a red pip on the medic. 5920 */ 5921 if (What_Am_I() == RTTI_INFANTRY && Combat_Damage() < 0) { 5922 CC_Draw_Shape(Class_Of().PipShapes, PIP_MEDIC, x+8, y, window, SHAPE_CENTER|SHAPE_WIN_REL); 5923 } 5924 5925 /* 5926 ** Display whether this unit is a leader unit or not. 5927 */ 5928 if (IsLeader && (window != WINDOW_VIRTUAL)) { 5929 PipEnum prishape = PIP_PRIMARY; 5930 5931 if(What_Am_I() == RTTI_BUILDING) { 5932 if(*((BuildingClass *)this) == STRUCT_KENNEL) { 5933 prishape = PIP_PRI; 5934 } 5935 } 5936 CC_Draw_Shape(Class_Of().PipShapes, prishape, x-2, y-3, window, /*SHAPE_CENTER|*/SHAPE_WIN_REL); 5937 } 5938 5939 /* 5940 ** Display what group this unit belongs to. This corresponds to the team 5941 ** number assigned with the <CTRL> key. 5942 */ 5943 if (Is_Foot() && ((FootClass *)this)->Group != 0xFF && ((FootClass *)this)->Group < 10) { 5944 int yval = -1; 5945 int group = ((FootClass *)this)->Group; 5946 5947 if (Class_Of().Max_Pips()) yval -= 4; 5948 if (group == 10) group = 0; 5949 5950 CC_Draw_Shape(Class_Of().PipShapes, PIP_NUMBERS+group, x+2, y+yval, window, SHAPE_CENTER|SHAPE_WIN_REL); 5951 5952 /* 5953 ** If this unit is part of a formation, draw an 'F' after the group 5954 ** number. 5955 */ 5956 if ( ((FootClass *)this)->XFormOffset != 0x80000000UL) { 5957 CC_Draw_Shape(Class_Of().PipShapes, PIP_LETTERF, x+8, y+yval, window, SHAPE_CENTER|SHAPE_WIN_REL); 5958 } 5959 } 5960 5961 /* 5962 ** If this building is being spied on by the player, draw the money or 5963 ** factory-producing item or whatever. 5964 */ 5965 if (What_Am_I() == RTTI_BUILDING) { 5966 int spiedby = Spied_By() & (1<<(PlayerPtr->Class->House)); 5967 5968 /* 5969 ** Print word "Decoy" above buildings that are spied upon or fake 5970 */ 5971 if (((BuildingClass *)this)->Class->IsFake) { 5972 if (spiedby || IsOwnedByPlayer) { 5973 CC_Draw_Shape(Class_Of().PipShapes, PIP_DECOY, x, y-16, window, SHAPE_WIN_REL); 5974 } 5975 } 5976 /* 5977 ** See if we should print the credits for a spied refinery 5978 */ 5979 if (spiedby) { 5980 // If it's a refinery/silo, print the enemy's money 5981 if (((BuildingClass *)this)->Class->Capacity) { 5982 long money = House->Available_Money(); 5983 5984 /* 5985 ** Determine how many digits will be printed. 5986 */ 5987 int digits; 5988 int factor = 10; 5989 for (digits = 1; digits < 9; digits++) { 5990 if (money < factor) break; 5991 factor *= 10; 5992 } 5993 5994 int startx = x + 6 * digits - 3;// + 6 * 8; 5995 while (money) { 5996 int xdigit = money % 10; 5997 money /= 10; 5998 CC_Draw_Shape(Class_Of().PipShapes, PIP_NUMBERS + xdigit, startx, y-6, window, SHAPE_CENTER|SHAPE_WIN_REL); 5999 startx -= 6; 6000 } 6001 } 6002 } 6003 } 6004 } 6005 6006 6007 /*********************************************************************************************** 6008 * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * 6009 * * 6010 * This routine will be used to find a building that can serve as a docking bay. The * 6011 * closest building that qualifies will be returned. If no building could be found then * 6012 * return with NULL. * 6013 * * 6014 * INPUT: b -- The structure type to look for. * 6015 * * 6016 * friendly -- Allow searching for allied buildings as well. * 6017 * * 6018 * OUTPUT: Returns with a pointer to the building that can serve as the best docking bay. * 6019 * * 6020 * WARNINGS: This routine might return NULL even if there are buildings of the specified * 6021 * type available. This is the case when the building(s) are currently busy and * 6022 * cannot serve as a docking bay at the moment. * 6023 * * 6024 * HISTORY: * 6025 * 07/18/1995 JLB : Created. * 6026 * 08/13/1995 JLB : Recognizes the "IsLeader" method of building preference. * 6027 *=============================================================================================*/ 6028 BuildingClass * TechnoClass::Find_Docking_Bay(StructType b, bool friendly) const 6029 { 6030 assert(IsActive); 6031 6032 BuildingClass * best = 0; 6033 6034 /* 6035 ** First check to see if there are ANY buildings of the specified 6036 ** type in this house's inventory. If not, then don't bother to scan 6037 ** for one. 6038 */ 6039 if (House->Get_Quantity(b) != 0) { 6040 int bestval = -1; 6041 6042 /* 6043 ** Loop through all the buildings and find the one that matches the specification 6044 ** and is willing to dock with this object. 6045 */ 6046 for (int index = 0; index < Buildings.Count(); index++) { 6047 BuildingClass * building = Buildings.Ptr(index); 6048 6049 /* 6050 ** Check to see if the building qualifies (preliminary scan). 6051 */ 6052 if (building != NULL && 6053 (friendly ? building->House->Is_Ally(this) : building->House == House) && 6054 !building->IsInLimbo && 6055 *building == b && 6056 (What_Am_I() == RTTI_AIRCRAFT || Map[building->Center_Coord()].Zones[Techno_Type_Class()->MZone] == Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]) && 6057 ((TechnoClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER) { 6058 6059 /* 6060 ** If the building qualifies and this building is better than the 6061 ** last qualifying building (as rated by distance), then record 6062 ** this building and keep scanning. 6063 */ 6064 if (bestval == -1 || Distance(building) < bestval || building->IsLeader) { 6065 best = building; 6066 bestval = Distance(building); 6067 } 6068 } 6069 } 6070 } 6071 return(best); 6072 } 6073 6074 6075 /*********************************************************************************************** 6076 * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * 6077 * * 6078 * This routine is called when an object would like to exit from this (presumed) transport. * 6079 * A suitable cell should be returned by this routine. The specified object will probably * 6080 * be unloaded at that cell. * 6081 * * 6082 * INPUT: techno -- Pointer to the object that would like to unload. This is used to * 6083 * determine suitability for placement. * 6084 * * 6085 * OUTPUT: Returns with the cell that is recommended for object exit. * 6086 * * 6087 * WARNINGS: none * 6088 * * 6089 * HISTORY: * 6090 * 08/12/1995 JLB : Created. * 6091 *=============================================================================================*/ 6092 CELL TechnoClass::Find_Exit_Cell(TechnoClass const * ) const 6093 { 6094 assert(IsActive); 6095 6096 return(Coord_Cell(Docking_Coord())); 6097 } 6098 6099 6100 /*********************************************************************************************** 6101 * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * 6102 * * 6103 * This routine is used by the selling back mechanism in order to credit the owning house * 6104 * with some refund credits. The value returned is the credits to refund to the owner. * 6105 * * 6106 * INPUT: none * 6107 * * 6108 * OUTPUT: Returns with the credits to refund to the owner. * 6109 * * 6110 * WARNINGS: none * 6111 * * 6112 * HISTORY: * 6113 * 08/13/1995 JLB : Created. * 6114 *=============================================================================================*/ 6115 int TechnoClass::Refund_Amount(void) const 6116 { 6117 assert(IsActive); 6118 6119 int cost = Techno_Type_Class()->Raw_Cost() * House->CostBias; 6120 6121 #ifdef TOFIX 6122 /* 6123 ** If the object is carrying Tiberium directly (i.e., the harvester), then 6124 ** account for the credits of the load. 6125 */ 6126 // cost += Fixed_To_Cardinal(UnitTypeClass::FULL_LOAD_CREDITS, Tiberium_Load())/2; 6127 #endif 6128 6129 if (House->IsHuman) { 6130 cost = cost * Rule.RefundPercent; 6131 // cost /= 2; 6132 } 6133 return(cost); 6134 } 6135 6136 6137 /*********************************************************************************************** 6138 * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. * 6139 * * 6140 * This routine will calculate and return the anti-aircraft strength of this object. * 6141 * Typical users of this strength value is the base defense expert system AI. * 6142 * * 6143 * INPUT: none * 6144 * * 6145 * OUTPUT: Returns with the anti-aircraft defense value of this object. The value returned * 6146 * is an abstract number to be used for relative comparisons only. * 6147 * * 6148 * WARNINGS: none * 6149 * * 6150 * HISTORY: * 6151 * 10/02/1995 JLB : Created. * 6152 *=============================================================================================*/ 6153 int TechnoClass::Anti_Air(void) const 6154 { 6155 assert(IsActive); 6156 6157 if (Is_Weapon_Equipped()) { 6158 WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; 6159 BulletTypeClass const * bullet = weapon->Bullet; 6160 WarheadTypeClass const * warhead = weapon->WarheadPtr; 6161 6162 if (bullet->IsAntiAircraft) { 6163 int value = ((weapon->Attack * warhead->Modifier[ARMOR_ALUMINUM]) * weapon->Range) / weapon->ROF; 6164 6165 if (Techno_Type_Class()->Is_Two_Shooter()) { 6166 value *= 2; 6167 } 6168 return(value/50); 6169 } 6170 } 6171 return(0); 6172 } 6173 6174 6175 /*********************************************************************************************** 6176 * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. * 6177 * * 6178 * This routine is used to examine and calculate the anti-armor strength of this object. * 6179 * Typical user user of this would be the expert system base defense AI. * 6180 * * 6181 * INPUT: none * 6182 * * 6183 * OUTPUT: Returns with the relative anti-armor combat value for this object. The value * 6184 * is abstract and is only to be used in relative comparisons. * 6185 * * 6186 * WARNINGS: none * 6187 * * 6188 * HISTORY: * 6189 * 10/02/1995 JLB : Created. * 6190 *=============================================================================================*/ 6191 int TechnoClass::Anti_Armor(void) const 6192 { 6193 assert(IsActive); 6194 6195 if (Is_Weapon_Equipped()) { 6196 if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0); 6197 6198 WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; 6199 BulletTypeClass const * bullet = weapon->Bullet; 6200 WarheadTypeClass const * warhead = weapon->WarheadPtr; 6201 int mrange = min(weapon->Range, 0x0400); 6202 6203 int value = ((weapon->Attack * warhead->Modifier[ARMOR_STEEL]) * mrange * warhead->SpreadFactor) / weapon->ROF; 6204 if (Techno_Type_Class()->Is_Two_Shooter()) { 6205 value *= 2; 6206 } 6207 if (bullet->IsInaccurate) { 6208 value /= 2; 6209 } 6210 return(value/50); 6211 } 6212 return(0); 6213 } 6214 6215 6216 /*********************************************************************************************** 6217 * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. * 6218 * * 6219 * This routine is used to determine the anti-infantry strength of this object. The * 6220 * typical user of this routine is the expert system base defense AI. * 6221 * * 6222 * INPUT: none * 6223 * * 6224 * OUTPUT: Returns with the anti-infantry strength of this object. The value returned is * 6225 * abstract and should only be used for relative comparisons. * 6226 * * 6227 * WARNINGS: none * 6228 * * 6229 * HISTORY: * 6230 * 10/02/1995 JLB : Created. * 6231 *=============================================================================================*/ 6232 int TechnoClass::Anti_Infantry(void) const 6233 { 6234 assert(IsActive); 6235 6236 if (Is_Weapon_Equipped()) { 6237 if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0); 6238 6239 WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; 6240 BulletTypeClass const * bullet = weapon->Bullet; 6241 WarheadTypeClass const * warhead = weapon->WarheadPtr; 6242 int mrange = min(weapon->Range, 0x0400); 6243 6244 int value = ((weapon->Attack * warhead->Modifier[ARMOR_NONE]) * mrange * warhead->SpreadFactor) / weapon->ROF; 6245 if (Techno_Type_Class()->Is_Two_Shooter()) { 6246 value *= 2; 6247 } 6248 if (bullet->IsInaccurate) { 6249 value /= 2; 6250 } 6251 return(value/50); 6252 } 6253 return(0); 6254 } 6255 6256 6257 /*********************************************************************************************** 6258 * TechnoClass::Look -- Performs a look around (map reveal) action. * 6259 * * 6260 * This routine will reveal the map around this object. * 6261 * * 6262 * INPUT: incremental -- This parameter can enable a more efficient map reveal logic. * 6263 * If it is absolutely known that the object has only moved one * 6264 * cell from its previous location that it performed a Look() at, * 6265 * then set this parameter to TRUE. It will only perform the look * 6266 * check on the perimeter cells. * 6267 * * 6268 * OUTPUT: none * 6269 * * 6270 * WARNINGS: This routine is slow, try to call it only when necessary. * 6271 * * 6272 * HISTORY: * 6273 * 03/14/1996 JLB : Created. * 6274 *=============================================================================================*/ 6275 void TechnoClass::Look(bool incremental) 6276 { 6277 assert(IsActive); 6278 assert(!IsInLimbo); 6279 6280 int sight_range = Techno_Type_Class()->SightRange; 6281 6282 if (sight_range) { 6283 6284 Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental); 6285 6286 6287 #if (0) // Leaving this here for posterity, in case we need it for revealing allies. ST - 10/17/2019 10:51AM 6288 /* 6289 ** Changed this function to reveal for the appropriate players in GlyphX multiplayer. ST - 8/7/2019 11:34AM 6290 */ 6291 if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { 6292 6293 Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental); 6294 6295 } else { 6296 6297 for (int i = 0; i < Session.Players.Count(); i++) { 6298 HousesType house_type = Session.Players[i]->Player.ID; 6299 HouseClass *house = HouseClass::As_Pointer(house_type); 6300 6301 if (Is_Owned_By_Player(house) || Is_Discovered_By_Player(house)) { 6302 Map.Sight_From(Coord_Cell(Center_Coord()), sight_range, house, incremental); 6303 } 6304 } 6305 } 6306 #endif 6307 } 6308 } 6309 6310 6311 //********************************************************************************************** 6312 // MODULE SEPARATION -- TechnoTypeClass member functions follow. 6313 //********************************************************************************************** 6314 6315 6316 /*********************************************************************************************** 6317 * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * 6318 * * 6319 * This is the normal constructor for techno type objects. It is called in the process of * 6320 * constructing all the object type (constant) data for the various techno type objects. * 6321 * * 6322 * INPUT: see below... * 6323 * * 6324 * OUTPUT: none * 6325 * * 6326 * WARNINGS: none * 6327 * * 6328 * HISTORY: * 6329 * 03/19/1995 JLB : Created. * 6330 * 05/11/1996 JLB : Moderated risk calc so range doesn't dominate. * 6331 *=============================================================================================*/ 6332 TechnoTypeClass::TechnoTypeClass( 6333 RTTIType rtti, 6334 int id, 6335 int name, 6336 char const * ininame, 6337 RemapType remap, 6338 int verticaloffset, 6339 int primaryoffset, 6340 int primarylateral, 6341 int secondaryoffset, 6342 int secondarylateral, 6343 bool is_nominal, 6344 bool is_stealthy, 6345 bool is_selectable, 6346 bool is_legal_target, 6347 bool is_insignificant, 6348 bool is_immune, 6349 bool is_theater, 6350 bool is_turret_equipped, 6351 bool is_remappable, 6352 bool is_footprint, 6353 int rotation, 6354 SpeedType speed, 6355 int horizontaloffset) : 6356 ObjectTypeClass( rtti, 6357 id, 6358 true, 6359 is_stealthy, 6360 is_selectable, 6361 is_legal_target, 6362 is_insignificant, 6363 is_immune, 6364 is_footprint, 6365 name, 6366 ininame), 6367 Remap(remap), 6368 IsDoubleOwned(false), 6369 IsInvisible(false), 6370 IsLeader(false), 6371 IsScanner(false), 6372 IsNominal(is_nominal), 6373 IsTheater(is_theater), 6374 IsTurretEquipped(is_turret_equipped), 6375 IsCrew(false), 6376 IsRepairable(true), 6377 IsRemappable(is_remappable), 6378 IsCloakable(false), 6379 IsSelfHealing(false), 6380 IsExploding(false), 6381 MZone(MZONE_NORMAL), 6382 ThreatRange(0), 6383 MaxPassengers(0), 6384 SightRange(0), 6385 Cost(0), 6386 Level(-1), 6387 Prerequisite(STRUCTF_NONE), 6388 Risk(0),Reward(0), 6389 MaxSpeed(MPH_IMMOBILE), 6390 Speed(speed), 6391 MaxAmmo(-1), 6392 Ownable(0), 6393 CameoData(NULL), 6394 Rotation(rotation), 6395 ROT(0), 6396 PrimaryWeapon(NULL), 6397 SecondaryWeapon(NULL), 6398 HorizontalOffset(horizontaloffset), 6399 VerticalOffset(verticaloffset), 6400 PrimaryOffset(primaryoffset), 6401 PrimaryLateral(primarylateral), 6402 SecondaryOffset(secondaryoffset), 6403 SecondaryLateral(secondarylateral), 6404 Points(0) 6405 { 6406 } 6407 6408 6409 /*********************************************************************************************** 6410 * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * 6411 * * 6412 * This routine is used to find the underlying cost for this object. The underlying cost * 6413 * does not include any free items that normally come with the object when purchased * 6414 * directly. Example: The raw cost of a refinery is the normal cost minus the cost of a * 6415 * harvester. * 6416 * * 6417 * INPUT: none * 6418 * * 6419 * OUTPUT: Returns with the credit cost of the base object type. * 6420 * * 6421 * WARNINGS: none * 6422 * * 6423 * HISTORY: * 6424 * 08/13/1995 JLB : Created. * 6425 *=============================================================================================*/ 6426 int TechnoTypeClass::Raw_Cost(void) const 6427 { 6428 return(Cost); 6429 } 6430 6431 6432 /*********************************************************************************************** 6433 * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * 6434 * * 6435 * This routine will return the ownable bits for this object type. The ownable bits are * 6436 * a bitflag composite of the houses that can own (build) this object type. * 6437 * * 6438 * INPUT: none * 6439 * * 6440 * OUTPUT: Returns with the ownable bits for this object type. * 6441 * * 6442 * WARNINGS: none * 6443 * * 6444 * HISTORY: * 6445 * 07/29/1995 JLB : Created. * 6446 *=============================================================================================*/ 6447 int TechnoTypeClass::Get_Ownable(void) const 6448 { 6449 if (IsDoubleOwned && Session.Type != GAME_NORMAL) { 6450 return(Ownable | HOUSEF_SOVIET | HOUSEF_ALLIES); 6451 } 6452 return(Ownable); 6453 } 6454 6455 6456 /*********************************************************************************************** 6457 * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * 6458 * * 6459 * This routine will return the time it takes to construct this object. Usually the time * 6460 * to produce is directly related to cost. * 6461 * * 6462 * INPUT: none * 6463 * * 6464 * OUTPUT: Returns with the time to produce this object type. The time is expressed in the * 6465 * form of game ticks. * 6466 * * 6467 * WARNINGS: none * 6468 * * 6469 * HISTORY: * 6470 * 07/29/1995 JLB : Created. * 6471 *=============================================================================================*/ 6472 //#define UNIT_BUILD_BIAS fixed(5,4) 6473 //#define UNIT_BUILD_BIAS fixed(6,4) 6474 #define UNIT_BUILD_BIAS fixed(1,1) 6475 //#define UNIT_BUILD_BIAS fixed(5,1) 6476 6477 extern int UnitBuildPenalty; 6478 6479 int TechnoTypeClass::Time_To_Build(HousesType house) const 6480 { 6481 int time = Cost * Rule.BuildSpeedBias * fixed(TICKS_PER_MINUTE, 1000); 6482 6483 HouseClass* hptr = HouseClass::As_Pointer(house); 6484 if (!hptr || !hptr->IsActive) { 6485 return time; 6486 } 6487 6488 #ifdef FIXIT_VERSION_3 6489 if (Session.Type == GAME_NORMAL) { 6490 #else 6491 if (Session.Type == GAME_NORMAL || 6492 PlayingAgainstVersion == VERSION_RED_ALERT_104 || 6493 PlayingAgainstVersion == VERSION_RED_ALERT_107) { 6494 #endif 6495 time *= hptr->BuildSpeedBias; 6496 } 6497 else { 6498 #if (0) // No longer desired. ST - 1/18/2021 6499 /* 6500 ** New feature - Turkey has a 10% build speed bonus even though it isn't specified in the rules 6501 */ 6502 if (hptr->ActLike == HOUSE_TURKEY) { 6503 if (hptr->BuildSpeedBias == fixed(1)) { 6504 time *= 9; 6505 time /= 10; 6506 } 6507 } 6508 #endif 6509 if (What_Am_I() == RTTI_BUILDINGTYPE || What_Am_I() == RTTI_INFANTRYTYPE) { 6510 time *= hptr->BuildSpeedBias; 6511 } 6512 else { 6513 time *= hptr->BuildSpeedBias * fixed(UnitBuildPenalty, 100); //UNIT_BUILD_BIAS; 6514 } 6515 } 6516 6517 /* 6518 ** Adjust time according to IQ setting of computer controlled house. The 6519 ** build time will range from double normal time at the slowest to 6520 ** just normal time at the fastest. 6521 */ 6522 if (!hptr->IsHuman && Rule.Diff[hptr->Difficulty].IsBuildSlowdown) { 6523 time = time * Inverse(fixed(hptr->IQ + Rule.MaxIQ, Rule.MaxIQ * 2)); 6524 } 6525 6526 /* 6527 ** Adjust the time to build based on the power output of the owning house. 6528 */ 6529 fixed power = hptr->Power_Fraction(); 6530 fixed scale(1); 6531 if (power == 0) { 6532 scale = fixed(4, 1); 6533 } 6534 else if (power < fixed::_1_2) { 6535 scale = fixed(5, 2); 6536 } 6537 else if (power < 1) { 6538 scale = fixed(3, 2); 6539 } 6540 time *= scale; 6541 6542 int divisor = hptr->Factory_Count(What_Am_I()); 6543 if (divisor != 0) { 6544 #ifdef FIXIT_CSII // checked - ajw 9/28/98 6545 // Hack: allow the multiple-factory bonus, but only up to two factories if 6546 // this is an AM<->AM game. 6547 if (NewUnitsEnabled) { 6548 time /= min(divisor, 2); 6549 } 6550 else { 6551 time /= divisor; 6552 } 6553 #else 6554 time /= divisor; 6555 #endif 6556 } 6557 6558 return(time); 6559 } 6560 6561 6562 /*********************************************************************************************** 6563 * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * 6564 * * 6565 * This routine will return the cost to produce an object of this type. * 6566 * * 6567 * INPUT: none * 6568 * * 6569 * OUTPUT: Returns with the cost to produce one object of this type. * 6570 * * 6571 * WARNINGS: none * 6572 * * 6573 * HISTORY: * 6574 * 07/29/1995 JLB : Created. * 6575 *=============================================================================================*/ 6576 int TechnoTypeClass::Cost_Of(void) const 6577 { 6578 return(Cost); 6579 } 6580 6581 6582 /*********************************************************************************************** 6583 * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * 6584 * * 6585 * This routine will fetch the cameo (sidebar small image) shape of this object type. * 6586 * If there is no cameo data available (typical for non-produceable units), then NULL will * 6587 * be returned. * 6588 * * 6589 * INPUT: none * 6590 * * 6591 * OUTPUT: Returns with a pointer to the cameo data for this object type if present. * 6592 * * 6593 * WARNINGS: none * 6594 * * 6595 * HISTORY: * 6596 * 07/29/1995 JLB : Created. * 6597 *=============================================================================================*/ 6598 void const * TechnoTypeClass::Get_Cameo_Data(void) const 6599 { 6600 return(CameoData); 6601 } 6602 6603 6604 /*********************************************************************************************** 6605 * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * 6606 * * 6607 * This routine will return the cost to repair one step. At the TechnoTypeClass level, * 6608 * this merely serves as a placeholder function. The derived classes will provide a * 6609 * functional version of this routine. * 6610 * * 6611 * INPUT: none * 6612 * * 6613 * OUTPUT: Returns with the cost to repair one step. * 6614 * * 6615 * WARNINGS: none * 6616 * * 6617 * HISTORY: * 6618 * 07/29/1995 JLB : Created. * 6619 *=============================================================================================*/ 6620 int TechnoTypeClass::Repair_Cost(void) const 6621 { 6622 // MBL 04.28.2020 "Fix" for RA Barracks structures coming back with a repair cost of 0, and repairing for free 6623 // 6624 // if (Is_Foot()) { 6625 // return((Raw_Cost()/(MaxStrength/Rule.URepairStep)) * Rule.URepairPercent); 6626 // } 6627 // return((Raw_Cost()/(MaxStrength/Rule.RepairStep)) * Rule.RepairPercent); 6628 // 6629 int repair_cost = 0; 6630 if (Is_Foot()) { 6631 repair_cost = ((Raw_Cost()/(MaxStrength/Rule.URepairStep)) * Rule.URepairPercent); 6632 } 6633 else 6634 { 6635 repair_cost = ((Raw_Cost()/(MaxStrength/Rule.RepairStep)) * Rule.RepairPercent); 6636 } 6637 repair_cost = MAX(repair_cost, 1); 6638 return repair_cost; 6639 } 6640 6641 6642 /*********************************************************************************************** 6643 * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * 6644 * * 6645 * This routine merely serves as placeholder virtual function. The various type classes * 6646 * will override this routine to return the number of health points to repair in one * 6647 * "step". * 6648 * * 6649 * INPUT: none * 6650 * * 6651 * OUTPUT: Returns with the number of health points to repair in one step. * 6652 * * 6653 * WARNINGS: none * 6654 * * 6655 * HISTORY: * 6656 * 07/29/1995 JLB : Created. * 6657 *=============================================================================================*/ 6658 int TechnoTypeClass::Repair_Step(void) const 6659 { 6660 if (Is_Foot()) { 6661 return(Rule.URepairStep); 6662 } 6663 return(Rule.RepairStep); 6664 } 6665 6666 6667 /*********************************************************************************************** 6668 * TechnoTypeClass::Is_Two_Shooter -- Determines if this object is a double shooter. * 6669 * * 6670 * Some objects fire two shots in quick succession. If this is true for this object, then * 6671 * a 'true' value will be returned from this routine. * 6672 * * 6673 * INPUT: none * 6674 * * 6675 * OUTPUT: bool; Is this object a two shooter? * 6676 * * 6677 * WARNINGS: none * 6678 * * 6679 * HISTORY: * 6680 * 07/29/1996 JLB : Created. * 6681 *=============================================================================================*/ 6682 bool TechnoTypeClass::Is_Two_Shooter(void) const 6683 { 6684 if (PrimaryWeapon != NULL && (PrimaryWeapon == SecondaryWeapon || PrimaryWeapon->Burst > 1)) { 6685 return(true); 6686 } 6687 return(false); 6688 } 6689 6690 6691 /*********************************************************************************************** 6692 * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. * 6693 * * 6694 * This is a helper routine that will take a decimal percentage number and convert it * 6695 * into a game based fixed point number. * 6696 * * 6697 * INPUT: val -- Decimal percent number to convert. * 6698 * * 6699 * OUTPUT: Returns with the decimal percent number converted to a game fixed point number. * 6700 * * 6701 * WARNINGS: none * 6702 * * 6703 * HISTORY: * 6704 * 06/17/1996 JLB : Created. * 6705 *=============================================================================================*/ 6706 static inline int _Scale_To_256(int val) 6707 { 6708 val = min(val, 100); 6709 val = max(val, 0); 6710 val = ((val * 256) / 100); 6711 val = min(val, 255); 6712 return(val); 6713 } 6714 6715 6716 /*********************************************************************************************** 6717 * TechnoTypeClass::Read_INI -- Reads the techno type data from the INI database. * 6718 * * 6719 * Use this routine to fill in the data for this techno type class object from the * 6720 * database specified. Typical use of this is for the rules parsing. * 6721 * * 6722 * INPUT: ini -- Reference to the INI database that the information will be lifted from. * 6723 * * 6724 * OUTPUT: bool; Was the database used to extract information? A failure (false) response * 6725 * would mean that the database didn't contain a section that applies to this * 6726 * techno class object. * 6727 * * 6728 * WARNINGS: none * 6729 * * 6730 * HISTORY: * 6731 * 07/19/1996 JLB : Created. * 6732 *=============================================================================================*/ 6733 bool TechnoTypeClass::Read_INI(CCINIClass & ini) 6734 { 6735 if (ini.Is_Present(Name())) { 6736 #ifdef FIXIT_NAME_OVERRIDE 6737 char buffer[256]; 6738 int id = ((RTTI+1) * 100) + ID; 6739 6740 ini.Get_String(Name(), "Name", "", buffer, sizeof(buffer)); 6741 if (strlen(buffer) > 0) { 6742 6743 #if defined (GERMAN)||(FRENCH) 6744 6745 for(int xx=0; NewName[xx] != NULL; xx++){ 6746 if(!strcmp(NewName[xx], buffer)){ 6747 memcpy(buffer, "", sizeof(buffer)); 6748 memcpy(buffer, NewName[xx+1], sizeof(buffer)); 6749 break; 6750 } 6751 } 6752 #endif 6753 6754 /* 6755 ** Insert the new name text into the buffer list. 6756 */ 6757 for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { 6758 if (NameIDOverride[index] == 0) { 6759 NameOverride[index] = strdup(buffer); 6760 NameIDOverride[index] = id; 6761 // FullName = -(index+1); 6762 break; 6763 } 6764 } 6765 } 6766 #endif 6767 6768 IsDoubleOwned = ini.Get_Bool(Name(), "DoubleOwned", IsDoubleOwned); 6769 ThreatRange = ini.Get_Lepton(Name(), "GuardRange", ThreatRange); 6770 IsExploding = ini.Get_Bool(Name(), "Explodes", IsExploding); 6771 PrimaryWeapon = WeaponTypeClass::As_Pointer(ini.Get_WeaponType(Name(), "Primary", PrimaryWeapon != NULL ? (WeaponType)(PrimaryWeapon->ID) : WEAPON_NONE)); 6772 SecondaryWeapon = WeaponTypeClass::As_Pointer(ini.Get_WeaponType(Name(), "Secondary", SecondaryWeapon != NULL ? (WeaponType)(SecondaryWeapon->ID) : WEAPON_NONE)); 6773 IsCloakable = ini.Get_Bool(Name(), "Cloakable", IsCloakable); 6774 IsCrushable = ini.Get_Bool(Name(), "Crushable", IsCrushable); 6775 IsScanner = ini.Get_Bool(Name(), "Sensors", IsScanner); 6776 Armor = ini.Get_ArmorType(Name(), "Armor", Armor); 6777 Prerequisite = ini.Get_Buildings(Name(), "Prerequisite", Prerequisite); 6778 MaxStrength = ini.Get_Int(Name(), "Strength", MaxStrength); 6779 SightRange = ini.Get_Int(Name(), "Sight", SightRange); 6780 Level = ini.Get_Int(Name(), "TechLevel", Level); 6781 MaxSpeed = MPHType(_Scale_To_256(ini.Get_Int(Name(), "Speed", fixed(MaxSpeed, 256) * 100))); 6782 Cost = ini.Get_Int(Name(), "Cost", Cost); 6783 MaxAmmo = ini.Get_Int(Name(), "Ammo", MaxAmmo); 6784 Risk = Reward = Points = ini.Get_Int(Name(), "Points", Points); 6785 Ownable = ini.Get_Owners(Name(), "Owner", Ownable); 6786 IsCrew = ini.Get_Bool(Name(), "Crewed", IsCrew); 6787 IsRepairable = ini.Get_Bool(Name(), "Repairable", IsRepairable); 6788 IsInvisible = ini.Get_Bool(Name(), "Invisible", IsInvisible); 6789 IsSelfHealing = ini.Get_Bool(Name(), "SelfHealing", IsSelfHealing); 6790 ROT = ini.Get_Int(Name(), "ROT", ROT); 6791 MaxPassengers = ini.Get_Int(Name(), "Passengers", MaxPassengers); 6792 //Mono_Printf("before image=: %s\n",GraphicName); 6793 ini.Get_String(Name(), "Image", GraphicName, GraphicName, sizeof(GraphicName)); 6794 //Mono_Printf("after image=: %s\n",GraphicName);if(Random_Pick(0,4)) Keyboard->Get(); 6795 6796 IsLeader = false; 6797 if (PrimaryWeapon != NULL && PrimaryWeapon->Attack > 0) { 6798 IsLeader = true; 6799 } 6800 6801 /* 6802 ** Check to see what zone this object should recognize. 6803 */ 6804 if (PrimaryWeapon != NULL && PrimaryWeapon->WarheadPtr != NULL && PrimaryWeapon->WarheadPtr->IsWallDestroyer) { 6805 MZone = MZONE_DESTROYER; 6806 } 6807 if (Speed == SPEED_FLOAT) { 6808 MZone = MZONE_WATER; 6809 } 6810 6811 return(true); 6812 } 6813 return(false); 6814 } 6815 6816 6817 int TechnoTypeClass::Legal_Placement(CELL pos) const 6818 { 6819 if (pos == -1) return(0); 6820 6821 /* 6822 ** Normal buildings must check to see that every foundation square is free of 6823 ** obstacles. If this check passes for all foundation squares, only then does the 6824 ** routine return that it is legal to place. 6825 */ 6826 short const * offset = Occupy_List(true); 6827 bool build = (What_Am_I() == RTTI_BUILDINGTYPE); 6828 6829 while (offset != NULL && *offset != REFRESH_EOL) { 6830 CELL cell = pos + *offset++; 6831 if (!Map.In_Radar(cell)) return(false); 6832 if (build) { 6833 if (!Map[cell].Is_Clear_To_Build(Speed)) { 6834 return(0); 6835 } 6836 } else { 6837 if (!Map[cell].Is_Clear_To_Move(Speed, false, false)) { 6838 return(0); 6839 } 6840 } 6841 } 6842 return(1); 6843 } 6844 6845 6846 6847 6848 6849 6850 6851 /* 6852 ** Additions to TechnoClass to track discovery per-player. ST - 3/6/2019 11:18AM 6853 ** 6854 ** 6855 ** 6856 ** 6857 ** 6858 */ 6859 6860 6861 /*********************************************************************************************** 6862 * TechnoClass::Is_Discovered_By_Player -- Has this object been disovered by the given player * 6863 * * 6864 * INPUT: Player pointer * 6865 * * 6866 * OUTPUT: True if discovered by that player * 6867 * * 6868 * WARNINGS: none * 6869 * * 6870 * HISTORY: * 6871 * 3/6/2019 11:20AM - ST * 6872 *=============================================================================================*/ 6873 bool TechnoClass::Is_Discovered_By_Player(HouseClass *player) const 6874 { 6875 if (player && player->Class) { 6876 int shift = (int) player->Class->House; 6877 return (IsDiscoveredByPlayerMask & (1 << shift)) ? true : false; 6878 } else { 6879 int shift = (int) PlayerPtr->Class->House; 6880 return (IsDiscoveredByPlayerMask & (1 << shift)) ? true : false; 6881 } 6882 return false; 6883 } 6884 6885 6886 /*********************************************************************************************** 6887 * TechnoClass::Set_Discovered_By_Player -- Mark that this object been disovered by the player * 6888 * * 6889 * INPUT: Player pointer * 6890 * * 6891 * OUTPUT: * 6892 * * 6893 * WARNINGS: none * 6894 * * 6895 * HISTORY: * 6896 * 3/6/2019 11:23AM - ST * 6897 *=============================================================================================*/ 6898 void TechnoClass::Set_Discovered_By_Player(HouseClass *player) 6899 { 6900 int shift = 0; 6901 if (player && player->Class) { 6902 shift = (int) player->Class->House; 6903 } else { 6904 shift = (int)PlayerPtr->Class->House; 6905 } 6906 IsDiscoveredByPlayerMask |= (1 << shift); 6907 6908 if (Session.Type == GAME_NORMAL && player == PlayerPtr) { 6909 IsDiscoveredByPlayer = true; 6910 } 6911 } 6912 6913 6914 6915 /*********************************************************************************************** 6916 * TechnoClass::Clear_Discovered_By_Players -- Clear player discovery flags * 6917 * * 6918 * INPUT: Player pointer * 6919 * * 6920 * OUTPUT: * 6921 * * 6922 * WARNINGS: none * 6923 * * 6924 * HISTORY: * 6925 * 4/27/2020 - SKY * 6926 *=============================================================================================*/ 6927 void TechnoClass::Clear_Discovered_By_Players() 6928 { 6929 IsDiscoveredByPlayerMask = 0; 6930 IsDiscoveredByPlayer = false; 6931 } 6932 6933 6934 6935 /*********************************************************************************************** 6936 * TechnoClass::Is_Owned_By_Player -- Is this object owned by the active human player * 6937 * * 6938 * INPUT: Player pointer * 6939 * * 6940 * OUTPUT: * 6941 * * 6942 * WARNINGS: none * 6943 * * 6944 * HISTORY: * 6945 * 3/13/2019 5:00PM - ST * 6946 *=============================================================================================*/ 6947 bool TechnoClass::Is_Owned_By_Player(HouseClass *player) const 6948 { 6949 if (player == NULL) { 6950 return (PlayerPtr == House) ? true : false; 6951 } 6952 return (player == House) ? true : false; 6953 } 6954 6955 6956 6957 /*********************************************************************************************** 6958 * TechnoClass::Spied_By -- Spied by flags for this object * 6959 * * 6960 * INPUT: Player pointer * 6961 * * 6962 * OUTPUT: * 6963 * * 6964 * WARNINGS: none * 6965 * * 6966 * HISTORY: * 6967 * 10/31/2019 - SKY * 6968 *=============================================================================================*/ 6969 unsigned TechnoClass::Spied_By() const 6970 { 6971 return SpiedBy; 6972 }