(61d00a474) v0.9.7.1
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Contacts;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
partial class RespawnManager : Entity, IServerSerializable
|
||||
{
|
||||
public enum State
|
||||
{
|
||||
Waiting,
|
||||
Transporting,
|
||||
Returning
|
||||
}
|
||||
|
||||
private NetworkMember networkMember;
|
||||
private Steering shuttleSteering;
|
||||
private List<Door> shuttleDoors;
|
||||
|
||||
//items created during respawn
|
||||
//any respawn items left in the shuttle are removed when the shuttle despawns
|
||||
private List<Item> respawnItems = new List<Item>();
|
||||
|
||||
public bool UsingShuttle
|
||||
{
|
||||
get { return RespawnShuttle != null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When will the shuttle be dispatched with respawned characters
|
||||
/// </summary>
|
||||
public DateTime RespawnTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// When will the sub start heading back out of the level
|
||||
/// </summary>
|
||||
public DateTime ReturnTime { get; private set; }
|
||||
|
||||
public bool RespawnCountdownStarted
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool ReturnCountdownStarted
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public State CurrentState { get; private set; }
|
||||
|
||||
private DateTime despawnTime;
|
||||
|
||||
private float maxTransportTime;
|
||||
|
||||
private float updateReturnTimer;
|
||||
|
||||
public Submarine RespawnShuttle { get; private set; }
|
||||
|
||||
public RespawnManager(NetworkMember networkMember, Submarine shuttle)
|
||||
: base(shuttle)
|
||||
{
|
||||
this.networkMember = networkMember;
|
||||
|
||||
if (shuttle != null)
|
||||
{
|
||||
RespawnShuttle = new Submarine(shuttle.FilePath, shuttle.MD5Hash.Hash, true);
|
||||
RespawnShuttle.Load(false);
|
||||
RespawnShuttle.PhysicsBody.FarseerBody.OnCollision += OnShuttleCollision;
|
||||
|
||||
//prevent wifi components from communicating between the respawn shuttle and other subs
|
||||
List<WifiComponent> wifiComponents = new List<WifiComponent>();
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine == RespawnShuttle) { wifiComponents.AddRange(item.GetComponents<WifiComponent>()); }
|
||||
}
|
||||
foreach (WifiComponent wifiComponent in wifiComponents)
|
||||
{
|
||||
wifiComponent.TeamID = Character.TeamType.FriendlyNPC;
|
||||
}
|
||||
|
||||
ResetShuttle();
|
||||
|
||||
shuttleDoors = new List<Door>();
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine != RespawnShuttle) continue;
|
||||
|
||||
var steering = item.GetComponent<Steering>();
|
||||
if (steering != null) shuttleSteering = steering;
|
||||
|
||||
var door = item.GetComponent<Door>();
|
||||
if (door != null) shuttleDoors.Add(door);
|
||||
|
||||
//lock all wires to prevent the players from messing up the electronics
|
||||
var connectionPanel = item.GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel != null)
|
||||
{
|
||||
foreach (Connection connection in connectionPanel.Connections)
|
||||
{
|
||||
foreach (Wire wire in connection.Wires)
|
||||
{
|
||||
if (wire != null) wire.Locked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RespawnShuttle = null;
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
if (networkMember is GameServer server)
|
||||
{
|
||||
maxTransportTime = server.ServerSettings.MaxTransportTime;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private bool OnShuttleCollision(Fixture sender, Fixture other, Contact contact)
|
||||
{
|
||||
//ignore collisions with the top barrier when returning
|
||||
return CurrentState != State.Returning || other?.Body != Level.Loaded?.TopBarrier;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (RespawnShuttle == null)
|
||||
{
|
||||
if (CurrentState != State.Waiting)
|
||||
{
|
||||
CurrentState = State.Waiting;
|
||||
}
|
||||
}
|
||||
|
||||
switch (CurrentState)
|
||||
{
|
||||
case State.Waiting:
|
||||
UpdateWaiting(deltaTime);
|
||||
break;
|
||||
case State.Transporting:
|
||||
UpdateTransporting(deltaTime);
|
||||
break;
|
||||
case State.Returning:
|
||||
UpdateReturning(deltaTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateWaiting(float deltaTime);
|
||||
|
||||
private void UpdateTransporting(float deltaTime)
|
||||
{
|
||||
//infinite transport time -> shuttle wont return
|
||||
if (maxTransportTime <= 0.0f) return;
|
||||
UpdateTransportingProjSpecific(deltaTime);
|
||||
}
|
||||
|
||||
partial void UpdateTransportingProjSpecific(float deltaTime);
|
||||
|
||||
public void ForceRespawn()
|
||||
{
|
||||
ResetShuttle();
|
||||
RespawnTime = DateTime.Now;
|
||||
CurrentState = State.Waiting;
|
||||
}
|
||||
|
||||
private void UpdateReturning(float deltaTime)
|
||||
{
|
||||
updateReturnTimer += deltaTime;
|
||||
if (updateReturnTimer > 1.0f)
|
||||
{
|
||||
updateReturnTimer = 0.0f;
|
||||
|
||||
if (shuttleSteering != null)
|
||||
{
|
||||
shuttleSteering.SetDestinationLevelStart();
|
||||
}
|
||||
UpdateReturningProjSpecific();
|
||||
}
|
||||
}
|
||||
|
||||
partial void DispatchShuttle();
|
||||
|
||||
partial void UpdateReturningProjSpecific();
|
||||
|
||||
private IEnumerable<object> ForceShuttleToPos(Vector2 position, float speed)
|
||||
{
|
||||
if (RespawnShuttle == null)
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
while (Math.Abs(position.Y - RespawnShuttle.WorldPosition.Y) > 100.0f)
|
||||
{
|
||||
Vector2 diff = position - RespawnShuttle.WorldPosition;
|
||||
if (diff.LengthSquared() > 0.01f)
|
||||
{
|
||||
Vector2 displayVel = Vector2.Normalize(diff) * speed;
|
||||
RespawnShuttle.SubBody.Body.LinearVelocity = ConvertUnits.ToSimUnits(displayVel);
|
||||
}
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
if (RespawnShuttle.SubBody == null) yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
private void ResetShuttle()
|
||||
{
|
||||
ReturnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(maxTransportTime * 1000));
|
||||
despawnTime = ReturnTime + new TimeSpan(0, 0, seconds: 30);
|
||||
|
||||
if (RespawnShuttle == null) return;
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine != RespawnShuttle) continue;
|
||||
|
||||
//remove respawn items that have been left in the shuttle
|
||||
if (respawnItems.Contains(item))
|
||||
{
|
||||
Spawner.AddToRemoveQueue(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
//restore other items to full condition and recharge batteries
|
||||
item.Condition = item.Prefab.Health;
|
||||
item.GetComponent<Repairable>()?.ResetDeterioration();
|
||||
var powerContainer = item.GetComponent<PowerContainer>();
|
||||
if (powerContainer != null)
|
||||
{
|
||||
powerContainer.Charge = powerContainer.Capacity;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Structure wall in Structure.WallList)
|
||||
{
|
||||
if (wall.Submarine != RespawnShuttle) continue;
|
||||
|
||||
for (int i = 0; i < wall.SectionCount; i++)
|
||||
{
|
||||
wall.AddDamage(i, -100000.0f);
|
||||
}
|
||||
}
|
||||
|
||||
var shuttleGaps = Gap.GapList.FindAll(g => g.Submarine == RespawnShuttle && g.ConnectedWall != null);
|
||||
shuttleGaps.ForEach(g => Spawner.AddToRemoveQueue(g));
|
||||
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.Submarine != RespawnShuttle) continue;
|
||||
|
||||
hull.OxygenPercentage = 100.0f;
|
||||
hull.WaterVolume = 0.0f;
|
||||
}
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.Submarine != RespawnShuttle) continue;
|
||||
|
||||
#if CLIENT
|
||||
if (Character.Controlled == c) Character.Controlled = null;
|
||||
#endif
|
||||
c.Kill(CauseOfDeathType.Unknown, null, true);
|
||||
c.Enabled = false;
|
||||
|
||||
Spawner.AddToRemoveQueue(c);
|
||||
if (c.Inventory != null)
|
||||
{
|
||||
foreach (Item item in c.Inventory.Items)
|
||||
{
|
||||
if (item == null) continue;
|
||||
Spawner.AddToRemoveQueue(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RespawnShuttle.SetPosition(new Vector2(Level.Loaded.StartPosition.X, Level.Loaded.Size.Y + RespawnShuttle.Borders.Height));
|
||||
RespawnShuttle.Velocity = Vector2.Zero;
|
||||
}
|
||||
|
||||
partial void RespawnCharactersProjSpecific();
|
||||
public void RespawnCharacters()
|
||||
{
|
||||
RespawnCharactersProjSpecific();
|
||||
}
|
||||
|
||||
public Vector2 FindSpawnPos()
|
||||
{
|
||||
if (Level.Loaded == null || Submarine.MainSub == null) { return Vector2.Zero; }
|
||||
|
||||
Rectangle dockedBorders = RespawnShuttle.GetDockedBorders();
|
||||
Vector2 diffFromDockedBorders =
|
||||
new Vector2(dockedBorders.Center.X, dockedBorders.Y - dockedBorders.Height / 2)
|
||||
- new Vector2(RespawnShuttle.Borders.Center.X, RespawnShuttle.Borders.Y - RespawnShuttle.Borders.Height / 2);
|
||||
|
||||
int minWidth = Math.Max(dockedBorders.Width, 1000);
|
||||
int minHeight = Math.Max(dockedBorders.Height, 1000);
|
||||
|
||||
List<Level.InterestingPosition> potentialSpawnPositions = new List<Level.InterestingPosition>();
|
||||
foreach (Level.InterestingPosition potentialSpawnPos in Level.Loaded.PositionsOfInterest.Where(p => p.PositionType == Level.PositionType.MainPath))
|
||||
{
|
||||
bool invalid = false;
|
||||
//make sure the shuttle won't overlap with any ruins
|
||||
foreach (var ruin in Level.Loaded.Ruins)
|
||||
{
|
||||
if (Math.Abs(ruin.Area.Center.X - potentialSpawnPos.Position.X) < (minWidth + ruin.Area.Width) / 2) { invalid = true; break; }
|
||||
if (Math.Abs(ruin.Area.Center.Y - potentialSpawnPos.Position.Y) < (minHeight + ruin.Area.Height) / 2) { invalid = true; break; }
|
||||
}
|
||||
if (invalid) { continue; }
|
||||
|
||||
//make sure there aren't any walls too close
|
||||
var tooCloseCells = Level.Loaded.GetTooCloseCells(potentialSpawnPos.Position.ToVector2(), Math.Max(minWidth, minHeight));
|
||||
if (tooCloseCells.Any()) { continue; }
|
||||
|
||||
//make sure the spawnpoint is far enough from other subs
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub == RespawnShuttle || RespawnShuttle.DockedTo.Contains(sub)) { continue; }
|
||||
float minDist = Math.Max(Math.Max(minWidth, minHeight) + Math.Max(sub.Borders.Width, sub.Borders.Height), 10000.0f);
|
||||
if (Vector2.DistanceSquared(sub.WorldPosition, potentialSpawnPos.Position.ToVector2()) < minDist * minDist)
|
||||
{
|
||||
invalid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (invalid) { continue; }
|
||||
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.IsDead)
|
||||
{
|
||||
//cannot spawn directly over dead bodies
|
||||
if (Math.Abs(character.WorldPosition.X - potentialSpawnPos.Position.X) < minWidth) { invalid = true; break; }
|
||||
if (Math.Abs(character.WorldPosition.Y - potentialSpawnPos.Position.Y) < minHeight) { invalid = true; break; }
|
||||
}
|
||||
else
|
||||
{
|
||||
//cannot spawn near alive characters (to prevent other players from seeing the shuttle
|
||||
//appear out of nowhere, or monsters from immediatelly wrecking the shuttle)
|
||||
if (Vector2.DistanceSquared(character.WorldPosition, potentialSpawnPos.Position.ToVector2()) < 5000.0f * 5000.0f)
|
||||
{
|
||||
invalid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (invalid) { continue; }
|
||||
|
||||
potentialSpawnPositions.Add(potentialSpawnPos);
|
||||
}
|
||||
Vector2 bestSpawnPos = new Vector2(Level.Loaded.StartPosition.X, Level.Loaded.Size.Y + RespawnShuttle.Borders.Height);
|
||||
float bestSpawnPosValue = 0.0f;
|
||||
foreach (var potentialSpawnPos in potentialSpawnPositions)
|
||||
{
|
||||
//the closer the spawnpos is to the main sub, the better
|
||||
float spawnPosValue = 100000.0f / Math.Max(Vector2.Distance(potentialSpawnPos.Position.ToVector2(), Submarine.MainSub.WorldPosition), 1.0f);
|
||||
|
||||
//prefer spawnpoints that are at the left side of the sub (so the shuttle doesn't have to go backwards)
|
||||
if (potentialSpawnPos.Position.X > Submarine.MainSub.WorldPosition.X)
|
||||
{
|
||||
spawnPosValue *= 0.1f;
|
||||
}
|
||||
|
||||
if (spawnPosValue > bestSpawnPosValue)
|
||||
{
|
||||
bestSpawnPos = potentialSpawnPos.Position.ToVector2();
|
||||
bestSpawnPosValue = spawnPosValue;
|
||||
}
|
||||
}
|
||||
|
||||
return bestSpawnPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user