From ebbce748ee6e667833bceb050e9af8fb5dea6f8e Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Tue, 28 Jul 2020 16:40:48 -0700 Subject: [PATCH] Fixed unit selection and cursor actions not being pixel accurate. Visible Cells/Visible Cell Render world positions are now cached. --- code/redalert/cell.cpp | 48 ++++++---------- code/redalert/cell.h | 2 + code/redalert/display.cpp | 87 +++++++++++++++++++++-------- code/redalert/display.h | 15 ++++- code/redalert/map.cpp | 114 +++++++++++++++++--------------------- code/redalert/map.h | 2 +- 6 files changed, 147 insertions(+), 121 deletions(-) diff --git a/code/redalert/cell.cpp b/code/redalert/cell.cpp index 3d1faad..1385fea 100644 --- a/code/redalert/cell.cpp +++ b/code/redalert/cell.cpp @@ -136,6 +136,7 @@ CellClass::CellClass(void) : x_world_pos = 0; y_world_pos = 0; bigOverlay = NULL; + debug_select = 0; for (int zone = MZONE_FIRST; zone < MZONE_COUNT; zone++) { Zones[zone] = 0; @@ -1182,13 +1183,16 @@ void CellClass::Draw_It(int x, int y, bool objects) */ if (ttype->Get_Image_Data()) { // jmarshall - hd image should always be valid even if loading legacy assets - x_screen_pos = x; - y_screen_pos = y; - ConvertCoordsToIsometric(x_screen_pos, y_screen_pos); - lastRenderX = x_screen_pos; - lastRenderY = y_screen_pos; int ticon = icon; + if (debug_select == g_startFrameTime) { + GL_SetColor(1.0f, 0.0f, 0.0f); + } + LogicPage->Draw_Stamp(ttype->Get_HDImage_Data(), icon, x_screen_pos, y_screen_pos, NULL, WINDOW_TACTICAL); + + if (debug_select == g_startFrameTime) { + GL_SetColor(1.0f, 1.0f, 1.0f); + } // jmarshall end //if (remap) { // LogicPage->Remap(x+Map.TacPixelX, y+Map.TacPixelY, ICON_PIXEL_W, ICON_PIXEL_H, remap); @@ -1211,10 +1215,7 @@ void CellClass::Draw_It(int x, int y, bool objects) ** Redraw any smudge. */ if (Smudge != SMUDGE_NONE) { - int xx = x; - int yy = y; - ConvertCoordsToIsometric(xx, yy); - SmudgeTypeClass::As_Reference(Smudge).Draw_It(xx, yy, SmudgeData); + SmudgeTypeClass::As_Reference(Smudge).Draw_It(lastRenderX, lastRenderY, SmudgeData); } /* @@ -1222,11 +1223,8 @@ void CellClass::Draw_It(int x, int y, bool objects) */ if (Overlay != OVERLAY_NONE) { OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); - int xx = x; - int yy = y; - ConvertCoordsToIsometric(xx, yy); IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific - CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (xx+(CELL_PIXEL_W>>1)), (yy+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, DisplayClass::UnitShadow); + CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (lastRenderX +(CELL_PIXEL_W>>1)), (lastRenderY+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, DisplayClass::UnitShadow); IsTheaterShape = false; } @@ -1294,19 +1292,13 @@ void CellClass::Draw_It(int x, int y, bool objects) ** Draw the hash-mark cursor: */ { - int xx = x; - int yy = y; - ConvertCoordsToIsometric(xx, yy); - lastRenderX = xx; - lastRenderY = yy; - if (Map.ProximityCheck && Is_Clear_To_Build(loco)) { //LogicPage->Draw_Stamp(DisplayClass::TransIconsetHD, 0, xx, yy, NULL, WINDOW_TACTICAL); - CC_DrawHD_Shape(DisplayClass::TransIconsetHD[0], 0, xx, yy, WINDOW_TACTICAL, SHAPE_NORMAL); + CC_DrawHD_Shape(DisplayClass::TransIconsetHD[0], 0, lastRenderX, lastRenderY, WINDOW_TACTICAL, SHAPE_NORMAL); } else { //LogicPage->Draw_Stamp(DisplayClass::TransIconsetHD, 2, xx, yy, NULL, WINDOW_TACTICAL); - CC_DrawHD_Shape(DisplayClass::TransIconsetHD[2], 0, xx, yy, WINDOW_TACTICAL, SHAPE_NORMAL); + CC_DrawHD_Shape(DisplayClass::TransIconsetHD[2], 0, lastRenderX, lastRenderY, WINDOW_TACTICAL, SHAPE_NORMAL); } } @@ -1329,12 +1321,7 @@ void CellClass::Draw_It(int x, int y, bool objects) (Cell_Y(cell) - Cell_Y(Map.ZoneCell + Map.ZoneOffset)) * tptr->Width; // jmarshall - hd image should always be valid even if loading legacy assets - int xx = x; - int yy = y; - ConvertCoordsToIsometric(xx, yy); - lastRenderX = xx; - lastRenderY = yy; - LogicPage->Draw_Stamp(tptr->Get_HDImage_Data(), icon, xx, yy, NULL, WINDOW_TACTICAL); + LogicPage->Draw_Stamp(tptr->Get_HDImage_Data(), icon, lastRenderX, lastRenderY, NULL, WINDOW_TACTICAL); // jmarshall end } break; @@ -1345,10 +1332,7 @@ void CellClass::Draw_It(int x, int y, bool objects) */ case RTTI_OVERLAYTYPE: { - int xx = x; - int yy = y; - ConvertCoordsToIsometric(xx, yy); - OverlayTypeClass::As_Reference(((OverlayTypeClass*)Map.PendingObject)->Type).Draw_It(xx, yy, OverlayData); + OverlayTypeClass::As_Reference(((OverlayTypeClass*)Map.PendingObject)->Type).Draw_It(lastRenderX, lastRenderY, OverlayData); } break; @@ -1356,7 +1340,7 @@ void CellClass::Draw_It(int x, int y, bool objects) ** Draw a smudge */ case RTTI_SMUDGETYPE: - SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, 0); + SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(lastRenderX, lastRenderY, 0); break; default: diff --git a/code/redalert/cell.h b/code/redalert/cell.h index ea9c9af..d117ee8 100644 --- a/code/redalert/cell.h +++ b/code/redalert/cell.h @@ -45,6 +45,7 @@ */ class CellClass { + friend class DisplayClass; public: /* ** This is the ID number of this cell. By placing the ID number here, it doesn't have @@ -185,6 +186,7 @@ class CellClass int y_world_pos; int x_screen_pos; int y_screen_pos; + int debug_select; /* ** Per-player view of whether a cell is mapped. One bit for each house type. ST - 8/2/2019 3:00PM diff --git a/code/redalert/display.cpp b/code/redalert/display.cpp index a2d7173..380e754 100644 --- a/code/redalert/display.cpp +++ b/code/redalert/display.cpp @@ -110,6 +110,8 @@ struct AdjancentWeight_t { */ LayerClass DisplayClass::Layer[LAYER_COUNT]; +CellDisplayCache_t DisplayClass::visibleCellTable[MAP_CELL_W][MAP_CELL_H]; + /* ** Fading tables */ @@ -1906,9 +1908,6 @@ void DisplayClass::CacheVisibleCells(void) { CELL cell = Coord_Cell(coord); coord = Coord_Whole(Cell_Coord(cell)); - if (Cell_Shadow(cell, PlayerPtr) >= 0 && !Debug_Unshroud) - continue; - /* ** Only cells flagged to be redraw are examined. */ @@ -1918,14 +1917,41 @@ void DisplayClass::CacheVisibleCells(void) { if (Coord_To_Pixel(coord, xpixel, ypixel)) { CellClass* cellptr = &(*this)[coord]; if ((cellptr->Is_Mapped(PlayerPtr) && cellptr->Is_Visible(PlayerPtr)) || Debug_Unshroud) { - cellptr->visibleFrame = g_startFrameTime; - cellptr->x_world_pos = xpixel; - cellptr->y_world_pos = ypixel; - cellDisplayCache[numCachedDisplayCells++].ptr = cellptr; + if (!(Cell_Shadow(cell, PlayerPtr) >= 0 && !Debug_Unshroud)) { + cellptr->visibleFrame = g_startFrameTime; + cellptr->x_world_pos = xpixel; + cellptr->y_world_pos = ypixel; + cellDisplayCache[numCachedDisplayCells++].ptr = cellptr; + } + else { + IsShadowPresent = true; + } } else { IsShadowPresent = true; - } + } + + // If the tile is on screen then we need to create render coordinates for it even if it isn't rendered. + // This allows us to use these bits for things like movement and cell selection later on. + { + cellptr->x_screen_pos = xpixel; + cellptr->y_screen_pos = ypixel; + CellClass::ConvertCoordsToIsometric(cellptr->x_screen_pos, cellptr->y_screen_pos); + cellptr->lastRenderX = cellptr->x_screen_pos; + cellptr->lastRenderY = cellptr->y_screen_pos; + + if (cellptr->x_screen_pos < -CELL_PIXEL_W || cellptr->y_screen_pos < -CELL_PIXEL_H) + continue; + + int tileX = cellptr->x_screen_pos / CELL_PIXEL_W; + int tileY = cellptr->y_screen_pos / CELL_PIXEL_H; + + if (tileX < MAP_CELL_W && tileY < MAP_CELL_H && tileX > 0 && tileY > 0) + { + Map.visibleCellTable[tileX][tileY].ptr = cellptr; + Map.visibleCellTable[tileX][tileY].lastFrameRendered = animFrameNum + 1; + } + } } } } @@ -2934,20 +2960,29 @@ int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) Map.Pixel_To_Zoom(x, y); - int screenX, screenY; - screenX = x; - screenY = y; - CellClass::ConvertIsoCoordsToScreen(x, y); + int tileX = x / CELL_PIXEL_W; + int tileY = y / CELL_PIXEL_H; - bool edge = (y == 0 || x == 0 || x == SeenBuff.Get_Width()-1 || y == SeenBuff.Get_Height()-1); - COORDINATE coord = Map.Pixel_To_Coord(x, y); - CELL cell = Coord_Cell(coord); + if (tileX > MAP_CELL_W || tileY > MAP_CELL_H) { + return (GadgetClass::Action(0, key)); + } + + CellDisplayCache_t* cellcache = &Map.visibleCellTable[tileX][tileY]; + if(animFrameNum > cellcache->lastFrameRendered || cellcache->ptr == NULL) { + return (GadgetClass::Action(0, key)); + } + CELL cell = cellcache->ptr->Cell_Number(); + COORDINATE coord = cellcache->ptr->Cell_Coord(); + + //cellcache->ptr->debug_select = g_startFrameTime; + + bool edge = (y == 0 || x == 0 || x == SeenBuff.Get_Width() - 1 || y == SeenBuff.Get_Height() - 1); if (coord) { //shadow = (!Map[cell].IsMapped && !Debug_Unshroud); shadow = (!Map[cell].Is_Mapped(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:49AM - x -= Map.TacPixelX; - y -= Map.TacPixelY; + //x -= Map.TacPixelX; + //y -= Map.TacPixelY; /* ** Cause any displayed cursor to move along with the mouse cursor. @@ -2960,7 +2995,7 @@ int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) ** Determine the object that the mouse is currently over. */ if (!shadow) { - object = Map.Close_Object(coord); + object = Map.Close_Object(x, y); /* ** Special case check to ignore cloaked object if not owned by the player. @@ -3108,7 +3143,7 @@ int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) ** intercepted and possible rubber-band mode is flagged. */ if (flags & LEFTRELEASE) { - Map.Mouse_Left_Release(cell, screenX, screenY, object, action); + Map.Mouse_Left_Release(cell, x, y, object, action); } /* @@ -3119,7 +3154,7 @@ int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) */ if (!edge && (flags & LEFTPRESS)) { Map.Mouse_Left_Up(cell, shadow, object, action); - Map.Mouse_Left_Press(screenX, screenY); + Map.Mouse_Left_Press(x, y); } /* @@ -3128,7 +3163,7 @@ int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) ** and flag the map to redraw it. */ if (flags & LEFTHELD) { - Map.Mouse_Left_Held(screenX, screenY); + Map.Mouse_Left_Held(x, y); } } @@ -3187,7 +3222,10 @@ int DisplayClass::TacticalClass::Selection_At_Mouse(unsigned flags, KeyNumType & ** Determine the object that the mouse is currently over. */ if (!shadow) { - object = Map.Close_Object(coord); + int screenx = UserInput.Mouse.X; + int screeny = UserInput.Mouse.Y; + Map.Pixel_To_Zoom(screenx, screeny); + object = Map.Close_Object(screenx, screeny); } if (object != nullptr) @@ -3266,7 +3304,10 @@ int DisplayClass::TacticalClass::Command_Object(unsigned flags, KeyNumType & key ** Determine the object that the mouse is currently over. */ if (!shadow) { - object = Map.Close_Object(coord); + int screenx = UserInput.Mouse.X; + int screeny = UserInput.Mouse.Y; + Map.Pixel_To_Zoom(screenx, screeny); + object = Map.Close_Object(screenx, screeny); } if (CurrentObject.Count()) { diff --git a/code/redalert/display.h b/code/redalert/display.h index 8c2ba20..198768c 100644 --- a/code/redalert/display.h +++ b/code/redalert/display.h @@ -70,6 +70,14 @@ struct CellCache_t { CellClass* ptr; }; +struct CellDisplayCache_t { + CellDisplayCache_t() { + ptr = NULL; + lastFrameRendered = 0; + } + CellClass* ptr; + int lastFrameRendered; +}; class DisplayClass: public MapClass { @@ -222,7 +230,6 @@ class DisplayClass: public MapClass */ virtual void Code_Pointers(void); virtual void Decode_Pointers(void); - protected: virtual void Mouse_Right_Press(void); virtual void Mouse_Left_Press(int x, int y); @@ -270,7 +277,7 @@ class DisplayClass: public MapClass protected: void CacheVisibleCells(void); - CellCache_t* cellDisplayCache; + CellCache_t* cellDisplayCache; // Potential Visible Cells. int numCachedDisplayCells; /* @@ -354,7 +361,9 @@ public: //ST - 1/21/2019 11:59AM ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load */ unsigned char SaveLoadPadding[1024]; - +public: + // Stored visible cells for fast lookup. + static CellDisplayCache_t visibleCellTable[MAP_CELL_W][MAP_CELL_H]; }; diff --git a/code/redalert/map.cpp b/code/redalert/map.cpp index 9b4e50b..1a14c23 100644 --- a/code/redalert/map.cpp +++ b/code/redalert/map.cpp @@ -1389,82 +1389,72 @@ if (BlubCell->Overlapper[1]) { * HISTORY: * * 08/20/1995 JLB : Created. * *=============================================================================================*/ -ObjectClass * MapClass::Close_Object(COORDINATE coord) const -{ - ObjectClass * object = 0; - int distance = 0; - CELL cell = Coord_Cell(coord); +ObjectClass * MapClass::Close_Object(int screenx, int screeny) const { + int x1 = screenx - 10; + int y1 = screeny - 10; + int x2 = screenx + 10; + int y2 = screeny + 10; /* - ** Scan through current and adjacent cells, looking for the - ** closest object (within reason) to the specified coordinate. + ** Ensure that coordinate number one represents the upper left corner + ** and coordinate number two represents the lower right corner. */ - static int _offsets[] = {0, -1, 1, -MAP_CELL_W, MAP_CELL_W, MAP_CELL_W-1, MAP_CELL_W+1, -(MAP_CELL_W-1), -(MAP_CELL_W+1)}; - for (int index = 0; index < (sizeof(_offsets) / sizeof(_offsets[0])); index++) { + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + AllowVoice = true; + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass* obj = DisplayClass::Layer[LAYER_GROUND][index]; + //COORDINATE ocoord = obj->Center_Coord(); + int x = obj->GetRenderX(); + int y = obj->GetRenderY(); + + // Not on screen. + if (x == -1 && y == -1) { + continue; + } /* - ** Examine the cell for close object. Make sure that the cell actually is a - ** legal one. + ** Only try to select objects that are allowed to be selected, and are within the bounding box. */ - CELL newcell = cell + _offsets[index]; - if (In_Radar(newcell)) { - - /* - ** Search through all objects that occupy this cell and then - ** find the closest object. Check against any previously found object - ** to ensure that it is actually closer. - */ - ObjectClass * o = Array[newcell].Cell_Occupier(); - while (o != NULL) { - - /* - ** Special case check to ignore cloaked object if not owned by the player. - */ - // Change for client/server multiplayer. ST - 8/7/2019 10:35AM - //if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) { - if (!o->Is_Techno() || ((TechnoClass *)o)->Is_Owned_By_Player() || ((TechnoClass *)o)->Cloak != CLOAKED) { - int d=-1; - if (o->What_Am_I() == RTTI_BUILDING) { - d = Distance(coord, Cell_Coord(newcell)); - if (d > 0x00C0) d = -1; - } else { - d = Distance(coord, o->Center_Coord()); - } - if (d >= 0 && (!object || d < distance)) { - distance = d; - object = o; - } - } - o = o->Next; - } + HouseClass* hptr = HouseClass::As_Pointer(obj->Owner()); + if (obj->Class_Of().IsSelectable && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + return obj; } } /* - ** Handle aircraft selection separately, since they aren't tracked in cells while flying + ** Select any aircraft with the bounding box. */ - for (int index = 0; index < Aircraft.Count(); index++) { - AircraftClass * aircraft = Aircraft.Ptr(index); + for (int air_index = 0; air_index < Aircraft.Count(); air_index++) { + AircraftClass* aircraft = Aircraft.Ptr(air_index); + //COORDINATE ocoord = aircraft->Center_Coord(); + int x = aircraft->GetRenderX(); + int y = aircraft->GetRenderY(); + // Not on screen. + if (x == -1 && y == -1) { + continue; + } - if (aircraft->In_Which_Layer() != LAYER_GROUND) { - if (aircraft->Is_Owned_By_Player() || (aircraft->Cloak != CLOAKED)) { - int d = Distance(coord, Coord_Add(aircraft->Center_Coord(), XY_Coord(0, -aircraft->Height))); - if (d >= 0 && (!object || d < distance)) { - distance = d; - object = aircraft; - } - } + /* + ** Only try to select objects that are allowed to be selected, and are within the bounding box. + */ + if (aircraft->Class->IsSelectable && + !aircraft->Is_Selected_By_Player() && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + return aircraft; } } - - /* - ** Only return the object if it is within 1/4 cell distance from the specified - ** coordinate. - */ - if (object && distance > 0xC0) { - object = 0; - } - return(object); + return NULL; } diff --git a/code/redalert/map.h b/code/redalert/map.h index 5820187..ba164ea 100644 --- a/code/redalert/map.h +++ b/code/redalert/map.h @@ -62,7 +62,7 @@ class MapClass: public GScreenClass int Intact_Bridge_Count(void) const; bool Base_Region(CELL cell, HousesType & house, ZoneType & zone) const; CELL Nearby_Location(CELL cell, SpeedType speed, int zone=-1, MZoneType check=MZONE_NORMAL, bool checkflagged=false) const; - ObjectClass * Close_Object(COORDINATE coord) const; + ObjectClass* Close_Object(int screenx, int screeny) const; virtual void Detach(ObjectClass * ) {}; int Cell_Region(CELL cell); int Cell_Threat(CELL cell, HousesType house);