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