TRIGGER.CPP (64079B)
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\trigger.cpv 2.17 16 Oct 1995 16:51:20 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 : TRIGGER.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : 11/12/94 * 28 * * 29 * Last Update : August 27, 1995 [JLB] * 30 * * 31 *---------------------------------------------------------------------------------------------* 32 * Functions: * 33 * Do_All_To_Hunt -- Forces all computer controlled units into hunt mode. * 34 * TriggerClass::Action_From_Name -- retrieves ActionType for given name * 35 * TriggerClass::Action_Need_Team -- Determines if this action event requires a team. * 36 * TriggerClass::As_Pointer -- returns pointer for the given trigger name * 37 * TriggerClass::As_Target -- Converts trigger to a target value * 38 * TriggerClass::Event_From_Name -- retrieves EventType for given name * 39 * TriggerClass::Event_Need_Data -- Determines if this event requires a data value. * 40 * TriggerClass::Event_Need_House -- Determines if this event requires a house identifier. * 41 * TriggerClass::Event_Need_Object -- Determines if the specified event requires an object. * 42 * TriggerClass::Init -- clears triggers for new scenario * 43 * TriggerClass::Name_From_Action -- retrieves name for ActionType * 44 * TriggerClass::Name_From_Event -- retrieves name for EventType * 45 * TriggerClass::Read_INI -- reads triggers from the INI file * 46 * TriggerClass::Remove -- removes this trigger from the game * 47 * TriggerClass::Spring -- Trigger processing routine for cell-based triggers * 48 * TriggerClass::Spring -- Trigger processing routine for house-based triggers * 49 * TriggerClass::Spring -- Trigger processing routine for object-based triggers * 50 * TriggerClass::TriggerClass -- constructor * 51 * TriggerClass::Validate -- validates trigger pointer * 52 * TriggerClass::Write_INI -- writes triggers to the INI file * 53 * TriggerClass::operator delete -- 'delete' operator * 54 * TriggerClass::operator new -- 'new' operator * 55 * TriggerClass::~TriggerClass -- Destructor for trigger objects. * 56 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 57 58 #include "function.h" 59 60 static void Do_All_To_Hunt(void); 61 62 #define FIXUP 0 63 64 /* 65 ********************************** Globals ********************************** 66 */ 67 static const char * EventText[EVENT_COUNT + 1] = { 68 "None", 69 "Player Enters", 70 "Discovered", 71 "Attacked", 72 "Destroyed", 73 "Any", 74 "House Discov.", 75 "Units Destr.", 76 "Bldgs Destr.", 77 "All Destr.", 78 "Credits", 79 "Time", 80 "# Bldgs Dstr.", 81 "# Units Dstr.", 82 "No Factories", 83 "Civ. Evac.", 84 "Built It" 85 }; 86 87 88 static const char * ActionText[TriggerClass::ACTION_COUNT + 1] = { 89 "None", 90 "Win", 91 "Lose", 92 "Production", 93 "Create Team", 94 "Dstry Teams", 95 "All to Hunt", 96 "Reinforce.", 97 "DZ at 'Z'", 98 "Airstrike", 99 "Nuclear Missile", 100 "Ion Cannon", 101 "Dstry Trig 'XXXX'", 102 "Dstry Trig 'YYYY'", 103 "Dstry Trig 'ZZZZ'", 104 "Autocreate", 105 "Cap=Win/Des=Lose", 106 "Allow Win" 107 }; 108 109 110 /*********************************************************************************************** 111 * TriggerClass::Validate -- validates trigger pointer * 112 * * 113 * INPUT: * 114 * none. * 115 * * 116 * OUTPUT: * 117 * 1 = ok, 0 = error * 118 * * 119 * WARNINGS: * 120 * none. * 121 * * 122 * HISTORY: * 123 * 08/09/1995 BRR : Created. * 124 *=============================================================================================*/ 125 #ifdef CHEAT_KEYS 126 int TriggerClass::Validate(void) const 127 { 128 int num; 129 130 num = Triggers.ID(this); 131 if (num < 0 || num >= TRIGGER_MAX) { 132 Validate_Error("TRIGGER"); 133 return (0); 134 } 135 else 136 return (1); 137 } 138 #else 139 #define Validate() 140 #endif 141 142 143 /*********************************************************************************************** 144 * TriggerClass::Event_Need_Object -- Determines if the specified event requires an object. * 145 * * 146 * This routine determines if the specified event must be attached to an object. Such * 147 * events can only exist in a parasitic fashion attached to object(s) in the game. * 148 * * 149 * INPUT: event -- The event type to examine. * 150 * * 151 * OUTPUT: Does the specified event require attachement to an object? * 152 * * 153 * WARNINGS: none * 154 * * 155 * HISTORY: * 156 * 08/27/1995 JLB : Created. * 157 *=============================================================================================*/ 158 bool TriggerClass::Event_Need_Object(EventType event) 159 { 160 switch (event) { 161 case EVENT_PLAYER_ENTERED: 162 case EVENT_DISCOVERED: 163 case EVENT_ATTACKED: 164 case EVENT_DESTROYED: 165 case EVENT_ANY: 166 return(true); 167 } 168 return(false); 169 } 170 171 172 /*********************************************************************************************** 173 * TriggerClass::Event_Need_House -- Determines if this event requires a house identifier. * 174 * * 175 * This routine is used to determine if the specified event requires a house identifier. * 176 * All trigger events that affect a house will require a house identifier. * 177 * * 178 * INPUT: event -- The event type to examine. * 179 * * 180 * OUTPUT: Does the specified event type require a house identifier? * 181 * * 182 * WARNINGS: none * 183 * * 184 * HISTORY: * 185 * 08/27/1995 JLB : Created. * 186 *=============================================================================================*/ 187 bool TriggerClass::Event_Need_House(EventType event) 188 { 189 switch (event) { 190 case EVENT_PLAYER_ENTERED: 191 case EVENT_HOUSE_DISCOVERED: 192 case EVENT_UNITS_DESTROYED: 193 case EVENT_BUILDINGS_DESTROYED: 194 case EVENT_ALL_DESTROYED: 195 case EVENT_CREDITS: 196 case EVENT_TIME: 197 case EVENT_NBUILDINGS_DESTROYED: 198 case EVENT_NUNITS_DESTROYED: 199 case EVENT_NOFACTORIES: 200 case EVENT_EVAC_CIVILIAN: 201 case EVENT_BUILD: 202 return(true); 203 } 204 return(false); 205 } 206 207 208 /*********************************************************************************************** 209 * TriggerClass::Event_Need_Data -- Determines if this event requires a data value. * 210 * * 211 * This routine will determine if the specified event requires a data number parameter. * 212 * This is commonly needed for trigger events. * 213 * * 214 * INPUT: event -- The event to examine. * 215 * * 216 * OUTPUT: Does the specified event require a data number parameter? * 217 * * 218 * WARNINGS: none * 219 * * 220 * HISTORY: * 221 * 08/27/1995 JLB : Created. * 222 *=============================================================================================*/ 223 bool TriggerClass::Event_Need_Data(EventType event) 224 { 225 switch (event) { 226 case EVENT_CREDITS: 227 case EVENT_TIME: 228 case EVENT_NBUILDINGS_DESTROYED: 229 case EVENT_NUNITS_DESTROYED: 230 case EVENT_BUILD: 231 return(true); 232 } 233 return(false); 234 } 235 236 237 /*********************************************************************************************** 238 * TriggerClass::Action_Need_Team -- Determines if this action event requires a team. * 239 * * 240 * This routine will determine if the specified action requires a team name parameter. * 241 * Typically, this is needed for reinforcements or other trigger events that affect * 242 * a particular team type. * 243 * * 244 * INPUT: action -- The action that is to be examined. * 245 * * 246 * OUTPUT: Does the specified action require a team type name? * 247 * * 248 * WARNINGS: none * 249 * * 250 * HISTORY: * 251 * 08/27/1995 JLB : Created. * 252 *=============================================================================================*/ 253 bool TriggerClass::Action_Need_Team(TriggerClass::ActionType action) 254 { 255 switch (action) { 256 case ACTION_CREATE_TEAM: 257 case ACTION_DESTROY_TEAM: 258 case ACTION_REINFORCEMENTS: 259 return(true); 260 } 261 return(false); 262 } 263 264 265 /*********************************************************************************************** 266 * TriggerClass::TriggerClass -- constructor * 267 * * 268 * INPUT: * 269 * none. * 270 * * 271 * OUTPUT: * 272 * none. * 273 * * 274 * WARNINGS: * 275 * none. * 276 * * 277 * HISTORY: * 278 * 11/28/1994 BR : Created. * 279 *=============================================================================================*/ 280 TriggerClass::TriggerClass(void) 281 { 282 IsPersistant = VOLATILE; 283 AttachCount = 0; 284 Event = EVENT_NONE; 285 Action = ACTION_NONE; 286 House = HOUSE_NONE; 287 DataCopy = Data = 0L; 288 Name[0] = '\0'; 289 Team = NULL; 290 } 291 292 293 /*********************************************************************************************** 294 * TriggerClass::~TriggerClass -- Destructor for trigger objects. * 295 * * 296 * This destructor will update the house blockage value if necessary. No other action need * 297 * be performed on trigger destruction. * 298 * * 299 * INPUT: none * 300 * * 301 * OUTPUT: none * 302 * * 303 * WARNINGS: none * 304 * * 305 * HISTORY: * 306 * 07/29/1995 JLB : Created. * 307 *=============================================================================================*/ 308 TriggerClass::~TriggerClass(void) 309 { 310 if (GameActive && House != HOUSE_NONE && Action == ACTION_ALLOWWIN) { 311 if (Houses.Ptr(House)->Blockage) Houses.Ptr(House)->Blockage--; 312 Houses.Ptr(House)->BorrowedTime = TICKS_PER_SECOND*4; 313 } 314 } 315 316 317 /*********************************************************************************************** 318 * TriggerClass::Init -- clears triggers for new scenario * 319 * * 320 * INPUT: * 321 * none. * 322 * * 323 * OUTPUT: * 324 * none. * 325 * * 326 * WARNINGS: * 327 * none. * 328 * * 329 * HISTORY: * 330 * 11/29/1994 BR : Created. * 331 *=============================================================================================*/ 332 void TriggerClass::Init(void) 333 { 334 Triggers.Free_All(); 335 } 336 337 338 /*********************************************************************************************** 339 * TriggerClass::Spring -- Trigger processing routine * 340 * * 341 * Checks whether this trigger should "spring" for the given event & object; * 342 * If it should, then some really cool undocumented stuff magically happens. * 343 * * 344 * INPUT: * 345 * event EventType: What happened? * 346 * object Ptr to object containing this trigger: What did it happen to? * 347 * * 348 * OUTPUT: * 349 * 0 = nothing happened; 1 = the trigger was sprung * 350 * * 351 * WARNINGS: * 352 * none. * 353 * * 354 * HISTORY: * 355 * 12/06/1994 BR : Created. * 356 * 06/25/1995 JLB : Added more trigger events. * 357 *=============================================================================================*/ 358 bool TriggerClass::Spring(EventType event, ObjectClass *obj) 359 { 360 Validate(); 361 /* 362 ** If this is not the event for this trigger, just return. 363 */ 364 if (event != Event && Event != EVENT_ANY) { 365 return(false); 366 } 367 368 /* 369 ** If time-based, decrement the minute counter; return if it's not time yet 370 */ 371 if (Event == EVENT_TIME) { 372 Data--; 373 if (Data > 0) { 374 return(false); 375 } 376 Data = DataCopy; 377 } 378 379 /* 380 ** Semi-persistant trigger: first detach it from the calling object, then 381 ** see if this is the last object we're attached to; if so, the trigger 382 ** will spring. 383 */ 384 if (IsPersistant == SEMIPERSISTANT) { 385 386 /* 387 ** Detach ourselves from the object 388 */ 389 obj->Trigger = NULL; 390 391 /* 392 ** Decrement our attachment counter 393 */ 394 AttachCount--; 395 396 /* 397 ** If we're attached to more objects, don't spring; otherwise, spring. 398 ** And, mark ourselves as volatile so we'll completely remove ourselves 399 ** from the game after we go off. 400 */ 401 if (AttachCount > 0) { 402 return(false); 403 } else { 404 IsPersistant = VOLATILE; 405 } 406 } 407 408 /* 409 ** Otherwise, take an appropriate action. 410 */ 411 bool success = true; 412 TriggerClass * trig = NULL; 413 switch (Action) { 414 case ACTION_NUKE: 415 HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false); 416 HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD); 417 break; 418 419 case ACTION_ION: 420 HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false); 421 HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD); 422 break; 423 424 case ACTION_WINLOSE: 425 switch (event) { 426 case EVENT_DESTROYED: 427 if (!PlayerPtr->IsToWin || PlayerPtr->Blockage > 0) PlayerPtr->Flag_To_Lose(); 428 success = true; 429 break; 430 431 case EVENT_PLAYER_ENTERED: 432 if (!PlayerPtr->IsToLose) PlayerPtr->Flag_To_Win(); 433 success = true; 434 break; 435 436 default: 437 success = false; 438 break; 439 } 440 break; 441 442 case ACTION_DESTROY_XXXX: 443 trig = As_Pointer("XXXX"); 444 if (trig) { 445 trig->Remove(); 446 } 447 delete trig; 448 break; 449 450 case ACTION_DESTROY_YYYY: 451 trig = As_Pointer("YYYY"); 452 if (trig) { 453 trig->Remove(); 454 } 455 delete trig; 456 break; 457 458 case ACTION_DESTROY_ZZZZ: 459 trig = As_Pointer("ZZZZ"); 460 if (trig) { 461 trig->Remove(); 462 } 463 delete trig; 464 break; 465 466 case ACTION_AIRSTRIKE: 467 PlayerPtr->IsAirstrikePending = true; 468 // PlayerPtr->Make_Air_Strike_Available(true); 469 break; 470 471 case ACTION_DZ: 472 new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25])); 473 break; 474 475 case ACTION_NONE: 476 break; 477 478 case ACTION_WIN: 479 PlayerPtr->Flag_To_Win(); 480 break; 481 482 case ACTION_LOSE: 483 PlayerPtr->Flag_To_Lose(); 484 break; 485 486 case ACTION_BEGIN_PRODUCTION: 487 HouseClass::As_Pointer(House)->Begin_Production(); 488 break; 489 490 case ACTION_AUTOCREATE: 491 if (obj && obj->Is_Techno()) { 492 ((TechnoClass *)obj)->House->IsAlerted = true; 493 } 494 break; 495 496 case ACTION_CREATE_TEAM: 497 if (Team) { 498 ScenarioInit++; 499 Team->Create_One_Of(); 500 ScenarioInit--; 501 } 502 break; 503 504 case ACTION_DESTROY_TEAM: 505 if (Team) { 506 Team->Destroy_All_Of(); 507 } 508 break; 509 510 case ACTION_REINFORCEMENTS: 511 if (Team) { 512 success = Do_Reinforcements(Team); 513 } 514 break; 515 516 case ACTION_ALL_HUNT: 517 Do_All_To_Hunt(); 518 break; 519 520 default: 521 break; 522 } 523 524 if (!success && Event == EVENT_TIME) Data = 1; 525 526 /* 527 ** Remove trigger from the game. 528 */ 529 if (success && IsPersistant == VOLATILE) { 530 Remove(); 531 } 532 533 return(true); 534 } 535 536 537 /*********************************************************************************************** 538 * TriggerClass::Spring -- Trigger processing routine * 539 * * 540 * This version of Spring is for cell-based triggers. * 541 * * 542 * INPUT: * 543 * data elapsed time, or credits, depending on what 'Event' is. * 544 * * 545 * OUTPUT: * 546 * 0 = nothing happened; 1 = the trigger was sprung * 547 * * 548 * WARNINGS: * 549 * none. * 550 * * 551 * HISTORY: * 552 * 12/06/1994 BR : Created. * 553 *=============================================================================================*/ 554 bool TriggerClass::Spring(EventType event, CELL cell) 555 { 556 Validate(); 557 /* 558 ** If this is not the event for this trigger, just return. 559 */ 560 if (event != Event) { 561 return(false); 562 } 563 564 /* 565 ** If time-based, decrement the minute counter; return if it's not time yet 566 */ 567 if (Event == EVENT_TIME) { 568 Data--; 569 if (Data > 0) { 570 return(false); 571 } 572 Data = DataCopy; 573 } 574 575 /* 576 ** Semi-persistant trigger: first detach it from the calling cell, then 577 ** see if this is the last cell we're attached to; if so, the trigger 578 ** will spring. 579 */ 580 if (IsPersistant == SEMIPERSISTANT) { 581 582 /* 583 ** Detach ourselves from the cell 584 */ 585 Map[cell].IsTrigger = 0; 586 587 /* 588 ** Decrement our attachment counter 589 */ 590 AttachCount--; 591 592 /* 593 ** If we're attached to more cells, don't spring; otherwise, spring. 594 ** And, mark ourselves as volatile so we'll completely remove ourselves 595 ** from the game after we go off. 596 */ 597 if (AttachCount > 0) { 598 return(false); 599 } else { 600 IsPersistant = VOLATILE; 601 } 602 } 603 604 /* 605 ** Otherwise, take an appropriate action. 606 */ 607 bool success = true; 608 TriggerClass * trig = NULL; 609 int index; 610 switch (Action) { 611 case ACTION_NUKE: 612 HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false); 613 HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD); 614 break; 615 616 case ACTION_ION: 617 HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false); 618 HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD); 619 break; 620 621 case ACTION_AUTOCREATE: 622 for (index = 0; index < Houses.Count(); index++) { 623 Houses.Ptr(index)->IsAlerted = true; 624 } 625 break; 626 627 case ACTION_DESTROY_XXXX: 628 trig = As_Pointer("XXXX"); 629 if (trig) { 630 trig->Remove(); 631 } 632 delete trig; 633 break; 634 635 case ACTION_DESTROY_YYYY: 636 trig = As_Pointer("YYYY"); 637 if (trig) { 638 trig->Remove(); 639 } 640 delete trig; 641 break; 642 643 case ACTION_DESTROY_ZZZZ: 644 trig = As_Pointer("ZZZZ"); 645 if (trig) { 646 trig->Remove(); 647 } 648 delete trig; 649 break; 650 651 case ACTION_AIRSTRIKE: 652 HouseClass::As_Pointer(House)->AirStrike.Enable(false, true); 653 if (House == PlayerPtr->Class->House) { 654 PlayerPtr->AirStrike.Forced_Charge(true); 655 Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); 656 Map.Column[1].Flag_To_Redraw(); 657 } 658 // PlayerPtr->Make_Air_Strike_Available(true); 659 break; 660 661 case ACTION_DZ: 662 new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25])); 663 break; 664 665 case ACTION_NONE: 666 break; 667 668 case ACTION_WIN: 669 PlayerPtr->Flag_To_Win(); 670 break; 671 672 case ACTION_LOSE: 673 PlayerPtr->Flag_To_Lose(); 674 break; 675 676 case ACTION_BEGIN_PRODUCTION: 677 if (PlayerPtr->Class->House == HOUSE_GOOD) { 678 HouseClass::As_Pointer(HOUSE_BAD)->Begin_Production(); 679 } else { 680 HouseClass::As_Pointer(HOUSE_GOOD)->Begin_Production(); 681 } 682 break; 683 684 case ACTION_CREATE_TEAM: 685 if (Team) { 686 ScenarioInit++; 687 Team->Create_One_Of(); 688 ScenarioInit--; 689 } 690 break; 691 692 case ACTION_DESTROY_TEAM: 693 if (Team) { 694 Team->Destroy_All_Of(); 695 } 696 break; 697 698 case ACTION_REINFORCEMENTS: 699 if (Team) { 700 success = Do_Reinforcements(Team); 701 } 702 break; 703 704 case ACTION_ALL_HUNT: 705 Do_All_To_Hunt(); 706 break; 707 708 default: 709 break; 710 } 711 712 if (!success && Event == EVENT_TIME) Data = 1; 713 714 /* 715 ** Remove trigger from the game. 716 */ 717 if (success && IsPersistant == VOLATILE) { 718 Remove(); 719 } 720 721 return(true); 722 } 723 724 725 /*********************************************************************************************** 726 * TriggerClass::Spring -- Trigger processing routine * 727 * * 728 * This version of Spring is for house-specific triggers. * 729 * For a time-based trigger, 'data' will the the current TickCount. * 730 * For a credit-based trigger, 'data' will be the credits for the HouseClass * 731 * containing this trigger. * 732 * * 733 * INPUT: * 734 * event the event that happened * 735 * house house that this event relates to * 736 * data elapsed time, or credits, depending on what 'Event' is. * 737 * * 738 * OUTPUT: * 739 * 0 = nothing happened; 1 = the trigger was sprung * 740 * * 741 * WARNINGS: * 742 * none. * 743 * * 744 * HISTORY: * 745 * 12/06/1994 BR : Created. * 746 * 06/25/1995 JLB : Added more trigger events. * 747 *=============================================================================================*/ 748 bool TriggerClass::Spring(EventType event, HousesType house, long data) 749 { 750 Validate(); 751 /* 752 ** If this is not the event for this trigger, just return. 753 */ 754 if (event != Event || house != House) { 755 return(false); 756 } 757 758 /* 759 ** If credits-based, check 'data' 760 */ 761 if (Event == EVENT_CREDITS && data < Data) { 762 return(false); 763 } 764 765 /* 766 ** Building event check to ensure that the building number matches. 767 */ 768 if (Event == EVENT_BUILD && data != Data) { 769 return(false); 770 } 771 772 /* 773 ** Number of objects destroyed checker. If the data supplied indicates that 774 ** the correct number of objects have been destroyed, then this trigger 775 ** will succeed. 776 */ 777 if (Event == EVENT_NBUILDINGS_DESTROYED || Event == EVENT_NUNITS_DESTROYED) { 778 if (data < Data) { 779 return(false); 780 } 781 } 782 783 /* 784 ** If time-based, decrement the minute counter; return if it's not time yet 785 */ 786 if (Event == EVENT_TIME) { 787 Data--; 788 if (Data > 0) { 789 return(false); 790 } 791 Data = DataCopy; 792 } 793 794 /* 795 ** The trigger has gone off; take appropriate action 796 */ 797 bool success = true; 798 TriggerClass * trig = NULL; 799 switch (Action) { 800 801 case ACTION_NUKE: 802 HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false); 803 HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD); 804 break; 805 806 case ACTION_ION: 807 HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false); 808 HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD); 809 break; 810 811 /* 812 ** This will remove a blockage to the win condition. No action need 813 ** be performed here since the act of deleting the trigger will 814 ** remove the blockage. 815 */ 816 case ACTION_ALLOWWIN: 817 break; 818 819 case ACTION_AUTOCREATE: 820 HouseClass::As_Pointer(House)->IsAlerted = true; 821 break; 822 823 case ACTION_DESTROY_XXXX: 824 trig = As_Pointer("XXXX"); 825 if (trig) { 826 trig->Remove(); 827 } 828 delete trig; 829 break; 830 831 case ACTION_DESTROY_YYYY: 832 trig = As_Pointer("YYYY"); 833 if (trig) { 834 trig->Remove(); 835 } 836 delete trig; 837 break; 838 839 case ACTION_DESTROY_ZZZZ: 840 trig = As_Pointer("ZZZZ"); 841 if (trig) { 842 trig->Remove(); 843 } 844 delete trig; 845 break; 846 847 case ACTION_AIRSTRIKE: 848 PlayerPtr->AirStrike.Enable(false, true); 849 if (House == PlayerPtr->Class->House) { 850 PlayerPtr->AirStrike.Forced_Charge(true); 851 Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); 852 Map.Column[1].Flag_To_Redraw(); 853 } 854 break; 855 856 case ACTION_NONE: 857 break; 858 859 case ACTION_DZ: 860 new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25])); 861 break; 862 863 case ACTION_WIN: 864 PlayerPtr->Flag_To_Win(); 865 break; 866 867 case ACTION_LOSE: 868 PlayerPtr->Flag_To_Lose(); 869 break; 870 871 case ACTION_BEGIN_PRODUCTION: 872 HouseClass::As_Pointer(House)->Begin_Production(); 873 break; 874 875 case ACTION_CREATE_TEAM: 876 if (Team) { 877 ScenarioInit++; 878 Team->Create_One_Of(); 879 ScenarioInit--; 880 } 881 break; 882 883 case ACTION_DESTROY_TEAM: 884 if (Team) { 885 Team->Destroy_All_Of(); 886 } 887 break; 888 889 case ACTION_REINFORCEMENTS: 890 if (Team) { 891 success = Do_Reinforcements(Team); 892 } 893 break; 894 895 case ACTION_ALL_HUNT: 896 Do_All_To_Hunt(); 897 break; 898 899 default: 900 break; 901 } 902 903 if (!success && Event == EVENT_TIME) Data = 1; 904 905 /* 906 ** Remove trigger from the game. 907 */ 908 if (success && IsPersistant == VOLATILE) { 909 Remove(); 910 } 911 912 return(true); 913 } 914 915 916 /*********************************************************************************************** 917 * TriggerClass::Remove -- removes this trigger from the game * 918 * * 919 * INPUT: * 920 * none. * 921 * * 922 * OUTPUT: * 923 * 1 = trigger was removed, 0 = it wasn't * 924 * * 925 * WARNINGS: * 926 * none. * 927 * * 928 * HISTORY: * 929 * 12/06/1994 BR : Created. * 930 *=============================================================================================*/ 931 bool TriggerClass::Remove(void) 932 { 933 Validate(); 934 CELL cell; 935 HousesType h; 936 int index; 937 938 /* 939 ** Loop through all cells; remove any reference to this trigger 940 */ 941 for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { 942 if (Map[cell].IsTrigger) { 943 if (CellTriggers[cell] == this) { 944 Map[cell].IsTrigger = 0; 945 CellTriggers[cell] = NULL; 946 } 947 } 948 } 949 950 /* 951 ** Loop through all objects, removing any reference to this trigger 952 */ 953 for (index = 0; index < Infantry.Count(); index++) { 954 if (Infantry.Ptr(index)->Trigger == this) { 955 Infantry.Ptr(index)->Trigger = NULL; 956 } 957 } 958 for (index = 0; index < Buildings.Count(); index++) { 959 if (Buildings.Ptr(index)->Trigger == this) { 960 Buildings.Ptr(index)->Trigger = NULL; 961 } 962 } 963 for (index = 0; index < Units.Count(); index++) { 964 if (Units.Ptr(index)->Trigger == this) { 965 Units.Ptr(index)->Trigger = NULL; 966 } 967 } 968 for (index = 0; index < Terrains.Count(); index++) { 969 if (Terrains.Ptr(index)->Trigger == this) { 970 Terrains.Ptr(index)->Trigger = NULL; 971 } 972 } 973 974 /* 975 ** Remove this trigger from any house list it's in. Invoking '-=' with a 976 ** pointer not in the list has no effect; loop through all houses just to 977 ** be on the safe side. 978 */ 979 for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { 980 HouseTriggers[h].Delete(this); 981 } 982 983 delete this; 984 985 return(true); 986 } 987 988 989 /*********************************************************************************************** 990 * TriggerClass::Read_INI -- reads triggers from the INI file * 991 * * 992 * INI entry format: * 993 * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * 994 * * 995 * This routine reads in the triggers & creates them. Then, other classes can * 996 * get pointers to the triggers they're linked to. * 997 * * 998 * The routine relies on the TeamTypeClasses already being loaded so it can resolve * 999 * references to teams in this function. * 1000 * * 1001 * Cell Trigger pointers & IsTrigger flags are set in DisplayClass::Read_INI(), * 1002 * and cleared in the Map::Init() routine (which clears all cell objects to 0's). * 1003 * * 1004 * Object's pointers are set in: * 1005 * InfantryClass::Read_INI() * 1006 * BuildingClass::Read_INI() * 1007 * UnitClass::Read_INI() * 1008 * TerrainClass::Read_INI() * 1009 * The object trigger pointers are cleared in the ObjectClass constructor. * 1010 * * 1011 * The House's EMSListOf triggers is set in this routine, and cleared in the * 1012 * HouseClass::Init() routine. * 1013 * * 1014 * INPUT: * 1015 * buffer buffer to hold the INI data * 1016 * * 1017 * OUTPUT: * 1018 * none. * 1019 * * 1020 * WARNINGS: * 1021 * This function must be called before any other class's Read_INI. * 1022 * * 1023 * HISTORY: * 1024 * 11/28/1994 BR : Created. * 1025 *=============================================================================================*/ 1026 void TriggerClass::Read_INI(char *buffer) 1027 { 1028 TriggerClass *trigger; // Working trigger pointer. 1029 char *tbuffer; // Accumulation buffer of trigger IDs. 1030 int len; // Length of data in buffer. 1031 char buf[128]; 1032 1033 /* 1034 ** Set 'tbuffer' to point just past the INI buffer 1035 */ 1036 len = strlen(buffer) + 2; 1037 tbuffer = buffer + len; 1038 1039 /* 1040 ** Read all TRIGGER entry names into 'tbuffer' 1041 */ 1042 WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); 1043 1044 /* 1045 ** Loop for all trigger entries. 1046 */ 1047 while (*tbuffer != '\0') { 1048 1049 /* 1050 ** Create a new trigger. 1051 */ 1052 trigger = new TriggerClass(); 1053 1054 /* 1055 ** Set its name. 1056 */ 1057 trigger->Set_Name (tbuffer); 1058 1059 /* 1060 ** Get the trigger entry. 1061 */ 1062 WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); 1063 1064 /* 1065 ** Fill in the trigger. 1066 */ 1067 trigger->Fill_In(tbuffer,buf); 1068 1069 /* 1070 ** Add 'trigger' to the House's list. 1071 */ 1072 // if (trigger->House != HOUSE_NONE && trigger->Event != EVENT_PLAYER_ENTERED) { 1073 // if (Event_Need_House(trigger->Event) && !Event_Need_Object(trigger->Event)) { 1074 if (trigger->House != HOUSE_NONE) { 1075 if (trigger->Action == ACTION_ALLOWWIN) HouseClass::As_Pointer(trigger->House)->Blockage++; 1076 HouseTriggers[trigger->House].Add(trigger); 1077 trigger->AttachCount++; 1078 } 1079 1080 /* 1081 ** Go to next entry. 1082 */ 1083 tbuffer += strlen(tbuffer)+1; 1084 } 1085 } 1086 1087 1088 /*********************************************************************************************** 1089 * TriggerClass::Fill_In -- fills in trigger from the given INI entry * 1090 * * 1091 * This routine fills in the given trigger with the given name, and values from * 1092 * the given INI entry. * 1093 * * 1094 * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * 1095 * * 1096 * INI entry format: * 1097 * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * 1098 * * 1099 * INPUT: * 1100 * name mnemonic for the desired trigger * 1101 * entry INI entry to parse * 1102 * * 1103 * OUTPUT: * 1104 * none. * 1105 * * 1106 * WARNINGS: * 1107 * none. * 1108 * * 1109 * HISTORY: * 1110 * 11/28/1994 BR : Created. * 1111 *=============================================================================================*/ 1112 void TriggerClass::Fill_In(char * name, char *entry) 1113 { 1114 Validate(); 1115 char *p; 1116 1117 /* 1118 ** Set its name. 1119 */ 1120 Set_Name(name); 1121 1122 /* 1123 ** 1st token: Event. 1124 */ 1125 Event = Event_From_Name(strtok(entry, ",")); 1126 1127 /* 1128 ** 2nd token: Action. 1129 */ 1130 Action = Action_From_Name(strtok(NULL, ",")); 1131 1132 /* 1133 ** 3rd token: Data. 1134 */ 1135 DataCopy = Data = atol(strtok(NULL, ",")); 1136 1137 /* 1138 ** 4th token: House. 1139 */ 1140 House = HouseTypeClass::From_Name(strtok(NULL, ",")); 1141 if (House == HOUSE_NONE && Event == EVENT_PLAYER_ENTERED) { 1142 House = PlayerPtr->Class->House; 1143 } 1144 1145 /* 1146 ** 5th token: Team. 1147 */ 1148 Team = TeamTypeClass::As_Pointer(strtok(NULL, ",")); 1149 1150 /* 1151 ** 6th token: IsPersistant. This token was added later, so we must check 1152 ** for its existence. 1153 */ 1154 p = strtok(NULL, ","); 1155 if (p) { 1156 IsPersistant = (PersistantType)atoi(p); 1157 } else { 1158 IsPersistant = VOLATILE; 1159 } 1160 } 1161 1162 1163 /*********************************************************************************************** 1164 * TriggerClass::Write_INI -- writes triggers to the INI file * 1165 * * 1166 * INI entry format: * 1167 * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * 1168 * * 1169 * INPUT: * 1170 * buffer buffer to hold the INI data * 1171 * * 1172 * OUTPUT: * 1173 * none. * 1174 * * 1175 * WARNINGS: * 1176 * none. * 1177 * * 1178 * HISTORY: * 1179 * 11/28/1994 BR : Created. * 1180 *=============================================================================================*/ 1181 void TriggerClass::Write_INI(char *buffer, bool refresh) 1182 { 1183 int index; 1184 char buf[128]; 1185 TriggerClass *trigger; 1186 char const *hname; 1187 char const *tname; 1188 1189 /* 1190 ** First, clear out all existing trigger data from the INI file. 1191 */ 1192 if (refresh) { 1193 WWWritePrivateProfileString(INI_Name(), NULL, NULL, buffer); 1194 } 1195 1196 /* 1197 ** Now write all the trigger data out 1198 */ 1199 for (index = 0; index < Triggers.Count(); index++) { 1200 1201 /* 1202 ** Get ptr to next active trigger. 1203 */ 1204 trigger = Triggers.Ptr(index); 1205 1206 /* 1207 ** Generate INI entry. 1208 */ 1209 if (trigger->House==HOUSE_NONE) { 1210 hname = "None"; 1211 } else { 1212 hname = HouseClass::As_Pointer(trigger->House)->Class->IniName; 1213 } 1214 1215 if (trigger->Team==NULL) { 1216 tname = "None"; 1217 } else { 1218 tname = trigger->Team->IniName; 1219 } 1220 1221 sprintf(buf,"%s,%s,%ld,%s,%s,%d", 1222 TriggerClass::Name_From_Event(trigger->Event), 1223 TriggerClass::Name_From_Action(trigger->Action), 1224 trigger->Data, 1225 hname, 1226 tname, 1227 trigger->IsPersistant); 1228 WWWritePrivateProfileString(INI_Name(), trigger->Get_Name(), buf, buffer); 1229 } 1230 } 1231 1232 1233 /*********************************************************************************************** 1234 * TriggerClass::As_Pointer -- returns pointer for the given trigger name * 1235 * * 1236 * This function is designed for use at initialization time, ie when the * 1237 * trigger mnemonics are read from the INI and the Read_INI functions need * 1238 * to get a pointer to that trigger. * 1239 * * 1240 * INPUT: * 1241 * name mnemonic for the desired trigger * 1242 * * 1243 * OUTPUT: * 1244 * near pointer to that trigger, NULL if not found * 1245 * * 1246 * WARNINGS: * 1247 * none. * 1248 * * 1249 * HISTORY: * 1250 * 11/28/1994 BR : Created. * 1251 *=============================================================================================*/ 1252 TriggerClass * TriggerClass::As_Pointer(char const * name) 1253 { 1254 if (name == NULL) { 1255 return(NULL); 1256 } 1257 1258 for (int i = 0; i < Triggers.Count(); i++) { 1259 TriggerClass * trigger = Triggers.Ptr(i); 1260 1261 if (!stricmp(name, trigger->Name)) { 1262 return(trigger); 1263 } 1264 } 1265 1266 return(NULL); 1267 } 1268 1269 1270 /*********************************************************************************************** 1271 * TriggerClass::operator new -- 'new' operator * 1272 * * 1273 * INPUT: * 1274 * none. * 1275 * * 1276 * OUTPUT: * 1277 * pointer to new trigger * 1278 * * 1279 * WARNINGS: * 1280 * none. * 1281 * * 1282 * HISTORY: * 1283 * 11/28/1994 BR : Created. * 1284 *=============================================================================================*/ 1285 void * TriggerClass::operator new(size_t ) 1286 { 1287 void * ptr = Triggers.Allocate(); 1288 if (ptr) { 1289 ((TriggerClass *)ptr)->IsActive = true; 1290 } 1291 return(ptr); 1292 } 1293 1294 1295 /*********************************************************************************************** 1296 * TriggerClass::operator delete -- 'delete' operator * 1297 * * 1298 * INPUT: * 1299 * ptr pointer to delete * 1300 * * 1301 * OUTPUT: * 1302 * none. * 1303 * * 1304 * WARNINGS: * 1305 * none. * 1306 * * 1307 * HISTORY: * 1308 * 11/28/1994 BR : Created. * 1309 *=============================================================================================*/ 1310 void TriggerClass::operator delete(void *ptr) 1311 { 1312 if (ptr) { 1313 ((TriggerClass *)ptr)->IsActive = false; 1314 } 1315 Triggers.Free((TriggerClass *)ptr); 1316 } 1317 1318 1319 /*********************************************************************************************** 1320 * TriggerClass::Event_From_Name -- retrieves EventType for given name * 1321 * * 1322 * INPUT: * 1323 * name name to get event for * 1324 * * 1325 * OUTPUT: * 1326 * EventType for given name * 1327 * * 1328 * WARNINGS: * 1329 * none. * 1330 * * 1331 * HISTORY: * 1332 * 11/29/1994 BR : Created. * 1333 *=============================================================================================*/ 1334 EventType TriggerClass::Event_From_Name (char const *name) 1335 { 1336 int i; 1337 1338 if (name == NULL) { 1339 return(EVENT_NONE); 1340 } 1341 1342 for (i = EVENT_NONE; i < EVENT_COUNT; i++) { 1343 if (!stricmp(name,EventText[i + 1])) { 1344 return((EventType)i); 1345 } 1346 } 1347 1348 return(EVENT_NONE); 1349 } 1350 1351 1352 /*********************************************************************************************** 1353 * TriggerClass::Name_From_Event -- retrieves name for EventType * 1354 * * 1355 * INPUT: * 1356 * event EventType to get name for * 1357 * * 1358 * OUTPUT: * 1359 * name for EventType * 1360 * * 1361 * WARNINGS: * 1362 * none. * 1363 * * 1364 * HISTORY: * 1365 * 11/29/1994 BR : Created. * 1366 *=============================================================================================*/ 1367 char const *TriggerClass::Name_From_Event(EventType event) 1368 { 1369 return(EventText[event + 1]); 1370 } 1371 1372 1373 /*********************************************************************************************** 1374 * TriggerClass::Action_From_Name -- retrieves ActionType for given name * 1375 * * 1376 * INPUT: * 1377 * name name to get ActionType for * 1378 * * 1379 * OUTPUT: * 1380 * ActionType for given name * 1381 * * 1382 * WARNINGS: * 1383 * none. * 1384 * * 1385 * HISTORY: * 1386 * 11/29/1994 BR : Created. * 1387 *=============================================================================================*/ 1388 TriggerClass::ActionType TriggerClass::Action_From_Name (char const *name) 1389 { 1390 int i; 1391 1392 if (name == NULL) { 1393 return(ACTION_NONE); 1394 } 1395 1396 for (i = ACTION_NONE; i < ACTION_COUNT; i++) { 1397 if (!stricmp(name,ActionText[i + 1])) { 1398 return((ActionType)i); 1399 } 1400 } 1401 1402 return(ACTION_NONE); 1403 } 1404 1405 1406 /*********************************************************************************************** 1407 * TriggerClass::Name_From_Action -- retrieves name for ActionType * 1408 * * 1409 * INPUT: * 1410 * action ActionType to get name for * 1411 * * 1412 * OUTPUT: * 1413 * name of ActionType * 1414 * * 1415 * WARNINGS: * 1416 * none. * 1417 * * 1418 * HISTORY: * 1419 * 11/29/1994 BR : Created. * 1420 *=============================================================================================*/ 1421 char const *TriggerClass::Name_From_Action(ActionType action) 1422 { 1423 return(ActionText[action + 1]); 1424 } 1425 1426 1427 /*********************************************************************************************** 1428 * TriggerClass::As_Target -- Converts trigger to a target value * 1429 * * 1430 * INPUT: none * 1431 * * 1432 * OUTPUT: TARGET value * 1433 * * 1434 * WARNINGS: none * 1435 * * 1436 * HISTORY: * 1437 * 09/19/1994 JLB : Created. * 1438 *=============================================================================================*/ 1439 TARGET TriggerClass::As_Target(void) const 1440 { 1441 Validate(); 1442 return(Build_Target(KIND_TRIGGER, Triggers.ID(this))); 1443 } 1444 1445 1446 /*********************************************************************************************** 1447 * Do_All_To_Hunt -- Forces all computer controlled units into hunt mode. * 1448 * * 1449 * This trigger action will cause the computer units and infantry to go into hunt mode. * 1450 * Use it to bring a scenario to a sudden conclusion. * 1451 * * 1452 * INPUT: none * 1453 * * 1454 * OUTPUT: none * 1455 * * 1456 * WARNINGS: none * 1457 * * 1458 * HISTORY: * 1459 * 04/20/1995 JLB : Created. * 1460 * 08/14/1995 JLB : Removes the member from a team if necessary. * 1461 *=============================================================================================*/ 1462 static void Do_All_To_Hunt(void) 1463 { 1464 int index; 1465 1466 for (index = 0; index < Units.Count(); index++) { 1467 UnitClass * unit = Units.Ptr(index); 1468 1469 if (!unit->House->IsHuman && unit->IsDown && !unit->IsInLimbo) { 1470 if (unit->Team) unit->Team->Remove(unit); 1471 unit->Assign_Mission(MISSION_HUNT); 1472 } 1473 } 1474 1475 for (index = 0; index < Infantry.Count(); index++) { 1476 InfantryClass * infantry = Infantry.Ptr(index); 1477 1478 if (!infantry->House->IsHuman && infantry->IsDown && !infantry->IsInLimbo) { 1479 if (infantry->Team) infantry->Team->Remove(infantry); 1480 infantry->Assign_Mission(MISSION_HUNT); 1481 } 1482 } 1483 }