VORTEX.CPP (51596B)
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 /************************************************************************************* 17 ** 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 ** 18 ************************************************************************************* 19 * * 20 * Project Name : Command & Conquer - Red Alert * 21 * * 22 * File Name : VORTEX.CPP * 23 * * 24 * Programmer : Steve Tall * 25 * * 26 * Start Date : August 12th, 1996 * 27 * * 28 * Last Update : September 6th, 1996 [ST] * 29 * * 30 *-----------------------------------------------------------------------------------* 31 * Overview: * 32 * * 33 * Circley vortexy swirly type thing. (Really just a pixel & color remap). * 34 * * 35 *-----------------------------------------------------------------------------------* 36 * Functions: * 37 * * 38 * CVC::ChronalVortexClass -- vortex class constructor * 39 * CVC::~ChronalVortexClass -- vortex class destructor * 40 * CVC::Appear -- Makes a chronal vortex appear at the given coordinate. * 41 * CVC::Disappear -- Makes the chronal vortex go away. * 42 * CVC::Hide -- Makes the vortex hide. It might come back later. * 43 * CVC::Show -- Makes a hidden vortex visible again. * 44 * CVC::Stop -- Stops the vortex without going through the hide animation * 45 * CVC::Load -- Loads the chronal vortex from a savegame file. * 46 * CVC::Save -- Saves the vortex class data to a savegame file * 47 * CVC::AI -- AI for the vortex. Includes movement and firing. * 48 * CVC::Movement -- Movement AI for the vortex. * 49 * CVC::Set_Target -- Make the vortex zap a particular object. * 50 * CVC::Attack -- look for objects to attack * 51 * CVC::Zap_Target -- If the vortex has a target object then zap it with lightning. * 52 * CVC::Coordinate_Remap -- Draws the vortex * 53 * CVC::Render -- Renders the vortex at its current position. * 54 * CVC::Set_Redraw -- Flags the cells under to vortex to redraw. * 55 * CVC::Setup_Remap_Tables -- Initialises the color remap tables based on theater. * 56 * CVC::Build_Fading_Table -- Builds a fading color lookup table. * 57 * * 58 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 59 60 #include "function.h" 61 #include "vortex.h" 62 63 64 /* 65 ** Instance of chronal vortex class. This must be the only instance. 66 */ 67 ChronalVortexClass ChronalVortex; 68 69 70 71 /*********************************************************************************************** 72 * CVC::ChronalVortexClass -- vortex class constructor * 73 * * 74 * * 75 * * 76 * INPUT: Nothing * 77 * * 78 * OUTPUT: Nothing * 79 * * 80 * WARNINGS: None * 81 * * 82 * HISTORY: * 83 * 8/29/96 4:25PM ST : Created * 84 *=============================================================================================*/ 85 ChronalVortexClass::ChronalVortexClass (void) 86 { 87 Active = 0; 88 Theater = THEATER_NONE; 89 Speed = 10; 90 Range = 10; 91 Damage = 200; 92 RenderBuffer = NULL; //We havn't allocated it yet. It will be allocated as needed. 93 } 94 95 96 97 /*********************************************************************************************** 98 * CVC::~ChronalVortexClass -- vortex class destructor * 99 * * 100 * * 101 * * 102 * INPUT: Nothing * 103 * * 104 * OUTPUT: Nothing * 105 * * 106 * WARNINGS: None * 107 * * 108 * HISTORY: * 109 * 8/29/96 4:25PM ST : Created * 110 *=============================================================================================*/ 111 ChronalVortexClass::~ChronalVortexClass (void) 112 { 113 if (RenderBuffer) delete RenderBuffer; 114 Active = 0; 115 } 116 117 118 119 /*********************************************************************************************** 120 * CVC::Appear -- Makes a chronal vortex appear at the given coordinate. * 121 * * 122 * * 123 * * 124 * INPUT: Coordinate that vortex should appear at. * 125 * * 126 * OUTPUT: Nothing * 127 * * 128 * WARNINGS: This member does nothing if the vortex is already active * 129 * * 130 * HISTORY: * 131 * 8/29/96 4:27PM ST : Created * 132 *=============================================================================================*/ 133 void ChronalVortexClass::Appear (COORDINATE coordinate) 134 { 135 if (Active) return; 136 137 /* 138 ** Adjust the given coordinate so the vortex appears in a central position 139 */ 140 int x = Lepton_To_Pixel(Coord_X(coordinate)); 141 int y = Lepton_To_Pixel(Coord_Y(coordinate)); 142 143 x -= 32; 144 y -= 32; 145 146 LEPTON lx = Pixel_To_Lepton (x); 147 LEPTON ly = Pixel_To_Lepton (y); 148 149 Position = XY_Coord (lx, ly); 150 151 /* 152 ** Initialise the vortex variables. 153 */ 154 AnimateDir = 1; 155 AnimateFrame = 0; 156 State = STATE_GROW; 157 Active = true; 158 Animate = 0; 159 StartShutdown = false; 160 LastAttackFrame= Frame; 161 TargetObject = TARGET_NONE; 162 ZapFrame = 0; 163 Hidden = false; 164 StartHiding = false; 165 XDir = 0; 166 YDir = 0; 167 168 /* 169 ** Vortex starts off in a random direction. 170 */ 171 DesiredXDir = Random_Pick (-Speed, Speed); 172 DesiredYDir = Random_Pick (-Speed, Speed); 173 174 } 175 176 177 /*********************************************************************************************** 178 * CVC::Disappear -- Makes the chronal vortex go away. * 179 * * 180 * * 181 * * 182 * INPUT: Nothing * 183 * * 184 * OUTPUT: Nothing * 185 * * 186 * WARNINGS: None * 187 * * 188 * HISTORY: * 189 * 8/29/96 4:30PM ST : Created * 190 *=============================================================================================*/ 191 void ChronalVortexClass::Disappear (void) 192 { 193 if (Hidden) { 194 Active = false; 195 } else { 196 StartShutdown = true; 197 } 198 } 199 200 201 202 /*********************************************************************************************** 203 * CVC::Hide -- Makes the vortex hide. It might come back later. * 204 * * 205 * * 206 * * 207 * INPUT: Nothing * 208 * * 209 * OUTPUT: Nothing * 210 * * 211 * WARNINGS: This doesnt deactivate the vortex. Use Disappear to get rid of it permanently. * 212 * * 213 * HISTORY: * 214 * 8/29/96 4:30PM ST : Created * 215 *=============================================================================================*/ 216 void ChronalVortexClass::Hide (void) 217 { 218 if (!StartShutdown) { 219 StartHiding = true; 220 } 221 } 222 223 224 /*********************************************************************************************** 225 * CVC::Show -- Makes a hidden vortex visible again. * 226 * * 227 * * 228 * * 229 * INPUT: Nothing * 230 * * 231 * OUTPUT: Nothing * 232 * * 233 * WARNINGS: None * 234 * * 235 * HISTORY: * 236 * 8/29/96 4:31PM ST : Created * 237 *=============================================================================================*/ 238 void ChronalVortexClass::Show (void) 239 { 240 /* 241 ** Dont do anything if vortx is dying. 242 */ 243 if (!StartShutdown) { 244 245 /* 246 ** If the vortex is hidden then show it again. 247 */ 248 if (Hidden) { 249 Hidden = false; 250 StartHiding = false; 251 AnimateFrame = 0; 252 State = STATE_GROW; 253 XDir = 0; 254 YDir = 0; 255 } else { 256 /* 257 ** If the vortex is in the process of hiding then reverse it. 258 */ 259 StartHiding = false; 260 if (State == STATE_SHRINK) { 261 State = STATE_GROW; 262 AnimateFrame = VORTEX_FRAMES - AnimateFrame; 263 } 264 } 265 } 266 } 267 268 269 270 /*********************************************************************************************** 271 * CVC::Stop -- Stops the vortex without going through the hide animation * 272 * * 273 * * 274 * * 275 * INPUT: Nothing * 276 * * 277 * OUTPUT: Nothing * 278 * * 279 * WARNINGS: None * 280 * * 281 * HISTORY: * 282 * 8/29/96 4:32PM ST : Created * 283 *=============================================================================================*/ 284 void ChronalVortexClass::Stop(void) 285 { 286 if (Active) Active = false; 287 } 288 289 290 291 292 /*********************************************************************************************** 293 * CVC::Load -- Loads the chronal vortex from a savegame file. * 294 * * 295 * * 296 * * 297 * INPUT: ptr to file * 298 * * 299 * OUTPUT: Nothing * 300 * * 301 * WARNINGS: None * 302 * * 303 * HISTORY: * 304 * 8/29/96 4:32PM ST : Created * 305 *=============================================================================================*/ 306 void ChronalVortexClass::Load(Straw &file) 307 { 308 /* 309 ** Delete the render buffer as we are going to lose the pointer anyway. 310 ** It will be re-allocated when needed. 311 */ 312 if (RenderBuffer) delete RenderBuffer; 313 314 file.Get (this, sizeof (ChronalVortexClass)); 315 } 316 317 318 319 /*********************************************************************************************** 320 * CVC::Save -- Saves the vortex class data to a savegame file * 321 * * 322 * * 323 * * 324 * INPUT: file * 325 * * 326 * OUTPUT: Nothing * 327 * * 328 * WARNINGS: None * 329 * * 330 * HISTORY: * 331 * 8/29/96 4:33PM ST : Created * 332 *=============================================================================================*/ 333 void ChronalVortexClass::Save(Pipe &file) 334 { 335 GraphicBufferClass *save_ptr = NULL; 336 337 if (RenderBuffer){ 338 /* 339 ** Save the ptr to the render buffer so we can null it for the save 340 */ 341 save_ptr = RenderBuffer; 342 RenderBuffer = NULL; 343 } 344 345 file.Put (this, sizeof (ChronalVortexClass)); 346 347 /* 348 ** Restore the render buffer ptr 349 */ 350 if (save_ptr){ 351 RenderBuffer = save_ptr; 352 } 353 } 354 355 356 357 /*********************************************************************************************** 358 * CVC::AI -- AI for the vortex. Includes movement and firing. * 359 * * 360 * * 361 * * 362 * INPUT: Nothing * 363 * * 364 * OUTPUT: Nothing * 365 * * 366 * WARNINGS: None * 367 * * 368 * HISTORY: * 369 * 8/29/96 4:34PM ST : Created * 370 *=============================================================================================*/ 371 void ChronalVortexClass::AI(void) 372 { 373 374 int chance; 375 376 /* 377 ** No AI if vortex isnt active 378 */ 379 if (Active) { 380 381 /* 382 ** Do the movement AI 383 */ 384 Movement(); 385 386 /* 387 ** Do the attack AI 388 */ 389 Zap_Target(); 390 391 392 if (Hidden && (Frame - HiddenFrame > 50) ) { 393 /* 394 ** Vortex is hidden. Chance of it showing itself increases the longer its stays hidden. 395 */ 396 chance = Random_Pick(0,2000); 397 if (chance <= Frame - HiddenFrame) { 398 Show(); 399 } 400 } else { 401 402 if (Animate == 0) { 403 404 /* 405 ** Its time to animate the vortex. 406 */ 407 AnimateFrame += AnimateDir; 408 409 if (AnimateFrame > VORTEX_FRAMES) { 410 /* 411 ** State changes can only occur on final animation frames. 412 */ 413 AnimateFrame = 1; 414 415 if (StartShutdown) { 416 417 /* 418 ** Vortex is in the process of dying. 419 */ 420 if (State == STATE_SHRINK) { 421 Set_Redraw(); 422 Active = false; 423 AnimateFrame = 0; 424 } else { 425 Attack(); 426 State = STATE_SHRINK; 427 } 428 } else { 429 430 if (StartHiding) { 431 /* 432 ** Vortex wants to hide. 433 */ 434 if (State == STATE_SHRINK) { 435 /* 436 ** Hide the vortex now. 437 */ 438 Set_Redraw(); 439 StartHiding = false; 440 Hidden = true; 441 HiddenFrame = Frame; 442 if (Random_Pick(0,4) == 4) { 443 Disappear(); 444 } 445 } else { 446 /* 447 ** Start hiding the vortex. 448 */ 449 Attack(); 450 State = STATE_SHRINK; 451 } 452 } else { 453 454 Attack(); 455 if (State == STATE_GROW) { 456 State = STATE_ROTATE; 457 } else { 458 //Attack(); 459 } 460 } 461 } 462 } else { 463 if (AnimateFrame == VORTEX_FRAMES / 2) Attack(); 464 } 465 } 466 Animate++; 467 Animate &= 1; 468 } 469 } 470 } 471 472 473 474 475 476 /*********************************************************************************************** 477 * CVC::Movement -- Movement AI for the vortex. * 478 * * 479 * * 480 * * 481 * INPUT: Nothing * 482 * * 483 * OUTPUT: Nothing * 484 * * 485 * WARNINGS: None * 486 * * 487 * HISTORY: * 488 * 8/29/96 4:39PM ST : Created * 489 *=============================================================================================*/ 490 void ChronalVortexClass::Movement (void) 491 { 492 bool newpick = true; 493 494 /* 495 ** Update the vortex position by applying the x and y direction variables 496 */ 497 LEPTON x = Coord_X(Position); 498 LEPTON y = Coord_Y(Position); 499 500 x += XDir; 501 y += YDir; 502 503 Position = XY_Coord (x,y); 504 505 /* 506 ** Reverse the direction of the vortex if its drifting off the map. 507 */ 508 if (x > CELL_LEPTON_W *(Map.MapCellX + Map.MapCellWidth -4)) { 509 newpick = false; 510 if (DesiredXDir >0 ) DesiredXDir = -DesiredXDir; 511 } 512 513 if (y > CELL_LEPTON_H *(Map.MapCellY + Map.MapCellHeight -4)) { 514 newpick = false; 515 if (DesiredYDir >0 ) DesiredYDir = -DesiredYDir; 516 } 517 518 if (x < CELL_LEPTON_W *Map.MapCellX + 2*CELL_LEPTON_W) { 519 newpick = false; 520 if (DesiredXDir <0 ) DesiredXDir = -DesiredXDir; 521 } 522 523 if (y < CELL_LEPTON_H *Map.MapCellY + 2*CELL_LEPTON_W) { 524 newpick = false; 525 if (DesiredYDir <0 ) DesiredYDir = -DesiredYDir; 526 } 527 528 /* 529 ** Vortex direction tends towards the desired direction unless the vortex is shutting down or 530 ** appearing in which case the direction tends towards 0. 531 */ 532 if (State == STATE_ROTATE || Hidden) { 533 if (XDir < DesiredXDir) XDir ++; 534 if (XDir > DesiredXDir) XDir --; 535 if (YDir < DesiredYDir) YDir ++; 536 if (YDir > DesiredYDir) YDir --; 537 } else { 538 if (XDir > 0) XDir -= Speed/8; 539 if (XDir < 0) XDir += Speed/8; 540 if (YDir > 0) YDir -= Speed/8; 541 if (YDir < 0) YDir += Speed/8; 542 } 543 544 /* 545 ** Occasionally change the direction of the vortex. 546 */ 547 if (newpick && Random_Pick (0, 100) == 100) { 548 DesiredXDir = Random_Pick (-Speed, Speed); 549 DesiredYDir = Random_Pick (-Speed, Speed); 550 } 551 } 552 553 554 555 /*********************************************************************************************** 556 * CVC::Set_Target -- Make the vortex zap a particular object. * 557 * * 558 * * 559 * * 560 * INPUT: ptr to object to zap * 561 * * 562 * OUTPUT: Nothing * 563 * * 564 * WARNINGS: None * 565 * * 566 * HISTORY: * 567 * 8/29/96 4:42PM ST : Created * 568 *=============================================================================================*/ 569 void ChronalVortexClass::Set_Target (ObjectClass *target) 570 { 571 if (Active){ 572 ZapFrame = 0; 573 TargetObject = TARGET_NONE; 574 if (target != NULL) TargetObject = target->As_Target(); 575 LastAttackFrame = Frame; 576 TargetDistance = (target != NULL) ? Distance (target->Center_Coord(), Position) : 0; 577 } 578 } 579 580 581 /*********************************************************************************************** 582 * CVC::Attack -- look for objects to attack * 583 * * 584 * * 585 * * 586 * INPUT: Nothing * 587 * * 588 * OUTPUT: Nothing * 589 * * 590 * WARNINGS: None * 591 * * 592 * HISTORY: * 593 * 8/29/96 4:42PM ST : Created * 594 *=============================================================================================*/ 595 void ChronalVortexClass::Attack(void) 596 { 597 int distance; 598 // if(TargetObject) return; 599 // if(!TargetObject) return; 600 /* 601 ** Calculate the position of the center of the vortex. 602 */ 603 int x = Lepton_To_Pixel(Coord_X(Position)); 604 int y = Lepton_To_Pixel(Coord_Y(Position)); 605 606 x += 32; 607 y += 12; 608 609 LEPTON lx = Pixel_To_Lepton (x); 610 LEPTON ly = Pixel_To_Lepton (y); 611 612 COORDINATE here = XY_Coord (lx, ly); 613 614 /* 615 ** Scan through the ground layer objects and see who we should attack 616 */ 617 618 /* 619 ** First scan - find any object directly above the vortex. 620 */ 621 for (int i= 0; i < Map.Layer[LAYER_GROUND].Count(); i++) { 622 ObjectClass * obj = Map.Layer[LAYER_GROUND][i]; 623 624 if ( obj->Is_Techno() && obj->Strength > 0 ) { 625 626 distance = Distance (obj->Center_Coord(), here); 627 628 if (distance <= CELL_LEPTON_W*2) { 629 Set_Target (obj); 630 break; 631 } 632 } 633 } 634 635 /* 636 ** If we found something to attack then just return 637 */ 638 if (!Target_Legal(TargetObject)) return; 639 640 641 /* 642 ** Scan through all ground level objects. 643 ** 644 ** Objects within range have a chance of being selected based on their distance from the vortex. 645 */ 646 647 int chance = Random_Pick (0, 1000); 648 if (chance > Frame - LastAttackFrame) return; 649 650 for (int i= 0; i < Map.Layer[LAYER_GROUND].Count(); i++) { 651 ObjectClass * obj = Map.Layer[LAYER_GROUND][i]; 652 653 if ( obj && obj->Is_Techno() ) { 654 655 distance = Distance (obj->Center_Coord(), Position); 656 657 if (distance < CELL_LEPTON_W * Range) { 658 chance = Random_Pick (0, distance); 659 if (chance < CELL_LEPTON_W) { 660 Set_Target (obj); 661 break; 662 } 663 } 664 } 665 } 666 } 667 668 669 670 671 /*********************************************************************************************** 672 * CVC::Zap_Target -- If the vortex has a target object then zap it with lightning. * 673 * * 674 * * 675 * * 676 * INPUT: Nothing * 677 * * 678 * OUTPUT: Nothing * 679 * * 680 * WARNINGS: None * 681 * * 682 * HISTORY: * 683 * 8/29/96 4:45PM ST : Created * 684 *=============================================================================================*/ 685 #define ZAP_COUNT 1 686 void ChronalVortexClass::Zap_Target (void) 687 { 688 if (!Hidden && Target_Legal(TargetObject) && ZapFrame < ZAP_COUNT) { 689 690 /* 691 ** Get the center of the vortex. 692 */ 693 int x = Lepton_To_Pixel(Coord_X(Position)); 694 int y = Lepton_To_Pixel(Coord_Y(Position)); 695 696 x += 32; 697 y += 12; 698 699 LEPTON lx = Pixel_To_Lepton (x); 700 LEPTON ly = Pixel_To_Lepton (y); 701 702 COORDINATE here = XY_Coord (lx, ly); 703 704 /* 705 ** Create a temporary techno object se we can access the lightning ability of the tesla. 706 */ 707 TechnoClass *temptech = new BuildingClass (STRUCT_TESLA, HOUSE_GOOD); 708 if (temptech != NULL) { 709 temptech->Coord = here; 710 ObjectClass * obj = As_Object(TargetObject); 711 TARGET target = As_Target (obj->Center_Coord()); 712 Sound_Effect(VOC_TESLA_ZAP, obj->Center_Coord()); 713 temptech->Electric_Zap (target, 0, WINDOW_TACTICAL, here, LightningRemap); 714 delete temptech; 715 716 /* 717 ** Flag the whole map to redraw to cover the lightning. 718 */ 719 Map.Flag_To_Redraw(true); 720 721 /* 722 ** Zap the target 3 times but only do damage on the last frame. 723 */ 724 ZapFrame++; 725 726 if (ZapFrame == ZAP_COUNT) { 727 ZapFrame = 0; 728 int damage = Damage; 729 obj->Take_Damage(damage, TargetDistance, WARHEAD_TESLA, NULL, 1); 730 TargetObject = TARGET_NONE; 731 } 732 } 733 734 /* 735 ** Vortex might pretend to go away after zapping the target. 736 */ 737 if (Random_Pick (0,2) == 2) Hide(); 738 } 739 } 740 741 742 743 744 745 /*********************************************************************************************** 746 * CVC::Coordinate_Remap -- Draws the vortex * 747 * * 748 * * 749 * * 750 * INPUT: ptr to view port to draw the vortex into * 751 * x offset * 752 * y offset * 753 * width of vortex * 754 * height of vortex * 755 * ptr to shading remap tables * 756 * * 757 * OUTPUT: Nothing * 758 * * 759 * WARNINGS: None * 760 * * 761 * HISTORY: * 762 * 8/29/96 4:48PM ST : Created * 763 *=============================================================================================*/ 764 void ChronalVortexClass::Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table) 765 { 766 unsigned char getx,gety, remap_color, pixel_color; 767 768 769 BufferClass destbuf (width * height); 770 771 unsigned char *destptr = (unsigned char*) destbuf.Get_Buffer(); 772 773 int destx = x; 774 int desty = y; 775 776 int dest_width = width; 777 int dest_height = height; 778 779 if (inbuffer->Lock()) { 780 781 /* 782 ** Get a pointer to the section of buffer we are going to work on. 783 */ 784 unsigned char *bufptr = (unsigned char *) inbuffer->Get_Offset() 785 + destx 786 #ifdef WIN32 787 + desty* (inbuffer->Get_Width() + inbuffer->Get_XAdd() + inbuffer->Get_Pitch()); 788 #else 789 + desty* (inbuffer->Get_Width() + inbuffer->Get_XAdd()); 790 #endif 791 792 793 #ifdef WIN32 794 int modulo = inbuffer->Get_Pitch() + inbuffer->Get_XAdd() + inbuffer->Get_Width(); 795 #else 796 int modulo = inbuffer->Get_XAdd() + inbuffer->Get_Width(); 797 #endif 798 799 800 for (int yy = desty ; yy < desty+dest_height ; yy++) { 801 for (int xx = destx ; xx < destx+dest_width ; xx++) { 802 /* 803 ** Get the coordinates of the pixel to draw 804 */ 805 getx = *(remap_table++); 806 gety = *(remap_table++); 807 remap_color = *(remap_table++); 808 809 pixel_color = * (bufptr + getx + (gety * modulo) ); 810 811 *(destptr++) = VortexRemapTables [remap_color] [pixel_color]; 812 } 813 814 remap_table += 3*(width - dest_width); 815 destptr += width - dest_width; 816 817 } 818 819 destbuf.To_Page(destx, desty, dest_width, dest_height, *inbuffer); 820 821 822 inbuffer->Unlock(); 823 } 824 } 825 826 827 828 /*********************************************************************************************** 829 * CVC::Render -- Renders the vortex at its current position. * 830 * * 831 * * 832 * * 833 * INPUT: Nothing * 834 * * 835 * OUTPUT: Nothing * 836 * * 837 * WARNINGS: None * 838 * * 839 * HISTORY: * 840 * 8/29/96 4:49PM ST : Created * 841 *=============================================================================================*/ 842 void ChronalVortexClass::Render (void) 843 { 844 if (Active && !Hidden) { 845 char fname [80]; 846 847 int frame; 848 849 /* 850 ** Calculate which coordinate lookup table we should be using for this frame. 851 */ 852 switch (State) { 853 case STATE_GROW: 854 frame = 0; 855 break; 856 857 case STATE_ROTATE: 858 frame = VORTEX_FRAMES; 859 break; 860 861 case STATE_SHRINK: 862 frame = VORTEX_FRAMES*2; 863 break; 864 } 865 866 frame += AnimateFrame; 867 868 sprintf (fname, "HOLE%04d.lut", frame); 869 870 void const *lut_ptr = MFCD::Retrieve(fname); 871 if (lut_ptr) { 872 873 /* 874 ** Build a representation of the area of the screen where the vortex will be 875 ** in an off-screen buffer. 876 ** This is necessary for clipping as we cant remap pixels from off screen if we build 877 ** the image from the hidpage. 878 */ 879 if (!RenderBuffer) { 880 RenderBuffer = new GraphicBufferClass(CELL_PIXEL_W * 4, CELL_PIXEL_H * 4, (void*)NULL); 881 } 882 CELL xc = Coord_XCell (Position); 883 CELL yc = Coord_YCell (Position); 884 CellClass *cellptr; 885 CELL cell; 886 TemplateTypeClass const * ttype = 0; 887 int icon; // The icon number to use from the template set. 888 889 890 #ifdef WIN32 891 GraphicViewPortClass * oldpage = Set_Logic_Page(RenderBuffer); 892 #else 893 GraphicBufferClass * oldpage = Set_Logic_Page(RenderBuffer); 894 #endif 895 896 /* 897 ** Temporarily modify the tactical window so it works with our offscreen buffer 898 */ 899 int wx = WindowList[WINDOW_TACTICAL][WINDOWX]; 900 int wy = WindowList[WINDOW_TACTICAL][WINDOWY]; 901 int ww = WindowList[WINDOW_TACTICAL][WINDOWWIDTH]; 902 int wh = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT]; 903 904 WindowList[WINDOW_TACTICAL][WINDOWX] = 0; 905 WindowList[WINDOW_TACTICAL][WINDOWY] = 0; 906 WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = RenderBuffer->Get_Width(); 907 WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = RenderBuffer->Get_Height(); 908 909 910 /* 911 ** Loop through all the cells that the vortex overlaps and render the template, smudge 912 ** and overlay for each cell. 913 */ 914 for (int y = 0 ; y<4 ; y++) { 915 for (int x = 0 ; x<4 ; x++) { 916 917 cell = XY_Cell (xc+x,yc+y); 918 if (cell != -1) { 919 920 //cellptr = &Map[ Coord_Whole (Cell_Coord(cell)) ]; 921 cellptr = &Map [cell]; 922 923 /* 924 ** Fetch a pointer to the template type associated with this cell. 925 */ 926 if (cellptr->TType != TEMPLATE_NONE && cellptr->TType != TEMPLATE_CLEAR1 && cellptr->TType != 255) { 927 ttype = &TemplateTypeClass::As_Reference(cellptr->TType); 928 icon = cellptr->TIcon; 929 } else { 930 ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); 931 icon = cellptr->Clear_Icon(); 932 } 933 934 /* 935 ** Draw the template 936 */ 937 if (ttype->Get_Image_Data()) { 938 RenderBuffer->Draw_Stamp(ttype->Get_Image_Data(), icon, x*CELL_PIXEL_W, y*CELL_PIXEL_H, NULL, WINDOW_MAIN); 939 } 940 941 /* 942 ** Draw any smudge. 943 */ 944 if (cellptr->Smudge != SMUDGE_NONE) { 945 SmudgeTypeClass::As_Reference(cellptr->Smudge).Draw_It(x*CELL_PIXEL_W, y*CELL_PIXEL_H, cellptr->SmudgeData); 946 } 947 948 /* 949 ** Draw the overlay object. 950 */ 951 if (cellptr->Overlay != OVERLAY_NONE) { 952 OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); 953 IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific 954 CC_Draw_Shape(otype.Get_Image_Data(), 955 cellptr->OverlayData, 956 x*CELL_PIXEL_W + (CELL_PIXEL_W >> 1), 957 y*CELL_PIXEL_H + (CELL_PIXEL_H >> 1), 958 WINDOW_TACTICAL, 959 SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, 960 NULL, 961 DisplayClass::UnitShadow); 962 963 IsTheaterShape = false; 964 } 965 966 /* 967 ** Draw the flag if there is one located at this cell. 968 */ 969 if (cellptr->IsFlagged) { 970 void const * flag_remap = HouseClass::As_Pointer(cellptr->Owner)->Remap_Table(false, REMAP_NORMAL); 971 CC_Draw_Shape(MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, flag_remap, DisplayClass::UnitShadow); 972 } 973 } 974 } 975 } 976 977 978 Set_Logic_Page(oldpage); 979 980 /* 981 ** Restore the tactical window to its correct value 982 */ 983 WindowList[WINDOW_TACTICAL][WINDOWX] = wx; 984 WindowList[WINDOW_TACTICAL][WINDOWY] = wy; 985 WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = ww; 986 WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = wh; 987 988 /* 989 ** Render the vortex over the cells we just rendered to our buffer 990 */ 991 Coordinate_Remap (RenderBuffer, Lepton_To_Pixel(Coord_X(Coord_Fraction(Position))), 992 Lepton_To_Pixel(Coord_Y(Coord_Fraction(Position))), 993 64, 994 64, 995 (unsigned char*) lut_ptr); 996 997 998 /* 999 ** Calculate the pixel position of our fresh block of cells on the tactical map so 1000 ** we can blit it to the hid page. 1001 */ 1002 COORDINATE render_pos = XY_Coord(xc * CELL_LEPTON_W, yc * CELL_LEPTON_H); //Coord_Whole(Position); 1003 1004 int x, y; 1005 Map.Coord_To_Pixel(render_pos, x, y); 1006 1007 /* 1008 ** Create a view port to blit to 1009 */ 1010 GraphicViewPortClass target (LogicPage->Get_Graphic_Buffer(), 1011 0, 1012 LogicPage->Get_YPos(), 1013 Lepton_To_Pixel (Map.TacLeptonWidth), 1014 Lepton_To_Pixel (Map.TacLeptonHeight)); 1015 1016 1017 /* 1018 ** Do some clipping since the library clipping gets it wrong. 1019 */ 1020 int diff; 1021 1022 int source_x = 0; 1023 int source_y = 0; 1024 int source_width = CELL_PIXEL_W*4; 1025 int source_height = CELL_PIXEL_H*4; 1026 1027 int dest_x = x; 1028 int dest_y = y; 1029 int dest_width = source_width; 1030 int dest_height = source_height; 1031 1032 if (dest_x < 0) { 1033 source_width += dest_x; 1034 dest_width += dest_x; 1035 source_x -= dest_x; 1036 dest_x = 0; 1037 } 1038 1039 if (dest_y <0) { 1040 source_height += dest_y; 1041 dest_height += dest_y; 1042 source_y -= dest_y; 1043 dest_y = 0; 1044 } 1045 1046 if (dest_x + dest_width > target.Get_Width() ) { 1047 diff = dest_x + dest_width - target.Get_Width(); 1048 dest_width -= diff; 1049 source_width -= diff; 1050 } 1051 1052 if (dest_y + dest_height > target.Get_Height() ) { 1053 diff = dest_y + dest_height - target.Get_Height(); 1054 dest_height -= diff; 1055 source_height -= diff; 1056 } 1057 1058 1059 /* 1060 ** Blit our freshly draw cells and vortex into their correct position on the hidpage 1061 */ 1062 if (dest_width > 0 && dest_height > 0) { 1063 RenderBuffer->Blit (target, source_x, source_y, dest_x, dest_y, dest_width, dest_height, false); 1064 } 1065 1066 } 1067 } 1068 } 1069 1070 1071 1072 /*********************************************************************************************** 1073 * CVC::Set_Redraw -- Flags the cells under to vortex to redraw. * 1074 * * 1075 * * 1076 * * 1077 * INPUT: Nothing * 1078 * * 1079 * OUTPUT: Nothing * 1080 * * 1081 * WARNINGS: None * 1082 * * 1083 * HISTORY: * 1084 * 8/29/96 4:50PM ST : Created * 1085 *=============================================================================================*/ 1086 void ChronalVortexClass::Set_Redraw(void) 1087 { 1088 1089 if (Active) { 1090 1091 CELL xc = Coord_XCell (Position); 1092 CELL yc = Coord_YCell (Position); 1093 1094 CELL cell; 1095 1096 for (int y = MAX(0,yc - 1) ; y< yc+4 ; y++) { 1097 for (int x = MAX(0, xc-1) ; x< xc + 4 ; x++) { 1098 cell = XY_Cell (x,y); 1099 if (cell != -1) { 1100 Map[cell].Redraw_Objects(); 1101 } 1102 } 1103 } 1104 } 1105 } 1106 1107 1108 1109 1110 /*********************************************************************************************** 1111 * CVC::Setup_Remap_Tables -- Initialises the color remap tables based on theater. * 1112 * * 1113 * * 1114 * * 1115 * INPUT: Theater * 1116 * * 1117 * OUTPUT: Nothing * 1118 * * 1119 * WARNINGS: None * 1120 * * 1121 * HISTORY: * 1122 * 8/29/96 4:51PM ST : Created * 1123 *=============================================================================================*/ 1124 void ChronalVortexClass::Setup_Remap_Tables (TheaterType theater) 1125 { 1126 /* 1127 ** The names of the remap files for each theater 1128 */ 1129 static char _remaps[3][13] ={ 1130 "TEMP_VTX.PAL", 1131 "SNOW_VTX.PAL", 1132 "INTR_VTX.PAL" 1133 }; 1134 1135 int i; 1136 1137 /* 1138 ** If the theater has changed then load the remap tables from disk if they exist or create 1139 ** them if they dont. 1140 */ 1141 if (theater != Theater) { 1142 1143 Theater = theater; 1144 1145 CCFileClass file (_remaps[(int)Theater]); 1146 1147 if (file.Is_Available()) { 1148 file.Read (VortexRemapTables, MAX_REMAP_SHADES*256); 1149 } else { 1150 1151 for (i=0 ; i<MAX_REMAP_SHADES ; i++) { 1152 Build_Fading_Table ( GamePalette, &VortexRemapTables[i][0], 0, 240- ((i*256)/MAX_REMAP_SHADES) ); 1153 } 1154 1155 file.Write (VortexRemapTables, MAX_REMAP_SHADES*256); 1156 } 1157 } 1158 1159 /* 1160 ** Set up the remap table for the lightning 1161 */ 1162 for (i=0 ; i<256 ; i++) { 1163 LightningRemap[i] = i; 1164 } 1165 LightningRemap[192] = 208; 1166 LightningRemap[193] = 209; 1167 LightningRemap[194] = 210; 1168 LightningRemap[195] = 211; 1169 LightningRemap[196] = 212; 1170 LightningRemap[197] = 213; 1171 LightningRemap[198] = 214; 1172 LightningRemap[199] = 215; 1173 } 1174 1175 1176 1177 1178 /*********************************************************************************************** 1179 * CVC::Build_Fading_Table -- Builds a fading color lookup table. * 1180 * * 1181 * * 1182 * * 1183 * INPUT: ptr to palette to base tables on * 1184 * ptr to buffer to put clut in. * 1185 * color to bias colors to * 1186 * percentage of bias * 1187 * * 1188 * OUTPUT: Nothing * 1189 * * 1190 * WARNINGS: Based on Conquer_Build_Fading_Table * 1191 * * 1192 * HISTORY: * 1193 * 8/29/96 4:53PM ST : Created * 1194 *=============================================================================================*/ 1195 void ChronalVortexClass::Build_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac) 1196 { 1197 if (dest) { 1198 unsigned char * ptr = (unsigned char *)dest; 1199 1200 /* 1201 ** Find an appropriate remap color index for every color in the palette. 1202 ** There are certain exceptions to this, but they are trapped within the 1203 ** loop. 1204 */ 1205 for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) { 1206 1207 /* 1208 ** If this color should not be remapped, then it will be stored as a remap 1209 ** to itself. This is effectively no remap. 1210 */ 1211 if (index == 0 || 1212 (index >= CYCLE_COLOR_START && index < (CYCLE_COLOR_START + CYCLE_COLOR_COUNT)) || 1213 index == CC_PULSE_COLOR || 1214 index == CC_EMBER_COLOR) { 1215 *ptr++ = index; 1216 } else { 1217 1218 /* 1219 ** Find the color that, ideally, the working color should be remapped 1220 ** to in the special remap range. 1221 */ 1222 RGBClass trycolor = palette[index]; 1223 trycolor.Adjust(frac, palette[color]); // Try to match this color. 1224 1225 /* 1226 ** Search through the remap range to find the color that should be remapped 1227 ** to. 1228 */ 1229 int best = -1; 1230 int bvalue = 0; 1231 for (int id = 0; id < PaletteClass::COLOR_COUNT; id++) { 1232 1233 if (id != 0 && 1234 (id < CYCLE_COLOR_START || id >= (CYCLE_COLOR_START + CYCLE_COLOR_COUNT)) && 1235 id != CC_PULSE_COLOR && 1236 id != CC_EMBER_COLOR) { 1237 1238 int diff = palette[id].Difference(trycolor); 1239 if (best == -1 || diff < bvalue) { 1240 best = id; 1241 bvalue = diff; 1242 } 1243 } 1244 } 1245 *ptr++ = best; 1246 } 1247 } 1248 } 1249 } 1250 1251 1252 void ChronalVortexClass::Detach(TARGET target) 1253 { 1254 if (Target_Legal(target) && target == TargetObject) { 1255 Set_Target(NULL); 1256 } 1257 }