Files
OpenRA/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs
2016-09-25 17:04:17 +01:00

481 lines
13 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
{
public enum WorldTooltipType { None, Unexplored, Actor, FrozenActor, Resource }
public class ViewportControllerWidget : Widget
{
readonly ResourceLayer resourceLayer;
public readonly string TooltipTemplate = "WORLD_TOOLTIP";
public readonly string TooltipContainer;
Lazy<TooltipContainerWidget> tooltipContainer;
public WorldTooltipType TooltipType { get; private set; }
public ITooltip ActorTooltip { get; private set; }
public IProvideTooltipInfo[] ActorTooltipExtra { get; private set; }
public FrozenActor FrozenActorTooltip { get; private set; }
public ResourceType ResourceTooltip { get; private set; }
public int EdgeScrollThreshold = 15;
public int EdgeCornerScrollThreshold = 35;
int2? joystickScrollStart, joystickScrollEnd;
int2? standardScrollStart;
bool isStandardScrolling;
static readonly Dictionary<ScrollDirection, string> ScrollCursors = new Dictionary<ScrollDirection, string>
{
{ ScrollDirection.Up | ScrollDirection.Left, "scroll-tl" },
{ ScrollDirection.Up | ScrollDirection.Right, "scroll-tr" },
{ ScrollDirection.Down | ScrollDirection.Left, "scroll-bl" },
{ ScrollDirection.Down | ScrollDirection.Right, "scroll-br" },
{ ScrollDirection.Up, "scroll-t" },
{ ScrollDirection.Down, "scroll-b" },
{ ScrollDirection.Left, "scroll-l" },
{ ScrollDirection.Right, "scroll-r" },
};
static readonly Dictionary<ScrollDirection, string> JoystickCursors = new Dictionary<ScrollDirection, string>
{
{ ScrollDirection.Up | ScrollDirection.Left, "joystick-tl-blocked" },
{ ScrollDirection.Up | ScrollDirection.Right, "joystick-tr-blocked" },
{ ScrollDirection.Down | ScrollDirection.Left, "joystick-bl-blocked" },
{ ScrollDirection.Down | ScrollDirection.Right, "joystick-br-blocked" },
{ ScrollDirection.Up, "joystick-t-blocked" },
{ ScrollDirection.Down, "joystick-b-blocked" },
{ ScrollDirection.Left, "joystick-l-blocked" },
{ ScrollDirection.Right, "joystick-r-blocked" },
};
static readonly Dictionary<ScrollDirection, float2> ScrollOffsets = new Dictionary<ScrollDirection, float2>
{
{ ScrollDirection.Up, new float2(0, -1) },
{ ScrollDirection.Down, new float2(0, 1) },
{ ScrollDirection.Left, new float2(-1, 0) },
{ ScrollDirection.Right, new float2(1, 0) },
};
ScrollDirection keyboardDirections;
ScrollDirection edgeDirections;
World world;
WorldRenderer worldRenderer;
WPos?[] viewPortBookmarkSlots = new WPos?[4];
void SaveBookmark(int index, WPos position)
{
viewPortBookmarkSlots[index] = position;
}
void SaveCurrentPositionToBookmark(int index)
{
SaveBookmark(index, worldRenderer.Viewport.CenterPosition);
}
WPos? JumpToBookmark(int index)
{
return viewPortBookmarkSlots[index];
}
void JumpToSavedBookmark(int index)
{
var bookmark = JumpToBookmark(index);
if (bookmark != null)
worldRenderer.Viewport.Center((WPos)bookmark);
}
[ObjectCreator.UseCtor]
public ViewportControllerWidget(World world, WorldRenderer worldRenderer)
{
this.world = world;
this.worldRenderer = worldRenderer;
tooltipContainer = Exts.Lazy(() =>
Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
resourceLayer = world.WorldActor.TraitOrDefault<ResourceLayer>();
}
public override void MouseEntered()
{
if (TooltipContainer == null)
return;
tooltipContainer.Value.SetTooltip(TooltipTemplate,
new WidgetArgs() { { "world", world }, { "viewport", this } });
}
public override void MouseExited()
{
if (TooltipContainer == null)
return;
tooltipContainer.Value.RemoveTooltip();
}
long lastScrollTime = 0;
public override void Draw()
{
if (IsJoystickScrolling)
{
// Base the JoystickScrolling speed on the Scroll Speed slider
var rate = 0.01f * Game.Settings.Game.ViewportEdgeScrollStep;
var scroll = (joystickScrollEnd.Value - joystickScrollStart.Value).ToFloat2() * rate;
worldRenderer.Viewport.Scroll(scroll, false);
}
else
{
edgeDirections = ScrollDirection.None;
if (Game.Settings.Game.ViewportEdgeScroll && Game.HasInputFocus)
edgeDirections = CheckForDirections();
if (keyboardDirections != ScrollDirection.None || edgeDirections != ScrollDirection.None)
{
var scroll = float2.Zero;
foreach (var kv in ScrollOffsets)
if (keyboardDirections.Includes(kv.Key) || edgeDirections.Includes(kv.Key))
scroll += kv.Value;
// Scroll rate is defined for a 40ms interval
var deltaScale = Math.Min(Game.RunTime - lastScrollTime, 25f);
var length = Math.Max(1, scroll.Length);
scroll *= (deltaScale / (25 * length)) * Game.Settings.Game.ViewportEdgeScrollStep;
worldRenderer.Viewport.Scroll(scroll, false);
lastScrollTime = Game.RunTime;
}
}
UpdateMouseover();
base.Draw();
}
public void UpdateMouseover()
{
TooltipType = WorldTooltipType.None;
ActorTooltipExtra = null;
var cell = worldRenderer.Viewport.ViewToWorld(Viewport.LastMousePos);
if (!world.Map.Contains(cell))
return;
if (world.ShroudObscures(cell))
{
TooltipType = WorldTooltipType.Unexplored;
return;
}
var worldPixel = worldRenderer.Viewport.ViewToWorldPx(Viewport.LastMousePos);
var underCursor = world.ScreenMap.ActorsAt(worldPixel)
.Where(a => !world.FogObscures(a) && a.Info.HasTraitInfo<ITooltipInfo>())
.WithHighestSelectionPriority(worldPixel);
if (underCursor != null)
{
ActorTooltip = underCursor.TraitsImplementing<ITooltip>().First();
ActorTooltipExtra = underCursor.TraitsImplementing<IProvideTooltipInfo>().ToArray();
TooltipType = WorldTooltipType.Actor;
return;
}
var frozen = world.ScreenMap.FrozenActorsAt(world.RenderPlayer, worldPixel)
.Where(a => a.TooltipInfo != null && a.IsValid)
.WithHighestSelectionPriority(worldPixel);
if (frozen != null)
{
var actor = frozen.Actor;
if (actor != null && actor.TraitsImplementing<IVisibilityModifier>().All(t => t.IsVisible(actor, world.RenderPlayer)))
{
FrozenActorTooltip = frozen;
if (frozen.Actor != null)
ActorTooltipExtra = frozen.Actor.TraitsImplementing<IProvideTooltipInfo>().ToArray();
TooltipType = WorldTooltipType.FrozenActor;
return;
}
}
if (resourceLayer != null)
{
var resource = resourceLayer.GetRenderedResource(cell);
if (resource != null)
{
TooltipType = WorldTooltipType.Resource;
ResourceTooltip = resource;
}
}
}
public override string GetCursor(int2 pos)
{
if (!(IsJoystickScrolling || isStandardScrolling) &&
(!Game.Settings.Game.ViewportEdgeScroll || Ui.MouseOverWidget != this))
return null;
var blockedDirections = worldRenderer.Viewport.GetBlockedDirections();
if (IsJoystickScrolling || isStandardScrolling)
{
foreach (var dir in JoystickCursors)
if (blockedDirections.Includes(dir.Key))
return dir.Value;
return "joystick-all";
}
foreach (var dir in ScrollCursors)
if (edgeDirections.Includes(dir.Key))
return dir.Value + (blockedDirections.Includes(dir.Key) ? "-blocked" : "");
return null;
}
bool IsJoystickScrolling
{
get
{
return joystickScrollStart.HasValue && joystickScrollEnd.HasValue &&
(joystickScrollStart.Value - joystickScrollEnd.Value).Length > Game.Settings.Game.MouseScrollDeadzone;
}
}
bool IsZoomAllowed(float zoom)
{
return world.IsGameOver || zoom >= 1.0f || world.IsReplay || world.LocalPlayer == null || world.LocalPlayer.Spectating;
}
void Zoom(int direction)
{
var zoomSteps = worldRenderer.Viewport.AvailableZoomSteps;
var currentZoom = worldRenderer.Viewport.Zoom;
var nextIndex = zoomSteps.IndexOf(currentZoom);
if (direction < 0)
nextIndex++;
else
nextIndex--;
if (nextIndex < 0 || nextIndex >= zoomSteps.Count())
return;
var zoom = zoomSteps.ElementAt(nextIndex);
if (!IsZoomAllowed(zoom))
return;
worldRenderer.Viewport.Zoom = zoom;
}
public override bool HandleMouseInput(MouseInput mi)
{
if (mi.Event == MouseInputEvent.Scroll &&
Game.Settings.Game.AllowZoom && mi.Modifiers.HasModifier(Game.Settings.Game.ZoomModifier))
{
Zoom(mi.ScrollDelta);
return true;
}
var scrollType = MouseScrollType.Disabled;
if (mi.Button == MouseButton.Middle || mi.Button == (MouseButton.Left | MouseButton.Right))
scrollType = Game.Settings.Game.MiddleMouseScroll;
else if (mi.Button == MouseButton.Right)
scrollType = Game.Settings.Game.RightMouseScroll;
if (scrollType == MouseScrollType.Disabled)
return false;
if (scrollType == MouseScrollType.Standard || scrollType == MouseScrollType.Inverted)
{
if (mi.Event == MouseInputEvent.Down && !isStandardScrolling)
standardScrollStart = mi.Location;
else if (mi.Event == MouseInputEvent.Move && (isStandardScrolling ||
(standardScrollStart.HasValue && ((standardScrollStart.Value - mi.Location).Length > Game.Settings.Game.MouseScrollDeadzone))))
{
isStandardScrolling = true;
var d = scrollType == MouseScrollType.Inverted ? -1 : 1;
worldRenderer.Viewport.Scroll((Viewport.LastMousePos - mi.Location) * d, false);
return true;
}
else if (mi.Event == MouseInputEvent.Up)
{
var wasStandardScrolling = isStandardScrolling;
isStandardScrolling = false;
standardScrollStart = null;
if (wasStandardScrolling)
return true;
}
}
// Tiberian Sun style click-and-drag scrolling
if (scrollType == MouseScrollType.Joystick)
{
if (mi.Event == MouseInputEvent.Down)
{
if (!TakeMouseFocus(mi))
return false;
joystickScrollStart = mi.Location;
}
if (mi.Event == MouseInputEvent.Up)
{
var wasJoystickScrolling = IsJoystickScrolling;
joystickScrollStart = joystickScrollEnd = null;
YieldMouseFocus(mi);
if (wasJoystickScrolling)
return true;
}
if (mi.Event == MouseInputEvent.Move && joystickScrollStart.HasValue)
joystickScrollEnd = mi.Location;
}
return false;
}
public override bool YieldKeyboardFocus()
{
keyboardDirections = ScrollDirection.None;
return base.YieldKeyboardFocus();
}
public override bool HandleKeyPress(KeyInput e)
{
var key = Hotkey.FromKeyInput(e);
var ks = Game.Settings.Keys;
if (key == ks.MapScrollUp)
{
keyboardDirections = keyboardDirections.Set(ScrollDirection.Up, e.Event == KeyInputEvent.Down);
return true;
}
if (key == ks.MapScrollDown)
{
keyboardDirections = keyboardDirections.Set(ScrollDirection.Down, e.Event == KeyInputEvent.Down);
return true;
}
if (key == ks.MapScrollLeft)
{
keyboardDirections = keyboardDirections.Set(ScrollDirection.Left, e.Event == KeyInputEvent.Down);
return true;
}
if (key == ks.MapScrollRight)
{
keyboardDirections = keyboardDirections.Set(ScrollDirection.Right, e.Event == KeyInputEvent.Down);
return true;
}
if (key == ks.MapPushTop)
{
worldRenderer.Viewport.Center(new WPos(worldRenderer.Viewport.CenterPosition.X, 0, 0));
return false;
}
if (key == ks.MapPushBottom)
{
worldRenderer.Viewport.Center(new WPos(worldRenderer.Viewport.CenterPosition.X, worldRenderer.World.Map.ProjectedBottomRight.Y, 0));
return false;
}
if (key == ks.MapPushLeftEdge)
{
worldRenderer.Viewport.Center(new WPos(0, worldRenderer.Viewport.CenterPosition.Y, 0));
return false;
}
if (key == ks.MapPushRightEdge)
{
worldRenderer.Viewport.Center(new WPos(worldRenderer.World.Map.ProjectedBottomRight.X, worldRenderer.Viewport.CenterPosition.Y, 0));
}
if (key == ks.ViewPortBookmarkSaveSlot1)
{
SaveCurrentPositionToBookmark(0);
return false;
}
if (key == ks.ViewPortBookmarkSaveSlot2)
{
SaveCurrentPositionToBookmark(1);
return false;
}
if (key == ks.ViewPortBookmarkSaveSlot3)
{
SaveCurrentPositionToBookmark(2);
return false;
}
if (key == ks.ViewPortBookmarkSaveSlot4)
{
SaveCurrentPositionToBookmark(3);
return false;
}
if (key == ks.ViewPortBookmarkUseSlot1)
{
JumpToSavedBookmark(0);
return false;
}
if (key == ks.ViewPortBookmarkUseSlot2)
{
JumpToSavedBookmark(1);
return false;
}
if (key == ks.ViewPortBookmarkUseSlot3)
{
JumpToSavedBookmark(2);
return false;
}
if (key == ks.ViewPortBookmarkUseSlot4)
{
JumpToSavedBookmark(3);
return false;
}
return false;
}
ScrollDirection CheckForDirections()
{
var directions = ScrollDirection.None;
if (Viewport.LastMousePos.X < EdgeScrollThreshold)
directions |= ScrollDirection.Left;
if (Viewport.LastMousePos.Y < EdgeScrollThreshold)
directions |= ScrollDirection.Up;
if (Viewport.LastMousePos.X >= Game.Renderer.Resolution.Width - EdgeScrollThreshold)
directions |= ScrollDirection.Right;
if (Viewport.LastMousePos.Y >= Game.Renderer.Resolution.Height - EdgeScrollThreshold)
directions |= ScrollDirection.Down;
return directions;
}
}
}