Support custom zoom levels
This commit is contained in:
@@ -143,7 +143,7 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
++RenderFrame;
|
++RenderFrame;
|
||||||
viewport.DrawRegions(worldRenderer, new DefaultInputHandler( orderManager.world ));
|
viewport.DrawRegions(worldRenderer, new DefaultInputHandler( orderManager.world ));
|
||||||
Sound.SetListenerPosition(viewport.Location + .5f * new float2(viewport.Width, viewport.Height));
|
Sound.SetListenerPosition(viewport.CenterLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
PerfHistory.items["render"].Tick();
|
PerfHistory.items["render"].Tick();
|
||||||
|
|||||||
@@ -73,15 +73,15 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
internal IGraphicsDevice Device { get { return device; } }
|
internal IGraphicsDevice Device { get { return device; } }
|
||||||
|
|
||||||
public void BeginFrame(float2 scroll)
|
public void BeginFrame(float2 scroll, float zoom)
|
||||||
{
|
{
|
||||||
device.Clear(Color.Black);
|
device.Clear(Color.Black);
|
||||||
|
|
||||||
float2 r1 = new float2(2f/Resolution.Width, -2f/Resolution.Height);
|
float2 r1 = new float2(2f/Resolution.Width, -2f/Resolution.Height);
|
||||||
float2 r2 = new float2(-1, 1);
|
float2 r2 = new float2(-1, 1);
|
||||||
|
var zr1 = zoom*r1;
|
||||||
SetShaderParams( WorldSpriteShader, r1, r2, scroll );
|
SetShaderParams( WorldSpriteShader, zr1, r2, scroll );
|
||||||
SetShaderParams( WorldLineShader, r1, r2, scroll );
|
SetShaderParams( WorldLineShader, zr1, r2, scroll );
|
||||||
SetShaderParams( LineShader, r1, r2, scroll );
|
SetShaderParams( LineShader, r1, r2, scroll );
|
||||||
SetShaderParams( RgbaSpriteShader, r1, r2, scroll );
|
SetShaderParams( RgbaSpriteShader, r1, r2, scroll );
|
||||||
SetShaderParams( SpriteShader, r1, r2, scroll );
|
SetShaderParams( SpriteShader, r1, r2, scroll );
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
int verticesPerRow = map.Bounds.Width * 4;
|
int verticesPerRow = map.Bounds.Width * 4;
|
||||||
|
|
||||||
int visibleRows = (int)(viewport.Height * 1f / Game.CellSize + 2);
|
int visibleRows = (int)(viewport.Height * 1f / Game.CellSize / viewport.Zoom + 2);
|
||||||
|
|
||||||
int firstRow = (int)(viewport.Location.Y * 1f / Game.CellSize - map.Bounds.Top);
|
int firstRow = (int)(viewport.Location.Y * 1f / Game.CellSize - map.Bounds.Top);
|
||||||
int lastRow = firstRow + visibleRows;
|
int lastRow = firstRow + visibleRows;
|
||||||
|
|||||||
@@ -34,10 +34,13 @@ namespace OpenRA.Graphics
|
|||||||
readonly Renderer renderer;
|
readonly Renderer renderer;
|
||||||
readonly Rectangle adjustedMapBounds;
|
readonly Rectangle adjustedMapBounds;
|
||||||
|
|
||||||
|
// Top-left of the viewport, in world-px units
|
||||||
public float2 Location { get { return scrollPosition; } }
|
public float2 Location { get { return scrollPosition; } }
|
||||||
|
public float2 CenterLocation { get { return scrollPosition + 0.5f/Zoom*screenSize.ToFloat2(); } }
|
||||||
|
|
||||||
public int Width { get { return screenSize.X; } }
|
public int Width { get { return screenSize.X; } }
|
||||||
public int Height { get { return screenSize.Y; } }
|
public int Height { get { return screenSize.Y; } }
|
||||||
|
public float Zoom = 1f;
|
||||||
|
|
||||||
float cursorFrame = 0f;
|
float cursorFrame = 0f;
|
||||||
|
|
||||||
@@ -51,7 +54,8 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
public void Scroll(float2 delta, bool ignoreBorders)
|
public void Scroll(float2 delta, bool ignoreBorders)
|
||||||
{
|
{
|
||||||
var d = delta.ToInt2();
|
// Convert from world-px to viewport-px
|
||||||
|
var d = (1f/Zoom*delta).ToInt2();
|
||||||
var newScrollPosition = scrollPosition + d;
|
var newScrollPosition = scrollPosition + d;
|
||||||
|
|
||||||
if(!ignoreBorders)
|
if(!ignoreBorders)
|
||||||
@@ -88,7 +92,7 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
public void DrawRegions( WorldRenderer wr, IInputHandler inputHandler )
|
public void DrawRegions( WorldRenderer wr, IInputHandler inputHandler )
|
||||||
{
|
{
|
||||||
renderer.BeginFrame(scrollPosition);
|
renderer.BeginFrame(scrollPosition, Zoom);
|
||||||
if (wr != null)
|
if (wr != null)
|
||||||
wr.Draw();
|
wr.Draw();
|
||||||
|
|
||||||
@@ -115,23 +119,20 @@ namespace OpenRA.Graphics
|
|||||||
{
|
{
|
||||||
cursorFrame += 0.5f;
|
cursorFrame += 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert from viewport coords to cell coords (not px)
|
||||||
|
public float2 ViewToWorld(MouseInput mi) { return ViewToWorld(mi.Location); }
|
||||||
public float2 ViewToWorld(int2 loc)
|
public float2 ViewToWorld(int2 loc)
|
||||||
{
|
{
|
||||||
return (1f / Game.CellSize) * (loc.ToFloat2() + Location);
|
return (1f / Game.CellSize) * (1f/Zoom*loc.ToFloat2() + Location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float2 ViewToWorld(MouseInput mi)
|
public int2 ViewToWorldPx(int2 loc) { return (1f/Zoom*loc.ToFloat2() + Location).ToInt2(); }
|
||||||
{
|
|
||||||
return ViewToWorld(mi.Location);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int2 ViewToWorldPx(int2 loc) { return loc + Location.ToInt2(); }
|
|
||||||
public int2 ViewToWorldPx(MouseInput mi) { return ViewToWorldPx(mi.Location); }
|
public int2 ViewToWorldPx(MouseInput mi) { return ViewToWorldPx(mi.Location); }
|
||||||
|
|
||||||
public void Center(float2 loc)
|
public void Center(float2 loc)
|
||||||
{
|
{
|
||||||
scrollPosition = this.NormalizeScrollPosition((Game.CellSize*loc - screenSize / 2).ToInt2());
|
scrollPosition = this.NormalizeScrollPosition((Game.CellSize*loc - 1f/(2*Zoom)*screenSize.ToFloat2()).ToInt2());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Center(IEnumerable<Actor> actors)
|
public void Center(IEnumerable<Actor> actors)
|
||||||
@@ -141,27 +142,26 @@ namespace OpenRA.Graphics
|
|||||||
var avgPos = actors
|
var avgPos = actors
|
||||||
.Select(a => a.CenterLocation)
|
.Select(a => a.CenterLocation)
|
||||||
.Aggregate((a, b) => a + b) / actors.Count();
|
.Aggregate((a, b) => a + b) / actors.Count();
|
||||||
scrollPosition = this.NormalizeScrollPosition((avgPos - screenSize / 2));
|
scrollPosition = this.NormalizeScrollPosition((avgPos - 1f/(2*Zoom)*screenSize.ToFloat2()).ToInt2());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rectangle (in viewport coords) that contains things to be drawn
|
||||||
public Rectangle ViewBounds(World world)
|
public Rectangle ViewBounds(World world)
|
||||||
{
|
{
|
||||||
var r = WorldBounds(world);
|
var r = WorldBounds(world);
|
||||||
var left = (int)(Game.CellSize * r.Left - Game.viewport.Location.X);
|
var origin = Location.ToInt2();
|
||||||
var top = (int)(Game.CellSize * r.Top - Game.viewport.Location.Y);
|
var left = Math.Max(0, Game.CellSize * r.Left - origin.X)*Zoom;
|
||||||
var right = left + (int)(Game.CellSize * r.Width);
|
var top = Math.Max(0, Game.CellSize * r.Top - origin.Y)*Zoom;
|
||||||
var bottom = top + (int)(Game.CellSize * r.Height);
|
var right = Math.Min((Game.CellSize * r.Right - origin.X) * Zoom, Width);
|
||||||
|
var bottom = Math.Min((Game.CellSize * r.Bottom - origin.Y) * Zoom, Height);
|
||||||
if (left < 0) left = 0;
|
|
||||||
if (top < 0) top = 0;
|
return Rectangle.FromLTRB((int)left, (int)top, (int)right, (int)bottom);
|
||||||
if (right > Game.viewport.Width) right = Game.viewport.Width;
|
|
||||||
if (bottom > Game.viewport.Height) bottom = Game.viewport.Height;
|
|
||||||
return new Rectangle(left, top, right - left, bottom - top);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int2 cachedScroll = new int2(int.MaxValue, int.MaxValue);
|
int2 cachedScroll = new int2(int.MaxValue, int.MaxValue);
|
||||||
Rectangle cachedRect;
|
Rectangle cachedRect;
|
||||||
|
|
||||||
|
// Rectangle (in cell coords) of cells that are currently visible on the screen
|
||||||
public Rectangle WorldBounds(World world)
|
public Rectangle WorldBounds(World world)
|
||||||
{
|
{
|
||||||
if (cachedScroll != scrollPosition)
|
if (cachedScroll != scrollPosition)
|
||||||
@@ -169,6 +169,7 @@ namespace OpenRA.Graphics
|
|||||||
int2 boundary = new int2(1,1); // Add a curtain of cells around the viewport to account for rounding errors
|
int2 boundary = new int2(1,1); // Add a curtain of cells around the viewport to account for rounding errors
|
||||||
var tl = ViewToWorld(int2.Zero).ToInt2() - boundary;
|
var tl = ViewToWorld(int2.Zero).ToInt2() - boundary;
|
||||||
var br = ViewToWorld(new int2(Width, Height)).ToInt2() + boundary;
|
var br = ViewToWorld(new int2(Width, Height)).ToInt2() + boundary;
|
||||||
|
|
||||||
cachedRect = Rectangle.Intersect(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y), world.Map.Bounds);
|
cachedRect = Rectangle.Intersect(Rectangle.FromLTRB(tl.X, tl.Y, br.X, br.Y), world.Map.Bounds);
|
||||||
cachedScroll = scrollPosition;
|
cachedScroll = scrollPosition;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,14 +52,12 @@ namespace OpenRA.Graphics
|
|||||||
|
|
||||||
IEnumerable<Renderable> SpritesToRender()
|
IEnumerable<Renderable> SpritesToRender()
|
||||||
{
|
{
|
||||||
var bounds = Game.viewport.ViewBounds(world);
|
var bounds = Game.viewport.WorldBounds(world);
|
||||||
var comparer = new SpriteComparer();
|
var comparer = new SpriteComparer();
|
||||||
|
|
||||||
bounds.Offset((int)Game.viewport.Location.X, (int)Game.viewport.Location.Y);
|
|
||||||
|
|
||||||
var actors = world.FindUnits(
|
var actors = world.FindUnits(
|
||||||
new int2(bounds.Left, bounds.Top),
|
new int2(Game.CellSize*bounds.Left, Game.CellSize*bounds.Top),
|
||||||
new int2(bounds.Right, bounds.Bottom));
|
new int2(Game.CellSize*bounds.Right, Game.CellSize*bounds.Bottom));
|
||||||
|
|
||||||
var renderables = actors.SelectMany(a => a.Render())
|
var renderables = actors.SelectMany(a => a.Render())
|
||||||
.OrderBy(r => r, comparer);
|
.OrderBy(r => r, comparer);
|
||||||
|
|||||||
@@ -48,9 +48,7 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
float GetIntensity()
|
float GetIntensity()
|
||||||
{
|
{
|
||||||
var cp = Game.viewport.Location
|
var cp = Game.viewport.CenterLocation;
|
||||||
+ .5f * new float2(Game.viewport.Width, Game.viewport.Height);
|
|
||||||
|
|
||||||
var intensity = Game.CellSize * Game.CellSize * 100 * shakeEffects.Sum(
|
var intensity = Game.CellSize * Game.CellSize * 100 * shakeEffects.Sum(
|
||||||
e => e.Intensity / (e.Position - cp).LengthSquared);
|
e => e.Intensity / (e.Position - cp).LengthSquared);
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ namespace OpenRA.Mods.Cnc
|
|||||||
loadTimer.Reset();
|
loadTimer.Reset();
|
||||||
|
|
||||||
loadTick = ++loadTick % 8;
|
loadTick = ++loadTick % 8;
|
||||||
r.BeginFrame(float2.Zero);
|
r.BeginFrame(float2.Zero, 1f);
|
||||||
r.RgbaSpriteRenderer.DrawSprite(gdiLogo, gdiPos);
|
r.RgbaSpriteRenderer.DrawSprite(gdiLogo, gdiPos);
|
||||||
r.RgbaSpriteRenderer.DrawSprite(nodLogo, nodPos);
|
r.RgbaSpriteRenderer.DrawSprite(nodLogo, nodPos);
|
||||||
r.RgbaSpriteRenderer.DrawSprite(evaLogo, evaPos);
|
r.RgbaSpriteRenderer.DrawSprite(evaLogo, evaPos);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace OpenRA.Mods.RA.Effects
|
|||||||
string s;
|
string s;
|
||||||
int remaining;
|
int remaining;
|
||||||
int velocity;
|
int velocity;
|
||||||
float2 pos;
|
float2 pos, offset;
|
||||||
Color color;
|
Color color;
|
||||||
|
|
||||||
public CashTick(int value, int lifetime, int velocity, float2 pos, Color color)
|
public CashTick(int value, int lifetime, int velocity, float2 pos, Color color)
|
||||||
@@ -28,7 +28,8 @@ namespace OpenRA.Mods.RA.Effects
|
|||||||
this.color = color;
|
this.color = color;
|
||||||
this.velocity = velocity;
|
this.velocity = velocity;
|
||||||
s = "{0}${1}".F(value < 0 ? "-" : "+", value);
|
s = "{0}${1}".F(value < 0 ? "-" : "+", value);
|
||||||
this.pos = pos - 0.5f*Game.Renderer.Fonts["TinyBold"].Measure(s).ToFloat2();
|
this.pos = pos;
|
||||||
|
offset = 0.5f*Game.Renderer.Fonts["TinyBold"].Measure(s).ToFloat2();
|
||||||
remaining = lifetime;
|
remaining = lifetime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ namespace OpenRA.Mods.RA.Effects
|
|||||||
|
|
||||||
public IEnumerable<Renderable> Render()
|
public IEnumerable<Renderable> Render()
|
||||||
{
|
{
|
||||||
Game.Renderer.Fonts["TinyBold"].DrawTextWithContrast(s, pos - Game.viewport.Location, color, Color.Black,1);
|
Game.Renderer.Fonts["TinyBold"].DrawTextWithContrast(s, Game.viewport.Zoom*(pos - Game.viewport.Location) - offset, color, Color.Black,1);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace OpenRA.Mods.RA
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Draw a black screen
|
// Draw a black screen
|
||||||
Game.Renderer.BeginFrame(float2.Zero);
|
Game.Renderer.BeginFrame(float2.Zero, 1f);
|
||||||
Game.Renderer.EndFrame( new NullInputHandler() );
|
Game.Renderer.EndFrame( new NullInputHandler() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace OpenRA.Mods.RA
|
|||||||
var text = Comments.Random(Game.CosmeticRandom);
|
var text = Comments.Random(Game.CosmeticRandom);
|
||||||
var textSize = r.Fonts["Bold"].Measure(text);
|
var textSize = r.Fonts["Bold"].Measure(text);
|
||||||
|
|
||||||
r.BeginFrame(float2.Zero);
|
r.BeginFrame(float2.Zero, 1f);
|
||||||
WidgetUtils.FillRectWithSprite(StripeRect, Stripe);
|
WidgetUtils.FillRectWithSprite(StripeRect, Stripe);
|
||||||
r.RgbaSpriteRenderer.DrawSprite(Logo, LogoPos);
|
r.RgbaSpriteRenderer.DrawSprite(Logo, LogoPos);
|
||||||
r.Fonts["Bold"].DrawText(text, new float2(Renderer.Resolution.Width - textSize.X - 20, Renderer.Resolution.Height - textSize.Y - 20), Color.White);
|
r.Fonts["Bold"].DrawText(text, new float2(Renderer.Resolution.Width - textSize.X - 20, Renderer.Resolution.Height - textSize.Y - 20), Color.White);
|
||||||
|
|||||||
@@ -129,27 +129,28 @@ namespace OpenRA.Mods.RA.Widgets
|
|||||||
if( world == null || world.LocalPlayer == null ) return;
|
if( world == null || world.LocalPlayer == null ) return;
|
||||||
|
|
||||||
radarCollection = "radar-" + world.LocalPlayer.Country.Race;
|
radarCollection = "radar-" + world.LocalPlayer.Country.Race;
|
||||||
|
var rsr = Game.Renderer.RgbaSpriteRenderer;
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(ChromeProvider.GetImage(radarCollection, "left"), radarOrigin);
|
rsr.DrawSprite(ChromeProvider.GetImage(radarCollection, "left"), radarOrigin);
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(ChromeProvider.GetImage(radarCollection, "right"), radarOrigin + new float2(201, 0));
|
rsr.DrawSprite(ChromeProvider.GetImage(radarCollection, "right"), radarOrigin + new float2(201, 0));
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(ChromeProvider.GetImage(radarCollection, "bottom"), radarOrigin + new float2(0, 192));
|
rsr.DrawSprite(ChromeProvider.GetImage(radarCollection, "bottom"), radarOrigin + new float2(0, 192));
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(ChromeProvider.GetImage(radarCollection, "bg"), radarOrigin + new float2(9, 0));
|
rsr.DrawSprite(ChromeProvider.GetImage(radarCollection, "bg"), radarOrigin + new float2(9, 0));
|
||||||
|
|
||||||
// Don't draw the radar if the tray is moving
|
// Don't draw the radar if the tray is moving
|
||||||
if (radarAnimationFrame >= radarSlideAnimationLength)
|
if (radarAnimationFrame >= radarSlideAnimationLength)
|
||||||
{
|
{
|
||||||
var o = new float2(mapRect.Location.X, mapRect.Location.Y + world.Map.Bounds.Height * previewScale * (1 - radarMinimapHeight)/2);
|
var o = new float2(mapRect.Location.X, mapRect.Location.Y + world.Map.Bounds.Height * previewScale * (1 - radarMinimapHeight)/2);
|
||||||
var s = new float2(mapRect.Size.Width, mapRect.Size.Height*radarMinimapHeight);
|
var s = new float2(mapRect.Size.Width, mapRect.Size.Height*radarMinimapHeight);
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(terrainSprite, o, s);
|
rsr.DrawSprite(terrainSprite, o, s);
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(customTerrainSprite, o, s);
|
rsr.DrawSprite(customTerrainSprite, o, s);
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(actorSprite, o, s);
|
rsr.DrawSprite(actorSprite, o, s);
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(shroudSprite, o, s);
|
rsr.DrawSprite(shroudSprite, o, s);
|
||||||
|
|
||||||
// Draw viewport rect
|
// Draw viewport rect
|
||||||
if (radarAnimationFrame == radarSlideAnimationLength + radarActivateAnimationLength)
|
if (radarAnimationFrame == radarSlideAnimationLength + radarActivateAnimationLength)
|
||||||
{
|
{
|
||||||
var tl = CellToMinimapPixel(new int2((int)(Game.viewport.Location.X/Game.CellSize), (int)(Game.viewport.Location.Y/Game.CellSize)));
|
var tl = CellToMinimapPixel(new int2((int)(Game.viewport.Location.X/Game.CellSize), (int)(Game.viewport.Location.Y/Game.CellSize)));
|
||||||
var br = CellToMinimapPixel(new int2((int)((Game.viewport.Location.X + Game.viewport.Width)/Game.CellSize), (int)((Game.viewport.Location.Y + Game.viewport.Height)/Game.CellSize)));
|
var br = CellToMinimapPixel(new int2((int)((Game.viewport.Location.X + Game.viewport.Width)/Game.CellSize), (int)((Game.viewport.Location.Y + Game.viewport.Height)/Game.CellSize)));
|
||||||
|
|
||||||
Game.Renderer.EnableScissor((int)mapRect.Left, (int)mapRect.Top, (int)mapRect.Width, (int)mapRect.Height);
|
Game.Renderer.EnableScissor((int)mapRect.Left, (int)mapRect.Top, (int)mapRect.Width, (int)mapRect.Height);
|
||||||
Game.Renderer.LineRenderer.DrawRect(tl, br, Color.White);
|
Game.Renderer.LineRenderer.DrawRect(tl, br, Color.White);
|
||||||
Game.Renderer.DisableScissor();
|
Game.Renderer.DisableScissor();
|
||||||
|
|||||||
@@ -129,10 +129,11 @@ namespace OpenRA.Mods.RA.Widgets
|
|||||||
|
|
||||||
var o = new float2(mapRect.Location.X, mapRect.Location.Y + world.Map.Bounds.Height * previewScale * (1 - radarMinimapHeight)/2);
|
var o = new float2(mapRect.Location.X, mapRect.Location.Y + world.Map.Bounds.Height * previewScale * (1 - radarMinimapHeight)/2);
|
||||||
var s = new float2(mapRect.Size.Width, mapRect.Size.Height*radarMinimapHeight);
|
var s = new float2(mapRect.Size.Width, mapRect.Size.Height*radarMinimapHeight);
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(terrainSprite, o, s);
|
var rsr = Game.Renderer.RgbaSpriteRenderer;
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(customTerrainSprite, o, s);
|
rsr.DrawSprite(terrainSprite, o, s);
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(actorSprite, o, s);
|
rsr.DrawSprite(customTerrainSprite, o, s);
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(shroudSprite, o, s);
|
rsr.DrawSprite(actorSprite, o, s);
|
||||||
|
rsr.DrawSprite(shroudSprite, o, s);
|
||||||
|
|
||||||
// Draw viewport rect
|
// Draw viewport rect
|
||||||
if (hasRadar && !Animating)
|
if (hasRadar && !Animating)
|
||||||
|
|||||||
Reference in New Issue
Block a user