Fixed unit selection and cursor actions not being pixel accurate. Visible Cells/Visible Cell Render world positions are now cached.

This commit is contained in:
Justin Marshall
2020-07-28 16:40:48 -07:00
parent a97502f650
commit ebbce748ee
6 changed files with 147 additions and 121 deletions
+16 -32
View File
@@ -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:
+2
View File
@@ -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
+64 -23
View File
@@ -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()) {
+12 -3
View File
@@ -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];
};
+52 -62
View File
@@ -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;
}
+1 -1
View File
@@ -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);