} // end of CleanUpString() public static void Deactivate() { if (Active) { active = false; // Ensure that we're rendering to the whole screen. InGame.RestoreViewportToFull(); BokuGame.ScreenSize = new Vector2(BokuGame.bokuGame.GraphicsDevice.Viewport.Width, BokuGame.bokuGame.GraphicsDevice.Viewport.Height); BokuGame.ScreenPosition = Vector2.Zero; } } // end of Deactivate()
public static void Update() { // Lazy creation since we don't have a c'tor. if (modalDisplay == null) { modalDisplay = new ModalDisplay(OnContinue, OnBack, OnExitTutorial); } if (emptyCrumb == null) { emptyCrumb = new Crumb(); } if (Active) { modalDisplay.Update(); InGame.RestoreViewportToFull(); // Adjust BokuGame screen size and position depending on whether or not the modal dialog is active. // When the dislog is active, we don't have the bar across the top so we should return to full size. // Note, when display is changing active/inactive we want to twitch. When the screen size changes // because the window got resized we don't want to twitch. // TODO (****) Twitching taken out since it was getting mixed up. if (modalDisplay.Active) { // Calc new screen size and position for full viewport. BokuGame.ScreenSize = new Vector2(BokuGame.bokuGame.GraphicsDevice.Viewport.Width, BokuGame.bokuGame.GraphicsDevice.Viewport.Height); BokuGame.ScreenPosition = Vector2.Zero; } else { // Yes, this looks clumsy. The issue here is that the modalDisplay Update call may have // deactivated tutorial mode. If so, Active is no longer true and we don't want to mess // with the screen position and size. if (Active) { // Set the screen size to leave some room for the tutorial stuff. float rtHeight = rt != null ? rt.Height : 0; BokuGame.ScreenSize = new Vector2(BokuGame.bokuGame.GraphicsDevice.Viewport.Width, BokuGame.bokuGame.GraphicsDevice.Viewport.Height - rtHeight); targetPositionY = BokuGame.bokuGame.GraphicsDevice.Viewport.Height - BokuGame.ScreenSize.Y; BokuGame.ScreenPosition = new Vector2(0, targetPositionY); } } } else { // Tutorial mode is not active. } #if DEBUG // For testing purposes... if (KeyboardInput.WasPressed(Keys.F12)) { if (Active) { Deactivate(); } else { Activate(); } } #endif // Always keep this up to date in case someone else wants to use it. SetGameMode(); if (curGameMode != lastGameMode) { lastGameMode = curGameMode; } // Test for null shouldn't be needed in normal operation but allows // for debugging of screen position and scaling without having to // run a full tutorial level. if (Active && InGame.XmlWorldData != null) { curStep = null; if (InGame.XmlWorldData.tutorialSteps.Count > 0) { // Is the current step modal? If so, activate the modal display if needed. curStep = InGame.XmlWorldData.tutorialSteps[curStepIndex]; if (modalDisplay.Active == false && curStep.DisplayMode == Step.Display.Modal) { modalDisplay.Activate(curStep.GamepadText, curStep.MouseText, curStep.TouchText, true, true); } } else { // This only happens when the tutorial system is activated in debug mode // and there's no actual tutorial. So, create a fake, nonmodal step. curStep = new Step(); curStep.DisplayMode = Step.Display.NonModal; curStep.GamepadText = "Test"; curStep.MouseText = "Test"; curStep.TargetModeGamepad = GameMode.MainMenu; curStep.TargetModeMouse = GameMode.MainMenu; } // NonModal? if (curStep.DisplayMode == Step.Display.NonModal) { curCrumb = null; targetModeReached |= CurGameMode == curStep.TargetMode; // HACK When the target mode is GamepadEditObject and the curretn mode is // GamepadEditObjectFocus we give the user the instruction to move the cursor // to a blank spot int he world. This kind of sucks. So detect that case // and set reached to true for either. if (curStep.TargetMode == GameMode.GamepadEditObject && CurGameMode == GameMode.GamepadEditObjectFocus) { targetModeReached = true; } // Check for completion. If we have a completion test then use that // as the criteria. If not, we consider the step complete when the // target mode is reached. if (curStep.CompletionTest == null) { if (targetModeReached) { OnContinue(); } } else { if (targetModeReached && curStep.CompletionTest.Evaluate()) { OnContinue(); } } // Hack. Right now the code is set up to latch targetModeReached until that step is // completed. This behavior is required for some things to work correctly but this // also causes a problem; when the user moves away from the target mode they no // longer get instructions on how to get to the target mode. So hack in special // case code for times where this a problem. if ((targetModeReached && CurGameMode != curStep.TargetMode) && ( curStep.TargetMode == GameMode.Programming || curStep.TargetMode == GameMode.AddItem )) { targetModeReached = false; } // Note yet at the target mode? Get next crumb. if (!targetModeReached) { curCrumb = FindNextCrumb(curStep.TargetMode); //Debug.Assert(curCrumb != null, "Don't know how to get there from here..."); } // Swallow any mouse clicks just in case user thinks to click on display. // TODO (****) With changes, mouse hits will never be outside of active screen // so this should never happen... /* * if (GamePadInput.ActiveMode == GamePadInput.InputMode.KeyboardMouse) * { * Vector2 mouseHit = new Vector2(MouseInput.Position.X, MouseInput.Position.Y); * if (MouseInput.Left.WasPressedOrRepeat) * { * if (backdropBox.Contains(mouseHit)) * { * MouseInput.Left.ClearAllWasPressedState(); * Foley.PlayNoBudget(); * } * } * if (MouseInput.Right.WasPressedOrRepeat) * { * if (backdropBox.Contains(mouseHit)) * { * MouseInput.Right.ClearAllWasPressedState(); * Foley.PlayNoBudget(); * } * } * if (MouseInput.Middle.WasPressedOrRepeat) * { * if (backdropBox.Contains(mouseHit)) * { * MouseInput.Middle.ClearAllWasPressedState(); * Foley.PlayNoBudget(); * } * } * } */ } // end if non-Modal // Find focus actor if needed. string focusActorName = curStep.TargetCharacter; if (string.IsNullOrEmpty(focusActorName)) { focusActor = null; } else { // ALWAYS set the character to null since it may be destroyed any frame // and we don't want to reference a missing character. focusActor = null; if (focusActor == null || focusActor.DisplayNameNumber != focusActorName) { for (int i = 0; i < InGame.inGame.gameThingList.Count; i++) { GameActor actor = InGame.inGame.gameThingList[i] as GameActor; if (actor != null && actor.DisplayNameNumber == focusActorName) { // Found it! focusActor = actor; break; } } } } } // end if active. } // end of Update()
} // end of PreRender() public static void Render() { if (!active || backdrop == null || rt == null) { return; } ScreenSpaceQuad quad = ScreenSpaceQuad.GetInstance(); Vector2 size = new Vector2(rt.Width, rt.Height); backdropBox.Set(Vector2.Zero, size); // Tutorial info needs to go at top of "real" screen. InGame.RestoreViewportToFull(); quad.Render(rt, Vector2.Zero, size, "TexturedNoAlpha"); if (modalDisplay.Active) { quad.Render(dropShadow, Vector2.Zero, BokuGame.ScreenSize, "TexturedRegularAlpha"); } modalDisplay.Render(); // Display debug spew if active. if (DebugMode && active) { Vector2 pos = new Vector2(20, 400); SpriteBatch batch = UI2D.Shared.SpriteBatch; Color color = Color.Yellow; TextBlob blob = new TextBlob(UI2D.Shared.GetGameFont20, "", 1000); string text = "Tutorial Manager\n"; try { text += " current game mode : " + curGameMode.ToString() + "\n"; text += " current help overlay : " + HelpOverlay.Peek() + "\n"; text += " current step\n"; text += " display mode : " + curStep.DisplayMode.ToString() + "\n"; text += " target mode : " + curStep.TargetMode.ToString() + "\n"; if (curStep.CompletionTest != null) { text += " completion test : " + curStep.CompletionTest.Name.ToString() + "\n"; text += " args : " + curStep.CompletionTest.Args.ToString() + "\n"; } text += " current input mode : " + GamePadInput.ActiveMode.ToString() + "\n"; if (curCrumb != null) { text += "current crumb id " + curCrumb.id + "\n"; } } catch { } blob.RawText = text; blob.RenderWithButtons(pos, Color.Black, outlineColor: Color.White, outlineWidth: 1.2f); } } // end of Render()
} // end of PreRender() public void Render() { if (Active) { GraphicsDevice device = BokuGame.bokuGame.GraphicsDevice; RenderTarget2D rtFull = UI2D.Shared.RenderTargetDepthStencil1024_768; // Rendertarget we render whole display into. RenderTarget2D rt1k = UI2D.Shared.RenderTarget1024_768; Vector2 rtSize = new Vector2(rtFull.Width, rtFull.Height); CameraSpaceQuad csquad = CameraSpaceQuad.GetInstance(); ScreenSpaceQuad ssquad = ScreenSpaceQuad.GetInstance(); Color darkTextColor = new Color(20, 20, 20); Color greyTextColor = new Color(127, 127, 127); Color greenTextColor = new Color(8, 123, 110); Color whiteTextColor = new Color(255, 255, 255); // Render the scene to our rendertarget. InGame.SetRenderTarget(rtFull); // Clear to transparent. InGame.Clear(Color.Transparent); // Set up params for rendering UI with this camera. Fx.ShaderGlobals.SetCamera(camera); Vector2 pos; // Now render the background tiles. Vector2 backgroundSize = new Vector2(backgroundTexture.Width, backgroundTexture.Height); pos = (rtSize - backgroundSize) / 2.0f; ssquad.Render(backgroundTexture, pos, backgroundSize, @"TexturedRegularAlpha"); displayPosition = pos; // Now render the contents of the rt1k texture but with the edges blended using the mask. Vector2 rt1kSize = new Vector2(rt1k.Width, rt1k.Height); pos -= new Vector2(40, 70); try//minimize bug fix. { Vector4 limits = new Vector4(0.095f, 0.112f, 0.57f, 0.64f); ssquad.RenderWithYLimits(rt1k, limits, pos, rt1kSize, @"TexturedPreMultAlpha"); } catch { return; } // // Render buttons. // float margin = 64.0f; float totalWidth = continueButton.GetSize().X + /* margin + backButton.GetSize().X + */ margin + exitTutorialButton.GetSize().X; pos = new Vector2(rtSize.X / 2.0f, rtSize.Y / 2.0f + backgroundSize.Y * 0.28f); pos.X -= totalWidth / 2.0f; SpriteBatch batch = UI2D.Shared.SpriteBatch; batch.Begin(); continueButton.Render(pos); pos.X += continueButton.GetSize().X + margin; /* * backButton.Render(pos); * pos.X += backButton.GetSize().X + margin; */ exitTutorialButton.Render(pos); batch.End(); // Add left stick if needed. Vector2 min; Vector2 max; if (blob.NumLines >= textVisibleLines) { pos = displayPosition + new Vector2(-31, 300); ssquad.Render(leftStick, pos, new Vector2(leftStick.Width, leftStick.Height), "TexturedRegularAlpha"); min = pos; max = min + new Vector2(leftStick.Width, leftStick.Height / 2.0f); upBox.Set(min, max); min.Y = max.Y; max.Y += leftStick.Height / 2.0f; downBox.Set(min, max); } InGame.RestoreRenderTarget(); InGame.SetViewportToScreen(); // No put it all together. // Start with the background. if (useBackgroundThumbnail) { if (!thumbnail.GraphicsDevice.IsDisposed && !thumbnail.IsDisposed) { // Render the blurred thumbnail (if valid) full screen. if (!thumbnail.GraphicsDevice.IsDisposed) { InGame.RestoreViewportToFull(); Vector2 screenSize = new Vector2(device.Viewport.Width, device.Viewport.Height); ssquad.Render(thumbnail, Vector2.Zero, screenSize, @"TexturedNoAlpha"); InGame.SetViewportToScreen(); } } else { // No valid thumbnail, clear to dark. device.Clear(darkTextColor); } } // Calc scaling and position for rt. renderPosition = Vector2.Zero; renderScale = 1.0f; // The part of the dialog we care about is 1024x600 so we want to use // that as the size to fit to the screen. Vector2 dialogSize = new Vector2(1024, 600); Vector2 scale = BokuGame.ScreenSize / dialogSize; renderScale = Math.Min(Math.Min(scale.X, scale.Y), 1.0f); Vector2 renderSize = rt1kSize * renderScale; // Center on screen. renderPosition = (BokuGame.ScreenSize - renderSize) / 2.0f; ssquad.Render(rtFull, renderPosition, renderSize, @"TexturedRegularAlpha"); } } // end of ScrollableTextDisplay Render()