internal bool Add(pSprite p) { if (p == null) { return(false); } if (ForwardPlayOptimisations && ForwardPlayOptimisedAdd) { p.AlwaysDraw = false; } if (ForwardPlayOptimisedAdd && p.Transformations.Count > 0) { int index = ForwardPlayList.BinarySearch(p); if (index < 0) { ForwardPlayList.Insert(~index, p); } else { ForwardPlayList.Insert(index, p); } return(true); } lock (spriteList) { int index = spriteList.BinarySearch(p, pSpriteDepthComparer); p.HoverPriority = HoverPriorityCurrent++; spriteList.Insert(index < 0 ? ~index : index, p); } return(true); }
/// <summary> /// Reset click handling, useful to allow rapid clicking of a sprite when holding mouse button down. /// </summary> internal void ResetClick() { GameBase.Scheduler.Add(delegate { ClickHandledSprite = null; clickHandledAtLeastOnce = false; globalClickHandledAtLeastOnce = false; }, true); }
internal void Remove(pSprite p) { if (p != null) { lock (spriteList) spriteList.Remove(p); p.IsVisible = false; } }
/// <summary> /// Handle input for all sprites contained in this handler. /// </summary> /// <returns>true if input was handled</returns> internal bool CheckInput(bool highPass, bool alreadyHandled) { if (!HandleInput) { return(false); } if (highPass != HandleOverlayInput) { return(false); } if (Alpha < 0.01f || Bypass) { return(false); } pSprite lastHoverSprite = CurrentHoverSprite; resetHoverSprite(); lock (SpriteList) { for (int i = 0; i < SpriteList.Count; i++) { pDrawable s = SpriteList[i]; if (s.Bypass) { continue; } if (!alreadyHandled) { CheckHover(s); } else { s.Hovering = false; } } } if (CurrentHoverSprite != null) { CurrentHoverSprite.Hovering = true; } if (lastHoverSprite != null && lastHoverSprite != CurrentHoverSprite) { lastHoverSprite.Hovering = false; } return(CurrentHoverSprite != null); }
internal void Add(List <pSprite> sprites, Object tag) { int c = sprites.Count; for (int i = 0; i < c; i++) { pSprite p = sprites[i]; Add(p); p.Tag = tag; } }
/// <summary> /// Special case scenario where we don't want to add using ForwardPlayOptimisedAdd. /// Easier than toggling. /// </summary> /// <param name = "p"></param> internal void AddNonOptimised(pSprite p) { if (p == null) { return; } lock (SpriteLock) { int index = SpriteList.BinarySearch(p, pDrawableDepthComparer); //if (p.HoverPriority >= 0) p.HoverPriority = hoverPriorityCurrent++; SpriteList.Insert(index < 0 ? ~index : index, p); } }
private void handleClick(bool repeat) { if (Alpha == 0 || LastFrameRendered < GameBase.TotalFramesRendered - 1) { clickHandledAtLeastOnce = true; return; } //Force update of currently hovered sprite. pSprite lastHoverSprite = CurrentHoverSprite; resetHoverSprite(); int count = SpriteList.Count; if (repeat && clickHandledAtLeastOnce) { return; } for (int i = 0; i < count; i++) { CheckHover(SpriteList[i]); } clickHandledAtLeastOnce = true; if (lastHoverSprite != null && lastHoverSprite != CurrentHoverSprite) { lastHoverSprite.Hovering = false; } if (CurrentHoverSprite == null) { return; } if (!HandleInput || ClickHandledSprite != null || !CurrentHoverSprite.HandleInput || !CurrentHoverSprite.IsVisible || MouseManager.MouseMiddle == ButtonState.Pressed) { return; } if (CurrentHoverSprite.Click(false)) { InputManager.LastClickHandled = true; } ClickHandledSprite = CurrentHoverSprite; }
internal void CheckHover <T>(T d) where T : pDrawable { if (d == null || !d.IsVisible || d.Bypass) { return; } pSprite s = d as pSprite; if (s == null) { return; } if (s.HandleInput) { if (MouseManager.MouseInWindow && (CurrentHoverSprite == null || s.HoverPriority < CurrentHoverSprite.HoverPriority) && s.CheckHover(MouseManager.MousePosition) //TODO: this will fail for circular sprites inside a masked spriteManager. see what Masking adjustments do to CurrentPositionScaled in Draw() && (!Masking || ViewRectangleScaled.Contains(MouseManager.MousePoint)) ) { if (CurrentHoverSprite != null) { CurrentHoverSprite.Hovering = false; } CurrentHoverSprite = s; } else if (s.Hovering) { s.Hovering = false; } } else if (s.Hovering) { s.Hovering = false; } }
internal static int GetSpriteTime(pSprite s) { return(s.Clock == Clocks.Game ? GameBase.Time : (int)AudioEngine.Time); }
private void resetHoverSprite() { CurrentHoverSprite = null; }
internal static int GetSpriteTime(pSprite s) { return(s.Clock == ClockTypes.Audio ? AudioTime : GameTime); }
/// <summary> /// Draws this instance. /// </summary> internal void Draw() { lock (spriteList) { int spriteListCount = spriteList.Count; hoverSprite = null; hoverIndex = -1; if (spriteListCount == 0 && !ForwardPlayOptimisations) { return; } if (Alpha == 0) { for (int m = 0; m < spriteListCount; m++) { spriteList[m].IsVisible = false; } return; } GameTime = GameBase.Time; AudioTime = AudioEngine.Time; //Setup view rectangle ViewRectangleScaled = ViewRectangle; ViewRectangleScaled.X = (int)(ViewRectangleScaled.X * GameBase.WindowRatio) - 1; ViewRectangleScaled.Y = (int)(ViewRectangleScaled.Y * GameBase.WindowRatio) - 1 + GameBase.WindowVOffset; ViewRectangleScaled.Width = (int)(ViewRectangleScaled.Width * GameBase.WindowRatio) + 1; ViewRectangleScaled.Height = (int)(ViewRectangleScaled.Height * GameBase.WindowRatio) + 1; bool hoverFound = false; bool mouseInWindow = GameBase.WindowRectangle.Contains(MouseHandler.MousePoint); if (ForwardPlayOptimisations) { bool success = true; while (success && ForwardPlayQueue.Count > 0) { pSprite fpqp = ForwardPlayQueue.Peek(); success = fpqp != null && (fpqp.Transformations.Count == 0 || fpqp.Transformations[0].Time1 <= GetSpriteTime(fpqp)); if (success) { if (Add(ForwardPlayQueue.Dequeue())) { spriteListCount++; } } } } try { GameBase.spriteBatch.Begin(Additive ? SpriteBlendMode.Additive : SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None); GameBase.spriteBatch.GraphicsDevice.RenderState.SeparateAlphaBlendEnabled = true; GameBase.spriteBatch.GraphicsDevice.RenderState.AlphaSourceBlend = Blend.One; GameBase.spriteBatch.GraphicsDevice.RenderState.AlphaDestinationBlend = Blend.InverseSourceAlpha; //GameBase.spriteBatch.GraphicsDevice.RenderState.AlphaTestEnable = false; Blend defaultBlend = GameBase.spriteBatch.GraphicsDevice.RenderState.DestinationBlend; for (int m = 0; m < spriteListCount; m++) { pSprite s = spriteList[m]; lock (s) { if (s == null) { if (firstNull) { BanchoClient.SendRequest(RequestType.Osu_ErrorReport, new bString(ConfigManager.sUsername + " Got a null")); } firstNull = false; spriteList.Remove(s); spriteListCount--; m--; continue; } int time = GetSpriteTime(s); bool drawThis = s.AlwaysDraw; bool hasFutureTransformations = false; bool hasPastTransformations = false; bool horizontalFlip = false; bool verticalFlip = false; bool hasRotation = false; bool hasScale = false; bool hasFade = false; bool hasMovement = false; bool hasColour = false; bool vectorScaleActive = false; int indexAfter = -1; int transformationCount = s.Transformations.Count; for (int i = 0; i < transformationCount; i++) { Transformation t = s.Transformations[i]; if (t == null) { throw new ArgumentNullException("Transformation", "transformation t was null " + s.Text); } if (t.Time1 > time && indexAfter == -1) { hasFutureTransformations = true; indexAfter = i; } else if (t.Time2 <= time) { hasPastTransformations = true; } //first check for active transformations if (t.Time1 > time || t.Time2 < time) { continue; } drawThis = true; switch (t.Type) { case TransformationType.Fade: if (!hasFade) { double amount = tweenValues(t.StartFloat, t.EndFloat, time, t.Time1, t.Time2, t.Easing); s.CurrentColour = new Color(s.CurrentColour.R, s.CurrentColour.G, s.CurrentColour.B, (byte)(amount * 255)); hasFade = true; } break; case TransformationType.Movement: if (!hasMovement) { s.CurrentPosition = tweenValues(t.StartVector, t.EndVector, time, t.Time1, t.Time2, t.Easing); if (s.TrackRotation) { Vector2 sub = t.EndVector - t.StartVector; s.CurrentRotation = (float)Math.Atan2(sub.Y, sub.X); if (s.Reverse) { s.CurrentRotation -= (float)Math.PI; } } hasMovement = true; } break; case TransformationType.Scale: if (!hasScale) { s.CurrentScale = tweenValues(t.StartFloat, t.EndFloat, time, t.Time1, t.Time2, t.Easing); hasScale = true; } break; case TransformationType.VectorScale: if (!hasScale) { s.VectorScale = tweenValues(t.StartVector, t.EndVector, time, t.Time1, t.Time2, t.Easing); vectorScaleActive = true; hasScale = true; } break; case TransformationType.Rotation: if (!hasRotation) { s.CurrentRotation = tweenValues(t.StartFloat, t.EndFloat, time, t.Time1, t.Time2, t.Easing); hasRotation = true; } break; case TransformationType.Colour: if (!hasColour) { s.StartColour = tweenValues(t.StartColour, t.EndColour, time, t.Time1, t.Time2, t.Easing); hasColour = true; } break; case TransformationType.ParameterFlipHorizonal: if (!horizontalFlip) { horizontalFlip = true; } else if (t.Type == TransformationType.ParameterFlipVertical) { if (!verticalFlip) { verticalFlip = true; } } break; } } if ((hasFutureTransformations && hasPastTransformations) || drawThis) { //past for (int i = transformationCount - 1; i >= 0; i--) { if (s.Transformations[i].Time2 >= time) { continue; } Transformation t = s.Transformations[i]; drawThis = true; switch (t.Type) { case TransformationType.Fade: if (!hasFade) { double amount = t.EndFloat; s.CurrentColour = new Color(s.CurrentColour.R, s.CurrentColour.G, s.CurrentColour.B, (byte)(amount * 255)); hasFade = true; } break; case TransformationType.Movement: if (!hasMovement) { s.CurrentPosition = t.EndVector; hasMovement = true; } break; case TransformationType.Scale: if (!hasScale) { s.CurrentScale = t.EndFloat; hasScale = true; } break; case TransformationType.VectorScale: if (!hasScale) { s.VectorScale = t.EndVector; vectorScaleActive = true; hasScale = true; } break; case TransformationType.Rotation: if (!hasRotation) { s.CurrentRotation = t.EndFloat; hasRotation = true; } break; case TransformationType.Colour: if (!hasColour) { s.StartColour = t.EndColour; hasColour = true; } break; } if (s.Clock == ClockTypes.Game && !t.Loop && s.AlwaysDraw) { s.Transformations.RemoveAt(i); transformationCount--; } } if (!(hasFutureTransformations && s.Clock == ClockTypes.Game)) { for (int i = 0; i < transformationCount; i++) { if (s.Transformations[i].Time1 < time) { continue; } Transformation t = s.Transformations[i]; switch (t.Type) { case TransformationType.Fade: if (!hasFade) { double amount = t.StartFloat; s.CurrentColour = new Color(s.CurrentColour.R, s.CurrentColour.G, s.CurrentColour.B, (byte)(amount * 255)); hasFade = true; } break; case TransformationType.Movement: if (!hasMovement) { s.CurrentPosition = t.StartVector; hasMovement = true; } break; case TransformationType.Scale: if (!hasScale) { s.CurrentScale = t.StartFloat; hasScale = true; } break; case TransformationType.VectorScale: if (!hasScale) { s.VectorScale = t.StartVector; vectorScaleActive = true; hasScale = true; } break; case TransformationType.Rotation: if (!hasRotation) { s.CurrentRotation = t.StartFloat; hasRotation = true; } break; case TransformationType.Colour: if (!hasColour) { s.StartColour = t.StartColour; hasColour = true; } break; } } } //Handle looping transformations. if (!hasFutureTransformations && hasPastTransformations && s.IsLoopable) { s.Transformations.Sort(); List <Transformation> trloop = s.Transformations.FindAll(t => t.Time2 <= time && t.Loop); foreach (Transformation tr in trloop) { int diff = tr.Time2 - tr.Time1; tr.Time1 = time; tr.Time2 = time + diff; } } byte alpha = (byte)(s.CurrentColour.A * Alpha); //Calculate current position and scale. if ((s.Type == SpriteTypes.Text || s.Type == SpriteTypes.SpriteText || ((AllowNativeTextCaching || OriginTypes.TopLeft != s.OriginType) && s.Type == SpriteTypes.NativeText)) && s.textChanged) { s.MeasureText(); } Vector2 currentPos = s.CurrentPosition; Vector2 origin = s.OriginPosition; float scale = s.CurrentScale; if (s.Type == SpriteTypes.Text || s.Type == SpriteTypes.NativeText || s.Type == SpriteTypes.SpriteText) { switch (s.OriginType) { case OriginTypes.Centre: origin += s.lastMeasure * 0.5F; break; case OriginTypes.TopRight: origin += new Vector2(s.lastMeasure.X, 0); break; case OriginTypes.BottomCentre: origin += new Vector2(s.lastMeasure.X / 2, s.lastMeasure.Y); break; } if (s.ExactCoordinates) { origin.X = (int)origin.X; origin.Y = (int)origin.Y; } } else if (s.Type == SpriteTypes.Text && s.OriginType == OriginTypes.CentreLeft) { origin = new Vector2(s.OriginPosition.X, s.lastMeasure.Y * 0.5F); } if (Masking && !s.ViewOffsetImmune) { currentPos -= ViewOffset; } switch (s.Field) { case FieldTypes.WindowScaleOnly: scale = scale * GameBase.WindowRatioInverse; break; case FieldTypes.Window: scale = scale * GameBase.WindowRatioInverse; currentPos = currentPos * GameBase.WindowRatio; currentPos.Y += GameBase.WindowVOffset; break; case FieldTypes.WindowNone: currentPos = currentPos * GameBase.WindowRatio; currentPos.Y += GameBase.WindowVOffset; break; case FieldTypes.WindowNoneExact: currentPos = currentPos * GameBase.WindowRatio; currentPos.Y += GameBase.WindowVOffset; break; case FieldTypes.WindowGamefieldRatio: scale = scale * GameBase.WindowRatio * GameBase.GamefieldRatioWindowIndependent; currentPos = GameBase.WindowRatio * currentPos; break; case FieldTypes.Gamefield: scale = scale * HitObjectManager.SpriteRatio; GameBase.GamefieldToDisplay(ref currentPos); break; case FieldTypes.GamefieldRatio: /*int offy = * (int) * ((GameBase.WindowDefaultHeight - * (GameBase.WindowDefaultHeight*GameBase.GamefieldRatioWindowIndependent))/8*2);*/ currentPos = currentPos * GameBase.WindowRatio * GameBase.GamefieldSize; currentPos.X += GameBase.WindowWidth * ((1 - GameBase.GamefieldSize) * 0.5f); currentPos.Y += GameBase.WindowHeight * ((1 - GameBase.GamefieldSize) * 0.75f) + GameBase.WindowVOffset; scale = scale * GameBase.GamefieldRatio; break; } if (s.ExactCoordinates) { currentPos.X = (int)Math.Round(currentPos.X); currentPos.Y = (int)Math.Round(currentPos.Y); } if (drawThis && alpha > 0) { if (s.Type == SpriteTypes.Text || s.Type == SpriteTypes.NativeText || s.Type == SpriteTypes.SpriteText) { s.DrawWidth = (int)Math.Round(s.lastMeasure.X); s.DrawHeight = (int)Math.Round(s.lastMeasure.Y); } Rectangle srcRect = new Rectangle(s.DrawLeft, s.DrawTop, s.DrawWidth, s.DrawHeight); //todo: remove this? s.CurrentPositionScaled = currentPos; Vector2 scaleVector = (s.UseVectorScale || vectorScaleActive ? s.VectorScale * scale : new Vector2(scale, scale)); if (Masking) { currentPos.X += ViewRectangleScaled.X + 1; currentPos.Y += ViewRectangleScaled.Y + 1 - GameBase.WindowVOffset; } float scaledPosX = currentPos.X - (origin.X * scaleVector.X); float scaledPosY = currentPos.Y - (origin.Y * scaleVector.Y); if (Masking) { int rightExcess = (int)((scaledPosX + s.DrawWidth * scaleVector.X) - ViewRectangleScaled.Right); if (rightExcess > 0) { srcRect.Width -= rightExcess; } int bottomExcess = (int)((scaledPosY + s.DrawHeight * scaleVector.Y) - ViewRectangleScaled.Bottom); if (bottomExcess > 0) { if (srcRect.Height > 1) { srcRect.Height -= (int)(bottomExcess * 1F / scaleVector.Y); } else { scaleVector.Y -= bottomExcess; } } int topExcess = (int)(ViewRectangleScaled.Top - (scaledPosY)); if (topExcess > 0) { if (srcRect.Height > 1) { srcRect.Y += (int)(topExcess * 1F / scaleVector.Y); srcRect.Height -= (int)(topExcess * 1F / scaleVector.Y); } else { scaleVector.Y -= topExcess; } currentPos.Y += topExcess; scaledPosY += topExcess; } } //todo: this doesn't reflect the displayRectangle s.Rectangle = new Rectangle((int)Math.Round(scaledPosX), (int)Math.Round(scaledPosY), (int)Math.Round(srcRect.Width * scaleVector.X), (int)Math.Round(srcRect.Height * scaleVector.Y)); if (((!UniversalFocusCaught && !IgnoreHover && !UniversalIgnoreHover) || s.CatchUniversal) && (s.IsClickable || s.IsClickRepeatable)) { if ((!hoverFound || s.HoverPriority < hoverIndex) && ((!s.Circular && s.Rectangle.Contains(MouseHandler.MousePoint)) || (s.Circular && Vector2.Distance(MouseHandler.MousePosition, s.CurrentPositionScaled) < (s.Radius * GameBase.WindowRatio) / 2)) && mouseInWindow) { if (hoverSprite != null) { hoverSprite.Hovering = false; } hoverFound = true; hoverSprite = s; hoverIndex = s.HoverPriority; if (s.CatchUniversal) { UniversalFocusCaught = true; } } else if (s.Hovering) { s.Hovering = false; } } if (!s.Rectangle.Intersects(ViewRectangleScaled) || (Masking && !ViewRectangleScaled.Intersects(s.Rectangle))) { s.IsVisible = false; } else { s.IsVisible = true; Color drawColour = s.CurrentColour; //late updating of colour to save on constructors if (Alpha != 100 || Blackness != 0 || hasColour) { byte blackByte = (byte)(MathHelper.Clamp(s.DimImmune ? 0 : Blackness, 0, 1) * 255); s.CurrentColour = new Color(s.StartColour.R, s.StartColour.G, s.StartColour.B, s.CurrentColour.A); drawColour = new Color((byte)Math.Max(0, s.StartColour.R - blackByte), (byte)Math.Max(0, s.StartColour.G - blackByte), (byte)Math.Max(0, s.StartColour.B - blackByte), alpha); } GameBase.spriteBatch.GraphicsDevice.RenderState.DestinationBlend = s.Additive ? Blend.One : defaultBlend; //draw the actual sprite if (s.Type != SpriteTypes.Text && s.Type != SpriteTypes.SpriteText) { if (s.Texture != null && !s.Texture.IsDisposed) { GameBase.spriteBatch.Draw(s.Texture, currentPos, srcRect, drawColour, s.CurrentRotation, origin, scaleVector, s.SpriteEffect | (horizontalFlip ? SpriteEffects.FlipHorizontally : SpriteEffects.None) | (verticalFlip ? SpriteEffects.FlipVertically : SpriteEffects.None), 0); } } else if (!string.IsNullOrEmpty(s.Text)) { //SpriteText rendering pSpriteText st = s as pSpriteText; for (int i = 0; i < st.renderCoordinates.Count; i++) { GameBase.spriteBatch.Draw(st.renderTextures[i], new Vector2( st.renderCoordinates[i].X * scale + scaledPosX, scaledPosY), null, drawColour, s.CurrentRotation, Vector2.Zero, scaleVector, s.SpriteEffect, s.Depth + i * 0.00001f); } } } } else { s.IsVisible = false; } } else { s.IsVisible = false; } if (!hasFutureTransformations && !drawThis) { if (!s.AlwaysDraw && (s.Clock == ClockTypes.Game || ForwardPlayOptimisations)) { spriteList.RemoveAt(m--); spriteListCount--; s.Dispose(s.Disposable); } } } } GameBase.spriteBatch.End(); } catch { GameBase.spriteBatch.Dispose(); GameBase.spriteBatch = new SpriteBatch(GameBase.graphics.GraphicsDevice); } if (hoverSprite != null) { if (hoverSprite.ToolTip != null) { GameBase.toolTipping = true; GameBase.toolTip.Text = hoverSprite.ToolTip; } if (!hoverSprite.Hovering) { hoverSprite.Hovering = true; } } } }