LOGIC.CPP (18735B)
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/LOGIC.CPP 1 3/03/97 10:25a Joe_bostic $ */ 17 /*********************************************************************************************** 18 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : LOGIC.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : September 27, 1993 * 28 * * 29 * Last Update : July 30, 1996 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * LogicClass::AI -- Handles AI logic processing for game objects. * 34 * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * 35 * LogicClass::Detach -- Detatch the specified target from the logic system. * 36 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 37 38 #include "function.h" 39 #include "logic.h" 40 #include "vortex.h" 41 42 static unsigned FramesPerSecond=0; 43 44 45 #ifdef CHEAT_KEYS 46 /*********************************************************************************************** 47 * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * 48 * * 49 * This is a debugging support routine. It displays the current state of the logic class * 50 * to the monochrome monitor. It assumes that it is being called once per second. * 51 * * 52 * INPUT: none * 53 * * 54 * OUTPUT: none * 55 * * 56 * WARNINGS: Call this routine only once per second. * 57 * * 58 * HISTORY: * 59 * 05/31/1994 JLB : Created. * 60 * 01/26/1996 JLB : Prints game time value. * 61 *=============================================================================================*/ 62 void LogicClass::Debug_Dump(MonoClass * mono) const 63 { 64 #define RECORDCOUNT 40 65 #define RECORDHEIGHT 21 66 static int _framecounter = 0; 67 68 static bool first = true; 69 if (first) { 70 first = false; 71 mono->Set_Cursor(0, 0); 72 mono->Print(Text_String(TXT_DEBUG_STRESS)); 73 } 74 75 //mono->Set_Cursor(0,0);mono->Printf("%d", AllowVoice); 76 77 78 _framecounter++; 79 mono->Set_Cursor(1, 1);mono->Printf("%ld", (long)Scen.Timer); 80 mono->Set_Cursor(10, 1);mono->Printf("%3d", FramesPerSecond); 81 mono->Set_Cursor(1, 3);mono->Printf("%02d:%02d:%02d", Scen.Timer / TICKS_PER_HOUR, (Scen.Timer % TICKS_PER_HOUR)/TICKS_PER_MINUTE, (Scen.Timer % TICKS_PER_MINUTE)/TICKS_PER_SECOND); 82 83 mono->Set_Cursor(1, 11);mono->Printf("%3d", Units.Count()); 84 mono->Set_Cursor(1, 12);mono->Printf("%3d", Infantry.Count()); 85 mono->Set_Cursor(1, 13);mono->Printf("%3d", Aircraft.Count()); 86 mono->Set_Cursor(1, 14);mono->Printf("%3d", Vessels.Count()); 87 mono->Set_Cursor(1, 15);mono->Printf("%3d", Buildings.Count()); 88 mono->Set_Cursor(1, 16);mono->Printf("%3d", Terrains.Count()); 89 mono->Set_Cursor(1, 17);mono->Printf("%3d", Bullets.Count()); 90 mono->Set_Cursor(1, 18);mono->Printf("%3d", Anims.Count()); 91 mono->Set_Cursor(1, 19);mono->Printf("%3d", Teams.Count()); 92 mono->Set_Cursor(1, 20);mono->Printf("%3d", Triggers.Count()); 93 mono->Set_Cursor(1, 21);mono->Printf("%3d", TriggerTypes.Count()); 94 mono->Set_Cursor(1, 22);mono->Printf("%3d", Factories.Count()); 95 96 SpareTicks = min((long)SpareTicks, (long)TIMER_SECOND); 97 98 /* 99 ** CPU utilization record. 100 */ 101 mono->Sub_Window(15, 1, 6, 11); 102 mono->Scroll(); 103 mono->Set_Cursor(0, 10); 104 mono->Printf("%3d%%", ((TIMER_SECOND-SpareTicks)*100) / TIMER_SECOND); 105 106 /* 107 ** Update the frame rate log. 108 */ 109 mono->Sub_Window(22, 1, 6, 11); 110 mono->Scroll(); 111 mono->Set_Cursor(0, 10); 112 mono->Printf("%4d", FramesPerSecond); 113 114 /* 115 ** Update the findpath calc record. 116 */ 117 mono->Sub_Window(50, 1, 6, 11); 118 mono->Scroll(); 119 mono->Set_Cursor(0, 10); 120 mono->Printf("%4d", PathCount); 121 PathCount = 0; 122 123 /* 124 ** Update the cell redraw record. 125 */ 126 mono->Sub_Window(29, 1, 6, 11); 127 mono->Scroll(); 128 mono->Set_Cursor(0, 10); 129 mono->Printf("%5d", CellCount); 130 CellCount = 0; 131 132 /* 133 ** Update the target scan record. 134 */ 135 mono->Sub_Window(36, 1, 6, 11); 136 mono->Scroll(); 137 mono->Set_Cursor(0, 10); 138 mono->Printf("%5d", TargetScan); 139 TargetScan = 0; 140 141 /* 142 ** Sidebar redraw record. 143 */ 144 mono->Sub_Window(43, 1, 6, 11); 145 mono->Scroll(); 146 mono->Set_Cursor(0, 10); 147 mono->Printf("%5d", SidebarRedraws); 148 SidebarRedraws = 0; 149 150 /* 151 ** Update the CPU utilization chart. 152 */ 153 mono->Sub_Window(15, 13, 63, 10); 154 mono->Pan(1); 155 mono->Sub_Window(15, 13, 64, 10); 156 int graph = RECORDHEIGHT * fixed(TIMER_SECOND-SpareTicks, TIMER_SECOND); 157 for (int row = 1; row < RECORDHEIGHT; row += 2) { 158 static char _barchar[4] = {' ', 220, 0, 219}; 159 char str[2]; 160 int index = 0; 161 162 index |= (graph >= row) ? 0x01 : 0x00; 163 index |= (graph >= row+1) ? 0x02: 0x00; 164 165 str[1] = '\0'; 166 str[0] = _barchar[index]; 167 mono->Text_Print(str, 62, 9-(row/2)); 168 } 169 mono->Sub_Window(); 170 171 172 SpareTicks = 0; 173 FramesPerSecond = 0; 174 } 175 #endif 176 177 178 /*********************************************************************************************** 179 * LogicClass::AI -- Handles AI logic processing for game objects. * 180 * * 181 * This routine is used to perform the AI processing for all game objects. This includes * 182 * all houses, factories, objects, and teams. * 183 * * 184 * INPUT: none * 185 * * 186 * OUTPUT: none * 187 * * 188 * WARNINGS: none * 189 * * 190 * HISTORY: * 191 * 05/29/1994 JLB : Created. * 192 * 12/17/1994 JLB : Must perform one complete pass rather than bailing early. * 193 * 12/23/1994 JLB : Ensures that no object gets skipped if it was deleted. * 194 *=============================================================================================*/ 195 void LogicClass::AI(void) 196 { 197 int index; 198 199 FramesPerSecond++; 200 201 /* 202 ** Fading to B&W or color due to the chronosphere is handled here. 203 */ 204 Scen.Do_Fade_AI(); 205 206 /* 207 ** Handle any general timer trigger events. 208 */ 209 for (LogicTriggerID = 0; LogicTriggerID < LogicTriggers.Count(); LogicTriggerID++) { 210 TriggerClass * trig = LogicTriggers[LogicTriggerID]; 211 212 /* 213 ** Global changed trigger event might be triggered. 214 */ 215 if (Scen.IsGlobalChanged) { 216 if (trig->Spring(TEVENT_GLOBAL_SET)) continue; 217 if (trig->Spring(TEVENT_GLOBAL_CLEAR)) continue; 218 } 219 220 /* 221 ** Bridge change event. 222 */ 223 if (Scen.IsBridgeChanged) { 224 if (trig->Spring(TEVENT_ALL_BRIDGES_DESTROYED)) continue; 225 } 226 227 /* 228 ** General time expire trigger events can be sprung without warning. 229 */ 230 if (trig->Spring(TEVENT_TIME)) continue; 231 232 /* 233 ** The mission timer expiration trigger event might spring if the timer is active 234 ** but at a value of zero. 235 */ 236 if (Scen.MissionTimer.Is_Active() && Scen.MissionTimer == 0) { 237 if (trig->Spring(TEVENT_MISSION_TIMER_EXPIRED)) continue; 238 } 239 } 240 241 if (Scen.MissionTimer.Is_Active()) { 242 long secs = Scen.MissionTimer / TICKS_PER_SECOND; 243 long mins = secs / 60; 244 long hours = mins / 60; 245 secs %= 60; 246 mins %= 60; 247 248 /* 249 ** Speak mission timer reminders. 250 */ 251 VoxType vox = VOX_NONE; 252 if (Scen.MissionTimer == (1 * TICKS_PER_MINUTE)) vox = VOX_TIME_1; 253 if (Scen.MissionTimer == (2 * TICKS_PER_MINUTE)) vox = VOX_TIME_2; 254 if (Scen.MissionTimer == (3 * TICKS_PER_MINUTE)) vox = VOX_TIME_3; 255 if (Scen.MissionTimer == (4 * TICKS_PER_MINUTE)) vox = VOX_TIME_4; 256 if (Scen.MissionTimer == (5 * TICKS_PER_MINUTE)) vox = VOX_TIME_5; 257 if (Scen.MissionTimer == (10 * TICKS_PER_MINUTE)) vox = VOX_TIME_10; 258 if (Scen.MissionTimer == (20 * TICKS_PER_MINUTE)) vox = VOX_TIME_20; 259 if (Scen.MissionTimer == (30 * TICKS_PER_MINUTE)) vox = VOX_TIME_30; 260 if (Scen.MissionTimer == (40 * TICKS_PER_MINUTE)) vox = VOX_TIME_40; 261 if (vox != VOX_NONE) { 262 Speak(vox); 263 Map.FlasherTimer = 7; 264 } 265 } 266 267 /* 268 ** Clean up any status values that were maintained only for logic trigger 269 ** purposes. 270 */ 271 if (Scen.MissionTimer.Is_Active() && Scen.MissionTimer == 0) { 272 Scen.MissionTimer.Stop(); 273 Map.Flag_To_Redraw(true); // Used only to cause tabs to redraw in new state. 274 } 275 Scen.IsGlobalChanged = false; 276 Scen.IsBridgeChanged = false; 277 /* 278 ** Shadow creeping back over time is handled here. 279 */ 280 if (Special.IsShadowGrow && Rule.ShroudRate != 0 && Scen.ShroudTimer == 0) { 281 Scen.ShroudTimer = TICKS_PER_MINUTE * Rule.ShroudRate; 282 283 /* 284 ** Do this for all players in Client/Server multiplayer. ST - 8/9/2019 10:23AM 285 */ 286 if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { 287 Map.Encroach_Shadow(PlayerPtr); 288 } else { 289 for (int i=0 ; i<Session.Players.Count() ; i++) { 290 HouseClass *player_ptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); 291 if (player_ptr && player_ptr->IsHuman) { 292 Map.Encroach_Shadow(player_ptr); 293 } 294 } 295 } 296 } 297 298 /* 299 ** Team AI is processed. 300 */ 301 for (index = 0; index < Teams.Count(); index++) { 302 Teams.Ptr(index)->AI(); 303 } 304 305 /* 306 ** If there's a time quake, handle it here. 307 */ 308 if (TimeQuake) { 309 Sound_Effect(VOC_KABOOM15); 310 Shake_The_Screen(8); 311 } 312 313 ChronalVortex.AI(); 314 /* 315 ** AI for all sentient objects is processed. 316 */ 317 for (index = 0; index < Count(); index++) { 318 ObjectClass * obj = (*this)[index]; 319 int count = Count(); 320 321 BStart(BENCH_AI); 322 obj->AI(); 323 BEnd(BENCH_AI); 324 325 if (TimeQuake && obj != NULL && obj->IsActive && !obj->IsInLimbo && obj->Strength) { 326 int damage = (int)obj->Class_Of().MaxStrength * Rule.QuakeDamagePercent; 327 #ifdef FIXIT_CSII // checked - ajw 9/28/98 328 if (TimeQuakeCenter) { 329 if(::Distance(obj->As_Target(),TimeQuakeCenter)/256 < MTankDistance) { 330 switch(obj->What_Am_I()) { 331 case RTTI_INFANTRY: 332 damage = QuakeInfantryDamage; 333 break; 334 case RTTI_BUILDING: 335 damage = QuakeBuildingDamage * (int)obj->Class_Of().MaxStrength; 336 break; 337 default: 338 damage = QuakeUnitDamage * (int)obj->Class_Of().MaxStrength; 339 break; 340 } 341 if (damage) { 342 obj->Clicked_As_Target(HOUSE_COUNT); // 2019/09/20 JAS - Added record of who clicked on the object, HOUSE_COUNT is used to mark for all houses 343 new AnimClass(ANIM_MINE_EXP1, obj->Center_Coord()); 344 } 345 obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); 346 } 347 } else { 348 obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); 349 } 350 #else 351 obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); 352 #endif 353 } 354 /* 355 ** If the object was destroyed in the process of performing its AI, then 356 ** adjust the index so that no object gets skipped. 357 */ 358 int count_diff = Count() - count; 359 if (count_diff < 0) { 360 index += count_diff; 361 } 362 } 363 HouseClass::Recalc_Attributes(); 364 365 /* 366 ** Map related logic is performed. 367 */ 368 Map.Logic(); 369 370 /* 371 ** Factory processing is performed. 372 */ 373 for (index = 0; index < Factories.Count(); index++) { 374 Factories.Ptr(index)->AI(); 375 } 376 377 /* 378 ** House processing is performed. 379 */ 380 #ifdef FIXIT_VERSION_3 381 if( Session.Type != GAME_NORMAL ) 382 { 383 for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { 384 HouseClass * hptr = HouseClass::As_Pointer(house); 385 if (hptr && hptr->IsActive) { 386 hptr->AI(); 387 } 388 } 389 } 390 else 391 { 392 for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { 393 HouseClass * hptr = HouseClass::As_Pointer(house); 394 if (hptr && hptr->IsActive) { 395 hptr->AI(); 396 } 397 } 398 } 399 #else // AI() is called redundantly 12 times in multiplayer games here. ajw 400 for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { 401 HouseClass * hptr = HouseClass::As_Pointer(house); 402 if (hptr && hptr->IsActive) { 403 hptr->AI(); 404 } 405 } 406 #endif 407 408 #ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. 409 if( Session.Type != GAME_NORMAL && Scen.AutoSonarTimer == 0 ) 410 { 411 if( bAutoSonarPulse ) 412 { 413 Map.Activate_Pulse(); 414 Sound_Effect(VOC_SONAR); 415 bAutoSonarPulse = false; 416 } 417 #define AUTOSONAR_PERIOD TICKS_PER_SECOND * 40; 418 Scen.AutoSonarTimer = AUTOSONAR_PERIOD; 419 } 420 #endif 421 } 422 423 424 /*********************************************************************************************** 425 * LogicClass::Detach -- Detatch the specified target from the logic system. * 426 * * 427 * This routine is called when the specified target object is about to be removed from the * 428 * game system and all references to it must be severed. The only thing that the logic * 429 * system looks for in this case is to see if the target refers to a trigger and if so, * 430 * it scans through the trigger list and removes all references to it. * 431 * * 432 * INPUT: target -- The target to remove from the sytem. * 433 * * 434 * OUTPUT: none * 435 * * 436 * WARNINGS: none * 437 * * 438 * HISTORY: * 439 * 07/30/1996 JLB : Created. * 440 *=============================================================================================*/ 441 void LogicClass::Detach(TARGET target, bool ) 442 { 443 /* 444 ** Remove any triggers from the logic trigger list. 445 */ 446 if (Is_Target_Trigger(target)) { 447 for (int index = 0; index < LogicTriggers.Count(); index++) { 448 if (As_Trigger(target) == LogicTriggers[index]) { 449 LogicTriggers.Delete(index); 450 index--; 451 } 452 } 453 } 454 } 455 456 457 /*********************************************************************************************** 458 * LogicClass::Clear_Recently_Created_Bits -- Clear out the indicators that objects were * 459 * recently created * 460 * * 461 * INPUT: none * 462 * * 463 * OUTPUT: none * 464 * * 465 * WARNINGS: none * 466 * * 467 * HISTORY: * 468 * 8/19/2019 5:47PM ST : Created. * 469 *=============================================================================================*/ 470 void LogicClass::Clear_Recently_Created_Bits(void) 471 { 472 for (int index = 0; index < Count(); index++) { 473 ObjectClass * obj = (*this)[index]; 474 obj->IsRecentlyCreated = false; 475 } 476 }