/* Copyright (c) 2007 Ben Howell
* This software is licensed under the MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using MouseButtons = System.Windows.Forms.MouseButtons;
using Decal.Adapter;
using Decal.Adapter.Wrappers;
using ICSharpCode.SharpZipLib.Zip;
using GoArrow.RouteFinding;
namespace GoArrow.Huds
{
class MapHud : WindowHud
{
#region Constants
const int TileCacheSize = 25;
const double ZoomBase = 1.4142135623730950488016887242097; // sqrt(2)
const double MaxZoomFactor = 10;
private static readonly Rectangle DefaultRegion = new Rectangle(75, 100, 480, 360);
/// 12x14 arrow centered at (0,0)
private static readonly Point[] PlayerArrowPolygon = {
new Point( 0, -7),
new Point( 6, 7),
new Point( 0, 4),
new Point(-6, 7),
};
private static readonly float[] PossibleCoordDeltas =
{ 0.01f, 0.02f, 0.05f, 0.1f, 0.2f, 0.5f, 1.0f, 2.0f, 5.0f, 10.0f, 20.0f, 50.0f };
private static readonly Font CoordTextFont = new Font(FontFamily.GenericSansSerif, 8);
private static readonly Pen CoordGridline = new Pen(Color.FromArgb(0x60, Color.Black));
private static readonly Brush CoordGutterFill = new SolidBrush(Color.FromArgb(0x60, Color.Black));
private const int CoordGutterSize = 15;
private static readonly Pen RouteLine = new Pen(Color.FromArgb(unchecked((int)0xFFCCFFCC)), 1.75f);
private static readonly Pen RouteLinePortal = new Pen(Color.FromArgb(unchecked((int)0xFFFF66FF)), 1.25f);
private static readonly Pen RouteLineBackground = new Pen(Color.FromArgb(unchecked((int)0xC0000000)), 4.0f);
private static readonly Brush TextGlow1 = new SolidBrush(Color.FromArgb(0xC0, Color.Black));
private static readonly Brush TextGlow2 = new SolidBrush(Color.FromArgb(0x60, Color.Black));
private static readonly StringFormat VerticalText = new StringFormat(StringFormatFlags.DirectionVertical);
#endregion
#region Private Fields
private PluginCore mPluginCore;
private int mMapSize;
private float mPixPerClick;
private float mCoordTickDelta;
private int mTileSize, mTilePaddedSize;
private int mMaxTile;
private Dictionary mTileCache = new Dictionary(TileCacheSize);
/// List for determining least recently used tile
private LinkedList mTileCacheAccesses = new LinkedList();
private Bitmap mDerethMapLowRes;
private ZipFile mZipFile;
private Hud mSpriteHud;
private Bitmap mSpriteBuffer = new Bitmap(32, 32);
private Brush mPlayerArrowFill = new SolidBrush(Color.FromArgb(unchecked((int)0xFF88E223))); // Green
private Pen mPlayerArrowOutline = Pens.Black;
private bool mNeedsSpriteRepaint = true;
private bool mNeedsPlayerRecenter = true;
private Coordinates mCenterOnCoords = Coordinates.NO_COORDINATES;
private ToolbarHud mContextMenu = null;
private bool mIsDragging = false;
private PointF mOffset;
private PointF mMouseDownOffset;
private double mZoomFactor = 0.0;
private float mZoomMultiplier = 1.0f;
private float mActualZoom;
private float mPreviousZoomMultiplier = 0.0f;
private Size mPreviousClientSize;
// Display
private Route mRoute;
private LocationDatabase mLocDb;
private bool mShowRoute = true;
private bool mShowCoords = true;
private bool mShowLocations = true;
private bool mShowLocationsAllZooms = false;
private bool mShowLabels = true;
private Rectangle mClickedHotspotRect = Rectangle.Empty;
private LinkedList mHotspots = new LinkedList();
private Hotspot mActiveHotspot = Hotspot.None;
private struct Hotspot
{
public static readonly Hotspot None = new Hotspot();
public readonly Rectangle rect;
public readonly Location loc;
public Hotspot(Rectangle r, Location l)
{
rect = r;
loc = l;
}
public override string ToString() { return loc.ToString(); }
public override bool Equals(object obj)
{
if (obj is Hotspot)
{
Hotspot h = (Hotspot)obj;
return (this.rect == h.rect && this.loc == h.loc);
}
return false;
}
public override int GetHashCode() { return rect.GetHashCode() ^ (loc == null ? int.MaxValue : loc.GetHashCode()); }
public static bool operator ==(Hotspot a, Hotspot b) { return a.rect == b.rect && a.loc == b.loc; }
public static bool operator !=(Hotspot a, Hotspot b) { return !(a == b); }
}
private Coordinates mPlayerCoords, mLastPaintedPlayerCoords;
private double mPlayerHeadingRadians;
private bool mPlayerInDungeon;
private bool mCenterOnPlayer = true;
private bool mDrawCoords = true;
private MouseButtons mDragButton = MouseButtons.Left | MouseButtons.Middle;
private MouseButtons mSelectLocationButton = MouseButtons.Left;
private MouseButtons mContextMenuButton = MouseButtons.Right;
private MouseButtons mDetailsButton = MouseButtons.None;
#endregion
#region Initialization/Termination
static MapHud()
{
RouteLine.StartCap = LineCap.Round;
RouteLine.EndCap = LineCap.Custom;
RouteLine.CustomEndCap = new AdjustableArrowCap(4.0f, 5.0f);
RouteLinePortal.DashStyle = DashStyle.Dash;
RouteLinePortal.DashCap = DashCap.Flat;
RouteLinePortal.StartCap = LineCap.Round;
RouteLinePortal.EndCap = LineCap.Custom;
RouteLinePortal.CustomEndCap = new AdjustableArrowCap(4.0f, 5.0f);
}
public MapHud(HudManager manager, PluginCore pluginCore, LocationDatabase locationDatabase)
: base(DefaultRegion, "Map of Dereth", manager)
{
mPluginCore = pluginCore;
mZipFile = new ZipFile(Util.FullPath(@"Huds\DerethMap.zip"));
using (StreamReader mapInfoReader = new StreamReader(mZipFile.GetInputStream(mZipFile.GetEntry("map.txt"))))
{
// File format has 3 lines: mapSize, tileSize, padding
mMapSize = int.Parse(mapInfoReader.ReadLine());
mTileSize = int.Parse(mapInfoReader.ReadLine());
mTilePaddedSize = mTileSize + int.Parse(mapInfoReader.ReadLine());
mMaxTile = (mMapSize - 1) / mTileSize;
mPixPerClick = mMapSize / 204.1f;
}
mActualZoom = mZoomMultiplier;
mCoordTickDelta = CalculateCoordTickDelta(Zoom);
SetAlphaFading(255, 128);
ClientMouseDown += new EventHandler(MapHud_ClientMouseDown);
ClientMouseUp += new EventHandler(MapHud_ClientMouseUp);
ClientMouseDrag += new EventHandler(MapHud_ClientMouseDrag);
ClientMouseDoubleClick += new EventHandler(MapHud_ClientMouseDoubleClick);
ClientMouseWheel += new EventHandler(MapHud_ClientMouseWheel);
ClientMouseMove += new EventHandler(MapHud_ClientMouseMove);
ClientMouseLeave += new EventHandler(MapHud_ClientMouseLeave);
ResizeEnd += new EventHandler(MapHud_ResizeEnd);
ClientVisibleChanged += new EventHandler(MapHud_ClientVisibleChanged);
Moving += new EventHandler(MapHud_Moving);
Heartbeat += new EventHandler(MapHud_Heartbeat);
ResizeDrawMode = HudResizeDrawMode.Repaint;
mLocDb = locationDatabase;
WindowMessage += new EventHandler(MapHud_WindowMessage);
AlphaChanged += new EventHandler(MapHud_AlphaChanged);
//MaxSize = new Size(1024, 1024);
}
public override void Dispose()
{
if (!Disposed)
{
mSpriteHud.Enabled = false;
if (mDerethMapLowRes != null)
{
mDerethMapLowRes.Dispose();
mDerethMapLowRes = null;
}
mTileCache.Clear();
mTileCacheAccesses.Clear();
mZipFile.Close();
}
base.Dispose();
}
#endregion
#region Properties
public void ResetPosition()
{
Region = DefaultRegion;
}
public bool CenterOnPlayer
{
get { return mCenterOnPlayer; }
set
{
mCenterOnPlayer = value;
mNeedsPlayerRecenter = value;
if (mCenterOnPlayer)
{
RepaintAll();
}
}
}
public bool DrawCoords
{
get { return mDrawCoords; }
set
{
if (mDrawCoords != value)
{
mDrawCoords = value;
RepaintAll();
}
}
}
public MouseButtons DragButton
{
get { return mDragButton; }
set { mDragButton = value; }
}
public MouseButtons SelectLocationButton
{
get { return mSelectLocationButton; }
set { mSelectLocationButton = value; }
}
public MouseButtons ContextMenuButton
{
get { return mContextMenuButton; }
set { mContextMenuButton = value; }
}
public MouseButtons DetailsButton
{
get { return mDetailsButton; }
set { mDetailsButton = value; }
}
public double PlayerHeadingRadians
{
get { return mPlayerHeadingRadians; }
set
{
if (mPlayerHeadingRadians != value)
{
mPlayerHeadingRadians = value;
RepaintSprites();
}
}
}
public Coordinates PlayerCoords
{
get { return mPlayerCoords; }
set
{
if (Math.Abs(mPlayerCoords.NS - value.NS) > 0.005
|| Math.Abs(mPlayerCoords.EW - value.EW) > 0.005)
{
mPlayerCoords = value;
RepaintSprites();
if (CenterOnPlayer)
{
mNeedsPlayerRecenter = true;
// Number of pixels player can move before recentering
const int pixelAllowance = 20;
float delta = 0.5f / (mPixPerClick * Zoom) * pixelAllowance;
PointF playerPix = CoordsToPix(mPlayerCoords, ActualZoom);
if (playerPix.X < 0 || playerPix.X >= ClientSize.Width ||
playerPix.Y < 0 || playerPix.Y >= ClientSize.Height ||
Math.Abs(mLastPaintedPlayerCoords.NS - value.NS) > delta ||
Math.Abs(mLastPaintedPlayerCoords.EW - value.EW) > delta)
{
mLastPaintedPlayerCoords = value;
RepaintAll();
}
}
}
}
}
public bool PlayerInDungeon
{
get { return mPlayerInDungeon; }
set
{
if (mPlayerInDungeon != value)
{
mPlayerInDungeon = value;
if (mPlayerInDungeon)
mNeedsPlayerRecenter = false;
RepaintSprites();
}
}
}
public bool ShowCoords
{
get { return mShowCoords; }
set
{
if (mShowCoords != value)
{
mShowCoords = value;
RepaintAll();
}
}
}
public Route Route
{
get { return mRoute; }
set
{
mRoute = value;
RepaintAll();
}
}
public bool ShowRoute
{
get { return mShowRoute; }
set
{
if (mShowRoute != value)
{
mShowRoute = value;
RepaintAll();
}
}
}
public bool ShowLocations
{
get { return mShowLocations; }
set
{
if (mShowLocations != value)
{
mShowLocations = value;
RepaintAll();
}
}
}
public bool ShowLocationsAllZooms
{
get { return mShowLocationsAllZooms; }
set
{
if (mShowLocationsAllZooms != value)
{
mShowLocationsAllZooms = value;
RepaintAll();
}
}
}
public bool ShowLabels
{
get { return mShowLabels; }
set
{
if (mShowLabels != value)
{
mShowLabels = value;
RepaintAll();
}
}
}
public LocationDatabase LocationDatabase
{
get { return mLocDb; }
set
{
mLocDb = value;
RepaintAll();
}
}
public Coordinates CoordsAtCenter
{
get
{
PointF centerPt = new PointF(ClientSize.Width / 2.0f, ClientSize.Height / 2.0f);
return PixToCoords(centerPt, ActualZoom);
}
set { CenterOnCoords(value); }
}
///
/// Gets the zoom multiplier, not accounting for any constraints that
/// occurred by painting when the map was zoom all the way out.
///
public float Zoom
{
get { return mZoomMultiplier; }
set { ZoomFactor = Math.Log(value, ZoomBase); }
}
///
/// Gets the current zoom multiplier, as it was last painted. This
/// accounts for the zoom limiting that happens when the map is zoomed
/// all the way out.
///
private float ActualZoom
{
get { return mActualZoom; }
set { mActualZoom = value; }
}
private double ZoomFactor
{
get { return mZoomFactor; }
set
{
mZoomFactor = value;
if (mZoomFactor > MaxZoomFactor)
mZoomFactor = MaxZoomFactor;
mZoomMultiplier = (float)Math.Pow(ZoomBase, Math.Floor(mZoomFactor));
float zoomedSize = mMapSize * mZoomMultiplier;
if (zoomedSize < ClientSize.Width || zoomedSize < ClientSize.Height)
{
float z = Math.Max(ClientSize.Width / (float)mMapSize, ClientSize.Height / (float)mMapSize);
if (z > 1) { z = 1; }
mZoomFactor = Math.Log(z, ZoomBase);
if (mZoomFactor > MaxZoomFactor)
mZoomFactor = MaxZoomFactor;
mZoomMultiplier = z;
}
mCoordTickDelta = CalculateCoordTickDelta(Zoom);
ActualZoom = mZoomMultiplier;
}
}
#endregion
#region Mouse Handling
private void MapHud_ClientMouseDown(object sender, HudMouseEventArgs e)
{
if ((e.Button & DragButton) != 0)
{
mMouseDownOffset.X = (e.Location.X - mOffset.X * Zoom) / Zoom;
mMouseDownOffset.Y = (e.Location.Y - mOffset.Y * Zoom) / Zoom;
mIsDragging = true;
e.Eat = true;
}
if ((e.Button & (SelectLocationButton | ContextMenuButton | DetailsButton)) != 0
&& ActiveHotspot != Hotspot.None && ActiveHotspot.rect.Contains(e.Location))
{
mClickedHotspotRect = ActiveHotspot.rect;
e.Eat = true;
}
}
private void MapHud_ClientMouseUp(object sender, HudMouseEventArgs e)
{
if (((ClientMouseButtons & ~e.Button) & DragButton) == 0 && mIsDragging)
{
mIsDragging = false;
RepaintAll();
}
if (mClickedHotspotRect.Contains(e.Location))
{
if ((e.Button & SelectLocationButton) != 0)
{
if (e.Control)
{
mPluginCore.SetRouteEnd(ActiveHotspot.loc);
}
else if (e.Shift)
{
mPluginCore.SetRouteStart(ActiveHotspot.loc);
}
else
{
mPluginCore.mArrowHud.DestinationLocation = ActiveHotspot.loc;
mPluginCore.mArrowHud.Visible = true;
}
}
else if ((e.Button & ContextMenuButton) != 0)
{
ShowContextMenu(ActiveHotspot.loc);
}
else if ((e.Button & DetailsButton) != 0)
{
mPluginCore.ShowDetails(ActiveHotspot.loc);
}
}
else if (ActiveHotspot == Hotspot.None && (e.Button & ContextMenuButton) != 0)
{
ShowContextMenu(PixToCoords(e.Location, ActualZoom));
}
mClickedHotspotRect = Rectangle.Empty;
}
private void MapHud_ClientMouseDrag(object sender, HudMouseDragEventArgs e)
{
if ((e.Button & DragButton) != 0)
{
mOffset.X = (e.Location.X - mMouseDownOffset.X * Zoom) / Zoom;
mOffset.Y = (e.Location.Y - mMouseDownOffset.Y * Zoom) / Zoom;
mNeedsPlayerRecenter = false;
RepaintAll();
}
}
private void MapHud_ClientMouseDoubleClick(object sender, HudMouseEventArgs e)
{
if ((e.Button & DragButton) != 0)
{
CenterOnPix(e.Location, Zoom);
RepaintAll();
e.Eat = true;
}
}
private void MapHud_ClientMouseWheel(object sender, HudMouseEventArgs e)
{
float origZoom = ActualZoom;
ZoomFactor += e.Delta / (double)HudMouseEventArgs.WHEEL_DELTA;
if (CenterOnPlayer && IsCenteredOnPlayer(origZoom, ClientSize))
{
mNeedsPlayerRecenter = true;
}
else
{
PointF centerOn;
if (!e.Control && !e.Shift)
{
// Keep point under mouse cursor in same spot
centerOn = e.Location;
}
else
{
// Keep center in same spot
centerOn = new PointF(ClientSize.Width / 2.0f, ClientSize.Height / 2.0f);
}
mOffset.X += centerOn.X * (1 / ActualZoom - 1 / origZoom);
mOffset.Y += centerOn.Y * (1 / ActualZoom - 1 / origZoom);
}
RepaintAll();
e.Eat = true;
}
private void MapHud_ClientMouseMove(object sender, HudMouseEventArgs e)
{
CalcActiveHotspot(e.Location);
}
private void MapHud_ClientMouseLeave(object sender, HudMouseEventArgs e)
{
ActiveHotspot = Hotspot.None;
}
private void MapHud_ResizeEnd(object sender, EventArgs e)
{
RepaintAll();
}
public void ArrowHud_DestinationChanged(object sender, DestinationChangedEventArgs e)
{
RepaintAll();
}
private void CalcActiveHotspot(Point mousePos)
{
if (MouseOnClient)
{
foreach (Hotspot h in mHotspots)
{
if (h.rect.Contains(mousePos))
{
ActiveHotspot = h;
return;
}
}
}
ActiveHotspot = Hotspot.None;
}
private Hotspot ActiveHotspot
{
get { return mActiveHotspot; }
set
{
if (mActiveHotspot != value)
{
mActiveHotspot = value;
RepaintSprites();
if (mActiveHotspot == Hotspot.None)
{
Manager.HideToolTip();
}
else
{
Point toolTipLocation = new Point(MouseLocation.X + 16, MouseLocation.Y + 16);
Manager.ShowToolTip(toolTipLocation, mActiveHotspot.loc.Name, 4000);
}
}
}
}
private void MapHud_WindowMessage(object sender, WindowMessageEventArgs e)
{
const short WM_MOUSEMOVE = 0x0200;
const short WM_LBUTTONDOWN = 0x0201;
const short WM_LBUTTONUP = 0x0202;
const short WM_RBUTTONDOWN = 0x0204;
const short WM_RBUTTONUP = 0x0205;
const short WM_MBUTTONDOWN = 0x0207;
const short WM_MBUTTONUP = 0x0208;
if (mContextMenu != null && (
e.Msg == WM_LBUTTONDOWN || e.Msg == WM_LBUTTONUP ||
e.Msg == WM_MBUTTONDOWN || e.Msg == WM_MBUTTONUP ||
e.Msg == WM_RBUTTONDOWN || e.Msg == WM_RBUTTONUP))
{
CloseContextMenu();
}
if (e.Msg == WM_MOUSEMOVE && mIsDragging && (ClientMouseButtons & DragButton) == 0)
{
mIsDragging = false;
RepaintAll();
}
}
#endregion
#region Context Menus
private void ShowContextMenu(Location loc)
{
CloseContextMenu();
mContextMenu = new ToolbarHud(Manager);
mContextMenu.Location = MouseLocation;
mContextMenu.Orientation = ToolbarOrientation.Vertical;
mContextMenu.PositionLocked = true;
mContextMenu.RespondsToRightClick = true;
ToolbarButton label;
switch (loc.Type)
{
case LocationType.Bindstone:
label = new ToolbarButton(Icons.Toolbar.HouseStone, loc.Name);
break;
case LocationType.Dungeon:
case LocationType.AllegianceHall:
label = new ToolbarButton(Icons.Toolbar.Dungeon, loc.Name);
break;
case LocationType.Landmark:
label = new ToolbarButton(Icons.Toolbar.Landmark, loc.Name);
break;
case LocationType.Lifestone:
label = new ToolbarButton(Icons.Toolbar.Lifestone, loc.Name);
break;
case LocationType.NPC:
label = new ToolbarButton(Icons.Map.NPC, loc.Name);
break;
case LocationType.Outpost:
label = new ToolbarButton(Icons.Map.TownSmall, loc.Name);
break;
case LocationType.Portal:
case LocationType.AnyPortal:
case LocationType.SettlementPortal:
case LocationType.TownPortal:
case LocationType.UndergroundPortal:
case LocationType.WildernessPortal:
case LocationType.PortalDevice:
label = new ToolbarButton(Icons.Toolbar.Portal, loc.Name);
break;
case LocationType.PortalHub:
label = new ToolbarButton(Icons.Toolbar.PortalHub, loc.Name);
break;
case LocationType.Village:
label = new ToolbarButton(Icons.Map.Settlement, loc.Name);
break;
case LocationType.Town:
label = new ToolbarButton(Icons.Map.Town, loc.Name);
break;
case LocationType.Vendor:
label = new ToolbarButton(Icons.Map.Store, loc.Name);
break;
case LocationType.Custom:
label = new ToolbarButton(Icons.Toolbar.Dereth, loc.Name);
break;
default:
label = new ToolbarButton(loc.Name);
break;
}
ToolbarButton coordsLabel = mContextMenu.AddButton(label);
coordsLabel.IsLabelOnly = true;
ToolbarButton showDetails = new ToolbarButton(Icons.Toolbar.MagnifyingGlass, "Show Details");
showDetails.Click += new EventHandler(delegate(object s, EventArgs e)
{
mPluginCore.ShowDetails(loc);
CloseContextMenu();
});
mContextMenu.AddButton(showDetails);
ToolbarButton arrowDest = new ToolbarButton(Icons.Toolbar.GoArrow, "Point Arrow Here");
arrowDest.Click += new EventHandler(delegate(object s, EventArgs e)
{
mPluginCore.mArrowHud.DestinationLocation = loc;
mPluginCore.mArrowHud.Visible = true;
CloseContextMenu();
});
mContextMenu.AddButton(arrowDest);
ToolbarButton routeStart = new ToolbarButton(Icons.Toolbar.RouteStart, "Start Route Here");
routeStart.Click += new EventHandler(delegate(object s, EventArgs e)
{
mPluginCore.SetRouteStart(loc);
CloseContextMenu();
});
mContextMenu.AddButton(routeStart);
ToolbarButton routeEnd = new ToolbarButton(Icons.Toolbar.RouteEnd, "End Route Here");
routeEnd.Click += new EventHandler(delegate(object s, EventArgs e)
{
mPluginCore.SetRouteEnd(loc);
CloseContextMenu();
});
mContextMenu.AddButton(routeEnd);
if (mPluginCore.mDungeonHud.DungeonMapAvailable(loc))
{
ToolbarButton showDungeon = new ToolbarButton(Icons.Toolbar.Dungeon, "Show Dungeon Map");
showDungeon.Click += new EventHandler(delegate(object s, EventArgs e)
{
mPluginCore.mDungeonHud.Visible = true;
mPluginCore.mDungeonHud.LoadDungeonById(mPluginCore.mDungeonHud.GetDungeonId(loc));
CloseContextMenu();
});
mContextMenu.AddButton(showDungeon);
}
if (loc.HasExitCoords)
{
ToolbarButton jumpToExit = new ToolbarButton(Icons.Toolbar.JumpToExit, "Jump to Exit (" + loc.ExitCoords + ")");
jumpToExit.Click += new EventHandler(delegate(object s, EventArgs e)
{
mPluginCore.mMapHud.CenterOnCoords(loc.ExitCoords);
CloseContextMenu();
});
mContextMenu.AddButton(jumpToExit);
}
mContextMenu.Visible = true;
}
private void ShowContextMenu(Coordinates coords)
{
CloseContextMenu();
mContextMenu = new ToolbarHud(Manager);
mContextMenu.Location = MouseLocation;
mContextMenu.Orientation = ToolbarOrientation.Vertical;
mContextMenu.PositionLocked = true;
mContextMenu.RespondsToRightClick = true;
ToolbarButton coordsLabel = mContextMenu.AddButton(new ToolbarButton(coords.ToString("0.0")));
coordsLabel.IsLabelOnly = true;
ToolbarButton arrowDest = new ToolbarButton(Icons.Toolbar.GoArrow, "Point Arrow Here");
arrowDest.Click += new EventHandler(delegate(object s, EventArgs e)
{
mPluginCore.mArrowHud.DestinationCoords = coords;
mPluginCore.mArrowHud.Visible = true;
CloseContextMenu();
});
mContextMenu.AddButton(arrowDest);
ToolbarButton routeStart = new ToolbarButton(Icons.Toolbar.RouteStart, "Start Route Here");
routeStart.Click += new EventHandler(delegate(object s, EventArgs e)
{
mPluginCore.SetRouteStart(coords);
CloseContextMenu();
});
mContextMenu.AddButton(routeStart);
ToolbarButton routeEnd = new ToolbarButton(Icons.Toolbar.RouteEnd, "End Route Here");
routeEnd.Click += new EventHandler(delegate(object s, EventArgs e)
{
mPluginCore.SetRouteEnd(coords);
CloseContextMenu();
});
mContextMenu.AddButton(routeEnd);
mContextMenu.Visible = true;
}
private void CloseContextMenu()
{
if (mContextMenu != null)
{
mContextMenu.Dispose();
mContextMenu = null;
}
}
#endregion
#region Painting
private void RepaintAll()
{
//mNeedsFullRepaint = true;
Repaint();
}
private void RepaintSprites()
{
mNeedsSpriteRepaint = true;
}
private void MapHud_Heartbeat(object sender, EventArgs e)
{
#if !USING_D3D_CONTAINER
PlayerCoords = new Coordinates(Host.Actions.Landcell,
Host.Actions.LocationY, Host.Actions.LocationX);
PlayerInDungeon = IsDungeon(Host.Actions.Landcell);
PlayerHeadingRadians = Host.Actions.HeadingRadians;
#endif
if (ClientVisible && mNeedsSpriteRepaint)
{
PaintSprites(ActualZoom);
}
}
public bool IsDungeon(int landblock)
{
return Util.IsDungeon(landblock);
//return (landblock & 0x0000FF00) != 0;
}
public override void RecreateHud()
{
base.RecreateHud();
if (mSpriteHud != null)
{
mSpriteHud.Enabled = false;
Host.Render.RemoveHud(mSpriteHud);
mSpriteHud.Dispose();
mSpriteHud = null;
}
PaintSprites(Zoom);
}
private void MapHud_ClientVisibleChanged(object sender, EventArgs e)
{
if (ClientVisible)
{
if (CenterOnPlayer)
{
PointF playerPoint = CoordsToPix(PlayerCoords, Zoom);
float dX = playerPoint.X - ClientSize.Width / 2.0f;
float dY = playerPoint.X - ClientSize.Height / 2.0f;
// More than 4 pixels away from the center
if (dX * dX + dY * dY > 16)
{
mNeedsPlayerRecenter = true;
RepaintAll();
}
}
PaintSprites(Zoom);
mSpriteHud.Enabled = ClientVisible;
mSpriteHud.Alpha = AlphaFrame;
}
else if (mSpriteHud != null && !mSpriteHud.Lost)
{
mSpriteHud.Enabled = ClientVisible;
mSpriteHud.Alpha = AlphaFrame;
}
}
private void MapHud_Moving(object sender, EventArgs e)
{
if (mSpriteHud != null && !mSpriteHud.Lost)
{
mSpriteHud.Region = new Rectangle(ClientLocation, mSpriteHud.Region.Size);
}
}
private void MapHud_AlphaChanged(object sender, AlphaChangedEventArgs e)
{
if (mSpriteHud != null && !mSpriteHud.Lost)
{
mSpriteHud.Alpha = e.Alpha;
}
}
protected override void PaintClient(Graphics g, bool imageDataLost)
{
float z = Zoom;
if (mPreviousClientSize != ClientSize)
{
if (!mNeedsPlayerRecenter && mCenterOnCoords == Coordinates.NO_COORDINATES)
{
if (CenterOnPlayer && IsCenteredOnPlayer(z, mPreviousClientSize))
mNeedsPlayerRecenter = true;
else
CenterOnPix(mPreviousClientSize.Width / 2, mPreviousClientSize.Height / 2, z);
}
mPreviousClientSize = ClientSize;
}
float zoomedSize = mMapSize * z;
if (zoomedSize < ClientSize.Width || zoomedSize < ClientSize.Height)
{
z = Math.Max(ClientSize.Width / (float)mMapSize, ClientSize.Height / (float)mMapSize);
if (z > 1)
z = 1;
Zoom = z;
ActualZoom = z;
zoomedSize = mMapSize * z;
}
// Paint Map
Matrix origTransform = g.Transform;
g.Clear(Clear);
float coordPadX = 0, coordPadY = 0;
if (mCenterOnCoords != Coordinates.NO_COORDINATES)
{
CenterOnCoords(mCenterOnCoords, z);
mCenterOnCoords = Coordinates.NO_COORDINATES;
mNeedsPlayerRecenter = false;
}
else if (mNeedsPlayerRecenter && (ClientMouseButtons & DragButton) == 0)
{
mNeedsPlayerRecenter = false;
if (!PlayerInDungeon)
CenterOnCoords(mPlayerCoords, z);
}
if (ClientSize.Width > zoomedSize)
{
// Center horizontally
mOffset.X = (ClientSize.Width - zoomedSize) / (2 * z);
coordPadX = mOffset.X * z;
}
else if (mOffset.X > 0)
mOffset.X = 0;
else if (mOffset.X < (ClientSize.Width - zoomedSize) / z)
mOffset.X = (ClientSize.Width - zoomedSize) / z;
if (ClientSize.Height > zoomedSize)
{
// Center vertically
mOffset.Y = (ClientSize.Height - zoomedSize) / (2 * z);
coordPadY = mOffset.Y * z;
}
else if (mOffset.Y > 0)
mOffset.Y = 0;
else if (mOffset.Y < (ClientSize.Height - zoomedSize) / z)
mOffset.Y = (ClientSize.Height - zoomedSize) / z;
#region Draw Map
if (z < 1)
{
// Lazy load low res map
if (mDerethMapLowRes == null)
{
mDerethMapLowRes = new Bitmap(mZipFile.GetInputStream(mZipFile.GetEntry("lowres.png")));
}
float relSize = (float)mDerethMapLowRes.Width / mMapSize;
if (z / relSize <= 0.5f)
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
else
g.InterpolationMode = InterpolationMode.Bilinear;
RectangleF srcRect = new RectangleF(-mOffset.X * relSize, -mOffset.Y * relSize,
ClientSize.Width * relSize / z, ClientSize.Height * relSize / z);
RectangleF destRect = new RectangleF(0, 0, ClientSize.Width, ClientSize.Height);
g.DrawImage(mDerethMapLowRes, destRect, srcRect, GraphicsUnit.Pixel);
}
else
{
float w = ClientSize.Width / z;
int minTileX = Math.Max((int)(-mOffset.X / mTileSize), 0);
int minTileY = Math.Max((int)(-mOffset.Y / mTileSize), 0);
int maxTileX = Math.Min(
(int)((-mOffset.X + ClientSize.Width / z) / mTileSize), mMaxTile);
int maxTileY = Math.Min(
(int)((-mOffset.Y + ClientSize.Height / z) / mTileSize), mMaxTile);
if (z == 1)
{
int offX = (int)mOffset.X, offY = (int)mOffset.Y;
int dX, dY;
for (int i = minTileX; i <= maxTileX; i++)
{
dX = i * mTileSize + offX;
for (int j = minTileY; j <= maxTileY; j++)
{
dY = j * mTileSize + offY;
GraphicsUtil.BitBlt(GetTile(i, j), ClientImage, dX, dY);
}
}
}
else
{
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.ScaleTransform(z, z);
float dX, dY;
for (int i = minTileX; i <= maxTileX; i++)
{
dX = i * mTileSize + mOffset.X;
for (int j = minTileY; j <= maxTileY; j++)
{
dY = j * mTileSize + mOffset.Y;
g.DrawImage(GetTile(i, j), dX, dY);
}
}
}
}
#endregion
g.Transform = origTransform;
#region Draw Route-Finding Regions
#if false
float regionZoom = z * mPixPerClick;
g.ScaleTransform(z, z);
g.TranslateTransform(mOffset.X + mMapSize / 2.0f - 1.0f, mOffset.Y + mMapSize / 2.0f);
g.ScaleTransform(mPixPerClick, -mPixPerClick);
Brush regionFill = new SolidBrush(Color.FromArgb(0x77CC0000));
Brush innerRegionFill = new SolidBrush(Color.FromArgb(0x77AB10BC));
RouteFinder.DrawRegions(g, regionFill, innerRegionFill);
g.Transform = origTransform;
#endif
#endregion
Coordinates minCoords = PixToCoords(0, 0, z);
Coordinates maxCoords = PixToCoords(ClientSize.Width, ClientSize.Height, z);
#region Draw Coordinates (Part I: Gridlines)
float lastTickNS = 0, firstTickEW = 0, lastTickEW = 0, firstTickNS = 0;
RectangleF mapRect = new RectangleF(), insideGutter = new RectangleF();
Region coordGutter = null;
string precision = "";
if (DrawCoords)
{
g.SmoothingMode = SmoothingMode.Default;
if (mCoordTickDelta >= 1)
precision = "0";
else if (mCoordTickDelta >= 0.1)
precision = "0.0";
else
precision = "0.00";
lastTickNS = (float)(Math.Floor(minCoords.NS / mCoordTickDelta) * mCoordTickDelta);
firstTickEW = (float)(Math.Floor(minCoords.EW / mCoordTickDelta) * mCoordTickDelta);
lastTickEW = (float)(Math.Ceiling(maxCoords.EW / mCoordTickDelta) * mCoordTickDelta);
firstTickNS = (float)(Math.Ceiling(maxCoords.NS / mCoordTickDelta) * mCoordTickDelta);
mapRect = new RectangleF(coordPadX, coordPadY,
ClientSize.Width - 2 * coordPadX, ClientSize.Height - 2 * coordPadY);
insideGutter = new RectangleF(
coordPadX + CoordGutterSize,
coordPadY + CoordGutterSize,
ClientSize.Width - 2 * (CoordGutterSize + coordPadX),
ClientSize.Height - 2 * (CoordGutterSize + coordPadY));
coordGutter = new Region(mapRect);
coordGutter.Xor(insideGutter);
g.FillRegion(CoordGutterFill, coordGutter);
// Draw Gridlines
for (float ns = firstTickNS, ew = firstTickEW;
ns <= lastTickNS || ew <= lastTickEW;
ns += mCoordTickDelta, ew += mCoordTickDelta)
{
PointF pos = CoordsToPix(ns, ew, z);
if (pos.Y > insideGutter.Top && pos.Y < insideGutter.Bottom)
{
// Draw horizontal NS gridline
g.DrawLine(CoordGridline, CoordGutterSize + coordPadX, pos.Y,
ClientSize.Width - CoordGutterSize - coordPadX - 1, pos.Y);
}
if (pos.X > insideGutter.Left && pos.X < insideGutter.Right)
{
// Draw vertical EW gridline
g.DrawLine(CoordGridline, pos.X, CoordGutterSize + coordPadY,
pos.X, ClientSize.Height - CoordGutterSize - coordPadY - 1);
}
}
}
#endregion
#region Draw Locations
mHotspots.Clear();
float z2 = (float)(Math.Pow(z / ZoomBase, 0.25));
if (ShowLocations)
{
g.SmoothingMode = SmoothingMode.Default;
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
Dictionary> visibleLocations
= new Dictionary>();
foreach (Location loc in mLocDb.Locations.Values)
{
if (loc.Coords.NS <= minCoords.NS && loc.Coords.NS >= maxCoords.NS
&& loc.Coords.EW >= minCoords.EW && loc.Coords.EW <= maxCoords.EW)
{
List addTo;
LocationType type = loc.Type;
if ((type & LocationType.AnyPortal) != 0)
{
type = LocationType.AnyPortal;
}
if (!visibleLocations.TryGetValue(type, out addTo))
{
addTo = new List();
visibleLocations.Add(type, addTo);
}
addTo.Add(loc);
}
}
if (ShowLocationsAllZooms || ZoomFactor >= 5)
{
DrawLocationType(visibleLocations, LocationType.NPC, Icons.Map.NPC, g, z, z2);
DrawLocationType(visibleLocations, LocationType.Vendor, Icons.Map.Store, g, z, z2);
}
if (ShowLocationsAllZooms || ZoomFactor >= 2)
{
DrawLocationType(visibleLocations, LocationType.Village, Icons.Map.Settlement, g, z, z2);
DrawLocationType(visibleLocations, LocationType.Landmark, Icons.Map.PointOfInterest, g, z, z2);
DrawLocationType(visibleLocations, LocationType.AllegianceHall, Icons.Map.Dungeon, g, z, z2);
DrawLocationType(visibleLocations, LocationType.Bindstone, Icons.Map.Bindstone, g, z, z2);
}
if (ShowLocationsAllZooms || ZoomFactor >= -1)
{
DrawLocationType(visibleLocations, LocationType.Custom, Icons.Map.PointOfInterest, g, z, z2);
DrawLocationType(visibleLocations, LocationType.Lifestone, Icons.Map.Lifestone, g, z, z2);
DrawLocationType(visibleLocations, LocationType.Dungeon, Icons.Map.Dungeon, g, z, z2);
DrawLocationType(visibleLocations, LocationType.AnyPortal, Icons.Map.Portal, g, z, z2);
DrawLocationType(visibleLocations, LocationType.PortalHub, Icons.Map.PortalHub, g, z, z2);
DrawLocationType(visibleLocations, LocationType.Outpost, Icons.Map.TownSmall, g, z, z2);
}
DrawLocationType(visibleLocations, LocationType.Town, Icons.Map.Town, g, z, z2);
//DrawLocationType(visibleLocations, LocationType._StartPoint, Icons.Map.StartPoint, g, z, z2);
//DrawLocationType(visibleLocations, LocationType._EndPoint, Icons.Map.EndPoint, g, z, z2);
if (ShowLabels)
{
float z3 = (float)(Math.Pow(z / ZoomBase, 0.1));
Font f = new Font(FontFamily.GenericSerif, Math.Min(8 * z3, 10));
Font fTown = new Font(FontFamily.GenericSerif, Math.Min(9 * z3, 12), FontStyle.Bold);
List usedLabelRects = new List();
if (ZoomFactor >= -4)
{
LabelLocationType(g, fTown, usedLabelRects, LocationType.Town);
}
if (ZoomFactor >= 1)
{
LabelLocationType(g, f, usedLabelRects, LocationType.Outpost);
LabelLocationType(g, f, usedLabelRects, LocationType.PortalHub);
LabelLocationType(g, f, usedLabelRects, LocationType.AnyPortal);
LabelLocationType(g, f, usedLabelRects, LocationType.Dungeon);
LabelLocationType(g, f, usedLabelRects, LocationType.Lifestone);
LabelLocationType(g, f, usedLabelRects, LocationType.Custom);
}
if (ZoomFactor >= 4)
{
LabelLocationType(g, f, usedLabelRects, LocationType.Bindstone);
LabelLocationType(g, f, usedLabelRects, LocationType.AllegianceHall);
LabelLocationType(g, f, usedLabelRects, LocationType.Landmark);
LabelLocationType(g, f, usedLabelRects, LocationType.Village);
}
//if (ZoomFactor >= 6) {
// LabelLocation(g, f, usedLabelRects, LocationType.Vendor);
// LabelLocation(g, f, usedLabelRects, LocationType.NPC);
//}
}
}
#endregion
#region Draw Route
if (ShowRoute && mRoute != null && mRoute.Count > 1)
{
g.SmoothingMode = SmoothingMode.AntiAlias;
PointF lastPoint, curPoint = CoordsToPix(mRoute[0].Coords, z);
for (int i = 1; i < mRoute.Count; i++)
{
// Running
lastPoint = curPoint;
curPoint = CoordsToPix(mRoute[i].Coords, z);
double dX = lastPoint.X - curPoint.X;
double dY = lastPoint.Y - curPoint.Y;
double dist = Math.Sqrt(dX * dX + dY * dY);
if (dist >= 5.0)
{
g.DrawLine(RouteLineBackground, lastPoint, curPoint);
g.DrawLine(RouteLine, lastPoint, curPoint);
}
// Portals
if (mRoute[i].HasExitCoords)
{
lastPoint = curPoint;
curPoint = CoordsToPix(mRoute[i].ExitCoords, z);
dX = lastPoint.X - curPoint.X;
dY = lastPoint.Y - curPoint.Y;
dist = Math.Sqrt(dX * dX + dY * dY);
if (dist >= 5.0)
{
//g.DrawLine(RouteLineBackground, lastPoint, curPoint);
g.DrawLine(RouteLinePortal, lastPoint, curPoint);
}
}
}
}
#endregion
#region Draw Arrow Destination
PointF ptf = CoordsToPix(mPluginCore.mArrowHud.DestinationCoords, z);
if (ptf.X >= 0 && ptf.Y >= 0 && ptf.X < ClientSize.Width && ptf.Y < ClientSize.Height)
{
float zw = z2 * Icons.Map.ArrowDest2.Width;
float zh = z2 * Icons.Map.ArrowDest2.Height;
RectangleF rectf = new RectangleF(ptf.X - zw / 2, ptf.Y - zh / 2, zw, zh);
g.DrawImage(Icons.Map.ArrowDest2, rectf);
Rectangle rect = new Rectangle(Point.Truncate(rectf.Location), Size.Ceiling(rectf.Size));
}
#endregion
#region Draw Coordinates (Part II: Labels)
if (DrawCoords)
{
Region clipNS = new Region(new RectangleF(mapRect.X, mapRect.Y + CoordGutterSize, mapRect.Width, mapRect.Height - 2 * CoordGutterSize));
Region clipEW = new Region(new RectangleF(mapRect.X + CoordGutterSize, mapRect.Y, mapRect.Width - 2 * CoordGutterSize, mapRect.Height));
Region origClip = g.Clip;
// Draw Labels
for (float ns = firstTickNS, ew = firstTickEW;
ns <= lastTickNS || ew <= lastTickEW;
ns += mCoordTickDelta, ew += mCoordTickDelta)
{
PointF pos = CoordsToPix(ns, ew, z);
string nsString = Math.Abs(ns).ToString(precision) + (ns >= 0 ? "N" : "S");
string ewString = Math.Abs(ew).ToString(precision) + (ew >= 0 ? "E" : "W");
SizeF nsSize = g.MeasureString(nsString, CoordTextFont);
SizeF ewSize = g.MeasureString(ewString, CoordTextFont);
g.Clip = clipNS;
float nsY = pos.Y - nsSize.Width / 2;
g.DrawString(nsString, CoordTextFont, Brushes.White, coordPadX, nsY, VerticalText);
g.DrawString(nsString, CoordTextFont, Brushes.White,
ClientSize.Width - coordPadX - nsSize.Height, nsY, VerticalText);
g.Clip = coordGutter;
float ewX = pos.X - ewSize.Width / 2;
g.DrawString(ewString, CoordTextFont, Brushes.White, ewX, coordPadY);
g.DrawString(ewString, CoordTextFont, Brushes.White,
ewX, ClientSize.Height - ewSize.Height - coordPadY);
}
g.Clip = origClip;
}
#endregion
PaintSprites(z);
mPreviousZoomMultiplier = z;
mLastPaintedPlayerCoords = PlayerCoords;
}
private void DrawLocationType(Dictionary> visible,
LocationType typeToDraw, Bitmap imageToUse, Graphics drawOn, float zoom, float imageZoom)
{
List locs;
List portalHubs;
if ((typeToDraw & LocationType.AnyPortal) == 0 || !visible.TryGetValue(LocationType.PortalHub, out portalHubs))
{
portalHubs = null;
}
if (visible.TryGetValue(typeToDraw, out locs))
{
float zw = imageZoom * imageToUse.Width;
float zh = imageZoom * imageToUse.Height;
foreach (Location loc in locs)
{
PointF ptf = CoordsToPix(loc.Coords, zoom);
RectangleF rectf = new RectangleF(ptf.X - zw / 2, ptf.Y - zh / 2, zw, zh);
bool draw = true;
// Hide portals that overlap a portal hub
// (likely that the portal is part of the hub)
if (portalHubs != null)
{
foreach (Location portalHub in portalHubs)
{
PointF portalHubPt = CoordsToPix(portalHub.Coords, zoom);
if (rectf.Contains(portalHubPt))
{
draw = false;
break;
}
}
}
if (draw)
{
drawOn.DrawImage(loc.IsFavorite ? Icons.Map.Favorite : imageToUse, rectf);
Rectangle rect = new Rectangle(Point.Truncate(rectf.Location), Size.Ceiling(rectf.Size));
mHotspots.AddFirst(new Hotspot(rect, loc));
}
}
}
}
private void LabelLocationType(Graphics drawOn, Font textFont, List usedLabelRects, LocationType typeToDraw)
{
foreach (Hotspot h in mHotspots)
{
if ((h.loc.Type & typeToDraw) != 0)
{
SizeF txtSize = drawOn.MeasureString(h.loc.Name, textFont);
float mX = (h.rect.Left + h.rect.Right) / 2.0f;
float mY = (h.rect.Top + h.rect.Bottom) / 2.0f;
float mbY = (h.rect.Top + 3 * h.rect.Bottom) / 4.0f;
RectangleF[] tryRects = {
// Bottom, Top, BelowCenter, Right, Left
new RectangleF(new PointF(mX - txtSize.Width / 2.0f, h.rect.Bottom - 1), txtSize),
new RectangleF(new PointF(mX - txtSize.Width / 2.0f, h.rect.Top - txtSize.Height + 1), txtSize),
new RectangleF(new PointF(mX - txtSize.Width / 2.0f, mbY - txtSize.Height / 2.0f), txtSize),
new RectangleF(new PointF(h.rect.Right - 1, mY - txtSize.Height / 2.0f), txtSize),
new RectangleF(new PointF(h.rect.Left - txtSize.Width + 1, mY - txtSize.Height / 2.0f), txtSize),
//// Farther Below, Above, Right, Left
//new RectangleF(new PointF(mX - txtSize.Width / 2.0f, h.rect.Bottom + 4), txtSize),
//new RectangleF(new PointF(mX - txtSize.Width / 2.0f, h.rect.Top - txtSize.Height - 4), txtSize),
//new RectangleF(new PointF(h.rect.Right + 4, mY - txtSize.Height / 2.0f), txtSize),
//new RectangleF(new PointF(h.rect.Left - txtSize.Width - 4, mY - txtSize.Height / 2.0f), txtSize),
// Corners: TopRight, BottomRight, TopLeft, BottomLeft
new RectangleF(new PointF(h.rect.Right - 1, h.rect.Top - txtSize.Height + 1), txtSize),
new RectangleF(new PointF(h.rect.Right - 1, h.rect.Bottom - 1), txtSize),
new RectangleF(new PointF(h.rect.Left - txtSize.Width + 1, h.rect.Top - txtSize.Height + 1), txtSize),
new RectangleF(new PointF(h.rect.Left - txtSize.Width + 1, h.rect.Bottom - 1), txtSize),
};
for (int i = 0; i < tryRects.Length; i++)
{
bool intersects = false;
foreach (RectangleF usedRect in usedLabelRects)
{
if (tryRects[i].IntersectsWith(usedRect))
{
intersects = true;
break;
}
}
if (!intersects)
{
PointF p = tryRects[i].Location;
drawOn.DrawString(h.loc.Name, textFont, TextGlow1, new PointF(p.X - 1, p.Y));
drawOn.DrawString(h.loc.Name, textFont, TextGlow1, new PointF(p.X, p.Y - 1));
drawOn.DrawString(h.loc.Name, textFont, TextGlow1, new PointF(p.X + 1, p.Y));
drawOn.DrawString(h.loc.Name, textFont, TextGlow1, new PointF(p.X, p.Y + 1));
drawOn.DrawString(h.loc.Name, textFont, Brushes.White, p);
usedLabelRects.Add(tryRects[i]);
break;
}
}
}
}
}
private void PaintSprites(float z)
{
mNeedsSpriteRepaint = false;
if (mSpriteHud == null || mSpriteHud.Lost)
{
if (mSpriteHud != null)
mSpriteHud.Dispose();
mSpriteHud = Host.Render.CreateHud(new Rectangle(ClientLocation, MaxSize));
mSpriteHud.Alpha = AlphaFrame;
mSpriteHud.Enabled = ClientVisible;
}
if (mSpriteHud.Region.Location != ClientLocation)
{
mSpriteHud.Region = new Rectangle(ClientLocation, mSpriteHud.Region.Size);
}
mSpriteHud.Clear();
// Paint hotspot rectangle
CalcActiveHotspot(MouseLocationClient);
if (ActiveHotspot != Hotspot.None)
{
Rectangle border = ConstrainRectangle(ShrinkRect(ActiveHotspot.rect, -1), 0, 0, ClientSize.Width - 1, ClientSize.Height - 1);
Rectangle inner = ConstrainRectangle(ActiveHotspot.rect, 0, 0, ClientSize.Width - 1, ClientSize.Height - 1);
if (inner.Width > 0 && inner.Height > 0)
{
mSpriteHud.Fill(border, Color.Gold);
mSpriteHud.Clear(inner);
}
}
// Paint Arrows
if (!PlayerInDungeon)
{
using (Graphics g = Graphics.FromImage(mSpriteBuffer))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
//Matrix origTransform = g.Transform;
float zArrow = z < 1.0f ? (float)Math.Pow(z, 1.0 / 3.0) : 1.0f;
// Paint Player Arrow
PointF playerPointPix = CoordsToPix(mPlayerCoords, z);
g.Clear(Clear);
Point hudDrawPoint = new Point(
(int)playerPointPix.X - mSpriteBuffer.Width / 2,
(int)playerPointPix.Y - mSpriteBuffer.Height / 2);
if (hudDrawPoint.X < 0)
hudDrawPoint.X = 0;
else if (hudDrawPoint.X > ClientSize.Width - mSpriteBuffer.Width)
hudDrawPoint.X = ClientSize.Width - mSpriteBuffer.Width;
if (hudDrawPoint.Y < 0)
hudDrawPoint.Y = 0;
else if (hudDrawPoint.Y > ClientSize.Height - mSpriteBuffer.Height)
hudDrawPoint.Y = ClientSize.Height - mSpriteBuffer.Height;
float offX = playerPointPix.X - hudDrawPoint.X;
float offY = playerPointPix.Y - hudDrawPoint.Y;
if (offX >= -4 && offX <= mSpriteBuffer.Width + 4 && offY >= -4 && offY <= mSpriteBuffer.Height + 4)
{
g.TranslateTransform(offX, offY);
g.RotateTransform((float)(PlayerHeadingRadians * 180.0 / Math.PI));
g.ScaleTransform(zArrow, zArrow);
g.FillPolygon(mPlayerArrowFill, PlayerArrowPolygon);
g.DrawPolygon(mPlayerArrowOutline, PlayerArrowPolygon);
//g.Transform = origTransform;
mSpriteHud.BeginRender();
mSpriteHud.DrawImage(mSpriteBuffer, new Rectangle(hudDrawPoint, mSpriteBuffer.Size));
mSpriteHud.EndRender();
}
}
}
}
private Rectangle ConstrainRectangle(Rectangle rect, int minX, int minY, int maxX, int maxY)
{
int left = rect.Left, top = rect.Top, right = rect.Right, bottom = rect.Bottom;
if (left < minX) { left = minX; }
if (top < minY) { top = minY; }
if (right > maxX) { right = maxX; }
if (bottom > maxY) { bottom = maxY; }
return Rectangle.FromLTRB(left, top, right, bottom);
}
private Bitmap GetTile(int x, int y)
{
return GetTile(new Point(x, y));
}
private Bitmap GetTile(Point p)
{
Bitmap tile;
if (mTileCache.TryGetValue(p, out tile))
{
mTileCacheAccesses.Remove(p);
}
else
{
if (mTileCache.Count >= TileCacheSize)
{
mTileCache.Remove(mTileCacheAccesses.Last.Value);
mTileCacheAccesses.RemoveLast();
}
tile = new Bitmap(mZipFile.GetInputStream(mZipFile.GetEntry(p.X + "," + p.Y + ".png")));
mTileCache.Add(p, tile);
}
mTileCacheAccesses.AddFirst(p);
return tile;
}
private float CalculateCoordTickDelta(float zoom)
{
const int DesiredPixBetweenTicks = 40;
float delta = DesiredPixBetweenTicks / (mPixPerClick * zoom);
float minDist = float.PositiveInfinity;
for (int i = 0; i < PossibleCoordDeltas.Length; i++)
{
float dist = Math.Abs(PossibleCoordDeltas[i] - delta);
if (dist < minDist || i == 0)
minDist = dist;
else
return PossibleCoordDeltas[i];
}
return PossibleCoordDeltas[PossibleCoordDeltas.Length - 1];
}
#endregion
#region Coordinate/Pixel Translation
private PointF CoordsToPix(Coordinates coords, float zoom)
{
return CoordsToPix((float)coords.NS, (float)coords.EW, zoom);
}
private PointF CoordsToPix(float NS, float EW, float zoom)
{
return new PointF(
(mMapSize / 2.0f + mOffset.X + EW * mPixPerClick - 1.0f) * zoom,
(mMapSize / 2.0f + mOffset.Y - NS * mPixPerClick) * zoom);
}
private Coordinates PixToCoords(PointF pix, float zoom)
{
return PixToCoords(pix.X, pix.Y, zoom);
}
private Coordinates PixToCoords(float x, float y, float zoom)
{
return new Coordinates(
-(y / zoom - mMapSize / 2.0f - mOffset.Y) / mPixPerClick,
(x / zoom - mMapSize / 2.0f - mOffset.X + 1.0f) / mPixPerClick);
}
private bool IsCenteredOnPlayer(float zoom, Size clientSize)
{
if (PlayerInDungeon)
return false;
PointF player = CoordsToPix(mPlayerCoords, zoom);
PointF center = new PointF(clientSize.Width / 2.0f, clientSize.Height / 2.0f);
return Math.Abs(player.X - center.X) < 5.0f && Math.Abs(player.Y - center.Y) < 5.0f;
}
private bool CenterOnPix(float x, float y, float zoom)
{
float deltaX = (float)Math.Round(ClientSize.Width / 2.0f - x) / zoom;
float deltaY = (float)Math.Round(ClientSize.Height / 2.0f - y) / zoom;
if (deltaX != 0 || deltaY != 0)
{
mOffset.X += deltaX;
mOffset.Y += deltaY;
mNeedsPlayerRecenter = false;
return true;
}
return false;
}
private bool CenterOnPix(PointF centerOn, float zoom)
{
return CenterOnPix(centerOn.X, centerOn.Y, zoom);
}
private bool CenterOnCoords(Coordinates centerOn, float zoom)
{
return CenterOnPix(CoordsToPix(centerOn, zoom), zoom);
}
public void CenterOnCoords(Coordinates centerOn)
{
mCenterOnCoords = centerOn;
RepaintAll();
}
#endregion
}
}