Unstable v0.10.601.0

This commit is contained in:
Juan Pablo Arce
2020-10-07 10:39:32 -03:00
parent ebe1ce1427
commit 768f516e7c
65 changed files with 743 additions and 139 deletions

View File

@@ -321,6 +321,17 @@ namespace Barotrauma
{
character.SelectedConstruction.DrawHUD(spriteBatch, cam, Character.Controlled);
}
if (Character.Controlled.Inventory != null)
{
foreach (Item item in Character.Controlled.Inventory.Items)
{
if (item == null) { continue; }
if (Character.Controlled.HasEquippedItem(item))
{
item.DrawHUD(spriteBatch, cam, Character.Controlled);
}
}
}
if (IsCampaignInterfaceOpen) { return; }
@@ -431,6 +442,14 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUI.SubHeadingFont);
textPos.X += 10.0f * GUI.Scale;
textPos.Y += GUI.SubHeadingFont.MeasureString(focusName).Y;
if (!character.FocusedCharacter.IsIncapacitated && character.FocusedCharacter.IsPet)
{
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("PlayHint", GameMain.Config.KeyBindText(InputType.Use)),
GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
textPos.Y += largeTextSize.Y;
}
if (character.FocusedCharacter.CanBeDragged)
{
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBindText(InputType.Grab)),

View File

@@ -278,17 +278,7 @@ namespace Barotrauma
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams));
break;
case "conditionalsprite":
ISerializableEntity targetEntity;
string target = subElement.GetAttributeString("target", null);
if (string.Equals(target, "character", StringComparison.OrdinalIgnoreCase))
{
targetEntity = character;
}
else
{
targetEntity = this;
}
var conditionalSprite = new ConditionalSprite(subElement, targetEntity, file: GetSpritePath(subElement, null));
var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(), file: GetSpritePath(subElement, null));
ConditionalSprites.Add(conditionalSprite);
if (conditionalSprite.DeformableSprite != null)
{
@@ -300,7 +290,7 @@ namespace Barotrauma
CreateDeformations(subElement);
break;
case "lightsource":
LightSource = new LightSource(subElement)
LightSource = new LightSource(subElement, GetConditionalTarget())
{
ParentBody = body,
SpriteScale = Vector2.One * Scale * TextureScale
@@ -310,6 +300,21 @@ namespace Barotrauma
break;
}
ISerializableEntity GetConditionalTarget()
{
ISerializableEntity targetEntity;
string target = subElement.GetAttributeString("target", null);
if (string.Equals(target, "character", StringComparison.OrdinalIgnoreCase))
{
targetEntity = character;
}
else
{
targetEntity = this;
}
return targetEntity;
}
void CreateDeformations(XElement e)
{
foreach (XElement animationElement in e.GetChildElements("spritedeformation"))
@@ -341,6 +346,7 @@ namespace Barotrauma
}
}
}
LightSource?.CheckConditionals();
}
public void RecreateSprites()
@@ -561,6 +567,11 @@ namespace Barotrauma
}
}
foreach (var conditionalSprite in ConditionalSprites)
{
conditionalSprite.CheckConditionals();
}
if (LightSource != null)
{
LightSource.ParentSub = body.Submarine;
@@ -573,6 +584,7 @@ namespace Barotrauma
{
LightSource.DeformableLightSprite.Sprite.Depth = ActiveSprite.Depth;
}
LightSource.CheckConditionals();
}
UpdateSpriteStates(deltaTime);

View File

@@ -271,7 +271,7 @@ namespace Barotrauma
private static List<GUIButton> CreateConversation(GUIListBox parentBox, string text, Character speaker, IEnumerable<string> options, bool drawChathead = true)
{
var content = new GUILayoutGroup(new RectTransform(Vector2.One, parentBox.Content.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
var content = new GUILayoutGroup(new RectTransform(Vector2.One, parentBox.Content.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true)
{
Stretch = true,
CanBeFocused = true,
@@ -289,7 +289,7 @@ namespace Barotrauma
});
}
var textContent = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), content.RectTransform), childAnchor: Anchor.TopCenter)
var textContent = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0f), content.RectTransform), childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = GUI.IntScale(5)
};
@@ -316,7 +316,7 @@ namespace Barotrauma
content.Recalculate();
textContent.Recalculate();
textBlock.CalculateHeightFromText();
textBlock.RectTransform.MinSize = new Point(0, (int)(textBlock.Rect.Height * 1.2f));
textBlock.RectTransform.MinSize = new Point(0, textBlock.Rect.Height);
foreach (GUIButton btn in buttons)
{
btn.TextBlock.SetTextPos();
@@ -324,10 +324,12 @@ namespace Barotrauma
btn.RectTransform.MinSize = new Point(0, (int)(btn.TextBlock.Rect.Height * 1.2f));
}
textContent.RectTransform.MinSize = new Point(0, textContent.Children.Sum(c => c.Rect.Height + textContent.AbsoluteSpacing) + GUI.IntScale(16));
textContent.RectTransform.MinSize = new Point(0, textContent.Children.Sum(c => c.Rect.Height) + GUI.IntScale(16));
content.RectTransform.MinSize = new Point(0, content.Children.Sum(c => c.Rect.Height));
// Recalculate the text size as it is scaled up and no longer matching the text height due to the textContent's minSize increasing
textBlock.CalculateHeightFromText();
textBlock.TextAlignment = Alignment.TopLeft;
//content.RectTransform.MinSize = new Point(0, textContent.Rect.Height);
return buttons;

View File

@@ -650,6 +650,8 @@ namespace Barotrauma
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{
radio.Channel = channel;
GameMain.Client?.CreateEntityEvent(radio.Item, new object[] { NetEntityEvent.Type.ChangeProperty, radio.SerializableProperties["channel"] });
if (setText)
{
string text = radio.Channel.ToString().PadLeft(4, '0');

View File

@@ -348,7 +348,7 @@ namespace Barotrauma
spriteBatch.Draw(currSplashScreen.GetTexture(), new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End();
if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 500) && GameMain.WindowActive && (PlayerInput.KeyHit(Keys.Space) || PlayerInput.KeyHit(Keys.Enter) || PlayerInput.PrimaryMouseButtonDown()))
if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 500) && GameMain.WindowActive && (PlayerInput.KeyHit(Keys.Escape) || PlayerInput.KeyHit(Keys.Space) || PlayerInput.KeyHit(Keys.Enter) || PlayerInput.PrimaryMouseButtonDown()))
{
currSplashScreen.Dispose(); currSplashScreen = null;
}

View File

@@ -261,12 +261,7 @@ namespace Barotrauma
var sub = Character.Controlled.Submarine;
if (sub == null || sub.TeamID != Character.Controlled.TeamID || sub.Info.IsWreck) { return false; }
SetCharacterOrder(null, order, null, Character.Controlled);
var visibleHulls = new List<Hull>(Character.Controlled.GetVisibleHulls());
foreach (var hull in visibleHulls)
{
HumanAIController.PropagateHullSafety(Character.Controlled, hull);
HumanAIController.RefreshTargets(Character.Controlled, order, hull);
}
if (IsSinglePlayer) { HumanAIController.ReportProblem(Character.Controlled, order); }
return true;
},
UserData = order,
@@ -2221,8 +2216,8 @@ namespace Barotrauma
var operateWeaponsPrefab = Order.GetPrefab(orderIdentifier);
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Components.Any(c => c is Controller))
{
var turret = itemContext.GetConnectedComponents<Turret>().FirstOrDefault(c => operateWeaponsPrefab.TargetItems.Contains(c.Item.Prefab.Identifier)) ??
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => operateWeaponsPrefab.TargetItems.Contains(c.Item.Prefab.Identifier));
var turret = itemContext.GetConnectedComponents<Turret>().FirstOrDefault(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)) ??
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems));
if (turret != null) { contextualOrders.Add(new Order(operateWeaponsPrefab, turret.Item, turret, Character.Controlled)); }
}
@@ -2316,8 +2311,8 @@ namespace Barotrauma
if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold)) { return true; }
var operateWeaponsPrefab = Order.GetPrefab("operateweapons");
return item.Components.Any(c => c is Controller) &&
(item.GetConnectedComponents<Turret>().Any(c => operateWeaponsPrefab.TargetItems.Contains(c.Item.Prefab.Identifier)) ||
item.GetConnectedComponents<Turret>(recursive: true).Any(c => operateWeaponsPrefab.TargetItems.Contains(c.Item.Prefab.Identifier)));
(item.GetConnectedComponents<Turret>().Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)) ||
item.GetConnectedComponents<Turret>(recursive: true).Any(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)));
}
private GUIButton CreateOrderNode(Point size, RectTransform parent, Point offset, Order order, int hotkey, bool disableNode = false, bool checkIfOrderCanBeHeard = true)

View File

@@ -311,6 +311,8 @@ namespace Barotrauma.Tutorials
}
yield return null;
} while (engineer_reactor.AvailableFuel == 0);
RemoveCompletedObjective(segments[1]);
TriggerTutorialSegment(2);
CoroutineManager.StartCoroutine(ReactorOperatedProperly());
do
{
@@ -352,7 +354,7 @@ namespace Barotrauma.Tutorials
} while (wait > 0.0f);
engineer.SelectedConstruction = null;
engineer_reactor.CanBeSelected = false;
RemoveCompletedObjective(segments[1]);
RemoveCompletedObjective(segments[2]);
SetHighlight(engineer_reactor.Item, false);
SetHighlight(engineer_brokenJunctionBox, true);
SetDoorAccess(engineer_secondDoor, engineer_secondDoorLight, true);
@@ -361,7 +363,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!engineer_secondDoor.IsOpen);
yield return new WaitForSeconds(1f, false);
Repairable repairableJunctionBoxComponent = engineer_brokenJunctionBox.GetComponent<Repairable>();
TriggerTutorialSegment(2, GameMain.Config.KeyBindText(InputType.Select)); // Repair the junction box
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Select)); // Repair the junction box
do
{
if (!engineer.HasEquippedItem("screwdriver"))
@@ -389,7 +391,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!engineer_thirdDoor.IsOpen);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.FaultyWiring"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Use), GameMain.Config.KeyBindText(InputType.Deselect)); // Connect the junction boxes
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Use), GameMain.Config.KeyBindText(InputType.Deselect)); // Connect the junction boxes
do { CheckGhostWires(); HandleJunctionBoxWiringHighlights(); yield return null; } while (engineer_workingPump.Voltage < engineer_workingPump.MinVoltage); // Wait until connected all the way to the pump
CheckGhostWires();
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
@@ -406,7 +408,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!tutorial_enteredSubmarineSensor.MotionDetected);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Submarine"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(4); // Repair junction box
TriggerTutorialSegment(5); // Repair junction box
while (ContentRunning) yield return null;
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_1, engineer_repairIcon, engineer_repairIconColor);
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_2, engineer_repairIcon, engineer_repairIconColor);
@@ -425,7 +427,7 @@ namespace Barotrauma.Tutorials
RemoveCompletedObjective(segments[4]);
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(5); // Powerup reactor
TriggerTutorialSegment(6); // Powerup reactor
SetHighlight(engineer_submarineReactor.Item, true);
engineer.AddActiveObjectiveEntity(engineer_submarineReactor.Item, engineer_reactorIcon, engineer_reactorIconColor);
do { yield return null; } while (!IsReactorPoweredUp(engineer_submarineReactor)); // Wait until ~matches load

View File

@@ -141,7 +141,7 @@ namespace Barotrauma.Tutorials
mechanic_brokenWall_1.SpriteColor = Color.White;
for (int i = 0; i < mechanic_brokenWall_1.SectionCount; i++)
{
mechanic_brokenWall_1.AddDamage(i, 165);
mechanic_brokenWall_1.AddDamage(i, 85);
}
mechanic_brokenhull_1 = mechanic_brokenWall_1.Sections[0].gap.FlowTargetHull;
@@ -199,7 +199,7 @@ namespace Barotrauma.Tutorials
mechanic_brokenWall_2.SpriteColor = Color.White;
for (int i = 0; i < mechanic_brokenWall_2.SectionCount; i++)
{
mechanic_brokenWall_2.AddDamage(i, 165);
mechanic_brokenWall_2.AddDamage(i, 85);
}
mechanic_brokenhull_2 = mechanic_brokenWall_2.Sections[0].gap.FlowTargetHull;
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false);

View File

@@ -362,10 +362,10 @@ namespace Barotrauma.Tutorials
SetHighlight(officer_rangedWeaponHolder.Item, false);
do
{
HighlightInventorySlot(officer.Inventory, "harpoongun", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlot(officer.Inventory, "shotgun", highlightColor, 0.5f, 0.5f, 0f);
yield return null;
} while (!officer.HasEquippedItem("harpoongun")); // Wait until equipped
ItemContainer harpoonGunChamber = officer.Inventory.FindItemByIdentifier("harpoongun").GetComponent<ItemContainer>();
} while (!officer.HasEquippedItem("shotgun")); // Wait until equipped
ItemContainer shotGunChamber = officer.Inventory.FindItemByIdentifier("shotgun").GetComponent<ItemContainer>();
SetHighlight(officer_rangedWeaponCabinet.Item, true);
do
{
@@ -376,7 +376,7 @@ namespace Barotrauma.Tutorials
for (int i = 0; i < officer_rangedWeaponCabinet.Inventory.Items.Length; i++)
{
if (officer_rangedWeaponCabinet.Inventory.Items[i] == null) continue;
if (officer_rangedWeaponCabinet.Inventory.Items[i].Prefab.Identifier == "spear")
if (officer_rangedWeaponCabinet.Inventory.Items[i].Prefab.Identifier == "shotgunshell")
{
HighlightInventorySlot(officer_rangedWeaponCabinet.Inventory, i, highlightColor, 0.5f, 0.5f, 0f);
}
@@ -387,18 +387,18 @@ namespace Barotrauma.Tutorials
for (int i = 0; i < officer.Inventory.Items.Length; i++)
{
if (officer.Inventory.Items[i] == null) continue;
if (officer.Inventory.Items[i].Prefab.Identifier == "spear")
if (officer.Inventory.Items[i].Prefab.Identifier == "shotgunshell")
{
HighlightInventorySlot(officer.Inventory, i, highlightColor, 0.5f, 0.5f, 0f);
}
}
if (officer.Inventory.FindItemByIdentifier("spear") != null || (IsSelectedItem(officer_rangedWeaponCabinet.Item) && officer_rangedWeaponCabinet.Inventory.FindItemByIdentifier("spear") != null))
if (officer.Inventory.FindItemByIdentifier("shotgunshell") != null || (IsSelectedItem(officer_rangedWeaponCabinet.Item) && officer_rangedWeaponCabinet.Inventory.FindItemByIdentifier("shotgunshell") != null))
{
HighlightInventorySlot(officer.Inventory, "harpoongun", highlightColor, 0.5f, 0.5f, 0f);
HighlightInventorySlot(officer.Inventory, "shotgun", highlightColor, 0.5f, 0.5f, 0f);
}
yield return null;
} while (!harpoonGunChamber.Inventory.IsFull()); // Wait until all six harpoons loaded
} while (!shotGunChamber.Inventory.IsFull()); // Wait until all six harpoons loaded
RemoveCompletedObjective(segments[5]);
SetHighlight(officer_rangedWeaponCabinet.Item, false);
SetDoorAccess(officer_fourthDoor, officer_fourthDoorLight, true);

View File

@@ -51,13 +51,13 @@ namespace Barotrauma
limbSlotIcons.Add(InvSlotType.LeftHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(634, 0, 128, 128)));
limbSlotIcons.Add(InvSlotType.RightHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(762, 0, 128, 128)));
limbSlotIcons.Add(InvSlotType.OuterClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.Bag, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.Bag, new Sprite("Content/UI/CommandUIAtlas.png", new Rectangle(639, 926, 128,80)));
}
return limbSlotIcons;
}
}
public const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head;
public const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Bag | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head;
private Point screenResolution;
@@ -730,6 +730,7 @@ namespace Barotrauma
{
for (int i = 0; i < indicators.Length; i++)
{
if (indicatorIndexes[i] < 0) { continue; }
Item item = Items[indicatorIndexes[i]];
if (item != null)
{

View File

@@ -1002,6 +1002,7 @@ namespace Barotrauma
{
if (DraggingItemToWorld &&
Character.Controlled.FocusedItem?.OwnInventory != null &&
(Character.Controlled.FocusedItem.GetComponent<ItemContainer>()?.HasRequiredItems(Character.Controlled, addMessage: false) ?? false) &&
Character.Controlled.FocusedItem.OwnInventory.CanBePut(draggingItem) &&
Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled))
{

View File

@@ -62,7 +62,7 @@ namespace Barotrauma
}
}
public override bool DrawBelowWater => (!(Screen.Selected is SubEditorScreen editor) || !editor.WiringMode || !isWire) && base.DrawBelowWater;
public override bool DrawBelowWater => (!(Screen.Selected is SubEditorScreen editor) || !editor.WiringMode || !isWire) && (base.DrawBelowWater || ParentInventory is CharacterInventory);
public override bool DrawOverWater => base.DrawOverWater || (IsSelected || Screen.Selected is SubEditorScreen editor && editor.WiringMode) && isWire;
@@ -332,6 +332,7 @@ namespace Barotrauma
var holdable = GetComponent<Holdable>();
if (holdable != null && holdable.Picker?.AnimController != null)
{
if (!back) { return; }
float depthStep = 0.000001f;
if (holdable.Picker.SelectedItems[0] == this)
{
@@ -855,7 +856,7 @@ namespace Barotrauma
public void UpdateHUD(Camera cam, Character character, float deltaTime)
{
bool editingHUDCreated = false;
if ((HasInGameEditableProperties && character.SelectedConstruction == this) ||
if ((HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped)) ||
Screen.Selected == GameMain.SubEditorScreen)
{
GUIComponent prevEditingHUD = editingHUD;
@@ -956,7 +957,7 @@ namespace Barotrauma
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character)
{
if (HasInGameEditableProperties)
if (HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped))
{
DrawEditing(spriteBatch, cam);
}
@@ -1028,13 +1029,13 @@ namespace Barotrauma
}
else
{
if (HasInGameEditableProperties)
if (HasInGameEditableProperties && Character.Controlled != null && (Character.Controlled.SelectedConstruction == this || EditableWhenEquipped))
{
if (editingHUD != null && editingHUD.UserData == this) { editingHUD.AddToGUIUpdateList(); }
}
}
if (Character.Controlled != null && Character.Controlled?.SelectedConstruction != this) { return; }
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != this) { return; }
bool needsLayoutUpdate = false;
foreach (ItemComponent ic in activeHUDs)

View File

@@ -94,20 +94,21 @@ namespace Barotrauma
{
if (entity == this || !entity.IsHighlighted) { continue; }
if (!entity.IsMouseOn(position)) { continue; }
if (entity.linkedTo != null && entity.linkedTo.Contains(this))
if (entity.linkedTo == null || !entity.Linkable) { continue; }
if (entity.linkedTo.Contains(this) || linkedTo.Contains(entity) || rClick)
{
if (entity == this || !entity.IsHighlighted) continue;
if (!entity.IsMouseOn(position)) continue;
if (entity.Linkable && entity.linkedTo != null && !entity.linkedTo.Contains(this))
if (entity == this || !entity.IsHighlighted) { continue; }
if (!entity.IsMouseOn(position)) { continue; }
if (entity.linkedTo.Contains(this))
{
entity.linkedTo.Add(this);
linkedTo.Add(entity);
entity.linkedTo.Remove(this);
linkedTo.Remove(entity);
}
}
else if (entity.Linkable && entity.linkedTo != null)
else
{
entity.linkedTo.Add(this);
linkedTo.Add(entity);
if (!entity.linkedTo.Contains(this)) { entity.linkedTo.Add(this); }
if (!linkedTo.Contains(this)) { linkedTo.Add(entity); }
}
}
}
@@ -605,11 +606,6 @@ namespace Barotrauma
{
float colorStrength = message.ReadRangedSingle(0.0f, 1.0f, 8);
Color color = new Color(message.ReadUInt32());
float prevColorStrength = BackgroundSections[i].ColorStrength;
BackgroundSections[i].SetColorStrength(colorStrength);
BackgroundSections[i].SetColor(color);
paintAmount = Math.Max(0, paintAmount + (BackgroundSections[i].ColorStrength - prevColorStrength) / BackgroundSections.Count);
var remoteBackgroundSection = remoteBackgroundSections.Find(s => s.Index == i);
if (remoteBackgroundSection != null)
{
@@ -655,8 +651,10 @@ namespace Barotrauma
{
foreach (BackgroundSection remoteBackgroundSection in remoteBackgroundSections)
{
float prevColorStrength = BackgroundSections[remoteBackgroundSection.Index].ColorStrength;
BackgroundSections[remoteBackgroundSection.Index].SetColor(remoteBackgroundSection.Color);
BackgroundSections[remoteBackgroundSection.Index].SetColorStrength(remoteBackgroundSection.ColorStrength);
paintAmount = Math.Max(0, paintAmount + (BackgroundSections[remoteBackgroundSection.Index].ColorStrength - prevColorStrength) / BackgroundSections.Count);
}
remoteBackgroundSections.Clear();

View File

@@ -1,3 +1,4 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -6,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma.Lights
{
class LightSourceParams : ISerializableEntity
@@ -331,16 +333,42 @@ namespace Barotrauma.Lights
public bool Enabled = true;
public LightSource (XElement element)
private ISerializableEntity conditionalTarget;
private readonly PropertyConditional.Comparison comparison;
private readonly List<PropertyConditional> conditionals = new List<PropertyConditional>();
public LightSource (XElement element, ISerializableEntity conditionalTarget = null)
: this(Vector2.Zero, 100.0f, Color.White, null)
{
lightSourceParams = new LightSourceParams(element);
CastShadows = element.GetAttributeBool("castshadows", true);
string comparison = element.GetAttributeString("comparison", null);
if (comparison != null)
{
Enum.TryParse(comparison, ignoreCase: true, out this.comparison);
}
if (lightSourceParams.DeformableLightSpriteElement != null)
{
DeformableLightSprite = new DeformableSprite(lightSourceParams.DeformableLightSpriteElement, invert: true);
}
this.conditionalTarget = conditionalTarget;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "conditional":
foreach (XAttribute attribute in subElement.Attributes())
{
if (PropertyConditional.IsValid(attribute))
{
conditionals.Add(new PropertyConditional(attribute));
}
}
break;
}
}
}
public LightSource(LightSourceParams lightSourceParams)
@@ -363,7 +391,8 @@ namespace Barotrauma.Lights
CastShadows = true;
texture = LightTexture;
diffToSub = new Dictionary<Submarine, Vector2>();
if (addLight) GameMain.LightManager.AddLight(this);
if (addLight) { GameMain.LightManager.AddLight(this); }
}
/// <summary>
@@ -1128,8 +1157,21 @@ namespace Barotrauma.Lights
GUI.DrawLine(spriteBatch, drawPos - Vector2.One * Range, drawPos + Vector2.One * Range, Color);
GUI.DrawLine(spriteBatch, drawPos - new Vector2(1.0f, -1.0f) * Range, drawPos + new Vector2(1.0f, -1.0f) * Range, Color);
}
}
}
public void CheckConditionals()
{
if (conditionals.None()) { return; }
if (conditionalTarget == null) { return; }
if (comparison == PropertyConditional.Comparison.And)
{
Enabled = conditionals.All(c => c.Matches(conditionalTarget));
}
else
{
Enabled = conditionals.Any(c => c.Matches(conditionalTarget));
}
}
public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform)

View File

@@ -75,6 +75,17 @@ namespace Barotrauma
{
Character.Controlled.SelectedConstruction.AddToGUIUpdateList();
}
if (Character.Controlled?.Inventory != null)
{
foreach (Item item in Character.Controlled.Inventory.Items)
{
if (item == null) { continue; }
if (Character.Controlled.HasEquippedItem(item))
{
item.AddToGUIUpdateList();
}
}
}
if (GameMain.GameSession != null) GameMain.GameSession.AddToGUIUpdateList();

View File

@@ -879,7 +879,8 @@ namespace Barotrauma
ChildServerRelay.Start(processInfo);
Thread.Sleep(1000); //wait until the server is ready before connecting
GameMain.Client = new GameClient(name, System.Net.IPAddress.Loopback.ToString(), Steam.SteamManager.GetSteamID(), name, ownerKey, true);
GameMain.Client = new GameClient(string.IsNullOrEmpty(GameMain.Config.PlayerName) ? name : GameMain.Config.PlayerName,
System.Net.IPAddress.Loopback.ToString(), Steam.SteamManager.GetSteamID(), name, ownerKey, true);
}
catch (Exception e)
{

View File

@@ -2409,7 +2409,6 @@ namespace Barotrauma
int min = Math.Min(6, AutoSaveInfo.Root.Elements().Count());
var loadAutoSave = new GUIDropDown(new RectTransform(Vector2.One, deleteButtonHolder.RectTransform, Anchor.BottomCenter), TextManager.Get("LoadAutoSave"), elementCount: min)
{
Enabled = File.Exists(Path.Combine(SubmarineInfo.SavePath, ".AutoSaves", "AutoSave.sub")),
ToolTip = TextManager.Get("LoadAutoSaveTooltip"),
UserData = "loadautosave",
OnSelected = (button, o) =>

View File

@@ -418,7 +418,7 @@ namespace Barotrauma
private static void UpdateWaterAmbience(float ambienceVolume, float deltaTime)
{
if (GameMain.SoundManager.Disabled) { return; }
if (GameMain.SoundManager.Disabled || GameMain.GameScreen?.Cam == null) { return; }
//how fast the sub is moving, scaled to 0.0 -> 1.0
float movementSoundVolume = 0.0f;
@@ -426,6 +426,7 @@ namespace Barotrauma
float insideSubFactor = 0.0f;
foreach (Submarine sub in Submarine.Loaded)
{
if (sub == null || sub.Removed) { continue; }
float movementFactor = (sub.Velocity == Vector2.Zero) ? 0.0f : sub.Velocity.Length() / 10.0f;
movementFactor = MathHelper.Clamp(movementFactor, 0.0f, 1.0f);

View File

@@ -99,6 +99,7 @@ namespace Barotrauma
{
Sprite = new Sprite(element, path, file, lazyLoad: lazyLoad);
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
// TODO: what's the purpose of this?
foreach (XElement subElement in element.Elements())
{
List<PropertyConditional> conditionalList = null;

View File

@@ -105,6 +105,10 @@ namespace Barotrauma
if (soundChannel != null) { soundChannel.Looping = loopSound; }
}
}
else
{
soundChannel.Position = new Vector3(worldPosition, 0.0f);
}
if (soundChannel != null && soundChannel.Looping)
{

View File

@@ -138,6 +138,7 @@ namespace Barotrauma
public static Color GradientLerp(float t, params Color[] gradient)
{
if (!MathUtils.IsValid(t)) { return Color.Purple; }
System.Diagnostics.Debug.Assert(gradient.Length > 0, "Empty color array passed to the GradientLerp method");
if (gradient.Length == 0)
{

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.10.600.0</Version>
<Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.10.600.0</Version>
<Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product>
<Version>0.10.600.0</Version>
<Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.10.600.0</Version>
<Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.10.600.0</Version>
<Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -15,7 +15,7 @@ namespace Barotrauma
msg.Write((byte)monsters.Count);
foreach (Character monster in monsters)
{
monster.WriteSpawnData(msg, monster.ID, restrictMessageSize: false);
monster.WriteSpawnData(msg, monster.OriginalID, restrictMessageSize: false);
}
}
}

View File

@@ -131,7 +131,7 @@ namespace Barotrauma
case NetEntityEvent.Type.ChangeProperty:
try
{
WritePropertyChange(msg, extraData, false);
WritePropertyChange(msg, extraData, inGameEditableOnly: !GameMain.NetworkMember.IsServer);
}
catch (Exception e)
{
@@ -222,7 +222,7 @@ namespace Barotrauma
break;
case NetEntityEvent.Type.ChangeProperty:
ReadPropertyChange(msg, true, c);
ReadPropertyChange(msg, inGameEditableOnly: GameMain.NetworkMember.IsServer, sender: c);
break;
case NetEntityEvent.Type.Combine:
UInt16 combineTargetID = msg.ReadUInt16();
@@ -367,7 +367,7 @@ namespace Barotrauma
public void CreateServerEvent<T>(T ic) where T : ItemComponent, IServerSerializable
{
if (GameMain.Server == null) return;
if (GameMain.Server == null) { return; }
if (!ItemList.Contains(this))
{
@@ -378,12 +378,31 @@ namespace Barotrauma
}
int index = components.IndexOf(ic);
if (index == -1) return;
if (index == -1) { return; }
object[] extraData = new object[] { NetEntityEvent.Type.ComponentState, index };
ic.ServerAppendExtraData(ref extraData);
GameMain.Server.CreateEntityEvent(this, extraData);
}
public void CreateServerEvent<T>(T ic, object[] extraData) where T : ItemComponent, IServerSerializable
{
if (GameMain.Server == null) { return; }
if (!ItemList.Contains(this))
{
string errorMsg = "Attempted to create a network event for an item (" + Name + ") that hasn't been fully initialized yet.\n" + Environment.StackTrace.CleanupStackTrace();
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Item.CreateServerEvent:EventForUninitializedItem" + Name + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return;
}
int index = components.IndexOf(ic);
if (index == -1) { return; }
object[] data = new object[] { NetEntityEvent.Type.ComponentState, index }.Concat(extraData).ToArray();
GameMain.Server.CreateEntityEvent(this, data);
}
}
}

View File

@@ -119,7 +119,11 @@ namespace Barotrauma.Networking
if (type == ChatMessageType.Order)
{
if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead) { return; }
if (!orderMsg.Order.TargetAllCharacters && orderTargetCharacter != null)
if (orderMsg.Order.TargetAllCharacters)
{
HumanAIController.ReportProblem(orderMsg.Sender, orderMsg.Order);
}
else if (orderTargetCharacter != null)
{
var order = orderTargetPosition == null ?
new Order(orderMsg.Order.Prefab, orderTargetEntity, orderMsg.Order.Prefab?.GetTargetItemComponent(orderTargetEntity as Item), orderMsg.Sender) :

View File

@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product>
<Version>0.10.600.0</Version>
<Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName>

View File

@@ -52,6 +52,8 @@
<Item file="Content/Items/Legacy/legacypump.xml" />
<Item file="Content/Items/Legacy/legacyrailgun.xml" />
<Item file="Content/Items/Legacy/legacysearchlight.xml" />
<Item file="Content/Items/Pets/PetEggs.xml" />
<Item file="Content/Items/Pets/PetItems.xml" />
<Item file="Content/Items/Shipwrecks/wreckeditems.xml" />
<Item file="Content/Map/Thalamus/thalamusitems.xml" />
<Item file="Content/Items/Gardening/growableplants.xml" />
@@ -88,11 +90,14 @@
<Character file="Content/Characters/Moloch_m/Moloch_m.xml" />
<Character file="Content/Characters/Molochblack/Molochblack.xml" />
<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/Tigerthresher/Tigerthresher.xml" />
<Character file="Content/Characters/Bonethresher/Bonethresher.xml" />
<Character file="Content/Characters/Leucocyte/Leucocyte.xml" />
<Character file="Content/Characters/Terminalcell/Terminalcell.xml" />
<Character file="Content/Characters/Watcher/Watcher.xml" />
<Character file="Content/Characters/Smallcrawler/Smallcrawler.xml" />
<Wreck file="Content/Map/Wrecks/Dugong_Wrecked.sub" />
<Wreck file="Content/Map/Wrecks/Kastrull_Wrecked.sub" />
<Wreck file="Content/Map/Wrecks/Berilia_Wrecked.sub" />

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Barotrauma
{
public enum AIState { Idle, Attack, Escape, Eat, Flee, Avoid, Aggressive, PassiveAggressive, Protect, Observe }
public enum AIState { Idle, Attack, Escape, Eat, Flee, Avoid, Aggressive, PassiveAggressive, Protect, Observe, Freeze }
abstract partial class AIController : ISteerable
{

View File

@@ -99,6 +99,7 @@ namespace Barotrauma
public LatchOntoAI LatchOntoAI { get; private set; }
public SwarmBehavior SwarmBehavior { get; private set; }
public PetBehavior PetBehavior { get; private set; }
public CharacterParams.TargetParams SelectedTargetingParams { get { return selectedTargetingParams; } }
@@ -151,7 +152,10 @@ namespace Barotrauma
private set
{
reverse = value;
FishAnimController.reverse = reverse;
if (FishAnimController != null)
{
FishAnimController.reverse = reverse;
}
}
}
@@ -204,6 +208,9 @@ namespace Barotrauma
case "swarmbehavior":
SwarmBehavior = new SwarmBehavior(subElement, this);
break;
case "petbehavior":
PetBehavior = new PetBehavior(subElement, this);
break;
}
}
@@ -221,7 +228,7 @@ namespace Barotrauma
avoidLookAheadDistance = Math.Max(colliderWidth * 3, 1.5f);
}
private CharacterParams.AIParams AIParams => Character.Params.AI;
public CharacterParams.AIParams AIParams => Character.Params.AI;
private CharacterParams.TargetParams GetTargetParams(string targetTag) => AIParams.GetTarget(targetTag, false);
private CharacterParams.TargetParams GetTargetParams(AITarget aiTarget) => GetTargetParams(GetTargetingTag(aiTarget));
private string GetTargetingTag(AITarget aiTarget)
@@ -423,6 +430,9 @@ namespace Barotrauma
bool run = false;
switch (State)
{
case AIState.Freeze:
SteeringManager.Reset();
break;
case AIState.Idle:
UpdateIdle(deltaTime);
break;
@@ -582,6 +592,7 @@ namespace Barotrauma
if (!Character.AnimController.SimplePhysicsEnabled)
{
LatchOntoAI?.Update(this, deltaTime);
PetBehavior?.Update(deltaTime);
}
IsSteeringThroughGap = false;
if (SwarmBehavior != null)
@@ -1619,7 +1630,7 @@ namespace Barotrauma
State = AIState.Idle;
return;
}
if (SelectedAiTarget.Entity is Character target)
if (SelectedAiTarget.Entity is Character || SelectedAiTarget.Entity is Item)
{
Limb mouthLimb = Character.AnimController.GetLimb(LimbType.Head);
if (mouthLimb == null)
@@ -1629,12 +1640,34 @@ namespace Barotrauma
return;
}
Vector2 mouthPos = Character.AnimController.SimplePhysicsEnabled ? SimPosition : Character.AnimController.GetMouthPosition().Value;
Vector2 attackSimPosition = Character.GetRelativeSimPosition(target);
Vector2 attackSimPosition = Character.GetRelativeSimPosition(SelectedAiTarget.Entity);
Vector2 limbDiff = attackSimPosition - mouthPos;
float extent = Math.Max(mouthLimb.body.GetMaxExtent(), 2);
if (limbDiff.LengthSquared() < extent * extent)
{
Character.SelectCharacter(target);
if (SelectedAiTarget.Entity is Character targetCharacter)
{
Character.SelectCharacter(targetCharacter);
}
else if (SelectedAiTarget.Entity is Item item)
{
if (!item.Removed && item.body != null)
{
float itemBodyExtent = item.body.GetMaxExtent() * 2;
if (limbDiff.LengthSquared() < itemBodyExtent * itemBodyExtent)
{
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)
{
Entity.Spawner.AddToRemoveQueue(item);
}
PetBehavior?.OnEat(item.GetTags(), 0.1f / item.MaxCondition);
}
}
}
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(limbDiff) * 3);
Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos);
}

View File

@@ -715,6 +715,17 @@ namespace Barotrauma
}
}
public static void ReportProblem(Character reporter, Order order)
{
if (reporter == null || order == null) { return; }
var visibleHulls = new List<Hull>(reporter.GetVisibleHulls());
foreach (var hull in visibleHulls)
{
PropagateHullSafety(reporter, hull);
RefreshTargets(reporter, order, hull);
}
}
private void UpdateSpeaking()
{
if (Character.Oxygen < 20.0f)

View File

@@ -66,6 +66,7 @@ namespace Barotrauma
//if (rootContainer != null) { return false; }
var pickable = item.GetComponent<Pickable>();
if (pickable == null) { return false; }
if (pickable is Holdable h && h.Attachable && h.Attached) { return false; }
var wire = item.GetComponent<Wire>();
if (wire != null)
{

View File

@@ -0,0 +1,291 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class PetBehavior
{
public float Hunger { get; set; } = 50.0f;
public float Happiness { get; set; } = 50.0f;
public float MaxHappiness { get; set; }
public float MaxHunger { get; set; }
public float HappinessDecreaseRate { get; set; }
public float HungerIncreaseRate { get; set; }
public float PlayForce { get; set; }
public float PlayTimer { get; set; }
public EnemyAIController AiController { get; private set; } = null;
public Character Owner { get; set; }
private class ItemProduction
{
public struct Item
{
public ItemPrefab Prefab;
public float Commonness;
}
public List<Item> Items;
public Vector2 HungerRange;
public Vector2 HappinessRange;
public float Rate;
public float HungerRate;
public float InvHungerRate;
public float HappinessRate;
public float InvHappinessRate;
private readonly float totalCommonness;
private float timer;
public ItemProduction(XElement element)
{
Items = new List<Item>();
HungerRate = element.GetAttributeFloat("hungerrate", 0.0f);
InvHungerRate = element.GetAttributeFloat("invhungerrate", 0.0f);
HappinessRate = element.GetAttributeFloat("happinessrate", 0.0f);
InvHappinessRate = element.GetAttributeFloat("invhappinessrate", 0.0f);
string[] requiredHappinessStr = element.GetAttributeString("requiredhappiness", "0-100").Split('-');
string[] requiredHungerStr = element.GetAttributeString("requiredhunger", "0-100").Split('-');
HappinessRange = new Vector2(0, 100);
HungerRange = new Vector2(0, 100);
float tempF;
if (requiredHappinessStr.Length >= 2)
{
if (float.TryParse(requiredHappinessStr[0], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HappinessRange.X = tempF; }
if (float.TryParse(requiredHappinessStr[1], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HappinessRange.Y = tempF; }
}
if (requiredHungerStr.Length >= 2)
{
if (float.TryParse(requiredHungerStr[0], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HungerRange.X = tempF; }
if (float.TryParse(requiredHungerStr[1], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HungerRange.Y = tempF; }
}
Rate = element.GetAttributeFloat("rate", 0.016f);
totalCommonness = 0.0f;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.LocalName.ToLowerInvariant())
{
case "item":
Item newItemToProduce = new Item
{
Prefab = ItemPrefab.Find("", subElement.GetAttributeString("identifier", "")),
Commonness = subElement.GetAttributeFloat("commonness", 0.0f)
};
totalCommonness += newItemToProduce.Commonness;
Items.Add(newItemToProduce);
break;
}
}
timer = 1.0f;
}
public void Update(PetBehavior pet, float deltaTime)
{
if (pet.Happiness < HappinessRange.X || pet.Happiness > HappinessRange.Y) { return; }
if (pet.Hunger < HungerRange.X || pet.Hunger > HungerRange.Y) { return; }
float currentRate = Rate;
currentRate += HappinessRate * (pet.Happiness - HappinessRange.X) / (HappinessRange.Y - HappinessRange.X);
currentRate += InvHappinessRate * (1.0f - ((pet.Happiness - HappinessRange.X) / (HappinessRange.Y - HappinessRange.X)));
currentRate += HungerRate * (pet.Hunger - HungerRange.X) / (HungerRange.Y - HungerRange.X);
currentRate += InvHungerRate * (1.0f - ((pet.Hunger - HungerRange.X) / (HungerRange.Y - HungerRange.X)));
timer -= currentRate * deltaTime;
if (timer <= 0.0f)
{
timer = 1.0f;
float r = Rand.Range(0.0f, totalCommonness);
float aggregate = 0.0f;
for (int i = 0; i < Items.Count; i++)
{
aggregate += Items[i].Commonness;
if (aggregate >= r)
{
Entity.Spawner.AddToSpawnQueue(Items[i].Prefab, pet.AiController.Character.WorldPosition);
break;
}
}
}
}
}
private class Food
{
public string Tag;
public Vector2 HungerRange;
public float Hunger;
public float Happiness;
public float Priority;
public CharacterParams.TargetParams TargetParams = null;
}
private readonly List<ItemProduction> itemsToProduce = new List<ItemProduction>();
private readonly List<Food> foods = new List<Food>();
public PetBehavior(XElement element, EnemyAIController aiController)
{
AiController = aiController;
AiController.Character.CanBeDragged = true;
MaxHappiness = element.GetAttributeFloat("maxhappiness", 100.0f);
MaxHunger = element.GetAttributeFloat("maxhunger", 100.0f);
Happiness = MaxHappiness * 0.5f;
Hunger = MaxHunger * 0.5f;
HappinessDecreaseRate = element.GetAttributeFloat("happinessdecreaserate", 0.1f);
HungerIncreaseRate = element.GetAttributeFloat("hungerincreaserate", 0.25f);
PlayForce = element.GetAttributeFloat("playforce", 15.0f);
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.LocalName.ToLowerInvariant())
{
case "itemproduction":
itemsToProduce.Add(new ItemProduction(subElement));
break;
case "eat":
Food food = new Food
{
Tag = subElement.GetAttributeString("tag", "")
};
string[] requiredHungerStr = subElement.GetAttributeString("requiredhunger", "0-100").Split('-');
food.HungerRange = new Vector2(0, 100);
if (requiredHungerStr.Length >= 2)
{
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 void OnEat(IEnumerable<string> tags, float amount)
{
for (int i = 0; i < foods.Count; i++)
{
if (tags.Any(t => t.Equals(foods[i].Tag, System.StringComparison.OrdinalIgnoreCase)))
{
Hunger += foods[i].Hunger * amount;
Happiness += foods[i].Happiness * amount;
break;
}
}
}
public void OnEat(string tag, float amount)
{
for (int i = 0; i < foods.Count; i++)
{
if (tag.Equals(foods[i].Tag, System.StringComparison.OrdinalIgnoreCase))
{
Hunger += foods[i].Hunger * amount;
Happiness += foods[i].Happiness * amount;
break;
}
}
}
public void Play()
{
if (PlayTimer > 0.0f) { return; }
PlayTimer = 5.0f;
AiController.Character.Stun = 1.0f;
Happiness += 10.0f;
if (Happiness > MaxHappiness) { Happiness = MaxHappiness; }
AiController.Character.AnimController.MainLimb.body.LinearVelocity += new Vector2(0, PlayForce);
}
public string GetName()
{
if (AiController.Character.Inventory != null)
{
var items = AiController.Character.Inventory.Items;
for (int i = 0; i < items.Length; i++)
{
var item = items[i];
if (item == null) { continue; }
var tag = item.GetComponent<NameTag>();
if (tag != null && !string.IsNullOrWhiteSpace(tag.WrittenName))
{
return tag.WrittenName;
}
}
}
return AiController.Character.Name;
}
public void Update(float deltaTime)
{
var character = AiController.Character;
if (character?.Removed ?? true || character.IsDead) { return; }
if (GameMain.NetworkMember?.IsClient ?? false) { return; } //TODO: syncing
Hunger += HungerIncreaseRate * deltaTime;
Happiness -= HappinessDecreaseRate * deltaTime;
PlayTimer -= deltaTime;
for (int i = 0; i < foods.Count; i++)
{
if (Hunger >= foods[i].HungerRange.X && Hunger <= foods[i].HungerRange.Y)
{
if (foods[i].TargetParams == null &&
AiController.AIParams.TryAddNewTarget(foods[i].Tag, AIState.Eat, foods[i].Priority, out CharacterParams.TargetParams targetParams))
{
foods[i].TargetParams = targetParams;
}
}
else if (foods[i].TargetParams != null)
{
AiController.AIParams.RemoveTarget(foods[i].TargetParams);
foods[i].TargetParams = null;
}
}
if (Hunger < 0.0f) { Hunger = 0.0f; }
if (Hunger > MaxHunger) { Hunger = MaxHunger; }
if (Happiness < 0.0f) { Happiness = 0.0f; }
if (Happiness > MaxHappiness) { Happiness = MaxHappiness; }
if (PlayTimer < 0.0f) { PlayTimer = 0.0f; }
if (Hunger >= MaxHunger * 0.99f)
{
character.CharacterHealth.ApplyAffliction(character.AnimController.MainLimb, new Affliction(AfflictionPrefab.InternalDamage, 8.0f * deltaTime));
}
else if (Hunger < MaxHunger * 0.1f)
{
character.CharacterHealth.ReduceAffliction(null, null, 8.0f * deltaTime);
}
if (character.SelectedBy != null)
{
character.Stun = 1.0f;
}
for (int i = 0; i < itemsToProduce.Count; i++)
{
itemsToProduce[i].Update(this, deltaTime);
}
}
}
}

View File

@@ -381,6 +381,12 @@ namespace Barotrauma
{
//only one limb left, the character is now full eaten
Entity.Spawner?.AddToRemoveQueue(target);
if (Character.AIController is EnemyAIController enemyAi)
{
enemyAi.PetBehavior?.OnEat("dead", 1.0f);
}
character.SelectedCharacter = null;
}
else //sever a random joint

View File

@@ -753,6 +753,7 @@ 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)
{

View File

@@ -215,9 +215,6 @@ namespace Barotrauma
public bool IsDismissed => Info != null && Info.IsDismissed;
private readonly List<StatusEffect> statusEffects = new List<StatusEffect>();
private readonly List<float> speedMultipliers = new List<float>();
private float greatestNegativeSpeedMultiplier = 1f;
private float greatestPositiveSpeedMultiplier = 1f;
public Entity ViewTarget
{
@@ -274,6 +271,11 @@ namespace Barotrauma
{
get
{
if (IsPet)
{
return (AIController as EnemyAIController).PetBehavior.GetName();
}
if (info != null && !string.IsNullOrWhiteSpace(info.Name)) { return info.Name; }
var displayName = Params.DisplayName;
if (string.IsNullOrWhiteSpace(displayName))
@@ -473,6 +475,11 @@ namespace Barotrauma
get { return CharacterHealth.IsUnconscious; }
}
public bool IsPet
{
get { return AIController is EnemyAIController enemyController && enemyController.PetBehavior != null; }
}
public float Oxygen
{
get { return CharacterHealth.OxygenAmount; }
@@ -488,7 +495,7 @@ namespace Barotrauma
get { return oxygenAvailable; }
set { oxygenAvailable = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public float Stun
{
get { return IsRagdolled ? 1.0f : CharacterHealth.StunTimer; }
@@ -638,7 +645,7 @@ namespace Barotrauma
{
if (!canBeDragged) { return false; }
if (Removed || !AnimController.Draggable) { return false; }
return IsDead || Stun > 0.0f || LockHands || IsIncapacitated;
return IsDead || Stun > 0.0f || LockHands || IsIncapacitated || IsPet;
}
set { canBeDragged = value; }
}
@@ -1218,10 +1225,13 @@ namespace Barotrauma
return targetMovement;
}
private float greatestNegativeSpeedMultiplier = 1f;
private float greatestPositiveSpeedMultiplier = 1f;
/// <summary>
/// Can be used to modify the character's speed via StatusEffects
/// </summary>
public float SpeedMultiplier { get; private set; }
public float SpeedMultiplier { get; private set; } = 1;
public void StackSpeedMultiplier(float val)
{
@@ -1247,6 +1257,40 @@ namespace Barotrauma
greatestNegativeSpeedMultiplier = 1f;
}
private float greatestNegativeHealthMultiplier = 1f;
private float greatestPositiveHealthMultiplier = 1f;
/// <summary>
/// Can be used to modify the character's health via StatusEffects
/// </summary>
public float HealthMultiplier { get; private set; } = 1;
public void StackHealthMultiplier(float val)
{
if (val < 1f)
{
if (val < greatestNegativeHealthMultiplier)
{
greatestNegativeHealthMultiplier = val;
}
}
else
{
if (val > greatestPositiveHealthMultiplier)
{
greatestPositiveHealthMultiplier = val;
}
}
}
private void CalculateHealthMultiplier()
{
HealthMultiplier = greatestPositiveHealthMultiplier - (1f - greatestNegativeHealthMultiplier);
// Reset, status effects should set the values again, if the conditions match
greatestPositiveHealthMultiplier = 1f;
greatestNegativeHealthMultiplier = 1f;
}
/// <summary>
/// Speed reduction from the current limb specific damage. Min 0, max 1.
/// </summary>
@@ -2132,6 +2176,10 @@ namespace Barotrauma
{
SelectCharacter(FocusedCharacter);
}
else if (FocusedCharacter != null && !FocusedCharacter.IsIncapacitated && IsKeyHit(InputType.Use) && FocusedCharacter.IsPet && CanInteract)
{
(FocusedCharacter.AIController as EnemyAIController).PetBehavior.Play();
}
else if (FocusedCharacter != null && IsKeyHit(InputType.Health) && FocusedCharacter.CharacterHealth.UseHealthWindow && CanInteract && CanInteractWith(FocusedCharacter, 160f, false))
{
if (FocusedCharacter == SelectedCharacter)
@@ -2369,6 +2417,7 @@ namespace Barotrauma
}
ApplyStatusEffects(AnimController.InWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
ApplyStatusEffects(ActionType.OnActive, deltaTime);
UpdateControlled(deltaTime, cam);
@@ -2377,6 +2426,8 @@ namespace Barotrauma
{
UpdateOxygen(deltaTime);
}
CalculateHealthMultiplier();
CharacterHealth.Update(deltaTime);
if (IsIncapacitated)

View File

@@ -141,17 +141,17 @@ namespace Barotrauma
{
get
{
float max = maxVitality;
if (Character?.Info?.Job?.Prefab != null)
{
return maxVitality + Character.Info.Job.Prefab.VitalityModifier;
max += Character.Info.Job.Prefab.VitalityModifier;
}
return maxVitality;
return max * Character.HealthMultiplier;
}
set
{
maxVitality = Math.Max(0, value);
}
}
public float MinVitality
@@ -450,9 +450,13 @@ namespace Barotrauma
matchingAfflictions.AddRange(limbHealth.Afflictions);
}
}
matchingAfflictions.RemoveAll(a =>
!a.Prefab.Identifier.Equals(affliction, StringComparison.OrdinalIgnoreCase) &&
!a.Prefab.AfflictionType.Equals(affliction, StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(affliction))
{
matchingAfflictions.RemoveAll(a =>
!a.Prefab.Identifier.Equals(affliction, StringComparison.OrdinalIgnoreCase) &&
!a.Prefab.AfflictionType.Equals(affliction, StringComparison.OrdinalIgnoreCase));
}
if (matchingAfflictions.Count == 0) return;
@@ -617,8 +621,8 @@ namespace Barotrauma
private void AddAffliction(Affliction newAffliction)
{
if (!DoesBleed && newAffliction is AfflictionBleeding) return;
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) return;
if (!DoesBleed && newAffliction is AfflictionBleeding) { return; }
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) { return; }
if (newAffliction.Prefab.AfflictionType == "huskinfection")
{
var huskPrefab = newAffliction.Prefab as AfflictionPrefabHusk;
@@ -636,7 +640,10 @@ namespace Barotrauma
affliction.Strength = newStrength;
affliction.Source = newAffliction.Source;
CalculateVitality();
if (Vitality <= MinVitality) Kill();
if (Vitality <= MinVitality)
{
Kill();
}
return;
}
}
@@ -650,7 +657,10 @@ namespace Barotrauma
Character.HealthUpdateInterval = 0.0f;
CalculateVitality();
if (Vitality <= MinVitality) Kill();
if (Vitality <= MinVitality)
{
Kill();
}
}
@@ -707,7 +717,11 @@ namespace Barotrauma
UpdateLimbAfflictionOverlays();
CalculateVitality();
if (Vitality <= MinVitality) Kill();
if (Vitality <= MinVitality)
{
Kill();
}
}
private void UpdateOxygen(float deltaTime)

View File

@@ -163,7 +163,13 @@ namespace Barotrauma
{
item.AddTag("job:" + job.Name);
}
var idCardTags = itemElement.GetAttributeStringArray("tags", new string[0]);
foreach (string tag in idCardTags)
{
item.AddTag(tag);
}
}
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
{
wifiComponent.TeamID = character.TeamID;

View File

@@ -62,9 +62,9 @@ namespace Barotrauma
{
Sprites.Add(new Sprite(subElement));
}
}
Color = new Color(element.GetAttributeVector4("color", Vector4.One));
}
Color = element.GetAttributeColor("color", Color.White);
LifeTime = element.GetAttributeFloat("lifetime", 10.0f);
FadeOutTime = Math.Min(LifeTime, element.GetAttributeFloat("fadeouttime", 1.0f));

View File

@@ -120,6 +120,7 @@ namespace Barotrauma
AddChildEvents(initialEventSet);
void AddChildEvents(EventSet eventSet)
{
if (eventSet == null) { return; }
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.First))
{
if (!level.LevelData.NonRepeatableEvents.Contains(ep))

View File

@@ -717,11 +717,10 @@ namespace Barotrauma.Items.Components
bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
(leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);
if (leakFixed && leak.FlowTargetHull != null)
if (leakFixed && leak.FlowTargetHull?.DisplayName != null)
{
if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
{
{
character.Speak(TextManager.GetWithVariable("DialogLeaksFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leaksfixed", 10.0f);
}
else

View File

@@ -146,7 +146,7 @@ namespace Barotrauma.Items.Components
public bool DrawHudWhenEquipped
{
get;
private set;
protected set;
}
[Serialize(false, false, description: "Can the item be selected by interacting with it.")]

View File

@@ -0,0 +1,21 @@
using Barotrauma.Networking;
using System.Xml.Linq;
#if CLIENT
using Microsoft.Xna.Framework.Graphics;
#endif
namespace Barotrauma.Items.Components
{
class NameTag : ItemComponent
{
[InGameEditable, Serialize("", false, description: "Name written on the tag.", alwaysUseInstanceValues: true)]
public string WrittenName { get; set; }
public NameTag(Item item, XElement element) : base(item, element)
{
AllowInGameEditing = true;
DrawHudWhenEquipped = true;
item.EditableWhenEquipped = true;
}
}
}

View File

@@ -1,10 +1,11 @@
using System.Xml.Linq;
using Barotrauma.Networking;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Terminal : ItemComponent
{
private const int MaxMessageLength = 150;
private const int MaxMessageLength = ChatMessage.MaxLength;
public string DisplayedWelcomeMessage
{

View File

@@ -304,13 +304,16 @@ namespace Barotrauma
for (int j = 0; j < otherInventory.capacity; j++)
{
if (otherInventory.Items[j] == item) otherInventory.Items[j] = null;
if (otherInventory.Items[j] == item) { otherInventory.Items[j] = null; }
}
for (int j = 0; j < capacity; j++)
{
if (Items[j] == existingItem) Items[j] = null;
if (Items[j] == existingItem) { Items[j] = null; }
}
(otherInventory.Owner as Character)?.DeselectItem(item);
(otherInventory.Owner as Character)?.DeselectItem(existingItem);
bool swapSuccessful = false;
if (otherIsEquipped)
{

View File

@@ -119,6 +119,8 @@ namespace Barotrauma
}
}
public bool EditableWhenEquipped { get; set; } = false;
//the inventory in which the item is contained in
public Inventory ParentInventory
{
@@ -2257,7 +2259,7 @@ namespace Barotrauma
var propertyOwner = allProperties.Find(p => p.Second == property);
if (allProperties.Count > 1)
{
msg.WriteRangedInteger(allProperties.FindIndex(p => p.Second == property), 0, allProperties.Count - 1);
msg.Write((byte)allProperties.FindIndex(p => p.Second == property));
}
object value = property.GetValue(propertyOwner.First);
@@ -2339,7 +2341,7 @@ namespace Barotrauma
int propertyIndex = 0;
if (allProperties.Count > 1)
{
propertyIndex = msg.ReadRangedInteger(0, allProperties.Count - 1);
propertyIndex = msg.ReadByte();
}
bool allowEditing = true;
@@ -2360,6 +2362,7 @@ namespace Barotrauma
if (type == typeof(string))
{
string val = msg.ReadString();
logValue = val;
if (allowEditing)
{
property.TrySetValue(parentObject, val);

View File

@@ -73,13 +73,13 @@ namespace Barotrauma
{
get;
set;
}
} = true;
public bool DamagesCharacters
{
get;
set;
}
} = true;
public bool Removed
{

View File

@@ -65,8 +65,8 @@ namespace Barotrauma
Noise = new Vector2(
PerlinNoise.GetPerlin(Rect.X / 1000.0f, Rect.Y / 1000.0f),
PerlinNoise.GetPerlin(Rect.Y / 1000.0f + 0.5f, Rect.X / 1000.0f + 0.5f));
Color = DirtColor = Color.Lerp(new Color(10, 10, 10, 100), new Color(54, 57, 28, 200), Noise.X);
DirtColor = Color.Lerp(new Color(10, 10, 10, 100), new Color(54, 57, 28, 200), Noise.X);
}
public bool SetColor(Color color)

View File

@@ -480,6 +480,9 @@ namespace Barotrauma
case "IsDead":
{ if (parentObject is Character character) { return character.IsDead; } }
break;
case "IsHuman":
{ if (parentObject is Character character) { return character.IsHuman; } }
break;
case "IsOn":
{ if (parentObject is LightComponent lightComponent) { return lightComponent.IsOn; } }
break;
@@ -543,6 +546,9 @@ namespace Barotrauma
case "SpeedMultiplier":
{ if (parentObject is Character character && value is float) { character.StackSpeedMultiplier((float)value); return true; } }
break;
case "HealthMultiplier":
{ if (parentObject is Character character && value is float) { character.StackHealthMultiplier((float)value); return true; } }
break;
case "IsOn":
{ if (parentObject is LightComponent lightComponent && value is bool) { lightComponent.IsOn = (bool)value; return true; } }
break;

View File

@@ -8,14 +8,7 @@ namespace Barotrauma
partial class ConditionalSprite
{
public readonly List<PropertyConditional> conditionals = new List<PropertyConditional>();
public bool IsActive
{
get
{
if (Target == null) { return false; }
return Comparison == PropertyConditional.Comparison.And ? conditionals.All(c => c.Matches(Target)) : conditionals.Any(c => c.Matches(Target));
}
}
public bool IsActive { get; private set; } = true;
public readonly PropertyConditional.Comparison Comparison;
public readonly bool Exclusive;
@@ -55,5 +48,17 @@ namespace Barotrauma
}
}
}
public void CheckConditionals()
{
if (Target == null)
{
IsActive = false;
}
else
{
IsActive = Comparison == PropertyConditional.Comparison.And ? conditionals.All(c => c.Matches(Target)) : conditionals.Any(c => c.Matches(Target));
}
}
}
}

View File

@@ -687,16 +687,8 @@ namespace Barotrauma
}
if (targetIdentifiers != null && currentTargets.Count == 0) { return; }
if (!HasRequiredItems(entity)) { return; }
// If "This" is defined as the target, let's target the conditions only to this entity, because when you use "NearbyTargets", this entity is also taken into account.
// If we want to target both, leaving "This" out and using only "NearbyTargets" should work.
// Currently we don't have means for targeting only the nearby characters, though.
if (HasTargetType(TargetType.This))
{
if (!HasRequiredConditions(((ISerializableEntity)entity).ToEnumerable())) { return; }
}
else if (!HasRequiredConditions(currentTargets)) { return; }
if (!HasRequiredItems(entity) || !HasRequiredConditions(currentTargets)) { return; }
if (duration > 0.0f && !Stackable)
{
@@ -919,6 +911,13 @@ namespace Barotrauma
Entity.Spawner.AddToSpawnQueue(characterSpawnInfo.SpeciesName, position + Rand.Vector(characterSpawnInfo.Spread, Rand.RandSync.Server) + characterSpawnInfo.Offset,
onSpawn: newCharacter =>
{
if (newCharacter.AIController is EnemyAIController enemyAi &&
enemyAi.PetBehavior != null &&
entity is Item item &&
item.ParentInventory is CharacterInventory inv)
{
enemyAi.PetBehavior.Owner = inv.Owner as Character;
}
characters.Add(newCharacter);
if (characters.Count == characterSpawnInfo.Count)
{

View File

@@ -1,3 +1,34 @@
---------------------------------------------------------------------------------------------------------
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)
---------------------------------------------------------------------------------------------------------