// // Copyright 2020 Electronic Arts Inc. // // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free // software: you can redistribute it and/or modify it under the terms of // the GNU General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed // in the hope that it will be useful, but with permitted additional restrictions // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT // distributed with this program. You should have received a copy of the // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection /************************************************************************************* ** 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 ** ************************************************************************************* * * * Project Name : Command & Conquer - Red Alert * * * * File Name : VORTEX.CPP * * * * Programmer : Steve Tall * * * * Start Date : August 12th, 1996 * * * * Last Update : September 6th, 1996 [ST] * * * *-----------------------------------------------------------------------------------* * Overview: * * * * Circley vortexy swirly type thing. (Really just a pixel & color remap). * * * *-----------------------------------------------------------------------------------* * Functions: * * * * CVC::ChronalVortexClass -- vortex class constructor * * CVC::~ChronalVortexClass -- vortex class destructor * * CVC::Appear -- Makes a chronal vortex appear at the given coordinate. * * CVC::Disappear -- Makes the chronal vortex go away. * * CVC::Hide -- Makes the vortex hide. It might come back later. * * CVC::Show -- Makes a hidden vortex visible again. * * CVC::Stop -- Stops the vortex without going through the hide animation * * CVC::Load -- Loads the chronal vortex from a savegame file. * * CVC::Save -- Saves the vortex class data to a savegame file * * CVC::AI -- AI for the vortex. Includes movement and firing. * * CVC::Movement -- Movement AI for the vortex. * * CVC::Set_Target -- Make the vortex zap a particular object. * * CVC::Attack -- look for objects to attack * * CVC::Zap_Target -- If the vortex has a target object then zap it with lightning. * * CVC::Coordinate_Remap -- Draws the vortex * * CVC::Render -- Renders the vortex at its current position. * * CVC::Set_Redraw -- Flags the cells under to vortex to redraw. * * CVC::Setup_Remap_Tables -- Initialises the color remap tables based on theater. * * CVC::Build_Fading_Table -- Builds a fading color lookup table. * * * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "function.h" #include "vortex.h" /* ** Instance of chronal vortex class. This must be the only instance. */ ChronalVortexClass ChronalVortex; /*********************************************************************************************** * CVC::ChronalVortexClass -- vortex class constructor * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:25PM ST : Created * *=============================================================================================*/ ChronalVortexClass::ChronalVortexClass (void) { Active = 0; Theater = THEATER_NONE; Speed = 10; Range = 10; Damage = 200; RenderBuffer = NULL; //We havn't allocated it yet. It will be allocated as needed. } /*********************************************************************************************** * CVC::~ChronalVortexClass -- vortex class destructor * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:25PM ST : Created * *=============================================================================================*/ ChronalVortexClass::~ChronalVortexClass (void) { if (RenderBuffer) delete RenderBuffer; Active = 0; } /*********************************************************************************************** * CVC::Appear -- Makes a chronal vortex appear at the given coordinate. * * * * * * * * INPUT: Coordinate that vortex should appear at. * * * * OUTPUT: Nothing * * * * WARNINGS: This member does nothing if the vortex is already active * * * * HISTORY: * * 8/29/96 4:27PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Appear (COORDINATE coordinate) { if (Active) return; /* ** Adjust the given coordinate so the vortex appears in a central position */ int x = Lepton_To_Pixel(Coord_X(coordinate)); int y = Lepton_To_Pixel(Coord_Y(coordinate)); x -= 32; y -= 32; LEPTON lx = Pixel_To_Lepton (x); LEPTON ly = Pixel_To_Lepton (y); Position = XY_Coord (lx, ly); /* ** Initialise the vortex variables. */ AnimateDir = 1; AnimateFrame = 0; State = STATE_GROW; Active = true; Animate = 0; StartShutdown = false; LastAttackFrame= Frame; TargetObject = TARGET_NONE; ZapFrame = 0; Hidden = false; StartHiding = false; XDir = 0; YDir = 0; /* ** Vortex starts off in a random direction. */ DesiredXDir = Random_Pick (-Speed, Speed); DesiredYDir = Random_Pick (-Speed, Speed); } /*********************************************************************************************** * CVC::Disappear -- Makes the chronal vortex go away. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:30PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Disappear (void) { if (Hidden) { Active = false; } else { StartShutdown = true; } } /*********************************************************************************************** * CVC::Hide -- Makes the vortex hide. It might come back later. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: This doesnt deactivate the vortex. Use Disappear to get rid of it permanently. * * * * HISTORY: * * 8/29/96 4:30PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Hide (void) { if (!StartShutdown) { StartHiding = true; } } /*********************************************************************************************** * CVC::Show -- Makes a hidden vortex visible again. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:31PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Show (void) { /* ** Dont do anything if vortx is dying. */ if (!StartShutdown) { /* ** If the vortex is hidden then show it again. */ if (Hidden) { Hidden = false; StartHiding = false; AnimateFrame = 0; State = STATE_GROW; XDir = 0; YDir = 0; } else { /* ** If the vortex is in the process of hiding then reverse it. */ StartHiding = false; if (State == STATE_SHRINK) { State = STATE_GROW; AnimateFrame = VORTEX_FRAMES - AnimateFrame; } } } } /*********************************************************************************************** * CVC::Stop -- Stops the vortex without going through the hide animation * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:32PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Stop(void) { if (Active) Active = false; } /*********************************************************************************************** * CVC::Load -- Loads the chronal vortex from a savegame file. * * * * * * * * INPUT: ptr to file * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:32PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Load(Straw &file) { /* ** Delete the render buffer as we are going to lose the pointer anyway. ** It will be re-allocated when needed. */ if (RenderBuffer) delete RenderBuffer; file.Get (this, sizeof (ChronalVortexClass)); } /*********************************************************************************************** * CVC::Save -- Saves the vortex class data to a savegame file * * * * * * * * INPUT: file * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:33PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Save(Pipe &file) { GraphicBufferClass *save_ptr = NULL; if (RenderBuffer){ /* ** Save the ptr to the render buffer so we can null it for the save */ save_ptr = RenderBuffer; RenderBuffer = NULL; } file.Put (this, sizeof (ChronalVortexClass)); /* ** Restore the render buffer ptr */ if (save_ptr){ RenderBuffer = save_ptr; } } /*********************************************************************************************** * CVC::AI -- AI for the vortex. Includes movement and firing. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:34PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::AI(void) { int chance; /* ** No AI if vortex isnt active */ if (Active) { /* ** Do the movement AI */ Movement(); /* ** Do the attack AI */ Zap_Target(); if (Hidden && (Frame - HiddenFrame > 50) ) { /* ** Vortex is hidden. Chance of it showing itself increases the longer its stays hidden. */ chance = Random_Pick(0,2000); if (chance <= Frame - HiddenFrame) { Show(); } } else { if (Animate == 0) { /* ** Its time to animate the vortex. */ AnimateFrame += AnimateDir; if (AnimateFrame > VORTEX_FRAMES) { /* ** State changes can only occur on final animation frames. */ AnimateFrame = 1; if (StartShutdown) { /* ** Vortex is in the process of dying. */ if (State == STATE_SHRINK) { Set_Redraw(); Active = false; AnimateFrame = 0; } else { Attack(); State = STATE_SHRINK; } } else { if (StartHiding) { /* ** Vortex wants to hide. */ if (State == STATE_SHRINK) { /* ** Hide the vortex now. */ Set_Redraw(); StartHiding = false; Hidden = true; HiddenFrame = Frame; if (Random_Pick(0,4) == 4) { Disappear(); } } else { /* ** Start hiding the vortex. */ Attack(); State = STATE_SHRINK; } } else { Attack(); if (State == STATE_GROW) { State = STATE_ROTATE; } else { //Attack(); } } } } else { if (AnimateFrame == VORTEX_FRAMES / 2) Attack(); } } Animate++; Animate &= 1; } } } /*********************************************************************************************** * CVC::Movement -- Movement AI for the vortex. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:39PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Movement (void) { bool newpick = true; /* ** Update the vortex position by applying the x and y direction variables */ LEPTON x = Coord_X(Position); LEPTON y = Coord_Y(Position); x += XDir; y += YDir; Position = XY_Coord (x,y); /* ** Reverse the direction of the vortex if its drifting off the map. */ if (x > CELL_LEPTON_W *(Map.MapCellX + Map.MapCellWidth -4)) { newpick = false; if (DesiredXDir >0 ) DesiredXDir = -DesiredXDir; } if (y > CELL_LEPTON_H *(Map.MapCellY + Map.MapCellHeight -4)) { newpick = false; if (DesiredYDir >0 ) DesiredYDir = -DesiredYDir; } if (x < CELL_LEPTON_W *Map.MapCellX + 2*CELL_LEPTON_W) { newpick = false; if (DesiredXDir <0 ) DesiredXDir = -DesiredXDir; } if (y < CELL_LEPTON_H *Map.MapCellY + 2*CELL_LEPTON_W) { newpick = false; if (DesiredYDir <0 ) DesiredYDir = -DesiredYDir; } /* ** Vortex direction tends towards the desired direction unless the vortex is shutting down or ** appearing in which case the direction tends towards 0. */ if (State == STATE_ROTATE || Hidden) { if (XDir < DesiredXDir) XDir ++; if (XDir > DesiredXDir) XDir --; if (YDir < DesiredYDir) YDir ++; if (YDir > DesiredYDir) YDir --; } else { if (XDir > 0) XDir -= Speed/8; if (XDir < 0) XDir += Speed/8; if (YDir > 0) YDir -= Speed/8; if (YDir < 0) YDir += Speed/8; } /* ** Occasionally change the direction of the vortex. */ if (newpick && Random_Pick (0, 100) == 100) { DesiredXDir = Random_Pick (-Speed, Speed); DesiredYDir = Random_Pick (-Speed, Speed); } } /*********************************************************************************************** * CVC::Set_Target -- Make the vortex zap a particular object. * * * * * * * * INPUT: ptr to object to zap * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:42PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Set_Target (ObjectClass *target) { if (Active){ ZapFrame = 0; TargetObject = TARGET_NONE; if (target != NULL) TargetObject = target->As_Target(); LastAttackFrame = Frame; TargetDistance = (target != NULL) ? Distance (target->Center_Coord(), Position) : 0; } } /*********************************************************************************************** * CVC::Attack -- look for objects to attack * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:42PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Attack(void) { int distance; // if(TargetObject) return; // if(!TargetObject) return; /* ** Calculate the position of the center of the vortex. */ int x = Lepton_To_Pixel(Coord_X(Position)); int y = Lepton_To_Pixel(Coord_Y(Position)); x += 32; y += 12; LEPTON lx = Pixel_To_Lepton (x); LEPTON ly = Pixel_To_Lepton (y); COORDINATE here = XY_Coord (lx, ly); /* ** Scan through the ground layer objects and see who we should attack */ /* ** First scan - find any object directly above the vortex. */ for (int i= 0; i < Map.Layer[LAYER_GROUND].Count(); i++) { ObjectClass * obj = Map.Layer[LAYER_GROUND][i]; if ( obj->Is_Techno() && obj->Strength > 0 ) { distance = Distance (obj->Center_Coord(), here); if (distance <= CELL_LEPTON_W*2) { Set_Target (obj); break; } } } /* ** If we found something to attack then just return */ if (!Target_Legal(TargetObject)) return; /* ** Scan through all ground level objects. ** ** Objects within range have a chance of being selected based on their distance from the vortex. */ int chance = Random_Pick (0, 1000); if (chance > Frame - LastAttackFrame) return; for (int i= 0; i < Map.Layer[LAYER_GROUND].Count(); i++) { ObjectClass * obj = Map.Layer[LAYER_GROUND][i]; if ( obj && obj->Is_Techno() ) { distance = Distance (obj->Center_Coord(), Position); if (distance < CELL_LEPTON_W * Range) { chance = Random_Pick (0, distance); if (chance < CELL_LEPTON_W) { Set_Target (obj); break; } } } } } /*********************************************************************************************** * CVC::Zap_Target -- If the vortex has a target object then zap it with lightning. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:45PM ST : Created * *=============================================================================================*/ #define ZAP_COUNT 1 void ChronalVortexClass::Zap_Target (void) { if (!Hidden && Target_Legal(TargetObject) && ZapFrame < ZAP_COUNT) { /* ** Get the center of the vortex. */ int x = Lepton_To_Pixel(Coord_X(Position)); int y = Lepton_To_Pixel(Coord_Y(Position)); x += 32; y += 12; LEPTON lx = Pixel_To_Lepton (x); LEPTON ly = Pixel_To_Lepton (y); COORDINATE here = XY_Coord (lx, ly); /* ** Create a temporary techno object se we can access the lightning ability of the tesla. */ TechnoClass *temptech = new BuildingClass (STRUCT_TESLA, HOUSE_GOOD); if (temptech != NULL) { temptech->Coord = here; ObjectClass * obj = As_Object(TargetObject); TARGET target = As_Target (obj->Center_Coord()); Sound_Effect(VOC_TESLA_ZAP, obj->Center_Coord()); temptech->Electric_Zap (target, 0, WINDOW_TACTICAL, here, LightningRemap); delete temptech; /* ** Flag the whole map to redraw to cover the lightning. */ Map.Flag_To_Redraw(true); /* ** Zap the target 3 times but only do damage on the last frame. */ ZapFrame++; if (ZapFrame == ZAP_COUNT) { ZapFrame = 0; int damage = Damage; obj->Take_Damage(damage, TargetDistance, WARHEAD_TESLA, NULL, 1); TargetObject = TARGET_NONE; } } /* ** Vortex might pretend to go away after zapping the target. */ if (Random_Pick (0,2) == 2) Hide(); } } /*********************************************************************************************** * CVC::Coordinate_Remap -- Draws the vortex * * * * * * * * INPUT: ptr to view port to draw the vortex into * * x offset * * y offset * * width of vortex * * height of vortex * * ptr to shading remap tables * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:48PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table) { unsigned char getx,gety, remap_color, pixel_color; BufferClass destbuf (width * height); unsigned char *destptr = (unsigned char*) destbuf.Get_Buffer(); int destx = x; int desty = y; int dest_width = width; int dest_height = height; if (inbuffer->Lock()) { /* ** Get a pointer to the section of buffer we are going to work on. */ unsigned char *bufptr = (unsigned char *) inbuffer->Get_Offset() + destx #ifdef WIN32 + desty* (inbuffer->Get_Width() + inbuffer->Get_XAdd() + inbuffer->Get_Pitch()); #else + desty* (inbuffer->Get_Width() + inbuffer->Get_XAdd()); #endif #ifdef WIN32 int modulo = inbuffer->Get_Pitch() + inbuffer->Get_XAdd() + inbuffer->Get_Width(); #else int modulo = inbuffer->Get_XAdd() + inbuffer->Get_Width(); #endif for (int yy = desty ; yy < desty+dest_height ; yy++) { for (int xx = destx ; xx < destx+dest_width ; xx++) { /* ** Get the coordinates of the pixel to draw */ getx = *(remap_table++); gety = *(remap_table++); remap_color = *(remap_table++); pixel_color = * (bufptr + getx + (gety * modulo) ); *(destptr++) = VortexRemapTables [remap_color] [pixel_color]; } remap_table += 3*(width - dest_width); destptr += width - dest_width; } destbuf.To_Page(destx, desty, dest_width, dest_height, *inbuffer); inbuffer->Unlock(); } } /*********************************************************************************************** * CVC::Render -- Renders the vortex at its current position. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:49PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Render (void) { if (Active && !Hidden) { char fname [80]; int frame; /* ** Calculate which coordinate lookup table we should be using for this frame. */ switch (State) { case STATE_GROW: frame = 0; break; case STATE_ROTATE: frame = VORTEX_FRAMES; break; case STATE_SHRINK: frame = VORTEX_FRAMES*2; break; } frame += AnimateFrame; sprintf (fname, "HOLE%04d.lut", frame); void const *lut_ptr = MFCD::Retrieve(fname); if (lut_ptr) { /* ** Build a representation of the area of the screen where the vortex will be ** in an off-screen buffer. ** This is necessary for clipping as we cant remap pixels from off screen if we build ** the image from the hidpage. */ if (!RenderBuffer) { RenderBuffer = new GraphicBufferClass(CELL_PIXEL_W * 4, CELL_PIXEL_H * 4, (void*)NULL); } CELL xc = Coord_XCell (Position); CELL yc = Coord_YCell (Position); CellClass *cellptr; CELL cell; TemplateTypeClass const * ttype = 0; int icon; // The icon number to use from the template set. #ifdef WIN32 GraphicViewPortClass * oldpage = Set_Logic_Page(RenderBuffer); #else GraphicBufferClass * oldpage = Set_Logic_Page(RenderBuffer); #endif /* ** Temporarily modify the tactical window so it works with our offscreen buffer */ int wx = WindowList[WINDOW_TACTICAL][WINDOWX]; int wy = WindowList[WINDOW_TACTICAL][WINDOWY]; int ww = WindowList[WINDOW_TACTICAL][WINDOWWIDTH]; int wh = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT]; WindowList[WINDOW_TACTICAL][WINDOWX] = 0; WindowList[WINDOW_TACTICAL][WINDOWY] = 0; WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = RenderBuffer->Get_Width(); WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = RenderBuffer->Get_Height(); /* ** Loop through all the cells that the vortex overlaps and render the template, smudge ** and overlay for each cell. */ for (int y = 0 ; y<4 ; y++) { for (int x = 0 ; x<4 ; x++) { cell = XY_Cell (xc+x,yc+y); if (cell != -1) { //cellptr = &Map[ Coord_Whole (Cell_Coord(cell)) ]; cellptr = &Map [cell]; /* ** Fetch a pointer to the template type associated with this cell. */ if (cellptr->TType != TEMPLATE_NONE && cellptr->TType != TEMPLATE_CLEAR1 && cellptr->TType != 255) { ttype = &TemplateTypeClass::As_Reference(cellptr->TType); icon = cellptr->TIcon; } else { ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); icon = cellptr->Clear_Icon(); } /* ** Draw the template */ if (ttype->Get_Image_Data()) { RenderBuffer->Draw_Stamp(ttype->Get_Image_Data(), icon, x*CELL_PIXEL_W, y*CELL_PIXEL_H, NULL, WINDOW_MAIN); } /* ** Draw any smudge. */ if (cellptr->Smudge != SMUDGE_NONE) { SmudgeTypeClass::As_Reference(cellptr->Smudge).Draw_It(x*CELL_PIXEL_W, y*CELL_PIXEL_H, cellptr->SmudgeData); } /* ** Draw the overlay object. */ if (cellptr->Overlay != OVERLAY_NONE) { OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific CC_Draw_Shape(otype.Get_Image_Data(), cellptr->OverlayData, x*CELL_PIXEL_W + (CELL_PIXEL_W >> 1), y*CELL_PIXEL_H + (CELL_PIXEL_H >> 1), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, DisplayClass::UnitShadow); IsTheaterShape = false; } /* ** Draw the flag if there is one located at this cell. */ if (cellptr->IsFlagged) { void const * flag_remap = HouseClass::As_Pointer(cellptr->Owner)->Remap_Table(false, REMAP_NORMAL); 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); } } } } Set_Logic_Page(oldpage); /* ** Restore the tactical window to its correct value */ WindowList[WINDOW_TACTICAL][WINDOWX] = wx; WindowList[WINDOW_TACTICAL][WINDOWY] = wy; WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = ww; WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = wh; /* ** Render the vortex over the cells we just rendered to our buffer */ Coordinate_Remap (RenderBuffer, Lepton_To_Pixel(Coord_X(Coord_Fraction(Position))), Lepton_To_Pixel(Coord_Y(Coord_Fraction(Position))), 64, 64, (unsigned char*) lut_ptr); /* ** Calculate the pixel position of our fresh block of cells on the tactical map so ** we can blit it to the hid page. */ COORDINATE render_pos = XY_Coord(xc * CELL_LEPTON_W, yc * CELL_LEPTON_H); //Coord_Whole(Position); int x, y; Map.Coord_To_Pixel(render_pos, x, y); /* ** Create a view port to blit to */ GraphicViewPortClass target (LogicPage->Get_Graphic_Buffer(), 0, LogicPage->Get_YPos(), Lepton_To_Pixel (Map.TacLeptonWidth), Lepton_To_Pixel (Map.TacLeptonHeight)); /* ** Do some clipping since the library clipping gets it wrong. */ int diff; int source_x = 0; int source_y = 0; int source_width = CELL_PIXEL_W*4; int source_height = CELL_PIXEL_H*4; int dest_x = x; int dest_y = y; int dest_width = source_width; int dest_height = source_height; if (dest_x < 0) { source_width += dest_x; dest_width += dest_x; source_x -= dest_x; dest_x = 0; } if (dest_y <0) { source_height += dest_y; dest_height += dest_y; source_y -= dest_y; dest_y = 0; } if (dest_x + dest_width > target.Get_Width() ) { diff = dest_x + dest_width - target.Get_Width(); dest_width -= diff; source_width -= diff; } if (dest_y + dest_height > target.Get_Height() ) { diff = dest_y + dest_height - target.Get_Height(); dest_height -= diff; source_height -= diff; } /* ** Blit our freshly draw cells and vortex into their correct position on the hidpage */ if (dest_width > 0 && dest_height > 0) { RenderBuffer->Blit (target, source_x, source_y, dest_x, dest_y, dest_width, dest_height, false); } } } } /*********************************************************************************************** * CVC::Set_Redraw -- Flags the cells under to vortex to redraw. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:50PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Set_Redraw(void) { if (Active) { CELL xc = Coord_XCell (Position); CELL yc = Coord_YCell (Position); CELL cell; for (int y = MAX(0,yc - 1) ; y< yc+4 ; y++) { for (int x = MAX(0, xc-1) ; x< xc + 4 ; x++) { cell = XY_Cell (x,y); if (cell != -1) { Map[cell].Redraw_Objects(); } } } } } /*********************************************************************************************** * CVC::Setup_Remap_Tables -- Initialises the color remap tables based on theater. * * * * * * * * INPUT: Theater * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/29/96 4:51PM ST : Created * *=============================================================================================*/ void ChronalVortexClass::Setup_Remap_Tables (TheaterType theater) { /* ** The names of the remap files for each theater */ static char _remaps[3][13] ={ "TEMP_VTX.PAL", "SNOW_VTX.PAL", "INTR_VTX.PAL" }; int i; /* ** If the theater has changed then load the remap tables from disk if they exist or create ** them if they dont. */ if (theater != Theater) { Theater = theater; CCFileClass file (_remaps[(int)Theater]); if (file.Is_Available()) { file.Read (VortexRemapTables, MAX_REMAP_SHADES*256); } else { for (i=0 ; i= CYCLE_COLOR_START && index < (CYCLE_COLOR_START + CYCLE_COLOR_COUNT)) || index == CC_PULSE_COLOR || index == CC_EMBER_COLOR) { *ptr++ = index; } else { /* ** Find the color that, ideally, the working color should be remapped ** to in the special remap range. */ RGBClass trycolor = palette[index]; trycolor.Adjust(frac, palette[color]); // Try to match this color. /* ** Search through the remap range to find the color that should be remapped ** to. */ int best = -1; int bvalue = 0; for (int id = 0; id < PaletteClass::COLOR_COUNT; id++) { if (id != 0 && (id < CYCLE_COLOR_START || id >= (CYCLE_COLOR_START + CYCLE_COLOR_COUNT)) && id != CC_PULSE_COLOR && id != CC_EMBER_COLOR) { int diff = palette[id].Difference(trycolor); if (best == -1 || diff < bvalue) { best = id; bvalue = diff; } } } *ptr++ = best; } } } } void ChronalVortexClass::Detach(TARGET target) { if (Target_Legal(target) && target == TargetObject) { Set_Target(NULL); } }