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