/// <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; } } } }
private void HoverStart() { int time = SpriteManager.GetSpriteTime(this); if (HoverEffect != null) { Transformations.RemoveAll(t => t.NumericalTag == 1); Transformation h = HoverEffect.Clone(); h.StartVector = CurrentPosition; switch (HoverEffect.Type) { case TransformationType.Fade: h.StartFloat = (float)CurrentColour.A / 255; break; case TransformationType.Scale: h.StartFloat = CurrentScale; break; case TransformationType.Rotation: h.StartFloat = CurrentRotation; break; } h.StartColour = StartColour; int duration = HoverEffect.Duration; h.Time1 = time; h.Time2 = time + duration; h.NumericalTag = 1; Transformations.Add(h); } if (HoverEffects != null) { Transformations.RemoveAll(t => t.NumericalTag == 1); bool first = true; foreach (Transformation t in HoverEffects) { Transformation h = t.Clone(); if (first) { h.StartVector = CurrentPosition; switch (h.Type) { case TransformationType.Fade: h.StartFloat = (float)CurrentColour.A / 255; break; case TransformationType.Scale: h.StartFloat = CurrentScale; break; case TransformationType.Rotation: h.StartFloat = CurrentRotation; break; } h.StartColour = StartColour; first = false; } h.Time1 += time; h.Time2 += time; h.NumericalTag = 1; Transformations.Add(h); } } }