ANIM.CPP (60712B)
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/ANIM.CPP 1 3/03/97 10:24a 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 : Dune * 22 * * 23 * File Name : ANIM.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : June 3, 1991 * 28 * * 29 *---------------------------------------------------------------------------------------------* 30 * Functions: * 31 * AnimClass::AI -- This is the low level anim processor. * 32 * AnimClass::AnimClass -- The constructor for animation objects. * 33 * AnimClass::Attach_To -- Attaches animation to object specified. * 34 * AnimClass::Sort_Above -- Sorts the animation above the target specified. * 35 * AnimClass::Center_Coord -- Determine center of animation. * 36 * AnimClass::Detach -- Remove animation if attached to target. * 37 * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. * 38 * AnimClass::Draw_It -- Draws the animation at the location specified. * 39 * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * 40 * AnimClass::Init -- Performs pre-scenario initialization. * 41 * AnimClass::Mark -- Signals to map that redrawing is necessary. * 42 * AnimClass::Middle -- Processes any middle events. * 43 * AnimClass::Occupy_List -- Determines the occupy list for the animation. * 44 * AnimClass::Overlap_List -- Determines the overlap list for the animation. * 45 * AnimClass::Render -- Draws an animation object. * 46 * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * 47 * AnimClass::Start -- Processes initial animation side effects. * 48 * AnimClass::delete -- Returns an anim object back to the free pool. * 49 * AnimClass::new -- Allocates an anim object from the pool. * 50 * AnimClass::~AnimClass -- Destructor for anim objects. * 51 * Anim_From_Name -- Given a name, this finds the corresponding anim type. * 52 * Shorten_Attached_Anims -- Reduces attached animation durations. * 53 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 54 55 #include "function.h" 56 #define VIC 1 57 58 /*********************************************************************************************** 59 * Anim_From_Name -- Given a name, this finds the corresponding anim type. * 60 * * 61 * This routine will convert the supplied ASCII name into the animation type that it * 62 * represents. * 63 * * 64 * INPUT: name -- Pointer to the ASCII name to convert. * 65 * * 66 * OUTPUT: Returns with the animation type that matches the name specified. If no match could * 67 * be found, then ANIM_NONE is returned. * 68 * * 69 * WARNINGS: none * 70 * * 71 * HISTORY: * 72 * 07/06/1996 JLB : Created. * 73 *=============================================================================================*/ 74 AnimType Anim_From_Name(char const * name) 75 { 76 #ifdef VIC 77 if (name == NULL) return(ANIM_NONE); 78 79 for (int anim = ANIM_FIRST; anim < ANIM_COUNT; anim++) { 80 if (stricmp(AnimTypeClass::As_Reference((AnimType)anim).IniName, name) == 0) { 81 return((AnimType)anim); 82 } 83 } 84 #endif 85 return(ANIM_NONE); 86 } 87 88 89 /*********************************************************************************************** 90 * Shorten_Attached_Anims -- Reduces attached animation durations. * 91 * * 92 * This routine is used to reduce the amount of time any attached animations will process. * 93 * Typical use of this is when an object is on fire and the object should now be destroyed * 94 * but the attached animations are to run until completion before destruction can follow. * 95 * This routine will make the animation appear to run its course, but in as short of time * 96 * as possible. The shortening effect is achieved by reducing the number of times the * 97 * animation will loop. * 98 * * 99 * INPUT: obj -- Pointer to the object that all attached animations will be processed. * 100 * * 101 * OUTPUT: none * 102 * * 103 * WARNINGS: none * 104 * * 105 * HISTORY: * 106 * 12/11/1994 JLB : Created. * 107 *=============================================================================================*/ 108 void Shorten_Attached_Anims(ObjectClass * obj) 109 { 110 if (obj != NULL) { 111 for (int index = 0; index < Anims.Count(); index++) { 112 AnimClass & anim = * Anims.Ptr(index); 113 114 if (As_Object(anim.xObject) == obj) { 115 anim.Loops = 0; 116 } 117 } 118 } 119 } 120 121 122 /*********************************************************************************************** 123 * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * 124 * * 125 * This routine is used by the sorting system. Animations that are located in the ground * 126 * layer will be sorted by this the value returned from this function. * 127 * * 128 * INPUT: none * 129 * * 130 * OUTPUT: Returns with the sort coordinate to use for this animation. * 131 * * 132 * WARNINGS: none * 133 * * 134 * HISTORY: * 135 * 10/17/1994 JLB : Created. * 136 * 12/15/1994 JLB : Handles flat anims (infantry decay anims). * 137 *=============================================================================================*/ 138 COORDINATE AnimClass::Sort_Y(void) const 139 { 140 #ifdef VIC 141 assert(Anims.ID(this) == ID); 142 assert(IsActive); 143 144 if (xObject != TARGET_NONE) { 145 return(Coord_Add(As_Object(xObject)->Sort_Y(), 0x00010000L)); 146 } 147 if (Target_Legal(SortTarget)) { 148 ObjectClass * obj = As_Object(SortTarget); 149 if (obj && obj->IsActive) { 150 return(Coord_Add(obj->Sort_Y(), 0x00010000L)); 151 } 152 } 153 if (*this == ANIM_MOVE_FLASH) { 154 return(Coord_Add(Center_Coord(), XYP_COORD(0, -24))); 155 } 156 if (Class->IsGroundLayer || *this == ANIM_LZ_SMOKE) { 157 return(Coord_Add(Center_Coord(), XYP_COORD(0, 14))); 158 } 159 #endif 160 return(Coord); 161 } 162 163 164 /*********************************************************************************************** 165 * AnimClass::Center_Coord -- Determine center of animation. * 166 * * 167 * This support function will return the "center" of the animation. The actual coordinate * 168 * of the animation may be dependant on if the the animation is attached to an object. * 169 * In such a case, it must factor in the object's location. * 170 * * 171 * INPUT: none * 172 * * 173 * OUTPUT: Returns the coordinate of the center of the animation. The coordinate is in real * 174 * game coordinates -- taking into consideration if the animation is attached. * 175 * * 176 * WARNINGS: none * 177 * * 178 * HISTORY: * 179 * 09/19/1994 JLB : Created. * 180 * 02/02/1996 JLB : Coordinate based on visual center of object. * 181 *=============================================================================================*/ 182 COORDINATE AnimClass::Center_Coord(void) const 183 { 184 #ifdef VIC 185 assert(Anims.ID(this) == ID); 186 assert(IsActive); 187 188 if (xObject != TARGET_NONE) { 189 return(Coord_Add(Coord, As_Object(xObject)->Target_Coord())); 190 } 191 #endif 192 return(Coord); 193 } 194 195 196 /*********************************************************************************************** 197 * AnimClass::Render -- Draws an animation object. * 198 * * 199 * This is the working routine that renders the animation shape. It gets called once * 200 * per animation per frame. It needs to be fast. * 201 * * 202 * INPUT: bool; Should the animation be rendered in spite of render flag? * 203 * * 204 * OUTPUT: bool; Was the animation rendered? * 205 * * 206 * WARNINGS: none * 207 * * 208 * HISTORY: * 209 * 05/31/1994 JLB : Created. * 210 *=============================================================================================*/ 211 bool AnimClass::Render(bool forced) const 212 { 213 #ifdef VIC 214 assert(Anims.ID(this) == ID); 215 assert(IsActive); 216 217 if (Delay) return(false); 218 if (Map[Center_Coord()].IsVisible) { 219 const_cast<AnimClass*>(this)->IsToDisplay = true; // const_cast. ST - 5/8/2019 220 } 221 #endif 222 return(ObjectClass::Render(forced)); 223 } 224 225 226 /*********************************************************************************************** 227 * AnimClass::Draw_It -- Draws the animation at the location specified. * 228 * * 229 * This routine is used to render the animation object at the location specified. This is * 230 * how the map imagery gets updated. * 231 * * 232 * INPUT: x,y -- The pixel coordinates to draw the animation at. * 233 * * 234 * window -- The to base the draw coordinates upon. * 235 * * 236 * OUTPUT: none * 237 * * 238 * WARNINGS: none * 239 * * 240 * HISTORY: * 241 * 09/24/1994 JLB : Created. * 242 * 05/19/1995 JLB : Added white translucent effect. * 243 *=============================================================================================*/ 244 void AnimClass::Draw_It(int x, int y, WindowNumberType window) const 245 { 246 #ifdef VIC 247 assert(Anims.ID(this) == ID); 248 assert(IsActive); 249 250 bool render_legacy = !IsInvisible && (Class->VirtualAnim == ANIM_NONE || window != WINDOW_VIRTUAL); 251 bool render_virtual = Target_Legal(VirtualAnimTarget) && window == WINDOW_VIRTUAL; 252 253 if (render_legacy) { 254 BStart(BENCH_ANIMS); 255 256 IsTheaterShape = Class->IsTheater; 257 258 void const * shapefile = Get_Image_Data(); 259 if (shapefile != NULL) { 260 void const * transtable = NULL; 261 int shapenum = Class->Start + Fetch_Stage(); 262 void const * remap = NULL; 263 ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; 264 bool alt = false; 265 int width = 0; 266 int height = 0; 267 268 /* 269 ** Some animations require special fixups. 270 */ 271 switch (Class->Type) { 272 case ANIM_ATOM_BLAST: 273 transtable = Map.UnitShadow; 274 break; 275 276 case ANIM_FLAG: 277 x += (ICON_PIXEL_W / 2) - 2; 278 y += (3 * ICON_PIXEL_H / 4) - Get_Build_Frame_Height(shapefile); 279 transtable = Map.UnitShadow; 280 alt = true; 281 break; 282 283 case ANIM_BEACON_VIRTUAL: 284 width = 29; 285 height = 39; 286 flags = flags | SHAPE_BOTTOM | SHAPE_COMPACT; 287 break; 288 } 289 290 /* 291 ** If the translucent table hasn't been determined yet, then check to see if it 292 ** should use the white or normal translucent tables. 293 */ 294 if (transtable == NULL && Class->IsWhiteTrans) transtable = DisplayClass::WhiteTranslucentTable; 295 if (transtable == NULL && Class->IsTranslucent) transtable = DisplayClass::TranslucentTable; 296 297 /* 298 ** Set the shape flags to properly take into account any fading or ghosting 299 ** table necessary. 300 */ 301 if (alt) { 302 flags = flags | SHAPE_FADING; 303 if (OwnerHouse != HOUSE_NONE) { 304 remap = HouseClass::As_Pointer(OwnerHouse)->Remap_Table(false); 305 } else { 306 remap = ColorRemaps[PCOLOR_GOLD].RemapTable; 307 } 308 } 309 if (transtable != NULL) flags = flags | SHAPE_GHOST; 310 311 /* 312 ** Draw the animation shape, but ignore legacy if beyond normal stage count. 313 */ 314 if ((window == WINDOW_VIRTUAL) || (Fetch_Stage() < Class->Stages)) { 315 // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 316 CC_Draw_Shape(this, shapefile, shapenum, x, y, window, flags, remap, transtable, DIR_N, Class->VirtualScale, width, height); 317 } 318 } 319 IsTheaterShape = false; 320 BEnd(BENCH_ANIMS); 321 } 322 if (render_virtual) { 323 AnimClass* virtual_anim = As_Animation(VirtualAnimTarget); 324 virtual_anim->Make_Visible(); 325 virtual_anim->Draw_It(x, y, window); 326 virtual_anim->Make_Invisible(); 327 } 328 #endif 329 } 330 331 332 /*********************************************************************************************** 333 * AnimClass::Mark -- Signals to map that redrawing is necessary. * 334 * * 335 * This routine is used by the animation logic system to inform the map that the cells * 336 * under the animation must be rerendered. * 337 * * 338 * INPUT: * 339 * * 340 * OUTPUT: none * 341 * * 342 * WARNINGS: none * 343 * * 344 * HISTORY: * 345 * 05/31/1994 JLB : Created. * 346 *=============================================================================================*/ 347 bool AnimClass::Mark(MarkType mark) 348 { 349 #ifdef VIC 350 assert(Anims.ID(this) == ID); 351 assert(IsActive); 352 353 if (ObjectClass::Mark(mark)) { 354 Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); 355 // ObjectClass::Mark(mark); 356 return(true); 357 } 358 #endif 359 return(false); 360 } 361 362 363 /*********************************************************************************************** 364 * AnimClass::Overlap_List -- Determines the overlap list for the animation. * 365 * * 366 * Use this routine to fetch the overlap list for the animation. This overlap list is the * 367 * cells that this animation spills over. * 368 * * 369 * INPUT: none * 370 * * 371 * OUTPUT: Returns a pointer to the overlap list for this particular instance of the * 372 * animation. * 373 * * 374 * WARNINGS: none * 375 * * 376 * HISTORY: * 377 * 03/19/1995 JLB : Created. * 378 *=============================================================================================*/ 379 short const * AnimClass::Overlap_List(void) const 380 { 381 #ifdef VIC 382 assert(Anims.ID(this) == ID); 383 assert(IsActive); 384 static short const OverlapAtom[] = { 385 (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1, 386 (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1, 387 (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1, 388 ( MAP_CELL_W * 1) - 1, ( MAP_CELL_W * 1), ( MAP_CELL_W * 1) + 1, 389 ( MAP_CELL_W * 2) - 1, ( MAP_CELL_W * 2), ( MAP_CELL_W * 2) + 1, 390 REFRESH_EOL 391 }; 392 static short const OverlapFlag[] = { 0, 1, -MAP_CELL_W, -(MAP_CELL_W-1), MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL }; 393 394 if (IsToDelete) { 395 static short const _list[] = {REFRESH_EOL}; 396 return(_list); 397 } 398 399 if (Class->Type == ANIM_ATOM_BLAST) { 400 return(OverlapAtom); 401 } 402 403 if (Class->Type == ANIM_FLAG) { 404 return(OverlapFlag); 405 } 406 407 #ifdef PARTIAL 408 IsTheaterShape = Class->IsTheater; 409 if (Class->Get_Image_Data() != NULL) { 410 int shapenum = Class->Start + Fetch_Stage(); 411 int count = Get_Build_Frame_Count(Class->Get_Image_Data()); 412 shapenum = min(shapenum, count-1); 413 414 if (Class->DimensionData == NULL) { 415 Class->DimensionData = new Rect [count]; 416 } 417 if (Class->DimensionData != NULL && !Class->DimensionData[shapenum].Is_Valid()) { 418 Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum); 419 IsTheaterShape = false; 420 return(Coord_Spillage_List(Center_Coord(), Class->DimensionData[shapenum])); 421 } 422 } 423 IsTheaterShape = false; 424 #endif 425 #endif 426 return(Coord_Spillage_List(Center_Coord(), Class->Size)); 427 } 428 429 430 /*********************************************************************************************** 431 * AnimClass::Occupy_List -- Determines the occupy list for the animation. * 432 * * 433 * Animations always occupy only the cell that their center is located over. As such, this * 434 * routine always returns a simple (center cell) occupation list. * 435 * * 436 * INPUT: none * 437 * * 438 * OUTPUT: Returns with the occupation list for the animation. * 439 * * 440 * WARNINGS: none * 441 * * 442 * HISTORY: * 443 * 03/19/1995 JLB : Created. * 444 *=============================================================================================*/ 445 short const * AnimClass::Occupy_List(bool) const 446 { 447 #ifdef VIC 448 assert(Anims.ID(this) == ID); 449 assert(IsActive); 450 451 static short _simple[] = {REFRESH_EOL}; 452 453 #endif 454 return(_simple); 455 } 456 457 458 /*********************************************************************************************** 459 * AnimClass::Init -- Performs pre-scenario initialization. * 460 * * 461 * This routine is used to initialize the animation system prior to a scenario being loaded * 462 * or reloaded. It effectively removes all animations from the system. * 463 * * 464 * INPUT: none * 465 * * 466 * OUTPUT: none * 467 * * 468 * WARNINGS: none * 469 * * 470 * HISTORY: * 471 * 05/31/1994 JLB : Created. * 472 *=============================================================================================*/ 473 void AnimClass::Init(void) 474 { 475 Anims.Free_All(); 476 } 477 478 479 /*********************************************************************************************** 480 * AnimClass::new -- Allocates an anim object from the pool. * 481 * * 482 * This routine is used to allocate a free anim class object from the preallocated pool * 483 * in the near heap. If there are no free animation objects, then null is returned. * 484 * * 485 * INPUT: none * 486 * * 487 * OUTPUT: Returns with a pointer to a free anim object. * 488 * * 489 * WARNINGS: none * 490 * * 491 * HISTORY: * 492 * 05/31/1994 JLB : Created. * 493 *=============================================================================================*/ 494 void * AnimClass::operator new(size_t) 495 { 496 void * ptr = Anims.Allocate(); 497 if (ptr != NULL) { 498 ((AnimClass *)ptr)->Set_Active(); 499 } else { 500 GlyphX_Debug_Print("AnimClass::operator new FAILED"); 501 } 502 return(ptr); 503 } 504 505 506 /*********************************************************************************************** 507 * AnimClass::delete -- Returns an anim object back to the free pool. * 508 * * 509 * This routine is used to return an anim object back to the pool of free anim objects. * 510 * Anim objects so returned are available to be reallocated for the next animation. * 511 * * 512 * INPUT: ptr -- Pointer to the anim object to return to the pool. * 513 * * 514 * OUTPUT: none * 515 * * 516 * WARNINGS: none * 517 * * 518 * HISTORY: * 519 * 05/31/1994 JLB : Created. * 520 *=============================================================================================*/ 521 void AnimClass::operator delete(void * ptr) 522 { 523 if (ptr != NULL) { 524 ((AnimClass *)ptr)->IsActive = false; 525 } 526 Anims.Free((AnimClass *)ptr); 527 } 528 529 530 /*********************************************************************************************** 531 * AnimClass::AnimClass -- The constructor for animation objects. * 532 * * 533 * This routine is used as the constructor of animation objects. It initializes and adds * 534 * the animation object to the display and logic systems. * 535 * * 536 * INPUT: animnum -- The animation number to start. * 537 * * 538 * coord -- The location of the animation. * 539 * * 540 * timedelay-- The delay before the animation starts. * 541 * * 542 * loop -- The number of times to loop this animation. * 543 * * 544 * OUTPUT: none * 545 * * 546 * WARNINGS: none * 547 * * 548 * HISTORY: * 549 * 05/31/1994 JLB : Created. * 550 * 08/03/1994 JLB : Added a delayed affect parameter. * 551 *=============================================================================================*/ 552 AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, char loop) : 553 ObjectClass(RTTI_ANIM, Anims.ID(this)), 554 Class(AnimTypes.Ptr((int)animnum)), 555 xObject(TARGET_NONE), 556 SortTarget(TARGET_NONE), 557 OwnerHouse(HOUSE_NONE), 558 Loops(1), 559 IsToDelete(false), 560 IsBrandNew(true), 561 IsInvisible(false), 562 Delay(timedelay), 563 Accum(0), 564 AttachLayer(LAYER_NONE), 565 KillTime(0ULL) 566 { 567 #ifdef VIC 568 if (Class->Stages == -1) { 569 IsTheaterShape = Class->IsTheater; 570 ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); 571 IsTheaterShape = false; 572 573 } 574 if (Class->LoopEnd == -1) { 575 ((int&)Class->LoopEnd) = Class->Stages; 576 } 577 if (Class->IsNormalized) { 578 Set_Rate(Options.Normalize_Delay(Class->Delay)); 579 } else { 580 Set_Rate(Class->Delay); 581 } 582 Set_Stage(0); 583 584 if (Class->IsGroundLayer) { 585 Height = FLIGHT_LEVEL; 586 } 587 588 AnimClass::Unlimbo(coord); 589 590 VisibleFlags = static_cast<unsigned int>(-1); 591 592 /* 593 ** Drop zone smoke always reveals the map around itself. 594 */ 595 if (*this == ANIM_LZ_SMOKE) { 596 // Added PlayerPtr here as Sight_From now needs to know who to perform the action for. This should be OK as long as it's not used in MP. ST - 8/2/2019 2:34PM 597 Map.Sight_From(Coord_Cell(coord), Rule.DropZoneRadius / CELL_LEPTON_W, PlayerPtr, false); 598 } 599 600 if (Class->Loops >= 0) { 601 Loops = (char)(max(loop, 1) * Class->Loops); 602 Loops = (char)max(Loops, 1); 603 } else { 604 Loops = Class->Loops; 605 } 606 607 /* 608 ** If the animation starts immediately, then play the associated sound effect now. 609 */ 610 if (!Delay) { 611 Start(); 612 } 613 614 /* 615 ** Check for a virtual animation 616 */ 617 if (Class->VirtualAnim != ANIM_NONE) { 618 AnimClass* virtual_anim = new AnimClass(Class->VirtualAnim, Coord, timedelay, loop); 619 if (virtual_anim != NULL) { 620 virtual_anim->Make_Invisible(); 621 VirtualAnimTarget = virtual_anim->As_Target(); 622 } else { 623 VirtualAnimTarget = TARGET_NONE; 624 } 625 } else { 626 VirtualAnimTarget = TARGET_NONE; 627 } 628 #endif 629 } 630 631 632 /*********************************************************************************************** 633 * AnimClass::~AnimClass -- Destructor for anim objects. * 634 * * 635 * This destructor handles removing the animation object from the system. It might require * 636 * informing any object this animation is attached to that it is no longer attached. * 637 * * 638 * INPUT: none * 639 * * 640 * OUTPUT: none * 641 * * 642 * WARNINGS: none * 643 * * 644 * HISTORY: * 645 * 11/29/1994 JLB : Created. * 646 *=============================================================================================*/ 647 AnimClass::~AnimClass(void) 648 { 649 #ifdef VIC 650 assert(Anims.ID(this) == ID); 651 assert(IsActive); 652 if (GameActive) { 653 654 /* 655 ** If this anim is attached to another object 656 ** then check to see if this is the last anim attached to it. If this 657 ** is the case, then inform the object that it is no longer attached to 658 ** an animation. 659 */ 660 if (Target_Legal(xObject) && As_Object(xObject) != NULL) { 661 ObjectClass * to = As_Object(xObject); 662 663 /* 664 ** Remove the object from the appropriate display list. 665 */ 666 Map.Remove(this, In_Which_Layer()); 667 668 /* 669 ** Scan for any other animations that are attached to the object that 670 ** this animation is attached to. If there are no others, then inform the 671 ** attached object of this fact. 672 */ 673 int index; 674 for (index = 0; index < Anims.Count(); index++) { 675 if (Anims.Ptr(index) != this && Anims.Ptr(index)->xObject == xObject) break; 676 } 677 678 /* 679 ** Tell the object that it is no longer being damaged. 680 */ 681 if (index == Anims.Count()) { 682 to->Fire_Out(); 683 if (to->In_Which_Layer() == LAYER_GROUND) to->Mark(MARK_OVERLAP_UP); 684 to->IsAnimAttached = false; 685 if (to->In_Which_Layer() == LAYER_GROUND) to->Mark(MARK_OVERLAP_DOWN); 686 } 687 Coord = Coord_Add(to->Center_Coord(), Coord); 688 xObject = TARGET_NONE; 689 } 690 691 Limbo(); 692 } 693 694 xObject = TARGET_NONE; 695 Class = 0; 696 ID = -1; 697 698 #endif 699 } 700 701 702 /*********************************************************************************************** 703 * AnimClass::AI -- This is the low level anim processor. * 704 * * 705 * This routine is called once per frame per animation. It handles transition between * 706 * animation frames and marks the map for redraw as necessary. * 707 * * 708 * INPUT: none * 709 * * 710 * OUTPUT: none * 711 * * 712 * WARNINGS: Speed is of upmost importance. * 713 * * 714 * HISTORY: * 715 * 05/31/1994 JLB : Created. * 716 *=============================================================================================*/ 717 void AnimClass::AI(void) 718 { 719 #ifdef VIC 720 assert(Anims.ID(this) == ID); 721 assert(IsActive); 722 723 /* 724 ** For ground level based animations (ones that can run slowly as well as 725 ** occur behind other ground objects) always cause the cell to be redrawn. 726 */ 727 #ifdef PARTIAL 728 if (!Delay && Class->IsGroundLayer) { 729 Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); 730 } 731 #else 732 Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); 733 #endif 734 735 /* 736 ** Special case check to make sure that building on top of a smoke marker 737 ** causes the smoke marker to vanish. 738 */ 739 if (Class->Type == ANIM_LZ_SMOKE && Map[Center_Coord()].Cell_Building()) { 740 IsToDelete = true; 741 } 742 743 /* 744 ** Don't allow corpses on rocks, rivers, and water (looks really weird). 745 */ 746 if (Class->Type >= ANIM_CORPSE1 && Class->Type <= ANIM_CORPSE3) { 747 LandType land = Map[Center_Coord()].Land_Type(); 748 if (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER) { 749 IsToDelete = true; 750 } 751 } 752 753 /* 754 ** Check the kill time. 755 */ 756 if (KillTime > 0ULL) { 757 FILETIME ft; 758 GetSystemTimeAsFileTime(&ft); 759 760 unsigned long long now = (unsigned long long)ft.dwLowDateTime + ((unsigned long long)ft.dwHighDateTime << 32ULL); 761 if (now >= KillTime) { 762 IsToDelete = true; 763 } 764 } 765 766 /* 767 ** Delete this animation and bail early if the animation is flagged to be deleted 768 ** immediately. 769 */ 770 if (IsToDelete) { 771 delete this; 772 return; 773 } 774 775 /* 776 ** If this is a brand new animation, then don't process it the first logic pass 777 ** since it might end up skipping the first animation frame before it has had a 778 ** chance to draw it. 779 */ 780 if (IsBrandNew) { 781 IsBrandNew = false; 782 return; 783 } 784 785 #ifdef FIXIT_MULTI_SAVE 786 if (Class->Stages == -1) { 787 IsTheaterShape = Class->IsTheater; 788 ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); 789 IsTheaterShape = false; 790 } 791 if (Class->LoopEnd == -1) { 792 ((int&)Class->LoopEnd) = Class->Stages; 793 } 794 #endif 795 796 if (Delay) { 797 Delay--; 798 if (!Delay) { 799 Start(); 800 } 801 } else { 802 803 #ifdef FIXIT_MULTI_SAVE 804 if (Class->Stages == -1) { 805 IsTheaterShape = Class->IsTheater; 806 ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); 807 IsTheaterShape = false; 808 } 809 if (Class->LoopEnd == -1) { 810 ((int&)Class->LoopEnd) = Class->Stages; 811 } 812 #endif 813 814 /* 815 ** This is necessary because there is no recording of animations on the map 816 ** and thus the animation cannot be intelligently flagged for redraw. Most 817 ** animations move fast enough that they would need to be redrawn every 818 ** game frame anyway so this isn't TOO bad. 819 */ 820 Mark(MARK_CHANGE); 821 822 if (StageClass::Graphic_Logic()) { 823 int stage = Fetch_Stage(); 824 825 /* 826 ** If this animation is attached to another object and it is a 827 ** damaging kind of animation, then do the damage to the other 828 ** object. 829 */ 830 if (xObject != TARGET_NONE && Class->Damage > 0 && stage < Class->Stages) { 831 Accum += Class->Damage; 832 833 if (Accum >= 1) { 834 835 /* 836 ** Administer the damage. If the object was destroyed by this anim, 837 ** then the attached damaging anim is also destroyed. 838 */ 839 int damage = Accum; 840 Accum -= damage; 841 if (As_Object(xObject)->Take_Damage(damage, 0, WARHEAD_FIRE) == RESULT_DESTROYED) { 842 if (Target_Legal(VirtualAnimTarget)) { 843 delete As_Animation(VirtualAnimTarget); 844 } 845 delete this; 846 return; 847 } 848 } 849 } 850 851 /* 852 ** During the biggest stage (covers the most ground), perform any ground altering 853 ** action required. This masks craters and scorch marks, so that they appear 854 ** naturally rather than "popping" into existence while in plain sight. 855 */ 856 if (Class->Biggest && Class->Start+stage == Class->Biggest) { 857 Middle(); 858 } 859 860 /* 861 ** Check to see if the last frame has been displayed. If so, then the 862 ** animation either ends or loops. 863 */ 864 if ((Loops <= 1 && stage >= Class->Stages) || (Loops > 1 && stage >= Class->LoopEnd-Class->Start)) { 865 866 /* 867 ** Determine if this animation should loop another time. If so, then start the loop 868 ** but if not, then proceed into the animation termination handler. 869 */ 870 if (Loops > 0) Loops--; 871 if (Loops != 0) { 872 Set_Stage(Class->LoopStart); 873 } else { 874 875 if (Class->VirtualAnim != ANIM_NONE) { 876 Make_Invisible(); 877 if (!Target_Legal(VirtualAnimTarget)) { 878 if (Class->ChainTo != ANIM_NONE) { 879 Chain(); 880 } 881 else { 882 delete this; 883 } 884 } 885 } 886 else { 887 if ((Class->VirtualStages < 0) || (stage >= Class->VirtualStages)) { 888 if (Class->ChainTo != ANIM_NONE) { 889 Chain(); 890 } 891 else { 892 delete this; 893 } 894 } 895 } 896 } 897 } 898 } 899 } 900 #endif 901 } 902 903 904 /*********************************************************************************************** 905 * AnimClass::Attach_To -- Attaches animation to object specified. * 906 * * 907 * An animation can be "attached" to an object. In such cases, the animation is rendered * 908 * as an offset from the center of the object it is attached to. This allows affects such * 909 * as fire or smoke to be consistently placed on the vehicle it is associated with. * 910 * * 911 * INPUT: obj -- Pointer to the object to attach the animation to. * 912 * * 913 * OUTPUT: none * 914 * * 915 * WARNINGS: none * 916 * * 917 * HISTORY: * 918 * 09/19/1994 JLB : Created. * 919 *=============================================================================================*/ 920 void AnimClass::Attach_To(ObjectClass * obj) 921 { 922 #ifdef VIC 923 assert(Anims.ID(this) == ID); 924 assert(IsActive); 925 926 if (obj == NULL) return; 927 assert(obj->IsActive); 928 929 if (obj->In_Which_Layer() == LAYER_GROUND) obj->Mark(MARK_OVERLAP_UP); 930 obj->IsAnimAttached = true; 931 if (obj->In_Which_Layer() == LAYER_GROUND) obj->Mark(MARK_OVERLAP_DOWN); 932 Limbo(); 933 xObject = obj->As_Target(); 934 Unlimbo(Coord); 935 AttachLayer = In_Which_Layer(); 936 Height = (AttachLayer == LAYER_GROUND) ? FLIGHT_LEVEL : 0; 937 Coord = Coord_Sub(Coord, obj->Target_Coord()); 938 #endif 939 } 940 941 942 /*********************************************************************************************** 943 * AnimClass::Sort_Above -- Sorts the animation right above the specified target. * 944 * * 945 * Allows an animation to always be sorted above a particular target. Typically used * 946 * for explosions and other effects that look weird beneath those objects. * 947 * * 948 * INPUT: target -- Target to sort above. * 949 * * 950 * OUTPUT: none * 951 * * 952 * WARNINGS: none * 953 * * 954 * HISTORY: * 955 * 08/14/2019 SKY : Created. * 956 *=============================================================================================*/ 957 void AnimClass::Sort_Above(TARGET target) 958 { 959 #ifdef VIC 960 SortTarget = target; 961 #endif 962 } 963 964 965 /*********************************************************************************************** 966 * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * 967 * * 968 * Use this routine to find out which display layer (ground or air) that the animation * 969 * should be in. This information is used to place the animation into the correct display * 970 * list. * 971 * * 972 * INPUT: none * 973 * * 974 * OUTPUT: Returns with the layer that the animation should exist in. * 975 * * 976 * WARNINGS: none * 977 * * 978 * HISTORY: * 979 * 12/25/1994 JLB : Created. * 980 *=============================================================================================*/ 981 LayerType AnimClass::In_Which_Layer(void) const 982 { 983 #ifdef VIC 984 assert(Anims.ID(this) == ID); 985 assert(IsActive); 986 987 if (AttachLayer != LAYER_NONE) { 988 return AttachLayer; 989 } 990 991 if (Class->Type >= ANIM_CORPSE1 && Class->Type <= ANIM_CORPSE3) { 992 return(LAYER_SURFACE); 993 } 994 995 if (Target_Legal(xObject)) { 996 return As_Object(xObject)->In_Which_Layer(); 997 } 998 999 if (Class->IsGroundLayer) { 1000 return(LAYER_GROUND); 1001 } 1002 #endif 1003 return(LAYER_AIR); 1004 } 1005 1006 1007 /*********************************************************************************************** 1008 * AnimClass::Start -- Processes initial animation side effects. * 1009 * * 1010 * This routine is called when the animation first starts. Sometimes there are side effects * 1011 * associated with this animation that must occur immediately. Typically, this is the * 1012 * sound effect assigned to this animation. If this animation is supposed to attach itself * 1013 * to any object at its location, then do so at this time as well. * 1014 * * 1015 * INPUT: none * 1016 * * 1017 * OUTPUT: none * 1018 * * 1019 * WARNINGS: none * 1020 * * 1021 * HISTORY: * 1022 * 06/30/1995 JLB : Created. * 1023 *=============================================================================================*/ 1024 void AnimClass::Start(void) 1025 { 1026 #ifdef VIC 1027 assert(Anims.ID(this) == ID); 1028 assert(IsActive); 1029 1030 Mark(); 1031 1032 /* 1033 ** Play the sound effect for this animation. 1034 */ 1035 Sound_Effect(Class->Sound, Coord); 1036 1037 /* 1038 ** If the stage where collateral effects occur is the first stage of the animation, then 1039 ** perform this action now. Subsequent checks against this stage value starts with the 1040 ** second frame of the animation. 1041 */ 1042 if (!Class->Biggest) { 1043 Middle(); 1044 } 1045 #endif 1046 } 1047 1048 1049 /*********************************************************************************************** 1050 * AnimClass::Middle -- Processes any middle events. * 1051 * * 1052 * This routine is called when the animation as reached its largest stage. Typically, this * 1053 * routine is used to cause scorches or craters to appear at a cosmetically pleasing * 1054 * moment. * 1055 * * 1056 * INPUT: none * 1057 * * 1058 * OUTPUT: none * 1059 * * 1060 * WARNINGS: none * 1061 * * 1062 * HISTORY: * 1063 * 06/30/1995 JLB : Created. * 1064 * 10/17/1995 JLB : Ion camera added. * 1065 *=============================================================================================*/ 1066 void AnimClass::Middle(void) 1067 { 1068 #ifdef VIC 1069 assert(Anims.ID(this) == ID); 1070 assert(IsActive); 1071 1072 CELL cell = Coord_Cell(Center_Coord()); 1073 CellClass * cellptr = &Map[cell]; 1074 1075 if (Class->Type == ANIM_ATOM_BLAST) { 1076 Do_Atom_Damage(OwnerHouse, cell); 1077 } 1078 1079 /* 1080 ** If this animation leaves scorch marks (e.g., napalm), then do so at this time. 1081 */ 1082 if (Class->IsScorcher) { 1083 new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Center_Coord()); 1084 } 1085 1086 /* 1087 ** Some animations leave a crater when they occur. Artillery is a good example. 1088 ** Craters always remove the Tiberium where they occur. 1089 */ 1090 if (Class->IsCraterForming) { 1091 1092 /* 1093 ** Craters reduce the level of Tiberium in the cell. 1094 */ 1095 cellptr->Reduce_Tiberium(6); 1096 1097 /* 1098 ** If there already is a crater in the cell, then just expand the 1099 ** crater. 1100 */ 1101 new SmudgeClass(SMUDGE_CRATER1, Center_Coord()); 1102 } 1103 1104 AnimClass * newanim; 1105 1106 /* 1107 ** If this animation spawns side effects during its lifetime, then 1108 ** do so now. Usually, these side effects are in the form of other 1109 ** animations. 1110 */ 1111 switch (Class->Type) { 1112 case ANIM_NAPALM1: 1113 case ANIM_NAPALM2: 1114 case ANIM_NAPALM3: 1115 new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0040), true), 0, Random_Pick(1, 2)); 1116 if (Percent_Chance(50)) { 1117 new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x00A0), true), 0, Random_Pick(1, 2)); 1118 } 1119 if (Percent_Chance(50)) { 1120 new AnimClass(ANIM_FIRE_MED, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0070), true), 0, Random_Pick(1, 2)); 1121 } 1122 break; 1123 1124 case ANIM_FIRE_MED: 1125 case ANIM_FIRE_MED2: 1126 newanim = new AnimClass(ANIM_FIRE_SMALL, Center_Coord(), 0, Random_Pick(1, 2)); 1127 if (newanim != NULL && xObject != TARGET_NONE) { 1128 newanim->Attach_To(As_Object(xObject)); 1129 } 1130 break; 1131 1132 default: 1133 break; 1134 } 1135 #endif 1136 } 1137 1138 1139 void AnimClass::Chain(void) 1140 { 1141 /* 1142 ** The animation should end now, but first check to see if 1143 ** it needs to chain into another animation. If so, then the 1144 ** animation isn't technically over. It metamorphoses into the 1145 ** new form. 1146 */ 1147 if (Class->ChainTo != ANIM_NONE) { 1148 1149 Class = (AnimTypeClass *)&AnimTypeClass::As_Reference(Class->ChainTo); 1150 1151 if (Class->Stages == -1) { 1152 IsTheaterShape = Class->IsTheater; 1153 ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); 1154 IsTheaterShape = false; 1155 } 1156 if (Class->LoopEnd == -1) { 1157 ((int&)Class->LoopEnd) = Class->Stages; 1158 } 1159 1160 IsToDelete = false; 1161 Loops = Class->Loops; 1162 Accum = 0; 1163 if (Class->IsNormalized) { 1164 Set_Rate(Options.Normalize_Delay(Class->Delay)); 1165 } else { 1166 Set_Rate(Class->Delay); 1167 } 1168 Set_Stage(Class->Start); 1169 Start(); 1170 } 1171 } 1172 1173 1174 /*********************************************************************************************** 1175 * AnimClass::Detach -- Remove animation if attached to target. * 1176 * * 1177 * This routine is called when the specified target is being removed from the game. If this * 1178 * animation happens to be attached to this object, then the animation must be remove as * 1179 * well. * 1180 * * 1181 * INPUT: target -- The target that is about to be destroyed. * 1182 * * 1183 * all -- Is the target being destroyed RIGHT NOW? If not, then it will be * 1184 * destroyed soon. In that case, the animation should continue to remain * 1185 * attached for cosmetic reasons. * 1186 * * 1187 * OUTPUT: none * 1188 * * 1189 * WARNINGS: none * 1190 * * 1191 * HISTORY: * 1192 * 06/30/1995 JLB : Created. * 1193 * 07/02/1995 JLB : Detach is a precursor to animation destruction. * 1194 *=============================================================================================*/ 1195 void AnimClass::Detach(TARGET target, bool all) 1196 { 1197 #ifdef VIC 1198 assert(Anims.ID(this) == ID); 1199 assert(IsActive); 1200 1201 if (all) { 1202 if (Target_Legal(VirtualAnimTarget) && VirtualAnimTarget == target) { 1203 VirtualAnimTarget = TARGET_NONE; 1204 if (IsInvisible) { 1205 IsToDelete = true; 1206 Mark(MARK_UP); 1207 } 1208 } 1209 if (xObject == target) { 1210 Map.Remove(this, In_Which_Layer()); 1211 xObject = TARGET_NONE; 1212 AttachLayer = LAYER_NONE; 1213 IsToDelete = true; 1214 Mark(MARK_UP); 1215 } 1216 } 1217 #endif 1218 } 1219 1220 1221 /*********************************************************************************************** 1222 * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. * 1223 * * 1224 * This routine will apply damage around the ground-zero cell specified. * 1225 * * 1226 * INPUT: ownerhouse -- The owner of this atom bomb. * 1227 * * 1228 * cell -- The ground zero location to apply the atom bomb damage. * 1229 * * 1230 * OUTPUT: none * 1231 * * 1232 * WARNINGS: none * 1233 * * 1234 * HISTORY: * 1235 * 07/06/1996 JLB : Created. * 1236 *=============================================================================================*/ 1237 void AnimClass::Do_Atom_Damage(HousesType ownerhouse, CELL cell) 1238 { 1239 #ifdef VIC 1240 /* 1241 ** Find someone to blame the explosion on. This is necessary in 1242 ** order to properly enact retribution and record the kill for 1243 ** score purposes. 1244 */ 1245 BuildingClass * building = NULL; 1246 TechnoClass * backup = NULL; 1247 if (ownerhouse != HOUSE_NONE) { 1248 for (int index = 0; index < Logic.Count(); index++) { 1249 ObjectClass * obj = Logic[index]; 1250 1251 if (obj != NULL && obj->Is_Techno() && obj->Owner() == ownerhouse) { 1252 backup = (TechnoClass *)obj; 1253 if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_MSLO) { 1254 building = (BuildingClass *)obj; 1255 break; 1256 } 1257 } 1258 } 1259 1260 if (building == NULL) building = (BuildingClass *)backup; 1261 } 1262 1263 int radius; 1264 int rawdamage; 1265 if (Session.Type == GAME_NORMAL) { 1266 radius = 4; 1267 rawdamage = Rule.AtomDamage; 1268 //WhitePalette.Set(FADE_PALETTE_SLOW, Call_Back); //TO_FIX. ST 5/8/2019 1269 } else { 1270 radius = 3; 1271 rawdamage = Rule.AtomDamage/5; 1272 } 1273 1274 Wide_Area_Damage(Cell_Coord(cell), radius * CELL_LEPTON_W, rawdamage, building, WARHEAD_FIRE); 1275 Shake_The_Screen(3); 1276 if (Session.Type == GAME_NORMAL) { 1277 //GamePalette.Set(FADE_PALETTE_SLOW, Call_Back); //TO_FIX. ST 5/8/2019 1278 } 1279 #endif 1280 } 1281 1282 void AnimClass::Set_Owner(HousesType owner) 1283 { 1284 OwnerHouse = owner; 1285 if (Target_Legal(VirtualAnimTarget)) { 1286 As_Animation(VirtualAnimTarget)->Set_Owner(owner); 1287 } 1288 } 1289 1290 void AnimClass::Set_Visible_Flags(unsigned flags) 1291 { 1292 VisibleFlags = flags; 1293 if (Target_Legal(VirtualAnimTarget)) { 1294 As_Animation(VirtualAnimTarget)->Set_Visible_Flags(flags); 1295 } 1296 }