You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Duality;
using Duality.Resources;
using Duality.Drawing;
namespace DualStickSpaceShooter
{
public class GameOverScreen : Component, ICmpRenderer, ICmpUpdatable
{
private ContentRef<Font> font = null;
private BatchInfo blendMaterial = null;
private ContentRef<Material> controlInfoMouseKeyboard = null;
private ContentRef<Material> controlInfoGamepad = null;
[DontSerialize] private bool gameStarted = false;
[DontSerialize] private bool gameOver = false;
[DontSerialize] private bool gameWin = false;
[DontSerialize] private float lastTimeAnyAlive = 0.0f;
[DontSerialize] private CanvasBuffer buffer = null;
float ICmpRenderer.BoundRadius
{
get { return float.MaxValue; }
}
public ContentRef<Font> Font
{
get { return this.font; }
set { this.font = value; }
}
public BatchInfo BlendMaterial
{
get { return this.blendMaterial; }
set { this.blendMaterial = value; }
}
public ContentRef<Material> ControlsMouseKeyboard
{
get { return this.controlInfoMouseKeyboard; }
set { this.controlInfoMouseKeyboard = value; }
}
public ContentRef<Material> ControlsGamepad
{
get { return this.controlInfoGamepad; }
set { this.controlInfoGamepad = value; }
}
public bool HasGameEnded
{
get { return this.gameOver || this.gameWin; }
}
void ICmpUpdatable.OnUpdate()
{
// If the game has ended, nothing to do here
if (this.gameOver) return;
if (this.gameWin) return;
// Determine whether the game has started / ended
if (Player.IsAnyPlayerAlive)
{
this.gameStarted = true;
this.lastTimeAnyAlive = (float)Time.MainTimer.TotalMilliseconds;
}
if (this.gameStarted)
{
if (Player.AllPlayers.All(p => !p.Active || !p.IsPlaying || p.HasReachedGoal))
{
this.gameWin = true;
SpawnPoint.LastVisitedIndex = -1;
}
if (!Player.IsAnyPlayerAlive)
{
this.gameOver = true;
}
}
}
bool ICmpRenderer.IsVisible(IDrawDevice device)
{
// Only render when in screen overlay mode and the visibility mask is non-empty.
return
(device.VisibilityMask & VisibilityFlag.AllGroups) != VisibilityFlag.None &&
(device.VisibilityMask & VisibilityFlag.ScreenOverlay) != VisibilityFlag.None;
}
void ICmpRenderer.Draw(IDrawDevice device)
{
// Create a buffer to cache and re-use vertices. Not required, but will boost performance.
if (this.buffer == null) this.buffer = new CanvasBuffer();
// Create a Canvas to auto-generate vertices from high-level drawing commands.
Canvas canvas = new Canvas(device, this.buffer);
canvas.State.TextFont = this.font;
// If the game is over or won, display "game over" screen
if (this.gameOver || this.gameWin)
{
// Various animation timing variables.
float animOffset = this.gameWin ? 0.0f : 2500.0f;
float animTime = this.gameWin ? 10000.0f : 4500.0f;
float blendDurationRatio = this.gameWin ? 0.6f : 0.5f;
float textOffsetRatio = this.gameWin ? 0.2f : 0.0f;
float timeSinceGameOver = (float)Time.MainTimer.TotalMilliseconds - this.lastTimeAnyAlive;
float gameOverAnimProgress = MathF.Clamp((timeSinceGameOver - animOffset) / animTime, 0.0f, 1.0f);
float controlInfoAnimProgress = MathF.Clamp(((timeSinceGameOver - animOffset) - animTime - 2000.0f) / 2000.0f, 0.0f, 1.0f);
float blendAnimProgress = MathF.Clamp(gameOverAnimProgress / blendDurationRatio, 0.0f, 1.0f);
float textAnimProgress = MathF.Clamp((gameOverAnimProgress - blendDurationRatio - textOffsetRatio) / (1.0f - blendDurationRatio - textOffsetRatio), 0.0f, 1.0f);
if (this.blendMaterial != null && blendAnimProgress > 0.0f)
{
canvas.PushState();
if (this.gameOver)
{
// Set up our special blending Material and specify the threshold to blend to
this.blendMaterial.SetUniform("threshold", 1.0f - blendAnimProgress);
canvas.State.SetMaterial(this.blendMaterial);
canvas.State.ColorTint = ColorRgba.Black;
// Specify a texture coordinate rect so it spans the entire screen repeating itself, instead of being stretched
if (this.blendMaterial.MainTexture != null)
{
Random rnd = new Random((int)this.lastTimeAnyAlive);
Vector2 randomTranslate = rnd.NextVector2(0.0f, 0.0f, canvas.State.TextureBaseSize.X, canvas.State.TextureBaseSize.Y);
canvas.State.TextureCoordinateRect = new Rect(
randomTranslate.X,
randomTranslate.Y,
device.TargetSize.X / canvas.State.TextureBaseSize.X,
device.TargetSize.Y / canvas.State.TextureBaseSize.Y);
}
}
else
{
// If we won, simply fade to white
canvas.State.SetMaterial(new BatchInfo(DrawTechnique.Add, ColorRgba.White));
canvas.State.ColorTint = ColorRgba.White.WithAlpha(blendAnimProgress);
}
// Fill the screen with a rect of our Material
canvas.FillRect(0, 0, device.TargetSize.X, device.TargetSize.Y);
canvas.PopState();
}
if (this.font != null && textAnimProgress > 0.0f)
{
canvas.PushState();
// Determine which text to draw to screen and where to draw it
string gameOverText = this.gameWin ? "is it over..?" : "darkness...";
Vector2 fullTextSize = canvas.MeasureText(gameOverText);
Vector2 textPos = device.TargetSize * 0.5f - fullTextSize * 0.5f;
gameOverText = gameOverText.Substring(0, MathF.RoundToInt(gameOverText.Length * textAnimProgress));
// Make sure not to draw inbetween pixels, so the text is perfectly sharp.
textPos.X = MathF.Round(textPos.X);
textPos.Y = MathF.Round(textPos.Y);
// Draw the text to screen
canvas.State.ColorTint = this.gameWin ? ColorRgba.Black : ColorRgba.White;
canvas.DrawText(gameOverText, textPos.X, textPos.Y);
canvas.PopState();
}
if (controlInfoAnimProgress > 0.0f)
{
Vector2 infoBasePos = device.TargetSize * 0.5f + new Vector2(0.0f, device.TargetSize.Y * 0.25f);
if (this.controlInfoMouseKeyboard != null)
{
canvas.PushState();
Vector2 texSize = this.controlInfoMouseKeyboard.Res.MainTexture.Res.Size * 0.5f;
canvas.State.SetMaterial(this.controlInfoMouseKeyboard);
canvas.State.ColorTint = ColorRgba.White.WithAlpha(controlInfoAnimProgress);
canvas.FillRect(
infoBasePos.X - texSize.X * 0.5f,
infoBasePos.Y - texSize.Y - 10,
texSize.X,
texSize.Y);
canvas.PopState();
}
if (this.controlInfoGamepad != null)
{
canvas.PushState();
Vector2 texSize = this.controlInfoGamepad.Res.MainTexture.Res.Size * 0.5f;
canvas.State.SetMaterial(this.controlInfoGamepad);
canvas.State.ColorTint = ColorRgba.White.WithAlpha(controlInfoAnimProgress);
canvas.FillRect(
infoBasePos.X - texSize.X * 0.5f,
infoBasePos.Y + 10,
texSize.X,
texSize.Y);
canvas.PopState();
}
}
}
}
}
}