Renamed project folders from Subsurface to Barotrauma
This commit is contained in:
@@ -0,0 +1,289 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
struct LimbPos
|
||||
{
|
||||
public LimbType limbType;
|
||||
public Vector2 position;
|
||||
|
||||
public LimbPos(LimbType limbType, Vector2 position)
|
||||
{
|
||||
this.limbType = limbType;
|
||||
this.position = position;
|
||||
}
|
||||
}
|
||||
|
||||
class Controller : ItemComponent
|
||||
{
|
||||
//where the limbs of the user should be positioned when using the controller
|
||||
private List<LimbPos> limbPositions;
|
||||
|
||||
private Direction dir;
|
||||
|
||||
//the position where the user walks to when using the controller
|
||||
//(relative to the position of the item)
|
||||
private Vector2 userPos;
|
||||
|
||||
private Camera cam;
|
||||
|
||||
private Character character;
|
||||
|
||||
public Vector2 UserPos
|
||||
{
|
||||
get { return userPos; }
|
||||
set { userPos = value; }
|
||||
}
|
||||
|
||||
public Controller(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
limbPositions = new List<LimbPos>();
|
||||
|
||||
userPos = ToolBox.GetAttributeVector2(element, "UserPos", Vector2.Zero);
|
||||
|
||||
Enum.TryParse<Direction>(ToolBox.GetAttributeString(element, "direction", "None"), out dir);
|
||||
|
||||
foreach (XElement el in element.Elements())
|
||||
{
|
||||
if (el.Name != "limbposition") continue;
|
||||
|
||||
LimbPos lp = new LimbPos();
|
||||
|
||||
try
|
||||
{
|
||||
lp.limbType = (LimbType)Enum.Parse(typeof(LimbType), el.Attribute("limb").Value, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + ": " + e.Message, e);
|
||||
}
|
||||
|
||||
lp.position = ToolBox.GetAttributeVector2(el, "position", Vector2.Zero);
|
||||
|
||||
limbPositions.Add(lp);
|
||||
}
|
||||
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
this.cam = cam;
|
||||
|
||||
if (character == null
|
||||
|| character.IsDead
|
||||
|| character.Stun > 0.0f
|
||||
|| character.SelectedConstruction != item
|
||||
|| Vector2.Distance(character.Position, item.Position) > item.PickDistance * 2.0f)
|
||||
{
|
||||
if (character != null)
|
||||
{
|
||||
CancelUsing(character);
|
||||
character = null;
|
||||
}
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
character.AnimController.Anim = AnimController.Animation.UsingConstruction;
|
||||
|
||||
if (userPos != Vector2.Zero)
|
||||
{
|
||||
Vector2 diff = (item.WorldPosition + userPos) - character.WorldPosition;
|
||||
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
if (diff.Length() > 30.0f)
|
||||
{
|
||||
character.AnimController.TargetMovement = Vector2.Clamp(diff*0.01f, -Vector2.One, Vector2.One);
|
||||
character.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AnimController.TargetMovement = Vector2.Zero;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
diff.Y = 0.0f;
|
||||
if (diff != Vector2.Zero && diff.Length() > 10.0f)
|
||||
{
|
||||
character.AnimController.TargetMovement = Vector2.Normalize(diff);
|
||||
character.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
|
||||
return;
|
||||
}
|
||||
character.AnimController.TargetMovement = Vector2.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, character);
|
||||
|
||||
if (limbPositions.Count == 0) return;
|
||||
|
||||
character.AnimController.Anim = AnimController.Animation.UsingConstruction;
|
||||
|
||||
character.AnimController.ResetPullJoints();
|
||||
|
||||
if (dir != 0) character.AnimController.TargetDir = dir;
|
||||
|
||||
foreach (LimbPos lb in limbPositions)
|
||||
{
|
||||
Limb limb = character.AnimController.GetLimb(lb.limbType);
|
||||
if (limb == null) continue;
|
||||
|
||||
limb.Disabled = true;
|
||||
|
||||
if (limb.pullJoint == null) continue;
|
||||
|
||||
Vector2 position = ConvertUnits.ToSimUnits(lb.position + new Vector2(item.Rect.X, item.Rect.Y));
|
||||
limb.pullJoint.Enabled = true;
|
||||
limb.pullJoint.WorldAnchorB = position;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character activator = null)
|
||||
{
|
||||
if (character == null || activator != character || character.SelectedConstruction != item)
|
||||
{
|
||||
character = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
item.SendSignal(0, "1", "trigger_out", character);
|
||||
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, activator);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
if (this.character == null || this.character != character || this.character.SelectedConstruction != item)
|
||||
{
|
||||
character = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Entity focusTarget = null;
|
||||
|
||||
if (character == null) return;
|
||||
|
||||
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
if (c.Name != "position_out") continue;
|
||||
|
||||
foreach (Connection c2 in c.Recipients)
|
||||
{
|
||||
if (c2 == null || c2.Item == null || !c2.Item.Prefab.FocusOnSelected) continue;
|
||||
|
||||
focusTarget = c2.Item;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (focusTarget == null)
|
||||
{
|
||||
item.SendSignal(0, ToolBox.Vector2ToString(character.CursorWorldPosition), "position_out", character);
|
||||
return;
|
||||
}
|
||||
|
||||
character.ViewTarget = focusTarget;
|
||||
if (character == Character.Controlled && cam != null)
|
||||
{
|
||||
Lights.LightManager.ViewTarget = focusTarget;
|
||||
cam.TargetPos = focusTarget.WorldPosition;
|
||||
|
||||
cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, (focusTarget as Item).Prefab.OffsetOnSelected, deltaTime*10.0f);
|
||||
}
|
||||
|
||||
if (!character.IsRemotePlayer || character.ViewTarget == focusTarget)
|
||||
{
|
||||
item.SendSignal(0, ToolBox.Vector2ToString(character.CursorWorldPosition), "position_out", character);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
item.SendSignal(0, "1", "signal_out", picker);
|
||||
|
||||
PlaySound(ActionType.OnUse, item.WorldPosition);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CancelUsing(Character character)
|
||||
{
|
||||
foreach (LimbPos lb in limbPositions)
|
||||
{
|
||||
Limb limb = character.AnimController.GetLimb(lb.limbType);
|
||||
if (limb == null) continue;
|
||||
|
||||
limb.Disabled = false;
|
||||
|
||||
limb.pullJoint.Enabled = false;
|
||||
}
|
||||
|
||||
if (character.SelectedConstruction == this.item) character.SelectedConstruction = null;
|
||||
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
}
|
||||
|
||||
public override bool Select(Character activator)
|
||||
{
|
||||
if (activator == null) return false;
|
||||
|
||||
//someone already using the item
|
||||
if (character != null)
|
||||
{
|
||||
if (character == activator)
|
||||
{
|
||||
IsActive = false;
|
||||
CancelUsing(character);
|
||||
character = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
character = activator;
|
||||
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
item.SendSignal(0, "1", "signal_out", character);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void FlipX()
|
||||
{
|
||||
if (dir != Direction.None)
|
||||
{
|
||||
dir = dir == Direction.Left ? Direction.Right : Direction.Left;
|
||||
}
|
||||
|
||||
userPos.X = -UserPos.X;
|
||||
|
||||
for (int i = 0; i < limbPositions.Count; i++)
|
||||
{
|
||||
float diff = (item.Rect.X + limbPositions[i].position.X) - item.Rect.Center.X;
|
||||
|
||||
Vector2 flippedPos =
|
||||
new Vector2(
|
||||
item.Rect.Center.X - diff - item.Rect.X,
|
||||
limbPositions[i].position.Y);
|
||||
|
||||
limbPositions[i] = new LimbPos(limbPositions[i].limbType, flippedPos);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
using Barotrauma.Networking;
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class Deconstructor : Powered, IServerSerializable, IClientSerializable
|
||||
{
|
||||
GUIProgressBar progressBar;
|
||||
GUIButton activateButton;
|
||||
|
||||
float progressTimer;
|
||||
|
||||
ItemContainer container;
|
||||
|
||||
public Deconstructor(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
progressBar = new GUIProgressBar(new Rectangle(0,0,200,20), Color.Green, "", 0.0f, Alignment.BottomCenter, GuiFrame);
|
||||
|
||||
activateButton = new GUIButton(new Rectangle(0, 0, 200, 20), "Deconstruct", Alignment.TopCenter, "", GuiFrame);
|
||||
activateButton.OnClicked = ToggleActive;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (container == null || container.Inventory.Items.All(i => i == null))
|
||||
{
|
||||
SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (voltage < minVoltage) return;
|
||||
|
||||
if (powerConsumption == 0.0f) voltage = 1.0f;
|
||||
|
||||
progressTimer += deltaTime*voltage;
|
||||
Voltage -= deltaTime * 10.0f;
|
||||
|
||||
var targetItem = container.Inventory.Items.FirstOrDefault(i => i != null);
|
||||
progressBar.BarSize = Math.Min(progressTimer / targetItem.Prefab.DeconstructTime, 1.0f);
|
||||
if (progressTimer>targetItem.Prefab.DeconstructTime)
|
||||
{
|
||||
var containers = item.GetComponents<ItemContainer>();
|
||||
if (containers.Count < 2)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Deconstructor.Update: Deconstructors must have two ItemContainer components!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (DeconstructItem deconstructProduct in targetItem.Prefab.DeconstructItems)
|
||||
{
|
||||
if (deconstructProduct.RequireFullCondition && targetItem.Condition < 100.0f) continue;
|
||||
|
||||
var itemPrefab = MapEntityPrefab.list.FirstOrDefault(ip => ip.Name.ToLowerInvariant() == deconstructProduct.ItemPrefabName.ToLowerInvariant()) as ItemPrefab;
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Tried to deconstruct item \"" + targetItem.Name + "\" but couldn't find item prefab \"" + deconstructProduct + "\"!");
|
||||
continue;
|
||||
}
|
||||
|
||||
//container full, drop the items outside the deconstructor
|
||||
if (containers[1].Inventory.Items.All(i => i != null))
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(itemPrefab, item.Position, item.Submarine);
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(itemPrefab, containers[1].Inventory);
|
||||
}
|
||||
}
|
||||
|
||||
container.Inventory.RemoveItem(targetItem);
|
||||
Entity.Spawner.AddToRemoveQueue(targetItem);
|
||||
|
||||
if (container.Inventory.Items.Any(i => i != null))
|
||||
{
|
||||
progressTimer = 0.0f;
|
||||
progressBar.BarSize = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
GuiFrame.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character)
|
||||
{
|
||||
GuiFrame.Update((float)Timing.Step);
|
||||
}
|
||||
|
||||
private bool ToggleActive(GUIButton button, object obj)
|
||||
{
|
||||
SetActive(!IsActive, Character.Controlled);
|
||||
|
||||
currPowerConsumption = IsActive ? powerConsumption : 0.0f;
|
||||
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
else if (GameMain.Client != null)
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetActive(bool active, Character user = null)
|
||||
{
|
||||
container = item.GetComponent<ItemContainer>();
|
||||
if (container == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Deconstructor.Activate: Deconstructors must have two ItemContainer components");
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.Inventory.Items.All(i => i == null)) active = false;
|
||||
|
||||
IsActive = active;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
GameServer.Log(user.Name + (IsActive ? " activated " : " deactivated ") + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
|
||||
if (!IsActive)
|
||||
{
|
||||
progressBar.BarSize = 0.0f;
|
||||
progressTimer = 0.0f;
|
||||
|
||||
activateButton.Text = "Deconstruct";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
activateButton.Text = "Cancel";
|
||||
}
|
||||
|
||||
container.Inventory.Locked = IsActive;
|
||||
}
|
||||
|
||||
|
||||
public void ClientWrite(NetBuffer msg, object[] extraData = null)
|
||||
{
|
||||
msg.Write(IsActive);
|
||||
}
|
||||
|
||||
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
|
||||
{
|
||||
bool active = msg.ReadBoolean();
|
||||
|
||||
item.CreateServerEvent(this);
|
||||
|
||||
if (item.CanClientAccess(c))
|
||||
{
|
||||
SetActive(active, c.Character);
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(IsActive);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
|
||||
{
|
||||
SetActive(msg.ReadBoolean());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class Engine : Powered
|
||||
{
|
||||
|
||||
private float force;
|
||||
|
||||
private float targetForce;
|
||||
|
||||
private float maxForce;
|
||||
|
||||
//[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
|
||||
{
|
||||
get { return maxForce; }
|
||||
set
|
||||
{
|
||||
maxForce = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
public float Force
|
||||
{
|
||||
get { return force;}
|
||||
set { force = MathHelper.Clamp(value, -100.0f, 100.0f); }
|
||||
}
|
||||
|
||||
public Engine(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
var button = new GUIButton(new Rectangle(160, 50, 30, 30), "-", "", GuiFrame);
|
||||
button.OnClicked = (GUIButton btn, object obj) =>
|
||||
{
|
||||
targetForce -= 1.0f;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
button = new GUIButton(new Rectangle(200, 50, 30, 30), "+", "", GuiFrame);
|
||||
button.OnClicked = (GUIButton btn, object obj) =>
|
||||
{
|
||||
targetForce += 1.0f;
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
public float CurrentVolume
|
||||
{
|
||||
get { return Math.Abs((force / 100.0f) * (voltage / minVoltage)); }
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
currPowerConsumption = Math.Abs(targetForce)/100.0f * powerConsumption;
|
||||
|
||||
if (powerConsumption == 0.0f) voltage = 1.0f;
|
||||
|
||||
Force = MathHelper.Lerp(force, (voltage < minVoltage) ? 0.0f : targetForce, 0.1f);
|
||||
if (Math.Abs(Force) > 1.0f)
|
||||
{
|
||||
Vector2 currForce = new Vector2((force / 100.0f) * maxForce * (voltage / minVoltage), 0.0f);
|
||||
|
||||
item.Submarine.ApplyForce(currForce);
|
||||
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
item.CurrentHull.AiTarget.SoundRange = Math.Max(currForce.Length(), item.CurrentHull.AiTarget.SoundRange);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("bubbles", item.WorldPosition - (Vector2.UnitX * item.Rect.Width/2),
|
||||
-currForce / 5.0f + new Vector2(Rand.Range(-100.0f, 100.0f), Rand.Range(-50f, 50f)),
|
||||
0.0f, item.CurrentHull);
|
||||
}
|
||||
}
|
||||
|
||||
voltage = 0.0f;
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
//isActive = true;
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
//int width = 300, height = 300;
|
||||
//int x = Game1.GraphicsWidth / 2 - width / 2;
|
||||
//int y = Game1.GraphicsHeight / 2 - height / 2 - 50;
|
||||
|
||||
//GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black, true);
|
||||
|
||||
GUI.Font.DrawString(spriteBatch, "Force: " + (int)(targetForce) + " %", new Vector2(GuiFrame.Rect.X + 30, GuiFrame.Rect.Y + 30), Color.White);
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
GuiFrame.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character)
|
||||
{
|
||||
GuiFrame.Update(1.0f / 60.0f);
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
force = MathHelper.Lerp(force, 0.0f, 0.1f);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power=0.0f)
|
||||
{
|
||||
base.ReceiveSignal(stepsTaken, signal, connection, source, sender, power);
|
||||
|
||||
if (connection.Name == "set_force")
|
||||
{
|
||||
float tempForce;
|
||||
if (float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out tempForce))
|
||||
{
|
||||
targetForce = MathHelper.Clamp(tempForce, -100.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,522 @@
|
||||
using Barotrauma.Networking;
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class FabricableItem
|
||||
{
|
||||
public readonly ItemPrefab TargetItem;
|
||||
|
||||
public readonly List<Tuple<ItemPrefab, int>> RequiredItems;
|
||||
|
||||
public readonly float RequiredTime;
|
||||
|
||||
public readonly List<Skill> RequiredSkills;
|
||||
|
||||
public FabricableItem(XElement element)
|
||||
{
|
||||
string name = ToolBox.GetAttributeString(element, "name", "");
|
||||
|
||||
TargetItem = ItemPrefab.list.Find(ip => ip.Name.ToLowerInvariant() == name.ToLowerInvariant()) as ItemPrefab;
|
||||
if (TargetItem == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in fabricable item "+name+"! Item \"" + element.Name + "\" not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
RequiredSkills = new List<Skill>();
|
||||
|
||||
RequiredTime = ToolBox.GetAttributeFloat(element, "requiredtime", 1.0f);
|
||||
|
||||
RequiredItems = new List<Tuple<ItemPrefab, int>>();
|
||||
|
||||
string[] requiredItemNames = ToolBox.GetAttributeString(element, "requireditems", "").Split(',');
|
||||
foreach (string requiredItemName in requiredItemNames)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(requiredItemName)) continue;
|
||||
|
||||
ItemPrefab requiredItem = ItemPrefab.list.Find(ip => ip.Name.ToLowerInvariant() == requiredItemName.Trim().ToLowerInvariant()) as ItemPrefab;
|
||||
if (requiredItem == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in fabricable item " + name + "! Required item \"" + requiredItemName + "\" not found.");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var existing = RequiredItems.Find(r => r.Item1 == requiredItem);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
|
||||
RequiredItems.Add(new Tuple<ItemPrefab, int>(requiredItem, 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
RequiredItems.Remove(existing);
|
||||
RequiredItems.Add(new Tuple<ItemPrefab, int>(requiredItem, existing.Item2+1));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "requiredskill":
|
||||
RequiredSkills.Add(new Skill(
|
||||
ToolBox.GetAttributeString(subElement, "name", ""),
|
||||
ToolBox.GetAttributeInt(subElement, "level", 0)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class Fabricator : Powered, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private List<FabricableItem> fabricableItems;
|
||||
|
||||
private GUIListBox itemList;
|
||||
|
||||
private GUIFrame selectedItemFrame;
|
||||
|
||||
private GUIProgressBar progressBar;
|
||||
private GUIButton activateButton;
|
||||
|
||||
private FabricableItem fabricatedItem;
|
||||
private float timeUntilReady;
|
||||
|
||||
//used for checking if contained items have changed
|
||||
//(in which case we need to recheck which items can be fabricated)
|
||||
private Item[] prevContainedItems;
|
||||
|
||||
public Fabricator(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
fabricableItems = new List<FabricableItem>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString() != "fabricableitem") continue;
|
||||
|
||||
FabricableItem fabricableItem = new FabricableItem(subElement);
|
||||
if (fabricableItem.TargetItem != null) fabricableItems.Add(fabricableItem);
|
||||
}
|
||||
|
||||
GuiFrame.Padding = new Vector4(20.0f, 20.0f, 20.0f, 20.0f);
|
||||
|
||||
itemList = new GUIListBox(new Rectangle(0,0,GuiFrame.Rect.Width/2-20,0), "", GuiFrame);
|
||||
itemList.OnSelected = SelectItem;
|
||||
|
||||
foreach (FabricableItem fi in fabricableItems)
|
||||
{
|
||||
GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 50), Color.Transparent, null, itemList)
|
||||
{
|
||||
UserData = fi,
|
||||
Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f),
|
||||
HoverColor = Color.Gold * 0.2f,
|
||||
SelectedColor = Color.Gold * 0.5f,
|
||||
ToolTip = fi.TargetItem.Description
|
||||
};
|
||||
|
||||
GUITextBlock textBlock = new GUITextBlock(
|
||||
new Rectangle(40, 0, 0, 25),
|
||||
fi.TargetItem.Name,
|
||||
Color.Transparent, Color.White,
|
||||
Alignment.Left, Alignment.Left,
|
||||
null, frame);
|
||||
textBlock.ToolTip = fi.TargetItem.Description;
|
||||
textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f);
|
||||
|
||||
if (fi.TargetItem.sprite != null)
|
||||
{
|
||||
GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), fi.TargetItem.sprite, Alignment.Left, frame);
|
||||
img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f);
|
||||
img.Color = fi.TargetItem.SpriteColor;
|
||||
img.ToolTip = fi.TargetItem.Description;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private bool SelectItem(GUIComponent component, object obj)
|
||||
{
|
||||
FabricableItem targetItem = obj as FabricableItem;
|
||||
if (targetItem == null) return false;
|
||||
|
||||
if (selectedItemFrame != null) GuiFrame.RemoveChild(selectedItemFrame);
|
||||
|
||||
//int width = 200, height = 150;
|
||||
selectedItemFrame = new GUIFrame(new Rectangle(0, 0, (int)(GuiFrame.Rect.Width * 0.4f), 300), Color.Black * 0.8f, Alignment.CenterY | Alignment.Right, null, GuiFrame);
|
||||
|
||||
selectedItemFrame.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f);
|
||||
|
||||
progressBar = new GUIProgressBar(new Rectangle(0, 0, 0, 20), Color.Green, "", 0.0f, Alignment.BottomCenter, selectedItemFrame);
|
||||
progressBar.IsHorizontal = true;
|
||||
|
||||
if (targetItem.TargetItem.sprite != null)
|
||||
{
|
||||
int y = 0;
|
||||
|
||||
GUIImage img = new GUIImage(new Rectangle(10, 0, 40, 40), targetItem.TargetItem.sprite, Alignment.TopLeft, selectedItemFrame);
|
||||
img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f);
|
||||
img.Color = targetItem.TargetItem.SpriteColor;
|
||||
|
||||
new GUITextBlock(
|
||||
new Rectangle(60, 0, 0, 25),
|
||||
targetItem.TargetItem.Name,
|
||||
Color.Transparent, Color.White,
|
||||
Alignment.TopLeft,
|
||||
Alignment.TopLeft, null,
|
||||
selectedItemFrame, true);
|
||||
|
||||
y += 40;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(targetItem.TargetItem.Description))
|
||||
{
|
||||
var description = new GUITextBlock(
|
||||
new Rectangle(0, y, 0, 0),
|
||||
targetItem.TargetItem.Description,
|
||||
"", Alignment.TopLeft, Alignment.TopLeft,
|
||||
selectedItemFrame, true, GUI.SmallFont);
|
||||
|
||||
y += description.Rect.Height + 10;
|
||||
}
|
||||
|
||||
|
||||
List<Skill> inadequateSkills = new List<Skill>();
|
||||
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
inadequateSkills = targetItem.RequiredSkills.FindAll(skill => Character.Controlled.GetSkillLevel(skill.Name) < skill.Level);
|
||||
}
|
||||
|
||||
Color textColor = Color.White;
|
||||
string text;
|
||||
if (!inadequateSkills.Any())
|
||||
{
|
||||
text = "Required items:\n";
|
||||
foreach (Tuple<ItemPrefab, int> ip in targetItem.RequiredItems)
|
||||
{
|
||||
text += " - " + ip.Item1.Name + " x"+ip.Item2+"\n";
|
||||
}
|
||||
text += "Required time: " + targetItem.RequiredTime + " s";
|
||||
}
|
||||
else
|
||||
{
|
||||
text = "Skills required to calibrate:\n";
|
||||
foreach (Skill skill in inadequateSkills)
|
||||
{
|
||||
text += " - " + skill.Name + " lvl " + skill.Level + "\n";
|
||||
}
|
||||
|
||||
textColor = Color.Red;
|
||||
}
|
||||
|
||||
new GUITextBlock(
|
||||
new Rectangle(0, y, 0, 25),
|
||||
text,
|
||||
Color.Transparent, textColor,
|
||||
Alignment.TopLeft,
|
||||
Alignment.TopLeft, null,
|
||||
selectedItemFrame);
|
||||
|
||||
activateButton = new GUIButton(new Rectangle(0, -30, 100, 20), "Create", Color.White, Alignment.CenterX | Alignment.Bottom, "", selectedItemFrame);
|
||||
activateButton.OnClicked = StartButtonClicked;
|
||||
activateButton.UserData = targetItem;
|
||||
activateButton.Enabled = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
CheckFabricableItems(character);
|
||||
if (itemList.Selected != null)
|
||||
{
|
||||
SelectItem(itemList.Selected, itemList.Selected.UserData);
|
||||
}
|
||||
|
||||
|
||||
return base.Select(character);
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return (picker != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// check which of the items can be fabricated by the character
|
||||
/// and update the text colors of the item list accordingly
|
||||
/// </summary>
|
||||
private void CheckFabricableItems(Character character)
|
||||
{
|
||||
foreach (GUIComponent child in itemList.children)
|
||||
{
|
||||
var itemPrefab = child.UserData as FabricableItem;
|
||||
if (itemPrefab == null) continue;
|
||||
|
||||
bool canBeFabricated = CanBeFabricated(itemPrefab, character);
|
||||
|
||||
|
||||
child.GetChild<GUITextBlock>().TextColor = Color.White * (canBeFabricated ? 1.0f : 0.5f);
|
||||
child.GetChild<GUIImage>().Color = itemPrefab.TargetItem.SpriteColor * (canBeFabricated ? 1.0f : 0.5f);
|
||||
|
||||
}
|
||||
|
||||
var itemContainer = item.GetComponent<ItemContainer>();
|
||||
prevContainedItems = new Item[itemContainer.Inventory.Items.Length];
|
||||
itemContainer.Inventory.Items.CopyTo(prevContainedItems, 0);
|
||||
}
|
||||
|
||||
private bool StartButtonClicked(GUIButton button, object obj)
|
||||
{
|
||||
if (fabricatedItem == null)
|
||||
{
|
||||
StartFabricating(obj as FabricableItem, Character.Controlled);
|
||||
}
|
||||
else
|
||||
{
|
||||
CancelFabricating(Character.Controlled);
|
||||
}
|
||||
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
else if (GameMain.Client != null)
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void StartFabricating(FabricableItem selectedItem, Character user = null)
|
||||
{
|
||||
if (selectedItem == null) return;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
GameServer.Log(user.Name + " started fabricating " + selectedItem.TargetItem.Name + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
|
||||
itemList.Enabled = false;
|
||||
|
||||
activateButton.Text = "Cancel";
|
||||
|
||||
fabricatedItem = selectedItem;
|
||||
IsActive = true;
|
||||
|
||||
timeUntilReady = fabricatedItem.RequiredTime;
|
||||
|
||||
var containers = item.GetComponents<ItemContainer>();
|
||||
containers[0].Inventory.Locked = true;
|
||||
containers[1].Inventory.Locked = true;
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
}
|
||||
|
||||
private void CancelFabricating(Character user = null)
|
||||
{
|
||||
if (fabricatedItem != null && user != null)
|
||||
{
|
||||
GameServer.Log(user.Name + " cancelled the fabrication of " + fabricatedItem.TargetItem.Name + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
|
||||
itemList.Enabled = true;
|
||||
IsActive = false;
|
||||
fabricatedItem = null;
|
||||
|
||||
currPowerConsumption = 0.0f;
|
||||
|
||||
if (activateButton != null)
|
||||
{
|
||||
activateButton.Text = "Create";
|
||||
}
|
||||
if (progressBar != null) progressBar.BarSize = 0.0f;
|
||||
|
||||
timeUntilReady = 0.0f;
|
||||
|
||||
var containers = item.GetComponents<ItemContainer>();
|
||||
containers[0].Inventory.Locked = false;
|
||||
containers[1].Inventory.Locked = false;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (progressBar!=null)
|
||||
{
|
||||
progressBar.BarSize = fabricatedItem == null ? 0.0f : (fabricatedItem.RequiredTime - timeUntilReady) / fabricatedItem.RequiredTime;
|
||||
}
|
||||
|
||||
if (voltage < minVoltage) return;
|
||||
|
||||
if (powerConsumption == 0) voltage = 1.0f;
|
||||
|
||||
timeUntilReady -= deltaTime*voltage;
|
||||
|
||||
voltage -= deltaTime * 10.0f;
|
||||
|
||||
if (timeUntilReady > 0.0f) return;
|
||||
|
||||
var containers = item.GetComponents<ItemContainer>();
|
||||
if (containers.Count < 2)
|
||||
{
|
||||
DebugConsole.ThrowError("Error while fabricating a new item: fabricators must have two ItemContainer components");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Tuple<ItemPrefab, int> ip in fabricatedItem.RequiredItems)
|
||||
{
|
||||
for (int i = 0; i < ip.Item2; i++)
|
||||
{
|
||||
var requiredItem = containers[0].Inventory.Items.FirstOrDefault(it => it != null && it.Prefab == ip.Item1);
|
||||
if (requiredItem == null) continue;
|
||||
|
||||
Entity.Spawner.AddToRemoveQueue(requiredItem);
|
||||
containers[0].Inventory.RemoveItem(requiredItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (containers[1].Inventory.Items.All(i => i != null))
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine);
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, containers[1].Inventory);
|
||||
}
|
||||
|
||||
CancelFabricating(null);
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
GuiFrame.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character)
|
||||
{
|
||||
FabricableItem targetItem = itemList.SelectedData as FabricableItem;
|
||||
if (targetItem != null)
|
||||
{
|
||||
activateButton.Enabled = CanBeFabricated(targetItem, character);
|
||||
}
|
||||
|
||||
if (character != null)
|
||||
{
|
||||
bool itemsChanged = false;
|
||||
if (prevContainedItems == null)
|
||||
{
|
||||
itemsChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var itemContainer = item.GetComponent<ItemContainer>();
|
||||
for (int i = 0; i < itemContainer.Inventory.Items.Length; i++)
|
||||
{
|
||||
if (prevContainedItems[i] != itemContainer.Inventory.Items[i])
|
||||
{
|
||||
itemsChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemsChanged) CheckFabricableItems(character);
|
||||
}
|
||||
|
||||
|
||||
GuiFrame.Update((float)Timing.Step);
|
||||
}
|
||||
|
||||
private bool CanBeFabricated(FabricableItem fabricableItem, Character user)
|
||||
{
|
||||
if (fabricableItem == null) return false;
|
||||
|
||||
if (user != null &&
|
||||
fabricableItem.RequiredSkills.Any(skill => user.GetSkillLevel(skill.Name) < skill.Level))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemContainer container = item.GetComponent<ItemContainer>();
|
||||
foreach (Tuple<ItemPrefab, int> ip in fabricableItem.RequiredItems)
|
||||
{
|
||||
if (Array.FindAll(container.Inventory.Items, it => it != null && it.Prefab == ip.Item1).Length < ip.Item2) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ClientWrite(NetBuffer msg, object[] extraData = null)
|
||||
{
|
||||
int itemIndex = fabricatedItem == null ? -1 : fabricableItems.IndexOf(fabricatedItem);
|
||||
msg.WriteRangedInteger(-1, fabricableItems.Count - 1, itemIndex);
|
||||
}
|
||||
|
||||
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
|
||||
{
|
||||
int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1);
|
||||
|
||||
item.CreateServerEvent(this);
|
||||
|
||||
if (!item.CanClientAccess(c)) return;
|
||||
|
||||
if (itemIndex == -1)
|
||||
{
|
||||
CancelFabricating(c.Character);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if already fabricating the selected item, return
|
||||
if (fabricatedItem != null && fabricableItems.IndexOf(fabricatedItem) == itemIndex) return;
|
||||
if (itemIndex < 0 || itemIndex >= fabricableItems.Count) return;
|
||||
|
||||
SelectItem(null, fabricableItems[itemIndex]);
|
||||
StartFabricating(fabricableItems[itemIndex], c.Character);
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
|
||||
{
|
||||
int itemIndex = fabricatedItem == null ? -1 : fabricableItems.IndexOf(fabricatedItem);
|
||||
msg.WriteRangedInteger(-1, fabricableItems.Count - 1, itemIndex);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
|
||||
{
|
||||
int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1);
|
||||
|
||||
if (itemIndex == -1)
|
||||
{
|
||||
CancelFabricating();
|
||||
}
|
||||
else
|
||||
{
|
||||
//if already fabricating the selected item, return
|
||||
if (fabricatedItem != null && fabricableItems.IndexOf(fabricatedItem) == itemIndex) return;
|
||||
if (itemIndex < 0 || itemIndex >= fabricableItems.Count) return;
|
||||
|
||||
SelectItem(null, fabricableItems[itemIndex]);
|
||||
StartFabricating(fabricableItems[itemIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class MiniMap : Powered
|
||||
{
|
||||
class HullData
|
||||
{
|
||||
public float? Oxygen;
|
||||
public float? Water;
|
||||
}
|
||||
|
||||
private DateTime resetDataTime;
|
||||
|
||||
bool hasPower;
|
||||
|
||||
[Editable, HasDefaultValue(false, true)]
|
||||
public bool RequireWaterDetectors
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, HasDefaultValue(true, true)]
|
||||
public bool RequireOxygenDetectors
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, HasDefaultValue(false, true)]
|
||||
public bool ShowHullIntegrity
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
private Dictionary<Hull, HullData> hullDatas;
|
||||
|
||||
public MiniMap(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
hullDatas = new Dictionary<Hull, HullData>();
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
//periodically reset all hull data
|
||||
//(so that outdated hull info won't be shown if detectors stop sending signals)
|
||||
if (DateTime.Now > resetDataTime)
|
||||
{
|
||||
hullDatas.Clear();
|
||||
resetDataTime = DateTime.Now + new TimeSpan(0, 0, 1);
|
||||
}
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
|
||||
hasPower = voltage > minVoltage;
|
||||
|
||||
voltage = 0.0f;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
if (picker == null) return false;
|
||||
|
||||
//picker.SelectedConstruction = item;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
if (item.Submarine == null) return;
|
||||
|
||||
int width = GuiFrame.Rect.Width, height = GuiFrame.Rect.Height;
|
||||
int x = GuiFrame.Rect.X;
|
||||
int y = GuiFrame.Rect.Y;
|
||||
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
if (!hasPower) return;
|
||||
|
||||
Rectangle miniMap = new Rectangle(x + 20, y + 40, width - 40, height - 60);
|
||||
|
||||
float size = Math.Min((float)miniMap.Width / (float)item.Submarine.Borders.Width, (float)miniMap.Height / (float)item.Submarine.Borders.Height);
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
Point topLeft = new Point(
|
||||
miniMap.X + (int)((hull.Rect.X - item.Submarine.HiddenSubPosition.X - item.Submarine.Borders.X) * size),
|
||||
miniMap.Y - (int)((hull.Rect.Y - item.Submarine.HiddenSubPosition.Y - item.Submarine.Borders.Y) * size));
|
||||
|
||||
Point bottomRight = new Point(
|
||||
topLeft.X + (int)(hull.Rect.Width * size),
|
||||
topLeft.Y + (int)(hull.Rect.Height * size));
|
||||
|
||||
topLeft.X = (int)MathUtils.RoundTowardsClosest(topLeft.X, 4);
|
||||
topLeft.Y = (int)MathUtils.RoundTowardsClosest(topLeft.Y, 4);
|
||||
bottomRight.X = (int)MathUtils.RoundTowardsClosest(bottomRight.X, 4);
|
||||
bottomRight.Y = (int)MathUtils.RoundTowardsClosest(bottomRight.Y, 4);
|
||||
|
||||
Rectangle hullRect = new Rectangle(
|
||||
topLeft, bottomRight - topLeft);
|
||||
|
||||
HullData hullData;
|
||||
hullDatas.TryGetValue(hull, out hullData);
|
||||
|
||||
Color borderColor = Color.Green;
|
||||
|
||||
|
||||
//hull integrity -----------------------------------
|
||||
|
||||
float? gapOpenSum = 0.0f;
|
||||
if (ShowHullIntegrity)
|
||||
{
|
||||
gapOpenSum = hull.ConnectedGaps.Where(g => !g.IsRoomToRoom).Sum(g => g.Open);
|
||||
borderColor = Color.Lerp(borderColor, Color.Red, Math.Min((float)gapOpenSum, 1.0f));
|
||||
}
|
||||
|
||||
//oxygen -----------------------------------
|
||||
|
||||
float? oxygenAmount = null;
|
||||
if (RequireOxygenDetectors && (hullData == null || hullData.Oxygen == null))
|
||||
{
|
||||
borderColor *= 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
oxygenAmount = hullData != null && hullData.Oxygen != null ? (float)hullData.Oxygen : hull.OxygenPercentage;
|
||||
GUI.DrawRectangle(spriteBatch, hullRect, Color.Lerp(Color.Red * 0.5f, Color.Green * 0.3f, (float)oxygenAmount / 100.0f), true);
|
||||
}
|
||||
|
||||
//water -----------------------------------
|
||||
|
||||
float? waterAmount = null;
|
||||
if (RequireWaterDetectors && (hullData == null || hullData.Water == null))
|
||||
{
|
||||
borderColor *= 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
waterAmount = hullData != null && hullData.Water != null ?
|
||||
(float)hullData.Water :
|
||||
Math.Min(hull.Volume / hull.FullVolume, 1.0f);
|
||||
|
||||
if (hullRect.Height * waterAmount > 3.0f)
|
||||
{
|
||||
Rectangle waterRect = new Rectangle(
|
||||
hullRect.X,
|
||||
(int)(hullRect.Y + hullRect.Height * (1.0f - waterAmount)),
|
||||
hullRect.Width,
|
||||
(int)(hullRect.Height * waterAmount));
|
||||
|
||||
waterRect.Inflate(-3, -3);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, waterRect, Color.DarkBlue, true);
|
||||
GUI.DrawLine(spriteBatch, new Vector2(waterRect.X, waterRect.Y), new Vector2(waterRect.Right, waterRect.Y), Color.LightBlue);
|
||||
}
|
||||
}
|
||||
|
||||
if (hullRect.Contains(PlayerInput.MousePosition))
|
||||
{
|
||||
borderColor = Color.White;
|
||||
|
||||
if (gapOpenSum > 0.1f)
|
||||
{
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(x + 10, y + height - 60),
|
||||
"Hull breach", Color.Red, Color.Black * 0.5f, 2, GUI.SmallFont);
|
||||
}
|
||||
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(x + 10, y + height - 60),
|
||||
oxygenAmount == null ? "Air quality data not available" : "Air quality: " + (int)oxygenAmount + " %",
|
||||
oxygenAmount == null ? Color.Red : Color.Lerp(Color.Red, Color.LightGreen, (float)oxygenAmount / 100.0f),
|
||||
Color.Black * 0.5f, 2, GUI.SmallFont);
|
||||
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(x + 10, y + height - 40),
|
||||
waterAmount == null ? "Water level data not available" : "Water level: " + (int)(waterAmount * 100.0f) + " %",
|
||||
waterAmount == null ? Color.Red : Color.Lerp(Color.LightGreen, Color.Red, (float)waterAmount),
|
||||
Color.Black * 0.5f, 2, GUI.SmallFont);
|
||||
}
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, hullRect, borderColor, false, 0.0f, 2);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0)
|
||||
{
|
||||
base.ReceiveSignal(stepsTaken, signal, connection, source, sender, power);
|
||||
|
||||
if (sender == null || sender.CurrentHull == null) return;
|
||||
|
||||
Hull senderHull = sender.CurrentHull;
|
||||
|
||||
HullData hullData;
|
||||
if (!hullDatas.TryGetValue(senderHull, out hullData))
|
||||
{
|
||||
hullData = new HullData();
|
||||
hullDatas.Add(senderHull, hullData);
|
||||
}
|
||||
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "water_data_in":
|
||||
//cheating a bit because water detectors don't actually send the water level
|
||||
if (source.GetComponent<WaterDetector>() == null)
|
||||
{
|
||||
hullData.Water = Rand.Range(0.0f, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
hullData.Water = Math.Min(senderHull.Volume / senderHull.FullVolume, 1.0f);
|
||||
}
|
||||
break;
|
||||
case "oxygen_data_in":
|
||||
float oxy;
|
||||
|
||||
if (!float.TryParse(signal, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out oxy))
|
||||
{
|
||||
oxy = Rand.Range(0.0f, 100.0f);
|
||||
}
|
||||
|
||||
hullData.Oxygen = oxy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class OxygenGenerator : Powered
|
||||
{
|
||||
PropertyTask powerUpTask;
|
||||
|
||||
float powerDownTimer;
|
||||
|
||||
bool running;
|
||||
|
||||
private float generatedAmount;
|
||||
|
||||
List<Vent> ventList;
|
||||
|
||||
private float totalHullVolume;
|
||||
|
||||
public bool IsRunning()
|
||||
{
|
||||
return (running && item.Condition>0.0f);
|
||||
}
|
||||
|
||||
public float CurrFlow
|
||||
{
|
||||
get;
|
||||
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)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
//item.linkedTo.CollectionChanged += delegate { GetVents(); };
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
CurrFlow = 0.0f;
|
||||
currPowerConsumption = powerConsumption;
|
||||
|
||||
if (item.CurrentHull == null) return;
|
||||
|
||||
if (voltage < minVoltage)
|
||||
{
|
||||
powerDownTimer += deltaTime;
|
||||
running = false;
|
||||
if ((powerUpTask==null || powerUpTask.IsFinished) && powerDownTimer>5.0f)
|
||||
{
|
||||
powerUpTask = new PropertyTask(item, IsRunning, 50.0f, "Turn on the oxygen generator");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
powerDownTimer = 0.0f;
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
running = true;
|
||||
|
||||
CurrFlow = Math.Min(voltage, 1.0f) * generatedAmount*100.0f;
|
||||
//item.CurrentHull.Oxygen += CurrFlow * deltaTime;
|
||||
|
||||
UpdateVents(CurrFlow);
|
||||
|
||||
voltage -= deltaTime;
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
powerDownTimer += deltaTime;
|
||||
}
|
||||
|
||||
private void GetVents()
|
||||
{
|
||||
ventList.Clear();
|
||||
|
||||
foreach (MapEntity entity in item.linkedTo)
|
||||
{
|
||||
Item linkedItem = entity as Item;
|
||||
if (linkedItem == null) continue;
|
||||
|
||||
Vent vent = linkedItem.GetComponent<Vent>();
|
||||
if (vent == null) continue;
|
||||
|
||||
ventList.Add(vent);
|
||||
if (linkedItem.CurrentHull!=null) totalHullVolume += linkedItem.CurrentHull.FullVolume;
|
||||
}
|
||||
}
|
||||
|
||||
//public override void OnMapLoaded()
|
||||
//{
|
||||
// GetVents();
|
||||
//}
|
||||
|
||||
private void UpdateVents(float deltaOxygen)
|
||||
{
|
||||
if (ventList == null)
|
||||
{
|
||||
ventList = new List<Vent>();
|
||||
GetVents();
|
||||
}
|
||||
|
||||
if (!ventList.Any() || totalHullVolume == 0.0f) return;
|
||||
|
||||
foreach (Vent v in ventList)
|
||||
{
|
||||
if (v.Item.CurrentHull == null) continue;
|
||||
|
||||
v.OxygenFlow = deltaOxygen * (v.Item.CurrentHull.FullVolume / totalHullVolume);
|
||||
v.IsActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class Pump : Powered, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private float flowPercentage;
|
||||
private float maxFlow;
|
||||
|
||||
private float? targetLevel;
|
||||
|
||||
public Hull hull1;
|
||||
|
||||
private GUITickBox isActiveTickBox;
|
||||
|
||||
[HasDefaultValue(0.0f, true)]
|
||||
public float FlowPercentage
|
||||
{
|
||||
get { return flowPercentage; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(flowPercentage)) return;
|
||||
flowPercentage = MathHelper.Clamp(value,-100.0f,100.0f);
|
||||
flowPercentage = MathUtils.Round(flowPercentage, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(80.0f, false)]
|
||||
public float MaxFlow
|
||||
{
|
||||
get { return maxFlow; }
|
||||
set { maxFlow = value; }
|
||||
}
|
||||
|
||||
float currFlow;
|
||||
public float CurrFlow
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsActive) return 0.0f;
|
||||
return Math.Abs(currFlow);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.IsActive;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.IsActive = value;
|
||||
|
||||
if (isActiveTickBox != null) isActiveTickBox.Selected = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Pump(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
GetHull();
|
||||
|
||||
isActiveTickBox = new GUITickBox(new Rectangle(0, 0, 20, 20), "Running", Alignment.TopLeft, GuiFrame);
|
||||
isActiveTickBox.OnSelected = (GUITickBox box) =>
|
||||
{
|
||||
targetLevel = null;
|
||||
IsActive = !IsActive;
|
||||
if (!IsActive) currPowerConsumption = 0.0f;
|
||||
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
GameServer.Log(Character.Controlled + (IsActive ? " turned on " : " turned off ") + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
else if (GameMain.Client != null)
|
||||
{
|
||||
correctionTimer = CorrectionDelay;
|
||||
item.CreateClientEvent(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
var button = new GUIButton(new Rectangle(160, 40, 35, 30), "OUT", "", GuiFrame);
|
||||
button.OnClicked = (GUIButton btn, object obj) =>
|
||||
{
|
||||
FlowPercentage -= 10.0f;
|
||||
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
GameServer.Log(Character.Controlled + " set the pumping speed of " + item.Name + " to " + (int)(flowPercentage) + " %", ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
else if (GameMain.Client != null)
|
||||
{
|
||||
correctionTimer = CorrectionDelay;
|
||||
item.CreateClientEvent(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
button = new GUIButton(new Rectangle(210, 40, 35, 30), "IN", "", GuiFrame);
|
||||
button.OnClicked = (GUIButton btn, object obj) =>
|
||||
{
|
||||
FlowPercentage += 10.0f;
|
||||
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
GameServer.Log(Character.Controlled + " set the pumping speed of " + item.Name + " to " + (int)(flowPercentage) + " %", ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
else if (GameMain.Client != null)
|
||||
{
|
||||
correctionTimer = CorrectionDelay;
|
||||
item.CreateClientEvent(this);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
{
|
||||
base.Move(amount);
|
||||
|
||||
GetHull();
|
||||
}
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
GetHull();
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
currFlow = 0.0f;
|
||||
|
||||
if (targetLevel != null)
|
||||
{
|
||||
float hullPercentage = 0.0f;
|
||||
if (hull1 != null) hullPercentage = (hull1.Volume / hull1.FullVolume) * 100.0f;
|
||||
FlowPercentage = ((float)targetLevel - hullPercentage) * 10.0f;
|
||||
}
|
||||
|
||||
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
|
||||
|
||||
if (voltage < minVoltage) return;
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
if (hull1 == null) return;
|
||||
|
||||
float powerFactor = (currPowerConsumption==0.0f) ? 1.0f : voltage;
|
||||
//flowPercentage = maxFlow * powerFactor;
|
||||
|
||||
currFlow = (flowPercentage / 100.0f) * maxFlow * powerFactor;
|
||||
|
||||
hull1.Volume += currFlow;
|
||||
if (hull1.Volume > hull1.FullVolume) hull1.Pressure += 0.5f;
|
||||
|
||||
//if (hull2 != null)
|
||||
//{
|
||||
// hull2.Volume -= currFlow;
|
||||
// if (hull2.Volume > hull1.FullVolume) hull2.Pressure += 0.5f;
|
||||
//}
|
||||
|
||||
voltage = 0.0f;
|
||||
}
|
||||
|
||||
private void GetHull()
|
||||
{
|
||||
hull1 = Hull.FindHull(item.WorldPosition, item.CurrentHull);
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
int x = GuiFrame.Rect.X;
|
||||
int y = GuiFrame.Rect.Y;
|
||||
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
GUI.Font.DrawString(spriteBatch, "Pumping speed: " + (int)flowPercentage + " %", new Vector2(x + 40, y + 85), Color.White);
|
||||
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
GuiFrame.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character)
|
||||
{
|
||||
GuiFrame.Update(1.0f / 60.0f);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power=0.0f)
|
||||
{
|
||||
base.ReceiveSignal(stepsTaken, signal, connection, source, sender, power);
|
||||
|
||||
if (connection.Name == "toggle")
|
||||
{
|
||||
IsActive = !IsActive;
|
||||
}
|
||||
else if (connection.Name == "set_active")
|
||||
{
|
||||
IsActive = (signal != "0");
|
||||
}
|
||||
else if (connection.Name == "set_speed")
|
||||
{
|
||||
float tempSpeed;
|
||||
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out tempSpeed))
|
||||
{
|
||||
flowPercentage = MathHelper.Clamp(tempSpeed, -100.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
else if (connection.Name == "set_targetlevel")
|
||||
{
|
||||
float tempTarget;
|
||||
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out tempTarget))
|
||||
{
|
||||
targetLevel = MathHelper.Clamp((tempTarget+100.0f)/2.0f, 0.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsActive) currPowerConsumption = 0.0f;
|
||||
}
|
||||
|
||||
public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null)
|
||||
{
|
||||
//flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this
|
||||
msg.WriteRangedInteger(-10, 10, (int)(flowPercentage / 10.0f));
|
||||
msg.Write(IsActive);
|
||||
}
|
||||
|
||||
public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Client c)
|
||||
{
|
||||
float newFlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
|
||||
bool newIsActive = msg.ReadBoolean();
|
||||
|
||||
if (item.CanClientAccess(c))
|
||||
{
|
||||
if (newFlowPercentage != FlowPercentage)
|
||||
{
|
||||
GameServer.Log(c.Character + " set the pumping speed of " + item.Name + " to " + (int)(newFlowPercentage) + " %", ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
if (newIsActive != IsActive)
|
||||
{
|
||||
GameServer.Log(c.Character + (newIsActive ? " turned on " : " turned off ") + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
|
||||
FlowPercentage = newFlowPercentage;
|
||||
IsActive = newIsActive;
|
||||
}
|
||||
|
||||
//notify all clients of the changed state
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
|
||||
public void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null)
|
||||
{
|
||||
//flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this
|
||||
msg.WriteRangedInteger(-10, 10, (int)(flowPercentage / 10.0f));
|
||||
msg.Write(IsActive);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
|
||||
{
|
||||
if (correctionTimer > 0.0f)
|
||||
{
|
||||
StartDelayedCorrection(type, msg.ExtractBits(5 + 1), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
|
||||
IsActive = msg.ReadBoolean();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,584 @@
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Voronoi2;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class Radar : Powered, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private float range;
|
||||
|
||||
private float pingState;
|
||||
|
||||
private readonly Sprite pingCircle, screenOverlay;
|
||||
|
||||
private readonly Sprite radarBlip;
|
||||
|
||||
private GUITickBox isActiveTickBox;
|
||||
|
||||
private List<RadarBlip> radarBlips;
|
||||
private float prevPingRadius;
|
||||
|
||||
float prevPassivePingRadius;
|
||||
|
||||
private Vector2 center;
|
||||
private float displayRadius;
|
||||
private float displayScale;
|
||||
|
||||
private float displayBorderSize;
|
||||
|
||||
[HasDefaultValue(10000.0f, false)]
|
||||
public float Range
|
||||
{
|
||||
get { return range; }
|
||||
set { range = MathHelper.Clamp(value, 0.0f, 100000.0f); }
|
||||
}
|
||||
|
||||
[HasDefaultValue(false, false)]
|
||||
public bool DetectSubmarineWalls
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public override bool IsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.IsActive;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
base.IsActive = value;
|
||||
if (isActiveTickBox != null) isActiveTickBox.Selected = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Radar(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
radarBlips = new List<RadarBlip>();
|
||||
|
||||
displayBorderSize = ToolBox.GetAttributeFloat(element, "displaybordersize", 0.0f);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "pingcircle":
|
||||
pingCircle = new Sprite(subElement);
|
||||
break;
|
||||
case "screenoverlay":
|
||||
screenOverlay = new Sprite(subElement);
|
||||
break;
|
||||
case "blip":
|
||||
radarBlip = new Sprite(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
isActiveTickBox = new GUITickBox(new Rectangle(0, 0, 20, 20), "Active Sonar", Alignment.TopLeft, GuiFrame);
|
||||
isActiveTickBox.OnSelected = (GUITickBox box) =>
|
||||
{
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
else if (GameMain.Client != null)
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
correctionTimer = CorrectionDelay;
|
||||
}
|
||||
IsActive = box.Selected;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
GuiFrame.CanBeFocused = false;
|
||||
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
currPowerConsumption = powerConsumption;
|
||||
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
if (voltage >= minVoltage || powerConsumption <= 0.0f)
|
||||
{
|
||||
pingState = pingState + deltaTime * 0.5f;
|
||||
if (pingState > 1.0f)
|
||||
{
|
||||
if (item.CurrentHull != null) item.CurrentHull.AiTarget.SoundRange = Math.Max(Range * pingState, item.CurrentHull.AiTarget.SoundRange);
|
||||
item.Use(deltaTime);
|
||||
pingState = 0.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pingState = 0.0f;
|
||||
}
|
||||
|
||||
Voltage -= deltaTime;
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
return pingState > 1.0f;
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
GuiFrame.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character)
|
||||
{
|
||||
GuiFrame.Update((float)Timing.Step);
|
||||
|
||||
for (int i = radarBlips.Count - 1; i >= 0; i--)
|
||||
{
|
||||
radarBlips[i].FadeTimer -= (float)Timing.Step * 0.5f;
|
||||
if (radarBlips[i].FadeTimer <= 0.0f) radarBlips.RemoveAt(i);
|
||||
}
|
||||
|
||||
if (IsActive)
|
||||
{
|
||||
float pingRadius = displayRadius * pingState;
|
||||
Ping(item.WorldPosition, pingRadius, prevPingRadius, displayScale, range, 2.0f);
|
||||
prevPingRadius = pingRadius;
|
||||
}
|
||||
|
||||
float passivePingRadius = (float)Math.Sin(Timing.TotalTime * 10);
|
||||
if (passivePingRadius > 0.0f)
|
||||
{
|
||||
foreach (AITarget t in AITarget.List)
|
||||
{
|
||||
if (t.SoundRange <= 0.0f) continue;
|
||||
|
||||
if (Vector2.Distance(t.WorldPosition, item.WorldPosition) < t.SoundRange)
|
||||
{
|
||||
Ping(t.WorldPosition, t.SoundRange * passivePingRadius * 0.2f, t.SoundRange * prevPassivePingRadius * 0.2f, displayScale, t.SoundRange, 0.5f);
|
||||
|
||||
radarBlips.Add(new RadarBlip(t.WorldPosition, 1.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
prevPassivePingRadius = passivePingRadius;
|
||||
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
int radius = GuiFrame.Rect.Height / 2 - 10;
|
||||
DrawRadar(spriteBatch, new Rectangle((int)GuiFrame.Center.X - radius, (int)GuiFrame.Center.Y - radius, radius * 2, radius * 2));
|
||||
}
|
||||
|
||||
private void DrawRadar(SpriteBatch spriteBatch, Rectangle rect)
|
||||
{
|
||||
center = new Vector2(rect.X + rect.Width * 0.5f, rect.Center.Y);
|
||||
displayRadius = (rect.Width / 2.0f) * (1.0f - displayBorderSize);
|
||||
displayScale = displayRadius / range;
|
||||
|
||||
if (IsActive)
|
||||
{
|
||||
pingCircle.Draw(spriteBatch, center, Color.White * (1.0f - pingState), 0.0f, (displayRadius*2 / pingCircle.size.X) * pingState);
|
||||
}
|
||||
|
||||
if (item.Submarine != null && !DetectSubmarineWalls)
|
||||
{
|
||||
float simScale = displayScale * Physics.DisplayToSimRation;
|
||||
|
||||
foreach (Submarine submarine in Submarine.Loaded)
|
||||
{
|
||||
if (submarine != item.Submarine && !submarine.DockedTo.Contains(item.Submarine)) continue;
|
||||
|
||||
Vector2 offset = ConvertUnits.ToSimUnits(submarine.WorldPosition - item.WorldPosition);
|
||||
|
||||
for (int i = 0; i < submarine.HullVertices.Count; i++)
|
||||
{
|
||||
Vector2 start = (submarine.HullVertices[i] + offset) * simScale;
|
||||
start.Y = -start.Y;
|
||||
Vector2 end = (submarine.HullVertices[(i + 1) % submarine.HullVertices.Count] + offset) * simScale;
|
||||
end.Y = -end.Y;
|
||||
|
||||
GUI.DrawLine(spriteBatch, center + start, center + end, Color.LightBlue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (radarBlips.Count > 0)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive);
|
||||
|
||||
foreach (RadarBlip radarBlip in radarBlips)
|
||||
{
|
||||
DrawBlip(spriteBatch, radarBlip, center, radarBlip.FadeTimer / 2.0f);
|
||||
}
|
||||
|
||||
spriteBatch.End();
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, GameMain.ScissorTestEnable);
|
||||
}
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, rect.Location.ToVector2(), radarBlips.Count.ToString(), Color.White);
|
||||
}
|
||||
|
||||
if (screenOverlay != null)
|
||||
{
|
||||
screenOverlay.Draw(spriteBatch, center, 0.0f, rect.Width / screenOverlay.size.X);
|
||||
}
|
||||
|
||||
if (GameMain.GameSession == null) return;
|
||||
|
||||
DrawMarker(spriteBatch,
|
||||
GameMain.GameSession.StartLocation.Name,
|
||||
(Level.Loaded.StartPosition - item.WorldPosition), displayScale, center, (rect.Width * 0.5f));
|
||||
|
||||
DrawMarker(spriteBatch,
|
||||
GameMain.GameSession.EndLocation.Name,
|
||||
(Level.Loaded.EndPosition - item.WorldPosition), displayScale, center, (rect.Width * 0.5f));
|
||||
|
||||
if (GameMain.GameSession.Mission != null)
|
||||
{
|
||||
var mission = GameMain.GameSession.Mission;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mission.RadarLabel) && mission.RadarPosition != Vector2.Zero)
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
mission.RadarLabel,
|
||||
mission.RadarPosition - item.WorldPosition, displayScale, center, (rect.Width * 0.55f));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
if (!sub.OnRadar) continue;
|
||||
if (item.Submarine == sub || sub.DockedTo.Contains(item.Submarine)) continue;
|
||||
if (sub.WorldPosition.Y > Level.Loaded.Size.Y) continue;
|
||||
|
||||
DrawMarker(spriteBatch, sub.Name, sub.WorldPosition - item.WorldPosition, displayScale, center, (rect.Width * 0.45f));
|
||||
}
|
||||
|
||||
if (!GameMain.DebugDraw) return;
|
||||
|
||||
var steering = item.GetComponent<Steering>();
|
||||
if (steering == null || steering.SteeringPath == null) return;
|
||||
|
||||
Vector2 prevPos = Vector2.Zero;
|
||||
|
||||
foreach (WayPoint wp in steering.SteeringPath.Nodes)
|
||||
{
|
||||
Vector2 pos = (wp.Position - item.WorldPosition) * displayScale;
|
||||
if (pos.Length() > displayRadius) continue;
|
||||
|
||||
pos.Y = -pos.Y;
|
||||
pos += center;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X - 3 / 2, (int)pos.Y - 3, 6, 6), (steering.SteeringPath.CurrentNode == wp) ? Color.LightGreen : Color.Green, false);
|
||||
|
||||
if (prevPos != Vector2.Zero)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch, pos, prevPos, Color.Green);
|
||||
}
|
||||
|
||||
prevPos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
private void Ping(Vector2 pingSource, float pingRadius, float prevPingRadius, float displayScale, float range, float pingStrength = 1.0f)
|
||||
{
|
||||
foreach (Submarine submarine in Submarine.Loaded)
|
||||
{
|
||||
if (item.Submarine == submarine && !DetectSubmarineWalls) continue;
|
||||
if (item.Submarine != null && item.Submarine.DockedTo.Contains(submarine)) continue;
|
||||
if (submarine.HullVertices == null) continue;
|
||||
|
||||
for (int i = 0; i < submarine.HullVertices.Count; i++)
|
||||
{
|
||||
Vector2 start = ConvertUnits.ToDisplayUnits(submarine.HullVertices[i]);
|
||||
Vector2 end = ConvertUnits.ToDisplayUnits(submarine.HullVertices[(i + 1) % submarine.HullVertices.Count]);
|
||||
|
||||
if (item.Submarine == submarine)
|
||||
{
|
||||
start += Rand.Vector(500.0f);
|
||||
end += Rand.Vector(500.0f);
|
||||
}
|
||||
|
||||
CreateBlipsForLine(
|
||||
start + submarine.WorldPosition,
|
||||
end + submarine.WorldPosition,
|
||||
pingRadius, prevPingRadius,
|
||||
200.0f, 2.0f, range, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (Level.Loaded != null && (item.CurrentHull == null || !DetectSubmarineWalls))
|
||||
{
|
||||
if (Level.Loaded.Size.Y - pingSource.Y < range)
|
||||
{
|
||||
CreateBlipsForLine(
|
||||
new Vector2(pingSource.X - range, Level.Loaded.Size.Y),
|
||||
new Vector2(pingSource.X + range, Level.Loaded.Size.Y),
|
||||
pingRadius, prevPingRadius,
|
||||
250.0f, 150.0f, range, pingStrength);
|
||||
}
|
||||
|
||||
List<VoronoiCell> cells = Level.Loaded.GetCells(pingSource, 7);
|
||||
foreach (VoronoiCell cell in cells)
|
||||
{
|
||||
foreach (GraphEdge edge in cell.edges)
|
||||
{
|
||||
if (!edge.isSolid) continue;
|
||||
float cellDot = Vector2.Dot(cell.Center - pingSource, (edge.Center + cell.Translation) - cell.Center);
|
||||
if (cellDot > 0) continue;
|
||||
|
||||
float facingDot = Vector2.Dot(
|
||||
Vector2.Normalize(edge.point1 - edge.point2),
|
||||
Vector2.Normalize(cell.Center - pingSource));
|
||||
|
||||
CreateBlipsForLine(
|
||||
edge.point1 + cell.Translation,
|
||||
edge.point2 + cell.Translation,
|
||||
pingRadius, prevPingRadius,
|
||||
350.0f, 3.0f * (Math.Abs(facingDot) + 1.0f), range, pingStrength);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (RuinGeneration.Ruin ruin in Level.Loaded.Ruins)
|
||||
{
|
||||
if (!MathUtils.CircleIntersectsRectangle(pingSource, range, ruin.Area)) continue;
|
||||
|
||||
foreach (var ruinShape in ruin.RuinShapes)
|
||||
{
|
||||
foreach (RuinGeneration.Line wall in ruinShape.Walls)
|
||||
{
|
||||
float cellDot = Vector2.Dot(
|
||||
Vector2.Normalize(ruinShape.Center - pingSource),
|
||||
Vector2.Normalize((wall.A + wall.B) / 2.0f - ruinShape.Center));
|
||||
if (cellDot > 0) continue;
|
||||
|
||||
CreateBlipsForLine(
|
||||
wall.A, wall.B,
|
||||
pingRadius, prevPingRadius,
|
||||
100.0f, 1000.0f, range, pingStrength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.AnimController.CurrentHull != null || !c.Enabled) continue;
|
||||
if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) continue;
|
||||
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
float pointDist = (limb.WorldPosition - pingSource).Length() * displayScale;
|
||||
|
||||
if (limb.SimPosition == Vector2.Zero || pointDist > displayRadius) continue;
|
||||
|
||||
if (pointDist > prevPingRadius && pointDist < pingRadius)
|
||||
{
|
||||
for (int i = 0; i <= limb.Mass / 100.0f; i++)
|
||||
{
|
||||
var blip = new RadarBlip(limb.WorldPosition + Rand.Vector(limb.Mass / 10.0f), MathHelper.Clamp(limb.Mass, 0.1f, pingStrength));
|
||||
radarBlips.Add(blip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateBlipsForLine(Vector2 point1, Vector2 point2, float pingRadius, float prevPingRadius,
|
||||
float lineStep, float zStep, float range, float pingStrength)
|
||||
{
|
||||
float length = (point1 - point2).Length();
|
||||
|
||||
Vector2 lineDir = (point2 - point1) / length;
|
||||
|
||||
range *= displayScale;
|
||||
|
||||
for (float x = 0; x < length; x += lineStep*Rand.Range(0.8f,1.2f))
|
||||
{
|
||||
Vector2 point = point1 + lineDir * x;
|
||||
//point += cell.Translation;
|
||||
|
||||
float pointDist = Vector2.Distance(item.WorldPosition, point) * displayScale;
|
||||
|
||||
if (pointDist > displayRadius) continue;
|
||||
if (pointDist < prevPingRadius || pointDist > pingRadius) continue;
|
||||
|
||||
float alpha = pingStrength * Rand.Range(1.5f, 2.0f);
|
||||
for (float z = 0; z < displayRadius - pointDist * displayScale; z += zStep)
|
||||
{
|
||||
Vector2 pos = point + Rand.Vector(150.0f) + Vector2.Normalize(point - item.WorldPosition) * z / displayScale;
|
||||
float fadeTimer = alpha * (1.0f - pointDist / range);
|
||||
|
||||
int minDist = 200;
|
||||
radarBlips.RemoveAll(b => b.FadeTimer < fadeTimer && Math.Abs(pos.X - b.Position.X) < minDist && Math.Abs(pos.Y - b.Position.Y) < minDist);
|
||||
|
||||
var blip = new RadarBlip(pos, fadeTimer);
|
||||
|
||||
radarBlips.Add(blip);
|
||||
zStep += 0.5f;
|
||||
|
||||
if (z == 0)
|
||||
{
|
||||
alpha = Math.Min(alpha - 0.5f, 1.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
alpha -= 0.1f;
|
||||
}
|
||||
|
||||
if (alpha < 0) break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBlip(SpriteBatch spriteBatch, RadarBlip blip, Vector2 center, float strength)
|
||||
{
|
||||
strength = MathHelper.Clamp(strength, 0.0f, 1.0f);
|
||||
|
||||
Color[] colors = new Color[] {
|
||||
Color.TransparentBlack,
|
||||
new Color(0, 50, 160),
|
||||
new Color(0, 133, 166),
|
||||
new Color(2, 159, 30),
|
||||
new Color(255, 255, 255) };
|
||||
|
||||
float scaledT = strength * (colors.Length - 1);
|
||||
Color color = Color.Lerp(colors[(int)scaledT], colors[(int)Math.Min(scaledT+1, colors.Length-1)], (scaledT - (int)scaledT));
|
||||
|
||||
Vector2 pos = (blip.Position - item.WorldPosition) * displayScale;
|
||||
pos.Y = -pos.Y;
|
||||
|
||||
if (pos.Length() > displayRadius)
|
||||
{
|
||||
blip.FadeTimer = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
float posDist = pos.Length();
|
||||
Vector2 dir = pos / posDist;
|
||||
float distFactor = (posDist / displayRadius);
|
||||
|
||||
Vector2 normal = new Vector2(dir.Y, -dir.X);
|
||||
|
||||
float scale = (strength + 3.0f) * Math.Max(distFactor * 3.0f, 1.0f);
|
||||
|
||||
if (radarBlip == null)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, center + pos, Vector2.One * 4, Color.Magenta, true);
|
||||
return;
|
||||
}
|
||||
|
||||
radarBlip.Draw(spriteBatch, center + pos, color, radarBlip.Origin, MathUtils.VectorToAngle(pos),
|
||||
new Vector2(scale * 0.3f, scale) * 0.04f, SpriteEffects.None, 0);
|
||||
|
||||
pos += Rand.Range(0.0f, 1.0f) * dir + Rand.Range(-scale, scale) * normal;
|
||||
|
||||
radarBlip.Draw(spriteBatch, center + pos, color * 0.5f, radarBlip.Origin, MathUtils.VectorToAngle(pos),
|
||||
new Vector2(scale * 0.3f, scale) * 0.08f, SpriteEffects.None, 0);
|
||||
}
|
||||
|
||||
private void DrawMarker(SpriteBatch spriteBatch, string label, Vector2 position, float scale, Vector2 center, float radius)
|
||||
{
|
||||
//position += Level.Loaded.Position;
|
||||
|
||||
float dist = position.Length();
|
||||
|
||||
position *= scale;
|
||||
position.Y = -position.Y;
|
||||
|
||||
float textAlpha = MathHelper.Clamp(1.5f - dist / 50000.0f, 0.5f, 1.0f);
|
||||
|
||||
Vector2 dir = Vector2.Normalize(position);
|
||||
|
||||
Vector2 markerPos = (dist*scale>radius) ? dir * radius : position;
|
||||
markerPos += center;
|
||||
|
||||
markerPos.X = (int)markerPos.X;
|
||||
markerPos.Y = (int)markerPos.Y;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X, (int)markerPos.Y, 5, 5), Color.LightBlue);
|
||||
|
||||
if (dir.X < 0.0f) markerPos.X -= GUI.SmallFont.MeasureString(label).X+10;
|
||||
|
||||
string wrappedLabel = ToolBox.WrapText(label, 150, GUI.SmallFont);
|
||||
|
||||
wrappedLabel += "\n"+((int)(dist * Physics.DisplayToRealWorldRatio) + " m");
|
||||
|
||||
GUI.DrawString(spriteBatch,
|
||||
new Vector2(markerPos.X + 10, markerPos.Y),
|
||||
wrappedLabel,
|
||||
Color.LightBlue * textAlpha, Color.Black * textAlpha * 0.5f,
|
||||
2, GUI.SmallFont);
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
if (pingCircle!=null) pingCircle.Remove();
|
||||
if (screenOverlay != null) screenOverlay.Remove();
|
||||
}
|
||||
|
||||
public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null)
|
||||
{
|
||||
msg.Write(IsActive);
|
||||
}
|
||||
|
||||
public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c)
|
||||
{
|
||||
bool isActive = msg.ReadBoolean();
|
||||
|
||||
if (!item.CanClientAccess(c)) return;
|
||||
|
||||
IsActive = isActive;
|
||||
isActiveTickBox.Selected = IsActive;
|
||||
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
|
||||
public void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(IsActive);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
|
||||
{
|
||||
if (correctionTimer > 0.0f)
|
||||
{
|
||||
StartDelayedCorrection(type, msg.ExtractBits(1), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
IsActive = msg.ReadBoolean();
|
||||
isActiveTickBox.Selected = IsActive;
|
||||
}
|
||||
}
|
||||
|
||||
class RadarBlip
|
||||
{
|
||||
public float FadeTimer;
|
||||
public Vector2 Position;
|
||||
|
||||
public RadarBlip(Vector2 pos, float fadeTimer)
|
||||
{
|
||||
Position = pos;
|
||||
FadeTimer = Math.Max(fadeTimer, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,666 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class Reactor : Powered, IDrawableComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
const float NetworkUpdateInterval = 0.5f;
|
||||
|
||||
//the rate at which the reactor is being run un
|
||||
//higher rates generate more power (and heat)
|
||||
private float fissionRate;
|
||||
|
||||
//the rate at which the heat is being dissipated
|
||||
private float coolingRate;
|
||||
|
||||
private float temperature;
|
||||
|
||||
//is automatic temperature control on
|
||||
//(adjusts the cooling rate automatically to keep the
|
||||
//amount of power generated balanced with the load)
|
||||
private bool autoTemp;
|
||||
|
||||
//the temperature after which fissionrate is automatically
|
||||
//turned down and cooling increased
|
||||
private float shutDownTemp;
|
||||
|
||||
private float fireTemp, meltDownTemp;
|
||||
|
||||
//how much power is provided to the grid per 1 temperature unit
|
||||
private float powerPerTemp;
|
||||
|
||||
private int graphSize = 25;
|
||||
|
||||
private float graphTimer;
|
||||
|
||||
private int updateGraphInterval = 500;
|
||||
|
||||
private float[] fissionRateGraph;
|
||||
private float[] coolingRateGraph;
|
||||
private float[] tempGraph;
|
||||
private float[] loadGraph;
|
||||
|
||||
private float load;
|
||||
|
||||
private PropertyTask powerUpTask;
|
||||
|
||||
private GUITickBox autoTempTickBox;
|
||||
|
||||
private bool unsentChanges;
|
||||
private float sendUpdateTimer;
|
||||
|
||||
private Character lastUser;
|
||||
private float? nextServerLogWriteTime;
|
||||
private float lastServerLogWriteTime;
|
||||
|
||||
[Editable, HasDefaultValue(9500.0f, true)]
|
||||
public float MeltDownTemp
|
||||
{
|
||||
get { return meltDownTemp; }
|
||||
set
|
||||
{
|
||||
meltDownTemp = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, HasDefaultValue(9000.0f, true)]
|
||||
public float FireTemp
|
||||
{
|
||||
get { return fireTemp; }
|
||||
set
|
||||
{
|
||||
fireTemp = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, HasDefaultValue(1.0f, true)]
|
||||
public float PowerPerTemp
|
||||
{
|
||||
get { return powerPerTemp; }
|
||||
set
|
||||
{
|
||||
powerPerTemp = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(0.0f, true)]
|
||||
public float FissionRate
|
||||
{
|
||||
get { return fissionRate; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
fissionRate = MathHelper.Clamp(value, 0.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(0.0f, true)]
|
||||
public float CoolingRate
|
||||
{
|
||||
get { return coolingRate; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
coolingRate = MathHelper.Clamp(value, 0.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(0.0f, true)]
|
||||
public float Temperature
|
||||
{
|
||||
get { return temperature; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
temperature = MathHelper.Clamp(value, 0.0f, 10000.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRunning()
|
||||
{
|
||||
return (temperature > 0.0f);
|
||||
}
|
||||
|
||||
[HasDefaultValue(false, true)]
|
||||
public bool AutoTemp
|
||||
{
|
||||
get { return autoTemp; }
|
||||
set
|
||||
{
|
||||
autoTemp = value;
|
||||
if (autoTempTickBox!=null) autoTempTickBox.Selected = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float ExtraCooling { get; set; }
|
||||
|
||||
public float AvailableFuel { get; set; }
|
||||
|
||||
[HasDefaultValue(500.0f, true)]
|
||||
public float ShutDownTemp
|
||||
{
|
||||
get { return shutDownTemp; }
|
||||
set { shutDownTemp = MathHelper.Clamp(value, 0.0f, 10000.0f); }
|
||||
}
|
||||
|
||||
public Reactor(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
fissionRateGraph = new float[graphSize];
|
||||
coolingRateGraph = new float[graphSize];
|
||||
tempGraph = new float[graphSize];
|
||||
loadGraph = new float[graphSize];
|
||||
|
||||
shutDownTemp = 500.0f;
|
||||
|
||||
powerPerTemp = 1.0f;
|
||||
|
||||
IsActive = true;
|
||||
|
||||
var button = new GUIButton(new Rectangle(410, 70, 40, 40), "-", "", GuiFrame);
|
||||
button.OnPressed = () =>
|
||||
{
|
||||
lastUser = Character.Controlled;
|
||||
if (nextServerLogWriteTime == null)
|
||||
{
|
||||
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
|
||||
}
|
||||
unsentChanges = true;
|
||||
ShutDownTemp -= 100.0f;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
button = new GUIButton(new Rectangle(460, 70, 40,40), "+", "", GuiFrame);
|
||||
button.OnPressed = () =>
|
||||
{
|
||||
lastUser = Character.Controlled;
|
||||
if (nextServerLogWriteTime == null)
|
||||
{
|
||||
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
|
||||
}
|
||||
unsentChanges = true;
|
||||
ShutDownTemp += 100.0f;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
autoTempTickBox = new GUITickBox(new Rectangle(410, 170, 20, 20), "Automatic temperature control", Alignment.TopLeft, GuiFrame);
|
||||
autoTempTickBox.OnSelected = ToggleAutoTemp;
|
||||
|
||||
button = new GUIButton(new Rectangle(210, 290, 40, 40), "+", "", GuiFrame);
|
||||
button.OnPressed = () =>
|
||||
{
|
||||
lastUser = Character.Controlled;
|
||||
if (nextServerLogWriteTime == null)
|
||||
{
|
||||
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
|
||||
}
|
||||
unsentChanges = true;
|
||||
FissionRate += 1.0f;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
button = new GUIButton(new Rectangle(210, 340, 40, 40), "-", "", GuiFrame);
|
||||
button.OnPressed = () =>
|
||||
{
|
||||
lastUser = Character.Controlled;
|
||||
if (nextServerLogWriteTime == null)
|
||||
{
|
||||
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
|
||||
}
|
||||
unsentChanges = true;
|
||||
FissionRate -= 1.0f;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
button = new GUIButton(new Rectangle(500, 290, 40, 40), "+", "", GuiFrame);
|
||||
button.OnPressed = () =>
|
||||
{
|
||||
lastUser = Character.Controlled;
|
||||
if (nextServerLogWriteTime == null)
|
||||
{
|
||||
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
|
||||
}
|
||||
unsentChanges = true;
|
||||
CoolingRate += 1.0f;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
button = new GUIButton(new Rectangle(500, 340, 40, 40), "-", "", GuiFrame);
|
||||
button.OnPressed = () =>
|
||||
{
|
||||
lastUser = Character.Controlled;
|
||||
if (nextServerLogWriteTime == null)
|
||||
{
|
||||
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
|
||||
}
|
||||
unsentChanges = true;
|
||||
CoolingRate -= 1.0f;
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (GameMain.Server != null && nextServerLogWriteTime != null)
|
||||
{
|
||||
if (Timing.TotalTime >= (float)nextServerLogWriteTime)
|
||||
{
|
||||
GameServer.Log(lastUser + " adjusted reactor settings: " +
|
||||
"Temperature: " + (int)temperature +
|
||||
", Fission rate: " + (int)fissionRate +
|
||||
", Cooling rate: " + (int)coolingRate +
|
||||
", Cooling rate: " + coolingRate +
|
||||
", Shutdown temp: " + shutDownTemp +
|
||||
(autoTemp ? ", Autotemp ON" : ", Autotemp OFF"),
|
||||
ServerLog.MessageType.ItemInteraction);
|
||||
|
||||
nextServerLogWriteTime = null;
|
||||
lastServerLogWriteTime = (float)Timing.TotalTime;
|
||||
}
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
fissionRate = Math.Min(fissionRate, AvailableFuel);
|
||||
|
||||
float heat = 80 * fissionRate * (AvailableFuel/2000.0f);
|
||||
float heatDissipation = 50 * coolingRate + Math.Max(ExtraCooling, 5.0f);
|
||||
|
||||
float deltaTemp = (((heat - heatDissipation) * 5) - temperature) / 10000.0f;
|
||||
Temperature = temperature + deltaTemp;
|
||||
|
||||
if (temperature>fireTemp && temperature-deltaTemp<fireTemp)
|
||||
{
|
||||
Vector2 baseVel = Rand.Vector(300.0f);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var particle = GameMain.ParticleManager.CreateParticle("spark", item.WorldPosition,
|
||||
baseVel + Rand.Vector(100.0f), 0.0f, item.CurrentHull);
|
||||
|
||||
if (particle != null) particle.Size *= Rand.Range(0.5f, 1.0f);
|
||||
}
|
||||
|
||||
new FireSource(item.WorldPosition);
|
||||
}
|
||||
|
||||
if (temperature > meltDownTemp)
|
||||
{
|
||||
MeltDown();
|
||||
return;
|
||||
}
|
||||
else if (temperature == 0.0f)
|
||||
{
|
||||
if (powerUpTask == null || powerUpTask.IsFinished)
|
||||
{
|
||||
powerUpTask = new PropertyTask(item, IsRunning, 50.0f, "Power up the reactor");
|
||||
}
|
||||
}
|
||||
|
||||
load = 0.0f;
|
||||
|
||||
List<Connection> connections = item.Connections;
|
||||
if (connections != null && connections.Count > 0)
|
||||
{
|
||||
foreach (Connection connection in connections)
|
||||
{
|
||||
if (!connection.IsPower) continue;
|
||||
foreach (Connection recipient in connection.Recipients)
|
||||
{
|
||||
Item it = recipient.Item as Item;
|
||||
if (it == null) continue;
|
||||
|
||||
PowerTransfer pt = it.GetComponent<PowerTransfer>();
|
||||
if (pt == null) continue;
|
||||
|
||||
load = Math.Max(load,pt.PowerLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//item.Condition -= temperature * deltaTime * 0.00005f;
|
||||
|
||||
if (temperature > shutDownTemp)
|
||||
{
|
||||
CoolingRate += 0.5f;
|
||||
FissionRate -= 0.5f;
|
||||
}
|
||||
else if (autoTemp)
|
||||
{
|
||||
//take deltaTemp into account to slow down the change in temperature when getting closer to the desired value
|
||||
float target = temperature + deltaTemp * 100.0f;
|
||||
|
||||
//-1.0f in order to gradually turn down both rates when the target temperature is reached
|
||||
FissionRate += (MathHelper.Clamp(load - target, -10.0f, 10.0f) - 1.0f) * deltaTime;
|
||||
CoolingRate += (MathHelper.Clamp(target - load, -5.0f, 5.0f) - 1.0f) * deltaTime;
|
||||
}
|
||||
|
||||
//the power generated by the reactor is equal to the temperature
|
||||
currPowerConsumption = -temperature*powerPerTemp;
|
||||
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
//the sound can be heard from 20 000 display units away when running at full power
|
||||
item.CurrentHull.SoundRange = Math.Max(temperature * 2, item.CurrentHull.AiTarget.SoundRange);
|
||||
}
|
||||
|
||||
UpdateGraph(deltaTime);
|
||||
|
||||
ExtraCooling = 0.0f;
|
||||
AvailableFuel = 0.0f;
|
||||
|
||||
item.SendSignal(0, ((int)temperature).ToString(), "temperature_out", null);
|
||||
|
||||
sendUpdateTimer = Math.Max(sendUpdateTimer - deltaTime, 0.0f);
|
||||
|
||||
if (unsentChanges && sendUpdateTimer<= 0.0f)
|
||||
{
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
else if (GameMain.Client != null)
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
}
|
||||
|
||||
sendUpdateTimer = NetworkUpdateInterval;
|
||||
unsentChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
base.UpdateBroken(deltaTime, cam);
|
||||
|
||||
Temperature -= deltaTime * 1000.0f;
|
||||
FissionRate -= deltaTime * 10.0f;
|
||||
CoolingRate -= deltaTime * 10.0f;
|
||||
|
||||
currPowerConsumption = -temperature;
|
||||
|
||||
UpdateGraph(deltaTime);
|
||||
|
||||
ExtraCooling = 0.0f;
|
||||
}
|
||||
|
||||
private void UpdateGraph(float deltaTime)
|
||||
{
|
||||
graphTimer += deltaTime * 1000.0f;
|
||||
|
||||
if (graphTimer > updateGraphInterval)
|
||||
{
|
||||
UpdateGraph(fissionRateGraph, fissionRate);
|
||||
UpdateGraph(coolingRateGraph, coolingRate);
|
||||
UpdateGraph(tempGraph, temperature);
|
||||
|
||||
UpdateGraph(loadGraph, load);
|
||||
|
||||
graphTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void MeltDown()
|
||||
{
|
||||
if (item.Condition <= 0.0f) return;
|
||||
|
||||
GameServer.Log("Reactor meltdown!", ServerLog.MessageType.ItemInteraction);
|
||||
|
||||
new RepairTask(item, 60.0f, "Reactor meltdown!");
|
||||
item.Condition = 0.0f;
|
||||
|
||||
var containedItems = item.ContainedItems;
|
||||
if (containedItems == null) return;
|
||||
|
||||
foreach (Item containedItem in item.ContainedItems)
|
||||
{
|
||||
if (containedItem == null) continue;
|
||||
containedItem.Condition = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return picker != null;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Vector2(item.Rect.X + item.Rect.Width / 2 - 6, -item.Rect.Y + 29),
|
||||
new Vector2(12, 42), Color.Black);
|
||||
|
||||
if (temperature > 0)
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Vector2(item.Rect.X + item.Rect.Width / 2 - 5, -item.Rect.Y + 30 + (40.0f * (1.0f - temperature / 10000.0f))),
|
||||
new Vector2(10, 40 * (temperature / 10000.0f)), new Color(temperature / 10000.0f, 1.0f - (temperature / 10000.0f), 0.0f, 1.0f), true);
|
||||
}
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
switch (objective.Option.ToLowerInvariant())
|
||||
{
|
||||
case "power up":
|
||||
float tempDiff = load - temperature;
|
||||
|
||||
shutDownTemp = Math.Min(load + 1000.0f, 7500.0f);
|
||||
|
||||
//temperature too high/low
|
||||
if (Math.Abs(tempDiff)>500.0f)
|
||||
{
|
||||
AutoTemp = false;
|
||||
FissionRate += deltaTime * 100.0f * Math.Sign(tempDiff);
|
||||
CoolingRate -= deltaTime * 100.0f * Math.Sign(tempDiff);
|
||||
}
|
||||
//temperature OK
|
||||
else
|
||||
{
|
||||
AutoTemp = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case "shutdown":
|
||||
|
||||
shutDownTemp = 0.0f;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
int x = GuiFrame.Rect.X;
|
||||
int y = GuiFrame.Rect.Y;
|
||||
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
float xOffset = graphTimer / updateGraphInterval;
|
||||
|
||||
//GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black, true);
|
||||
|
||||
GUI.Font.DrawString(spriteBatch, "Output: " + (int)temperature + " kW",
|
||||
new Vector2(x + 450, y + 30), Color.Red);
|
||||
GUI.Font.DrawString(spriteBatch, "Grid load: " + (int)load + " kW",
|
||||
new Vector2(x + 600, y + 30), Color.Yellow);
|
||||
|
||||
float maxLoad = 0.0f;
|
||||
foreach (float loadVal in loadGraph)
|
||||
{
|
||||
maxLoad = Math.Max(maxLoad, loadVal);
|
||||
}
|
||||
|
||||
DrawGraph(tempGraph, spriteBatch,
|
||||
new Rectangle(x + 30, y + 30, 400, 250), Math.Max(10000.0f, maxLoad), xOffset, Color.Red);
|
||||
|
||||
DrawGraph(loadGraph, spriteBatch,
|
||||
new Rectangle(x + 30, y + 30, 400, 250), Math.Max(10000.0f, maxLoad), xOffset, Color.Yellow);
|
||||
|
||||
GUI.Font.DrawString(spriteBatch, "Shutdown Temperature: " + (int)shutDownTemp, new Vector2(x + 450, y + 80), Color.White);
|
||||
|
||||
//GUI.Font.DrawString(spriteBatch, "Automatic Temperature Control: " + ((autoTemp) ? "ON" : "OFF"), new Vector2(x + 450, y + 180), Color.White);
|
||||
|
||||
y += 300;
|
||||
|
||||
GUI.Font.DrawString(spriteBatch, "Fission rate: " + (int)fissionRate + " %", new Vector2(x + 30, y), Color.White);
|
||||
DrawGraph(fissionRateGraph, spriteBatch,
|
||||
new Rectangle(x + 30, y + 30, 200, 100), 100.0f, xOffset, Color.Orange);
|
||||
|
||||
|
||||
GUI.Font.DrawString(spriteBatch, "Cooling rate: " + (int)coolingRate + " %", new Vector2(x + 320, y), Color.White);
|
||||
DrawGraph(coolingRateGraph, spriteBatch,
|
||||
new Rectangle(x + 320, y + 30, 200, 100), 100.0f, xOffset, Color.LightBlue);
|
||||
|
||||
|
||||
//y = y - 260;
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
GuiFrame.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character)
|
||||
{
|
||||
GuiFrame.Update(1.0f / 60.0f);
|
||||
}
|
||||
|
||||
private bool ToggleAutoTemp(GUITickBox tickBox)
|
||||
{
|
||||
unsentChanges = true;
|
||||
autoTemp = tickBox.Selected;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void UpdateGraph<T>(IList<T> graph, T newValue)
|
||||
{
|
||||
for (int i = graph.Count - 1; i > 0; i--)
|
||||
{
|
||||
graph[i] = graph[i - 1];
|
||||
}
|
||||
graph[0] = newValue;
|
||||
}
|
||||
|
||||
static void DrawGraph(IList<float> graph, SpriteBatch spriteBatch, Rectangle rect, float maxVal, float xOffset, Color color)
|
||||
{
|
||||
float lineWidth = (float)rect.Width / (float)(graph.Count - 2);
|
||||
float yScale = (float)rect.Height / maxVal;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, rect, Color.White);
|
||||
|
||||
Vector2 prevPoint = new Vector2(rect.Right, rect.Bottom - (graph[1] + (graph[0] - graph[1]) * xOffset) * yScale);
|
||||
|
||||
float currX = rect.Right - ((xOffset - 1.0f) * lineWidth);
|
||||
|
||||
for (int i = 1; i < graph.Count - 1; i++)
|
||||
{
|
||||
currX -= lineWidth;
|
||||
|
||||
Vector2 newPoint = new Vector2(currX, rect.Bottom - graph[i] * yScale);
|
||||
|
||||
GUI.DrawLine(spriteBatch, prevPoint, newPoint - new Vector2(1.0f, 0), color);
|
||||
|
||||
prevPoint = newPoint;
|
||||
}
|
||||
|
||||
Vector2 lastPoint = new Vector2(rect.X,
|
||||
rect.Bottom - (graph[graph.Count - 1] + (graph[graph.Count - 2] - graph[graph.Count - 1]) * xOffset) * yScale);
|
||||
|
||||
GUI.DrawLine(spriteBatch, prevPoint, lastPoint, color);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "shutdown":
|
||||
if (shutDownTemp > 0.0f)
|
||||
{
|
||||
unsentChanges = true;
|
||||
shutDownTemp = 0.0f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(NetBuffer msg, object[] extraData = null)
|
||||
{
|
||||
msg.Write(autoTemp);
|
||||
msg.WriteRangedSingle(shutDownTemp, 0.0f, 10000.0f, 15);
|
||||
|
||||
msg.WriteRangedSingle(coolingRate, 0.0f, 100.0f, 8);
|
||||
msg.WriteRangedSingle(fissionRate, 0.0f, 100.0f, 8);
|
||||
|
||||
correctionTimer = CorrectionDelay;
|
||||
}
|
||||
|
||||
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
|
||||
{
|
||||
bool autoTemp = msg.ReadBoolean();
|
||||
float shutDownTemp = msg.ReadRangedSingle(0.0f, 10000.0f, 15);
|
||||
float coolingRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||
float fissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||
|
||||
if (!item.CanClientAccess(c)) return;
|
||||
|
||||
AutoTemp = autoTemp;
|
||||
ShutDownTemp = shutDownTemp;
|
||||
|
||||
CoolingRate = coolingRate;
|
||||
FissionRate = fissionRate;
|
||||
|
||||
lastUser = c.Character;
|
||||
if (nextServerLogWriteTime == null)
|
||||
{
|
||||
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
|
||||
}
|
||||
|
||||
//need to create a server event to notify all clients of the changed state
|
||||
unsentChanges = true;
|
||||
}
|
||||
|
||||
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.WriteRangedSingle(temperature, 0.0f, 10000.0f, 16);
|
||||
|
||||
msg.Write(autoTemp);
|
||||
msg.WriteRangedSingle(shutDownTemp, 0.0f, 10000.0f, 15);
|
||||
|
||||
msg.WriteRangedSingle(coolingRate, 0.0f, 100.0f, 8);
|
||||
msg.WriteRangedSingle(fissionRate, 0.0f, 100.0f, 8);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
|
||||
{
|
||||
if (correctionTimer > 0.0f)
|
||||
{
|
||||
StartDelayedCorrection(type, msg.ExtractBits(16 + 1 + 15 + 8 + 8), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
Temperature = msg.ReadRangedSingle(0.0f, 10000.0f, 16);
|
||||
|
||||
AutoTemp = msg.ReadBoolean();
|
||||
ShutDownTemp = msg.ReadRangedSingle(0.0f, 10000.0f, 15);
|
||||
|
||||
CoolingRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||
FissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,662 @@
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Voronoi2;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class Steering : Powered, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private const float AutopilotRayCastInterval = 0.5f;
|
||||
|
||||
private Vector2 currVelocity;
|
||||
private Vector2 targetVelocity;
|
||||
|
||||
private GUITickBox autopilotTickBox, maintainPosTickBox;
|
||||
private GUITickBox levelEndTickBox, levelStartTickBox;
|
||||
|
||||
private bool autoPilot;
|
||||
|
||||
private Vector2? posToMaintain;
|
||||
|
||||
private SteeringPath steeringPath;
|
||||
|
||||
private PathFinder pathFinder;
|
||||
|
||||
private float networkUpdateTimer;
|
||||
private bool unsentChanges;
|
||||
|
||||
private float autopilotRayCastTimer;
|
||||
|
||||
private Vector2 avoidStrength;
|
||||
|
||||
private float neutralBallastLevel;
|
||||
|
||||
public bool AutoPilot
|
||||
{
|
||||
get { return autoPilot; }
|
||||
set
|
||||
{
|
||||
if (value == autoPilot) return;
|
||||
|
||||
autoPilot = value;
|
||||
autopilotTickBox.Selected = value;
|
||||
|
||||
maintainPosTickBox.Enabled = autoPilot;
|
||||
levelEndTickBox.Enabled = autoPilot;
|
||||
levelStartTickBox.Enabled = autoPilot;
|
||||
|
||||
if (autoPilot)
|
||||
{
|
||||
if (pathFinder == null) pathFinder = new PathFinder(WayPoint.WayPointList, false);
|
||||
ToggleMaintainPosition(maintainPosTickBox);
|
||||
}
|
||||
else
|
||||
{
|
||||
maintainPosTickBox.Selected = false;
|
||||
levelEndTickBox.Selected = false;
|
||||
levelStartTickBox.Selected = false;
|
||||
|
||||
posToMaintain = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool MaintainPos
|
||||
{
|
||||
get { return maintainPosTickBox.Selected; }
|
||||
set { maintainPosTickBox.Selected = value; }
|
||||
}
|
||||
|
||||
|
||||
[Editable, HasDefaultValue(0.5f, true)]
|
||||
public float NeutralBallastLevel
|
||||
{
|
||||
get { return neutralBallastLevel; }
|
||||
set
|
||||
{
|
||||
neutralBallastLevel = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 TargetVelocity
|
||||
{
|
||||
get { return targetVelocity;}
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
targetVelocity.X = MathHelper.Clamp(value.X, -100.0f, 100.0f);
|
||||
targetVelocity.Y = MathHelper.Clamp(value.Y, -100.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public SteeringPath SteeringPath
|
||||
{
|
||||
get { return steeringPath; }
|
||||
}
|
||||
|
||||
public Steering(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
autopilotTickBox = new GUITickBox(new Rectangle(0,25,20,20), "Autopilot", Alignment.TopLeft, GuiFrame);
|
||||
autopilotTickBox.OnSelected = (GUITickBox box) =>
|
||||
{
|
||||
AutoPilot = box.Selected;
|
||||
unsentChanges = true;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
maintainPosTickBox = new GUITickBox(new Rectangle(5, 50, 15, 15), "Maintain position", Alignment.TopLeft, GUI.SmallFont, GuiFrame);
|
||||
maintainPosTickBox.Enabled = false;
|
||||
maintainPosTickBox.OnSelected = ToggleMaintainPosition;
|
||||
|
||||
levelStartTickBox = new GUITickBox(
|
||||
new Rectangle(5, 70, 15, 15),
|
||||
GameMain.GameSession == null ? "" : ToolBox.LimitString(GameMain.GameSession.StartLocation.Name, 20),
|
||||
Alignment.TopLeft, GUI.SmallFont, GuiFrame);
|
||||
levelStartTickBox.Enabled = false;
|
||||
levelStartTickBox.OnSelected = SelectDestination;
|
||||
|
||||
levelEndTickBox = new GUITickBox(
|
||||
new Rectangle(5, 90, 15, 15),
|
||||
GameMain.GameSession == null ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.Name, 20),
|
||||
Alignment.TopLeft, GUI.SmallFont, GuiFrame);
|
||||
levelEndTickBox.Enabled = false;
|
||||
levelEndTickBox.OnSelected = SelectDestination;
|
||||
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (unsentChanges)
|
||||
{
|
||||
networkUpdateTimer -= deltaTime;
|
||||
if (networkUpdateTimer <= 0.0f)
|
||||
{
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
item.CreateClientEvent(this);
|
||||
correctionTimer = CorrectionDelay;
|
||||
}
|
||||
else if (GameMain.Server != null)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
|
||||
networkUpdateTimer = 0.5f;
|
||||
unsentChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (voltage < minVoltage && powerConsumption > 0.0f) return;
|
||||
|
||||
if (autoPilot)
|
||||
{
|
||||
UpdateAutoPilot(deltaTime);
|
||||
}
|
||||
|
||||
item.SendSignal(0, targetVelocity.X.ToString(CultureInfo.InvariantCulture), "velocity_x_out", null);
|
||||
|
||||
float targetLevel = -targetVelocity.Y;
|
||||
|
||||
targetLevel += (neutralBallastLevel - 0.5f) * 100.0f;
|
||||
|
||||
item.SendSignal(0, targetLevel.ToString(CultureInfo.InvariantCulture), "velocity_y_out", null);
|
||||
|
||||
|
||||
voltage -= deltaTime;
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
//if (voltage < minVoltage) return;
|
||||
|
||||
int width = GuiFrame.Rect.Width, height = GuiFrame.Rect.Height;
|
||||
int x = GuiFrame.Rect.X;
|
||||
int y = GuiFrame.Rect.Y;
|
||||
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
if (voltage < minVoltage && powerConsumption > 0.0f) return;
|
||||
|
||||
Rectangle velRect = new Rectangle(x + 20, y + 20, width - 40, height - 40);
|
||||
//GUI.DrawRectangle(spriteBatch, velRect, Color.White, false);
|
||||
|
||||
if (item.Submarine != null && Level.Loaded != null)
|
||||
{
|
||||
Vector2 realWorldVelocity = ConvertUnits.ToDisplayUnits(item.Submarine.Velocity * Physics.DisplayToRealWorldRatio) * 3.6f;
|
||||
float realWorldDepth = Math.Abs(item.Submarine.Position.Y - Level.Loaded.Size.Y) * Physics.DisplayToRealWorldRatio;
|
||||
GUI.DrawString(spriteBatch, new Vector2(x + 20, y + height - 65),
|
||||
"Velocity: " + (int)realWorldVelocity.X + " km/h", Color.LightGreen, null, 0, GUI.SmallFont);
|
||||
GUI.DrawString(spriteBatch, new Vector2(x + 20, y + height - 50),
|
||||
"Descent velocity: " + -(int)realWorldVelocity.Y + " km/h", Color.LightGreen, null, 0, GUI.SmallFont);
|
||||
|
||||
GUI.DrawString(spriteBatch, new Vector2(x + 20, y + height - 30),
|
||||
"Depth: " + (int)realWorldDepth + " m", Color.LightGreen, null, 0, GUI.SmallFont);
|
||||
}
|
||||
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(velRect.Center.X,velRect.Center.Y),
|
||||
new Vector2(velRect.Center.X + currVelocity.X, velRect.Center.Y - currVelocity.Y),
|
||||
Color.Gray);
|
||||
|
||||
Vector2 targetVelPos = new Vector2(velRect.Center.X + targetVelocity.X, velRect.Center.Y - targetVelocity.Y);
|
||||
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(velRect.Center.X, velRect.Center.Y),
|
||||
targetVelPos,
|
||||
Color.LightGray);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)targetVelPos.X - 5, (int)targetVelPos.Y - 5, 10, 10), Color.White);
|
||||
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, new Vector2(velRect.Center.X, velRect.Center.Y)) < 200.0f)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)targetVelPos.X -10, (int)targetVelPos.Y - 10, 20, 20), Color.Red);
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
GuiFrame.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character)
|
||||
{
|
||||
GuiFrame.Update(1.0f / 60.0f);
|
||||
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, new Vector2(GuiFrame.Rect.Center.X, GuiFrame.Rect.Center.Y)) < 200.0f)
|
||||
{
|
||||
if (PlayerInput.LeftButtonHeld())
|
||||
{
|
||||
TargetVelocity = PlayerInput.MousePosition - new Vector2(GuiFrame.Rect.Center.X, GuiFrame.Rect.Center.Y);
|
||||
targetVelocity.Y = -targetVelocity.Y;
|
||||
|
||||
unsentChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAutoPilot(float deltaTime)
|
||||
{
|
||||
if (posToMaintain != null)
|
||||
{
|
||||
SteerTowardsPosition((Vector2)posToMaintain);
|
||||
return;
|
||||
}
|
||||
|
||||
autopilotRayCastTimer -= deltaTime;
|
||||
|
||||
steeringPath.CheckProgress(ConvertUnits.ToSimUnits(item.Submarine.WorldPosition), 10.0f);
|
||||
|
||||
if (autopilotRayCastTimer <= 0.0f && steeringPath.NextNode != null)
|
||||
{
|
||||
Vector2 diff = Vector2.Normalize(ConvertUnits.ToSimUnits(steeringPath.NextNode.Position - item.Submarine.WorldPosition));
|
||||
|
||||
bool nextVisible = true;
|
||||
for (int x = -1; x < 2; x += 2)
|
||||
{
|
||||
for (int y = -1; y < 2; y += 2)
|
||||
{
|
||||
Vector2 cornerPos =
|
||||
new Vector2(item.Submarine.Borders.Width * x, item.Submarine.Borders.Height * y) / 2.0f;
|
||||
|
||||
cornerPos = ConvertUnits.ToSimUnits(cornerPos * 1.2f + item.Submarine.WorldPosition);
|
||||
|
||||
float dist = Vector2.Distance(cornerPos, steeringPath.NextNode.SimPosition);
|
||||
|
||||
if (Submarine.PickBody(cornerPos, cornerPos + diff * dist, null, Physics.CollisionLevel) == null) continue;
|
||||
|
||||
nextVisible = false;
|
||||
x = 2;
|
||||
y = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextVisible) steeringPath.SkipToNextNode();
|
||||
|
||||
autopilotRayCastTimer = AutopilotRayCastInterval;
|
||||
}
|
||||
|
||||
if (steeringPath.CurrentNode != null)
|
||||
{
|
||||
SteerTowardsPosition(steeringPath.CurrentNode.WorldPosition);
|
||||
}
|
||||
|
||||
float avoidRadius = Math.Max(item.Submarine.Borders.Width, item.Submarine.Borders.Height) * 2.0f;
|
||||
avoidRadius = Math.Max(avoidRadius, 2000.0f);
|
||||
|
||||
Vector2 newAvoidStrength = Vector2.Zero;
|
||||
|
||||
//steer away from nearby walls
|
||||
var closeCells = Level.Loaded.GetCells(item.Submarine.WorldPosition, 4);
|
||||
foreach (VoronoiCell cell in closeCells)
|
||||
{
|
||||
foreach (GraphEdge edge in cell.edges)
|
||||
{
|
||||
var intersection = MathUtils.GetLineIntersection(edge.point1, edge.point2, item.Submarine.WorldPosition, cell.Center);
|
||||
if (intersection != null)
|
||||
{
|
||||
Vector2 diff = item.Submarine.WorldPosition - (Vector2)intersection;
|
||||
|
||||
//far enough -> ignore
|
||||
if (diff.Length() > avoidRadius) continue;
|
||||
|
||||
float dot = item.Submarine.Velocity == Vector2.Zero ?
|
||||
0.0f : Vector2.Dot(item.Submarine.Velocity, -Vector2.Normalize(diff));
|
||||
|
||||
//not heading towards the wall -> ignore
|
||||
if (dot < 0.5) continue;
|
||||
|
||||
Vector2 change = (Vector2.Normalize(diff) * Math.Max((avoidRadius - diff.Length()), 0.0f)) / avoidRadius;
|
||||
|
||||
newAvoidStrength += change * dot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
avoidStrength = Vector2.Lerp(avoidStrength, newAvoidStrength, deltaTime * 10.0f);
|
||||
|
||||
targetVelocity += avoidStrength * 100.0f;
|
||||
|
||||
//steer away from other subs
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub == item.Submarine) continue;
|
||||
if (item.Submarine.DockedTo.Contains(sub)) continue;
|
||||
|
||||
float thisSize = Math.Max(item.Submarine.Borders.Width, item.Submarine.Borders.Height);
|
||||
float otherSize = Math.Max(sub.Borders.Width, sub.Borders.Height);
|
||||
|
||||
Vector2 diff = item.Submarine.WorldPosition - sub.WorldPosition;
|
||||
|
||||
float dist = diff == Vector2.Zero ? 0.0f : diff.Length();
|
||||
|
||||
//far enough -> ignore
|
||||
if (dist > thisSize + otherSize) continue;
|
||||
|
||||
diff = Vector2.Normalize(diff);
|
||||
|
||||
float dot = item.Submarine.Velocity == Vector2.Zero ?
|
||||
0.0f : Vector2.Dot(Vector2.Normalize(item.Submarine.Velocity), -Vector2.Normalize(diff));
|
||||
|
||||
//heading away -> ignore
|
||||
if (dot < 0.0f) continue;
|
||||
|
||||
targetVelocity += diff * 200.0f;
|
||||
}
|
||||
|
||||
//clamp velocity magnitude to 100.0f
|
||||
float velMagnitude = targetVelocity.Length();
|
||||
if (velMagnitude > 100.0f)
|
||||
{
|
||||
targetVelocity *= 100.0f / velMagnitude;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void UpdatePath()
|
||||
{
|
||||
if (pathFinder == null) pathFinder = new PathFinder(WayPoint.WayPointList, false);
|
||||
|
||||
Vector2 target;
|
||||
if (levelEndTickBox.Selected)
|
||||
{
|
||||
target = ConvertUnits.ToSimUnits(Level.Loaded.EndPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
target = ConvertUnits.ToSimUnits(Level.Loaded.StartPosition);
|
||||
}
|
||||
|
||||
|
||||
steeringPath = pathFinder.FindPath(ConvertUnits.ToSimUnits(item.WorldPosition), target);
|
||||
}
|
||||
|
||||
private void SteerTowardsPosition(Vector2 worldPosition)
|
||||
{
|
||||
float prediction = 10.0f;
|
||||
|
||||
Vector2 futurePosition = ConvertUnits.ToDisplayUnits(item.Submarine.Velocity) * prediction;
|
||||
Vector2 targetSpeed = ((worldPosition - item.Submarine.WorldPosition) - futurePosition);
|
||||
|
||||
if (targetSpeed.Length()>500.0f)
|
||||
{
|
||||
targetSpeed = Vector2.Normalize(targetSpeed);
|
||||
TargetVelocity = targetSpeed * 100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
TargetVelocity = targetSpeed / 5.0f;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ToggleMaintainPosition(GUITickBox tickBox)
|
||||
{
|
||||
unsentChanges = true;
|
||||
|
||||
levelStartTickBox.Selected = false;
|
||||
levelEndTickBox.Selected = false;
|
||||
|
||||
if (item.Submarine == null)
|
||||
{
|
||||
posToMaintain = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
posToMaintain = item.Submarine.WorldPosition;
|
||||
}
|
||||
|
||||
tickBox.Selected = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetDestinationLevelStart()
|
||||
{
|
||||
AutoPilot = true;
|
||||
|
||||
MaintainPos = false;
|
||||
posToMaintain = null;
|
||||
|
||||
levelEndTickBox.Selected = false;
|
||||
|
||||
if (!levelStartTickBox.Selected)
|
||||
{
|
||||
levelStartTickBox.Selected = true;
|
||||
UpdatePath();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDestinationLevelEnd()
|
||||
{
|
||||
AutoPilot = false;
|
||||
|
||||
MaintainPos = false;
|
||||
posToMaintain = null;
|
||||
|
||||
levelStartTickBox.Selected = false;
|
||||
|
||||
if (!levelEndTickBox.Selected)
|
||||
{
|
||||
levelEndTickBox.Selected = true;
|
||||
UpdatePath();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool SelectDestination(GUITickBox tickBox)
|
||||
{
|
||||
unsentChanges = true;
|
||||
|
||||
if (tickBox == levelStartTickBox)
|
||||
{
|
||||
levelEndTickBox.Selected = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
levelStartTickBox.Selected = false;
|
||||
}
|
||||
|
||||
maintainPosTickBox.Selected = false;
|
||||
posToMaintain = null;
|
||||
tickBox.Selected = true;
|
||||
|
||||
UpdatePath();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power=0.0f)
|
||||
{
|
||||
if (connection.Name == "velocity_in")
|
||||
{
|
||||
currVelocity = ToolBox.ParseToVector2(signal, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ReceiveSignal(stepsTaken, signal, connection, source, sender, power);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(Lidgren.Network.NetBuffer msg, object[] extraData = null)
|
||||
{
|
||||
msg.Write(autoPilot);
|
||||
|
||||
if (!autoPilot)
|
||||
{
|
||||
//no need to write steering info if autopilot is controlling
|
||||
msg.Write(targetVelocity.X);
|
||||
msg.Write(targetVelocity.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write(posToMaintain != null);
|
||||
if (posToMaintain != null)
|
||||
{
|
||||
msg.Write(((Vector2)posToMaintain).X);
|
||||
msg.Write(((Vector2)posToMaintain).Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write(levelStartTickBox.Selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c)
|
||||
{
|
||||
bool autoPilot = msg.ReadBoolean();
|
||||
Vector2 newTargetVelocity = targetVelocity;
|
||||
bool maintainPos = false;
|
||||
Vector2? newPosToMaintain = null;
|
||||
bool headingToStart = false;
|
||||
|
||||
if (autoPilot)
|
||||
{
|
||||
maintainPos = msg.ReadBoolean();
|
||||
if (maintainPos)
|
||||
{
|
||||
newPosToMaintain = new Vector2(
|
||||
msg.ReadFloat(),
|
||||
msg.ReadFloat());
|
||||
}
|
||||
else
|
||||
{
|
||||
headingToStart = msg.ReadBoolean();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newTargetVelocity = new Vector2(msg.ReadFloat(), msg.ReadFloat());
|
||||
}
|
||||
|
||||
if (!item.CanClientAccess(c)) return;
|
||||
|
||||
AutoPilot = autoPilot;
|
||||
|
||||
if (!AutoPilot)
|
||||
{
|
||||
targetVelocity = newTargetVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
maintainPosTickBox.Selected = newPosToMaintain != null;
|
||||
posToMaintain = newPosToMaintain;
|
||||
|
||||
if (posToMaintain == null)
|
||||
{
|
||||
levelStartTickBox.Selected = headingToStart;
|
||||
levelEndTickBox.Selected = !headingToStart;
|
||||
UpdatePath();
|
||||
}
|
||||
else
|
||||
{
|
||||
levelStartTickBox.Selected = false;
|
||||
levelEndTickBox.Selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
//notify all clients of the changed state
|
||||
unsentChanges = true;
|
||||
}
|
||||
|
||||
public void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c, object[] extraData = null)
|
||||
{
|
||||
msg.Write(autoPilot);
|
||||
|
||||
if (!autoPilot)
|
||||
{
|
||||
//no need to write steering info if autopilot is controlling
|
||||
msg.Write(targetVelocity.X);
|
||||
msg.Write(targetVelocity.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write(posToMaintain != null);
|
||||
if (posToMaintain != null)
|
||||
{
|
||||
msg.Write(((Vector2)posToMaintain).X);
|
||||
msg.Write(((Vector2)posToMaintain).Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write(levelStartTickBox.Selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
|
||||
{
|
||||
long msgStartPos = msg.Position;
|
||||
|
||||
bool autoPilot = msg.ReadBoolean();
|
||||
Vector2 newTargetVelocity = targetVelocity;
|
||||
bool maintainPos = false;
|
||||
Vector2? newPosToMaintain = null;
|
||||
bool headingToStart = false;
|
||||
|
||||
if (autoPilot)
|
||||
{
|
||||
maintainPos = msg.ReadBoolean();
|
||||
if (maintainPos)
|
||||
{
|
||||
newPosToMaintain = new Vector2(
|
||||
msg.ReadFloat(),
|
||||
msg.ReadFloat());
|
||||
}
|
||||
else
|
||||
{
|
||||
headingToStart = msg.ReadBoolean();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newTargetVelocity = new Vector2(msg.ReadFloat(), msg.ReadFloat());
|
||||
}
|
||||
|
||||
if (correctionTimer > 0.0f)
|
||||
{
|
||||
int msgLength = (int)(msg.Position - msgStartPos);
|
||||
msg.Position = msgStartPos;
|
||||
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
AutoPilot = autoPilot;
|
||||
|
||||
if (!AutoPilot)
|
||||
{
|
||||
targetVelocity = newTargetVelocity;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
maintainPosTickBox.Selected = newPosToMaintain != null;
|
||||
posToMaintain = newPosToMaintain;
|
||||
|
||||
if (posToMaintain == null)
|
||||
{
|
||||
levelStartTickBox.Selected = headingToStart;
|
||||
levelEndTickBox.Selected = !headingToStart;
|
||||
UpdatePath();
|
||||
}
|
||||
else
|
||||
{
|
||||
levelStartTickBox.Selected = false;
|
||||
levelEndTickBox.Selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class Vent : ItemComponent
|
||||
{
|
||||
private float oxygenFlow;
|
||||
|
||||
public float OxygenFlow
|
||||
{
|
||||
get { return oxygenFlow; }
|
||||
set { oxygenFlow = Math.Max(value, 0.0f); }
|
||||
}
|
||||
|
||||
public Vent (Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (item.CurrentHull == null) return;
|
||||
|
||||
if (item.InWater) return;
|
||||
|
||||
item.CurrentHull.Oxygen += oxygenFlow * deltaTime;
|
||||
OxygenFlow -= deltaTime * 1000.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user