CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

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 }