38f1ddb...178a853: v0.8.9.1, removed content folder

This commit is contained in:
Joonas Rikkonen
2019-03-18 19:46:58 +02:00
parent 38f1ddb6fe
commit 6c0679c297
1054 changed files with 151673 additions and 144931 deletions
@@ -4,15 +4,15 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Tutorials
{
class BasicTutorial : TutorialType
class BasicTutorial : ScenarioTutorial
{
public BasicTutorial(string name)
: base(name)
public BasicTutorial(XElement element)
: base(element)
{
}
public override IEnumerable<object> UpdateState()
@@ -69,7 +69,7 @@ namespace Barotrauma.Tutorials
infoBox = CreateInfoFrame("At the moment the submarine has no power, which means that crucial systems such as the oxygen generator or the engine aren't running. Let's fix this: go to the upper left corner of the submarine, where you'll find a nuclear reactor.");
Reactor reactor = Item.ItemList.Find(i => i.HasTag("tutorialreactor")).GetComponent<Reactor>();
reactor.MeltDownTemp = 20000.0f;
//reactor.MeltDownTemp = 20000.0f;
while (Vector2.Distance(Controlled.Position, reactor.Item.Position) > 200.0f)
{
@@ -78,14 +78,14 @@ namespace Barotrauma.Tutorials
infoBox = CreateInfoFrame("The reactor requires fuel rods to generate power. You can grab one from the steel cabinet by walking next to it and pressing E.");
while (Controlled.SelectedConstruction == null || Controlled.SelectedConstruction.Name != "Steel Cabinet")
while (Controlled.SelectedConstruction == null || Controlled.SelectedConstruction.Prefab.Identifier != "steelcabinet")
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("Pick up one of the fuel rods either by double-clicking or dragging and dropping it into your inventory.");
while (!HasItem("Fuel Rod"))
while (!HasItem("fuelrod"))
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
@@ -116,7 +116,8 @@ namespace Barotrauma.Tutorials
infoBox = CreateInfoFrame("The reactor core has started generating heat, which in turn generates power for the submarine. The power generation is very low at the moment,"
+ " because the reactor is set to shut itself down when the temperature rises above 500 degrees Celsius. You can adjust the temperature limit by changing the \"Shutdown Temperature\" in the control panel.", true);
while (infoBox != null)
//TODO: reimplement
/*while (infoBox != null)
{
reactor.ShutDownTemp = Math.Min(reactor.ShutDownTemp, 5000.0f);
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
@@ -142,14 +143,14 @@ namespace Barotrauma.Tutorials
while (!reactor.AutoTemp)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
}*/
yield return new WaitForSeconds(0.5f);
infoBox = CreateInfoFrame("That's the basics of operating the reactor! Now that there's power available for the engines, it's time to get the submarine moving. "
+ "Deselect the reactor by pressing E and head to the command room at the right edge of the vessel.");
Steering steering = Item.ItemList.Find(i => i.HasTag("tutorialsteering")).GetComponent<Steering>();
Radar radar = steering.Item.GetComponent<Radar>();
Sonar sonar = steering.Item.GetComponent<Sonar>();
while (Vector2.Distance(Controlled.Position, steering.Item.Position) > 150.0f)
{
@@ -181,7 +182,7 @@ namespace Barotrauma.Tutorials
+ " Equip a screwdriver by pulling it to either of the slots with a hand symbol, and then use it on the terminal by left clicking.");
while (Controlled.SelectedConstruction != steering.Item ||
Controlled.SelectedItems.FirstOrDefault(i => i != null && i.Name == "Screwdriver") == null)
Controlled.SelectedItems.FirstOrDefault(i => i != null && i.Prefab.Identifier == "screwdriver") == null)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
@@ -191,7 +192,7 @@ namespace Barotrauma.Tutorials
+ " going into the to the power connection - that's why the monitor isn't working."
+ " You should find a piece of wire to connect it. Try searching some of the cabinets scattered around the sub.");
while (!HasItem("Wire"))
while (!HasItem("wire"))
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
@@ -202,7 +203,7 @@ namespace Barotrauma.Tutorials
while ((Controlled.SelectedConstruction != junctionBox.Item &&
Controlled.SelectedConstruction != steering.Item) ||
Controlled.SelectedItems.FirstOrDefault(i => i != null && i.Name == "Screwdriver") == null)
Controlled.SelectedItems.FirstOrDefault(i => i != null && i.Prefab.Identifier == "screwdriver") == null)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
@@ -248,7 +249,7 @@ namespace Barotrauma.Tutorials
infoBox = CreateInfoFrame("Connect the wire to the junction box by pulling it to the power connection, the same way you did with the navigation terminal.");
while (radar.Voltage < 0.1f)
while (sonar.Voltage < 0.1f)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
@@ -263,7 +264,7 @@ namespace Barotrauma.Tutorials
infoBox = CreateInfoFrame("You can take a look at the area around the sub by selecting the \"Active Sonar\" checkbox.");
while (!radar.IsActive)
while (!sonar.IsActive)
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
@@ -296,13 +297,13 @@ namespace Barotrauma.Tutorials
var moloch = Character.Create(
"Content/Characters/Moloch/moloch.xml",
steering.Item.WorldPosition + new Vector2(3000.0f, -500.0f));
steering.Item.WorldPosition + new Vector2(3000.0f, -500.0f), "");
moloch.PlaySound(CharacterSound.SoundType.Attack);
yield return new WaitForSeconds(1.0f);
infoBox = CreateInfoFrame("Uh-oh... Something enormous just appeared on the radar.");
infoBox = CreateInfoFrame("Uh-oh... Something enormous just appeared on the sonar.");
List<Structure> windows = new List<Structure>();
foreach (Structure s in Structure.WallList)
@@ -389,10 +390,10 @@ namespace Barotrauma.Tutorials
bool divingMaskSelected = false;
while (!HasItem("Diving Mask") && !HasItem("Diving Suit"))
while (!HasItem("divingmask") && !HasItem("divingsuit"))
{
if (!divingMaskSelected &&
Controlled.FocusedItem != null && Controlled.FocusedItem.Name == "Diving Suit")
Controlled.FocusedItem != null && Controlled.FocusedItem.Prefab.Identifier == "divingsuit")
{
infoBox = CreateInfoFrame("There can only be one item in each inventory slot, so you need to take off "
+ "the jumpsuit if you wish to wear a diving suit.");
@@ -403,20 +404,20 @@ namespace Barotrauma.Tutorials
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
if (HasItem("Diving Mask"))
if (HasItem("divingmask"))
{
infoBox = CreateInfoFrame("The diving mask will let you breathe underwater, but it won't protect from the water pressure outside the sub. " +
"It should be fine for the situation at hand, but you still need to find an oxygen tank and drag it into the same slot as the mask." +
"You should grab one or two from one of the cabinets.");
}
else if (HasItem("Diving Suit"))
else if (HasItem("divingsuit"))
{
infoBox = CreateInfoFrame("In addition to letting you breathe underwater, the suit will protect you from the water pressure outside the sub " +
"(unlike the diving mask). However, you still need to drag an oxygen tank into the same slot as the suit to supply oxygen. " +
"You should grab one or two from one of the cabinets.");
}
while (!HasItem("Oxygen Tank"))
while (!HasItem("oxygentank"))
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
@@ -444,7 +445,7 @@ namespace Barotrauma.Tutorials
"time to head to the room below and load some shells for the railgun.");
var loader = Item.ItemList.Find(i => i.Name == "Railgun Loader").GetComponent<ItemContainer>();
var loader = Item.ItemList.Find(i => i.Prefab.Identifier == "railgunloader").GetComponent<ItemContainer>();
while (Math.Abs(Controlled.Position.Y - loader.Item.Position.Y) > 80)
{
@@ -454,9 +455,10 @@ namespace Barotrauma.Tutorials
infoBox = CreateInfoFrame("Grab one of the shells. You can load it by selecting the railgun loader and dragging the shell to. "
+ "one of the free slots. You need two hands to carry a shell, so make sure you don't have anything else in either hand.");
while (loader.Item.ContainedItems.FirstOrDefault(i => i != null && i.Name == "Railgun Shell") == null)
while (loader.Item.ContainedItems.FirstOrDefault(i => i != null && i.Prefab.Identifier == "railgunshell") == null)
{
moloch.Health = 50.0f;
//TODO: reimplement
//moloch.Health = 50.0f;
capacitor1.Charge += 5.0f;
capacitor2.Charge += 5.0f;
@@ -465,7 +467,7 @@ namespace Barotrauma.Tutorials
infoBox = CreateInfoFrame("Now we're ready to shoot! Select the railgun controller.");
while (Controlled.SelectedConstruction == null || Controlled.SelectedConstruction.Name != "Railgun Controller")
while (Controlled.SelectedConstruction == null || Controlled.SelectedConstruction.Prefab.Identifier != "railguncontroller")
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
@@ -491,7 +493,7 @@ namespace Barotrauma.Tutorials
infoBox = CreateInfoFrame("The creature has died. Now you should fix the damages in the control room: " +
"Grab a welding tool from the closet in the railgun room.");
while (!HasItem("Welding Tool"))
while (!HasItem("weldingtool"))
{
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
}
@@ -501,9 +503,9 @@ namespace Barotrauma.Tutorials
do
{
var weldingTool = Controlled.Inventory.Items.FirstOrDefault(i => i != null && i.Name == "Welding Tool");
var weldingTool = Controlled.Inventory.Items.FirstOrDefault(i => i != null && i.Prefab.Identifier == "weldingtool");
if (weldingTool != null &&
weldingTool.ContainedItems.FirstOrDefault(contained => contained != null && contained.Name == "Welding Fuel Tank") != null) break;
weldingTool.ContainedItems.FirstOrDefault(contained => contained != null && contained.Prefab.Identifier == "weldingfueltank") != null) break;
yield return Controlled.IsDead ? CoroutineStatus.Success : CoroutineStatus.Running;
} while (true);
@@ -606,13 +608,15 @@ namespace Barotrauma.Tutorials
infoBox = CreateInfoFrame("That was all there is to this tutorial! Now you should be able to handle " +
"most of the basic tasks on board the submarine.");
Completed = true;
yield return new WaitForSeconds(4.0f);
Controlled = null;
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
GameMain.LightManager.LosEnabled = false;
var cinematic = new TransitionCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, 5.0f);
var cinematic = new RoundEndCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, 5.0f);
while (cinematic.Running)
{
@@ -625,21 +629,20 @@ namespace Barotrauma.Tutorials
yield return CoroutineStatus.Success;
}
private bool HasItem(string itemName)
private bool HasItem(string itemIdentifier)
{
if (Character.Controlled == null) return false;
return Character.Controlled.Inventory.FindItem(itemName) != null;
return Character.Controlled.Inventory.FindItemByIdentifier(itemIdentifier) != null;
}
protected IEnumerable<object> KeepReactorRunning(Reactor reactor)
{
do
{
reactor.AutoTemp = true;
reactor.ShutDownTemp = 5000.0f;
//TODO: reimplement
/*reactor.AutoTemp = true;
reactor.ShutDownTemp = 5000.0f;*/
yield return CoroutineStatus.Running;
} while (Item.ItemList.Contains(reactor.Item));
@@ -657,16 +660,17 @@ namespace Barotrauma.Tutorials
{
if (enemy == null || Character.Controlled == null) break;
enemy.Health = 50.0f;
//TODO: reimplement
//enemy.Health = 50.0f;
enemy.AIController.State = AIController.AIState.None;
enemy.AIController.State = AIController.AIState.Idle;
Vector2 targetPos = Character.Controlled.WorldPosition + new Vector2(0.0f, 3000.0f);
Vector2 steering = targetPos - enemy.WorldPosition;
if (steering != Vector2.Zero) steering = Vector2.Normalize(steering);
enemy.AIController.Steering = steering * 2.0f;
enemy.AIController.Steering = steering * enemy.AnimController.GetCurrentSpeed(true);
yield return CoroutineStatus.Running;
} while (capacitors.FirstOrDefault(c => c.Charge > 0.4f) == null);
@@ -0,0 +1,512 @@
using System.Collections.Generic;
using System.Xml.Linq;
using System;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Barotrauma.Items.Components;
using System.Linq;
namespace Barotrauma.Tutorials
{
class ContextualTutorial : Tutorial
{
public static bool Selected = false;
public static bool ContentRunning = false;
public static bool Initialized = false;
private enum ContentTypes { None = 0, Video = 1, Text = 2 };
private TutorialSegment activeSegment;
private List<TutorialSegment> segments;
private SpriteSheetPlayer spriteSheetPlayer;
private Steering navConsole;
private Reactor reactor;
private Sonar sonar;
private Vector2 subStartingPosition;
private List<Character> crew;
private List<Pair<Character, float>> characterTimeOnSonar;
private float requiredTimeOnSonar = 5f;
private bool started = false;
private string playableContentPath;
private float inputGracePeriodTimer;
private const float inputGracePeriod = .5f;
private float tutorialTimer;
private float degrading2ActivationCountdown;
private bool disableTutorialOnDeficiencyFound = true;
private class TutorialSegment
{
public string Name;
public ContentTypes ContentType;
public XElement Content;
public bool IsTriggered;
public TutorialSegment(XElement config)
{
Name = config.GetAttributeString("name", "Missing Name");
Enum.TryParse(config.GetAttributeString("contenttype", "None"), true, out ContentType);
IsTriggered = config.GetAttributeBool("istriggered", false);
switch (ContentType)
{
case ContentTypes.None:
break;
case ContentTypes.Video:
Content = config.Element("Video");
break;
case ContentTypes.Text:
Content = config.Element("Text");
break;
}
}
}
public ContextualTutorial(XElement element) : base(element)
{
playableContentPath = element.GetAttributeString("playablecontentpath", "");
segments = new List<TutorialSegment>();
foreach (var segment in element.Elements("Segment"))
{
segments.Add(new TutorialSegment(segment));
}
Name = "ContextualTutorial";
}
public override void Initialize()
{
if (Initialized) return;
Initialized = true;
base.Initialize();
spriteSheetPlayer = new SpriteSheetPlayer();
characterTimeOnSonar = new List<Pair<Character, float>>();
for (int i = 0; i < segments.Count; i++)
{
segments[i].IsTriggered = false;
}
}
public void LoadPartiallyComplete(XElement element)
{
int[] completedSegments = element.GetAttributeIntArray("completedsegments", null);
if (completedSegments == null || completedSegments.Length == 0)
{
return;
}
if (completedSegments.Length == segments.Count) // Completed all segments
{
Stop();
return;
}
for (int i = 0; i < completedSegments.Length; i++)
{
segments[completedSegments[i]].IsTriggered = true;
}
}
private void PreloadVideoContent()
{
for (int i = 0; i < segments.Count; i++)
{
if (segments[i].ContentType != ContentTypes.Video || segments[i].IsTriggered) continue;
spriteSheetPlayer.PreloadContent(playableContentPath, "tutorial", segments[i].Name, segments[i].Content);
}
}
public void SavePartiallyComplete(XElement element)
{
XElement tutorialElement = new XElement("contextualtutorial");
tutorialElement.Add(new XAttribute("completedsegments", GetCompletedSegments()));
element.Add(tutorialElement);
}
private string GetCompletedSegments()
{
string completedSegments = string.Empty;
for (int i = 0; i < segments.Count; i++)
{
if (segments[i].IsTriggered)
{
completedSegments += i + ",";
}
}
if (completedSegments.Length > 0)
{
completedSegments = completedSegments.TrimEnd(',');
}
return completedSegments;
}
public override void Start()
{
if (!Initialized) return;
PreloadVideoContent();
Completed = true; // Trigger completed at start to prevent the contextual tutorial from automatically activating on starting new campaigns after this one
base.Start();
activeSegment = null;
tutorialTimer = 0.0f;
inputGracePeriodTimer = 0.0f;
degrading2ActivationCountdown = -1;
subStartingPosition = Vector2.Zero;
characterTimeOnSonar.Clear();
subStartingPosition = Submarine.MainSub.WorldPosition;
navConsole = Item.ItemList.Find(i => i.HasTag("command"))?.GetComponent<Steering>();
sonar = navConsole?.Item.GetComponent<Sonar>();
reactor = Item.ItemList.Find(i => i.HasTag("reactor"))?.GetComponent<Reactor>();
#if DEBUG
if (reactor == null || navConsole == null || sonar == null)
{
infoBox = CreateInfoFrame("Submarine not compatible with the tutorial:"
+ "\nReactor - " + (reactor != null ? "OK" : "Tag 'reactor' not found")
+ "\nNavigation Console - " + (navConsole != null ? "OK" : "Tag 'command' not found")
+ "\nSonar - " + (sonar != null ? "OK" : "Not found under Navigation Console"), true);
CoroutineManager.StartCoroutine(WaitForErrorClosed());
return;
}
#endif
if (disableTutorialOnDeficiencyFound)
{
if (reactor == null || navConsole == null || sonar == null)
{
Stop();
return;
}
}
else
{
if (navConsole == null) segments[2].IsTriggered = true; // Disable navigation console usage tutorial
if (reactor == null) segments[5].IsTriggered = true; // Disable reactor usage tutorial
if (sonar == null) segments[6].IsTriggered = true; // Disable enemy on sonar tutorial
}
crew = GameMain.GameSession.CrewManager.GetCharacters().ToList();
started = true;
}
#if DEBUG
private IEnumerable<object> WaitForErrorClosed()
{
while (infoBox != null) yield return null;
Stop();
}
#endif
public void Stop()
{
started = ContentRunning = Initialized = false;
spriteSheetPlayer.Remove();
spriteSheetPlayer = null;
characterTimeOnSonar = null;
}
public override void AddToGUIUpdateList()
{
base.AddToGUIUpdateList();
if (spriteSheetPlayer != null)
{
spriteSheetPlayer.AddToGUIUpdateList();
}
}
public override void Update(float deltaTime)
{
if (!started) return;
deltaTime *= 0.5f;
if (ContentRunning) // Content is running, wait until dismissed
{
if (inputGracePeriodTimer < inputGracePeriod)
{
inputGracePeriodTimer += deltaTime;
}
else if (Keyboard.GetState().GetPressedKeys().Length > 0 || Mouse.GetState().LeftButton == ButtonState.Pressed || Mouse.GetState().RightButton == ButtonState.Pressed)
{
switch (activeSegment.ContentType)
{
case ContentTypes.None:
break;
case ContentTypes.Video:
spriteSheetPlayer.Stop();
break;
case ContentTypes.Text:
infoBox = null;
break;
}
activeSegment = null;
ContentRunning = false;
inputGracePeriodTimer = 0.0f;
}
return;
}
for (int i = 0; i < segments.Count; i++)
{
if (segments[i].IsTriggered) continue;
if (CheckContextualTutorials(i, deltaTime)) // Found a relevant tutorial, halt finding new ones
{
break;
}
}
}
private bool CheckContextualTutorials(int index, float deltaTime)
{
switch (index)
{
case 0: // Welcome: Game Start [Text]
if (tutorialTimer < 0.5f)
{
tutorialTimer += deltaTime;
return false;
}
break;
case 1: // Command Reactor: 10 seconds after 'Welcome' dismissed and only if no command given to start reactor [Video]
if (tutorialTimer < 10.5f)
{
tutorialTimer += deltaTime;
if (HasOrder("operatereactor"))
{
segments[index].IsTriggered = true;
tutorialTimer = 10.5f;
}
return false;
}
break;
case 2: // Nav Console: 20 seconds after 'Command Reactor' dismissed or if nav console is activated [Video]
if (Character.Controlled?.SelectedConstruction != navConsole.Item)
{
if (!segments[1].IsTriggered) return false; // Do not advance tutorial timer based on this segment if reactor has not been powered up
if (tutorialTimer < 30.5f)
{
tutorialTimer += deltaTime;
return false;
}
}
else
{
if (!segments[1].IsTriggered || !HasOrder("operatereactor")) // If reactor has not been powered up or ordered to be, default to that one first
{
if (tutorialTimer < 10.5f)
{
tutorialTimer = 10.5f;
}
return false;
}
tutorialTimer = 30.5f;
}
break;
case 3: // Objective: Travel ~150 meters and while sub is not flooding [Text]
if (Vector2.Distance(subStartingPosition, Submarine.MainSub.WorldPosition) < 12000f || IsFlooding())
{
return false;
}
else // Called earlier than others due to requiring specific args
{
TriggerTutorialSegment(index, GameMain.GameSession.EndLocation.Name);
return true;
}
case 4: // Flood: Hull is breached and sub is taking on water [Video]
if (!IsFlooding())
{
return false;
}
break;
case 5: // Reactor: Player uses reactor for the first time [Video]
if (Character.Controlled?.SelectedConstruction != reactor.Item)
{
return false;
}
break;
case 6: // Enemy on Sonar: Player witnesses creature signal on sonar for 5 seconds [Video]
if (!HasEnemyOnSonarForDuration(deltaTime))
{
return false;
}
break;
case 7: // Degrading1: Any equipment degrades to 50% health or less and player has not assigned any crew to perform maintenance [Text]
bool degradedEquipmentFound = false;
foreach (Item item in Item.ItemList)
{
if (!item.Repairables.Any() || item.Condition > 50.0f) continue;
degradedEquipmentFound = true;
break;
}
if (degradedEquipmentFound)
{
degrading2ActivationCountdown = 5f;
if (HasOrder("repairsystems"))
{
segments[index].IsTriggered = true;
return false;
}
}
else
{
return false;
}
break;
case 8: // Degrading2: 5 seconds after 'Degrading1' dismissed, and only if player has not assigned any crew to perform maintenance [Video]
if (degrading2ActivationCountdown == -1f)
{
return false;
}
else if (degrading2ActivationCountdown > 0.0f)
{
degrading2ActivationCountdown -= deltaTime;
if (HasOrder("repairsystems"))
{
segments[index].IsTriggered = true;
}
return false;
}
break;
case 9: // Medical: Crewmember is injured but not killed [Video]
bool injuredFound = false;
for (int i = 0; i < crew.Count; i++)
{
Character member = crew[i];
if (member.Vitality < member.MaxVitality && !member.IsDead)
{
injuredFound = true;
break;
}
}
if (!injuredFound) return false;
break;
case 10: // Approach1: Destination is within ~100m [Video]
if (Vector2.Distance(Submarine.MainSub.WorldPosition, Level.Loaded.EndPosition) > 8000f)
{
return false;
}
break;
case 11: // Approach2: Sub is docked [Text]
if (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0)
{
return false;
}
break;
}
TriggerTutorialSegment(index);
return true;
}
private bool HasOrder(string aiTag)
{
for (int i = 0; i < crew.Count; i++)
{
if (crew[i].CurrentOrder?.AITag == aiTag) return true;
}
return false;
}
private bool IsFlooding()
{
foreach (Gap gap in Gap.GapList)
{
if (gap.ConnectedWall == null) continue;
if (gap.ConnectedDoor != null || gap.Open <= 0.0f) continue;
if (gap.Submarine == null) continue;
if (gap.Submarine != Submarine.MainSub) continue;
return true;
}
return false;
}
private bool HasEnemyOnSonarForDuration(float deltaTime)
{
foreach (Character c in Character.CharacterList)
{
if (c.AnimController.CurrentHull != null || !c.Enabled || !(c.AIController is EnemyAIController)) continue;
if (sonar.DetectSubmarineWalls && c.AnimController.CurrentHull == null && sonar.Item.CurrentHull != null) continue;
if (Vector2.DistanceSquared(c.WorldPosition, sonar.Item.WorldPosition) > sonar.Range * sonar.Range)
{
for (int i = 0; i < characterTimeOnSonar.Count; i++)
{
if (characterTimeOnSonar[i].First == c)
{
characterTimeOnSonar.RemoveAt(i);
break;
}
}
continue;
}
Pair<Character, float> pair = characterTimeOnSonar.Find(ct => ct.First == c);
if (pair != null)
{
pair.Second += deltaTime;
}
else
{
characterTimeOnSonar.Add(new Pair<Character, float>(c, deltaTime));
}
}
return characterTimeOnSonar.Find(ct => ct.Second >= requiredTimeOnSonar) != null;
}
private void TriggerTutorialSegment(int index, params object[] args)
{
ContentRunning = true;
activeSegment = segments[index];
activeSegment.IsTriggered = true;
switch (activeSegment.ContentType)
{
case ContentTypes.None:
break;
case ContentTypes.Video:
spriteSheetPlayer.LoadContent(playableContentPath, activeSegment.Content, activeSegment.Name, true);
break;
case ContentTypes.Text:
infoBox = CreateInfoFrame(TextManager.Get(activeSegment.Name), TextManager.Get(activeSegment.Content.GetAttributeString("tag", ""), false, args),
activeSegment.Content.GetAttributeInt("width", 300),
activeSegment.Content.GetAttributeInt("height", 80),
activeSegment.Content.GetAttributeString("anchor", "Center"), false);
break;
}
for (int i = 0; i < segments.Count; i++)
{
if (!segments[i].IsTriggered) return;
}
CoroutineManager.StartCoroutine(WaitToStop()); // Completed
}
private IEnumerable<object> WaitToStop()
{
while (ContentRunning) yield return null;
Stop();
}
}
}
@@ -1,19 +1,20 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Tutorials
{
class EditorTutorial : TutorialType
class EditorTutorial : Tutorial
{
public EditorTutorial(string name)
: base (name)
public EditorTutorial(XElement element)
: base (element)
{
}
public override IEnumerable<object> UpdateState()
{
infoBox = CreateInfoFrame("Use the mouse wheel to zoom in and out, and WASD to move the camera around.", true);
/*infoBox = CreateInfoFrame("Use the mouse wheel to zoom in and out, and WASD to move the camera around.", true);
while (infoBox!=null)
while (infoBox != null)
{
yield return CoroutineStatus.Running;
}
@@ -32,7 +33,7 @@ namespace Barotrauma.Tutorials
yield return CoroutineStatus.Running;
}
infoBox = CreateInfoFrame("You can now create a horizontal wall by clicking and dragging. When you're done, right click to stop creating walls.");
infoBox = CreateInfoFrame("You can now create a horizontal wall by clicking and dragging. When you're done, right click to stop creating walls.");*/
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Tutorials
{
class ScenarioTutorial : Tutorial
{
private Character character;
private SpawnType spawnPointType;
private string submarinePath;
private string levelSeed;
public ScenarioTutorial(XElement element) : base(element)
{
submarinePath = element.GetAttributeString("submarinepath", "");
levelSeed = element.GetAttributeString("levelseed", "tuto");
Enum.TryParse(element.GetAttributeString("spawnpointtype", "Human"), true, out spawnPointType);
}
public override void Initialize()
{
base.Initialize();
GameMain.Instance.ShowLoading(Loading());
}
public override void Start()
{
base.Start();
WayPoint wayPoint = WayPoint.GetRandom(spawnPointType, null);
if (wayPoint == null)
{
DebugConsole.ThrowError("A waypoint with the spawntype \"" + spawnPointType + "\" is required for the tutorial event");
return;
}
CharacterInfo charInfo = configElement.Element("Character") == null ?
new CharacterInfo(Character.HumanConfigFile, "", Gender.None, JobPrefab.List.Find(jp => jp.Identifier == "engineer")) :
new CharacterInfo(configElement.Element("Character"));
character = Character.Create(charInfo, wayPoint.WorldPosition, "", false, false);
Character.Controlled = character;
character.GiveJobItems(null);
var idCard = character.Inventory.FindItemByIdentifier("idcard");
if (idCard == null)
{
DebugConsole.ThrowError("Item prefab \"ID Card\" not found!");
return;
}
idCard.AddTag("com");
idCard.AddTag("eng");
CoroutineManager.StartCoroutine(UpdateState());
}
private IEnumerable<object> Loading()
{
Submarine.MainSub = Submarine.Load(submarinePath, "", true);
yield return CoroutineStatus.Running;
GameMain.GameSession = new GameSession(Submarine.MainSub, "",
GameModePreset.List.Find(g => g.Identifier == "tutorial"));
(GameMain.GameSession.GameMode as TutorialMode).tutorial = this;
GameMain.GameSession.StartRound(levelSeed);
GameMain.GameSession.EventManager.Events.Clear();
GameMain.GameScreen.Select();
yield return CoroutineStatus.Success;
}
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (character != null)
{
if (Character.Controlled == null)
{
CoroutineManager.StopCoroutines("TutorialMode.UpdateState");
infoBox = null;
}
else if (Character.Controlled.IsDead)
{
Character.Controlled = null;
CoroutineManager.StopCoroutines("TutorialMode.UpdateState");
infoBox = null;
CoroutineManager.StartCoroutine(Dead());
}
}
}
private IEnumerable<object> Dead()
{
yield return new WaitForSeconds(3.0f);
var messageBox = new GUIMessageBox("You have died", "Do you want to try again?", new string[] { "Yes", "No" });
messageBox.Buttons[0].OnClicked += Restart;
messageBox.Buttons[0].OnClicked += messageBox.Close;
messageBox.Buttons[1].OnClicked = GameMain.MainMenuScreen.SelectTab;
messageBox.Buttons[1].OnClicked += messageBox.Close;
yield return CoroutineStatus.Success;
}
}
}
@@ -0,0 +1,219 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml.Linq;
namespace Barotrauma.Tutorials
{
abstract class Tutorial
{
public static List<Tutorial> Tutorials;
protected GUIComponent infoBox;
protected XElement configElement;
private enum TutorialType { None, Scenario, Contextual };
private TutorialType tutorialType = TutorialType.None;
public string Name
{
get;
protected set;
}
private bool completed;
public bool Completed
{
get { return completed; }
protected set
{
if (completed == value) return;
completed = value;
GameMain.Config.Save();
}
}
public static void Init()
{
Tutorials = new List<Tutorial>();
foreach (string file in GameMain.Instance.GetFilesOfType(ContentType.Tutorials))
{
XDocument doc = XMLExtensions.TryLoadXml(file);
if (doc?.Root == null) continue;
foreach (XElement element in doc.Root.Elements())
{
Tutorial newTutorial = Load(element);
if (newTutorial != null) Tutorials.Add(newTutorial);
}
}
}
private static Tutorial Load(XElement element)
{
Type t;
string type = element.Name.ToString().ToLowerInvariant();
try
{
// Get the type of a specified class.
t = Type.GetType("Barotrauma.Tutorials." + type + "", false, true);
if (t == null)
{
DebugConsole.ThrowError("Could not find tutorial type \"" + type + "\"");
return null;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Could not find tutorial type \"" + type + "\"", e);
return null;
}
ConstructorInfo constructor;
try
{
if (!t.IsSubclassOf(typeof(Tutorial))) return null;
constructor = t.GetConstructor(new Type[] { typeof(XElement) });
if (constructor == null)
{
DebugConsole.ThrowError("Could not find the constructor of tutorial type \"" + type + "\"");
return null;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Could not find the constructor of tutorial type \"" + type + "\"", e);
return null;
}
Tutorial tutorial = null;
try
{
object component = constructor.Invoke(new object[] { element });
tutorial = (Tutorial)component;
}
catch (TargetInvocationException e)
{
DebugConsole.ThrowError("Error while loading tutorial of the type " + t + ".", e.InnerException);
}
return tutorial;
}
public Tutorial(XElement element)
{
configElement = element;
Name = element.GetAttributeString("name", "Unnamed");
Completed = GameMain.Config.CompletedTutorialNames.Contains(Name);
Enum.TryParse(element.GetAttributeString("tutorialtype", "Scenario"), true, out tutorialType);
}
public virtual void Initialize()
{
}
public virtual void Start()
{
}
public virtual void AddToGUIUpdateList()
{
if (infoBox != null) infoBox.AddToGUIUpdateList();
}
public virtual void Update(float deltaTime)
{
}
public virtual IEnumerable<object> UpdateState()
{
yield return CoroutineStatus.Success;
}
protected bool CloseInfoFrame(GUIButton button, object userData)
{
infoBox = null;
return true;
}
protected GUIComponent CreateInfoFrame(string text, bool hasButton = false)
{
int width = 300;
int height = hasButton ? 110 : 80;
string wrappedText = ToolBox.WrapText(text, width, GUI.Font);
height += wrappedText.Split('\n').Length * 25;
var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), GUI.Canvas, Anchor.TopRight) { AbsoluteOffset = new Point(20) });
infoBlock.Flash(Color.Green);
var textBlock = new GUITextBlock(new RectTransform(new Vector2(0.9f, 0.7f), infoBlock.RectTransform, Anchor.Center),
text, wrap: true);
if (hasButton)
{
var okButton = new GUIButton(new RectTransform(new Point(80, 25), infoBlock.RectTransform, Anchor.BottomCenter) { AbsoluteOffset = new Point(0, 5) },
TextManager.Get("OK"))
{
OnClicked = CloseInfoFrame
};
}
GUI.PlayUISound(GUISoundType.Message);
return infoBlock;
}
protected GUIComponent CreateInfoFrame(string title, string text, int width, int height, string anchorStr, bool hasButton = false)
{
if (hasButton) height += 30;
string wrappedText = ToolBox.WrapText(text, width, GUI.Font);
height += wrappedText.Split('\n').Length * 25;
if (title.Length > 0)
{
height += 35;
}
Anchor anchor = Anchor.TopRight;
Enum.TryParse(anchorStr, out anchor);
var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), GUI.Canvas, anchor) { AbsoluteOffset = new Point(20) });
infoBlock.Flash(Color.Green);
if (title.Length > 0)
{
var titleBlock = new GUITextBlock(new RectTransform(new Vector2(1f, .35f), infoBlock.RectTransform, Anchor.TopCenter,
Pivot.TopCenter), title, font: GUI.LargeFont, textAlignment: Alignment.Center);
}
var textBlock = new GUITextBlock(new RectTransform(new Vector2(0.9f, 1f), infoBlock.RectTransform, Anchor.BottomCenter),
text, wrap: true);
if (hasButton)
{
var okButton = new GUIButton(new RectTransform(new Point(80, 25), infoBlock.RectTransform, Anchor.BottomCenter) { AbsoluteOffset = new Point(0, 5) },
TextManager.Get("OK"))
{
OnClicked = CloseInfoFrame
};
}
GUI.PlayUISound(GUISoundType.Message);
return infoBlock;
}
protected bool Restart(GUIButton button, object obj)
{
TutorialMode.StartTutorial(this);
return true;
}
}
}
@@ -5,13 +5,11 @@ namespace Barotrauma
{
class TutorialMode : GameMode
{
public TutorialType tutorialType;
public Tutorial tutorial;
public static void StartTutorial(TutorialType tutorialType)
{
Submarine.MainSub = Submarine.Load("Content/Map/TutorialSub.sub", "", true);
tutorialType.Initialize();
public static void StartTutorial(Tutorial tutorial)
{
tutorial.Initialize();
}
public TutorialMode(GameModePreset preset, object param)
@@ -22,32 +20,18 @@ namespace Barotrauma
public override void Start()
{
base.Start();
tutorialType.Start();
tutorial.Start();
}
public override void AddToGUIUpdateList()
{
tutorialType.AddToGUIUpdateList();
tutorial.AddToGUIUpdateList();
}
public override void Update(float deltaTime)
{
base.Update(deltaTime);
tutorialType.Update(deltaTime);
tutorial.Update(deltaTime);
}
public override void Draw(SpriteBatch spriteBatch)
{
base.Draw(spriteBatch);
//CrewManager.Draw(spriteBatch);
tutorialType.Draw(spriteBatch);
}
}
}
@@ -1,181 +0,0 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
namespace Barotrauma.Tutorials
{
class TutorialType
{
public static List<TutorialType> TutorialTypes;
protected GUIComponent infoBox;
Character character;
public string Name
{
get;
private set;
}
static TutorialType()
{
TutorialTypes = new List<TutorialType>();
TutorialTypes.Add(new BasicTutorial("Basic tutorial"));
}
public TutorialType(string name)
{
this.Name = name;
}
public virtual void Initialize()
{
GameMain.GameSession = new GameSession(Submarine.MainSub, "", GameModePreset.list.Find(gm => gm.Name.ToLowerInvariant() == "tutorial"));
(GameMain.GameSession.GameMode as TutorialMode).tutorialType = this;
GameMain.GameSession.StartRound("tuto");
GameMain.GameSession.EventManager.Events.Clear();
GameMain.GameScreen.Select();
}
public virtual void Start()
{
WayPoint wayPoint = WayPoint.GetRandom(SpawnType.Cargo, null);
if (wayPoint == null)
{
DebugConsole.ThrowError("A waypoint with the spawntype \"cargo\" is required for the tutorial event");
return;
}
CharacterInfo charInfo = new CharacterInfo(Character.HumanConfigFile, "", Gender.None, JobPrefab.List.Find(jp => jp.Name == "Engineer"));
character = Character.Create(charInfo, wayPoint.WorldPosition, false, false);
Character.Controlled = character;
character.GiveJobItems(null);
var idCard = character.Inventory.FindItem("ID Card");
if (idCard == null)
{
DebugConsole.ThrowError("Item prefab \"ID Card\" not found!");
return;
}
idCard.AddTag("com");
idCard.AddTag("eng");
//CoroutineManager.StartCoroutine(QuitChecker());
CoroutineManager.StartCoroutine(UpdateState());
}
public virtual void AddToGUIUpdateList()
{
if (infoBox != null) infoBox.AddToGUIUpdateList();
}
public virtual void Update(float deltaTime)
{
if (character!=null)
{
if (Character.Controlled==null)
{
CoroutineManager.StopCoroutines("TutorialMode.UpdateState");
infoBox = null;
}
else if (Character.Controlled.IsDead)
{
Character.Controlled = null;
CoroutineManager.StopCoroutines("TutorialMode.UpdateState");
infoBox = null;
CoroutineManager.StartCoroutine(Dead());
}
}
//CrewManager.Update(deltaTime);
if (infoBox != null) infoBox.Update(deltaTime);
}
public virtual IEnumerable<object> UpdateState()
{
yield return CoroutineStatus.Success;
}
public virtual void Draw(SpriteBatch spriteBatch)
{
if (infoBox != null) infoBox.Draw(spriteBatch);
}
private IEnumerable<object> Dead()
{
yield return new WaitForSeconds(3.0f);
var messageBox = new GUIMessageBox("You have died", "Do you want to try again?", new string[] { "Yes", "No" });
messageBox.Buttons[0].OnClicked += Restart;
messageBox.Buttons[0].OnClicked += messageBox.Close;
messageBox.Buttons[1].OnClicked = GameMain.MainMenuScreen.SelectTab;
messageBox.Buttons[1].OnClicked += messageBox.Close;
yield return CoroutineStatus.Success;
}
protected bool CloseInfoFrame(GUIButton button, object userData)
{
infoBox = null;
return true;
}
protected GUIComponent CreateInfoFrame(string text, bool hasButton = false)
{
int width = 300;
int height = hasButton ? 110 : 80;
string wrappedText = ToolBox.WrapText(text, width, GUI.Font);
height += wrappedText.Split('\n').Length * 25;
var infoBlock = new GUIFrame(new Rectangle(-20, 20, width, height), null, Alignment.TopRight, "");
//infoBlock.Color = infoBlock.Color * 0.8f;
infoBlock.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f);
infoBlock.Flash(Color.Green);
var textBlock = new GUITextBlock(new Rectangle(10, 10, width - 40, height), text, "", infoBlock, true);
if (hasButton)
{
var okButton = new GUIButton(new Rectangle(0, -40, 80, 25), "OK", Alignment.BottomCenter, "", textBlock);
okButton.OnClicked = CloseInfoFrame;
}
GUI.PlayUISound(GUISoundType.Message);
return infoBlock;
}
private bool Restart(GUIButton button, object obj)
{
TutorialMode.StartTutorial(this);
return true;
}
}
}