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
@@ -321,6 +321,17 @@ namespace Barotrauma
{ {
character.SelectedConstruction.DrawHUD(spriteBatch, cam, Character.Controlled); 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; } if (IsCampaignInterfaceOpen) { return; }
@@ -431,6 +442,14 @@ namespace Barotrauma
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUI.SubHeadingFont); GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUI.SubHeadingFont);
textPos.X += 10.0f * GUI.Scale; textPos.X += 10.0f * GUI.Scale;
textPos.Y += GUI.SubHeadingFont.MeasureString(focusName).Y; 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) if (character.FocusedCharacter.CanBeDragged)
{ {
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBindText(InputType.Grab)), GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBindText(InputType.Grab)),
@@ -278,17 +278,7 @@ namespace Barotrauma
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams)); DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams));
break; break;
case "conditionalsprite": case "conditionalsprite":
ISerializableEntity targetEntity; var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(), file: GetSpritePath(subElement, null));
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));
ConditionalSprites.Add(conditionalSprite); ConditionalSprites.Add(conditionalSprite);
if (conditionalSprite.DeformableSprite != null) if (conditionalSprite.DeformableSprite != null)
{ {
@@ -300,7 +290,7 @@ namespace Barotrauma
CreateDeformations(subElement); CreateDeformations(subElement);
break; break;
case "lightsource": case "lightsource":
LightSource = new LightSource(subElement) LightSource = new LightSource(subElement, GetConditionalTarget())
{ {
ParentBody = body, ParentBody = body,
SpriteScale = Vector2.One * Scale * TextureScale SpriteScale = Vector2.One * Scale * TextureScale
@@ -310,6 +300,21 @@ namespace Barotrauma
break; 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) void CreateDeformations(XElement e)
{ {
foreach (XElement animationElement in e.GetChildElements("spritedeformation")) foreach (XElement animationElement in e.GetChildElements("spritedeformation"))
@@ -341,6 +346,7 @@ namespace Barotrauma
} }
} }
} }
LightSource?.CheckConditionals();
} }
public void RecreateSprites() public void RecreateSprites()
@@ -561,6 +567,11 @@ namespace Barotrauma
} }
} }
foreach (var conditionalSprite in ConditionalSprites)
{
conditionalSprite.CheckConditionals();
}
if (LightSource != null) if (LightSource != null)
{ {
LightSource.ParentSub = body.Submarine; LightSource.ParentSub = body.Submarine;
@@ -573,6 +584,7 @@ namespace Barotrauma
{ {
LightSource.DeformableLightSprite.Sprite.Depth = ActiveSprite.Depth; LightSource.DeformableLightSprite.Sprite.Depth = ActiveSprite.Depth;
} }
LightSource.CheckConditionals();
} }
UpdateSpriteStates(deltaTime); UpdateSpriteStates(deltaTime);
@@ -271,7 +271,7 @@ namespace Barotrauma
private static List<GUIButton> CreateConversation(GUIListBox parentBox, string text, Character speaker, IEnumerable<string> options, bool drawChathead = true) 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, Stretch = true,
CanBeFocused = 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) AbsoluteSpacing = GUI.IntScale(5)
}; };
@@ -316,7 +316,7 @@ namespace Barotrauma
content.Recalculate(); content.Recalculate();
textContent.Recalculate(); textContent.Recalculate();
textBlock.CalculateHeightFromText(); 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) foreach (GUIButton btn in buttons)
{ {
btn.TextBlock.SetTextPos(); btn.TextBlock.SetTextPos();
@@ -324,10 +324,12 @@ namespace Barotrauma
btn.RectTransform.MinSize = new Point(0, (int)(btn.TextBlock.Rect.Height * 1.2f)); 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 // 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.CalculateHeightFromText();
textBlock.TextAlignment = Alignment.TopLeft;
//content.RectTransform.MinSize = new Point(0, textContent.Rect.Height); //content.RectTransform.MinSize = new Point(0, textContent.Rect.Height);
return buttons; return buttons;
@@ -650,6 +650,8 @@ namespace Barotrauma
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio)) if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
{ {
radio.Channel = channel; radio.Channel = channel;
GameMain.Client?.CreateEntityEvent(radio.Item, new object[] { NetEntityEvent.Type.ChangeProperty, radio.SerializableProperties["channel"] });
if (setText) if (setText)
{ {
string text = radio.Channel.ToString().PadLeft(4, '0'); string text = radio.Channel.ToString().PadLeft(4, '0');
@@ -348,7 +348,7 @@ namespace Barotrauma
spriteBatch.Draw(currSplashScreen.GetTexture(), new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White); spriteBatch.Draw(currSplashScreen.GetTexture(), new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
spriteBatch.End(); 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; currSplashScreen.Dispose(); currSplashScreen = null;
} }
@@ -261,12 +261,7 @@ namespace Barotrauma
var sub = Character.Controlled.Submarine; var sub = Character.Controlled.Submarine;
if (sub == null || sub.TeamID != Character.Controlled.TeamID || sub.Info.IsWreck) { return false; } if (sub == null || sub.TeamID != Character.Controlled.TeamID || sub.Info.IsWreck) { return false; }
SetCharacterOrder(null, order, null, Character.Controlled); SetCharacterOrder(null, order, null, Character.Controlled);
var visibleHulls = new List<Hull>(Character.Controlled.GetVisibleHulls()); if (IsSinglePlayer) { HumanAIController.ReportProblem(Character.Controlled, order); }
foreach (var hull in visibleHulls)
{
HumanAIController.PropagateHullSafety(Character.Controlled, hull);
HumanAIController.RefreshTargets(Character.Controlled, order, hull);
}
return true; return true;
}, },
UserData = order, UserData = order,
@@ -2221,8 +2216,8 @@ namespace Barotrauma
var operateWeaponsPrefab = Order.GetPrefab(orderIdentifier); var operateWeaponsPrefab = Order.GetPrefab(orderIdentifier);
if (contextualOrders.None(o => o.Identifier.Equals(orderIdentifier)) && itemContext.Components.Any(c => c is Controller)) 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)) ?? var turret = itemContext.GetConnectedComponents<Turret>().FirstOrDefault(c => c.Item.HasTag(operateWeaponsPrefab.TargetItems)) ??
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => operateWeaponsPrefab.TargetItems.Contains(c.Item.Prefab.Identifier)); 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)); } 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; } if (item.Repairables.Any(r => item.ConditionPercentage < r.RepairThreshold)) { return true; }
var operateWeaponsPrefab = Order.GetPrefab("operateweapons"); var operateWeaponsPrefab = Order.GetPrefab("operateweapons");
return item.Components.Any(c => c is Controller) && return item.Components.Any(c => c is Controller) &&
(item.GetConnectedComponents<Turret>().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 => operateWeaponsPrefab.TargetItems.Contains(c.Item.Prefab.Identifier))); 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) private GUIButton CreateOrderNode(Point size, RectTransform parent, Point offset, Order order, int hotkey, bool disableNode = false, bool checkIfOrderCanBeHeard = true)
@@ -311,6 +311,8 @@ namespace Barotrauma.Tutorials
} }
yield return null; yield return null;
} while (engineer_reactor.AvailableFuel == 0); } while (engineer_reactor.AvailableFuel == 0);
RemoveCompletedObjective(segments[1]);
TriggerTutorialSegment(2);
CoroutineManager.StartCoroutine(ReactorOperatedProperly()); CoroutineManager.StartCoroutine(ReactorOperatedProperly());
do do
{ {
@@ -352,7 +354,7 @@ namespace Barotrauma.Tutorials
} while (wait > 0.0f); } while (wait > 0.0f);
engineer.SelectedConstruction = null; engineer.SelectedConstruction = null;
engineer_reactor.CanBeSelected = false; engineer_reactor.CanBeSelected = false;
RemoveCompletedObjective(segments[1]); RemoveCompletedObjective(segments[2]);
SetHighlight(engineer_reactor.Item, false); SetHighlight(engineer_reactor.Item, false);
SetHighlight(engineer_brokenJunctionBox, true); SetHighlight(engineer_brokenJunctionBox, true);
SetDoorAccess(engineer_secondDoor, engineer_secondDoorLight, true); SetDoorAccess(engineer_secondDoor, engineer_secondDoorLight, true);
@@ -361,7 +363,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!engineer_secondDoor.IsOpen); do { yield return null; } while (!engineer_secondDoor.IsOpen);
yield return new WaitForSeconds(1f, false); yield return new WaitForSeconds(1f, false);
Repairable repairableJunctionBoxComponent = engineer_brokenJunctionBox.GetComponent<Repairable>(); 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 do
{ {
if (!engineer.HasEquippedItem("screwdriver")) if (!engineer.HasEquippedItem("screwdriver"))
@@ -389,7 +391,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!engineer_thirdDoor.IsOpen); do { yield return null; } while (!engineer_thirdDoor.IsOpen);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.FaultyWiring"), ChatMessageType.Radio, null); GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.FaultyWiring"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false); 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 do { CheckGhostWires(); HandleJunctionBoxWiringHighlights(); yield return null; } while (engineer_workingPump.Voltage < engineer_workingPump.MinVoltage); // Wait until connected all the way to the pump
CheckGhostWires(); CheckGhostWires();
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++) for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
@@ -406,7 +408,7 @@ namespace Barotrauma.Tutorials
do { yield return null; } while (!tutorial_enteredSubmarineSensor.MotionDetected); do { yield return null; } while (!tutorial_enteredSubmarineSensor.MotionDetected);
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Submarine"), ChatMessageType.Radio, null); GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Submarine"), ChatMessageType.Radio, null);
yield return new WaitForSeconds(2f, false); yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(4); // Repair junction box TriggerTutorialSegment(5); // Repair junction box
while (ContentRunning) yield return null; while (ContentRunning) yield return null;
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_1, engineer_repairIcon, engineer_repairIconColor); engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_1, engineer_repairIcon, engineer_repairIconColor);
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_2, engineer_repairIcon, engineer_repairIconColor); engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_2, engineer_repairIcon, engineer_repairIconColor);
@@ -425,7 +427,7 @@ namespace Barotrauma.Tutorials
RemoveCompletedObjective(segments[4]); RemoveCompletedObjective(segments[4]);
yield return new WaitForSeconds(2f, false); yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(5); // Powerup reactor TriggerTutorialSegment(6); // Powerup reactor
SetHighlight(engineer_submarineReactor.Item, true); SetHighlight(engineer_submarineReactor.Item, true);
engineer.AddActiveObjectiveEntity(engineer_submarineReactor.Item, engineer_reactorIcon, engineer_reactorIconColor); engineer.AddActiveObjectiveEntity(engineer_submarineReactor.Item, engineer_reactorIcon, engineer_reactorIconColor);
do { yield return null; } while (!IsReactorPoweredUp(engineer_submarineReactor)); // Wait until ~matches load do { yield return null; } while (!IsReactorPoweredUp(engineer_submarineReactor)); // Wait until ~matches load
@@ -141,7 +141,7 @@ namespace Barotrauma.Tutorials
mechanic_brokenWall_1.SpriteColor = Color.White; mechanic_brokenWall_1.SpriteColor = Color.White;
for (int i = 0; i < mechanic_brokenWall_1.SectionCount; i++) 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; mechanic_brokenhull_1 = mechanic_brokenWall_1.Sections[0].gap.FlowTargetHull;
@@ -199,7 +199,7 @@ namespace Barotrauma.Tutorials
mechanic_brokenWall_2.SpriteColor = Color.White; mechanic_brokenWall_2.SpriteColor = Color.White;
for (int i = 0; i < mechanic_brokenWall_2.SectionCount; i++) 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; mechanic_brokenhull_2 = mechanic_brokenWall_2.Sections[0].gap.FlowTargetHull;
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false); SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false);
@@ -362,10 +362,10 @@ namespace Barotrauma.Tutorials
SetHighlight(officer_rangedWeaponHolder.Item, false); SetHighlight(officer_rangedWeaponHolder.Item, false);
do do
{ {
HighlightInventorySlot(officer.Inventory, "harpoongun", highlightColor, 0.5f, 0.5f, 0f); HighlightInventorySlot(officer.Inventory, "shotgun", highlightColor, 0.5f, 0.5f, 0f);
yield return null; yield return null;
} while (!officer.HasEquippedItem("harpoongun")); // Wait until equipped } while (!officer.HasEquippedItem("shotgun")); // Wait until equipped
ItemContainer harpoonGunChamber = officer.Inventory.FindItemByIdentifier("harpoongun").GetComponent<ItemContainer>(); ItemContainer shotGunChamber = officer.Inventory.FindItemByIdentifier("shotgun").GetComponent<ItemContainer>();
SetHighlight(officer_rangedWeaponCabinet.Item, true); SetHighlight(officer_rangedWeaponCabinet.Item, true);
do do
{ {
@@ -376,7 +376,7 @@ namespace Barotrauma.Tutorials
for (int i = 0; i < officer_rangedWeaponCabinet.Inventory.Items.Length; i++) 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] == 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); 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++) for (int i = 0; i < officer.Inventory.Items.Length; i++)
{ {
if (officer.Inventory.Items[i] == null) continue; 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); 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; 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]); RemoveCompletedObjective(segments[5]);
SetHighlight(officer_rangedWeaponCabinet.Item, false); SetHighlight(officer_rangedWeaponCabinet.Item, false);
SetDoorAccess(officer_fourthDoor, officer_fourthDoorLight, true); SetDoorAccess(officer_fourthDoor, officer_fourthDoorLight, true);
@@ -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.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.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.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; 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; private Point screenResolution;
@@ -730,6 +730,7 @@ namespace Barotrauma
{ {
for (int i = 0; i < indicators.Length; i++) for (int i = 0; i < indicators.Length; i++)
{ {
if (indicatorIndexes[i] < 0) { continue; }
Item item = Items[indicatorIndexes[i]]; Item item = Items[indicatorIndexes[i]];
if (item != null) if (item != null)
{ {
@@ -1002,6 +1002,7 @@ namespace Barotrauma
{ {
if (DraggingItemToWorld && if (DraggingItemToWorld &&
Character.Controlled.FocusedItem?.OwnInventory != null && 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.CanBePut(draggingItem) &&
Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled)) Character.Controlled.FocusedItem.OwnInventory.TryPutItem(draggingItem, Character.Controlled))
{ {
@@ -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; 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>(); var holdable = GetComponent<Holdable>();
if (holdable != null && holdable.Picker?.AnimController != null) if (holdable != null && holdable.Picker?.AnimController != null)
{ {
if (!back) { return; }
float depthStep = 0.000001f; float depthStep = 0.000001f;
if (holdable.Picker.SelectedItems[0] == this) if (holdable.Picker.SelectedItems[0] == this)
{ {
@@ -855,7 +856,7 @@ namespace Barotrauma
public void UpdateHUD(Camera cam, Character character, float deltaTime) public void UpdateHUD(Camera cam, Character character, float deltaTime)
{ {
bool editingHUDCreated = false; bool editingHUDCreated = false;
if ((HasInGameEditableProperties && character.SelectedConstruction == this) || if ((HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped)) ||
Screen.Selected == GameMain.SubEditorScreen) Screen.Selected == GameMain.SubEditorScreen)
{ {
GUIComponent prevEditingHUD = editingHUD; GUIComponent prevEditingHUD = editingHUD;
@@ -956,7 +957,7 @@ namespace Barotrauma
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character) public void DrawHUD(SpriteBatch spriteBatch, Camera cam, Character character)
{ {
if (HasInGameEditableProperties) if (HasInGameEditableProperties && (character.SelectedConstruction == this || EditableWhenEquipped))
{ {
DrawEditing(spriteBatch, cam); DrawEditing(spriteBatch, cam);
} }
@@ -1028,13 +1029,13 @@ namespace Barotrauma
} }
else else
{ {
if (HasInGameEditableProperties) if (HasInGameEditableProperties && Character.Controlled != null && (Character.Controlled.SelectedConstruction == this || EditableWhenEquipped))
{ {
if (editingHUD != null && editingHUD.UserData == this) { editingHUD.AddToGUIUpdateList(); } 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; bool needsLayoutUpdate = false;
foreach (ItemComponent ic in activeHUDs) foreach (ItemComponent ic in activeHUDs)
@@ -94,20 +94,21 @@ namespace Barotrauma
{ {
if (entity == this || !entity.IsHighlighted) { continue; } if (entity == this || !entity.IsHighlighted) { continue; }
if (!entity.IsMouseOn(position)) { 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 == this || !entity.IsHighlighted) { continue; }
if (!entity.IsMouseOn(position)) continue; if (!entity.IsMouseOn(position)) { continue; }
if (entity.Linkable && entity.linkedTo != null && !entity.linkedTo.Contains(this)) if (entity.linkedTo.Contains(this))
{ {
entity.linkedTo.Add(this); entity.linkedTo.Remove(this);
linkedTo.Add(entity); linkedTo.Remove(entity);
} }
} }
else if (entity.Linkable && entity.linkedTo != null) else
{ {
entity.linkedTo.Add(this); if (!entity.linkedTo.Contains(this)) { entity.linkedTo.Add(this); }
linkedTo.Add(entity); if (!linkedTo.Contains(this)) { linkedTo.Add(entity); }
} }
} }
} }
@@ -605,11 +606,6 @@ namespace Barotrauma
{ {
float colorStrength = message.ReadRangedSingle(0.0f, 1.0f, 8); float colorStrength = message.ReadRangedSingle(0.0f, 1.0f, 8);
Color color = new Color(message.ReadUInt32()); 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); var remoteBackgroundSection = remoteBackgroundSections.Find(s => s.Index == i);
if (remoteBackgroundSection != null) if (remoteBackgroundSection != null)
{ {
@@ -655,8 +651,10 @@ namespace Barotrauma
{ {
foreach (BackgroundSection remoteBackgroundSection in remoteBackgroundSections) foreach (BackgroundSection remoteBackgroundSection in remoteBackgroundSections)
{ {
float prevColorStrength = BackgroundSections[remoteBackgroundSection.Index].ColorStrength;
BackgroundSections[remoteBackgroundSection.Index].SetColor(remoteBackgroundSection.Color); BackgroundSections[remoteBackgroundSection.Index].SetColor(remoteBackgroundSection.Color);
BackgroundSections[remoteBackgroundSection.Index].SetColorStrength(remoteBackgroundSection.ColorStrength); BackgroundSections[remoteBackgroundSection.Index].SetColorStrength(remoteBackgroundSection.ColorStrength);
paintAmount = Math.Max(0, paintAmount + (BackgroundSections[remoteBackgroundSection.Index].ColorStrength - prevColorStrength) / BackgroundSections.Count);
} }
remoteBackgroundSections.Clear(); remoteBackgroundSections.Clear();
@@ -1,3 +1,4 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
@@ -6,6 +7,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
namespace Barotrauma.Lights namespace Barotrauma.Lights
{ {
class LightSourceParams : ISerializableEntity class LightSourceParams : ISerializableEntity
@@ -331,16 +333,42 @@ namespace Barotrauma.Lights
public bool Enabled = true; 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) : this(Vector2.Zero, 100.0f, Color.White, null)
{ {
lightSourceParams = new LightSourceParams(element); lightSourceParams = new LightSourceParams(element);
CastShadows = element.GetAttributeBool("castshadows", true); 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) if (lightSourceParams.DeformableLightSpriteElement != null)
{ {
DeformableLightSprite = new DeformableSprite(lightSourceParams.DeformableLightSpriteElement, invert: true); 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) public LightSource(LightSourceParams lightSourceParams)
@@ -363,7 +391,8 @@ namespace Barotrauma.Lights
CastShadows = true; CastShadows = true;
texture = LightTexture; texture = LightTexture;
diffToSub = new Dictionary<Submarine, Vector2>(); diffToSub = new Dictionary<Submarine, Vector2>();
if (addLight) GameMain.LightManager.AddLight(this); if (addLight) { GameMain.LightManager.AddLight(this); }
} }
/// <summary> /// <summary>
@@ -1129,7 +1158,20 @@ namespace Barotrauma.Lights
GUI.DrawLine(spriteBatch, drawPos - new Vector2(1.0f, -1.0f) * Range, drawPos + new Vector2(1.0f, -1.0f) * 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) public void DrawLightVolume(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform)
@@ -75,6 +75,17 @@ namespace Barotrauma
{ {
Character.Controlled.SelectedConstruction.AddToGUIUpdateList(); 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(); if (GameMain.GameSession != null) GameMain.GameSession.AddToGUIUpdateList();
@@ -879,7 +879,8 @@ namespace Barotrauma
ChildServerRelay.Start(processInfo); ChildServerRelay.Start(processInfo);
Thread.Sleep(1000); //wait until the server is ready before connecting 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) catch (Exception e)
{ {
@@ -2409,7 +2409,6 @@ namespace Barotrauma
int min = Math.Min(6, AutoSaveInfo.Root.Elements().Count()); 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) 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"), ToolTip = TextManager.Get("LoadAutoSaveTooltip"),
UserData = "loadautosave", UserData = "loadautosave",
OnSelected = (button, o) => OnSelected = (button, o) =>
@@ -418,7 +418,7 @@ namespace Barotrauma
private static void UpdateWaterAmbience(float ambienceVolume, float deltaTime) 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 //how fast the sub is moving, scaled to 0.0 -> 1.0
float movementSoundVolume = 0.0f; float movementSoundVolume = 0.0f;
@@ -426,6 +426,7 @@ namespace Barotrauma
float insideSubFactor = 0.0f; float insideSubFactor = 0.0f;
foreach (Submarine sub in Submarine.Loaded) 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; float movementFactor = (sub.Velocity == Vector2.Zero) ? 0.0f : sub.Velocity.Length() / 10.0f;
movementFactor = MathHelper.Clamp(movementFactor, 0.0f, 1.0f); movementFactor = MathHelper.Clamp(movementFactor, 0.0f, 1.0f);
@@ -99,6 +99,7 @@ namespace Barotrauma
{ {
Sprite = new Sprite(element, path, file, lazyLoad: lazyLoad); Sprite = new Sprite(element, path, file, lazyLoad: lazyLoad);
SerializableProperties = SerializableProperty.DeserializeProperties(this, element); SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
// TODO: what's the purpose of this?
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
{ {
List<PropertyConditional> conditionalList = null; List<PropertyConditional> conditionalList = null;
@@ -105,6 +105,10 @@ namespace Barotrauma
if (soundChannel != null) { soundChannel.Looping = loopSound; } if (soundChannel != null) { soundChannel.Looping = loopSound; }
} }
} }
else
{
soundChannel.Position = new Vector3(worldPosition, 0.0f);
}
if (soundChannel != null && soundChannel.Looping) if (soundChannel != null && soundChannel.Looping)
{ {
@@ -138,6 +138,7 @@ namespace Barotrauma
public static Color GradientLerp(float t, params Color[] gradient) 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"); System.Diagnostics.Debug.Assert(gradient.Length > 0, "Empty color array passed to the GradientLerp method");
if (gradient.Length == 0) if (gradient.Length == 0)
{ {
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.10.600.0</Version> <Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.10.600.0</Version> <Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.10.600.0</Version> <Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.10.600.0</Version> <Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.10.600.0</Version> <Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -15,7 +15,7 @@ namespace Barotrauma
msg.Write((byte)monsters.Count); msg.Write((byte)monsters.Count);
foreach (Character monster in monsters) foreach (Character monster in monsters)
{ {
monster.WriteSpawnData(msg, monster.ID, restrictMessageSize: false); monster.WriteSpawnData(msg, monster.OriginalID, restrictMessageSize: false);
} }
} }
} }
@@ -131,7 +131,7 @@ namespace Barotrauma
case NetEntityEvent.Type.ChangeProperty: case NetEntityEvent.Type.ChangeProperty:
try try
{ {
WritePropertyChange(msg, extraData, false); WritePropertyChange(msg, extraData, inGameEditableOnly: !GameMain.NetworkMember.IsServer);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -222,7 +222,7 @@ namespace Barotrauma
break; break;
case NetEntityEvent.Type.ChangeProperty: case NetEntityEvent.Type.ChangeProperty:
ReadPropertyChange(msg, true, c); ReadPropertyChange(msg, inGameEditableOnly: GameMain.NetworkMember.IsServer, sender: c);
break; break;
case NetEntityEvent.Type.Combine: case NetEntityEvent.Type.Combine:
UInt16 combineTargetID = msg.ReadUInt16(); UInt16 combineTargetID = msg.ReadUInt16();
@@ -367,7 +367,7 @@ namespace Barotrauma
public void CreateServerEvent<T>(T ic) where T : ItemComponent, IServerSerializable public void CreateServerEvent<T>(T ic) where T : ItemComponent, IServerSerializable
{ {
if (GameMain.Server == null) return; if (GameMain.Server == null) { return; }
if (!ItemList.Contains(this)) if (!ItemList.Contains(this))
{ {
@@ -378,12 +378,31 @@ namespace Barotrauma
} }
int index = components.IndexOf(ic); int index = components.IndexOf(ic);
if (index == -1) return; if (index == -1) { return; }
object[] extraData = new object[] { NetEntityEvent.Type.ComponentState, index }; object[] extraData = new object[] { NetEntityEvent.Type.ComponentState, index };
ic.ServerAppendExtraData(ref extraData); ic.ServerAppendExtraData(ref extraData);
GameMain.Server.CreateEntityEvent(this, 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);
}
} }
} }
@@ -119,7 +119,11 @@ namespace Barotrauma.Networking
if (type == ChatMessageType.Order) if (type == ChatMessageType.Order)
{ {
if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead) { return; } 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 ? var order = orderTargetPosition == null ?
new Order(orderMsg.Order.Prefab, orderTargetEntity, orderMsg.Order.Prefab?.GetTargetItemComponent(orderTargetEntity as Item), orderMsg.Sender) : new Order(orderMsg.Order.Prefab, orderTargetEntity, orderMsg.Order.Prefab?.GetTargetItemComponent(orderTargetEntity as Item), orderMsg.Sender) :
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.10.600.0</Version> <Version>0.10.601.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -52,6 +52,8 @@
<Item file="Content/Items/Legacy/legacypump.xml" /> <Item file="Content/Items/Legacy/legacypump.xml" />
<Item file="Content/Items/Legacy/legacyrailgun.xml" /> <Item file="Content/Items/Legacy/legacyrailgun.xml" />
<Item file="Content/Items/Legacy/legacysearchlight.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/Items/Shipwrecks/wreckeditems.xml" />
<Item file="Content/Map/Thalamus/thalamusitems.xml" /> <Item file="Content/Map/Thalamus/thalamusitems.xml" />
<Item file="Content/Items/Gardening/growableplants.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/Moloch_m/Moloch_m.xml" />
<Character file="Content/Characters/Molochblack/Molochblack.xml" /> <Character file="Content/Characters/Molochblack/Molochblack.xml" />
<Character file="Content/Characters/Molochbaby/Molochbaby.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/Tigerthresher/Tigerthresher.xml" />
<Character file="Content/Characters/Bonethresher/Bonethresher.xml" /> <Character file="Content/Characters/Bonethresher/Bonethresher.xml" />
<Character file="Content/Characters/Leucocyte/Leucocyte.xml" /> <Character file="Content/Characters/Leucocyte/Leucocyte.xml" />
<Character file="Content/Characters/Terminalcell/Terminalcell.xml" /> <Character file="Content/Characters/Terminalcell/Terminalcell.xml" />
<Character file="Content/Characters/Watcher/Watcher.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/Dugong_Wrecked.sub" />
<Wreck file="Content/Map/Wrecks/Kastrull_Wrecked.sub" /> <Wreck file="Content/Map/Wrecks/Kastrull_Wrecked.sub" />
<Wreck file="Content/Map/Wrecks/Berilia_Wrecked.sub" /> <Wreck file="Content/Map/Wrecks/Berilia_Wrecked.sub" />
@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Barotrauma 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 abstract partial class AIController : ISteerable
{ {
@@ -99,6 +99,7 @@ namespace Barotrauma
public LatchOntoAI LatchOntoAI { get; private set; } public LatchOntoAI LatchOntoAI { get; private set; }
public SwarmBehavior SwarmBehavior { get; private set; } public SwarmBehavior SwarmBehavior { get; private set; }
public PetBehavior PetBehavior { get; private set; }
public CharacterParams.TargetParams SelectedTargetingParams { get { return selectedTargetingParams; } } public CharacterParams.TargetParams SelectedTargetingParams { get { return selectedTargetingParams; } }
@@ -151,7 +152,10 @@ namespace Barotrauma
private set private set
{ {
reverse = value; reverse = value;
FishAnimController.reverse = reverse; if (FishAnimController != null)
{
FishAnimController.reverse = reverse;
}
} }
} }
@@ -204,6 +208,9 @@ namespace Barotrauma
case "swarmbehavior": case "swarmbehavior":
SwarmBehavior = new SwarmBehavior(subElement, this); SwarmBehavior = new SwarmBehavior(subElement, this);
break; break;
case "petbehavior":
PetBehavior = new PetBehavior(subElement, this);
break;
} }
} }
@@ -221,7 +228,7 @@ namespace Barotrauma
avoidLookAheadDistance = Math.Max(colliderWidth * 3, 1.5f); 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(string targetTag) => AIParams.GetTarget(targetTag, false);
private CharacterParams.TargetParams GetTargetParams(AITarget aiTarget) => GetTargetParams(GetTargetingTag(aiTarget)); private CharacterParams.TargetParams GetTargetParams(AITarget aiTarget) => GetTargetParams(GetTargetingTag(aiTarget));
private string GetTargetingTag(AITarget aiTarget) private string GetTargetingTag(AITarget aiTarget)
@@ -423,6 +430,9 @@ namespace Barotrauma
bool run = false; bool run = false;
switch (State) switch (State)
{ {
case AIState.Freeze:
SteeringManager.Reset();
break;
case AIState.Idle: case AIState.Idle:
UpdateIdle(deltaTime); UpdateIdle(deltaTime);
break; break;
@@ -582,6 +592,7 @@ namespace Barotrauma
if (!Character.AnimController.SimplePhysicsEnabled) if (!Character.AnimController.SimplePhysicsEnabled)
{ {
LatchOntoAI?.Update(this, deltaTime); LatchOntoAI?.Update(this, deltaTime);
PetBehavior?.Update(deltaTime);
} }
IsSteeringThroughGap = false; IsSteeringThroughGap = false;
if (SwarmBehavior != null) if (SwarmBehavior != null)
@@ -1619,7 +1630,7 @@ namespace Barotrauma
State = AIState.Idle; State = AIState.Idle;
return; return;
} }
if (SelectedAiTarget.Entity is Character target) if (SelectedAiTarget.Entity is Character || SelectedAiTarget.Entity is Item)
{ {
Limb mouthLimb = Character.AnimController.GetLimb(LimbType.Head); Limb mouthLimb = Character.AnimController.GetLimb(LimbType.Head);
if (mouthLimb == null) if (mouthLimb == null)
@@ -1629,12 +1640,34 @@ namespace Barotrauma
return; return;
} }
Vector2 mouthPos = Character.AnimController.SimplePhysicsEnabled ? SimPosition : Character.AnimController.GetMouthPosition().Value; 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; Vector2 limbDiff = attackSimPosition - mouthPos;
float extent = Math.Max(mouthLimb.body.GetMaxExtent(), 2); float extent = Math.Max(mouthLimb.body.GetMaxExtent(), 2);
if (limbDiff.LengthSquared() < extent * extent) 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); steeringManager.SteeringManual(deltaTime, Vector2.Normalize(limbDiff) * 3);
Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos); Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos);
} }
@@ -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() private void UpdateSpeaking()
{ {
if (Character.Oxygen < 20.0f) if (Character.Oxygen < 20.0f)
@@ -66,6 +66,7 @@ namespace Barotrauma
//if (rootContainer != null) { return false; } //if (rootContainer != null) { return false; }
var pickable = item.GetComponent<Pickable>(); var pickable = item.GetComponent<Pickable>();
if (pickable == null) { return false; } if (pickable == null) { return false; }
if (pickable is Holdable h && h.Attachable && h.Attached) { return false; }
var wire = item.GetComponent<Wire>(); var wire = item.GetComponent<Wire>();
if (wire != null) if (wire != null)
{ {
@@ -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);
}
}
}
}
@@ -381,6 +381,12 @@ namespace Barotrauma
{ {
//only one limb left, the character is now full eaten //only one limb left, the character is now full eaten
Entity.Spawner?.AddToRemoveQueue(target); Entity.Spawner?.AddToRemoveQueue(target);
if (Character.AIController is EnemyAIController enemyAi)
{
enemyAi.PetBehavior?.OnEat("dead", 1.0f);
}
character.SelectedCharacter = null; character.SelectedCharacter = null;
} }
else //sever a random joint else //sever a random joint
@@ -753,6 +753,7 @@ namespace Barotrauma
foreach (Limb limb in Limbs) foreach (Limb limb in Limbs)
{ {
if (connectedLimbs.Contains(limb)) { continue; } if (connectedLimbs.Contains(limb)) { continue; }
if (!character.IsDead && !limb.CanBeSeveredAlive) { continue; }
limb.IsSevered = true; limb.IsSevered = true;
if (limb.type == LimbType.RightHand) if (limb.type == LimbType.RightHand)
{ {
@@ -215,9 +215,6 @@ namespace Barotrauma
public bool IsDismissed => Info != null && Info.IsDismissed; public bool IsDismissed => Info != null && Info.IsDismissed;
private readonly List<StatusEffect> statusEffects = new List<StatusEffect>(); 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 public Entity ViewTarget
{ {
@@ -274,6 +271,11 @@ namespace Barotrauma
{ {
get get
{ {
if (IsPet)
{
return (AIController as EnemyAIController).PetBehavior.GetName();
}
if (info != null && !string.IsNullOrWhiteSpace(info.Name)) { return info.Name; } if (info != null && !string.IsNullOrWhiteSpace(info.Name)) { return info.Name; }
var displayName = Params.DisplayName; var displayName = Params.DisplayName;
if (string.IsNullOrWhiteSpace(displayName)) if (string.IsNullOrWhiteSpace(displayName))
@@ -473,6 +475,11 @@ namespace Barotrauma
get { return CharacterHealth.IsUnconscious; } get { return CharacterHealth.IsUnconscious; }
} }
public bool IsPet
{
get { return AIController is EnemyAIController enemyController && enemyController.PetBehavior != null; }
}
public float Oxygen public float Oxygen
{ {
get { return CharacterHealth.OxygenAmount; } get { return CharacterHealth.OxygenAmount; }
@@ -638,7 +645,7 @@ namespace Barotrauma
{ {
if (!canBeDragged) { return false; } if (!canBeDragged) { return false; }
if (Removed || !AnimController.Draggable) { 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; } set { canBeDragged = value; }
} }
@@ -1218,10 +1225,13 @@ namespace Barotrauma
return targetMovement; return targetMovement;
} }
private float greatestNegativeSpeedMultiplier = 1f;
private float greatestPositiveSpeedMultiplier = 1f;
/// <summary> /// <summary>
/// Can be used to modify the character's speed via StatusEffects /// Can be used to modify the character's speed via StatusEffects
/// </summary> /// </summary>
public float SpeedMultiplier { get; private set; } public float SpeedMultiplier { get; private set; } = 1;
public void StackSpeedMultiplier(float val) public void StackSpeedMultiplier(float val)
{ {
@@ -1247,6 +1257,40 @@ namespace Barotrauma
greatestNegativeSpeedMultiplier = 1f; 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> /// <summary>
/// Speed reduction from the current limb specific damage. Min 0, max 1. /// Speed reduction from the current limb specific damage. Min 0, max 1.
/// </summary> /// </summary>
@@ -2132,6 +2176,10 @@ namespace Barotrauma
{ {
SelectCharacter(FocusedCharacter); 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)) else if (FocusedCharacter != null && IsKeyHit(InputType.Health) && FocusedCharacter.CharacterHealth.UseHealthWindow && CanInteract && CanInteractWith(FocusedCharacter, 160f, false))
{ {
if (FocusedCharacter == SelectedCharacter) if (FocusedCharacter == SelectedCharacter)
@@ -2369,6 +2417,7 @@ namespace Barotrauma
} }
ApplyStatusEffects(AnimController.InWater ? ActionType.InWater : ActionType.NotInWater, deltaTime); ApplyStatusEffects(AnimController.InWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
ApplyStatusEffects(ActionType.OnActive, deltaTime);
UpdateControlled(deltaTime, cam); UpdateControlled(deltaTime, cam);
@@ -2377,6 +2426,8 @@ namespace Barotrauma
{ {
UpdateOxygen(deltaTime); UpdateOxygen(deltaTime);
} }
CalculateHealthMultiplier();
CharacterHealth.Update(deltaTime); CharacterHealth.Update(deltaTime);
if (IsIncapacitated) if (IsIncapacitated)
@@ -141,17 +141,17 @@ namespace Barotrauma
{ {
get get
{ {
float max = maxVitality;
if (Character?.Info?.Job?.Prefab != null) 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 set
{ {
maxVitality = Math.Max(0, value); maxVitality = Math.Max(0, value);
} }
} }
public float MinVitality public float MinVitality
@@ -450,9 +450,13 @@ namespace Barotrauma
matchingAfflictions.AddRange(limbHealth.Afflictions); matchingAfflictions.AddRange(limbHealth.Afflictions);
} }
} }
matchingAfflictions.RemoveAll(a =>
!a.Prefab.Identifier.Equals(affliction, StringComparison.OrdinalIgnoreCase) && if (!string.IsNullOrEmpty(affliction))
!a.Prefab.AfflictionType.Equals(affliction, StringComparison.OrdinalIgnoreCase)); {
matchingAfflictions.RemoveAll(a =>
!a.Prefab.Identifier.Equals(affliction, StringComparison.OrdinalIgnoreCase) &&
!a.Prefab.AfflictionType.Equals(affliction, StringComparison.OrdinalIgnoreCase));
}
if (matchingAfflictions.Count == 0) return; if (matchingAfflictions.Count == 0) return;
@@ -617,8 +621,8 @@ namespace Barotrauma
private void AddAffliction(Affliction newAffliction) private void AddAffliction(Affliction newAffliction)
{ {
if (!DoesBleed && newAffliction is AfflictionBleeding) return; if (!DoesBleed && newAffliction is AfflictionBleeding) { return; }
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) return; if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) { return; }
if (newAffliction.Prefab.AfflictionType == "huskinfection") if (newAffliction.Prefab.AfflictionType == "huskinfection")
{ {
var huskPrefab = newAffliction.Prefab as AfflictionPrefabHusk; var huskPrefab = newAffliction.Prefab as AfflictionPrefabHusk;
@@ -636,7 +640,10 @@ namespace Barotrauma
affliction.Strength = newStrength; affliction.Strength = newStrength;
affliction.Source = newAffliction.Source; affliction.Source = newAffliction.Source;
CalculateVitality(); CalculateVitality();
if (Vitality <= MinVitality) Kill(); if (Vitality <= MinVitality)
{
Kill();
}
return; return;
} }
} }
@@ -650,7 +657,10 @@ namespace Barotrauma
Character.HealthUpdateInterval = 0.0f; Character.HealthUpdateInterval = 0.0f;
CalculateVitality(); CalculateVitality();
if (Vitality <= MinVitality) Kill(); if (Vitality <= MinVitality)
{
Kill();
}
} }
@@ -707,7 +717,11 @@ namespace Barotrauma
UpdateLimbAfflictionOverlays(); UpdateLimbAfflictionOverlays();
CalculateVitality(); CalculateVitality();
if (Vitality <= MinVitality) Kill();
if (Vitality <= MinVitality)
{
Kill();
}
} }
private void UpdateOxygen(float deltaTime) private void UpdateOxygen(float deltaTime)
@@ -163,7 +163,13 @@ namespace Barotrauma
{ {
item.AddTag("job:" + job.Name); 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>()) foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
{ {
wifiComponent.TeamID = character.TeamID; wifiComponent.TeamID = character.TeamID;
@@ -64,7 +64,7 @@ namespace Barotrauma
} }
} }
Color = new Color(element.GetAttributeVector4("color", Vector4.One)); Color = element.GetAttributeColor("color", Color.White);
LifeTime = element.GetAttributeFloat("lifetime", 10.0f); LifeTime = element.GetAttributeFloat("lifetime", 10.0f);
FadeOutTime = Math.Min(LifeTime, element.GetAttributeFloat("fadeouttime", 1.0f)); FadeOutTime = Math.Min(LifeTime, element.GetAttributeFloat("fadeouttime", 1.0f));
@@ -120,6 +120,7 @@ namespace Barotrauma
AddChildEvents(initialEventSet); AddChildEvents(initialEventSet);
void AddChildEvents(EventSet eventSet) void AddChildEvents(EventSet eventSet)
{ {
if (eventSet == null) { return; }
foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.First)) foreach (EventPrefab ep in eventSet.EventPrefabs.Select(e => e.First))
{ {
if (!level.LevelData.NonRepeatableEvents.Contains(ep)) if (!level.LevelData.NonRepeatableEvents.Contains(ep))
@@ -717,11 +717,10 @@ namespace Barotrauma.Items.Components
bool leakFixed = (leak.Open <= 0.0f || leak.Removed) && bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
(leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1); (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)) 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); character.Speak(TextManager.GetWithVariable("DialogLeaksFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leaksfixed", 10.0f);
} }
else else
@@ -146,7 +146,7 @@ namespace Barotrauma.Items.Components
public bool DrawHudWhenEquipped public bool DrawHudWhenEquipped
{ {
get; get;
private set; protected set;
} }
[Serialize(false, false, description: "Can the item be selected by interacting with it.")] [Serialize(false, false, description: "Can the item be selected by interacting with it.")]
@@ -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;
}
}
}
@@ -1,10 +1,11 @@
using System.Xml.Linq; using Barotrauma.Networking;
using System.Xml.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
partial class Terminal : ItemComponent partial class Terminal : ItemComponent
{ {
private const int MaxMessageLength = 150; private const int MaxMessageLength = ChatMessage.MaxLength;
public string DisplayedWelcomeMessage public string DisplayedWelcomeMessage
{ {
@@ -304,13 +304,16 @@ namespace Barotrauma
for (int j = 0; j < otherInventory.capacity; j++) 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++) 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; bool swapSuccessful = false;
if (otherIsEquipped) if (otherIsEquipped)
{ {
@@ -119,6 +119,8 @@ namespace Barotrauma
} }
} }
public bool EditableWhenEquipped { get; set; } = false;
//the inventory in which the item is contained in //the inventory in which the item is contained in
public Inventory ParentInventory public Inventory ParentInventory
{ {
@@ -2257,7 +2259,7 @@ namespace Barotrauma
var propertyOwner = allProperties.Find(p => p.Second == property); var propertyOwner = allProperties.Find(p => p.Second == property);
if (allProperties.Count > 1) 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); object value = property.GetValue(propertyOwner.First);
@@ -2339,7 +2341,7 @@ namespace Barotrauma
int propertyIndex = 0; int propertyIndex = 0;
if (allProperties.Count > 1) if (allProperties.Count > 1)
{ {
propertyIndex = msg.ReadRangedInteger(0, allProperties.Count - 1); propertyIndex = msg.ReadByte();
} }
bool allowEditing = true; bool allowEditing = true;
@@ -2360,6 +2362,7 @@ namespace Barotrauma
if (type == typeof(string)) if (type == typeof(string))
{ {
string val = msg.ReadString(); string val = msg.ReadString();
logValue = val;
if (allowEditing) if (allowEditing)
{ {
property.TrySetValue(parentObject, val); property.TrySetValue(parentObject, val);
@@ -73,13 +73,13 @@ namespace Barotrauma
{ {
get; get;
set; set;
} } = true;
public bool DamagesCharacters public bool DamagesCharacters
{ {
get; get;
set; set;
} } = true;
public bool Removed public bool Removed
{ {
@@ -66,7 +66,7 @@ namespace Barotrauma
PerlinNoise.GetPerlin(Rect.X / 1000.0f, Rect.Y / 1000.0f), PerlinNoise.GetPerlin(Rect.X / 1000.0f, Rect.Y / 1000.0f),
PerlinNoise.GetPerlin(Rect.Y / 1000.0f + 0.5f, Rect.X / 1000.0f + 0.5f)); 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) public bool SetColor(Color color)
@@ -480,6 +480,9 @@ namespace Barotrauma
case "IsDead": case "IsDead":
{ if (parentObject is Character character) { return character.IsDead; } } { if (parentObject is Character character) { return character.IsDead; } }
break; break;
case "IsHuman":
{ if (parentObject is Character character) { return character.IsHuman; } }
break;
case "IsOn": case "IsOn":
{ if (parentObject is LightComponent lightComponent) { return lightComponent.IsOn; } } { if (parentObject is LightComponent lightComponent) { return lightComponent.IsOn; } }
break; break;
@@ -543,6 +546,9 @@ namespace Barotrauma
case "SpeedMultiplier": case "SpeedMultiplier":
{ if (parentObject is Character character && value is float) { character.StackSpeedMultiplier((float)value); return true; } } { if (parentObject is Character character && value is float) { character.StackSpeedMultiplier((float)value); return true; } }
break; break;
case "HealthMultiplier":
{ if (parentObject is Character character && value is float) { character.StackHealthMultiplier((float)value); return true; } }
break;
case "IsOn": case "IsOn":
{ if (parentObject is LightComponent lightComponent && value is bool) { lightComponent.IsOn = (bool)value; return true; } } { if (parentObject is LightComponent lightComponent && value is bool) { lightComponent.IsOn = (bool)value; return true; } }
break; break;
@@ -8,14 +8,7 @@ namespace Barotrauma
partial class ConditionalSprite partial class ConditionalSprite
{ {
public readonly List<PropertyConditional> conditionals = new List<PropertyConditional>(); public readonly List<PropertyConditional> conditionals = new List<PropertyConditional>();
public bool IsActive public bool IsActive { get; private set; } = true;
{
get
{
if (Target == null) { return false; }
return Comparison == PropertyConditional.Comparison.And ? conditionals.All(c => c.Matches(Target)) : conditionals.Any(c => c.Matches(Target));
}
}
public readonly PropertyConditional.Comparison Comparison; public readonly PropertyConditional.Comparison Comparison;
public readonly bool Exclusive; 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));
}
}
} }
} }
@@ -688,15 +688,7 @@ namespace Barotrauma
if (targetIdentifiers != null && currentTargets.Count == 0) { return; } if (targetIdentifiers != null && currentTargets.Count == 0) { return; }
if (!HasRequiredItems(entity)) { return; } if (!HasRequiredItems(entity) || !HasRequiredConditions(currentTargets)) { 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 (duration > 0.0f && !Stackable) 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, Entity.Spawner.AddToSpawnQueue(characterSpawnInfo.SpeciesName, position + Rand.Vector(characterSpawnInfo.Spread, Rand.RandSync.Server) + characterSpawnInfo.Offset,
onSpawn: newCharacter => 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); characters.Add(newCharacter);
if (characters.Count == characterSpawnInfo.Count) if (characters.Count == characterSpawnInfo.Count)
{ {
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+31
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) v0.10.600.0 (Unstable)
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------