EVENT.CPP (37191B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: F:\projects\c&c\vcs\code\event.cpv 2.17 16 Oct 1995 16:50:28 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 : EVENT.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : 12/09/94 * 28 * * 29 * Last Update : June 25, 1995 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * EventClass::EventClass -- Construct an id and cell based event. * 34 * EventClass::EventClass -- Construct simple target type event. * 35 * EventClass::EventClass -- Constructor for mission change events. * 36 * EventClass::EventClass -- Constructor for navigation computer events. * 37 * EventClass::EventClass -- Constructor for object types affecting cells event. * 38 * EventClass::EventClass -- Constructor for sidebar build events. * 39 * EventClass::EventClass -- Constructs event to transfer special flags. * 40 * EventClass::EventClass -- Default constructor for event objects. * 41 * EventClass::EventClass -- Event for sequencing animations. * 42 * EventClass::EventClass -- Megamission assigned to unit. * 43 * EventClass::Execute -- Execute a queued command. * 44 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 45 46 #include "function.h" 47 #include "ccdde.h" 48 49 /*************************************************************************** 50 ** Table of what data is really used in the EventClass struct for different 51 ** events. This table must be kept current with the EventType enum. 52 */ 53 unsigned char EventClass::EventLength[EventClass::LAST_EVENT] = { 54 0, // EMPTY 55 size_of(EventClass, Data.General ), // ALLY 56 size_of(EventClass, Data.MegaMission ), // MEGAMISSION 57 size_of(EventClass, Data.Target ), // IDLE 58 size_of(EventClass, Data.Target ), // SCATTER 59 0, // DESTRUCT 60 0, // DEPLOY 61 size_of(EventClass, Data.Place ), // PLACE 62 0, // OPTIONS 63 size_of(EventClass, Data.General ), // GAMESPEED 64 size_of(EventClass, Data.Specific ), // PRODUCE 65 size_of(EventClass, Data.Specific.Type ), // SUSPEND 66 size_of(EventClass, Data.Specific.Type ), // ABANDON 67 size_of(EventClass, Data.Target ), // PRIMARY 68 size_of(EventClass, Data.Special ), // SPECIAL_PLACE 69 0, // EXIT 70 size_of(EventClass, Data.Anim ), // ANIMATION 71 size_of(EventClass, Data.Target ), // REPAIR 72 size_of(EventClass, Data.Target ), // SELL 73 size_of(EventClass, Data.Options ), // SPECIAL 74 0, // FRAMESYNC 75 0, // MESSAGE 76 size_of(EventClass, Data.FrameInfo.Delay ), // RESPONSE_TIME 77 size_of(EventClass, Data.FrameInfo ), // FRAMEINFO 78 size_of(EventClass, Data.NavCom ), // ARCHIVE 79 size_of(EventClass, Data.Timing ), // TIMING 80 size_of(EventClass, Data.ProcessTime ), // PROCESS_TIME 81 }; 82 83 char * EventClass::EventNames[EventClass::LAST_EVENT] = { 84 "EMPTY", 85 "ALLY", 86 "MEGAMISSION", 87 "IDLE", 88 "SCATTER", 89 "DESTRUCT", 90 "DEPLOY", 91 "PLACE", 92 "OPTIONS", 93 "GAMESPEED", 94 "PRODUCE", 95 "SUSPEND", 96 "ABANDON", 97 "PRIMARY", 98 "SPECIAL_PLACE", 99 "EXIT", 100 "ANIMATION", 101 "REPAIR", 102 "SELL", 103 "SPECIAL", 104 "FRAMESYNC", 105 "MESSAGE", 106 "RESPONSE_TIME", 107 "FRAMEINFO", 108 "ARCHIVE", 109 "TIMING", 110 "PROCESS_TIME", 111 }; 112 113 114 /*********************************************************************************************** 115 * EventClass::EventClass -- Constructs event to transfer special flags. * 116 * * 117 * This constructs an event that will transfer the special flags. * 118 * * 119 * INPUT: data -- The special flags to be transported to all linked computers. * 120 * * 121 * OUTPUT: none * 122 * * 123 * WARNINGS: none * 124 * * 125 * HISTORY: * 126 * 06/25/1995 JLB : Created. * 127 *=============================================================================================*/ 128 EventClass::EventClass(SpecialClass data) 129 { 130 ID = Houses.ID(PlayerPtr); 131 Type = SPECIAL; 132 Frame = ::Frame; 133 Data.Options.Data = data; 134 } 135 136 137 /*********************************************************************************************** 138 * EventClass::EventClass -- Construct simple target type event. * 139 * * 140 * This will construct a generic event that needs only a target parameter. The actual * 141 * event and target values are specified as parameters. * 142 * * 143 * INPUT: type -- The event type to construct. * 144 * * 145 * target-- The target value that this event is to apply to. * 146 * * 147 * OUTPUT: none * 148 * * 149 * WARNINGS: none * 150 * * 151 * HISTORY: * 152 * 06/25/1995 JLB : Created. * 153 *=============================================================================================*/ 154 EventClass::EventClass(EventType type, TARGET target) 155 { 156 ID = Houses.ID(PlayerPtr); 157 Type = type; 158 Frame = ::Frame; 159 Data.Target.Whom = target; 160 } 161 162 163 /*********************************************************************************************** 164 * EventClass::EventClass -- Default constructor for event objects. * 165 * * 166 * This constructs a simple event object that requires no parameters other than the * 167 * type of event it is. * 168 * * 169 * INPUT: type -- The type of event to construct. * 170 * * 171 * OUTPUT: none * 172 * * 173 * WARNINGS: none * 174 * * 175 * HISTORY: * 176 * 12/27/1994 JLB : Created. * 177 *=============================================================================================*/ 178 EventClass::EventClass(EventType type) 179 { 180 ID = Houses.ID(PlayerPtr); 181 Type = type; 182 Frame = ::Frame; 183 } 184 185 186 /*********************************************************************************************** 187 * EventClass::EventClass -- Constructor for general-purpose-data events. * 188 * * 189 * INPUT: type -- The type of event to construct. * 190 * val -- data value * 191 * * 192 * OUTPUT: none * 193 * * 194 * WARNINGS: none * 195 * * 196 * HISTORY: * 197 * 12/27/1994 JLB : Created. * 198 *=============================================================================================*/ 199 EventClass::EventClass(EventType type, int val) 200 { 201 ID = Houses.ID(PlayerPtr); 202 Type = type; 203 Data.General.Value = val; 204 Frame = ::Frame; 205 } 206 207 208 /*********************************************************************************************** 209 * EventClass::EventClass -- Constructor for navigation computer events. * 210 * * 211 * Constructor for events that are used to assign the navigation computer. * 212 * * 213 * INPUT: type -- The type of event (this constructor can be used by other navigation * 214 * type events). * 215 * * 216 * src -- The object that the event should apply to. * 217 * * 218 * dest -- The destination (or target) that the event needs to complete. * 219 * * 220 * OUTPUT: none * 221 * * 222 * WARNINGS: none * 223 * * 224 * HISTORY: * 225 * 12/27/1994 JLB : Created. * 226 *=============================================================================================*/ 227 EventClass::EventClass(EventType type, TARGET src, TARGET dest) 228 { 229 ID = Houses.ID(PlayerPtr); 230 Type = type; 231 Frame = ::Frame; 232 Data.NavCom.Whom = src; 233 Data.NavCom.Where = dest; 234 } 235 236 237 /*********************************************************************************************** 238 * EventClass::EventClass -- Event for sequencing animations. * 239 * * 240 * This constructor is used for animations that must be created through the event system. * 241 * * 242 * INPUT: anim -- The animation that will be created. * 243 * * 244 * coord -- The location where the animation is to be created. * 245 * * 246 * OUTPUT: none * 247 * * 248 * WARNINGS: none * 249 * * 250 * HISTORY: * 251 * 05/19/1995 JLB : Created. * 252 *=============================================================================================*/ 253 EventClass::EventClass(AnimType anim, HousesType owner, COORDINATE coord, int visible) 254 { 255 ID = Houses.ID(PlayerPtr); 256 Type = ANIMATION; 257 Frame = ::Frame; 258 Data.Anim.What = anim; 259 Data.Anim.Owner = owner; 260 Data.Anim.Where = coord; 261 Data.Anim.Visible = visible; 262 } 263 264 265 /*********************************************************************************************** 266 * EventClass::EventClass -- Megamission assigned to unit. * 267 * * 268 * This is the event that is used to assign most missions to units. It combines both the * 269 * mission and the target (navcom and tarcom). * 270 * * 271 * INPUT: src -- The object that this mission is to apply to. * 272 * * 273 * mission -- The mission to assign to this object. * 274 * * 275 * target -- The target to assign to this object's TarCom. * 276 * * 277 * destination -- The destination to assign to this object's NavCom. * 278 * * 279 * OUTPUT: none * 280 * * 281 * WARNINGS: none * 282 * * 283 * HISTORY: * 284 * 05/18/1995 JLB : Created. * 285 *=============================================================================================*/ 286 EventClass::EventClass(TARGET src, MissionType mission, TARGET target, TARGET destination) 287 { 288 ID = Houses.ID(PlayerPtr); 289 Type = MEGAMISSION; 290 Frame = ::Frame; 291 Data.MegaMission.Whom = src; 292 Data.MegaMission.Mission = mission; 293 Data.MegaMission.Target = target; 294 Data.MegaMission.Destination = destination; 295 } 296 297 298 /*********************************************************************************************** 299 * EventClass::EventClass -- Constructor for sidebar build events. * 300 * * 301 * This constructor is used for events that deal with an object type and an object ID. * 302 * Typically, this is used exclusively by the sidebar. * 303 * * 304 * INPUT: type -- The event type of this object. * 305 * * 306 * object -- The object type number. * 307 * * 308 * id -- The object sub-type number. * 309 * * 310 * OUTPUT: none * 311 * * 312 * WARNINGS: none * 313 * * 314 * HISTORY: * 315 * 05/18/1995 JLB : Created. * 316 *=============================================================================================*/ 317 EventClass::EventClass(EventType type, RTTIType object, int id) 318 { 319 ID = Houses.ID(PlayerPtr); 320 Type = type; 321 Frame = ::Frame; 322 Data.Specific.Type = object; 323 Data.Specific.ID = id; 324 } 325 326 327 /*********************************************************************************************** 328 * EventClass::EventClass -- Constructor for object types affecting cells event. * 329 * * 330 * This constructor is used for those events that have an object type and associated cell. * 331 * Typically, this is for building placement after construction has completed. * 332 * * 333 * INPUT: type -- The event type for this object. * 334 * * 335 * object -- The object type number (actual object is probably inferred from the * 336 * sidebar data). * 337 * * 338 * cell -- The cell location where this event is to occur. * 339 * * 340 * OUTPUT: none * 341 * * 342 * WARNINGS: none * 343 * * 344 * HISTORY: * 345 * 05/18/1995 JLB : Created. * 346 *=============================================================================================*/ 347 EventClass::EventClass(EventType type, RTTIType object, CELL cell) 348 { 349 ID = Houses.ID(PlayerPtr); 350 Type = type; 351 Frame = ::Frame; 352 Data.Place.Type = object; 353 Data.Place.Cell = cell; 354 } 355 356 357 /*********************************************************************************************** 358 * EventClass::EventClass -- Construct an id and cell based event. * 359 * * 360 * This constructor is used for those events that require an ID number and a cell location. * 361 * * 362 * INPUT: type -- The event type this will be. * 363 * * 364 * id -- The arbitrary id number to assign. * 365 * * 366 * cell -- The location for this event. * 367 * * 368 * OUTPUT: none * 369 * * 370 * WARNINGS: none * 371 * * 372 * HISTORY: * 373 * 05/18/1995 JLB : Created. * 374 *=============================================================================================*/ 375 EventClass::EventClass(EventType type, int id, CELL cell) 376 { 377 ID = Houses.ID(PlayerPtr); 378 Type = type; 379 Frame = ::Frame; 380 Data.Special.ID = id; 381 Data.Special.Cell = cell; 382 } 383 384 385 /*********************************************************************************************** 386 * EventClass::Execute -- Execute a queued command. * 387 * * 388 * This routine executes an event. The even must already have been confirmed by any * 389 * remote machine before calling this routine. * 390 * * 391 * INPUT: none * 392 * * 393 * OUTPUT: none * 394 * * 395 * WARNINGS: none * 396 * * 397 * HISTORY: * 398 * 12/27/1994 JLB : Created. * 399 *=============================================================================================*/ 400 void EventClass::Execute(void) 401 { 402 TechnoClass * techno; 403 AnimClass * anim = 0; 404 HouseClass * house = 0; 405 char txt[80]; 406 int i; 407 //#if (0) 408 if (Type < 0 || Type > PROCESS_TIME){ 409 char tempbuf[128]; 410 sprintf (tempbuf, "Packet type %d received\n", Type); 411 CCDebugString (tempbuf); 412 413 sprintf (tempbuf, " ID = %d\n", ID); 414 CCDebugString (tempbuf); 415 416 sprintf (tempbuf, " Frame = %d\n", Frame); 417 CCDebugString (tempbuf); 418 419 sprintf (tempbuf, " MPlayer ID = %d\n", MPlayerID); 420 CCDebugString (tempbuf); 421 422 } 423 //#endif //(0) 424 425 426 switch (Type) { 427 /* 428 ** Update the archive target for this building. 429 */ 430 case ARCHIVE: 431 techno = As_Techno(Data.NavCom.Whom); 432 if (techno && techno->IsActive) { 433 techno->ArchiveTarget = Data.NavCom.Where; 434 } 435 break; 436 437 /* 438 ** Make or break alliance. 439 */ 440 case ALLY: 441 house = Houses.Raw_Ptr(Data.General.Value); 442 if (Houses.Raw_Ptr(ID)->Is_Ally(house)) { 443 Houses.Raw_Ptr(ID)->Make_Enemy((HousesType)Data.General.Value); 444 } else { 445 Houses.Raw_Ptr(ID)->Make_Ally((HousesType)Data.General.Value); 446 } 447 break; 448 449 /* 450 ** Special self destruct action requested. This is active in the multiplayer mode. 451 */ 452 case DESTRUCT: 453 CCDebugString ("C&C95 - Resignation packet received\n"); 454 Houses.Raw_Ptr(ID)->Flag_To_Die(); 455 Houses.Raw_Ptr(ID)->Resigned = true; 456 break; 457 458 /* 459 ** Update the special control flags. This is necessary so that in a multiplay 460 ** game, all machines will agree on the rules. If these options change during 461 ** game play, then all players are informed that options have changed. 462 */ 463 case SPECIAL: 464 { 465 Special = Data.Options.Data; 466 HouseClass * house = Houses.Raw_Ptr(ID); 467 468 sprintf(txt, Text_String(TXT_SPECIAL_WARNING), house->Name); 469 Messages.Add_Message(txt, MPlayerTColors[house->RemapColor], 470 TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1200, 0, 0); 471 Map.Flag_To_Redraw(false); 472 } 473 break; 474 475 /* 476 ** Starts or stops repair on the specified object. This event is triggered by the 477 ** player clicking the repair wrench on a building. 478 */ 479 case REPAIR: 480 CCDebugString ("C&C95 - Repair packet received\n"); 481 techno = As_Techno(Data.Target.Whom); 482 if (techno && techno->IsActive) { 483 techno->Repair(-1); 484 } 485 break; 486 487 /* 488 ** Tells a building/unit to sell. This event is triggered by the player clicking the 489 ** sell animating cursor over the building or unit. 490 */ 491 case SELL: 492 CCDebugString ("C&C95 - Sell packet received\n"); 493 techno = As_Techno(Data.Target.Whom); 494 if (techno && techno->IsActive && techno->House == Houses.Raw_Ptr(ID)) { 495 techno->Sell_Back(-1); 496 } else { 497 if (Is_Target_Cell(Data.Target.Whom)) { 498 Houses.Raw_Ptr(ID)->Sell_Wall(As_Cell(Data.Target.Whom)); 499 } 500 } 501 break; 502 503 /* 504 ** This even is used to trigger an animation that is generated as a direct 505 ** result of player intervention. 506 */ 507 case ANIMATION: 508 anim = new AnimClass(Data.Anim.What, Data.Anim.Where); 509 if (anim) { 510 anim->Set_Owner(Data.Anim.Owner); 511 512 if (Special.IsVisibleTarget) 513 { 514 anim->Set_Visible_Flags(static_cast<unsigned int>(-1)); 515 } 516 else 517 { 518 anim->Set_Visible_Flags(static_cast<unsigned int>(Data.Anim.Visible)); 519 } 520 521 /* 522 ** Beacons have a 30-second kill time. 523 */ 524 if (Data.Anim.What == ANIM_BEACON) { 525 FILETIME ft; 526 GetSystemTimeAsFileTime(&ft); 527 528 unsigned long long kill_time = ((unsigned long long)ft.dwLowDateTime + ((unsigned long long)ft.dwHighDateTime << 32ULL)) + 300000000ULL; 529 anim->Kill_At(kill_time); 530 } 531 } 532 break; 533 534 /* 535 ** This event will place the specified object at the specified location. 536 ** The event is used to place newly constructed buildings down on the map. The 537 ** object type is specified. From this object type, the house can determine the 538 ** exact factory and real object pointer to use. 539 */ 540 case PLACE: 541 CCDebugString ("C&C95 - Place packet received\n"); 542 Houses.Raw_Ptr(ID)->Place_Object(Data.Place.Type, Data.Place.Cell); 543 break; 544 545 /* 546 ** This event starts production of the speicified object type. The house can 547 ** determine from the type and ID value, what object to begin production on and 548 ** what factory to use. 549 */ 550 case PRODUCE: 551 CCDebugString ("C&C95 - Produce packet received\n"); 552 Houses.Raw_Ptr(ID)->Begin_Production(Data.Specific.Type, Data.Specific.ID); 553 break; 554 555 /* 556 ** This event is generated when the player puts production on hold. From the 557 ** object type, the factory can be inferred. 558 */ 559 case SUSPEND: 560 CCDebugString ("C&C95 - Suspend packet received\n"); 561 Houses.Raw_Ptr(ID)->Suspend_Production(Data.Specific.Type); 562 break; 563 564 /* 565 ** This event is generated when the player cancels production of the specified 566 ** object type. From the object type, the exact factory can be inferred. 567 */ 568 case ABANDON: 569 CCDebugString ("C&C95 - Abandon packet received\n"); 570 Houses.Raw_Ptr(ID)->Abandon_Production(Data.Specific.Type); 571 break; 572 573 /* 574 ** Toggles the primary factory state of the specified building. 575 */ 576 case PRIMARY:{ 577 CCDebugString ("C&C95 - Primary building packet received\n"); 578 BuildingClass * building = As_Building(Data.Target.Whom); 579 if (building && building->IsActive) { 580 building->Toggle_Primary(); 581 } 582 } 583 break; 584 585 /* 586 ** This is the general purpose mission control event. Most player 587 ** action routes through this event. It sets a unit's mission, TarCom, 588 ** and NavCom to the values specified. 589 */ 590 case MEGAMISSION: 591 techno = As_Techno(Data.MegaMission.Whom); 592 if (techno && techno->IsActive) { 593 594 /* 595 ** Fetch a pointer to the object of the mission. 596 */ 597 ObjectClass * object; 598 if (Target_Legal(Data.MegaMission.Target)) { 599 object = As_Object(Data.MegaMission.Target); 600 } else { 601 object = As_Object(Data.MegaMission.Destination); 602 } 603 604 /* 605 ** Break any existing team contact, since it is now invalid. 606 */ 607 if (!techno->IsTethered) { 608 techno->Transmit_Message(RADIO_OVER_OUT); 609 } 610 switch (techno->What_Am_I()) { 611 case RTTI_INFANTRY: 612 case RTTI_UNIT: 613 if (((FootClass *)techno)->Team) { 614 ((FootClass *)techno)->Team->Remove((FootClass *)techno); 615 } 616 break; 617 } 618 619 if (object) { 620 621 // 2019/09/20 JAS - Added record of who clicked on the object 622 HouseClass* house = Houses.Raw_Ptr(ID); 623 bool is_allied = house != nullptr && house->Is_Ally(techno); 624 if (is_allied || Special.IsVisibleTarget) { 625 object->Clicked_As_Target((HousesType)ID); 626 } 627 } 628 techno->Assign_Mission(Data.MegaMission.Mission); 629 630 /* 631 ** Guard area mode is handled with care. The specified target is actually 632 ** assigned as the location that should be guarded. In addition, the 633 ** movement destination is immediately set to this new location. 634 */ 635 if (Data.MegaMission.Mission == MISSION_GUARD_AREA && 636 // Target_Legal(Data.MegaMission.Target) && 637 (techno->What_Am_I() == RTTI_INFANTRY || techno->What_Am_I() == RTTI_UNIT || techno->What_Am_I() == RTTI_AIRCRAFT)) { 638 639 techno->ArchiveTarget = Data.MegaMission.Target; 640 techno->Assign_Target(TARGET_NONE); 641 techno->Assign_Destination(Data.MegaMission.Target); 642 } else if (Data.MegaMission.Mission == MISSION_ENTER && 643 object != NULL && 644 object->What_Am_I() == RTTI_BUILDING && 645 *((BuildingClass*)object) == STRUCT_REFINERY) { 646 techno->Transmit_Message(RADIO_HELLO, (BuildingClass*)object); 647 techno->Assign_Destination(TARGET_NONE); 648 } else { 649 techno->Assign_Target(Data.MegaMission.Target); 650 techno->Assign_Destination(Data.MegaMission.Destination); 651 } 652 653 #ifdef NEVER 654 if ((techno->What_Am_I() == RTTI_UNIT || techno->What_Am_I() == RTTI_INFANTRY) && 655 Data.MegaMission.Mission == MISSION_GUARD_AREA) { 656 657 techno->ArchiveTarget = Data.MegaMission.Destination; 658 } 659 #endif 660 } 661 break; 662 663 /* 664 ** Request that the unit/infantry/aircraft go into idle mode. 665 */ 666 case IDLE: 667 techno = As_Techno(Data.Target.Whom); 668 if (techno && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered) { 669 techno->Assign_Destination(TARGET_NONE); 670 techno->Assign_Target(TARGET_NONE); 671 techno->Enter_Idle_Mode(); 672 } 673 break; 674 675 /* 676 ** Request that the unit/infantry/aircraft scatter from its current location. 677 */ 678 case SCATTER: 679 techno = As_Techno(Data.Target.Whom); 680 if (techno && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered) { 681 techno->Scatter(0, true); 682 } 683 break; 684 685 /* 686 ** If we are placing down the ion cannon blast then lets take 687 ** care of it. 688 */ 689 case SPECIAL_PLACE: 690 CCDebugString ("C&C95 - Special blast packet received\n"); 691 Houses.Raw_Ptr(ID)->Place_Special_Blast((SpecialWeaponType)Data.Special.ID, Data.Special.Cell); 692 break; 693 694 /* 695 ** Exit the game. 696 ** Give parting message while palette is fading to black. 697 */ 698 case EXIT: 699 CCDebugString ("C&C95 - Exit game packet received\n"); 700 Theme.Queue_Song(THEME_NONE); 701 Stop_Speaking(); 702 Speak(VOX_CONTROL_EXIT); 703 while (Is_Speaking()) { 704 Call_Back(); 705 } 706 GameActive = false; 707 break; 708 709 /* 710 ** Process the options menu. 711 */ 712 case OPTIONS: 713 SpecialDialog = SDLG_OPTIONS; 714 break; 715 716 /* 717 ** Process the options Game Speed 718 */ 719 case GAMESPEED: 720 CCDebugString ("C&C95 - Game speed packet received\n"); 721 Options.GameSpeed = Data.General.Value; 722 break; 723 724 /* 725 ** Adjust connection timing for multiplayer games 726 */ 727 case RESPONSE_TIME: 728 char flip[128]; 729 sprintf (flip, "C&C95 - Changing MaxAhead to %d frames\n", Data.FrameInfo.Delay); 730 CCDebugString (flip); 731 MPlayerMaxAhead = Data.FrameInfo.Delay; 732 break; 733 734 // 735 // This event tells all systems to use new timing values. It's like 736 // RESPONSE_TIME, only it works. It's only used with the 737 // COMM_MULTI_E_COMP protocol. 738 // 739 case TIMING: 740 CCDebugString ("C&C95 - Timing packet received\n"); 741 //#if(TIMING_FIX) 742 // 743 // If MaxAhead is about to increase, we're vulnerable to a Packet- 744 // Received-Too-Late error, if any system generates an event after 745 // this TIMING event, but before it executes. So, record the 746 // period of vulnerability's frame start & end values, so we 747 // can reschedule these events to execute after it's over. 748 // 749 if (Data.Timing.MaxAhead > MPlayerMaxAhead) { 750 NewMaxAheadFrame1 = Frame; 751 NewMaxAheadFrame2 = Frame + Data.Timing.MaxAhead; 752 } 753 //#endif 754 755 DesiredFrameRate = Data.Timing.DesiredFrameRate; 756 MPlayerMaxAhead = Data.Timing.MaxAhead; 757 758 sprintf (flip, "C&C95 - Timing packet: DesiredFrameRate = %d\n", Data.Timing.DesiredFrameRate); 759 CCDebugString (flip); 760 sprintf (flip, "C&C95 - Timing packet: MaxAhead = %d\n", Data.Timing.MaxAhead); 761 CCDebugString (flip); 762 763 /* 764 ** If spawned from WChat then we should be getting poked every minute. If not then 765 ** deliberately break the max ahead value 766 */ 767 if (Special.IsFromWChat){ 768 MPlayerMaxAhead += DDEServer.Time_Since_Heartbeat()/(70*60); 769 //if (DDEServer.Time_Since_Heartbeat() >= 70*60) CCDebugString ("C&C95 - Missed a heartbeat\n"); 770 } 771 break; 772 773 // 774 // This event tells all systems what the other systems' process 775 // timing requirements are; it's used to compute a desired frame rate 776 // for the game. 777 // 778 case PROCESS_TIME: 779 for (i = 0; i < MPlayerCount; i++) { 780 if (MPlayerID == ::MPlayerID[i]) { 781 TheirProcessTime[i] = Data.ProcessTime.AverageTicks; 782 783 char flip[128]; 784 sprintf (flip, "C&C95 - Received PROCESS_TIME packet of %04x ticks\n", Data.ProcessTime.AverageTicks); 785 CCDebugString (flip); 786 787 break; 788 } 789 } 790 break; 791 792 /* 793 ** Default: do nothing. 794 */ 795 default: 796 break; 797 } 798 }