/* * MeteorWidgets.cs * Authors: August Zinsser * * Copyright Matthew Belmonte 2007 */ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Storage; using tAC_Engine; namespace Astropolis { // A collection of widgets for use in the Meteor Madness HUD /// /// A mock cockpit of the fighter in Meteor Madness. Used by the dot trial phase. /// Also generates a HUD element for shields ("the pilot's HUD") which is tied to a component in the cockpit /// public class Cockpit : Widget2D { protected StaticSprite mBase; // The main body of the ship and its console protected float mBufferPercentage = .3f; // How far the HUD extends past the edge of the screen protected StaticSprite mShieldScreenShip; // Console element protected StaticSprite mShieldScreenShield; // '' protected Texture2D mWarningFlashersOn; // '' protected Texture2D mButtonsOn; // '' protected Texture2D mButtonsOff; // '' protected Texture2D mFlash1On; // '' protected Texture2D mFlash2On; // '' protected Texture2D mFlash3On; // '' protected Texture2D[] mProgressModule; // '' protected Rectangle[] mProgressModuleLocs; // '' protected Entity mJoystick; // The joystick with a hand attached to it protected Vector2 mJoystickOrigOffset; // The initial position of the joystick relative to the center of the screen, in screen pixels protected float mJoystickOrigTheta; // What rotation it would take to put the joystick straight down and then rotate it into place protected Entity mLeftHand; // The pilot's left hand protected Vector2 mLeftHandOrigOffset; // Initial position protected Vector2 mLeftHandButtonOffset; // The offset of the hand when it pushes the glowing button protected Vector2 mLeftHandCurrentOffset; // The hand moves, so track it protected float mLeftHandOrigTheta; // Same as joystick, but for left hand protected float mLeftHandButtonTheta; // '' protected float mLeftHandCurrentTheta; // '' protected Vector2 mShieldOrigOffset; // '' (shield console) protected float mShieldOrigTheta; // '' protected Vector2[] mProgressModuleOrigOffset; // '' (progess bar) protected float[] mProgressModuleOrigTheta; // '' protected byte[] mProgressModuleOpacity; // '' protected float mLeftHandSpeed = 1f; // In percentage from orig to button per second protected float mRoll = 0f; // Roll of the entire ship (in radians) protected float mMaxRollOffset = .2f; // How far in either direction to allow the ship to roll protected float mTurnLeftTimer = 0f; // Turn left if > 0 protected float mTurnRightTimer = 0f; // '' right protected float mTurnTime = .5f; // How long to keep turning protected float mTurnSpeed = .03f; // Angular velocity during a turn protected float mReturnSpeed = .005f; // Angular velocity when a turn is completed, going towards a roll of 0 protected float mHitTimer = 0f; // Inidicate that the ship is being hit if > 0 protected float mHitTime = 1f; // The amount of time the ship indicates that it was hit protected float mWarningThreshold = .4f; // Warning lights stay on if shields drop below this protected float mWarningBlinkTimer = 0f; // Warning Lights flash if > 0 protected float mIdleFlash1Timer = 0f; // Persistent low-frequency flash protected float mIdleFlash2Timer = 0f; // Persistent low-frequency flash protected float mIdleFlash3Timer = 0f; // Persistent low-frequency flash protected float mIdleFlash1Period = 3f; // '' protected float mIdleFlash2Period = 4f; // '' protected float mIdleFlash3Period = 5f; // '' protected float mButtonPressChance = .4f; // How often the pilot will push a button when hit protected bool mPushingButton = false; // True when the pilot's hand is reaching towards the button protected bool mPushedButton = false; // True when the pilot's hand is going back out of frame protected float mHoldButtonTimer = 0f; // Hold the left hand on the button for a brief time protected float mHoldButtonTime = .2f; // '' protected Vector2 mShake; // Offsets for the cockpit shake when it gets hit protected float mShakeFrequency = 50f; // The frequency of the shake speed (arbitray units). protected float mShakeMagnitude = 1f; // How far to shake the cockpit when it gets hit (in screen %) protected float mShieldHudThreshold = .9f; // When shields drop below this, show the pilot's HUD shields protected ShieldWarning mShieldOverlay; // The pilot's HUD of the shields protected WindshieldNavInfo mNavInfo; // Nav stuff on the pilot's HUD /// /// Load images and set parameters /// public Cockpit() { int screenWidth = GenericBaseApplication.GameManager.ScreenWidth; int screenHeight = GenericBaseApplication.GameManager.ScreenHeight; Rectangle baseSize = new Rectangle(screenWidth / 2, screenHeight / 2, (int)(screenWidth * (1f + (mBufferPercentage * 2))), (int)(screenHeight * (1f + (mBufferPercentage * 2)))); mBase = new StaticSprite(baseSize, .90f, TextureManager.Load(@"Content\MiniGames\Cockpit")); mShieldOrigOffset = new Vector2(screenWidth * .28f, screenHeight * .42f); mShieldOrigTheta = (float)Math.Atan(mShieldOrigOffset.X / mShieldOrigOffset.Y); Texture2D shipTex = TextureManager.Load(@"Content\MiniGames\ShieldDisplayShip"); Texture2D shieldTex = TextureManager.Load(@"Content\MiniGames\ShieldDisplayShield"); float displayWidth = screenWidth * .20f; float displayHeight = shipTex.Height * displayWidth / shipTex.Width; mShieldScreenShip = new StaticSprite(new Rectangle(0, 0, (int)displayWidth, (int)displayHeight), .69f, shipTex); mShieldScreenShield = new StaticSprite(new Rectangle(0, 0, (int)displayWidth, (int)displayHeight), .70f, shieldTex); mJoystickOrigOffset = new Vector2(0, screenHeight * .62f); mJoystickOrigTheta = (float)Math.Atan(mJoystickOrigOffset.X / mJoystickOrigOffset.Y); Texture2D joystickTex = TextureManager.Load(@"Content\MiniGames\CockpitJoystick"); displayWidth = baseSize.Width * .4f; displayHeight = joystickTex.Height * displayWidth / joystickTex.Width; mJoystick = new Entity(joystickTex, displayWidth, displayHeight); mLeftHandOrigOffset = new Vector2(screenWidth * -.45f, screenHeight * 2f); mLeftHandButtonOffset = new Vector2(screenWidth * -.38f, screenHeight * .87f); mLeftHandOrigTheta = (float)Math.Atan(mLeftHandOrigOffset.X / mLeftHandOrigOffset.Y); mLeftHandButtonTheta = (float)Math.Atan(mLeftHandButtonOffset.X / mLeftHandButtonOffset.Y); mLeftHandCurrentOffset = mLeftHandOrigOffset; Texture2D handTex = TextureManager.Load(@"Content\MiniGames\CockpitLeftHand"); displayWidth = baseSize.Width * .2f; displayHeight = handTex.Height * displayWidth / handTex.Width; mLeftHand = new Entity(handTex, displayWidth, displayHeight); mWarningFlashersOn = TextureManager.Load(@"Content\Minigames\CockpitWarningLightsOn"); mButtonsOn = TextureManager.Load(@"Content\Minigames\CockpitButtonsOn"); mButtonsOff = TextureManager.Load(@"Content\Minigames\CockpitButtons"); mFlash1On = TextureManager.Load(@"Content\Minigames\CockpitFlashBut1On"); mFlash2On = TextureManager.Load(@"Content\Minigames\CockpitFlashBut2On"); mFlash3On = TextureManager.Load(@"Content\Minigames\CockpitFlashBut3On"); mProgressModule = new Texture2D[2]; mProgressModule[0] = TextureManager.Load(@"Content\MiniGames\CockpitProgressModule"); mProgressModule[1] = TextureManager.Load(@"Content\MiniGames\CockpitProgressModule2"); mProgressModuleLocs = new Rectangle[10]; mProgressModuleOrigOffset = new Vector2[10]; mProgressModuleOrigTheta = new float[10]; mProgressModuleOpacity = new byte[10]; displayWidth = baseSize.Width * .019f; displayHeight = mProgressModule[0].Height * displayWidth / mProgressModule[0].Width; displayWidth += baseSize.Width * .007f; // Stretch them width-wise float startX = screenWidth * -.184f; float endX = screenWidth * .109f; for (int i = 0; i < 10; i++) { mProgressModuleLocs[i] = new Rectangle(0, 0, (int)displayWidth, (int)displayHeight); mProgressModuleOrigOffset[i] = new Vector2(endX * (i / 9f) + startX * ((9 - i) / 9f), screenHeight * .204f); mProgressModuleOrigTheta[i] = (float)Math.Atan(mProgressModuleOrigOffset[i].X / mProgressModuleOrigOffset[i].Y); } mShieldOverlay = new ShieldWarning(); mNavInfo = new WindshieldNavInfo(); mShieldOverlay.Visible = false; mNavInfo.Visible = false; HUD.Add(mShieldOverlay); HUD.Add(mNavInfo); } /// /// Turns the entire ship to the left. Additionally moves the joystick. /// public void TurnLeft() { mTurnLeftTimer = mTurnTime; } /// /// Turns the entire ship to the right. Additionally moves the joystick. /// public void TurnRight() { mTurnRightTimer = mTurnTime; } /// /// Inform the user that ship is taking damage /// public void TakeDamage() { mHitTimer = mHitTime; mWarningBlinkTimer = mHitTime * 2; if (GenericBaseApplication.GameManager.RandomNumber() < mButtonPressChance) mPushingButton = true; if (AstroBaseApplication.GameManager.DotShields < mShieldHudThreshold) mShieldOverlay.Show(); } /// /// True if the cockpit isn't doing anything that would distract the user and taint the dot experiment. /// "Anything distracting" includes rotation, shaking, and some blinking /// /// public bool IsStable() { return (mRoll == 0 && mShake == Vector2.Zero) && mWarningBlinkTimer <= 0f && !mPushingButton && !mPushedButton && mShieldOverlay.IsStable() && mNavInfo.IsStable(); } /// /// Stays relatively calm unless the ship was just hit (ie fail a trial) /// public override void Update() { float dt = GenericBaseApplication.GameManager.ElapsedSeconds; int screenCenterX = GenericBaseApplication.GameManager.ScreenWidth / 2; int screenCenterY = GenericBaseApplication.GameManager.ScreenHeight / 2; if (mHitTimer > 0) { // Get shake offsets mShake.X = mShakeMagnitude * GenericBaseApplication.GameManager.ScreenWidth * (float)Math.Cos(mHitTimer * mShakeFrequency) * .01f * mHitTimer / mHitTime; mShake.Y = mShakeMagnitude * GenericBaseApplication.GameManager.ScreenHeight * (float)Math.Sin(mHitTimer * mShakeFrequency * .5f) * .01f * mHitTimer / mHitTime; } else { mShake = Vector2.Zero; } // Roll according to the current turning state if (mTurnLeftTimer > 0 && mRoll > -mMaxRollOffset) mRoll -= mTurnSpeed; if (mTurnRightTimer > 0 && mRoll < mMaxRollOffset) mRoll += mTurnSpeed; if (mTurnLeftTimer < 0 && mTurnRightTimer < 0) { if (mRoll < -mReturnSpeed) mRoll += mReturnSpeed; else if (mRoll > mReturnSpeed) mRoll -= mReturnSpeed; else mRoll = 0f; } // Shake the base mBase.Location.X = screenCenterX + (int)mShake.X; mBase.Location.Y = screenCenterY + (int)mShake.Y; // Move the hands mJoystick.X = screenCenterX + mShake.X + mJoystickOrigOffset.Length() * (float)Math.Sin(mJoystickOrigTheta - mRoll); mJoystick.Y = screenCenterY + mShake.Y + mJoystickOrigOffset.Length() * (float)Math.Cos(mJoystickOrigTheta - mRoll); mJoystick.Rotation = mRoll * 4 + (mShake.X * .005f); if (mPushingButton) { Vector2 distanceToMove = mLeftHandButtonOffset - mLeftHandOrigOffset; distanceToMove *= mLeftHandSpeed * dt; mLeftHandCurrentOffset += distanceToMove; mLeftHandCurrentTheta = (float)Math.Atan(mLeftHandCurrentOffset.X / mLeftHandCurrentOffset.Y); } if (mPushedButton && mHoldButtonTimer <= 0) { Vector2 distanceToMove = mLeftHandOrigOffset - mLeftHandButtonOffset; distanceToMove *= mLeftHandSpeed * dt; mLeftHandCurrentOffset += distanceToMove; mLeftHandCurrentTheta = (float)Math.Atan(mLeftHandCurrentOffset.X / mLeftHandCurrentOffset.Y); } if (mLeftHandCurrentOffset.Y < mLeftHandButtonOffset.Y) { mLeftHandCurrentOffset = mLeftHandButtonOffset; mPushingButton = false; mPushedButton = true; mHoldButtonTimer = mHoldButtonTime; } if (mLeftHandCurrentOffset.Y >= mLeftHandOrigOffset.Y) { mLeftHandCurrentOffset = mLeftHandOrigOffset; mPushedButton = false; } mLeftHand.X = screenCenterX + mShake.X * .5f + mLeftHandCurrentOffset.Length() * (float)Math.Sin(mLeftHandCurrentTheta - mRoll); mLeftHand.Y = screenCenterY + mShake.Y * .5f + mLeftHandCurrentOffset.Length() * (float)Math.Cos(mLeftHandCurrentTheta - mRoll); mLeftHand.Rotation = mRoll; // Move the shield console mShieldScreenShip.Location.X = (int)(screenCenterX + (int)mShake.X + mShieldOrigOffset.Length() * (float)Math.Sin(mShieldOrigTheta - mRoll)); mShieldScreenShip.Location.Y = (int)(screenCenterY + (int)mShake.Y + mShieldOrigOffset.Length() * (float)Math.Cos(mShieldOrigTheta - mRoll)); mShieldScreenShield.Location = mShieldScreenShip.Location; // Update the shield progress bar for (int i = 0; i < 10; i++) { if (AstroBaseApplication.GameManager.DotShields > (float)(i + 1) / 10f) mProgressModuleOpacity[i] = 255; else if (AstroBaseApplication.GameManager.DotShields > (float)(i) / 10f) mProgressModuleOpacity[i] = (byte)(255 * 10 * (AstroBaseApplication.GameManager.DotShields % .1f)); else mProgressModuleOpacity[i] = 0; mProgressModuleLocs[i].X = (int)(screenCenterX + (int)mShake.X + mProgressModuleOrigOffset[i].Length() * (float)Math.Sin(mProgressModuleOrigTheta[i] - mRoll)); mProgressModuleLocs[i].Y = (int)(screenCenterY + (int)mShake.Y + mProgressModuleOrigOffset[i].Length() * (float)Math.Cos(mProgressModuleOrigTheta[i] - mRoll)); } // Recycle flashes (slightly randomized to prevent brain signal harmonics) if (mIdleFlash1Timer < -mIdleFlash1Period) mIdleFlash1Timer += mIdleFlash1Period * 2 - GenericBaseApplication.GameManager.RandomNumber(0f, mIdleFlash1Period * .05f); if (mIdleFlash2Timer < -mIdleFlash2Period) mIdleFlash2Timer += mIdleFlash2Period * 2 - GenericBaseApplication.GameManager.RandomNumber(0f, mIdleFlash2Period * .05f); if (mIdleFlash3Timer < -mIdleFlash3Period) mIdleFlash3Timer += mIdleFlash3Period * 2 - GenericBaseApplication.GameManager.RandomNumber(0f, mIdleFlash3Period * .05f); // Update timers mTurnLeftTimer -= dt; mTurnRightTimer -= dt; mHitTimer -= dt; mWarningBlinkTimer -= dt; mIdleFlash1Timer -= dt; mIdleFlash2Timer -= dt; mIdleFlash3Timer -= dt; mHoldButtonTimer -= dt; // Sync windshield HUD mShieldOverlay.Visible = this.Visible; mNavInfo.Visible = this.Visible; mNavInfo.Roll = mRoll; mNavInfo.Shake = mShake; base.Update(); } /// /// Draw each component /// /// public override void Draw(SpriteBatch spriteBatch) { // Draw the cockpit spriteBatch.Draw( mBase.Texture, mBase.Location, null, Color.White, mRoll, new Vector2((float)mBase.Texture.Width / 2f, (float)mBase.Texture.Height / 2f), SpriteEffects.None, mBase.ZDepth); // Draw the components spriteBatch.Draw( mShieldScreenShield.Texture, mShieldScreenShield.Location, null, new Color( (byte)(255), (byte)(255 * AstroBaseApplication.GameManager.DotShields), (byte)(255 * AstroBaseApplication.GameManager.DotShields), (byte)(255 * .50 * AstroBaseApplication.GameManager.DotShields)), mRoll, new Vector2((float)mShieldScreenShield.Texture.Width / 2f, (float)mShieldScreenShield.Texture.Height / 2f), SpriteEffects.None, mShieldScreenShield.ZDepth); spriteBatch.Draw( mShieldScreenShip.Texture, mShieldScreenShip.Location, null, new Color(255,255,255,(byte)(255.0 * .50)), mRoll, new Vector2((float)mShieldScreenShip.Texture.Width / 2f, (float)mShieldScreenShip.Texture.Height / 2f), SpriteEffects.None, mShieldScreenShip.ZDepth); for (int i = 0; i < 10; i++) { spriteBatch.Draw( mProgressModule[i % 2], mProgressModuleLocs[i], null, new Color(255, 255, 255, mProgressModuleOpacity[i]), mRoll, new Vector2((float)mProgressModule[i % 2].Width / 2f, (float)mProgressModule[i % 2].Height / 2f), SpriteEffects.None, mBase.ZDepth - .01f * i); } if (mWarningBlinkTimer > 0 || AstroBaseApplication.GameManager.DotShields < mWarningThreshold) { spriteBatch.Draw( mWarningFlashersOn, mBase.Location, null, Color.White, mRoll, new Vector2((float)mWarningFlashersOn.Width / 2f, (float)mWarningFlashersOn.Height / 2f), SpriteEffects.None, mBase.ZDepth - .01f); } spriteBatch.Draw( mPushingButton ? mButtonsOn : mButtonsOff, mBase.Location, null, Color.White, mRoll, new Vector2((float)mBase.Texture.Width / 2f, (float)mBase.Texture.Height / 2f), SpriteEffects.None, mBase.ZDepth - .01f); spriteBatch.Draw( mFlash1On, mBase.Location, null, new Color(255, 255, 255, (byte)MathHelper.Clamp(Math.Abs(255 * mIdleFlash1Timer / mIdleFlash1Period), 0, 255)), mRoll, new Vector2((float)mFlash1On.Width / 2f, (float)mFlash1On.Height / 2f), SpriteEffects.None, mBase.ZDepth - .02f); spriteBatch.Draw( mFlash2On, mBase.Location, null, new Color(255, 255, 255, (byte)MathHelper.Clamp(Math.Abs(255 * mIdleFlash2Timer / mIdleFlash2Period), 0, 255)), mRoll, new Vector2((float)mFlash2On.Width / 2f, (float)mFlash2On.Height / 2f), SpriteEffects.None, mBase.ZDepth - .02f); spriteBatch.Draw( mFlash3On, mBase.Location, null, new Color(255, 255, 255, (byte)MathHelper.Clamp(Math.Abs(255 * mIdleFlash3Timer / mIdleFlash3Period), 0, 255)), mRoll, new Vector2((float)mFlash3On.Width / 2f, (float)mFlash3On.Height / 2f), SpriteEffects.None, mBase.ZDepth - .02f); // Draw the hands mJoystick.Draw(spriteBatch); mLeftHand.Draw(spriteBatch); base.Draw(spriteBatch); } } /// /// Displays a cross, circle, and other pilot-lookin stuff on the window of the cockpit /// public class WindshieldNavInfo : Widget2D { public float Roll; // Set by the cockpit public Vector2 Shake; // '' protected StaticSprite mCircle; // A HUD element protected StaticSprite mCross; // '' //protected Entity mTicks; // '' protected Vector2 mOrigCircleOffset; // Original position of this element protected Vector2 mOrigCrossOffset; // '' //protected Vector2 mOrigTickOffset; // '' protected float mOrigCircleTheta; // Original angle between this the ray from the screen center to this element and a vertical ray protected float mOrigCrossTheta; // '' //protected Vector2 mOrigTickTheta; // '' protected float mOveralOpacity = .8f; // Tone down the HUD element protected float[] mLagRolls; // Use to make the cross + "down" line lag behind the cockpit roll protected float mLagRoll; // Average of LagRolls protected int mLagCount = 50; // Frames to average over for lag roll protected int mLagIndex = 0; // Last lag roll record protected bool mLagRollStable; // True if all entries in LagRoll are 0 /// /// Create a new HUD Nav element /// public WindshieldNavInfo() { mHeightPercentage = .4f; mCircle = new StaticSprite( Rectangle.Empty, 0f, TextureManager.Load(@"Content\MiniGames\NavCircle")); mCross = new StaticSprite( Rectangle.Empty, 0f, TextureManager.Load(@"Content\MiniGames\NavCross")); ChangeResolution(); mLagRolls = new float[mLagCount]; } /// /// True if the widget is not moving /// /// public bool IsStable() { return mLagRollStable; } /// /// Change dimensions /// public override void ChangeResolution() { int screenCenterX = GenericBaseApplication.GameManager.ScreenWidth / 2; int screenCenterY = GenericBaseApplication.GameManager.ScreenHeight / 2; int screenWidth = GenericBaseApplication.GameManager.ScreenWidth; int screenHeight = GenericBaseApplication.GameManager.ScreenHeight; float heightCircle = screenHeight * mHeightPercentage; float widthCircle = heightCircle * ((float)mCircle.Texture.Width / (float)mCircle.Texture.Height); float heightCross = heightCircle * .5f; float widthCross = heightCross * ((float)mCross.Texture.Width / (float)mCross.Texture.Height); mCircle.Location.Width = (int)widthCircle; mCircle.Location.Height = (int)heightCircle; mCross.Location.Width = (int)widthCross; mCross.Location.Height = (int)heightCross; mOrigCircleOffset = new Vector2(screenWidth * .015f, -screenHeight * .18f); mOrigCrossOffset = new Vector2(0, -screenHeight * .1f); mOrigCircleTheta = (float)Math.Atan(mOrigCircleOffset.X / mOrigCircleOffset.Y); mOrigCrossTheta = (float)Math.Atan(mOrigCrossOffset.X / mOrigCrossOffset.Y); UpdatePositions(); base.ChangeResolution(); } /// /// Updates the widget /// public override void Update() { float dt = GenericBaseApplication.GameManager.ElapsedSeconds; mLagRolls[mLagIndex++] = Roll; if (mLagIndex >= mLagCount) mLagIndex = 0; mLagRoll = 0; for (int i = 0; i < mLagCount; i++) mLagRoll += mLagRolls[i] * (1 + Shake.Length() * .25f); mLagRoll /= mLagCount; mLagRollStable = mLagRoll == 0; UpdatePositions(); base.Update(); } /// /// Updates all items to their new position /// public void UpdatePositions() { int screenCenterX = GenericBaseApplication.GameManager.ScreenWidth / 2; int screenCenterY = GenericBaseApplication.GameManager.ScreenHeight / 2; // Move with cockpit mCircle.Location.X = (int)(screenCenterX + Shake.X + (mOrigCircleOffset.Y < 0 ? -1f : 1f) * mOrigCircleOffset.Length() * (float)Math.Sin(mOrigCircleTheta - Roll)); mCircle.Location.Y = (int)(screenCenterY + Shake.Y + (mOrigCircleOffset.Y < 0 ? -1f : 1f) * mOrigCircleOffset.Length() * (float)Math.Cos(mOrigCircleTheta - Roll)); mCross.Location.X = (int)(screenCenterX - mLagRoll * 200f + Shake.X + (mOrigCrossOffset.Y < 0 ? -1f : 1f) * mOrigCrossOffset.Length() * (float)Math.Sin(mOrigCrossTheta - Roll)); mCross.Location.Y = (int)(screenCenterY + Math.Abs(mLagRoll) * 100f + Shake.Y + (mOrigCrossOffset.Y < 0 ? -1f : 1f) * mOrigCrossOffset.Length() * (float)Math.Cos(mOrigCrossTheta - Roll)); } /// /// Draws the component to the spritebatch /// /// public override void Draw(SpriteBatch spriteBatch) { spriteBatch.Draw( mCircle.Texture, mCircle.Location, null, new Color(255,255,255,(byte)(255 * mOveralOpacity * .3f)), Roll, new Vector2(mCircle.Texture.Width / 2, mCircle.Texture.Height / 2), SpriteEffects.None, 0f); spriteBatch.Draw( mCross.Texture, mCross.Location, null, new Color(255,255,255,(byte)(255 * mOveralOpacity)), mLagRoll, new Vector2(mCross.Texture.Width / 2, mCross.Texture.Height / 2), SpriteEffects.None, 0f); base.Draw(spriteBatch); } } /// /// Displays a warning on the cockpit windshield when shields are low /// public class ShieldWarning : Widget2D { protected StaticSprite mIcon; // The icon protected StaticSprite mWarning; // WARNING protected float mDisplayTimer = 0f; // Displays stuff if > 0 protected float mDisplayTime = 2f; // Seconds to show the shields protected float mFadeTime = .2f; // How long it takes to fade out protected float mOveralOpacity = .7f; // Tone down the HUD element protected float mFadeOpacity = 1f; // Fade out if FadeTime is left protected Color mIconColor; // Based on shield percentage protected Color mWarningColor; // '' protected float mBlinkThreshold = .30f; // When shields drop below this, start blinking protected float mBlinkTimer = 0f; // Used for blinking protected float mBlinkHalfPeriod = .4f; // '' /// /// Load components /// public ShieldWarning() { mHeightPercentage = .075f; mIcon = new StaticSprite( Rectangle.Empty, 0f, TextureManager.Load(@"Content\MiniGames\ShieldWarningIcon")); mWarning = new StaticSprite( Rectangle.Empty, 0f, TextureManager.Load(@"Content\MiniGames\ShieldWarning")); ChangeResolution(); } /// /// True if the widget is completely invisible /// /// public bool IsStable() { return mDisplayTimer <= 0; } /// /// Displays the shield indicator and then fades out /// public void Show() { mDisplayTimer = mDisplayTime; } /// /// Change dimensions /// public override void ChangeResolution() { float height = GenericBaseApplication.GameManager.ScreenHeight * mHeightPercentage; float widthIcon = height * ((float)mIcon.Texture.Width / (float)mIcon.Texture.Height); float widthWarning = height * ((float)mWarning.Texture.Width / (float)mWarning.Texture.Height); int xIcon = (int)(GenericBaseApplication.GameManager.ScreenWidth * .3f); int yIcon = (int)(GenericBaseApplication.GameManager.ScreenHeight * .3f); int xWarning = GenericBaseApplication.GameManager.ScreenWidth / 2; int yWarning = (int)(GenericBaseApplication.GameManager.ScreenHeight * .4f); mIcon.Location = new Rectangle(xIcon, yIcon, (int)widthIcon, (int)height); mWarning.Location = new Rectangle(xWarning, yWarning, (int)widthWarning, (int)height); base.ChangeResolution(); } /// /// Updates the widget /// public override void Update() { float dt = GenericBaseApplication.GameManager.ElapsedSeconds; // Blink? if (AstroBaseApplication.GameManager.DotShields < mBlinkThreshold && mBlinkTimer < -mBlinkHalfPeriod) { // Slightly randomize the blink to prevent resonance with the brain mBlinkTimer = mBlinkHalfPeriod + GenericBaseApplication.GameManager.RandomNumber(0f, mBlinkHalfPeriod * .1f); } mBlinkTimer -= dt; mDisplayTimer -= dt; base.Update(); } /// /// Draws the component to the spritebatch /// /// public override void Draw(SpriteBatch spriteBatch) { if (mDisplayTimer > 0) { // Shield color mIconColor = new Color( 255, (byte)(255 - (255 * (1 - AstroBaseApplication.GameManager.DotShields))), (byte)(255 - (255 * (1 - AstroBaseApplication.GameManager.DotShields))), (byte)(255 * mOveralOpacity * mFadeOpacity)); mWarningColor = new Color( (byte)(0 + (255 * (1 - AstroBaseApplication.GameManager.DotShields))), 0, (byte)(255 - (255 * (1 - AstroBaseApplication.GameManager.DotShields))), (byte)(255 * mOveralOpacity * mFadeOpacity)); // Fade if (mDisplayTimer < mFadeTime) mFadeOpacity = mDisplayTimer / mFadeTime; else mFadeOpacity = 1f; spriteBatch.Draw( mIcon.Texture, mIcon.Location, null, mIconColor, 0f, new Vector2(mIcon.Texture.Width / 2, mIcon.Texture.Height / 2), SpriteEffects.None, mIcon.ZDepth); if (mBlinkTimer > 0) { spriteBatch.Draw( mWarning.Texture, mWarning.Location, null, mWarningColor, 0f, new Vector2(mWarning.Texture.Width / 2, mWarning.Texture.Height / 2), SpriteEffects.None, mWarning.ZDepth); } } base.Draw(spriteBatch); } } }