PDA

View Full Version : BackGround Safe Color (QT 3.x)



Tor K'tal
04-20-2009, 03:38 PM
I finally had a moment and enough knowledge/motivation to resolve the hated black lines on black background I complained about in years past. Unfortunately I am NOT skilled enough to make a diff that will produce a viable patch file so I'm going to just post the changed files in their entirety in code blocks here and let a much more proficent dev get it moved into the tree.

I adjusted the MapCommon class by adding a function to do the comparison and adjust the color accordingly. This new function requires the background color from the MapParamaters Class to be passed into it so it can do it's compairsons.

I then adjusted in the .cpp file where the p.setPen for painting of the lines so that they will use the new background safe color.

I used showeq-5.13.3.0 as my starting point.

note: gray lines on white backgrounds still draw properly, but are hard to see. If this is a major problem for folks I can look into putting some more conditions in on the comparison portion of the system. But my goal was to resolve black on black and other color on identical color background issues with SOE maps being used in SEQ.


~ TK



/*
* mapcore.h
*
* ShowEQ Distributed under GPL
* http://seq.sf.net/
*
* Portions Copyright 2001-2003 Zaphod ([email protected]).
*
*/

// Author: Zaphod ([email protected])
// Many parts derived from existing ShowEQ/SINS map code

//
// NOTE: Trying to keep this file ShowEQ/Everquest independent to allow it
// to be reused for other Show{} style projects. Any existing ShowEQ/EQ
// dependencies will be migrated out.
//
//

// ZBTEMP: Note: Currently hardcoded to use int16_t type for point data,
// should migrate this to a compiler define/typedef based value that
// can be defined differently depending on the needs of other projects
//
#ifndef _MAPCORE_H
#define _MAPCORE_H

#include <stdlib.h>
#include <stdio.h>

#include <qstring.h>
#include <qcolor.h>
#include <qfont.h>
#include <qpixmap.h>
#include <qptrlist.h>
#include <qpointarray.h>

#include "point.h"
#include "pointarray.h"
#include "fixpt.h"

///////////////////////////////////////////
// forward declarations
class MapData;
class MapLine;
class MapLocation;
class MapParameters;
class MapImage;

///////////////////////////////////////////
// type definitions
typedef Point3D<int16_t> MapPoint;
typedef Point3DArray<int16_t> MapPointArray;

///////////////////////////////////////////
// enumerated types
enum MapLineStyle
{
tMap_Normal,
tMap_DepthFiltered,
tMap_FadedFloors,
};

enum MapOptimizationMethod
{
tMap_MemoryOptim = 0,
tMap_NormalOptim = 1,
tMap_BestOptim = 2,
tMap_DefaultOptim = 3,
tMap_NoOptim = 4,
};

//----------------------------------------------------------------------
// constants

//----------------------------------------------------------------------
// MapParameters
class MapParameters
{
public:
// q format used for map fixed point math
enum { qFormat = 14 };

public:
MapParameters(const MapData& mapData);
~MapParameters();

// get methods
const MapPoint& player() const { return m_curPlayer; }
const QPoint& playerOffset() const { return m_curPlayerOffset; }
int playerXOffset() const { return m_curPlayerOffset.x(); }
int playerYOffset() const { return m_curPlayerOffset.y(); }
int16_t playerHeadRoom() const { return m_playerHeadRoom; }
int16_t playerFloorRoom() const { return m_playerFloorRoom; }
const QSize& screenLength() const { return m_screenLength; }
int screenLengthX() const { return m_screenLength.width(); }
int screenLengthY() const { return m_screenLength.height(); }
const QRect& screenBounds() const { return m_screenBounds; }
const QPoint& screenCenter() const { return m_screenCenter; }
int screenCenterX() const { return m_screenCenter.x(); }
int screenCenterY() const { return m_screenCenter.y(); }
const QSize& zoomMapLength() const { return m_zoomMapLength; }
int zoomMapLengthX() const { return m_zoomMapLength.width(); }
int zoomMapLengthY() const { return m_zoomMapLength.height(); }
int panOffsetX() const { return m_panOffsetX; }
int panOffsetY() const { return m_panOffsetY; }
int zoom() const { return m_zoom; }
int zoomDefault() const { return m_zoomDefault; }
double ratio() const { return m_ratio; }
double ratioX() const { return m_ratio; }
double ratioY() const { return m_ratio; }
int ratioIFixPt() const { return m_ratioIFixPt; }

int gridResolution() const { return m_gridResolution; }
const QColor& gridLineColor() const { return m_gridLineColor; }
const QColor& gridTickColor() const { return m_gridTickColor; }
const QColor& backgroundColor() const { return m_backgroundColor; }
const QFont& font() const { return m_font; }
int16_t headRoom() const { return m_headRoom; }
int16_t floorRoom() const { return m_floorRoom; }
MapOptimizationMethod mapOptimizationMethod() { return m_optimization; }
QPixmap::Optimization pixmapOptimizationMethod();
MapLineStyle mapLineStyle() { return m_mapLineStyle; }
bool fadeFloors() const { return (m_mapLineStyle == tMap_FadedFloors); }
bool depthFiltering() const { return (m_mapLineStyle == tMap_DepthFiltered); }
bool showBackgroundImage() const { return m_showBackgroundImage; }
bool showLocations() const { return m_showLocations; }
bool showLines() const { return m_showLines; }
bool showGridLines() const { return m_showGridLines; }
bool showGridTicks() const { return m_showGridTicks; }

// utility methods
int calcXOffset (int mapCoordinate) const;
int invertXOffset (int worldX) const;
int calcYOffset (int mapCoordinate) const;
int invertYOffset (int worldY) const;

int calcXOffsetI(int mapCoordinate) const;
int calcYOffsetI(int mapCoordinate) const;

// set/adjust methods
void setPlayer(const MapPoint& pos);

void setGridResolution(int res);
void increaseGridResolution(void);
void decreaseGridResolution(void);
void setZoomDefault(int zoom);
bool setZoom(int zoom);
bool zoomIn();
bool zoomOut();
void panX(int xPan);
void panY(int yPan);
void panXY(int xPan, int yPan);
void clearPan();
void setPan(int xPan, int yPan);
void setPanX(int xPan);
void setPanY(int yPan);
void setScreenSize(const QSize& size);
void setBackgroundColor(const QColor& color) { m_backgroundColor = color; }
void setFont(const QFont& font) { m_font = font; }
void setMapOptimizationMethod(MapOptimizationMethod method)
{ m_optimization = method; }
void setMapLineStyle(MapLineStyle style) { m_mapLineStyle = style; }
void setShowBackgroundImage(bool val) { m_showBackgroundImage = val; }
void setShowLocations(bool val) { m_showLocations = val; }
void setShowLines(bool val) { m_showLines = val; }
void setShowGridLines(bool val) { m_showGridLines = val; }
void setShowGridTicks(bool val) { m_showGridTicks = val; }
void setGridLineColor(const QColor& color) { m_gridLineColor = color; }
void setGridTickColor(const QColor& color) { m_gridTickColor = color; }
void setHeadRoom(int16_t headRoom);
void setFloorRoom(int16_t floorRoom);

void reAdjust(MapPoint* targetPoint);
void reAdjust();

void reset();
private:
const MapData* m_mapData;
MapPoint m_curPlayer;
QPoint m_curPlayerOffset;
int16_t m_playerHeadRoom;
int16_t m_playerFloorRoom;
QSize m_screenLength;
QRect m_screenBounds;
QPoint m_screenCenter;
QSize m_zoomMapLength;
uint32_t m_zoom;
uint32_t m_zoomDefault;
int m_panOffsetX;
int m_panOffsetY;
double m_ratio;
int m_ratioIFixPt;

// configuration parameters
int m_gridResolution;
QColor m_gridLineColor;
QColor m_gridTickColor;
QColor m_backgroundColor;
QFont m_font;
int16_t m_headRoom;
int16_t m_floorRoom;

MapPoint m_targetPoint;
bool m_targetPointSet;

MapOptimizationMethod m_optimization;
MapLineStyle m_mapLineStyle;
bool m_showBackgroundImage;
bool m_showLocations;
bool m_showLines;
bool m_showGridLines;
bool m_showGridTicks;
};

inline
int MapParameters::calcXOffset (int mapCoordinate) const
{
return screenCenterX() - (int)(mapCoordinate / m_ratio);
}

inline
int MapParameters::invertXOffset (int worldX) const
{
return (int)rint((screenCenterX() - worldX) * m_ratio);
}

inline
int MapParameters::calcYOffset (int mapCoordinate) const
{
return screenCenterY() - (int)(mapCoordinate / m_ratio);
}

inline
int MapParameters::invertYOffset (int worldY) const
{
return (int)rint((screenCenterY() - worldY) * m_ratio);
}

inline
int MapParameters::calcXOffsetI(int mapCoordinate) const
{
return screenCenterX() -
fixPtMulII(m_ratioIFixPt, qFormat, mapCoordinate);
}

inline
int MapParameters::calcYOffsetI(int mapCoordinate) const
{
return screenCenterY() -
fixPtMulII(m_ratioIFixPt, qFormat, mapCoordinate);
}

inline
void MapParameters::setGridResolution(int res)
{
if ((res >= 50) && (res <= 1500))
m_gridResolution = res;
}

inline
void MapParameters::increaseGridResolution(void)
{
if (m_gridResolution <= 100)
m_gridResolution = 50;
else
m_gridResolution -= 100;
reAdjust();
}

inline
void MapParameters::decreaseGridResolution(void)
{
if (m_gridResolution >= 1500)
return;

m_gridResolution += 100;
reAdjust();
}

inline
bool MapParameters::setZoom(int zoom)
{
if ((zoom <= 32) &&
(zoom >= 1))
{
m_zoom = zoom;
reAdjust();
return true;
}

return false;
}

inline
void MapParameters::setZoomDefault(int zoom)
{
if ((zoom <= 32) &&
(zoom >= 1))
m_zoomDefault = zoom;
}

inline
bool MapParameters::zoomIn()
{
if (m_zoom < 32)
{
m_zoom *= 2;
reAdjust();
return true;
}
return false;
}

inline
bool MapParameters::zoomOut()
{
if (m_zoom > 1)
{
m_zoom /= 2;
if (m_zoom == 1)
clearPan();

reAdjust();

return true;
}
return false;
}

inline
void MapParameters::panX(int xPan)
{
m_panOffsetX += m_zoomMapLength.width() / xPan;
reAdjust();
}

inline
void MapParameters::panY(int yPan)
{
m_panOffsetY += m_zoomMapLength.height() / yPan;
reAdjust();
}

inline
void MapParameters::panXY(int xPan, int yPan)
{
m_panOffsetX += m_zoomMapLength.width() / xPan;
m_panOffsetY += m_zoomMapLength.height() / yPan;
reAdjust();
}

inline
void MapParameters::clearPan()
{
m_panOffsetX = 0;
m_panOffsetY = 0;
reAdjust();
}

inline
void MapParameters::setPan(int xPan, int yPan)
{
m_panOffsetX = xPan;
m_panOffsetY = yPan;
reAdjust();
}

inline
void MapParameters::setPanX(int xPan)
{
m_panOffsetX = xPan;
reAdjust();
}

inline
void MapParameters::setPanY(int yPan)
{
m_panOffsetY = yPan;
reAdjust();
}

inline
void MapParameters::setScreenSize(const QSize& size)
{
m_screenLength = size;
reAdjust();
}

//----------------------------------------------------------------------
// MapCommon
class MapCommon
{
public:
MapCommon() {}
MapCommon(const QString& name, const QString& color)
: m_name(name), m_colorName(color), m_color(color) {}
MapCommon(const QString& name, const QColor& color)
: m_name(name), m_color(color) {}
virtual ~MapCommon();

const QString& name() const { return m_name; }
const QColor& color() const { return m_color; }
QString colorName() const;

void setName(const QString& name) { m_name = name; }
void setColor(const QString& color) { m_color = color; }

// Returns a QColor object for a color that will contrast the background better
QColor bgSafeColor(const QColor& bgColor);

private:
QString m_name;
QString m_colorName;
QColor m_color;
QColor m_bgSafeColor;
};

inline QString MapCommon::colorName() const
{
// if a color name was specified, return it
if (!m_colorName.isEmpty())
return m_colorName;

// otherwise return the string form of a QColor
return m_color.name();
}

inline QColor MapCommon::bgSafeColor(const QColor& bgColor)
{
QColor bgSafeColor = color();
// Compairing the draw color with the backround and adjusting if they are too similar
if ((abs(color().red() - bgColor.red()) <= 55) &&
(abs(color().green() - bgColor.green()) <= 55) &&
(abs(color().blue() - bgColor.blue()) <= 55))
{
// black on black fails to XOR
// if the color is too black, and we assume the background is black as well
// because we passed the previous if statement we set the color to gray
if (color().red() <= 55 && color().green() <= 55 && color().blue() <= 55)
{
bgSafeColor.setNamedColor("gray");
} else {
// the carrot symbol performs an XOR
// If the colors are identical the line will be drawn black
bgSafeColor.setRgb(color().red() ^ bgColor.red(),
color().green() ^ bgColor.green(),
color().blue() ^ bgColor.blue());
}
} else {
bgSafeColor = color();
}

return bgSafeColor;
}

//----------------------------------------------------------------------
// MapLineL
class MapLineL : public MapCommon, public QPointArray
{
public:
MapLineL();
MapLineL(const QString& name, const QString& color, uint32_t size);
MapLineL(const QString& name, const QString& color, uint32_t size, int16_t z);
virtual ~MapLineL();

int16_t z() const { return m_z; }
bool heightSet() const { return m_heightSet; }
const QRect& boundingRect() const { return m_bounds; }

void setZPos(uint16_t z)
{ m_z = z; m_heightSet = true; }
void calcBounds() { m_bounds = QPointArray::boundingRect(); }

private:
int16_t m_z;
bool m_heightSet;
QRect m_bounds;
};

//----------------------------------------------------------------------
// MapLineM
class MapLineM : public MapCommon, public MapPointArray
{
public:
MapLineM();
MapLineM(const QString& name, const QString& color, uint32_t size);
MapLineM(const QString& name, const QColor& color, uint32_t size);
MapLineM(const QString& name, const QString& color, const MapPoint& point);
virtual ~MapLineM();

const QRect& boundingRect() const { return m_bounds; }
void calcBounds() { m_bounds = MapPointArray::boundingRect(); }

private:
QRect m_bounds;
};

//----------------------------------------------------------------------
// MapLocation
class MapLocation : public MapCommon, public MapPoint
{
public:
MapLocation();
MapLocation(const QString& name, const QString& color, const QPoint& point);
MapLocation(const QString& name, const QString& color, const MapPoint& point);
MapLocation(const QString& name, const QString& color,
int16_t x, int16_t y);
MapLocation(const QString& name, const QString& color,
int16_t x, int16_t y, int16_t z);
MapLocation(const QString& name, const QColor& color,
int16_t x, int16_t y, int16_t z);
virtual ~MapLocation();
bool heightSet() const { return m_heightSet; }

private:
bool m_heightSet;
};

//----------------------------------------------------------------------
// MapAggro
class MapAggro
{
public:
MapAggro();
MapAggro(const QString& name, uint16_t range) : m_name(name), m_range(range) {}
virtual ~MapAggro();

const QString& name() { return m_name; }
uint16_t range() { return m_range; }

void setName(const QString& name) { m_name = name; }
void setRange(uint16_t range);
private:
QString m_name;
uint16_t m_range;
};

//----------------------------------------------------------------------
// MapData
class MapData
{
public:
// constructor/destructor
MapData();
~MapData();

// map loading/clearing/saving
void clear();
void loadMap(const QString& fileName, bool import = false);
void loadSOEMap(const QString& fileName, bool import = false);
void saveMap(const QString& fileName) const;
void saveSOEMap(const QString& fileName) const;

// accessors
const QString& fileName() const { return m_fileName; }
const QString& zoneShortName() const { return m_zoneShortName; }
const QString& zoneLongName() const { return m_zoneLongName; }
const QRect& boundingRect() const { return m_boundingRect; }
const QSize& size() const { return m_size; }
uint8_t zoneZEM() const { return m_zoneZEM; }
int16_t minX() const { return m_minX; }
int16_t minY() const { return m_minY; }
int16_t maxX() const { return m_maxX; }
int16_t maxY() const { return m_maxY; }
QPtrList<MapLineL>& lLines() { return m_lLines; }
QPtrList<MapLineM>& mLines() { return m_mLines; }
QPtrList<MapLocation>& locations() { return m_locations; }
QPtrList<MapAggro>& aggros() { return m_aggros; }
const QPixmap& image() const { return m_image; }
bool imageLoaded() const { return m_imageLoaded; }
bool mapLoaded() const { return m_mapLoaded; }
bool isAggro(const QString& name, uint16_t* range) const;

// make sure map is big enough, returns true if size modified
bool checkPos(int16_t x, int16_t y);
void quickCheckPos(int16_t x, int16_t y);
void updateBounds();

// map editing
void addLocation(const QString& name, const QString& color, const QPoint& point);
void setLocationName(const QString& name);
void setLocationColor(const QString& color);
void startLine(const QString& name, const QString& color, const MapPoint& point);
void addLinePoint(const MapPoint& point);
void delLinePoint(void);
void setLineName(const QString& name);
void setLineColor(const QString& color);
void setFileName(const QString& name) { m_fileName = name; }
void setZoneLongName(const QString& name) { m_zoneShortName = name; }
void setZoneShortName(const QString& name) { m_zoneLongName = name; }
void setZoneZEM(uint8_t zem) { m_zoneZEM = zem; }
void scaleDownZ(int16_t factor);
void scaleUpZ(int16_t factor);

// map painting
void paintGrid(MapParameters& param, QPainter& p) const;
void paintLines(MapParameters& param, QPainter& p) const;
void paintDepthFilteredLines(MapParameters& param, QPainter& p) const;
void paintFadedFloorsLines(MapParameters& param, QPainter& p) const;
void paintLocations(MapParameters& param, QPainter& p) const;
bool paintMapImage(MapParameters& param, QPainter& p) const;

private:
int16_t m_minX;
int16_t m_minY;
int16_t m_maxX;
int16_t m_maxY;
QRect m_boundingRect;
QSize m_size;
QString m_fileName;
QString m_zoneLongName;
QString m_zoneShortName;
QPtrList<MapLineL> m_lLines;
QPtrList<MapLineM> m_mLines;
MapLineM* m_editLineM;
QPtrList<MapLocation> m_locations;
MapLocation* m_editLocation;
QPtrList<MapAggro> m_aggros;
uint8_t m_zoneZEM;
QPixmap m_image;
bool m_imageLoaded;
bool m_mapLoaded;
};

inline
bool MapData::checkPos(int16_t x, int16_t y)
{
bool flag = false;

#if defined(MAP_DEBUG)
printf("in x: %i, in y: %i, max(%i,%i) Min(%i,%i)\n", x, y, m_maxX, m_maxY, m_minX, m_minY);
#endif /* MAP_DEBUG */

if (x > m_maxX)
{
m_maxX = x;
flag = true;
}
if (y > m_maxY)
{
m_maxY = y;
flag = true;
}
if (x < m_minX)
{
m_minX = x;
flag = true;
}
if (y < m_minY)
{
m_minY = y;
flag = true;
}

// update the boundary information if bounds changed
if (flag)
updateBounds();

return flag;
}

inline
void MapData::quickCheckPos(int16_t x, int16_t y)
{
// quick, no-nonsense checking of the bounds, for batch use.
// call updateBounds() after finished with the batch
if (x > m_maxX)
m_maxX = x;
if (y > m_maxY)
m_maxY = y;
if (x < m_minX)
m_minX = x;
if (y < m_minY)
m_minY = y;
}

inline
void MapData::updateBounds()
{
// update the boundary information
m_boundingRect = QRect(QPoint(m_minX, m_minY), QPoint(m_maxX, m_maxY));
m_size.setWidth(m_boundingRect.width());
m_size.setHeight(m_boundingRect.height());
}

//----------------------------------------------------------------------
// MapCache
class MapCache
{
public:
MapCache(const MapData& data);
~MapCache();

const QPixmap& getMapImage(MapParameters& param);

// get methods
bool needRepaint(MapParameters& param);
bool alwaysRepaint() const { return m_alwaysRepaint; }
#ifdef DEBUG
uint32_t paintCount() const { return m_paintCount; }
#endif

// set methods
void setAlwaysRepaint(bool val) { m_alwaysRepaint = val; }
void forceRepaint() { m_painted = false; }

private:
const MapData& m_mapData;
QPixmap m_mapImage;
MapParameters m_lastParam;
#ifdef DEBUG
uint32_t m_paintCount;
#endif
bool m_painted;
bool m_alwaysRepaint;
};

//----------------------------------------------------------------------
// assorted utility functions
inline bool inRect(const QRect& rect,
const int16_t& x,
const int16_t& y)
{
return ((rect.left() <= x) && (rect.right() >= x) &&
(rect.top() <= y) && (rect.bottom() >= y));
}

inline bool inRoom(const int16_t& headRoom,
const int16_t& floorRoom,
const int16_t& z)
{
return ((z <= headRoom) &&
(z >= floorRoom));
}

#endif // _MAPCORE_H

/*
* mapcore.cpp
*
* ShowEQ Distributed under GPL
* http://seq.sf.net/
*
* Portions Copyright 2001-2007 Zaphod ([email protected]).
*
*/

// Author: Zaphod ([email protected])
// Many parts derived from existing ShowEQ/SINS map code

//
// NOTE: Trying to keep this file ShowEQ/Everquest independent to allow it
// to be reused for other Show{} style projects. Any existing ShowEQ/EQ
// dependencies will be migrated out.
//

//#define DEBUGMAPLOAD

#include "mapcore.h"
#include "diagnosticmessages.h"

#include <errno.h>

#include <qpainter.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qfileinfo.h>
#include <qfile.h>
#include <qregexp.h>

//----------------------------------------------------------------------
// MapParameters
MapParameters::MapParameters(const MapData& mapData)
: m_mapData(&mapData),
m_curPlayer(0, 0, 0),
m_screenLength(600, 600)
{
m_zoomDefault = 1;
m_screenCenter = QPoint(300, 300);
m_zoomMapLength = QSize(100, 100);
m_panOffsetX = 0;
m_panOffsetY = 0;
m_ratio = 1.0;

// calculate fixed point inverse ratio using the defiend qFormat
// for calculate speed purposes (* is faster than /)
m_ratioIFixPt = fixPtToFixed<int, double>((1.0 / m_ratio), qFormat);

m_targetPoint = MapPoint(0, 0, 0);
m_targetPointSet = false;

m_gridResolution = 500;
m_gridTickColor.setRgb(225, 200, 75);
m_gridLineColor.setRgb(75, 200, 75);
m_backgroundColor = Qt::black;

m_headRoom = 75;
m_floorRoom = 75;

m_mapLineStyle = tMap_Normal;
m_showBackgroundImage = true;
m_showLocations = true;
m_showLines = true;
m_showGridLines = true;
m_showGridTicks = true;
m_optimization = tMap_NormalOptim;

reset();
}

MapParameters::~MapParameters()
{
}

void MapParameters::reset()
{
m_zoom = m_zoomDefault;
m_panOffsetX = 0;
m_panOffsetY = 0;
}

void MapParameters::reAdjust(MapPoint* targetPoint)
{
if (targetPoint)
{
m_targetPoint = *targetPoint;
m_targetPointSet = true;
}
else
{
m_targetPoint.setPoint(0, 0, 0);
m_targetPointSet = false;
}

reAdjust();
}

void MapParameters::reAdjust()
{
// get the map length
const QSize& mapSize = m_mapData->size();

if (m_zoom > 32)
m_zoom = 32;

// calculate zoomed map size
int pxrat = ((mapSize.width()) / (m_zoom));
int pyrat = ((mapSize.height()) / (m_zoom));

// if it's a bit small, zoom out
if ((pxrat <= 2) || (pyrat <= 2))
{
if (m_zoom > 1)
{
m_zoom /= 2;
if (m_zoom == 1)
clearPan();

// recalculate zoomed map size
pxrat = ((mapSize.width()) / (m_zoom));
pyrat = ((mapSize.height()) / (m_zoom));
}
}

// save zoomed map size
m_zoomMapLength.setWidth(pxrat);
m_zoomMapLength.setHeight(pyrat);

double xrat = (double)m_screenLength.width() / m_zoomMapLength.width();
double yrat = (double)m_screenLength.height() / m_zoomMapLength.height();

int xoff = 0;
int yoff = 0;

if (xrat < yrat)
{
m_zoomMapLength.setHeight((int)(m_screenLength.hei ght() / xrat));
if (m_zoom <= 1)
yoff = (m_zoomMapLength.height() - pyrat) >> 1;
}
else if (yrat)
{
m_zoomMapLength.setWidth((int)(m_screenLength.widt h() / yrat));
if (m_zoom <= 1)
xoff = (m_zoomMapLength.width() - pxrat) >> 1;
}

// calculate the scaling ratio to use
m_ratio = (double)m_zoomMapLength.width() / (double)m_screenLength.width();

// calculate fixed point inverse ratio using the defined qFormat
// for calculate speed purposes (* is faster than /)
m_ratioIFixPt = fixPtToFixed<int, double>((1.0 / m_ratio), qFormat);

int iZMaxX, iZMinX, iZMaxY, iZMinY;

// center on the target based on target
if (m_targetPointSet)
{
iZMaxX = m_targetPoint.x() + m_panOffsetX +( m_zoomMapLength.width() >> 1);
iZMinX = m_targetPoint.x() + m_panOffsetX - (m_zoomMapLength.width() >> 1);

iZMaxY = m_targetPoint.y() + m_panOffsetY + (m_zoomMapLength.height() >> 1);
iZMinY = m_targetPoint.y() + m_panOffsetY - (m_zoomMapLength.height() >> 1);
}
else
{
iZMaxX = m_panOffsetX + (m_zoomMapLength.width() >> 1);
iZMinX = m_panOffsetX - (m_zoomMapLength.width() >> 1);

iZMaxY = m_panOffsetY + (m_zoomMapLength.height() >> 1);
iZMinY = m_panOffsetY - (m_zoomMapLength.height() >> 1);
}

// try not to have blank space on the sides
if (iZMinX < m_mapData->minX())
{
iZMaxX -= (iZMinX - m_mapData->minX());
iZMinX -= (iZMinX - m_mapData->minX());
}
if (iZMinY < m_mapData->minY())
{
iZMaxY -= (iZMinY - m_mapData->minY());
iZMinY -= (iZMinY - m_mapData->minY());
}

if (iZMaxX > m_mapData->maxX())
{
iZMinX -= iZMaxX - m_mapData->maxX();
iZMaxX -= iZMaxX - m_mapData->maxX();
}
if (iZMaxY > m_mapData->maxY())
{
iZMinY -= (iZMaxY - m_mapData->maxY());
iZMaxY -= (iZMaxY - m_mapData->maxY());
}

// calculate the new screen center
m_screenCenter.setX((((iZMaxX + xoff) * m_screenLength.width()) /
m_zoomMapLength.width()));
m_screenCenter.setY((((iZMaxY + yoff) * m_screenLength.height()) /
m_zoomMapLength.height()));

// calculate screen bounds
m_screenBounds = QRect(int((m_screenCenter.x() - m_screenLength.width()) *
m_ratio),
int((m_screenCenter.y() - m_screenLength.height()) *
m_ratio),
int(m_screenLength.width() * m_ratio),
int(m_screenLength.height() * m_ratio));


// adjust pre-calculate player offsets
m_curPlayerOffset.setX(calcXOffset(m_curPlayer.x() ));
m_curPlayerOffset.setY(calcYOffset(m_curPlayer.y() ));
}

QPixmap::Optimization MapParameters::pixmapOptimizationMethod()
{
switch (m_optimization)
{
case tMap_MemoryOptim:
return QPixmap::MemoryOptim;
case tMap_NoOptim:
return QPixmap::NoOptim;
case tMap_NormalOptim:
return QPixmap::NormalOptim;
case tMap_BestOptim:
return QPixmap::BestOptim;
case tMap_DefaultOptim:
default:
return QPixmap::DefaultOptim;
}
/* Optimization Methods:
DefaultOptim - A pixmap with this optimization mode set always has the default optimization type
- default optimization type for qPixMap is NormalOptim
NoOptim - no optimization (currently the same as MemoryOptim).
MemoryOptim - optimize for minimal memory use.
NormalOptim - optimize for typical usage. Often uses more memory than MemoryOptim, and often faster.
BestOptim - optimize for pixmaps that are drawn very often and where performance is critical.
Generally uses more memory than NormalOptim and may provide a little better speed
*/
}

void MapParameters::setPlayer(const MapPoint& pos)
{
m_curPlayer = pos;

// re-calculate precomputed player head/floor room
m_playerHeadRoom = m_curPlayer.z() + m_headRoom;
m_playerFloorRoom = m_curPlayer.z() - m_floorRoom;

m_curPlayerOffset.setX(calcXOffset(m_curPlayer.x() ));
m_curPlayerOffset.setY(calcYOffset(m_curPlayer.y() ));
}

void MapParameters::setHeadRoom(int16_t headRoom)
{
m_headRoom = headRoom;

// re-calculate precomputed player head/floor room
m_playerHeadRoom = m_curPlayer.z() + m_headRoom;
}

void MapParameters::setFloorRoom(int16_t floorRoom)
{
m_floorRoom = floorRoom;

// re-calculate precomputed player head/floor room
m_playerFloorRoom = m_curPlayer.z() - m_floorRoom;
}

//----------------------------------------------------------------------
// MapCommon
MapCommon::~MapCommon()
{
}

//----------------------------------------------------------------------
// MapLineL
MapLineL::MapLineL()
: MapCommon(), m_z(0), m_heightSet(false)
{
}

MapLineL::MapLineL(const QString& name,
const QString& color,
uint32_t size)
: MapCommon(name, color),
QPointArray(size),
m_z(0),
m_heightSet(false)
{
}

MapLineL::MapLineL(const QString& name,
const QString& color,
uint32_t size,
int16_t z)
: MapCommon(name, color),
QPointArray(size),
m_z(z),
m_heightSet(true)
{
}

MapLineL::~MapLineL()
{
}

//----------------------------------------------------------------------
// MapLineM
MapLineM::MapLineM()
: MapCommon()
{
}

MapLineM::MapLineM(const QString& name, const QString& color, uint32_t size)
: MapCommon(name, color),
MapPointArray(size)
{
}

MapLineM::MapLineM(const QString& name, const QColor& color, uint32_t size)
: MapCommon(name, color),
MapPointArray(size)
{
}

MapLineM::MapLineM(const QString& name, const QString& color, const MapPoint& point)
: MapCommon(name, color),
MapPointArray(1)
{
// set the first point
setPoint(0, point);
}

MapLineM::~MapLineM()
{
}

//----------------------------------------------------------------------
// MapLocation
MapLocation::MapLocation()
{
}

MapLocation::MapLocation(const QString& name,
const QString& color,
const QPoint& point)
: MapCommon(name, color),
MapPoint(point),
m_heightSet(false)
{
}

MapLocation::MapLocation(const QString& name,
const QString& color,
const MapPoint& point)
: MapCommon(name, color),
MapPoint(point),
m_heightSet(true)
{
}

MapLocation::MapLocation(const QString& name,
const QString& color,
int16_t x,
int16_t y)
: MapCommon(name, color),
MapPoint(x, y, 0),
m_heightSet(false)
{
}

MapLocation::MapLocation(const QString& name,
const QString& color,
int16_t x,
int16_t y,
int16_t z)
: MapCommon(name, color),
MapPoint(x, y, z),
m_heightSet(true)
{
}

MapLocation::MapLocation(const QString& name,
const QColor& color,
int16_t x,
int16_t y,
int16_t z)
: MapCommon(name, color),
MapPoint(x, y, z),
m_heightSet(true)
{
}

MapLocation::~MapLocation()
{
}

//----------------------------------------------------------------------
// MapAggro
MapAggro::~MapAggro()
{
}


//----------------------------------------------------------------------
// MapData
MapData::MapData()
{
// make all lists auto delete
m_lLines.setAutoDelete(true);
m_mLines.setAutoDelete(true);
m_locations.setAutoDelete(true);
m_aggros.setAutoDelete(true);

// clear the structure
clear();
}

MapData::~MapData()
{
}

void MapData::clear()
{
m_fileName = "";
m_zoneLongName = "";
m_zoneShortName = "";
m_minX = -50;
m_maxX = 50;
m_minY = -50;
m_maxY = 50;
updateBounds();
m_lLines.clear();
m_mLines.clear();
m_locations.clear();
m_aggros.clear();

m_mapLoaded = false;
m_imageLoaded = false;

m_editLineM = NULL;
m_editLocation = NULL;
m_zoneZEM = 75;
}

void MapData::loadMap(const QString& fileName, bool import)
{
int16_t mx, my, mz;
uint numPoints;
int16_t globHeight=0;
bool globHeightSet = false;
int filelines = 1; // number of lines in map file
QString name;
QString color;
uint16_t rangeVal;
uint32_t linePoints;
uint32_t specifiedLinePoints;
MapLineL* currentLineL = NULL;
MapLineM* currentLineM = NULL;

// set the map filename
setFileName(fileName);

// clear any existing map data (if not importing)
if (!import)
clear();

/* Kind of stupid to try a non-existant map, don't you think? */
if (fileName.contains("/.map") != 0)
return;


const char* filename = (const char*)fileName;

QFile mapFile(fileName);

if (!mapFile.open(IO_ReadOnly))
{
seqWarn("Error opening map file '%s'!", filename);

return;
}

// note the file name
m_fileName = filename;

// allocate memory in a QCString to hold the entire file contents
QCString textData(mapFile.size() + 1);

// read the file as one big chunk
mapFile.readBlock(textData.data(), textData.size());

// construct a regex to deal with either style line termination
QRegExp lineTerm("[\r\n]{1,2}");

// split the data into lines at the line termination
QStringList lines = QStringList::split(lineTerm,
QString::fromUtf8(textData), false);


// start iterating over the lines
QStringList::Iterator lit = lines.begin();

filelines = 1;

#ifdef DEBUGMAPLOAD
seqDebug("Zone info line: %s", (const char*)(*lit));
#endif

QString fieldSep = ",";

// split the line into fields
QStringList fields = QStringList::split(fieldSep, *lit);

size_t count = fields.count();
if (!count)
{
seqWarn("Error, no fields in first line of map file '%s'",
filename);
return;
}

if (count < 2)
{
seqWarn("Error, too few fields in first line of map file '%s'",
filename);
return;
}

// start iterator over the fields
QStringList::Iterator fit = fields.begin();

m_zoneLongName = (*fit++);
m_zoneShortName = (*fit++);

if (count > 2)
{
m_size.setWidth((*fit++).toInt());
m_size.setHeight((*fit++).toInt());
}

// start looping at the next map line
for (++lit; lit != lines.end(); ++lit)
{
// increment line count
filelines++;

#ifdef DEBUGMAPLOAD
seqWarn("Map line %d: %s", filelines, (const char*)*lit);
#endif

// split the line into fields
fields = QStringList::split(fieldSep, *lit);

// skip empty lines
if (fields.isEmpty())
continue;

// entry type is the first character of the line
QChar entryType = fields.first().at(0);

// pop the entry type off the front of the fields list
fields.pop_front();

// start at the first argument to the entry
fit = fields.begin();

// get the field count
count = fields.count();

bool ok;

switch (entryType)
{
case 'M':
{
#ifdef DEBUGMAPLOAD
seqDebug("M record [%d] [%d fields]: %s",
filelines, count, (const char*)*lit);
#endif

if (count < 3)
{
seqWarn("Error reading M line %d on map '%s'! %d is too few fields",
filelines, filename, count);
continue;
}

// calculate the number of line points
linePoints = ((count - 3) / 3);

// only bother going forward if there will be enough line points
if (linePoints < 2)
{
seqWarn("M Line %d in map '%s' only had %d points, not loading.",
filelines, filename, linePoints );
continue;
}

// Line Name
name = (*fit++);

// Line Color
color = (*fit++);
if (color.isEmpty())
color = "#7F7F7F";

// Number of points
specifiedLinePoints = (*fit++).toUInt(&ok);
if (!ok)
{
seqWarn("Error reading number of points on line %d in map '%s'!",
filelines, filename);
continue;
}

if ((specifiedLinePoints != linePoints) && (specifiedLinePoints != 0))
{
seqWarn("M Line %d in map '%s' has %d points as opposed to the %d points it specified!",
filelines, filename, linePoints, specifiedLinePoints);
}

// create an M line
currentLineM = new MapLineM(name, color, linePoints);

numPoints = 0;
while ((fit != fields.end()) && (numPoints < linePoints))
{
// X coord
mx = (*fit++).toShort();
my = (*fit++).toShort();
mz = (*fit++).toShort();

// set the point data
currentLineM->setPoint(numPoints, mx, my, mz);

// increment point count
numPoints++;
}

// calculate the XY bounds of the line
currentLineM->calcBounds();

// get the bounding rect
const QRect& bounds = currentLineM->boundingRect();

// adjust map boundaries based on the bounding rect
quickCheckPos(bounds.left(), bounds.top());
quickCheckPos(bounds.right(), bounds.bottom());

// add it to the list of L lines
m_mLines.append(currentLineM);
}
break;

case 'L':
{
#ifdef DEBUGMAPLOAD
seqDebug("L record [%d] [%d fields] %s",
filelines, count, (const char*)*lit);
#endif

if (count < 3)
{
seqWarn("Error reading L line %d on map '%s'! %d is too few fields",
filelines, filename, count);
continue;
}

// calculate the number of line points
linePoints = ((count - 3) >> 1);

// only bother going forward if there will be enough line points
if (linePoints < 2)
{
seqWarn("L Line %d in map '%s' only had %d points, not loading.",
filelines, filename, linePoints);
continue;
}

// Line Name
name = (*fit++);

// Line Color
color = (*fit++);
if (color.isEmpty())
color = "#7F7F7F";

// Number of points
specifiedLinePoints = (*fit++).toUInt(&ok);
if (!ok)
{
seqWarn("Error reading number of points on line %d in map '%s'!",
filelines, filename);
continue;
}

if ((specifiedLinePoints != linePoints) && (specifiedLinePoints != 0))
{
seqWarn("L Line %d in map '%s' has %d points as opposed to the %d points it specified!",
filelines, filename, linePoints, specifiedLinePoints);
}

// create the appropriate style L line depending on if the global
// height has been set
if (globHeightSet)
currentLineL = new MapLineL(name, color, linePoints, globHeight);
else
currentLineL = new MapLineL(name, color, linePoints);

numPoints = 0;

// keep going until we run out of fields...
while ((fit != fields.end()) && (numPoints < linePoints))
{
// X coord
mx = (*fit++).toShort();

// Y coord
my = (*fit++).toShort();

// store the point
currentLineL->setPoint(numPoints, mx, my);

// increment point count
numPoints++;
}

// calculate the XY bounds of the line
currentLineL->calcBounds();

// get the bounding rect
const QRect& bounds = currentLineL->boundingRect();

// adjust map boundaries based on the bounding rect
quickCheckPos(bounds.left(), bounds.top());
quickCheckPos(bounds.right(), bounds.bottom());

// add it to the list of L lines
m_lLines.append(currentLineL);
}
break;

case 'P':
{
#ifdef DEBUGMAPLOAD
seqWarn("P record [%d] [%d fields]: %s",
filelines, count, (const char*)*lit);
#endif

if (count < 4)
{
seqWarn("Error reading P line %d on map '%s'! %d is too few fields",
filelines, filename, count);
continue;
}

name = (*fit++); // Location name
color = (*fit++); // Location color
mx = (*fit++).toShort();
my = (*fit++).toShort();

if (count == 5)
{
mz = (*fit++).toShort();

// add the appropriate style Location depending on if the global height is set
m_locations.append(new MapLocation(name, color, mx, my, mz));
}

// add the appropriate style Location depending on if the global
// height has been set
if (globHeightSet)
m_locations.append(new MapLocation(name, color, mx, my, globHeight));
else
m_locations.append(new MapLocation(name, color, mx, my));

// adjust map boundaries
quickCheckPos(mx, my);
}
break;

case 'A': //Creates aggro ranges.
{
#ifdef DEBUGMAPLOAD
seqWarn("A record [%d] [%d fields]: %s",
filelines, count, (const char*)*lit);
#endif

if (count < 2)
{
seqWarn("Line %d in map '%s' has an A record with too few fields (%d)!",
filelines, filename, count);
break;
}

name = (*fit++);
if (name.isEmpty())
{
seqWarn("Line %d in map '%s' has an A marker with no Name expression!",
filelines, filename);
break;
}
rangeVal = (*fit++).toUShort();
if (!rangeVal)
{
seqWarn("Line %d in map '%s' has an A marker with no or 0 Range radius!",
filelines, filename);
break;
}

// create and add a new aggro object
m_aggros.append(new MapAggro(name, rangeVal));

break;
case 'H': //Sets global height for L lines.
#ifdef DEBUGMAPLOAD
seqDebug("H record [%d] [%d fields]: %s",
filelines, count, (const char*)*lit);
#endif

if (count < 1)
{
seqWarn("Line %d in map '%s' has an H record with too few fields (%d)!",
filelines, filename, count);
break;
}

// get global height
globHeight = (*fit++).toShort(&ok);
if (!ok)
{
seqWarn("Line %d in map '%s' has an H marker with invalid Z!",
filelines, filename);
break;
}
globHeightSet = true;
}
break;

case 'Z': // Quick and dirty ZEM implementation
{
#ifdef DEBUGMAPLOAD
seqWarn("Z record [%d] [%d fields]: %s",
filelines, count, (const char*)*lit);
#endif

if (count < 1)
{
seqWarn("Line %d in map '%s' has a Z record with too few fields (%d)!",
filelines, filename, count);
break;
}

m_zoneZEM = (*fit++).toUShort(&ok);
if (!ok)
{
seqWarn("Line %d in map '%s' has an Z marker with invalid ZEM!",
filelines, filename);
break;
}
#ifdef DEBUGMAPLOAD
seqDebug("ZEM set to %d", m_zoneZEM);
#endif
}
break;

}
}

// calculate the bounding rect
updateBounds();

m_mapLoaded = true;

m_imageLoaded = false;
QString imageFileName = filename;
imageFileName.truncate(imageFileName.findRev('.')) ;
imageFileName += ".pgm";

if (m_image.load(imageFileName))
{
m_imageLoaded = true;
seqInfo("Loaded map image: '%s'", (const char*)imageFileName);
}

seqInfo("Loaded map: '%s'", filename);
}

void MapData::loadSOEMap(const QString& fileName, bool import)
{
int16_t x1, y1, z1;
int16_t x2, y2, z2;
MapPoint src, dest, oldSrc;
uint8_t r, g, b;
uint32_t numPoints = 0;
uint32_t checkPoint = 0;
int filelines = 1; // number of lines in map file
QString name;
MapLineM* currentLineM = 0;
size_t count;

// if the same map is already loaded, don't reload it.
if (m_mapLoaded && (m_fileName.lower() == fileName.lower()))
return;

// set the map filename
setFileName(fileName);

// clear any existing map data if not importing
if (!import)
clear();

/* Kind of stupid to try a non-existant map, don't you think? */
if (fileName.contains("/.txt") != 0)
return;

const char* filename = (const char*)fileName;

QFile mapFile(fileName);

if (!mapFile.open(IO_ReadOnly))
{
seqWarn("Error opening map file '%s'!", filename);

return;
}

// note the file name
m_fileName = filename;

// allocate memory in a QCString to hold the entire file contents
QCString textData(mapFile.size() + 1);

// read the file as one big chunk
mapFile.readBlock(textData.data(), textData.size());

// construct a regex to deal with either style line termination
QRegExp lineTerm("[\r\n]{1,2}");

// split the data into lines at the line termination
QStringList lines = QStringList::split(lineTerm,
QString::fromUtf8(textData), false);


// start iterating over the lines
QStringList::Iterator lit = lines.begin();

filelines = 1;

QRegExp fieldSep(",\\s*");

// split the line into fields
QStringList fields;
QStringList::Iterator fit;

// use the file base name as the zone long/short name, it isn't perfect,
// but neither is this file format
QFileInfo fileInfo(m_fileName);
QRegExp reStripTrailer("_[1-3]");

m_zoneLongName = fileInfo.baseName().remove(reStripTrailer);
m_zoneShortName = m_zoneLongName;

// start looping at the next map line
for (; lit != lines.end(); ++lit)
{
// increment line count
filelines++;

#ifdef DEBUGMAPLOAD
seqDebug("Map line %d: %s", filelines, (const char*)*lit);
#endif

// entry type is the first character of the line
QChar entryType = (*lit).at(0);

// remove the entryType and the leading space
(*lit).remove(0, 2);

// split the line into fields
fields = QStringList::split(fieldSep, *lit);

// skip empty lines
if (fields.isEmpty())
continue;

// start at the first argument to the entry
fit = fields.begin();

// get the field count
count = fields.count();

switch (entryType)
{
case 'L':
{
#ifdef DEBUGMAPLOAD
seqDebug("L record [%d] [%d fields]: %s",
filelines, count, (const char*)*lit);
#endif

if (count != 9)
{
seqWarn("Error reading L line %d on map '%s'! %d is an incorrect field count!",
filelines, filename, count);
continue;
}

x1 = -int16_t((*fit++).toFloat());
y1 = -int16_t((*fit++).toFloat());
z1 = int16_t((*fit++).toFloat());
x2 = -int16_t((*fit++).toFloat());
y2 = -int16_t((*fit++).toFloat());
z2 = int16_t((*fit++).toFloat());
r = (*fit++).toUShort();
g = (*fit++).toUShort();
b = (*fit).toUShort();

if (!currentLineM ||
!currentLineM->point(checkPoint).isEqual(x2, y2, z2) ||
(currentLineM->color().red() != r) ||
(currentLineM->color().green() != g) ||
(currentLineM->color().blue() != b))
{
numPoints = 0;

// create an M line (start with 2 points because of SOE's lame
// format).
currentLineM = new MapLineM("soe", QColor(r, g, b), 2);

// set the first point
currentLineM->setPoint(numPoints++, x2, y2, z2);

// set the second point
currentLineM->setPoint(checkPoint = numPoints++, x1, y1, z1);

// add it to the list of M lines
m_mLines.append(currentLineM);
}
else
{
// if necessary, add room for a point
if (currentLineM->size() < (numPoints+1))
currentLineM->resize(numPoints+1);

// add the point
currentLineM->setPoint(checkPoint = numPoints++, x1, y1, z1);
}

// calculate the XY bounds of the line
currentLineM->calcBounds();

// get the bounding rect
const QRect& bounds = currentLineM->boundingRect();

// adjust map boundaries based on the bounding rect
quickCheckPos(bounds.left(), bounds.top());
quickCheckPos(bounds.right(), bounds.bottom());
}
break;

case 'P':
{
#ifdef DEBUGMAPLOAD
seqDebug("P record [%d] [%d fields]: %s",
filelines, count, (const char*)*lit);
#endif

if (count != 8)
{
seqWarn("Error reading L line %d on map '%s'! %d is an incorrect field count!",
filelines, filename, count);
continue;
}

// get all the fields
x1 = -int16_t((*fit++).toFloat());
y1 = -int16_t((*fit++).toFloat());
z1 = int16_t((*fit++).toFloat());
r = (*fit++).toUShort();
g = (*fit++).toUShort();
b = (*fit++).toUShort();
fit++; // skip unknown
name = (*fit); // Location name, conver

// convert underscores to spaces.
name.replace("_", " ");

// add it to the list of locations
m_locations.append(new MapLocation(name, QColor(r, g, b),
x1, y1, z1));

// adjust map boundaries
quickCheckPos(x1, y1);
}
break;

}
}

// calculate the bounding rect
updateBounds();

m_mapLoaded = true;

m_imageLoaded = false;
QString imageFileName = filename;
imageFileName.truncate(imageFileName.findRev('.')) ;
imageFileName += ".pgm";

if (m_image.load(imageFileName))
{
m_imageLoaded = true;
seqInfo("Loaded map image: '%s'", (const char*)imageFileName);
}

seqInfo("Loaded SOE map: '%s'", filename);
}

void MapData::saveMap(const QString& fileName) const
{
#ifdef DEBUG
debug ("saveMap()");
#endif /* DEBUG */
FILE * fh;
uint32_t i;

const char* filename;
if (!fileName.isEmpty())
filename = (const char*)fileName;
else
filename = (const char*)m_fileName;

if ((fh = fopen(filename, "w")) == NULL)
{
seqWarn("Error saving map '%s'!", filename);
return;
}

// write out header info
fprintf(fh, "%s,%s,%d,%d\n",
(const char*)m_zoneLongName, (const char*)m_zoneShortName,
m_size.width(), m_size.height());

// write out ZEM info if non-default
if (m_zoneZEM != 75)
fprintf(fh, "Z,%i\n", m_zoneZEM);

// write out the L (2D) lines with possible fixed Z
bool heightSet = false;
int16_t lastHeightSet = 0;
MapLineL* currentLineL;
QPtrListIterator<MapLineL> mlit(m_lLines);
for (currentLineL = mlit.toFirst();
currentLineL != NULL;
currentLineL = ++mlit)
{
// was the global height set?
if (currentLineL->heightSet())
{
// if no height was set previously, or this one doesn't match the previously
// set height, write out an H record
if ((!heightSet) || (lastHeightSet != currentLineL->z()))
{
// write out an H record.
fprintf(fh, "H,%i\n", currentLineL->z());

// note the last height set, and that it was set
lastHeightSet = currentLineL->z();
heightSet = true;
}
}

// write out the start of the line info
fprintf (fh, "L,%s,%s,%d",
(const char*)currentLineL->name(),
(const char*)currentLineL->colorName(),
currentLineL->size());

// write out all the 2D points in the line
for(i = 0; i < currentLineL->size(); i++)
{
QPoint curQPoint = currentLineL->point(i);
fprintf (fh, ",%d,%d", curQPoint.x(), curQPoint.y());
}

// terminate the line
fprintf (fh, "\n");
}

// write out the M (3D) lines
MapLineM* currentLineM;
QPtrListIterator<MapLineM> mmit(m_mLines);
for (currentLineM = mmit.toFirst();
currentLineM;
currentLineM = ++mmit)
{
// write out the start of the line info
fprintf (fh, "M,%s,%s,%d",
(const char*)currentLineM->name(),
(const char*)currentLineM->colorName(),
currentLineM->size());

// write out all the 3D points in the line
for(i = 0; i < currentLineM->size(); i++)
{
MapPoint curMPoint = currentLineM->point(i);

fprintf (fh, ",%d,%d,%d",
curMPoint.x(), curMPoint.y(), curMPoint.z());
}
// terminate the line
fprintf (fh, "\n");
}

// write out location information
QPtrListIterator<MapLocation> lit(m_locations);
for(; lit.current(); ++lit)
{
MapLocation* currentLoc = lit.current();

if (!currentLoc->heightSet())
fprintf (fh, "P,%s,%s,%d,%d\n",
(const char*)currentLoc->name(),
(const char*)currentLoc->colorName(),
currentLoc->x(),
currentLoc->y());
else
fprintf (fh, "P,%s,%s,%d,%d,%d\n",
(const char*)currentLoc->name(),
(const char*)currentLoc->colorName(),
currentLoc->x(),
currentLoc->y(),
currentLoc->z());
}

// write out aggro information
QPtrListIterator<MapAggro> ait(m_aggros);
for (; ait.current(); ++ait)
{
MapAggro* currentAggro = ait.current();

fprintf (fh, "A,%s,%d\n",
(const char*)currentAggro->name(), currentAggro->range());
}

#ifdef DEBUGMAP
seqDebug("saveMap() - map '%s' saved with %d L lines, %d M lines, %d locations", filename,
m_lLines.count(), m_mLines.count(), m_locations.count());
#endif

fclose (fh);

seqInfo("Saved map: '%s'", filename);
}

void MapData::saveSOEMap(const QString& fileName) const
{
#ifdef DEBUG
debug ("saveMap()");
#endif /* DEBUG */
FILE * fh;
uint i;

const char* filename;
if (!fileName.isEmpty())
filename = (const char*)fileName;
else
filename = (const char*)m_fileName;

if ((fh = fopen(filename, "w")) == NULL)
{
seqWarn("Error saving map '%s'!", filename);
return;
}

// write out the L (2D) lines with possible fixed Z
uint8_t r, g, b;
float z1;
QString name;
MapLineL* currentLineL;
QPtrListIterator<MapLineL> mlit(m_lLines);
for (currentLineL = mlit.toFirst();
currentLineL != NULL;
currentLineL = ++mlit)
{
z1 = float(currentLineL->z());

const QColor& color = currentLineL->color();
r = color.red();
g = color.green();
b = color.blue();

QPoint lastQPoint = currentLineL->point(0);

// write out all the 2D points in the line
for(i = 1; i < currentLineL->size(); ++i)
{
const QPoint& curQPoint = currentLineL->point(i);

// write out the line info
fprintf (fh, "L %.1f, %.1f, %.1f, %.1f, %.1f, %.1f, %d, %d, %d\n",
-float(curQPoint.x()), -float(curQPoint.y()), z1,
-float(lastQPoint.x()), -float(lastQPoint.y()), z1,
r, g, b);

lastQPoint = curQPoint;
}

// terminate the line
fprintf (fh, "\n");
}

// write out the M (3D) lines
MapLineM* currentLineM;
QPtrListIterator<MapLineM> mmit(m_mLines);
for (currentLineM = mmit.toFirst();
currentLineM;
currentLineM = ++mmit)
{
const QColor& color = currentLineM->color();
r = color.red();
g = color.green();
b = color.blue();

MapPoint lastMPoint = currentLineM->point(0);

// write out all the 3D points in the line
for(i = 1; i < currentLineM->size() ; ++i)
{
const MapPoint& curMPoint = currentLineM->point(i);

// write out the line info
fprintf (fh, "L %.1f, %.1f, %.1f, %.1f, %.1f, %.1f, %d, %d, %d\n",
-float(curMPoint.x()), -float(curMPoint.y()), float(curMPoint.z()),
-float(lastMPoint.x()), -float(lastMPoint.y()), float(lastMPoint.z()),
r, g, b);

lastMPoint = curMPoint;
}
}

// write out location information
QPtrListIterator<MapLocation> lit(m_locations);
MapLocation* currentLoc;
for(currentLoc = lit.toFirst();
currentLoc;
currentLoc = ++lit)
{
const QColor& color = currentLoc->color();

// convert spaces to underscores
name = currentLoc->name();
name.replace(" ", "_");

fprintf(fh, "P %.1f, %.1f, %.1f, %d, %d, %d, 3, %s\n",
-float(currentLoc->x()), -float(currentLoc->y()),
float(currentLoc->z()),
color.red(), color.green(), color.blue(),
(const char*)name);
}
#ifdef DEBUGMAP
seqDebug("saveMap() - map '%s' saved with %d L lines, %d M lines, %d locations", filename,
m_lLines.count(), m_mLines.count(), m_locations.count());
#endif

fclose (fh);

seqInfo("Saved SOE map: '%s'", filename);
}

bool MapData::isAggro(const QString& name, uint16_t* range) const
{
MapAggro* aggro;
QPtrListIterator<MapAggro> ait(m_aggros);
for (aggro = ait.toFirst();
aggro != NULL;
aggro = ++ait)
{
// does the name match this aggro?
if (name.find(aggro->name(), 0, false) != -1)
{
if (range != NULL)
*range = aggro->range();

return true;
}
}

return false;
}

void MapData::addLocation(const QString& name,
const QString& color,
const QPoint& point)
{
// create the new location
m_editLocation = new MapLocation(name, color, point);

// add it to the list of locations
m_locations.append(m_editLocation);
}

void MapData::setLocationName(const QString& name)
{
// make sure there is a location to edit
if (m_editLocation == NULL)
return;

// set the location name
m_editLocation->setName(name);
}

void MapData::setLocationColor(const QString& color)
{
// make sure there is a location to edit
if (m_editLocation == NULL)
return;

// set the location color
m_editLocation->setColor(color);
}

void MapData::startLine(const QString& name,
const QString& color,
const MapPoint& point)
{
// create the new line, with just the first point
m_editLineM = new MapLineM(name, color, point);

// calculate the XY bounds of the line
m_editLineM->calcBounds();

// add line to the line list
m_mLines.append(m_editLineM);
}

void MapData::addLinePoint(const MapPoint& point)
{
// make sure there is a line to add to
if (m_editLineM == NULL)
return;

uint32_t pos = m_editLineM->size();

// increase the size of the line by one point
m_editLineM->resize(pos + 1);

// set the point data
m_editLineM->setPoint(pos, point);

// calculate the XY bounds of the line
m_editLineM->calcBounds();
}

void MapData::delLinePoint(void)
{
// make sure there is a line to add to
if (m_editLineM == NULL)
return;

// remove the last entry from the line
m_editLineM->resize(m_editLineM->size() - 1);

// if the user has deleted that last point in the line, delete the line
if (m_editLineM->size() == 0)
{
// remove the line
m_mLines.remove(m_editLineM);

// clear the currently edited line entry
m_editLineM = NULL;
}
else
{
// calculate the XY bounds of the line
m_editLineM->calcBounds();
}
}

void MapData::setLineName(const QString& name)
{
// make sure there is a line to add to
if (m_editLineM == NULL)
return;

// set the line name
m_editLineM->setName(name);
}

void MapData::setLineColor(const QString& color)
{
// make sure there is a line to add to
if (m_editLineM == NULL)
return;

// set the line color
m_editLineM->setColor(color);
}

void MapData::scaleDownZ(int16_t factor)
{
// first scale down the L lines
MapLineL* currentLineL;
QPtrListIterator<MapLineL> mlit(m_lLines);
for (currentLineL = mlit.toFirst();
currentLineL != NULL;
currentLineL = ++mlit)
currentLineL->setZPos(currentLineL->z() / factor);

// finish off by scaling down the M lines
MapLineM* currentLineM;
MapPoint* mData;
size_t numPoints;
size_t i;
QPtrListIterator<MapLineM> mmit(m_mLines);
for (currentLineM = mmit.toFirst();
currentLineM;
currentLineM = ++mmit)
{
// get the number of points in the line
numPoints = currentLineM->size();

// get the underlying array
mData = currentLineM->data();

for (i = 0; i < numPoints; i++)
mData[i].setZPos(mData[i].z() / factor);
}
}

void MapData::scaleUpZ(int16_t factor)
{
// first scale down the L lines
MapLineL* currentLineL;
QPtrListIterator<MapLineL> mlit(m_lLines);
for (currentLineL = mlit.toFirst();
currentLineL != NULL;
currentLineL = ++mlit)
currentLineL->setZPos(currentLineL->z() * factor);

// finish off by scaling down the M lines
MapLineM* currentLineM;
MapPoint* mData;
size_t numPoints;
size_t i;
QPtrListIterator<MapLineM> mmit(m_mLines);
for (currentLineM = mmit.toFirst();
currentLineM;
currentLineM = ++mmit)
{
// get the number of points in the line
numPoints = currentLineM->size();

// get the underlying array
mData = currentLineM->data();

for (i = 0; i < numPoints; i++)
mData[i].setZPos(mData[i].z() * factor);
}
}

void MapData::paintGrid(MapParameters& param, QPainter& p) const
{
// if nothing will be drawn, don't go through the motions
if (!param.showGridLines() && !param.showGridTicks())
return;

/* Paint the grid */

// set the brush
p.setBrush(QColor (80, 80, 80));

// Need to put in some stuff to auto resize the gridres
// based on the screenLength and map size
int gridres = param.gridResolution();
int offsetPos;
int pos;
const QRect& screenBounds = param.screenBounds();
int lastGrid = (maxX() / gridres) + 1;

// start from the minimum position and increment to the last grid position
for (int gx = (minX() / gridres) - 1;
gx <= lastGrid;
gx++)
{
// calculate position
pos = gx * gridres;

// haven't reached visible portion yet, continue
if (screenBounds.left() > pos)
continue;

// past the visible portion, stop
if (screenBounds.right() < pos)
break;

// calculate offset
offsetPos = param.calcXOffsetI(pos);

// if grid lines are show, draw them
if (param.showGridLines())
{
p.setPen(param.gridLineColor());
p.drawLine(offsetPos, 0,
offsetPos, param.screenLengthY());
}

// if grid ticks are shown, draw them
if (param.showGridTicks())
{
p.setPen(param.gridTickColor());
p.drawText(offsetPos, param.screenLengthY(), QString::number(pos));
}
}

lastGrid = (maxY() / gridres) + 1;

// start from the minimum position and increment to the last grid position
for (int gy = (minY() / gridres) - 1;
gy <= lastGrid;
gy++)
{
// calculate position
pos = gy * gridres;

// haven't reached visible portion yet, continue
if (screenBounds.top() > pos)
continue;

if (screenBounds.bottom() < pos)
break;

// calculate the offset position
offsetPos = param.calcYOffsetI(pos);

// if grid lines are shown, draw them
if (param.showGridLines())
{
p.setPen(param.gridLineColor());
p.drawLine(0, offsetPos,
param.screenLengthX(), offsetPos);
}

// if grid ticks are shown, draw thm
if (param.showGridTicks())
{
p.setPen(param.gridTickColor());
p.drawText(0, offsetPos, QString::number(pos));
}

}
}

void MapData::paintLines(MapParameters& param, QPainter& p) const
{
//----------------------------------------------------------------------
/* Paint the lines */
#ifdef DEBUGMAP
seqDebug("Paint the lines");
#endif
// Note: none of the map loops below check for zero length lines,
// because all line manipulation code makes sure that they don't occur

// stuff used no matter how the map is drawn
MapLineL* currentLineL;
MapLineM* currentLineM;

// set the brush
p.setBrush(QColor (80, 80, 80));

const QRect& screenBounds = param.screenBounds();

// determine which painting method is to be used.
// no depth filtering, cool, let's make this quick and easy
bool lastInBounds;
bool curInBounds;
int16_t curX, curY;
int cur2DX, cur2DY;
uint numPoints;
QPoint* lData;
MapPoint* mData;

// first paint the L lines
QPtrListIterator<MapLineL> mlit(m_lLines);
for (currentLineL = mlit.toFirst();
currentLineL != NULL;
currentLineL = ++mlit)
{
// if line is outside the currently visible region, skip it.
if (!currentLineL->boundingRect().intersects(screenBounds))
continue;

// get the number of points in the line
numPoints = currentLineL->size();

// get the underlying array
lData = currentLineL->data();

// set pen color
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineL->colorName());
#endif
p.setPen(currentLineL->bgSafeColor( param.backgroundColor()));

cur2DX = lData[0].x();
cur2DY = lData[0].y();

// see if the starting position is in bounds
lastInBounds = inRect(screenBounds, cur2DX, cur2DY);

// move to the starting position
p.moveTo(param.calcXOffsetI(cur2DX),
param.calcYOffsetI(cur2DY));

// iterate over all the points in the line
for (uint32_t i = 1; i < numPoints; i++)
{
cur2DX = lData[i].x();
cur2DY = lData[i].y();

// determine if the current position is in bounds
curInBounds = inRect(screenBounds, cur2DX, cur2DY);

// draw the line segment if either end is in bounds
if (lastInBounds || curInBounds)
p.lineTo(param.calcXOffsetI(cur2DX),
param.calcYOffsetI(cur2DY));
else
p.moveTo(param.calcXOffsetI(cur2DX),
param.calcYOffsetI(cur2DY));

// current becomes the last
lastInBounds = curInBounds;
}
}

// then paint the M lines
QPtrListIterator<MapLineM> mmit(m_mLines);
for (currentLineM = mmit.toFirst();
currentLineM;
currentLineM = ++mmit)
{
// if line is outside the currently visible region, skip it.
if (!currentLineM->boundingRect().intersects(screenBounds))
continue;

// get the number of points in the line
numPoints = currentLineM->size();

// get the underlying array
mData = currentLineM->data();

// set pen color
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineM->colorName());
#endif
p.setPen(currentLineM->bgSafeColor( param.backgroundColor()));

curX = mData[0].x();
curY = mData[0].y();

// see if the starting position is in bounds
lastInBounds = inRect(screenBounds, curX, curY);

// move to the starting position
p.moveTo(param.calcXOffsetI(curX),
param.calcYOffsetI(curY));

// iterate over all the points in the line
for (uint32_t i = 1; i < numPoints; i++)
{
curX = mData[i].x();
curY = mData[i].y();

// determine if the current position is in bounds
curInBounds = inRect(screenBounds, curX, curY);

// draw the line segment if either end is in bounds
if (lastInBounds || curInBounds)
p.lineTo(param.calcXOffsetI(curX),
param.calcYOffsetI(curY));
else
p.moveTo(param.calcXOffsetI(curX),
param.calcYOffsetI(curY));

// current becomes the last
lastInBounds = curInBounds;
}
}
}

void MapData::paintDepthFilteredLines(MapParameters& param, QPainter& p) const
{
//----------------------------------------------------------------------
/* Paint the lines */
#ifdef DEBUGMAP
seqDebug("Paint Depth Filtered lines");
#endif
// Note: none of the map loops below check for zero length lines,
// because all line manipulation code makes sure that they don't occur

// stuff used no matter how the map is drawn
MapLineL* currentLineL;
MapLineM* currentLineM;

// set the brush
p.setBrush(QColor (80, 80, 80));

const QRect& screenBounds = param.screenBounds();

// map depth filtering, without faded floors
bool lastInBounds;
bool curInBounds;
int16_t curX, curY, curZ;
int cur2DX, cur2DY;
uint32_t numPoints;
QPoint* lData;
MapPoint* mData;

// get the players position for it's Z information
MapPoint playerPos = param.player();

// first paint the L lines
QPtrListIterator<MapLineL> mlit(m_lLines);
for (currentLineL = mlit.toFirst();
currentLineL != NULL;
currentLineL = ++mlit)
{
// if line is outside the currently visible region, skip it.
if (!currentLineL->boundingRect().intersects(screenBounds))
continue;

// since it's an L type line, check for the depth is easy
// just check if height is set, and if so, check if it's within range
if (currentLineL->heightSet() &&
!inRoom(param.playerHeadRoom(), param.playerFloorRoom(),
currentLineL->z()))
continue; // outside of range, continue to the next line

// get the number of points in the line
numPoints = currentLineL->size();

// get the underlying array
lData = currentLineL->data();

// set the line color
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineL->color());
#endif
p.setPen(currentLineL->bgSafeColor( param.backgroundColor()));

cur2DX = lData[0].x();
cur2DY = lData[0].y();

// see if the starting position is in bounds
lastInBounds = inRect(screenBounds, cur2DX, cur2DY);

// move to the starting position
p.moveTo(param.calcXOffsetI(cur2DX),
param.calcYOffsetI(cur2DY));

// iterate over all the points in the line
for (uint32_t i = 1; i < numPoints; i++)
{
cur2DX = lData[i].x();
cur2DY = lData[i].y();

// determine if the current position is in bounds
curInBounds = inRect(screenBounds, cur2DX, cur2DY);

// draw the line segment if either end is in bounds
if (lastInBounds || curInBounds)
p.lineTo(param.calcXOffsetI(cur2DX),
param.calcYOffsetI(cur2DY));
else
p.moveTo(param.calcXOffsetI(cur2DX),
param.calcYOffsetI(cur2DY));

// current becomes the last
lastInBounds = curInBounds;
}
}

// then paint the M lines
QPtrListIterator<MapLineM> mmit(m_mLines);
for (currentLineM = mmit.toFirst();
currentLineM;
currentLineM = ++mmit)
{
// if line is outside the currently visible region, skip it.
if (!currentLineM->boundingRect().intersects(screenBounds))
continue;

// get the number of points in the line
numPoints = currentLineM->size();

// get the underlying array
mData = currentLineM->data();

// set the line color
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineM->color());
#endif
p.setPen(currentLineM->bgSafeColor( param.backgroundColor()));

// get current coordinates
curX = mData[0].x();
curY = mData[0].y();
curZ = mData[0].z();

// see if the starting position is in bounds
lastInBounds = (inRect(screenBounds, curX, curY) &&
inRoom(param.playerHeadRoom(), param.playerFloorRoom(),
curZ));

#ifdef DEBUGMAP
seqDebug("Line has %i points:", currentLineM->size());
#endif

// move to the starting position
p.moveTo(param.calcXOffsetI(curX),
param.calcYOffsetI(curY));

// iterate over all the points in the line
for (uint32_t i = 1; i < numPoints; i++)
{
// get current coordinates
curX = mData[i].x();
curY = mData[i].y();
curZ = mData[i].z();

// determine if the current position is in bounds
curInBounds = (inRect(screenBounds, curX, curY) &&
inRoom(param.playerHeadRoom(), param.playerFloorRoom(),
curZ));

// draw the line segment if either end is in bounds
if (lastInBounds || curInBounds)
p.lineTo(param.calcXOffsetI(curX),
param.calcYOffsetI(curY));
else
p.moveTo(param.calcXOffsetI(curX),
param.calcYOffsetI(curY));

// current becomes the last
lastInBounds = curInBounds;
}
}
}

void MapData::paintFadedFloorsLines(MapParameters& param, QPainter& p) const
{
//----------------------------------------------------------------------
/* Paint the lines */
#ifdef DEBUGMAP
seqDebug("Paint Faded Floor lines");
#endif
// Note: none of the map loops below check for zero length lines,
// because all line manipulation code makes sure that they don't occur

// stuff used no matter how the map is drawn
MapLineL* currentLineL;
MapLineM* currentLineM;

// set the brush
p.setBrush(QColor (80, 80, 80));

const QRect& screenBounds = param.screenBounds();

// depth filtering with faded floors
int oldColor, newColor, useColor;
bool lastInBounds;
bool curInBounds;
int16_t curX, curY, curZ;
int cur2DX, cur2DY, cur2DZ;
uint32_t numPoints;
QPoint* lData;
MapPoint* mData;

// get the players position for it's Z information
MapPoint playerPos = param.player();

double topm = 0 - 255.0 / (double)param.headRoom();
double botm = 255.0 / (double)param.floorRoom();
double topb = 255 - (topm * playerPos.z());
double botb = 255 - (botm * playerPos.z());

// first paint the L lines
QPtrListIterator<MapLineL> mlit(m_lLines);
for (currentLineL = mlit.toFirst();
currentLineL != NULL;
currentLineL = ++mlit)
{
// if line is outside the currently visible region, skip it.
if (!currentLineL->boundingRect().intersects(screenBounds))
continue;

// get the number of points in the line
numPoints = currentLineL->size();

// get the underlying array
lData = currentLineL->data();

// get first point coordinates
cur2DX = lData[0].x();
cur2DY = lData[0].y();
cur2DZ = currentLineL->z();

// color determination is different depending on if a height was set
if (!currentLineL->heightSet())
{
// set the line color
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineL->color());
#endif
p.setPen(currentLineL->bgSafeColor( param.backgroundColor()));
}
else
{
// calculate color to use for the line (since L type, only do this once)
if (currentLineL->z() > playerPos.z())
useColor = (int)((cur2DZ * topm) + topb);
else
useColor = (int)((cur2DZ * botm) + botb);

if (useColor > 255) useColor = 255;
if (useColor < 0) useColor = 0;

// set the line color
#ifdef DEBUGMAP
seqDebug("lineColor = '#%2x%2x%2x'", useColor, useColor, useColor);
#endif
p.setPen(QColor(useColor, useColor, useColor));
}

// see if the starting position is in bounds
lastInBounds = inRect(screenBounds, cur2DX, cur2DY);

// move to the starting position
p.moveTo(param.calcXOffsetI(cur2DX),
param.calcYOffsetI(cur2DY));

// iterate over all the points in the line
for (uint32_t i = 1; i < numPoints; i++)
{
// get coordinates
cur2DX = lData[i].x();
cur2DY = lData[i].y();

// determine if the current position is in bounds
curInBounds = inRect(screenBounds, cur2DX, cur2DY);

// draw the line segment if either end is in bounds
if (lastInBounds || curInBounds)
p.lineTo(param.calcXOffsetI(cur2DX),
param.calcYOffsetI(cur2DY));
else
p.moveTo(param.calcXOffsetI(cur2DX),
param.calcYOffsetI(cur2DY));

// current becomes the last
lastInBounds = curInBounds;
}
}

// then paint the M lines
QPtrListIterator<MapLineM> mmit(m_mLines);
for (currentLineM = mmit.toFirst();
currentLineM;
currentLineM = ++mmit)
{
// if line is outside the currently visible region, skip it.
if (!currentLineM->boundingRect().intersects(screenBounds))
continue;

// get the number of points in the line
numPoints = currentLineM->size();

// get the underlying array
mData = currentLineM->data();

// set the line color
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineM->color());
#endif
p.setPen(currentLineM->bgSafeColor( param.backgroundColor()));

// get starting point coordinates
curX = mData[0].x();
curY = mData[0].y();
curZ = mData[0].z();

// see if the starting position is in bounds
lastInBounds = inRect(screenBounds, curX, curY);

#ifdef DEBUGMAP
seqDebug("Line has %i points:", currentLineM->size());
#endif

// calculate starting color info for the line
if (curZ > playerPos.z())
oldColor = (int)((curZ * topm) + topb);
else
oldColor = (int)((curZ * botm) + botb);

if (oldColor > 255) oldColor = 255;
if (oldColor < 0) oldColor = 0;

// move to the starting position
p.moveTo(param.calcXOffsetI(curX),
param.calcYOffsetI(curY));

// iterate over all the points in the line
for (uint i = 1; i < numPoints; i++)
{
curX = mData[i].x();
curY = mData[i].y();
curZ = mData[i].z();

// calculate the new color
if (curZ > playerPos.z())
newColor = (int)((curZ * topm) + topb);
else
newColor = (int)((curZ * botm) + botb);
if (newColor > 255) newColor = 255;
if (newColor < 0) newColor = 0;

// determine if the current position is in bounds
curInBounds = inRect(screenBounds, curX, curY);

// the use color is the average of the two colors
useColor = (newColor + oldColor) >> 1;

// draw the line segment if either end is in bounds
if ((lastInBounds || curInBounds) && (useColor != 0))
{
p.setPen(QColor(useColor, useColor, useColor));
p.lineTo(param.calcXOffsetI(curX),
param.calcYOffsetI(curY));
}
else
p.moveTo(param.calcXOffsetI(curX),
param.calcYOffsetI(curY));

// current becomes the last/old
lastInBounds = curInBounds;
oldColor = newColor;
}
}
}

void MapData::paintLocations(MapParameters& param, QPainter& p) const
{
//----------------------------------------------------------------------
/* Paint the locations */
#ifdef DEBUGMAP
seqDebug("Paint the locations");
#endif
// set the brush
p.setBrush(QColor (80, 80, 80));

// set the font
p.setFont(param.font());

// iterate over all the map locations
QPtrListIterator<MapLocation> lit(m_locations);
for(; lit.current(); ++lit)
{
MapLocation* currentLoc = lit.current();

// set the color
QColor color(currentLoc->color());

// make sure the color isn't the same as the background color
if (color == param.backgroundColor())
color = QColor( ~ param.backgroundColor().rgb());

// set the pen color
p.setPen(color);

// draw the text
p.drawText(param.calcXOffsetI(currentLoc->x()) - 2,
param.calcYOffsetI(currentLoc->y()) - 2,
currentLoc->name());
}
}

bool MapData::paintMapImage(MapParameters& param, QPainter& p) const
{
p.save();

double scaleX = (double(param.screenLength().width()) /
double(m_image.width())) * double(param.zoom());
double scaleY = (double(param.screenLength().height()) /
double(m_image.height())) * double(param.zoom());

if (scaleX > 3.0 || scaleY > 3.0)
return false;

p.scale(scaleX, scaleY);

int x = param.calcXOffset(m_maxX);
int y = param.calcYOffset(m_maxY);

p.drawPixmap(x, y, m_image);
p.restore();

return true;
}

//----------------------------------------------------------------------
// MapCache
MapCache::MapCache(const MapData& mapData)
: m_mapData(mapData),
m_lastParam(mapData)
{
#ifdef DEBUG
m_paintCount = 0;
#endif
m_painted = false;
m_alwaysRepaint = false;
}

MapCache::~MapCache()
{
}

bool MapCache::needRepaint(MapParameters& param)
{
// if any of these conditions are true, then a repaint is needed
// NOTE: May need to add more conditions
if (!m_painted || m_alwaysRepaint ||
(m_lastParam.screenCenter() != param.screenCenter()) ||
(m_lastParam.screenLength() != param.screenLength()) ||
(m_lastParam.ratio() != param.ratio()) ||
(m_lastParam.zoomMapLength() != param.zoomMapLength()) ||
(m_lastParam.screenBounds() != param.screenBounds() ) ||
((param.mapLineStyle() == tMap_FadedFloors) &&
(m_lastParam.player().z() != param.player().z())) ||
((param.mapLineStyle() == tMap_DepthFiltered) &&
((m_lastParam.playerHeadRoom() != param.playerHeadRoom()) ||
(m_lastParam.playerFloorRoom() != param.playerFloorRoom()))) ||
(m_lastParam.mapLineStyle() != param.mapLineStyle()) ||
(m_lastParam.showLocations() != param.showLocations()) ||
(m_lastParam.showLines() != param.showLines()) ||
(m_lastParam.showGridLines() != param.showGridLines()) ||
(m_lastParam.showGridTicks() != param.showGridTicks()) ||
(m_lastParam.showBackgroundImage() != param.showBackgroundImage()) ||
(m_lastParam.gridTickColor() != param.gridTickColor()) ||
(m_lastParam.backgroundColor() != param.backgroundColor()) ||
(m_lastParam.font() != param.font()))
return true;

// if none of the above conditions is true, no need to repaint.
return false;
}

const QPixmap& MapCache::getMapImage(MapParameters& param)
{
// only repaint the map if absolutely necessary.
if (!needRepaint(param))
return m_mapImage;

#ifdef DEBUG
// increment paint count
m_paintCount++;
#endif
// set pixmap optimization if it's changed
if (m_lastParam.mapOptimizationMethod() !=
param.mapOptimizationMethod())
m_mapImage.setOptimization(param.pixmapOptimizatio nMethod());

// make sure the map is the correct size
m_mapImage.resize(param.screenLength());

QPainter tmp;

// Begin Painting
tmp.begin (&m_mapImage);
tmp.setPen (QPen::NoPen);
tmp.setFont (param.font());

// load the background image or paint the background
if (!m_mapData.imageLoaded() ||
!param.showBackgroundImage() ||
!m_mapData.paintMapImage(param, tmp))
{
// paint the map backdrop with the users background color for all
// map line styles except faded floor, which only really works with
// a black background
if (param.mapLineStyle() != tMap_FadedFloors)
tmp.fillRect(m_mapImage.rect(), param.backgroundColor());
else
tmp.fillRect(m_mapImage.rect(), Qt::black);
}

tmp.setPen (QColor (80, 80, 80));
tmp.setBrush (QColor (80, 80, 80));

//----------------------------------------------------------------------
/* Paint the grid */
if (param.showGridLines() || param.showGridTicks())
m_mapData.paintGrid(param, tmp);

//----------------------------------------------------------------------
/* Paint the lines */
if (param.showLines())
{
switch (param.mapLineStyle())
{
case tMap_Normal:
m_mapData.paintLines(param, tmp);
break;
case tMap_DepthFiltered:
m_mapData.paintDepthFilteredLines(param, tmp);
break;
case tMap_FadedFloors:
m_mapData.paintFadedFloorsLines(param, tmp);
break;
#ifdef DEBUGMAP
default:
seqWarn("Unknown Map Line Style: %d!", param.mapLineStyle());
break;
#endif
}
}

//----------------------------------------------------------------------
/* Paint the locations */
if (param.showLocations())
m_mapData.paintLocations(param, tmp);

// finished painting
tmp.end();

// note that painting has been done
m_painted = true;

// note parameters used to paint, for later comparison
m_lastParam = param;

// return the map image
return m_mapImage;
}

Razzle
04-20-2009, 07:06 PM
Try this change and see how it works. I didn't test it. But is should make lines distinct no matter what, by inverting color if too close to background. If it and background are close to grey, it sets to black.



inline QColor MapCommon::bgSafeColor(const QColor& bgColor)
{
QColor bgSafeColor = color();
// Compairing the draw color with the backround and adjusting if they are too similar
if ((abs(color().red() - bgColor.red()) <= 75) &&
(abs(color().green() - bgColor.green()) <= 75) &&
(abs(color().blue() - bgColor.blue()) <= 75))
{
// We are too close to the background color, so inverse the color
bgSafeColor.setRgb(255 - color().red(),255 - color().green(), 255 - color().blue());
// perform another check to see if color inverted is too close to background still
// if it is too close, then the color and background are both close to grey
if ((abs(bgSafeColor.red() - bgColor.red()) <= 75) &&
(abs(bgSafeColor.green() - bgColor.green()) <= 75) &&
(abs(bgSafeColor.blue() - bgColor.blue()) <= 75))
{
// We are too close still, so both are almost grey, so set to black
bgSafeColor.setNamedColor("black");
}
} else {
bgSafeColor = color();
}
return bgSafeColor;
}



Razzle

purple
04-23-2009, 10:31 AM
Does everyone think this is a good idea? Can someone take some screen shots of what it does? Also can someone make me a unified diff?

ieatacid
04-23-2009, 05:32 PM
I haven't tried it but I think it's great if it works. Not having to convert maps anymore would be wonderful.

Tor K'tal
04-23-2009, 06:05 PM
I thought about inverting the colors as you did Razzle. The issue was that I didn't want that harsh white on the map (issues with my monitor not the principle), since I prefer the "gray" color as the default line color I chose not to. I'm sure what you made works fantastically and is more elegant than my solution.

I would love to make a unified diff but when I run the patch command to try and apply it to the standard 5.13.3.0 it just hangs up, making me think I'm not making the diff correctly. I've tried making a 5.13.3.1 directory and then running the following command, where the 5.13.3.1 has the new mapcore.cpp and .h files:
"diff -rdu ./showeq-5.13.3.0 ./showeq-5.13.3.1"

I'll see if I can post a screen shot of the version I made. My test map was the SOE file format version of thedeep_1.txt since the majority of it's lines are black.

~ TK

Razzle
04-23-2009, 06:21 PM
Let me think about it for a little bit. I know I like the grey better than that bright white on a black background. I am sure there is a way to get it to work a little better. I will try to put something together later tonight.

Razzle

ieatacid
04-23-2009, 09:32 PM
diff -wurd ./old ./new >> test.diff

is what I use.

Tor K'tal
04-23-2009, 10:17 PM
Here is a diff, and when you do a "patch -i diff-FileName" from the showeq-5.13.3.0/src directory it appears to apply it correctly.

There are some changes in the #ifdef when I was trying to test it and couldn't get the color names to pump out to the console window as I expected those #ifdef's to do. I don't think they work but they shouldn't hurt either.


diff -wurd ./showeq-5.13.3.0/src/mapcore.cpp ./showeq-5.13.3.1/src/mapcore.cpp
--- ./showeq-5.13.3.0/src/mapcore.cpp 2009-01-05 11:06:39.000000000 -0700
+++ ./showeq-5.13.3.1/src/mapcore.cpp 2009-04-20 14:46:23.000000000 -0600
@@ -1,5 +1,5 @@
/*
- * mapcore.h
+ * mapcore.cpp
*
* ShowEQ Distributed under GPL
* http://seq.sf.net/
@@ -1707,9 +1707,9 @@

// set pen color
#ifdef DEBUGMAP
- seqDebug("lineColor = '%s'", (char *) currentLineL->color());
+ seqDebug("lineColor = '%s'", (char *) currentLineL->colorName());
#endif
- p.setPen(currentLineL->color());
+ p.setPen(currentLineL->bgSafeColor(param.backgroundColor()));

cur2DX = lData[0].x();
cur2DY = lData[0].y();
@@ -1761,9 +1761,9 @@

// set pen color
#ifdef DEBUGMAP
- seqDebug("lineColor = '%s'", (char *) currentLineM->color());
+ seqDebug("lineColor = '%s'", (char *) currentLineM->colorName());
#endif
- p.setPen(currentLineM->color());
+ p.setPen(currentLineM->bgSafeColor(param.backgroundColor()));

curX = mData[0].x();
curY = mData[0].y();
@@ -1856,7 +1856,7 @@
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineL->color());
#endif
- p.setPen(currentLineL->color());
+ p.setPen(currentLineL->bgSafeColor(param.backgroundColor()));

cur2DX = lData[0].x();
cur2DY = lData[0].y();
@@ -1910,7 +1910,7 @@
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineM->color());
#endif
- p.setPen(currentLineM->color());
+ p.setPen(currentLineM->bgSafeColor(param.backgroundColor()));

// get current coordinates
curX = mData[0].x();
@@ -2022,7 +2022,7 @@
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineL->color());
#endif
- p.setPen(currentLineL->color());
+ p.setPen(currentLineL->bgSafeColor(param.backgroundColor()));
}
else
{
@@ -2092,7 +2092,7 @@
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineM->color());
#endif
- p.setPen(currentLineM->color());
+ p.setPen(currentLineM->bgSafeColor(param.backgroundColor()));

// get starting point coordinates
curX = mData[0].x();
diff -wurd ./showeq-5.13.3.0/src/mapcore.h ./showeq-5.13.3.1/src/mapcore.h
--- ./showeq-5.13.3.0/src/mapcore.h 2009-01-05 11:06:39.000000000 -0700
+++ ./showeq-5.13.3.1/src/mapcore.h 2009-04-20 14:37:45.000000000 -0600
@@ -406,10 +406,14 @@
void setName(const QString& name) { m_name = name; }
void setColor(const QString& color) { m_color = color; }

+ // Returns a QColor object for a color that will contrast the background better
+ QColor bgSafeColor(const QColor& bgColor);
+
private:
QString m_name;
QString m_colorName;
QColor m_color;
+ QColor m_bgSafeColor;
};

inline QString MapCommon::colorName() const
@@ -422,6 +426,34 @@
return m_color.name();
}

+inline QColor MapCommon::bgSafeColor(const QColor& bgColor)
+{
+ QColor bgSafeColor = color();
+ // Compairing the draw color with the backround and adjusts if they are too similar
+ if ((abs(color().red() - bgColor.red()) <= 55) &&
+ (abs(color().green() - bgColor.green()) <= 55) &&
+ (abs(color().blue() - bgColor.blue()) <= 55))
+ {
+ // black on black fails to XOR
+ // if the color is too black, and we assume the background is black as well
+ // because we passed the previous if statement we set the color to gray
+ if (color().red() <= 55 && color().green() <= 55 && color().blue() <= 55)
+ {
+ bgSafeColor.setNamedColor("gray");
+ } else {
+ // the carrot symbol performs an XOR
+ // If the colors are identical the line will be drawn black
+ bgSafeColor.setRgb(color().red() ^ bgColor.red(),
+ color().green() ^ bgColor.green(),
+ color().blue() ^ bgColor.blue());
+ }
+ } else {
+ bgSafeColor = color();
+ }
+
+ return bgSafeColor;
+}
+
//----------------------------------------------------------------------
// MapLineL
class MapLineL : public MapCommon, public QPointArray


For some reason I'm not allowed to attach a screen shot and haven't figured out another way to get it onto the web that I'm comfortable with.

~ TK

BlueAdept
04-24-2009, 09:01 AM
Sorry I havent been able to test this. Actually I havent been able to play EQ in several weeks.

To attach an image, you have to upload it to an online image place. Like photobucket or image shack.

http://photobucket.com/
http://imageshack.us/

Razzle
04-24-2009, 05:17 PM
Here is a diff based on Tor's, expanded to cover different combinations of fore and backgrounds. I also tweaked it so it wont be the bright white on black, but a shade darker.


diff -wurd ./old/src/mapcore.cpp ./new/src/mapcore.cpp
--- ./old/src/mapcore.cpp 2009-01-05 19:06:39.000000000 +0100
+++ ./new/src/mapcore.cpp 2009-04-25 00:19:55.000000000 +0200
@@ -1709,7 +1709,7 @@
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineL->color());
#endif
- p.setPen(currentLineL->color());
+ p.setPen(currentLineL->safeColor(param.backgroundColor()));

cur2DX = lData[0].x();
cur2DY = lData[0].y();
@@ -1763,7 +1763,7 @@
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineM->color());
#endif
- p.setPen(currentLineM->color());
+ p.setPen(currentLineM->safeColor(param.backgroundColor()));

curX = mData[0].x();
curY = mData[0].y();
@@ -1856,7 +1856,7 @@
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineL->color());
#endif
- p.setPen(currentLineL->color());
+ p.setPen(currentLineL->safeColor(param.backgroundColor()));

cur2DX = lData[0].x();
cur2DY = lData[0].y();
@@ -1910,7 +1910,7 @@
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineM->color());
#endif
- p.setPen(currentLineM->color());
+ p.setPen(currentLineM->safeColor(param.backgroundColor()));

// get current coordinates
curX = mData[0].x();
@@ -2022,7 +2022,7 @@
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineL->color());
#endif
- p.setPen(currentLineL->color());
+ p.setPen(currentLineL->safeColor(param.backgroundColor()));
}
else
{
@@ -2092,7 +2092,7 @@
#ifdef DEBUGMAP
seqDebug("lineColor = '%s'", (char *) currentLineM->color());
#endif
- p.setPen(currentLineM->color());
+ p.setPen(currentLineM->safeColor(param.backgroundColor()));

// get starting point coordinates
curX = mData[0].x();
Only in ./new/src: mapcore.cpp~
diff -wurd ./old/src/mapcore.h ./new/src/mapcore.h
--- ./old/src/mapcore.h 2009-01-05 19:06:39.000000000 +0100
+++ ./new/src/mapcore.h 2009-04-25 00:58:14.000000000 +0200
@@ -402,6 +402,7 @@
const QString& name() const { return m_name; }
const QColor& color() const { return m_color; }
QString colorName() const;
+ QColor safeColor(const QColor& bgColor);

void setName(const QString& name) { m_name = name; }
void setColor(const QString& color) { m_color = color; }
@@ -422,6 +423,19 @@
return m_color.name();
}

+inline QColor MapCommon::safeColor(const QColor& bgColor)
+{
+ if ((abs(color().red() - bgColor.red()) < 55) &&
+ (abs(color().green() - bgColor.green()) < 55) &&
+ (abs(color().blue() - bgColor.blue()) < 55))
+ m_color.setRgb(192 - 0.75 * color().red(), 192 - 0.75 * color().green(), 192 - 0.75 * color().blue());
+ if ((abs(color().red() - bgColor.red()) < 55) &&
+ (abs(color().green() - bgColor.green()) < 55) &&
+ (abs(color().blue() - bgColor.blue()) < 55))
+ m_color.setNamedColor("black");
+ return m_color;
+}
+
//----------------------------------------------------------------------
// MapLineL
class MapLineL : public MapCommon, public QPointArrayRazzle