@@ -273,14 +273,15 @@ public static void ExecuteCommand(string command, GameMain game)
}
break;
case "tutorial":
TutorialMode.Start();
TutorialMode.StartTutorial();
break;
case "lobbyscreen":
case "lobby":
GameMain.LobbyScreen.Select();
break;
case "savemap":
case "savesub":
case "save":
if (commands.Length < 2) break;

string fileName = string.Join(" ", commands.Skip(1));
@@ -294,6 +295,7 @@ public static void ExecuteCommand(string command, GameMain game)
break;
case "loadmap":
case "loadsub":
case "load":
if (commands.Length < 2) break;
Submarine.Load(string.Join(" ", commands.Skip(1)));
break;
@@ -37,7 +37,7 @@ public SalvageQuest(XElement element)

public override void Start(Level level)
{
Vector2 position = level.PositionsOfInterest[Rand.Int(level.PositionsOfInterest.Count)];
Vector2 position = level.PositionsOfInterest[Rand.Int(level.PositionsOfInterest.Count, false)];

item = new Item(itemPrefab, position + level.Position);
item.MoveWithLevel = true;
@@ -268,6 +268,8 @@ protected override void Update(GameTime gameTime)

if (hasLoaded && !titleScreenOpen)
{
AmbientSoundManager.Update();

if (PlayerInput.KeyHit(Keys.Escape)) GUI.TogglePauseMenu();

DebugConsole.Update(this, (float)deltaTime);
@@ -10,14 +10,10 @@ class GameMode
{
public static List<GameModePreset> PresetList = new List<GameModePreset>();

TimeSpan duration;
protected DateTime startTime;
protected DateTime endTime;

//public readonly bool IsSinglePlayer;

private GUIProgressBar timerBar;

protected bool isRunning;

//protected string name;
@@ -31,16 +27,6 @@ public virtual Quest Quest
get { return null; }
}

public DateTime StartTime
{
get { return startTime; }
}

public DateTime EndTime
{
get { return endTime; }
}

public bool IsRunning
{
get { return isRunning; }
@@ -68,19 +54,19 @@ public GameMode(GameModePreset preset)

public virtual void Draw(SpriteBatch spriteBatch)
{
if (timerBar != null) timerBar.Draw(spriteBatch);
//if (timerBar != null) timerBar.Draw(spriteBatch);
}

public virtual void Start(TimeSpan duration)
public virtual void Start()
{
startTime = DateTime.Now;
if (duration!=TimeSpan.Zero)
{
endTime = startTime + duration;
this.duration = duration;
//if (duration!=TimeSpan.Zero)
//{
// endTime = startTime + duration;
// this.duration = duration;

timerBar = new GUIProgressBar(new Rectangle(GameMain.GraphicsWidth - 120, 20, 100, 25), Color.Gold, 0.0f, null);
}
// timerBar = new GUIProgressBar(new Rectangle(GameMain.GraphicsWidth - 120, 20, 100, 25), Color.Gold, 0.0f, null);
//}

endMessage = "The round has ended!";

@@ -89,13 +75,13 @@ public virtual void Start(TimeSpan duration)

public virtual void Update(float deltaTime)
{
if (!isRunning) return;
//if (!isRunning) return;

if (duration != TimeSpan.Zero)
{
double elapsedTime = (DateTime.Now - startTime).TotalSeconds;
timerBar.BarSize = (float)(elapsedTime / duration.TotalSeconds);
}
//if (duration != TimeSpan.Zero)
//{
// double elapsedTime = (DateTime.Now - startTime).TotalSeconds;
// timerBar.BarSize = (float)(elapsedTime / duration.TotalSeconds);
//}
//if (DateTime.Now >= endTime)
//{
// End(endMessage);
@@ -32,9 +32,9 @@ public QuestMode(GameModePreset preset)
quest = Quest.LoadRandom(locations, rand);
}

public override void Start(TimeSpan duration)
public override void Start()
{
base.Start(duration);
base.Start();

new GUIMessageBox(quest.Name, quest.Description, 400, 400);

@@ -88,14 +88,16 @@ public SinglePlayerMode(XElement element)

CrewManager = new CrewManager(subElement);
}

savedOnStart = true;
}

public void GenerateMap(string seed)
{
Map = new Map(seed, 500);
}

public override void Start(TimeSpan duration)
public override void Start()
{
CargoManager.CreateItems();

@@ -181,11 +183,13 @@ public override void End(string endMessage = "")

isRunning = false;

GameMain.GameSession.EndShift("");

//if (endMessage != "" || this.endMessage == null) this.endMessage = endMessage;


StringBuilder sb = new StringBuilder();
List<Character> casualties = CrewManager.characters.FindAll(c => c.IsDead);
List<Character> casualties = CrewManager.characters.FindAll(c => c.IsDead);

if (casualties.Count == CrewManager.characters.Count)
{
@@ -226,8 +230,7 @@ public override void End(string endMessage = "")
Character.CharacterList[i].Remove();
}

GameMain.GameSession.EndShift("");

Submarine.Unload();
}

private bool EndShift(GUIButton button, object obj)
@@ -15,9 +15,9 @@ public TraitorMode(GameModePreset preset)

}

public override void Start(TimeSpan duration)
public override void Start()
{
base.Start(duration);
base.Start();

traitor = null;
target = null;
@@ -56,7 +56,7 @@ public override void Update(float deltaTime)
{
string endMessage = traitor.character.Info.Name + " was a traitor! ";
endMessage += (traitor.character.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + target.character.Info.Name + ". The task was succesful.";
endMessage += " task was to assassinate " + target.character.Info.Name + ". The task was successful.";
End(endMessage);
}
else if (traitor.character.IsDead)
@@ -78,14 +78,6 @@ public override void Update(float deltaTime)
endMessage += "The task was unsuccessful - the has submarine reached its destination.";
End(endMessage);
return;
}
else if (DateTime.Now >= endTime)
{
string endMessage = traitor.character.Info.Name + " was a traitor! ";
endMessage += (traitor.character.Info.Gender == Gender.Male) ? "His" : "Her";
endMessage += " task was to assassinate " + target.character.Info.Name + ". The task was unsuccesful.";
End(endMessage);
return;
}

}
@@ -15,13 +15,13 @@ class TutorialMode : GameMode

private GUIComponent infoBox;

public static void Start()
public static void StartTutorial()
{
Submarine.Load("Content/Map/TutorialSub.sub", "");

GameMain.GameSession = new GameSession(Submarine.Loaded, "", GameModePreset.list.Find(gm => gm.Name.ToLower()=="tutorial"));

GameMain.GameSession.StartShift(TimeSpan.Zero, "tutorial");
GameMain.GameSession.StartShift("tutorial");

GameMain.GameSession.taskManager.Tasks.Clear();

@@ -34,9 +34,9 @@ public TutorialMode(GameModePreset preset)
CrewManager = new CrewManager();
}

public override void Start(TimeSpan duration)
public override void Start()
{
base.Start(duration);
base.Start();

WayPoint wayPoint = WayPoint.GetRandom(SpawnType.Cargo, null);
if (wayPoint==null)
@@ -273,8 +273,15 @@ private IEnumerable<object> UpdateState()
yield return new WaitForSeconds(2.0f);

infoBox = CreateInfoFrame("You can now move the other end of the wire around, and attach it on the wall by left clicking or "
+ "remove the previous attachment by right clicking. Or you can just run the wire straight to the junction box and attach it "
+ " the same way you did to the navigation terminal.");
+ "remove the previous attachment by right clicking. Or if you don't care for neatly laid out wiring, you can just "
+"run it straight to the junction box.");

while (Character.Controlled.SelectedConstruction == null || Character.Controlled.SelectedConstruction.GetComponent<PowerTransfer>()==null)
{
yield return CoroutineStatus.Running;
}

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)
{
@@ -346,10 +353,10 @@ private IEnumerable<object> UpdateState()
Vector2 steeringDir = windows[0].Position - moloch.Position;
if (steeringDir != Vector2.Zero) steeringDir = Vector2.Normalize(steeringDir);

foreach (Limb limb in moloch.AnimController.Limbs)
{
limb.body.LinearVelocity = new Vector2(limb.LinearVelocity.X*2.0f, limb.LinearVelocity.Y + steeringDir.Y*10.0f);
}
//foreach (Limb limb in moloch.AnimController.Limbs)
//{
// limb.body.LinearVelocity = new Vector2(limb.LinearVelocity.X*2.0f, limb.LinearVelocity.Y + steeringDir.Y*10.0f);
//}

moloch.AIController.Steering = steeringDir;

@@ -365,11 +372,12 @@ private IEnumerable<object> UpdateState()
}


yield return new WaitForSeconds(1.0f);
yield return new WaitForSeconds(0.1f);
} while (!broken);

yield return new WaitForSeconds(0.5f);


Submarine.Loaded.GodMode = true;

var capacitor1 = Item.itemList.Find(i => i.HasTag("capacitor1")).GetComponent<PowerContainer>();
var capacitor2 = Item.itemList.Find(i => i.HasTag("capacitor1")).GetComponent<PowerContainer>();
@@ -470,6 +478,8 @@ private IEnumerable<object> UpdateState()
yield return CoroutineStatus.Running;
}

Submarine.Loaded.GodMode = false;

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.");

@@ -584,7 +594,7 @@ private IEnumerable<object> Dead()

private bool Restart(GUIButton button, object obj)
{
TutorialMode.Start();
TutorialMode.StartTutorial();

return true;
}
@@ -98,14 +98,14 @@ public GameSession(Submarine selectedSub, string saveFile, string filePath)
}
}

public void StartShift(TimeSpan duration, string levelSeed)
public void StartShift(string levelSeed)
{
Level level = Level.CreateRandom(levelSeed);

StartShift(duration, level);
StartShift(level);
}

public void StartShift(TimeSpan duration, Level level, bool reloadSub = true)
public void StartShift(Level level, bool reloadSub = true)
{
GameMain.LightManager.LosEnabled = (GameMain.Server==null || GameMain.Server.CharacterInfo!=null);

@@ -129,7 +129,7 @@ public void StartShift(TimeSpan duration, Level level, bool reloadSub = true)

if (Quest!=null) Quest.Start(Level.Loaded);

if (gameMode!=null) gameMode.Start(duration);
if (gameMode!=null) gameMode.Start();

taskManager.StartShift(level);
}
@@ -145,7 +145,7 @@ public void EndShift(string endMessage)
}
else if (GameMain.Client==null)
{
Submarine.Unload();
//Submarine.Unload();
GameMain.LobbyScreen.Select();
}

@@ -1,4 +1,5 @@
using System;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -73,10 +74,16 @@ public void Load(string filePath)
}

XElement graphicsMode = doc.Root.Element("graphicsmode");
GraphicsWidth = int.Parse(graphicsMode.Attribute("width").Value);
GraphicsHeight = int.Parse(graphicsMode.Attribute("height").Value);

FullScreenEnabled = graphicsMode.Attribute("fullscreen").Value == "true";
GraphicsWidth = ToolBox.GetAttributeInt(graphicsMode, "width", 0);
GraphicsHeight = ToolBox.GetAttributeInt(graphicsMode, "height", 0);

if (GraphicsWidth==0 || GraphicsHeight==0)
{
GraphicsWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
GraphicsHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
}

FullScreenEnabled = ToolBox.GetAttributeBool(graphicsMode, "fullscreen", true);

MasterServerUrl = ToolBox.GetAttributeString(doc.Root, "masterserverurl", "");

@@ -155,7 +155,12 @@ public override bool TryPutItem(Item item, int i, bool createNetworkEvent)
if (items[i].Combine(item))
{
//PutItem(items[i], i, false, false);
new Networking.NetworkEvent(Networking.NetworkEventType.InventoryUpdate, items[i].ID, true);
Inventory otherInventory = items[i].inventory;
if (otherInventory!=null)
{
new Networking.NetworkEvent(Networking.NetworkEventType.InventoryUpdate, otherInventory.ID, true);
}

combined = true;
}

@@ -2,6 +2,7 @@
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using Microsoft.Xna.Framework;
using System;
using System.Xml.Linq;

namespace Barotrauma.Items.Components
@@ -18,13 +19,24 @@ class MeleeWeapon : Holdable

private Character user;

private float reload;

private float reloadTimer;

[HasDefaultValue(0.0f, false)]
public float Range
{
get { return ConvertUnits.ToDisplayUnits(range); }
set { range = ConvertUnits.ToSimUnits(value); }
}

[HasDefaultValue(0.5f, false)]
public float Reload
{
get { return reload; }
set { reload = Math.Max(0.0f, value); }
}

public MeleeWeapon(Item item, XElement element)
: base(item, element)
{
@@ -35,22 +47,19 @@ public MeleeWeapon(Item item, XElement element)
if (subElement.Name.ToString().ToLower() != "attack") continue;
attack = new Attack(subElement);
}

if (attack==null)
{
DebugConsole.ThrowError("Item ''"+item.Name+"'' doesn't have an attack configured");
}
}

public override bool Use(float deltaTime, Character character = null)
{
if (character == null) return false;
if (character == null || reloadTimer>0.0f) return false;
if (!character.GetInputState(InputType.SecondaryHeld) || hitting) return false;

user = character;

if (hitPos < MathHelper.Pi * 0.69f) return false;

reloadTimer = reload;

item.body.FarseerBody.CollisionCategories = Physics.CollisionProjectile;
item.body.FarseerBody.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall;
item.body.FarseerBody.OnCollision += OnCollision;
@@ -95,6 +104,8 @@ public override void Update(float deltaTime, Camera cam)
if (!item.body.Enabled) return;
if (!picker.HasSelectedItem(item)) IsActive = false;

reloadTimer -= deltaTime;

if (!picker.GetInputState(InputType.SecondaryHeld) && !hitting) hitPos = 0.0f;

ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
@@ -117,8 +128,6 @@ public override void Update(float deltaTime, Camera cam)
{
ac.HoldItem(deltaTime, item, handlePos, new Vector2(hitPos, 0.0f), aimPos, false, 0.0f);
}


}
else
{
@@ -174,20 +183,24 @@ private bool OnCollision(Fixture f1, Fixture f2, Contact contact)
if (limb.character == picker) return false;
target = limb.character;
}
else
{
return false;
}

if (target==null)
if (target == null)
{
target = f2.Body.UserData as IDamageable;
}

if (target == null) return false;

attack.DoDamage(user, target, item.Position, 1.0f);
if (attack!=null) attack.DoDamage(user, target, item.Position, 1.0f);

RestoreCollision();
hitting = false;

ApplyStatusEffects(ActionType.OnUse, 1.0f, picker);
ApplyStatusEffects(ActionType.OnUse, 1.0f, limb.character);

return true;
}
@@ -20,15 +20,15 @@ class Engine : Powered

float powerPerForce;

[Editable, HasDefaultValue(1.0f, true)]
public float PowerPerForce
{
get { return powerPerForce; }
set
{
powerPerForce = Math.Max(0.0f, value);
}
}
//[Editable, HasDefaultValue(1.0f, true)]
//public float PowerPerForce
//{
// get { return powerPerForce; }
// set
// {
// powerPerForce = Math.Max(0.0f, value);
// }
//}

[Editable, HasDefaultValue(2000.0f, true)]
public float MaxForce
@@ -61,7 +61,7 @@ public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);

currPowerConsumption = Math.Abs(targetForce) * powerPerForce;
currPowerConsumption = Math.Abs(targetForce)/100.0f * powerConsumption;

Force = MathHelper.Lerp(force, (voltage < minVoltage) ? 0.0f : targetForce, 0.1f);
if (Force != 0.0f)
@@ -14,10 +14,14 @@ public MiniMap(Item item, XElement element)
IsActive = true;
}

bool hasPower;

public override void Update(float deltaTime, Camera cam)
{
currPowerConsumption = powerConsumption;


hasPower = voltage > minVoltage;

voltage = 0.0f;
}

@@ -38,6 +42,8 @@ public override void DrawHUD(SpriteBatch spriteBatch, Character character)

GuiFrame.Draw(spriteBatch);

if (!hasPower) return;

//GUI.DrawRectangle(spriteBatch, new Rectangle(x,y,width,height), Color.Black, true);

Rectangle miniMap = new Rectangle(x + 20, y + 40, width - 40, height - 60);
@@ -1,4 +1,5 @@
using System;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;

@@ -12,6 +13,8 @@ class OxygenGenerator : Powered

bool running;

private float generatedAmount;

List<Vent> ventList;

public bool IsRunning()
@@ -25,6 +28,13 @@ public float CurrFlow
private set;
}

[Editable, HasDefaultValue(100.0f, true)]
public float GeneratedAmount
{
get { return generatedAmount; }
set { generatedAmount = MathHelper.Clamp(value, -10000.0f, 10000.0f); }
}

public OxygenGenerator(Item item, XElement element)
: base(item, element)
{
@@ -64,7 +74,7 @@ public override void Update(float deltaTime, Camera cam)

running = true;

CurrFlow = Math.Min(voltage, 1.0f) * 50000.0f;
CurrFlow = Math.Min(voltage, 1.0f) * generatedAmount * 1000.0f;
item.CurrentHull.Oxygen += CurrFlow * deltaTime;

UpdateVents(CurrFlow);
@@ -816,8 +816,11 @@ public static Item FindPickable(Vector2 position, Vector2 pickPosition, Hull hul
if (item.prefab.PickDistance == 0.0f) continue;
if (Vector2.Distance(position, item.SimPosition) > item.prefab.PickDistance) continue;

Body body = Submarine.CheckVisibility(position, item.SimPosition);
if (body != null && body.UserData as Item != item) continue;
if (!item.prefab.PickThroughWalls)
{
Body body = Submarine.CheckVisibility(position, item.SimPosition);
if (body != null && body.UserData as Item != item) continue;
}

dist = Vector2.Distance(pickPosition, item.SimPosition);
if (dist < item.prefab.PickDistance && (closest == null || dist < closestDist))
@@ -25,6 +25,8 @@ class ItemPrefab : MapEntityPrefab
//how close the character has to be to the item to pick it up
private float pickDistance;

private bool pickThroughWalls;

//an area next to the construction
//the construction can be Activated() by a character inside the area
public List<Rectangle> Triggers;
@@ -39,6 +41,11 @@ public float PickDistance
get { return pickDistance; }
}

public bool PickThroughWalls
{
get { return pickThroughWalls; }
}


public override bool IsLinkable
{
@@ -146,6 +153,7 @@ public ItemPrefab (XElement element, string filePath)
name = ToolBox.GetAttributeString(element, "name", "");
if (name == "") DebugConsole.ThrowError("Unnamed item in "+filePath+"!");

pickThroughWalls = ToolBox.GetAttributeBool(element, "pickthroughwalls", false);
pickDistance = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "pickdistance", 0.0f));

isLinkable = ToolBox.GetAttributeBool(element, "linkable", false);
@@ -71,6 +71,42 @@ public RelatedItem(string[] names)
statusEffects = new List<StatusEffect>();
}

public bool CheckRequirements(Character character, Item parentItem)
{
switch (type)
{
case RelationType.Contained:
if (parentItem == null) return false;
foreach (Item contained in parentItem.ContainedItems)
{
if (contained.Condition>0.0f && MatchesItem(contained)) return true;
}
break;
case RelationType.Equipped:
if (character == null) return false;
foreach (Item equippedItem in character.SelectedItems)
{
if (equippedItem == null) continue;

if (equippedItem.Condition>0.0f && MatchesItem(equippedItem)) return true;
}
break;
case RelationType.Picked:
if (character == null || character.Inventory==null) return false;
foreach (Item pickedItem in character.Inventory.items)
{
if (pickedItem == null) continue;

if (MatchesItem(pickedItem)) return true;
}
break;
default:
return true;
}

return false;
}

public void Save(XElement element)
{
element.Add(
@@ -745,7 +745,7 @@ public void Move(Vector2 amount)
}

AtStartPosition = Vector2.Distance(startPosition, -Position) < ExitDistance;
AtEndPosition = Vector2.Distance(endPosition, -Position) < ExitDistance;
AtEndPosition = Vector2.Distance(endPosition, -Position) < ExitDistance;

prevVelocity = simVelocity;
}
@@ -423,6 +423,7 @@ public Vector2 SectionPosition(int sectionIndex)

public AttackResult AddDamage(IDamageable attacker, Vector2 position, Attack attack, float deltaTime, bool playSound = false)
{
if (Submarine.Loaded != null && Submarine.Loaded.GodMode) return new AttackResult(0.0f, 0.0f);
if (!prefab.HasBody || prefab.IsPlatform) return new AttackResult(0.0f, 0.0f);

int i = FindSectionIndex(ConvertUnits.ToDisplayUnits(position));
@@ -445,13 +446,13 @@ public AttackResult AddDamage(IDamageable attacker, Vector2 position, Attack att

private void SetDamage(int sectionIndex, float damage)
{
if (Submarine.Loaded != null && Submarine.Loaded.GodMode) return;
if (!prefab.HasBody) return;

if (damage != sections[sectionIndex].damage && Math.Abs(sections[sectionIndex].lastSentDamage - damage)>5.0f)
{
new NetworkEvent(NetworkEventType.UpdateEntity, ID, false, sectionIndex);
new NetworkEvent(NetworkEventType.WallDamage, ID, false, sectionIndex);
sections[sectionIndex].lastSentDamage = damage;

}

if (damage < prefab.MaxHealth*0.5f)
@@ -61,6 +61,12 @@ public static float LastPickedFraction
get { return lastPickedFraction; }
}

public bool GodMode
{
get;
set;
}

public Md5Hash MD5Hash
{
get
@@ -247,12 +247,15 @@ private IEnumerable<object> WaitForStartingInfo()
if (connectionStatus != NetConnectionStatus.Connected)
{
string denyMessage = inc.ReadString();
DebugConsole.ThrowError(denyMessage);

new GUIMessageBox("Couldn't connect to server", denyMessage);
connectCanceled = true;
}

break;
default:
Console.WriteLine(inc.ReadString() + " Strange message");
connectCanceled = true;
break;
}
}
@@ -457,17 +460,14 @@ private IEnumerable<object> StartGame(NetIncomingMessage inc)

yield return CoroutineStatus.Running;

double durationMinutes = inc.ReadDouble();

TimeSpan duration = new TimeSpan(0, (int)durationMinutes, 0);
Rand.SetSyncedSeed(seed);
//int gameModeIndex = inc.ReadInt32();

GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedMap, "", gameMode);

yield return CoroutineStatus.Running;

GameMain.GameSession.StartShift(duration, levelSeed);
GameMain.GameSession.StartShift(levelSeed);

yield return CoroutineStatus.Running;

@@ -501,9 +501,10 @@ private IEnumerable<object> StartGame(NetIncomingMessage inc)

public IEnumerable<object> EndGame(string endMessage)
{
var messageBox = new GUIMessageBox("The round has ended", endMessage, 400, 300);

if (!gameStarted) yield return CoroutineStatus.Success;
gameStarted = false;

var messageBox = new GUIMessageBox("The round has ended", endMessage);

Character.Controlled = null;
GameMain.LightManager.LosEnabled = false;
@@ -529,7 +530,7 @@ public IEnumerable<object> EndGame(string endMessage)
yield return CoroutineStatus.Running;
} while (secondsLeft > 0.0f);

messageBox.Text = endMessage;
messageBox.Close(null,null);

Submarine.Unload();

@@ -601,7 +601,7 @@ private IEnumerable<object> StartGame(Submarine selectedSub)
int seed = DateTime.Now.Millisecond;
Rand.SetSyncedSeed(seed);
GameMain.GameSession = new GameSession(selectedSub, "", GameMain.NetLobbyScreen.SelectedMode);
GameMain.GameSession.StartShift(GameMain.NetLobbyScreen.GameDuration, GameMain.NetLobbyScreen.LevelSeed);
GameMain.GameSession.StartShift(GameMain.NetLobbyScreen.LevelSeed);

yield return CoroutineStatus.Running;

@@ -662,7 +662,7 @@ private IEnumerable<object> StartGame(Submarine selectedSub)

msg.Write(GameMain.NetLobbyScreen.SelectedMode.Name);

msg.Write(GameMain.NetLobbyScreen.GameDuration.TotalMinutes);
//msg.Write(GameMain.NetLobbyScreen.GameDuration.TotalMinutes);

msg.Write((myCharacter == null) ? connectedClients.Count : connectedClients.Count + 1);
foreach (Client client in connectedClients)
@@ -703,8 +703,7 @@ private bool EndButtonHit(GUIButton button, object obj)

public IEnumerable<object> EndGame(string endMessage)
{

var messageBox = new GUIMessageBox("The round has ended", endMessage);
var messageBox = new GUIMessageBox("The round has ended", endMessage, 400, 300);

Character.Controlled = null;
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
@@ -754,6 +753,8 @@ public IEnumerable<object> EndGame(string endMessage)

Submarine.Unload();

messageBox.Close(null, null);

GameMain.NetLobbyScreen.Select();

yield return CoroutineStatus.Success;
@@ -1003,17 +1004,18 @@ private void AssignJobs()
}

//share the rest of the jobs according to the ''commonness'' of the job
float totalCommonness = 0.0f;
for (int i = 0; i < JobPrefab.List.Count; i++)
{
if (JobPrefab.List[i].AllowAlways || JobPrefab.List[i].MaxNumber == 0) continue;
//float totalCommonness = 0.0f;
//for (int i = 0; i < JobPrefab.List.Count; i++)
//{
// if (JobPrefab.List[i].AllowAlways || JobPrefab.List[i].MaxNumber == 0) continue;

totalCommonness += JobPrefab.List[i].Commonness;
}
// totalCommonness += JobPrefab.List[i].Commonness;
//}

for (int preferenceIndex = 0; preferenceIndex < 3; preferenceIndex++)
//find a suitable job for the rest of the players
for (int i = unassigned.Count - 1; i >= 0; i--)
{
for (int i = unassigned.Count - 1; i >= 0; i--)
for (int preferenceIndex = 0; preferenceIndex < 3; preferenceIndex++)
{
int jobIndex = JobPrefab.List.FindIndex(jp => jp == unassigned[i].jobPreferences[preferenceIndex]);

@@ -1024,6 +1026,7 @@ private void AssignJobs()

assignedClientCount[jobIndex]++;
unassigned.RemoveAt(i);
break;
}
}

@@ -1038,6 +1041,7 @@ private Client FindClientWithJobPreference(List<Client> clients, JobPrefab job,
foreach (Client c in clients)
{
int index = c.jobPreferences.FindIndex(jp => jp == job);
if (index == -1) index = 1000;
if (preferredClient == null || index < bestPreference)
{
bestPreference = index;
@@ -11,14 +11,17 @@ enum NetworkEventType
DropItem = 3,
InventoryUpdate = 4,
PickItem = 5,
UpdateProperty = 6
UpdateProperty = 6,
WallDamage = 7,
SelectCharacter = 8
}

class NetworkEvent
{
public static List<NetworkEvent> events = new List<NetworkEvent>();

private static bool[] isImportant = { false, true, false, true, true, true, true };
private static bool[] isImportant = { false, true, false, true, true, true, true, true, true };
private static bool[] overridePrevious = { true, false, true, false, false, false, true, true, true };

private int id;

@@ -68,7 +71,7 @@ public NetworkEvent(NetworkEventType type, int id, bool isClient, object data =

eventType = type;

if (!isImportant[(int)type])
if (overridePrevious[(int)type])
{
if (events.Find(e => e.id == id && e.eventType == type) != null) return;
}
@@ -65,8 +65,6 @@ public override void Update(double deltaTime)
//http://gafferongames.com/game-physics/fix-your-timestep/
Physics.accumulator += deltaTime;

AmbientSoundManager.Update();

#if DEBUG
if (GameMain.GameSession != null && GameMain.GameSession.Level != null && GameMain.GameSession.Submarine != null)
{
@@ -447,15 +447,15 @@ private bool HireCharacter(GUIButton button, object selection)
private bool StartShift(GUIButton button, object selection)
{
//GameMain.ShowLoading(ShiftLoading());
GameMain.GameSession.StartShift(TimeSpan.Zero, selectedLevel, false);
GameMain.GameSession.StartShift(selectedLevel, false);
GameMain.GameScreen.Select();

return true;
}

private IEnumerable<object> ShiftLoading()
{
GameMain.GameSession.StartShift(TimeSpan.Zero, selectedLevel, false);
GameMain.GameSession.StartShift(selectedLevel, false);
GameMain.GameScreen.Select();

yield return CoroutineStatus.Success;
@@ -196,7 +196,7 @@ public void SelectTab(Tab tab)

private bool TutorialButtonClicked(GUIButton button, object obj)
{
TutorialMode.Start();
TutorialMode.StartTutorial();

return true;
}
@@ -23,7 +23,7 @@ class NetLobbyScreen : Screen

private GUITextBox textBox, seedBox;

private GUIScrollBar durationBar;
//private GUIScrollBar durationBar;

private GUIFrame playerFrame;

@@ -60,16 +60,7 @@ public string GetServerMessage()
{
return ServerMessage;
}

public TimeSpan GameDuration
{
get
{
int minutes = (int)(durationBar.BarScroll* 60.0f);
return new TimeSpan(0, minutes, 0);
}
}


public List<JobPrefab> JobPreferences
{
get
@@ -100,10 +91,10 @@ private set
}
}

public string DurationText()
{
return "Duration: " + GameDuration.TotalMinutes + " min";
}
//public string DurationText()
//{
// return "Duration: " + GameDuration.TotalMinutes + " min";
//}

public NetLobbyScreen()
{
@@ -214,20 +205,20 @@ public NetLobbyScreen()

//duration ------------------------------------------------------------------

GUITextBlock durationText = new GUITextBlock(new Rectangle(columnX, 120, columnWidth, 20),
"Duration: ", GUI.Style, Alignment.Left, Alignment.TopLeft, infoFrame);
durationText.TextGetter = DurationText;
//GUITextBlock durationText = new GUITextBlock(new Rectangle(columnX, 120, columnWidth, 20),
// "Duration: ", GUI.Style, Alignment.Left, Alignment.TopLeft, infoFrame);
//durationText.TextGetter = DurationText;

durationBar = new GUIScrollBar(new Rectangle(columnX, 150, columnWidth, 20),
GUI.Style, 0.1f, infoFrame);
durationBar.BarSize = 0.1f;
//durationBar = new GUIScrollBar(new Rectangle(columnX, 150, columnWidth, 20),
// GUI.Style, 0.1f, infoFrame);
//durationBar.BarSize = 0.1f;

//seed ------------------------------------------------------------------

new GUITextBlock(new Rectangle(columnX, 190, columnWidth, 20),
new GUITextBlock(new Rectangle(columnX, 120, columnWidth, 20),
"Level Seed: ", GUI.Style, Alignment.Left, Alignment.TopLeft, infoFrame);

seedBox = new GUITextBox(new Rectangle(columnX, 220, columnWidth, 20),
seedBox = new GUITextBox(new Rectangle(columnX, 150, columnWidth, 20),
Alignment.TopLeft, GUI.Style, infoFrame);
seedBox.OnTextChanged = SelectSeed;
LevelSeed = ToolBox.RandomSeed(8);
@@ -262,8 +253,7 @@ public override void Select()
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;

subList.Enabled = GameMain.Server != null;
modeList.Enabled = GameMain.Server != null;
durationBar.Enabled = GameMain.Server != null;
modeList.Enabled = GameMain.Server != null;
seedBox.Enabled = GameMain.Server != null;
serverMessage.Enabled = GameMain.Server != null;
ServerName = (GameMain.Server==null) ? "Server" : GameMain.Server.Name;
@@ -279,8 +269,7 @@ public override void Select()
startButton.UserData = "startButton";

//mapList.OnSelected = new GUIListBox.OnSelectedHandler(Game1.server.UpdateNetLobby);
modeList.OnSelected += GameMain.Server.UpdateNetLobby;
durationBar.OnMoved = GameMain.Server.UpdateNetLobby;
modeList.OnSelected += GameMain.Server.UpdateNetLobby;

if (subList.CountChildren > 0 && subList.Selected == null) subList.Select(-1);
if (GameModePreset.list.Count > 0 && modeList.Selected == null) modeList.Select(-1);
@@ -702,10 +691,10 @@ public void WriteData(NetOutgoingMessage msg)
msg.Write(ServerMessage);

msg.Write(modeList.SelectedIndex-1);
msg.Write(durationBar.BarScroll);
//msg.Write(durationBar.BarScroll);
msg.Write(LevelSeed);

msg.Write((byte)(playerList.CountChildren - 1));
msg.Write((byte)(playerList.CountChildren));
for (int i = 0; i < playerList.CountChildren; i++)
{
string clientName = playerList.children[i].UserData as string;
@@ -721,7 +710,7 @@ public void ReadData(NetIncomingMessage msg)
string mapName="", md5Hash="";

int modeIndex = 0;
float durationScroll = 0.0f;
//float durationScroll = 0.0f;
string levelSeed = "";

try
@@ -734,7 +723,7 @@ public void ReadData(NetIncomingMessage msg)

modeIndex = msg.ReadInt32();

durationScroll = msg.ReadFloat();
//durationScroll = msg.ReadFloat();

levelSeed = msg.ReadString();

@@ -756,7 +745,7 @@ public void ReadData(NetIncomingMessage msg)

modeList.Select(modeIndex);

durationBar.BarScroll = durationScroll;
//durationBar.BarScroll = durationScroll;

LevelSeed = levelSeed;
}
@@ -42,6 +42,7 @@ public ServerListScreen()
Rectangle panelRect = new Rectangle(0, 0, width, height);

menu = new GUIFrame(panelRect, null, Alignment.Center, GUI.Style);
menu.Padding = new Vector4(40.0f, 40.0f, 40.0f, 20.0f);

new GUITextBlock(new Rectangle(0, -25, 0, 30), "Join Server", GUI.Style, Alignment.CenterX, Alignment.CenterX, menu, false, GUI.LargeFont);

@@ -53,7 +54,7 @@ public ServerListScreen()

int middleX = (int)(width * 0.4f);

serverList = new GUIListBox(new Rectangle(middleX,60,0,(int)(height*0.7f)), GUI.Style, menu);
serverList = new GUIListBox(new Rectangle(middleX,60,0,height-160), GUI.Style, menu);
serverList.OnSelected = SelectServer;

float[] columnRelativeX = new float[] { 0.15f, 0.55f, 0.15f, 0.15f };
@@ -161,6 +161,22 @@ public static void Update()
startDrone = null;
}

if (Submarine.Loaded==null)
{


if (waterAmbienceIndexes[0] > 0)
{
SoundManager.Stop(waterAmbienceIndexes[0]);
SoundManager.Stop(waterAmbienceIndexes[1]);

waterAmbienceIndexes[0] = 0;
waterAmbienceIndexes[1] = 0;
}

return;
}

float ambienceVolume = 0.8f;
float lowpassHFGain = 1.0f;
if (Character.Controlled != null)
@@ -529,7 +529,14 @@ void EnsureBuffersFilled()

for (int i = 0; i < tempBuffers.Length; i++)
{
finished |= FillBuffer(stream, tempBuffers[i]);
try
{
finished |= FillBuffer(stream, tempBuffers[i]);
}
catch
{
continue;
}

if (finished)
{
@@ -130,7 +130,7 @@ public static string[] GetSaveFiles()
return files;
}

public static string CreateSavePath(string fileName="save")
public static string CreateSavePath(string fileName="Save")
{
if (!Directory.Exists(SaveFolder))
{
@@ -142,12 +142,12 @@ public static string CreateSavePath(string fileName="save")
string pathWithoutExtension = Path.Combine(SaveFolder, fileName);

int i = 0;
while (File.Exists(pathWithoutExtension + i + extension))
while (File.Exists(pathWithoutExtension + " " + i + extension))
{
i++;
}

return fileName + i;
return fileName + " " + i;
}

public static void CompressStringToFile(string fileName, string value)
@@ -1,4 +1,67 @@

---------------------------------------------------------------------------------------------------------
v0.2
---------------------------------------------------------------------------------------------------------

Multiplayer:
- major optimization and much better lag compensation
- tons of bugfixes that should fix most of the syncing issues
- admins can play on their own server without launching another instance of the game
- setting the job preferences actually does something now
- reconnecting to a server if the connection is temporarily lost works much better now
- proper error messages if UPnP port forwarding fails

Items:
- security guard gear: ballistic vest, helmet & stun baton
- wifi components that can be used for transmitting signals between devices
- wall labels with a configurable text
- a bunch of new sprites and sounds
- wearing a diving suit slows the characters down
- supercapacitors
- attempting to fire the railgun when there are no shells loaded doesn't consume power anymore

Submarine:
- improved version of Aegir
- a new submarine, "Vellamo"
- nuclear reactors overheat much more slowly and there are warning signals for overheating and a remote
shutdown button in both of the default subs
- ambient sounds change according to the speed of the sub
- fixed parts of the submarine getting stuck in the level on collision
- the autopilot is a much better driver now (although it may still crash at very tight spots)

Crew:
- aiming underwater is much easier
- improved humanoid animations
- stunned/dead characters can be dragged
- all characters can now use plasma cutters and welders regardless of their skills, but insufficient
skill level will make them flicker and work much less efficiently
- same for the harpoon gun, anyone can shoot but lower levels will make the gun less accurate
- rewiring devices may cause electric shocks if the electrical engineering level is too low

Creatures:
- some random "prop fish" swimming in the background
- a new hostile creature

Misc:
- a tutorial going through all the basic tasks and game mechanics (more in-depth tutorials will be
added in future versions, including one for making custom subs)
- an auto-updater in the launcher
- the game generates a detailed report if it crashes
- physics optimization (i.e. using simplified physics & animation for off-screen characters and
disabling them entirely if they're far enough)
- lighting optimization (caching the lights/shadows if a light source hasn't moved instead of
recalculating them every frame)
- two new background music tracks
- better looking explosions
- better looking water particle effects
- minor UI improvements
- better UI scaling on different resolutions
- health/oxygen bar improvements and status icons for bleeding and water pressure
- gap-hull connections are visible in the sub editor
- pumps don't have to be manually connected to a hull in the editor anymore, they automatically
empty/fill the hull they're inside


---------------------------------------------------------------------------------------------------------
v0.1.3.2
---------------------------------------------------------------------------------------------------------
BIN +134 KB (120%) Subsurface_Solution.v12.suo
Binary file not shown.