Build 0.18.0.0
This commit is contained in:
@@ -400,7 +400,7 @@ namespace Barotrauma
|
||||
|
||||
partial void UpdateControlled(float deltaTime, Camera cam)
|
||||
{
|
||||
if (controlled != this) return;
|
||||
if (controlled != this) { return; }
|
||||
|
||||
ControlLocalPlayer(deltaTime, cam);
|
||||
|
||||
|
||||
@@ -2008,8 +2008,27 @@ namespace Barotrauma
|
||||
DisplayedVitality = Vitality;
|
||||
}
|
||||
|
||||
partial void UpdateSkinTint()
|
||||
{
|
||||
if (!Character.IsVisible) { return; }
|
||||
FaceTint = DefaultFaceTint;
|
||||
BodyTint = Color.TransparentBlack;
|
||||
|
||||
if (!(Character?.Params?.Health.ApplyAfflictionColors ?? false)) { return; }
|
||||
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
{
|
||||
var affliction = kvp.Key;
|
||||
Color faceTint = affliction.GetFaceTint();
|
||||
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||
Color bodyTint = affliction.GetBodyTint();
|
||||
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateLimbAfflictionOverlays()
|
||||
{
|
||||
if (!Character.IsVisible) { return; }
|
||||
foreach (Limb limb in Character.AnimController.Limbs)
|
||||
{
|
||||
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count) { continue; }
|
||||
|
||||
@@ -58,21 +58,19 @@ namespace Barotrauma
|
||||
|
||||
public class OutfitPreview
|
||||
{
|
||||
/// <summary>
|
||||
/// Pair.First = sprite, Pair.Second = draw offset
|
||||
/// </summary>
|
||||
public readonly List<Pair<Sprite, Vector2>> Sprites;
|
||||
public readonly List<(Sprite sprite, Vector2 drawOffset)> Sprites;
|
||||
|
||||
public Vector2 Dimensions;
|
||||
|
||||
public OutfitPreview()
|
||||
{
|
||||
Sprites = new List<Pair<Sprite, Vector2>>();
|
||||
Sprites = new List<(Sprite sprite, Vector2 drawOffset)>();
|
||||
Dimensions = Vector2.One;
|
||||
}
|
||||
|
||||
public void AddSprite(Sprite sprite, Vector2 drawOffset)
|
||||
{
|
||||
Sprites.Add(new Pair<Sprite, Vector2>(sprite, drawOffset));
|
||||
Sprites.Add((sprite, drawOffset));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -224,14 +224,14 @@ namespace Barotrauma
|
||||
public float DamageOverlayStrength
|
||||
{
|
||||
get { return damageOverlayStrength; }
|
||||
set { damageOverlayStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
set { damageOverlayStrength = MathHelper.Clamp(value, 0.0f, 1.0f); }
|
||||
}
|
||||
|
||||
private float burnOverLayStrength;
|
||||
public float BurnOverlayStrength
|
||||
{
|
||||
get { return burnOverLayStrength; }
|
||||
set { burnOverLayStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
set { burnOverLayStrength = MathHelper.Clamp(value, 0.0f, 1.0f); }
|
||||
}
|
||||
|
||||
public string HitSoundTag => Params?.Sound?.Tag;
|
||||
@@ -279,7 +279,7 @@ namespace Barotrauma
|
||||
for (int i = 0; i < Params.decorativeSpriteParams.Count; i++)
|
||||
{
|
||||
var param = Params.decorativeSpriteParams[i];
|
||||
var decorativeSprite = new DecorativeSprite(param.Element, file: GetSpritePath(param.Element, param));
|
||||
var decorativeSprite = new DecorativeSprite(param.Element, file: GetSpritePath(param.Element, param, ref _texturePath));
|
||||
DecorativeSprites.Add(decorativeSprite);
|
||||
int groupID = decorativeSprite.RandomGroupID;
|
||||
if (!DecorativeSpriteGroups.ContainsKey(groupID))
|
||||
@@ -295,13 +295,13 @@ namespace Barotrauma
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "sprite":
|
||||
Sprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.normalSpriteParams));
|
||||
Sprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.normalSpriteParams, ref _texturePath));
|
||||
break;
|
||||
case "damagedsprite":
|
||||
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams));
|
||||
case "damagedsprite":
|
||||
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams, ref _damagedTexturePath));
|
||||
break;
|
||||
case "conditionalsprite":
|
||||
var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(), file: GetSpritePath(subElement, null));
|
||||
var conditionalSprite = new ConditionalSprite(subElement, GetConditionalTarget(), file: GetSpritePath(subElement, null, ref _texturePath));
|
||||
ConditionalSprites.Add(conditionalSprite);
|
||||
if (conditionalSprite.DeformableSprite != null)
|
||||
{
|
||||
@@ -311,7 +311,7 @@ namespace Barotrauma
|
||||
}
|
||||
break;
|
||||
case "deformablesprite":
|
||||
_deformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement, Params.deformSpriteParams));
|
||||
_deformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement, Params.deformSpriteParams, ref _texturePath));
|
||||
var deformations = CreateDeformations(subElement);
|
||||
Deformations.AddRange(deformations);
|
||||
NonConditionalDeformations.AddRange(deformations);
|
||||
@@ -435,33 +435,33 @@ namespace Barotrauma
|
||||
{
|
||||
Sprite.Remove();
|
||||
var source = Sprite.SourceElement;
|
||||
Sprite = new Sprite(source, file: GetSpritePath(source, Params.normalSpriteParams));
|
||||
Sprite = new Sprite(source, file: GetSpritePath(source, Params.normalSpriteParams, ref _texturePath));
|
||||
}
|
||||
if (_deformSprite != null)
|
||||
{
|
||||
_deformSprite.Remove();
|
||||
var source = _deformSprite.Sprite.SourceElement;
|
||||
_deformSprite = new DeformableSprite(source, filePath: GetSpritePath(source, Params.deformSpriteParams));
|
||||
_deformSprite = new DeformableSprite(source, filePath: GetSpritePath(source, Params.deformSpriteParams, ref _texturePath));
|
||||
}
|
||||
if (DamagedSprite != null)
|
||||
{
|
||||
DamagedSprite.Remove();
|
||||
var source = DamagedSprite.SourceElement;
|
||||
DamagedSprite = new Sprite(source, file: GetSpritePath(source, Params.damagedSpriteParams));
|
||||
DamagedSprite = new Sprite(source, file: GetSpritePath(source, Params.damagedSpriteParams, ref _damagedTexturePath));
|
||||
}
|
||||
for (int i = 0; i < ConditionalSprites.Count; i++)
|
||||
{
|
||||
var conditionalSprite = ConditionalSprites[i];
|
||||
var source = conditionalSprite.ActiveSprite.SourceElement;
|
||||
conditionalSprite.Remove();
|
||||
ConditionalSprites[i] = new ConditionalSprite(source, character, file: GetSpritePath(source, null));
|
||||
ConditionalSprites[i] = new ConditionalSprite(source, character, file: GetSpritePath(source, null, ref _texturePath));
|
||||
}
|
||||
for (int i = 0; i < DecorativeSprites.Count; i++)
|
||||
{
|
||||
var decorativeSprite = DecorativeSprites[i];
|
||||
decorativeSprite.Remove();
|
||||
var source = decorativeSprite.Sprite.SourceElement;
|
||||
DecorativeSprites[i] = new DecorativeSprite(source, file: GetSpritePath(source, Params.decorativeSpriteParams[i]));
|
||||
DecorativeSprites[i] = new DecorativeSprite(source, file: GetSpritePath(source, Params.decorativeSpriteParams[i], ref _texturePath));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,16 +472,17 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private string _texturePath;
|
||||
private string GetSpritePath(ContentXElement element, SpriteParams spriteParams)
|
||||
private string _damagedTexturePath;
|
||||
private string GetSpritePath(ContentXElement element, SpriteParams spriteParams, ref string path)
|
||||
{
|
||||
if (_texturePath == null)
|
||||
if (path == null)
|
||||
{
|
||||
if (spriteParams != null)
|
||||
{
|
||||
ContentPath texturePath =
|
||||
character.Params.VariantFile?.Root?.GetAttributeContentPath("texture", character.Prefab.ContentPackage)
|
||||
?? ContentPath.FromRaw(character.Prefab.ContentPackage, spriteParams.GetTexturePath());
|
||||
_texturePath = GetSpritePath(texturePath);
|
||||
path = GetSpritePath(texturePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -489,10 +490,10 @@ namespace Barotrauma
|
||||
texturePath = texturePath.IsNullOrWhiteSpace()
|
||||
? ContentPath.FromRaw(character.Prefab.ContentPackage, ragdoll.RagdollParams.Texture)
|
||||
: texturePath;
|
||||
_texturePath = GetSpritePath(texturePath);
|
||||
path = GetSpritePath(texturePath);
|
||||
}
|
||||
}
|
||||
return _texturePath;
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -625,12 +626,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (!body.Enabled) { return; }
|
||||
|
||||
if (!IsDead)
|
||||
{
|
||||
DamageOverlayStrength -= deltaTime;
|
||||
BurnOverlayStrength -= deltaTime;
|
||||
}
|
||||
else
|
||||
if (IsDead)
|
||||
{
|
||||
var spriteParams = Params.GetSprite();
|
||||
if (spriteParams != null && spriteParams.DeadColorTime > 0 && deadTimer < spriteParams.DeadColorTime)
|
||||
@@ -688,7 +684,7 @@ namespace Barotrauma
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null, bool disableDeformations = false)
|
||||
{
|
||||
float brightness = 1.0f - (burnOverLayStrength / 100.0f) * 0.5f;
|
||||
float brightness = Math.Max(1.0f - burnOverLayStrength, 0.2f);
|
||||
var spriteParams = Params.GetSprite();
|
||||
if (spriteParams == null) { return; }
|
||||
|
||||
@@ -831,32 +827,6 @@ namespace Barotrauma
|
||||
{
|
||||
LightSource.LightSpriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
}
|
||||
if (damageOverlayStrength > 0.0f && DamagedSprite != null && !hideLimb)
|
||||
{
|
||||
DamagedSprite.Draw(spriteBatch,
|
||||
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
|
||||
color * Math.Min(damageOverlayStrength, 1.0f), activeSprite.Origin,
|
||||
-body.DrawRotation,
|
||||
Scale, spriteEffect, activeSprite.Depth - (depthStep * 90));
|
||||
}
|
||||
foreach (var decorativeSprite in DecorativeSprites)
|
||||
{
|
||||
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
||||
Color c = new Color(decorativeSprite.Color.R / 255f * brightness, decorativeSprite.Color.G / 255f * brightness, decorativeSprite.Color.B / 255f * brightness, decorativeSprite.Color.A / 255f);
|
||||
if (deadTimer > 0)
|
||||
{
|
||||
c = Color.Lerp(c, spriteParams.DeadColor, MathUtils.InverseLerp(0, Params.GetSprite().DeadColorTime, deadTimer));
|
||||
}
|
||||
c = overrideColor ?? c;
|
||||
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
|
||||
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier) * Scale;
|
||||
var ca = (float)Math.Cos(-body.Rotation);
|
||||
var sa = (float)Math.Sin(-body.Rotation);
|
||||
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
|
||||
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), c,
|
||||
-body.Rotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffect,
|
||||
depth: activeSprite.Depth - (depthStep * 100));
|
||||
}
|
||||
float step = depthStep;
|
||||
WearableSprite onlyDrawable = wearingItems.Find(w => w.HideOtherWearables);
|
||||
if (Params.MirrorHorizontally)
|
||||
@@ -925,6 +895,36 @@ namespace Barotrauma
|
||||
//if there are multiple sprites on this limb, make the successive ones be drawn in front
|
||||
depthStep += step;
|
||||
}
|
||||
if (!Hide && onlyDrawable == null)
|
||||
{
|
||||
foreach (var decorativeSprite in DecorativeSprites)
|
||||
{
|
||||
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
||||
Color c = new Color(decorativeSprite.Color.R / 255f * brightness, decorativeSprite.Color.G / 255f * brightness, decorativeSprite.Color.B / 255f * brightness, decorativeSprite.Color.A / 255f);
|
||||
if (deadTimer > 0)
|
||||
{
|
||||
c = Color.Lerp(c, spriteParams.DeadColor, MathUtils.InverseLerp(0, Params.GetSprite().DeadColorTime, deadTimer));
|
||||
}
|
||||
c = overrideColor ?? c;
|
||||
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
|
||||
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier) * Scale;
|
||||
var ca = (float)Math.Cos(-body.Rotation);
|
||||
var sa = (float)Math.Sin(-body.Rotation);
|
||||
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
|
||||
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), c,
|
||||
-body.Rotation + rotation, decorativeSprite.GetScale(spriteAnimState[decorativeSprite].RandomScaleFactor) * Scale, spriteEffect,
|
||||
depth: activeSprite.Depth - depthStep);
|
||||
depthStep += step;
|
||||
}
|
||||
if (damageOverlayStrength > 0.0f && DamagedSprite != null)
|
||||
{
|
||||
DamagedSprite.Draw(spriteBatch,
|
||||
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
|
||||
color * damageOverlayStrength, activeSprite.Origin,
|
||||
-body.DrawRotation,
|
||||
Scale, spriteEffect, activeSprite.Depth - depthStep * Math.Max(1, WearingItems.Count * 2)); // Multiply by 2 to get rid of z-fighting with some clothing combos
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
|
||||
@@ -359,7 +359,7 @@ namespace Barotrauma.Transition
|
||||
else
|
||||
{
|
||||
//copying a mod: we have a neat method for that!
|
||||
await SteamManager.Workshop.CopyDirectory(path, Path.GetFileName(path), path, destPath);
|
||||
await SteamManager.Workshop.CopyDirectory(path, Path.GetFileName(path), path, destPath, SteamManager.Workshop.ShouldCorrectPaths.Yes);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (isOpen)
|
||||
{
|
||||
frame.AddToGUIUpdateList();
|
||||
frame.AddToGUIUpdateList(order: 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1714,9 +1714,47 @@ namespace Barotrauma
|
||||
//check missing mission texts
|
||||
foreach (var missionPrefab in MissionPrefab.Prefabs)
|
||||
{
|
||||
Identifier missionId = (missionPrefab.ConfigElement.GetAttribute("textidentifier") == null ? missionPrefab.Identifier : missionPrefab.ConfigElement.GetAttributeIdentifier("textidentifier", Identifier.Empty));
|
||||
addIfMissing($"missionname.{missionId}".ToIdentifier(), language);
|
||||
addIfMissing($"missiondescription.{missionId}".ToIdentifier(), language);
|
||||
Identifier missionId = missionPrefab.ConfigElement.GetAttribute("textidentifier") == null ?
|
||||
missionPrefab.Identifier :
|
||||
missionPrefab.ConfigElement.GetAttributeIdentifier("textidentifier", Identifier.Empty);
|
||||
|
||||
if (!tags[language].Contains(missionPrefab.ConfigElement.GetAttributeIdentifier("name", Identifier.Empty)))
|
||||
{
|
||||
addIfMissing($"missionname.{missionId}".ToIdentifier(), language);
|
||||
}
|
||||
|
||||
if (missionPrefab.Type == MissionType.Combat)
|
||||
{
|
||||
addIfMissing($"MissionDescriptionNeutral.{missionId}".ToIdentifier(), language);
|
||||
addIfMissing($"MissionDescription1.{missionId}".ToIdentifier(), language);
|
||||
addIfMissing($"MissionDescription2.{missionId}".ToIdentifier(), language);
|
||||
addIfMissing($"MissionTeam1.{missionId}".ToIdentifier(), language);
|
||||
addIfMissing($"MissionTeam2.{missionId}".ToIdentifier(), language);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!tags[language].Contains(missionPrefab.ConfigElement.GetAttributeIdentifier("description", Identifier.Empty)))
|
||||
{
|
||||
addIfMissing($"missiondescription.{missionId}".ToIdentifier(), language);
|
||||
}
|
||||
if (!tags[language].Contains(missionPrefab.ConfigElement.GetAttributeIdentifier("successmessage", Identifier.Empty)))
|
||||
{
|
||||
addIfMissing($"missionsuccess.{missionId}".ToIdentifier(), language);
|
||||
}
|
||||
//only check failure message if there's something defined in the xml (otherwise we just use the generic "missionfailed" text)
|
||||
if (missionPrefab.ConfigElement.GetAttribute("failuremessage") != null &&
|
||||
!tags[language].Contains(missionPrefab.ConfigElement.GetAttributeIdentifier("failuremessage", Identifier.Empty)))
|
||||
{
|
||||
addIfMissing($"missionfailure.{missionId}".ToIdentifier(), language);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i<missionPrefab.Messages.Length; i++)
|
||||
{
|
||||
if (missionPrefab.Messages[i].IsNullOrWhiteSpace())
|
||||
{
|
||||
addIfMissing($"MissionMessage{i}.{missionId}".ToIdentifier(), language);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Type itemComponentType in typeof(ItemComponent).Assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(ItemComponent))))
|
||||
@@ -2503,7 +2541,7 @@ namespace Barotrauma
|
||||
var entity = MapEntity.mapEntityList[i] as ISerializableEntity;
|
||||
if (entity != null)
|
||||
{
|
||||
List<Pair<object, SerializableProperty>> allProperties = new List<Pair<object, SerializableProperty>>();
|
||||
List<(object obj, SerializableProperty property)> allProperties = new List<(object obj, SerializableProperty property)>();
|
||||
|
||||
if (entity is Item item)
|
||||
{
|
||||
@@ -2518,14 +2556,14 @@ namespace Barotrauma
|
||||
|
||||
for (int k = 0; k < properties.Count; k++)
|
||||
{
|
||||
allProperties.Add(new Pair<object, SerializableProperty>(entity, properties[k]));
|
||||
allProperties.Add((entity, properties[k]));
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < allProperties.Count; j++)
|
||||
{
|
||||
var property = allProperties[j].Second;
|
||||
string propertyName = (allProperties[j].First.GetType().Name + "." + property.PropertyInfo.Name).ToLowerInvariant();
|
||||
var property = allProperties[j].property;
|
||||
string propertyName = (allProperties[j].obj.GetType().Name + "." + property.PropertyInfo.Name).ToLowerInvariant();
|
||||
LocalizedString displayName = TextManager.Get($"sp.{propertyName}.name");
|
||||
if (displayName.IsNullOrEmpty())
|
||||
{
|
||||
|
||||
@@ -35,7 +35,6 @@ namespace Barotrauma
|
||||
public TextGetterHandler TextGetter;
|
||||
|
||||
public bool Wrap;
|
||||
private bool playerInput;
|
||||
|
||||
public bool RoundToNearestPixel = true;
|
||||
|
||||
@@ -287,8 +286,7 @@ namespace Barotrauma
|
||||
/// If the rectT height is set 0, the height is calculated from the text.
|
||||
/// </summary>
|
||||
public GUITextBlock(RectTransform rectT, RichString text, Color? textColor = null, GUIFont font = null,
|
||||
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null,
|
||||
bool playerInput = false)
|
||||
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null)
|
||||
: base(style, rectT)
|
||||
{
|
||||
if (color.HasValue)
|
||||
@@ -307,7 +305,6 @@ namespace Barotrauma
|
||||
this.textAlignment = textAlignment;
|
||||
this.Wrap = wrap;
|
||||
this.Text = text ?? "";
|
||||
this.playerInput = playerInput;
|
||||
if (rectT.Rect.Height == 0 && !text.IsNullOrEmpty())
|
||||
{
|
||||
CalculateHeightFromText();
|
||||
|
||||
@@ -261,7 +261,7 @@ namespace Barotrauma
|
||||
this.color = color ?? Color.White;
|
||||
frame = new GUIFrame(new RectTransform(Vector2.One, rectT, Anchor.Center), style, color);
|
||||
GUIStyle.Apply(frame, style == "" ? "GUITextBox" : style);
|
||||
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft), text ?? "", textColor, font, textAlignment, wrap, playerInput: true);
|
||||
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft), text ?? "", textColor, font, textAlignment, wrap);
|
||||
GUIStyle.Apply(textBlock, "", this);
|
||||
if (font != null) { textBlock.Font = font; }
|
||||
CaretEnabled = true;
|
||||
|
||||
@@ -657,6 +657,7 @@ namespace Barotrauma
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
GUILayoutGroup parentLayout = new GUILayoutGroup(new RectTransform(Vector2.One, backgroundFrame.RectTransform), isHorizontal: true) { Stretch = true };
|
||||
|
||||
if (!(affliction.Prefab is { } prefab)) { return; }
|
||||
@@ -676,7 +677,7 @@ namespace Barotrauma
|
||||
GUIFrame textContainer = new GUIFrame(new RectTransform(new Vector2(0.6f, 1f), textLayout.RectTransform), style: null);
|
||||
GUITextBlock afflictionName = new GUITextBlock(new RectTransform(Vector2.One, textContainer.RectTransform), name, font: GUIStyle.SubHeadingFont);
|
||||
|
||||
GUITextBlock healCost = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), textLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), textAlignment: Alignment.Center, font: GUIStyle.LargeFont)
|
||||
GUITextBlock healCost = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1f), textLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
Padding = Vector4.Zero
|
||||
};
|
||||
@@ -746,12 +747,12 @@ namespace Barotrauma
|
||||
|
||||
ClosePopup();
|
||||
|
||||
GUIFrame mainFrame = new GUIFrame(new RectTransform(new Vector2(0.28f, 0.45f), container.RectTransform)
|
||||
GUIFrame mainFrame = new GUIFrame(new RectTransform(new Vector2(0.28f, 0.5f), container.RectTransform)
|
||||
{
|
||||
ScreenSpaceOffset = location.ToPoint()
|
||||
});
|
||||
|
||||
GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), mainFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.01f, Stretch = true };
|
||||
GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), mainFrame.RectTransform, Anchor.Center)) { RelativeSpacing = 0.01f, Stretch = true };
|
||||
|
||||
if (mainFrame.Rect.Bottom > GameMain.GraphicsHeight)
|
||||
{
|
||||
@@ -819,7 +820,9 @@ namespace Barotrauma
|
||||
if (!(affliction.Prefab is { } prefab)) { return ImmutableArray<GUIComponent>.Empty; }
|
||||
|
||||
GUIFrame backgroundFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.33f), parent.RectTransform), style: "ListBoxElement");
|
||||
GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), backgroundFrame.RectTransform, Anchor.Center))
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), backgroundFrame.RectTransform, Anchor.BottomCenter), style: "HorizontalLine");
|
||||
|
||||
GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), backgroundFrame.RectTransform, Anchor.Center))
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
@@ -862,13 +865,27 @@ namespace Barotrauma
|
||||
|
||||
GUILayoutGroup bottomLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.66f), mainLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
|
||||
GUILayoutGroup bottomTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1f), bottomLayout.RectTransform));
|
||||
GUITextBlock descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), bottomTextLayout.RectTransform), ToolBox.LimitString(prefab.Description, GUIStyle.Font, GUI.IntScale(64)), wrap: true)
|
||||
GUILayoutGroup bottomTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1f), bottomLayout.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
GUITextBlock descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.6f), bottomTextLayout.RectTransform), prefab.Description, font: GUIStyle.SmallFont, wrap: true)
|
||||
{
|
||||
ToolTip = prefab.Description
|
||||
};
|
||||
bool truncated = false;
|
||||
while (descriptionBlock.TextSize.Y > descriptionBlock.Rect.Height && descriptionBlock.WrappedText.Contains('\n'))
|
||||
{
|
||||
var split = descriptionBlock.WrappedText.Value.Split('\n');
|
||||
descriptionBlock.Text = string.Join('\n', split.Take(split.Length - 1));
|
||||
truncated = true;
|
||||
}
|
||||
if (truncated)
|
||||
{
|
||||
descriptionBlock.Text += "...";
|
||||
}
|
||||
|
||||
GUITextBlock priceBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), bottomTextLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), font: GUIStyle.LargeFont);
|
||||
GUITextBlock priceBlock = new GUITextBlock(new RectTransform(new Vector2(1f, 0.25f), bottomTextLayout.RectTransform), TextManager.FormatCurrency(affliction.Price), font: GUIStyle.SubHeadingFont);
|
||||
|
||||
GUIButton buyButton = new GUIButton(new RectTransform(new Vector2(0.2f, 0.75f), bottomLayout.RectTransform), style: "CrewManagementAddButton");
|
||||
|
||||
@@ -923,6 +940,7 @@ namespace Barotrauma
|
||||
});
|
||||
}
|
||||
|
||||
#warning TODO: this doesn't seem like the right place for this, and it's not clear from the method signature how this differs from ToolBox.LimitString
|
||||
public static void EnsureTextDoesntOverflow(string? text, GUITextBlock textBlock, Rectangle bounds, ImmutableArray<GUILayoutGroup>? layoutGroups = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text)) { return; }
|
||||
|
||||
@@ -46,9 +46,7 @@ namespace Barotrauma
|
||||
private int buyTotal, sellTotal, sellFromSubTotal;
|
||||
|
||||
private GUITextBlock storeNameBlock;
|
||||
private GUITextBlock merchantBalanceBlock;
|
||||
private GUITextBlock currentSellValueBlock, newSellValueBlock;
|
||||
private GUIImage sellValueChangeArrow;
|
||||
private GUITextBlock reputationEffectBlock;
|
||||
private GUIDropDown sortingDropDown;
|
||||
private GUITextBox searchBox;
|
||||
private GUILayoutGroup categoryButtonContainer;
|
||||
@@ -376,41 +374,29 @@ namespace Barotrauma
|
||||
AutoScaleVertical = true,
|
||||
ForceUpperCase = ForceUpperCase.Yes
|
||||
};
|
||||
merchantBalanceBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), merchantBalanceContainer.RectTransform),
|
||||
"", font: GUIStyle.SubHeadingFont)
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), merchantBalanceContainer.RectTransform), "",
|
||||
color: Color.White, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
AutoScaleVertical = true,
|
||||
TextScale = 1.1f,
|
||||
TextGetter = () =>
|
||||
{
|
||||
merchantBalanceBlock.TextColor = ActiveStore?.BalanceColor ?? Color.Red;
|
||||
return GetMerchantBalanceText();
|
||||
}
|
||||
TextGetter = () => GetMerchantBalanceText()
|
||||
};
|
||||
|
||||
// Item sell value ------------------------------------------------
|
||||
var sellValueContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), balanceAndValueGroup.RectTransform))
|
||||
var reputationEffectContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), balanceAndValueGroup.RectTransform))
|
||||
{
|
||||
CanBeFocused = true,
|
||||
RelativeSpacing = 0.005f
|
||||
RelativeSpacing = 0.005f,
|
||||
ToolTip = TextManager.Get("campaignstore.reputationtooltip")
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform),
|
||||
TextManager.Get("campaignstore.sellvalue"), font: GUIStyle.Font, textAlignment: Alignment.BottomLeft)
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), reputationEffectContainer.RectTransform),
|
||||
TextManager.Get("reputation"), font: GUIStyle.Font, textAlignment: Alignment.BottomLeft)
|
||||
{
|
||||
AutoScaleVertical = true,
|
||||
CanBeFocused = false,
|
||||
ForceUpperCase = ForceUpperCase.Yes
|
||||
ForceUpperCase = ForceUpperCase.Yes,
|
||||
};
|
||||
|
||||
var valueChangeGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
float blockWidth = GUI.IsFourByThree() ? 0.32f : 0.28f;
|
||||
Point blockMaxSize = new Point((int)(GameSettings.CurrentConfig.Graphics.TextScale * 60), valueChangeGroup.Rect.Height);
|
||||
currentSellValueBlock = new GUITextBlock(new RectTransform(new Vector2(blockWidth, 1.0f), valueChangeGroup.RectTransform) { MaxSize = blockMaxSize },
|
||||
"", font: GUIStyle.SubHeadingFont)
|
||||
reputationEffectBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), reputationEffectContainer.RectTransform), "", font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
AutoScaleVertical = true,
|
||||
CanBeFocused = false,
|
||||
@@ -419,64 +405,27 @@ namespace Barotrauma
|
||||
{
|
||||
if (CurrentLocation != null)
|
||||
{
|
||||
int balanceAfterTransaction = activeTab switch
|
||||
Color textColor = GUIStyle.ColorReputationNeutral;
|
||||
string sign = "";
|
||||
int reputationModifier = (int)MathF.Round((CurrentLocation.GetStoreReputationModifier(activeTab == StoreTab.Buy) - 1) * 100);
|
||||
if (reputationModifier > 0)
|
||||
{
|
||||
StoreTab.Buy => ActiveStore.Balance + buyTotal,
|
||||
StoreTab.Sell => ActiveStore.Balance - sellTotal,
|
||||
StoreTab.SellSub => ActiveStore.Balance - sellFromSubTotal,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
if (balanceAfterTransaction != ActiveStore.Balance)
|
||||
{
|
||||
var newStatus = CurrentLocation.GetStoreBalanceStatus(balanceAfterTransaction);
|
||||
if (ActiveStore.ActiveBalanceStatus.SellPriceModifier != newStatus.SellPriceModifier)
|
||||
{
|
||||
string tooltipTag = newStatus.SellPriceModifier > ActiveStore.ActiveBalanceStatus.SellPriceModifier ?
|
||||
"campaingstore.valueincreasetooltip" : "campaingstore.valuedecreasetooltip";
|
||||
sellValueContainer.ToolTip = TextManager.Get(tooltipTag);
|
||||
currentSellValueBlock.TextColor = newStatus.Color;
|
||||
sellValueChangeArrow.Color = newStatus.Color;
|
||||
sellValueChangeArrow.Visible = true;
|
||||
newSellValueBlock.TextColor = newStatus.Color;
|
||||
newSellValueBlock.Text = $"{(newStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
|
||||
return $"{(ActiveStore.ActiveBalanceStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
|
||||
}
|
||||
textColor = IsBuying ? GUIStyle.ColorReputationLow : GUIStyle.ColorReputationHigh;
|
||||
sign = "+";
|
||||
}
|
||||
sellValueContainer.ToolTip = TextManager.Get("campaignstore.sellvaluetooltip");
|
||||
currentSellValueBlock.TextColor = ActiveStore.BalanceColor;
|
||||
sellValueChangeArrow.Visible = false;
|
||||
newSellValueBlock.Text = null;
|
||||
return $"{(ActiveStore.ActiveBalanceStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
|
||||
else if (reputationModifier < 0)
|
||||
{
|
||||
textColor = IsBuying ? GUIStyle.ColorReputationHigh : GUIStyle.ColorReputationLow;
|
||||
}
|
||||
reputationEffectBlock.TextColor = textColor;
|
||||
return $"{sign}{reputationModifier}%";
|
||||
}
|
||||
else
|
||||
{
|
||||
sellValueContainer.ToolTip = null;
|
||||
sellValueChangeArrow.Visible = false;
|
||||
newSellValueBlock.Text = null;
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
Vector4 newPadding = currentSellValueBlock.Padding;
|
||||
newPadding.Z = 0;
|
||||
currentSellValueBlock.Padding = newPadding;
|
||||
float relativeHeight = 0.45f;
|
||||
float relativeWidth = (relativeHeight * valueChangeGroup.Rect.Height) / valueChangeGroup.Rect.Width;
|
||||
sellValueChangeArrow = new GUIImage(new RectTransform(new Vector2(relativeWidth, relativeHeight), valueChangeGroup.RectTransform), "StoreArrow", scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Visible = false
|
||||
};
|
||||
newSellValueBlock = new GUITextBlock(new RectTransform(new Vector2(blockWidth, 1.0f), valueChangeGroup.RectTransform) { MaxSize = blockMaxSize },
|
||||
"", font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
AutoScaleVertical = true,
|
||||
CanBeFocused = false,
|
||||
TextScale = 1.1f
|
||||
};
|
||||
newPadding = newSellValueBlock.Padding;
|
||||
newPadding.X = 0;
|
||||
newSellValueBlock.Padding = newPadding;
|
||||
|
||||
// Store mode buttons ------------------------------------------------
|
||||
var modeButtonFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.4f / 14.0f), storeContent.RectTransform), style: null);
|
||||
|
||||
@@ -626,7 +626,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
|
||||
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
|
||||
RefreshSubmarineDisplay(true);
|
||||
}
|
||||
else
|
||||
@@ -664,7 +664,7 @@ namespace Barotrauma
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
|
||||
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
|
||||
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
|
||||
RefreshSubmarineDisplay(true);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -54,47 +54,65 @@ namespace Barotrauma
|
||||
|
||||
private ushort currentPing;
|
||||
private readonly Character character;
|
||||
private readonly bool hasCharacter;
|
||||
private readonly bool wasCharacterAlive;
|
||||
private readonly GUITextBlock textBlock;
|
||||
private readonly GUIFrame frame;
|
||||
|
||||
private readonly GUIImage permissionIcon;
|
||||
|
||||
public LinkedGUI(Client client, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock, GUIImage permissionIcon)
|
||||
public LinkedGUI(Client client, GUIFrame frame, GUITextBlock textBlock, GUIImage permissionIcon)
|
||||
{
|
||||
this.Client = client;
|
||||
this.textBlock = textBlock;
|
||||
this.frame = frame;
|
||||
this.hasCharacter = hasCharacter;
|
||||
this.permissionIcon = permissionIcon;
|
||||
character = client?.Character;
|
||||
wasCharacterAlive = client.Character != null && !client.Character.IsDead;
|
||||
}
|
||||
|
||||
public LinkedGUI(Character character, GUIFrame frame, bool hasCharacter, GUITextBlock textBlock)
|
||||
public LinkedGUI(Character character, GUIFrame frame, GUITextBlock textBlock)
|
||||
{
|
||||
this.character = character;
|
||||
this.textBlock = textBlock;
|
||||
this.frame = frame;
|
||||
this.hasCharacter = hasCharacter;
|
||||
wasCharacterAlive = character != null && !character.IsDead;
|
||||
}
|
||||
|
||||
public bool HasMultiplayerCharacterChanged()
|
||||
{
|
||||
if (Client == null) { return false; }
|
||||
bool characterState = Client.Character != null;
|
||||
if (characterState && Client.Character.IsDead) characterState = false;
|
||||
return hasCharacter != characterState;
|
||||
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
if (Client.Character != character)
|
||||
{
|
||||
DebugConsole.Log($"Refreshing tab menu crew list (client \"{Client.Name}\"'s character changed from \"{character?.Name ?? "null"}\" to \"{Client.Character?.Name ?? "null"}\")");
|
||||
}
|
||||
}
|
||||
return Client.Character != character;
|
||||
}
|
||||
|
||||
public bool HasMultiplayerCharacterDied()
|
||||
{
|
||||
if (Client == null || !hasCharacter || Client.Character == null) { return false; }
|
||||
return Client.Character.IsDead;
|
||||
}
|
||||
|
||||
public bool HasAICharacterDied()
|
||||
public bool HasCharacterDied()
|
||||
{
|
||||
if (character == null) { return false; }
|
||||
return character.IsDead;
|
||||
bool isAlive = !(character?.IsDead ?? true);
|
||||
if (GameSettings.CurrentConfig.VerboseLogging)
|
||||
{
|
||||
if (wasCharacterAlive && !isAlive)
|
||||
{
|
||||
DebugConsole.Log(Client == null ?
|
||||
$"Refreshing tab menu crew list (character \"{character?.Name ?? "null"}\" died)" :
|
||||
$"Refreshing tab menu crew list (client \"{Client.Name}\"'s character \"{character?.Name ?? "null"}\" died)");
|
||||
}
|
||||
else if (!wasCharacterAlive && isAlive)
|
||||
{
|
||||
DebugConsole.Log(Client == null ?
|
||||
|
||||
$"Refreshing tab menu crew list (character \"{character?.Name ?? "null"}\" came back to life)" :
|
||||
$"Refreshing tab menu crew list (client \"{Client.Name}\"'s character \"{character?.Name ?? "null"}\" came back to life)");
|
||||
}
|
||||
}
|
||||
return isAlive != wasCharacterAlive;
|
||||
}
|
||||
|
||||
public void TryPingRefresh()
|
||||
@@ -207,7 +225,7 @@ namespace Barotrauma
|
||||
{
|
||||
linkedGUIList[i].TryPingRefresh();
|
||||
linkedGUIList[i].TryPermissionIconRefresh(GetPermissionIcon(linkedGUIList[i].Client));
|
||||
if (linkedGUIList[i].HasMultiplayerCharacterChanged() || linkedGUIList[i].HasMultiplayerCharacterDied() || linkedGUIList[i].HasAICharacterDied())
|
||||
if (linkedGUIList[i].HasMultiplayerCharacterChanged() || linkedGUIList[i].HasCharacterDied())
|
||||
{
|
||||
RemoveCurrentElements();
|
||||
CreateMultiPlayerList(true);
|
||||
@@ -219,10 +237,11 @@ namespace Barotrauma
|
||||
{
|
||||
for (int i = 0; i < linkedGUIList.Count; i++)
|
||||
{
|
||||
if (linkedGUIList[i].HasAICharacterDied())
|
||||
if (linkedGUIList[i].HasCharacterDied())
|
||||
{
|
||||
RemoveCurrentElements();
|
||||
CreateSinglePlayerList(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,6 +316,10 @@ namespace Barotrauma
|
||||
|
||||
var balanceFrame = new GUIFrame(new RectTransform(new Point(innerLayoutGroup.Rect.Width, innerLayoutGroup.Rect.Height - infoFrameHolderHeight), parent: innerLayoutGroup.RectTransform), style: "InnerFrame");
|
||||
GUITextBlock balanceText = new GUITextBlock(new RectTransform(Vector2.One, balanceFrame.RectTransform), string.Empty, textAlignment: Alignment.Right);
|
||||
if (GameMain.IsMultiplayer)
|
||||
{
|
||||
balanceText.ToolTip = TextManager.Get("bankdescription");
|
||||
}
|
||||
GUIFrame bottomDisclaimerFrame = new GUIFrame(new RectTransform(new Vector2(contentFrameSize.X, 0.1f), infoFrame.RectTransform)
|
||||
{
|
||||
AbsoluteOffset = new Point(contentFrame.Rect.X, contentFrame.Rect.Bottom + GUI.IntScale(8))
|
||||
@@ -337,7 +360,7 @@ namespace Barotrauma
|
||||
var talentsButton = createTabButton(InfoFrameTab.Talents, "tabmenu.character");
|
||||
talentsButton.OnAddedToGUIUpdateList += (component) =>
|
||||
{
|
||||
talentsButton.Enabled = Character.Controlled?.Info != null;
|
||||
talentsButton.Enabled = Character.Controlled?.Info != null || (GameMain.Client?.CharacterInfo != null && GameMain.GameSession?.GameMode is MultiPlayerCampaign);
|
||||
if (!talentsButton.Enabled && selectedTab == InfoFrameTab.Talents)
|
||||
{
|
||||
SelectInfoFrameTab(InfoFrameTab.Crew);
|
||||
@@ -560,7 +583,7 @@ namespace Barotrauma
|
||||
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
|
||||
ToolBox.LimitString(character.Info.Name, GUIStyle.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: character.Info.Job.Prefab.UIColor);
|
||||
|
||||
linkedGUIList.Add(new LinkedGUI(character, frame, !character.IsDead, textBlock: null));
|
||||
linkedGUIList.Add(new LinkedGUI(character, frame, textBlock: null));
|
||||
}
|
||||
|
||||
private void CreateMultiPlayerListContentHolder(GUILayoutGroup headerFrame)
|
||||
@@ -657,7 +680,7 @@ namespace Barotrauma
|
||||
if (client != null)
|
||||
{
|
||||
CreateNameWithPermissionIcon(client, paddedFrame, out GUIImage permissionIcon);
|
||||
linkedGUIList.Add(new LinkedGUI(client, frame, true,
|
||||
linkedGUIList.Add(new LinkedGUI(client, frame,
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center),
|
||||
permissionIcon));
|
||||
}
|
||||
@@ -668,12 +691,12 @@ namespace Barotrauma
|
||||
|
||||
if (character is AICharacter)
|
||||
{
|
||||
linkedGUIList.Add(new LinkedGUI(character, frame, !character.IsDead,
|
||||
linkedGUIList.Add(new LinkedGUI(character, frame,
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), TextManager.Get("tabmenu.bot"), textAlignment: Alignment.Center) { ForceUpperCase = ForceUpperCase.Yes }));
|
||||
}
|
||||
else
|
||||
{
|
||||
linkedGUIList.Add(new LinkedGUI(client: null, frame, true, textBlock: null, permissionIcon: null));
|
||||
linkedGUIList.Add(new LinkedGUI(client: null, frame, textBlock: null, permissionIcon: null));
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => DrawDisconnectedIcon(sb, component.Rect))
|
||||
{
|
||||
@@ -718,7 +741,7 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
CreateNameWithPermissionIcon(client, paddedFrame, out GUIImage permissionIcon);
|
||||
linkedGUIList.Add(new LinkedGUI(client, frame, false,
|
||||
linkedGUIList.Add(new LinkedGUI(client, frame,
|
||||
new GUITextBlock(new RectTransform(new Point(pingColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform), client.Ping.ToString(), textAlignment: Alignment.Center),
|
||||
permissionIcon));
|
||||
|
||||
@@ -775,19 +798,27 @@ namespace Barotrauma
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUIFrame(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform), style: null)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
ToolTip = TextManager.Get("walletdescription")
|
||||
};
|
||||
|
||||
if (character.IsBot) { return; }
|
||||
|
||||
Sprite walletSprite = GUIStyle.CrewWalletIconSmall.Value.Sprite;
|
||||
|
||||
GUIImage icon = new GUIImage(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform, scaleBasis: ScaleBasis.BothHeight), walletSprite, scaleToFit: true);
|
||||
GUIImage icon = new GUIImage(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform, scaleBasis: ScaleBasis.BothHeight), walletSprite, scaleToFit: true) { CanBeFocused = false };
|
||||
GUITextBlock walletBlock = new GUITextBlock(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform), string.Empty, textAlignment: Alignment.Right, font: GUIStyle.Font)
|
||||
{
|
||||
AutoScaleHorizontal = true,
|
||||
Padding = Vector4.Zero
|
||||
Padding = Vector4.Zero,
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
GUIImage largeIcon = new GUIImage(new RectTransform(Vector2.One, paddedLayoutGroup.RectTransform), walletSprite, scaleToFit: true)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
IgnoreLayoutGroups = true,
|
||||
Visible = false
|
||||
};
|
||||
@@ -971,16 +1002,25 @@ namespace Barotrauma
|
||||
float relativeX = icon.RectTransform.NonScaledSize.X / (float)icon.Parent.RectTransform.NonScaledSize.X;
|
||||
GUILayoutGroup headerTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - relativeX, 1f), headerLayout.RectTransform), isHorizontal: true) { Stretch = true };
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), headerTextLayout.RectTransform), TextManager.Get("crewwallet.wallet"), font: GUIStyle.LargeFont);
|
||||
GUIFrame walletTooltipFrame = new GUIFrame(new RectTransform(Vector2.One, headerLayout.RectTransform), style: null)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
ToolTip = TextManager.Get("walletdescription")
|
||||
};
|
||||
GUITextBlock moneyBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), headerTextLayout.RectTransform), TextManager.FormatCurrency(targetWallet.Balance), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right);
|
||||
|
||||
GUILayoutGroup middleLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.66f), walletLayout.RectTransform));
|
||||
GUILayoutGroup salaryTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), middleLayout.RectTransform), isHorizontal: true);
|
||||
GUITextBlock salaryTitle = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), salaryTextLayout.RectTransform), TextManager.Get("crewwallet.salary"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
|
||||
GUITextBlock rewardBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), salaryTextLayout.RectTransform), string.Empty, textAlignment: Alignment.BottomRight);
|
||||
GUIFrame salaryTooltipFrame = new GUIFrame(new RectTransform(Vector2.One, middleLayout.RectTransform), style: null)
|
||||
{
|
||||
IgnoreLayoutGroups = true,
|
||||
ToolTip = TextManager.Get("crewwallet.salary.tooltip")
|
||||
};
|
||||
GUILayoutGroup sliderLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), middleLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.Center);
|
||||
GUIScrollBar salarySlider = new GUIScrollBar(new RectTransform(new Vector2(0.9f, 1f), sliderLayout.RectTransform), style: "GUISlider", barSize: 0.03f)
|
||||
{
|
||||
ToolTip = TextManager.Get("crewwallet.salary.tooltip"),
|
||||
Range = new Vector2(0, 1),
|
||||
BarScrollValue = targetWallet.RewardDistribution / 100f,
|
||||
Step = 0.01f,
|
||||
@@ -1050,149 +1090,195 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Identifier eventIdentifier = nameof(CreateWalletFrame).ToIdentifier();
|
||||
|
||||
ToggleTransferMenuIcon(transferMenuButton, open: isTransferMenuOpen);
|
||||
ToggleCenterButton(centerButton, isSending);
|
||||
|
||||
|
||||
if (!(Character.Controlled is { } myCharacter))
|
||||
{
|
||||
salarySlider.Enabled = false;
|
||||
transferAmountInput.Enabled = false;
|
||||
centerButton.Enabled = false;
|
||||
confirmButton.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasMoneyPermissions = CampaignMode.AllowedToManageWallets();
|
||||
salarySlider.Enabled = hasMoneyPermissions;
|
||||
Wallet otherWallet;
|
||||
GameMain.Client?.OnPermissionChanged.RegisterOverwriteExisting(eventIdentifier, e => UpdateWalletInterface(registerEvents: false));
|
||||
UpdateWalletInterface(registerEvents: true);
|
||||
|
||||
switch (hasMoneyPermissions)
|
||||
void UpdateWalletInterface(bool registerEvents)
|
||||
{
|
||||
case true:
|
||||
rightName.Text = TextManager.Get("crewwallet.bank");
|
||||
otherWallet = campaign.Bank;
|
||||
break;
|
||||
case false when character == myCharacter:
|
||||
rightName.Text = TextManager.Get("crewwallet.bank");
|
||||
otherWallet = campaign.Bank;
|
||||
isSending = true;
|
||||
ToggleCenterButton(centerButton, isSending);
|
||||
break;
|
||||
default:
|
||||
rightName.Text = myCharacter.Name;
|
||||
otherWallet = campaign.PersonalWallet;
|
||||
break;
|
||||
}
|
||||
|
||||
MedicalClinicUI.EnsureTextDoesntOverflow(rightName.Text.ToString(), rightName, rightLayout.Rect, layoutGroups);
|
||||
updateButtonText();
|
||||
if (!hasMoneyPermissions)
|
||||
{
|
||||
if (character != Character.Controlled)
|
||||
if (!(Character.Controlled is { } myCharacter))
|
||||
{
|
||||
centerButton.Enabled = centerButton.CanBeFocused = false;
|
||||
salarySlider.Enabled = false;
|
||||
transferAmountInput.Enabled = false;
|
||||
centerButton.Enabled = false;
|
||||
confirmButton.Enabled = false;
|
||||
return;
|
||||
}
|
||||
salarySlider.Enabled = salarySlider.CanBeFocused = false;
|
||||
}
|
||||
|
||||
leftBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
|
||||
bool hasMoneyPermissions = CampaignMode.AllowedToManageWallets();
|
||||
salarySlider.Enabled = hasMoneyPermissions;
|
||||
|
||||
UpdateAllInputs();
|
||||
switch (hasMoneyPermissions)
|
||||
{
|
||||
case true:
|
||||
rightName.Text = TextManager.Get("crewwallet.bank");
|
||||
otherWallet = campaign.Bank;
|
||||
break;
|
||||
case false when character == myCharacter:
|
||||
rightName.Text = TextManager.Get("crewwallet.bank");
|
||||
otherWallet = campaign.Bank;
|
||||
isSending = true;
|
||||
ToggleCenterButton(centerButton, isSending);
|
||||
break;
|
||||
default:
|
||||
rightName.Text = myCharacter.Name;
|
||||
otherWallet = campaign.PersonalWallet;
|
||||
break;
|
||||
}
|
||||
|
||||
MedicalClinicUI.EnsureTextDoesntOverflow(rightName.Text.ToString(), rightName, rightLayout.Rect, layoutGroups);
|
||||
|
||||
UpdatedConfirmButtonText();
|
||||
|
||||
if (!hasMoneyPermissions)
|
||||
{
|
||||
if (character != Character.Controlled)
|
||||
{
|
||||
centerButton.Enabled = centerButton.CanBeFocused = false;
|
||||
}
|
||||
|
||||
salarySlider.Enabled = salarySlider.CanBeFocused = false;
|
||||
}
|
||||
|
||||
leftBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
|
||||
|
||||
centerButton.OnClicked = (btn, o) =>
|
||||
{
|
||||
isSending = !isSending;
|
||||
updateButtonText();
|
||||
ToggleCenterButton(btn, isSending);
|
||||
UpdateAllInputs();
|
||||
return true;
|
||||
};
|
||||
|
||||
void updateButtonText()
|
||||
{
|
||||
confirmButton.Text = TextManager.Get(hasMoneyPermissions || isSending ? "confirm" : "crewwallet.requestmoney");
|
||||
}
|
||||
if (!registerEvents) { return; }
|
||||
|
||||
transferAmountInput.OnValueChanged = input =>
|
||||
{
|
||||
UpdateInputs();
|
||||
};
|
||||
|
||||
transferAmountInput.OnValueEntered = input =>
|
||||
{
|
||||
UpdateAllInputs();
|
||||
};
|
||||
|
||||
Identifier eventIdentifier = nameof(CreateWalletFrame).ToIdentifier();
|
||||
campaign.OnMoneyChanged.RegisterOverwriteExisting(eventIdentifier, e =>
|
||||
{
|
||||
if (e.Wallet == targetWallet)
|
||||
centerButton.OnClicked = (btn, o) =>
|
||||
{
|
||||
moneyBlock.Text = TextManager.FormatCurrency(e.Info.Balance);
|
||||
salarySlider.BarScrollValue = e.Info.RewardDistribution / 100f;
|
||||
isSending = !isSending;
|
||||
UpdatedConfirmButtonText();
|
||||
ToggleCenterButton(btn, isSending);
|
||||
UpdateAllInputs();
|
||||
return true;
|
||||
};
|
||||
|
||||
transferAmountInput.OnValueChanged = input =>
|
||||
{
|
||||
UpdateInputs();
|
||||
};
|
||||
|
||||
transferAmountInput.OnValueEntered = input =>
|
||||
{
|
||||
UpdateAllInputs();
|
||||
};
|
||||
|
||||
resetButton.OnClicked = (button, o) =>
|
||||
{
|
||||
transferAmountInput.IntValue = 0;
|
||||
UpdateAllInputs();
|
||||
return true;
|
||||
};
|
||||
|
||||
confirmButton.OnClicked = (button, o) =>
|
||||
{
|
||||
int amount = transferAmountInput.IntValue;
|
||||
if (amount == 0) { return false; }
|
||||
|
||||
Option<Character> target1 = Option<Character>.Some(character),
|
||||
target2 = otherWallet == campaign.Bank ? Option<Character>.None() : Option<Character>.Some(myCharacter);
|
||||
if (isSending) { (target1, target2) = (target2, target1); }
|
||||
|
||||
SendTransaction(target1, target2, amount);
|
||||
isTransferMenuOpen = false;
|
||||
ToggleTransferMenuIcon(transferMenuButton, isTransferMenuOpen);
|
||||
return true;
|
||||
};
|
||||
|
||||
campaign.OnMoneyChanged.RegisterOverwriteExisting(eventIdentifier, e =>
|
||||
{
|
||||
if (e.Wallet == targetWallet)
|
||||
{
|
||||
moneyBlock.Text = TextManager.FormatCurrency(e.Info.Balance);
|
||||
salarySlider.BarScrollValue = e.Info.RewardDistribution / 100f;
|
||||
}
|
||||
|
||||
UpdateAllInputs();
|
||||
});
|
||||
|
||||
registeredEvents.Add(eventIdentifier);
|
||||
|
||||
void UpdatedConfirmButtonText()
|
||||
{
|
||||
confirmButton.Text = TextManager.Get(hasMoneyPermissions || isSending ? "confirm" : "crewwallet.requestmoney");
|
||||
}
|
||||
UpdateAllInputs();
|
||||
});
|
||||
registeredEvents.Add(eventIdentifier);
|
||||
|
||||
resetButton.OnClicked = (button, o) =>
|
||||
{
|
||||
transferAmountInput.IntValue = 0;
|
||||
UpdateAllInputs();
|
||||
return true;
|
||||
};
|
||||
|
||||
confirmButton.OnClicked = (button, o) =>
|
||||
{
|
||||
int amount = transferAmountInput.IntValue;
|
||||
if (amount == 0) { return false; }
|
||||
|
||||
Option<Character> target1 = Option<Character>.Some(character),
|
||||
target2 = otherWallet == campaign.Bank ? Option<Character>.None() : Option<Character>.Some(myCharacter);
|
||||
if (isSending) { (target1, target2) = (target2, target1); }
|
||||
|
||||
SendTransaction(target1, target2, amount);
|
||||
isTransferMenuOpen = false;
|
||||
ToggleTransferMenuIcon(transferMenuButton, isTransferMenuOpen);
|
||||
return true;
|
||||
};
|
||||
|
||||
void UpdateAllInputs()
|
||||
{
|
||||
UpdateInputs();
|
||||
UpdateMaxInput();
|
||||
}
|
||||
|
||||
void UpdateInputs()
|
||||
{
|
||||
confirmButton.Enabled = resetButton.Enabled = transferAmountInput.IntValue > 0;
|
||||
if (transferAmountInput.IntValue == 0)
|
||||
void UpdateAllInputs()
|
||||
{
|
||||
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
|
||||
rightBalance.TextColor = GUIStyle.TextColorNormal;
|
||||
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance);
|
||||
leftBalance.TextColor = GUIStyle.TextColorNormal;
|
||||
UpdateInputs();
|
||||
UpdateMaxInput();
|
||||
}
|
||||
else if (isSending)
|
||||
|
||||
void UpdateInputs()
|
||||
{
|
||||
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance + transferAmountInput.IntValue);
|
||||
rightBalance.TextColor = GUIStyle.Blue;
|
||||
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance - transferAmountInput.IntValue);
|
||||
leftBalance.TextColor = GUIStyle.Red;
|
||||
confirmButton.Enabled = resetButton.Enabled = transferAmountInput.IntValue > 0;
|
||||
if (transferAmountInput.IntValue == 0)
|
||||
{
|
||||
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance);
|
||||
rightBalance.TextColor = GUIStyle.TextColorNormal;
|
||||
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance);
|
||||
leftBalance.TextColor = GUIStyle.TextColorNormal;
|
||||
}
|
||||
else if (isSending)
|
||||
{
|
||||
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance + transferAmountInput.IntValue);
|
||||
rightBalance.TextColor = GUIStyle.Blue;
|
||||
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance - transferAmountInput.IntValue);
|
||||
leftBalance.TextColor = GUIStyle.Red;
|
||||
}
|
||||
else
|
||||
{
|
||||
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance - transferAmountInput.IntValue);
|
||||
rightBalance.TextColor = GUIStyle.Red;
|
||||
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance + transferAmountInput.IntValue);
|
||||
leftBalance.TextColor = GUIStyle.Blue;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
void UpdateMaxInput()
|
||||
{
|
||||
rightBalance.Text = TextManager.FormatCurrency(otherWallet.Balance - transferAmountInput.IntValue);
|
||||
rightBalance.TextColor = GUIStyle.Red;
|
||||
leftBalance.Text = TextManager.FormatCurrency(targetWallet.Balance + transferAmountInput.IntValue);
|
||||
leftBalance.TextColor = GUIStyle.Blue;
|
||||
int maxValue = isSending ? targetWallet.Balance : otherWallet.Balance;
|
||||
transferAmountInput.MaxValueInt = maxValue;
|
||||
|
||||
transferAmountInput.Enabled = true;
|
||||
transferAmountInput.ToolTip = string.Empty;
|
||||
|
||||
if (!hasMoneyPermissions && GameMain.Client?.ServerSettings is { } serverSettings)
|
||||
{
|
||||
transferAmountInput.MaxValueInt = Math.Min(maxValue, serverSettings.MaximumTransferRequest);
|
||||
if (serverSettings.MaximumTransferRequest <= 0)
|
||||
{
|
||||
transferAmountInput.Enabled = false;
|
||||
transferAmountInput.ToolTip = TextManager.Get("wallettransferrequestdisabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMaxInput()
|
||||
void SetRewardText(int value, GUITextBlock block)
|
||||
{
|
||||
transferAmountInput.MaxValueInt = isSending ? targetWallet.Balance : otherWallet.Balance;
|
||||
var (_, percentage, sum) = Mission.GetRewardShare(value, salaryCrew, Option<int>.None());
|
||||
LocalizedString tooltip = string.Empty;
|
||||
block.TextColor = GUIStyle.TextColorNormal;
|
||||
|
||||
if (sum > 100)
|
||||
{
|
||||
tooltip = TextManager.GetWithVariables("crewwallet.salary.over100toolitp", ("[sum]", $"{(int)sum}"), ("[newvalue]", $"{percentage}"));
|
||||
block.TextColor = GUIStyle.Orange;
|
||||
}
|
||||
|
||||
LocalizedString text = TextManager.GetWithVariable("percentageformat", "[value]", $"{value}");
|
||||
|
||||
block.Text = text;
|
||||
block.ToolTip = RichString.Rich(tooltip);
|
||||
}
|
||||
|
||||
static void ToggleTransferMenuIcon(GUIButton btn, bool open)
|
||||
@@ -1235,24 +1321,6 @@ namespace Barotrauma
|
||||
transfer.Write(msg);
|
||||
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
void SetRewardText(int value, GUITextBlock block)
|
||||
{
|
||||
var (_, percentage, sum) = Mission.GetRewardShare(value, salaryCrew, Option<int>.None());
|
||||
LocalizedString tooltip = string.Empty;
|
||||
block.TextColor = GUIStyle.TextColorNormal;
|
||||
|
||||
if (sum > 100)
|
||||
{
|
||||
tooltip = TextManager.GetWithVariables("crewwallet.salary.over100toolitp", ("[sum]", $"{(int)sum}"), ("[newvalue]", $"{percentage}"));
|
||||
block.TextColor = GUIStyle.Orange;
|
||||
}
|
||||
|
||||
LocalizedString text = TextManager.GetWithVariable("percentageformat", "[value]", $"{value}");
|
||||
|
||||
block.Text = text;
|
||||
block.ToolTip = RichString.Rich(tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
private GUIComponent CreateClientInfoFrame(GUIFrame frame, Client client, Sprite permissionIcon = null)
|
||||
@@ -1740,9 +1808,6 @@ namespace Barotrauma
|
||||
talentButtons.Clear();
|
||||
talentCornerIcons.Clear();
|
||||
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
if (controlledCharacter == null) { return; }
|
||||
|
||||
GUIFrame talentFrameBackground = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
|
||||
int padding = GUI.IntScale(15);
|
||||
GUIFrame talentFrameContent = new GUIFrame(new RectTransform(new Point(talentFrameBackground.Rect.Width - padding, talentFrameBackground.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null);
|
||||
@@ -1762,13 +1827,20 @@ namespace Barotrauma
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(playerFrame, alwaysAllowEditing: true, createPendingText: false);
|
||||
}
|
||||
|
||||
/*Character controlledCharacter = Character.Controlled;
|
||||
if (controlledCharacter == null) { return; }
|
||||
|
||||
if (controlledCharacter.Info is null)
|
||||
{
|
||||
DebugConsole.ThrowError("No character info found for talent UI");
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
|
||||
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
CharacterInfo info = controlledCharacter?.Info ?? GameMain.Client?.CharacterInfo;
|
||||
if (info == null) { return; }
|
||||
|
||||
Job job = info.Job;
|
||||
|
||||
GUILayoutGroup talentFrameLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), talentFrameMain.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
@@ -1776,9 +1848,7 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
GUILayoutGroup talentInfoLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), talentFrameLayoutGroup.RectTransform, Anchor.Center), isHorizontal: true);
|
||||
|
||||
CharacterInfo info = controlledCharacter.Info;
|
||||
Job job = info.Job;
|
||||
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1f), talentInfoLayoutGroup.RectTransform), onDraw: (batch, component) =>
|
||||
{
|
||||
@@ -1801,11 +1871,11 @@ namespace Barotrauma
|
||||
GUITextBlock traitBlock = new GUITextBlock(new RectTransform(Vector2.One, nameLayout.RectTransform), traitString, font: GUIStyle.SmallFont);
|
||||
traitBlock.RectTransform.NonScaledSize = traitSize.Pad(traitBlock.Padding).ToPoint();
|
||||
|
||||
GUIFrame endocrineFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.35f), nameLayout.RectTransform, Anchor.BottomCenter), style: null);
|
||||
GUIFrame talentsOutsideTreeFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.35f), nameLayout.RectTransform, Anchor.BottomCenter), style: null);
|
||||
|
||||
if (!(GameMain.NetworkMember is null))
|
||||
{
|
||||
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.675f, 1f), endocrineFrame.RectTransform, Anchor.TopLeft), text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"))
|
||||
GUIButton newCharacterBox = new GUIButton(new RectTransform(new Vector2(0.675f, 1f), talentsOutsideTreeFrame.RectTransform, Anchor.TopLeft), text: GameMain.NetLobbyScreen.CampaignCharacterDiscarded ? TextManager.Get("settings") : TextManager.Get("createnew"))
|
||||
{
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
@@ -1852,13 +1922,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<TalentPrefab> endocrineTalents = info.GetEndocrineTalents().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e));
|
||||
IEnumerable<TalentPrefab> talentsOutsideTree = info.GetUnlockedTalentsOutsideTree().Select(e => TalentPrefab.TalentPrefabs.Find(c => c.Identifier == e));
|
||||
|
||||
if (endocrineTalents.Count() > 0)
|
||||
if (talentsOutsideTree.Count() > 0)
|
||||
{
|
||||
GUIImage endocrineIcon = new GUIImage(new RectTransform(new Vector2(0.275f, 1f), endocrineFrame.RectTransform, anchor: Anchor.TopRight, scaleBasis: ScaleBasis.Normal), style: "EndocrineReminderIcon")
|
||||
//TODO: replace with something more generic
|
||||
GUIImage endocrineIcon = new GUIImage(new RectTransform(new Vector2(0.275f, 1f), talentsOutsideTreeFrame.RectTransform, anchor: Anchor.TopRight, scaleBasis: ScaleBasis.Normal), style: "EndocrineReminderIcon")
|
||||
{
|
||||
ToolTip = $"{TextManager.Get("afflictionname.endocrineboost")}\n\n{string.Join(", ", endocrineTalents.Select(e => e.DisplayName))}"
|
||||
ToolTip = $"{TextManager.Get("afflictionname.endocrineboost")}\n\n{string.Join(", ", talentsOutsideTree.Select(e => e.DisplayName))}"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1870,49 +1941,55 @@ namespace Barotrauma
|
||||
skillBlock.RectTransform.NonScaledSize = skillSize.Pad(skillBlock.Padding).ToPoint();
|
||||
|
||||
skillListBox = new GUIListBox(new RectTransform(new Vector2(1f, 1f - skillBlock.RectTransform.RelativeSize.Y), skillLayout.RectTransform), style: null);
|
||||
CreateTalentSkillList(controlledCharacter, skillListBox);
|
||||
CreateTalentSkillList(controlledCharacter, info, skillListBox);
|
||||
|
||||
if (!TalentTree.JobTalentTrees.TryGet(controlledCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1f, 1f), talentFrameLayoutGroup.RectTransform), style: "HorizontalLine");
|
||||
|
||||
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
|
||||
|
||||
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
|
||||
foreach (var subTree in talentTree.TalentSubTrees)
|
||||
if (controlledCharacter != null)
|
||||
{
|
||||
GUIFrame subTreeFrame = new GUIFrame(new RectTransform(new Vector2(0.333f, 1f), talentTreeListBox.Content.RectTransform, anchor: Anchor.TopLeft), style: null);
|
||||
GUILayoutGroup subTreeLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), subTreeFrame.RectTransform, Anchor.Center), false, childAnchor: Anchor.TopCenter);
|
||||
if (!TalentTree.JobTalentTrees.TryGet(info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
|
||||
|
||||
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.111f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
|
||||
int elementPadding = GUI.IntScale(8);
|
||||
Point headerSize = subtreeTitleFrame.RectTransform.NonScaledSize;
|
||||
GUIFrame subTreeTitleBackground = new GUIFrame(new RectTransform(new Point(headerSize.X - elementPadding, headerSize.Y), subtreeTitleFrame.RectTransform, anchor: Anchor.Center), style: "SubtreeHeader");
|
||||
subTreeNames.Add(new GUITextBlock(new RectTransform(Vector2.One, subTreeTitleBackground.RectTransform, anchor: Anchor.TopCenter), subTree.DisplayName, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center));
|
||||
new GUIFrame(new RectTransform(new Vector2(1f, 1f), talentFrameLayoutGroup.RectTransform), style: "HorizontalLine");
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.7f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
|
||||
|
||||
selectedTalents = info.GetUnlockedTalentsInTree().ToList();
|
||||
|
||||
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
|
||||
foreach (var subTree in talentTree.TalentSubTrees)
|
||||
{
|
||||
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.222f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
|
||||
GUIFrame subTreeFrame = new GUIFrame(new RectTransform(new Vector2(0.333f, 1f), talentTreeListBox.Content.RectTransform, anchor: Anchor.TopLeft), style: null);
|
||||
GUILayoutGroup subTreeLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), subTreeFrame.RectTransform, Anchor.Center), false, childAnchor: Anchor.TopCenter);
|
||||
|
||||
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
|
||||
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.111f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
|
||||
int elementPadding = GUI.IntScale(8);
|
||||
Point headerSize = subtreeTitleFrame.RectTransform.NonScaledSize;
|
||||
GUIFrame subTreeTitleBackground = new GUIFrame(new RectTransform(new Point(headerSize.X - elementPadding, headerSize.Y), subtreeTitleFrame.RectTransform, anchor: Anchor.Center), style: "SubtreeHeader");
|
||||
subTreeNames.Add(new GUITextBlock(new RectTransform(Vector2.One, subTreeTitleBackground.RectTransform, anchor: Anchor.TopCenter), subTree.DisplayName, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Center));
|
||||
|
||||
GUIFrame talentBackground = new GUIFrame(new RectTransform(new Point(talentFrameSize.X - elementPadding, talentFrameSize.Y - elementPadding), talentOptionFrame.RectTransform, anchor: Anchor.Center), style: "TalentBackground");
|
||||
GUIFrame talentBackgroundHighlight = new GUIFrame(new RectTransform(Vector2.One, talentBackground.RectTransform, anchor: Anchor.Center), style: "TalentBackgroundGlow") { Visible = false };
|
||||
|
||||
GUIImage cornerIcon = new GUIImage(new RectTransform(new Vector2(0.2f), talentOptionFrame.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight) { MaxSize = new Point(16) }, style: null)
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.222f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: null);
|
||||
|
||||
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
|
||||
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
|
||||
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
|
||||
|
||||
GUIFrame talentBackground = new GUIFrame(new RectTransform(new Point(talentFrameSize.X - elementPadding, talentFrameSize.Y - elementPadding), talentOptionFrame.RectTransform, anchor: Anchor.Center), style: "TalentBackground")
|
||||
{
|
||||
Color = talentStageBackgroundColors[TalentTree.TalentTreeStageState.Locked]
|
||||
};
|
||||
GUIFrame talentBackgroundHighlight = new GUIFrame(new RectTransform(Vector2.One, talentBackground.RectTransform, anchor: Anchor.Center), style: "TalentBackgroundGlow") { Visible = false };
|
||||
|
||||
GUIImage cornerIcon = new GUIImage(new RectTransform(new Vector2(0.2f), talentOptionFrame.RectTransform, anchor: Anchor.BottomRight, scaleBasis: ScaleBasis.BothHeight) { MaxSize = new Point(16) }, style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = talentStageBackgroundColors[TalentTree.TalentTreeStageState.Locked]
|
||||
};
|
||||
|
||||
Point iconSize = cornerIcon.RectTransform.NonScaledSize;
|
||||
cornerIcon.RectTransform.AbsoluteOffset = new Point(iconSize.X / 2, iconSize.Y / 2);
|
||||
|
||||
if (subTree.TalentOptionStages.Count <= i) { continue; }
|
||||
|
||||
if (subTree.TalentOptionStages.Count > i)
|
||||
{
|
||||
TalentOption talentOption = subTree.TalentOptionStages[i];
|
||||
|
||||
GUILayoutGroup talentOptionCenterGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 0.7f), talentOptionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft);
|
||||
|
||||
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, talentOptionCenterGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
|
||||
foreach (TalentPrefab talent in talentOption.Talents.OrderBy(t => t.Identifier))
|
||||
@@ -1929,6 +2006,7 @@ namespace Barotrauma
|
||||
ToolTip = RichString.Rich(talent.DisplayName + "\n\n" + talent.Description),
|
||||
UserData = talent.Identifier,
|
||||
PressedColor = pressedColor,
|
||||
Enabled = controlledCharacter != null,
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
// deselect other buttons in tier by removing their selected talents from pool
|
||||
@@ -1961,7 +2039,7 @@ namespace Barotrauma
|
||||
},
|
||||
};
|
||||
|
||||
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = Color.Transparent;
|
||||
talentButton.Color = talentButton.HoverColor = talentButton.PressedColor = talentButton.SelectedColor = talentButton.DisabledColor = Color.Transparent;
|
||||
|
||||
GUIComponent iconImage;
|
||||
if (talent.Icon is null)
|
||||
@@ -1971,6 +2049,7 @@ namespace Barotrauma
|
||||
OutlineColor = GUIStyle.Red,
|
||||
TextColor = GUIStyle.Red,
|
||||
PressedColor = unselectableColor,
|
||||
DisabledColor = unselectableColor,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
}
|
||||
@@ -1979,63 +2058,63 @@ namespace Barotrauma
|
||||
iconImage = new GUIImage(new RectTransform(Vector2.One, talentButton.RectTransform, anchor: Anchor.Center), sprite: talent.Icon, scaleToFit: true)
|
||||
{
|
||||
PressedColor = unselectableColor,
|
||||
DisabledColor = unselectableColor * 0.5f,
|
||||
CanBeFocused = false,
|
||||
};
|
||||
}
|
||||
|
||||
iconImage.Enabled = talentButton.Enabled;
|
||||
talentButtons.Add((talentButton, iconImage));
|
||||
}
|
||||
|
||||
talentCornerIcons.Add((subTree.Identifier, i, cornerIcon, talentBackground, talentBackgroundHighlight));
|
||||
talentCornerIcons.Add((subTree.Identifier, i, cornerIcon, talentBackground, talentBackgroundHighlight));
|
||||
}
|
||||
}
|
||||
}
|
||||
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
|
||||
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
|
||||
|
||||
GUILayoutGroup talentBottomFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true) { RelativeSpacing = 0.01f };
|
||||
GUILayoutGroup talentBottomFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.07f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true) { RelativeSpacing = 0.01f };
|
||||
|
||||
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), talentBottomFrame.RectTransform));
|
||||
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null);
|
||||
GUILayoutGroup experienceLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.59f, 1f), talentBottomFrame.RectTransform));
|
||||
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.5f), experienceLayout.RectTransform), style: null);
|
||||
|
||||
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
|
||||
barSize: controlledCharacter.Info.GetProgressTowardsNextLevel(), color: GUIStyle.Green)
|
||||
{
|
||||
IsHorizontal = true,
|
||||
};
|
||||
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
|
||||
barSize: info.GetProgressTowardsNextLevel(), color: GUIStyle.Green)
|
||||
{
|
||||
IsHorizontal = true,
|
||||
};
|
||||
|
||||
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
Shadow = true,
|
||||
ToolTip = TextManager.Get("experiencetooltip")
|
||||
};
|
||||
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceBarFrame.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
Shadow = true,
|
||||
ToolTip = TextManager.Get("experiencetooltip")
|
||||
};
|
||||
|
||||
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true };
|
||||
talentPointText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), experienceLayout.RectTransform, anchor: Anchor.Center), "", font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight) { AutoScaleVertical = true };
|
||||
|
||||
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ResetTalentSelection
|
||||
};
|
||||
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ApplyTalentSelection,
|
||||
};
|
||||
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
|
||||
talentResetButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("reset"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ResetTalentSelection
|
||||
};
|
||||
talentApplyButton = new GUIButton(new RectTransform(new Vector2(0.19f, 1f), talentBottomFrame.RectTransform), text: TextManager.Get("applysettingsbutton"), style: "GUIButtonFreeScale")
|
||||
{
|
||||
OnClicked = ApplyTalentSelection,
|
||||
};
|
||||
GUITextBlock.AutoScaleAndNormalize(talentResetButton.TextBlock, talentApplyButton.TextBlock);
|
||||
}
|
||||
|
||||
UpdateTalentInfo();
|
||||
}
|
||||
|
||||
private void CreateTalentSkillList(Character character, GUIListBox parent)
|
||||
private void CreateTalentSkillList(Character character, CharacterInfo info, GUIListBox parent)
|
||||
{
|
||||
parent.Content.ClearChildren();
|
||||
List<GUITextBlock> skillNames = new List<GUITextBlock>();
|
||||
foreach (Skill skill in character.Info.Job.GetSkills())
|
||||
foreach (Skill skill in info.Job.GetSkills())
|
||||
{
|
||||
GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = false };
|
||||
|
||||
skillNames.Add(new GUITextBlock(new RectTransform(new Vector2(0.7f, 1f), skillContainer.RectTransform), TextManager.Get($"skillname.{skill.Identifier}").Fallback(skill.Identifier.Value)));
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), skillContainer.RectTransform), Math.Floor(skill.Level).ToString("F0"), textAlignment: Alignment.CenterRight) { Padding = new Vector4(0, 0, 4, 0) };
|
||||
|
||||
float modifiedSkillLevel = character.GetSkillLevel(skill.Identifier);
|
||||
float modifiedSkillLevel = character?.GetSkillLevel(skill.Identifier) ?? skill.Level;
|
||||
if (!MathUtils.NearlyEqual(MathF.Floor(modifiedSkillLevel), MathF.Floor(skill.Level)))
|
||||
{
|
||||
int skillChange = (int)MathF.Floor(modifiedSkillLevel - skill.Level);
|
||||
@@ -2129,7 +2208,7 @@ namespace Barotrauma
|
||||
talentButton.icon.HoverColor = hoverColor;
|
||||
}
|
||||
|
||||
CreateTalentSkillList(controlledCharacter, skillListBox);
|
||||
CreateTalentSkillList(controlledCharacter, controlledCharacter.Info, skillListBox);
|
||||
}
|
||||
|
||||
private void ApplyTalents(Character controlledCharacter)
|
||||
@@ -2157,6 +2236,7 @@ namespace Barotrauma
|
||||
private bool ResetTalentSelection(GUIButton guiButton, object userData)
|
||||
{
|
||||
Character controlledCharacter = Character.Controlled;
|
||||
if (controlledCharacter?.Info == null) { return false; }
|
||||
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
|
||||
UpdateTalentInfo();
|
||||
return true;
|
||||
|
||||
@@ -546,6 +546,10 @@ namespace Barotrauma
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
#if DEBUG
|
||||
LevelGenerationParams.CheckValidity();
|
||||
#endif
|
||||
|
||||
MainMenuScreen.Select();
|
||||
|
||||
foreach (Identifier steamError in SteamManager.InitializationErrors)
|
||||
@@ -1033,11 +1037,6 @@ namespace Barotrauma
|
||||
{
|
||||
GUI.SetSavingIndicatorState(true);
|
||||
|
||||
if (GameSession.Submarine != null && !GameSession.Submarine.Removed)
|
||||
{
|
||||
GameSession.SubmarineInfo = new SubmarineInfo(GameSession.Submarine);
|
||||
}
|
||||
|
||||
// Update store stock when saving and quitting in an outpost (normally updated when CampaignMode.End() is called)
|
||||
if (GameSession?.Campaign is SinglePlayerCampaign spCampaign && Level.IsLoadedOutpost && spCampaign.Map?.CurrentLocation != null && spCampaign.CargoManager != null)
|
||||
{
|
||||
|
||||
@@ -81,7 +81,6 @@ namespace Barotrauma
|
||||
: this(isSinglePlayer)
|
||||
{
|
||||
AddCharacterElements(element);
|
||||
ActiveOrdersElement = element.GetChildElement("activeorders");
|
||||
}
|
||||
|
||||
partial void InitProjectSpecific()
|
||||
@@ -3661,9 +3660,9 @@ namespace Barotrauma
|
||||
crewList.ClearChildren();
|
||||
}
|
||||
|
||||
public void Save(XElement parentElement)
|
||||
public XElement Save(XElement parentElement)
|
||||
{
|
||||
XElement element = new XElement("crew");
|
||||
var element = new XElement("crew");
|
||||
for (int i = 0; i < characterInfos.Count; i++)
|
||||
{
|
||||
var ci = characterInfos[i];
|
||||
@@ -3674,8 +3673,8 @@ namespace Barotrauma
|
||||
infoElement.Add(new XAttribute("crewlistindex", ci.CrewListIndex));
|
||||
if (ci.LastControlled) { infoElement.Add(new XAttribute("lastcontrolled", true)); }
|
||||
}
|
||||
SaveActiveOrders(element);
|
||||
parentElement.Add(element);
|
||||
parentElement?.Add(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
public static void ClientReadActiveOrders(IReadMessage inc)
|
||||
|
||||
@@ -13,6 +13,11 @@ namespace Barotrauma
|
||||
|
||||
partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged)
|
||||
{
|
||||
if (Owner is Some<Character> { Value: var character })
|
||||
{
|
||||
if (!character.IsPlayer) { return; }
|
||||
}
|
||||
|
||||
CampaignMode campaign = GameMain.GameSession?.Campaign;
|
||||
WalletChangedData data = new WalletChangedData
|
||||
{
|
||||
|
||||
@@ -92,6 +92,7 @@ namespace Barotrauma
|
||||
break;
|
||||
case "crew":
|
||||
GameMain.GameSession.CrewManager = new CrewManager(subElement, true);
|
||||
ActiveOrdersElement = element.GetChildElement("activeorders");
|
||||
break;
|
||||
case "map":
|
||||
map = Map.Load(this, subElement, Settings);
|
||||
@@ -242,11 +243,10 @@ namespace Barotrauma
|
||||
crewDead = false;
|
||||
endTimer = 5.0f;
|
||||
CrewManager.InitSinglePlayerRound();
|
||||
if (petsElement != null)
|
||||
{
|
||||
PetBehavior.LoadPets(petsElement);
|
||||
}
|
||||
CrewManager.LoadActiveOrders();
|
||||
LoadPets();
|
||||
LoadActiveOrders();
|
||||
|
||||
CargoManager.InitPurchasedIDCards();
|
||||
|
||||
GUI.DisableSavingIndicatorDelayed();
|
||||
}
|
||||
@@ -461,41 +461,7 @@ namespace Barotrauma
|
||||
|
||||
if (success)
|
||||
{
|
||||
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
|
||||
{
|
||||
Submarine.MainSub = leavingSub;
|
||||
GameMain.GameSession.Submarine = leavingSub;
|
||||
GameMain.GameSession.SubmarineInfo = leavingSub.Info;
|
||||
leavingSub.Info.FilePath = System.IO.Path.Combine(SaveUtil.TempPath, leavingSub.Info.Name + ".sub");
|
||||
var subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);
|
||||
GameMain.GameSession.OwnedSubmarines.Add(leavingSub.Info);
|
||||
foreach (Submarine sub in subsToLeaveBehind)
|
||||
{
|
||||
GameMain.GameSession.OwnedSubmarines.RemoveAll(s => s != leavingSub.Info && s.Name == sub.Info.Name);
|
||||
MapEntity.mapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine);
|
||||
LinkedSubmarine.CreateDummy(leavingSub, sub);
|
||||
}
|
||||
}
|
||||
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
|
||||
if (PendingSubmarineSwitch != null)
|
||||
{
|
||||
SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo;
|
||||
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
|
||||
|
||||
for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++)
|
||||
{
|
||||
if (GameMain.GameSession.OwnedSubmarines[i].Name == previousSub.Name)
|
||||
{
|
||||
GameMain.GameSession.OwnedSubmarines[i] = previousSub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
PendingSubmarineSwitch = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -766,11 +732,10 @@ namespace Barotrauma
|
||||
c.Info.SaveOrderData();
|
||||
}
|
||||
|
||||
petsElement = new XElement("pets");
|
||||
PetBehavior.SavePets(petsElement);
|
||||
modeElement.Add(petsElement);
|
||||
SavePets(modeElement);
|
||||
var crewManagerElement = CrewManager.Save(modeElement);
|
||||
SaveActiveOrders(crewManagerElement);
|
||||
|
||||
CrewManager.Save(modeElement);
|
||||
CampaignMetadata.Save(modeElement);
|
||||
Map.Save(modeElement);
|
||||
CargoManager?.SavePurchasedItems(modeElement);
|
||||
|
||||
@@ -257,7 +257,7 @@ namespace Barotrauma.Tutorials
|
||||
yield return new WaitForSeconds(2.0f);
|
||||
}*/
|
||||
|
||||
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Medical supplies objective
|
||||
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect)); // Medical supplies objective
|
||||
|
||||
do
|
||||
{
|
||||
|
||||
@@ -275,7 +275,7 @@ namespace Barotrauma.Tutorials
|
||||
do { yield return null; } while (!engineer_equipmentObjectiveSensor.MotionDetected);
|
||||
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Equipment"), ChatMessageType.Radio, null);
|
||||
yield return new WaitForSeconds(0.5f, false);
|
||||
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Retrieve equipment
|
||||
TriggerTutorialSegment(0, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect)); // Retrieve equipment
|
||||
bool firstSlotRemoved = false;
|
||||
bool secondSlotRemoved = false;
|
||||
bool thirdSlotRemoved = false;
|
||||
|
||||
@@ -330,7 +330,7 @@ namespace Barotrauma.Tutorials
|
||||
yield return new WaitForSeconds(0.0f, false);
|
||||
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Equipment"), ChatMessageType.Radio, null);
|
||||
do { yield return null; } while (!mechanic_equipmentObjectiveSensor.MotionDetected);
|
||||
TriggerTutorialSegment(1, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Equipment & inventory objective
|
||||
TriggerTutorialSegment(1, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Select), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Deselect)); // Equipment & inventory objective
|
||||
SetHighlight(mechanic_equipmentCabinet.Item, true);
|
||||
bool firstSlotRemoved = false;
|
||||
bool secondSlotRemoved = false;
|
||||
@@ -372,7 +372,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
// Room 3
|
||||
do { yield return null; } while (!mechanic_weldingObjectiveSensor.MotionDetected);
|
||||
TriggerTutorialSegment(2, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.ToggleInventory)); // Welding objective
|
||||
TriggerTutorialSegment(2, GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Aim), GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Shoot)); // Welding objective
|
||||
do
|
||||
{
|
||||
if (!mechanic.HasEquippedItem("divingmask".ToIdentifier()))
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Barotrauma.Tutorials
|
||||
Character.Controlled = character;
|
||||
character.GiveJobItems(null);
|
||||
|
||||
var idCard = character.Inventory.FindItemByIdentifier("idcard".ToIdentifier());
|
||||
var idCard = character.Inventory.FindItemByTag("identitycard".ToIdentifier());
|
||||
if (idCard == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Item prefab \"ID Card\" not found!");
|
||||
|
||||
@@ -525,6 +525,7 @@ namespace Barotrauma
|
||||
if (!AccessibleWhenAlive && !character.IsDead && !AccessibleByOwner)
|
||||
{
|
||||
syncItemsDelay = Math.Max(syncItemsDelay - deltaTime, 0.0f);
|
||||
doubleClickedItems.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -931,7 +932,7 @@ namespace Barotrauma
|
||||
// Move the item from the subinventory to the selected container
|
||||
return QuickUseAction.PutToContainer;
|
||||
}
|
||||
else
|
||||
else if (character.Inventory.AccessibleWhenAlive || character.Inventory.AccessibleByOwner)
|
||||
{
|
||||
// Take from the subinventory and place it in the character's main inventory if no target container is selected
|
||||
return QuickUseAction.TakeFromContainer;
|
||||
@@ -959,7 +960,8 @@ namespace Barotrauma
|
||||
}
|
||||
else if (character.SelectedBy?.Inventory != null &&
|
||||
Character.Controlled == character.SelectedBy &&
|
||||
!character.SelectedBy.Inventory.Locked &&
|
||||
!character.SelectedBy.Inventory.Locked &&
|
||||
(character.SelectedBy.Inventory.AccessibleWhenAlive || character.SelectedBy.Inventory.AccessibleByOwner) &&
|
||||
allowInventorySwap)
|
||||
{
|
||||
return QuickUseAction.TakeFromCharacter;
|
||||
|
||||
@@ -66,6 +66,9 @@ namespace Barotrauma.Items.Components
|
||||
rect.Height = (int)(rect.Height * (1.0f - openState));
|
||||
}
|
||||
|
||||
//only merge the door's convex hull with overlapping wall segments if it's fully open or fully closed
|
||||
//it's the heaviest part of changing the convex hull, and doesn't need to be done while the door is still in motion
|
||||
bool mergeOverlappingSegments = openState <= 0.0f || openState >= 1.0f;
|
||||
if (Window.Height > 0 && Window.Width > 0)
|
||||
{
|
||||
if (IsHorizontal)
|
||||
@@ -88,7 +91,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
convexHull2.Enabled = true;
|
||||
convexHull2.SetVertices(GetConvexHullCorners(rect2));
|
||||
convexHull2.SetVertices(GetConvexHullCorners(rect2), mergeOverlappingSegments);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,7 +115,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
convexHull2.Enabled = true;
|
||||
convexHull2.SetVertices(GetConvexHullCorners(rect2));
|
||||
convexHull2.SetVertices(GetConvexHullCorners(rect2), mergeOverlappingSegments);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +130,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
convexHull.Enabled = true;
|
||||
convexHull.SetVertices(GetConvexHullCorners(rect));
|
||||
convexHull.SetVertices(GetConvexHullCorners(rect), mergeOverlappingSegments);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Linq;
|
||||
@@ -78,7 +79,7 @@ namespace Barotrauma.Items.Components
|
||||
activateButton = new GUIButton(new RectTransform(new Vector2(0.95f, 0.8f), buttonContainer.RectTransform), TextManager.Get("DeconstructorDeconstruct"), style: "DeviceButton")
|
||||
{
|
||||
TextBlock = { AutoScaleHorizontal = true },
|
||||
OnClicked = ToggleActive
|
||||
OnClicked = OnActivateButtonClicked
|
||||
};
|
||||
inSufficientPowerWarning = new GUITextBlock(new RectTransform(Vector2.One, activateButton.RectTransform),
|
||||
TextManager.Get("DeconstructorNoPower"), textColor: GUIStyle.Orange, textAlignment: Alignment.Center, color: Color.Black, style: "OuterGlow", wrap: true)
|
||||
@@ -164,7 +165,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
activateButton.Enabled = outputsFound;
|
||||
activateButton.Enabled = outputsFound || !InputContainer.Inventory.IsEmpty();
|
||||
activateButton.Text = TextManager.Get(ActivateButtonText);
|
||||
};
|
||||
}
|
||||
@@ -236,8 +237,19 @@ namespace Barotrauma.Items.Components
|
||||
inSufficientPowerWarning.Visible = IsActive && !hasPower;
|
||||
}
|
||||
|
||||
private bool ToggleActive(GUIButton button, object obj)
|
||||
private bool OnActivateButtonClicked(GUIButton button, object obj)
|
||||
{
|
||||
var disallowedItem = inputContainer.Inventory.FindItem(i => !i.AllowDeconstruct, recursive: false);
|
||||
if (disallowedItem != null)
|
||||
{
|
||||
int index = inputContainer.Inventory.FindIndex(disallowedItem);
|
||||
if (index >= 0 && index < inputContainer.Inventory.visualSlots.Length)
|
||||
{
|
||||
var slot = inputContainer.Inventory.visualSlots[index];
|
||||
slot?.ShowBorderHighlight(GUIStyle.Red, 0.1f, 0.9f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
pendingState = !IsActive;
|
||||
@@ -247,7 +259,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
SetActive(!IsActive, Character.Controlled);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1367,6 +1367,15 @@ namespace Barotrauma.Items.Components
|
||||
pingRadius, prevPingRadius,
|
||||
250.0f, 150.0f, range, pingStrength, passive);
|
||||
}
|
||||
if (pingSource.Y - Level.Loaded.BottomPos < range)
|
||||
{
|
||||
CreateBlipsForLine(
|
||||
new Vector2(pingSource.X - range, Level.Loaded.BottomPos),
|
||||
new Vector2(pingSource.X + range, Level.Loaded.BottomPos),
|
||||
pingSource, transducerPos,
|
||||
pingRadius, prevPingRadius,
|
||||
250.0f, 150.0f, range, pingStrength, passive);
|
||||
}
|
||||
|
||||
List<Voronoi2.VoronoiCell> cells = Level.Loaded.GetCells(pingSource, 7);
|
||||
foreach (Voronoi2.VoronoiCell cell in cells)
|
||||
|
||||
@@ -23,10 +23,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
#endif
|
||||
|
||||
private List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
||||
private List<ParticleEmitter> particleEmitterHitStructure = new List<ParticleEmitter>();
|
||||
private List<ParticleEmitter> particleEmitterHitCharacter = new List<ParticleEmitter>();
|
||||
private List<Pair<RelatedItem, ParticleEmitter>> particleEmitterHitItem = new List<Pair<RelatedItem, ParticleEmitter>>();
|
||||
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
||||
private readonly List<ParticleEmitter> particleEmitterHitStructure = new List<ParticleEmitter>();
|
||||
private readonly List<ParticleEmitter> particleEmitterHitCharacter = new List<ParticleEmitter>();
|
||||
private readonly List<(RelatedItem relatedItem, ParticleEmitter emitter)> particleEmitterHitItem = new List<(RelatedItem relatedItem, ParticleEmitter emitter)>();
|
||||
|
||||
private float prevProgressBarState;
|
||||
private Item prevProgressBarTarget = null;
|
||||
@@ -46,10 +46,7 @@ namespace Barotrauma.Items.Components
|
||||
Identifier[] excludedIdentifiers = subElement.GetAttributeIdentifierArray("excludedidentifiers", Array.Empty<Identifier>());
|
||||
if (excludedIdentifiers.Length == 0) { excludedIdentifiers = subElement.GetAttributeIdentifierArray("excludedidentifier", Array.Empty<Identifier>()); }
|
||||
|
||||
particleEmitterHitItem.Add(
|
||||
new Pair<RelatedItem, ParticleEmitter>(
|
||||
new RelatedItem(identifiers, excludedIdentifiers),
|
||||
new ParticleEmitter(subElement)));
|
||||
particleEmitterHitItem.Add((new RelatedItem(identifiers, excludedIdentifiers), new ParticleEmitter(subElement)));
|
||||
break;
|
||||
case "particleemitterhitstructure":
|
||||
particleEmitterHitStructure.Add(new ParticleEmitter(subElement));
|
||||
@@ -139,11 +136,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Vector2 particlePos = ConvertUnits.ToDisplayUnits(pickedPosition);
|
||||
if (targetItem.Submarine != null) particlePos += targetItem.Submarine.DrawPosition;
|
||||
foreach (var emitter in particleEmitterHitItem)
|
||||
foreach ((RelatedItem relatedItem, ParticleEmitter emitter) in particleEmitterHitItem)
|
||||
{
|
||||
if (!emitter.First.MatchesItem(targetItem)) { continue; }
|
||||
if (!relatedItem.MatchesItem(targetItem)) { continue; }
|
||||
float particleAngle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
emitter.Second.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
|
||||
emitter.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Barotrauma.Items.Components
|
||||
int totalWireCount = 0;
|
||||
foreach (Connection c in panel.Connections)
|
||||
{
|
||||
totalWireCount += c.Wires.Count(w => w != null);
|
||||
totalWireCount += c.Wires.Count;
|
||||
}
|
||||
|
||||
Wire equippedWire = null;
|
||||
@@ -87,8 +87,8 @@ namespace Barotrauma.Items.Components
|
||||
(DraggingConnected.Connections[0] == null && DraggingConnected.Connections[1] == null) ||
|
||||
(DraggingConnected.Connections.Contains(c) && DraggingConnected.Connections.Contains(null)))
|
||||
{
|
||||
int linkIndex = c.FindWireIndex(DraggingConnected.Item);
|
||||
if (linkIndex > -1 || panel.DisconnectedWires.Contains(DraggingConnected))
|
||||
var linkedWire = c.FindWireByItem(DraggingConnected.Item);
|
||||
if (linkedWire != null || panel.DisconnectedWires.Contains(DraggingConnected))
|
||||
{
|
||||
Inventory.DraggingItems.Clear();
|
||||
Inventory.DraggingItems.Add(DraggingConnected.Item);
|
||||
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
|
||||
c.DrawWires(spriteBatch, panel, rightPos, rightWirePos, mouseInRect, equippedWire, wireInterval);
|
||||
}
|
||||
rightPos.Y += connectorIntervalLeft;
|
||||
rightWirePos.Y += c.Wires.Count(w => w != null) * wireInterval;
|
||||
rightWirePos.Y += c.Wires.Count * wireInterval;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -121,7 +121,7 @@ namespace Barotrauma.Items.Components
|
||||
c.DrawWires(spriteBatch, panel, leftPos, leftWirePos, mouseInRect, equippedWire, wireInterval);
|
||||
}
|
||||
leftPos.Y += connectorIntervalRight;
|
||||
leftWirePos.Y += c.Wires.Count(w => w != null) * wireInterval;
|
||||
leftWirePos.Y += c.Wires.Count * wireInterval;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,15 +228,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
float connectorSpriteScale = (35.0f / connectionSprite.SourceRect.Width) * panel.Scale;
|
||||
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
if (wires[i] == null || wires[i].Hidden || (DraggingConnected == wires[i] && (mouseIn || Screen.Selected == GameMain.SubEditorScreen))) { continue; }
|
||||
if (wires[i].HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; }
|
||||
if (wire.Hidden || (DraggingConnected == wire && (mouseIn || Screen.Selected == GameMain.SubEditorScreen))) { continue; }
|
||||
if (wire.HiddenInGame && Screen.Selected == GameMain.GameScreen) { continue; }
|
||||
|
||||
Connection recipient = wires[i].OtherConnection(this);
|
||||
Connection recipient = wire.OtherConnection(this);
|
||||
LocalizedString label = recipient == null ? "" : recipient.item.Name + $" ({recipient.DisplayName})";
|
||||
if (wires[i].Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
|
||||
DrawWire(spriteBatch, wires[i], position, wirePosition, equippedWire, panel, label);
|
||||
if (wire.Locked) { label += "\n" + TextManager.Get("ConnectionLocked"); }
|
||||
DrawWire(spriteBatch, wire, position, wirePosition, equippedWire, panel, label);
|
||||
|
||||
wirePosition.Y += wireInterval;
|
||||
}
|
||||
@@ -248,18 +248,17 @@ namespace Barotrauma.Items.Components
|
||||
if (!PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
if ((GameMain.NetworkMember != null || panel.CheckCharacterSuccess(Character.Controlled)) &&
|
||||
Wires.Count(w => w != null) < MaxPlayerConnectableWires)
|
||||
Wires.Count < MaxPlayerConnectableWires)
|
||||
{
|
||||
//find an empty cell for the new connection
|
||||
int index = FindEmptyIndex();
|
||||
if (index > -1 && !Wires.Contains(DraggingConnected))
|
||||
if (WireSlotsAvailable() && !Wires.Contains(DraggingConnected))
|
||||
{
|
||||
bool alreadyConnected = DraggingConnected.IsConnectedTo(panel.Item);
|
||||
DraggingConnected.RemoveConnection(panel.Item);
|
||||
if (DraggingConnected.Connect(this, !alreadyConnected, true))
|
||||
{
|
||||
var otherConnection = DraggingConnected.OtherConnection(this);
|
||||
SetWire(index, DraggingConnected);
|
||||
ConnectWire(DraggingConnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,7 +283,7 @@ namespace Barotrauma.Items.Components
|
||||
flashColor * (float)Math.Sin(FlashTimer % flashCycleDuration / flashCycleDuration * MathHelper.Pi * 0.8f), scale: connectorSpriteScale);
|
||||
}
|
||||
|
||||
if (Wires.Any(w => w != null && w != DraggingConnected && !w.Hidden && (!w.HiddenInGame || Screen.Selected != GameMain.GameScreen)))
|
||||
if (Wires.Any(w => w != DraggingConnected && !w.Hidden && (!w.HiddenInGame || Screen.Selected != GameMain.GameScreen)))
|
||||
{
|
||||
int screwIndex = (int)Math.Floor(position.Y / 30.0f) % screwSprites.Count;
|
||||
screwSprites[screwIndex].Draw(spriteBatch, position, scale: connectorSpriteScale);
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
{
|
||||
if (item.Submarine == null || item.Submarine.Loading || Screen.Selected != GameMain.SubEditorScreen) { return; }
|
||||
MoveConnectedWires(amount);
|
||||
@@ -173,9 +173,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void ApplyRemoteState(IReadMessage msg)
|
||||
{
|
||||
List<Wire> prevWires = Connections.SelectMany(c => c.Wires.Where(w => w != null)).ToList();
|
||||
List<Wire> newWires = new List<Wire>();
|
||||
|
||||
List<Wire> prevWires = Connections.SelectMany(c => c.Wires).ToList();
|
||||
|
||||
ushort userID = msg.ReadUInt16();
|
||||
|
||||
if (userID == 0)
|
||||
@@ -195,7 +194,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (Connection connection in Connections)
|
||||
{
|
||||
for (int i = 0; i < connection.MaxWires; i++)
|
||||
HashSet<Wire> newWires = new HashSet<Wire>();
|
||||
uint wireCount = msg.ReadVariableUInt32();
|
||||
for (int i = 0; i < wireCount; i++)
|
||||
{
|
||||
ushort wireId = msg.ReadUInt16();
|
||||
|
||||
@@ -204,9 +205,18 @@ namespace Barotrauma.Items.Components
|
||||
if (wireComponent == null) { continue; }
|
||||
|
||||
newWires.Add(wireComponent);
|
||||
}
|
||||
|
||||
connection.SetWire(i, wireComponent);
|
||||
wireComponent.Connect(connection, false);
|
||||
Wire[] oldWires = connection.Wires.Where(w => !newWires.Contains(w)).ToArray();
|
||||
foreach (var wire in oldWires)
|
||||
{
|
||||
connection.DisconnectWire(wire);
|
||||
}
|
||||
|
||||
foreach (var wire in newWires.Where(w => !connection.Wires.Contains(w)).ToArray())
|
||||
{
|
||||
connection.ConnectWire(wire);
|
||||
wire.Connect(connection, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (var uiElement in uiElements)
|
||||
{
|
||||
if (!(uiElement.UserData is CustomInterfaceElement element)) { continue; }
|
||||
bool visible = Screen.Selected == GameMain.SubEditorScreen || element.StatusEffects.Any() || element.HasPropertyName || (element.Connection != null && element.Connection.Wires.Any(w => w != null));
|
||||
bool visible = Screen.Selected == GameMain.SubEditorScreen || element.StatusEffects.Any() || element.HasPropertyName || (element.Connection != null && element.Connection.Wires.Count > 0);
|
||||
if (visible) { visibleElementCount++; }
|
||||
if (uiElement.Visible != visible)
|
||||
{
|
||||
|
||||
@@ -526,7 +526,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
{
|
||||
//only used in the sub editor, hence only in the client project
|
||||
if (!item.IsSelected) { return; }
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
{
|
||||
widgets.Clear();
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class DockingPort : ItemComponent, IDrawableComponent, IServerSerializable
|
||||
partial class DockingPort : ItemComponent, IDrawableComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private GUIMessageBox autodockingVerification;
|
||||
|
||||
public Vector2 DrawSize
|
||||
{
|
||||
//use the extents of the item as the draw size
|
||||
@@ -180,5 +182,10 @@ namespace Barotrauma.Items.Components
|
||||
Undock();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write((byte)allowOutpostAutoDocking);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
LocalizedString description = item.Description;
|
||||
if (item.Prefab.Identifier == "idcard" || item.Tags.Contains("despawncontainer"))
|
||||
if (item.HasTag("identitycard") || item.HasTag("despawncontainer"))
|
||||
{
|
||||
string[] readTags = item.Tags.Split(',');
|
||||
string idName = null;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.MapCreatures.Behavior;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
@@ -6,13 +8,8 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.MapCreatures.Behavior;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Contacts;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -720,7 +717,7 @@ namespace Barotrauma
|
||||
//remove identifiers from the available container tags
|
||||
//(otherwise the list will include many irrelevant options,
|
||||
//e.g. "weldingtool" because a welding fuel tank can be placed inside the container, etc)
|
||||
.Where(t => !ItemPrefab.Prefabs.Any(ip => ip.Identifier == t))
|
||||
.Where(t => !ItemPrefab.Prefabs.ContainsKey(t))
|
||||
.ToImmutableHashSet();
|
||||
new GUIButton(new RectTransform(new Vector2(0.1f, 1), tagsField.RectTransform, Anchor.TopRight), "...")
|
||||
{
|
||||
@@ -1174,7 +1171,7 @@ namespace Barotrauma
|
||||
texts.Clear();
|
||||
|
||||
string nameText = Name;
|
||||
if (Prefab.Identifier == "idcard" || Tags.Contains("despawncontainer"))
|
||||
if (Prefab.Tags.Contains("identitycard") || Tags.Contains("despawncontainer"))
|
||||
{
|
||||
string[] readTags = Tags.Split(',');
|
||||
string idName = null;
|
||||
|
||||
@@ -80,15 +80,17 @@ namespace Barotrauma
|
||||
}
|
||||
Vector2 center = new Vector2((minX + maxX) / 2.0f, (minY + maxY) / 2.0f);
|
||||
if (Submarine.MainSub != null) { center -= Submarine.MainSub.HiddenSubPosition; }
|
||||
center.X -= MathUtils.RoundTowardsClosest(center.X, Submarine.GridSize.X);
|
||||
center.Y -= MathUtils.RoundTowardsClosest(center.Y, Submarine.GridSize.Y);
|
||||
|
||||
Vector2 offsetFromGrid = new Vector2(
|
||||
MathUtils.RoundTowardsClosest(center.X, Submarine.GridSize.X) - center.X,
|
||||
MathUtils.RoundTowardsClosest(center.Y, Submarine.GridSize.Y) - center.Y - Submarine.GridSize.Y / 2);
|
||||
|
||||
MapEntity.SelectedList.Clear();
|
||||
assemblyEntities.ForEach(e => MapEntity.AddSelection(e));
|
||||
|
||||
foreach (MapEntity mapEntity in assemblyEntities)
|
||||
{
|
||||
mapEntity.Move(-center);
|
||||
mapEntity.Move(-center - offsetFromGrid);
|
||||
mapEntity.Submarine = Submarine.MainSub;
|
||||
var entityElement = mapEntity.Save(element);
|
||||
if (disabledEntities.Contains(mapEntity))
|
||||
|
||||
@@ -461,7 +461,7 @@ namespace Barotrauma.Lights
|
||||
Matrix.CreateTranslation(-origin.X, -origin.Y, 0.0f) *
|
||||
Matrix.CreateRotationZ(amount) *
|
||||
Matrix.CreateTranslation(origin.X, origin.Y, 0.0f);
|
||||
SetVertices(vertices.Select(v => v.Pos).ToArray(), rotationMatrix);
|
||||
SetVertices(vertices.Select(v => v.Pos).ToArray(), rotationMatrix: rotationMatrix);
|
||||
}
|
||||
|
||||
private void CalculateDimensions()
|
||||
@@ -541,7 +541,7 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
|
||||
public void SetVertices(Vector2[] points, Matrix? rotationMatrix = null)
|
||||
public void SetVertices(Vector2[] points, bool mergeOverlappingSegments = true, Matrix? rotationMatrix = null)
|
||||
{
|
||||
Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported");
|
||||
|
||||
@@ -594,13 +594,16 @@ namespace Barotrauma.Lights
|
||||
|
||||
if (ParentEntity == null) { return; }
|
||||
|
||||
var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
|
||||
if (chList != null)
|
||||
if (mergeOverlappingSegments)
|
||||
{
|
||||
overlappingHulls.Clear();
|
||||
foreach (ConvexHull ch in chList.List)
|
||||
var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
|
||||
if (chList != null)
|
||||
{
|
||||
MergeOverlappingSegments(ch);
|
||||
overlappingHulls.Clear();
|
||||
foreach (ConvexHull ch in chList.List)
|
||||
{
|
||||
MergeOverlappingSegments(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,11 +81,18 @@ namespace Barotrauma
|
||||
}
|
||||
catch (System.IO.FileNotFoundException e)
|
||||
{
|
||||
string errorMsg = "Failed to load sound file \"" + filename + "\".";
|
||||
string errorMsg = "Failed to load sound file \"" + filename + "\" (file not found).";
|
||||
DebugConsole.ThrowError(errorMsg, e);
|
||||
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:FileNotFound" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
return null;
|
||||
}
|
||||
catch (System.IO.InvalidDataException e)
|
||||
{
|
||||
string errorMsg = "Failed to load sound file \"" + filename + "\" (invalid data).";
|
||||
DebugConsole.ThrowError(errorMsg, e);
|
||||
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:InvalidData" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
RoundSound newSound = new RoundSound(element, existingSound);
|
||||
|
||||
@@ -525,7 +525,7 @@ namespace Barotrauma
|
||||
Item.ItemList.Count(it2 => it2.linkedTo.Contains(item) && !item.linkedTo.Contains(it2));
|
||||
for (int i = 0; i < item.Connections.Count; i++)
|
||||
{
|
||||
int wireCount = item.Connections[i].Wires.Count(w => w != null);
|
||||
int wireCount = item.Connections[i].Wires.Count;
|
||||
if (doorLinks + wireCount > item.Connections[i].MaxWires)
|
||||
{
|
||||
errorMsgs.Add(TextManager.GetWithVariables("InsufficientFreeConnectionsWarning",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
@@ -183,6 +184,11 @@ namespace Barotrauma.Networking
|
||||
break;
|
||||
default:
|
||||
GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType, textColor: textColor);
|
||||
if (type == ChatMessageType.Radio && CanUseRadio(senderCharacter, out WifiComponent radio))
|
||||
{
|
||||
Signal s = new Signal(txt, sender: senderCharacter, source: radio.Item);
|
||||
radio.TransmitSignal(s, sentFromChat: true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
LastID = id;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Barotrauma.Steam;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
@@ -11,6 +12,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
@@ -182,6 +184,20 @@ namespace Barotrauma.Networking
|
||||
get { return ownerKey > 0 || steamP2POwner; }
|
||||
}
|
||||
|
||||
internal readonly struct PermissionChangedEvent
|
||||
{
|
||||
public readonly ClientPermissions NewPermissions;
|
||||
public readonly ImmutableArray<string> NewPermittedConsoleCommands;
|
||||
|
||||
public PermissionChangedEvent(ClientPermissions newPermissions, IReadOnlyList<string> newPermittedConsoleCommands)
|
||||
{
|
||||
NewPermissions = newPermissions;
|
||||
NewPermittedConsoleCommands = newPermittedConsoleCommands.ToImmutableArray();
|
||||
}
|
||||
}
|
||||
|
||||
public readonly NamedEvent<PermissionChangedEvent> OnPermissionChanged = new NamedEvent<PermissionChangedEvent>();
|
||||
|
||||
public GameClient(string newName, string ip, UInt64 steamId, string serverName = null, int ownerKey = 0, bool steamP2POwner = false)
|
||||
{
|
||||
//TODO: gui stuff should probably not be here?
|
||||
@@ -570,7 +586,12 @@ namespace Barotrauma.Networking
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
#if DEBUG
|
||||
if (PlayerInput.GetKeyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.P)) return;
|
||||
if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.P)) return;
|
||||
|
||||
if (PlayerInput.KeyHit(Keys.Home))
|
||||
{
|
||||
OnPermissionChanged.Invoke(new PermissionChangedEvent(permissions, permittedConsoleCommands));
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach (Client c in ConnectedClients)
|
||||
@@ -1019,40 +1040,25 @@ namespace Barotrauma.Networking
|
||||
GameMain.GameSession.EnforceMissionOrder(serverMissionIdentifiers);
|
||||
}
|
||||
|
||||
byte equalityCheckValueCount = inc.ReadByte();
|
||||
List<int> levelEqualityCheckValues = new List<int>();
|
||||
for (int i = 0; i < equalityCheckValueCount; i++)
|
||||
var levelEqualityCheckValues = new Dictionary<Level.LevelGenStage, int>();
|
||||
foreach (Level.LevelGenStage stage in Enum.GetValues(typeof(Level.LevelGenStage)).OfType<Level.LevelGenStage>().OrderBy(s => s))
|
||||
{
|
||||
levelEqualityCheckValues.Add(inc.ReadInt32());
|
||||
levelEqualityCheckValues.Add(stage, inc.ReadInt32());
|
||||
}
|
||||
|
||||
if (Level.Loaded.EqualityCheckValues.Count != levelEqualityCheckValues.Count)
|
||||
foreach (var stage in levelEqualityCheckValues.Keys)
|
||||
{
|
||||
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" +
|
||||
" (client value count: " + Level.Loaded.EqualityCheckValues.Count +
|
||||
", level value count: " + levelEqualityCheckValues.Count +
|
||||
", seed: " + Level.Loaded.Seed +
|
||||
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation + ")" +
|
||||
", mirrored: " + Level.Loaded.Mirrored + ").";
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < equalityCheckValueCount; i++)
|
||||
if (Level.Loaded.EqualityCheckValues[stage] != levelEqualityCheckValues[stage])
|
||||
{
|
||||
if (Level.Loaded.EqualityCheckValues[i] != levelEqualityCheckValues[i])
|
||||
{
|
||||
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" +
|
||||
" (client value #" + i + ": " + Level.Loaded.EqualityCheckValues[i] +
|
||||
", server value #" + i + ": " + levelEqualityCheckValues[i].ToString("X") +
|
||||
", level value count: " + levelEqualityCheckValues.Count +
|
||||
", seed: " + Level.Loaded.Seed +
|
||||
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation + ")" +
|
||||
", mirrored: " + Level.Loaded.Mirrored + ").";
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
string errorMsg = "Level equality check failed. The level generated at your end doesn't match the level generated by the server" +
|
||||
" (client value " + stage + ": " + Level.Loaded.EqualityCheckValues[stage].ToString("X") +
|
||||
", server value " + stage + ": " + levelEqualityCheckValues[stage].ToString("X") +
|
||||
", level value count: " + levelEqualityCheckValues.Count +
|
||||
", seed: " + Level.Loaded.Seed +
|
||||
", sub: " + Submarine.MainSub.Info.Name + " (" + Submarine.MainSub.Info.MD5Hash.ShortRepresentation + ")" +
|
||||
", mirrored: " + Level.Loaded.Mirrored + ").";
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:LevelsDontMatch" + Level.Loaded.Seed, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1076,6 +1082,7 @@ namespace Barotrauma.Networking
|
||||
reconnectBox?.Close();
|
||||
reconnectBox = null;
|
||||
|
||||
GameMain.ModDownloadScreen.Reset();
|
||||
ContentPackageManager.EnabledPackages.Restore();
|
||||
|
||||
GUI.ClearCursorWait();
|
||||
@@ -1380,18 +1387,13 @@ namespace Barotrauma.Networking
|
||||
private void SetMyPermissions(ClientPermissions newPermissions, IEnumerable<string> permittedConsoleCommands)
|
||||
{
|
||||
if (!(this.permittedConsoleCommands.Any(c => !permittedConsoleCommands.Contains(c)) ||
|
||||
permittedConsoleCommands.Any(c => !this.permittedConsoleCommands.Contains(c))))
|
||||
permittedConsoleCommands.Any(c => !this.permittedConsoleCommands.Contains(c))))
|
||||
{
|
||||
if (newPermissions == permissions) return;
|
||||
}
|
||||
|
||||
bool refreshCampaignUI = false;
|
||||
|
||||
if (permissions.HasFlag(ClientPermissions.ManageCampaign) != newPermissions.HasFlag(ClientPermissions.ManageCampaign) ||
|
||||
permissions.HasFlag(ClientPermissions.ManageRound) != newPermissions.HasFlag(ClientPermissions.ManageRound))
|
||||
{
|
||||
refreshCampaignUI = true;
|
||||
}
|
||||
bool refreshCampaignUI = permissions.HasFlag(ClientPermissions.ManageCampaign) != newPermissions.HasFlag(ClientPermissions.ManageCampaign) ||
|
||||
permissions.HasFlag(ClientPermissions.ManageRound) != newPermissions.HasFlag(ClientPermissions.ManageRound);
|
||||
|
||||
permissions = newPermissions;
|
||||
this.permittedConsoleCommands = new List<string>(permittedConsoleCommands);
|
||||
@@ -1430,7 +1432,7 @@ namespace Barotrauma.Networking
|
||||
if (newPermissions.HasFlag(ClientPermissions.ConsoleCommands))
|
||||
{
|
||||
var commandsLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), rightColumn.RectTransform),
|
||||
TextManager.Get("PermittedConsoleCommands"), wrap: true, font: GUIStyle.SubHeadingFont);
|
||||
TextManager.Get("PermittedConsoleCommands"), wrap: true, font: GUIStyle.SubHeadingFont);
|
||||
var commandList = new GUIListBox(new RectTransform(new Vector2(1.0f, 1.0f), rightColumn.RectTransform));
|
||||
foreach (string permittedCommand in permittedConsoleCommands)
|
||||
{
|
||||
@@ -1469,6 +1471,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
GameMain.NetLobbyScreen.RefreshEnabledElements();
|
||||
OnPermissionChanged.Invoke(new PermissionChangedEvent(permissions, this.permittedConsoleCommands));
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> StartGame(IReadMessage inc)
|
||||
@@ -3680,7 +3683,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
errorLines.Add("Level: " + Level.Loaded.Seed + ", " + string.Join(", ", Level.Loaded.EqualityCheckValues.Select(cv => cv.ToString("X"))));
|
||||
errorLines.Add("Level: " + Level.Loaded.Seed + ", "
|
||||
+ string.Join("; ", Level.Loaded.EqualityCheckValues.Select(cv
|
||||
=> cv.Key + "=" + cv.Value.ToString("X"))));
|
||||
errorLines.Add("Entity count before generating level: " + Level.Loaded.EntityCountBeforeGenerate);
|
||||
errorLines.Add("Entities:");
|
||||
foreach (Entity e in Level.Loaded.EntitiesBeforeGenerate.OrderBy(e => e.CreationIndex))
|
||||
|
||||
@@ -621,7 +621,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
|
||||
var losModeRadioButtonGroup = new GUIRadioButtonGroup();
|
||||
LosMode[] losModes = (LosMode[])Enum.GetValues(typeof(LosMode));
|
||||
for (int i = 0; i < losModes.Length; i++)
|
||||
@@ -634,6 +634,14 @@ namespace Barotrauma.Networking
|
||||
var traitorsMinPlayerCount = CreateLabeledNumberInput(roundsTab, "ServerSettingsTraitorsMinPlayerCount", 1, 16, "ServerSettingsTraitorsMinPlayerCountToolTip");
|
||||
GetPropertyData(nameof(TraitorsMinPlayerCount)).AssignGUIComponent(traitorsMinPlayerCount);
|
||||
|
||||
var maximumTransferAmount = CreateLabeledNumberInput(roundsTab, "serversettingsmaximumtransferrequest", 0, CampaignMode.MaxMoney, "serversettingsmaximumtransferrequesttooltip");
|
||||
GetPropertyData(nameof(MaximumTransferRequest)).AssignGUIComponent(maximumTransferAmount);
|
||||
|
||||
var lootedMoneyDestination = CreateLabeledDropdown(roundsTab, "serversettingslootedmoneydestination", numElements: 2, "serversettingslootedmoneydestinationtooltip");
|
||||
lootedMoneyDestination.AddItem(TextManager.Get("lootedmoneydestination.bank"), LootedMoneyDestination.Bank);
|
||||
lootedMoneyDestination.AddItem(TextManager.Get("lootedmoneydestination.wallet"), LootedMoneyDestination.Wallet);
|
||||
GetPropertyData(nameof(LootedMoneyDestination)).AssignGUIComponent(lootedMoneyDestination);
|
||||
|
||||
var ragdollButtonBox = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.05f), roundsTab.RectTransform), TextManager.Get("ServerSettingsAllowRagdollButton"));
|
||||
GetPropertyData(nameof(AllowRagdollButton)).AssignGUIComponent(ragdollButtonBox);
|
||||
|
||||
@@ -991,6 +999,32 @@ namespace Barotrauma.Networking
|
||||
return input;
|
||||
}
|
||||
|
||||
private GUIDropDown CreateLabeledDropdown(GUIComponent parent, string labelTag, int numElements, string toolTipTag = null)
|
||||
{
|
||||
var container = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), parent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.05f,
|
||||
ToolTip = TextManager.Get(labelTag)
|
||||
};
|
||||
|
||||
var label = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), container.RectTransform),
|
||||
TextManager.Get(labelTag), textAlignment: Alignment.CenterLeft, font: GUIStyle.SmallFont)
|
||||
{
|
||||
AutoScaleHorizontal = true
|
||||
};
|
||||
if (!string.IsNullOrEmpty(toolTipTag))
|
||||
{
|
||||
label.ToolTip = TextManager.Get(toolTipTag);
|
||||
}
|
||||
var input = new GUIDropDown(new RectTransform(new Vector2(0.3f, 1.0f), container.RectTransform), elementCount: numElements);
|
||||
|
||||
container.RectTransform.MinSize = new Point(0, input.RectTransform.MinSize.Y);
|
||||
container.RectTransform.MaxSize = new Point(int.MaxValue, input.RectTransform.MaxSize.Y);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
private bool SelectSettingsTab(GUIButton button, object obj)
|
||||
{
|
||||
settingsTabIndex = (int)obj;
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Barotrauma.Particles
|
||||
{
|
||||
return debugName;
|
||||
}
|
||||
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, float lifeTimeMultiplier = 1f, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
{
|
||||
this.prefab = prefab;
|
||||
#if DEBUG
|
||||
@@ -149,13 +149,13 @@ namespace Barotrauma.Particles
|
||||
|
||||
if (prefab.LifeTimeMin <= 0.0f)
|
||||
{
|
||||
totalLifeTime = prefab.LifeTime;
|
||||
lifeTime = prefab.LifeTime;
|
||||
totalLifeTime = prefab.LifeTime * lifeTimeMultiplier;
|
||||
lifeTime = prefab.LifeTime * lifeTimeMultiplier;
|
||||
}
|
||||
else
|
||||
{
|
||||
totalLifeTime = Rand.Range(prefab.LifeTimeMin, prefab.LifeTime);
|
||||
lifeTime = totalLifeTime;
|
||||
totalLifeTime = Rand.Range(prefab.LifeTimeMin, prefab.LifeTime) * lifeTimeMultiplier;
|
||||
lifeTime = totalLifeTime * lifeTimeMultiplier;
|
||||
}
|
||||
|
||||
startDelay = Rand.Range(prefab.StartDelayMin, prefab.StartDelayMax);
|
||||
|
||||
@@ -85,6 +85,9 @@ namespace Barotrauma.Particles
|
||||
[Editable, Serialize("1,1,1,1", IsPropertySaveable.Yes)]
|
||||
public Color ColorMultiplier { get; set; }
|
||||
|
||||
[Editable, Serialize(1f, IsPropertySaveable.Yes)]
|
||||
public float LifeTimeMultiplier { get; set; }
|
||||
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool DrawOnTop { get; set; }
|
||||
|
||||
@@ -197,7 +200,7 @@ namespace Barotrauma.Particles
|
||||
position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax);
|
||||
}
|
||||
|
||||
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop, tracerPoints: tracerPoints);
|
||||
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop, lifeTimeMultiplier: Prefab.Properties.LifeTimeMultiplier, tracerPoints: tracerPoints);
|
||||
|
||||
if (particle != null)
|
||||
{
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Barotrauma.Particles
|
||||
return CreateParticle(prefab, position, velocity, rotation, hullGuess, collisionIgnoreTimer: collisionIgnoreTimer, tracerPoints:tracerPoints);
|
||||
}
|
||||
|
||||
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, float lifeTimeMultiplier = 1f, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
{
|
||||
if (prefab == null || prefab.Sprites.Count == 0) { return null; }
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Barotrauma.Particles
|
||||
|
||||
if (particles[particleCount] == null) { particles[particleCount] = new Particle(); }
|
||||
|
||||
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer, tracerPoints: tracerPoints);
|
||||
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer, lifeTimeMultiplier, tracerPoints: tracerPoints);
|
||||
|
||||
particleCount++;
|
||||
|
||||
|
||||
@@ -77,9 +77,20 @@ namespace Barotrauma
|
||||
if (remoteContentDoc?.Root != null)
|
||||
{
|
||||
remoteContentContainer.ClearChildren();
|
||||
foreach (var subElement in remoteContentDoc.Root.Elements())
|
||||
try
|
||||
{
|
||||
GUIComponent.FromXML(subElement.FromPackage(null), remoteContentContainer.RectTransform);
|
||||
foreach (var subElement in remoteContentDoc.Root.Elements())
|
||||
{
|
||||
GUIComponent.FromXML(subElement.FromPackage(null), remoteContentContainer.RectTransform);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Reading received remote main menu content failed.", e);
|
||||
#endif
|
||||
GameAnalyticsManager.AddErrorEventOnce("MainMenuScreen.RemoteContentParse:Exception", GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Reading received remote main menu content failed. " + e.Message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,10 +20,11 @@ namespace Barotrauma
|
||||
private ServerContentPackage? currentDownload;
|
||||
|
||||
private readonly List<ContentPackage> downloadedPackages = new List<ContentPackage>();
|
||||
public IEnumerable<ContentPackage> DownloadedPackages => downloadedPackages;
|
||||
|
||||
private bool confirmDownload;
|
||||
|
||||
private void Reset()
|
||||
public void Reset()
|
||||
{
|
||||
pendingDownloads.Clear();
|
||||
downloadedPackages.Clear();
|
||||
@@ -255,12 +256,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deselect()
|
||||
{
|
||||
Reset();
|
||||
base.Deselect();
|
||||
}
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
@@ -1400,7 +1400,7 @@ namespace Barotrauma
|
||||
public void CreatePlayerFrame(GUIComponent parent, bool createPendingText = true, bool alwaysAllowEditing = false)
|
||||
{
|
||||
UpdatePlayerFrame(
|
||||
Character.Controlled?.Info ?? playerInfoContainer.Children?.First().UserData as CharacterInfo,
|
||||
Character.Controlled?.Info ?? playerInfoContainer.Children?.First().UserData as CharacterInfo ?? GameMain.Client.CharacterInfo,
|
||||
allowEditing: alwaysAllowEditing || campaignCharacterInfo == null,
|
||||
parent: parent,
|
||||
createPendingText: createPendingText);
|
||||
@@ -3131,10 +3131,11 @@ namespace Barotrauma
|
||||
retVal[i] = new GUIImage[outfitPreview.Sprites.Count];
|
||||
for (int j = 0; j < outfitPreview.Sprites.Count; j++)
|
||||
{
|
||||
Pair<Sprite, Vector2> sprite = outfitPreview.Sprites[j];
|
||||
Sprite sprite = outfitPreview.Sprites[j].sprite;
|
||||
Vector2 drawOffset = outfitPreview.Sprites[j].drawOffset;
|
||||
float aspectRatio = outfitPreview.Dimensions.Y / outfitPreview.Dimensions.X;
|
||||
retVal[i][j] = new GUIImage(new RectTransform(new Vector2(0.7f / aspectRatio, 0.7f), innerFrame.RectTransform, Anchor.Center)
|
||||
{ RelativeOffset = sprite.Second / outfitPreview.Dimensions }, sprite.First, scaleToFit: true)
|
||||
{ RelativeOffset = drawOffset / outfitPreview.Dimensions }, sprite, scaleToFit: true)
|
||||
{
|
||||
PressedColor = Color.White,
|
||||
CanBeFocused = false
|
||||
|
||||
@@ -1190,18 +1190,23 @@ namespace Barotrauma
|
||||
frame.RectTransform.MaxSize = new Point(int.MaxValue, frame.Rect.Width);
|
||||
|
||||
LocalizedString name = legacy ? TextManager.GetWithVariable("legacyitemformat", "[name]", ep.Name) : ep.Name;
|
||||
frame.ToolTip = ep.Description.IsNullOrEmpty() ? name : name + '\n' + ep.Description;
|
||||
frame.ToolTip = $"{frame.ToolTip}\n‖color:{XMLExtensions.ToStringHex(GUIStyle.TextColorBright)}‖{name}‖color:end‖";
|
||||
if (!ep.Description.IsNullOrEmpty())
|
||||
{
|
||||
frame.ToolTip += '\n' + ep.Description;
|
||||
}
|
||||
|
||||
if (ep.ContentPackage != GameMain.VanillaContent && ep.ContentPackage != null)
|
||||
{
|
||||
frame.Color = Color.Magenta;
|
||||
frame.ToolTip = RichString.Rich($"{frame.ToolTip}\n‖color:{XMLExtensions.ToStringHex(Color.MediumPurple)}‖{ep.ContentPackage?.Name}‖color:end‖");
|
||||
frame.ToolTip = $"{frame.ToolTip}\n‖color:{XMLExtensions.ToStringHex(Color.MediumPurple)}‖{ep.ContentPackage?.Name}‖color:end‖";
|
||||
}
|
||||
if (ep.HideInMenus)
|
||||
{
|
||||
frame.Color = Color.Red;
|
||||
name = "[HIDDEN] " + name;
|
||||
}
|
||||
frame.ToolTip = RichString.Rich(frame.ToolTip);
|
||||
|
||||
GUILayoutGroup paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), frame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
@@ -1976,7 +1981,7 @@ namespace Barotrauma
|
||||
return true;
|
||||
};
|
||||
|
||||
nameBox.Text = subNameLabel?.Text?.SanitizedValue ?? "";
|
||||
nameBox.Text = MainSub?.Info.Name ?? "";
|
||||
|
||||
submarineNameCharacterCount.Text = nameBox.Text.Length + " / " + submarineNameLimit;
|
||||
|
||||
@@ -2750,6 +2755,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
nameBox.Text = nameBox.Text.Trim();
|
||||
|
||||
bool hideInMenus = nameBox.Parent.GetChildByUserData("hideinmenus") is GUITickBox hideInMenusTickBox && hideInMenusTickBox.Selected;
|
||||
string saveFolder = Path.Combine(ContentPackage.LocalModsDir, nameBox.Text);
|
||||
string filePath = Path.Combine(saveFolder, $"{nameBox.Text}.xml").CleanUpPathCrossPlatform();
|
||||
@@ -2884,8 +2891,30 @@ namespace Barotrauma
|
||||
{
|
||||
if (deleteButtonHolder.FindChild("delete") is GUIButton deleteBtn)
|
||||
{
|
||||
deleteBtn.Enabled = userData is SubmarineInfo subInfo
|
||||
&& GetContentPackageIntrinsicallyTiedToSub(subInfo) != null;
|
||||
deleteBtn.ToolTip = string.Empty;
|
||||
if (!(userData is SubmarineInfo subInfo))
|
||||
{
|
||||
deleteBtn.Enabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
var package = GetContentPackageIntrinsicallyTiedToSub(subInfo);
|
||||
if (package != null)
|
||||
{
|
||||
deleteBtn.Enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
deleteBtn.Enabled = false;
|
||||
if (ContentPackageManager.VanillaCorePackage?.Files.Any(f => f.Path == subInfo.FilePath) ?? false)
|
||||
{
|
||||
deleteBtn.ToolTip = TextManager.Get("cantdeletevanillasub");
|
||||
}
|
||||
else if (ContentPackageManager.AllPackages.FirstOrDefault(p => p.Files.Any(f => f.Path == subInfo.FilePath)) is ContentPackage subPackage)
|
||||
{
|
||||
deleteBtn.ToolTip = TextManager.GetWithVariable("cantdeletemodsub", "[modname]", subPackage.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -2925,6 +2954,21 @@ namespace Barotrauma
|
||||
ToolTip = sub.FilePath
|
||||
};
|
||||
|
||||
if (!(ContentPackageManager.VanillaCorePackage?.Files.Any(f => f.Path == sub.FilePath) ?? false))
|
||||
{
|
||||
if (GetContentPackageIntrinsicallyTiedToSub(sub) == null &&
|
||||
ContentPackageManager.AllPackages.FirstOrDefault(p => p.Files.Any(f => f.Path == sub.FilePath)) is ContentPackage subPackage)
|
||||
{
|
||||
//workshop mod
|
||||
textBlock.OverrideTextColor(Color.MediumPurple);
|
||||
}
|
||||
else
|
||||
{
|
||||
//local mod
|
||||
textBlock.OverrideTextColor(GUIStyle.TextColorBright);
|
||||
}
|
||||
}
|
||||
|
||||
if (sub.HasTag(SubmarineTag.Shuttle))
|
||||
{
|
||||
var shuttleText = new GUITextBlock(new RectTransform(new Vector2(0.2f, 1.0f), textBlock.RectTransform, Anchor.CenterRight),
|
||||
@@ -3037,6 +3081,24 @@ namespace Barotrauma
|
||||
if (!(child.UserData is SubmarineInfo sub)) { continue; }
|
||||
child.Visible = string.IsNullOrEmpty(filter) || sub.Name.ToLower().Contains(filter.ToLower());
|
||||
}
|
||||
|
||||
//go through the elements backwards, and disable the labels for sub categories if there's no subs visible in them
|
||||
bool subVisibleInCategory = false;
|
||||
foreach (GUIComponent child in subList.Content.Children.Reverse())
|
||||
{
|
||||
if (!(child.UserData is SubmarineInfo sub))
|
||||
{
|
||||
if (child.Enabled)
|
||||
{
|
||||
child.Visible = subVisibleInCategory;
|
||||
}
|
||||
subVisibleInCategory = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
subVisibleInCategory |= child.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -4828,7 +4890,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (GameSettings.CurrentConfig.KeyMap.Bindings[InputType.ToggleInventory].IsHit() && mode == Mode.Default)
|
||||
if (PlayerInput.KeyHit(Keys.Q) && mode == Mode.Default)
|
||||
{
|
||||
toggleEntityMenuButton.OnClicked?.Invoke(toggleEntityMenuButton, toggleEntityMenuButton.UserData);
|
||||
}
|
||||
|
||||
@@ -233,16 +233,18 @@ namespace Barotrauma
|
||||
|
||||
float dist = diff.Length();
|
||||
float distFallOff = dist / FlowSoundRange;
|
||||
if (distFallOff >= 0.99f) continue;
|
||||
if (distFallOff >= 0.99f) { continue; }
|
||||
|
||||
float gain = MathHelper.Clamp(gapFlow / 100.0f, 0.0f, 1.0f);
|
||||
|
||||
//flow at the left side
|
||||
if (diff.X < 0)
|
||||
{
|
||||
targetFlowLeft[flowSoundIndex] += 1.0f - distFallOff;
|
||||
targetFlowLeft[flowSoundIndex] += gain - distFallOff;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetFlowRight[flowSoundIndex] += 1.0f - distFallOff;
|
||||
targetFlowRight[flowSoundIndex] += gain - distFallOff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,7 +289,7 @@ namespace Barotrauma
|
||||
flowSoundChannels[i] = FlowSounds[i].Sound.Play(1.0f, FlowSoundRange, soundPos);
|
||||
flowSoundChannels[i].Looping = true;
|
||||
}
|
||||
flowSoundChannels[i].Gain = Math.Min(Math.Max(flowVolumeRight[i], flowVolumeLeft[i]), 1.0f);
|
||||
flowSoundChannels[i].Gain = Math.Max(flowVolumeRight[i], flowVolumeLeft[i]);
|
||||
flowSoundChannels[i].Position = new Vector3(soundPos, 0.0f);
|
||||
}
|
||||
}
|
||||
@@ -853,7 +855,7 @@ namespace Barotrauma
|
||||
if (SplashSounds.Count == 0) { return; }
|
||||
int splashIndex = MathHelper.Clamp((int)(strength + Rand.Range(-2.0f, 2.0f)), 0, SplashSounds.Count - 1);
|
||||
float range = 800.0f;
|
||||
var channel = SplashSounds[splashIndex].Sound.Play(1.0f, range, worldPosition, muffle: ShouldMuffleSound(Character.Controlled, worldPosition, range, null));
|
||||
SplashSounds[splashIndex].Sound?.Play(1.0f, range, worldPosition, muffle: ShouldMuffleSound(Character.Controlled, worldPosition, range, null));
|
||||
}
|
||||
|
||||
public static void PlayDamageSound(string damageType, float damage, PhysicsBody body)
|
||||
|
||||
@@ -39,9 +39,8 @@ namespace Barotrauma
|
||||
get { return texture != null && !cannotBeLoaded; }
|
||||
}
|
||||
|
||||
public Sprite(Sprite other) : this(other.texture, other.sourceRect, other.offset, other.rotation)
|
||||
public Sprite(Sprite other) : this(other.texture, other.sourceRect, other.offset, other.rotation, other.FilePath.Value)
|
||||
{
|
||||
FilePath = other.FilePath;
|
||||
Compress = other.Compress;
|
||||
size = other.size;
|
||||
effects = other.effects;
|
||||
@@ -58,6 +57,17 @@ namespace Barotrauma
|
||||
rotation = newRotation;
|
||||
FilePath = ContentPath.FromRaw(path);
|
||||
AddToList(this);
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
Identifier fullPath = Path.GetFullPath(path).CleanUpPathCrossPlatform(correctFilenameCase: false).ToIdentifier();
|
||||
lock (list)
|
||||
{
|
||||
if (!textureRefCounts.TryAdd(fullPath, new TextureRefCounter { RefCount = 1, Texture = texture }))
|
||||
{
|
||||
textureRefCounts[fullPath].RefCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void LoadTexture(ref Vector4 sourceVector, ref bool shouldReturn)
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
public static partial class Workshop
|
||||
{
|
||||
public const int MaxThumbnailSize = 1024 * 1024;
|
||||
|
||||
public static readonly ImmutableArray<Identifier> Tags = new []
|
||||
{
|
||||
"submarine",
|
||||
@@ -177,7 +179,7 @@ namespace Barotrauma.Steam
|
||||
|
||||
DeletePublishStagingCopy();
|
||||
Directory.CreateDirectory(PublishStagingDir);
|
||||
await CopyDirectory(contentPackage.Dir, contentPackage.Name, Path.GetDirectoryName(contentPackage.Path)!, PublishStagingDir);
|
||||
await CopyDirectory(contentPackage.Dir, contentPackage.Name, Path.GetDirectoryName(contentPackage.Path)!, PublishStagingDir, ShouldCorrectPaths.No);
|
||||
|
||||
//Load filelist.xml and write the hash into it so anyone downloading this mod knows what it should be
|
||||
ModProject modProject = new ModProject(contentPackage)
|
||||
@@ -218,7 +220,7 @@ namespace Barotrauma.Steam
|
||||
throw new Exception($"{newPath} already exists");
|
||||
}
|
||||
|
||||
await CopyDirectory(contentPackage.Dir, contentPackage.Name, Path.GetDirectoryName(contentPackage.Path)!, newPath);
|
||||
await CopyDirectory(contentPackage.Dir, contentPackage.Name, Path.GetDirectoryName(contentPackage.Path)!, newPath, ShouldCorrectPaths.Yes);
|
||||
|
||||
ModProject modProject = new ModProject(contentPackage);
|
||||
modProject.DiscardHashAndInstallTime();
|
||||
|
||||
@@ -197,6 +197,11 @@ namespace Barotrauma.Steam
|
||||
|
||||
FileSelection.OnFileSelected = (fn) =>
|
||||
{
|
||||
if (new FileInfo(fn).Length > SteamManager.Workshop.MaxThumbnailSize)
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.Get("WorkshopItemPreviewImageTooLarge"));
|
||||
return;
|
||||
}
|
||||
thumbnailPath = fn;
|
||||
CreateLocalThumbnail(thumbnailPath, thumbnailContainer);
|
||||
};
|
||||
|
||||
@@ -119,6 +119,11 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
new GUICustomComponent(new RectTransform(Vector2.Zero, searchHolder.RectTransform), onUpdate:
|
||||
(f, component) =>
|
||||
{
|
||||
searchTitle.RectTransform.NonScaledSize = searchBox.Frame.RectTransform.NonScaledSize;
|
||||
});
|
||||
searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
|
||||
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = searchBox.Text.IsNullOrWhiteSpace(); };
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ namespace Barotrauma
|
||||
private readonly int maxWidth;
|
||||
|
||||
private ScalableFont? cachedFont = null;
|
||||
private uint cachedFontSize = 0;
|
||||
|
||||
public LimitLString(LocalizedString text, GUIFont font, int maxWidth)
|
||||
{
|
||||
@@ -27,7 +26,6 @@ namespace Barotrauma
|
||||
{
|
||||
cachedValue = ToolBox.LimitString(nestedStr.Value, font.Value, maxWidth);
|
||||
cachedFont = font.Value;
|
||||
cachedFontSize = font.Size;
|
||||
UpdateLanguage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ namespace Barotrauma
|
||||
|
||||
public static string LimitString(string str, ScalableFont font, int maxWidth)
|
||||
{
|
||||
if (maxWidth <= 0 || string.IsNullOrWhiteSpace(str)) return "";
|
||||
if (maxWidth <= 0 || string.IsNullOrWhiteSpace(str)) { return ""; }
|
||||
|
||||
float currWidth = font.MeasureString("...").X;
|
||||
for (int i = 0; i < str.Length; i++)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.17.15.0</Version>
|
||||
<Version>0.18.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.17.15.0</Version>
|
||||
<Version>0.18.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.17.15.0</Version>
|
||||
<Version>0.18.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.17.15.0</Version>
|
||||
<Version>0.18.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.17.15.0</Version>
|
||||
<Version>0.18.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Barotrauma
|
||||
partial void OnPermanentStatChanged(StatTypes statType)
|
||||
{
|
||||
if (Character == null || Character.Removed) { return; }
|
||||
GameMain.NetworkMember.CreateEntityEvent(Character, new Character.UpdatePermanentStatsEventData());
|
||||
GameMain.NetworkMember.CreateEntityEvent(Character, new Character.UpdatePermanentStatsEventData(statType));
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg)
|
||||
|
||||
@@ -25,9 +25,9 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Saves bots in multiplayer
|
||||
/// </summary>
|
||||
public void SaveMultiplayer(XElement root)
|
||||
public XElement SaveMultiplayer(XElement parentElement)
|
||||
{
|
||||
XElement saveElement = new XElement("bots", new XAttribute("hasbots", HasBots));
|
||||
var element = new XElement("bots", new XAttribute("hasbots", HasBots));
|
||||
foreach (CharacterInfo info in characterInfos)
|
||||
{
|
||||
if (Level.Loaded != null)
|
||||
@@ -35,13 +35,13 @@ namespace Barotrauma
|
||||
if (!info.IsNewHire && (info.Character == null || info.Character.IsDead)) { continue; }
|
||||
}
|
||||
|
||||
XElement characterElement = info.Save(saveElement);
|
||||
XElement characterElement = info.Save(element);
|
||||
if (info.InventoryData != null) { characterElement.Add(info.InventoryData); }
|
||||
if (info.HealthData != null) { characterElement.Add(info.HealthData); }
|
||||
if (info.OrderData != null) { characterElement.Add(info.OrderData); }
|
||||
}
|
||||
SaveActiveOrders(saveElement);
|
||||
root.Add(saveElement);
|
||||
parentElement?.Add(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
public void ServerWriteActiveOrders(IWriteMessage msg)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -10,6 +11,31 @@ namespace Barotrauma
|
||||
protected set;
|
||||
}
|
||||
|
||||
private static bool IsOwner(Client client) => client != null && client.Connection == GameMain.Server.OwnerConnection;
|
||||
|
||||
/// <summary>
|
||||
/// There is a client-side implementation of the method in <see cref="CampaignMode"/>
|
||||
/// </summary>
|
||||
public bool AllowedToManageCampaign(Client client, ClientPermissions permissions)
|
||||
{
|
||||
//allow managing the campaign if the client has permissions, is the owner, or the only client in the server,
|
||||
//or if no-one has management permissions
|
||||
return
|
||||
client.HasPermission(permissions) ||
|
||||
client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
GameMain.Server.ConnectedClients.Count == 1 ||
|
||||
IsOwner(client) ||
|
||||
GameMain.Server.ConnectedClients.None(c => c.InGame && (IsOwner(c) || c.HasPermission(permissions)));
|
||||
}
|
||||
|
||||
public bool AllowedToManageWallets(Client client)
|
||||
{
|
||||
return
|
||||
client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
client.HasPermission(ClientPermissions.ManageMoney) ||
|
||||
IsOwner(client);
|
||||
}
|
||||
|
||||
public override void ShowStartMessage()
|
||||
{
|
||||
foreach (Mission mission in Missions)
|
||||
|
||||
@@ -163,29 +163,6 @@ namespace Barotrauma
|
||||
|
||||
private static bool IsOwner(Client client) => client != null && client.Connection == GameMain.Server.OwnerConnection;
|
||||
|
||||
/// <summary>
|
||||
/// There is a client-side implementation of the method in <see cref="CampaignMode"/>
|
||||
/// </summary>
|
||||
public bool AllowedToManageCampaign(Client client, ClientPermissions permissions)
|
||||
{
|
||||
//allow managing the campaign if the client has permissions, is the owner, or the only client in the server,
|
||||
//or if no-one has management permissions
|
||||
return
|
||||
client.HasPermission(permissions) ||
|
||||
client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
GameMain.Server.ConnectedClients.Count == 1 ||
|
||||
IsOwner(client) ||
|
||||
GameMain.Server.ConnectedClients.None(c => c.InGame && (IsOwner(c) || c.HasPermission(permissions)));
|
||||
}
|
||||
|
||||
public bool AllowedToManageWallets(Client client)
|
||||
{
|
||||
return
|
||||
client.HasPermission(ClientPermissions.ManageCampaign) ||
|
||||
client.HasPermission(ClientPermissions.ManageMoney) ||
|
||||
IsOwner(client);
|
||||
}
|
||||
|
||||
public void SaveExperiencePoints(Client client)
|
||||
{
|
||||
ClearSavedExperiencePoints(client);
|
||||
@@ -200,14 +177,6 @@ namespace Barotrauma
|
||||
savedExperiencePoints.RemoveAll(s => s.SteamID != 0 && client.SteamID == s.SteamID || client.EndpointMatches(s.EndPoint));
|
||||
}
|
||||
|
||||
public void LoadPets()
|
||||
{
|
||||
if (petsElement != null)
|
||||
{
|
||||
PetBehavior.LoadPets(petsElement);
|
||||
}
|
||||
}
|
||||
|
||||
public void SavePlayers()
|
||||
{
|
||||
//refresh the character data of clients who are still in the server
|
||||
@@ -261,8 +230,7 @@ namespace Barotrauma
|
||||
|
||||
characterData.ForEach(cd => cd.HasSpawned = false);
|
||||
|
||||
petsElement = new XElement("pets");
|
||||
PetBehavior.SavePets(petsElement);
|
||||
SavePets();
|
||||
|
||||
//remove all items that are in someone's inventory
|
||||
foreach (Character c in Character.CharacterList)
|
||||
@@ -285,6 +253,8 @@ namespace Barotrauma
|
||||
|
||||
c.Inventory.DeleteAllItems();
|
||||
}
|
||||
|
||||
SaveActiveOrders();
|
||||
}
|
||||
|
||||
public void MoveDiscardedCharacterBalancesToBank()
|
||||
@@ -348,44 +318,10 @@ namespace Barotrauma
|
||||
if (success)
|
||||
{
|
||||
SavePlayers();
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
if (leavingSub != Submarine.MainSub && !leavingSub.DockedTo.Contains(Submarine.MainSub))
|
||||
{
|
||||
Submarine.MainSub = leavingSub;
|
||||
GameMain.GameSession.Submarine = leavingSub;
|
||||
GameMain.GameSession.SubmarineInfo = leavingSub.Info;
|
||||
leavingSub.Info.FilePath = System.IO.Path.Combine(SaveUtil.TempPath, leavingSub.Info.Name + ".sub");
|
||||
var subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);
|
||||
GameMain.GameSession.OwnedSubmarines.Add(leavingSub.Info);
|
||||
foreach (Submarine sub in subsToLeaveBehind)
|
||||
{
|
||||
GameMain.GameSession.OwnedSubmarines.RemoveAll(s => s != leavingSub.Info && s.Name == sub.Info.Name);
|
||||
MapEntity.mapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine);
|
||||
LinkedSubmarine.CreateDummy(leavingSub, sub);
|
||||
}
|
||||
}
|
||||
LeaveUnconnectedSubs(leavingSub);
|
||||
NextLevel = newLevel;
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
|
||||
if (PendingSubmarineSwitch != null)
|
||||
{
|
||||
SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo;
|
||||
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
|
||||
|
||||
for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++)
|
||||
{
|
||||
if (GameMain.GameSession.OwnedSubmarines[i].Name == previousSub.Name)
|
||||
{
|
||||
GameMain.GameSession.OwnedSubmarines[i] = previousSub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
PendingSubmarineSwitch = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -977,6 +913,8 @@ namespace Barotrauma
|
||||
{
|
||||
NetWalletTransfer transfer = INetSerializableStruct.Read<NetWalletTransfer>(msg);
|
||||
|
||||
if (GameMain.Server is null) { return; }
|
||||
|
||||
switch (transfer.Sender)
|
||||
{
|
||||
case Some<ushort> { Value: var id }:
|
||||
@@ -992,7 +930,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (transfer.Receiver is Some<ushort> { Value: var receiverId } && receiverId == sender.CharacterID)
|
||||
{
|
||||
GameMain.Server?.Voting.StartTransferVote(sender, null, transfer.Amount, sender);
|
||||
if (transfer.Amount > GameMain.Server.ServerSettings.MaximumTransferRequest) { return; }
|
||||
GameMain.Server.Voting.StartTransferVote(sender, null, transfer.Amount, sender);
|
||||
GameServer.Log($"{sender.Name} started a vote to transfer {transfer.Amount} mk from the bank.", ServerLog.MessageType.Money);
|
||||
}
|
||||
return;
|
||||
@@ -1301,7 +1240,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// save bots
|
||||
CrewManager.SaveMultiplayer(modeElement);
|
||||
var crewManagerElement = CrewManager.SaveMultiplayer(modeElement);
|
||||
if (ActiveOrdersElement != null)
|
||||
{
|
||||
crewManagerElement.Add(ActiveOrdersElement);
|
||||
}
|
||||
|
||||
XElement savedExperiencePointsElement = new XElement("SavedExperiencePoints");
|
||||
foreach (var savedExperiencePoint in savedExperiencePoints)
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class DockingPort : ItemComponent, IDrawableComponent, IServerSerializable
|
||||
partial class DockingPort : ItemComponent, IDrawableComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(docked);
|
||||
|
||||
if (docked)
|
||||
{
|
||||
msg.Write(DockingTarget.item.ID);
|
||||
msg.Write(IsLocked);
|
||||
}
|
||||
}
|
||||
public void ServerEventRead(IReadMessage msg, Client c)
|
||||
{
|
||||
var allowOutpostAutoDocking = (AllowOutpostAutoDocking)msg.ReadByte();
|
||||
if (outpostAutoDockingPromptShown &&
|
||||
(GameMain.GameSession?.Campaign?.AllowedToManageCampaign(c, ClientPermissions.ManageMap) ?? false))
|
||||
{
|
||||
this.allowOutpostAutoDocking = allowOutpostAutoDocking;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace Barotrauma.Items.Components
|
||||
for (int i = 0; i < Connections.Count; i++)
|
||||
{
|
||||
wires[i] = new List<Wire>();
|
||||
for (int j = 0; j < Connections[i].MaxWires; j++)
|
||||
uint wireCount = msg.ReadVariableUInt32();
|
||||
for (int j = 0; j < wireCount; j++)
|
||||
{
|
||||
ushort wireId = msg.ReadUInt16();
|
||||
|
||||
@@ -91,12 +92,8 @@ namespace Barotrauma.Items.Components
|
||||
//go through existing wire links
|
||||
for (int i = 0; i < Connections.Count; i++)
|
||||
{
|
||||
int j = -1;
|
||||
foreach (Wire existingWire in Connections[i].Wires)
|
||||
foreach (Wire existingWire in Connections[i].Wires.ToArray())
|
||||
{
|
||||
j++;
|
||||
if (existingWire == null) { continue; }
|
||||
|
||||
//existing wire not in the list of new wires -> disconnect it
|
||||
if (!wires[i].Contains(existingWire))
|
||||
{
|
||||
@@ -163,7 +160,7 @@ namespace Barotrauma.Items.Components
|
||||
}*/
|
||||
}
|
||||
|
||||
Connections[i].SetWire(j, null);
|
||||
Connections[i].DisconnectWire(existingWire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Barotrauma.MapCreatures.Behavior
|
||||
foreach (BallastFloraBranch branch in Branches)
|
||||
{
|
||||
//don't notify about minuscule amounts of damage (<= 1.0f)
|
||||
if (branch.AccumulatedDamage > 1.0f)
|
||||
if (Math.Abs(branch.AccumulatedDamage) > 1.0f)
|
||||
{
|
||||
CreateNetworkMessage(new BranchDamageEventData(branch));
|
||||
branch.AccumulatedDamage = 0.0f;
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Barotrauma.Networking
|
||||
public UInt16 LastRecvCampaignUpdate = 0;
|
||||
public UInt16 LastRecvCampaignSave = 0;
|
||||
|
||||
public Pair<UInt16, float> LastCampaignSaveSendTime;
|
||||
public (UInt16 saveId, float time) LastCampaignSaveSendTime;
|
||||
|
||||
public readonly List<ChatMessage> ChatMsgQueue = new List<ChatMessage>();
|
||||
public UInt16 LastChatMsgQueueID;
|
||||
|
||||
@@ -391,7 +391,7 @@ namespace Barotrauma.Networking
|
||||
StartTransfer(inc.Sender, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
|
||||
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign)
|
||||
{
|
||||
client.LastCampaignSaveSendTime = new Pair<ushort, float>(campaign.LastSaveID, (float)Lidgren.Network.NetTime.Now);
|
||||
client.LastCampaignSaveSendTime = (campaign.LastSaveID, (float)Lidgren.Network.NetTime.Now);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -955,7 +955,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
errorLines.Add("Level: " + Level.Loaded.Seed + ", " + string.Join(", ", Level.Loaded.EqualityCheckValues.Select(cv => cv.ToString("X"))));
|
||||
errorLines.Add("Level: " + Level.Loaded.Seed + ", "
|
||||
+ string.Join("; ", Level.Loaded.EqualityCheckValues.Select(cv
|
||||
=> cv.Key + "=" + cv.Value.ToString("X"))));
|
||||
errorLines.Add("Entity count before generating level: " + Level.Loaded.EntityCountBeforeGenerate);
|
||||
errorLines.Add("Entities:");
|
||||
foreach (Entity e in Level.Loaded.EntitiesBeforeGenerate.OrderBy(e => e.CreationIndex))
|
||||
@@ -1548,11 +1550,11 @@ namespace Barotrauma.Networking
|
||||
NetIdUtils.IdMoreRecent(campaign.LastSaveID, c.LastRecvCampaignSave))
|
||||
{
|
||||
//already sent an up-to-date campaign save
|
||||
if (c.LastCampaignSaveSendTime != null && campaign.LastSaveID == c.LastCampaignSaveSendTime.First)
|
||||
if (c.LastCampaignSaveSendTime != default && campaign.LastSaveID == c.LastCampaignSaveSendTime.saveId)
|
||||
{
|
||||
//the save was sent less than 5 second ago, don't attempt to resend yet
|
||||
//(the client may have received it but hasn't acked us yet)
|
||||
if (c.LastCampaignSaveSendTime.Second > NetTime.Now - 5.0f)
|
||||
if (c.LastCampaignSaveSendTime.time > NetTime.Now - 5.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1561,7 +1563,7 @@ namespace Barotrauma.Networking
|
||||
if (!FileSender.ActiveTransfers.Any(t => t.Connection == c.Connection && t.FileType == FileTransferType.CampaignSave))
|
||||
{
|
||||
FileSender.StartTransfer(c.Connection, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
|
||||
c.LastCampaignSaveSendTime = new Pair<ushort, float>(campaign.LastSaveID, (float)NetTime.Now);
|
||||
c.LastCampaignSaveSendTime = (campaign.LastSaveID, (float)NetTime.Now);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2193,7 +2195,7 @@ namespace Barotrauma.Networking
|
||||
Level.Loaded?.SpawnNPCs();
|
||||
Level.Loaded?.SpawnCorpses();
|
||||
Level.Loaded?.PrepareBeaconStation();
|
||||
AutoItemPlacer.PlaceIfNeeded();
|
||||
AutoItemPlacer.SpawnItems();
|
||||
|
||||
CrewManager crewManager = campaign?.CrewManager;
|
||||
|
||||
@@ -2388,7 +2390,9 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
campaign?.LoadPets();
|
||||
crewManager?.LoadActiveOrders();
|
||||
campaign?.LoadActiveOrders();
|
||||
|
||||
campaign?.CargoManager.InitPurchasedIDCards();
|
||||
|
||||
foreach (Submarine sub in Submarine.MainSubs)
|
||||
{
|
||||
@@ -2400,7 +2404,7 @@ namespace Barotrauma.Networking
|
||||
spawnList.Add(new PurchasedItem(kvp.Key, kvp.Value, buyer: null));
|
||||
}
|
||||
|
||||
CargoManager.CreateItems(spawnList, sub);
|
||||
CargoManager.CreateItems(spawnList, sub, cargoManager: null);
|
||||
}
|
||||
|
||||
TraitorManager = null;
|
||||
@@ -2531,10 +2535,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
msg.Write(mission.Prefab.Identifier);
|
||||
}
|
||||
msg.Write((byte)GameMain.GameSession.Level.EqualityCheckValues.Count);
|
||||
foreach (int equalityCheckValue in GameMain.GameSession.Level.EqualityCheckValues)
|
||||
foreach (Level.LevelGenStage stage in Enum.GetValues(typeof(Level.LevelGenStage)).OfType<Level.LevelGenStage>().OrderBy(s => s))
|
||||
{
|
||||
msg.Write(equalityCheckValue);
|
||||
msg.Write(GameMain.GameSession.Level.EqualityCheckValues[stage]);
|
||||
}
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
@@ -3178,9 +3181,9 @@ namespace Barotrauma.Networking
|
||||
Client recipient = connectedClients.Find(c => c.Connection == transfer.Connection);
|
||||
if (transfer.FileType == FileTransferType.CampaignSave &&
|
||||
(transfer.Status == FileTransferStatus.Sending || transfer.Status == FileTransferStatus.Finished) &&
|
||||
recipient.LastCampaignSaveSendTime != null)
|
||||
recipient.LastCampaignSaveSendTime != default)
|
||||
{
|
||||
recipient.LastCampaignSaveSendTime.Second = (float)Lidgren.Network.NetTime.Now;
|
||||
recipient.LastCampaignSaveSendTime.time = (float)NetTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -278,12 +278,12 @@ namespace Barotrauma
|
||||
|
||||
static bool isValid(Item item)
|
||||
{
|
||||
return item.Prefab.Identifier == "idcard" || item.GetComponent<RangedWeapon>() != null || item.GetComponent<MeleeWeapon>() != null;
|
||||
return item.GetComponent<IdCard>() != null || item.GetComponent<RangedWeapon>() != null || item.GetComponent<MeleeWeapon>() != null;
|
||||
}
|
||||
|
||||
if (foundItem == null) { return; }
|
||||
|
||||
bool isIdCard = ((MapEntity)foundItem).Prefab.Identifier == "idcard";
|
||||
bool isIdCard = foundItem.GetComponent<IdCard>() != null;
|
||||
bool isWeapon = foundItem.GetComponent<RangedWeapon>() != null || foundItem.GetComponent<MeleeWeapon>() != null;
|
||||
|
||||
if (isIdCard)
|
||||
|
||||
@@ -441,32 +441,43 @@ namespace Barotrauma.Networking
|
||||
GameServer.Log(string.Format("Respawning {0} ({1}) as {2}", GameServer.ClientLogName(clients[i]), clients[i].Connection?.EndPointString, characterInfos[i].Job.Name), ServerLog.MessageType.Spawning);
|
||||
}
|
||||
|
||||
if (divingSuitPrefab != null && oxyPrefab != null && RespawnShuttle != null)
|
||||
if (RespawnShuttle != null)
|
||||
{
|
||||
Vector2 pos = cargoSp == null ? character.Position : cargoSp.Position;
|
||||
if (divingSuitPrefab != null && oxyPrefab != null)
|
||||
if (divingSuitPrefab != null)
|
||||
{
|
||||
var divingSuit = new Item(divingSuitPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(divingSuit));
|
||||
respawnItems.Add(divingSuit);
|
||||
|
||||
var oxyTank = new Item(oxyPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(oxyTank));
|
||||
divingSuit.Combine(oxyTank, user: null);
|
||||
respawnItems.Add(oxyTank);
|
||||
if (oxyPrefab != null && divingSuit.GetComponent<ItemContainer>() != null)
|
||||
{
|
||||
var oxyTank = new Item(oxyPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(oxyTank));
|
||||
divingSuit.Combine(oxyTank, user: null);
|
||||
respawnItems.Add(oxyTank);
|
||||
}
|
||||
}
|
||||
|
||||
if (scooterPrefab != null && batteryPrefab != null)
|
||||
if (!(GameMain.GameSession.GameMode is CampaignMode))
|
||||
{
|
||||
var scooter = new Item(scooterPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(scooter));
|
||||
|
||||
var battery = new Item(batteryPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(battery));
|
||||
|
||||
scooter.Combine(battery, user: null);
|
||||
respawnItems.Add(scooter);
|
||||
respawnItems.Add(battery);
|
||||
if (scooterPrefab != null)
|
||||
{
|
||||
var scooter = new Item(scooterPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(scooter));
|
||||
respawnItems.Add(scooter);
|
||||
if (batteryPrefab != null)
|
||||
{
|
||||
var battery = new Item(batteryPrefab, pos, respawnSub);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(battery));
|
||||
scooter.Combine(battery, user: null);
|
||||
respawnItems.Add(battery);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (respawnContainer != null)
|
||||
{
|
||||
AutoItemPlacer.RegenerateLoot(RespawnShuttle, respawnContainer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,7 +515,7 @@ namespace Barotrauma.Networking
|
||||
//add the ID card tags they should've gotten when spawning in the shuttle
|
||||
foreach (Item item in character.Inventory.AllItems.Distinct())
|
||||
{
|
||||
if (item.Prefab.Identifier != "idcard") { continue; }
|
||||
if (item.GetComponent<IdCard>() == null) { continue; }
|
||||
foreach (string s in shuttleSpawnPoints[i].IdCardTags)
|
||||
{
|
||||
item.AddTag(s);
|
||||
|
||||
@@ -44,12 +44,14 @@ namespace Barotrauma
|
||||
{
|
||||
GameMain.Server?.SwitchSubmarine();
|
||||
}
|
||||
else
|
||||
{
|
||||
voting.RegisterRejectedVote(this);
|
||||
}
|
||||
voting.StopSubmarineVote(passed);
|
||||
}
|
||||
}
|
||||
|
||||
public static IVote ActiveVote;
|
||||
|
||||
public class TransferVote : IVote
|
||||
{
|
||||
public Client VoteStarter { get; }
|
||||
@@ -83,12 +85,22 @@ namespace Barotrauma
|
||||
toWallet.Give(TransferAmount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
voting.RegisterRejectedVote(this);
|
||||
}
|
||||
voting.StopMoneyTransferVote(passed);
|
||||
}
|
||||
}
|
||||
|
||||
public static IVote ActiveVote;
|
||||
|
||||
private static readonly Queue<IVote> pendingVotes = new Queue<IVote>();
|
||||
|
||||
private readonly TimeSpan rejectedVoteCooldown = new TimeSpan(0, 1, 0);
|
||||
|
||||
private readonly Dictionary<Client, (VoteType voteType, DateTime time)> rejectedVoteTimes = new Dictionary<Client, (VoteType voteType, DateTime time)>();
|
||||
|
||||
private void StartSubmarineVote(SubmarineInfo subInfo, VoteType voteType, Client sender)
|
||||
{
|
||||
if (ActiveVote == null)
|
||||
@@ -136,6 +148,10 @@ namespace Barotrauma
|
||||
|
||||
public void StartTransferVote(Client starter, Client from, int transferAmount, Client to)
|
||||
{
|
||||
if (ShouldRejectVote(starter, VoteType.TransferMoney))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ActiveVote == null)
|
||||
{
|
||||
starter.SetVote(VoteType.TransferMoney, 2);
|
||||
@@ -156,6 +172,31 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldRejectVote(Client sender, VoteType voteType)
|
||||
{
|
||||
if (rejectedVoteTimes.ContainsKey(sender))
|
||||
{
|
||||
TimeSpan remainingCooldown = (rejectedVoteTimes[sender].time + rejectedVoteCooldown) - DateTime.Now;
|
||||
if (rejectedVoteTimes[sender].voteType == voteType &&
|
||||
remainingCooldown.TotalSeconds > 0)
|
||||
{
|
||||
GameMain.Server.SendDirectChatMessage(
|
||||
TextManager.FormatServerMessage("voterejectedpleasewait", ("[time]", ((int)remainingCooldown.TotalSeconds).ToString())),
|
||||
sender, ChatMessageType.ServerMessageBox);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void RegisterRejectedVote(IVote vote)
|
||||
{
|
||||
if (vote.VoteStarter != null)
|
||||
{
|
||||
rejectedVoteTimes[vote.VoteStarter] = (vote.VoteType, DateTime.Now);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (ActiveVote == null) { return; }
|
||||
@@ -227,7 +268,6 @@ namespace Barotrauma
|
||||
GameServer.Log(GameServer.ClientLogName(sender) + (ready ? " is ready to start the game." : " is not ready to start the game."), ServerLog.MessageType.ServerMessage);
|
||||
}
|
||||
break;
|
||||
|
||||
case VoteType.PurchaseAndSwitchSub:
|
||||
case VoteType.PurchaseSub:
|
||||
case VoteType.SwitchSub:
|
||||
@@ -240,18 +280,25 @@ namespace Barotrauma
|
||||
int amount = inc.ReadInt32();
|
||||
int fromClientId = inc.ReadByte();
|
||||
int toClientId = inc.ReadByte();
|
||||
pendingVotes.Enqueue(new TransferVote(sender,
|
||||
GameMain.Server.ConnectedClients.Find(c => c.ID == fromClientId),
|
||||
amount,
|
||||
GameMain.Server.ConnectedClients.Find(c => c.ID == toClientId)));
|
||||
if (!ShouldRejectVote(sender, voteType))
|
||||
{
|
||||
pendingVotes.Enqueue(new TransferVote(sender,
|
||||
GameMain.Server.ConnectedClients.Find(c => c.ID == fromClientId),
|
||||
amount,
|
||||
GameMain.Server.ConnectedClients.Find(c => c.ID == toClientId)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string subName = inc.ReadString();
|
||||
SubmarineInfo subInfo = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == subName);
|
||||
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign && (campaign.CanPurchaseSub(subInfo, sender) || GameMain.GameSession.IsSubmarineOwned(subInfo)))
|
||||
if (!ShouldRejectVote(sender, voteType))
|
||||
{
|
||||
StartSubmarineVote(subInfo, voteType, sender);
|
||||
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign &&
|
||||
(campaign.CanPurchaseSub(subInfo, sender) || GameMain.GameSession.IsSubmarineOwned(subInfo)))
|
||||
{
|
||||
StartSubmarineVote(subInfo, voteType, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.17.15.0</Version>
|
||||
<Version>0.18.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -31,6 +31,17 @@ namespace Barotrauma
|
||||
if (_previousAiTarget != null)
|
||||
{
|
||||
_lastAiTarget = _previousAiTarget;
|
||||
if (_selectedAiTarget != null)
|
||||
{
|
||||
if (_selectedAiTarget.Entity is Item i && _previousAiTarget.Entity is Character c)
|
||||
{
|
||||
if (i.IsOwnedBy(c)) { return; }
|
||||
}
|
||||
else if (_previousAiTarget.Entity is Item it && _selectedAiTarget.Entity is Character ch)
|
||||
{
|
||||
if (it.IsOwnedBy(ch)) { return; }
|
||||
}
|
||||
}
|
||||
}
|
||||
OnTargetChanged(_previousAiTarget, _selectedAiTarget);
|
||||
}
|
||||
|
||||
@@ -1055,6 +1055,9 @@ namespace Barotrauma
|
||||
|
||||
private Vector2 attackWorldPos;
|
||||
private Vector2 attackSimPos;
|
||||
private float reachTimer;
|
||||
// How long the monster tries to reach out for the target when it's close to it before ignoring it.
|
||||
private const float reachTimeOut = 10;
|
||||
|
||||
private void UpdateAttack(float deltaTime)
|
||||
{
|
||||
@@ -1427,6 +1430,22 @@ namespace Barotrauma
|
||||
// Check that we can reach the target
|
||||
distance = toTarget.Length();
|
||||
canAttack = distance < AttackLimb.attack.Range;
|
||||
if (canAttack)
|
||||
{
|
||||
reachTimer = 0;
|
||||
}
|
||||
else if (selectedTargetingParams.AttackPattern == AttackPattern.Straight && distance < AttackLimb.attack.Range * 5)
|
||||
{
|
||||
reachTimer += deltaTime;
|
||||
if (reachTimer > reachTimeOut)
|
||||
{
|
||||
reachTimer = 0;
|
||||
IgnoreTarget(SelectedAiTarget);
|
||||
State = AIState.Idle;
|
||||
ResetAITarget();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Crouch if the target is down (only humanoids), so that we can reach it.
|
||||
if (Character.AnimController is HumanoidAnimController humanoidAnimController && distance < AttackLimb.attack.Range * 2)
|
||||
@@ -1958,9 +1977,8 @@ namespace Barotrauma
|
||||
}
|
||||
if (!isFriendly && attackResult.Damage > 0.0f)
|
||||
{
|
||||
ignoredTargets.Remove(attacker.AiTarget);
|
||||
bool canAttack = attacker.Submarine == Character.Submarine && canAttackCharacters || attacker.Submarine != null && canAttackWalls;
|
||||
if (AIParams.AttackWhenProvoked && canAttack)
|
||||
if (AIParams.AttackWhenProvoked && canAttack && !ignoredTargets.Contains(attacker.AiTarget))
|
||||
{
|
||||
if (attacker.IsHusk)
|
||||
{
|
||||
@@ -3476,6 +3494,7 @@ namespace Barotrauma
|
||||
{
|
||||
observeTimer = targetParams.Timer * Rand.Range(0.75f, 1.25f);
|
||||
}
|
||||
reachTimer = 0;
|
||||
}
|
||||
|
||||
protected override void OnStateChanged(AIState from, AIState to)
|
||||
@@ -3496,6 +3515,7 @@ namespace Barotrauma
|
||||
SetStateResetTimer();
|
||||
}
|
||||
blockCheckTimer = 0;
|
||||
reachTimer = 0;
|
||||
}
|
||||
|
||||
private void SetStateResetTimer() => stateResetTimer = stateResetCooldown * Rand.Range(0.75f, 1.25f);
|
||||
|
||||
@@ -59,7 +59,11 @@ namespace Barotrauma
|
||||
private readonly float enemyCheckInterval = 0.2f;
|
||||
private readonly float enemySpotDistanceOutside = 800;
|
||||
private readonly float enemySpotDistanceInside = 1000;
|
||||
private float enemycheckTimer;
|
||||
private float enemyCheckTimer;
|
||||
|
||||
private readonly float reportProblemsInterval = 1.0f;
|
||||
private float reportProblemsTimer;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// How far other characters can hear reports done by this character (e.g. reports for fires, intruders). Defaults to infinity.
|
||||
@@ -166,6 +170,7 @@ namespace Barotrauma
|
||||
objectiveManager = new AIObjectiveManager(c);
|
||||
reactTimer = GetReactionTime();
|
||||
SortTimer = Rand.Range(0f, sortObjectiveInterval);
|
||||
reportProblemsTimer = Rand.Range(0f, reportProblemsInterval);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
@@ -309,10 +314,10 @@ namespace Barotrauma
|
||||
{
|
||||
// Spot enemies while staying outside or inside an enemy ship.
|
||||
// does not apply for escorted characters, such as prisoners or terrorists who have their own behavior
|
||||
enemycheckTimer -= deltaTime;
|
||||
if (enemycheckTimer < 0)
|
||||
enemyCheckTimer -= deltaTime;
|
||||
if (enemyCheckTimer < 0)
|
||||
{
|
||||
enemycheckTimer = enemyCheckInterval * Rand.Range(0.75f, 1.25f);
|
||||
enemyCheckTimer = enemyCheckInterval * Rand.Range(0.75f, 1.25f);
|
||||
if (!objectiveManager.IsCurrentObjective<AIObjectiveCombat>())
|
||||
{
|
||||
float closestDistance = 0;
|
||||
@@ -407,19 +412,29 @@ namespace Barotrauma
|
||||
{
|
||||
if (Character.IsOnPlayerTeam)
|
||||
{
|
||||
VisibleHulls.ForEach(h => PropagateHullSafety(Character, h));
|
||||
foreach (Hull h in VisibleHulls)
|
||||
{
|
||||
PropagateHullSafety(Character, h);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Outpost npcs don't inform each other about threats, like crew members do.
|
||||
VisibleHulls.ForEach(h => RefreshHullSafety(h));
|
||||
foreach (Hull h in VisibleHulls)
|
||||
{
|
||||
RefreshHullSafety(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Character.SpeechImpediment < 100.0f)
|
||||
{
|
||||
if (Character.Submarine != null && (Character.Submarine.TeamID == Character.TeamID || Character.IsEscorted) && !Character.Submarine.Info.IsWreck)
|
||||
reportProblemsTimer -= deltaTime;
|
||||
if (reportProblemsTimer <= 0.0f)
|
||||
{
|
||||
ReportProblems();
|
||||
if (Character.Submarine != null && (Character.Submarine.TeamID == Character.TeamID || Character.IsEscorted) && !Character.Submarine.Info.IsWreck)
|
||||
{
|
||||
ReportProblems();
|
||||
}
|
||||
reportProblemsTimer = reportProblemsInterval;
|
||||
}
|
||||
UpdateSpeaking();
|
||||
}
|
||||
@@ -785,9 +800,10 @@ namespace Barotrauma
|
||||
if (item == null || item.Removed) { return; }
|
||||
if (!itemsToRelocate.Contains(item)) { return; }
|
||||
var mainSub = Submarine.MainSub;
|
||||
if (item.ParentInventory != null)
|
||||
Entity owner = item.GetRootInventoryOwner();
|
||||
if (owner != null)
|
||||
{
|
||||
if (item.ParentInventory.Owner is Character c)
|
||||
if (owner is Character c)
|
||||
{
|
||||
if (c.TeamID == CharacterTeamType.Team1 || c.TeamID == CharacterTeamType.Team2)
|
||||
{
|
||||
@@ -795,24 +811,37 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (item.ParentInventory.Owner.Submarine == mainSub)
|
||||
else if (owner.Submarine == mainSub)
|
||||
{
|
||||
// Placed inside an inventory that's already in the main sub.
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Laying on ground inside the main sub.
|
||||
// Laying on the ground inside the main sub.
|
||||
if (item.Submarine == mainSub)
|
||||
{
|
||||
return;
|
||||
}
|
||||
WayPoint wp = WayPoint.GetRandom(SpawnType.Cargo, null, mainSub);
|
||||
if (wp != null)
|
||||
if (owner != null && owner != item)
|
||||
{
|
||||
item.Submarine = mainSub;
|
||||
item.SetTransform(wp.SimPosition, 0.0f);
|
||||
item.Drop(null);
|
||||
}
|
||||
item.Submarine = mainSub;
|
||||
Item newContainer = mainSub.FindContainerFor(item, onlyPrimary: false);
|
||||
if (newContainer == null || !newContainer.OwnInventory.TryPutItem(item, user: null))
|
||||
{
|
||||
WayPoint wp = WayPoint.GetRandom(SpawnType.Cargo, null, mainSub) ?? WayPoint.GetRandom(SpawnType.Path, null, mainSub);
|
||||
if (wp != null)
|
||||
{
|
||||
item.SetTransform(wp.SimPosition, 0.0f, findNewHull: false, setPrevTransform: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"Failed to relocate item {item.Prefab.Identifier} ({item.ID}), because no cargo spawn point could be found!");
|
||||
}
|
||||
}
|
||||
itemsToRelocate.Remove(item);
|
||||
DebugConsole.Log($"Relocated item {item.Prefab.Identifier} ({item.ID}) back to the main sub.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1149,7 +1178,7 @@ namespace Barotrauma
|
||||
bool isAttackerFightingEnemy = false;
|
||||
float minorDamageThreshold = 1;
|
||||
float majorDamageThreshold = 20;
|
||||
if (attacker.TeamID == Character.TeamID)
|
||||
if (attacker.TeamID == Character.TeamID && !attacker.IsInstigator)
|
||||
{
|
||||
minorDamageThreshold = 10;
|
||||
majorDamageThreshold = 40;
|
||||
@@ -1356,6 +1385,10 @@ namespace Barotrauma
|
||||
|
||||
Character FindInstigator()
|
||||
{
|
||||
if (Character.IsInstigator)
|
||||
{
|
||||
return Character;
|
||||
}
|
||||
if (attacker.IsInstigator)
|
||||
{
|
||||
return attacker;
|
||||
@@ -1545,7 +1578,7 @@ namespace Barotrauma
|
||||
(!requireEquipped || character.HasEquippedItem(i)) &&
|
||||
(predicate == null || predicate(i)), recursive, matchingItems);
|
||||
items = matchingItems;
|
||||
return matchingItems.Any(i => i != null && (containedTag.IsEmpty || i.ContainedItems.Any(it => it.HasTag(containedTag) && it.ConditionPercentage > conditionPercentage)));
|
||||
return matchingItems.Any(i => i != null && (containedTag.IsEmpty || i.OwnInventory == null || i.ContainedItems.Any(it => it.HasTag(containedTag) && it.ConditionPercentage > conditionPercentage)));
|
||||
}
|
||||
|
||||
public static void StructureDamaged(Structure structure, float damageAmount, Character character)
|
||||
@@ -1889,7 +1922,7 @@ namespace Barotrauma
|
||||
float fireFactor = 1;
|
||||
if (!ignoreFire)
|
||||
{
|
||||
float calculateFire(Hull h) => h.FireSources.Count * 0.5f + h.FireSources.Sum(fs => fs.DamageRange) / h.Size.X;
|
||||
static float calculateFire(Hull h) => h.FireSources.Count * 0.5f + h.FireSources.Sum(fs => fs.DamageRange) / h.Size.X;
|
||||
// Even the smallest fire reduces the safety by 50%
|
||||
float fire = visibleHulls == null ? calculateFire(hull) : visibleHulls.Sum(h => calculateFire(h));
|
||||
fireFactor = MathHelper.Lerp(1, 0, MathHelper.Clamp(fire, 0, 1));
|
||||
@@ -1897,10 +1930,22 @@ namespace Barotrauma
|
||||
float enemyFactor = 1;
|
||||
if (!ignoreEnemies)
|
||||
{
|
||||
bool isValidTarget(Character e) => IsActive(e) && !IsFriendly(character, e) && !e.IsArrested;
|
||||
int enemyCount = visibleHulls == null ?
|
||||
Character.CharacterList.Count(e => isValidTarget(e) && e.CurrentHull == hull) :
|
||||
Character.CharacterList.Count(e => isValidTarget(e) && visibleHulls.Contains(e.CurrentHull));
|
||||
int enemyCount = 0;
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (visibleHulls == null)
|
||||
{
|
||||
if (c.CurrentHull != hull) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!visibleHulls.Contains(c.CurrentHull)) { continue; }
|
||||
}
|
||||
if (IsActive(c) && !IsFriendly(character, c) && !c.IsArrested)
|
||||
{
|
||||
enemyCount++;
|
||||
}
|
||||
}
|
||||
// The hull safety decreases 90% per enemy up to 100% (TODO: test smaller percentages)
|
||||
enemyFactor = MathHelper.Lerp(1, 0, MathHelper.Clamp(enemyCount * 0.9f, 0, 1));
|
||||
}
|
||||
@@ -1911,6 +1956,7 @@ namespace Barotrauma
|
||||
if (item.Prefab != null && item.Prefab.IsDangerous)
|
||||
{
|
||||
dangerousItemsFactor = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
float safety = oxygenFactor * waterFactor * fireFactor * enemyFactor * dangerousItemsFactor;
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
var connectionPanel = item.GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel != null && connectionPanel.Connections.Any(c => c.Wires.Any(w => w != null)))
|
||||
if (connectionPanel != null && connectionPanel.Connections.Any(c => c.Wires.Count > 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using static Barotrauma.AIObjectiveFindSafety;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -775,7 +776,13 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
retreatTarget = findSafety.FindBestHull(HumanAIController.VisibleHulls, allowChangingTheSubmarine: character.TeamID != CharacterTeamType.FriendlyNPC);
|
||||
HullSearchStatus hullSearchStatus = findSafety.FindBestHull(out Hull potentialSafeHull, HumanAIController.VisibleHulls, allowChangingSubmarine: character.TeamID != CharacterTeamType.FriendlyNPC);
|
||||
if (hullSearchStatus != HullSearchStatus.Finished)
|
||||
{
|
||||
findSafety.UpdateSimpleEscape(deltaTime);
|
||||
return;
|
||||
}
|
||||
retreatTarget = potentialSafeHull;
|
||||
findHullTimer = findHullInterval * Rand.Range(0.9f, 1.1f);
|
||||
}
|
||||
}
|
||||
@@ -785,21 +792,21 @@ namespace Barotrauma
|
||||
{
|
||||
UsePathingOutside = false
|
||||
},
|
||||
onAbandon: () =>
|
||||
onAbandon: () =>
|
||||
{
|
||||
if (Enemy != null && HumanAIController.VisibleHulls.Contains(Enemy.CurrentHull))
|
||||
{
|
||||
if (Enemy != null && HumanAIController.VisibleHulls.Contains(Enemy.CurrentHull))
|
||||
{
|
||||
// If in the same room with an enemy -> don't try to escape because we'd want to fight it
|
||||
SteeringManager.Reset();
|
||||
RemoveSubObjective(ref retreatObjective);
|
||||
}
|
||||
else
|
||||
{
|
||||
// else abandon and fall back to find safety mode
|
||||
Abandon = true;
|
||||
}
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref retreatObjective));
|
||||
// If in the same room with an enemy -> don't try to escape because we'd want to fight it
|
||||
SteeringManager.Reset();
|
||||
RemoveSubObjective(ref retreatObjective);
|
||||
}
|
||||
else
|
||||
{
|
||||
// else abandon and fall back to find safety mode
|
||||
Abandon = true;
|
||||
}
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref retreatObjective));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FarseerPhysics;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -192,9 +193,17 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
HullSearchStatus hullSearchStatus = FindBestHull(out Hull potentialSafeHull, allowChangingSubmarine: character.TeamID != CharacterTeamType.FriendlyNPC);
|
||||
if (hullSearchStatus != HullSearchStatus.Finished)
|
||||
{
|
||||
UpdateSimpleEscape(deltaTime);
|
||||
return;
|
||||
}
|
||||
|
||||
searchHullTimer = SearchHullInterval * Rand.Range(0.9f, 1.1f);
|
||||
previousSafeHull = currentSafeHull;
|
||||
currentSafeHull = FindBestHull(allowChangingTheSubmarine: character.TeamID != CharacterTeamType.FriendlyNPC);
|
||||
currentSafeHull = potentialSafeHull;
|
||||
|
||||
cannotFindSafeHull = currentSafeHull == null || HumanAIController.NeedsDivingGear(currentSafeHull, out _);
|
||||
if (currentSafeHull == null)
|
||||
{
|
||||
@@ -250,58 +259,122 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
if (subObjectives.Any(so => so.CanBeCompleted)) { return; }
|
||||
if (currentHull != null)
|
||||
UpdateSimpleEscape(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSimpleEscape(float deltaTime)
|
||||
{
|
||||
Vector2 escapeVel = Vector2.Zero;
|
||||
if (character.CurrentHull != null)
|
||||
{
|
||||
foreach (Hull hull in HumanAIController.VisibleHulls)
|
||||
{
|
||||
//goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found)
|
||||
// -> attempt to manually steer away from hazards
|
||||
Vector2 escapeVel = Vector2.Zero;
|
||||
foreach (Hull hull in HumanAIController.VisibleHulls)
|
||||
foreach (FireSource fireSource in hull.FireSources)
|
||||
{
|
||||
foreach (FireSource fireSource in hull.FireSources)
|
||||
{
|
||||
Vector2 dir = character.Position - fireSource.Position;
|
||||
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f);
|
||||
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
|
||||
}
|
||||
}
|
||||
foreach (Character enemy in Character.CharacterList)
|
||||
{
|
||||
if (!HumanAIController.IsActive(enemy) || HumanAIController.IsFriendly(enemy) || enemy.IsArrested) { continue; }
|
||||
if (HumanAIController.VisibleHulls.Contains(enemy.CurrentHull))
|
||||
{
|
||||
Vector2 dir = character.Position - enemy.Position;
|
||||
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f);
|
||||
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
|
||||
}
|
||||
}
|
||||
if (escapeVel != Vector2.Zero)
|
||||
{
|
||||
float left = currentHull.Rect.X + 50;
|
||||
float right = currentHull.Rect.Right - 50;
|
||||
//only move if we haven't reached the edge of the room
|
||||
if (escapeVel.X < 0 && character.Position.X > left || escapeVel.X > 0 && character.Position.X < right)
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left;
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
return;
|
||||
Vector2 dir = character.Position - fireSource.Position;
|
||||
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f);
|
||||
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
|
||||
}
|
||||
}
|
||||
foreach (Character enemy in Character.CharacterList)
|
||||
{
|
||||
if (!HumanAIController.IsActive(enemy) || HumanAIController.IsFriendly(enemy) || enemy.IsArrested) { continue; }
|
||||
if (HumanAIController.VisibleHulls.Contains(enemy.CurrentHull))
|
||||
{
|
||||
Vector2 dir = character.Position - enemy.Position;
|
||||
float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f);
|
||||
escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (escapeVel != Vector2.Zero)
|
||||
{
|
||||
float left = character.CurrentHull.Rect.X + 50;
|
||||
float right = character.CurrentHull.Rect.Right - 50;
|
||||
//only move if we haven't reached the edge of the room
|
||||
if (escapeVel.X < 0 && character.Position.X > left || escapeVel.X > 0 && character.Position.X < right)
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left;
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
objectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public Hull FindBestHull(IEnumerable<Hull> ignoredHulls = null, bool allowChangingTheSubmarine = true)
|
||||
public enum HullSearchStatus
|
||||
{
|
||||
//sort the hulls based on distance and which sub they're in
|
||||
//tends to make the method much faster, because we find a potential hull earlier and can discard further-away hulls more easily
|
||||
//(for instance, an NPC in an outpost might otherwise go through all the hulls in the main sub first and do tons of expensive
|
||||
//path calculations, only to discard all of them when going through the hulls in the outpost)
|
||||
float EstimateHullSuitability(Hull hull)
|
||||
Running,
|
||||
Finished
|
||||
}
|
||||
|
||||
private readonly List<Hull> hulls = new List<Hull>();
|
||||
private int hullSearchIndex = -1;
|
||||
float bestHullValue = 0;
|
||||
bool bestHullIsAirlock = false;
|
||||
Hull potentialBestHull;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the best (safe, nearby) hull the character can find a path to.
|
||||
/// Checks one hull at a time, and returns HullSearchStatus.Finished when all potential hulls have been checked.
|
||||
/// </summary>
|
||||
public HullSearchStatus FindBestHull(out Hull bestHull, IEnumerable<Hull> ignoredHulls = null, bool allowChangingSubmarine = true)
|
||||
{
|
||||
if (hullSearchIndex == -1)
|
||||
{
|
||||
bestHullValue = 0;
|
||||
potentialBestHull = null;
|
||||
bestHullIsAirlock = false;
|
||||
hulls.Clear();
|
||||
var connectedSubs = character.Submarine?.GetConnectedSubs();
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
if (hull.Submarine == null) { continue; }
|
||||
// Ruins are mazes filled with water. There's no safe hulls and we don't want to use the resources on it.
|
||||
if (hull.Submarine.Info.IsRuin) { continue; }
|
||||
if (!allowChangingSubmarine && hull.Submarine != character.Submarine) { continue; }
|
||||
if (hull.Rect.Height < ConvertUnits.ToDisplayUnits(character.AnimController.ColliderHeightFromFloor) * 2) { continue; }
|
||||
if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; }
|
||||
if (HumanAIController.UnreachableHulls.Contains(hull)) { continue; }
|
||||
if (connectedSubs != null && !connectedSubs.Contains(hull.Submarine)) { continue; }
|
||||
|
||||
//sort the hulls based on distance and which sub they're in
|
||||
//tends to make the method much faster, because we find a potential hull earlier and can discard further-away hulls more easily
|
||||
//(for instance, an NPC in an outpost might otherwise go through all the hulls in the main sub first and do tons of expensive
|
||||
//path calculations, only to discard all of them when going through the hulls in the outpost)
|
||||
float hullSuitability = EstimateHullSuitability(character, hull);
|
||||
if (!hulls.Any())
|
||||
{
|
||||
hulls.Add(hull);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < hulls.Count; i++)
|
||||
{
|
||||
if (hullSuitability > EstimateHullSuitability(character, hulls[i]))
|
||||
{
|
||||
hulls.Insert(i, hull);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hulls.None())
|
||||
{
|
||||
bestHull = null;
|
||||
return HullSearchStatus.Finished;
|
||||
}
|
||||
hullSearchIndex = 0;
|
||||
}
|
||||
|
||||
static float EstimateHullSuitability(Character character, Hull hull)
|
||||
{
|
||||
float dist =
|
||||
Math.Abs(hull.WorldPosition.X - character.WorldPosition.X) +
|
||||
@@ -314,86 +387,91 @@ namespace Barotrauma
|
||||
return suitability;
|
||||
}
|
||||
|
||||
Hull bestHull = null;
|
||||
float bestValue = 0;
|
||||
bool bestIsAirlock = false;
|
||||
foreach (Hull hull in Hull.HullList.OrderByDescending(h => EstimateHullSuitability(h)))
|
||||
Hull potentialHull = hulls[hullSearchIndex];
|
||||
|
||||
float hullSafety = 0;
|
||||
bool hullIsAirlock = false;
|
||||
bool isCharacterInside = character.CurrentHull != null && character.Submarine != null;
|
||||
if (isCharacterInside)
|
||||
{
|
||||
if (hull.Submarine == null) { continue; }
|
||||
// Ruins are mazes filled with water. There's no safe hulls and we don't want to use the resources on it.
|
||||
if (hull.Submarine.Info.IsRuin) { continue; }
|
||||
if (!allowChangingTheSubmarine && hull.Submarine != character.Submarine) { continue; }
|
||||
if (hull.Rect.Height < ConvertUnits.ToDisplayUnits(character.AnimController.ColliderHeightFromFloor) * 2) { continue; }
|
||||
if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; }
|
||||
if (HumanAIController.UnreachableHulls.Contains(hull)) { continue; }
|
||||
float hullSafety = 0;
|
||||
bool hullIsAirlock = false;
|
||||
bool isCharacterInside = character.CurrentHull != null && character.Submarine != null;
|
||||
if (isCharacterInside)
|
||||
{
|
||||
if (!character.Submarine.IsConnectedTo(hull.Submarine)) { continue; }
|
||||
hullSafety = HumanAIController.GetHullSafety(hull, hull.GetConnectedHulls(true, 1), character);
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - hull.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 3 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - hull.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.9f, MathUtils.InverseLerp(0, 10000, dist));
|
||||
hullSafety *= distanceFactor;
|
||||
//skip the hull if the safety is already less than the best hull
|
||||
//(no need to do the expensive pathfinding if we already know we're not going to choose this hull)
|
||||
if (hullSafety < bestValue) { continue; }
|
||||
hullSafety = HumanAIController.GetHullSafety(potentialHull, potentialHull.GetConnectedHulls(true, 1), character);
|
||||
float yDist = Math.Abs(character.WorldPosition.Y - potentialHull.WorldPosition.Y);
|
||||
yDist = yDist > 100 ? yDist * 3 : 0;
|
||||
float dist = Math.Abs(character.WorldPosition.X - potentialHull.WorldPosition.X) + yDist;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.9f, MathUtils.InverseLerp(0, 10000, dist));
|
||||
hullSafety *= distanceFactor;
|
||||
//skip the hull if the safety is already less than the best hull
|
||||
//(no need to do the expensive pathfinding if we already know we're not going to choose this hull)
|
||||
if (hullSafety > bestHullValue)
|
||||
{
|
||||
//avoid airlock modules if not allowed to change the sub
|
||||
if (!allowChangingTheSubmarine && hull.OutpostModuleTags.Any(t => t == "airlock"))
|
||||
if (allowChangingSubmarine || !potentialHull.OutpostModuleTags.Any(t => t == "airlock"))
|
||||
{
|
||||
continue;
|
||||
// Don't allow to go outside if not already outside.
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, potentialHull.SimPosition, character.Submarine, nodeFilter: node => node.Waypoint.CurrentHull != null);
|
||||
if (path.Unreachable)
|
||||
{
|
||||
hullSafety = 0;
|
||||
HumanAIController.UnreachableHulls.Add(potentialHull);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Each unsafe node reduces the hull safety value.
|
||||
// Ignore the current hull, because otherwise we couldn't find a path out.
|
||||
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
|
||||
hullSafety /= 1 + unsafeNodes;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(potentialHull, true))
|
||||
{
|
||||
hullSafety /= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't allow to go outside if not already outside.
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition, character.Submarine, nodeFilter: node => node.Waypoint.CurrentHull != null);
|
||||
if (path.Unreachable)
|
||||
else
|
||||
{
|
||||
HumanAIController.UnreachableHulls.Add(hull);
|
||||
continue;
|
||||
hullSafety = 0;
|
||||
}
|
||||
// Each unsafe node reduces the hull safety value.
|
||||
// Ignore the current hull, because otherwise we couldn't find a path out.
|
||||
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
|
||||
hullSafety /= 1 + unsafeNodes;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(hull, true))
|
||||
{
|
||||
hullSafety /= 10;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: could also target gaps that get us inside?
|
||||
if (hull.IsTaggedAirlock())
|
||||
{
|
||||
hullSafety = 100;
|
||||
hullIsAirlock = true;
|
||||
}
|
||||
else if(!bestIsAirlock && hull.LeadsOutside(character))
|
||||
{
|
||||
hullSafety = 100;
|
||||
}
|
||||
// Huge preference for closer targets
|
||||
float distance = Vector2.DistanceSquared(character.WorldPosition, hull.WorldPosition);
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, MathUtils.Pow(100000, 2), distance));
|
||||
hullSafety *= distanceFactor;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
// Intentionally exclude wrecks from this check
|
||||
if (hull.Submarine.TeamID != character.TeamID && hull.Submarine.TeamID != CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
hullSafety /= 10;
|
||||
}
|
||||
}
|
||||
if (hullSafety > bestValue || (!isCharacterInside && hullIsAirlock && !bestIsAirlock))
|
||||
{
|
||||
bestHull = hull;
|
||||
bestValue = hullSafety;
|
||||
bestIsAirlock = hullIsAirlock;
|
||||
}
|
||||
}
|
||||
return bestHull;
|
||||
else
|
||||
{
|
||||
// TODO: could also target gaps that get us inside?
|
||||
if (potentialHull.IsTaggedAirlock())
|
||||
{
|
||||
hullSafety = 100;
|
||||
hullIsAirlock = true;
|
||||
}
|
||||
else if(!bestHullIsAirlock && potentialHull.LeadsOutside(character))
|
||||
{
|
||||
hullSafety = 100;
|
||||
}
|
||||
// Huge preference for closer targets
|
||||
float distance = Vector2.DistanceSquared(character.WorldPosition, potentialHull.WorldPosition);
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, MathUtils.Pow(100000, 2), distance));
|
||||
hullSafety *= distanceFactor;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
// Intentionally exclude wrecks from this check
|
||||
if (potentialHull.Submarine.TeamID != character.TeamID && potentialHull.Submarine.TeamID != CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
hullSafety /= 10;
|
||||
}
|
||||
}
|
||||
if (hullSafety > bestHullValue || (!isCharacterInside && hullIsAirlock && !bestHullIsAirlock))
|
||||
{
|
||||
potentialBestHull = potentialHull;
|
||||
bestHullValue = hullSafety;
|
||||
bestHullIsAirlock = hullIsAirlock;
|
||||
}
|
||||
|
||||
bestHull = potentialBestHull;
|
||||
hullSearchIndex++;
|
||||
|
||||
if (hullSearchIndex >= hulls.Count)
|
||||
{
|
||||
hullSearchIndex = -1;
|
||||
return HullSearchStatus.Finished;
|
||||
}
|
||||
return HullSearchStatus.Running;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
|
||||
@@ -165,6 +165,7 @@ namespace Barotrauma
|
||||
requiredCondition = () =>
|
||||
Leak.Submarine == character.Submarine &&
|
||||
Leak.linkedTo.Any(e => e is Hull h && character.CurrentHull == h),
|
||||
endNodeFilter = n => n.Waypoint.CurrentHull != null && Leak.linkedTo.Any(e => e is Hull h && h == n.Waypoint.CurrentHull),
|
||||
// The Go To objective can be abandoned if the leak is fixed (in which case we don't want to use the dialogue)
|
||||
SpeakCannotReachCondition = () => !CheckObjectiveSpecific()
|
||||
},
|
||||
|
||||
@@ -471,7 +471,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (spawnItemIfNotFound)
|
||||
{
|
||||
if (!(MapEntityPrefab.List.FirstOrDefault(me => me is ItemPrefab ip && IdentifiersOrTags.Any(id => id == ip.Identifier || ip.Tags.Contains(id))) is ItemPrefab prefab))
|
||||
ItemPrefab prefab = FindItemToSpawn();
|
||||
if (prefab == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"{character.Name}: Cannot find an item with the following identifier(s) or tag(s): {string.Join(", ", IdentifiersOrTags)}, tried to spawn the item but no matching item prefabs were found.", Color.Yellow);
|
||||
@@ -501,6 +502,33 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the "best" item to spawn when using <see cref="spawnItemIfNotFound"/> and there's multiple suitable items.
|
||||
/// Best in this context is the one that's sold at the lowest price in stores (usually the most "basic" item)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private ItemPrefab FindItemToSpawn()
|
||||
{
|
||||
ItemPrefab bestItem = null;
|
||||
float lowestCost = float.MaxValue;
|
||||
foreach (MapEntityPrefab prefab in MapEntityPrefab.List)
|
||||
{
|
||||
if (!(prefab is ItemPrefab itemPrefab)) { continue; }
|
||||
if (IdentifiersOrTags.Any(id => id == prefab.Identifier || prefab.Tags.Contains(id)))
|
||||
{
|
||||
float cost = itemPrefab.DefaultPrice != null && itemPrefab.CanBeBought ?
|
||||
itemPrefab.DefaultPrice.Price :
|
||||
float.MaxValue;
|
||||
if (cost < lowestCost || bestItem == null)
|
||||
{
|
||||
bestItem = itemPrefab;
|
||||
lowestCost = cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestItem;
|
||||
}
|
||||
|
||||
protected override bool CheckObjectiveSpecific()
|
||||
{
|
||||
if (IsCompleted) { return true; }
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static Barotrauma.AIObjectiveFindSafety;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -186,7 +187,9 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
safeHull = objectiveManager.GetObjective<AIObjectiveFindSafety>().FindBestHull(HumanAIController.VisibleHulls);
|
||||
HullSearchStatus hullSearchStatus = objectiveManager.GetObjective<AIObjectiveFindSafety>().FindBestHull(out Hull potentialSafeHull, HumanAIController.VisibleHulls);
|
||||
if (hullSearchStatus != HullSearchStatus.Finished) { return; }
|
||||
safeHull = potentialSafeHull;
|
||||
findHullTimer = findHullInterval * Rand.Range(0.9f, 1.1f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Barotrauma
|
||||
public bool IsAiming => wasAiming;
|
||||
public bool IsAimingMelee => wasAimingMelee;
|
||||
|
||||
protected bool Aiming => aiming || aimingMelee;
|
||||
protected bool Aiming => aiming || aimingMelee || LockFlippingUntil > Timing.TotalTime && character.IsKeyDown(InputType.Aim);
|
||||
|
||||
public float ArmLength => upperArmLength + forearmLength;
|
||||
|
||||
@@ -275,6 +275,8 @@ namespace Barotrauma
|
||||
// We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative.
|
||||
public bool IsAboveFloor => GetHeightFromFloor() > -0.1f;
|
||||
|
||||
public float LockFlippingUntil;
|
||||
|
||||
public void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
|
||||
{
|
||||
useItemTimer = 0.5f;
|
||||
@@ -380,18 +382,10 @@ namespace Barotrauma
|
||||
{
|
||||
//if holding two items that should control the characters' pose, let the item in the right hand do it
|
||||
bool anotherItemControlsPose = equippedInLefthand && rightHandItem != item && (rightHandItem?.GetComponent<Holdable>()?.ControlPose ?? false);
|
||||
if (!anotherItemControlsPose)
|
||||
if (!anotherItemControlsPose && TargetMovement == Vector2.Zero && inWater)
|
||||
{
|
||||
var head = GetLimb(LimbType.Head);
|
||||
if (head != null)
|
||||
{
|
||||
head.body.SmoothRotate(itemAngle, force: 30 * head.Mass);
|
||||
}
|
||||
if (TargetMovement == Vector2.Zero && inWater)
|
||||
{
|
||||
torso.body.AngularVelocity -= torso.body.AngularVelocity * 0.1f;
|
||||
torso.body.ApplyForce(torso.body.LinearVelocity * -0.5f);
|
||||
}
|
||||
torso.body.AngularVelocity -= torso.body.AngularVelocity * 0.1f;
|
||||
torso.body.ApplyForce(torso.body.LinearVelocity * -0.5f);
|
||||
}
|
||||
aiming = true;
|
||||
}
|
||||
|
||||
@@ -164,8 +164,6 @@ namespace Barotrauma
|
||||
public float LegBendTorque => CurrentGroundedParams.LegBendTorque * RagdollParams.JointScale;
|
||||
public Vector2 HandMoveOffset => CurrentGroundedParams.HandMoveOffset * RagdollParams.JointScale;
|
||||
|
||||
public float LockFlippingUntil;
|
||||
|
||||
public override Vector2 AimSourceSimPos
|
||||
{
|
||||
get
|
||||
@@ -841,7 +839,7 @@ namespace Barotrauma
|
||||
rotation += 360;
|
||||
}
|
||||
float targetSpeed = TargetMovement.Length();
|
||||
if (targetSpeed > 0.1f && !character.IsRemotelyControlled && !character.IsKeyDown(InputType.Aim))
|
||||
if (targetSpeed > 0.1f && !character.IsRemotelyControlled && !Aiming)
|
||||
{
|
||||
if (Anim != Animation.UsingConstruction && !(character.SelectedConstruction?.GetComponent<Controller>()?.ControlCharacterPose ?? false))
|
||||
{
|
||||
|
||||
@@ -153,12 +153,12 @@ namespace Barotrauma
|
||||
protected ActiveTeamChange currentTeamChange;
|
||||
const string OriginalTeamIdentifier = "original";
|
||||
|
||||
public static void ThrowIfAccessingWalletsInSingleplayer()
|
||||
private void ThrowIfAccessingWalletsInSingleplayer()
|
||||
{
|
||||
#if CLIENT && DEBUG
|
||||
if (Screen.Selected is TestScreen) { return; }
|
||||
#endif
|
||||
if (GameMain.NetworkMember is null || GameMain.IsSingleplayer)
|
||||
if ((GameMain.NetworkMember is null || GameMain.IsSingleplayer) && IsPlayer)
|
||||
{
|
||||
throw new InvalidOperationException($"Tried to access crew wallets in singleplayer. Use {nameof(CampaignMode)}.{nameof(CampaignMode.Bank)} or {nameof(CampaignMode)}.{nameof(CampaignMode.GetWallet)} instead.");
|
||||
}
|
||||
@@ -560,18 +560,35 @@ namespace Barotrauma
|
||||
|
||||
#if CLIENT
|
||||
CharacterHealth.SetHealthBarVisibility(value == null);
|
||||
#elif SERVER
|
||||
if (value is { IsDead: true, Wallet: { Balance: var balance } grabbedWallet } && balance > 0)
|
||||
#endif
|
||||
bool isServerOrSingleplayer = GameMain.IsSingleplayer || GameMain.NetworkMember is { IsServer: true };
|
||||
if (IsPlayer && isServerOrSingleplayer && value is { IsDead: true, Wallet: { Balance: var balance } grabbedWallet } && balance > 0)
|
||||
{
|
||||
if (GameMain.GameSession.Campaign is MultiPlayerCampaign mpCampaign)
|
||||
#if SERVER
|
||||
if (GameMain.GameSession.Campaign is MultiPlayerCampaign mpCampaign && GameMain.Server is { ServerSettings: { } settings })
|
||||
{
|
||||
mpCampaign.Bank.Give(balance);
|
||||
switch (settings.LootedMoneyDestination)
|
||||
{
|
||||
case LootedMoneyDestination.Wallet when IsPlayer:
|
||||
Wallet.Give(balance);
|
||||
break;
|
||||
default:
|
||||
mpCampaign.Bank.Give(balance);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
grabbedWallet.Deduct(balance);
|
||||
GameServer.Log($"{GameServer.CharacterLogName(this)} grabbed {value.Name}'s body and received {grabbedWallet.Balance} mk.", ServerLog.MessageType.Money);
|
||||
}
|
||||
#elif CLIENT
|
||||
if (GameMain.GameSession.Campaign is SinglePlayerCampaign spCampaign)
|
||||
{
|
||||
spCampaign.Bank.Give(balance);
|
||||
}
|
||||
#endif
|
||||
|
||||
grabbedWallet.Deduct(balance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1443,7 +1460,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (Item item in Inventory.AllItems)
|
||||
{
|
||||
if (item?.Prefab.Identifier != "idcard") { continue; }
|
||||
if (item?.GetComponent<IdCard>() == null) { continue; }
|
||||
foreach (string s in spawnPoint.IdCardTags)
|
||||
{
|
||||
item.AddTag(s);
|
||||
@@ -3954,7 +3971,10 @@ namespace Barotrauma
|
||||
if (actionType != ActionType.OnDamaged && actionType != ActionType.OnSevered)
|
||||
{
|
||||
// OnDamaged is called only for the limb that is hit.
|
||||
AnimController.Limbs.ForEach(l => l.ApplyStatusEffects(actionType, deltaTime));
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
limb.ApplyStatusEffects(actionType, deltaTime);
|
||||
}
|
||||
}
|
||||
//OnActive effects are handled by the afflictions themselves
|
||||
if (actionType != ActionType.OnActive)
|
||||
|
||||
@@ -252,12 +252,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Endocrine boosters can unlock talents outside the user's talent tree. This method is used to specifically get them
|
||||
/// Returns unlocked talents that aren't part of the character's talent tree (which can be unlocked e.g. with an endocrine booster)
|
||||
/// </summary>
|
||||
public IEnumerable<Identifier> GetEndocrineTalents()
|
||||
public IEnumerable<Identifier> GetUnlockedTalentsOutsideTree()
|
||||
{
|
||||
if (!TalentTree.JobTalentTrees.TryGet(Job.Prefab.Identifier, out TalentTree talentTree)) { return Enumerable.Empty<Identifier>(); }
|
||||
|
||||
return UnlockedTalents.Where(t => !talentTree.TalentIsInTree(t));
|
||||
}
|
||||
|
||||
@@ -1182,7 +1181,7 @@ namespace Barotrauma
|
||||
// Replace the name tag of any existing id cards or duffel bags
|
||||
foreach (var item in Item.ItemList)
|
||||
{
|
||||
if (item.Prefab.Identifier != "idcard" && !item.Tags.Contains("despawncontainer")) { continue; }
|
||||
if (!item.HasTag("identitycard") && !item.HasTag("despawncontainer")) { continue; }
|
||||
foreach (var tag in item.Tags.Split(','))
|
||||
{
|
||||
var splitTag = tag.Split(":");
|
||||
|
||||
@@ -300,6 +300,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public static AfflictionPrefab InternalDamage => Prefabs["internaldamage"];
|
||||
public static AfflictionPrefab BiteWounds => Prefabs["bitewounds"];
|
||||
public static AfflictionPrefab ImpactDamage => Prefabs["blunttrauma"];
|
||||
public static AfflictionPrefab Bleeding => Prefabs["bleeding"];
|
||||
public static AfflictionPrefab Burn => Prefabs["burn"];
|
||||
|
||||
@@ -124,7 +124,18 @@ namespace Barotrauma
|
||||
|
||||
public float PressureKillDelay { get; private set; } = 5.0f;
|
||||
|
||||
public float Vitality { get; private set; }
|
||||
private float vitality;
|
||||
public float Vitality
|
||||
{
|
||||
get
|
||||
{
|
||||
return Character.IsDead ? minVitality : vitality;
|
||||
}
|
||||
private set
|
||||
{
|
||||
vitality = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float HealthPercentage => MathUtils.Percentage(Vitality, MaxVitality);
|
||||
|
||||
@@ -725,6 +736,8 @@ namespace Barotrauma
|
||||
AddLimbAffliction(limbHealth: null, newAffliction, allowStacking);
|
||||
}
|
||||
|
||||
partial void UpdateSkinTint();
|
||||
|
||||
partial void UpdateLimbAfflictionOverlays();
|
||||
|
||||
public void Update(float deltaTime)
|
||||
@@ -788,7 +801,7 @@ namespace Barotrauma
|
||||
if (!Character.GodMode)
|
||||
{
|
||||
UpdateLimbAfflictionOverlays();
|
||||
UpdateSkinTint();
|
||||
UpdateSkinTint();
|
||||
CalculateVitality();
|
||||
|
||||
if (Vitality <= MinVitality)
|
||||
@@ -798,23 +811,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSkinTint()
|
||||
{
|
||||
FaceTint = DefaultFaceTint;
|
||||
BodyTint = Color.TransparentBlack;
|
||||
|
||||
if (!(Character?.Params?.Health.ApplyAfflictionColors ?? false)) { return; }
|
||||
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
{
|
||||
var affliction = kvp.Key;
|
||||
Color faceTint = affliction.GetFaceTint();
|
||||
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||
Color bodyTint = affliction.GetBodyTint();
|
||||
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDamageReductions(float deltaTime)
|
||||
{
|
||||
float healthRegen = Character.Params.Health.ConstantHealthRegeneration;
|
||||
@@ -905,6 +901,7 @@ namespace Barotrauma
|
||||
if (Unkillable || Character.GodMode) { return; }
|
||||
|
||||
var (type, affliction) = GetCauseOfDeath();
|
||||
UpdateLimbAfflictionOverlays();
|
||||
UpdateSkinTint();
|
||||
Character.Kill(type, affliction);
|
||||
#if CLIENT
|
||||
|
||||
@@ -105,9 +105,9 @@ namespace Barotrauma
|
||||
return spawnPointTags;
|
||||
}
|
||||
|
||||
public JobPrefab GetJobPrefab(Rand.RandSync randSync = Rand.RandSync.Unsynced)
|
||||
public JobPrefab GetJobPrefab(Rand.RandSync randSync = Rand.RandSync.Unsynced, Func<JobPrefab, bool> predicate = null)
|
||||
{
|
||||
return Job != null && Job != "any" ? JobPrefab.Get(Job) : JobPrefab.Random(randSync);
|
||||
return Job != null && Job != "any" ? JobPrefab.Get(Job) : JobPrefab.Random(randSync, predicate);
|
||||
}
|
||||
|
||||
public void InitializeCharacter(Character npc, ISpatialEntity positionToStayIn = null)
|
||||
|
||||
@@ -209,11 +209,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Prefab.Identifier == "idcard")
|
||||
{
|
||||
IdCard idCardComponent = item.GetComponent<IdCard>();
|
||||
idCardComponent?.Initialize(spawnPoint, character);
|
||||
}
|
||||
IdCard idCardComponent = item.GetComponent<IdCard>();
|
||||
idCardComponent?.Initialize(spawnPoint, character);
|
||||
|
||||
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
|
||||
{
|
||||
|
||||
@@ -293,6 +293,6 @@ namespace Barotrauma
|
||||
//ClothingElement = element.GetChildElement("PortraitClothing");
|
||||
}
|
||||
|
||||
public static JobPrefab Random(Rand.RandSync sync) => Prefabs.GetRandom(p => !p.HiddenJob, sync);
|
||||
public static JobPrefab Random(Rand.RandSync sync, Func<JobPrefab, bool> predicate = null) => Prefabs.GetRandom(p => !p.HiddenJob && (predicate == null || predicate(p)), sync);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
if (isSevered)
|
||||
{
|
||||
damageOverlayStrength = 100.0f;
|
||||
damageOverlayStrength = 1.0f;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -597,18 +597,7 @@ namespace Barotrauma
|
||||
dir = Direction.Right;
|
||||
body = new PhysicsBody(limbParams);
|
||||
type = limbParams.Type;
|
||||
if (limbParams.IgnoreCollisions)
|
||||
{
|
||||
body.CollisionCategories = Category.None;
|
||||
body.CollidesWith = Category.None;
|
||||
IgnoreCollisions = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//limbs don't collide with each other
|
||||
body.CollisionCategories = Physics.CollisionCharacter;
|
||||
body.CollidesWith = Physics.CollisionAll & ~Physics.CollisionCharacter & ~Physics.CollisionItem & ~Physics.CollisionItemBlocking;
|
||||
}
|
||||
IgnoreCollisions = limbParams.IgnoreCollisions;
|
||||
body.UserData = this;
|
||||
pullJoint = new FixedMouseJoint(body.FarseerBody, ConvertUnits.ToSimUnits(limbParams.PullPos * Scale))
|
||||
{
|
||||
@@ -646,10 +635,9 @@ namespace Barotrauma
|
||||
}
|
||||
attack.DamageRange = ConvertUnits.ToDisplayUnits(attack.DamageRange);
|
||||
}
|
||||
if (!character.VariantOf.IsEmpty)
|
||||
if (character is { VariantOf: { IsEmpty: false } })
|
||||
{
|
||||
var attackElement = CharacterPrefab.Prefabs.TryGet(character.VariantOf, out var basePrefab)
|
||||
? basePrefab.ConfigElement.GetChildElement("attack") : null;
|
||||
var attackElement = character.Params.VariantFile.Root.GetChildElement("attack");
|
||||
if (attackElement != null)
|
||||
{
|
||||
attack.DamageMultiplier = attackElement.GetAttributeFloat("damagemultiplier", 1f);
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
|
||||
protected override bool MatchesSingular(Identifier identifier) => identifier == "ballastflorabehavior";
|
||||
protected override bool MatchesPlural(Identifier identifier) => identifier == "ballastflorabehaviors";
|
||||
protected override PrefabCollection<BallastFloraPrefab> prefabs => BallastFloraPrefab.Prefabs;
|
||||
protected override PrefabCollection<BallastFloraPrefab> Prefabs => BallastFloraPrefab.Prefabs;
|
||||
|
||||
protected override BallastFloraPrefab CreatePrefab(ContentXElement element)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
|
||||
protected override bool MatchesSingular(Identifier identifier) => identifier == "cave";
|
||||
protected override bool MatchesPlural(Identifier identifier) => identifier == "cavegenerationparameters";
|
||||
protected override PrefabCollection<CaveGenerationParams> prefabs => CaveGenerationParams.CaveParams;
|
||||
protected override PrefabCollection<CaveGenerationParams> Prefabs => CaveGenerationParams.CaveParams;
|
||||
protected override CaveGenerationParams CreatePrefab(ContentXElement element)
|
||||
{
|
||||
return new CaveGenerationParams(element, this);
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Barotrauma
|
||||
{
|
||||
HashSet<string> texturePaths = new HashSet<string>
|
||||
{
|
||||
ragdollParams.Texture
|
||||
ContentPath.FromRaw(CharacterPrefab.Prefabs[speciesName].ContentPackage, ragdollParams.Texture).Value
|
||||
};
|
||||
foreach (RagdollParams.LimbParams limb in ragdollParams.Limbs)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
|
||||
protected override bool MatchesSingular(Identifier identifier) => identifier == "corpse";
|
||||
protected override bool MatchesPlural(Identifier identifier) => identifier == "corpses";
|
||||
protected override PrefabCollection<CorpsePrefab> prefabs => CorpsePrefab.Prefabs;
|
||||
protected override PrefabCollection<CorpsePrefab> Prefabs => CorpsePrefab.Prefabs;
|
||||
protected override CorpsePrefab CreatePrefab(ContentXElement element)
|
||||
{
|
||||
return new CorpsePrefab(element, this);
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Barotrauma
|
||||
|
||||
protected override bool MatchesSingular(Identifier identifier) => !MatchesPlural(identifier);
|
||||
protected override bool MatchesPlural(Identifier identifier) => identifier == "EventManagerSettings";
|
||||
protected override PrefabCollection<EventManagerSettings> prefabs => EventManagerSettings.Prefabs;
|
||||
protected override PrefabCollection<EventManagerSettings> Prefabs => EventManagerSettings.Prefabs;
|
||||
protected override EventManagerSettings CreatePrefab(ContentXElement element)
|
||||
{
|
||||
return new EventManagerSettings(element, this);
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
|
||||
protected override bool MatchesSingular(Identifier identifier) => identifier == "faction";
|
||||
protected override bool MatchesPlural(Identifier identifier) => identifier == "factions";
|
||||
protected override PrefabCollection<FactionPrefab> prefabs => FactionPrefab.Prefabs;
|
||||
protected override PrefabCollection<FactionPrefab> Prefabs => FactionPrefab.Prefabs;
|
||||
protected override FactionPrefab CreatePrefab(ContentXElement element)
|
||||
{
|
||||
return new FactionPrefab(element, this);
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
|
||||
protected abstract bool MatchesSingular(Identifier identifier);
|
||||
protected abstract bool MatchesPlural(Identifier identifier);
|
||||
protected abstract PrefabCollection<T> prefabs { get; }
|
||||
protected abstract PrefabCollection<T> Prefabs { get; }
|
||||
protected abstract T CreatePrefab(ContentXElement element);
|
||||
|
||||
private void LoadFromXElement(ContentXElement parentElement, bool overriding)
|
||||
@@ -29,14 +29,14 @@ namespace Barotrauma
|
||||
}
|
||||
else if (elemName == "clear")
|
||||
{
|
||||
prefabs.AddOverrideFile(this);
|
||||
Prefabs.AddOverrideFile(this);
|
||||
}
|
||||
else if (MatchesSingular(elemName))
|
||||
{
|
||||
T prefab = CreatePrefab(parentElement);
|
||||
try
|
||||
{
|
||||
prefabs.Add(prefab, overriding);
|
||||
Prefabs.Add(prefab, overriding);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -53,7 +53,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Invalid {GetType().Name} element: {parentElement.Name} in {Path}");
|
||||
DebugConsole.ThrowError($"GenericPrefabFile: Invalid {GetType().Name} element: {parentElement.Name} in {Path}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,12 +68,12 @@ namespace Barotrauma
|
||||
|
||||
public override sealed void UnloadFile()
|
||||
{
|
||||
prefabs.RemoveByFile(this);
|
||||
Prefabs.RemoveByFile(this);
|
||||
}
|
||||
|
||||
public sealed override void Sort()
|
||||
{
|
||||
prefabs.SortAll();
|
||||
Prefabs.SortAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user