Unstable v0.10.6.0 (October 13th 2020)
This commit is contained in:
@@ -593,7 +593,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (info != null || Vitality < MaxVitality * 0.98f)
|
||||
if (info != null || Vitality < MaxVitality * 0.98f || IsPet)
|
||||
{
|
||||
hudInfoTimer -= deltaTime;
|
||||
if (hudInfoTimer <= 0.0f)
|
||||
@@ -772,49 +772,68 @@ namespace Barotrauma
|
||||
MathHelper.Clamp(1.0f - (cursorDist - (hoverRange - fadeOutRange)) / fadeOutRange, 0.2f, 1.0f) :
|
||||
1.0f;
|
||||
|
||||
if (!GUI.DisableCharacterNames && hudInfoVisible && info != null &&
|
||||
(controlled == null || this != controlled.FocusedCharacter) && cam.Zoom > 0.4f)
|
||||
if (!GUI.DisableCharacterNames && hudInfoVisible &&
|
||||
(controlled == null || this != controlled.FocusedCharacter || IsPet) && cam.Zoom > 0.4f)
|
||||
{
|
||||
string name = Info.DisplayName;
|
||||
if (controlled == null && name != Info.Name) { name += " " + TextManager.Get("Disguised"); }
|
||||
|
||||
Vector2 nameSize = GUI.Font.MeasureString(name);
|
||||
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
|
||||
|
||||
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
|
||||
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
|
||||
namePos *= screenSize / viewportSize;
|
||||
namePos.X = (float)Math.Floor(namePos.X); namePos.Y = (float)Math.Floor(namePos.Y);
|
||||
namePos *= viewportSize / screenSize;
|
||||
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
|
||||
|
||||
Color nameColor = Color.White;
|
||||
if (Controlled != null && TeamID != Controlled.TeamID)
|
||||
if (info != null)
|
||||
{
|
||||
nameColor = TeamID == TeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
|
||||
string name = Info.DisplayName;
|
||||
if (controlled == null && name != Info.Name) { name += " " + TextManager.Get("Disguised"); }
|
||||
|
||||
Vector2 nameSize = GUI.Font.MeasureString(name);
|
||||
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
|
||||
|
||||
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
|
||||
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
|
||||
namePos *= screenSize / viewportSize;
|
||||
namePos.X = (float)Math.Floor(namePos.X); namePos.Y = (float)Math.Floor(namePos.Y);
|
||||
namePos *= viewportSize / screenSize;
|
||||
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
|
||||
|
||||
Color nameColor = Color.White;
|
||||
if (Controlled != null && TeamID != Controlled.TeamID)
|
||||
{
|
||||
nameColor = TeamID == TeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
|
||||
}
|
||||
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
|
||||
{
|
||||
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
|
||||
if (iconStyle != null)
|
||||
{
|
||||
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.WorldPosition ?? WorldPosition + Vector2.UnitY * 100.0f;
|
||||
Vector2 iconPos = headPos;
|
||||
iconPos.Y = -iconPos.Y;
|
||||
nameColor = iconStyle.Color;
|
||||
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
|
||||
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
|
||||
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
|
||||
}
|
||||
}
|
||||
|
||||
GUI.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
|
||||
GUI.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
GUI.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
|
||||
}
|
||||
}
|
||||
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
|
||||
|
||||
var petBehavior = (AIController as EnemyAIController)?.PetBehavior;
|
||||
if (petBehavior != null && !IsDead && !IsUnconscious)
|
||||
{
|
||||
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
|
||||
var petStatus = petBehavior.GetCurrentStatusIndicatorType();
|
||||
var iconStyle = GUI.Style.GetComponentStyle("PetIcon." + petStatus);
|
||||
if (iconStyle != null)
|
||||
{
|
||||
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.WorldPosition ?? WorldPosition + Vector2.UnitY * 100.0f;
|
||||
Vector2 iconPos = headPos;
|
||||
iconPos.Y = -iconPos.Y;
|
||||
nameColor = iconStyle.Color;
|
||||
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
|
||||
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
|
||||
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
|
||||
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
|
||||
}
|
||||
}
|
||||
|
||||
GUI.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
|
||||
GUI.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
GUI.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsDead) { return; }
|
||||
|
||||
@@ -98,6 +98,9 @@ namespace Barotrauma
|
||||
case "pendingupgrades":
|
||||
UpgradeManager = new UpgradeManager(this, subElement, isSingleplayer: true);
|
||||
break;
|
||||
case "pets":
|
||||
petsElement = subElement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +216,10 @@ namespace Barotrauma
|
||||
crewDead = false;
|
||||
endTimer = 5.0f;
|
||||
CrewManager.InitSinglePlayerRound();
|
||||
if (petsElement != null)
|
||||
{
|
||||
PetBehavior.LoadPets(petsElement);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadInitialLevel()
|
||||
@@ -705,6 +712,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
XElement petsElement = new XElement("pets");
|
||||
PetBehavior.SavePets(petsElement);
|
||||
modeElement.Add(petsElement);
|
||||
|
||||
CrewManager.Save(modeElement);
|
||||
CampaignMetadata.Save(modeElement);
|
||||
Map.Save(modeElement);
|
||||
|
||||
@@ -186,8 +186,7 @@ namespace Barotrauma.Tutorials
|
||||
// TODO: Rework order highlighting for new command UI
|
||||
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_mechanic, "repairsystems", highlightColor, new Vector2(5, 5));
|
||||
//HighlightOrderOption("jobspecific");
|
||||
}
|
||||
while (!HasOrder(captain_mechanic, "repairsystems"));
|
||||
} while (!HasOrder(captain_mechanic, "repairsystems") && !HasOrder(captain_mechanic, "repairmechanical") && !HasOrder(captain_mechanic, "repairelectrical"));
|
||||
RemoveCompletedObjective(segments[1]);
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
TriggerTutorialSegment(2, GameMain.Config.KeyBindText(InputType.Command));
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
// Room 3
|
||||
engineer_reactorObjectiveSensor = Item.ItemList.Find(i => i.HasTag("engineer_reactorobjectivesensor")).GetComponent<MotionSensor>();
|
||||
tutorial_oxygenGenerator = Item.ItemList.Find(i => i.HasTag("tutorial_oxygengenerator")).GetComponent<Powered>();
|
||||
tutorial_oxygenGenerator = Item.ItemList.Find(i => i.HasTag("tutorial_oxygengenerator")).GetComponent<OxygenGenerator>();
|
||||
engineer_reactor = Item.ItemList.Find(i => i.HasTag("engineer_reactor")).GetComponent<Reactor>();
|
||||
engineer_reactor.FireDelay = engineer_reactor.MeltdownDelay = float.PositiveInfinity;
|
||||
engineer_reactor.FuelConsumptionRate = 0.0f;
|
||||
@@ -380,7 +380,7 @@ namespace Barotrauma.Tutorials
|
||||
yield return null;
|
||||
} while (engineer_brokenJunctionBox.Condition < repairableJunctionBoxComponent.RepairThreshold); // Wait until repaired
|
||||
SetHighlight(engineer_brokenJunctionBox, false);
|
||||
RemoveCompletedObjective(segments[2]);
|
||||
RemoveCompletedObjective(segments[3]);
|
||||
SetDoorAccess(engineer_thirdDoor, engineer_thirdDoorLight, true);
|
||||
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
|
||||
{
|
||||
@@ -398,7 +398,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
SetHighlight(engineer_disconnectedJunctionBoxes[i].Item, false);
|
||||
}
|
||||
RemoveCompletedObjective(segments[3]);
|
||||
RemoveCompletedObjective(segments[4]);
|
||||
do { yield return null; } while (engineer_workingPump.Item.CurrentHull.WaterPercentage > waterVolumeBeforeOpening); // Wait until drained
|
||||
wiringActive = false;
|
||||
SetDoorAccess(engineer_fourthDoor, engineer_fourthDoorLight, true);
|
||||
@@ -424,7 +424,7 @@ namespace Barotrauma.Tutorials
|
||||
// Remove highlights when each individual machine is repaired
|
||||
do { CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3); yield return null; } while (engineer_submarineJunctionBox_1.Condition < repairableJunctionBoxComponent1.RepairThreshold || engineer_submarineJunctionBox_2.Condition < repairableJunctionBoxComponent2.RepairThreshold || engineer_submarineJunctionBox_3.Condition < repairableJunctionBoxComponent3.RepairThreshold);
|
||||
CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3);
|
||||
RemoveCompletedObjective(segments[4]);
|
||||
RemoveCompletedObjective(segments[5]);
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
|
||||
TriggerTutorialSegment(6); // Powerup reactor
|
||||
@@ -433,7 +433,7 @@ namespace Barotrauma.Tutorials
|
||||
do { yield return null; } while (!IsReactorPoweredUp(engineer_submarineReactor)); // Wait until ~matches load
|
||||
engineer.RemoveActiveObjectiveEntity(engineer_submarineReactor.Item);
|
||||
SetHighlight(engineer_submarineReactor.Item, false);
|
||||
RemoveCompletedObjective(segments[5]);
|
||||
RemoveCompletedObjective(segments[6]);
|
||||
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Complete"), ChatMessageType.Radio, null);
|
||||
|
||||
yield return new WaitForSeconds(4f, false);
|
||||
|
||||
@@ -144,17 +144,13 @@ namespace Barotrauma
|
||||
protected override ItemInventory GetActiveEquippedSubInventory(int slotIndex)
|
||||
{
|
||||
var item = Items[slotIndex];
|
||||
if (item == null) return null;
|
||||
if (item == null) { return null; }
|
||||
|
||||
var container = item.GetComponent<ItemContainer>();
|
||||
if (container == null ||
|
||||
!character.CanAccessInventory(container.Inventory) ||
|
||||
!container.KeepOpenWhenEquipped ||
|
||||
!character.HasEquippedItem(container.Item))
|
||||
if (container == null || !container.KeepOpenWhenEquippedBy(character))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return container.Inventory;
|
||||
}
|
||||
|
||||
@@ -626,7 +622,7 @@ namespace Barotrauma
|
||||
{
|
||||
var itemContainer = item.GetComponent<ItemContainer>();
|
||||
if (itemContainer != null &&
|
||||
itemContainer.KeepOpenWhenEquipped &&
|
||||
itemContainer.KeepOpenWhenEquippedBy(character) &&
|
||||
character.CanAccessInventory(itemContainer.Inventory) &&
|
||||
!highlightedSubInventorySlots.Any(s => s.Inventory == itemContainer.Inventory))
|
||||
{
|
||||
|
||||
@@ -70,6 +70,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
[Serialize(false, false, description: "Should the inventory of this item be kept open when the item is equipped by a character.")]
|
||||
public bool KeepOpenWhenEquipped { get; set; }
|
||||
|
||||
[Serialize(false, false, description: "Can the inventory of this item be moved around on the screen by the player.")]
|
||||
public bool MovableFrame { get; set; }
|
||||
|
||||
@@ -162,6 +163,30 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool KeepOpenWhenEquippedBy(Character character)
|
||||
{
|
||||
if (!character.CanAccessInventory(Inventory) ||
|
||||
!KeepOpenWhenEquipped ||
|
||||
!character.HasEquippedItem(Item))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//if holding 2 different "always open" items in different hands, don't force them to stay open
|
||||
if (character.SelectedItems[0] != null &&
|
||||
character.SelectedItems[1] != null &&
|
||||
character.SelectedItems[0] != character.SelectedItems[1])
|
||||
{
|
||||
if ((character.SelectedItems[0].GetComponent<ItemContainer>()?.KeepOpenWhenEquipped ?? false) &&
|
||||
(character.SelectedItems[1].GetComponent<ItemContainer>()?.KeepOpenWhenEquipped ?? false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
{
|
||||
if (hideItems || (item.body != null && !item.body.Enabled)) { return; }
|
||||
|
||||
@@ -342,7 +342,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Vector2.DistanceSquared(nodeWorldPos, draggingWire.nodes[(int)highlightedNodeIndex]) > Submarine.GridSize.X * Submarine.GridSize.X || PlayerInput.IsShiftDown())
|
||||
if ((highlightedNodeIndex.HasValue && Vector2.DistanceSquared(nodeWorldPos, draggingWire.nodes[(int)highlightedNodeIndex]) > Submarine.GridSize.X * Submarine.GridSize.X) ||
|
||||
PlayerInput.IsShiftDown())
|
||||
{
|
||||
selectedNodeIndex = highlightedNodeIndex;
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ namespace Barotrauma.Items.Components
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
|
||||
|
||||
if (!editing || GUI.DisableHUD) { return; }
|
||||
if (!editing || GUI.DisableHUD || !item.IsSelected) { return; }
|
||||
|
||||
float widgetRadius = 60.0f;
|
||||
|
||||
@@ -305,8 +305,6 @@ namespace Barotrauma.Items.Components
|
||||
drawPos + new Vector2((float)Math.Cos((maxRotation + minRotation) / 2), (float)Math.Sin((maxRotation + minRotation) / 2)) * widgetRadius,
|
||||
Color.LightGreen);
|
||||
|
||||
if (!item.IsSelected) { return; }
|
||||
|
||||
Widget minRotationWidget = GetWidget("minrotation", spriteBatch, size: 10, initMethod: (widget) =>
|
||||
{
|
||||
widget.Selected += () =>
|
||||
|
||||
@@ -404,8 +404,8 @@ namespace Barotrauma
|
||||
container = (this as ItemInventory).Container;
|
||||
}
|
||||
|
||||
if (container == null) return false;
|
||||
return owner.SelectedCharacter != null || !container.KeepOpenWhenEquipped || (!(owner is Character)) || !owner.HasEquippedItem(container.Item);
|
||||
if (container == null) { return false; }
|
||||
return owner.SelectedCharacter != null|| (!(owner is Character character)) || !container.KeepOpenWhenEquippedBy(character) || !owner.HasEquippedItem(container.Item);
|
||||
}
|
||||
|
||||
protected virtual bool HideSlot(int i)
|
||||
|
||||
@@ -10,11 +10,30 @@ namespace Barotrauma
|
||||
{
|
||||
partial class Hull : MapEntity, ISerializableEntity, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private class RemoteDecal
|
||||
{
|
||||
public readonly UInt32 DecalId;
|
||||
public readonly int SpriteIndex;
|
||||
public Vector2 NormalizedPos;
|
||||
public readonly float Scale;
|
||||
|
||||
public RemoteDecal(UInt32 decalId, int spriteIndex, Vector2 normalizedPos, float scale)
|
||||
{
|
||||
DecalId = decalId;
|
||||
SpriteIndex = spriteIndex;
|
||||
NormalizedPos = normalizedPos;
|
||||
Scale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
private float serverUpdateDelay;
|
||||
private float remoteWaterVolume, remoteOxygenPercentage;
|
||||
private List<Vector3> remoteFireSources;
|
||||
private readonly List<BackgroundSection> remoteBackgroundSections = new List<BackgroundSection>();
|
||||
private readonly List<RemoteDecal> remoteDecals = new List<RemoteDecal>();
|
||||
|
||||
private readonly HashSet<Decal> pendingDecalUpdates = new HashSet<Decal>();
|
||||
|
||||
private double lastAmbientLightEditTime;
|
||||
|
||||
public override bool SelectableInEditor
|
||||
@@ -126,10 +145,14 @@ namespace Barotrauma
|
||||
networkUpdateTimer += deltaTime;
|
||||
if (networkUpdateTimer > 0.2f)
|
||||
{
|
||||
if (!pendingSectionUpdates.Any())
|
||||
if (!pendingSectionUpdates.Any() && !pendingDecalUpdates.Any())
|
||||
{
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this);
|
||||
}
|
||||
foreach (Decal decal in pendingDecalUpdates)
|
||||
{
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { decal });
|
||||
}
|
||||
foreach (int pendingSectionUpdate in pendingSectionUpdates)
|
||||
{
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { pendingSectionUpdate });
|
||||
@@ -538,9 +561,9 @@ namespace Barotrauma
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
{
|
||||
msg.Write(extraData != null);
|
||||
if (extraData == null)
|
||||
{
|
||||
msg.WriteRangedInteger(0, 0, 2);
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(waterVolume / Volume, 0.0f, 1.5f), 0.0f, 1.5f, 8);
|
||||
|
||||
msg.Write(FireSources.Count > 0);
|
||||
@@ -560,8 +583,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (extraData[0] is Decal decal)
|
||||
{
|
||||
msg.WriteRangedInteger(1, 0, 2);
|
||||
int decalIndex = decals.IndexOf(decal);
|
||||
msg.Write((byte)(decalIndex < 0 ? 255 : decalIndex));
|
||||
msg.WriteRangedSingle(decal.BaseAlpha, 0.0f, 1.0f, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.WriteRangedInteger(2, 0, 2);
|
||||
int sectorToUpdate = (int)extraData[0];
|
||||
int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
|
||||
int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
|
||||
@@ -622,22 +653,16 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
int decalCount = message.ReadRangedInteger(0, MaxDecalsPerHull);
|
||||
decals.Clear();
|
||||
if (decalCount == 0) { decals.Clear(); }
|
||||
remoteDecals.Clear();
|
||||
for (int i = 0; i < decalCount; i++)
|
||||
{
|
||||
UInt32 decalId = message.ReadUInt32();
|
||||
int spriteIndex = message.ReadByte();
|
||||
float normalizedXPos = message.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
float normalizedYPos = message.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
float decalPosX = MathHelper.Lerp(rect.X, rect.Right, normalizedXPos);
|
||||
float decalPosY = MathHelper.Lerp(rect.Y - rect.Height, rect.Y, normalizedYPos);
|
||||
float decalScale = message.ReadRangedSingle(0.0f, 2.0f, 12);
|
||||
if (Submarine != null)
|
||||
{
|
||||
decalPosX += Submarine.Position.X;
|
||||
decalPosY += Submarine.Position.Y;
|
||||
}
|
||||
AddDecal(decalId, new Vector2(decalPosX, decalPosY), decalScale, isNetworkEvent: true, spriteIndex: spriteIndex);
|
||||
remoteDecals.Add(new RemoteDecal(decalId, spriteIndex, new Vector2(normalizedXPos, normalizedYPos), decalScale));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -658,6 +683,23 @@ namespace Barotrauma
|
||||
}
|
||||
remoteBackgroundSections.Clear();
|
||||
|
||||
if (remoteDecals.Any())
|
||||
{
|
||||
decals.Clear();
|
||||
foreach (RemoteDecal remoteDecal in remoteDecals)
|
||||
{
|
||||
float decalPosX = MathHelper.Lerp(rect.X, rect.Right, remoteDecal.NormalizedPos.X);
|
||||
float decalPosY = MathHelper.Lerp(rect.Y - rect.Height, rect.Y, remoteDecal.NormalizedPos.Y);
|
||||
if (Submarine != null)
|
||||
{
|
||||
decalPosX += Submarine.Position.X;
|
||||
decalPosY += Submarine.Position.Y;
|
||||
}
|
||||
AddDecal(remoteDecal.DecalId, new Vector2(decalPosX, decalPosY), remoteDecal.Scale, isNetworkEvent: true, spriteIndex: remoteDecal.SpriteIndex);
|
||||
}
|
||||
remoteDecals.Clear();
|
||||
}
|
||||
|
||||
if (remoteFireSources == null) { return; }
|
||||
|
||||
WaterVolume = remoteWaterVolume;
|
||||
|
||||
@@ -44,6 +44,12 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(1f, true), Editable(minValue: 0.01f, maxValue: 100f, ValueStep = 0.1f, DecimalCount = 2)]
|
||||
public float Scale { get; set; }
|
||||
|
||||
[Serialize("0, 0", true), Editable(ValueStep = 1, DecimalCount = 1, MinValueFloat = -1000f, MaxValueFloat = 1000f)]
|
||||
public Vector2 Offset { get; set; }
|
||||
|
||||
public float TextureRange
|
||||
{
|
||||
get;
|
||||
@@ -241,11 +247,13 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 _spriteScale = Vector2.One;
|
||||
|
||||
public Vector2 SpriteScale
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = Vector2.One;
|
||||
get { return _spriteScale * lightSourceParams.Scale; }
|
||||
set { _spriteScale = value; }
|
||||
}
|
||||
|
||||
public float? OverrideLightSpriteAlpha
|
||||
{
|
||||
@@ -280,7 +288,9 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
get { return lightSourceParams.LightSprite; }
|
||||
}
|
||||
|
||||
|
||||
private Vector2 OverrideLightTextureOrigin => OverrideLightTexture.Origin + LightSourceParams.Offset;
|
||||
|
||||
public Color Color
|
||||
{
|
||||
get { return lightSourceParams.Color; }
|
||||
@@ -564,7 +574,7 @@ namespace Barotrauma.Lights
|
||||
|
||||
var overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
|
||||
|
||||
Vector2 origin = OverrideLightTexture.Origin;
|
||||
Vector2 origin = OverrideLightTextureOrigin;
|
||||
|
||||
origin /= Math.Max(overrideTextureDims.X, overrideTextureDims.Y);
|
||||
origin -= Vector2.One * 0.5f;
|
||||
@@ -873,7 +883,7 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
|
||||
|
||||
Vector2 origin = OverrideLightTexture.Origin;
|
||||
Vector2 origin = OverrideLightTextureOrigin;
|
||||
if (LightSpriteEffect == SpriteEffects.FlipHorizontally) { origin.X = OverrideLightTexture.SourceRect.Width - origin.X; }
|
||||
if (LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = OverrideLightTexture.SourceRect.Height - origin.Y; }
|
||||
uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
|
||||
@@ -1060,7 +1070,7 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
var overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
|
||||
|
||||
Vector2 origin = OverrideLightTexture.Origin;
|
||||
Vector2 origin = OverrideLightTextureOrigin;
|
||||
|
||||
origin /= Math.Max(overrideTextureDims.X, overrideTextureDims.Y);
|
||||
origin *= TextureRange;
|
||||
@@ -1088,13 +1098,12 @@ namespace Barotrauma.Lights
|
||||
|
||||
if (DeformableLightSprite != null)
|
||||
{
|
||||
Vector2 origin = DeformableLightSprite.Origin;
|
||||
Vector2 origin = DeformableLightSprite.Origin + LightSourceParams.Offset;
|
||||
Vector2 drawPos = position;
|
||||
if (ParentSub != null)
|
||||
{
|
||||
drawPos += ParentSub.DrawPosition;
|
||||
}
|
||||
|
||||
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
|
||||
{
|
||||
origin.X = DeformableLightSprite.Sprite.SourceRect.Width - origin.X;
|
||||
@@ -1113,7 +1122,7 @@ namespace Barotrauma.Lights
|
||||
|
||||
if (LightSprite != null)
|
||||
{
|
||||
Vector2 origin = LightSprite.Origin;
|
||||
Vector2 origin = LightSprite.Origin + LightSourceParams.Offset;
|
||||
if ((LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally)
|
||||
{
|
||||
origin.X = LightSprite.SourceRect.Width - origin.X;
|
||||
|
||||
@@ -1023,7 +1023,10 @@ namespace Barotrauma.Networking
|
||||
if (Enum.TryParse(splitMsg[0], out disconnectReason)) { disconnectReasonIncluded = true; }
|
||||
}
|
||||
|
||||
if (disconnectMsg == Lidgren.Network.NetConnection.NoResponseMessage)
|
||||
if (disconnectMsg == Lidgren.Network.NetConnection.NoResponseMessage ||
|
||||
disconnectReason == DisconnectReason.Banned ||
|
||||
disconnectReason == DisconnectReason.Kicked ||
|
||||
disconnectReason == DisconnectReason.TooManyFailedLogins)
|
||||
{
|
||||
allowReconnect = false;
|
||||
}
|
||||
@@ -2138,6 +2141,7 @@ namespace Barotrauma.Networking
|
||||
return;
|
||||
}
|
||||
|
||||
entities.Add(entity);
|
||||
if (entity != null && (entity is Item || entity is Character || entity is Submarine))
|
||||
{
|
||||
entity.ClientRead(objHeader.Value, inc, sendingTime);
|
||||
@@ -2188,7 +2192,8 @@ namespace Barotrauma.Networking
|
||||
errorLines.Add(ex.StackTrace.CleanupStackTrace());
|
||||
errorLines.Add(" ");
|
||||
if (prevObjHeader == ServerNetObject.ENTITY_EVENT || prevObjHeader == ServerNetObject.ENTITY_EVENT_INITIAL ||
|
||||
objHeader == ServerNetObject.ENTITY_EVENT || objHeader == ServerNetObject.ENTITY_EVENT_INITIAL)
|
||||
objHeader == ServerNetObject.ENTITY_EVENT || objHeader == ServerNetObject.ENTITY_EVENT_INITIAL ||
|
||||
objHeader == ServerNetObject.ENTITY_POSITION || prevObjHeader == ServerNetObject.ENTITY_POSITION)
|
||||
{
|
||||
foreach (IServerSerializable ent in entities)
|
||||
{
|
||||
|
||||
@@ -373,7 +373,17 @@ namespace Barotrauma.Networking
|
||||
|
||||
info.GameMode = element.GetAttributeString("GameMode", "");
|
||||
info.GameVersion = element.GetAttributeString("GameVersion", "");
|
||||
info.MaxPlayers = element.GetAttributeInt("MaxPlayers", 0);
|
||||
|
||||
int maxPlayersElement = element.GetAttributeInt("MaxPlayers", 0);
|
||||
|
||||
if (maxPlayersElement > NetConfig.MaxPlayers)
|
||||
{
|
||||
DebugConsole.IsOpen = true;
|
||||
DebugConsole.NewMessage($"Setting the maximum amount of players to {maxPlayersElement} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", Color.Red);
|
||||
maxPlayersElement = NetConfig.MaxPlayers;
|
||||
}
|
||||
|
||||
info.MaxPlayers = maxPlayersElement;
|
||||
|
||||
if (Enum.TryParse(element.GetAttributeString("PlayStyle", ""), out PlayStyle playStyleTemp)) { info.PlayStyle = playStyleTemp; }
|
||||
if (bool.TryParse(element.GetAttributeString("UsingWhiteList", ""), out bool whitelistTemp)) { info.UsingWhiteList = whitelistTemp; }
|
||||
|
||||
@@ -306,7 +306,7 @@ namespace Barotrauma.CharacterEditor
|
||||
var lastJoint = selectedJoints.LastOrDefault();
|
||||
if (lastJoint != null)
|
||||
{
|
||||
lastLimb = PlayerInput.KeyDown(Keys.LeftAlt) ? lastJoint.LimbA : lastJoint.LimbB;
|
||||
lastLimb = PlayerInput.KeyDown(Keys.LeftAlt) ? lastJoint.LimbB : lastJoint.LimbA;
|
||||
}
|
||||
}
|
||||
if (lastLimb != null)
|
||||
@@ -941,7 +941,7 @@ namespace Barotrauma.CharacterEditor
|
||||
var lastJoint = selectedJoints.LastOrDefault();
|
||||
if (lastJoint != null)
|
||||
{
|
||||
lastLimb = PlayerInput.KeyDown(Keys.LeftAlt) ? lastJoint.LimbA : lastJoint.LimbB;
|
||||
lastLimb = PlayerInput.KeyDown(Keys.LeftAlt) ? lastJoint.LimbB : lastJoint.LimbA;
|
||||
}
|
||||
}
|
||||
if (lastLimb != null)
|
||||
@@ -2389,7 +2389,7 @@ namespace Barotrauma.CharacterEditor
|
||||
IEnumerable<Limb> limbs = selectedLimbs;
|
||||
if (limbs.None())
|
||||
{
|
||||
limbs = selectedJoints.Select(j => PlayerInput.KeyDown(Keys.LeftAlt) ? j.LimbA : j.LimbB);
|
||||
limbs = selectedJoints.Select(j => PlayerInput.KeyDown(Keys.LeftAlt) ? j.LimbB : j.LimbA);
|
||||
}
|
||||
foreach (var limb in limbs)
|
||||
{
|
||||
@@ -4452,11 +4452,11 @@ namespace Barotrauma.CharacterEditor
|
||||
}
|
||||
if (editJoints)
|
||||
{
|
||||
if (!altDown && joint.BodyA == limb.body.FarseerBody)
|
||||
if (altDown && joint.BodyA == limb.body.FarseerBody)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (altDown && joint.BodyB == limb.body.FarseerBody)
|
||||
if (!altDown && joint.BodyB == limb.body.FarseerBody)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1104,7 +1104,16 @@ namespace Barotrauma
|
||||
{
|
||||
port = settingsDoc.Root.GetAttributeInt("port", port);
|
||||
queryPort = settingsDoc.Root.GetAttributeInt("queryport", queryPort);
|
||||
maxPlayers = settingsDoc.Root.GetAttributeInt("maxplayers", maxPlayers);
|
||||
|
||||
int maxPlayersElement = settingsDoc.Root.GetAttributeInt("maxplayers", maxPlayers);
|
||||
if (maxPlayersElement > NetConfig.MaxPlayers)
|
||||
{
|
||||
DebugConsole.IsOpen = true;
|
||||
DebugConsole.NewMessage($"Setting the maximum amount of players to {maxPlayersElement} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", Color.Red);
|
||||
maxPlayersElement = NetConfig.MaxPlayers;
|
||||
}
|
||||
|
||||
maxPlayers = maxPlayersElement;
|
||||
karmaEnabled = settingsDoc.Root.GetAttributeBool("karmaenabled", true);
|
||||
selectedKarmaPreset = settingsDoc.Root.GetAttributeString("karmapreset", "default");
|
||||
string playStyleStr = settingsDoc.Root.GetAttributeString("playstyle", "Casual");
|
||||
|
||||
@@ -4375,9 +4375,18 @@ namespace Barotrauma
|
||||
Submarine.DrawFront(spriteBatch, editing: true, e => ShowThalamus || !(e.prefab?.Category.HasFlag(MapEntityCategory.Thalamus) ?? false));
|
||||
if (!WiringMode && !IsMouseOnEditorGUI())
|
||||
{
|
||||
MapEntityPrefab.Selected?.DrawPlacing(spriteBatch, cam);
|
||||
MapEntityPrefab.Selected?.DrawPlacing(spriteBatch, cam);
|
||||
MapEntity.DrawSelecting(spriteBatch, cam);
|
||||
}
|
||||
if (dummyCharacter != null && WiringMode)
|
||||
{
|
||||
for (int i = 0; i < dummyCharacter.SelectedItems.Length; i++)
|
||||
{
|
||||
if (dummyCharacter.SelectedItems[i] == null) { continue; }
|
||||
if (i > 0 && dummyCharacter.SelectedItems[0] == dummyCharacter.SelectedItems[i]) { continue; }
|
||||
dummyCharacter.SelectedItems[i].Draw(spriteBatch, editing: false, back: true);
|
||||
}
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
if (GameMain.LightManager.LightingEnabled && lightingEnabled)
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Barotrauma
|
||||
|
||||
//ambience
|
||||
private static Sound waterAmbienceIn, waterAmbienceOut, waterAmbienceMoving;
|
||||
private static SoundChannel[] waterAmbienceChannels = new SoundChannel[3];
|
||||
private static readonly SoundChannel[] waterAmbienceChannels = new SoundChannel[3];
|
||||
|
||||
private static float ambientSoundTimer;
|
||||
private static Vector2 ambientSoundInterval = new Vector2(20.0f, 40.0f); //x = min, y = max
|
||||
@@ -85,6 +85,7 @@ namespace Barotrauma
|
||||
private static Vector2 hullSoundInterval = new Vector2(45.0f, 90.0f); //x = min, y = max
|
||||
|
||||
//misc
|
||||
private static float[] targetFlowLeft, targetFlowRight;
|
||||
public static List<Sound> FlowSounds = new List<Sound>();
|
||||
public static List<Sound> SplashSounds = new List<Sound>();
|
||||
private static SoundChannel[] flowSoundChannels;
|
||||
@@ -109,6 +110,8 @@ namespace Barotrauma
|
||||
|
||||
private static Dictionary<GUISoundType, List<Sound>> guiSounds;
|
||||
|
||||
private static bool firstTimeInMainMenu = true;
|
||||
|
||||
private static Sound startUpSound;
|
||||
|
||||
public static bool Initialized;
|
||||
@@ -335,11 +338,14 @@ namespace Barotrauma
|
||||
else { miscSoundList.Add(new KeyValuePair<string, Sound>(g.Key, s)); }
|
||||
}));
|
||||
|
||||
|
||||
flowSoundChannels?.ForEach(ch => ch?.Dispose());
|
||||
flowSoundChannels = new SoundChannel[FlowSounds.Count];
|
||||
flowVolumeLeft = new float[FlowSounds.Count];
|
||||
flowVolumeRight = new float[FlowSounds.Count];
|
||||
targetFlowLeft = new float[FlowSounds.Count];
|
||||
targetFlowRight = new float[FlowSounds.Count];
|
||||
|
||||
fireSoundChannels?.ForEach(ch => ch?.Dispose());
|
||||
fireSoundChannels = new SoundChannel[fireSizes];
|
||||
fireVolumeLeft = new float[fireSizes];
|
||||
fireVolumeRight = new float[fireSizes];
|
||||
@@ -494,9 +500,6 @@ namespace Barotrauma
|
||||
{
|
||||
if (FlowSounds.Count == 0) { return; }
|
||||
|
||||
float[] targetFlowLeft = new float[FlowSounds.Count];
|
||||
float[] targetFlowRight = new float[FlowSounds.Count];
|
||||
|
||||
Vector2 listenerPos = new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y);
|
||||
foreach (Gap gap in Gap.GapList)
|
||||
{
|
||||
@@ -931,7 +934,9 @@ namespace Barotrauma
|
||||
return "editor";
|
||||
}
|
||||
|
||||
if (Screen.Selected != GameMain.GameScreen) { return "menu"; }
|
||||
if (Screen.Selected != GameMain.GameScreen) { return firstTimeInMainMenu ? "menu" : "default"; }
|
||||
|
||||
firstTimeInMainMenu = false;
|
||||
|
||||
|
||||
if (Character.Controlled != null)
|
||||
@@ -973,12 +978,12 @@ namespace Barotrauma
|
||||
float totalArea = 0.0f;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.Submarine != targetSubmarine) continue;
|
||||
if (hull.Submarine != targetSubmarine) { continue; }
|
||||
floodedArea += hull.WaterVolume;
|
||||
totalArea += hull.Volume;
|
||||
}
|
||||
|
||||
if (totalArea > 0.0f && floodedArea / totalArea > 0.25f) return "flooded";
|
||||
if (totalArea > 0.0f && floodedArea / totalArea > 0.25f) { return "flooded"; }
|
||||
}
|
||||
|
||||
float enemyDistThreshold = 5000.0f;
|
||||
@@ -991,7 +996,7 @@ namespace Barotrauma
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.IsDead || !character.Enabled) continue;
|
||||
if (!(character.AIController is EnemyAIController enemyAI) || (!enemyAI.AttackHumans && !enemyAI.AttackRooms)) continue;
|
||||
if (!(character.AIController is EnemyAIController enemyAI) || (!enemyAI.AttackHumans && !enemyAI.AttackRooms)) { continue; }
|
||||
|
||||
if (targetSubmarine != null)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.10.601.0</Version>
|
||||
<Version>0.10.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.10.601.0</Version>
|
||||
<Version>0.10.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.10.601.0</Version>
|
||||
<Version>0.10.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.10.601.0</Version>
|
||||
<Version>0.10.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.10.601.0</Version>
|
||||
<Version>0.10.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -478,7 +478,7 @@ namespace Barotrauma
|
||||
msg.Write(Info == null);
|
||||
msg.Write(entityId);
|
||||
msg.Write(SpeciesName);
|
||||
msg.Write(seed);
|
||||
msg.Write(Seed);
|
||||
|
||||
if (Removed)
|
||||
{
|
||||
|
||||
@@ -1119,6 +1119,12 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (maxPlayers > NetConfig.MaxPlayers)
|
||||
{
|
||||
NewMessage($"Setting the maximum amount of players to {maxPlayers} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.");
|
||||
maxPlayers = NetConfig.MaxPlayers;
|
||||
}
|
||||
|
||||
GameMain.Server.ServerSettings.MaxPlayers = maxPlayers;
|
||||
NewMessage("Set the maximum player count to " + maxPlayers + ".");
|
||||
}
|
||||
@@ -1132,6 +1138,12 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (maxPlayers > NetConfig.MaxPlayers)
|
||||
{
|
||||
GameMain.Server.SendConsoleMessage($"Setting the maximum amount of players to {maxPlayers} failed due to exceeding the limit of {NetConfig.MaxPlayers} players per server. Using the maximum of {NetConfig.MaxPlayers} instead.", client);
|
||||
maxPlayers = NetConfig.MaxPlayers;
|
||||
}
|
||||
|
||||
GameMain.Server.ServerSettings.MaxPlayers = maxPlayers;
|
||||
NewMessage(client.Name + " set the maximum player count to " + maxPlayers + ".");
|
||||
GameMain.Server.SendConsoleMessage("Set the maximum player count to " + maxPlayers + ".", client);
|
||||
|
||||
@@ -147,6 +147,14 @@ namespace Barotrauma
|
||||
c.InGame && (IsOwner(c) || c.HasPermission(ClientPermissions.ManageCampaign)));
|
||||
}
|
||||
|
||||
public void LoadPets()
|
||||
{
|
||||
if (petsElement != null)
|
||||
{
|
||||
PetBehavior.LoadPets(petsElement);
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<object> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults)
|
||||
{
|
||||
lastUpdateID++;
|
||||
@@ -229,6 +237,9 @@ namespace Barotrauma
|
||||
c.Inventory.DeleteAllItems();
|
||||
}
|
||||
|
||||
petsElement = new XElement("pets");
|
||||
PetBehavior.SavePets(petsElement);
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
|
||||
@@ -768,6 +779,11 @@ namespace Barotrauma
|
||||
CargoManager?.SavePurchasedItems(modeElement);
|
||||
UpgradeManager?.SavePendingUpgrades(modeElement, UpgradeManager?.PendingUpgrades);
|
||||
|
||||
if (petsElement != null)
|
||||
{
|
||||
modeElement.Add(petsElement);
|
||||
}
|
||||
|
||||
// save bots
|
||||
CrewManager.SaveMultiplayer(modeElement);
|
||||
|
||||
|
||||
@@ -128,28 +128,8 @@ namespace Barotrauma
|
||||
//used when clients use the water/fire console commands or section / decal updates are received
|
||||
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
|
||||
{
|
||||
bool hasExtraData = msg.ReadBoolean();
|
||||
if (hasExtraData)
|
||||
{
|
||||
int sectorToUpdate = msg.ReadRangedInteger(0, BackgroundSections.Count - 1);
|
||||
int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
|
||||
int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
float colorStrength = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
Color color = new Color(msg.ReadUInt32());
|
||||
|
||||
//TODO: verify the client is close enough to this hull to paint it, that the sprayer is functional and that the color matches
|
||||
if (c.Character != null && c.Character.AllowInput && c.Character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() != null))
|
||||
{
|
||||
BackgroundSections[i].SetColorStrength(colorStrength);
|
||||
BackgroundSections[i].SetColor(color);
|
||||
}
|
||||
}
|
||||
//add to pending updates to notify other clients as well
|
||||
pendingSectionUpdates.Add(sectorToUpdate);
|
||||
}
|
||||
else
|
||||
int messageType = msg.ReadRangedInteger(0, 2);
|
||||
if (messageType == 0)
|
||||
{
|
||||
float newWaterVolume = msg.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
|
||||
|
||||
@@ -205,6 +185,37 @@ namespace Barotrauma
|
||||
FireSources.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (messageType == 1)
|
||||
{
|
||||
byte decalIndex = msg.ReadByte();
|
||||
float decalAlpha = msg.ReadRangedSingle(0.0f, 1.0f, 255);
|
||||
if (decalIndex < 0 || decalIndex >= decals.Count) { return; }
|
||||
if (c.Character != null && c.Character.AllowInput && c.Character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() != null))
|
||||
{
|
||||
decals[decalIndex].BaseAlpha = decalAlpha;
|
||||
}
|
||||
decalUpdatePending = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int sectorToUpdate = msg.ReadRangedInteger(0, BackgroundSections.Count - 1);
|
||||
int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
|
||||
int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
float colorStrength = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
Color color = new Color(msg.ReadUInt32());
|
||||
|
||||
//TODO: verify the client is close enough to this hull to paint it, that the sprayer is functional and that the color matches
|
||||
if (c.Character != null && c.Character.AllowInput && c.Character.SelectedItems.Any(it => it?.GetComponent<Sprayer>() != null))
|
||||
{
|
||||
BackgroundSections[i].SetColorStrength(colorStrength);
|
||||
BackgroundSections[i].SetColor(color);
|
||||
}
|
||||
}
|
||||
//add to pending updates to notify other clients as well
|
||||
pendingSectionUpdates.Add(sectorToUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1574,8 +1574,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
//if docked to a sub with a smaller ID, don't send an update
|
||||
// (= update is only sent for the docked sub that has the smallest ID, doesn't matter if it's the main sub or a shuttle)
|
||||
if (sub.Info.IsOutpost || sub.DockedTo.Any(s => s.ID < sub.ID)) continue;
|
||||
if (!c.PendingPositionUpdates.Contains(sub)) c.PendingPositionUpdates.Enqueue(sub);
|
||||
if (sub.Info.IsOutpost || sub.DockedTo.Any(s => s.ID < sub.ID)) { continue; }
|
||||
if (sub.PhysicsBody == null || sub.PhysicsBody.BodyType == FarseerPhysics.BodyType.Static) { continue; }
|
||||
if (!c.PendingPositionUpdates.Contains(sub)) { c.PendingPositionUpdates.Enqueue(sub); }
|
||||
}
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
@@ -2291,6 +2292,8 @@ namespace Barotrauma.Networking
|
||||
crewManager?.InitRound();
|
||||
}
|
||||
|
||||
campaign?.LoadPets();
|
||||
|
||||
foreach (Submarine sub in Submarine.MainSubs)
|
||||
{
|
||||
if (sub == null) continue;
|
||||
@@ -2359,7 +2362,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
bool missionAllowRespawn = campaign == null && (missionMode?.Mission == null || missionMode.Mission.AllowRespawn);
|
||||
bool outpostAllowRespawn = campaign != null && campaign.NextLevel?.Type == LevelData.LevelType.Outpost;
|
||||
msg.Write(missionAllowRespawn || outpostAllowRespawn);
|
||||
msg.Write(serverSettings.AllowRespawn && (missionAllowRespawn || outpostAllowRespawn));
|
||||
msg.Write(serverSettings.AllowDisguises);
|
||||
msg.Write(serverSettings.AllowRewiring);
|
||||
msg.Write(serverSettings.AllowRagdollButton);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.10.601.0</Version>
|
||||
<Version>0.10.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<Item file="Content/Items/Gardening/growableplants.xml" />
|
||||
<Item file="Content/Items/Gardening/gardeningtools.xml" />
|
||||
<Item file="Content/Items/Gardening/plantproducts.xml" />
|
||||
<Character file="Content/Characters/Balloon/Balloon.xml" />
|
||||
<Character file="Content/Characters/Carrier/Carrier.xml" />
|
||||
<Character file="Content/Characters/Charybdis/Charybdis.xml" />
|
||||
<Character file="Content/Characters/Coelanth/Coelanth.xml" />
|
||||
@@ -92,6 +93,7 @@
|
||||
<Character file="Content/Characters/Molochbaby/Molochbaby.xml" />
|
||||
<Character file="Content/Characters/Peanut/Peanut.xml" />
|
||||
<Character file="Content/Characters/Psilotoad/Psilotoad.xml" />
|
||||
<Character file="Content/Characters/Orangeboy/Orangeboy.xml" />
|
||||
<Character file="Content/Characters/Tigerthresher/Tigerthresher.xml" />
|
||||
<Character file="Content/Characters/Bonethresher/Bonethresher.xml" />
|
||||
<Character file="Content/Characters/Leucocyte/Leucocyte.xml" />
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public enum AIState { Idle, Attack, Escape, Eat, Flee, Avoid, Aggressive, PassiveAggressive, Protect, Observe, Freeze }
|
||||
public enum AIState { Idle, Attack, Escape, Eat, Flee, Avoid, Aggressive, PassiveAggressive, Protect, Observe, Freeze, Follow }
|
||||
|
||||
abstract partial class AIController : ISteerable
|
||||
{
|
||||
|
||||
@@ -48,13 +48,11 @@ namespace Barotrauma
|
||||
private float attackLimbResetTimer;
|
||||
|
||||
private bool IsCoolDownRunning => AttackingLimb != null && AttackingLimb.attack.CoolDownTimer > 0;
|
||||
public float CombatStrength => Character.Params.AI.CombatStrength;
|
||||
private float Sight => Character.Params.AI.Sight;
|
||||
private float Hearing => Character.Params.AI.Hearing;
|
||||
private float FleeHealthThreshold => Character.Params.AI.FleeHealthThreshold;
|
||||
private float AggressionGreed => Character.Params.AI.AggressionGreed;
|
||||
private float AggressionHurt => Character.Params.AI.AggressionHurt;
|
||||
private bool AggressiveBoarding => Character.Params.AI.AggressiveBoarding;
|
||||
public float CombatStrength => AIParams.CombatStrength;
|
||||
private float Sight => AIParams.Sight;
|
||||
private float Hearing => AIParams.Hearing;
|
||||
private float FleeHealthThreshold => AIParams.FleeHealthThreshold;
|
||||
private bool AggressiveBoarding => AIParams.AggressiveBoarding;
|
||||
|
||||
private FishAnimController FishAnimController => Character.AnimController as FishAnimController;
|
||||
|
||||
@@ -90,7 +88,6 @@ namespace Barotrauma
|
||||
|
||||
private readonly float priorityFearIncreasement = 2;
|
||||
private readonly float memoryFadeTime = 0.5f;
|
||||
private readonly float avoidTime = 3;
|
||||
|
||||
private float avoidTimer;
|
||||
private float observeTimer;
|
||||
@@ -241,6 +238,10 @@ namespace Barotrauma
|
||||
{
|
||||
targetingTag = "dead";
|
||||
}
|
||||
else if (PetBehavior != null && aiTarget.Entity == PetBehavior.Owner)
|
||||
{
|
||||
targetingTag = "owner";
|
||||
}
|
||||
else if (AIParams.TryGetTarget(targetCharacter.SpeciesName, out CharacterParams.TargetParams tP))
|
||||
{
|
||||
targetingTag = tP.Tag;
|
||||
@@ -384,7 +385,7 @@ namespace Barotrauma
|
||||
{
|
||||
updateTargetsTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
else if (avoidTimer <= 0)
|
||||
{
|
||||
CharacterParams.TargetParams targetingParams = null;
|
||||
UpdateTargets(Character, out targetingParams);
|
||||
@@ -393,11 +394,7 @@ namespace Barotrauma
|
||||
UpdateWallTarget();
|
||||
}
|
||||
updateTargetsTimer = updateTargetsInterval * Rand.Range(0.75f, 1.25f);
|
||||
if (avoidTimer > 0)
|
||||
{
|
||||
State = AIState.Escape;
|
||||
}
|
||||
else if (SelectedAiTarget == null)
|
||||
if (SelectedAiTarget == null)
|
||||
{
|
||||
State = AIState.Idle;
|
||||
}
|
||||
@@ -502,17 +499,21 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
case AIState.Protect:
|
||||
case AIState.Follow:
|
||||
if (SelectedAiTarget == null || SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed)
|
||||
{
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
}
|
||||
if (SelectedAiTarget.Entity is Character targetCharacter && targetCharacter.LastAttacker is Character attacker && !attacker.Removed && !attacker.IsDead)
|
||||
if (State == AIState.Protect)
|
||||
{
|
||||
// Attack the character that attacked the target we are protecting
|
||||
ChangeTargetState(attacker, AIState.Attack, selectedTargetingParams.Priority * 2);
|
||||
SelectTarget(attacker.AiTarget);
|
||||
return;
|
||||
if (SelectedAiTarget.Entity is Character targetCharacter && targetCharacter.LastAttacker is Character attacker && !attacker.Removed && !attacker.IsDead)
|
||||
{
|
||||
// Attack the character that attacked the target we are protecting
|
||||
ChangeTargetState(attacker, AIState.Attack, selectedTargetingParams.Priority * 2);
|
||||
SelectTarget(attacker.AiTarget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
float sqrDist = Vector2.DistanceSquared(WorldPosition, SelectedAiTarget.WorldPosition);
|
||||
float reactDist = selectedTargetingParams != null && selectedTargetingParams.ReactDistance > 0 ? selectedTargetingParams.ReactDistance : GetPerceivingRange(SelectedAiTarget);
|
||||
@@ -568,6 +569,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: doesn't work right here
|
||||
FaceTarget(SelectedAiTarget.Entity);
|
||||
}
|
||||
observeTimer -= deltaTime;
|
||||
@@ -592,7 +594,6 @@ namespace Barotrauma
|
||||
if (!Character.AnimController.SimplePhysicsEnabled)
|
||||
{
|
||||
LatchOntoAI?.Update(this, deltaTime);
|
||||
PetBehavior?.Update(deltaTime);
|
||||
}
|
||||
IsSteeringThroughGap = false;
|
||||
if (SwarmBehavior != null)
|
||||
@@ -1454,7 +1455,8 @@ namespace Barotrauma
|
||||
bool isFriendly = IsFriendly(Character, attacker);
|
||||
if (wasLatched)
|
||||
{
|
||||
avoidTimer = avoidTime * Rand.Range(0.75f, 1.25f);
|
||||
State = AIState.Escape;
|
||||
avoidTimer = AIParams.AvoidTime * Rand.Range(0.75f, 1.25f);
|
||||
if (!isFriendly)
|
||||
{
|
||||
SelectTarget(attacker.AiTarget);
|
||||
@@ -1527,7 +1529,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
AITargetMemory targetMemory = GetTargetMemory(attacker.AiTarget, true);
|
||||
targetMemory.Priority += GetRelativeDamage(attackResult.Damage, Character.Vitality) * AggressionHurt;
|
||||
targetMemory.Priority += GetRelativeDamage(attackResult.Damage, Character.Vitality) * AIParams.AggressionHurt;
|
||||
|
||||
// Only allow to react once. Otherwise would attack the target with only a fraction of a cooldown
|
||||
bool retaliate = !isFriendly && SelectedAiTarget != attacker.AiTarget && attacker.Submarine == Character.Submarine;
|
||||
@@ -1552,12 +1554,14 @@ namespace Barotrauma
|
||||
}
|
||||
else if (avoidGunFire)
|
||||
{
|
||||
avoidTimer = avoidTime * Rand.Range(0.75f, 1.25f);
|
||||
State = AIState.Escape;
|
||||
avoidTimer = AIParams.AvoidTime * Rand.Range(0.75f, 1.25f);
|
||||
SelectTarget(attacker.AiTarget);
|
||||
}
|
||||
if (Character.HealthPercentage <= FleeHealthThreshold)
|
||||
{
|
||||
avoidTimer = Rand.Range(15, 30);
|
||||
State = AIState.Flee;
|
||||
avoidTimer = AIParams.MinFleeTime * Rand.Range(0.75f, 1.25f);
|
||||
SelectTarget(attacker.AiTarget);
|
||||
}
|
||||
}
|
||||
@@ -1587,7 +1591,7 @@ namespace Barotrauma
|
||||
if (damageTarget.Health > 0)
|
||||
{
|
||||
// Managed to hit a living/non-destroyed target. Increase the priority more if the target is low in health -> dies easily/soon
|
||||
selectedTargetMemory.Priority += GetRelativeDamage(attackResult.Damage, damageTarget.Health) * AggressionGreed;
|
||||
selectedTargetMemory.Priority += GetRelativeDamage(attackResult.Damage, damageTarget.Health) * AIParams.AggressionGreed;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1654,10 +1658,13 @@ namespace Barotrauma
|
||||
if (!item.Removed && item.body != null)
|
||||
{
|
||||
float itemBodyExtent = item.body.GetMaxExtent() * 2;
|
||||
if (limbDiff.LengthSquared() < itemBodyExtent * itemBodyExtent)
|
||||
if (Math.Abs(limbDiff.X) < itemBodyExtent &&
|
||||
Math.Abs(limbDiff.Y) < Character.AnimController.Collider.GetMaxExtent() + Character.AnimController.ColliderHeightFromFloor)
|
||||
{
|
||||
item.body.LinearVelocity *= 0.9f;
|
||||
item.body.LinearVelocity -= limbDiff * 0.25f;
|
||||
|
||||
item.AddDamage(Character, item.WorldPosition, new Attack(0.0f, 0.0f, 0.0f, 0.0f, 0.1f), deltaTime);
|
||||
item.body.ApplyForce(-limbDiff * item.body.Mass);
|
||||
|
||||
if (item.Condition <= 0.0f)
|
||||
{
|
||||
@@ -1697,15 +1704,40 @@ namespace Barotrauma
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
}
|
||||
Vector2 dir = Vector2.Normalize(SelectedAiTarget.Entity.WorldPosition - Character.WorldPosition);
|
||||
if (!MathUtils.IsValid(dir))
|
||||
if (Character.CurrentHull != null && PathSteering != null)
|
||||
{
|
||||
return;
|
||||
// Inside
|
||||
Character targetCharacter = SelectedAiTarget.Entity as Character;
|
||||
if ((Character.AnimController.InWater || !Character.AnimController.CanWalk) &&
|
||||
(targetCharacter != null && VisibleHulls.Contains(targetCharacter.CurrentHull) || Character.CanSeeTarget(SelectedAiTarget.Entity)))
|
||||
{
|
||||
// Steer towards the target if in the same room and swimming
|
||||
Vector2 dir = Vector2.Normalize(SelectedAiTarget.Entity.WorldPosition - Character.WorldPosition);
|
||||
if (MathUtils.IsValid(dir))
|
||||
{
|
||||
SteeringManager.SteeringManual(deltaTime, dir);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use path finding
|
||||
SteeringManager.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), 2);
|
||||
if (!PathSteering.IsPathDirty && PathSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
// Can't reach
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
SteeringManager.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), 5);
|
||||
if (Character.AnimController.InWater)
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
|
||||
// Outside
|
||||
SteeringManager.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), 5);
|
||||
if (Character.AnimController.InWater)
|
||||
{
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1748,6 +1780,10 @@ namespace Barotrauma
|
||||
{
|
||||
targetingTag = "dead";
|
||||
}
|
||||
else if (PetBehavior != null && aiTarget.Entity == PetBehavior.Owner)
|
||||
{
|
||||
targetingTag = "owner";
|
||||
}
|
||||
else if (AIParams.TryGetTarget(targetCharacter.SpeciesName, out CharacterParams.TargetParams tP))
|
||||
{
|
||||
targetingTag = tP.Tag;
|
||||
@@ -1992,9 +2028,16 @@ namespace Barotrauma
|
||||
if (targetingTag == null) { continue; }
|
||||
var targetParams = GetTargetParams(targetingTag);
|
||||
if (targetParams == null) { continue; }
|
||||
if (targetCharacter != null && targetCharacter.Submarine != Character.Submarine && targetParams.State == AIState.Observe)
|
||||
if (targetParams.State == AIState.Observe || targetParams.State == AIState.Eat)
|
||||
{
|
||||
if (targetCharacter != null && targetCharacter.Submarine != Character.Submarine)
|
||||
{
|
||||
// Don't allow to target characters that are inside a different submarine / outside when we are inside.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (aiTarget.Entity is Item targetItem && targetParams.IgnoreContained && targetItem.ParentInventory != null)
|
||||
{
|
||||
// Don't allow to observe characters that are inside a different submarine / outside when we are inside.
|
||||
continue;
|
||||
}
|
||||
valueModifier *= targetParams.Priority;
|
||||
@@ -2066,6 +2109,17 @@ namespace Barotrauma
|
||||
}
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
if (Character.CurrentHull != null && targetCharacter.CurrentHull != Character.CurrentHull)
|
||||
{
|
||||
if (targetParams.State == AIState.Follow || targetParams.State == AIState.Protect || targetParams.State == AIState.Observe)
|
||||
{
|
||||
// Ignore targets that cannot see
|
||||
if (!VisibleHulls.Contains(targetCharacter.CurrentHull))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetCharacter.Submarine != Character.Submarine)
|
||||
{
|
||||
if (targetCharacter.Submarine != null)
|
||||
|
||||
@@ -1111,7 +1111,10 @@ namespace Barotrauma
|
||||
public bool TakeItem(Item item, Inventory targetInventory, bool equip, bool dropOtherIfCannotMove = true, bool allowSwapping = false, bool storeUnequipped = false)
|
||||
{
|
||||
var pickable = item.GetComponent<Pickable>();
|
||||
if (pickable == null) { return false; }
|
||||
if (item.ParentInventory is ItemInventory itemInventory)
|
||||
{
|
||||
if (!itemInventory.Container.HasRequiredItems(Character, addMessage: false)) { return false; }
|
||||
}
|
||||
if (equip)
|
||||
{
|
||||
int targetSlot = -1;
|
||||
|
||||
@@ -190,6 +190,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
pathFinder.InsideSubmarine = character.Submarine != null;
|
||||
pathFinder.ApplyPenaltyToOutsideNodes = character.PressureProtection <= 0;
|
||||
var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility);
|
||||
bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || findPathTimer < -1;
|
||||
if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable)
|
||||
@@ -343,7 +344,7 @@ namespace Barotrauma
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
else if (!canClimb || character.AnimController.InWater)
|
||||
else if (character.AnimController.InWater)
|
||||
{
|
||||
// If the character is underwater, we don't need the ladders anymore
|
||||
if (character.IsClimbing && isDiving)
|
||||
@@ -370,7 +371,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!IsNextLadderSameAsCurrent)
|
||||
else if (!canClimb || !IsNextLadderSameAsCurrent)
|
||||
{
|
||||
// Walking horizontally
|
||||
Vector2 colliderBottom = character.AnimController.GetColliderBottom();
|
||||
@@ -378,6 +379,8 @@ namespace Barotrauma
|
||||
Vector2 velocity = collider.LinearVelocity;
|
||||
// If the character is smaller than this, it would fail to use the waypoint nodes because they are always too high.
|
||||
float minHeight = 1;
|
||||
// If the character is very thin, without a min value, it would often fail to reach the waypoints, because the horizontal distance is too small.
|
||||
float minWidth = 0.17f;
|
||||
// Cannot use the head position, because not all characters have head or it can be below the total height of the character
|
||||
float characterHeight = Math.Max(colliderSize.Y + character.AnimController.ColliderHeightFromFloor, minHeight);
|
||||
float horizontalDistance = Math.Abs(collider.SimPosition.X - currentPath.CurrentNode.SimPosition.X);
|
||||
@@ -386,7 +389,7 @@ namespace Barotrauma
|
||||
var door = currentPath.CurrentNode.ConnectedDoor;
|
||||
bool blockedByDoor = door != null && !door.IsOpen && !door.IsBroken;
|
||||
float margin = MathHelper.Lerp(1, 10, MathHelper.Clamp(Math.Abs(velocity.X) / 10, 0, 1));
|
||||
float targetDistance = collider.radius * margin;
|
||||
float targetDistance = Math.Max(collider.radius * margin, minWidth);
|
||||
if (horizontalDistance < targetDistance && isAboveFeet && isNotTooHigh && !blockedByDoor)
|
||||
{
|
||||
currentPath.SkipToNextNode();
|
||||
|
||||
@@ -204,11 +204,15 @@ namespace Barotrauma
|
||||
{
|
||||
// Don't ignore any hulls if outside, because apparently it happens that we can't find a path, in which case we just want to try again.
|
||||
// If we ignore the hull, it might be the only airlock in the target sub, which ignores the whole sub.
|
||||
if (currentHull != null && goToObjective != null)
|
||||
// If the target hull is inside a submarine that is not our main sub, just ignore it normally when it cannot be reached. This happens with outposts.
|
||||
if (goToObjective != null)
|
||||
{
|
||||
if (goToObjective.Target is Hull hull)
|
||||
{
|
||||
HumanAIController.UnreachableHulls.Add(hull);
|
||||
if (currentHull != null || !Submarine.MainSubs.Contains(hull.Submarine))
|
||||
{
|
||||
HumanAIController.UnreachableHulls.Add(hull);
|
||||
}
|
||||
}
|
||||
}
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
|
||||
@@ -257,6 +257,10 @@ namespace Barotrauma
|
||||
// Don't allow going into another sub, unless it's connected and of the same team and type.
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(item, includingConnectedSubs: true)) { continue; }
|
||||
if (character.IsItemTakenBySomeoneElse(item)) { continue; }
|
||||
if (item.ParentInventory is ItemInventory itemInventory)
|
||||
{
|
||||
if (!itemInventory.Container.HasRequiredItems(character, addMessage: false)) { continue; }
|
||||
}
|
||||
float itemPriority = 1;
|
||||
if (GetItemPriority != null)
|
||||
{
|
||||
|
||||
@@ -342,7 +342,7 @@ namespace Barotrauma
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(Target.WorldPosition - character.WorldPosition));
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 15);
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -360,7 +360,10 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
SteeringManager.SteeringSeek(character.GetRelativeSimPosition(Target), 10);
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 15);
|
||||
if (character.AnimController.InWater)
|
||||
{
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace Barotrauma
|
||||
private readonly List<PathNode> nodes;
|
||||
|
||||
public bool InsideSubmarine { get; set; }
|
||||
public bool ApplyPenaltyToOutsideNodes { get; set; }
|
||||
|
||||
public PathFinder(List<WayPoint> wayPoints, bool indoorsSteering = false)
|
||||
{
|
||||
@@ -178,7 +179,7 @@ namespace Barotrauma
|
||||
node.TempDistance = xDiff + (InsideSubmarine ? yDiff * 10.0f : yDiff); //higher cost for vertical movement when inside the sub
|
||||
|
||||
//much higher cost to waypoints that are outside
|
||||
if (node.Waypoint.CurrentHull == null && InsideSubmarine) { node.TempDistance *= 10.0f; }
|
||||
if (node.Waypoint.CurrentHull == null && ApplyPenaltyToOutsideNodes) { node.TempDistance *= 10.0f; }
|
||||
|
||||
//prefer nodes that are closer to the end position
|
||||
node.TempDistance += (Math.Abs(end.X - node.TempPosition.X) + Math.Abs(end.Y - node.TempPosition.Y)) / 100.0f;
|
||||
@@ -231,8 +232,11 @@ namespace Barotrauma
|
||||
node.TempDistance = Vector2.DistanceSquared(end, node.TempPosition);
|
||||
if (InsideSubmarine)
|
||||
{
|
||||
//much higher cost to waypoints that are outside
|
||||
if (node.Waypoint.CurrentHull == null) { node.TempDistance *= 10.0f; }
|
||||
if (ApplyPenaltyToOutsideNodes)
|
||||
{
|
||||
//much higher cost to waypoints that are outside
|
||||
if (node.Waypoint.CurrentHull == null) { node.TempDistance *= 10.0f; }
|
||||
}
|
||||
//avoid stopping at a doorway
|
||||
if (node.Waypoint.ConnectedDoor != null) { node.TempDistance *= 10.0f; }
|
||||
//avoid stopping at a ladder
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
@@ -9,6 +10,14 @@ namespace Barotrauma
|
||||
{
|
||||
class PetBehavior
|
||||
{
|
||||
public enum StatusIndicatorType
|
||||
{
|
||||
None,
|
||||
Happy,
|
||||
Sad,
|
||||
Hungry
|
||||
}
|
||||
|
||||
public float Hunger { get; set; } = 50.0f;
|
||||
public float Happiness { get; set; } = 50.0f;
|
||||
|
||||
@@ -21,6 +30,7 @@ namespace Barotrauma
|
||||
public float PlayForce { get; set; }
|
||||
|
||||
public float PlayTimer { get; set; }
|
||||
private float? unstunY { get; set; }
|
||||
|
||||
public EnemyAIController AiController { get; private set; } = null;
|
||||
|
||||
@@ -126,6 +136,7 @@ namespace Barotrauma
|
||||
public float Hunger;
|
||||
public float Happiness;
|
||||
public float Priority;
|
||||
public bool IgnoreContained;
|
||||
|
||||
public CharacterParams.TargetParams TargetParams = null;
|
||||
}
|
||||
@@ -159,7 +170,11 @@ namespace Barotrauma
|
||||
case "eat":
|
||||
Food food = new Food
|
||||
{
|
||||
Tag = subElement.GetAttributeString("tag", "")
|
||||
Tag = subElement.GetAttributeString("tag", ""),
|
||||
Hunger = subElement.GetAttributeFloat("hunger", -1),
|
||||
Happiness = subElement.GetAttributeFloat("happiness", 1),
|
||||
Priority = subElement.GetAttributeFloat("priority", 100),
|
||||
IgnoreContained = subElement.GetAttributeBool("ignorecontained", true)
|
||||
};
|
||||
string[] requiredHungerStr = subElement.GetAttributeString("requiredhunger", "0-100").Split('-');
|
||||
food.HungerRange = new Vector2(0, 100);
|
||||
@@ -168,15 +183,20 @@ namespace Barotrauma
|
||||
if (float.TryParse(requiredHungerStr[0], NumberStyles.Any, CultureInfo.InvariantCulture, out float tempF)) { food.HungerRange.X = tempF; }
|
||||
if (float.TryParse(requiredHungerStr[1], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { food.HungerRange.Y = tempF; }
|
||||
}
|
||||
food.Hunger = subElement.GetAttributeFloat("hunger", -1);
|
||||
food.Happiness = subElement.GetAttributeFloat("happiness", 1);
|
||||
food.Priority = subElement.GetAttributeFloat("priority", 100);
|
||||
foods.Add(food);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public StatusIndicatorType GetCurrentStatusIndicatorType()
|
||||
{
|
||||
if (Hunger > MaxHunger * 0.5f) { return StatusIndicatorType.Hungry; }
|
||||
if (Happiness > MaxHappiness * 0.75f) { return StatusIndicatorType.Happy; }
|
||||
if (Happiness < MaxHappiness * 0.25f) { return StatusIndicatorType.Sad; }
|
||||
return StatusIndicatorType.None;
|
||||
}
|
||||
|
||||
public void OnEat(IEnumerable<string> tags, float amount)
|
||||
{
|
||||
for (int i = 0; i < foods.Count; i++)
|
||||
@@ -203,17 +223,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void Play()
|
||||
public void Play(Character player)
|
||||
{
|
||||
if (PlayTimer > 0.0f) { return; }
|
||||
if (Owner == null) { Owner = player; }
|
||||
PlayTimer = 5.0f;
|
||||
AiController.Character.Stun = 1.0f;
|
||||
AiController.Character.IsRagdolled = true;
|
||||
Happiness += 10.0f;
|
||||
if (Happiness > MaxHappiness) { Happiness = MaxHappiness; }
|
||||
AiController.Character.AnimController.MainLimb.body.LinearVelocity += new Vector2(0, PlayForce);
|
||||
unstunY = AiController.Character.SimPosition.Y;
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
public string GetTagName()
|
||||
{
|
||||
if (AiController.Character.Inventory != null)
|
||||
{
|
||||
@@ -230,14 +252,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
return AiController.Character.Name;
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
var character = AiController.Character;
|
||||
if (character?.Removed ?? true || character.IsDead) { return; }
|
||||
if (GameMain.NetworkMember?.IsClient ?? false) { return; } //TODO: syncing
|
||||
if (GameMain.NetworkMember?.IsClient ?? false) { return; }
|
||||
|
||||
if (Owner != null && (Owner.Removed || Owner.IsDead)) { Owner = null; }
|
||||
|
||||
Hunger += HungerIncreaseRate * deltaTime;
|
||||
|
||||
@@ -245,20 +269,45 @@ namespace Barotrauma
|
||||
|
||||
PlayTimer -= deltaTime;
|
||||
|
||||
for (int i = 0; i < foods.Count; i++)
|
||||
if (unstunY.HasValue)
|
||||
{
|
||||
if (Hunger >= foods[i].HungerRange.X && Hunger <= foods[i].HungerRange.Y)
|
||||
if (PlayTimer > 4.0f)
|
||||
{
|
||||
if (foods[i].TargetParams == null &&
|
||||
AiController.AIParams.TryAddNewTarget(foods[i].Tag, AIState.Eat, foods[i].Priority, out CharacterParams.TargetParams targetParams))
|
||||
float extent = character.AnimController.MainLimb.body.GetMaxExtent();
|
||||
if (character.SimPosition.Y < (unstunY.Value + extent * 3.0f) &&
|
||||
character.AnimController.MainLimb.body.LinearVelocity.Y < 0.0f)
|
||||
{
|
||||
foods[i].TargetParams = targetParams;
|
||||
character.IsRagdolled = false;
|
||||
unstunY = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
character.IsRagdolled = true;
|
||||
}
|
||||
}
|
||||
else if (foods[i].TargetParams != null)
|
||||
else
|
||||
{
|
||||
AiController.AIParams.RemoveTarget(foods[i].TargetParams);
|
||||
foods[i].TargetParams = null;
|
||||
character.IsRagdolled = false;
|
||||
unstunY = null;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < foods.Count; i++)
|
||||
{
|
||||
Food food = foods[i];
|
||||
if (Hunger >= food.HungerRange.X && Hunger <= food.HungerRange.Y)
|
||||
{
|
||||
if (food.TargetParams == null &&
|
||||
AiController.AIParams.TryAddNewTarget(food.Tag, AIState.Eat, food.Priority, out CharacterParams.TargetParams targetParams))
|
||||
{
|
||||
targetParams.IgnoreContained = food.IgnoreContained;
|
||||
food.TargetParams = targetParams;
|
||||
}
|
||||
}
|
||||
else if (food.TargetParams != null)
|
||||
{
|
||||
AiController.AIParams.RemoveTarget(food.TargetParams);
|
||||
food.TargetParams = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +328,8 @@ namespace Barotrauma
|
||||
|
||||
if (character.SelectedBy != null)
|
||||
{
|
||||
character.Stun = 1.0f;
|
||||
character.IsRagdolled = true;
|
||||
unstunY = character.SimPosition.Y;
|
||||
}
|
||||
|
||||
for (int i = 0; i < itemsToProduce.Count; i++)
|
||||
@@ -287,5 +337,73 @@ namespace Barotrauma
|
||||
itemsToProduce[i].Update(this, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SavePets(XElement petsElement)
|
||||
{
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (!c.IsPet || c.IsDead) { continue; }
|
||||
if (c.Submarine?.Info.Type != SubmarineType.Player) { continue; }
|
||||
|
||||
var petBehavior = (c.AIController as EnemyAIController)?.PetBehavior;
|
||||
if (petBehavior == null) { continue; }
|
||||
|
||||
XElement petElement = new XElement("pet",
|
||||
new XAttribute("speciesname", c.SpeciesName),
|
||||
new XAttribute("ownerid", petBehavior.Owner?.ID ?? Entity.NullEntityID),
|
||||
new XAttribute("seed", c.Seed));
|
||||
|
||||
var petBehaviorElement = new XElement("petbehavior",
|
||||
new XAttribute("hunger", petBehavior.Hunger.ToString("G", CultureInfo.InvariantCulture)),
|
||||
new XAttribute("happiness", petBehavior.Happiness.ToString("G", CultureInfo.InvariantCulture)));
|
||||
petElement.Add(petBehaviorElement);
|
||||
|
||||
var healthElement = new XElement("health");
|
||||
c.CharacterHealth.Save(healthElement);
|
||||
petElement.Add(healthElement);
|
||||
|
||||
if (c.Inventory != null)
|
||||
{
|
||||
var inventoryElement = new XElement("inventory");
|
||||
c.SaveInventory(c.Inventory, inventoryElement);
|
||||
petElement.Add(inventoryElement);
|
||||
}
|
||||
|
||||
petsElement.Add(petElement);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadPets(XElement petsElement)
|
||||
{
|
||||
foreach (XElement subElement in petsElement.Elements())
|
||||
{
|
||||
string speciesName = subElement.GetAttributeString("speciesname", "");
|
||||
string seed = subElement.GetAttributeString("seed", "123");
|
||||
ushort ownerID = (ushort)subElement.GetAttributeInt("ownerid", 0);
|
||||
Vector2 spawnPos = Vector2.Zero;
|
||||
Character owner = Entity.FindEntityByID(ownerID) as Character;
|
||||
if (owner != null)
|
||||
{
|
||||
spawnPos = owner.WorldPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
var spawnPoint = WayPoint.WayPointList.Where(wp => wp.SpawnType == SpawnType.Human && wp.Submarine?.Info.Type == SubmarineType.Player).GetRandom();
|
||||
spawnPos = spawnPoint?.WorldPosition ?? Submarine.MainSub.WorldPosition;
|
||||
}
|
||||
var pet = Character.Create(speciesName, spawnPos, seed);
|
||||
var petBehavior = (pet.AIController as EnemyAIController)?.PetBehavior;
|
||||
if (petBehavior != null)
|
||||
{
|
||||
petBehavior.Owner = owner;
|
||||
var petBehaviorElement = subElement.Attribute("petbehavior");
|
||||
if (petBehaviorElement != null)
|
||||
{
|
||||
petBehavior.Hunger = petBehaviorElement.GetAttributeFloat(50.0f);
|
||||
petBehavior.Happiness = petBehaviorElement.GetAttributeFloat(50.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ namespace Barotrauma
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
if (!Enabled) { return; }
|
||||
if (!IsRemotePlayer && AIController is EnemyAIController enemyAi)
|
||||
{
|
||||
enemyAi.PetBehavior?.Update(deltaTime);
|
||||
}
|
||||
if (IsDead || Vitality <= 0.0f || Stun > 0.0f || IsIncapacitated)
|
||||
{
|
||||
//don't enable simple physics on dead/incapacitated characters
|
||||
|
||||
@@ -429,113 +429,195 @@ namespace Barotrauma
|
||||
{
|
||||
WalkPos = MathHelper.SmoothStep(WalkPos, MathHelper.PiOver2, deltaTime * 5);
|
||||
mainLimb.PullJointWorldAnchorB = Collider.SimPosition;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 transformedMovement = reverse ? -movement : movement;
|
||||
float movementAngle = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
|
||||
float mainLimbAngle = 0;
|
||||
if (mainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
|
||||
{
|
||||
mainLimbAngle = TorsoAngle.Value;
|
||||
}
|
||||
else if (mainLimb.type == LimbType.Head && HeadAngle.HasValue)
|
||||
{
|
||||
mainLimbAngle = HeadAngle.Value;
|
||||
}
|
||||
mainLimbAngle *= Dir;
|
||||
while (mainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
|
||||
{
|
||||
movementAngle += MathHelper.TwoPi;
|
||||
}
|
||||
while (mainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
|
||||
{
|
||||
movementAngle -= MathHelper.TwoPi;
|
||||
}
|
||||
|
||||
if (CurrentSwimParams.RotateTowardsMovement)
|
||||
{
|
||||
Collider.SmoothRotate(movementAngle, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
|
||||
if (TorsoAngle.HasValue)
|
||||
{
|
||||
Limb torso = GetLimb(LimbType.Torso);
|
||||
if (torso != null)
|
||||
{
|
||||
SmoothRotateWithoutWrapping(torso, movementAngle + TorsoAngle.Value * Dir, mainLimb, TorsoTorque);
|
||||
}
|
||||
}
|
||||
if (HeadAngle.HasValue)
|
||||
{
|
||||
Limb head = GetLimb(LimbType.Head);
|
||||
if (head != null)
|
||||
{
|
||||
SmoothRotateWithoutWrapping(head, movementAngle + HeadAngle.Value * Dir, mainLimb, HeadTorque);
|
||||
}
|
||||
}
|
||||
if (TailAngle.HasValue)
|
||||
{
|
||||
Limb tail = GetLimb(LimbType.Tail);
|
||||
if (tail != null)
|
||||
{
|
||||
float? mainLimbTargetAngle = null;
|
||||
if (mainLimb.type == LimbType.Torso)
|
||||
{
|
||||
mainLimbTargetAngle = TorsoAngle;
|
||||
}
|
||||
else if (mainLimb.type == LimbType.Head)
|
||||
{
|
||||
mainLimbTargetAngle = HeadAngle;
|
||||
}
|
||||
float torque = TailTorque;
|
||||
float maxMultiplier = CurrentSwimParams.TailTorqueMultiplier;
|
||||
if (mainLimbTargetAngle.HasValue && maxMultiplier > 1)
|
||||
{
|
||||
float diff = Math.Abs(mainLimb.Rotation - tail.Rotation);
|
||||
float offset = Math.Abs(mainLimbTargetAngle.Value - TailAngle.Value);
|
||||
torque *= MathHelper.Lerp(1, maxMultiplier, MathUtils.InverseLerp(0, MathHelper.PiOver2, diff - offset));
|
||||
}
|
||||
SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, mainLimb, torque);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
|
||||
if (reverse)
|
||||
Vector2 transformedMovement = reverse ? -movement : movement;
|
||||
float movementAngle = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
|
||||
float mainLimbAngle = 0;
|
||||
if (mainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
|
||||
{
|
||||
movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
|
||||
mainLimbAngle = TorsoAngle.Value;
|
||||
}
|
||||
if (mainLimb.type == LimbType.Head && HeadAngle.HasValue)
|
||||
else if (mainLimb.type == LimbType.Head && HeadAngle.HasValue)
|
||||
{
|
||||
Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
|
||||
mainLimbAngle = HeadAngle.Value;
|
||||
}
|
||||
else if (mainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
|
||||
mainLimbAngle *= Dir;
|
||||
while (mainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
|
||||
{
|
||||
Collider.SmoothRotate(TorsoAngle.Value * Dir, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
|
||||
movementAngle += MathHelper.TwoPi;
|
||||
}
|
||||
if (TorsoAngle.HasValue)
|
||||
while (mainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
|
||||
{
|
||||
Limb torso = GetLimb(LimbType.Torso);
|
||||
torso?.body.SmoothRotate(TorsoAngle.Value * Dir, TorsoTorque);
|
||||
movementAngle -= MathHelper.TwoPi;
|
||||
}
|
||||
if (HeadAngle.HasValue)
|
||||
if (CurrentSwimParams.RotateTowardsMovement)
|
||||
{
|
||||
Limb head = GetLimb(LimbType.Head);
|
||||
head?.body.SmoothRotate(HeadAngle.Value * Dir, HeadTorque);
|
||||
}
|
||||
if (TailAngle.HasValue)
|
||||
{
|
||||
Limb tail = GetLimb(LimbType.Tail);
|
||||
tail?.body.SmoothRotate(TailAngle.Value * Dir, TailTorque);
|
||||
}
|
||||
}
|
||||
Collider.SmoothRotate(movementAngle, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
|
||||
if (TorsoAngle.HasValue)
|
||||
{
|
||||
Limb torso = GetLimb(LimbType.Torso);
|
||||
if (torso != null)
|
||||
{
|
||||
SmoothRotateWithoutWrapping(torso, movementAngle + TorsoAngle.Value * Dir, mainLimb, TorsoTorque);
|
||||
}
|
||||
}
|
||||
if (HeadAngle.HasValue)
|
||||
{
|
||||
Limb head = GetLimb(LimbType.Head);
|
||||
if (head != null)
|
||||
{
|
||||
SmoothRotateWithoutWrapping(head, movementAngle + HeadAngle.Value * Dir, mainLimb, HeadTorque);
|
||||
}
|
||||
}
|
||||
if (TailAngle.HasValue)
|
||||
{
|
||||
bool isAngleApplied = false;
|
||||
foreach (var limb in Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.type != LimbType.Tail) { continue; }
|
||||
if (!limb.Params.ApplyTailAngle) { continue; }
|
||||
RotateTail(limb);
|
||||
isAngleApplied = true;
|
||||
}
|
||||
if (!isAngleApplied)
|
||||
{
|
||||
RotateTail(GetLimb(LimbType.Tail));
|
||||
}
|
||||
|
||||
var waveLength = Math.Abs(CurrentSwimParams.WaveLength * RagdollParams.JointScale);
|
||||
var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude * character.SpeedMultiplier);
|
||||
if (waveLength > 0 && waveAmplitude > 0)
|
||||
{
|
||||
WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
|
||||
WalkPos = MathUtils.WrapAngleTwoPi(WalkPos);
|
||||
void RotateTail(Limb tail)
|
||||
{
|
||||
if (tail == null) { return; }
|
||||
float? mainLimbTargetAngle = null;
|
||||
if (mainLimb.type == LimbType.Torso)
|
||||
{
|
||||
mainLimbTargetAngle = TorsoAngle;
|
||||
}
|
||||
else if (mainLimb.type == LimbType.Head)
|
||||
{
|
||||
mainLimbTargetAngle = HeadAngle;
|
||||
}
|
||||
float torque = TailTorque;
|
||||
float maxMultiplier = CurrentSwimParams.TailTorqueMultiplier;
|
||||
if (mainLimbTargetAngle.HasValue && maxMultiplier > 1)
|
||||
{
|
||||
float diff = Math.Abs(mainLimb.Rotation - tail.Rotation);
|
||||
float offset = Math.Abs(mainLimbTargetAngle.Value - TailAngle.Value);
|
||||
torque *= MathHelper.Lerp(1, maxMultiplier, MathUtils.InverseLerp(0, MathHelper.PiOver2, diff - offset));
|
||||
}
|
||||
SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, mainLimb, torque);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
|
||||
if (reverse)
|
||||
{
|
||||
movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
|
||||
}
|
||||
if (mainLimb.type == LimbType.Head && HeadAngle.HasValue)
|
||||
{
|
||||
Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
|
||||
}
|
||||
else if (mainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
|
||||
{
|
||||
Collider.SmoothRotate(TorsoAngle.Value * Dir, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
|
||||
}
|
||||
if (TorsoAngle.HasValue)
|
||||
{
|
||||
Limb torso = GetLimb(LimbType.Torso);
|
||||
torso?.body.SmoothRotate(TorsoAngle.Value * Dir, TorsoTorque);
|
||||
}
|
||||
if (HeadAngle.HasValue)
|
||||
{
|
||||
Limb head = GetLimb(LimbType.Head);
|
||||
head?.body.SmoothRotate(HeadAngle.Value * Dir, HeadTorque);
|
||||
}
|
||||
if (TailAngle.HasValue)
|
||||
{
|
||||
bool isAngleApplied = false;
|
||||
foreach (var limb in Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.type != LimbType.Tail) { continue; }
|
||||
if (!limb.Params.ApplyTailAngle) { continue; }
|
||||
RotateTail(limb);
|
||||
isAngleApplied = true;
|
||||
}
|
||||
if (!isAngleApplied)
|
||||
{
|
||||
RotateTail(GetLimb(LimbType.Tail));
|
||||
}
|
||||
|
||||
void RotateTail(Limb tail)
|
||||
{
|
||||
if (tail != null)
|
||||
{
|
||||
tail.body.SmoothRotate(TailAngle.Value * Dir, TailTorque);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var waveLength = Math.Abs(CurrentSwimParams.WaveLength * RagdollParams.JointScale);
|
||||
var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude * character.SpeedMultiplier);
|
||||
if (waveLength > 0 && waveAmplitude > 0)
|
||||
{
|
||||
WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
|
||||
WalkPos = MathUtils.WrapAngleTwoPi(WalkPos);
|
||||
}
|
||||
|
||||
foreach (var limb in Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
switch (limb.type)
|
||||
{
|
||||
case LimbType.LeftFoot:
|
||||
case LimbType.RightFoot:
|
||||
if (CurrentSwimParams.FootAnglesInRadians.ContainsKey(limb.Params.ID))
|
||||
{
|
||||
SmoothRotateWithoutWrapping(limb, movementAngle + CurrentSwimParams.FootAnglesInRadians[limb.Params.ID] * Dir, mainLimb, FootTorque);
|
||||
}
|
||||
break;
|
||||
case LimbType.Tail:
|
||||
if (waveLength > 0 && waveAmplitude > 0)
|
||||
{
|
||||
float waveRotation = (float)Math.Sin(WalkPos * limb.Params.SineFrequencyMultiplier);
|
||||
limb.body.ApplyTorque(waveRotation * limb.Mass * waveAmplitude * limb.Params.SineAmplitudeMultiplier);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < Limbs.Length; i++)
|
||||
{
|
||||
var limb = Limbs[i];
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.SteerForce <= 0.0f) { continue; }
|
||||
if (!Collider.PhysEnabled) { continue; }
|
||||
Vector2 pullPos = limb.PullJointWorldAnchorA;
|
||||
limb.body.ApplyForce(movement * limb.SteerForce * limb.Mass * Math.Max(character.SpeedMultiplier, 1), pullPos);
|
||||
}
|
||||
|
||||
Vector2 mainLimbDiff = mainLimb.PullJointWorldAnchorB - mainLimb.SimPosition;
|
||||
if (CurrentSwimParams.UseSineMovement)
|
||||
{
|
||||
mainLimb.PullJointWorldAnchorB = Vector2.SmoothStep(
|
||||
mainLimb.PullJointWorldAnchorB,
|
||||
Collider.SimPosition,
|
||||
mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : (float)Math.Abs(Math.Sin(WalkPos)));
|
||||
}
|
||||
else
|
||||
{
|
||||
//mainLimb.PullJointWorldAnchorB = Collider.SimPosition;
|
||||
mainLimb.PullJointWorldAnchorB = Vector2.Lerp(
|
||||
mainLimb.PullJointWorldAnchorB,
|
||||
Collider.SimPosition,
|
||||
mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var limb in Limbs)
|
||||
@@ -543,55 +625,15 @@ namespace Barotrauma
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (Math.Abs(limb.Params.ConstantTorque) > 0)
|
||||
{
|
||||
limb.body.SmoothRotate(movementAngle + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Params.ConstantTorque, wrapAngle: true);
|
||||
limb.body.SmoothRotate(MainLimb.Rotation + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Mass * limb.Params.ConstantTorque, wrapAngle: true);
|
||||
}
|
||||
switch (limb.type)
|
||||
if (limb.Params.BlinkFrequency > 0)
|
||||
{
|
||||
case LimbType.LeftFoot:
|
||||
case LimbType.RightFoot:
|
||||
if (CurrentSwimParams.FootAnglesInRadians.ContainsKey(limb.Params.ID))
|
||||
{
|
||||
SmoothRotateWithoutWrapping(limb, movementAngle + CurrentSwimParams.FootAnglesInRadians[limb.Params.ID] * Dir, mainLimb, FootTorque);
|
||||
}
|
||||
break;
|
||||
case LimbType.Tail:
|
||||
if (waveLength > 0 && waveAmplitude > 0)
|
||||
{
|
||||
float waveRotation = (float)Math.Sin(WalkPos);
|
||||
limb.body.ApplyTorque(waveRotation * limb.Mass * waveAmplitude);
|
||||
}
|
||||
break;
|
||||
limb.Blink(deltaTime, MainLimb.Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < Limbs.Length; i++)
|
||||
{
|
||||
var limb = Limbs[i];
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.SteerForce <= 0.0f) { continue; }
|
||||
if (!Collider.PhysEnabled) { continue; }
|
||||
Vector2 pullPos = limb.PullJointWorldAnchorA;
|
||||
limb.body.ApplyForce(movement * limb.SteerForce * limb.Mass * Math.Max(character.SpeedMultiplier, 1), pullPos);
|
||||
}
|
||||
|
||||
Vector2 mainLimbDiff = mainLimb.PullJointWorldAnchorB - mainLimb.SimPosition;
|
||||
if (CurrentSwimParams.UseSineMovement)
|
||||
{
|
||||
mainLimb.PullJointWorldAnchorB = Vector2.SmoothStep(
|
||||
mainLimb.PullJointWorldAnchorB,
|
||||
Collider.SimPosition,
|
||||
mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : (float)Math.Abs(Math.Sin(WalkPos)));
|
||||
}
|
||||
else
|
||||
{
|
||||
//mainLimb.PullJointWorldAnchorB = Collider.SimPosition;
|
||||
mainLimb.PullJointWorldAnchorB = Vector2.Lerp(
|
||||
mainLimb.PullJointWorldAnchorB,
|
||||
Collider.SimPosition,
|
||||
mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : 0.5f);
|
||||
}
|
||||
|
||||
floorY = Limbs[0].SimPosition.Y;
|
||||
floorY = Limbs[0].SimPosition.Y;
|
||||
}
|
||||
|
||||
void UpdateWalkAnim(float deltaTime)
|
||||
@@ -686,10 +728,26 @@ namespace Barotrauma
|
||||
|
||||
if (TailAngle.HasValue)
|
||||
{
|
||||
var tail = GetLimb(LimbType.Tail);
|
||||
if (tail != null)
|
||||
bool isAngleApplied = false;
|
||||
foreach (var limb in Limbs)
|
||||
{
|
||||
SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, mainLimb, TailTorque);
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.type != LimbType.Tail) { continue; }
|
||||
if (!limb.Params.ApplyTailAngle) { continue; }
|
||||
RotateTail(limb);
|
||||
isAngleApplied = true;
|
||||
}
|
||||
if (!isAngleApplied)
|
||||
{
|
||||
RotateTail(GetLimb(LimbType.Tail));
|
||||
}
|
||||
|
||||
void RotateTail(Limb tail)
|
||||
{
|
||||
if (tail != null)
|
||||
{
|
||||
SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, mainLimb, TailTorque);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,7 +767,11 @@ namespace Barotrauma
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (Math.Abs(limb.Params.ConstantTorque) > 0)
|
||||
{
|
||||
limb.body.SmoothRotate(movementAngle + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Params.ConstantTorque, wrapAngle: true);
|
||||
limb.body.SmoothRotate(MainLimb.Rotation + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Mass * limb.Params.ConstantTorque, wrapAngle: true);
|
||||
}
|
||||
if (limb.Params.BlinkFrequency > 0)
|
||||
{
|
||||
limb.Blink(deltaTime, MainLimb.Rotation);
|
||||
}
|
||||
switch (limb.type)
|
||||
{
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
private LimbJoint shoulder;
|
||||
private LimbJoint rightShoulder, leftShoulder;
|
||||
|
||||
private float upperLegLength = 0.0f, lowerLegLength = 0.0f;
|
||||
|
||||
@@ -241,12 +241,13 @@ namespace Barotrauma
|
||||
Limb rightHand = GetLimb(LimbType.RightHand);
|
||||
if (rightHand == null) { return; }
|
||||
|
||||
shoulder = GetJointBetweenLimbs(LimbType.Torso, LimbType.RightArm);
|
||||
rightShoulder = GetJointBetweenLimbs(LimbType.Torso, LimbType.RightArm);
|
||||
leftShoulder = GetJointBetweenLimbs(LimbType.Torso, LimbType.LeftArm);
|
||||
Vector2 localAnchorShoulder = Vector2.Zero;
|
||||
Vector2 localAnchorElbow = Vector2.Zero;
|
||||
if (shoulder != null)
|
||||
if (rightShoulder != null)
|
||||
{
|
||||
localAnchorShoulder = shoulder.LimbA.type == LimbType.RightArm ? shoulder.LocalAnchorA : shoulder.LocalAnchorB;
|
||||
localAnchorShoulder = rightShoulder.LimbA.type == LimbType.RightArm ? rightShoulder.LocalAnchorA : rightShoulder.LocalAnchorB;
|
||||
}
|
||||
LimbJoint rightElbow = rightForearm == null ?
|
||||
GetJointBetweenLimbs(LimbType.RightArm, LimbType.RightHand) :
|
||||
@@ -1612,7 +1613,7 @@ namespace Barotrauma
|
||||
pullLimb.PullJointMaxForce = 5000.0f;
|
||||
targetMovement *= MathHelper.Clamp(Mass / target.Mass, 0.5f, 1.0f);
|
||||
|
||||
Vector2 shoulderPos = shoulder.WorldAnchorA;
|
||||
Vector2 shoulderPos = rightShoulder.WorldAnchorA;
|
||||
Vector2 dragDir = inWater ? Vector2.Normalize(targetLimb.SimPosition - shoulderPos) : Vector2.UnitY;
|
||||
|
||||
targetAnchor = shoulderPos - dragDir * ConvertUnits.ToSimUnits(upperArmLength + forearmLength);
|
||||
@@ -1757,10 +1758,10 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
itemAngle = (torso.body.Rotation + holdAngle * Dir);
|
||||
itemAngle = torso.body.Rotation + holdAngle * Dir;
|
||||
}
|
||||
|
||||
Vector2 transformedHoldPos = shoulder.WorldAnchorA;
|
||||
Vector2 transformedHoldPos = rightShoulder.WorldAnchorA;
|
||||
if (itemPos == Vector2.Zero || isClimbing || usingController)
|
||||
{
|
||||
if (character.SelectedItems[0] == item)
|
||||
@@ -1781,15 +1782,17 @@ namespace Barotrauma
|
||||
if (character.SelectedItems[0] == item)
|
||||
{
|
||||
if (rightHand == null || rightHand.IsSevered) { return; }
|
||||
transformedHoldPos = rightShoulder.WorldAnchorA;
|
||||
rightHand.Disabled = true;
|
||||
}
|
||||
if (character.SelectedItems[1] == item)
|
||||
{
|
||||
if (leftHand == null || leftHand.IsSevered) { return; }
|
||||
transformedHoldPos = leftShoulder.WorldAnchorA;
|
||||
leftHand.Disabled = true;
|
||||
}
|
||||
|
||||
itemPos.X = itemPos.X * Dir;
|
||||
itemPos.X *= Dir;
|
||||
transformedHoldPos += Vector2.Transform(itemPos, Matrix.CreateRotationZ(itemAngle));
|
||||
}
|
||||
|
||||
@@ -1858,18 +1861,21 @@ namespace Barotrauma
|
||||
|
||||
private void HandIK(Limb hand, Vector2 pos, float force = 1.0f)
|
||||
{
|
||||
if (shoulder == null) { return; }
|
||||
Vector2 shoulderPos = shoulder.WorldAnchorA;
|
||||
Vector2 shoulderPos;
|
||||
|
||||
Limb arm, forearm;
|
||||
if (hand.type == LimbType.LeftHand)
|
||||
{
|
||||
if (leftShoulder == null) { return; }
|
||||
shoulderPos = leftShoulder.WorldAnchorA;
|
||||
arm = GetLimb(LimbType.LeftArm);
|
||||
forearm = GetLimb(LimbType.LeftForearm);
|
||||
LeftHandIKPos = pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rightShoulder == null) { return; }
|
||||
shoulderPos = rightShoulder.WorldAnchorA;
|
||||
arm = GetLimb(LimbType.RightArm);
|
||||
forearm = GetLimb(LimbType.RightForearm);
|
||||
RightHandIKPos = pos;
|
||||
|
||||
@@ -216,16 +216,18 @@ namespace Barotrauma
|
||||
get
|
||||
{
|
||||
Limb mainLimb = GetLimb(RagdollParams.MainLimb);
|
||||
if (mainLimb == null)
|
||||
if (!IsValid(mainLimb))
|
||||
{
|
||||
Limb torso = GetLimb(LimbType.Torso);
|
||||
Limb head = GetLimb(LimbType.Head);
|
||||
mainLimb = torso ?? head;
|
||||
if (mainLimb == null)
|
||||
if (!IsValid(mainLimb))
|
||||
{
|
||||
mainLimb = Limbs.FirstOrDefault(l => !l.IsSevered && !l.ignoreCollisions);
|
||||
mainLimb = Limbs.FirstOrDefault(l => IsValid(l));
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValid(Limb limb) => limb != null && !limb.IsSevered && !limb.ignoreCollisions;
|
||||
return mainLimb;
|
||||
}
|
||||
}
|
||||
@@ -753,7 +755,6 @@ namespace Barotrauma
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (connectedLimbs.Contains(limb)) { continue; }
|
||||
if (!character.IsDead && !limb.CanBeSeveredAlive) { continue; }
|
||||
limb.IsSevered = true;
|
||||
if (limb.type == LimbType.RightHand)
|
||||
{
|
||||
@@ -765,7 +766,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!string.IsNullOrEmpty(character.BloodDecalName))
|
||||
{
|
||||
character.CurrentHull?.AddDecal(character.BloodDecalName,
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace Barotrauma
|
||||
protected float oxygenAvailable;
|
||||
|
||||
//seed used to generate this character
|
||||
private readonly string seed;
|
||||
public readonly string Seed;
|
||||
protected Item focusedItem;
|
||||
private Character selectedCharacter, selectedBy;
|
||||
public Character LastAttacker;
|
||||
@@ -273,7 +273,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (IsPet)
|
||||
{
|
||||
return (AIController as EnemyAIController).PetBehavior.GetName();
|
||||
string petName = (AIController as EnemyAIController).PetBehavior.GetTagName();
|
||||
if (!string.IsNullOrEmpty(petName)) { return petName; }
|
||||
}
|
||||
|
||||
if (info != null && !string.IsNullOrWhiteSpace(info.Name)) { return info.Name; }
|
||||
@@ -827,7 +828,7 @@ namespace Barotrauma
|
||||
{
|
||||
prefab = CharacterPrefab.FindBySpeciesName(speciesName);
|
||||
|
||||
this.seed = seed;
|
||||
this.Seed = seed;
|
||||
MTRandom random = new MTRandom(ToolBox.StringToInt(seed));
|
||||
|
||||
selectedItems = new Item[2];
|
||||
@@ -2178,7 +2179,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (FocusedCharacter != null && !FocusedCharacter.IsIncapacitated && IsKeyHit(InputType.Use) && FocusedCharacter.IsPet && CanInteract)
|
||||
{
|
||||
(FocusedCharacter.AIController as EnemyAIController).PetBehavior.Play();
|
||||
(FocusedCharacter.AIController as EnemyAIController).PetBehavior.Play(this);
|
||||
}
|
||||
else if (FocusedCharacter != null && IsKeyHit(InputType.Health) && FocusedCharacter.CharacterHealth.UseHealthWindow && CanInteract && CanInteractWith(FocusedCharacter, 160f, false))
|
||||
{
|
||||
@@ -2918,7 +2919,7 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
// Don't allow beheading for monster attacks, because it happens too frequently (crawlers/tigerthreshers etc attacking each other -> they will most often target to the head)
|
||||
TrySeverLimbJoints(limbHit, attack.SeverLimbsProbability, attackResult.Damage, allowBeheading: AIController == null || AIController is HumanAIController);
|
||||
TrySeverLimbJoints(limbHit, attack.SeverLimbsProbability, attackResult.Damage, allowBeheading: attacker.IsHuman || attacker.IsPlayer);
|
||||
|
||||
return attackResult;
|
||||
}
|
||||
@@ -2944,7 +2945,8 @@ namespace Barotrauma
|
||||
foreach (LimbJoint joint in AnimController.LimbJoints)
|
||||
{
|
||||
if (!joint.CanBeSevered) { continue; }
|
||||
if (joint.LimbA != targetLimb && joint.LimbB != targetLimb) { continue; }
|
||||
// Limb A is where we usually create the joints from. Let's not allow severing when the "parent" limb is hit, or the head can pop off when we hit the torso, for example.
|
||||
if (joint.LimbB != targetLimb) { continue; }
|
||||
float probability = severLimbsProbability;
|
||||
if (!IsDead)
|
||||
{
|
||||
@@ -3225,6 +3227,11 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
|
||||
}
|
||||
|
||||
IsDead = true;
|
||||
|
||||
ApplyStatusEffects(ActionType.OnDeath, 1.0f);
|
||||
|
||||
@@ -768,6 +768,18 @@ namespace Barotrauma
|
||||
{
|
||||
attack.UpdateCoolDown(deltaTime);
|
||||
}
|
||||
|
||||
if (Params.BlinkFrequency > 0)
|
||||
{
|
||||
if (blinkTimer > -TotalBlinkDurationOut)
|
||||
{
|
||||
blinkTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
blinkTimer = Params.BlinkFrequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
@@ -1039,6 +1051,45 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private float blinkTimer;
|
||||
private float blinkPhase;
|
||||
|
||||
private float TotalBlinkDurationOut => Params.BlinkDurationOut + Params.BlinkHoldTime;
|
||||
|
||||
public void Blink(float deltaTime, float referenceRotation)
|
||||
{
|
||||
if (blinkTimer > -TotalBlinkDurationOut)
|
||||
{
|
||||
blinkPhase -= deltaTime;
|
||||
if (blinkPhase > 0)
|
||||
{
|
||||
// in
|
||||
float t = ToolBox.GetEasing(Params.BlinkTransitionIn, MathUtils.InverseLerp(1, 0, blinkPhase / Params.BlinkDurationIn));
|
||||
body.SmoothRotate(referenceRotation + MathHelper.ToRadians(Params.BlinkRotationIn) * Dir, Mass * Params.BlinkForce * t, wrapAngle: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Math.Abs(blinkPhase) < Params.BlinkHoldTime)
|
||||
{
|
||||
// hold
|
||||
body.SmoothRotate(referenceRotation + MathHelper.ToRadians(Params.BlinkRotationIn) * Dir, Mass * Params.BlinkForce, wrapAngle: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// out
|
||||
float t = ToolBox.GetEasing(Params.BlinkTransitionOut, MathUtils.InverseLerp(0, 1, -blinkPhase / TotalBlinkDurationOut));
|
||||
body.SmoothRotate(referenceRotation + MathHelper.ToRadians(Params.BlinkRotationOut) * Dir, Mass * Params.BlinkForce * t, wrapAngle: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// out
|
||||
blinkPhase = Params.BlinkDurationIn;
|
||||
body.SmoothRotate(referenceRotation + MathHelper.ToRadians(Params.BlinkRotationOut) * Dir, Mass * Params.BlinkForce, wrapAngle: true);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
body?.Remove();
|
||||
|
||||
@@ -305,7 +305,7 @@ namespace Barotrauma
|
||||
instance.Load(fullPath, speciesName);
|
||||
anims.Add(fileName, instance);
|
||||
DebugConsole.NewMessage($"[AnimationParams] New animation file of type {animationType} created.", Color.GhostWhite);
|
||||
return instance as T;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public bool Serialize() => base.Serialize();
|
||||
|
||||
@@ -474,6 +474,12 @@ namespace Barotrauma
|
||||
[Serialize(true, true, description: "The character will flee for a brief moment when being shot at if not performing an attack."), Editable]
|
||||
public bool AvoidGunfire { get; private set; }
|
||||
|
||||
[Serialize(3f, true, description: "How long the creature avoids gunfire. Also used when the creature is unlatched."), Editable(minValue: 0f, maxValue: 100f)]
|
||||
public float AvoidTime { get; private set; }
|
||||
|
||||
[Serialize(20f, true, description: "How long the creature flees before returning to normal state. When the creature sees the target or is being chased, it will always flee, if it's in the flee state."), Editable(minValue: 0f, maxValue: 100f)]
|
||||
public float MinFleeTime { get; private set; }
|
||||
|
||||
[Serialize(false, true, description: "Does the character try to break inside the sub?"), Editable()]
|
||||
public bool AggressiveBoarding { get; private set; }
|
||||
|
||||
@@ -580,6 +586,9 @@ namespace Barotrauma
|
||||
[Serialize(0f, true, description: "Generic timer that can be used for different purposes depending on the state. E.g. in Observe state this defines how long the character in general keeps staring the targets (Some random is always applied)."), Editable]
|
||||
public float Timer { get; set; }
|
||||
|
||||
[Serialize(false, true, description: "Should the target be ignored if it's inside a container/inventory. Only affects items."), Editable]
|
||||
public bool IgnoreContained { get; set; }
|
||||
|
||||
public TargetParams(XElement element, CharacterParams character) : base(element, character) { }
|
||||
|
||||
public TargetParams(string tag, AIState state, float priority, CharacterParams character) : base(CreateNewElement(tag, state, priority), character) { }
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Barotrauma
|
||||
[Serialize(LimbType.Torso, true), Editable]
|
||||
public LimbType MainLimb { get; set; }
|
||||
|
||||
private static Dictionary<string, Dictionary<string, RagdollParams>> allRagdolls = new Dictionary<string, Dictionary<string, RagdollParams>>();
|
||||
private readonly static Dictionary<string, Dictionary<string, RagdollParams>> allRagdolls = new Dictionary<string, Dictionary<string, RagdollParams>>();
|
||||
|
||||
public List<ColliderParams> Colliders { get; private set; } = new List<ColliderParams>();
|
||||
public List<LimbParams> Limbs { get; private set; } = new List<LimbParams>();
|
||||
@@ -546,14 +546,17 @@ namespace Barotrauma
|
||||
[Serialize(LimbType.None, true, description: "The limb type affects many things, like the animations. Torso or Head are considered as the main limbs. Every character should have at least one Torso or Head."), Editable()]
|
||||
public LimbType Type { get; set; }
|
||||
|
||||
[Serialize(float.NaN, true, description: "The orientation of the sprite as drawn on the sprite sheet. Overrides the value defined in the Ragdoll settings. Used mainly for animations and widgets."), Editable(-360, 360)]
|
||||
public float SpriteOrientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The orientation of the sprite as drawn on the sprite sheet (in radians).
|
||||
/// </summary>
|
||||
public float GetSpriteOrientation() => MathHelper.ToRadians(float.IsNaN(SpriteOrientation) ? Ragdoll.SpritesheetOrientation : SpriteOrientation);
|
||||
|
||||
[Serialize("", true), Editable]
|
||||
public string Notes { get; set; }
|
||||
|
||||
[Serialize(1f, true), Editable]
|
||||
public float Scale { get; set; }
|
||||
|
||||
[Serialize(true, true, description: "Does the limb flip when the character flips?"), Editable()]
|
||||
public bool Flip { get; set; }
|
||||
|
||||
@@ -566,8 +569,8 @@ namespace Barotrauma
|
||||
[Serialize(false, true, description: "Disable drawing for this limb."), Editable()]
|
||||
public bool Hide { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "Higher values make AI characters prefer attacking this limb."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 10)]
|
||||
public float AttackPriority { get; set; }
|
||||
[Serialize(float.NaN, true, description: "The orientation of the sprite as drawn on the sprite sheet. Overrides the value defined in the Ragdoll settings. Used mainly for animations and widgets."), Editable(-360, 360, ValueStep = 90, DecimalCount = 0)]
|
||||
public float SpriteOrientation { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 500)]
|
||||
public float SteerForce { get; set; }
|
||||
@@ -590,6 +593,9 @@ namespace Barotrauma
|
||||
[Serialize(7f, true, description: "Increasing the damping makes the limb stop rotating more quickly."), Editable]
|
||||
public float AngularDamping { get; set; }
|
||||
|
||||
[Serialize(1f, true, description: "Higher values make AI characters prefer attacking this limb."), Editable(MinValueFloat = 0.1f, MaxValueFloat = 10)]
|
||||
public float AttackPriority { get; set; }
|
||||
|
||||
[Serialize("0, 0", true, description: "The position which is used to lead the IK chain to the IK goal. Only applicable if the limb is hand or foot."), Editable()]
|
||||
public Vector2 PullPos { get; set; }
|
||||
|
||||
@@ -602,18 +608,12 @@ namespace Barotrauma
|
||||
[Serialize("0, 0", true, description: "Relative offset for the mouth position (starting from the center). Only applicable for LimbType.Head. Used for eating."), Editable(DecimalCount = 2, MinValueFloat = -10f, MaxValueFloat = 10f)]
|
||||
public Vector2 MouthPos { get; set; }
|
||||
|
||||
[Serialize("", true), Editable]
|
||||
public string Notes { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable]
|
||||
public float ConstantTorque { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable]
|
||||
public float ConstantAngle { get; set; }
|
||||
|
||||
[Serialize(1f, true), Editable]
|
||||
public float Scale { get; set; }
|
||||
|
||||
[Serialize(1f, true), Editable(DecimalCount = 2, MinValueFloat = 0, MaxValueFloat = 10)]
|
||||
public float AttackForceMultiplier { get; set; }
|
||||
|
||||
@@ -624,6 +624,42 @@ namespace Barotrauma
|
||||
[Serialize(10f, true, "How long it takes for the severed limb to fade out"), Editable(MinValueFloat = 0, MaxValueFloat = 100, ValueStep = 1)]
|
||||
public float SeveredFadeOutTime { get; set; } = 10.0f;
|
||||
|
||||
[Serialize(false, true, description: "Only applied when the limb is of type Tail. If none of the tails have been defined to use the angle and an angle is defined in the animation parameters, the first tail limb is used."), Editable]
|
||||
public bool ApplyTailAngle { get; set; }
|
||||
|
||||
[Serialize(1f, true), Editable(ValueStep = 0.1f, DecimalCount = 2)]
|
||||
public float SineFrequencyMultiplier { get; set; }
|
||||
|
||||
[Serialize(1f, true), Editable(ValueStep = 0.1f, DecimalCount = 2)]
|
||||
public float SineAmplitudeMultiplier { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(0, 100, ValueStep = 1, DecimalCount = 1)]
|
||||
public float BlinkFrequency { get; set; }
|
||||
|
||||
[Serialize(0.2f, true), Editable(0.01f, 10, ValueStep = 1, DecimalCount = 2)]
|
||||
public float BlinkDurationIn { get; set; }
|
||||
|
||||
[Serialize(0.5f, true), Editable(0.01f, 10, ValueStep = 1, DecimalCount = 2)]
|
||||
public float BlinkDurationOut { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(0, 10, ValueStep = 1, DecimalCount = 2)]
|
||||
public float BlinkHoldTime { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(-360, 360, ValueStep = 1, DecimalCount = 0)]
|
||||
public float BlinkRotationIn { get; set; }
|
||||
|
||||
[Serialize(45f, true), Editable(-360, 360, ValueStep = 1, DecimalCount = 0)]
|
||||
public float BlinkRotationOut { get; set; }
|
||||
|
||||
[Serialize(50f, true), Editable]
|
||||
public float BlinkForce { get; set; }
|
||||
|
||||
[Serialize(TransitionMode.Linear, true), Editable]
|
||||
public TransitionMode BlinkTransitionIn { get; private set; }
|
||||
|
||||
[Serialize(TransitionMode.Linear, true), Editable]
|
||||
public TransitionMode BlinkTransitionOut { get; private set; }
|
||||
|
||||
// Non-editable ->
|
||||
// TODO: make read-only
|
||||
[Serialize(0, true)]
|
||||
|
||||
@@ -592,13 +592,13 @@ namespace Barotrauma
|
||||
(character.CurrentHull.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(character.CurrentHull.Submarine)))
|
||||
{
|
||||
//crawler inside the sub adds 0.1f to enemy danger, mantis 0.25f
|
||||
enemyDanger += enemyAI.CombatStrength / 1000.0f;
|
||||
enemyDanger += enemyAI.CombatStrength / 100.0f;
|
||||
}
|
||||
else if (enemyAI.SelectedAiTarget?.Entity?.Submarine != null)
|
||||
{
|
||||
//enemy outside and targeting the sub or something in it
|
||||
//moloch adds 0.24 to enemy danger, a crawler 0.02
|
||||
enemyDanger += enemyAI.CombatStrength / 2000.0f;
|
||||
enemyDanger += enemyAI.CombatStrength / 1000.0f;
|
||||
}
|
||||
}
|
||||
enemyDanger = MathHelper.Clamp(enemyDanger, 0.0f, 1.0f);
|
||||
@@ -654,12 +654,12 @@ namespace Barotrauma
|
||||
if (targetIntensity > currentIntensity)
|
||||
{
|
||||
//25 seconds for intensity to go from 0.0 to 1.0
|
||||
currentIntensity = MathHelper.Min(currentIntensity + 0.04f * IntensityUpdateInterval, targetIntensity);
|
||||
currentIntensity = Math.Min(currentIntensity + 0.04f * IntensityUpdateInterval, targetIntensity);
|
||||
}
|
||||
else
|
||||
{
|
||||
//400 seconds for intensity to go from 1.0 to 0.0
|
||||
currentIntensity = MathHelper.Max(0.0025f * IntensityUpdateInterval, targetIntensity);
|
||||
currentIntensity = Math.Max(currentIntensity - 0.0025f * IntensityUpdateInterval, targetIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace Barotrauma
|
||||
|
||||
public CampaignMetadata CampaignMetadata;
|
||||
|
||||
protected XElement petsElement;
|
||||
|
||||
public enum TransitionType
|
||||
{
|
||||
None,
|
||||
|
||||
@@ -282,6 +282,11 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError("Couldn't start game session, submarine file corrupted.");
|
||||
return;
|
||||
}
|
||||
if (SubmarineInfo.SubmarineElement.Elements().Count() == 0)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't start game session, saved submarine is empty. The submarine file may be corrupted.");
|
||||
return;
|
||||
}
|
||||
|
||||
LevelData = levelData;
|
||||
|
||||
|
||||
@@ -6,18 +6,20 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class Throwable : Holdable
|
||||
{
|
||||
private float throwForce, throwPos;
|
||||
private float throwPos;
|
||||
private bool throwing, throwDone;
|
||||
|
||||
private bool midAir;
|
||||
|
||||
[Serialize(1.0f, false, description: "The impulse applied to the physics body of the item when thrown. Higher values make the item be thrown faster.")]
|
||||
public float ThrowForce
|
||||
public Character CurrentThrower
|
||||
{
|
||||
get { return throwForce; }
|
||||
set { throwForce = value; }
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(1.0f, false, description: "The impulse applied to the physics body of the item when thrown. Higher values make the item be thrown faster.")]
|
||||
public float ThrowForce { get; set; }
|
||||
|
||||
public Throwable(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -60,6 +62,21 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (item.body.LinearVelocity.LengthSquared() < 0.01f)
|
||||
{
|
||||
CurrentThrower = null;
|
||||
if (statusEffectLists.ContainsKey(ActionType.OnImpact))
|
||||
{
|
||||
foreach (var statusEffect in statusEffectLists[ActionType.OnImpact])
|
||||
{
|
||||
statusEffect.SetUser(null);
|
||||
}
|
||||
}
|
||||
if (statusEffectLists.ContainsKey(ActionType.OnBroken))
|
||||
{
|
||||
foreach (var statusEffect in statusEffectLists[ActionType.OnBroken])
|
||||
{
|
||||
statusEffect.SetUser(null);
|
||||
}
|
||||
}
|
||||
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform;
|
||||
midAir = false;
|
||||
}
|
||||
@@ -117,9 +134,24 @@ namespace Barotrauma.Items.Components
|
||||
#if SERVER
|
||||
GameServer.Log(GameServer.CharacterLogName(picker) + " threw " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
#endif
|
||||
Character thrower = picker;
|
||||
item.Drop(thrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
|
||||
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
CurrentThrower = picker;
|
||||
if (statusEffectLists.ContainsKey(ActionType.OnImpact))
|
||||
{
|
||||
foreach (var statusEffect in statusEffectLists[ActionType.OnImpact])
|
||||
{
|
||||
statusEffect.SetUser(CurrentThrower);
|
||||
}
|
||||
}
|
||||
if (statusEffectLists.ContainsKey(ActionType.OnBroken))
|
||||
{
|
||||
foreach (var statusEffect in statusEffectLists[ActionType.OnBroken])
|
||||
{
|
||||
statusEffect.SetUser(CurrentThrower);
|
||||
}
|
||||
}
|
||||
|
||||
item.Drop(CurrentThrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
|
||||
item.body.ApplyLinearImpulse(throwVector * ThrowForce * item.body.Mass * 3.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
|
||||
//disable platform collisions until the item comes back to rest again
|
||||
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
|
||||
@@ -135,12 +167,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnSecondaryUse, this, thrower.ID });
|
||||
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnSecondaryUse, this, CurrentThrower.ID });
|
||||
}
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
//Stun grenades, flares, etc. all have their throw-related things handled in "onSecondaryUse"
|
||||
ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, thrower, user: thrower);
|
||||
ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, CurrentThrower, user: CurrentThrower);
|
||||
}
|
||||
throwing = false;
|
||||
}
|
||||
|
||||
@@ -349,6 +349,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
if (item.Submarine == null || !item.Submarine.Loading)
|
||||
{
|
||||
SpawnAlwaysContainedItems();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
if (itemIds != null)
|
||||
@@ -361,7 +369,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
itemIds = null;
|
||||
}
|
||||
SpawnAlwaysContainedItems();
|
||||
}
|
||||
|
||||
private void SpawnAlwaysContainedItems()
|
||||
{
|
||||
if (SpawnWithId.Length > 0)
|
||||
{
|
||||
ItemPrefab prefab = ItemPrefab.Prefabs.Find(m => m.Identifier == SpawnWithId);
|
||||
@@ -375,6 +387,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void ShallowRemoveComponentSpecific()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1658,10 +1658,14 @@ namespace Barotrauma
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
if (!Prefab.CanFlipX) { return; }
|
||||
|
||||
//call the base method even if the item can't flip, to handle repositioning when flipping the whole sub
|
||||
base.FlipX(relativeToSub);
|
||||
|
||||
if (!Prefab.CanFlipX)
|
||||
{
|
||||
flippedX = false;
|
||||
return;
|
||||
}
|
||||
#if CLIENT
|
||||
if (Prefab.CanSpriteFlipX)
|
||||
{
|
||||
@@ -1677,10 +1681,15 @@ namespace Barotrauma
|
||||
|
||||
public override void FlipY(bool relativeToSub)
|
||||
{
|
||||
if (!Prefab.CanFlipY) { return; }
|
||||
|
||||
//call the base method even if the item can't flip, to handle repositioning when flipping the whole sub
|
||||
base.FlipY(relativeToSub);
|
||||
|
||||
if (!Prefab.CanFlipY)
|
||||
{
|
||||
flippedY = false;
|
||||
return;
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (Prefab.CanSpriteFlipY)
|
||||
{
|
||||
|
||||
@@ -1340,6 +1340,9 @@ namespace Barotrauma
|
||||
decalsCleaned = true;
|
||||
#if SERVER
|
||||
decalUpdatePending = true;
|
||||
#elif CLIENT
|
||||
pendingDecalUpdates.Add(decal);
|
||||
networkUpdatePending = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -833,7 +833,7 @@ namespace Barotrauma
|
||||
if (location.Discovered)
|
||||
{
|
||||
#if CLIENT
|
||||
RemoveFogOfWar(StartLocation);
|
||||
RemoveFogOfWar(location);
|
||||
#endif
|
||||
if (furthestDiscoveredLocation == null || location.MapPosition.X > furthestDiscoveredLocation.MapPosition.X)
|
||||
{
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Barotrauma
|
||||
//observable collection because some entities may need to be notified when the collection is modified
|
||||
public readonly ObservableCollection<MapEntity> linkedTo = new ObservableCollection<MapEntity>();
|
||||
|
||||
private bool flippedX, flippedY;
|
||||
protected bool flippedX, flippedY;
|
||||
public bool FlippedX { get { return flippedX; } }
|
||||
public bool FlippedY { get { return flippedY; } }
|
||||
|
||||
@@ -534,7 +534,7 @@ namespace Barotrauma
|
||||
public virtual void FlipX(bool relativeToSub)
|
||||
{
|
||||
flippedX = !flippedX;
|
||||
if (!relativeToSub || Submarine == null) return;
|
||||
if (!relativeToSub || Submarine == null) { return; }
|
||||
|
||||
Vector2 relative = WorldPosition - Submarine.WorldPosition;
|
||||
relative.Y = 0.0f;
|
||||
@@ -548,7 +548,7 @@ namespace Barotrauma
|
||||
public virtual void FlipY(bool relativeToSub)
|
||||
{
|
||||
flippedY = !flippedY;
|
||||
if (!relativeToSub || Submarine == null) return;
|
||||
if (!relativeToSub || Submarine == null) { return; }
|
||||
|
||||
Vector2 relative = WorldPosition - Submarine.WorldPosition;
|
||||
relative.X = 0.0f;
|
||||
|
||||
@@ -415,7 +415,7 @@ namespace Barotrauma
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in SerializableProperty.TrySetValue", e);
|
||||
DebugConsole.ThrowError("Error in SerializableProperty.GetValue", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -733,9 +733,10 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targets.FirstOrDefault(t => t is MapEntity) is MapEntity targetEntity && !targetEntity.Removed)
|
||||
var targetLimb = targets.FirstOrDefault(t => t is Limb) as Limb;
|
||||
if (targetLimb != null && !targetLimb.Removed)
|
||||
{
|
||||
position = targetEntity.WorldPosition;
|
||||
position = targetLimb.WorldPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,10 @@
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.10.601.0 (Unstable)
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
- Added 2 pets: Peanut and Psilotoad. Currently only obtainable via console commands ("spawnitem peanutegg", "spawnitem psilotoadegg", "spawn peanut" or "spawn psilotoad").
|
||||
- Improvements to the Watcher.
|
||||
- Fixed 2 equipped storage containers from one hand to another causing one of them to get stuck mid-air.
|
||||
- Fixed a crash caused by humanoid enemies (e.g. husks).
|
||||
- Moved toolbelt slot to the right side of the generic slots, added inventory icon for the slot.
|
||||
- Fixed EventManager crashing if there are no event sets configured for the current location type (only affected mods that add new location types without adding any events for them).
|
||||
- Use player name instead of server name for the server owner when hosting a server.
|
||||
- Fixed diving suit's low oxygen warning beep not following the player wearing the diving suit.
|
||||
- Made large monsters immune to paralyzant (mudraptor is the largest affected monster).
|
||||
- Fixed bots not reacting to player reports in multiplayer.
|
||||
- Added more copper to chalcopyrite and bornite deconstruct recipe.
|
||||
- More calcium for aragonite, adjusted prices.
|
||||
- Fixed fires not damaging characters.
|
||||
- Set terminal's maximum message length to match maximum chat message length (otherwise chat-linked terminals work differently in multiplayer).
|
||||
- Fixed inability to unlink hulls in the sub editor.
|
||||
- Fixed bots "cleaning up" components attached to walls.
|
||||
- Fixed yet another cause for "missing entity" errors. Occasionally happened in monster missions when a monster happened to get assigned the same ID as an item in a player's inventory.
|
||||
- Fixed items held in the left hand being drawn in front of the characters.
|
||||
- Allow closing the splash screens with esc.
|
||||
- Fixed "inventory sizes don't match" error when a human becomes a husk.
|
||||
- Fixed ability to drag and drop items into outpost reactors.
|
||||
- Fixed torso getting hidden when wearing a toolbelt.
|
||||
- Fixed coilgun ammo only using 90% of the fabrication materials.
|
||||
- Fixed paints reverting to the dirt color client-side after spraying.
|
||||
- Added missing dialog for the new "Cleanup Items" order.
|
||||
- Rebalanced upgrade parameters, allowing for more noticable benefits.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.10.600.0 (Unstable)
|
||||
v0.10.6.0 (Unstable)
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Changes and additions:
|
||||
- Reworked Watcher.
|
||||
- Added pets (can be obtained by buying eggs from outposts). The pets produce items that can be used for crafting if they're kept happy and well-fed.
|
||||
- Added a new monster behavior: observe.
|
||||
- Added toolbelt (a wearable container with a capacity of 12 and it's own dedicated slot) as a replacement for the toolbox.
|
||||
- Improvements to the effects caused by psychosis: the affliction icon is not visible to the psychotic character, the fake fires and floods are a bit more convincing, the affliction plays random sounds and can cause other characters to become invisible.
|
||||
@@ -66,12 +36,20 @@ Changes and additions:
|
||||
- A minor change to the status effect condition targeting logic: If "This" and "NearbyCharacters" are both defined as the targets of a status effect, the conditions only apply to "this" entity, even though the effects are applied on all the targets.
|
||||
- Implement spread, speed, and rotation for the spawn item status effects.
|
||||
- Minor damage (less than 1 hp) doesn't spawn particles anymore.
|
||||
- Rebalanced upgrade parameters, allowing for more noticable benefits.
|
||||
- Allow closing the splash screens with esc.
|
||||
- Added more copper to chalcopyrite and bornite deconstruct recipe.
|
||||
- More calcium for aragonite, adjusted prices.
|
||||
- Made large monsters immune to paralyzant (mudraptor is the largest affected monster).
|
||||
- Use player name instead of server name for the server owner when hosting a server.
|
||||
- Don't draw turret range indicators in the sub editor when the turret isn't selected.
|
||||
|
||||
Character Editor:
|
||||
- Fixed a number of issues with the joint limit widgets. Also allowed to set a joint to rotate clockwise, which inverses the widget direction. Useful for heads or other limbs that extrude right from the main body.
|
||||
- Inversed the default joint ends, because it's more usual case to edit the second limb of the joint than the first.
|
||||
- The colliders of the hidden limbs are now hidden in the game view.
|
||||
- Changed the hotkey for toggling the parameter editor from "Tab" to "F1" and fix the inability to toggle the editor when a text field is selected.
|
||||
- Fixed load and save interfaces being broken on lower resolutions.
|
||||
|
||||
Sounds:
|
||||
- Added 2 new background music tracks
|
||||
@@ -110,6 +88,8 @@ AI improvements and fixes:
|
||||
|
||||
Misc fixes:
|
||||
- Fixed clients always getting the generic "could not connect" error message when connecting to a server fails, even if there's a specific reason to the connection failing (e.g. disallowed symbols in the player's name, mismatching content packages or game version).
|
||||
- Fixed yet another cause for "missing entity" errors. Occasionally happened in monster missions when a monster happened to get assigned the same ID as an item in a player's inventory.
|
||||
- Fixed clients spawning a respawn shuttle in non-campaign missions even if the server has disabled respawning, leading to "missing entity" errors.
|
||||
- Fixed planters dropping removed seeds after a save is reloaded.
|
||||
- Fixed plants not updating the health after being fully grown in multiplayer.
|
||||
- Fixed decal syncing working unreliably in multiplayer.
|
||||
@@ -145,6 +125,26 @@ Misc fixes:
|
||||
- OnDamaged status effects now launch only when there's any damage. Not when the damage is zero.
|
||||
- Fixed health bar pulsating even when no damage is done by an attack/status effect.
|
||||
- Fixed affliction probability not having any effect when used in status effects.
|
||||
- Fixed EventManager crashing if there are no event sets configured for the current location type (only affected mods that add new location types without adding any events for them).
|
||||
- Fixed items held in the left hand being drawn in front of the characters.
|
||||
- Fixed ability to drag and drop items into outpost reactors.
|
||||
- Set terminal's maximum message length to match maximum chat message length (otherwise chat-linked terminals work differently in multiplayer).
|
||||
- Fixed diving suit's low oxygen warning beep not following the player wearing the suit.
|
||||
- Fixed "propaganda" and "clown outbreak" outpost events never triggering.
|
||||
- Fixed "clown brutality" event getting stuck after the NPCs have been spawned.
|
||||
- Fixed "impromptu engineering" event giving only one coilgun ammo box despite the text saying 2.
|
||||
- Fixed "black market" event always giving the player the alien pistol.
|
||||
- Fixed incendium bars exploding when pressing the Use key while holding one.
|
||||
- Fixed outpost security not reacting to players throwing items that explode on impact (e.g. flash powder or nitroglycerin).
|
||||
- Fixed outpost security reacting to raptor bane extract injections with lethal force.
|
||||
- Fixed mantis' animation.
|
||||
- Fixed multiple wire nodes occasionally getting placed with one click when rewiring.
|
||||
- Fixed fire and water flow sounds staying active when returning from a multiplayer session to the main menu.
|
||||
- Fixed previously discovered map tiles becoming undiscovered when saving and loading a campaign.
|
||||
- Fixed 1st shot from SMG magazines spawned with console commands doing nothing.
|
||||
- Fixed calyxanide not damaging huskified humans or crawlers.
|
||||
- Fixed explosives exploding when combining them results in one being removed.
|
||||
- Fixed items that don't flip horizontally being positioned incorrectly in mirrored subs.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.10.5.1
|
||||
|
||||
Reference in New Issue
Block a user