Unstable 0.1500.6.0 (1984 edition)
This commit is contained in:
@@ -284,7 +284,7 @@ namespace Barotrauma
|
||||
limb.LastImpactSoundTime = (float)Timing.TotalTime;
|
||||
if (!string.IsNullOrWhiteSpace(limb.HitSoundTag))
|
||||
{
|
||||
bool inWater = limb.inWater;
|
||||
bool inWater = limb.InWater;
|
||||
if (character.CurrentHull != null &&
|
||||
character.CurrentHull.Surface > character.CurrentHull.Rect.Y - character.CurrentHull.Rect.Height + 5.0f &&
|
||||
limb.SimPosition.Y < ConvertUnits.ToSimUnits(character.CurrentHull.Rect.Y - character.CurrentHull.Rect.Height) + limb.body.GetMaxExtent())
|
||||
@@ -348,16 +348,27 @@ namespace Barotrauma
|
||||
float increment = 0.001f;
|
||||
foreach (Character otherCharacter in Character.CharacterList)
|
||||
{
|
||||
if (otherCharacter == character) continue;
|
||||
if (otherCharacter == character) { continue; }
|
||||
startDepth += increment;
|
||||
}
|
||||
//make sure each limb has a distinct depth value
|
||||
List<Limb> depthSortedLimbs = Limbs.OrderBy(l => l.ActiveSprite == null ? 0.0f : l.ActiveSprite.Depth).ToList();
|
||||
//make sure each limb has a distinct depth value
|
||||
List<Limb> depthSortedLimbs = Limbs.OrderBy(l => l.DefaultSpriteDepth).ToList();
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb.ActiveSprite != null)
|
||||
limb.ActiveSprite.Depth = startDepth + depthSortedLimbs.IndexOf(limb) * 0.00001f;
|
||||
if (limb.ActiveSprite == null) { continue; }
|
||||
limb.ActiveSprite.Depth = startDepth + depthSortedLimbs.IndexOf(limb) * 0.00001f;
|
||||
}
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb.ActiveSprite == null) { continue; }
|
||||
if (limb.Params.InheritLimbDepth == LimbType.None) { continue; }
|
||||
var matchingLimb = GetLimb(limb.Params.InheritLimbDepth);
|
||||
if (matchingLimb != null)
|
||||
{
|
||||
limb.ActiveSprite.Depth = matchingLimb.ActiveSprite.Depth - 0.0000001f;
|
||||
}
|
||||
}
|
||||
|
||||
depthSortedLimbs.Reverse();
|
||||
inversedLimbDrawOrder = depthSortedLimbs.ToArray();
|
||||
}
|
||||
@@ -567,6 +578,11 @@ namespace Barotrauma
|
||||
pos = ConvertUnits.ToDisplayUnits(humanoid.LeftHandIKPos);
|
||||
if (humanoid.character.Submarine != null) { pos += humanoid.character.Submarine.Position; }
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
|
||||
|
||||
Vector2 aimPos = humanoid.AimSourceWorldPos;
|
||||
aimPos.Y = -aimPos.Y;
|
||||
GUI.DrawLine(spriteBatch, aimPos - Vector2.UnitY * 3, aimPos + Vector2.UnitY * 3, Color.Red);
|
||||
GUI.DrawLine(spriteBatch, aimPos - Vector2.UnitX * 3, aimPos + Vector2.UnitX * 3, Color.Red);
|
||||
}
|
||||
|
||||
if (character.MemState.Count > 1)
|
||||
|
||||
@@ -405,7 +405,7 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.NetworkMember.RespawnManager?.UseRespawnPrompt ?? false)
|
||||
{
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
if (controlled != null || (!(GameMain.GameSession?.IsRunning ?? false))) { return; }
|
||||
var respawnPrompt = new GUIMessageBox(
|
||||
@@ -1052,8 +1052,18 @@ namespace Barotrauma
|
||||
Position + Vector2.UnitY * 150.0f,
|
||||
Vector2.UnitY * 10.0f,
|
||||
playSound: true,
|
||||
subId: Submarine?.ID ?? -1);;
|
||||
subId: Submarine?.ID ?? -1);
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnTalentGiven(string talentIdentifier)
|
||||
{
|
||||
GUI.AddMessage(TextManager.Get("talentname." + talentIdentifier.ToString()),
|
||||
GUI.Style.Yellow,
|
||||
Position + Vector2.UnitY * 150.0f,
|
||||
Vector2.UnitY * 10.0f,
|
||||
playSound: true,
|
||||
subId: Submarine?.ID ?? -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,7 +680,7 @@ namespace Barotrauma
|
||||
createAttachmentSlider(info.MoustacheIndex, WearableType.Moustache);
|
||||
createAttachmentSlider(info.FaceAttachmentIndex, WearableType.FaceAttachment);
|
||||
|
||||
void createColorSelector(string labelTag, IEnumerable<Color> options, Func<Color> getter,
|
||||
void createColorSelector(string labelTag, IEnumerable<(Color Color, float Commonness)> options, Func<Color> getter,
|
||||
Action<Color> setter)
|
||||
{
|
||||
var selectorItemRT = createItemRectTransform(labelTag, 0.4f);
|
||||
@@ -714,7 +714,7 @@ namespace Barotrauma
|
||||
dropdown.ListBox.Content.RectTransform),
|
||||
style: "ListBoxElement")
|
||||
{
|
||||
UserData = option,
|
||||
UserData = option.Color,
|
||||
CanBeFocused = true
|
||||
};
|
||||
var colorElement =
|
||||
@@ -723,9 +723,9 @@ namespace Barotrauma
|
||||
scaleBasis: ScaleBasis.Smallest),
|
||||
style: null)
|
||||
{
|
||||
Color = option,
|
||||
HoverColor = option,
|
||||
OutlineColor = Color.Lerp(Color.Black, option, 0.5f),
|
||||
Color = option.Color,
|
||||
HoverColor = option.Color,
|
||||
OutlineColor = Color.Lerp(Color.Black, option.Color, 0.5f),
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
@@ -784,19 +784,9 @@ namespace Barotrauma
|
||||
{
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
var headPreset = info.Heads.Keys.GetRandom(Rand.RandSync.Unsynced);
|
||||
info.Head.gender = headPreset.Gender;
|
||||
info.Head.race = headPreset.Race;
|
||||
info.Head.HeadSpriteId = headPreset.ID;
|
||||
|
||||
info.Head.HairIndex = Rand.Int(countAttachmentsOfType(WearableType.Hair), Rand.RandSync.Unsynced);
|
||||
info.Head.BeardIndex = Rand.Int(countAttachmentsOfType(WearableType.Beard), Rand.RandSync.Unsynced);
|
||||
info.Head.MoustacheIndex = Rand.Int(countAttachmentsOfType(WearableType.Moustache), Rand.RandSync.Unsynced);
|
||||
info.Head.FaceAttachmentIndex = Rand.Int(countAttachmentsOfType(WearableType.FaceAttachment), Rand.RandSync.Unsynced);
|
||||
|
||||
info.Head.HairColor = info.HairColors.GetRandom(Rand.RandSync.Unsynced);
|
||||
info.Head.FacialHairColor = info.FacialHairColors.GetRandom(Rand.RandSync.Unsynced);
|
||||
info.Head.SkinColor = info.SkinColors.GetRandom(Rand.RandSync.Unsynced);
|
||||
info.Head = new HeadInfo();
|
||||
info.SetGenderAndRace(Rand.RandSync.Unsynced);
|
||||
info.SetColors();
|
||||
|
||||
RecreateFrameContents();
|
||||
info.RefreshHead();
|
||||
|
||||
@@ -998,10 +998,14 @@ namespace Barotrauma
|
||||
if (Character.Controlled?.SelectedCharacter == null && openHealthWindow == null)
|
||||
{
|
||||
List<(Affliction affliction, string text)> statusIcons = new List<(Affliction affliction, string text)>();
|
||||
if (Character.CurrentHull == null || Character.CurrentHull.LethalPressure > 5.0f)
|
||||
if (Character.InPressure)
|
||||
{
|
||||
statusIcons.Add((pressureAffliction, TextManager.Get("PressureHUDWarning")));
|
||||
}
|
||||
if (Character.CurrentHull != null && Character.OxygenAvailable < LowOxygenThreshold && oxygenLowAffliction.Strength < oxygenLowAffliction.Prefab.ShowIconThreshold)
|
||||
{
|
||||
statusIcons.Add((oxygenLowAffliction, TextManager.Get("OxygenHUDWarning")));
|
||||
}
|
||||
|
||||
foreach (Affliction affliction in currentDisplayedAfflictions)
|
||||
{
|
||||
|
||||
@@ -109,6 +109,7 @@ namespace Barotrauma
|
||||
private float wetTimer;
|
||||
private float dripParticleTimer;
|
||||
private float deadTimer;
|
||||
private Color? randomColor;
|
||||
|
||||
/// <summary>
|
||||
/// Note that different limbs can share the same deformations.
|
||||
@@ -166,6 +167,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public float DefaultSpriteDepth { get; private set; }
|
||||
|
||||
public WearableSprite HuskSprite { get; private set; }
|
||||
public WearableSprite HerpesSprite { get; private set; }
|
||||
|
||||
@@ -309,12 +312,23 @@ namespace Barotrauma
|
||||
Deformations.AddRange(deformations);
|
||||
NonConditionalDeformations.AddRange(deformations);
|
||||
break;
|
||||
case "randomcolor":
|
||||
randomColor = subElement.GetAttributeColorArray("colors", null)?.GetRandom();
|
||||
if (randomColor.HasValue)
|
||||
{
|
||||
Params.GetSprite().Color = randomColor.Value;
|
||||
}
|
||||
break;
|
||||
case "lightsource":
|
||||
LightSource = new LightSource(subElement, GetConditionalTarget())
|
||||
{
|
||||
ParentBody = body,
|
||||
SpriteScale = Vector2.One * Scale * TextureScale
|
||||
};
|
||||
if (randomColor.HasValue)
|
||||
{
|
||||
LightSource.Color = new Color(randomColor.Value.R, randomColor.Value.G, randomColor.Value.B, LightSource.Color.A);
|
||||
}
|
||||
InitialLightSourceColor = LightSource.Color;
|
||||
InitialLightSpriteAlpha = LightSource.OverrideLightSpriteAlpha;
|
||||
break;
|
||||
@@ -383,6 +397,7 @@ namespace Barotrauma
|
||||
return deformations;
|
||||
}
|
||||
}
|
||||
DefaultSpriteDepth = ActiveSprite.Depth;
|
||||
LightSource?.CheckConditionals();
|
||||
}
|
||||
|
||||
@@ -571,8 +586,8 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (ParticleEmitter emitter in character.DamageEmitters)
|
||||
{
|
||||
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
|
||||
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
|
||||
if (InWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
|
||||
if (!InWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
|
||||
ParticlePrefab overrideParticle = null;
|
||||
foreach (DamageModifier damageModifier in result.AppliedDamageModifiers)
|
||||
{
|
||||
@@ -593,8 +608,8 @@ namespace Barotrauma
|
||||
|
||||
foreach (ParticleEmitter emitter in character.BloodEmitters)
|
||||
{
|
||||
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
|
||||
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
|
||||
if (InWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) { continue; }
|
||||
if (!InWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) { continue; }
|
||||
emitter.Emit(1.0f, WorldPosition, character.CurrentHull, sizeMultiplier: bloodParticleSize, amountMultiplier: bloodParticleAmount);
|
||||
}
|
||||
}
|
||||
@@ -618,7 +633,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (inWater)
|
||||
if (InWater)
|
||||
{
|
||||
wetTimer = 1.0f;
|
||||
}
|
||||
|
||||
@@ -2447,14 +2447,44 @@ namespace Barotrauma
|
||||
{
|
||||
if (messages.Any(msg => msg.Text == message)) { return; }
|
||||
messages.Add(new GUIMessage(message, color, lifeTime ?? MathHelper.Clamp(message.Length / 5.0f, 3.0f, 10.0f), font ?? LargeFont));
|
||||
if (playSound) SoundPlayer.PlayUISound(GUISoundType.UIMessage);
|
||||
if (playSound) { SoundPlayer.PlayUISound(GUISoundType.UIMessage); }
|
||||
}
|
||||
|
||||
public static void AddMessage(string message, Color color, Vector2 pos, Vector2 velocity, float lifeTime = 3.0f, bool playSound = true, GUISoundType soundType = GUISoundType.UIMessage, int subId = -1)
|
||||
{
|
||||
Submarine sub = Submarine.Loaded.FirstOrDefault(s => s.ID == subId);
|
||||
messages.Add(new GUIMessage(message, color, pos, velocity, lifeTime, Alignment.Center, LargeFont, sub: sub));
|
||||
if (playSound) SoundPlayer.PlayUISound(soundType);
|
||||
|
||||
var newMessage = new GUIMessage(message, color, pos, velocity, lifeTime, Alignment.Center, LargeFont, sub: sub);
|
||||
if (playSound) { SoundPlayer.PlayUISound(soundType); }
|
||||
bool overlapFound = true;
|
||||
int tries = 0;
|
||||
while (overlapFound)
|
||||
{
|
||||
overlapFound = false;
|
||||
foreach (var otherMessage in messages)
|
||||
{
|
||||
float xDiff = otherMessage.Pos.X - newMessage.Pos.X;
|
||||
if (Math.Abs(xDiff) > (newMessage.Size.X + otherMessage.Size.X) / 2) { continue; }
|
||||
float yDiff = otherMessage.Pos.Y - newMessage.Pos.Y;
|
||||
if (Math.Abs(yDiff) > (newMessage.Size.Y + otherMessage.Size.Y) / 2) { continue; }
|
||||
Vector2 moveDir = -(new Vector2(xDiff, yDiff) + Rand.Vector(1.0f));
|
||||
if (moveDir.LengthSquared() > 0.0001f)
|
||||
{
|
||||
moveDir = Vector2.Normalize(moveDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
moveDir = Rand.Vector(1.0f);
|
||||
}
|
||||
moveDir.Y = -Math.Abs(moveDir.Y);
|
||||
newMessage.Pos += moveDir * 20;
|
||||
overlapFound = true;
|
||||
}
|
||||
tries++;
|
||||
if (tries > 20) { break; }
|
||||
}
|
||||
|
||||
messages.Add(newMessage);
|
||||
}
|
||||
|
||||
public static void ClearMessages()
|
||||
|
||||
@@ -41,13 +41,13 @@ namespace Barotrauma
|
||||
public SpriteSheet SavingIndicator { get; private set; }
|
||||
|
||||
public UISprite UIGlow { get; private set; }
|
||||
public UISprite TalentGlow { get; private set; }
|
||||
|
||||
public UISprite PingCircle { get; private set; }
|
||||
|
||||
public UISprite UIGlowCircular { get; private set; }
|
||||
|
||||
public UISprite UIGlowSolidCircular { get; private set; }
|
||||
public UISprite UIThermalGlow { get; private set; }
|
||||
|
||||
public UISprite ButtonPulse { get; private set; }
|
||||
|
||||
@@ -91,8 +91,8 @@ namespace Barotrauma
|
||||
public Color TextColorDark { get; private set; } = Color.Black * 0.9f;
|
||||
public Color TextColorDim { get; private set; } = Color.White * 0.6f;
|
||||
|
||||
public Color ItemQualityColorPoor { get; private set; } = Color.Gray;
|
||||
public Color ItemQualityColorNormal { get; private set; } = Color.White;
|
||||
public Color ItemQualityColorPoor { get; private set; } = Color.DarkRed;
|
||||
public Color ItemQualityColorNormal { get; private set; } = Color.Gray;
|
||||
public Color ItemQualityColorGood { get; private set; } = Color.LightGreen;
|
||||
public Color ItemQualityColorExcellent { get; private set; } = Color.LightBlue;
|
||||
public Color ItemQualityColorMasterwork { get; private set; } = Color.MediumPurple;
|
||||
@@ -250,9 +250,6 @@ namespace Barotrauma
|
||||
case "uiglow":
|
||||
UIGlow = new UISprite(subElement);
|
||||
break;
|
||||
case "talentglow":
|
||||
TalentGlow = new UISprite(subElement);
|
||||
break;
|
||||
case "pingcircle":
|
||||
PingCircle = new UISprite(subElement);
|
||||
break;
|
||||
@@ -268,6 +265,9 @@ namespace Barotrauma
|
||||
case "uiglowsolidcircular":
|
||||
UIGlowSolidCircular = new UISprite(subElement);
|
||||
break;
|
||||
case "uithermalglow":
|
||||
UIThermalGlow = new UISprite(subElement);
|
||||
break;
|
||||
case "endroundbuttonpulse":
|
||||
ButtonPulse = new UISprite(subElement);
|
||||
break;
|
||||
|
||||
@@ -141,6 +141,10 @@ namespace Barotrauma
|
||||
{
|
||||
int talentCount = selectedTalents.Count - controlled.Info.UnlockedTalents.Count;
|
||||
talentResetButton.Enabled = talentApplyButton.Enabled = talentCount > 0;
|
||||
if (talentApplyButton.Enabled && talentApplyButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
talentApplyButton.Flash(GUI.Style.Orange);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedTab != InfoFrameTab.Crew) return;
|
||||
@@ -1184,7 +1188,7 @@ namespace Barotrauma
|
||||
private Color unselectableColor = new Color(100, 100, 100, 225);
|
||||
private Color pressedColor = new Color(60, 60, 60, 225);
|
||||
|
||||
private readonly List<(GUIButton button, GUIComponent icon, GUIImage glow)> talentButtons = new List<(GUIButton button, GUIComponent icon, GUIImage glow)>();
|
||||
private readonly List<(GUIButton button, GUIComponent icon)> talentButtons = new List<(GUIButton button, GUIComponent icon)>();
|
||||
private readonly List<(string talentTree, int index, GUIImage icon, GUIFrame background, GUIFrame backgroundGlow)> talentCornerIcons = new List<(string talentTree, int index, GUIImage icon, GUIFrame background, GUIFrame backgroundGlow)>();
|
||||
private List<string> selectedTalents = new List<string>();
|
||||
|
||||
@@ -1453,9 +1457,7 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
GUIImage iconGlow = new GUIImage(new RectTransform(Vector2.One, iconImage.RectTransform, anchor: Anchor.Center), sprite: GUI.Style.TalentGlow.Sprite, scaleToFit: true) { Visible = false };
|
||||
|
||||
talentButtons.Add((talentButton, iconImage, iconGlow));
|
||||
talentButtons.Add((talentButton, iconImage));
|
||||
}
|
||||
|
||||
talentCornerIcons.Add((subTree.Identifier, i, cornerIcon, talentBackground, talentBackgroundHighlight));
|
||||
@@ -1567,20 +1569,20 @@ namespace Barotrauma
|
||||
string talentIdentifier = talentButton.button.UserData as string;
|
||||
bool unselectable = !TalentTree.IsViableTalentForCharacter(controlledCharacter, talentIdentifier, selectedTalents) || controlledCharacter.HasTalent(talentIdentifier);
|
||||
Color newTalentColor = unselectable ? unselectableColor : unselectedColor;
|
||||
|
||||
talentButton.glow.Visible = false;
|
||||
Color hoverColor = Color.White;
|
||||
|
||||
if (controlledCharacter.HasTalent(talentIdentifier))
|
||||
{
|
||||
newTalentColor = new Color(140,225,140,255);
|
||||
newTalentColor = GUI.Style.Green;
|
||||
}
|
||||
else if (selectedTalents.Contains(talentIdentifier))
|
||||
{
|
||||
newTalentColor = new Color(174,164,124,255);
|
||||
talentButton.glow.Visible = true;
|
||||
newTalentColor = GUI.Style.Orange;
|
||||
hoverColor = Color.Lerp(GUI.Style.Orange, Color.White, 0.7f);
|
||||
}
|
||||
|
||||
talentButton.icon.Color = newTalentColor;
|
||||
talentButton.icon.HoverColor = hoverColor;
|
||||
}
|
||||
|
||||
CreateTalentSkillList(controlledCharacter, skillListBox);
|
||||
|
||||
@@ -1045,10 +1045,10 @@ namespace Barotrauma
|
||||
public static GUIFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
|
||||
{
|
||||
int price = prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
||||
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton);
|
||||
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
|
||||
}
|
||||
|
||||
public static GUIFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, string title, string body, int price, object? userData, bool addBuyButton = true, bool addProgressBar = true, string buttonStyle = "UpgradeBuyButton")
|
||||
public static GUIFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, string title, string body, int price, object? userData, bool addBuyButton = true, bool addProgressBar = true, string buttonStyle = "UpgradeBuyButton", UpgradePrefab upgradePrefab = null, int currentLevel = 0)
|
||||
{
|
||||
float progressBarHeight = 0.25f;
|
||||
|
||||
@@ -1089,7 +1089,7 @@ namespace Barotrauma
|
||||
//negative price = refund
|
||||
if (price < 0) { formattedPrice = "+" + formattedPrice; }
|
||||
buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
|
||||
var priceText = new GUITextBlock(rectT(1, 0.4f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Center);
|
||||
var priceText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Center);
|
||||
if (price < 0)
|
||||
{
|
||||
priceText.TextColor = GUI.Style.Green;
|
||||
@@ -1099,6 +1099,11 @@ namespace Barotrauma
|
||||
priceText.Text = string.Empty;
|
||||
}
|
||||
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: buttonStyle) { Enabled = false };
|
||||
if (upgradePrefab != null)
|
||||
{
|
||||
var increaseText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), "", textAlignment: Alignment.Center);
|
||||
UpdateUpgradePercentageText(increaseText, upgradePrefab, currentLevel);
|
||||
}
|
||||
}
|
||||
|
||||
description.CalculateHeightFromText();
|
||||
@@ -1127,6 +1132,19 @@ namespace Barotrauma
|
||||
return prefabFrame;
|
||||
}
|
||||
|
||||
private static void UpdateUpgradePercentageText(GUITextBlock text, UpgradePrefab upgradePrefab, int currentLevel)
|
||||
{
|
||||
float nextIncrease = upgradePrefab.IncreaseOnTooltip * (Math.Min(currentLevel + 1, upgradePrefab.MaxLevel));
|
||||
if (nextIncrease != 0f)
|
||||
{
|
||||
text.Text = $"{Math.Round(nextIncrease, 1)} %";
|
||||
if (currentLevel == upgradePrefab.MaxLevel)
|
||||
{
|
||||
text.TextColor = Color.Gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, List<Item>? itemsOnSubmarine)
|
||||
{
|
||||
if (Campaign is null) { return; }
|
||||
@@ -1541,7 +1559,9 @@ namespace Barotrauma
|
||||
|
||||
if (prefabFrame.FindChild("buybutton", true) is { } buttonParent)
|
||||
{
|
||||
GUITextBlock priceLabel = buttonParent.GetChild<GUITextBlock>();
|
||||
List<GUITextBlock> textBlocks = buttonParent.GetAllChildren<GUITextBlock>().ToList();
|
||||
|
||||
GUITextBlock priceLabel = textBlocks[0];
|
||||
int price = prefab.Price.GetBuyprice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation);
|
||||
|
||||
if (priceLabel != null && !WaitForServerUpdate)
|
||||
@@ -1562,6 +1582,11 @@ namespace Barotrauma
|
||||
button.Enabled = false;
|
||||
}
|
||||
}
|
||||
GUITextBlock increaseLabel = textBlocks[1];
|
||||
if (increaseLabel != null && !WaitForServerUpdate)
|
||||
{
|
||||
UpdateUpgradePercentageText(increaseLabel, prefab, currentLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
internal partial class EntitySpawnerComponent
|
||||
{
|
||||
public Vector2 DrawSize => Vector2.Zero;
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
{
|
||||
if (!editing) { return; }
|
||||
|
||||
switch (SpawnAreaShape)
|
||||
{
|
||||
case AreaShape.Rectangle:
|
||||
{
|
||||
RectangleF rect = GetAreaRectangle(SpawnAreaBounds, SpawnAreaOffset, draw: true);
|
||||
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUI.Style.Red, isFilled: false, 0f, 4f);
|
||||
|
||||
if (MaximumAmountRangePadding > 0f)
|
||||
{
|
||||
rect.Inflate(MaximumAmountRangePadding, MaximumAmountRangePadding);
|
||||
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUI.Style.Red, isFilled: false, 0f, 2f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AreaShape.Circle:
|
||||
Vector2 center = item.WorldPosition;
|
||||
center.Y = -center.Y;
|
||||
center += SpawnAreaOffset;
|
||||
spriteBatch.DrawCircle(center, SpawnAreaRadius, 32, GUI.Style.Red, thickness: 4f);
|
||||
|
||||
if (MaximumAmountRangePadding > 0f)
|
||||
{
|
||||
spriteBatch.DrawCircle(center, SpawnAreaRadius + MaximumAmountRangePadding, 32, GUI.Style.Red, thickness: 2f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!OnlySpawnWhenCrewInRange) { return; }
|
||||
|
||||
switch (CrewAreaShape)
|
||||
{
|
||||
case AreaShape.Rectangle:
|
||||
{
|
||||
RectangleF rect = GetAreaRectangle(CrewAreaBounds, CrewAreaOffset, draw: true);
|
||||
GUI.DrawRectangle(spriteBatch, rect.Location, rect.Size, GUI.Style.Green, isFilled: false, 0f, 4f);
|
||||
break;
|
||||
}
|
||||
case AreaShape.Circle:
|
||||
Vector2 center = item.WorldPosition;
|
||||
center.Y = -center.Y;
|
||||
center += CrewAreaOffset;
|
||||
spriteBatch.DrawCircle(center, CrewAreaRadius, 32, GUI.Style.Green);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -342,7 +342,7 @@ namespace Barotrauma.Items.Components
|
||||
new Vector2(currentItemPos.X, -currentItemPos.Y),
|
||||
isWiringMode ? containedItem.GetSpriteColor() * 0.15f : containedItem.GetSpriteColor(),
|
||||
origin,
|
||||
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation + MathHelper.ToRadians(-item.Rotation)),
|
||||
-(containedItem.body == null ? 0.0f : containedItem.body.DrawRotation ),
|
||||
containedItem.Scale,
|
||||
spriteEffects,
|
||||
depth: containedSpriteDepth);
|
||||
|
||||
@@ -78,7 +78,6 @@ namespace Barotrauma.Items.Components
|
||||
None,
|
||||
HullStatus,
|
||||
ElectricalView,
|
||||
HullCondition,
|
||||
ItemFinder
|
||||
}
|
||||
|
||||
@@ -238,7 +237,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
true when EnableHullStatus => MiniMapMode.HullStatus,
|
||||
true when EnableElectricalView => MiniMapMode.ElectricalView,
|
||||
true when EnableHullCondition => MiniMapMode.HullCondition,
|
||||
true when EnableItemFinder => MiniMapMode.ItemFinder,
|
||||
_ => MiniMapMode.None
|
||||
};
|
||||
@@ -263,8 +261,7 @@ namespace Barotrauma.Items.Components
|
||||
modeSwitchButtons = ImmutableArray.Create
|
||||
(
|
||||
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonLayout.RectTransform), string.Empty, style: "StatusMonitorButton.HullStatus") { UserData = MiniMapMode.HullStatus, Enabled = EnableHullStatus, ToolTip = TextManager.Get("StatusMonitorButton.HullStatus.Tooltip") },
|
||||
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonLayout.RectTransform), string.Empty, style: "StatusMonitorButton.ElectricalView") { UserData = MiniMapMode.ElectricalView, Enabled = EnableHullCondition, ToolTip = TextManager.Get("StatusMonitorButton.ElectricalView.Tooltip") },
|
||||
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonLayout.RectTransform), string.Empty, style: "StatusMonitorButton.HullCondition") { UserData = MiniMapMode.HullCondition, Enabled = EnableHullCondition, ToolTip = TextManager.Get("StatusMonitorButton.HullCondition.Tooltip") },
|
||||
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonLayout.RectTransform), string.Empty, style: "StatusMonitorButton.ElectricalView") { UserData = MiniMapMode.ElectricalView, Enabled = EnableElectricalView, ToolTip = TextManager.Get("StatusMonitorButton.ElectricalView.Tooltip") },
|
||||
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonLayout.RectTransform), string.Empty, style: "StatusMonitorButton.ItemFinder") { UserData = MiniMapMode.ItemFinder, Enabled = EnableItemFinder, ToolTip = TextManager.Get("StatusMonitorButton.ItemFinder.Tooltip") }
|
||||
);
|
||||
|
||||
@@ -390,9 +387,9 @@ namespace Barotrauma.Items.Components
|
||||
c.Children.ForEach(c2 => c2.CanBeFocused = false);
|
||||
});
|
||||
|
||||
submarineBack.RectTransform.MaxSize =
|
||||
submarineBack.RectTransform.MaxSize =
|
||||
submarineFront.RectTransform.MaxSize =
|
||||
submarineContainer.RectTransform.MaxSize =
|
||||
submarineContainer.RectTransform.MaxSize =
|
||||
new Point(int.MaxValue, paddedContainer.Rect.Height - bottomFrame.Rect.Height - buttonLayout.Rect.Height);
|
||||
}
|
||||
|
||||
@@ -493,7 +490,7 @@ namespace Barotrauma.Items.Components
|
||||
displayedSubs.Add(item.Submarine);
|
||||
displayedSubs.AddRange(item.Submarine.DockedTo);
|
||||
|
||||
subEntities = MapEntity.mapEntityList.Where(me => me.Submarine == item.Submarine && !me.HiddenInGame).OrderByDescending(w => w.SpriteDepth).ToList();
|
||||
subEntities = MapEntity.mapEntityList.Where(me => (item.Submarine is { } sub && sub.IsEntityFoundOnThisSub(me, includingConnectedSubs: true, allowDifferentType: false)) && !me.HiddenInGame).OrderByDescending(w => w.SpriteDepth).ToList();
|
||||
|
||||
BakeSubmarine(item.Submarine, parentRect);
|
||||
elementSize = GuiFrame.Rect.Size;
|
||||
@@ -598,7 +595,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (currentMode == MiniMapMode.HullStatus && !EnableHullStatus ||
|
||||
currentMode == MiniMapMode.ElectricalView && !EnableElectricalView ||
|
||||
currentMode == MiniMapMode.HullCondition && !EnableHullCondition ||
|
||||
currentMode == MiniMapMode.ItemFinder && !EnableItemFinder)
|
||||
{
|
||||
SetDefaultMode();
|
||||
@@ -606,8 +602,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
modeSwitchButtons[0].Enabled = EnableHullStatus;
|
||||
modeSwitchButtons[1].Enabled = EnableElectricalView;
|
||||
modeSwitchButtons[2].Enabled = EnableHullCondition;
|
||||
modeSwitchButtons[3].Enabled = EnableItemFinder;
|
||||
modeSwitchButtons[2].Enabled = EnableItemFinder;
|
||||
}
|
||||
|
||||
private void UpdateIDCards(Submarine sub)
|
||||
@@ -642,14 +637,14 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentMode == MiniMapMode.HullStatus || currentMode == MiniMapMode.HullCondition)
|
||||
if (currentMode == MiniMapMode.HullStatus)
|
||||
{
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
spriteBatch.End();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = submarineContainer.Rect;
|
||||
|
||||
if (currentMode == MiniMapMode.HullCondition && item.Submarine != null)
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
var sprite = GUI.Style.UIGlowSolidCircular?.Sprite;
|
||||
float alpha = (MathF.Sin(blipState / maxBlipState * MathHelper.TwoPi) + 1.5f) * 0.5f;
|
||||
@@ -849,7 +844,6 @@ namespace Barotrauma.Items.Components
|
||||
switch (currentMode)
|
||||
{
|
||||
case MiniMapMode.HullStatus:
|
||||
case MiniMapMode.HullCondition:
|
||||
UpdateHullStatus();
|
||||
miniMapFrame.Visible = true;
|
||||
reportFrame.Visible = true;
|
||||
@@ -992,8 +986,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!hullStatusComponents.ContainsKey(linkedHull)) { continue; }
|
||||
|
||||
isHoveringOver |=
|
||||
canHoverOverHull &&
|
||||
isHoveringOver |=
|
||||
canHoverOverHull &&
|
||||
(hullStatusComponents[linkedHull].RectComponent == GUI.MouseOn || (draggingReport && hullStatusComponents[linkedHull].RectComponent.MouseRect.Contains(PlayerInput.MousePosition)));
|
||||
if (isHoveringOver) { break; }
|
||||
}
|
||||
@@ -1089,7 +1083,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hullsVisible = currentMode == MiniMapMode.HullStatus || currentMode == MiniMapMode.HullCondition;
|
||||
bool hullsVisible = currentMode == MiniMapMode.HullStatus;
|
||||
|
||||
foreach (var (entity, component) in hullStatusComponents)
|
||||
{
|
||||
@@ -1202,7 +1196,7 @@ namespace Barotrauma.Items.Components
|
||||
GameMain.GameScreen.BlueprintEffect.Parameters["width"].SetValue((float)texture.Width);
|
||||
GameMain.GameScreen.BlueprintEffect.Parameters["height"].SetValue((float)texture.Height);
|
||||
|
||||
Color blueprintBlue = BlueprintBlue * currentMode switch { MiniMapMode.HullStatus => 0.1f, MiniMapMode.HullCondition => 0.1f, MiniMapMode.ElectricalView => 0.1f, _ => 0.5f };
|
||||
Color blueprintBlue = BlueprintBlue * currentMode switch { MiniMapMode.HullStatus => 0.1f, MiniMapMode.ElectricalView => 0.1f, _ => 0.5f };
|
||||
|
||||
Vector2 origin = new Vector2(texture.Width / 2f, texture.Height / 2f);
|
||||
float scale = currentMode == MiniMapMode.HullStatus ? 1.0f : Zoom;
|
||||
|
||||
@@ -132,7 +132,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool isConnectedToSteering;
|
||||
|
||||
private static string caveLabel, ruinLabel;
|
||||
private static string caveLabel;
|
||||
|
||||
|
||||
[Serialize(false, false)]
|
||||
public bool RightLayout
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private bool AllowUsingMineralScanner =>
|
||||
HasMineralScanner && !isConnectedToSteering;
|
||||
@@ -316,7 +324,7 @@ namespace Barotrauma.Items.Components
|
||||
"", warningColor, GUI.LargeFont, Alignment.Center);
|
||||
|
||||
// Setup layout for nav terminal
|
||||
if (isConnectedToSteering)
|
||||
if (isConnectedToSteering || RightLayout)
|
||||
{
|
||||
controlContainer.RectTransform.RelativeOffset = controlBoxOffset;
|
||||
controlContainer.RectTransform.SetPosition(Anchor.TopRight);
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
{
|
||||
if (target == null) { return; }
|
||||
if (target == null || target.Removed) { return; }
|
||||
|
||||
Vector2 startPos = GetSourcePos();
|
||||
startPos.Y = -startPos.Y;
|
||||
@@ -103,7 +103,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Vector2 barrelPos = FarseerPhysics.ConvertUnits.ToDisplayUnits(weapon.TransformedBarrelPos);
|
||||
barrelPos.Y = -barrelPos.Y;
|
||||
startPos += barrelPos * item.Scale;
|
||||
startPos += barrelPos;
|
||||
}
|
||||
}
|
||||
Vector2 endPos = new Vector2(target.DrawPosition.X, target.DrawPosition.Y);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -58,13 +59,13 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
|
||||
float x = 1.0f / (1 + RequiredSignalCount);
|
||||
float y = (x * paddedFrame.Rect.Width) / paddedFrame.Rect.Height;
|
||||
float y = Math.Min((x * paddedFrame.Rect.Width) / paddedFrame.Rect.Height, 0.5f);
|
||||
Vector2 relativeSize = new Vector2(x, y);
|
||||
|
||||
var containerSection = new GUIFrame(new RectTransform(new Vector2(x, 1.0f), paddedFrame.RectTransform), style: null);
|
||||
var containerSlot = new GUIFrame(new RectTransform(new Vector2(1.0f, y), containerSection.RectTransform, anchor: Anchor.Center), style: null);
|
||||
containerHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.2f), containerSlot.RectTransform, Anchor.BottomCenter), style: null);
|
||||
containerIndicator = new GUIImage(new RectTransform(new Vector2(0.5f, 0.5f * y), containerSection.RectTransform, anchor: Anchor.Center) { RelativeOffset = new Vector2(0.0f, 0.05f + 0.5f * y) },
|
||||
containerIndicator = new GUIImage(new RectTransform(new Vector2(0.5f, 0.5f * (1.0f - y)), containerSection.RectTransform, anchor: Anchor.BottomCenter),
|
||||
style: "IndicatorLightRed", scaleToFit: true);
|
||||
|
||||
for (int i = 0; i < RequiredSignalCount; i++)
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
[Serialize(false, false)]
|
||||
public bool SeeThroughWalls
|
||||
public bool ThermalGoggles
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
@@ -76,6 +76,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool isEquippable;
|
||||
|
||||
private float thermalEffectState;
|
||||
|
||||
public IEnumerable<Character> VisibleCharacters
|
||||
{
|
||||
get
|
||||
@@ -108,7 +110,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
refEntity = item;
|
||||
}
|
||||
|
||||
|
||||
thermalEffectState += deltaTime;
|
||||
thermalEffectState %= 10000.0f;
|
||||
|
||||
if (updateTimer > 0.0f)
|
||||
{
|
||||
updateTimer -= deltaTime;
|
||||
@@ -125,7 +130,7 @@ namespace Barotrauma.Items.Components
|
||||
if (dist < Range * Range)
|
||||
{
|
||||
Vector2 diff = c.WorldPosition - refEntity.WorldPosition;
|
||||
if (SeeThroughWalls || Submarine.CheckVisibility(refEntity.SimPosition, refEntity.SimPosition + ConvertUnits.ToSimUnits(diff)) == null)
|
||||
if (Submarine.CheckVisibility(refEntity.SimPosition, refEntity.SimPosition + ConvertUnits.ToSimUnits(diff)) == null)
|
||||
{
|
||||
visibleCharacters.Add(c);
|
||||
}
|
||||
@@ -180,21 +185,38 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (SeeThroughWalls)
|
||||
if (ThermalGoggles)
|
||||
{
|
||||
spriteBatch.End();
|
||||
GameMain.LightManager.SolidColorEffect.Parameters["color"].SetValue(Color.Red.ToVector4() * (0.35f + (float)Math.Sin(Timing.TotalTime * 1.6f) * 0.05f));
|
||||
GameMain.LightManager.SolidColorEffect.Parameters["color"].SetValue(Color.Red.ToVector4() * (0.3f + MathF.Sin(thermalEffectState) * 0.05f));
|
||||
GameMain.LightManager.SolidColorEffect.CurrentTechnique = GameMain.LightManager.SolidColorEffect.Techniques["SolidColorBlur"];
|
||||
GameMain.LightManager.SolidColorEffect.Parameters["blurDistance"].SetValue(0.03f + (float)Math.Sin(Timing.TotalTime) * 0.01f);
|
||||
GameMain.LightManager.SolidColorEffect.Parameters["blurDistance"].SetValue(0.01f + MathF.Sin(thermalEffectState) * 0.005f);
|
||||
GameMain.LightManager.SolidColorEffect.CurrentTechnique.Passes[0].Apply();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: Screen.Selected.Cam.Transform, effect: GameMain.LightManager.SolidColorEffect);
|
||||
|
||||
foreach (Character c in visibleCharacters)
|
||||
Entity refEntity = equipper;
|
||||
if (!isEquippable || refEntity == null)
|
||||
{
|
||||
if (c == character || !c.Enabled || c.Removed) { continue; }
|
||||
refEntity = item;
|
||||
}
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c == character || !c.Enabled || c.Removed || c.Params.HideInThermalGoggles) { continue; }
|
||||
if (!ShowDeadCharacters && c.IsDead) { continue; }
|
||||
|
||||
float dist = Vector2.DistanceSquared(refEntity.WorldPosition, c.WorldPosition);
|
||||
if (dist > Range * Range) { continue; }
|
||||
|
||||
Sprite pingCircle = GUI.Style.UIThermalGlow.Sprite;
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
limb.Draw(spriteBatch, Screen.Selected.Cam, disableDeformations: true);
|
||||
if (limb.Mass < 1.0f) { continue; }
|
||||
float noise1 = PerlinNoise.GetPerlin((thermalEffectState + limb.Params.ID + c.ID) * 0.01f, (thermalEffectState + limb.Params.ID + c.ID) * 0.02f);
|
||||
float noise2 = PerlinNoise.GetPerlin((thermalEffectState + limb.Params.ID + c.ID) * 0.01f, (thermalEffectState + limb.Params.ID + c.ID) * 0.008f);
|
||||
Vector2 spriteScale = ConvertUnits.ToDisplayUnits(limb.body.GetSize()) / pingCircle.size * (noise1 * 0.5f + 2f);
|
||||
Vector2 drawPos = new Vector2(limb.body.DrawPosition.X + (noise1 - 0.5f) * 100, -limb.body.DrawPosition.Y + (noise2 - 0.5f) * 100);
|
||||
pingCircle.Draw(spriteBatch, drawPos, 0.0f, scale: Math.Max(spriteScale.X, spriteScale.Y));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +317,7 @@ namespace Barotrauma
|
||||
string colorStr = XMLExtensions.ColorToString(!item.AllowStealing ? GUI.Style.Red : Color.White);
|
||||
|
||||
toolTip = $"‖color:{colorStr}‖{name}‖color:end‖";
|
||||
if (item.Quality > 0)
|
||||
if (item.GetComponent<Quality>() != null)
|
||||
{
|
||||
// substring by to get rid of the empty space at start, text file should be adjusted
|
||||
toolTip += $"\n{TextManager.GetWithVariable("itemname.quality" + item.Quality, "[itemname]", "", fallBackTag: "itemname.quality3")?.Substring(1)}";
|
||||
@@ -1567,7 +1567,7 @@ namespace Barotrauma
|
||||
if (containedItem != null && itemContainer.Inventory.Capacity == 1)
|
||||
{
|
||||
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(0));
|
||||
if (maxStackSize > 1)
|
||||
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
|
||||
{
|
||||
containedState = itemContainer.Inventory.slots[0].ItemCount / (float)maxStackSize;
|
||||
}
|
||||
|
||||
@@ -721,6 +721,7 @@ namespace Barotrauma
|
||||
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"), style: "GUIButtonSmall")
|
||||
{
|
||||
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
|
||||
Enabled = Prefab.CanFlipX,
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
foreach (MapEntity me in SelectedList)
|
||||
@@ -734,6 +735,7 @@ namespace Barotrauma
|
||||
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityY"), style: "GUIButtonSmall")
|
||||
{
|
||||
ToolTip = TextManager.Get("MirrorEntityYToolTip"),
|
||||
Enabled = Prefab.CanFlipY,
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
foreach (MapEntity me in SelectedList)
|
||||
@@ -1212,8 +1214,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (Character.Controlled != null && Character.Controlled.SelectedConstruction != this && GetComponent<RemoteController>() == null)
|
||||
{
|
||||
if (Character.Controlled.SelectedConstruction?.GetComponent<RemoteController>()?.TargetItem != this &&
|
||||
{
|
||||
if (Character.Controlled.SelectedConstruction?.GetComponent<RemoteController>()?.TargetItem != this &&
|
||||
!Character.Controlled.HeldItems.Any(it => it.GetComponent<RemoteController>()?.TargetItem == this))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -126,8 +126,8 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
|
||||
LosTexture?.Dispose();
|
||||
LosTexture = new RenderTarget2D(graphics,
|
||||
(int)(GameMain.GraphicsWidth * GameMain.Config.LightMapScale),
|
||||
LosTexture = new RenderTarget2D(graphics,
|
||||
(int)(GameMain.GraphicsWidth * GameMain.Config.LightMapScale),
|
||||
(int)(GameMain.GraphicsHeight * GameMain.Config.LightMapScale), false, SurfaceFormat.Color, DepthFormat.None);
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace Barotrauma.Lights
|
||||
activeLights.Clear();
|
||||
foreach (LightSource light in lights)
|
||||
{
|
||||
if (!light.Enabled) { continue; }
|
||||
if (!light.Enabled) { continue; }
|
||||
if ((light.Color.A < 1 || light.Range < 1.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; }
|
||||
if (light.ParentBody != null)
|
||||
{
|
||||
@@ -197,7 +197,9 @@ namespace Barotrauma.Lights
|
||||
float spriteRange = Math.Max(
|
||||
light.LightSprite.size.X * light.SpriteScale.X * (0.5f + Math.Abs(light.LightSprite.RelativeOrigin.X - 0.5f)),
|
||||
light.LightSprite.size.Y * light.SpriteScale.Y * (0.5f + Math.Abs(light.LightSprite.RelativeOrigin.Y - 0.5f)));
|
||||
range = Math.Max(spriteRange, range);
|
||||
|
||||
float targetSize = Math.Max(light.LightTextureTargetSize.X, light.LightTextureTargetSize.Y);
|
||||
range = Math.Max(Math.Max(spriteRange, targetSize), range);
|
||||
}
|
||||
if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, range, viewRect)) { continue; }
|
||||
activeLights.Add(light);
|
||||
@@ -247,7 +249,7 @@ namespace Barotrauma.Lights
|
||||
}*/
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, transformMatrix: spriteBatchTransform);
|
||||
Dictionary<Hull, Rectangle> visibleHulls = GetVisibleHulls(cam);
|
||||
Dictionary<Hull, Rectangle> visibleHulls = GetVisibleHulls(cam);
|
||||
foreach (KeyValuePair<Hull, Rectangle> hull in visibleHulls)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
@@ -264,12 +266,12 @@ namespace Barotrauma.Lights
|
||||
spriteBatch.End();
|
||||
|
||||
graphics.BlendState = BlendState.Additive;
|
||||
|
||||
|
||||
|
||||
//draw the focused item and character to highlight them,
|
||||
//and light sprites (done before drawing the actual light volumes so we can make characters obstruct the highlights and sprites)
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform);
|
||||
foreach (LightSource light in activeLights)
|
||||
{
|
||||
//don't draw limb lights at this point, they need to be drawn after lights have been obstructed by characters
|
||||
@@ -294,8 +296,8 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
|
||||
if (Character.Controlled?.FocusedCharacter == character) { continue; }
|
||||
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
|
||||
Color.Black :
|
||||
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
|
||||
Color.Black :
|
||||
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
@@ -304,7 +306,7 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
|
||||
DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidVertexColor"];
|
||||
DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
|
||||
@@ -312,8 +314,8 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
|
||||
if (Character.Controlled?.FocusedCharacter == character) { continue; }
|
||||
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
|
||||
Color.Black :
|
||||
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
|
||||
Color.Black :
|
||||
character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque();
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
@@ -343,9 +345,9 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
|
||||
lightEffect.World = transform;
|
||||
|
||||
|
||||
GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive);
|
||||
|
||||
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
DrawHalo(Character.Controlled);
|
||||
@@ -412,7 +414,7 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
if (highlightedEntities.Count == 0) { return false; }
|
||||
|
||||
|
||||
//draw characters in light blue first
|
||||
graphics.SetRenderTarget(HighlightMap);
|
||||
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"];
|
||||
@@ -484,9 +486,9 @@ namespace Barotrauma.Lights
|
||||
|
||||
//raster pattern on top of everything
|
||||
spriteBatch.Begin(blendState: BlendState.NonPremultiplied, samplerState: SamplerState.LinearWrap);
|
||||
spriteBatch.Draw(highlightRaster,
|
||||
new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height),
|
||||
new Rectangle(0, 0, (int)(HighlightMap.Width / currLightMapScale * 0.5f), (int)(HighlightMap.Height / currLightMapScale * 0.5f)),
|
||||
spriteBatch.Draw(highlightRaster,
|
||||
new Rectangle(0, 0, HighlightMap.Width, HighlightMap.Height),
|
||||
new Rectangle(0, 0, (int)(HighlightMap.Width / currLightMapScale * 0.5f), (int)(HighlightMap.Height / currLightMapScale * 0.5f)),
|
||||
Color.White * 0.5f);
|
||||
spriteBatch.End();
|
||||
|
||||
@@ -542,7 +544,7 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
graphics.Clear(Color.White);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
@@ -595,9 +597,9 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
}
|
||||
graphics.SetRenderTarget(null);
|
||||
graphics.SetRenderTarget(null);
|
||||
}
|
||||
|
||||
|
||||
public void ClearLights()
|
||||
{
|
||||
lights.Clear();
|
||||
|
||||
@@ -104,13 +104,13 @@ namespace Barotrauma.Lights
|
||||
blinkFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public float TextureRange
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
public Sprite OverrideLightTexture
|
||||
{
|
||||
get;
|
||||
@@ -137,7 +137,7 @@ namespace Barotrauma.Lights
|
||||
public LightSourceParams(XElement element)
|
||||
{
|
||||
Deserialize(element);
|
||||
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -206,9 +206,9 @@ namespace Barotrauma.Lights
|
||||
private short[] indices;
|
||||
|
||||
private List<ConvexHullList> hullsInRange;
|
||||
|
||||
|
||||
public Texture2D texture;
|
||||
|
||||
|
||||
public SpriteEffects LightSpriteEffect;
|
||||
|
||||
public Submarine ParentSub;
|
||||
@@ -224,7 +224,7 @@ namespace Barotrauma.Lights
|
||||
private float prevCalculatedRange;
|
||||
private Vector2 prevCalculatedPosition;
|
||||
|
||||
//do we need to recheck which convex hulls are within range
|
||||
//do we need to recheck which convex hulls are within range
|
||||
//(e.g. position or range of the lightsource has changed)
|
||||
public bool NeedsHullCheck = true;
|
||||
//do we need to recalculate the vertices of the light volume
|
||||
@@ -278,7 +278,7 @@ namespace Barotrauma.Lights
|
||||
translateVertices = position - prevCalculatedPosition;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NeedsHullCheck = true;
|
||||
NeedsRecalculation = true;
|
||||
}
|
||||
@@ -360,7 +360,7 @@ namespace Barotrauma.Lights
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
public float Range
|
||||
{
|
||||
get { return lightSourceParams.Range; }
|
||||
@@ -369,13 +369,29 @@ namespace Barotrauma.Lights
|
||||
|
||||
lightSourceParams.Range = value;
|
||||
if (Math.Abs(prevCalculatedRange - lightSourceParams.Range) < 10.0f) return;
|
||||
|
||||
|
||||
NeedsHullCheck = true;
|
||||
NeedsRecalculation = true;
|
||||
prevCalculatedRange = lightSourceParams.Range;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 lightTextureTargetSize;
|
||||
|
||||
public Vector2 LightTextureTargetSize
|
||||
{
|
||||
get => lightTextureTargetSize;
|
||||
set
|
||||
{
|
||||
NeedsRecalculation = true;
|
||||
NeedsHullCheck = true;
|
||||
lightTextureTargetSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 LightTextureOffset { get; set; }
|
||||
public Vector2 LightTextureScale { get; set; } = Vector2.One;
|
||||
|
||||
public float TextureRange
|
||||
{
|
||||
get
|
||||
@@ -386,7 +402,7 @@ namespace Barotrauma.Lights
|
||||
|
||||
/// <summary>
|
||||
/// Background lights are drawn behind submarines and they don't cast shadows.
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
public bool IsBackground
|
||||
{
|
||||
get;
|
||||
@@ -462,7 +478,7 @@ namespace Barotrauma.Lights
|
||||
this.ParentSub = submarine;
|
||||
this.position = position;
|
||||
lightSourceParams = new LightSourceParams(range, color);
|
||||
CastShadows = true;
|
||||
CastShadows = true;
|
||||
texture = LightTexture;
|
||||
diffToSub = new Dictionary<Submarine, Vector2>();
|
||||
if (addLight) { GameMain.LightManager.AddLight(this); }
|
||||
@@ -494,7 +510,7 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
CurrentBrightness = brightness;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Update the contents of ConvexHullList and check if we need to recalculate vertices
|
||||
/// </summary>
|
||||
@@ -509,7 +525,7 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recheck which convex hulls are in range (if needed),
|
||||
/// Recheck which convex hulls are in range (if needed),
|
||||
/// and check if we need to recalculate vertices due to changes in the convex hulls
|
||||
/// </summary>
|
||||
private void CheckHullsInRange()
|
||||
@@ -561,20 +577,20 @@ namespace Barotrauma.Lights
|
||||
chList.List.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
RefreshConvexHullList(chList, lightPos, sub);
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
//light is inside, convexhull outside
|
||||
if (sub == null) continue;
|
||||
|
||||
|
||||
//light and convexhull are both inside the same sub
|
||||
if (sub == ParentSub)
|
||||
{
|
||||
if (NeedsHullCheck)
|
||||
{
|
||||
{
|
||||
RefreshConvexHullList(chList, lightPos, sub);
|
||||
}
|
||||
}
|
||||
@@ -582,7 +598,7 @@ namespace Barotrauma.Lights
|
||||
else
|
||||
{
|
||||
if (sub.DockedTo.Contains(ParentSub) && !NeedsHullCheck) continue;
|
||||
|
||||
|
||||
lightPos -= (sub.Position - ParentSub.Position);
|
||||
|
||||
Rectangle subBorders = sub.Borders;
|
||||
@@ -642,7 +658,7 @@ namespace Barotrauma.Lights
|
||||
foreach (ConvexHull hull in hulls)
|
||||
{
|
||||
hull.RefreshWorldPositions();
|
||||
hull.GetVisibleSegments(drawPos, visibleSegments, ignoreEdges: false);
|
||||
hull.GetVisibleSegments(drawPos, visibleSegments, ignoreEdges: false);
|
||||
}
|
||||
|
||||
//add a square-shaped boundary to make sure we've got something to construct the triangles from
|
||||
@@ -829,13 +845,13 @@ namespace Barotrauma.Lights
|
||||
if (intersection2.index < 0) return null;
|
||||
Segment seg1 = visibleSegments[intersection1.index];
|
||||
Segment seg2 = visibleSegments[intersection2.index];
|
||||
|
||||
|
||||
bool isPoint1 = MathUtils.LineToPointDistanceSquared(seg1.Start.WorldPos, seg1.End.WorldPos, p.WorldPos) < 25.0f;
|
||||
bool isPoint2 = MathUtils.LineToPointDistanceSquared(seg2.Start.WorldPos, seg2.End.WorldPos, p.WorldPos) < 25.0f;
|
||||
|
||||
if (isPoint1 && isPoint2)
|
||||
{
|
||||
//hit at the current segmentpoint -> place the segmentpoint into the list
|
||||
//hit at the current segmentpoint -> place the segmentpoint into the list
|
||||
output.Add(p.WorldPos);
|
||||
|
||||
foreach (ConvexHullList hullList in hullsInRange)
|
||||
@@ -938,7 +954,7 @@ namespace Barotrauma.Lights
|
||||
segment = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (segment, closestIntersection == null ? rayEnd : (Vector2)closestIntersection);
|
||||
}
|
||||
|
||||
@@ -968,8 +984,8 @@ namespace Barotrauma.Lights
|
||||
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
|
||||
|
||||
Vector2 origin = OverrideLightTextureOrigin;
|
||||
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
|
||||
{
|
||||
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
|
||||
{
|
||||
origin.X = OverrideLightTexture.SourceRect.Width - origin.X;
|
||||
cosAngle = -cosAngle;
|
||||
sinAngle = -sinAngle;
|
||||
@@ -981,7 +997,7 @@ namespace Barotrauma.Lights
|
||||
// Add a vertex for the center of the mesh
|
||||
vertices[0] = new VertexPositionColorTexture(new Vector3(position.X, position.Y, 0),
|
||||
Color.White, GetUV(new Vector2(0.5f, 0.5f) + uvOffset, LightSpriteEffect));
|
||||
|
||||
|
||||
//hacky fix to exc excessively large light volumes (they used to be up to 4x the range of the light if there was nothing to block the rays).
|
||||
//might want to tweak the raycast logic in a way that this isn't necessary
|
||||
/*float boundRadius = Range * 1.1f / (1.0f - Math.Max(Math.Abs(uvOffset.X), Math.Abs(uvOffset.Y)));
|
||||
@@ -999,7 +1015,7 @@ namespace Barotrauma.Lights
|
||||
for (int i = 0; i < rayCastHits.Count; i++)
|
||||
{
|
||||
Vector2 vertex = rayCastHits[i];
|
||||
|
||||
|
||||
//we'll use the previous and next vertices to calculate the normals
|
||||
//of the two segments this vertex belongs to
|
||||
//so we can add new vertices based on these normals
|
||||
@@ -1007,7 +1023,7 @@ namespace Barotrauma.Lights
|
||||
Vector2 nextVertex = rayCastHits[i < rayCastHits.Count - 1 ? i + 1 : 0];
|
||||
|
||||
Vector2 rawDiff = vertex - drawPos;
|
||||
|
||||
|
||||
//calculate normal of first segment
|
||||
Vector2 nDiff1 = vertex - nextVertex;
|
||||
float tx = nDiff1.X; nDiff1.X = -nDiff1.Y; nDiff1.Y = tx;
|
||||
@@ -1015,7 +1031,7 @@ namespace Barotrauma.Lights
|
||||
//if the normal is pointing towards the light origin
|
||||
//rather than away from it, invert it
|
||||
if (Vector2.DistanceSquared(nDiff1, rawDiff) > Vector2.DistanceSquared(-nDiff1, rawDiff)) nDiff1 = -nDiff1;
|
||||
|
||||
|
||||
//calculate normal of second segment
|
||||
Vector2 nDiff2 = prevVertex - vertex;
|
||||
tx = nDiff2.X; nDiff2.X = -nDiff2.Y; nDiff2.Y = tx;
|
||||
@@ -1112,13 +1128,13 @@ namespace Barotrauma.Lights
|
||||
|
||||
static Vector2 GetUV(Vector2 vert, SpriteEffects effects)
|
||||
{
|
||||
if (effects == SpriteEffects.FlipHorizontally)
|
||||
{
|
||||
vert.X = 1.0f - vert.X;
|
||||
if (effects == SpriteEffects.FlipHorizontally)
|
||||
{
|
||||
vert.X = 1.0f - vert.X;
|
||||
}
|
||||
else if (effects == SpriteEffects.FlipVertically)
|
||||
{
|
||||
vert.Y = 1.0f - vert.Y;
|
||||
else if (effects == SpriteEffects.FlipVertically)
|
||||
{
|
||||
vert.Y = 1.0f - vert.Y;
|
||||
}
|
||||
else if (effects == (SpriteEffects.FlipHorizontally | SpriteEffects.FlipVertically))
|
||||
{
|
||||
@@ -1228,10 +1244,19 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
drawPos.Y = -drawPos.Y;
|
||||
|
||||
LightSprite.Draw(
|
||||
spriteBatch, drawPos,
|
||||
new Color(Color, (lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f) * CurrentBrightness),
|
||||
origin, -Rotation + MathHelper.ToRadians(LightSourceParams.Rotation), SpriteScale, LightSpriteEffect);
|
||||
Color color = new Color(Color, (lightSourceParams.OverrideLightSpriteAlpha ?? Color.A / 255.0f) * CurrentBrightness);
|
||||
|
||||
if (LightTextureTargetSize != Vector2.Zero)
|
||||
{
|
||||
LightSprite.DrawTiled(spriteBatch, drawPos, LightTextureTargetSize, color, startOffset: LightTextureOffset, textureScale: LightTextureScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
LightSprite.Draw(
|
||||
spriteBatch, drawPos,
|
||||
color,
|
||||
origin, -Rotation + MathHelper.ToRadians(LightSourceParams.Rotation), SpriteScale, LightSpriteEffect);
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
|
||||
@@ -1255,7 +1280,7 @@ namespace Barotrauma.Lights
|
||||
GUI.DrawLine(spriteBatch, drawPos - Vector2.One * Range, drawPos + Vector2.One * Range, Color);
|
||||
GUI.DrawLine(spriteBatch, drawPos - new Vector2(1.0f, -1.0f) * Range, drawPos + new Vector2(1.0f, -1.0f) * Range, Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckConditionals()
|
||||
@@ -1280,10 +1305,10 @@ namespace Barotrauma.Lights
|
||||
if (!CastShadows)
|
||||
{
|
||||
Texture2D currentTexture = texture ?? LightTexture;
|
||||
if (OverrideLightTexture != null) { currentTexture = OverrideLightTexture.Texture; }
|
||||
if (OverrideLightTexture != null) { currentTexture = OverrideLightTexture.Texture; }
|
||||
|
||||
Vector2 center = OverrideLightTexture == null ?
|
||||
new Vector2(currentTexture.Width / 2, currentTexture.Height / 2) :
|
||||
Vector2 center = OverrideLightTexture == null ?
|
||||
new Vector2(currentTexture.Width / 2, currentTexture.Height / 2) :
|
||||
OverrideLightTexture.Origin;
|
||||
float scale = Range / (currentTexture.Width / 2.0f);
|
||||
|
||||
@@ -1317,8 +1342,8 @@ namespace Barotrauma.Lights
|
||||
|
||||
Vector2 offset = ParentSub == null ? Vector2.Zero : ParentSub.DrawPosition;
|
||||
lightEffect.World =
|
||||
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
|
||||
Matrix.CreateRotationZ(rotateVertices) *
|
||||
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
|
||||
Matrix.CreateRotationZ(rotateVertices - MathHelper.ToRadians(LightSourceParams.Rotation)) *
|
||||
Matrix.CreateTranslation(new Vector3(position + offset + translateVertices, 0.0f)) *
|
||||
transform;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Lights;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -17,7 +18,7 @@ namespace Barotrauma
|
||||
private static Vector2 startMovingPos = Vector2.Zero;
|
||||
|
||||
private static float keyDelay;
|
||||
|
||||
|
||||
public static Vector2 StartMovingPos => startMovingPos;
|
||||
|
||||
public event Action<Rectangle> Resized;
|
||||
@@ -97,13 +98,13 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public float GetDrawDepth(float baseDepth, Sprite sprite)
|
||||
{
|
||||
float depth = baseDepth
|
||||
float depth = baseDepth
|
||||
//take texture into account to get entities with (roughly) the same base depth and texture to render consecutively to minimize texture swaps
|
||||
+ (sprite?.Texture?.SortingKey ?? 0) % 100 * 0.00001f
|
||||
+ ID % 100 * 0.000001f;
|
||||
return Math.Min(depth, 1.0f);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Update the selection logic in submarine editor
|
||||
/// </summary>
|
||||
@@ -218,7 +219,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
MapEntity highLightedEntity = null;
|
||||
@@ -284,13 +285,13 @@ namespace Barotrauma
|
||||
//mouse released -> move the entities to the new position of the mouse
|
||||
|
||||
Vector2 moveAmount = position - startMovingPos;
|
||||
|
||||
|
||||
if (!isShiftDown)
|
||||
{
|
||||
moveAmount.X = (float)(moveAmount.X > 0.0f ? Math.Floor(moveAmount.X / Submarine.GridSize.X) : Math.Ceiling(moveAmount.X / Submarine.GridSize.X)) * Submarine.GridSize.X;
|
||||
moveAmount.Y = (float)(moveAmount.Y > 0.0f ? Math.Floor(moveAmount.Y / Submarine.GridSize.Y) : Math.Ceiling(moveAmount.Y / Submarine.GridSize.Y)) * Submarine.GridSize.Y;
|
||||
}
|
||||
|
||||
|
||||
if (Math.Abs(moveAmount.X) >= Submarine.GridSize.X || Math.Abs(moveAmount.Y) >= Submarine.GridSize.Y || isShiftDown)
|
||||
{
|
||||
if (!isShiftDown) { moveAmount = Submarine.VectorToWorldGrid(moveAmount); }
|
||||
@@ -321,10 +322,10 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
SoundPlayer.PlayUISound(GUISoundType.PickItemFail);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity>(SelectedList),SelectedList.Select(entity => entity.Rect).ToList(), oldRects, false));
|
||||
if (deposited.Any() && deposited.Any(entity => entity is Item))
|
||||
{
|
||||
@@ -457,8 +458,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (PlayerInput.PrimaryMouseButtonHeld() &&
|
||||
PlayerInput.KeyUp(Keys.Space) &&
|
||||
PlayerInput.KeyUp(Keys.LeftAlt) &&
|
||||
PlayerInput.KeyUp(Keys.RightAlt) &&
|
||||
PlayerInput.KeyUp(Keys.LeftAlt) &&
|
||||
PlayerInput.KeyUp(Keys.RightAlt) &&
|
||||
(highlightedListBox == null || (GUI.MouseOn != highlightedListBox && !highlightedListBox.IsParentOf(GUI.MouseOn))))
|
||||
{
|
||||
//if clicking a selected entity, start moving it
|
||||
@@ -486,7 +487,7 @@ namespace Barotrauma
|
||||
|
||||
int xKeysDown = (left + right);
|
||||
int yKeysDown = (up + down);
|
||||
|
||||
|
||||
if (xKeysDown != 0 || yKeysDown != 0) { keyDelay += (float) Timing.Step; } else { keyDelay = 0; }
|
||||
|
||||
|
||||
@@ -516,7 +517,7 @@ namespace Barotrauma
|
||||
bool isShiftDown = PlayerInput.IsShiftDown();
|
||||
|
||||
if (!isShiftDown) return null;
|
||||
|
||||
|
||||
foreach (MapEntity e in mapEntityList)
|
||||
{
|
||||
if (!e.SelectableInEditor ||!(e is Item potentialContainer)) { continue; }
|
||||
@@ -666,7 +667,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (SelectedList.Contains(entity)) { return; }
|
||||
SelectedList.Add(entity);
|
||||
HandleDoorGapLinks(entity,
|
||||
HandleDoorGapLinks(entity,
|
||||
onGapFound: (door, gap) =>
|
||||
{
|
||||
door.RefreshLinkedGap();
|
||||
@@ -674,8 +675,8 @@ namespace Barotrauma
|
||||
{
|
||||
SelectedList.Add(gap);
|
||||
}
|
||||
},
|
||||
onDoorFound: (door, gap) =>
|
||||
},
|
||||
onDoorFound: (door, gap) =>
|
||||
{
|
||||
if (!SelectedList.Contains(door.Item))
|
||||
{
|
||||
@@ -719,7 +720,7 @@ namespace Barotrauma
|
||||
onGapFound: (door, gap) => SelectedList.Remove(gap),
|
||||
onDoorFound: (door, gap) => SelectedList.Remove(door.Item));
|
||||
}
|
||||
|
||||
|
||||
static partial void UpdateAllProjSpecific(float deltaTime)
|
||||
{
|
||||
var entitiesToRender = Submarine.VisibleEntities ?? mapEntityList;
|
||||
@@ -752,7 +753,7 @@ namespace Barotrauma
|
||||
moveAmount.Y = -moveAmount.Y;
|
||||
|
||||
bool isShiftDown = PlayerInput.IsShiftDown();
|
||||
|
||||
|
||||
if (!isShiftDown)
|
||||
{
|
||||
moveAmount.X = (float)(moveAmount.X > 0.0f ? Math.Floor(moveAmount.X / Submarine.GridSize.X) : Math.Ceiling(moveAmount.X / Submarine.GridSize.X)) * Submarine.GridSize.X;
|
||||
@@ -765,21 +766,21 @@ namespace Barotrauma
|
||||
foreach (MapEntity e in SelectedList)
|
||||
{
|
||||
SpriteEffects spriteEffects = SpriteEffects.None;
|
||||
switch (e)
|
||||
switch (e)
|
||||
{
|
||||
case Item item:
|
||||
case Item item:
|
||||
{
|
||||
if (item.FlippedX && item.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
|
||||
if (item.flippedY && item.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
|
||||
break;
|
||||
}
|
||||
case Structure structure:
|
||||
case Structure structure:
|
||||
{
|
||||
if (structure.FlippedX && structure.Prefab.CanSpriteFlipX) spriteEffects ^= SpriteEffects.FlipHorizontally;
|
||||
if (structure.flippedY && structure.Prefab.CanSpriteFlipY) spriteEffects ^= SpriteEffects.FlipVertically;
|
||||
break;
|
||||
}
|
||||
case WayPoint wayPoint:
|
||||
case WayPoint wayPoint:
|
||||
{
|
||||
Vector2 drawPos = e.WorldPosition;
|
||||
drawPos.Y = -drawPos.Y;
|
||||
@@ -816,7 +817,7 @@ namespace Barotrauma
|
||||
|
||||
posY = -posY;
|
||||
|
||||
Vector2[] corners =
|
||||
Vector2[] corners =
|
||||
{
|
||||
new Vector2(posX, posY),
|
||||
new Vector2(posX + sizeX, posY),
|
||||
@@ -882,7 +883,7 @@ namespace Barotrauma
|
||||
{
|
||||
MapEntity firstSelected = SelectedList.First();
|
||||
|
||||
float minX = firstSelected.WorldRect.X,
|
||||
float minX = firstSelected.WorldRect.X,
|
||||
maxX = firstSelected.WorldRect.Right;
|
||||
|
||||
foreach (MapEntity entity in SelectedList)
|
||||
@@ -907,7 +908,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (MapEntity entity in SelectedList)
|
||||
{
|
||||
|
||||
|
||||
minY = Math.Min(minY, entity.WorldRect.Y - entity.WorldRect.Height);
|
||||
maxY = Math.Max(maxY, entity.WorldRect.Y);
|
||||
}
|
||||
@@ -947,21 +948,21 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the selected entities to the "clipboard" (copiedList)
|
||||
/// Copy the selected entities to the "clipboard" (copiedList)
|
||||
/// </summary>
|
||||
public static void Copy(List<MapEntity> entities)
|
||||
{
|
||||
if (entities.Count == 0) { return; }
|
||||
CopyEntities(entities);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Copy the entities to the "clipboard" (copiedList) and delete them
|
||||
/// </summary>
|
||||
public static void Cut(List<MapEntity> entities)
|
||||
{
|
||||
if (entities.Count == 0) { return; }
|
||||
|
||||
|
||||
CopyEntities(entities);
|
||||
|
||||
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(entities), true));
|
||||
@@ -1057,7 +1058,7 @@ namespace Barotrauma
|
||||
|
||||
editingHUD.RectTransform.Resize(
|
||||
new Point(
|
||||
editingHUD.RectTransform.NonScaledSize.X,
|
||||
editingHUD.RectTransform.NonScaledSize.X,
|
||||
MathHelper.Clamp(contentHeight + padding * 2, 50, maxHeight)), resizeChildren: false);
|
||||
listBox.RectTransform.Resize(new Point(listBox.RectTransform.NonScaledSize.X, editingHUD.RectTransform.NonScaledSize.Y - padding * 2), resizeChildren: false);
|
||||
}
|
||||
@@ -1097,7 +1098,7 @@ namespace Barotrauma
|
||||
{
|
||||
prevRect = new Rectangle(Rect.Location, Rect.Size);
|
||||
}
|
||||
|
||||
|
||||
Vector2 placePosition = new Vector2(rect.X, rect.Y);
|
||||
Vector2 placeSize = new Vector2(rect.Width, rect.Height);
|
||||
|
||||
@@ -1148,6 +1149,15 @@ namespace Barotrauma
|
||||
var oldData = new List<Rectangle> { prevRect.Value };
|
||||
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity> { this }, newData, oldData, true));
|
||||
}
|
||||
|
||||
if (this is Structure structure)
|
||||
{
|
||||
foreach (LightSource light in structure.Lights)
|
||||
{
|
||||
light.LightTextureTargetSize = Rect.Size.ToVector2();
|
||||
light.Position = rect.Location.ToVector2();
|
||||
}
|
||||
}
|
||||
prevRect = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,14 @@ namespace Barotrauma
|
||||
{
|
||||
partial class Structure : MapEntity, IDamageable, IServerSerializable
|
||||
{
|
||||
public static bool ShowWalls = true, ShowStructures = true;
|
||||
public static bool ShowWalls = true, ShowStructures = true;
|
||||
|
||||
private List<ConvexHull> convexHulls;
|
||||
|
||||
private readonly Dictionary<DecorativeSprite, DecorativeSprite.State> spriteAnimState = new Dictionary<DecorativeSprite, DecorativeSprite.State>();
|
||||
|
||||
public readonly List<LightSource> Lights = new List<LightSource>();
|
||||
|
||||
public override bool SelectableInEditor
|
||||
{
|
||||
get
|
||||
@@ -41,7 +43,7 @@ namespace Barotrauma
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
@@ -88,7 +90,23 @@ namespace Barotrauma
|
||||
if (editingHUD == null || editingHUD.UserData as Structure != this)
|
||||
{
|
||||
editingHUD = CreateEditingHUD(Screen.Selected != GameMain.SubEditorScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLightTextureOffset()
|
||||
{
|
||||
Vector2 textOffset = textureOffset;
|
||||
if (FlippedX) { textOffset.X = -textOffset.X; }
|
||||
if (FlippedY) { textOffset.Y = -textOffset.Y; }
|
||||
|
||||
foreach (LightSource light in Lights)
|
||||
{
|
||||
Vector2 bgOffset = new Vector2(
|
||||
MathUtils.PositiveModulo((int)-textOffset.X, light.texture.Width),
|
||||
MathUtils.PositiveModulo((int)-textOffset.Y, light.texture.Height));
|
||||
|
||||
light.LightTextureOffset = bgOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public GUIComponent CreateEditingHUD(bool inGame = false)
|
||||
@@ -175,12 +193,12 @@ namespace Barotrauma
|
||||
buttonContainer.RectTransform.IsFixedSize = true;
|
||||
GUITextBlock.AutoScaleAndNormalize(buttonContainer.Children.Where(c => c is GUIButton).Select(b => ((GUIButton)b).TextBlock));
|
||||
editor.AddCustomContent(buttonContainer, editor.ContentCount);
|
||||
|
||||
|
||||
PositionEditingHUD();
|
||||
|
||||
return editingHUD;
|
||||
}
|
||||
|
||||
|
||||
partial void OnImpactProjSpecific(Fixture f1, Fixture f2, Contact contact)
|
||||
{
|
||||
if (!Prefab.Platform && Prefab.StairDirection == Direction.None)
|
||||
@@ -261,19 +279,21 @@ namespace Barotrauma
|
||||
else if (HiddenInGame) { return; }
|
||||
|
||||
Color color = IsIncludedInSelection && editing ? GUI.Style.Blue : IsHighlighted ? GUI.Style.Orange * Math.Max(spriteColor.A / (float) byte.MaxValue, 0.1f) : spriteColor;
|
||||
|
||||
|
||||
if (IsSelected && editing)
|
||||
{
|
||||
//color = Color.Lerp(color, Color.Gold, 0.5f);
|
||||
color = spriteColor;
|
||||
|
||||
|
||||
|
||||
Vector2 rectSize = rect.Size.ToVector2();
|
||||
if (BodyWidth > 0.0f) { rectSize.X = BodyWidth; }
|
||||
if (BodyHeight > 0.0f) { rectSize.Y = BodyHeight; }
|
||||
|
||||
Vector2 bodyPos = WorldPosition + BodyOffset;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(bodyPos.X, -bodyPos.Y), rectSize.X, rectSize.Y, BodyRotation, Color.White,
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(bodyPos.X, -bodyPos.Y), rectSize.X, rectSize.Y, BodyRotation, Color.White,
|
||||
thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
|
||||
}
|
||||
|
||||
@@ -305,14 +325,14 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
dropShadowOffset = IsHorizontal ?
|
||||
new Vector2(0.0f, Math.Sign(Submarine.HiddenSubPosition.Y - Position.Y) * 10.0f) :
|
||||
dropShadowOffset = IsHorizontal ?
|
||||
new Vector2(0.0f, Math.Sign(Submarine.HiddenSubPosition.Y - Position.Y) * 10.0f) :
|
||||
new Vector2(Math.Sign(Submarine.HiddenSubPosition.X - Position.X) * 10.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
dropShadowOffset.Y = -dropShadowOffset.Y;
|
||||
}
|
||||
|
||||
|
||||
SpriteEffects oldEffects = Prefab.BackgroundSprite.effects;
|
||||
Prefab.BackgroundSprite.effects ^= SpriteEffects;
|
||||
|
||||
@@ -372,13 +392,13 @@ namespace Barotrauma
|
||||
if (!HasDamage && i == 0)
|
||||
{
|
||||
drawSection = new Rectangle(
|
||||
drawSection.X,
|
||||
drawSection.Y,
|
||||
drawSection.X,
|
||||
drawSection.Y,
|
||||
Sections[Sections.Length -1 ].rect.Right - drawSection.X,
|
||||
drawSection.Y - (Sections[Sections.Length - 1].rect.Y - Sections[Sections.Length - 1].rect.Height));
|
||||
i = Sections.Length;
|
||||
}
|
||||
|
||||
|
||||
Vector2 sectionOffset = new Vector2(
|
||||
Math.Abs(rect.Location.X - drawSection.Location.X),
|
||||
Math.Abs(rect.Location.Y - drawSection.Location.Y));
|
||||
@@ -496,7 +516,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!conditional.Matches(this)) { return false; }
|
||||
if (!conditional.Matches(this)) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -288,7 +288,7 @@ namespace Barotrauma.Networking
|
||||
heartbeatTimer = 5.0;
|
||||
|
||||
#if DEBUG
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
if (GameMain.Client == null) { return; }
|
||||
if (Rand.Range(0.0f, 1.0f) < GameMain.Client.SimulatedLoss && sendType != Steamworks.P2PSend.Reliable) { return; }
|
||||
|
||||
@@ -1538,7 +1538,7 @@ namespace Barotrauma
|
||||
var existingFiles = ContentPackage.GetFilesOfType(GameMain.VanillaContent.ToEnumerable(), contentType);
|
||||
if (contentType == ContentType.OutpostModule)
|
||||
{
|
||||
existingFiles = existingFiles.Where(f => f.Path.Contains("Ruin") == Submarine.MainSub.Info.OutpostModuleInfo.ModuleFlags.Contains("ruin"));
|
||||
existingFiles = existingFiles.Where(f => f.Path.Contains("Ruin") == Submarine.MainSub.Info.OutpostModuleInfo.ModuleFlags.Contains("ruin"));
|
||||
}
|
||||
#else
|
||||
var existingFiles = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages.Where(c => c != GameMain.VanillaContent), contentType);
|
||||
@@ -3272,7 +3272,7 @@ namespace Barotrauma
|
||||
oldProperties[color].Add(sEntity);
|
||||
}
|
||||
|
||||
List<ISerializableEntity> affected = entities.Select(t => t.Entity).Where(se => se is MapEntity { Removed: false }).ToList();
|
||||
List<ISerializableEntity> affected = entities.Select(t => t.Entity).Where(se => se is MapEntity { Removed: false } || se is ItemComponent).ToList();
|
||||
StoreCommand(new PropertyCommand(affected, property.Name, newColor, oldProperties));
|
||||
|
||||
if (MapEntity.EditingHUD != null && (MapEntity.EditingHUD.UserData == entity || (!(entity is ItemComponent ic) || MapEntity.EditingHUD.UserData == ic.Item)))
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.1500.5.0</Version>
|
||||
<Version>0.1500.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.1500.5.0</Version>
|
||||
<Version>0.1500.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>0.1500.5.0</Version>
|
||||
<Version>0.1500.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.1500.5.0</Version>
|
||||
<Version>0.1500.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.1500.5.0</Version>
|
||||
<Version>0.1500.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -1717,16 +1717,84 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Client c in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (c.Character != revivedCharacter) continue;
|
||||
if (c.Character != revivedCharacter) { continue; }
|
||||
|
||||
//clients stop controlling the character when it dies, force control back
|
||||
GameMain.Server.SetClientCharacter(c, revivedCharacter);
|
||||
//clients stop controlling the character when it dies, force control back
|
||||
GameMain.Server.SetClientCharacter(c, revivedCharacter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
AssignOnClientRequestExecute(
|
||||
"givetalent",
|
||||
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||
{
|
||||
Character targetCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args, false);
|
||||
if (targetCharacter == null) { return; }
|
||||
|
||||
TalentPrefab talentPrefab = TalentPrefab.TalentPrefabs.Find(c =>
|
||||
c.Identifier.Equals(args[0], StringComparison.OrdinalIgnoreCase) ||
|
||||
c.DisplayName.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
if (talentPrefab == null)
|
||||
{
|
||||
GameMain.Server.SendConsoleMessage("Couldn't find the talent \"" + args[0] + "\".", client);
|
||||
return;
|
||||
}
|
||||
targetCharacter.GiveTalent(talentPrefab);
|
||||
NewMessage($"Talent \"{talentPrefab.DisplayName}\" given to \"{targetCharacter.Name}\" by \"{client.Name}\".");
|
||||
GameMain.Server.SendConsoleMessage($"Gave talent \"{talentPrefab.DisplayName}\" to \"{targetCharacter.Name}\".", client);
|
||||
}
|
||||
);
|
||||
|
||||
AssignOnClientRequestExecute(
|
||||
"unlocktalents",
|
||||
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||
{
|
||||
var targetCharacter = args.Length >= 2 ? FindMatchingCharacter(args.Skip(1).ToArray()) : Character.Controlled;
|
||||
if (targetCharacter == null) { return; }
|
||||
|
||||
List<TalentTree> talentTrees = new List<TalentTree>();
|
||||
if (args.Length == 0 || args[0].Equals("all", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
talentTrees.AddRange(TalentTree.JobTalentTrees.Values);
|
||||
}
|
||||
else
|
||||
{
|
||||
var job = JobPrefab.Prefabs.Find(jp => jp.Name != null && jp.Name.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
if (job == null)
|
||||
{
|
||||
GameMain.Server.SendConsoleMessage($"Failed to find the job \"{args[0]}\".", client);
|
||||
return;
|
||||
}
|
||||
if (!TalentTree.JobTalentTrees.TryGetValue(job.Identifier, out TalentTree talentTree))
|
||||
{
|
||||
GameMain.Server.SendConsoleMessage($"No talents configured for the job \"{args[0]}\".", client);
|
||||
return;
|
||||
}
|
||||
talentTrees.Add(talentTree);
|
||||
}
|
||||
|
||||
foreach (var talentTree in talentTrees)
|
||||
{
|
||||
foreach (var subTree in talentTree.TalentSubTrees)
|
||||
{
|
||||
foreach (var option in subTree.TalentOptionStages)
|
||||
{
|
||||
foreach (var talent in option.Talents)
|
||||
{
|
||||
targetCharacter.GiveTalent(talent);
|
||||
NewMessage($"Talent \"{talent.DisplayName}\" given to \"{targetCharacter.Name}\" by \"{client.Name}\".");
|
||||
GameMain.Server.SendConsoleMessage($"Gave talent \"{talent.DisplayName}\" to \"{targetCharacter.Name}\".", client);
|
||||
NewMessage($"Unlocked talent \"{talent.DisplayName}\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
AssignOnClientRequestExecute(
|
||||
"freeze",
|
||||
(Client client, Vector2 cursorWorldPos, string[] args) =>
|
||||
|
||||
+7
-6
@@ -200,7 +200,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveInventories()
|
||||
public void SavePlayers()
|
||||
{
|
||||
List<CharacterCampaignData> prevCharacterData = new List<CharacterCampaignData>(characterData);
|
||||
//client character has spawned this round -> remove old data (and replace with an up-to-date one if the client still has a character)
|
||||
@@ -220,12 +220,13 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (c.Character?.Info == null) { continue; }
|
||||
if (c.Character.IsDead && c.Character.CauseOfDeath?.Type != CauseOfDeathType.Disconnected)
|
||||
var characterInfo = c.Character?.Info ?? c.CharacterInfo;
|
||||
if (characterInfo == null) { continue; }
|
||||
if (characterInfo.CauseOfDeath?.Type != CauseOfDeathType.Disconnected)
|
||||
{
|
||||
continue;
|
||||
RespawnManager.ReduceCharacterSkills(characterInfo);
|
||||
}
|
||||
c.CharacterInfo = c.Character.Info;
|
||||
c.CharacterInfo = characterInfo;
|
||||
characterData.RemoveAll(cd => cd.MatchesClient(c));
|
||||
characterData.Add(new CharacterCampaignData(c));
|
||||
}
|
||||
@@ -313,7 +314,7 @@ namespace Barotrauma
|
||||
|
||||
if (success)
|
||||
{
|
||||
SaveInventories();
|
||||
SavePlayers();
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Barotrauma.Items.Components
|
||||
var panel2 = selectedWire.Connections[1]?.ConnectionPanel;
|
||||
if (panel2 != null && panel2 != this) { panel2.item.CreateServerEvent(panel2); }
|
||||
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
if (panel1 != null && panel1 != this) { panel1.item.CreateServerEvent(panel1); }
|
||||
|
||||
@@ -1318,7 +1318,7 @@ namespace Barotrauma.Networking
|
||||
Log("Client \"" + GameServer.ClientLogName(sender) + "\" ended the round.", ServerLog.MessageType.ServerMessage);
|
||||
if (mpCampaign != null && Level.IsLoadedOutpost)
|
||||
{
|
||||
mpCampaign.SaveInventories();
|
||||
mpCampaign.SavePlayers();
|
||||
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
|
||||
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
|
||||
}
|
||||
|
||||
@@ -374,12 +374,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Skill skill in characterInfos[i].Job.Skills)
|
||||
{
|
||||
var skillPrefab = characterInfos[i].Job.Prefab.Skills.Find(s => skill.Prefab == s);
|
||||
if (skillPrefab == null) { continue; }
|
||||
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.X, SkillReductionOnCampaignMidroundRespawn);
|
||||
}
|
||||
ReduceCharacterSkills(characterInfos[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -493,6 +488,17 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public static void ReduceCharacterSkills(CharacterInfo characterInfo)
|
||||
{
|
||||
if (characterInfo?.Job == null) { return; }
|
||||
foreach (Skill skill in characterInfo.Job.Skills)
|
||||
{
|
||||
var skillPrefab = characterInfo.Job.Prefab.Skills.Find(s => skill.Prefab == s);
|
||||
if (skillPrefab == null) { continue; }
|
||||
skill.Level = MathHelper.Lerp(skill.Level, skillPrefab.LevelRange.X, SkillReductionOnCampaignMidroundRespawn);
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
msg.WriteRangedInteger((int)CurrentState, 0, Enum.GetNames(typeof(State)).Length);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>0.1500.5.0</Version>
|
||||
<Version>0.1500.6.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2020</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -195,6 +195,7 @@
|
||||
<OutpostModule file="Content/Map/RuinModules/Alien_Chasm2.sub" />
|
||||
<OutpostModule file="Content/Map/RuinModules/Alien_Chasm3.sub" />
|
||||
<OutpostModule file="Content/Map/RuinModules/Alien_Entrance2.sub" />
|
||||
<OutpostModule file="Content/Map/RuinModules/Alien_Entrance3.sub" />
|
||||
<OutpostModule file="Content/Map/RuinModules/Alien_Storage1R.sub" />
|
||||
<OutpostModule file="Content/Map/RuinModules/Alien_Storage1L.sub" />
|
||||
<OutpostModule file="Content/Map/RuinModules/Alien_Offices1.sub" />
|
||||
|
||||
@@ -105,7 +105,6 @@ namespace Barotrauma
|
||||
|
||||
protected readonly float colliderWidth;
|
||||
protected readonly float minGapSize;
|
||||
protected readonly float minHullSize;
|
||||
protected readonly float colliderLength;
|
||||
protected readonly float avoidLookAheadDistance;
|
||||
|
||||
@@ -119,7 +118,6 @@ namespace Barotrauma
|
||||
colliderLength = size.Y;
|
||||
avoidLookAheadDistance = Math.Max(Math.Max(colliderWidth, colliderLength) * 3, 1.5f);
|
||||
minGapSize = ConvertUnits.ToDisplayUnits(Math.Min(colliderWidth, colliderLength));
|
||||
minHullSize = ConvertUnits.ToDisplayUnits(Math.Max(colliderLength, colliderWidth) * 1.1f);
|
||||
}
|
||||
|
||||
public virtual void OnAttacked(Character attacker, AttackResult attackResult) { }
|
||||
|
||||
@@ -148,6 +148,8 @@ namespace Barotrauma
|
||||
private CirclePhase CirclePhase;
|
||||
private float currentAttackIntensity;
|
||||
|
||||
private CoroutineHandle disableTailCoroutine;
|
||||
|
||||
private readonly IEnumerable<Body> myBodies;
|
||||
|
||||
public LatchOntoAI LatchOntoAI { get; private set; }
|
||||
@@ -556,7 +558,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Character.Submarine != null)
|
||||
if (Character.Submarine != null && Character.Params.UsePathFinding)
|
||||
{
|
||||
if (steeringManager != insideSteering)
|
||||
{
|
||||
@@ -678,8 +680,21 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
float sqrDist = Vector2.DistanceSquared(WorldPosition, SelectedAiTarget.WorldPosition);
|
||||
float reactDist = selectedTargetingParams != null && selectedTargetingParams.ReactDistance > 0 ? selectedTargetingParams.ReactDistance : GetPerceivingRange(SelectedAiTarget);
|
||||
if (sqrDist > Math.Pow(reactDist + movementMargin, 2))
|
||||
float reactDist = GetPerceivingRange(SelectedAiTarget);
|
||||
Vector2 offset = Vector2.Zero;
|
||||
if (selectedTargetingParams != null)
|
||||
{
|
||||
if (selectedTargetingParams.ReactDistance > 0)
|
||||
{
|
||||
reactDist = selectedTargetingParams.ReactDistance;
|
||||
}
|
||||
offset = selectedTargetingParams.Offset;
|
||||
}
|
||||
if (offset != Vector2.Zero)
|
||||
{
|
||||
reactDist += offset.Length();
|
||||
}
|
||||
if (sqrDist > MathUtils.Pow2(reactDist + movementMargin))
|
||||
{
|
||||
movementMargin = State == AIState.FleeTo ? 0 : reactDist;
|
||||
run = true;
|
||||
@@ -692,17 +707,43 @@ namespace Barotrauma
|
||||
{
|
||||
SteeringManager.Reset();
|
||||
Character.AnimController.TargetMovement = Vector2.Zero;
|
||||
float force = Character.AnimController.SwimSlowParams.SteerTorque;
|
||||
Character.AnimController.Collider.MoveToPos(SelectedAiTarget.Entity.SimPosition, force);
|
||||
if (SelectedAiTarget.Entity is Item item)
|
||||
if (Character.AnimController.InWater)
|
||||
{
|
||||
Character.AnimController.Collider.SmoothRotate(MathHelper.ToRadians(item.Rotation), force);
|
||||
float force = Character.AnimController.Collider.Mass / 10;
|
||||
Character.AnimController.Collider.MoveToPos(SelectedAiTarget.Entity.SimPosition + ConvertUnits.ToSimUnits(offset), force);
|
||||
if (SelectedAiTarget.Entity is Item item)
|
||||
{
|
||||
float rotation = item.Rotation;
|
||||
Character.AnimController.Collider.SmoothRotate(rotation, Character.AnimController.SwimFastParams.SteerTorque);
|
||||
var mainLimb = Character.AnimController.MainLimb;
|
||||
if (mainLimb.type == LimbType.Head)
|
||||
{
|
||||
mainLimb.body.SmoothRotate(rotation, Character.AnimController.SwimFastParams.HeadTorque);
|
||||
}
|
||||
else
|
||||
{
|
||||
mainLimb.body.SmoothRotate(rotation, Character.AnimController.SwimFastParams.TorsoTorque);
|
||||
}
|
||||
}
|
||||
if (disableTailCoroutine == null && SelectedAiTarget.Entity is Item i && i.HasTag("guardianshelter"))
|
||||
{
|
||||
if (!CoroutineManager.IsCoroutineRunning(disableTailCoroutine))
|
||||
{
|
||||
disableTailCoroutine = CoroutineManager.Invoke(() =>
|
||||
{
|
||||
if (Character != null && !Character.Removed)
|
||||
{
|
||||
Character.AnimController.HideAndDisable(LimbType.Tail, ignoreCollisions: false);
|
||||
}
|
||||
}, 1f);
|
||||
}
|
||||
}
|
||||
Character.AnimController.ApplyPose(
|
||||
new Vector2(0, -1),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(0, -1), footMoveForce: 1);
|
||||
}
|
||||
Character.AnimController.ApplyPose(
|
||||
new Vector2(0, -1),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(0, -1),
|
||||
new Vector2(0, -1), footMoveForce: 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -895,7 +936,7 @@ namespace Barotrauma
|
||||
else if (targetHulls.Any())
|
||||
{
|
||||
patrolTarget = ToolBox.SelectWeightedRandom(targetHulls, hullWeights, Rand.RandSync.Unsynced);
|
||||
var path = PathSteering.PathFinder.FindPath(Character.SimPosition, patrolTarget.SimPosition, minGapSize: minGapSize * 1.5f, nodeFilter: n => NodeFilter(n) && PatrolNodeFilter(n));
|
||||
var path = PathSteering.PathFinder.FindPath(Character.SimPosition, patrolTarget.SimPosition, minGapSize: minGapSize * 1.5f, nodeFilter: n => PatrolNodeFilter(n));
|
||||
|
||||
if (path.Unreachable)
|
||||
{
|
||||
@@ -926,7 +967,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (patrolTarget != null && pathSteering.CurrentPath != null && !pathSteering.CurrentPath.Finished && !pathSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
PathSteering.SteeringSeek(Character.GetRelativeSimPosition(patrolTarget), weight: 1, minGapWidth: minGapSize * 1.5f, nodeFilter: n => NodeFilter(n) && PatrolNodeFilter(n));
|
||||
PathSteering.SteeringSeek(Character.GetRelativeSimPosition(patrolTarget), weight: 1, minGapWidth: minGapSize * 1.5f, nodeFilter: n => PatrolNodeFilter(n));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -938,8 +979,6 @@ namespace Barotrauma
|
||||
UpdateIdle(deltaTime, followLastTarget);
|
||||
}
|
||||
|
||||
private bool NodeFilter(PathNode n) => n.Waypoint.CurrentHull == null || n.Waypoint.CurrentHull.Rect.Width > minHullSize && n.Waypoint.CurrentHull.Rect.Height > minHullSize;
|
||||
|
||||
private void FindTargetHulls()
|
||||
{
|
||||
if (Character.Submarine == null) { return; }
|
||||
@@ -1479,8 +1518,11 @@ namespace Barotrauma
|
||||
if (!canAttack || distance > margin)
|
||||
{
|
||||
// Steer towards the target if in the same room and swimming
|
||||
if (Character.CurrentHull != null && ((Character.AnimController.InWater || pursue || !Character.AnimController.CanWalk) &&
|
||||
(targetCharacter != null && VisibleHulls.Contains(targetCharacter.CurrentHull))))
|
||||
// Ruins have walls/pillars inside hulls and therefore we should navigate around them using the path steering.
|
||||
if (Character.CurrentHull != null &&
|
||||
Character.Submarine != null && !Character.Submarine.Info.IsRuin &&
|
||||
(Character.AnimController.InWater || pursue || !Character.AnimController.CanWalk) &&
|
||||
targetCharacter != null && VisibleHulls.Contains(targetCharacter.CurrentHull))
|
||||
{
|
||||
Vector2 myPos = Character.AnimController.SimplePhysicsEnabled ? Character.SimPosition : steeringLimb.SimPosition;
|
||||
SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(steerPos - myPos));
|
||||
@@ -1489,8 +1531,7 @@ namespace Barotrauma
|
||||
{
|
||||
pathSteering.SteeringSeek(steerPos, weight: 2,
|
||||
minGapWidth: minGapSize,
|
||||
startNodeFilter: n => (n.Waypoint.CurrentHull == null) == (Character.CurrentHull == null),
|
||||
nodeFilter: NodeFilter,
|
||||
startNodeFilter: n => (n.Waypoint.CurrentHull == null) == (Character.CurrentHull == null),
|
||||
checkVisiblity: true);
|
||||
|
||||
if (!pathSteering.IsPathDirty && pathSteering.CurrentPath.Unreachable)
|
||||
@@ -1526,7 +1567,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
pathSteering.SteeringSeek(steerPos, weight: 5, minGapWidth: minGapSize, NodeFilter);
|
||||
pathSteering.SteeringSeek(steerPos, weight: 5, minGapWidth: minGapSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1750,7 +1791,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (pathSteering != null)
|
||||
{
|
||||
pathSteering.SteeringSeek(steerPos, weight: 10, minGapWidth: minGapSize, NodeFilter);
|
||||
pathSteering.SteeringSeek(steerPos, weight: 10, minGapWidth: minGapSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1762,7 +1803,7 @@ namespace Barotrauma
|
||||
// Too close
|
||||
UpdateFallBack(attackWorldPos, deltaTime, followThrough: false);
|
||||
}
|
||||
if (SelectedAiTarget?.Entity is Character c && c.Submarine == null || distance == 0 || distance > ConvertUnits.ToDisplayUnits(avoidLookAheadDistance * 2))
|
||||
if (Character.CurrentHull == null && (SelectedAiTarget?.Entity is Character c && c.Submarine == null || distance == 0 || distance > ConvertUnits.ToDisplayUnits(avoidLookAheadDistance * 2)))
|
||||
{
|
||||
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 30);
|
||||
}
|
||||
@@ -2282,12 +2323,12 @@ namespace Barotrauma
|
||||
State = AIState.Idle;
|
||||
return;
|
||||
}
|
||||
if (Character.CurrentHull != null && PathSteering != null)
|
||||
if (Character.CurrentHull != null && steeringManager == insideSteering)
|
||||
{
|
||||
// Inside
|
||||
Character targetCharacter = SelectedAiTarget.Entity as Character;
|
||||
// Inside, but not inside ruins
|
||||
if ((Character.AnimController.InWater || !Character.AnimController.CanWalk) &&
|
||||
(targetCharacter != null && VisibleHulls.Contains(targetCharacter.CurrentHull) || Character.CanSeeTarget(SelectedAiTarget.Entity)))
|
||||
Character.Submarine != null && !Character.Submarine.Info.IsRuin &&
|
||||
SelectedAiTarget.Entity is Character c && VisibleHulls.Contains(c.CurrentHull))
|
||||
{
|
||||
// Steer towards the target if in the same room and swimming
|
||||
Vector2 dir = Vector2.Normalize(SelectedAiTarget.Entity.WorldPosition - Character.WorldPosition);
|
||||
@@ -2299,11 +2340,12 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
// Use path finding
|
||||
PathSteering.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), weight: 2, minGapWidth: minGapSize, NodeFilter);
|
||||
PathSteering.SteeringSeek(Character.GetRelativeSimPosition(SelectedAiTarget.Entity), weight: 2, minGapWidth: minGapSize);
|
||||
if (!PathSteering.IsPathDirty && PathSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
// Can't reach
|
||||
State = AIState.Idle;
|
||||
IgnoreTarget(SelectedAiTarget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -2419,10 +2461,12 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ignore all structures, items, and hulls inside wrecks and beacons
|
||||
// Ignore all structures, items, and hulls inside these subs.
|
||||
if (aiTarget.Entity.Submarine != null)
|
||||
{
|
||||
if (aiTarget.Entity.Submarine.Info.IsWreck || aiTarget.Entity.Submarine.Info.IsBeacon || UnattackableSubmarines.Contains(aiTarget.Entity.Submarine))
|
||||
if (aiTarget.Entity.Submarine.Info.IsWreck ||
|
||||
aiTarget.Entity.Submarine.Info.IsBeacon ||
|
||||
UnattackableSubmarines.Contains(aiTarget.Entity.Submarine))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -2433,6 +2477,7 @@ namespace Barotrauma
|
||||
if (character.CurrentHull != null) { continue; }
|
||||
// Ignore ruins
|
||||
if (hull.Submarine == null) { continue; }
|
||||
if (hull.Submarine.Info.IsRuin) { continue; }
|
||||
}
|
||||
|
||||
Door door = null;
|
||||
@@ -2448,14 +2493,6 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (door == null)
|
||||
{
|
||||
// Ignore items inside ruins, unless we are in the same hull. We can't target the ruin walls.
|
||||
if (item.Submarine == null && item.CurrentHull != Character.CurrentHull)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
foreach (var prio in AIParams.Targets)
|
||||
{
|
||||
if (item.HasTag(prio.Tag))
|
||||
@@ -2499,6 +2536,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (s.IsPlatform) { continue; }
|
||||
if (s.Submarine == null) { continue; }
|
||||
if (s.Submarine.Info.IsRuin) { continue; }
|
||||
bool isCharacterInside = character.CurrentHull != null;
|
||||
bool isInnerWall = s.prefab.Tags.Contains("inner");
|
||||
if (isInnerWall && !isCharacterInside)
|
||||
@@ -2709,7 +2747,11 @@ namespace Barotrauma
|
||||
{
|
||||
dist *= 0.9f;
|
||||
}
|
||||
if (!CanPerceive(aiTarget, dist)) { continue; }
|
||||
|
||||
if (!CanPerceive(aiTarget, dist, checkVisibility: SelectedAiTarget != aiTarget))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SelectedAiTarget == aiTarget)
|
||||
{
|
||||
@@ -2720,7 +2762,6 @@ namespace Barotrauma
|
||||
//if the target is very close, the distance doesn't make much difference
|
||||
// -> just ignore the distance and attack whatever has the highest priority
|
||||
dist = Math.Max(dist, 100.0f);
|
||||
|
||||
AITargetMemory targetMemory = GetTargetMemory(aiTarget, addIfNotFound: true);
|
||||
if (Character.Submarine != null && !Character.Submarine.Info.IsRuin && Character.CurrentHull != null)
|
||||
{
|
||||
@@ -2907,6 +2948,7 @@ namespace Barotrauma
|
||||
wallTarget = null;
|
||||
if (SelectedAiTarget == null) { return; }
|
||||
if (SelectedAiTarget.Entity == null) { return; }
|
||||
if (!canAttackWalls) { return; }
|
||||
if (HasValidPath(requireNonDirty: true)) { return; }
|
||||
wallHits.Clear();
|
||||
Structure wall = null;
|
||||
@@ -3129,7 +3171,7 @@ namespace Barotrauma
|
||||
{
|
||||
_selectedAiTarget = null;
|
||||
}
|
||||
else if (CanPerceive(_selectedAiTarget))
|
||||
else if (CanPerceive(_selectedAiTarget, checkVisibility: false))
|
||||
{
|
||||
var memory = GetTargetMemory(_selectedAiTarget, false);
|
||||
if (memory != null)
|
||||
@@ -3367,6 +3409,12 @@ namespace Barotrauma
|
||||
protected override void OnStateChanged(AIState from, AIState to)
|
||||
{
|
||||
LatchOntoAI?.DeattachFromBody(reset: true);
|
||||
if (disableTailCoroutine != null)
|
||||
{
|
||||
CoroutineManager.StopCoroutines(disableTailCoroutine);
|
||||
Character.AnimController.RestoreTemporarilyDisabled();
|
||||
disableTailCoroutine = null;
|
||||
}
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
AttackingLimb = null;
|
||||
movementMargin = 0;
|
||||
@@ -3382,11 +3430,16 @@ namespace Barotrauma
|
||||
|
||||
private float GetPerceivingRange(AITarget target) => Math.Max(target.SightRange * Sight, target.SoundRange * Hearing);
|
||||
|
||||
private bool CanPerceive(AITarget target, float dist = -1, float distSquared = -1)
|
||||
private bool CanPerceive(AITarget target, float dist = -1, float distSquared = -1, bool checkVisibility = false)
|
||||
{
|
||||
bool insideSightRange;
|
||||
bool insideSoundRange;
|
||||
checkVisibility = checkVisibility && Character.Submarine != null && target.Entity.Submarine == Character.Submarine;
|
||||
if (dist > 0)
|
||||
{
|
||||
return dist <= target.SightRange * Sight || dist <= target.SoundRange * Hearing;
|
||||
insideSightRange = IsInRange(dist, target.SightRange, Sight);
|
||||
if (!checkVisibility && insideSightRange) { return true; }
|
||||
insideSoundRange = IsInRange(dist, target.SoundRange, Hearing);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3394,8 +3447,42 @@ namespace Barotrauma
|
||||
{
|
||||
distSquared = Vector2.DistanceSquared(Character.WorldPosition, target.WorldPosition);
|
||||
}
|
||||
return distSquared <= MathUtils.Pow(target.SightRange * Sight, 2) || distSquared <= MathUtils.Pow(target.SoundRange * Hearing, 2);
|
||||
insideSightRange = IsInRangeSqr(distSquared, target.SightRange, Sight);
|
||||
if (!checkVisibility && insideSightRange) { return true; }
|
||||
insideSoundRange = IsInRangeSqr(distSquared, target.SoundRange, Hearing);
|
||||
}
|
||||
if (!checkVisibility)
|
||||
{
|
||||
return insideSightRange || insideSoundRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!insideSightRange && !insideSoundRange) { return false; }
|
||||
// Inside the same submarine -> check whether the target is behind a wall
|
||||
if (target.Entity is Character c && VisibleHulls.Contains(c.CurrentHull) || target.Entity is Item i && VisibleHulls.Contains(i.CurrentHull))
|
||||
{
|
||||
return insideSightRange || insideSoundRange;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No line of sight to the target -> Ignore sight and use only half of the sound range
|
||||
if (dist > 0)
|
||||
{
|
||||
return IsInRange(dist, target.SoundRange, Hearing / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (distSquared < 0)
|
||||
{
|
||||
distSquared = Vector2.DistanceSquared(Character.WorldPosition, target.WorldPosition);
|
||||
}
|
||||
return IsInRangeSqr(distSquared, target.SoundRange, Hearing / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsInRange(float dist, float range, float perception) => dist <= range * perception;
|
||||
bool IsInRangeSqr(float distSquared, float range, float perception) => distSquared <= MathUtils.Pow2(range * perception);
|
||||
}
|
||||
|
||||
public void ReevaluateAttacks()
|
||||
|
||||
@@ -34,9 +34,6 @@ namespace Barotrauma
|
||||
private float flipTimer;
|
||||
private const float FlipInterval = 0.5f;
|
||||
|
||||
private float teamChangeTimer;
|
||||
private const float TeamChangeInterval = 0.5f;
|
||||
|
||||
public const float HULL_SAFETY_THRESHOLD = 40;
|
||||
public const float HULL_LOW_OXYGEN_PERCENTAGE = 30;
|
||||
|
||||
@@ -1124,7 +1121,7 @@ namespace Barotrauma
|
||||
{
|
||||
(GameMain.GameSession?.GameMode as CampaignMode)?.OutpostNPCAttacked(Character, attacker, attackResult);
|
||||
// Inform other NPCs
|
||||
if (cumulativeDamage > 1)
|
||||
if (cumulativeDamage > 1 || totalDamage >= 10)
|
||||
{
|
||||
InformOtherNPCs(cumulativeDamage);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Barotrauma
|
||||
public bool AttachToWalls { get; private set; }
|
||||
public bool AttachToCharacters { get; private set; }
|
||||
|
||||
private readonly float minDeattachSpeed, maxDeattachSpeed, maxAttachDuration;
|
||||
private readonly float minDeattachSpeed, maxDeattachSpeed, maxAttachDuration, coolDown;
|
||||
private readonly float damageOnDetach, detachStun;
|
||||
private readonly bool weld;
|
||||
private float deattachCheckTimer;
|
||||
@@ -61,6 +61,7 @@ namespace Barotrauma
|
||||
minDeattachSpeed = element.GetAttributeFloat("mindeattachspeed", 5.0f);
|
||||
maxDeattachSpeed = Math.Max(minDeattachSpeed, element.GetAttributeFloat("maxdeattachspeed", 8.0f));
|
||||
maxAttachDuration = element.GetAttributeFloat("maxattachduration", -1.0f);
|
||||
coolDown = element.GetAttributeFloat("cooldown", 2f);
|
||||
damageOnDetach = element.GetAttributeFloat("damageondetach", 0.0f);
|
||||
detachStun = element.GetAttributeFloat("detachstun", 0.0f);
|
||||
localAttachPos = ConvertUnits.ToSimUnits(element.GetAttributeVector2("localattachpos", Vector2.Zero));
|
||||
@@ -283,6 +284,7 @@ namespace Barotrauma
|
||||
if (maxAttachDuration > 0)
|
||||
{
|
||||
deattach = true;
|
||||
attachCooldown = coolDown;
|
||||
}
|
||||
if (!deattach && targetWall != null && targetSubmarine != null)
|
||||
{
|
||||
@@ -291,7 +293,7 @@ namespace Barotrauma
|
||||
if (enemyAI.CanPassThroughHole(targetWall, targetSection))
|
||||
{
|
||||
deattach = true;
|
||||
attachCooldown = 2;
|
||||
attachCooldown = coolDown;
|
||||
}
|
||||
if (!deattach)
|
||||
{
|
||||
@@ -310,7 +312,7 @@ namespace Barotrauma
|
||||
{
|
||||
deattach = true;
|
||||
character.AddDamage(character.WorldPosition, new List<Affliction>() { AfflictionPrefab.InternalDamage.Instantiate(damageOnDetach) }, detachStun, true);
|
||||
attachCooldown = detachStun * 2;
|
||||
attachCooldown = Math.Max(detachStun * 2, coolDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1048,6 +1048,7 @@ namespace Barotrauma
|
||||
if (closeEnough)
|
||||
{
|
||||
UseWeapon(deltaTime);
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
else if (!character.IsFacing(Enemy.WorldPosition))
|
||||
{
|
||||
|
||||
+1
-1
@@ -163,7 +163,7 @@ namespace Barotrauma
|
||||
CoroutineManager.StopCoroutines(coroutine);
|
||||
DelayedObjectives.Remove(objective);
|
||||
}
|
||||
coroutine = CoroutineManager.InvokeAfter(() =>
|
||||
coroutine = CoroutineManager.Invoke(() =>
|
||||
{
|
||||
//round ended before the coroutine finished
|
||||
#if CLIENT
|
||||
|
||||
@@ -357,8 +357,11 @@ namespace Barotrauma
|
||||
float itemAngle;
|
||||
Holdable holdable = item.GetComponent<Holdable>();
|
||||
float torsoRotation = torso.Rotation;
|
||||
bool equippedInRightHand = character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == item && rightHand != null && !rightHand.IsSevered;
|
||||
bool equippedInLefthand = character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand) == item && leftHand != null && !leftHand.IsSevered;
|
||||
|
||||
Item rightHandItem = character.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand);
|
||||
bool equippedInRightHand = rightHandItem == item && rightHand != null && !rightHand.IsSevered;
|
||||
Item leftHandItem = character.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand);
|
||||
bool equippedInLefthand = leftHandItem == item && leftHand != null && !leftHand.IsSevered;
|
||||
if (aim && !isClimbing && !usingController && character.Stun <= 0.0f && itemPos != Vector2.Zero && !character.IsIncapacitated)
|
||||
{
|
||||
Vector2 mousePos = ConvertUnits.ToSimUnits(character.SmoothedCursorPosition);
|
||||
@@ -366,17 +369,23 @@ namespace Barotrauma
|
||||
holdAngle = MathUtils.VectorToAngle(new Vector2(diff.X, diff.Y * Dir)) - torsoRotation * Dir;
|
||||
holdAngle += GetAimWobble(rightHand, leftHand, item);
|
||||
itemAngle = torsoRotation + holdAngle * Dir;
|
||||
|
||||
if (holdable.ControlPose)
|
||||
{
|
||||
var head = GetLimb(LimbType.Head);
|
||||
if (head != null)
|
||||
//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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
aiming = true;
|
||||
}
|
||||
@@ -506,7 +515,11 @@ namespace Barotrauma
|
||||
DebugConsole.AddWarning($"Aim position for the item {item.Name} may be incorrect (further than the length of the character's arm)");
|
||||
}
|
||||
#endif
|
||||
HandIK(i == 0 ? rightHand : leftHand, transformedHoldPos + transformedHandlePos[i], CurrentAnimationParams.ArmIKStrength, CurrentAnimationParams.HandIKStrength);
|
||||
HandIK(
|
||||
i == 0 ? rightHand : leftHand, transformedHoldPos + transformedHandlePos[i],
|
||||
CurrentAnimationParams.ArmIKStrength,
|
||||
CurrentAnimationParams.HandIKStrength,
|
||||
maxAngularVelocity: 15.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,7 +544,7 @@ namespace Barotrauma
|
||||
return (lowFreqNoise * 1.0f + highFreqNoise * 0.1f) * wobbleStrength;
|
||||
}
|
||||
|
||||
public void HandIK(Limb hand, Vector2 pos, float armTorque = 1.0f, float handTorque = 1.0f)
|
||||
public void HandIK(Limb hand, Vector2 pos, float armTorque = 1.0f, float handTorque = 1.0f, float maxAngularVelocity = float.PositiveInfinity)
|
||||
{
|
||||
Vector2 shoulderPos;
|
||||
|
||||
@@ -572,11 +585,20 @@ namespace Barotrauma
|
||||
armAngle -= MathHelper.TwoPi;
|
||||
}
|
||||
|
||||
arm?.body.SmoothRotate(armAngle - upperArmAngle, 100.0f * armTorque * arm.Mass, wrapAngle: false);
|
||||
if (arm?.body != null && Math.Abs(arm.body.AngularVelocity) < maxAngularVelocity)
|
||||
{
|
||||
arm.body.SmoothRotate(armAngle - upperArmAngle, 100.0f * armTorque * arm.Mass, wrapAngle: false);
|
||||
}
|
||||
float forearmAngle = armAngle + lowerArmAngle;
|
||||
forearm?.body.SmoothRotate(forearmAngle, 100.0f * handTorque * forearm.Mass, wrapAngle: false);
|
||||
float handAngle = forearm != null ? forearmAngle : armAngle;
|
||||
hand?.body.SmoothRotate(handAngle, 10.0f * handTorque * hand.Mass, wrapAngle: false);
|
||||
if (forearm?.body != null && Math.Abs(forearm.body.AngularVelocity) < maxAngularVelocity)
|
||||
{
|
||||
forearm.body.SmoothRotate(forearmAngle, 100.0f * handTorque * forearm.Mass, wrapAngle: false);
|
||||
}
|
||||
if (hand?.body != null && Math.Abs(hand.body.AngularVelocity) < maxAngularVelocity)
|
||||
{
|
||||
float handAngle = forearm != null ? forearmAngle : armAngle;
|
||||
hand.body.SmoothRotate(handAngle, 10.0f * handTorque * hand.Mass, wrapAngle: false);
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyPose(Vector2 leftHandPos, Vector2 rightHandPos, Vector2 leftFootPos, Vector2 rightFootPos, float footMoveForce = 10)
|
||||
@@ -672,7 +694,7 @@ namespace Barotrauma
|
||||
|
||||
forearmLength += Vector2.Distance(
|
||||
rightHand.PullJointLocalAnchorA,
|
||||
rightElbow.LimbA.type == LimbType.RightHand ? rightElbow.LocalAnchorA : rightElbow.LocalAnchorB);
|
||||
rightWrist.LimbA.type == LimbType.RightHand ? rightWrist.LocalAnchorA : rightWrist.LocalAnchorB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,7 +815,7 @@ namespace Barotrauma
|
||||
{
|
||||
limb.body.SmoothRotate(MainLimb.Rotation + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Mass * limb.Params.ConstantTorque, wrapAngle: true);
|
||||
}
|
||||
if (limb.Params.BlinkFrequency > 0)
|
||||
if (limb.Params.BlinkFrequency > 0 && !limb.Params.OnlyBlinkInWater)
|
||||
{
|
||||
limb.UpdateBlink(deltaTime, MainLimb.Rotation);
|
||||
}
|
||||
|
||||
+4
-4
@@ -170,7 +170,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
float shoulderHeight = Collider.height / 2.0f - 0.1f;
|
||||
float shoulderHeight = Collider.height / 2.0f;
|
||||
if (inWater)
|
||||
{
|
||||
shoulderHeight += 0.4f;
|
||||
@@ -987,7 +987,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
handPos += head.LinearVelocity * 0.1f;
|
||||
handPos += head.LinearVelocity.ClampLength(1.0f) * 0.1f;
|
||||
|
||||
// Not sure why the params has to be flipped, but it works.
|
||||
var handMoveAmount = CurrentSwimParams.HandMoveAmount.Flip();
|
||||
@@ -1002,7 +1002,7 @@ namespace Barotrauma
|
||||
Vector2 rightHandPos = new Vector2(-handPosX, -handPosY) + handMoveOffset;
|
||||
rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, rightHandPos.X) : Math.Min(-0.3f, rightHandPos.X);
|
||||
rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
|
||||
float speedMultiplier = character.SpeedMultiplier * (1 - Character.GetRightHandPenalty());
|
||||
float speedMultiplier = Math.Min(character.SpeedMultiplier * (1 - Character.GetRightHandPenalty()), 1.0f);
|
||||
// Limb hand, Vector2 pos, float force = 1.0f
|
||||
HandIK(rightHand, handPos + rightHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
|
||||
}
|
||||
@@ -1012,7 +1012,7 @@ namespace Barotrauma
|
||||
Vector2 leftHandPos = new Vector2(handPosX, handPosY) + handMoveOffset;
|
||||
leftHandPos.X = (Dir == 1.0f) ? Math.Max(0.3f, leftHandPos.X) : Math.Min(-0.3f, leftHandPos.X);
|
||||
leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix);
|
||||
float speedMultiplier = character.SpeedMultiplier * (1 - Character.GetLeftHandPenalty());
|
||||
float speedMultiplier = Math.Min(character.SpeedMultiplier * (1 - Character.GetLeftHandPenalty()), 1.0f);
|
||||
HandIK(leftHand, handPos + leftHandPos, CurrentSwimParams.ArmMoveStrength * speedMultiplier, CurrentSwimParams.HandMoveStrength * speedMultiplier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,24 +1212,24 @@ namespace Barotrauma
|
||||
//the room where the ragdoll is in is used as the "guess", meaning that it's checked first
|
||||
Hull limbHull = currentHull == null ? null : Hull.FindHull(limb.WorldPosition, currentHull);
|
||||
|
||||
bool prevInWater = limb.inWater;
|
||||
limb.inWater = false;
|
||||
bool prevInWater = limb.InWater;
|
||||
limb.InWater = false;
|
||||
|
||||
if (forceStanding)
|
||||
{
|
||||
limb.inWater = false;
|
||||
limb.InWater = false;
|
||||
}
|
||||
else if (limbHull == null)
|
||||
{
|
||||
//limb isn't in any room -> it's in the water
|
||||
limb.inWater = true;
|
||||
limb.InWater = true;
|
||||
if (limb.type == LimbType.Head) headInWater = true;
|
||||
}
|
||||
else if (limbHull.WaterVolume > 0.0f && Submarine.RectContains(limbHull.Rect, limb.Position))
|
||||
{
|
||||
if (limb.Position.Y < limbHull.Surface)
|
||||
{
|
||||
limb.inWater = true;
|
||||
limb.InWater = true;
|
||||
surfaceY = limbHull.Surface;
|
||||
if (limb.type == LimbType.Head)
|
||||
{
|
||||
@@ -1237,7 +1237,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
//the limb has gone through the surface of the water
|
||||
if (Math.Abs(limb.LinearVelocity.Y) > 5.0f && limb.inWater != prevInWater)
|
||||
if (Math.Abs(limb.LinearVelocity.Y) > 5.0f && limb.InWater != prevInWater)
|
||||
{
|
||||
Splash(limb, limbHull);
|
||||
|
||||
@@ -1435,7 +1435,7 @@ namespace Barotrauma
|
||||
flowForce *= 1 - Math.Clamp(character.GetStatValue(StatTypes.FlowResistance), 0f, 1f);
|
||||
|
||||
float flowForceMagnitude = flowForce.Length();
|
||||
float limbMultipier = limbs.Count(l => l.inWater) / (float)limbs.Length;
|
||||
float limbMultipier = limbs.Count(l => l.InWater) / (float)limbs.Length;
|
||||
//if the force strong enough, stun the character to let it get thrown around by the water
|
||||
if ((flowForceMagnitude * limbMultipier) - flowStunTolerance > StunForceThreshold)
|
||||
{
|
||||
@@ -1472,7 +1472,7 @@ namespace Barotrauma
|
||||
Collider.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
foreach (Limb limb in limbs)
|
||||
{
|
||||
if (!limb.inWater) { continue; }
|
||||
if (!limb.InWater) { continue; }
|
||||
limb.body.ApplyForce(flowForce, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
}
|
||||
@@ -1840,9 +1840,23 @@ namespace Barotrauma
|
||||
|
||||
public void ReleaseStuckLimbs()
|
||||
{
|
||||
Limbs.ForEach(l => l.Release());
|
||||
// Commented out, because stuck limbs is not a feature that we currently use, as it would require that we sync all the limbs, which we don't do.
|
||||
//Limbs.ForEach(l => l.Release());
|
||||
}
|
||||
|
||||
public void HideAndDisable(LimbType limbType, float duration = 0, bool ignoreCollisions = true)
|
||||
{
|
||||
foreach (var limb in Limbs)
|
||||
{
|
||||
if (limb.type == limbType)
|
||||
{
|
||||
limb.HideAndDisable(duration, ignoreCollisions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RestoreTemporarilyDisabled() => Limbs.ForEach(l => l.ReEnable());
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
if (Limbs != null)
|
||||
|
||||
@@ -313,6 +313,15 @@ namespace Barotrauma
|
||||
public string TraitorCurrentObjective = "";
|
||||
public bool IsHuman => SpeciesName.Equals(CharacterPrefab.HumanSpeciesName, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Can be used by status effects to check the character's gender
|
||||
/// </summary>
|
||||
public bool IsMale => Info != null && Info.HasGenders && Info.Gender == Gender.Male;
|
||||
/// <summary>
|
||||
/// Can be used by status effects to check the character's gender
|
||||
/// </summary>
|
||||
public bool IsFemale => Info != null && Info.HasGenders && Info.Gender == Gender.Female;
|
||||
|
||||
private float attackCoolDown;
|
||||
|
||||
public List<OrderInfo> CurrentOrders => Info?.CurrentOrders;
|
||||
@@ -581,6 +590,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be used by status effects to check whether the characters is in a high-pressure environment
|
||||
/// </summary>
|
||||
public bool InPressure
|
||||
{
|
||||
get { return CurrentHull == null || CurrentHull.LethalPressure > 5.0f; }
|
||||
}
|
||||
|
||||
public const float KnockbackCooldown = 5.0f;
|
||||
public float KnockbackCooldownTimer;
|
||||
|
||||
@@ -641,22 +658,13 @@ namespace Barotrauma
|
||||
|
||||
public bool DisableHealthWindow;
|
||||
|
||||
public float Vitality
|
||||
{
|
||||
get { return CharacterHealth.Vitality; }
|
||||
}
|
||||
|
||||
public float Health
|
||||
{
|
||||
get { return CharacterHealth.Vitality; }
|
||||
}
|
||||
|
||||
// These properties needs to be exposed for status effects
|
||||
public float Vitality => CharacterHealth.Vitality;
|
||||
public float Health => Vitality;
|
||||
public float HealthPercentage => CharacterHealth.HealthPercentage;
|
||||
|
||||
public float MaxVitality
|
||||
{
|
||||
get { return CharacterHealth.MaxVitality; }
|
||||
}
|
||||
public float MaxVitality => CharacterHealth.MaxVitality;
|
||||
public float MaxHealth => MaxVitality;
|
||||
public AIState AIState => AIController is EnemyAIController enemyAI ? enemyAI.State : AIState.Idle;
|
||||
|
||||
public float Bloodloss
|
||||
{
|
||||
@@ -2405,7 +2413,7 @@ namespace Barotrauma
|
||||
var head = AnimController.GetLimb(LimbType.Head);
|
||||
bool headInWater = head == null ?
|
||||
AnimController.InWater :
|
||||
head.inWater;
|
||||
head.InWater;
|
||||
//climb ladders automatically when pressing up/down inside their trigger area
|
||||
Ladder currentLadder = SelectedConstruction?.GetComponent<Ladder>();
|
||||
if ((SelectedConstruction == null || currentLadder != null) &&
|
||||
@@ -3813,6 +3821,7 @@ namespace Barotrauma
|
||||
|
||||
foreach (var joint in AnimController.LimbJoints)
|
||||
{
|
||||
if (joint.LimbA.type == LimbType.Head || joint.LimbB.type == LimbType.Head) { continue; }
|
||||
if (joint.revoluteJoint != null)
|
||||
{
|
||||
joint.revoluteJoint.LimitEnabled = false;
|
||||
@@ -3950,7 +3959,10 @@ namespace Barotrauma
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
#if CLIENT
|
||||
if (limb.LightSource != null) limb.LightSource.Color = limb.InitialLightSourceColor;
|
||||
if (limb.LightSource != null)
|
||||
{
|
||||
limb.LightSource.Color = limb.InitialLightSourceColor;
|
||||
}
|
||||
#endif
|
||||
limb.body.Enabled = true;
|
||||
limb.IsSevered = false;
|
||||
@@ -4369,7 +4381,6 @@ namespace Barotrauma
|
||||
if (!info.UnlockedTalents.Add(talentPrefab.Identifier)) { return false; }
|
||||
}
|
||||
|
||||
DebugConsole.AddWarning("added " + talentPrefab.OriginalName);
|
||||
CharacterTalent characterTalent = new CharacterTalent(talentPrefab, this);
|
||||
characterTalent.ActivateTalent(addingFirstTime);
|
||||
characterTalents.Add(characterTalent);
|
||||
@@ -4377,7 +4388,10 @@ namespace Barotrauma
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateTalents });
|
||||
#endif
|
||||
|
||||
if (addingFirstTime)
|
||||
{
|
||||
OnTalentGiven(talentPrefab.Identifier);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4441,6 +4455,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
partial void OnMoneyChanged(int prevAmount, int newAmount);
|
||||
partial void OnTalentGiven(string talentIdentifier);
|
||||
|
||||
/// <summary>
|
||||
/// This dictionary is used for stats that are required very frequently. Not very performant, but easier to develop with for now.
|
||||
|
||||
@@ -451,9 +451,9 @@ namespace Barotrauma
|
||||
set => Head.FaceAttachmentIndex = value;
|
||||
}
|
||||
|
||||
public readonly ImmutableArray<Color> HairColors;
|
||||
public readonly ImmutableArray<Color> FacialHairColors;
|
||||
public readonly ImmutableArray<Color> SkinColors;
|
||||
public readonly ImmutableArray<(Color Color, float Commonness)> HairColors;
|
||||
public readonly ImmutableArray<(Color Color, float Commonness)> FacialHairColors;
|
||||
public readonly ImmutableArray<(Color Color, float Commonness)> SkinColors;
|
||||
|
||||
public Color HairColor
|
||||
{
|
||||
@@ -522,15 +522,12 @@ namespace Barotrauma
|
||||
// TODO: support for variants
|
||||
Head = new HeadInfo();
|
||||
HasGenders = CharacterConfigElement.GetAttributeBool("genders", false);
|
||||
Head.gender = GetRandomGender(randSync);
|
||||
HasRaces = CharacterConfigElement.GetAttributeBool("races", false);
|
||||
Head.race = GetRandomRace(randSync);
|
||||
CalculateHeadSpriteRange();
|
||||
HeadSpriteId = GetRandomHeadID(randSync);
|
||||
SetGenderAndRace(randSync);
|
||||
Job = (jobPrefab == null) ? Job.Random(Rand.RandSync.Unsynced) : new Job(jobPrefab, variant);
|
||||
HairColors = CharacterConfigElement.GetAttributeColorArray("haircolors", new Color[] { Color.WhiteSmoke }).ToImmutableArray();
|
||||
FacialHairColors = CharacterConfigElement.GetAttributeColorArray("facialhaircolors", new Color[] { Color.WhiteSmoke }).ToImmutableArray();
|
||||
SkinColors = CharacterConfigElement.GetAttributeColorArray("skincolors", new Color[] { new Color(255, 215, 200, 255) }).ToImmutableArray();
|
||||
HairColors = CharacterConfigElement.GetAttributeTupleArray("haircolors", new (Color, float)[] { (Color.WhiteSmoke, 100f) }).ToImmutableArray();
|
||||
FacialHairColors = CharacterConfigElement.GetAttributeTupleArray("facialhaircolors", new (Color, float)[] { (Color.WhiteSmoke, 100f) }).ToImmutableArray();
|
||||
SkinColors = CharacterConfigElement.GetAttributeTupleArray("skincolors", new (Color, float)[] { (new Color(255, 215, 200, 255), 100f) }).ToImmutableArray();
|
||||
SetColors();
|
||||
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
@@ -580,26 +577,38 @@ namespace Barotrauma
|
||||
return name;
|
||||
}
|
||||
|
||||
public static Color SelectRandomColor(in ImmutableArray<(Color Color, float Commonness)> array)
|
||||
=> ToolBox.SelectWeightedRandom(array, array.Select(p => p.Commonness).ToArray(), Rand.RandSync.Unsynced)
|
||||
.Color;
|
||||
|
||||
private void SetGenderAndRace(Rand.RandSync randSync)
|
||||
{
|
||||
Head.gender = GetRandomGender(randSync);
|
||||
Head.race = GetRandomRace(randSync);
|
||||
CalculateHeadSpriteRange();
|
||||
HeadSpriteId = GetRandomHeadID(randSync);
|
||||
}
|
||||
|
||||
private void SetColors()
|
||||
{
|
||||
HairColor = HairColors.GetRandom();
|
||||
FacialHairColor = FacialHairColors.GetRandom();
|
||||
SkinColor = SkinColors.GetRandom();
|
||||
HairColor = SelectRandomColor(HairColors);
|
||||
FacialHairColor = SelectRandomColor(FacialHairColors);
|
||||
SkinColor = SelectRandomColor(SkinColors);
|
||||
}
|
||||
|
||||
private void CheckColors()
|
||||
{
|
||||
if (HairColor == Color.Black)
|
||||
{
|
||||
HairColor = HairColors.GetRandom();
|
||||
HairColor = SelectRandomColor(HairColors);
|
||||
}
|
||||
if (FacialHairColor == Color.Black)
|
||||
{
|
||||
FacialHairColor = FacialHairColors.GetRandom();
|
||||
FacialHairColor = SelectRandomColor(FacialHairColors);
|
||||
}
|
||||
if (SkinColor == Color.Black)
|
||||
{
|
||||
SkinColor = SkinColors.GetRandom();
|
||||
SkinColor = SelectRandomColor(SkinColors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,7 +619,6 @@ namespace Barotrauma
|
||||
idCounter++;
|
||||
Name = infoElement.GetAttributeString("name", "");
|
||||
OriginalName = infoElement.GetAttributeString("originalname", null);
|
||||
string genderStr = infoElement.GetAttributeString("gender", "male").ToLowerInvariant();
|
||||
Salary = infoElement.GetAttributeInt("salary", 1000);
|
||||
ExperiencePoints = infoElement.GetAttributeInt("experiencepoints", 0);
|
||||
UnlockedTalents = new HashSet<string>(infoElement.GetAttributeStringArray("unlockedtalents", new string[0], convertToLowerInvariant: true));
|
||||
@@ -642,10 +650,10 @@ namespace Barotrauma
|
||||
{
|
||||
race = GetRandomRace(Rand.RandSync.Unsynced);
|
||||
}
|
||||
HairColors = CharacterConfigElement.GetAttributeColorArray("haircolors", new Color[] { Color.WhiteSmoke }).ToImmutableArray();
|
||||
FacialHairColors = CharacterConfigElement.GetAttributeColorArray("facialhaircolors", new Color[] { Color.WhiteSmoke }).ToImmutableArray();
|
||||
SkinColors = CharacterConfigElement.GetAttributeColorArray("skincolors", new Color[] { new Color(255, 215, 200, 255) }).ToImmutableArray();
|
||||
|
||||
HairColors = CharacterConfigElement.GetAttributeTupleArray("haircolors", new (Color, float)[] { (Color.WhiteSmoke, 100f) }).ToImmutableArray();
|
||||
FacialHairColors = CharacterConfigElement.GetAttributeTupleArray("facialhaircolors", new (Color, float)[] { (Color.WhiteSmoke, 100f) }).ToImmutableArray();
|
||||
SkinColors = CharacterConfigElement.GetAttributeTupleArray("skincolors", new (Color, float)[] { (new Color(255, 215, 200, 255), 100f) }).ToImmutableArray();
|
||||
|
||||
RecreateHead(
|
||||
infoElement.GetAttributeInt("headspriteid", 1),
|
||||
race,
|
||||
@@ -655,9 +663,35 @@ namespace Barotrauma
|
||||
infoElement.GetAttributeInt("moustacheindex", -1),
|
||||
infoElement.GetAttributeInt("faceattachmentindex", -1));
|
||||
|
||||
SkinColor = infoElement.GetAttributeColor("skincolor", Color.White);
|
||||
HairColor = infoElement.GetAttributeColor("haircolor", Color.White);
|
||||
FacialHairColor = infoElement.GetAttributeColor("facialhaircolor", Color.White);
|
||||
//backwards compatibility
|
||||
if (infoElement.Attribute("skincolor") == null && infoElement.Attribute("race") != null)
|
||||
{
|
||||
string raceStr = infoElement.GetAttributeString("race", string.Empty);
|
||||
Race obsoleteRace = Race.None;
|
||||
Enum.TryParse(raceStr, ignoreCase: true, out obsoleteRace);
|
||||
switch (obsoleteRace)
|
||||
{
|
||||
case Race.White:
|
||||
case Race.None:
|
||||
SkinColor = new Color(255, 215, 200, 255);
|
||||
break;
|
||||
case Race.Brown:
|
||||
SkinColor = new Color(158, 95, 72, 255);
|
||||
break;
|
||||
case Race.Black:
|
||||
SkinColor = new Color(153, 75, 42, 255);
|
||||
break;
|
||||
case Race.Asian:
|
||||
SkinColor = new Color(191, 116, 61, 255);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SkinColor = infoElement.GetAttributeColor("skincolor", Color.Black);
|
||||
}
|
||||
HairColor = infoElement.GetAttributeColor("haircolor", Color.Black);
|
||||
FacialHairColor = infoElement.GetAttributeColor("facialhaircolor", Color.Black);
|
||||
CheckColors();
|
||||
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
@@ -1128,7 +1162,7 @@ namespace Barotrauma
|
||||
return (int)(salary * Job.Prefab.PriceMultiplier);
|
||||
}
|
||||
|
||||
public void IncreaseSkillLevel(string skillIdentifier, float increase, Vector2 pos)
|
||||
public void IncreaseSkillLevel(string skillIdentifier, float increase, Vector2 pos, bool gainedFromApprenticeship = false)
|
||||
{
|
||||
if (Job == null || (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) || Character == null) { return; }
|
||||
|
||||
@@ -1148,7 +1182,7 @@ namespace Barotrauma
|
||||
{
|
||||
// assume we are getting at least 1 point in skill, since this logic only runs in such cases
|
||||
float increaseSinceLastSkillPoint = MathHelper.Max(increase, 1f);
|
||||
var abilitySkillGain = new AbilityValueStringCharacter(increaseSinceLastSkillPoint, skillIdentifier, Character);
|
||||
var abilitySkillGain = new AbilitySkillGain(increaseSinceLastSkillPoint, skillIdentifier, Character, gainedFromApprenticeship);
|
||||
Character.CheckTalents(AbilityEffectType.OnGainSkillPoint, abilitySkillGain);
|
||||
foreach (Character character in Character.GetFriendlyCrew(Character))
|
||||
{
|
||||
@@ -1747,4 +1781,19 @@ namespace Barotrauma
|
||||
RemoveAfterRound = retainAfterRound;
|
||||
}
|
||||
}
|
||||
|
||||
class AbilitySkillGain : AbilityObject, IAbilityValue, IAbilityString, IAbilityCharacter
|
||||
{
|
||||
public AbilitySkillGain(float value, string abilityString, Character character, bool gainedFromApprenticeship)
|
||||
{
|
||||
Value = value;
|
||||
String = abilityString;
|
||||
Character = character;
|
||||
GainedFromApprenticeship = gainedFromApprenticeship;
|
||||
}
|
||||
public Character Character { get; set; }
|
||||
public float Value { get; set; }
|
||||
public string String { get; set; }
|
||||
public bool GainedFromApprenticeship { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -749,7 +749,7 @@ namespace Barotrauma
|
||||
|
||||
StunTimer = Stun > 0 ? StunTimer + deltaTime : 0;
|
||||
|
||||
FaceTint = DefaultFaceTint;
|
||||
if (Character.GodMode) { return; }
|
||||
|
||||
for (int i = 0; i < limbHealths.Count; i++)
|
||||
{
|
||||
@@ -775,10 +775,6 @@ namespace Barotrauma
|
||||
{
|
||||
UpdateBleedingProjSpecific(bleeding, targetLimb, deltaTime);
|
||||
}
|
||||
Color faceTint = affliction.GetFaceTint();
|
||||
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||
Color bodyTint = affliction.GetBodyTint();
|
||||
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
|
||||
Character.StackSpeedMultiplier(affliction.GetSpeedMultiplier());
|
||||
}
|
||||
}
|
||||
@@ -798,10 +794,6 @@ namespace Barotrauma
|
||||
var affliction = afflictions[i];
|
||||
affliction.Update(this, null, deltaTime);
|
||||
affliction.DamagePerSecondTimer += deltaTime;
|
||||
Color faceTint = affliction.GetFaceTint();
|
||||
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||
Color bodyTint = affliction.GetBodyTint();
|
||||
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
|
||||
Character.StackSpeedMultiplier(affliction.GetSpeedMultiplier());
|
||||
}
|
||||
|
||||
@@ -818,7 +810,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
UpdateLimbAfflictionOverlays();
|
||||
|
||||
UpdateSkinTint();
|
||||
CalculateVitality();
|
||||
|
||||
if (Vitality <= MinVitality)
|
||||
@@ -827,6 +819,32 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSkinTint()
|
||||
{
|
||||
FaceTint = DefaultFaceTint;
|
||||
BodyTint = Color.TransparentBlack;
|
||||
|
||||
for (int i = 0; i < limbHealths.Count; i++)
|
||||
{
|
||||
for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--)
|
||||
{
|
||||
var affliction = limbHealths[i].Afflictions[j];
|
||||
Color faceTint = affliction.GetFaceTint();
|
||||
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||
Color bodyTint = affliction.GetBodyTint();
|
||||
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < afflictions.Count; i++)
|
||||
{
|
||||
var affliction = afflictions[i];
|
||||
Color faceTint = affliction.GetFaceTint();
|
||||
if (faceTint.A > FaceTint.A) { FaceTint = faceTint; }
|
||||
Color bodyTint = affliction.GetBodyTint();
|
||||
if (bodyTint.A > BodyTint.A) { BodyTint = bodyTint; }
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOxygen(float deltaTime)
|
||||
{
|
||||
if (!Character.NeedsOxygen) { return; }
|
||||
@@ -905,6 +923,7 @@ namespace Barotrauma
|
||||
if (Unkillable || Character.GodMode) { return; }
|
||||
|
||||
var (type, affliction) = GetCauseOfDeath();
|
||||
UpdateSkinTint();
|
||||
Character.Kill(type, affliction);
|
||||
#if CLIENT
|
||||
DisplayVitalityDelay = 0.0f;
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace Barotrauma
|
||||
|
||||
public Vector2 StepOffset => ConvertUnits.ToSimUnits(Params.StepOffset) * ragdoll.RagdollParams.JointScale;
|
||||
|
||||
public bool inWater;
|
||||
public bool InWater { get; set; }
|
||||
|
||||
private FixedMouseJoint pullJoint;
|
||||
|
||||
@@ -535,8 +535,11 @@ namespace Barotrauma
|
||||
|
||||
public string Name => Params.Name;
|
||||
|
||||
// Exposed for status effects
|
||||
// These properties are exposed for status effects
|
||||
public bool IsDead => character.IsDead;
|
||||
public float Health => character.Health;
|
||||
public float HealthPercentage => character.HealthPercentage;
|
||||
public AIState AIState => character.AIController is EnemyAIController enemyAI ? enemyAI.State : AIState.Idle;
|
||||
|
||||
public bool CanBeSeveredAlive
|
||||
{
|
||||
@@ -804,7 +807,7 @@ namespace Barotrauma
|
||||
{
|
||||
UpdateProjSpecific(deltaTime);
|
||||
|
||||
if (inWater)
|
||||
if (InWater)
|
||||
{
|
||||
body.ApplyWaterForces();
|
||||
}
|
||||
@@ -847,20 +850,25 @@ namespace Barotrauma
|
||||
attack?.UpdateCoolDown(deltaTime);
|
||||
}
|
||||
|
||||
private bool temporarilyDisabled;
|
||||
private float reEnableTimer = -1;
|
||||
public void HideAndDisable(float duration = 0)
|
||||
public void HideAndDisable(float duration = 0, bool ignoreCollisions = true)
|
||||
{
|
||||
if (Hidden || Disabled) { return; }
|
||||
if (ignoreCollisions && IgnoreCollisions) { return; }
|
||||
temporarilyDisabled = true;
|
||||
Hidden = true;
|
||||
Disabled = true;
|
||||
IgnoreCollisions = true;
|
||||
IgnoreCollisions = ignoreCollisions;
|
||||
if (duration > 0)
|
||||
{
|
||||
reEnableTimer = duration;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReEnable()
|
||||
public void ReEnable()
|
||||
{
|
||||
if (!temporarilyDisabled) { return; }
|
||||
Hidden = false;
|
||||
Disabled = false;
|
||||
IgnoreCollisions = false;
|
||||
|
||||
+2
-2
@@ -14,9 +14,9 @@ namespace Barotrauma
|
||||
NotDefined,
|
||||
Walk,
|
||||
Run,
|
||||
Crouch,
|
||||
SwimSlow,
|
||||
SwimFast
|
||||
SwimFast,
|
||||
Crouch
|
||||
}
|
||||
|
||||
abstract class GroundedMovementParams : AnimationParams
|
||||
|
||||
@@ -79,12 +79,18 @@ namespace Barotrauma
|
||||
[Serialize(10f, true, description: "How effectively/easily the character eats other characters. Affects the forces, the amount of particles, and the time required before the target is eaten away"), Editable(MinValueFloat = 1, MaxValueFloat = 1000, ValueStep = 1)]
|
||||
public float EatingSpeed { get; set; }
|
||||
|
||||
[Serialize(true, true), Editable]
|
||||
public bool UsePathFinding { get; set; }
|
||||
|
||||
[Serialize(1f, true, "Decreases the intensive path finding call frequency. Set to a lower value for insignificant creatures to improve performance."), Editable(minValue: 0f, maxValue: 1f)]
|
||||
public float PathFinderPriority { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
public bool HideInSonar { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
public bool HideInThermalGoggles { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable]
|
||||
public float SonarDisruption { get; set; }
|
||||
|
||||
@@ -706,6 +712,9 @@ namespace Barotrauma
|
||||
[Serialize(-1f, true, description: "A generic max threshold. Not used if set to negative."), Editable]
|
||||
public float ThresholdMax { get; private set; }
|
||||
|
||||
[Serialize("0.0, 0.0", true), Editable]
|
||||
public Vector2 Offset { get; private set; }
|
||||
|
||||
[Serialize(AttackPattern.Straight, true), Editable]
|
||||
public AttackPattern AttackPattern { get; set; }
|
||||
|
||||
|
||||
@@ -597,6 +597,9 @@ namespace Barotrauma
|
||||
[Serialize(float.NaN, true, description: "The orientation of the sprite as drawn on the sprite sheet. Overrides the value defined in the Ragdoll settings."), Editable(-360, 360, ValueStep = 90, DecimalCount = 0)]
|
||||
public float SpriteOrientation { get; set; }
|
||||
|
||||
[Serialize(LimbType.None, true, description: "If set, the limb sprite will use the same sprite depth as the specified limb. Generally only useful for limbs that get added on the ragdoll on the fly (e.g. extra limbs added via gene splicing).")]
|
||||
public LimbType InheritLimbDepth { get; set; }
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 500)]
|
||||
public float SteerForce { get; set; }
|
||||
|
||||
@@ -679,6 +682,9 @@ namespace Barotrauma
|
||||
[Serialize(50f, true), Editable]
|
||||
public float BlinkForce { get; set; }
|
||||
|
||||
[Serialize(false, true), Editable]
|
||||
public bool OnlyBlinkInWater { get; set; }
|
||||
|
||||
[Serialize(TransitionMode.Linear, true), Editable]
|
||||
public TransitionMode BlinkTransitionIn { get; private set; }
|
||||
|
||||
|
||||
-13
@@ -87,19 +87,6 @@ namespace Barotrauma.Abilities
|
||||
public string String { get; set; }
|
||||
}
|
||||
|
||||
class AbilityValueStringCharacter : AbilityObject, IAbilityValue, IAbilityString, IAbilityCharacter
|
||||
{
|
||||
public AbilityValueStringCharacter(float value, string abilityString, Character character)
|
||||
{
|
||||
Value = value;
|
||||
String = abilityString;
|
||||
Character = character;
|
||||
}
|
||||
public Character Character { get; set; }
|
||||
public float Value { get; set; }
|
||||
public string String { get; set; }
|
||||
}
|
||||
|
||||
class AbilityStringCharacter : AbilityObject, IAbilityCharacter, IAbilityString
|
||||
{
|
||||
public AbilityStringCharacter(string abilityString, Character character)
|
||||
|
||||
-1
@@ -125,7 +125,6 @@ namespace Barotrauma.Abilities
|
||||
return null;
|
||||
}
|
||||
|
||||
DebugConsole.AddWarning("Instantiated " + characterAbility + " for talent " + characterAbilityGroup.CharacterTalent.DebugIdentifier);
|
||||
return characterAbility;
|
||||
}
|
||||
}
|
||||
|
||||
+34
-7
@@ -1,33 +1,60 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class CharacterAbilityApplyForce : CharacterAbility
|
||||
{
|
||||
private readonly float impulseStrength;
|
||||
private readonly float force;
|
||||
private readonly float maxVelocity;
|
||||
|
||||
private readonly string afflictionIdentifier;
|
||||
|
||||
private readonly HashSet<LimbType> limbTypes = new HashSet<LimbType>();
|
||||
|
||||
public override bool AppliesEffectOnIntervalUpdate => true;
|
||||
public CharacterAbilityApplyForce(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
impulseStrength = abilityElement.GetAttributeFloat("impulsestrength", 0f);
|
||||
force = abilityElement.GetAttributeFloat("force", 0f);
|
||||
maxVelocity = abilityElement.GetAttributeFloat("maxvelocity", 10f);
|
||||
|
||||
afflictionIdentifier = abilityElement.GetAttributeString("afflictionidentifier", "");
|
||||
|
||||
string[] limbTypesStr = abilityElement.GetAttributeStringArray("limbtypes", new string[0]);
|
||||
foreach (string limbTypeStr in limbTypesStr)
|
||||
{
|
||||
if (Enum.TryParse(limbTypeStr, out LimbType limbType))
|
||||
{
|
||||
limbTypes.Add(limbType);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - \"{limbTypeStr}\" is not a valid limb type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
Affliction affliction = Character.CharacterHealth.GetAffliction(afflictionIdentifier);
|
||||
|
||||
if (affliction == null) { return; }
|
||||
float strength = 1.0f;
|
||||
if (!string.IsNullOrEmpty(afflictionIdentifier))
|
||||
{
|
||||
Affliction affliction = Character.CharacterHealth.GetAffliction(afflictionIdentifier);
|
||||
if (affliction == null) { return; }
|
||||
strength = affliction.Strength / affliction.Prefab.MaxStrength;
|
||||
}
|
||||
|
||||
foreach (Limb limb in Character.AnimController.Limbs)
|
||||
{
|
||||
limb.body.ApplyForce(Vector2.Normalize(limb.Mass * Character.AnimController.TargetMovement) * impulseStrength * (affliction.Strength / affliction.Prefab.MaxStrength), maxVelocity);
|
||||
if (limb.IsSevered || limb.Removed) { continue; }
|
||||
if (limbTypes.Any())
|
||||
{
|
||||
if (!limbTypes.Contains(limb.type)) { continue; }
|
||||
}
|
||||
if (Character.AnimController.TargetMovement.LengthSquared() < 0.001f) { continue; }
|
||||
limb.body.ApplyForce(Vector2.Normalize(limb.Mass * Character.AnimController.TargetMovement) * force * strength, maxVelocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -8,13 +8,13 @@ namespace Barotrauma.Abilities
|
||||
class CharacterAbilityAlienHoarder : CharacterAbility
|
||||
{
|
||||
private readonly float addedDamageMultiplierPerItem;
|
||||
private readonly int maxAmount;
|
||||
private readonly float maxAddedDamageMultiplier;
|
||||
private readonly string[] tags;
|
||||
|
||||
public CharacterAbilityAlienHoarder(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
addedDamageMultiplierPerItem = abilityElement.GetAttributeFloat("addeddamagemultiplierperitem", 0f);
|
||||
maxAmount = abilityElement.GetAttributeInt("maxamount", 0);
|
||||
maxAddedDamageMultiplier = abilityElement.GetAttributeFloat("maxaddedddamagemultiplier", float.MaxValue);
|
||||
tags = abilityElement.GetAttributeStringArray("tags", Array.Empty<string>(), convertToLowerInvariant: true);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma.Abilities
|
||||
totalAddedDamageMultiplier += addedDamageMultiplierPerItem;
|
||||
}
|
||||
}
|
||||
attackData.DamageMultiplier += addedDamageMultiplierPerItem;
|
||||
attackData.DamageMultiplier += Math.Min(totalAddedDamageMultiplier, maxAddedDamageMultiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+2
-2
@@ -12,9 +12,9 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if ((abilityObject as IAbilityString)?.String is string skillIdentifier && (abilityObject as IAbilityCharacter)?.Character is Character character)
|
||||
if (abilityObject is AbilitySkillGain abilitySkillGain && !abilitySkillGain.GainedFromApprenticeship && abilitySkillGain.Character != Character)
|
||||
{
|
||||
Character.Info?.IncreaseSkillLevel(skillIdentifier, 1.0f, character.Position + Vector2.UnitY * 175.0f);
|
||||
Character.Info?.IncreaseSkillLevel(abilitySkillGain.String, 1.0f, Character.Position + Vector2.UnitY * 175.0f, gainedFromApprenticeship: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,12 +58,7 @@ namespace Barotrauma
|
||||
return handle;
|
||||
}
|
||||
|
||||
public static CoroutineHandle Invoke(Action action)
|
||||
{
|
||||
return StartCoroutine(DoInvokeAfter(action, 0.0f));
|
||||
}
|
||||
|
||||
public static CoroutineHandle InvokeAfter(Action action, float delay)
|
||||
public static CoroutineHandle Invoke(Action action, float delay = 0f)
|
||||
{
|
||||
return StartCoroutine(DoInvokeAfter(action, delay));
|
||||
}
|
||||
|
||||
@@ -845,15 +845,24 @@ namespace Barotrauma
|
||||
var character = args.Length >= 2 ? FindMatchingCharacter(args.Skip(1).ToArray()) : Character.Controlled;
|
||||
if (character != null)
|
||||
{
|
||||
character.GiveTalent(args[0]);
|
||||
TalentPrefab talentPrefab = TalentPrefab.TalentPrefabs.Find(c =>
|
||||
c.Identifier.Equals(args[0], StringComparison.OrdinalIgnoreCase) ||
|
||||
c.DisplayName.Equals(args[0], StringComparison.OrdinalIgnoreCase));
|
||||
if (talentPrefab == null)
|
||||
{
|
||||
ThrowError($"Couldn't find the talent \"{args[0]}\".");
|
||||
return;
|
||||
}
|
||||
character.GiveTalent(talentPrefab);
|
||||
NewMessage($"Gave talent \"{talentPrefab.DisplayName}\" to \"{character.Name}\".");
|
||||
}
|
||||
},
|
||||
() =>
|
||||
{
|
||||
List<string> talentNames = new List<string>();
|
||||
foreach (TalentPrefab itemPrefab in TalentPrefab.TalentPrefabs)
|
||||
foreach (TalentPrefab talent in TalentPrefab.TalentPrefabs)
|
||||
{
|
||||
talentNames.Add(itemPrefab.Identifier);
|
||||
talentNames.Add(talent.DisplayName);
|
||||
}
|
||||
|
||||
return new string[][]
|
||||
@@ -863,24 +872,15 @@ namespace Barotrauma
|
||||
};
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("unlocktalents", "unlocktalents [all/[jobname]]: give the controlled characters all the talents of the specified class", (string[] args) =>
|
||||
commands.Add(new Command("unlocktalents", "unlocktalents [all/[jobname]] [character]: give the specified character all the talents of the specified class", (string[] args) =>
|
||||
{
|
||||
if (Character.Controlled == null) { return; }
|
||||
if (args.Length == 0 || args[0].Equals("all", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
foreach (var talentTree in TalentTree.JobTalentTrees)
|
||||
{
|
||||
foreach (var subTree in talentTree.Value.TalentSubTrees)
|
||||
{
|
||||
foreach (var option in subTree.TalentOptionStages)
|
||||
{
|
||||
foreach (var talent in option.Talents)
|
||||
{
|
||||
Character.Controlled.GiveTalent(talent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var character = args.Length >= 2 ? FindMatchingCharacter(args.Skip(1).ToArray()) : Character.Controlled;
|
||||
if (character == null) { return; }
|
||||
|
||||
List<TalentTree> talentTrees = new List<TalentTree>();
|
||||
if (args.Length == 0 || args[0].Equals("all", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
talentTrees.AddRange(TalentTree.JobTalentTrees.Values);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -893,15 +893,21 @@ namespace Barotrauma
|
||||
if (!TalentTree.JobTalentTrees.TryGetValue(job.Identifier, out TalentTree talentTree))
|
||||
{
|
||||
ThrowError($"No talents configured for the job \"{args[0]}\".");
|
||||
return;
|
||||
return;
|
||||
}
|
||||
talentTrees.Add(talentTree);
|
||||
}
|
||||
|
||||
foreach (var talentTree in talentTrees)
|
||||
{
|
||||
foreach (var subTree in talentTree.TalentSubTrees)
|
||||
{
|
||||
foreach (var option in subTree.TalentOptionStages)
|
||||
{
|
||||
foreach (var talent in option.Talents)
|
||||
{
|
||||
Character.Controlled.GiveTalent(talent);
|
||||
character.GiveTalent(talent);
|
||||
NewMessage($"Unlocked talent \"{talent.DisplayName}\".");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -913,7 +919,8 @@ namespace Barotrauma
|
||||
availableArgs.AddRange(JobPrefab.Prefabs.Select(j => j.Name));
|
||||
return new string[][]
|
||||
{
|
||||
availableArgs.ToArray()
|
||||
availableArgs.ToArray(),
|
||||
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
|
||||
};
|
||||
}, isCheat: true));
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class GodModeAction : EventAction
|
||||
{
|
||||
[Serialize(true, true)]
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
[Serialize("", true)]
|
||||
public string TargetTag { get; set; }
|
||||
|
||||
public GodModeAction(ScriptedEvent parentEvent, XElement element) : base(parentEvent, element) { }
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
public override bool IsFinished(ref string goTo)
|
||||
{
|
||||
return isFinished;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
isFinished = false;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished) { return; }
|
||||
var targets = ParentEvent.GetTargets(TargetTag);
|
||||
foreach (var target in targets)
|
||||
{
|
||||
if (target != null && target is Character character)
|
||||
{
|
||||
character.GodMode = Enabled;
|
||||
}
|
||||
}
|
||||
isFinished = true;
|
||||
}
|
||||
|
||||
public override string ToDebugString()
|
||||
{
|
||||
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(GodModeAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
|
||||
(Enabled ? "Enable godmode" : "Disable godmode");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,7 +91,7 @@ namespace Barotrauma
|
||||
int amount = Rand.Range(monsterCountRange.X, monsterCountRange.Y + 1);
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
//round ended before the coroutine finished
|
||||
if (GameMain.GameSession == null || Level.Loaded == null) { return; }
|
||||
|
||||
@@ -359,10 +359,19 @@ namespace Barotrauma
|
||||
crewCharacters.ForEach(c => c.CheckTalents(AbilityEffectType.OnAllyGainMissionExperience, experienceGainMultiplier));
|
||||
crewCharacters.ForEach(c => experienceGainMultiplier.Value += c.GetStatValue(StatTypes.MissionExperienceGainMultiplier));
|
||||
|
||||
int experienceGain = (int)(baseExperienceGain * experienceGainMultiplier.Value);
|
||||
#if CLIENT
|
||||
foreach (Character character in crewCharacters)
|
||||
{
|
||||
character.Info.GiveExperience((int)(baseExperienceGain * experienceGainMultiplier.Value), isMissionExperience: true);
|
||||
character.Info.GiveExperience(experienceGain, isMissionExperience: true);
|
||||
}
|
||||
#else
|
||||
foreach (Barotrauma.Networking.Client c in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
//give the experience to the stored characterinfo if the client isn't currently controlling a character
|
||||
(c.Character?.Info ?? c.CharacterInfo)?.GiveExperience(experienceGain, isMissionExperience: true);
|
||||
}
|
||||
#endif
|
||||
|
||||
// apply money gains afterwards to prevent them from affecting XP gains
|
||||
var moneyGainMission = new AbilityValueMission(1f, this);
|
||||
|
||||
@@ -21,10 +21,11 @@ namespace Barotrauma
|
||||
private bool disallowed;
|
||||
|
||||
private readonly Level.PositionType spawnPosType;
|
||||
private readonly string spawnPointTag;
|
||||
|
||||
private bool spawnPending;
|
||||
|
||||
private int maxAmountPerLevel = int.MaxValue;
|
||||
private readonly int maxAmountPerLevel = int.MaxValue;
|
||||
|
||||
public List<Character> Monsters => monsters;
|
||||
public Vector2? SpawnPos => spawnPos;
|
||||
@@ -87,6 +88,8 @@ namespace Barotrauma
|
||||
spawnPosType = Level.PositionType.Abyss;
|
||||
}
|
||||
|
||||
spawnPointTag = prefab.ConfigElement.GetAttributeString("spawnpointtag", string.Empty);
|
||||
|
||||
offset = prefab.ConfigElement.GetAttributeFloat("offset", 0);
|
||||
scatter = Math.Clamp(prefab.ConfigElement.GetAttributeFloat("scatter", 500), 0, 3000);
|
||||
|
||||
@@ -285,18 +288,19 @@ namespace Barotrauma
|
||||
spawnPos = chosenPosition.Position.ToVector2();
|
||||
if (chosenPosition.Submarine != null || chosenPosition.Ruin != null)
|
||||
{
|
||||
var spawnPoint = WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine ?? chosenPosition.Ruin?.Submarine, useSyncedRand: false);
|
||||
if (spawnPoint != null)
|
||||
var spawnPoint =
|
||||
WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine ?? chosenPosition.Ruin?.Submarine, useSyncedRand: false, spawnPointTag: spawnPointTag);
|
||||
if (spawnPoint != null)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(spawnPoint.Submarine == (chosenPosition.Submarine ?? chosenPosition.Ruin?.Submarine));
|
||||
spawnPos = spawnPoint.WorldPosition;
|
||||
spawnPos = spawnPoint.WorldPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
//no suitable position found, disable the event
|
||||
spawnPos = null;
|
||||
Finished();
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((chosenPosition.PositionType == Level.PositionType.MainPath || chosenPosition.PositionType == Level.PositionType.SidePath)
|
||||
@@ -447,7 +451,7 @@ namespace Barotrauma
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
string seed = Level.Loaded.Seed + i.ToString();
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
CoroutineManager.Invoke(() =>
|
||||
{
|
||||
//round ended before the coroutine finished
|
||||
if (GameMain.GameSession == null || Level.Loaded == null) { return; }
|
||||
|
||||
@@ -10,10 +10,15 @@ namespace Barotrauma
|
||||
{
|
||||
public static bool OutputDebugInfo = false;
|
||||
|
||||
/// <summary>
|
||||
/// If we are spawning in an area where difficulty should not be a factor, assume difficulty is at the exact "middle"
|
||||
/// </summary>
|
||||
public const float DefaultDifficultyModifier = 0f;
|
||||
|
||||
public static void PlaceIfNeeded()
|
||||
{
|
||||
if (GameMain.NetworkMember != null && !GameMain.NetworkMember.IsServer) { return; }
|
||||
|
||||
|
||||
for (int i = 0; i < Submarine.MainSubs.Length; i++)
|
||||
{
|
||||
if (Submarine.MainSubs[i] == null || Submarine.MainSubs[i].Info.InitialSuppliesSpawned) { continue; }
|
||||
@@ -23,6 +28,7 @@ namespace Barotrauma
|
||||
subs.ForEach(s => s.Info.InitialSuppliesSpawned = true);
|
||||
}
|
||||
|
||||
float difficultyModifier = GetLevelDifficultyModifier();
|
||||
foreach (var sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub.Info.Type == SubmarineType.Player ||
|
||||
@@ -32,7 +38,7 @@ namespace Barotrauma
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Place(sub.ToEnumerable());
|
||||
Place(sub.ToEnumerable(), difficultyModifier: difficultyModifier);
|
||||
}
|
||||
|
||||
if (Level.Loaded?.StartOutpost != null && Level.Loaded.Type == LevelData.LevelType.Outpost)
|
||||
@@ -42,12 +48,23 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegenerateLoot(Submarine sub, ItemContainer regeneratedContainer)
|
||||
private const float MaxDifficultyModifier = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// Spawn probability of loot is modified by difficulty, -20% less loot at 0% difficulty and +20% loot at 100% difficulty.
|
||||
/// </summary>
|
||||
private static float GetLevelDifficultyModifier()
|
||||
{
|
||||
Place(sub.ToEnumerable(), regeneratedContainer);
|
||||
return Math.Clamp(Level.Loaded?.Difficulty is float difficulty ? (difficulty / 100f) * (MaxDifficultyModifier * 2) - MaxDifficultyModifier : DefaultDifficultyModifier, -MaxDifficultyModifier, MaxDifficultyModifier);
|
||||
}
|
||||
|
||||
private static void Place(IEnumerable<Submarine> subs, ItemContainer regeneratedContainer = null)
|
||||
public static void RegenerateLoot(Submarine sub, ItemContainer regeneratedContainer)
|
||||
{
|
||||
// Level difficulty currently doesn't affect regenerated loot for the sake of simplicity
|
||||
Place(sub.ToEnumerable(), regeneratedContainer: regeneratedContainer);
|
||||
}
|
||||
|
||||
private static void Place(IEnumerable<Submarine> subs, ItemContainer regeneratedContainer = null, float difficultyModifier = DefaultDifficultyModifier)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
@@ -167,7 +184,7 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (var validContainer in validContainers)
|
||||
{
|
||||
var newItems = SpawnItem(itemPrefab, containers, validContainer);
|
||||
var newItems = SpawnItem(itemPrefab, containers, validContainer, difficultyModifier);
|
||||
if (newItems.Any())
|
||||
{
|
||||
spawnedItems.AddRange(newItems);
|
||||
@@ -201,11 +218,18 @@ namespace Barotrauma
|
||||
return validContainers;
|
||||
}
|
||||
|
||||
private static List<Item> SpawnItem(ItemPrefab itemPrefab, List<ItemContainer> containers, KeyValuePair<ItemContainer, PreferredContainer> validContainer)
|
||||
private static readonly float[] qualityCommonnesses = new float[Quality.MaxQuality + 1]
|
||||
{
|
||||
0.85f,
|
||||
0.125f,
|
||||
0.0225f,
|
||||
0.0025f,
|
||||
};
|
||||
|
||||
private static List<Item> SpawnItem(ItemPrefab itemPrefab, List<ItemContainer> containers, KeyValuePair<ItemContainer, PreferredContainer> validContainer, float difficultyModifier)
|
||||
{
|
||||
List<Item> spawnedItems = new List<Item>();
|
||||
bool success = false;
|
||||
if (Rand.Value(Rand.RandSync.Server) > validContainer.Value.SpawnProbability) { return spawnedItems; }
|
||||
if (Rand.Value(Rand.RandSync.Server) > validContainer.Value.SpawnProbability * (1f + difficultyModifier)) { return spawnedItems; }
|
||||
// Don't add dangerously reactive materials in thalamus wrecks
|
||||
if (validContainer.Key.Item.Submarine.WreckAI != null && itemPrefab.Tags.Contains("explodesinwater"))
|
||||
{
|
||||
@@ -220,10 +244,24 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
if (!validContainer.Key.Inventory.CanBePut(itemPrefab)) { break; }
|
||||
|
||||
int quality = 0;
|
||||
float qualityCommmonnessSum = qualityCommonnesses.Sum();
|
||||
float randomNumber = Rand.Range(0f, qualityCommmonnessSum, Rand.RandSync.Server);
|
||||
for (int k = qualityCommonnesses.Length - 1; k >= 0; k--)
|
||||
{
|
||||
if (randomNumber < qualityCommonnesses[k])
|
||||
{
|
||||
quality = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var item = new Item(itemPrefab, validContainer.Key.Item.Position, validContainer.Key.Item.Submarine)
|
||||
{
|
||||
SpawnedInOutpost = validContainer.Key.Item.SpawnedInOutpost,
|
||||
AllowStealing = validContainer.Key.Item.AllowStealing,
|
||||
Quality = quality,
|
||||
OriginalModuleIndex = validContainer.Key.Item.OriginalModuleIndex,
|
||||
OriginalContainerIndex =
|
||||
Item.ItemList.Where(it => it.Submarine == validContainer.Key.Item.Submarine && it.OriginalModuleIndex == validContainer.Key.Item.OriginalModuleIndex).ToList().IndexOf(validContainer.Key.Item)
|
||||
@@ -235,7 +273,6 @@ namespace Barotrauma
|
||||
spawnedItems.Add(item);
|
||||
validContainer.Key.Inventory.TryPutItem(item, null, createNetworkEvent: false);
|
||||
containers.AddRange(item.GetComponents<ItemContainer>());
|
||||
success = true;
|
||||
}
|
||||
return spawnedItems;
|
||||
}
|
||||
|
||||
@@ -563,13 +563,16 @@ namespace Barotrauma
|
||||
public override void End(CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
|
||||
{
|
||||
List<Item> takenItems = new List<Item>();
|
||||
foreach (Item item in Item.ItemList)
|
||||
if (Level.Loaded?.Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
if (!item.SpawnedInOutpost || item.OriginalModuleIndex < 0) { continue; }
|
||||
var owner = item.GetRootInventoryOwner();
|
||||
if ((!(owner?.Submarine?.Info?.IsOutpost ?? false)) || (owner is Character character && character.TeamID == CharacterTeamType.Team1) || item.Submarine == null || !item.Submarine.Info.IsOutpost)
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
takenItems.Add(item);
|
||||
if (!item.SpawnedInOutpost || item.OriginalModuleIndex < 0) { continue; }
|
||||
var owner = item.GetRootInventoryOwner();
|
||||
if ((!(owner?.Submarine?.Info?.IsOutpost ?? false)) || (owner is Character character && character.TeamID == CharacterTeamType.Team1) || item.Submarine == null || !item.Submarine.Info.IsOutpost)
|
||||
{
|
||||
takenItems.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (map != null && CargoManager != null)
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
internal partial class EntitySpawnerComponent : ItemComponent, IDrawableComponent
|
||||
{
|
||||
public enum AreaShape
|
||||
{
|
||||
Rectangle,
|
||||
Circle
|
||||
}
|
||||
|
||||
[Editable, Serialize("", true, "Identifier of the item to spawn, does nothing if SpeciesName is set. Separate by comma to have multiple items spawn at random.")]
|
||||
public string? ItemIdentifier { get; set; }
|
||||
|
||||
[Editable, Serialize("", true, "Species name of the creature to spawn, takes priority if ItemIdentifier is set. Separate by comma to have multiple creatures spawn at random.")]
|
||||
public string? SpeciesName { get; set; }
|
||||
|
||||
[Editable, Serialize(true, true, "Only spawn if crew members are within certain area")]
|
||||
public bool OnlySpawnWhenCrewInRange { get; set; }
|
||||
|
||||
[Editable, Serialize(AreaShape.Rectangle, true, "Shape of the area where crew members need to stay")]
|
||||
public AreaShape CrewAreaShape { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize("500,500", true, "Size of the rectangle where crew members need to stay. Does nothing if CrewAreaShape is set to Circle")]
|
||||
public Vector2 CrewAreaBounds { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, true, "Radius of the circle to spawn stuff in. Does nothing if CrewAreaShape is set to Rectangle")]
|
||||
public float CrewAreaRadius { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", true, "Offset of the crew area from the center of the item")]
|
||||
public Vector2 CrewAreaOffset { get; set; }
|
||||
|
||||
[Editable, Serialize(AreaShape.Rectangle, true, "Shape of the area where enemies or items are spawned")]
|
||||
public AreaShape SpawnAreaShape { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize("500,500", true, "Size of the rectangle where items or creatures will be spawned. Does nothing if SpawnAreaShape is set to Circle")]
|
||||
public Vector2 SpawnAreaBounds { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, true, "Radius of the circle where items or creatures will be spawned. Does nothing if SpawnAreaShape is set to Rectangle")]
|
||||
public float SpawnAreaRadius { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", true, "Offset of the spawn area from the center of the item")]
|
||||
public Vector2 SpawnAreaOffset { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 1f), Serialize("10,40", true, "Time range between spawn attempts in seconds")]
|
||||
public Vector2 SpawnTimerRange { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 1f, ValueStep = 1f, DecimalCount = 0), Serialize("1,3", true, "Minumum and maximum amount of items or creatures to spawn in one attempt")]
|
||||
public Vector2 SpawnAmountRange { get; set; }
|
||||
|
||||
[Editable(MinValueInt = 0), Serialize(8, true, "Amount of items or creatures in the spawn area that will prevent further items or creatures from being spawned")]
|
||||
public int MaximumAmount { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize(500f, true, "Inflate the circle of rectangle by this value to extend the area that counts towards the maximum amount of items or enemies to be spawned")]
|
||||
public float MaximumAmountRangePadding { get; set; }
|
||||
|
||||
[Serialize(true, true, "")]
|
||||
public bool CanSpawn { get; set; } = true;
|
||||
|
||||
private float SpawnTimer;
|
||||
private float? SpawnTimerGoal;
|
||||
|
||||
public EntitySpawnerComponent(Item item, XElement element) : base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ItemIdentifier))
|
||||
{
|
||||
string[] allItems = ItemIdentifier.Split(',');
|
||||
foreach (string itemIdentifier in allItems)
|
||||
{
|
||||
string trimmedString = itemIdentifier.Trim();
|
||||
|
||||
bool found = false;
|
||||
|
||||
foreach (ItemPrefab prefab in ItemPrefab.Prefabs)
|
||||
{
|
||||
if (string.Equals(trimmedString, prefab.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error loading {nameof(EntitySpawnerComponent)} - item prefab \"" + name + "\" (identifier \"" + trimmedString + "\") not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.OnItemLoaded();
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
item.SendSignal(CanSpawn ? "1" : "0", "state_out");
|
||||
|
||||
if (GameMain.NetworkMember is { IsClient: true }) { return; }
|
||||
|
||||
SpawnTimerGoal ??= Rand.Range(Math.Min(SpawnTimerRange.X, SpawnTimerRange.Y), Math.Max(SpawnTimerRange.X, SpawnTimerRange.Y), Rand.RandSync.Unsynced);
|
||||
|
||||
SpawnTimer += deltaTime;
|
||||
|
||||
if (SpawnTimer > SpawnTimerGoal)
|
||||
{
|
||||
Spawn();
|
||||
SpawnTimerGoal = null;
|
||||
SpawnTimer = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
{
|
||||
bool isNonZero = signal.value != "0";
|
||||
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "set_state":
|
||||
CanSpawn = isNonZero;
|
||||
break;
|
||||
case "toggle" when isNonZero:
|
||||
CanSpawn = !CanSpawn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private RectangleF GetAreaRectangle(Vector2 size, Vector2 offset, bool draw)
|
||||
{
|
||||
Vector2 pos = item.WorldPosition;
|
||||
if (draw)
|
||||
{
|
||||
pos.Y = -pos.Y;
|
||||
}
|
||||
|
||||
pos += offset;
|
||||
RectangleF rect = new RectangleF(pos.X - size.X / 2f, pos.Y - size.Y / 2f, size.X, size.Y);
|
||||
return rect;
|
||||
}
|
||||
|
||||
private bool CanSpawnMore()
|
||||
{
|
||||
if (!CanSpawn) { return false; }
|
||||
|
||||
if (OnlySpawnWhenCrewInRange)
|
||||
{
|
||||
if (!Character.CharacterList.Any(c => !c.IsDead && c.IsOnPlayerTeam && IsInRange(c.WorldPosition, crewArea: true, rangePad: false)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int amount;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(SpeciesName))
|
||||
{
|
||||
amount = Character.CharacterList.Count(c => !c.IsDead && c.SpeciesName.Equals(SpeciesName, StringComparison.OrdinalIgnoreCase) && IsInRange(c.WorldPosition, crewArea: false, rangePad: true));
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(ItemIdentifier))
|
||||
{
|
||||
amount = Item.ItemList.Count(it => it.Submarine == item.Submarine && it.Prefab.Identifier.Equals(ItemIdentifier, StringComparison.OrdinalIgnoreCase) && IsInRange(it.WorldPosition, crewArea: false, rangePad: true));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return amount < MaximumAmount;
|
||||
}
|
||||
|
||||
private bool IsInRange(Vector2 worldPos, bool crewArea = false, bool rangePad = false)
|
||||
{
|
||||
Vector2 offset = crewArea ? CrewAreaOffset : SpawnAreaOffset;
|
||||
offset.Y = -offset.Y;
|
||||
switch (crewArea ? CrewAreaShape : SpawnAreaShape)
|
||||
{
|
||||
case AreaShape.Circle:
|
||||
Vector2 center = item.WorldPosition + offset;
|
||||
float distance = (crewArea ? CrewAreaRadius : SpawnAreaRadius) + (rangePad ? MaximumAmountRangePadding : 0);
|
||||
return Vector2.DistanceSquared(worldPos, center) < distance * distance;
|
||||
|
||||
case AreaShape.Rectangle:
|
||||
RectangleF rect = GetAreaRectangle(crewArea ? CrewAreaBounds : SpawnAreaBounds, offset, draw: false);
|
||||
if (rangePad)
|
||||
{
|
||||
rect.Inflate(MaximumAmountRangePadding, MaximumAmountRangePadding);
|
||||
}
|
||||
|
||||
return rect.Contains(worldPos);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Spawn()
|
||||
{
|
||||
if (!CanSpawnMore()) { return; }
|
||||
|
||||
int minAmount = Math.Min((int)SpawnAmountRange.X, (int)SpawnAmountRange.Y),
|
||||
maxAmount = Math.Max((int)SpawnAmountRange.X, (int)SpawnAmountRange.Y);
|
||||
|
||||
int amount = Rand.Range(minAmount, maxAmount, Rand.RandSync.Unsynced);
|
||||
|
||||
Vector2 offset = SpawnAreaOffset;
|
||||
offset.Y = -offset.Y;
|
||||
|
||||
switch (SpawnAreaShape)
|
||||
{
|
||||
case AreaShape.Circle:
|
||||
{
|
||||
var (x, y) = item.WorldPosition + offset;
|
||||
|
||||
for (int i = 0; i < Math.Max(1, amount); i++)
|
||||
{
|
||||
float angle = Rand.Range(-MathHelper.TwoPi, MathHelper.TwoPi);
|
||||
float distance = Rand.Range(0, SpawnAreaRadius, Rand.RandSync.Unsynced);
|
||||
Vector2 spawnPos = new Vector2(x + distance * (float)Math.Cos(angle), y + distance * (float)Math.Sin(angle));
|
||||
|
||||
SpawnEntity(spawnPos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AreaShape.Rectangle:
|
||||
{
|
||||
RectangleF rect = GetAreaRectangle(SpawnAreaBounds, offset, draw: false);
|
||||
|
||||
for (int i = 0; i < Math.Max(1, amount); i++)
|
||||
{
|
||||
float minX = Math.Min(rect.Left, rect.Right),
|
||||
maxX = Math.Max(rect.Left, rect.Right),
|
||||
minY = Math.Min(rect.Top, rect.Bottom),
|
||||
maxY = Math.Max(rect.Top, rect.Bottom);
|
||||
|
||||
Vector2 spawnPos = new Vector2(Rand.Range(minX, maxX, Rand.RandSync.Unsynced), Rand.Range(minY, maxY, Rand.RandSync.Unsynced));
|
||||
|
||||
SpawnEntity(spawnPos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnEntity(Vector2 pos)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(SpeciesName))
|
||||
{
|
||||
string[] allSpecies = SpeciesName.Split(',');
|
||||
string species = allSpecies.GetRandom().Trim();
|
||||
Entity.Spawner.AddToSpawnQueue(species, pos);
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(ItemIdentifier))
|
||||
{
|
||||
string[] allItems = ItemIdentifier.Split(',');
|
||||
string itemIdentifier = allItems.GetRandom().Trim();
|
||||
ItemPrefab? prefab = ItemPrefab.Find(null, itemIdentifier);
|
||||
if (prefab is null) { return; }
|
||||
|
||||
if (item.Submarine is { } sub)
|
||||
{
|
||||
pos -= sub.Position;
|
||||
}
|
||||
|
||||
Entity.Spawner.AddToSpawnQueue(prefab, pos, item.Submarine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -598,9 +598,12 @@ namespace Barotrauma.Items.Components
|
||||
DebugConsole.AddWarning("Character without CharacterInfo attempting to attach a limited attachable item!");
|
||||
return false;
|
||||
}
|
||||
Vector2 attachPos = GetAttachPosition(character, useWorldCoordinates: true);
|
||||
Structure attachTarget = Structure.GetAttachTarget(attachPos);
|
||||
|
||||
int maxAttachableCount = (int)character.Info.GetSavedStatValue(StatTypes.MaxAttachableCount, item.Prefab.Identifier);
|
||||
int currentlyAttachedCount = Item.ItemList.Count(
|
||||
i => i.Submarine == item.Submarine && i.GetComponent<Holdable>() is Holdable holdable && holdable.Attached && i.Prefab.Identifier == item.prefab.Identifier);
|
||||
i => i.Submarine == attachTarget?.Submarine && i.GetComponent<Holdable>() is Holdable holdable && holdable.Attached && i.Prefab.Identifier == item.prefab.Identifier);
|
||||
if (currentlyAttachedCount >= maxAttachableCount)
|
||||
{
|
||||
#if CLIENT
|
||||
|
||||
@@ -77,6 +77,7 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
}
|
||||
item.IsShootable = true;
|
||||
item.RequireAimToUse = element.Parent.GetAttributeBool("requireaimtouse", true);
|
||||
PreferredContainedItems = element.GetAttributeStringArray("preferredcontaineditems", new string[0], convertToLowerInvariant: true);
|
||||
}
|
||||
|
||||
@@ -210,7 +211,7 @@ namespace Barotrauma.Items.Components
|
||||
bool aim = item.RequireAimToUse && picker.AllowInput && picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && picker.CanAim;
|
||||
if (aim)
|
||||
{
|
||||
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 5f, MathHelper.PiOver4));
|
||||
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 3f, MathHelper.PiOver4));
|
||||
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, aim: false, hitPos, holdAngle + hitPos, aimMelee: true);
|
||||
}
|
||||
else
|
||||
@@ -222,16 +223,16 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
// TODO: We might want to make this configurable
|
||||
hitPos = MathUtils.WrapAnglePi(hitPos - deltaTime * 15f);
|
||||
hitPos -= deltaTime * 15f;
|
||||
if (Swing)
|
||||
{
|
||||
ac.HoldItem(deltaTime, item, handlePos, SwingPos, Vector2.Zero, aim: false, hitPos, holdAngle + hitPos);
|
||||
ac.HoldItem(deltaTime, item, handlePos, SwingPos, Vector2.Zero, aim: false, hitPos, holdAngle);
|
||||
}
|
||||
else
|
||||
{
|
||||
ac.HoldItem(deltaTime, item, handlePos, holdPos, Vector2.Zero, aim: false, holdAngle);
|
||||
}
|
||||
if (hitPos < -MathHelper.PiOver2)
|
||||
if (hitPos < -MathHelper.Pi)
|
||||
{
|
||||
RestoreCollision();
|
||||
hitting = false;
|
||||
|
||||
@@ -74,8 +74,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation);
|
||||
Vector2 flippedPos = barrelPos;
|
||||
if (item.body.Dir < 0.0f) flippedPos.X = -flippedPos.X;
|
||||
return Vector2.Transform(flippedPos, bodyTransform);
|
||||
if (item.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; }
|
||||
return Vector2.Transform(flippedPos, bodyTransform) * item.Scale;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -712,7 +712,7 @@ namespace Barotrauma.Items.Components
|
||||
humanAnim.Crouching = true;
|
||||
}
|
||||
}
|
||||
if (dist > reach * 0.8f || dist > reach * 0.5f && character.AnimController.Limbs.Any(l => l.inWater))
|
||||
if (dist > reach * 0.8f || dist > reach * 0.5f && character.AnimController.Limbs.Any(l => l.InWater))
|
||||
{
|
||||
// Steer closer
|
||||
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
|
||||
|
||||
@@ -522,7 +522,7 @@ namespace Barotrauma.Items.Components
|
||||
if (Math.Abs(item.Rotation) > 0.01f)
|
||||
{
|
||||
Matrix transform = Matrix.CreateRotationZ(MathHelper.ToRadians(-item.Rotation));
|
||||
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
|
||||
transformedItemPos = Vector2.Transform(transformedItemPos - item.Position, transform) + item.Position;
|
||||
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
|
||||
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
|
||||
transformedItemIntervalVertical = Vector2.Transform(transformedItemIntervalVertical, transform);
|
||||
@@ -550,6 +550,10 @@ namespace Barotrauma.Items.Components
|
||||
currentRotation *= item.body.Dir;
|
||||
currentRotation += item.body.Rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRotation += MathHelper.ToRadians(-item.Rotation);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
Vector2 currentItemPos = transformedItemPos;
|
||||
|
||||
@@ -64,13 +64,6 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable hull condition mode.")]
|
||||
public bool EnableHullCondition
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable item finder mode.")]
|
||||
public bool EnableItemFinder
|
||||
{
|
||||
@@ -148,30 +141,49 @@ namespace Barotrauma.Items.Components
|
||||
hullDatas.Add(sourceHull, hullData);
|
||||
}
|
||||
|
||||
if (hullData.Distort) return;
|
||||
if (hullData.Distort) { return; }
|
||||
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "water_data_in":
|
||||
//cheating a bit because water detectors don't actually send the water level
|
||||
float waterAmount;
|
||||
if (source.GetComponent<WaterDetector>() == null)
|
||||
{
|
||||
hullData.ReceivedWaterAmount = Rand.Range(0.0f, 1.0f);
|
||||
waterAmount = Rand.Range(0.0f, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
hullData.ReceivedWaterAmount = Math.Min(sourceHull.WaterVolume / sourceHull.Volume, 1.0f);
|
||||
waterAmount = Math.Min(sourceHull.WaterVolume / sourceHull.Volume, 1.0f);
|
||||
}
|
||||
hullData.ReceivedWaterAmount = waterAmount;
|
||||
foreach (var linked in sourceHull.linkedTo)
|
||||
{
|
||||
if (!(linked is Hull linkedHull)) { continue; }
|
||||
if (!hullDatas.TryGetValue(linkedHull, out HullData linkedHullData))
|
||||
{
|
||||
linkedHullData = new HullData();
|
||||
hullDatas.Add(linkedHull, linkedHullData);
|
||||
}
|
||||
linkedHullData.ReceivedWaterAmount = waterAmount;
|
||||
}
|
||||
break;
|
||||
case "oxygen_data_in":
|
||||
float oxy;
|
||||
|
||||
if (!float.TryParse(signal.value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out oxy))
|
||||
if (!float.TryParse(signal.value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float oxy))
|
||||
{
|
||||
oxy = Rand.Range(0.0f, 100.0f);
|
||||
}
|
||||
|
||||
hullData.ReceivedOxygenAmount = oxy;
|
||||
foreach (var linked in sourceHull.linkedTo)
|
||||
{
|
||||
if (!(linked is Hull linkedHull)) { continue; }
|
||||
if (!hullDatas.TryGetValue(linkedHull, out HullData linkedHullData))
|
||||
{
|
||||
linkedHullData = new HullData();
|
||||
hullDatas.Add(linkedHull, linkedHullData);
|
||||
}
|
||||
linkedHullData.ReceivedOxygenAmount = oxy;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
public const int MaxQuality = 3;
|
||||
|
||||
public static readonly float[] QualityCommonnesses = new float[]
|
||||
{
|
||||
0.8f,
|
||||
0.15f,
|
||||
0.045f,
|
||||
0.005f,
|
||||
};
|
||||
|
||||
public enum StatType
|
||||
{
|
||||
Condition,
|
||||
@@ -39,7 +47,18 @@ namespace Barotrauma.Items.Components
|
||||
public int QualityLevel
|
||||
{
|
||||
get { return qualityLevel; }
|
||||
set { qualityLevel = MathHelper.Clamp(value, 0, MaxQuality); }
|
||||
set
|
||||
{
|
||||
if (value == qualityLevel) { return; }
|
||||
|
||||
bool wasInFullCondition = item.IsFullCondition;
|
||||
qualityLevel = MathHelper.Clamp(value, 0, MaxQuality);
|
||||
//set the condition to the new max condition
|
||||
if (wasInFullCondition && statValues.ContainsKey(StatType.Condition))
|
||||
{
|
||||
item.Condition = item.MaxCondition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Quality(Item item, XElement element) : base(item, element)
|
||||
|
||||
@@ -376,7 +376,7 @@ namespace Barotrauma.Items.Components
|
||||
tinkeringDuration -= deltaTime;
|
||||
// not great to interject it here, should be less reliant on returning
|
||||
|
||||
float conditionDecrease = deltaTime * (CurrentFixer.GetStatValue(StatTypes.TinkeringDamage) / item.MaxCondition) * 100f;
|
||||
float conditionDecrease = deltaTime * (CurrentFixer.GetStatValue(StatTypes.TinkeringDamage) / item.Prefab.Health) * 100f;
|
||||
item.Condition -= conditionDecrease;
|
||||
|
||||
if (!CanTinker(CurrentFixer) || tinkeringDuration <= 0f)
|
||||
@@ -424,7 +424,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
float conditionIncrease = deltaTime / (fixDuration / item.MaxCondition);
|
||||
// scale with prefab's health instead of real health to ensure repair speed remains static with upgrades
|
||||
float conditionIncrease = deltaTime / (fixDuration / item.Prefab.Health);
|
||||
item.Condition += conditionIncrease;
|
||||
#if SERVER
|
||||
GameMain.Server.KarmaManager.OnItemRepaired(CurrentFixer, this, conditionIncrease);
|
||||
@@ -458,7 +459,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
float conditionDecrease = deltaTime / (fixDuration / item.MaxCondition);
|
||||
// scale with prefab's health instead of real health to ensure sabotage speed remains static with (any) upgrades
|
||||
float conditionDecrease = deltaTime / (fixDuration / item.Prefab.Health);
|
||||
item.Condition -= conditionDecrease;
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +159,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
lightColor = value;
|
||||
#if CLIENT
|
||||
if (Light != null) Light.Color = IsActive ? lightColor : Color.Transparent;
|
||||
if (Light != null)
|
||||
{
|
||||
Light.Color = IsActive ? lightColor : Color.Transparent;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace Barotrauma.Items.Components
|
||||
//signal strength diminishes by distance
|
||||
float sentSignalStrength = signal.strength *
|
||||
MathHelper.Clamp(1.0f - (Vector2.Distance(item.WorldPosition, wifiComp.item.WorldPosition) / wifiComp.range), 0.0f, 1.0f);
|
||||
Signal s = new Signal(signal.value, ++signal.stepsTaken, sender: signal.sender, source: signal.source,
|
||||
Signal s = new Signal(signal.value, signal.stepsTaken + 1, sender: signal.sender, source: signal.source,
|
||||
power: 0.0f, strength: sentSignalStrength);
|
||||
|
||||
if (wifiComp.signalOutConnection != null)
|
||||
|
||||
@@ -48,8 +48,8 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (items[0].Prefab.Identifier != item.Prefab.Identifier ||
|
||||
items.Count + 1 > item.Prefab.MaxStackSize)
|
||||
if (items[0].Quality != item.Quality) { return false; }
|
||||
if (items[0].Prefab.Identifier != item.Prefab.Identifier || items.Count + 1 > item.Prefab.MaxStackSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Barotrauma
|
||||
//a dictionary containing lists of the status effects in all the components of the item
|
||||
private readonly bool[] hasStatusEffectsOfType;
|
||||
private readonly Dictionary<ActionType, List<StatusEffect>> statusEffectLists;
|
||||
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; protected set; }
|
||||
|
||||
private bool? hasInGameEditableProperties;
|
||||
@@ -232,7 +232,7 @@ namespace Barotrauma
|
||||
public bool IsInteractable(Character character)
|
||||
{
|
||||
if (character != null && character.IsOnPlayerTeam)
|
||||
{
|
||||
{
|
||||
return IsPlayerTeamInteractable;
|
||||
}
|
||||
else
|
||||
@@ -254,6 +254,12 @@ namespace Barotrauma
|
||||
{
|
||||
if (!Prefab.AllowRotatingInEditor) { return; }
|
||||
rotationRad = MathHelper.ToRadians(value);
|
||||
#if CLIENT
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
SetContainedItemPositions();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,7 +484,7 @@ namespace Barotrauma
|
||||
get => maxRepairConditionMultiplier;
|
||||
set { maxRepairConditionMultiplier = MathHelper.Clamp(value, 0.0f, float.PositiveInfinity); }
|
||||
}
|
||||
|
||||
|
||||
//the default value should be Prefab.Health, but because we can't use it in the attribute,
|
||||
//we'll just use NaN (which does nothing) and set the default value in the constructor/load
|
||||
[Serialize(float.NaN, false), Editable]
|
||||
@@ -628,9 +634,9 @@ namespace Barotrauma
|
||||
|
||||
public int Quality
|
||||
{
|
||||
get
|
||||
{
|
||||
return qualityComponent?.QualityLevel ?? 0;
|
||||
get
|
||||
{
|
||||
return qualityComponent?.QualityLevel ?? 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -1155,7 +1161,7 @@ namespace Barotrauma
|
||||
{
|
||||
return GetComponent<Quality>()?.GetValue(statType) ?? 0.0f;
|
||||
}
|
||||
|
||||
|
||||
public void RemoveContained(Item contained)
|
||||
{
|
||||
ownInventory?.RemoveItem(contained);
|
||||
@@ -1867,7 +1873,8 @@ namespace Barotrauma
|
||||
foreach (ItemComponent component in components)
|
||||
{
|
||||
component.FlipX(relativeToSub);
|
||||
}
|
||||
}
|
||||
SetContainedItemPositions();
|
||||
}
|
||||
|
||||
public override void FlipY(bool relativeToSub)
|
||||
@@ -1892,6 +1899,7 @@ namespace Barotrauma
|
||||
{
|
||||
component.FlipY(relativeToSub);
|
||||
}
|
||||
SetContainedItemPositions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2112,8 +2120,6 @@ namespace Barotrauma
|
||||
} while (CoroutineManager.DeltaTime <= 0.0f);
|
||||
|
||||
delayedSignals.Remove((signal, connection));
|
||||
|
||||
signal.source = this;
|
||||
connection.SendSignal(signal);
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
@@ -2470,6 +2476,8 @@ namespace Barotrauma
|
||||
parentInventory.RemoveItem(this);
|
||||
parentInventory = null;
|
||||
}
|
||||
|
||||
SetContainedItemPositions();
|
||||
}
|
||||
|
||||
public void Equip(Character character)
|
||||
@@ -2741,7 +2749,7 @@ namespace Barotrauma
|
||||
{
|
||||
CoroutineManager.StopCoroutines(logPropertyChangeCoroutine);
|
||||
}
|
||||
logPropertyChangeCoroutine = CoroutineManager.InvokeAfter(() =>
|
||||
logPropertyChangeCoroutine = CoroutineManager.Invoke(() =>
|
||||
{
|
||||
GameServer.Log($"{sender.Character.Name} set the value \"{property.Name}\" of the item \"{Name}\" to \"{logValue}\".", ServerLog.MessageType.ItemInteraction);
|
||||
}, delay: 1.0f);
|
||||
|
||||
@@ -920,7 +920,8 @@ namespace Barotrauma
|
||||
CanSpriteFlipY = subElement.GetAttributeBool("canflipy", true);
|
||||
|
||||
sprite = new Sprite(subElement, spriteFolder, lazyLoad: true);
|
||||
if (subElement.Attribute("sourcerect") == null)
|
||||
if (subElement.Attribute("sourcerect") == null &&
|
||||
subElement.Attribute("sheetindex") == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Warning - sprite sourcerect not configured for item \"" + Name + "\"!");
|
||||
}
|
||||
@@ -1152,11 +1153,8 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item prefab \"" + Name + "\" - suitable treatments should be defined using item identifiers, not item names.");
|
||||
}
|
||||
|
||||
string treatmentIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();
|
||||
|
||||
string treatmentIdentifier = (subElement.GetAttributeString("identifier", null) ?? subElement.GetAttributeString("type", string.Empty)).ToLowerInvariant();
|
||||
float suitability = subElement.GetAttributeFloat("suitability", 0.0f);
|
||||
|
||||
treatmentSuitability.Add(treatmentIdentifier, suitability);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1829,7 +1829,7 @@ namespace Barotrauma
|
||||
|
||||
private void GenerateRuin(Point ruinPos, bool mirror)
|
||||
{
|
||||
var ruinGenerationParams = RuinGenerationParams.GetRandom();
|
||||
var ruinGenerationParams = RuinGenerationParams.GetRandom(Rand.RandSync.Server);
|
||||
|
||||
LocationType locationType = StartLocation?.Type;
|
||||
if (locationType == null)
|
||||
@@ -1839,7 +1839,7 @@ namespace Barotrauma
|
||||
{
|
||||
locationType = LocationType.List.Where(lt =>
|
||||
ruinGenerationParams.AllowedLocationTypes.Any(allowedType =>
|
||||
allowedType.Equals("any", StringComparison.OrdinalIgnoreCase) || lt.Identifier.Equals(allowedType, StringComparison.OrdinalIgnoreCase))).GetRandom();
|
||||
allowedType.Equals("any", StringComparison.OrdinalIgnoreCase) || lt.Identifier.Equals(allowedType, StringComparison.OrdinalIgnoreCase))).GetRandom(Rand.RandSync.Server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3594,7 +3594,7 @@ namespace Barotrauma
|
||||
{
|
||||
locationType = LocationType.List.Where(lt =>
|
||||
outpostGenerationParams.AllowedLocationTypes.Any(allowedType =>
|
||||
allowedType.Equals("any", StringComparison.OrdinalIgnoreCase) || lt.Identifier.Equals(allowedType, StringComparison.OrdinalIgnoreCase))).GetRandom();
|
||||
allowedType.Equals("any", StringComparison.OrdinalIgnoreCase) || lt.Identifier.Equals(allowedType, StringComparison.OrdinalIgnoreCase))).GetRandom(Rand.RandSync.Server);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3953,7 +3953,7 @@ namespace Barotrauma
|
||||
bool TryGetExtraSpawnPoint(out Vector2 point)
|
||||
{
|
||||
point = Vector2.Zero;
|
||||
var hull = Hull.hullList.FindAll(h => h.Submarine == wreck).GetRandom();
|
||||
var hull = Hull.hullList.FindAll(h => h.Submarine == wreck).GetRandom(Rand.RandSync.Unsynced);
|
||||
if (hull != null)
|
||||
{
|
||||
point = hull.WorldPosition;
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Barotrauma.RuinGeneration
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public static RuinGenerationParams GetRandom()
|
||||
public static RuinGenerationParams GetRandom(Rand.RandSync randSync = Rand.RandSync.Server)
|
||||
{
|
||||
if (paramsList == null) { LoadAll(); }
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Barotrauma.RuinGeneration
|
||||
return new RuinGenerationParams(null, null);
|
||||
}
|
||||
|
||||
return paramsList[Rand.Int(paramsList.Count, Rand.RandSync.Server)];
|
||||
return paramsList[Rand.Int(paramsList.Count, randSync)];
|
||||
}
|
||||
|
||||
private static void LoadAll()
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace Barotrauma
|
||||
|
||||
private const float StoreMaxReputationModifier = 0.1f;
|
||||
private const float StoreSellPriceModifier = 0.8f;
|
||||
private const float DailySpecialPriceModifier = 0.9f;
|
||||
private const float DailySpecialPriceModifier = 0.5f;
|
||||
private const float RequestGoodPriceModifier = 1.5f;
|
||||
public const int StoreInitialBalance = 5000;
|
||||
/// <summary>
|
||||
|
||||
@@ -472,17 +472,18 @@ namespace Barotrauma
|
||||
|
||||
foreach (LocationConnection connection in Connections)
|
||||
{
|
||||
connection.Difficulty = MathHelper.Clamp((connection.CenterPos.X / Width * 100) + Rand.Range(-10.0f, 0.0f, Rand.RandSync.Server), 1.2f, 100.0f);
|
||||
float difficulty = GetLevelDifficulty(connection.CenterPos.X / Width);
|
||||
connection.Difficulty = MathHelper.Clamp(difficulty + Rand.Range(-10.0f, 0.0f, Rand.RandSync.Server), 1.2f, 100.0f);
|
||||
}
|
||||
|
||||
AssignBiomes();
|
||||
CreateEndLocation();
|
||||
|
||||
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
location.LevelData = new LevelData(location)
|
||||
{
|
||||
Difficulty = MathHelper.Clamp(location.MapPosition.X / Width * 100, 0.0f, 100.0f)
|
||||
Difficulty = MathHelper.Clamp(GetLevelDifficulty(location.MapPosition.X / Width), 0.0f, 100.0f)
|
||||
};
|
||||
location.UnlockInitialMissions();
|
||||
}
|
||||
@@ -490,6 +491,14 @@ namespace Barotrauma
|
||||
{
|
||||
connection.LevelData = new LevelData(connection);
|
||||
}
|
||||
|
||||
float GetLevelDifficulty(float areaDifficulty)
|
||||
{
|
||||
const float CurveModifier = 1.5f;
|
||||
const float DifficultyMultiplier = 1.1f;
|
||||
const float BaseDifficulty = -3f;
|
||||
return (float)(1 - Math.Pow(1 - areaDifficulty, CurveModifier)) * DifficultyMultiplier * 100f + BaseDifficulty;
|
||||
}
|
||||
}
|
||||
|
||||
partial void GenerateLocationConnectionVisuals();
|
||||
|
||||
@@ -85,7 +85,15 @@ namespace Barotrauma
|
||||
var subInfo = new SubmarineInfo(outpostModuleFile.Path);
|
||||
if (subInfo.OutpostModuleInfo != null)
|
||||
{
|
||||
if (subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin") != generationParams is RuinGeneration.RuinGenerationParams) { continue; }
|
||||
if (generationParams is RuinGeneration.RuinGenerationParams)
|
||||
{
|
||||
//if the module doesn't have the ruin flag or any other flag used in the generation params, don't use it in ruins
|
||||
if (!subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin") &&
|
||||
!generationParams.ModuleCounts.Any(m => subInfo.OutpostModuleInfo.ModuleFlags.Contains(m.Key)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
outpostModules.Add(subInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using System.Xml.Linq;
|
||||
using Barotrauma.Abilities;
|
||||
#if CLIENT
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Barotrauma.Lights;
|
||||
#endif
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -97,7 +98,7 @@ namespace Barotrauma
|
||||
{
|
||||
get { return Prefab.Body; }
|
||||
}
|
||||
|
||||
|
||||
public List<Body> Bodies { get; private set; }
|
||||
|
||||
public bool CastShadow
|
||||
@@ -113,7 +114,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private float? maxHealth;
|
||||
|
||||
|
||||
[Serialize(100.0f, true)]
|
||||
public float MaxHealth
|
||||
{
|
||||
@@ -168,7 +169,7 @@ namespace Barotrauma
|
||||
{
|
||||
get { return prefab.Tags; }
|
||||
}
|
||||
|
||||
|
||||
protected Color spriteColor;
|
||||
[Editable, Serialize("1.0,1.0,1.0,1.0", true)]
|
||||
public Color SpriteColor
|
||||
@@ -176,7 +177,7 @@ namespace Barotrauma
|
||||
get { return spriteColor; }
|
||||
set { spriteColor = value; }
|
||||
}
|
||||
|
||||
|
||||
[Editable, Serialize(false, true)]
|
||||
public bool UseDropShadow
|
||||
{
|
||||
@@ -216,6 +217,13 @@ namespace Barotrauma
|
||||
UpdateSections();
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
foreach (LightSource light in Lights)
|
||||
{
|
||||
light.SpriteScale = scale * textureScale;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +239,13 @@ namespace Barotrauma
|
||||
textureScale = new Vector2(
|
||||
MathHelper.Clamp(value.X, 0.01f, 10),
|
||||
MathHelper.Clamp(value.Y, 0.01f, 10));
|
||||
|
||||
#if CLIENT
|
||||
foreach (LightSource light in Lights)
|
||||
{
|
||||
light.LightTextureScale = textureScale * scale;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +254,13 @@ namespace Barotrauma
|
||||
public Vector2 TextureOffset
|
||||
{
|
||||
get { return textureOffset; }
|
||||
set { textureOffset = value; }
|
||||
set
|
||||
{
|
||||
textureOffset = value;
|
||||
#if CLIENT
|
||||
SetLightTextureOffset();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -282,10 +303,10 @@ namespace Barotrauma
|
||||
secRect.X += value.X; secRect.Y += value.Y;
|
||||
sec.rect = secRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public float BodyWidth
|
||||
{
|
||||
get { return Prefab.BodyWidth > 0.0f ? Prefab.BodyWidth * scale : rect.Width; }
|
||||
@@ -364,6 +385,12 @@ namespace Barotrauma
|
||||
|
||||
#if CLIENT
|
||||
convexHulls?.ForEach(x => x.Move(amount));
|
||||
|
||||
foreach (LightSource light in Lights)
|
||||
{
|
||||
light.LightTextureTargetSize = rect.Size.ToVector2();
|
||||
light.Position = rect.Location.ToVector2();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -375,7 +402,7 @@ namespace Barotrauma
|
||||
defaultRect = rectangle;
|
||||
|
||||
maxHealth = sp.Health;
|
||||
|
||||
|
||||
rect = rectangle;
|
||||
TextureScale = sp.TextureScale;
|
||||
|
||||
@@ -427,13 +454,48 @@ namespace Barotrauma
|
||||
if (StairDirection != Direction.None)
|
||||
{
|
||||
CreateStairBodies();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SerializableProperties = element != null ? SerializableProperty.DeserializeProperties(this, element) : SerializableProperty.GetProperties(this);
|
||||
|
||||
// Only add ai targets automatically to submarine/outpost walls
|
||||
#if CLIENT
|
||||
foreach (XElement subElement in sp.ConfigElement.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals("light", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Vector2 pos = rect.Location.ToVector2();
|
||||
pos.Y += rect.Height;
|
||||
LightSource light = new LightSource(subElement)
|
||||
{
|
||||
ParentSub = Submarine,
|
||||
Position = rect.Location.ToVector2(),
|
||||
CastShadows = false,
|
||||
IsBackground = false,
|
||||
Color = subElement.GetAttributeColor("lightcolor", Color.White),
|
||||
SpriteScale = Vector2.One,
|
||||
Range = 0,
|
||||
LightTextureTargetSize = rect.Size.ToVector2(),
|
||||
LightTextureScale = textureScale * scale,
|
||||
LightSourceParams =
|
||||
{
|
||||
Flicker = subElement.GetAttributeFloat("flicker", 0f),
|
||||
FlickerSpeed = subElement.GetAttributeFloat("flickerspeed", 0f),
|
||||
PulseAmount = subElement.GetAttributeFloat("pulseamount", 0f),
|
||||
PulseFrequency = subElement.GetAttributeFloat("pulsefrequency", 0f),
|
||||
BlinkFrequency = subElement.GetAttributeFloat("blinkfrequency", 0f)
|
||||
}
|
||||
};
|
||||
|
||||
Lights.Add(light);
|
||||
|
||||
SetLightTextureOffset();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Only add ai targets automatically to submarine/outpost walls
|
||||
if (aiTarget == null && HasBody && Tags.Contains("wall") && submarine != null && !submarine.Info.IsWreck && !NoAITarget)
|
||||
{
|
||||
aiTarget = new AITarget(this)
|
||||
@@ -445,7 +507,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
InsertToList();
|
||||
|
||||
|
||||
DebugConsole.Log("Created " + Name + " (" + ID + ")");
|
||||
}
|
||||
|
||||
@@ -477,7 +539,7 @@ namespace Barotrauma
|
||||
{
|
||||
Bodies = new List<Body>();
|
||||
bodyDebugDimensions.Clear();
|
||||
|
||||
|
||||
float stairAngle = MathHelper.ToRadians(Math.Min(Prefab.StairAngle, 75.0f));
|
||||
|
||||
float bodyWidth = ConvertUnits.ToSimUnits(rect.Width / Math.Cos(stairAngle));
|
||||
@@ -505,9 +567,9 @@ namespace Barotrauma
|
||||
{
|
||||
int xsections = 1, ysections = 1;
|
||||
int width = rect.Width, height = rect.Height;
|
||||
|
||||
|
||||
if (!HasBody)
|
||||
{
|
||||
{
|
||||
if (FlippedX && IsHorizontal)
|
||||
{
|
||||
xsections = (int)Math.Ceiling((float)rect.Width / prefab.sprite.SourceRect.Width);
|
||||
@@ -549,8 +611,8 @@ namespace Barotrauma
|
||||
if (FlippedX || FlippedY)
|
||||
{
|
||||
Rectangle sectionRect = new Rectangle(
|
||||
FlippedX ? rect.Right - (x + 1) * width : rect.X + x * width,
|
||||
FlippedY ? rect.Y - rect.Height + (y + 1) * height : rect.Y - y * height,
|
||||
FlippedX ? rect.Right - (x + 1) * width : rect.X + x * width,
|
||||
FlippedY ? rect.Y - rect.Height + (y + 1) * height : rect.Y - y * height,
|
||||
width, height);
|
||||
|
||||
if (FlippedX)
|
||||
@@ -646,8 +708,8 @@ namespace Barotrauma
|
||||
|
||||
Vector2 transformedMousePos = MathUtils.RotatePointAroundTarget(position, bodyPos, BodyRotation);
|
||||
|
||||
return
|
||||
Math.Abs(transformedMousePos.X - bodyPos.X) < rectSize.X / 2.0f &&
|
||||
return
|
||||
Math.Abs(transformedMousePos.X - bodyPos.X) < rectSize.X / 2.0f &&
|
||||
Math.Abs(transformedMousePos.Y - bodyPos.Y) < rectSize.Y / 2.0f;
|
||||
}
|
||||
else
|
||||
@@ -693,6 +755,10 @@ namespace Barotrauma
|
||||
|
||||
#if CLIENT
|
||||
if (convexHulls != null) convexHulls.ForEach(x => x.Remove());
|
||||
foreach (LightSource light in Lights)
|
||||
{
|
||||
light.Remove();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -725,6 +791,10 @@ namespace Barotrauma
|
||||
|
||||
#if CLIENT
|
||||
if (convexHulls != null) convexHulls.ForEach(x => x.Remove());
|
||||
foreach (LightSource light in Lights)
|
||||
{
|
||||
light.Remove();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -782,7 +852,7 @@ namespace Barotrauma
|
||||
|
||||
return (IsHorizontal ? Sections[sectionIndex].rect.Width : Sections[sectionIndex].rect.Height);
|
||||
}
|
||||
|
||||
|
||||
public override bool AddUpgrade(Upgrade upgrade, bool createNetworkEvent = false)
|
||||
{
|
||||
if (!upgrade.Prefab.IsWallUpgrade) { return false; }
|
||||
@@ -800,7 +870,7 @@ namespace Barotrauma
|
||||
Upgrades.Add(upgrade);
|
||||
upgrade.ApplyUpgrade();
|
||||
}
|
||||
|
||||
|
||||
UpdateSections();
|
||||
|
||||
return true;
|
||||
@@ -915,7 +985,7 @@ namespace Barotrauma
|
||||
{
|
||||
diffFromCenter = -diffFromCenter;
|
||||
}
|
||||
|
||||
|
||||
Vector2 sectionPos = Position + new Vector2(
|
||||
(float)Math.Cos(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation),
|
||||
(float)Math.Sin(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation)) * diffFromCenter;
|
||||
@@ -926,7 +996,7 @@ namespace Barotrauma
|
||||
}
|
||||
return sectionPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false)
|
||||
{
|
||||
@@ -949,7 +1019,7 @@ namespace Barotrauma
|
||||
GameMain.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
if (playSound && damageAmount > 0)
|
||||
{
|
||||
@@ -976,7 +1046,7 @@ namespace Barotrauma
|
||||
if (!MathUtils.IsValid(damage)) { return; }
|
||||
|
||||
damage = MathHelper.Clamp(damage, 0.0f, MaxHealth - Prefab.MinHealth);
|
||||
|
||||
|
||||
#if SERVER
|
||||
if (GameMain.Server != null && createNetworkEvent && damage != Sections[sectionIndex].damage)
|
||||
{
|
||||
@@ -1022,10 +1092,10 @@ namespace Barotrauma
|
||||
{
|
||||
diffFromCenter = (gapRect.Center.X - this.rect.Center.X) / (float)this.rect.Width * BodyWidth;
|
||||
if (BodyWidth > 0.0f) { gapRect.Width = (int)(BodyWidth * (gapRect.Width / (float)this.rect.Width)); }
|
||||
if (BodyHeight > 0.0f)
|
||||
{
|
||||
if (BodyHeight > 0.0f)
|
||||
{
|
||||
gapRect.Y = (gapRect.Y - gapRect.Height / 2) + (int)(BodyHeight / 2 + BodyOffset.Y * scale);
|
||||
gapRect.Height = (int)BodyHeight;
|
||||
gapRect.Height = (int)BodyHeight;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1034,7 +1104,7 @@ namespace Barotrauma
|
||||
if (BodyWidth > 0.0f)
|
||||
{
|
||||
gapRect.X = gapRect.Center.X + (int)(-BodyWidth / 2 + BodyOffset.X * scale);
|
||||
gapRect.Width = (int)BodyWidth;
|
||||
gapRect.Width = (int)BodyWidth;
|
||||
}
|
||||
if (BodyHeight > 0.0f) { gapRect.Height = (int)(BodyHeight * (gapRect.Height / (float)this.rect.Height)); }
|
||||
}
|
||||
@@ -1053,7 +1123,7 @@ namespace Barotrauma
|
||||
gapRect.Y += 10;
|
||||
gapRect.Width += 20;
|
||||
gapRect.Height += 20;
|
||||
|
||||
|
||||
bool horizontalGap = !IsHorizontal;
|
||||
if (Prefab.BodyRotation != 0.0f)
|
||||
{
|
||||
@@ -1090,7 +1160,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float gapOpen = MaxHealth <= 0.0f ? 0.0f : (damage / MaxHealth - LeakThreshold) * (1.0f / (1.0f - LeakThreshold));
|
||||
Sections[sectionIndex].gap.Open = gapOpen;
|
||||
Sections[sectionIndex].gap.Open = gapOpen;
|
||||
}
|
||||
|
||||
float damageDiff = damage - Sections[sectionIndex].damage;
|
||||
@@ -1106,9 +1176,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (damageDiff < 0.0f)
|
||||
{
|
||||
attacker.Info?.IncreaseSkillLevel("mechanical",
|
||||
attacker.Info?.IncreaseSkillLevel("mechanical",
|
||||
-damageDiff * SkillSettings.Current.SkillIncreasePerRepairedStructureDamage / Math.Max(attacker.GetSkillLevel("mechanical"), 1.0f),
|
||||
SectionPosition(sectionIndex));
|
||||
SectionPosition(sectionIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1116,7 +1186,7 @@ namespace Barotrauma
|
||||
bool hasHole = SectionBodyDisabled(sectionIndex);
|
||||
|
||||
if (hadHole == hasHole) { return; }
|
||||
|
||||
|
||||
UpdateSections();
|
||||
}
|
||||
|
||||
@@ -1198,7 +1268,7 @@ namespace Barotrauma
|
||||
if (BodyHeight > 0.0f) rect.Height = Math.Max((int)Math.Round(BodyHeight * (rect.Height / (float)this.rect.Height)), 1);
|
||||
}
|
||||
if (FlippedX) { diffFromCenter = -diffFromCenter; }
|
||||
|
||||
|
||||
Vector2 bodyOffset = ConvertUnits.ToSimUnits(Prefab.BodyOffset) * scale;
|
||||
if (FlippedX) { bodyOffset.X = -bodyOffset.X; }
|
||||
if (FlippedY) { bodyOffset.Y = -bodyOffset.Y; }
|
||||
@@ -1240,7 +1310,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
partial void CreateConvexHull(Vector2 position, Vector2 size, float rotation);
|
||||
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
base.FlipX(relativeToSub);
|
||||
@@ -1261,7 +1331,7 @@ namespace Barotrauma
|
||||
|
||||
CreateStairBodies();
|
||||
}
|
||||
|
||||
|
||||
if (HasBody)
|
||||
{
|
||||
CreateSections();
|
||||
@@ -1388,7 +1458,7 @@ namespace Barotrauma
|
||||
StructurePrefab prefab = null;
|
||||
if (string.IsNullOrEmpty(identifier))
|
||||
{
|
||||
//legacy support:
|
||||
//legacy support:
|
||||
//1. attempt to find a prefab with an empty identifier and a matching name
|
||||
prefab = MapEntityPrefab.Find(name, "") as StructurePrefab;
|
||||
//2. not found, attempt to find a prefab with a matching name
|
||||
@@ -1433,7 +1503,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
SerializableProperty.SerializeProperties(this, element);
|
||||
|
||||
|
||||
foreach (var upgrade in Upgrades)
|
||||
{
|
||||
upgrade.Save(element);
|
||||
|
||||
@@ -291,7 +291,8 @@ namespace Barotrauma
|
||||
{
|
||||
case "sprite":
|
||||
sp.sprite = new Sprite(subElement, lazyLoad: true);
|
||||
if (subElement.Attribute("sourcerect") == null)
|
||||
if (subElement.Attribute("sourcerect") == null &&
|
||||
subElement.Attribute("sheetindex") == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Warning - sprite sourcerect not configured for structure \"" + sp.name + "\"!");
|
||||
}
|
||||
|
||||
@@ -914,9 +914,11 @@ namespace Barotrauma
|
||||
mapEntity.Move(-HiddenSubPosition);
|
||||
}
|
||||
|
||||
var prevBodyType = subBody.Body.BodyType;
|
||||
Vector2 pos = new Vector2(subBody.Position.X, subBody.Position.Y);
|
||||
subBody.Body.Remove();
|
||||
subBody = new SubmarineBody(this);
|
||||
subBody.Body.BodyType = prevBodyType;
|
||||
SetPosition(pos, new List<Submarine>(parents.Where(p => p != this)));
|
||||
|
||||
if (entityGrid != null)
|
||||
@@ -1429,6 +1431,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (info.IsRuin)
|
||||
{
|
||||
ShowSonarMarker = false;
|
||||
PhysicsBody.FarseerBody.BodyType = BodyType.Static;
|
||||
}
|
||||
}
|
||||
|
||||
if (entityGrid != null)
|
||||
|
||||
@@ -868,11 +868,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static WayPoint GetRandom(SpawnType spawnType = SpawnType.Human, JobPrefab assignedJob = null, Submarine sub = null, bool useSyncedRand = false)
|
||||
public static WayPoint GetRandom(SpawnType spawnType = SpawnType.Human, JobPrefab assignedJob = null, Submarine sub = null, bool useSyncedRand = false, string spawnPointTag = null)
|
||||
{
|
||||
return WayPointList.GetRandom(wp =>
|
||||
wp.Submarine == sub &&
|
||||
wp.spawnType == spawnType &&
|
||||
(string.IsNullOrEmpty(spawnPointTag) || wp.Tags.Any(t => t.Equals(spawnPointTag, StringComparison.OrdinalIgnoreCase))) &&
|
||||
(assignedJob == null || (assignedJob != null && wp.AssignedJob == assignedJob)),
|
||||
useSyncedRand ? Rand.RandSync.Server : Rand.RandSync.Unsynced);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace Barotrauma
|
||||
}
|
||||
case ConditionType.AllowRotating:
|
||||
{
|
||||
return entity is Item item && item.Prefab.AllowRotatingInEditor && Screen.Selected == GameMain.SubEditorScreen;
|
||||
return entity is Item item && item.body == null && item.Prefab.AllowRotatingInEditor && Screen.Selected == GameMain.SubEditorScreen;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -15,6 +16,22 @@ namespace Barotrauma
|
||||
{
|
||||
public static class XMLExtensions
|
||||
{
|
||||
private static ImmutableDictionary<Type, Func<string, object, object>> converters
|
||||
= new Dictionary<Type, Func<string, object, object>>()
|
||||
{
|
||||
{ typeof(string), (str, defVal) => str },
|
||||
{ typeof(int), (str, defVal) => int.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out int result) ? result : defVal },
|
||||
{ typeof(uint), (str, defVal) => uint.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out uint result) ? result : defVal },
|
||||
{ typeof(UInt64), (str, defVal) => UInt64.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out UInt64 result) ? result : defVal },
|
||||
{ typeof(float), (str, defVal) => float.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out float result) ? result : defVal },
|
||||
{ typeof(bool), (str, defVal) => bool.TryParse(str, out bool result) ? result : defVal },
|
||||
{ typeof(Color), (str, defVal) => ParseColor(str) },
|
||||
{ typeof(Vector2), (str, defVal) => ParseVector2(str) },
|
||||
{ typeof(Vector3), (str, defVal) => ParseVector3(str) },
|
||||
{ typeof(Vector4), (str, defVal) => ParseVector4(str) },
|
||||
{ typeof(Rectangle), (str, defVal) => ParseRect(str, true) }
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
public static string ParseContentPathFromUri(this XObject element) => System.IO.Path.GetRelativePath(Environment.CurrentDirectory, element.BaseUri);
|
||||
|
||||
public static readonly XmlReaderSettings ReaderSettings = new XmlReaderSettings
|
||||
@@ -485,6 +502,25 @@ namespace Barotrauma
|
||||
return ParseRect(element.Attribute(name).Value, false);
|
||||
}
|
||||
|
||||
//TODO: nested tuples and and n-uples where n!=2 are unsupported
|
||||
public static (T1, T2) GetAttributeTuple<T1, T2>(this XElement element, string name, (T1, T2) defaultValue)
|
||||
{
|
||||
string strValue = element.GetAttributeString(name, $"({defaultValue.Item1}, {defaultValue.Item2})").Trim();
|
||||
|
||||
return ParseTuple(strValue, defaultValue);
|
||||
}
|
||||
|
||||
public static (T1, T2)[] GetAttributeTupleArray<T1, T2>(this XElement element, string name,
|
||||
(T1, T2)[] defaultValue)
|
||||
{
|
||||
if (element?.Attribute(name) == null) { return defaultValue; }
|
||||
|
||||
string stringValue = element.Attribute(name).Value;
|
||||
if (string.IsNullOrEmpty(stringValue)) { return defaultValue; }
|
||||
|
||||
return stringValue.Split(';').Select(s => ParseTuple<T1, T2>(s, default)).ToArray();
|
||||
}
|
||||
|
||||
public static string ElementInnerText(this XElement el)
|
||||
{
|
||||
StringBuilder str = new StringBuilder();
|
||||
@@ -525,12 +561,28 @@ namespace Barotrauma
|
||||
=> $"{color.R},{color.G},{color.B},{color.A}";
|
||||
|
||||
public static string ToStringHex(this Color color)
|
||||
=> $"#{color.R:X2}{color.G:X2}{color.B:X2}{color.A:X2}";
|
||||
=> $"#{color.R:X2}{color.G:X2}{color.B:X2}"
|
||||
+ ((color.A < 255) ? $"{color.A:X2}" : "");
|
||||
|
||||
public static string RectToString(Rectangle rect)
|
||||
{
|
||||
return rect.X + "," + rect.Y + "," + rect.Width + "," + rect.Height;
|
||||
}
|
||||
|
||||
public static (T1, T2) ParseTuple<T1, T2>(string strValue, (T1, T2) defaultValue)
|
||||
{
|
||||
strValue = strValue.Trim();
|
||||
//require parentheses
|
||||
if (strValue[0] != '(' || strValue[^1] != ')') { return defaultValue; }
|
||||
//remove parentheses
|
||||
strValue = strValue[1..^1];
|
||||
|
||||
string[] elems = strValue.Split(',');
|
||||
if (elems.Length != 2) { return defaultValue; }
|
||||
|
||||
return ((T1)converters[typeof(T1)].Invoke(elems[0], defaultValue.Item1),
|
||||
(T2)converters[typeof(T2)].Invoke(elems[1], defaultValue.Item2));
|
||||
}
|
||||
|
||||
public static Point ParsePoint(string stringPoint, bool errorMessages = true)
|
||||
{
|
||||
|
||||
@@ -197,6 +197,18 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public class GiveTalentInfo
|
||||
{
|
||||
public string[] TalentIdentifiers;
|
||||
public bool GiveRandom;
|
||||
|
||||
public GiveTalentInfo(XElement element, string parentDebugName)
|
||||
{
|
||||
TalentIdentifiers = element.GetAttributeStringArray("talentidentifiers", new string[0], convertToLowerInvariant: true);
|
||||
GiveRandom = element.GetAttributeBool("giverandom", false);
|
||||
}
|
||||
}
|
||||
|
||||
public class CharacterSpawnInfo : ISerializableEntity
|
||||
{
|
||||
public string Name => $"Character Spawn Info ({SpeciesName})";
|
||||
@@ -274,6 +286,8 @@ namespace Barotrauma
|
||||
private readonly bool spawnItemRandomly;
|
||||
private readonly List<CharacterSpawnInfo> spawnCharacters;
|
||||
|
||||
public readonly List<GiveTalentInfo> giveTalentInfos;
|
||||
|
||||
private readonly List<AITrigger> aiTriggers;
|
||||
|
||||
private readonly List<EventPrefab> triggeredEvents;
|
||||
@@ -374,6 +388,7 @@ namespace Barotrauma
|
||||
spawnItems = new List<ItemSpawnInfo>();
|
||||
spawnItemRandomly = element.GetAttributeBool("spawnitemrandomly", false);
|
||||
spawnCharacters = new List<CharacterSpawnInfo>();
|
||||
giveTalentInfos = new List<GiveTalentInfo>();
|
||||
aiTriggers = new List<AITrigger>();
|
||||
Afflictions = new List<Affliction>();
|
||||
Explosions = new List<Explosion>();
|
||||
@@ -576,7 +591,7 @@ namespace Barotrauma
|
||||
break;
|
||||
case "requiredaffliction":
|
||||
requiredAfflictions ??= new HashSet<(string, float)>();
|
||||
string[] ids = subElement.GetAttributeStringArray("identifier", new string[0]);
|
||||
string[] ids = subElement.GetAttributeStringArray("identifier", null) ?? subElement.GetAttributeStringArray("type", new string[0]);
|
||||
foreach (string afflictionId in ids)
|
||||
{
|
||||
requiredAfflictions.Add((
|
||||
@@ -669,6 +684,10 @@ namespace Barotrauma
|
||||
var newSpawnCharacter = new CharacterSpawnInfo(subElement, parentDebugName);
|
||||
if (!string.IsNullOrWhiteSpace(newSpawnCharacter.SpeciesName)) { spawnCharacters.Add(newSpawnCharacter); }
|
||||
break;
|
||||
case "givetalentinfo":
|
||||
var newGiveTalentInfo = new GiveTalentInfo(subElement, parentDebugName);
|
||||
if (newGiveTalentInfo.TalentIdentifiers.Any()) { giveTalentInfos.Add(newGiveTalentInfo); }
|
||||
break;
|
||||
case "aitrigger":
|
||||
aiTriggers.Add(new AITrigger(subElement));
|
||||
break;
|
||||
@@ -1305,6 +1324,33 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (giveTalentInfos.Any() && (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient))
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
if (targetCharacter?.Info == null) { continue; }
|
||||
if (!TalentTree.JobTalentTrees.TryGetValue(targetCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { continue; }
|
||||
// for the sake of technical simplicity, for now do not allow talents to be given if the character could unlock them in their talent tree as well
|
||||
IEnumerable<string> disallowedTalents = talentTree.TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier)));
|
||||
|
||||
foreach (GiveTalentInfo giveTalentInfo in giveTalentInfos)
|
||||
{
|
||||
IEnumerable<string> viableTalents = giveTalentInfo.TalentIdentifiers.Where(s => !targetCharacter.Info.UnlockedTalents.Contains(s) && !disallowedTalents.Contains(s));
|
||||
if (viableTalents.None()) { continue; }
|
||||
|
||||
if (giveTalentInfo.GiveRandom)
|
||||
{
|
||||
targetCharacter.GiveTalent(viableTalents.GetRandom(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string talent in viableTalents)
|
||||
{
|
||||
targetCharacter.GiveTalent(talent, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FireSize > 0.0f && entity != null)
|
||||
@@ -1367,6 +1413,7 @@ namespace Barotrauma
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (spawnItemRandomly)
|
||||
{
|
||||
SpawnItem(spawnItems.GetRandom());
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace Barotrauma
|
||||
/// <returns></returns>
|
||||
private static float ApplyPercentage(float value, float amount, int times)
|
||||
{
|
||||
return times <= 0 ? value : ApplyPercentage(value + (value * amount / 100), amount, --times);
|
||||
return (1f + (amount / 100f * times)) * value;
|
||||
}
|
||||
|
||||
public static PropertyReference[] ParseAttributes(IEnumerable<XAttribute> attributes, Upgrade upgrade)
|
||||
|
||||
@@ -68,7 +68,13 @@ namespace Barotrauma
|
||||
Name = element.GetAttributeString("name", string.Empty);
|
||||
IsWallUpgrade = element.GetAttributeBool("wallupgrade", false);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Name))
|
||||
string nameIdentifier = element.GetAttributeString("nameidentifier", "");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(nameIdentifier))
|
||||
{
|
||||
Name = TextManager.Get($"{nameIdentifier}", returnNull: true) ?? string.Empty;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(Name))
|
||||
{
|
||||
Name = TextManager.Get($"UpgradeCategory.{Identifier}", true) ?? string.Empty;
|
||||
}
|
||||
@@ -131,6 +137,8 @@ namespace Barotrauma
|
||||
|
||||
public string Description { get; }
|
||||
|
||||
public float IncreaseOnTooltip { get; }
|
||||
|
||||
public string Identifier { get; }
|
||||
|
||||
public string FilePath { get; }
|
||||
@@ -172,7 +180,13 @@ namespace Barotrauma
|
||||
|
||||
var targetProperties = new Dictionary<string, string[]>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Name))
|
||||
string nameIdentifier = element.GetAttributeString("nameidentifier", "");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(nameIdentifier))
|
||||
{
|
||||
Name = TextManager.Get($"UpgradeName.{nameIdentifier}", returnNull: true) ?? string.Empty;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(Name))
|
||||
{
|
||||
Name = TextManager.Get($"UpgradeName.{Identifier}", returnNull: true) ?? string.Empty;
|
||||
}
|
||||
@@ -182,6 +196,8 @@ namespace Barotrauma
|
||||
Description = TextManager.Get($"UpgradeDescription.{Identifier}", returnNull: true) ?? string.Empty;
|
||||
}
|
||||
|
||||
IncreaseOnTooltip = element.GetAttributeFloat("increaseontooltip", 0f);
|
||||
|
||||
DebugConsole.Log(" " + Name);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
|
||||
@@ -1,3 +1,54 @@
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.1500.6.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Additions and changes:
|
||||
- Improvements and additions to the alien ruins and the new fractal guardians.
|
||||
- More improvements and fixes to the overhauled character sprites and animations.
|
||||
- Upgrade system reworked to work better in conjunction with new talents and quality systems. Quality of life upgrades made better or cheaper, hull upgrades are less effective towards the lategame but are better early, reorganized categories.
|
||||
- Talent adjustments and balancing.
|
||||
- Nerfed thermal goggles: some enemies are invisible to them and targets behind walls are much less clear.
|
||||
- Removed the "burndamage" damage type (not the same as "burn") that was added as a temporary workaround to allow pulse lasers to bypass monster's damage modifiers.
|
||||
- Made welding tools a bit less effective early to compensate for increases to their effectiveness from quality/talents.
|
||||
- Fuel tanks, rods, grenades and other quality-based items can be stacked again. Items of different qualities still can't be put in the same stack.
|
||||
- Endocrine Booster now gives a random talent.
|
||||
- New sprite for the mudraptor beak grown by splicing mudraptor genes.
|
||||
- Fabricating fuel rods now requirse electrical skills instead of mechanical.
|
||||
- Reactor now requires electrical skills instead of mechanical to repair.
|
||||
- When the status monitor receives the oxygen/water level for a hull, it registers it on all the linked hulls as well (-> no need to put an oxygen/water detector in all the hulls of a multi-hull room).
|
||||
- Changed how skillbooks work: the skills are gained when you "finish" reading the book instead of continuously to prevent UI message spam and overlap, the books can be used on others in the health interface or by hitting them with the book.
|
||||
- Color the selected (but unapplied) talent icons orange instead of changing the icon in the talent UI, made the apply button flash when there's unapplied talents.
|
||||
- Merged status monitor's status and hull condition tabs.
|
||||
- Halved concussion's damage threshold to make it possible for more sources of damage to trigger it, + halved the probability to compensate.
|
||||
|
||||
Fixes:
|
||||
- Fixed high-quality batteries/tanks (or other items with an increased max condition) spawning in non-full condition (unstable only).
|
||||
- Fixed ropes sometimes crashing the game (unstable only).
|
||||
- Fixed some items occasionally disappearing from outposts when re-entering them. The most noticeable symptom was wires disappearing from the outpost's airlock, preventing the hatch from opening (unstable only).
|
||||
- Fixed multiplayer campaign characters resetting if they're dead when the round ends.
|
||||
- Fixed clients who aren't currently controlling a character not getting XP in the mp campaign (unstable only).
|
||||
- The overdosed NPC in the "good samaritan" event can't die until the player has triggered the event (completing the event after the NPC had already died made no sense).
|
||||
- Fixed paralyzant (and many other meds that don't do direct damage) not triggering guards.
|
||||
- Fixed sonar monitor's UI being unnecessarily small.
|
||||
- Fixed contained items inside contained items (e.g. magazines in a rifle on a weapon holder) not rotating in the sub editor.
|
||||
- Fixed boarding axe (unstable only).
|
||||
- Fixed signal source being wrong on delayed electrical signals (= signals that were delayed for the next frame after they'd passed through 10 steps). Most noticeably affected status monitors that need to know which oxygen/water detector a signal came from.
|
||||
- Fixed WifiComponents delaying the signals based on the number of receivers, not how many steps the signal has actually taken, contributing to the previous issue.
|
||||
- Hopefully fixed an oversight in sub editor where changing ItemComponent color with HSV picker would create an error in the console.
|
||||
- Fixed inability to hit downed characters with short melee weapons like the diving knife.
|
||||
- Fixed inability to sell nasonov and faraday artifacts (unstable only).
|
||||
- Fixed concussion description (unstable only).
|
||||
- Fixes gender-specific affliction sound effects (e.g. vomiting) not playing (unstable only).
|
||||
- Fixed humans' "aim source position" being too low, causing aim to be slightly off (unstable only).
|
||||
- Fixed artifact transport case displaying as empty when there's a nasonov/faraday artifact inside (unstable only).
|
||||
- Fixed "infiltration" event getting stuck on one of the conversation options.
|
||||
- Fixed ruin's physics bodies being dynamic in mirrored levels (causing them to sink).
|
||||
- Backwards compatibility: assign skin colors to characters saved in previous versions.
|
||||
- Assign random skin/hair colors to characters without one configured instead of defaulting to white. Fixes all tutorial characters having white skin/hair.
|
||||
Modding:
|
||||
- Added support for tileable light textures for Structures by using <light> XML element that has the same syntax as <LightComponent> does for Items.
|
||||
- Added "InPressure" property to characters.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v0.1500.5.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
@@ -8,7 +59,6 @@ Additions and changes:
|
||||
- New Alien Ruin mission: kill the guardians inhabiting the ruin and destroy their pods.
|
||||
- Improvements and fixes to the overhauled character sprites and animations.
|
||||
- More talent improvements and additions.
|
||||
- Balanced loot spawn rates.
|
||||
- Cap the amount argument of the spawnitem command to 100 to prevent freezing/crashing when trying to spawn a ridiculous amount of the item.
|
||||
- Nerfed concussions: they now require a larger amount of damage to the head to trigger and slowly heal by themselves.
|
||||
- Added "unlocktalents [job]" command.
|
||||
@@ -19,6 +69,17 @@ Additions and changes:
|
||||
- Added button to randomize character appearance in the character customization menus.
|
||||
- Permanently reduce character skills when respawning mid-round. The talent system makes it easier to gain skills and permanent improvements to the character, and this change is intended to balance that out.
|
||||
|
||||
Balance changes:
|
||||
- Reduced loot in wrecks.
|
||||
- Difficulty affects the amount of loot.
|
||||
- Reduced the amount of weapons and grenades in wrecks, pirate ships and abandoned outposts.
|
||||
- Disabled stacking quality-based items (experimental change, feedback is welcome).
|
||||
- Reduced diving suits damage resistances.
|
||||
- Buffed vigor and haste.
|
||||
- Modifed characters' base vitalities.
|
||||
- Adjustments to monster stats.
|
||||
- Reduced mission experience gains, level difficulty affects mission experience.
|
||||
|
||||
Fixes:
|
||||
- Fixed crash when firing a syring gun (unstable only).
|
||||
- Fixed crashing when using meds in multiplayer with friendly fire turned off (unstable only).
|
||||
|
||||
Reference in New Issue
Block a user