Mam/CO2 Map Coordinate Calculations

08/29/2018 14:08 Jonathis#1
Hi Everyone, Jonathis here again. I am still whiling away on my Monster and Me 2.5 Client rebuild project. Looking for some help on the TQ Coordinate system, as I am not getting expected results. I have confirmed MaM uses the same coordinate system as Conquer Online, and read through a number of posts regarding the calculations, including the extremely useful post found here by unknownone: [Only registered and activated users can see links. Click Here To Register...]

The map dimensions are the following:
pixels: 3840 x 2400
tiles: 135 x 135 (according to the map file)
tile size is the standard 64 x 32

So here's my problem... Mouse Coordinate to Map Coordinate works nearly perfectly utilizing unknownone's algorithm:
Code:
	int tempX = mouseX - (mouseX % tileWidth);
	int tempY = mouseY - (mouseY % tileHeight);

	int coordX = tempX / tileWidth + tempY / tileHeight;
	int coordY = tempY / tileHeight + (map->width - tempX) / tileWidth;

	//Array replicating 'Rainbow Tile'
	int posX = mouseX - tempX;
	int posY = mouseY - tempY;
	if (tileZone[posX][posY] == TileZone::TOPLEFT) coordX--;
	else if (tileZone[posX][posY] == TileZone::TOPRIGHT) coordY--;
	else if (tileZone[posX][posY] == TileZone::BOTTOMLEFT) coordY++;
	else if (tileZone[posX][posY] == TileZone::BOTTOMRIGHT) coordX++;
I cross referenced the given coordinates in quite some detail with the coordinate system in game, with the only hiccup being the x coordinate was always 1 off. I can fix that by adding 1 to it, though I'm not completely sure why it is off given the above. Once I add the 1 offset, I get exact matches to the ingame coordinates.

Now comes the problem. I try to do the reverse, and apply the Map Coordinate to Screen Point algorithm, and get very odd results. Below is the implemented algorithm.

Code:
SDL_Point GameMap::mapCoordinateToPoint(int x, int y) {
	SDL_Point newPoint;
	newPoint.x = (x - y) * tileHeight + (map->width / 2);
	newPoint.y = (x + y - (mapTileHeight-1)) * (tileWidth / 2) + (map->height / 2);
	return newPoint;
}
With this, I get a seemingly accurate x position, as can be seen by how the bounding rectangle bounds a tile that is in alignment with my mouse position. I am not completely sure it is right though, because the 0,60 calcs look iffy. The Y axis is definitely wrong though.

[Only registered and activated users can see links. Click Here To Register...]

Instead of starting in the middle, I figure take a simple example
The top left corner of the map is the coordinate 0,60. If I plug in the above I get the following results:
x = (0-60) * 32 + 1920 = 0
y = (0 + 60 - (135 - 1)) * 32 + 1200 = -49 * 32 + 1200 = -1168

Given the first tile origin is the very top left corner for the map, I would expect the tile and bounding rectangle to only be a quarter visible. Thus expected x and y should be -32,-16. Definitely not right! If I subtract an extra 1 from the x coordinate, I get the expected x values (which is obviously because I had to add 1 earlier). Still, the Y coordinate is really puzzling me.

If anyone has any suggestions, I'd much appreciate it. Thanks :)
08/29/2018 14:23 turk55#2
[Only registered and activated users can see links. Click Here To Register...]
08/29/2018 15:23 Jonathis#3
Quote:
Originally Posted by turk55 View Post
[Only registered and activated users can see links. Click Here To Register...]
Thanks for the link. Taking a look at it, the section of use here is:
Code:
void CGameMap::Cell2World(int iCellX,int iCellY,int& iWorldX,int& iWorldY)
{
	iWorldX	=_CELL_WIDTH*(iCellX-iCellY)/2+m_posOrigin.x; // _CELL_WIDTH = 64
	iWorldY	=_CELL_HEIGHT*(iCellX+iCellY)/2+m_posOrigin.y; // _CELL_HEIGHT = 32
}
If I input my values, I still don't get the right results. Assuming posOrigin is 0,0 for the top left corner, thus no current offset to the map, using the same 0,60 coordinate:
iWorldX = 64 * (0-60) / 2 = -1920
iWorldY = 32 * (0+60) / 2 = 960

What am I missing here?

-------------------------------------------------------------------------

Update:

Well, instead of trying to figure out the 'proper' equation for Y, I cheated a little and got accurate results.

If you can't find the equation, reverse engineer!

Since this gives me a valid X position
newPoint.x = ((x-1) - y) * tileHeight + (map->width / 2);

And we know that the mouse position > map coordinate is correct
int coordY = tempY / tileHeight + (map->width - tempX) / tileWidth;

Then.. just solve for Y using basic algebra!
newPoint.y = (y * tileHeight) - ((map->width - newPoint.x) * tileHeight) / tileWidth;

And voila! A valid tile. Note, I updated my draw polygon to actually just draw the tile instead of the bounding rectangle.
[Only registered and activated users can see links. Click Here To Register...]

(Wondering how many posts I need until imbedded images start to work..)
08/31/2018 09:42 teroareboss1#4
Code:
public Point ScreenPointToMapCoordinate(Point screenPoint)
        {
            var Y = screenPoint.Y / 32 + (this._puzzle.Width - screenPoint.X) / 64;
            var X = screenPoint.X / 64 + screenPoint.Y / 32;
            return new Point(X, Y);
        }

        public Point MapCoordinateToScreenPoint(Point mapCoordinate)
        {
            return new Point((mapCoordinate.X - mapCoordinate.Y) * 32 + this._puzzle.Width / 2
                , (mapCoordinate.X + mapCoordinate.Y - (this._mapData.Bounds.Height - 1)) * 16 + this._puzzle.Height / 2);
        }

 private void OnClick(object sender, MouseEventArgs e)
        {
         
            var npoint = new Point(this.Origin.X + e.X, this.Origin.Y + e.Y);
            var isocoords = isometricmap.ScreenPointToMapCoordinate(npoint);

...
}

public void ShowLocation(int x, int y)
        {
            var ScreenCoords = isometricmap.MapCoordinateToScreenPoint(new Point(x, y));

            this.Origin.X = ScreenCoords.X - RectangleMiddle.X;
            this.Origin.Y = ScreenCoords.Y - RectangleMiddle.Y;

            this.Origin.X = Math.Min(Math.Max(this.Origin.X, 0), Map.PuzzleBmp.Width - this.RenderSize.Width);
            this.Origin.Y = Math.Min(Math.Max(this.Origin.Y, 0), Map.PuzzleBmp.Height - this.RenderSize.Height);
            UpdateScreenRect();
        }

where:
 RectangleMiddle = new Point(panel1.Width / 2, panel1.Height / 2);

and randerSize is the panel i draw this.RenderSize = this.panel1.Size;

private void UpdateScreenRect()
        {
                if (CanDraw)
                {
                    var renderSize = new Size(Math.Min(this.panel1.Width, Map.PuzzleBmp.Width), Math.Min(this.panel1.Height, Map.PuzzleBmp.Height));
                    screenRect = new Rectangle(this.Origin.X, this.Origin.Y, this.panel1.Width, this.panel1.Height);
                    foreach (var DrawObjects in DrawingCollection.Values)
                    {
                        foreach (var obj in DrawObjects.Values)
                        {
                            if (obj.Enabled)
                                obj.UpdateScreenRect(screenRect);
                        }
                    }
                    var xCenter = (screenRect.X + screenRect.Width) / 2;
                    var yCenter = (screenRect.Y + screenRect.Height) / 2;
                    MiddleLocation = isometricmap.ScreenPointToMapCoordinate(new Point(xCenter, yCenter));
                }
            
        }
This is what i have found in my old project ... it was a educational project
09/01/2018 14:12 { Angelius }#5
Quote:
Originally Posted by Jonathis View Post
Thanks for the link. Taking a look at it, the section of use here is:
Code:
void CGameMap::Cell2World(int iCellX,int iCellY,int& iWorldX,int& iWorldY)
{
	iWorldX	=_CELL_WIDTH*(iCellX-iCellY)/2+m_posOrigin.x; // _CELL_WIDTH = 64
	iWorldY	=_CELL_HEIGHT*(iCellX+iCellY)/2+m_posOrigin.y; // _CELL_HEIGHT = 32
}
If I input my values, I still don't get the right results. Assuming posOrigin is 0,0 for the top left corner, thus no current offset to the map, using the same 0,60 coordinate:
iWorldX = 64 * (0-60) / 2 = -1920
iWorldY = 32 * (0+60) / 2 = 960

What am I missing here?
posOrigin.x = _CELL_WIDTH(64) * Map.Width / 2;
posOrigin.y = _CELL_HEIGHT(32) / 2;
09/02/2018 23:26 Ultimation#6
Quote:
Originally Posted by teroareboss1 View Post
Code:
public Point ScreenPointToMapCoordinate(Point screenPoint)
        {
            var Y = screenPoint.Y / 32 + (this._puzzle.Width - screenPoint.X) / 64;
            var X = screenPoint.X / 64 + screenPoint.Y / 32;
            return new Point(X, Y);
        }

        public Point MapCoordinateToScreenPoint(Point mapCoordinate)
        {
            return new Point((mapCoordinate.X - mapCoordinate.Y) * 32 + this._puzzle.Width / 2
                , (mapCoordinate.X + mapCoordinate.Y - (this._mapData.Bounds.Height - 1)) * 16 + this._puzzle.Height / 2);
        }

 private void OnClick(object sender, MouseEventArgs e)
        {
         
            var npoint = new Point(this.Origin.X + e.X, this.Origin.Y + e.Y);
            var isocoords = isometricmap.ScreenPointToMapCoordinate(npoint);

...
}

public void ShowLocation(int x, int y)
        {
            var ScreenCoords = isometricmap.MapCoordinateToScreenPoint(new Point(x, y));

            this.Origin.X = ScreenCoords.X - RectangleMiddle.X;
            this.Origin.Y = ScreenCoords.Y - RectangleMiddle.Y;

            this.Origin.X = Math.Min(Math.Max(this.Origin.X, 0), Map.PuzzleBmp.Width - this.RenderSize.Width);
            this.Origin.Y = Math.Min(Math.Max(this.Origin.Y, 0), Map.PuzzleBmp.Height - this.RenderSize.Height);
            UpdateScreenRect();
        }

where:
 RectangleMiddle = new Point(panel1.Width / 2, panel1.Height / 2);

and randerSize is the panel i draw this.RenderSize = this.panel1.Size;

private void UpdateScreenRect()
        {
                if (CanDraw)
                {
                    var renderSize = new Size(Math.Min(this.panel1.Width, Map.PuzzleBmp.Width), Math.Min(this.panel1.Height, Map.PuzzleBmp.Height));
                    screenRect = new Rectangle(this.Origin.X, this.Origin.Y, this.panel1.Width, this.panel1.Height);
                    foreach (var DrawObjects in DrawingCollection.Values)
                    {
                        foreach (var obj in DrawObjects.Values)
                        {
                            if (obj.Enabled)
                                obj.UpdateScreenRect(screenRect);
                        }
                    }
                    var xCenter = (screenRect.X + screenRect.Width) / 2;
                    var yCenter = (screenRect.Y + screenRect.Height) / 2;
                    MiddleLocation = isometricmap.ScreenPointToMapCoordinate(new Point(xCenter, yCenter));
                }
            
        }
This is what i have found in my old project ... it was a educational project [Only registered and activated users can see links. Click Here To Register...]
This code is not yours, it was stolen via a RAT on my computer years ago.
09/03/2018 00:33 Spirited#7
Quote:
Originally Posted by Ultimation View Post
This code is not yours, it was stolen via a RAT on my computer years ago.
Figured that was a little too smart for him to come up with. Nothing against him, just not his skillset. Had me surprised for a minute, but it makes sense now.
09/03/2018 02:48 Jonathis#8
Quote:
Originally Posted by { Angelius } View Post
posOrigin.x = _CELL_WIDTH(64) * Map.Width / 2;
posOrigin.y = _CELL_HEIGHT(32) / 2;
Wish I saw this before I started digging in the client to find what the calcs were in memory. Just came on to post my final findings with the chunks of code. After implementing the below, I am getting accurate results for all the maps.

First we have an amalgam of spirited's code, where the m_posOrigin x/y has the following formula
Code:
void GameMap::pointCoordinateToMapCoordinate(int x, int y) {
	int origin_x = (map_tile_width * 64 / 2);
	int origin_y = 16;
	int offset_x = origin_x - (map_width / 2);
	int offset_y = origin_y + (map_tile_height * 32 / 2) - (map_height / 2);

	int iWorldX = (offset_x + x) - origin_x;
	int iWorldY = (offset_y + y) - origin_y;

	double dWorldX = (double)iWorldX;
	double dWorldY = (double)iWorldY;
	double dCellWidth = (double)64;
	double dCellHeight = (double)32;

	double dTemp0 = (1.0*dWorldX) / (1.0*dCellWidth) + (1.0*dWorldY) / (1.0*dCellHeight);
	double dTemp1 = (1.0*dWorldY) / (1.0*dCellHeight) - (1.0*dWorldX) / (1.0*dCellWidth);
	mouseX = doubleToInt(dTemp0);
	mouseY = doubleToInt(dTemp1);
}


int GameMap::doubleToInt(double dValue) {
	if ((int)(dValue + 0.5) >(int)dValue)
		return int(dValue) + 1;
	else
		return int(dValue);
}
Next we have the Reverse function to retrieve a real x,y point on the map for placing objects/sprites etc.
Code:
SDL_Point GameMap::mapCoordinateToPoint(int x, int y) {
	int offset_x = (map_width / 2);
	int offset_y = (map_tile_height * 32 / 2) - (map_height / 2);

	SDL_Point newPoint;
	newPoint.x = ((x - y - 1) * 64 / 2) + offset_x;
	newPoint.y = ((x + y - 1) * 32 / 2) - offset_y;
	return newPoint;
}
I have fully tested both aspects of these, and cross examined (without objection) the coordinates and real points to the ones in the live game client, and it all matches up. I am a bit puzzled as to why my coord>point is different than spirited's and what Angelius references. Perhaps further operations are done to the given x,y at a later point and I am just consolidating them here? Well, I'm not going to dwell on it too much.
09/03/2018 10:03 teroareboss1#9
Quote:
Originally Posted by Ultimation View Post
This code is not yours, it was stolen via a RAT on my computer years ago.
[Only registered and activated users can see links. Click Here To Register...]

When i was working on the project i have used the unknownone project.Maybe this is the reason you think it was stolen from you.

[Only registered and activated users can see links. Click Here To Register...]
09/04/2018 13:47 Super Aids#10
Quote:
Originally Posted by teroareboss1 View Post
[Only registered and activated users can see links. Click Here To Register...]

When i was working on the project i have used the unknownone project.Maybe this is the reason you think it was stolen from you.

[Only registered and activated users can see links. Click Here To Register...]
You still ripped it off Unknownone and claimed it as yours from your project,when really you took it from another project and copied it into yours.

Smh.
09/28/2018 23:36 teroareboss1#11
Quote:
Originally Posted by Super Aids View Post
You still ripped it off Unknownone and claimed it as yours from your project,when really you took it from another project and copied it into yours.

Smh.
:))))) nooob
09/29/2018 12:44 Super Aids#12
Quote:
Originally Posted by teroareboss1 View Post
:))))) nooob
Your stupidity was far beyond what I thought it was.

I'm sorry you got caught for being idiotic.