private static void TestFindNextCrumb() { // Only run this once. if (test) { test = false; } else { return; } for (int i = 1; i < (int)GameMode.LAST_MODE; i++) { for (int j = 1; j < (int)GameMode.LAST_MODE; j++) { TutorialManager.curGameMode = (GameMode)i; GameMode target = (GameMode)j; Crumb crumb = FindNextCrumb(target); if (i == j && crumb == null) { // Not interesting... continue; } /* * if (i == 13 || j == 13 || i == 42 || j == 42 || i == 43 || j == 43 || i == 44 || j == 44) * { * continue; * } */ #if !NETFX_CORE Debug.Print("cur " + TutorialManager.curGameMode.ToString() + " -> " + target.ToString()); #endif if (crumb == null) { #if !NETFX_CORE Debug.Print(" null crumb"); #endif } else { #if !NETFX_CORE Debug.Print(" crumb target " + crumb.targetMode.ToString()); #endif } crumb = null; } } }
public static Crumb[] Init() { CrumbList crumbList = null; CrumbList crumbStrings = null; const string stringsFilename = @"TutorialStrings.Xml"; const string crumbsFilename = @"TutorialCrumbs.Xml"; // Get the actual crumb list. This contains all the mode links. // Note, since this no longer contains strings it is no longer inthe Localizable folder... string filename = Path.Combine(@"Content\Xml", crumbsFilename); if (Storage4.FileExists(filename, StorageSource.TitleSpace)) { crumbList = Load(filename, StorageSource.TitleSpace); } else { Debug.Assert(false, "Missing file!"); } // Now get the strings for each of the crumbs. // First we need to get the default, English version. Look in both user and title space // since the shipped version may have been superceeded by a downloaded one. if (Storage4.FileExists(Path.Combine(Localizer.DefaultLanguageDir, stringsFilename), StorageSource.All)) { crumbStrings = Load(Path.Combine(Localizer.DefaultLanguageDir, stringsFilename), StorageSource.All); } else { Debug.Assert(false, "Missing file!"); } Crumb[] result = null; if (crumbStrings != null) { // Start with list of crumbs. result = crumbList.Crumbs; // Now match up crumbs with their strings. foreach (Crumb crumb in result) { // Find matching strings and copy over. for (int i = 0; i < crumbStrings.Crumbs.Length; i++) { Crumb strCrumb = crumbStrings.Crumbs[i]; if (crumb.id == strCrumb.id) { crumb.gamepadText = strCrumb.gamepadText; crumb.mouseText = strCrumb.mouseText; crumb.touchText = strCrumb.touchText; } } } } // Is our run-time local language different from the default? if (!Localizer.IsLocalDefault) { var localPath = Localizer.LocalLanguageDir; // Do we have a directory for the local language? if (localPath != null) { var localFile = Path.Combine(localPath, stringsFilename); if (Storage4.FileExists(localFile, StorageSource.All)) { CrumbList localCrumbs = Load(localFile, StorageSource.All); if (result != null && localCrumbs != null) { // Loop over each of the crumbs we currently have... for (int i = 0; i < result.Length; i++) { var defCrumb = result[i]; var foundLocalCrumb = false; // Loop over each of the localized crumbs searching for a match to existing crumbs. for (int j = 0; j < localCrumbs.Crumbs.Length; j++) { var localCrumb = localCrumbs.Crumbs[j]; // If the crumbs are the same, override the default with the localized if (localCrumb.id == defCrumb.id) { if (Localizer.ShouldReportMissing && localCrumb.GamepadText.Equals(defCrumb.GamepadText, StringComparison.OrdinalIgnoreCase) && localCrumb.MouseText.Equals(defCrumb.MouseText, StringComparison.OrdinalIgnoreCase) && localCrumb.TouchText.Equals(defCrumb.TouchText, StringComparison.OrdinalIgnoreCase) ) { Localizer.ReportIdentical(stringsFilename, "TargetMode: " + defCrumb.TargetMode.ToString()); } // Copy any localized strings over. if (!string.IsNullOrEmpty(localCrumb.GamepadText)) { result[i].gamepadText = localCrumb.GamepadText; } if (!string.IsNullOrEmpty(localCrumb.MouseText)) { result[i].mouseText = localCrumb.MouseText; } if (!string.IsNullOrEmpty(localCrumb.TouchText)) { result[i].touchText = localCrumb.TouchText; } foundLocalCrumb = true; break; } } if (!foundLocalCrumb) { Localizer.ReportMissing(stringsFilename, "TargetMode: " + defCrumb.TargetMode.ToString()); } } } else { Localizer.ReportMissing(stringsFilename, "CAN'T LOAD CRUMBS!"); } } else { Localizer.ReportMissing(stringsFilename, "CAN'T FIND FILE!"); } } else { Localizer.ReportMissing(localPath, "CAN'T FIND PATH FOR THIS LANGUAGE!"); } } /* * // Output crumb data for GraphViz * foreach (Crumb crumb in result) * { * foreach (TutorialManager.GameMode curmode in crumb.CurModes) * { * Debug.Print(curmode.ToString() + " -> " + crumb.targetMode.ToString() + " [ label = \" \" ];"); * } * } */ return(result); } // end of Init()
public static void LoadContent(bool immediate) { if (backdrop == null) { //backdrop = BokuGame.Load<Texture2D>(BokuGame.Settings.MediaPath + @"Textures\GridElements\TutorialTitle"); backdrop = BokuGame.Load <Texture2D>(BokuGame.Settings.MediaPath + @"Textures\GridElements\CheckboxWhite"); //backdrop = BokuGame.Load<Texture2D>(BokuGame.Settings.MediaPath + @"Textures\HelpCard\BlackHighlight"); } if (dropShadow == null) { dropShadow = BokuGame.Load <Texture2D>(BokuGame.Settings.MediaPath + @"Textures\GridElements\DropShadow"); } CreateRenderTarget(); if (crumbsArray == null) { crumbsArray = Crumb.Init(); crumbListMouse = new List <Crumb>(crumbsArray.Length); crumbListTouch = new List <Crumb>(crumbsArray.Length); crumbListGamepad = new List <Crumb>(crumbsArray.Length); for (int i = 0; i < crumbsArray.Length; i++) { crumbListMouse.Add(crumbsArray[i]); crumbListTouch.Add(crumbsArray[i]); crumbListGamepad.Add(crumbsArray[i]); } // For each list, pull the input specific crumbs to the top. int top = 0; for (int i = 0; i < crumbsArray.Length; i++) { if (crumbListMouse[i].TargetMode.ToString().Contains("Mouse")) { // Swap with top. Crumb tmp = crumbListMouse[top]; crumbListMouse[top] = crumbListMouse[i]; crumbListMouse[i] = tmp; ++top; } } top = 0; for (int i = 0; i < crumbsArray.Length; i++) { if (crumbListTouch[i].TargetMode.ToString().Contains("Touch")) { // Swap with top. Crumb tmp = crumbListTouch[top]; crumbListTouch[top] = crumbListTouch[i]; crumbListTouch[i] = tmp; ++top; } } top = 0; for (int i = 0; i < crumbsArray.Length; i++) { if (crumbListGamepad[i].TargetMode.ToString().Contains("Gamepad")) { // Swap with top. Crumb tmp = crumbListGamepad[top]; crumbListGamepad[top] = crumbListGamepad[i]; crumbListGamepad[i] = tmp; ++top; } } } if (modalDisplay != null) { modalDisplay.LoadContent(immediate); } } // end of LoadContent()
} // end of Print() #endregion #region Internal /// <summary> /// Given the targetMode, find the next crumb that will lead us from /// our current mode to the target. /// Returns null if no valid Crumb. /// </summary> /// <param name="targetMode"></param> /// <returns></returns> private static Crumb FindNextCrumb(GameMode targetMode) { crumbList.Clear(); // Pick which list to use basedon current input mode. List <Crumb> crumbs = crumbListMouse; if (GamePadInput.ActiveMode == GamePadInput.InputMode.Touch) { crumbs = crumbListTouch; } else if (GamePadInput.ActiveMode == GamePadInput.InputMode.GamePad) { crumbs = crumbListGamepad; } // Start with the current mode. crumbList.Add(new CrumbNode(null, curGameMode, 0)); int cur = 0; do { // For each crumb. for (int i = 0; i < crumbs.Count; i++) { Crumb crumb = crumbs[i]; for (int j = 0; j < crumb.CurModes.Length; j++) { // Is this crumb a match? if (crumbList[cur].mode == crumb.CurModes[j]) { // See if this crumb's target is already in the list, if not, don't add it. bool needToAdd = true; for (int k = 0; k < crumbList.Count; k++) { if (crumbList[k].mode == crumb.targetMode) { needToAdd = false; break; } } if (needToAdd) { crumbList.Add(new CrumbNode(crumb, crumb.targetMode, cur)); // Are we done? if (crumb.targetMode == targetMode) { // Work backwards from what we just added and find the mode // along the path that has the current mode as its parent. cur = crumbList.Count - 1; while (crumbList[cur].parentIndex != 0) { cur = crumbList[cur].parentIndex; } // Found it! return(crumbList[cur].crumb); } } break; } } } ++cur; } while (cur < crumbList.Count); // No solution found, return null. return(null); } // end of FindNextCrumb()
public CrumbNode(Crumb crumb, GameMode mode, int parent) { this.crumb = crumb; this.mode = mode; this.parentIndex = parent; }
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()