Unstable 0.1500.0.0

This commit is contained in:
Markus Isberg
2021-08-26 21:08:21 +09:00
parent 265a2e7ab3
commit 501e02c026
245 changed files with 9775 additions and 2034 deletions

View File

@@ -1,5 +1,4 @@
using Barotrauma.Sounds;
using Barotrauma.Particles;
using Barotrauma.Particles;
using Microsoft.Xna.Framework;
using System.Xml.Linq;

View File

@@ -21,7 +21,6 @@ namespace Barotrauma
public static bool DebugDrawInteract;
protected float soundTimer;
protected float soundInterval;
protected float hudInfoTimer = 1.0f;
protected bool hudInfoVisible = false;
@@ -130,7 +129,7 @@ namespace Barotrauma
}
public static bool IsMouseOnUI => GUI.MouseOn != null ||
(CharacterInventory.IsMouseOnInventory() && !CharacterInventory.DraggingItemToWorld);
(CharacterInventory.IsMouseOnInventory && !CharacterInventory.DraggingItemToWorld);
public class ObjectiveEntity
{
@@ -161,8 +160,7 @@ namespace Barotrauma
partial void InitProjSpecific(XElement mainElement)
{
soundInterval = mainElement.GetAttributeFloat("soundinterval", 10.0f);
soundTimer = Rand.Range(0.0f, soundInterval);
soundTimer = Rand.Range(0.0f, Params.SoundInterval);
sounds = new List<CharacterSound>();
Params.Sounds.ForEach(s => sounds.Add(new CharacterSound(s)));
@@ -390,12 +388,7 @@ namespace Barotrauma
{
if (attackResult.Damage <= 1.0f) { return; }
}
if (soundTimer < soundInterval * 0.5f)
{
PlaySound(CharacterSound.SoundType.Damage);
soundTimer = soundInterval;
}
PlaySound(CharacterSound.SoundType.Damage, maxInterval: 2);
}
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log)
@@ -470,9 +463,9 @@ namespace Barotrauma
}
private List<Item> debugInteractablesInRange = new List<Item>();
private List<Item> debugInteractablesAtCursor = new List<Item>();
private List<Pair<Item, float>> debugInteractablesNearCursor = new List<Pair<Item, float>>();
private readonly List<Item> debugInteractablesInRange = new List<Item>();
private readonly List<Item> debugInteractablesAtCursor = new List<Item>();
private readonly List<(Item item, float dist)> debugInteractablesNearCursor = new List<(Item item, float dist)>();
/// <summary>
/// Finds the front (lowest depth) interactable item at a position. "Interactable" in this case means that the character can "reach" the item.
@@ -568,7 +561,7 @@ namespace Barotrauma
if (distanceToItem > closestItemDistance) { continue; }
if (!CanInteractWith(item)) { continue; }
debugInteractablesNearCursor.Add(new Pair<Item, float>(item, 1.0f - distanceToItem / (100.0f * aimAssistModifier)));
debugInteractablesNearCursor.Add((item, 1.0f - distanceToItem / (100.0f * aimAssistModifier)));
closestItem = item;
closestItemDistance = distanceToItem;
}
@@ -579,31 +572,20 @@ namespace Barotrauma
private Character FindCharacterAtPosition(Vector2 mouseSimPos, float maxDist = 150.0f)
{
Character closestCharacter = null;
float closestDist = 0.0f;
maxDist = ConvertUnits.ToSimUnits(maxDist);
float closestDist = maxDist * maxDist;
foreach (Character c in CharacterList)
{
if (!CanInteractWith(c, checkVisibility: false) || (c.AnimController?.SimplePhysicsEnabled ?? true)) { continue; }
float dist = Vector2.DistanceSquared(mouseSimPos, c.SimPosition);
if (dist < maxDist * maxDist && (closestCharacter == null || dist < closestDist))
if (dist < closestDist ||
(c.CampaignInteractionType != CampaignMode.InteractionType.None && closestCharacter?.CampaignInteractionType == CampaignMode.InteractionType.None && dist * 0.9f < closestDist))
{
closestCharacter = c;
closestDist = dist;
}
/*FarseerPhysics.Common.Transform transform;
c.AnimController.Collider.FarseerBody.GetTransform(out transform);
for (int i = 0; i < c.AnimController.Collider.FarseerBody.FixtureList.Count; i++)
{
if (c.AnimController.Collider.FarseerBody.FixtureList[i].Shape.TestPoint(ref transform, ref mouseSimPos))
{
Console.WriteLine("Hit: " + i);
closestCharacter = c;
}
}*/
}
return closestCharacter;
@@ -638,7 +620,7 @@ namespace Barotrauma
if (!enabled) { return; }
if (!IsDead && !IsIncapacitated)
if (!IsIncapacitated)
{
if (soundTimer > 0)
{
@@ -649,7 +631,14 @@ namespace Barotrauma
switch (enemyAI.State)
{
case AIState.Attack:
PlaySound(CharacterSound.SoundType.Attack);
if (Rand.Value() > 0.5f)
{
PlaySound(CharacterSound.SoundType.Attack);
}
else
{
PlaySound(CharacterSound.SoundType.Idle);
}
break;
default:
var petBehavior = enemyAI.PetBehavior;
@@ -660,7 +649,6 @@ namespace Barotrauma
else
{
PlaySound(CharacterSound.SoundType.Idle);
}
break;
}
@@ -827,12 +815,12 @@ namespace Barotrauma
GUI.DrawLine(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y),
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), Color.White * 0.1f, width: 4);
}
foreach (Pair<Item, float> item in debugInteractablesNearCursor)
foreach ((Item item, float dist) in debugInteractablesNearCursor)
{
GUI.DrawLine(spriteBatch,
cursorPos,
new Vector2(item.First.DrawPosition.X, -item.First.DrawPosition.Y),
ToolBox.GradientLerp(item.Second, GUI.Style.Red, GUI.Style.Orange, GUI.Style.Green), width: 2);
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y),
ToolBox.GradientLerp(dist, GUI.Style.Red, GUI.Style.Orange, GUI.Style.Green), width: 2);
}
}
return;
@@ -856,6 +844,7 @@ namespace Barotrauma
Vector2 nameSize = GUI.Font.MeasureString(name);
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
Color nameColor = GetNameColor();
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
@@ -865,18 +854,6 @@ namespace Barotrauma
namePos *= viewportSize / screenSize;
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
Color nameColor = Color.White;
if (Controlled != null && TeamID != Controlled.TeamID)
{
if (TeamID == CharacterTeamType.FriendlyNPC)
{
nameColor = UniqueNameColor ?? Color.SkyBlue;
}
else
{
nameColor = GUI.Style.Red;
}
}
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
{
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionBubble." + CampaignInteractionType);
@@ -931,6 +908,40 @@ namespace Barotrauma
}
}
public Color GetNameColor()
{
CharacterTeamType team = teamID;
if (Info?.IsDisguisedAsAnother != null)
{
var idCard = Inventory.GetItemInLimbSlot(InvSlotType.Card)?.GetComponent<IdCard>();
if (idCard != null)
{
if (team == CharacterTeamType.Team2 && idCard.TeamID != CharacterTeamType.Team2)
{
team = CharacterTeamType.Team1;
}
else if (team == CharacterTeamType.Team1 && idCard.TeamID == CharacterTeamType.Team2)
{
team = CharacterTeamType.Team2;
}
}
}
Color nameColor = GUI.Style.TextColor;
if (Controlled != null && team != Controlled.TeamID)
{
if (TeamID == CharacterTeamType.FriendlyNPC)
{
nameColor = UniqueNameColor ?? Color.SkyBlue;
}
else
{
nameColor = GUI.Style.Red;
}
}
return nameColor;
}
/// <summary>
/// Creates a progress bar that's "linked" to the specified object (or updates an existing one if there's one already linked to the object)
/// The progress bar will automatically fade out after 1 sec if the method hasn't been called during that time
@@ -958,12 +969,13 @@ namespace Barotrauma
private readonly List<CharacterSound> matchingSounds = new List<CharacterSound>();
private SoundChannel soundChannel;
public void PlaySound(CharacterSound.SoundType soundType, float soundIntervalFactor = 1.0f)
public void PlaySound(CharacterSound.SoundType soundType, float soundIntervalFactor = 1.0f, float maxInterval = 0)
{
if (sounds == null || sounds.Count == 0) { return; }
if (soundChannel != null && soundChannel.IsPlaying) { return; }
if (GameMain.SoundManager?.Disabled ?? true) { return; }
if (soundTimer > soundInterval * soundIntervalFactor) { return; }
if (soundTimer > Params.SoundInterval * soundIntervalFactor) { return; }
if (Params.SoundInterval - soundTimer < maxInterval) { return; }
matchingSounds.Clear();
foreach (var s in sounds)
{
@@ -975,7 +987,7 @@ namespace Barotrauma
var selectedSound = matchingSounds.GetRandom();
if (selectedSound?.Sound == null) { return; }
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, AnimController.WorldPosition, selectedSound.Volume, selectedSound.Range, hullGuess: CurrentHull, ignoreMuffling: selectedSound.IgnoreMuffling);
soundTimer = soundInterval;
soundTimer = Params.SoundInterval;
}
public void AddActiveObjectiveEntity(Entity entity, Sprite sprite, Color? color = null)
@@ -1028,5 +1040,20 @@ namespace Barotrauma
Rand.Range(50.0f, 500.0f), null);
}
}
partial void OnMoneyChanged(int prevAmount, int newAmount)
{
if (newAmount > prevAmount)
{
int increase = newAmount - prevAmount;
GUI.AddMessage(
"+" + TextManager.GetWithVariable("currencyformat", "[credits]", increase.ToString()),
GUI.Style.Yellow,
Position + Vector2.UnitY * 150.0f,
Vector2.UnitY * 10.0f,
playSound: true,
subId: Submarine?.ID ?? -1);;
}
}
}
}

View File

@@ -391,7 +391,26 @@ namespace Barotrauma
if (npc.CampaignInteractionType == CampaignMode.InteractionType.None || npc.Submarine != character.Submarine || npc.IsDead || npc.IsIncapacitated) { continue; }
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionIcon." + npc.CampaignInteractionType);
GUI.DrawIndicator(spriteBatch, npc.WorldPosition, cam, npc.CurrentHull == character.CurrentHull ? 500.0f : 100.0f, iconStyle.GetDefaultSprite(), iconStyle.Color);
Range<float> visibleRange = new Range<float>(npc.CurrentHull == Character.Controlled.CurrentHull ? 500.0f : 100.0f, float.PositiveInfinity);
if (npc.CampaignInteractionType == CampaignMode.InteractionType.Examine)
{
//TODO: we could probably do better than just hardcoding
//a check for InteractionType.Examine here.
if (Vector2.DistanceSquared(character.Position, npc.Position) > 500f * 500f) { continue; }
var body = Submarine.CheckVisibility(character.SimPosition, npc.SimPosition, ignoreLevel: true);
if (body != null && body.UserData as Character != npc) { continue; }
visibleRange = new Range<float>(-100f, 500f);
}
GUI.DrawIndicator(
spriteBatch,
npc.WorldPosition,
cam,
visibleRange,
iconStyle.GetDefaultSprite(),
iconStyle.Color);
}
foreach (Item item in Item.ItemList)
@@ -400,7 +419,7 @@ namespace Barotrauma
if (Vector2.DistanceSquared(character.Position, item.Position) > 500f*500f) { continue; }
var body = Submarine.CheckVisibility(character.SimPosition, item.SimPosition, ignoreLevel: true);
if (body != null && body.UserData as Item != item) { continue; }
GUI.DrawIndicator(spriteBatch, item.WorldPosition + new Vector2(0f, item.RectHeight * 0.65f), cam, new Vector2(-100f, 500.0f), item.IconStyle.GetDefaultSprite(), item.IconStyle.Color, createOffset: false);
GUI.DrawIndicator(spriteBatch, item.WorldPosition + new Vector2(0f, item.RectHeight * 0.65f), cam, new Range<float>(-100f, 500.0f), item.IconStyle.GetDefaultSprite(), item.IconStyle.Color, createOffset: false);
}
}
@@ -525,12 +544,7 @@ namespace Barotrauma
textPos -= new Vector2(textSize.X / 2, textSize.Y);
Color nameColor = GUI.Style.TextColor;
if (character.TeamID != character.FocusedCharacter.TeamID)
{
nameColor = character.FocusedCharacter.TeamID == CharacterTeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
}
Color nameColor = character.FocusedCharacter.GetNameColor();
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUI.SubHeadingFont);
textPos.X += 10.0f * GUI.Scale;
textPos.Y += GUI.SubHeadingFont.MeasureString(focusName).Y;
@@ -544,11 +558,14 @@ namespace Barotrauma
if (character.FocusedCharacter.CanBeDragged)
{
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBindText(InputType.Grab)),
string text = character.CanEat ? "EatHint" : "GrabHint";
GUI.DrawString(spriteBatch, textPos, GetCachedHudText(text, GameMain.Config.KeyBindText(InputType.Grab)),
GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
textPos.Y += largeTextSize.Y;
}
if (!character.DisableHealthWindow &&
character.IsFriendly(character.FocusedCharacter) &&
character.FocusedCharacter.CharacterHealth.UseHealthWindow &&
character.CanInteractWith(character.FocusedCharacter, 160f, false))
{

View File

@@ -170,15 +170,37 @@ namespace Barotrauma
if (TeamID == CharacterTeamType.FriendlyNPC) { return; }
if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; }
// if we increased by more than 1 in one increase, then display special color (for talents)
bool specialIncrease = Math.Abs(newLevel - prevLevel) >= 1.0f;
if ((int)newLevel > (int)prevLevel)
{
int increase = Math.Max((int)newLevel - (int)prevLevel, 1);
GUI.AddMessage(
string.Format("+{0} {1}", increase, TextManager.Get("SkillName." + skillIdentifier)),
GUI.Style.Green,
string.Format("+{0} {1}", increase, TextManager.Get("SkillName." + skillIdentifier)),
specialIncrease ? GUI.Style.Orange : GUI.Style.Green,
textPopupPos,
Vector2.UnitY * 10.0f,
playSound: false,
playSound: specialIncrease,
subId: Character?.Submarine?.ID ?? -1);
}
}
partial void OnExperienceChanged(int prevAmount, int newAmount, Vector2 textPopupPos)
{
if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; }
GameSession.TabMenuInstance?.OnExperienceChanged(Character);
if (newAmount > prevAmount)
{
int increase = newAmount - prevAmount;
GUI.AddMessage(
string.Format("+{0} {1}", increase, TextManager.Get("experienceshort")),
GUI.Style.Blue,
textPopupPos,
Vector2.UnitY * 10.0f,
playSound: true,
subId: Character?.Submarine?.ID ?? -1);
}
}
@@ -591,6 +613,17 @@ namespace Barotrauma
}
ch.Job.Skills.RemoveAll(s => !skillLevels.ContainsKey(s.Identifier));
}
byte savedStatValueCount = inc.ReadByte();
for (int i = 0; i < savedStatValueCount; i++)
{
int statType = inc.ReadByte();
string statIdentifier = inc.ReadString();
float statValue = inc.ReadSingle();
bool removeOnDeath = inc.ReadBoolean();
ch.ChangeSavedStatValue((StatTypes)statType, statValue, statIdentifier, removeOnDeath);
}
return ch;
}
}

View File

@@ -119,15 +119,23 @@ namespace Barotrauma
switch ((NetEntityEvent.Type)extraData[0])
{
case NetEntityEvent.Type.InventoryState:
msg.WriteRangedInteger(0, 0, 3);
msg.WriteRangedInteger(0, 0, 4);
Inventory.ClientWrite(msg, extraData);
break;
case NetEntityEvent.Type.Treatment:
msg.WriteRangedInteger(1, 0, 3);
msg.WriteRangedInteger(1, 0, 4);
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
break;
case NetEntityEvent.Type.Status:
msg.WriteRangedInteger(2, 0, 3);
msg.WriteRangedInteger(2, 0, 4);
break;
case NetEntityEvent.Type.UpdateTalents:
msg.WriteRangedInteger(3, 0, 4);
msg.Write((ushort)characterTalents.Count);
foreach (var unlockedTalent in characterTalents)
{
msg.Write(unlockedTalent.Prefab.UIntIdentifier);
}
break;
}
}
@@ -258,7 +266,7 @@ namespace Barotrauma
if (readStatus)
{
ReadStatus(msg);
(AIController as EnemyAIController)?.PetBehavior?.ClientRead(msg);
AIController?.ClientRead(msg);
}
msg.ReadPadBits();
@@ -291,7 +299,7 @@ namespace Barotrauma
break;
case ServerNetObject.ENTITY_EVENT:
int eventType = msg.ReadRangedInteger(0, 9);
int eventType = msg.ReadRangedInteger(0, 12);
switch (eventType)
{
case 0: //NetEntityEvent.Type.InventoryState
@@ -387,6 +395,7 @@ namespace Barotrauma
if (eventType == 4)
{
SetAttackTarget(attackLimb, targetEntity, targetSimPos);
PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
}
else
{
@@ -450,6 +459,23 @@ namespace Barotrauma
}
}
break;
case 10: //NetEntityEvent.Type.UpdateExperience
int experienceAmount = msg.ReadInt32();
info?.SetExperience(experienceAmount);
break;
case 11: //NetEntityEvent.Type.UpdateTalents:
ushort talentCount = msg.ReadUInt16();
for (int i = 0; i < talentCount; i++)
{
UInt32 talentIdentifier = msg.ReadUInt32();
GiveTalent(talentIdentifier);
}
break;
case 12: //NetEntityEvent.Type.UpdateMoney:
int moneyAmount = msg.ReadInt32();
SetMoney(moneyAmount);
break;
}
msg.ReadPadBits();
break;

View File

@@ -543,7 +543,7 @@ namespace Barotrauma
else
{
var causeOfDeath = GetCauseOfDeath();
Character.Controlled.Kill(causeOfDeath.First, causeOfDeath.Second);
Character.Controlled.Kill(causeOfDeath.type, causeOfDeath.affliction);
Character.Controlled = null;
}
}
@@ -683,19 +683,33 @@ namespace Barotrauma
float particleMaxScale = emitter?.Prefab.Properties.ScaleMax ?? 1;
float severity = Math.Min(affliction.Strength / affliction.Prefab.MaxStrength * Character.Params.BleedParticleMultiplier, 1);
float bloodParticleSize = MathHelper.Lerp(particleMinScale, particleMaxScale, severity);
Vector2 velocity = Rand.Vector(affliction.Strength * 0.1f);
if (!inWater)
{
bloodParticleSize *= 2.0f;
velocity = targetLimb.LinearVelocity * 100.0f;
}
// TODO: use the blood emitter?
var blood = GameMain.ParticleManager.CreateParticle(
inWater ? Character.Params.BleedParticleWater : Character.Params.BleedParticleAir,
targetLimb.WorldPosition, Rand.Vector(affliction.Strength), 0.0f, Character.AnimController.CurrentHull);
targetLimb.WorldPosition, velocity, 0.0f, Character.AnimController.CurrentHull);
if (blood != null)
if (blood != null && !inWater)
{
blood.Size *= bloodParticleSize;
if (!string.IsNullOrEmpty(Character.BloodDecalName) && Rand.Range(0.0f, 1.0f) < 0.05f)
{
blood.OnCollision += (Vector2 pos, Hull hull) =>
{
var decal = hull?.AddDecal(Character.BloodDecalName, pos, Rand.Range(1.0f, 2.0f), isNetworkEvent: true);
if (decal != null)
{
decal.FadeTimer = decal.LifeTime - decal.FadeOutTime * 2;
}
};
}
}
bloodParticleTimer = MathHelper.Lerp(2, 0.5f, severity);
}
@@ -1968,9 +1982,9 @@ namespace Barotrauma
healthBarHolder.Visible = value;
}
private readonly List<Pair<AfflictionPrefab, float>> newAfflictions = new List<Pair<AfflictionPrefab, float>>();
private readonly List<Triplet<LimbHealth, AfflictionPrefab, float>> newLimbAfflictions = new List<Triplet<LimbHealth, AfflictionPrefab, float>>();
private readonly List<Pair<AfflictionPrefab.PeriodicEffect, float>> newPeriodicEffects = new List<Pair<AfflictionPrefab.PeriodicEffect, float>>();
private readonly List<(AfflictionPrefab afflictionPrefab, float strength)> newAfflictions = new List<(AfflictionPrefab afflictionPrefab, float strength)>();
private readonly List<(LimbHealth limb, AfflictionPrefab afflictionPrefab, float strength)> newLimbAfflictions = new List<(LimbHealth limb, AfflictionPrefab afflictionPrefab, float strength)>();
private readonly List<(AfflictionPrefab.PeriodicEffect effect, float timer)> newPeriodicEffects = new List<(AfflictionPrefab.PeriodicEffect effect, float timer)>();
public void ClientRead(IReadMessage inc)
{
@@ -1997,41 +2011,41 @@ namespace Barotrauma
for (int j = 0; j < periodicAfflictionCount; j++)
{
float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
newPeriodicEffects.Add(new Pair<AfflictionPrefab.PeriodicEffect, float>(afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
newPeriodicEffects.Add((afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
}
newAfflictions.Add(new Pair<AfflictionPrefab, float>(afflictionPrefab, afflictionStrength));
newAfflictions.Add((afflictionPrefab, afflictionStrength));
}
foreach (Affliction affliction in afflictions)
{
//deactivate afflictions that weren't included in the network message
if (!newAfflictions.Any(a => a.First == affliction.Prefab))
if (!newAfflictions.Any(a => a.afflictionPrefab == affliction.Prefab))
{
affliction.Strength = 0.0f;
}
}
foreach (Pair<AfflictionPrefab, float> newAffliction in newAfflictions)
foreach (var (afflictionPrefab, strength) in newAfflictions)
{
Affliction existingAffliction = afflictions.Find(a => a.Prefab == newAffliction.First);
Affliction existingAffliction = afflictions.Find(a => a.Prefab == afflictionPrefab);
if (existingAffliction == null)
{
existingAffliction = newAffliction.First.Instantiate(newAffliction.Second);
existingAffliction = afflictionPrefab.Instantiate(strength);
afflictions.Add(existingAffliction);
}
existingAffliction.SetStrength(newAffliction.Second);
existingAffliction.SetStrength(strength);
if (existingAffliction == stunAffliction)
{
Character.SetStun(existingAffliction.Strength, true, true);
}
foreach (var periodicEffect in newPeriodicEffects)
{
if (!existingAffliction.Prefab.PeriodicEffects.Contains(periodicEffect.First)) { continue; }
if (!existingAffliction.Prefab.PeriodicEffects.Contains(periodicEffect.effect)) { continue; }
//timer has wrapped around, apply the effect
if (periodicEffect.Second - existingAffliction.PeriodicEffectTimers[periodicEffect.First] > periodicEffect.First.MinInterval / 2)
if (periodicEffect.timer - existingAffliction.PeriodicEffectTimers[periodicEffect.effect] > periodicEffect.effect.MinInterval / 2)
{
existingAffliction.PeriodicEffectTimers[periodicEffect.First] = periodicEffect.Second;
foreach (StatusEffect effect in periodicEffect.First.StatusEffects)
existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer;
foreach (StatusEffect effect in periodicEffect.effect.StatusEffects)
{
existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: null);
}
@@ -2063,9 +2077,9 @@ namespace Barotrauma
for (int j = 0; j < periodicAfflictionCount; j++)
{
float periodicAfflictionTimer = inc.ReadRangedSingle(afflictionPrefab.PeriodicEffects[j].MinInterval, afflictionPrefab.PeriodicEffects[j].MaxInterval, 8);
newPeriodicEffects.Add(new Pair<AfflictionPrefab.PeriodicEffect, float>(afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
newPeriodicEffects.Add((afflictionPrefab.PeriodicEffects[j], periodicAfflictionTimer));
}
newLimbAfflictions.Add(new Triplet<LimbHealth, AfflictionPrefab, float>(limbHealths[limbIndex], afflictionPrefab, afflictionStrength));
newLimbAfflictions.Add((limbHealths[limbIndex], afflictionPrefab, afflictionStrength));
}
foreach (LimbHealth limbHealth in limbHealths)
@@ -2073,33 +2087,33 @@ namespace Barotrauma
foreach (Affliction affliction in limbHealth.Afflictions)
{
//deactivate afflictions that weren't included in the network message
if (!newLimbAfflictions.Any(a => a.First == limbHealth && a.Second == affliction.Prefab))
if (!newLimbAfflictions.Any(a => a.limb == limbHealth && a.afflictionPrefab == affliction.Prefab))
{
affliction.Strength = 0.0f;
}
}
foreach (Triplet<LimbHealth, AfflictionPrefab, float> newAffliction in newLimbAfflictions)
foreach (var (limb, afflictionPrefab, strength) in newLimbAfflictions)
{
if (newAffliction.First != limbHealth) { continue; }
Affliction existingAffliction = limbHealth.Afflictions.Find(a => a.Prefab == newAffliction.Second);
if (limb != limbHealth) { continue; }
Affliction existingAffliction = limbHealth.Afflictions.Find(a => a.Prefab == afflictionPrefab);
if (existingAffliction == null)
{
existingAffliction = newAffliction.Second.Instantiate(newAffliction.Third);
existingAffliction = afflictionPrefab.Instantiate(strength);
limbHealth.Afflictions.Add(existingAffliction);
}
existingAffliction.SetStrength(newAffliction.Third);
existingAffliction.SetStrength(strength);
foreach (var periodicEffect in newPeriodicEffects)
{
if (!existingAffliction.Prefab.PeriodicEffects.Contains(periodicEffect.First)) { continue; }
if (!existingAffliction.Prefab.PeriodicEffects.Contains(periodicEffect.effect)) { continue; }
//timer has wrapped around, apply the effect
if (periodicEffect.Second - existingAffliction.PeriodicEffectTimers[periodicEffect.First] > periodicEffect.First.MinInterval / 2)
if (periodicEffect.timer - existingAffliction.PeriodicEffectTimers[periodicEffect.effect] > periodicEffect.effect.MinInterval / 2)
{
existingAffliction.PeriodicEffectTimers[periodicEffect.First] = periodicEffect.Second;
foreach (StatusEffect effect in periodicEffect.First.StatusEffects)
existingAffliction.PeriodicEffectTimers[periodicEffect.effect] = periodicEffect.timer;
foreach (StatusEffect effect in periodicEffect.effect.StatusEffects)
{
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == limbHealths.IndexOf(newAffliction.First));
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == limbHealths.IndexOf(limb));
existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: targetLimb);
}
}

View File

@@ -418,14 +418,14 @@ namespace Barotrauma
ShowQuestionPrompt("The automatic hull generation may not work correctly if your submarine uses curved walls. Do you want to continue? Y/N",
(option2) =>
{
if (option2.ToLower() == "y") { GameMain.SubEditorScreen.AutoHull(); }
if (option2.ToLowerInvariant() == "y") { GameMain.SubEditorScreen.AutoHull(); }
});
});
}
else
{
ShowQuestionPrompt("The automatic hull generation may not work correctly if your submarine uses curved walls. Do you want to continue? Y/N",
(option) => { if (option.ToLower() == "y") GameMain.SubEditorScreen.AutoHull(); });
(option) => { if (option.ToLowerInvariant() == "y") GameMain.SubEditorScreen.AutoHull(); });
}
}));
@@ -608,7 +608,7 @@ namespace Barotrauma
ShowQuestionPrompt($"Some keybinds may render the game unusable, are you sure you want to make these keybinds persistent? ({Keybinds.Count} keybind(s) assigned) Y/N",
(option2) =>
{
if (option2.ToLower() != "y")
if (option2.ToLowerInvariant() != "y")
{
NewMessage("Aborted.", GUI.Style.Red);
return;
@@ -689,6 +689,9 @@ namespace Barotrauma
AssignRelayToServer("setskill", true);
AssignRelayToServer("readycheck", true);
AssignRelayToServer("givetalent", true);
AssignRelayToServer("giveexperience", true);
AssignOnExecute("control", (string[] args) =>
{
if (args.Length < 1) return;

View File

@@ -163,6 +163,7 @@ namespace Barotrauma
public static ScalableFont SubHeadingFont => Style?.SubHeadingFont;
public static ScalableFont DigitalFont => Style?.DigitalFont;
public static ScalableFont HotkeyFont => Style?.HotkeyFont;
public static ScalableFont MonospacedFont => Style?.MonospacedFont;
public static ScalableFont CJKFont { get; private set; }
@@ -306,7 +307,7 @@ namespace Barotrauma
});
SubmarineIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(452, 385, 182, 81), new Vector2(0.5f, 0.5f));
arrow = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(392, 393, 49, 45), new Vector2(0.5f, 0.5f));
arrow = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(393, 393, 49, 45), new Vector2(0.5f, 0.5f));
SpeechBubbleIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(385, 449, 66, 60), new Vector2(0.5f, 0.5f));
BrokenIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(898, 386, 123, 123), new Vector2(0.5f, 0.5f));
}
@@ -672,6 +673,12 @@ namespace Barotrauma
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: SamplerStateClamp, rasterizerState: GameMain.ScissorTestEnable);
if (GameMain.GameSession?.CrewManager is { DraggedOrder: { SymbolSprite: { } orderSprite, Color: var color }, DragOrder: true })
{
float spriteSize = Math.Max(orderSprite.size.X, orderSprite.size.Y);
orderSprite.Draw(spriteBatch, PlayerInput.LatestMousePosition, color, orderSprite.size / 2f, scale: 32f / spriteSize * Scale);
}
var sprite = MouseCursorSprites[(int)MouseCursor] ?? MouseCursorSprites[(int)CursorState.Default];
sprite.Draw(spriteBatch, PlayerInput.LatestMousePosition, Color.White, sprite.Origin, 0f, Scale / 1.5f);
@@ -927,13 +934,14 @@ namespace Barotrauma
GUIComponent prevMouseOn = MouseOn;
MouseOn = null;
int inventoryIndex = -1;
if (Inventory.IsMouseOnInventory())
Inventory.RefreshMouseOnInventory();
if (Inventory.IsMouseOnInventory)
{
inventoryIndex = updateList.IndexOf(CharacterHUD.HUDFrame);
}
if (!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked())
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || prevMouseOn == null)
{
for (var i = updateList.Count - 1; i > inventoryIndex; i--)
{
@@ -941,10 +949,9 @@ namespace Barotrauma
if (!c.CanBeFocused) { continue; }
if (c.MouseRect.Contains(PlayerInput.MousePosition))
{
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || c == prevMouseOn)
if ((!PlayerInput.PrimaryMouseButtonHeld() && !PlayerInput.PrimaryMouseButtonClicked()) || c == prevMouseOn || prevMouseOn == null)
{
MouseOn = c;
var sakdjfnsjkd = c.MouseRect;
}
break;
}
@@ -958,7 +965,6 @@ namespace Barotrauma
MouseCursor = UpdateMouseCursorState(MouseOn);
return MouseOn;
}
}
private static CursorState UpdateMouseCursorState(GUIComponent c)
@@ -1050,7 +1056,7 @@ namespace Barotrauma
{
if (listBox.DraggedElement != null) { return CursorState.Dragging; }
if (listBox.CanDragElements) { return CursorState.Move; }
var hoverParent = c;
while (true)
{
@@ -1059,14 +1065,14 @@ namespace Barotrauma
hoverParent = hoverParent.Parent;
}
}
if (parent != null && parent.CanBeFocused)
{
if (!parent.Rect.Equals(monitorRect)) { return parent.HoverCursor; }
}
}
if (Inventory.IsMouseOnInventory()) { return Inventory.GetInventoryMouseCursor(); }
if (Inventory.IsMouseOnInventory) { return Inventory.GetInventoryMouseCursor(); }
var character = Character.Controlled;
// ReSharper disable once InvertIf
@@ -1343,7 +1349,7 @@ namespace Barotrauma
/// <param name="createOffset">Should the indicator move based on the camera position?</param>
/// <param name="overrideAlpha">Override the distance-based alpha value with the specified alpha value</param>
public static void DrawIndicator(SpriteBatch spriteBatch, in Vector2 worldPosition, Camera cam, in Vector2 visibleRange, Sprite sprite, in Color color,
public static void DrawIndicator(SpriteBatch spriteBatch, in Vector2 worldPosition, Camera cam, in Range<float> visibleRange, Sprite sprite, in Color color,
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null)
{
Vector2 diff = worldPosition - cam.WorldViewCenter;
@@ -1351,9 +1357,9 @@ namespace Barotrauma
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier * Scale;
if (overrideAlpha.HasValue || (dist > visibleRange.X && dist < visibleRange.Y))
if (overrideAlpha.HasValue || (dist > visibleRange.Start && dist < visibleRange.End))
{
float alpha = overrideAlpha ?? MathUtils.Min((dist - visibleRange.X) / 100.0f, 1.0f - ((dist - visibleRange.Y + 100f) / 100.0f), 1.0f);
float alpha = overrideAlpha ?? MathUtils.Min((dist - visibleRange.Start) / 100.0f, 1.0f - ((dist - visibleRange.End + 100f) / 100.0f), 1.0f);
Vector2 targetScreenPos = cam.WorldToScreen(worldPosition);
if (!createOffset)
@@ -1417,7 +1423,7 @@ namespace Barotrauma
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color,
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null)
{
DrawIndicator(spriteBatch, worldPosition, cam, new Vector2(hideDist, float.PositiveInfinity), sprite, color, createOffset, scaleMultiplier, overrideAlpha);
DrawIndicator(spriteBatch, worldPosition, cam, new Range<float>(hideDist, float.PositiveInfinity), sprite, color, createOffset, scaleMultiplier, overrideAlpha);
}
public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, float width = 1)
@@ -1520,6 +1526,11 @@ namespace Barotrauma
}
}
public static void DrawFilledRectangle(SpriteBatch sb, RectangleF rect, Color clr, float depth = 0.0f)
{
DrawFilledRectangle(sb, rect.Location, rect.Size, clr, depth);
}
public static void DrawFilledRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, float depth = 0.0f)
{
if (size.X < 0)

View File

@@ -52,13 +52,13 @@ namespace Barotrauma
public GUIComponent GetChild(int index)
{
if (index < 0 || index >= CountChildren) return null;
if (index < 0 || index >= CountChildren) { return null; }
return RectTransform.GetChild(index).GUIComponent;
}
public int GetChildIndex(GUIComponent child)
{
if (child == null) return -1;
if (child == null) { return -1; }
return RectTransform.GetChildIndex(child.RectTransform);
}
@@ -66,7 +66,7 @@ namespace Barotrauma
{
foreach (GUIComponent child in Children)
{
if (child.UserData == obj || (child.userData != null && child.userData.Equals(obj))) return child;
if (child.UserData == obj || (child.userData != null && child.userData.Equals(obj))) { return child; }
}
return null;
}
@@ -175,6 +175,8 @@ namespace Barotrauma
public bool GlowOnSelect { get; set; }
public Vector2 UVOffset { get; set; }
private CoroutineHandle pulsateCoroutine;
protected Color flashColor;
@@ -256,9 +258,9 @@ namespace Barotrauma
protected Rectangle ClampRect(Rectangle r)
{
if (Parent == null || !ClampMouseRectToParent) return r;
if (Parent == null || !ClampMouseRectToParent) { return r; }
Rectangle parentRect = Parent.ClampRect(Parent.Rect);
if (parentRect.Width <= 0 || parentRect.Height <= 0) return Rectangle.Empty;
if (parentRect.Width <= 0 || parentRect.Height <= 0) { return Rectangle.Empty; }
if (parentRect.X > r.X)
{
int diff = parentRect.X - r.X;
@@ -281,7 +283,7 @@ namespace Barotrauma
int diff = (r.Y + r.Height) - (parentRect.Y + parentRect.Height);
r.Height -= diff;
}
if (r.Width <= 0 || r.Height <= 0) return Rectangle.Empty;
if (r.Width <= 0 || r.Height <= 0) { return Rectangle.Empty; }
return r;
}
@@ -295,7 +297,7 @@ namespace Barotrauma
{
get
{
if (!CanBeFocused) return Rectangle.Empty;
if (!CanBeFocused) { return Rectangle.Empty; }
return ClampMouseRectToParent ? ClampRect(Rect) : Rect;
}
}
@@ -431,7 +433,7 @@ namespace Barotrauma
#region Updating
public virtual void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
{
if (!Visible) return;
if (!Visible) { return; }
UpdateOrder = order;
GUI.AddToUpdateList(this);
@@ -463,7 +465,7 @@ namespace Barotrauma
/// </summary>
public void UpdateManually(float deltaTime, bool alsoChildren = false, bool recursive = true)
{
if (!Visible) return;
if (!Visible) { return; }
AutoUpdate = false;
Update(deltaTime);
@@ -475,7 +477,7 @@ namespace Barotrauma
protected virtual void Update(float deltaTime)
{
if (!Visible) return;
if (!Visible) { return; }
if (CanBeFocused && OnSecondaryClicked != null)
{
@@ -555,7 +557,7 @@ namespace Barotrauma
/// </summary>
public virtual void DrawManually(SpriteBatch spriteBatch, bool alsoChildren = false, bool recursive = true)
{
if (!Visible) return;
if (!Visible) { return; }
AutoDraw = false;
Draw(spriteBatch);
@@ -598,7 +600,7 @@ namespace Barotrauma
protected virtual void Draw(SpriteBatch spriteBatch)
{
if (!Visible) return;
if (!Visible) { return; }
var rect = Rect;
GetBlendedColor(GetColor(State), ref _currentColor);
@@ -653,7 +655,7 @@ namespace Barotrauma
? MathUtils.InverseLerp(0, SpriteCrossFadeTime, ToolBox.GetEasing(uiSprite.TransitionMode, spriteFadeTimer)) : 0;
if (alphaMultiplier > 0)
{
uiSprite.Draw(spriteBatch, rect, previousColor * alphaMultiplier, SpriteEffects);
uiSprite.Draw(spriteBatch, rect, previousColor * alphaMultiplier, SpriteEffects, uvOffset: UVOffset);
}
}
}
@@ -667,7 +669,11 @@ namespace Barotrauma
? MathUtils.InverseLerp(SpriteCrossFadeTime, 0, ToolBox.GetEasing(uiSprite.TransitionMode, spriteFadeTimer)) : (_currentColor.A / 255.0f);
if (alphaMultiplier > 0)
{
uiSprite.Draw(spriteBatch, rect, _currentColor * alphaMultiplier, SpriteEffects);
// * (rect.Location.Y - PlayerInput.MousePosition.Y) / rect.Height
Vector2 offset = new Vector2(
MathUtils.PositiveModulo((int)-UVOffset.X, uiSprite.Sprite.SourceRect.Width),
MathUtils.PositiveModulo((int)-UVOffset.Y, uiSprite.Sprite.SourceRect.Height));
uiSprite.Draw(spriteBatch, rect, _currentColor * alphaMultiplier, SpriteEffects, uvOffset: offset);
}
}
}
@@ -708,7 +714,7 @@ namespace Barotrauma
/// </summary>
public void DrawToolTip(SpriteBatch spriteBatch)
{
if (!Visible) return;
if (!Visible) { return; }
DrawToolTip(spriteBatch, ToolTip, GUI.MouseOn.Rect, TooltipRichTextData);
}
@@ -1048,7 +1054,7 @@ namespace Barotrauma
{
case "language":
string[] languages = element.GetAttributeStringArray(attribute.Name.ToString(), new string[0]);
if (!languages.Any(l => GameMain.Config.Language.ToLower() == l.ToLower())) { return false; }
if (!languages.Any(l => GameMain.Config.Language.Equals(l, StringComparison.OrdinalIgnoreCase))) { return false; }
break;
case "gameversion":
var version = new Version(attribute.Value);
@@ -1213,8 +1219,7 @@ namespace Barotrauma
private static GUIImage LoadGUIImage(XElement element, RectTransform parent)
{
Sprite sprite = null;
Sprite sprite;
string url = element.GetAttributeString("url", "");
if (!string.IsNullOrEmpty(url))
{

View File

@@ -28,7 +28,7 @@ namespace Barotrauma
if (OutlineColor != Color.Transparent)
{
GUI.DrawRectangle(spriteBatch, Rect, OutlineColor * (OutlineColor.A/255.0f), false, thickness: OutlineThickness);
GUI.DrawRectangle(spriteBatch, Rect, OutlineColor, false, thickness: OutlineThickness);
}
}
}

View File

@@ -33,7 +33,7 @@ namespace Barotrauma
public GUIFrame Content { get; private set; }
public GUIScrollBar ScrollBar { get; private set; }
private Dictionary<GUIComponent, bool> childVisible = new Dictionary<GUIComponent, bool>();
private readonly Dictionary<GUIComponent, bool> childVisible = new Dictionary<GUIComponent, bool>();
private int totalSize;
private bool childrenNeedsRecalculation;
@@ -224,7 +224,7 @@ namespace Barotrauma
{
if (value == false && canDragElements && draggedElement != null)
{
draggedElement = null;
DraggedElement = null;
}
canDragElements = value;
}
@@ -233,8 +233,21 @@ namespace Barotrauma
private GUIComponent draggedElement;
private Rectangle draggedReferenceRectangle;
private Point draggedReferenceOffset;
public bool HasDraggedElementIndexChanged { get; private set; }
public GUIComponent DraggedElement => draggedElement;
public GUIComponent DraggedElement
{
get
{
return draggedElement;
}
set
{
if (value == draggedElement) { return; }
draggedElement = value;
HasDraggedElementIndexChanged = false;
}
}
private readonly bool isHorizontal;
@@ -472,7 +485,7 @@ namespace Barotrauma
if (!PlayerInput.PrimaryMouseButtonHeld())
{
OnRearranged?.Invoke(this, draggedElement.UserData);
draggedElement = null;
DraggedElement = null;
RepositionChildren();
}
else
@@ -518,6 +531,7 @@ namespace Barotrauma
if (currIndex != index)
{
draggedElement.RectTransform.RepositionChildInHierarchy(currIndex);
HasDraggedElementIndexChanged = true;
}
return;
@@ -577,7 +591,7 @@ namespace Barotrauma
if (CanDragElements && PlayerInput.PrimaryMouseButtonDown() && GUI.MouseOn == child)
{
draggedElement = child;
DraggedElement = child;
draggedReferenceRectangle = child.Rect;
draggedReferenceOffset = child.RectTransform.AbsoluteOffset;
}
@@ -750,7 +764,7 @@ namespace Barotrauma
}
}
if ((GUI.IsMouseOn(this) || GUI.IsMouseOn(ScrollBar)) && AllowMouseWheelScroll && PlayerInput.ScrollWheelSpeed != 0)
if (PlayerInput.ScrollWheelSpeed != 0 && AllowMouseWheelScroll && (FindScrollableParentListBox(GUI.MouseOn) == this || GUI.IsMouseOn(ScrollBar)))
{
if (SmoothScroll)
{
@@ -773,7 +787,6 @@ namespace Barotrauma
ScrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * BarSize;
}
}
ScrollBar.Enabled = ScrollBarEnabled && BarSize < 1.0f;
if (AutoHideScrollBar)
@@ -785,6 +798,13 @@ namespace Barotrauma
UpdateDimensions();
}
}
private static GUIListBox FindScrollableParentListBox(GUIComponent target)
{
if (target is GUIListBox listBox && listBox.ScrollBarEnabled && listBox.BarSize < 1.0f) { return listBox; }
if (target?.Parent == null) { return null; }
return FindScrollableParentListBox(target.Parent);
}
public void SelectNext(bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
{
@@ -982,7 +1002,7 @@ namespace Barotrauma
if (child == null) { return; }
child.RectTransform.Parent = null;
if (selected.Contains(child)) { selected.Remove(child); }
if (draggedElement == child) { draggedElement = null; }
if (draggedElement == child) { DraggedElement = null; }
UpdateScrollBarSize();
}

View File

@@ -0,0 +1,87 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
public class GUIScissorComponent: GUIComponent
{
public GUIComponent Content;
public GUIScissorComponent(RectTransform rectT) : base(null, rectT)
{
Content = new GUIFrame(new RectTransform(Vector2.One, rectT), style: null)
{
CanBeFocused = false
};
}
protected override void Update(float deltaTime)
{
base.Update(deltaTime);
foreach (GUIComponent child in Children)
{
if (child == Content) { continue; }
throw new InvalidOperationException($"Children were found in {nameof(GUIScissorComponent)}, Add them to {nameof(GUIScissorComponent)}.{nameof(Content)} instead.");
}
ClampChildMouseRects(Content);
}
protected override void Draw(SpriteBatch spriteBatch)
{
if (!Visible) { return; }
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
RasterizerState prevRasterizerState = spriteBatch.GraphicsDevice.RasterizerState;
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, Rect);
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
foreach (GUIComponent child in Content.Children)
{
if (!child.Visible) { continue; }
child.DrawManually(spriteBatch, alsoChildren: true, recursive: true);
}
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: prevRasterizerState);
}
private void ClampChildMouseRects(GUIComponent child)
{
child.ClampMouseRectToParent = true;
if (child is GUIListBox) { return; }
foreach (GUIComponent grandChild in child.Children)
{
ClampChildMouseRects(grandChild);
}
}
public override void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
{
if (!Visible) { return; }
UpdateOrder = order;
GUI.AddToUpdateList(this);
if (ignoreChildren)
{
OnAddedToGUIUpdateList?.Invoke(this);
return;
}
foreach (GUIComponent child in Content.Children)
{
if (!child.Visible) { continue; }
child.AddToGUIUpdateList(false, order);
}
OnAddedToGUIUpdateList?.Invoke(this);
}
}
}

View File

@@ -25,6 +25,7 @@ namespace Barotrauma
public ScalableFont SubHeadingFont { get; private set; }
public ScalableFont DigitalFont { get; private set; }
public ScalableFont HotkeyFont { get; private set; }
public ScalableFont MonospacedFont { get; private set; }
public Dictionary<ScalableFont, bool> ForceFontUpperCase
{
@@ -40,11 +41,16 @@ namespace Barotrauma
public SpriteSheet SavingIndicator { get; private set; }
public UISprite UIGlow { get; private set; }
public UISprite PingCircle { get; private set; }
public UISprite UIGlowCircular { get; private set; }
public UISprite ButtonPulse { get; private set; }
public SpriteSheet FocusIndicator { get; private set; }
public UISprite IconOverflowIndicator { get; private set; }
/// <summary>
/// General green color used for elements whose colors are set from code
@@ -235,6 +241,9 @@ namespace Barotrauma
case "uiglow":
UIGlow = new UISprite(subElement);
break;
case "pingcircle":
PingCircle = new UISprite(subElement);
break;
case "radiation":
RadiationSprite = new UISprite(subElement);
break;
@@ -247,6 +256,9 @@ namespace Barotrauma
case "endroundbuttonpulse":
ButtonPulse = new UISprite(subElement);
break;
case "iconoverflowindicator":
IconOverflowIndicator = new UISprite(subElement);
break;
case "focusindicator":
FocusIndicator = new SpriteSheet(subElement);
break;
@@ -277,6 +289,10 @@ namespace Barotrauma
DigitalFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[DigitalFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "monospacedfont":
MonospacedFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[MonospacedFont] = subElement.GetAttributeBool("forceuppercase", false);
break;
case "hotkeyfont":
HotkeyFont = LoadFont(subElement, graphicsDevice);
ForceFontUpperCase[HotkeyFont] = subElement.GetAttributeBool("forceuppercase", false);

View File

@@ -185,7 +185,7 @@ namespace Barotrauma
if (GUI.MouseOn != null) { return false; }
//don't close when hovering over an inventory element
if (Inventory.IsMouseOnInventory()) { return false; }
if (Inventory.IsMouseOnInventory) { return false; }
bool input = PlayerInput.PrimaryMouseButtonDown() || PlayerInput.SecondaryMouseButtonClicked();
return input && !rect.Contains(PlayerInput.MousePosition);

View File

@@ -193,7 +193,7 @@ namespace Barotrauma
if (LoadState == 100.0f)
{
#if DEBUG
if (GameMain.Config.AutomaticQuickStartEnabled || GameMain.Config.AutomaticCampaignLoadEnabled && GameMain.FirstLoad)
if (GameMain.Config.AutomaticQuickStartEnabled || GameMain.Config.AutomaticCampaignLoadEnabled || GameMain.Config.TestScreenEnabled && GameMain.FirstLoad)
{
loadText = "QUICKSTARTING ...";
}

View File

@@ -55,14 +55,47 @@ namespace Barotrauma
var texture = GetTexture(spriteBatch);
for (var i = 0; i < points.Count - 1; i++)
DrawPolygonEdge(spriteBatch, texture, points[i] + offset, points[i + 1] + offset, color, thickness);
DrawPolygonEdge(spriteBatch, points[i] + offset, points[i + 1] + offset, color, thickness);
DrawPolygonEdge(spriteBatch, texture, points[points.Count - 1] + offset, points[0] + offset, color,
DrawPolygonEdge(spriteBatch, points[points.Count - 1] + offset, points[0] + offset, color,
thickness);
}
/// <summary>
/// Draws a closed polygon from an array of points
/// </summary>
public static void DrawPolygonInner(this SpriteBatch spriteBatch, Vector2 offset, IReadOnlyList<Vector2> points, Color color, float thickness = 1f)
{
if (points.Count == 0) { return; }
private static void DrawPolygonEdge(SpriteBatch spriteBatch, Texture2D texture, Vector2 point1, Vector2 point2,
Color color, float thickness)
if (points.Count == 1)
{
DrawPoint(spriteBatch, points[0], color, (int)thickness);
return;
}
for (var i = 0; i < points.Count - 1; i++)
{
Vector2 point1 = points[i] + offset,
point2 = points[i + 1] + offset;
DrawPolygonEdgeInner(spriteBatch, point1, point2, color, thickness);
}
DrawPolygonEdgeInner(spriteBatch, points[^1] + offset, points[0] + offset, color, thickness);
}
private static void DrawPolygonEdgeInner(SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, float thickness)
{
var length = Vector2.Distance(point1, point2) + thickness;
var angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
var scale = new Vector2(length, thickness);
Vector2 middle = new Vector2((point1.X + point2.X) / 2f, (point1.Y + point2.Y) / 2f);
Texture2D tex = GetTexture(spriteBatch);
spriteBatch.Draw(GetTexture(spriteBatch), middle, null, color, angle, new Vector2(tex.Width / 2f, tex.Height / 2f), scale, SpriteEffects.None, 0);
}
private static void DrawPolygonEdge(SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, float thickness)
{
var length = Vector2.Distance(point1, point2);
var angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);

View File

@@ -37,7 +37,7 @@ namespace Barotrauma
private Color storeSpecialColor;
private GUIListBox shoppingCrateBuyList, shoppingCrateSellList, shoppingCrateSellFromSubList;
private GUITextBlock shoppingCrateTotal;
private GUITextBlock relevantBalanceName, shoppingCrateTotal;
private GUIButton clearAllButton, confirmButton;
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh, needsSellingFromSubRefresh, needsItemsToSellFromSubRefresh;
@@ -58,7 +58,6 @@ namespace Barotrauma
StoreTab.SellFromSub => false,
_ => throw new NotImplementedException()
};
private bool IsSelling => !IsBuying;
private GUIListBox ActiveShoppingCrateList => activeTab switch
{
StoreTab.Buy => shoppingCrateBuyList,
@@ -222,16 +221,8 @@ namespace Barotrauma
TextScale = 1.1f,
TextGetter = () =>
{
if (CurrentLocation != null)
{
merchantBalanceBlock.TextColor = CurrentLocation.BalanceColor;
return GetCurrencyFormatted(CurrentLocation.StoreCurrentBalance);
}
else
{
merchantBalanceBlock.TextColor = Color.Red;
return GetCurrencyFormatted(0);
}
merchantBalanceBlock.TextColor = CurrentLocation?.BalanceColor ?? Color.Red;
return GetMerchantBalanceText();
}
};
@@ -375,28 +366,37 @@ namespace Barotrauma
//don't show categories with no buyable items
itemCategories.RemoveAll(c => !ItemPrefab.Prefabs.Any(ep => ep.Category.HasFlag(c) && ep.CanBeBought));
itemCategoryButtons.Clear();
var categoryButton = new GUIButton(new RectTransform(new Point(categoryButtonContainer.Rect.Width, categoryButtonContainer.Rect.Width), categoryButtonContainer.RectTransform), style: "CategoryButton.All")
{
ToolTip = TextManager.Get("MapEntityCategory.All"),
OnClicked = OnClickedCategoryButton
};
itemCategoryButtons.Add(categoryButton);
foreach (MapEntityCategory category in itemCategories)
{
var categoryButton = new GUIButton(new RectTransform(new Point(categoryButtonContainer.Rect.Width, categoryButtonContainer.Rect.Width), categoryButtonContainer.RectTransform),
categoryButton = new GUIButton(new RectTransform(new Point(categoryButtonContainer.Rect.Width, categoryButtonContainer.Rect.Width), categoryButtonContainer.RectTransform),
style: "CategoryButton." + category)
{
ToolTip = TextManager.Get("MapEntityCategory." + category),
UserData = category,
OnClicked = (btn, userdata) =>
{
MapEntityCategory? newCategory = !btn.Selected ? (MapEntityCategory?)userdata : null;
if (newCategory.HasValue) { searchBox.Text = ""; }
if (newCategory != selectedItemCategory) { tabLists[activeTab].ScrollBar.BarScroll = 0f; }
FilterStoreItems(newCategory, searchBox.Text);
return true;
}
OnClicked = OnClickedCategoryButton
};
itemCategoryButtons.Add(categoryButton);
categoryButton.RectTransform.SizeChanged += () =>
}
bool OnClickedCategoryButton(GUIButton button, object userData)
{
MapEntityCategory? newCategory = !button.Selected ? (MapEntityCategory?)userData : null;
if (newCategory.HasValue) { searchBox.Text = ""; }
if (newCategory != selectedItemCategory) { tabLists[activeTab].ScrollBar.BarScroll = 0f; }
FilterStoreItems(newCategory, searchBox.Text);
return true;
}
foreach (var btn in itemCategoryButtons)
{
btn.RectTransform.SizeChanged += () =>
{
var sprite = categoryButton.Frame.sprites[GUIComponent.ComponentState.None].First();
categoryButton.RectTransform.NonScaledSize =
new Point(categoryButton.Rect.Width, (int)(categoryButton.Rect.Width * ((float)sprite.Sprite.SourceRect.Height / sprite.Sprite.SourceRect.Width)));
var sprite = btn.Frame.sprites[GUIComponent.ComponentState.None].First();
btn.RectTransform.NonScaledSize = new Point(btn.Rect.Width, (int)(btn.Rect.Width * ((float)sprite.Sprite.SourceRect.Height / sprite.Sprite.SourceRect.Width)));
};
}
@@ -503,11 +503,11 @@ namespace Barotrauma
ForceUpperCase = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform),
"", font: GUI.SubHeadingFont, textAlignment: Alignment.TopRight)
"", textColor: Color.White, font: GUI.SubHeadingFont, textAlignment: Alignment.TopRight)
{
AutoScaleVertical = true,
TextScale = 1.1f,
TextGetter = () => GetCurrencyFormatted(PlayerMoney)
TextGetter = GetPlayerBalanceText
};
// Divider ------------------------------------------------
@@ -523,7 +523,7 @@ namespace Barotrauma
RelativeSpacing = 0.015f,
Stretch = true
};
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.85f), shoppingCrateInventoryContainer.RectTransform), style: null);
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.8f), shoppingCrateInventoryContainer.RectTransform), style: null);
shoppingCrateBuyList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
if (GameMain.IsSingleplayer)
@@ -531,6 +531,21 @@ namespace Barotrauma
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
}
var relevantBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true)
{
Stretch = true
};
relevantBalanceName = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), relevantBalanceContainer.RectTransform), "", font: GUI.Font)
{
CanBeFocused = false
};
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), relevantBalanceContainer.RectTransform), "", textColor: Color.White, font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
{
CanBeFocused = false,
TextScale = 1.1f,
TextGetter = () => IsBuying ? GetPlayerBalanceText() : GetMerchantBalanceText()
};
var totalContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true)
{
Stretch = true
@@ -576,6 +591,10 @@ namespace Barotrauma
resolutionWhenCreated = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
private string GetMerchantBalanceText() => GetCurrencyFormatted(CurrentLocation?.StoreCurrentBalance ?? 0);
private string GetPlayerBalanceText() => GetCurrencyFormatted(PlayerMoney);
private GUILayoutGroup CreateDealsGroup(GUIListBox parentList)
{
var elementHeight = (int)(GUI.yScale * 80);
@@ -604,17 +623,14 @@ namespace Barotrauma
{
prevLocation.Reputation.OnReputationValueChanged = null;
}
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
if (ItemPrefab.Prefabs.Any(p => p.CanBeBoughtAtLocation(CurrentLocation, out PriceInfo _)))
{
if (itemPrefab.CanBeBoughtAtLocation(CurrentLocation, out PriceInfo _))
selectedItemCategory = null;
searchBox.Text = "";
ChangeStoreTab(StoreTab.Buy);
if (newLocation?.Reputation != null)
{
ChangeStoreTab(StoreTab.Buy);
if (newLocation?.Reputation != null)
{
newLocation.Reputation.OnReputationValueChanged += () => { needsRefresh = true; };
}
return;
newLocation.Reputation.OnReputationValueChanged += () => { needsRefresh = true; };
}
}
}
@@ -628,6 +644,7 @@ namespace Barotrauma
tabButton.Selected = (StoreTab)tabButton.UserData == activeTab;
}
sortingDropDown.SelectItem(tabSortingMethods[tab]);
relevantBalanceName.Text = IsBuying ? TextManager.Get("campaignstore.balance") : TextManager.Get("campaignstore.storebalance");
SetShoppingCrateTotalText();
SetClearAllButtonStatus();
SetConfirmButtonBehavior();
@@ -697,7 +714,7 @@ namespace Barotrauma
}
foreach (GUIButton btn in itemCategoryButtons)
{
btn.Selected = category.HasValue && (MapEntityCategory)btn.UserData == selectedItemCategory;
btn.Selected = (MapEntityCategory?)btn.UserData == selectedItemCategory;
}
list.UpdateScrollBarSize();
}

View File

@@ -18,7 +18,7 @@ namespace Barotrauma
private static UISprite spectateIcon, disconnectedIcon;
private static Sprite ownerIcon, moderatorIcon;
private enum InfoFrameTab { Crew, Mission, Reputation, MyCharacter, Traitor, Submarine };
private enum InfoFrameTab { Crew, Mission, Reputation, MyCharacter, Traitor, Submarine, Talents };
private static InfoFrameTab selectedTab;
private GUIFrame infoFrame, contentFrame;
@@ -258,6 +258,8 @@ namespace Barotrauma
{
var myCharacterButton = createTabButton(InfoFrameTab.MyCharacter, "tabmenu.character");
}
var talentsButton = createTabButton(InfoFrameTab.Talents, "tabmenu.talents");
}
private bool SelectInfoFrameTab(GUIButton button, object userData)
@@ -296,6 +298,9 @@ namespace Barotrauma
case InfoFrameTab.Submarine:
CreateSubmarineInfo(infoFrameHolder, Submarine.MainSub);
break;
case InfoFrameTab.Talents:
CreateTalentInfo(infoFrameHolder);
break;
}
return true;
@@ -1159,5 +1164,319 @@ namespace Barotrauma
sub.Info.CreateSpecsWindow(specsListBox, GUI.Font, includeTitle: false, includeClass: false, includeDescription: true);
}
}
private Color unselectedColor = new Color(240, 255, 255, 225);
private Color selectedColor = new Color(220, 255, 220, 225);
private Color ownedColor = new Color(140, 180, 140, 225);
private Color unselectableColor = new Color(100, 100, 100, 225);
private Color pressedColor = new Color(60, 60, 60, 225);
private readonly List<(GUIButton button, GUIImage background, GUIImage icon)> talentButtons = new List<(GUIButton button, GUIImage background, GUIImage icon)>();
private List<string> selectedTalents = new List<string>();
private GUITextBlock talentTitleText;
private GUITextBlock talentDescriptionText;
private GUITextBlock talentPointsText;
private GUITextBlock experienceText;
private Color experienceBackgroundColor = new Color(255, 255, 255, 155);
private GUIProgressBar experienceBar;
private void CreateTalentInfo(GUIFrame infoFrame)
{
infoFrame.ClearChildren();
talentButtons.Clear();
GUIFrame talentFrameBackground = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
int padding = GUI.IntScale(15);
GUIFrame talentFrameContent = new GUIFrame(new RectTransform(new Point(talentFrameBackground.Rect.Width - padding, talentFrameBackground.Rect.Height - padding), infoFrame.RectTransform, Anchor.Center), style: null);
Character controlledCharacter = Character.Controlled;
if (controlledCharacter.Info == null)
{
DebugConsole.ThrowError("No character info found for talent UI");
return;
}
selectedTalents = controlledCharacter.Info.UnlockedTalents.ToList();
GUILayoutGroup talentFrameLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), talentFrameContent.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = GUI.IntScale(5)
};
GUILayoutGroup talentInfoLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.325f), talentFrameLayoutGroup.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter);
GUIFrame talentTitleFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.25f), talentInfoLayoutGroup.RectTransform, Anchor.TopCenter), style: null);
talentTitleText = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), talentTitleFrame.RectTransform, Anchor.TopLeft), "", font: GUI.LargeFont);
talentPointsText = new GUITextBlock(new RectTransform(new Vector2(0.25f, 1.0f), talentTitleFrame.RectTransform, Anchor.TopRight), "", font: GUI.Font, textAlignment: Alignment.Center);
GUIFrame talentDescriptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.4f), talentInfoLayoutGroup.RectTransform, Anchor.TopCenter), style: null);
talentDescriptionText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), talentDescriptionFrame.RectTransform, Anchor.TopLeft), "", font: GUI.Font, textAlignment: Alignment.TopLeft, wrap: true);
GUIFrame characterInfoFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.3f), talentInfoLayoutGroup.RectTransform, Anchor.TopLeft), style: null);
GUILayoutGroup characterInfoColumn = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), characterInfoFrame.RectTransform, anchor: Anchor.TopLeft), childAnchor: Anchor.TopLeft, isHorizontal: true);
CreateCharacterSheet(characterInfoColumn);
if (!TalentTree.JobTalentTrees.TryGetValue(controlledCharacter.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return; }
GUIListBox talentTreeListBox = new GUIListBox(new RectTransform(new Vector2(1f, 0.6f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
int spacing = GUI.IntScale(5);
foreach (var subTree in talentTree.TalentSubTrees)
{
GUIFrame subTreeFrame = new GUIFrame(new RectTransform(new Vector2(0.333f, 1f), talentTreeListBox.Content.RectTransform, anchor: Anchor.TopLeft), style: null);
GUILayoutGroup subTreeLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), subTreeFrame.RectTransform, Anchor.Center), false, childAnchor: Anchor.TopCenter);
GUIFrame subtreeTitleFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.111f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: "SubtreeHeader");
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), subtreeTitleFrame.RectTransform, anchor: Anchor.TopCenter), subTree.Identifier, font: GUI.LargeFont, textAlignment: Alignment.Center);
for (int i = 0; i < 4; i++)
{
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.222f), subTreeLayoutGroup.RectTransform, anchor: Anchor.TopCenter), style: "TalentOptionFrame");
GUIImage talentBackground = new GUIImage(new RectTransform(Vector2.One, talentOptionFrame.RectTransform, anchor: Anchor.Center), style: "TalentBackground")
{
CanBeFocused = false,
Color = unselectableColor,
};
if (subTree.TalentOptionStages.Count > i)
{
TalentOption talentOption = subTree.TalentOptionStages[i];
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 1f), talentOptionFrame.RectTransform, Anchor.CenterLeft), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true,
};
foreach (Talent talent in talentOption.Talents)
{
int optionPadding = GUI.IntScale(10);
GUIFrame talentFrame = new GUIFrame(new RectTransform(new Point(talentOptionFrame.Rect.Width, talentOptionFrame.Rect.Height - optionPadding), talentOptionLayoutGroup.RectTransform), style: null)
{
CanBeFocused = false,
};
new GUIImage(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center), style: "TalentFrameBackground")
{
CanBeFocused = false,
};
GUIImage iconImage = null;
GUIButton talentButton = new GUIButton(new RectTransform(Vector2.One, talentFrame.RectTransform, anchor: Anchor.Center), style: "TalentFrame")
{
ToolTip = $"{TextManager.Get("talentname." + talent.Identifier, returnNull: true) ?? talent.Identifier} \n\n{TextManager.Get("talentdescription." + talent.Identifier, returnNull: true) ?? string.Empty}",
UserData = talent.Identifier,
PressedColor = pressedColor,
OnClicked = (button, userData) =>
{
talentTitleText.Text = TextManager.Get("talentname." + talent.Identifier, returnNull: true) ?? string.Empty;
talentDescriptionText.Text = TextManager.Get("talentdescription." + talent.Identifier, returnNull: true) ?? string.Empty;
// deselect other buttons in tier by removing their selected talents from pool
foreach (GUIButton guiButton in talentOptionLayoutGroup.GetAllChildren<GUIButton>())
{
if (guiButton.UserData is string otherTalentIdentifier && guiButton != button)
{
if (!controlledCharacter.HasTalent(otherTalentIdentifier))
{
selectedTalents.Remove(otherTalentIdentifier);
}
}
}
string talentIdentifier = userData as string;
if (TalentTree.IsViableTalentForCharacter(controlledCharacter, talentIdentifier, selectedTalents))
{
if (!selectedTalents.Contains(talentIdentifier))
{
selectedTalents.Add(talentIdentifier);
}
}
else if (!controlledCharacter.HasTalent(talentIdentifier))
{
selectedTalents.Remove(talentIdentifier);
}
UpdateTalentButtons();
return true;
},
};
int iconPadding = GUI.IntScale(15);
iconImage = new GUIImage(new RectTransform(new Point(talentFrame.Rect.Width - iconPadding, talentFrame.Rect.Height - iconPadding), talentFrame.RectTransform, anchor: Anchor.Center), sprite: talent.Icon, scaleToFit: true)
{
PressedColor = unselectableColor,
CanBeFocused = false,
};
talentButtons.Add((talentButton, talentBackground, iconImage));
}
}
}
}
GUIFrame talentBottomFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.07f), talentFrameLayoutGroup.RectTransform, Anchor.TopCenter), style: null);
GUIFrame experienceBarFrame = new GUIFrame(new RectTransform(new Vector2(0.775f, 0.75f), talentBottomFrame.RectTransform, Anchor.TopCenter), style: null);
experienceBar = new GUIProgressBar(new RectTransform(new Vector2(1f, 1f), experienceBarFrame.RectTransform, Anchor.CenterLeft),
barSize: controlledCharacter.Info.GetProgressTowardsNextLevel(), color: Color.White, style: "ExperienceBar")
{
IsHorizontal = true
};
GUIImage experienceTextBackground = new GUIImage(new RectTransform(new Vector2(0.2f, 0.475f), experienceBarFrame.RectTransform, anchor: Anchor.Center), style: "ExperienceTextBackground");
experienceText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), experienceTextBackground.RectTransform, anchor: Anchor.Center), "", font: GUI.Font, textColor: Color.White, textAlignment: Alignment.Center);
new GUIButton(new RectTransform(new Vector2(0.1f, 0.3f), talentBottomFrame.RectTransform, anchor: Anchor.TopRight), text: TextManager.Get("applysettingsbutton"))
{
OnClicked = ApplyTalentSelection,
};
new GUIButton(new RectTransform(new Vector2(0.1f, 0.3f), talentBottomFrame.RectTransform, anchor: Anchor.TopLeft), text: TextManager.Get("reset"))
{
OnClicked = ResetTalentSelection,
};
UpdateTalentButtons();
}
private void UpdateTalentButtons()
{
Character controlledCharacter = Character.Controlled;
talentPointsText.Text = $"{TextManager.Get("talentpointsleft")}{controlledCharacter.Info.GetAvailableTalentPoints()}";
experienceText.Text = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}";
experienceBar.BarSize = controlledCharacter.Info.GetProgressTowardsNextLevel();
//experienceBar.ToolTip = $"{controlledCharacter.Info.ExperiencePoints - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()} / {controlledCharacter.Info.GetExperienceRequiredToLevelUp() - controlledCharacter.Info.GetExperienceRequiredForCurrentLevel()}";
selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents);
foreach (var talentButton in talentButtons)
{
talentButton.background.Color = unselectableColor;
}
foreach (var talentButton in talentButtons)
{
string talentIdentifier = talentButton.button.UserData as string;
bool unselectable = !TalentTree.IsViableTalentForCharacter(controlledCharacter, talentIdentifier, selectedTalents) || controlledCharacter.HasTalent(talentIdentifier);
Color newTalentColor = unselectable ? unselectableColor : unselectedColor;
if (controlledCharacter.HasTalent(talentIdentifier))
{
newTalentColor = ownedColor;
}
else if (selectedTalents.Contains(talentIdentifier))
{
newTalentColor = selectedColor;
}
talentButton.button.Color = newTalentColor;
talentButton.button.SelectedColor = newTalentColor;
talentButton.button.HoverColor = newTalentColor;
talentButton.button.DisabledColor = newTalentColor;
talentButton.icon.Color = newTalentColor;
// update background color as well, if not defined yet
if (talentButton.background.Color == unselectableColor)
{
talentButton.background.Color = newTalentColor;
}
}
}
private void ApplyTalents(Character controlledCharacter)
{
selectedTalents = TalentTree.CheckTalentSelection(controlledCharacter, selectedTalents);
foreach (string talent in selectedTalents)
{
controlledCharacter.GiveTalent(talent);
if (GameMain.Client != null)
{
GameMain.Client.CreateEntityEvent(controlledCharacter, new object[] { NetEntityEvent.Type.UpdateTalents });
}
}
UpdateTalentButtons();
}
private bool ApplyTalentSelection(GUIButton guiButton, object userData)
{
Character controlledCharacter = Character.Controlled;
ApplyTalents(controlledCharacter);
return true;
}
private bool ResetTalentSelection(GUIButton guiButton, object userData)
{
Character controlledCharacter = Character.Controlled;
selectedTalents = controlledCharacter.Info.UnlockedTalents.ToList();
UpdateTalentButtons();
return true;
}
public void OnExperienceChanged(Character character)
{
if (character != Character.Controlled) { return; }
UpdateTalentButtons();
}
private readonly StatTypes[] basicStats = new StatTypes[]
{
StatTypes.MaximumHealthMultiplier,
StatTypes.MovementSpeed,
StatTypes.SwimmingSpeed,
StatTypes.RepairSpeed,
};
private readonly StatTypes[] combatStats = new StatTypes[]
{
StatTypes.MaximumHealthMultiplier,
StatTypes.MovementSpeed,
StatTypes.SwimmingSpeed,
StatTypes.RepairSpeed,
};
private readonly StatTypes[] miscStats = new StatTypes[]
{
StatTypes.ReputationGainMultiplier,
StatTypes.MissionMoneyGainMultiplier,
StatTypes.ExperienceGainMultiplier,
StatTypes.MissionExperienceGainMultiplier,
};
private void CreateCharacterSheet(GUILayoutGroup characterInfoColumn)
{
Character controlledCharacter = Character.Controlled;
CreateRow(basicStats);
CreateRow(combatStats);
CreateRow(miscStats);
void CreateRow(StatTypes[] statTypes)
{
GUILayoutGroup characterInfoRow = new GUILayoutGroup(new RectTransform(new Vector2(0.33f, 1.0f), characterInfoColumn.RectTransform, anchor: Anchor.TopLeft), childAnchor: Anchor.TopCenter);
foreach (StatTypes statType in statTypes)
{
ShowStat(statType, characterInfoRow);
}
}
void ShowStat(StatTypes statType, GUILayoutGroup characterInfoRow)
{
GUIFrame textInfoFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.33f), characterInfoRow.RectTransform, Anchor.TopCenter), style: null);
new GUITextBlock(new RectTransform(new Vector2(1f, 1f), textInfoFrame.RectTransform, Anchor.TopLeft), statType.ToString(), font: GUI.SmallFont, textAlignment: Alignment.TopLeft);
new GUITextBlock(new RectTransform(new Vector2(1f, 1f), textInfoFrame.RectTransform, Anchor.TopLeft), (int)(100f * (1 + controlledCharacter.GetStatValue(statType))) + "%", font: GUI.Font, textAlignment: Alignment.TopRight);
}
}
}
}

View File

@@ -115,8 +115,9 @@ namespace Barotrauma
return MathHelper.Clamp(Math.Min(Math.Min(scale.X, scale.Y), GUI.SlicedSpriteScale), minBorderScale, maxBorderScale);
}
public void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None)
public void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None, Vector2? uvOffset = null)
{
uvOffset ??= Vector2.Zero;
if (Sprite.Texture == null)
{
GUI.DrawRectangle(spriteBatch, rect, Color.Magenta);
@@ -157,7 +158,7 @@ namespace Barotrauma
else if (Tile)
{
Vector2 startPos = new Vector2(rect.X, rect.Y);
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), color);
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), color, startOffset: uvOffset);
}
else
{

View File

@@ -43,6 +43,7 @@ namespace Barotrauma
public static SteamWorkshopScreen SteamWorkshopScreen;
public static SubEditorScreen SubEditorScreen;
public static TestScreen TestScreen;
public static ParticleEditorScreen ParticleEditorScreen;
public static LevelEditorScreen LevelEditorScreen;
public static SpriteEditorScreen SpriteEditorScreen;
@@ -89,7 +90,16 @@ namespace Barotrauma
public static ParticleManager ParticleManager;
public static DecalManager DecalManager;
public static World World;
private static World world;
public static World World
{
get
{
if (world == null) { world = new World(new Vector2(0, -9.82f)); }
return world;
}
set { world = value; }
}
public static LoadingScreen TitleScreen;
private bool loadingScreenOpen;
@@ -239,7 +249,6 @@ namespace Barotrauma
GameMain.ResetFrameTime();
fixedTime = new GameTime();
World = new World(new Vector2(0, -9.82f));
FarseerPhysics.Settings.AllowSleep = true;
FarseerPhysics.Settings.ContinuousPhysics = false;
FarseerPhysics.Settings.VelocityIterations = 1;
@@ -567,6 +576,8 @@ namespace Barotrauma
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
TalentPrefab.LoadAll(GetFilesOfType(ContentType.Talents));
TalentTree.LoadAll(GetFilesOfType(ContentType.TalentTrees));
Order.Init();
EventManagerSettings.Init();
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
@@ -620,6 +631,7 @@ namespace Barotrauma
#endif
SubEditorScreen = new SubEditorScreen();
TestScreen = new TestScreen();
TitleScreen.LoadState = 75.0f;
yield return CoroutineStatus.Running;
@@ -792,12 +804,16 @@ namespace Barotrauma
}
#if DEBUG
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (Config.AutomaticQuickStartEnabled || Config.AutomaticCampaignLoadEnabled) && FirstLoad && !PlayerInput.KeyDown(Keys.LeftShift))
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (Config.AutomaticQuickStartEnabled || Config.AutomaticCampaignLoadEnabled || Config.TestScreenEnabled) && FirstLoad && !PlayerInput.KeyDown(Keys.LeftShift))
{
loadingScreenOpen = false;
FirstLoad = false;
if (Config.AutomaticQuickStartEnabled)
if (Config.TestScreenEnabled)
{
TestScreen.Select();
}
else if (Config.AutomaticQuickStartEnabled)
{
MainMenuScreen.QuickStart();
}

View File

@@ -48,11 +48,7 @@ namespace Barotrauma
var equipmentSlots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card };
return character.Inventory.FindAllItems(item =>
{
if (item.SpawnedInOutpost) { return false; }
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
// There must be no contained items or the contained items must be confirmed as sold
if (!item.ContainedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
if (!IsItemSellable(item, confirmedSoldEntities)) { return false; }
// Item must be in a non-equipment slot if possible
if (!item.AllowedSlots.All(s => equipmentSlots.Contains(s)) && IsInEquipmentSlot(item)) { return false; }
// Item must not be contained inside an item in an equipment slot
@@ -76,15 +72,10 @@ namespace Barotrauma
var confirmedSoldEntities = GetConfirmedSoldEntities();
return Submarine.MainSub.GetItems(true).FindAll(item =>
{
if (!item.Prefab.CanBeSold) { return false; }
if (item.SpawnedInOutpost) { return false; }
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
if (!IsItemSellable(item, confirmedSoldEntities)) { return false; }
if (!item.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { return false; }
if (!item.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { return false; }
if (!ItemAndAllContainersInteractable(item)) { return false; }
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
// There must be no contained items or the contained items must be confirmed as sold
if (!item.ContainedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
return true;
}).Distinct();
@@ -107,6 +98,24 @@ namespace Barotrauma
return SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
}
private bool IsItemSellable(Item item, IEnumerable<SoldEntity> confirmedSoldEntities)
{
if (!item.Prefab.CanBeSold) { return false; }
if (item.SpawnedInOutpost) { return false; }
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
if (item.OwnInventory?.Container is ItemContainer itemContainer)
{
var containedItems = item.ContainedItems;
if (containedItems.None()) { return true; }
// Allow selling the item if contained items are unsellable and set to be removed on deconstruct
if (itemContainer.RemoveContainedItemsOnDeconstruct && containedItems.All(it => !it.Prefab.CanBeSold)) { return true; }
// Otherwise there must be no contained items or the contained items must be confirmed as sold
if (!containedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
}
return true;
}
public void SetItemsInBuyCrate(List<PurchasedItem> items)
{
ItemsInBuyCrate.Clear();

View File

@@ -761,7 +761,7 @@ namespace Barotrauma
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
if (faction?.Reputation != null)
{
faction.Reputation.Value = rep;
faction.Reputation.SetReputation(rep);
}
else
{
@@ -771,7 +771,7 @@ namespace Barotrauma
if (reputation.HasValue)
{
campaign.Map.CurrentLocation.Reputation.Value = reputation.Value;
campaign.Map.CurrentLocation.Reputation.SetReputation(reputation.Value);
campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
}

View File

@@ -24,7 +24,7 @@ namespace Barotrauma
if (GameMain.NetLobbyScreen.HeadSelectionList != null) { GameMain.NetLobbyScreen.HeadSelectionList.Visible = false; }
if (GameMain.NetLobbyScreen.JobSelectionFrame != null) { GameMain.NetLobbyScreen.JobSelectionFrame.Visible = false; }
}
if (tabMenu == null && GameMode is TutorialMode == false)
if (tabMenu == null && !(GameMode is TutorialMode) && !ConversationAction.IsDialogOpen)
{
tabMenu = new TabMenu();
HintManager.OnShowTabMenu();
@@ -34,7 +34,6 @@ namespace Barotrauma
tabMenu = null;
NetLobbyScreen.JobInfoFrame = null;
}
return true;
}
@@ -44,7 +43,7 @@ namespace Barotrauma
private GUIComponent respawnInfoFrame, respawnButtonContainer;
private GUITextBlock respawnInfoText;
private GUITickBox respawnTickBox;
private GUILayoutGroup TopLeftButtonGroup;
private void CreateTopLeftButtons()
{
if (topLeftButtonGroup != null)

View File

@@ -1518,6 +1518,12 @@ namespace Barotrauma
"Automatic quickstart enabled",
"Will the game automatically move on to Quickstart when the game is launched");
addDebugTickBox(
TestScreenEnabled,
(b) => TestScreenEnabled = b,
"Test screen enabled",
"Will the game automatically move on to a test screen when the game is launched");
addDebugTickBox(
AutomaticCampaignLoadEnabled,
(b) => AutomaticCampaignLoadEnabled = b,

View File

@@ -161,7 +161,7 @@ namespace Barotrauma
public override void CreateSlots()
{
if (visualSlots == null) { visualSlots = new VisualSlot[capacity]; }
visualSlots ??= new VisualSlot[capacity];
float multiplier = !GUI.IsFourByThree() ? UIScale : UIScale * 0.925f;
@@ -359,7 +359,8 @@ namespace Barotrauma
int personalSlotX = HUDLayoutSettings.InventoryAreaLower.Right - SlotSize.X - Spacing;
for (int i = 0; i < visualSlots.Length; i++)
{
if (HideSlot(i)) continue;
if (HideSlot(i)) { continue; }
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand) { continue; }
if (PersonalSlots.HasFlag(SlotTypes[i]))
{
//upperX -= slotSize.X + spacing;
@@ -371,10 +372,18 @@ namespace Barotrauma
}
int lowerX = x;
int handSlotX = x;
int personalSlotY = GameMain.GraphicsHeight - bottomOffset * 2 - Spacing * 2 - (int)(!GUI.IsFourByThree() ? UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment : UnequippedIndicator.size.Y * UIScale * IndicatorScaleAdjustment * 2f);
for (int i = 0; i < SlotPositions.Length; i++)
{
if (HideSlot(i)) continue;
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand)
{
SlotPositions[i] = new Vector2(handSlotX, personalSlotY);
handSlotX += visualSlots[i].Rect.Width + Spacing;
continue;
}
if (HideSlot(i)) { continue; }
if (PersonalSlots.HasFlag(SlotTypes[i]))
{
SlotPositions[i] = new Vector2(personalSlotX, personalSlotY);
@@ -390,7 +399,8 @@ namespace Barotrauma
x = lowerX;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (!HideSlot(i)) continue;
if (!HideSlot(i)) { continue; }
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand) { continue; }
x -= visualSlots[i].Rect.Width + Spacing;
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
}
@@ -404,7 +414,8 @@ namespace Barotrauma
for (int i = 0; i < SlotPositions.Length; i++)
{
if (HideSlot(i)) continue;
if (HideSlot(i)) { continue; }
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand) { continue; }
if (PersonalSlots.HasFlag(SlotTypes[i]))
{
SlotPositions[i] = new Vector2(personalSlotX, personalSlotY);
@@ -416,9 +427,16 @@ namespace Barotrauma
x += visualSlots[i].Rect.Width + Spacing;
}
}
int handSlotX = x - visualSlots[0].Rect.Width - Spacing;
for (int i = 0; i < SlotPositions.Length; i++)
{
if (!HideSlot(i)) continue;
if (SlotTypes[i] == InvSlotType.RightHand || SlotTypes[i] == InvSlotType.LeftHand)
{
bool rightSlot = SlotTypes[i] == InvSlotType.RightHand;
SlotPositions[i] = new Vector2(rightSlot ? handSlotX : handSlotX - visualSlots[0].Rect.Width - Spacing, personalSlotY);
continue;
}
if (!HideSlot(i)) { continue; }
SlotPositions[i] = new Vector2(x, GameMain.GraphicsHeight - bottomOffset);
x += visualSlots[i].Rect.Width + Spacing;
}

View File

@@ -7,6 +7,7 @@ using System.Collections.Generic;
using Barotrauma.IO;
using System.Text;
using System.Xml.Linq;
using Barotrauma.Sounds;
namespace Barotrauma.Items.Components
{
@@ -18,7 +19,11 @@ namespace Barotrauma.Items.Components
protected float currentCrossHairScale, currentCrossHairPointerScale;
private RoundSound chargeSound;
private SoundChannel chargeSoundChannel;
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
private readonly List<ParticleEmitter> particleEmitterCharges = new List<ParticleEmitter>();
[Serialize(1.0f, false, description: "The scale of the crosshair sprite (if there is one).")]
public float CrossHairScale
@@ -48,6 +53,12 @@ namespace Barotrauma.Items.Components
case "particleemitter":
particleEmitters.Add(new ParticleEmitter(subElement));
break;
case "particleemittercharge":
particleEmitterCharges.Add(new ParticleEmitter(subElement));
break;
case "chargesound":
chargeSound = Submarine.LoadRoundSound(subElement, false);
break;
}
}
}
@@ -84,6 +95,51 @@ namespace Barotrauma.Items.Components
crosshairPointerPos = PlayerInput.MousePosition;
}
partial void UpdateProjSpecific(float deltaTime)
{
float chargeRatio = currentChargeTime / MaxChargeTime;
switch (currentChargingState)
{
case ChargingState.WindingUp:
case ChargingState.WindingDown:
Vector2 particlePos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);
float sizeMultiplier = Math.Clamp(chargeRatio, 0.1f, 1f);
foreach (ParticleEmitter emitter in particleEmitterCharges)
{
emitter.Emit(deltaTime, particlePos, hullGuess: null, sizeMultiplier: sizeMultiplier, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier);
}
if (chargeSoundChannel == null || !chargeSoundChannel.IsPlaying)
{
if (chargeSound != null)
{
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling);
if (chargeSoundChannel != null) chargeSoundChannel.Looping = true;
}
}
else if (chargeSoundChannel != null)
{
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(0.5f, 1.5f, chargeRatio);
}
break;
default:
if (chargeSoundChannel != null)
{
if (chargeSoundChannel.IsPlaying)
{
chargeSoundChannel.FadeOutAndDispose();
chargeSoundChannel.Looping = false;
}
else
{
chargeSoundChannel = null;
}
}
break;
}
}
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
{
if (character == null || !character.IsKeyDown(InputType.Aim)) { return; }
@@ -92,7 +148,7 @@ namespace Barotrauma.Items.Components
if (character.ViewTarget != null && (character.ViewTarget is Item item) && item.Prefab.FocusOnSelected) { return; }
GUI.HideCursor = (crosshairSprite != null || crosshairPointerSprite != null) &&
GUI.MouseOn == null && !Inventory.IsMouseOnInventory() && !GameMain.Instance.Paused;
GUI.MouseOn == null && !Inventory.IsMouseOnInventory && !GameMain.Instance.Paused;
if (GUI.HideCursor)
{
crosshairSprite?.Draw(spriteBatch, crosshairPos, Color.White, 0, currentCrossHairScale);

View File

@@ -246,6 +246,7 @@ namespace Barotrauma.Items.Components
public void PlaySound(ActionType type, Character user = null)
{
if (!hasSoundsOfType[(int)type]) { return; }
if (GameMain.Client?.MidRoundSyncing ?? false) { return; }
if (loopingSound != null)
{
@@ -429,7 +430,7 @@ namespace Barotrauma.Items.Components
}
foreach (ItemComponent component in item.Components)
{
if (component.name.ToLower() == LinkUIToComponent.ToLower())
if (component.name.Equals(LinkUIToComponent, StringComparison.OrdinalIgnoreCase))
{
linkToUIComponent = component;
}

View File

@@ -126,7 +126,8 @@ namespace Barotrauma.Items.Components
private void DrawOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
{
overlayComponent.RectTransform.SetAsLastChild();
var lastSlot = inputContainer.Inventory.visualSlots.Last();
if (!(inputContainer?.Inventory?.visualSlots is { } visualSlots)) { return; }
var lastSlot = visualSlots.Last();
GUI.DrawRectangle(spriteBatch,
new Rectangle(

View File

@@ -255,12 +255,15 @@ namespace Barotrauma.Items.Components
var item1 = c1.GUIComponent.UserData as FabricationRecipe;
var item2 = c2.GUIComponent.UserData as FabricationRecipe;
bool hasSkills1 = FabricationDegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f;
bool hasSkills2 = FabricationDegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f;
int itemPlacement1 = FabricationDegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f ? 0 : -1;
int itemPlacement2 = FabricationDegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f ? 0 : -1;
if (hasSkills1 != hasSkills2)
itemPlacement1 += item1.RequiresRecipe && !character.HasRecipeForItem(item1.TargetItem.Identifier) ? -2 : 0;
itemPlacement2 += item2.RequiresRecipe && !character.HasRecipeForItem(item2.TargetItem.Identifier) ? -2 : 0;
if (itemPlacement1 != itemPlacement2)
{
return hasSkills1 ? -1 : 1;
return itemPlacement1 > itemPlacement2 ? -1 : 1;
}
return string.Compare(item1.DisplayName, item2.DisplayName);
@@ -285,6 +288,18 @@ namespace Barotrauma.Items.Components
{
insufficientSkillsText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstinSufficient.RectTransform));
}
var requiresRecipeText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), itemList.Content.RectTransform),
TextManager.Get("fabricatorrequiresrecipe", returnNull: true) ?? "Requires recipe to fabricate", textColor: Color.Red, font: GUI.SubHeadingFont)
{
AutoScaleHorizontal = true,
CanBeFocused = false
};
var firstRequiresRecipe = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe fabricableItem && (fabricableItem.RequiresRecipe && !character.HasRecipeForItem(fabricableItem.TargetItem.Identifier)));
if (firstRequiresRecipe != null)
{
requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform));
}
}
private void DrawInputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
@@ -297,6 +312,7 @@ namespace Barotrauma.Items.Components
int slotIndex = 0;
var missingItems = new List<FabricationRecipe.RequiredItem>();
foreach (FabricationRecipe.RequiredItem requiredItem in targetItem.RequiredItems)
{
for (int i = 0; i < requiredItem.Amount; i++)
@@ -308,6 +324,8 @@ namespace Barotrauma.Items.Components
{
missingItems.Remove(missingItems.FirstOrDefault(mi => mi.ItemPrefabs.Contains(item.prefab)));
}
var missingCounts = missingItems.GroupBy(missingItem => missingItem).ToDictionary(x => x.Key, x => x.Count());
missingItems = missingItems.Distinct().ToList();
var availableIngredients = GetAvailableIngredients();
@@ -318,30 +336,30 @@ namespace Barotrauma.Items.Components
slotIndex++;
}
//highlight suitable ingredients in linked inventories
foreach (Item item in availableIngredients)
{
if (item.ParentInventory != inputContainer.Inventory && IsItemValidIngredient(item, requiredItem))
{
int availableSlotIndex = item.ParentInventory.FindIndex(item);
//slots are null if the inventory has never been displayed
//(linked item, but the UI is not set to be displayed at the same time)
if (item.ParentInventory.visualSlots != null)
{
if (item.ParentInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
{
item.ParentInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
if (slotIndex < inputContainer.Capacity)
requiredItem.ItemPrefabs
.Where(requiredPrefab => availableIngredients.ContainsKey(requiredPrefab.Identifier))
.ForEach(requiredPrefab => {
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
availablePrefabs
.Where(availablePrefab => availablePrefab.ParentInventory != inputContainer.Inventory)
.Where(availablePrefab => availablePrefab.ParentInventory.visualSlots != null) //slots are null if the inventory has never been displayed
.ForEach(availablePrefab => { //(linked item, but the UI is not set to be displayed at the same time)
int availableSlotIndex = availablePrefab.ParentInventory.FindIndex(availablePrefab);
if (availablePrefab.ParentInventory.visualSlots[availableSlotIndex].HighlightTimer <= 0.0f)
{
inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
availablePrefab.ParentInventory.visualSlots[availableSlotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
if (slotIndex < inputContainer.Capacity)
{
inputContainer.Inventory.visualSlots[slotIndex].ShowBorderHighlight(GUI.Style.Green, 0.5f, 0.5f, 0.2f);
}
}
}
}
}
}
});
});
if (slotIndex >= inputContainer.Capacity) { break; }
var itemIcon = requiredItem.ItemPrefabs.First().InventoryIcon ?? requiredItem.ItemPrefabs.First().sprite;
Rectangle slotRect = inputContainer.Inventory.visualSlots[slotIndex].Rect;
itemIcon.Draw(
@@ -350,6 +368,16 @@ namespace Barotrauma.Items.Components
color: requiredItem.ItemPrefabs.First().InventoryIconColor * 0.3f,
scale: Math.Min(slotRect.Width / itemIcon.size.X, slotRect.Height / itemIcon.size.Y));
if (missingCounts[requiredItem] > 1)
{
Vector2 stackCountPos = new Vector2(slotRect.Right, slotRect.Bottom);
string stackCountText = "x" + missingCounts[requiredItem];
stackCountPos -= GUI.SmallFont.MeasureString(stackCountText) + new Vector2(4, 2);
GUI.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos + Vector2.One, Color.Black);
GUI.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos, Color.White);
}
if (requiredItem.UseCondition && requiredItem.MinCondition < 1.0f)
{
GUI.DrawRectangle(spriteBatch, new Rectangle(slotRect.X, slotRect.Bottom - 8, slotRect.Width, 8), Color.Black * 0.8f, true);
@@ -601,7 +629,7 @@ namespace Barotrauma.Items.Components
var itemPrefab = child.UserData as FabricationRecipe;
if (itemPrefab == null) continue;
bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients);
bool canBeFabricated = CanBeFabricated(itemPrefab, availableIngredients, character);
if (itemPrefab == selectedItem)
{
activateButton.Enabled = canBeFabricated;

View File

@@ -142,6 +142,7 @@ namespace Barotrauma.Items.Components
partial void UpdateProjSpecific(float deltaTime)
{
float rotationRad = MathHelper.ToRadians(item.Rotation);
if (FlowPercentage < 0.0f)
{
foreach (var (position, emitter) in pumpOutEmitters)
@@ -149,12 +150,13 @@ namespace Barotrauma.Items.Components
if (item.CurrentHull != null && item.CurrentHull.Surface < item.Rect.Location.Y + position.Y) { continue; }
//only emit "pump out" particles when underwater
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
float angle = 0.0f;
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, item.FlippedX ? rotationRad : -rotationRad);
float angle = -rotationRad;
if (item.FlippedX)
{
relativeParticlePos.X = -relativeParticlePos.X;
angle = MathHelper.Pi;
angle += MathHelper.Pi;
}
if (item.FlippedY)
{
@@ -170,11 +172,12 @@ namespace Barotrauma.Items.Components
foreach (var (position, emitter) in pumpInEmitters)
{
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
float angle = 0.0f;
relativeParticlePos = MathUtils.RotatePoint(relativeParticlePos, item.FlippedX ? rotationRad : -rotationRad);
float angle = -rotationRad;
if (item.FlippedX)
{
relativeParticlePos.X = -relativeParticlePos.X;
angle = MathHelper.Pi;
angle += MathHelper.Pi;
}
if (item.FlippedY)
{

View File

@@ -888,6 +888,7 @@ namespace Barotrauma.Items.Components
maintainPosOriginIndicator?.Remove();
steeringIndicator?.Remove();
enterOutpostPrompt?.Close();
pathFinder = null;
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)

View File

@@ -71,7 +71,7 @@ namespace Barotrauma.Items.Components
var chargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), textArea.RectTransform, Anchor.CenterRight),
"", textColor: GUI.Style.TextColor, font: GUI.Font, textAlignment: Alignment.CenterRight)
{
TextGetter = () => $"{(int)charge}/{(int)capacity} {kWmin} ({((int)MathUtils.Percentage(charge, capacity)).ToString()} %)"
TextGetter = () => $"{(int)Math.Round(charge)}/{(int)capacity} {kWmin} ({(int)Math.Round(MathUtils.Percentage(charge, capacity))} %)"
};
if (chargeText.TextSize.X > chargeText.Rect.Width) { chargeText.Font = GUI.SmallFont; }

View File

@@ -15,6 +15,8 @@ namespace Barotrauma.Items.Components
public GUIButton SabotageButton { get; private set; }
public GUIButton TinkerButton { get; private set; }
private GUIProgressBar progressBar;
private List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
@@ -25,6 +27,7 @@ namespace Barotrauma.Items.Components
private string repairButtonText, repairingText;
private string sabotageButtonText, sabotagingText;
private string tinkerButtonText, tinkeringText;
private FixActions requestStartFixAction;
@@ -46,7 +49,7 @@ namespace Barotrauma.Items.Components
public override bool ShouldDrawHUD(Character character)
{
if (!HasRequiredItems(character, false) || character.SelectedConstruction != item) return false;
return item.ConditionPercentage < RepairThreshold || character.IsTraitor && item.ConditionPercentage > MinSabotageCondition || (CurrentFixer == character && (!item.IsFullCondition || (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition)));
return item.ConditionPercentage < RepairThreshold || character.IsTraitor && item.ConditionPercentage > MinSabotageCondition || (CurrentFixer == character && (!item.IsFullCondition || (character.IsTraitor && item.ConditionPercentage > MinSabotageCondition))) || CanTinker(character);
}
partial void InitProjSpecific(XElement element)
@@ -148,6 +151,20 @@ namespace Barotrauma.Items.Components
return true;
}
};
tinkerButtonText = "Tinker";
tinkeringText = "Tinkering";
TinkerButton = new GUIButton(new RectTransform(new Vector2(0.8f, 0.15f), paddedFrame.RectTransform, Anchor.BottomCenter), tinkerButtonText, style: "GUIButtonSmall")
{
IgnoreLayoutGroups = true,
Visible = false,
OnClicked = (btn, obj) =>
{
requestStartFixAction = FixActions.Tinker;
item.CreateClientEvent(this);
return true;
}
};
}
partial void UpdateProjSpecific(float deltaTime)
@@ -176,6 +193,7 @@ namespace Barotrauma.Items.Components
{
case FixActions.Repair:
case FixActions.Sabotage:
case FixActions.Tinker:
StartRepairing(Character.Controlled, requestStartFixAction);
requestStartFixAction = FixActions.None;
break;
@@ -226,6 +244,13 @@ namespace Barotrauma.Items.Components
sabotageButtonText :
sabotagingText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
TinkerButton.Visible = CanTinker(character);
TinkerButton.IgnoreLayoutGroups = !TinkerButton.Visible;
TinkerButton.Enabled = (currentFixerAction == FixActions.None || (CurrentFixer == character && currentFixerAction != FixActions.Tinker)) && CanTinker(character);
TinkerButton.Text = (currentFixerAction == FixActions.None || CurrentFixer != character || currentFixerAction != FixActions.Tinker && CanTinker(character)) ?
tinkerButtonText :
tinkeringText + new string('.', ((int)(Timing.TotalTime * 2.0f) % 3) + 1);
System.Diagnostics.Debug.Assert(GuiFrame.GetChild(0) is GUILayoutGroup, "Repair UI hierarchy has changed, could not find skill texts");
foreach (GUIComponent c in GuiFrame.GetChild(0).Children)
{

View File

@@ -85,7 +85,7 @@ namespace Barotrauma.Items.Components
GUITextBlock newBlock = new GUITextBlock(
new RectTransform(new Vector2(1, 0), historyBox.Content.RectTransform, anchor: Anchor.TopCenter),
"> " + input,
textColor: Color.LimeGreen, wrap: true)
textColor: Color.LimeGreen, wrap: true, font: UseMonospaceFont ? GUI.MonospacedFont : GUI.GlobalFont)
{
CanBeFocused = false
};

View File

@@ -534,20 +534,20 @@ namespace Barotrauma.Items.Components
minRotationWidget.Draw(spriteBatch, (float)Timing.Step);
maxRotationWidget.Draw(spriteBatch, (float)Timing.Step);
Vector2 GetDrawPos()
{
Vector2 drawPos = new Vector2(item.Rect.X + transformedBarrelPos.X, item.Rect.Y - transformedBarrelPos.Y);
if (item.Submarine != null) { drawPos += item.Submarine.DrawPosition; }
drawPos.Y = -drawPos.Y;
return drawPos;
}
void UpdateBarrel()
{
rotation = (minRotation + maxRotation) / 2;
}
}
public Vector2 GetDrawPos()
{
Vector2 drawPos = new Vector2(item.Rect.X + transformedBarrelPos.X, item.Rect.Y - transformedBarrelPos.Y);
if (item.Submarine != null) { drawPos += item.Submarine.DrawPosition; }
drawPos.Y = -drawPos.Y;
return drawPos;
}
private Widget GetWidget(string id, SpriteBatch spriteBatch, int size = 5, float thickness = 1f, Action<Widget> initMethod = null)
{
Vector2 offset = new Vector2(size / 2 + 5, -10);

View File

@@ -478,7 +478,7 @@ namespace Barotrauma
}
if (container == null) { return false; }
return owner.SelectedCharacter != null|| (!(owner is Character character)) || !container.KeepOpenWhenEquippedBy(character) || !owner.HasEquippedItem(container.Item);
return owner.SelectedCharacter != null|| (!(owner is Character character)) || !container.KeepOpenWhenEquippedBy(character) || !owner.HasEquippedItem(container.Item);
}
protected virtual bool HideSlot(int i)
@@ -667,6 +667,10 @@ namespace Barotrauma
if (subInventory.visualSlots == null) { subInventory.CreateSlots(); }
canMove = container.MovableFrame && !subInventory.IsInventoryHoverAvailable(Owner as Character, container) && subInventory.originalPos != Point.Zero;
if (this is CharacterInventory characterInventory && characterInventory.CurrentLayout != CharacterInventory.Layout.Default)
{
canMove = false;
}
if (canMove)
{
@@ -826,11 +830,23 @@ namespace Barotrauma
return rect.Contains(PlayerInput.MousePosition);
}
public static bool IsMouseOnInventory
{
get; private set;
}
/// <summary>
/// Refresh the value of IsMouseOnInventory
/// </summary>
public static void RefreshMouseOnInventory()
{
IsMouseOnInventory = DetermineMouseOnInventory();
}
/// <summary>
/// Is the mouse on any inventory element (slot, equip button, subinventory...)
/// </summary>
/// <returns></returns>
public static bool IsMouseOnInventory(bool ignoreDraggedItem = false)
private static bool DetermineMouseOnInventory(bool ignoreDraggedItem = false)
{
if (GameMain.GameSession?.Campaign != null &&
(GameMain.GameSession.Campaign.ShowCampaignUI || GameMain.GameSession.Campaign.ForceMapUI))
@@ -1112,7 +1128,7 @@ namespace Barotrauma
{
Character.Controlled.ClearInputs();
if (!IsMouseOnInventory(ignoreDraggedItem: true) &&
if (!DetermineMouseOnInventory(ignoreDraggedItem: true) &&
CharacterHealth.OpenHealthWindow != null)
{
bool dropSuccessful = false;
@@ -1306,7 +1322,9 @@ namespace Barotrauma
protected static Rectangle GetSubInventoryHoverArea(SlotReference subSlot)
{
Rectangle hoverArea;
if (!subSlot.Inventory.Movable() || Character.Controlled?.Inventory == subSlot.ParentInventory && !Character.Controlled.HasEquippedItem(subSlot.Item))
if (!subSlot.Inventory.Movable() ||
(Character.Controlled?.Inventory == subSlot.ParentInventory && !Character.Controlled.HasEquippedItem(subSlot.Item)) ||
(subSlot.ParentInventory is CharacterInventory characterInventory && characterInventory.CurrentLayout != CharacterInventory.Layout.Default))
{
hoverArea = subSlot.Slot.Rect;
hoverArea.Location += subSlot.Slot.DrawOffset.ToPoint();

View File

@@ -332,7 +332,7 @@ namespace Barotrauma
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, flippedX && Prefab.CanSpriteFlipX ? rotationRad : -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.DrawTiled(spriteBatch,
@@ -368,7 +368,7 @@ namespace Barotrauma
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState, spriteAnimState[decorativeSprite].RandomRotationFactor);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, flippedX && Prefab.CanSpriteFlipX ? rotationRad : -rotationRad) * Scale;
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
@@ -1623,6 +1623,10 @@ namespace Barotrauma
{
wifiComponent.TeamID = (CharacterTeamType)teamID;
}
foreach (IdCard idCard in item.GetComponents<IdCard>())
{
idCard.TeamID = (CharacterTeamType)teamID;
}
if (descriptionChanged) { item.Description = itemDesc; }
if (tagsChanged) { item.Tags = tags; }
var nameTag = item.GetComponent<NameTag>();

View File

@@ -8,6 +8,8 @@ namespace Barotrauma
{
partial void ExplodeProjSpecific(Vector2 worldPosition, Hull hull)
{
if (GameMain.Client?.MidRoundSyncing ?? false) { return; }
if (shockwave)
{
GameMain.ParticleManager.CreateParticle("shockwave", worldPosition,

View File

@@ -676,7 +676,7 @@ namespace Barotrauma
}
else
{
remoteBackgroundSections.Add(new BackgroundSection(new Rectangle(0, 0, 1, 1), i, colorStrength, color, 0));
remoteBackgroundSections.Add(new BackgroundSection(new Rectangle(0, 0, 1, 1), (ushort)i, colorStrength, color, 0));
}
}
paintAmount = BackgroundSections.Sum(s => s.ColorStrength);

View File

@@ -238,13 +238,13 @@ namespace Barotrauma.Lights
//draw a black rectangle on hulls to hide background lights behind subs
//---------------------------------------------------------------------------------------------------
if (backgroundObstructor != null)
/*if (backgroundObstructor != null)
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
spriteBatch.Draw(backgroundObstructor, new Rectangle(0, 0,
(int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black);
spriteBatch.End();
}
}*/
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, transformMatrix: spriteBatchTransform);
Dictionary<Hull, Rectangle> visibleHulls = GetVisibleHulls(cam);
@@ -258,7 +258,7 @@ namespace Barotrauma.Lights
spriteBatch.End();
SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"];
SolidColorEffect.Parameters["color"].SetValue(AmbientLight.ToVector4());
SolidColorEffect.Parameters["color"].SetValue(AmbientLight.Opaque().ToVector4());
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform, effect: SolidColorEffect);
Submarine.DrawDamageable(spriteBatch, null);
spriteBatch.End();

View File

@@ -7,6 +7,7 @@ using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
@@ -403,6 +404,8 @@ namespace Barotrauma
}
}
// TODO remove
[Obsolete("Use MiniMap.CreateMiniMap()")]
public void CreateMiniMap(GUIComponent parent, IEnumerable<Entity> pointsOfInterest = null, bool ignoreOutpost = false)
{
Rectangle worldBorders = GetDockedBorders();
@@ -417,24 +420,125 @@ namespace Barotrauma
GUIFrame hullContainer = new GUIFrame(new RectTransform(
(parentAspectRatio > aspectRatio ? new Vector2(aspectRatio / parentAspectRatio, 1.0f) : new Vector2(1.0f, parentAspectRatio / aspectRatio)) * scale,
parent.RectTransform, Anchor.Center),
style: null);
style: null)
{
UserData = "hullcontainer"
};
var connectedSubs = GetConnectedSubs();
foreach (Hull hull in Hull.hullList)
{
if (hull.Submarine != this && !connectedSubs.Contains(hull.Submarine)) { continue; }
if (ignoreOutpost && !IsEntityFoundOnThisSub(hull, true)) { continue; }
HashSet<Hull> hullList = Hull.hullList.Where(hull => hull.Submarine == this || connectedSubs.Contains(hull.Submarine)).Where(hull => !ignoreOutpost || IsEntityFoundOnThisSub(hull, true)).ToHashSet();
Dictionary<Hull, HashSet<Hull>> combinedHulls = new Dictionary<Hull, HashSet<Hull>>();
foreach (Hull hull in hullList)
{
if (combinedHulls.ContainsKey(hull) || combinedHulls.Values.Any(hh => hh.Contains(hull))) { continue; }
List<Hull> linkedHulls = new List<Hull>();
MiniMap.GetLinkedHulls(hull, linkedHulls);
linkedHulls.Remove(hull);
foreach (Hull linkedHull in linkedHulls)
{
if (!combinedHulls.ContainsKey(hull))
{
combinedHulls.Add(hull, new HashSet<Hull>());
}
combinedHulls[hull].Add(linkedHull);
}
}
foreach (Hull hull in hullList)
{
Vector2 relativeHullPos = new Vector2(
(hull.WorldRect.X - worldBorders.X) / (float)worldBorders.Width,
(worldBorders.Y - hull.WorldRect.Y) / (float)worldBorders.Height);
Vector2 relativeHullSize = new Vector2(hull.Rect.Width / (float)worldBorders.Width, hull.Rect.Height / (float)worldBorders.Height);
var hullFrame = new GUIFrame(new RectTransform(relativeHullSize, hullContainer.RectTransform) { RelativeOffset = relativeHullPos }, style: "MiniMapRoom", color: Color.DarkCyan * 0.8f)
bool hideHull = combinedHulls.ContainsKey(hull) || combinedHulls.Values.Any(hh => hh.Contains(hull));
if (hideHull) { continue; }
Color color = Color.DarkCyan * 0.8f;
var hullFrame = new GUIFrame(new RectTransform(relativeHullSize, hullContainer.RectTransform) { RelativeOffset = relativeHullPos }, style: "MiniMapRoom", color: color)
{
UserData = hull
};
new GUIFrame(new RectTransform(Vector2.One, hullFrame.RectTransform), style: "ScanLines", color: Color.DarkCyan * 0.8f);
new GUIFrame(new RectTransform(Vector2.One, hullFrame.RectTransform), style: "ScanLines", color: color);
}
foreach (var (mainHull, linkedHulls) in combinedHulls)
{
MiniMapHullData data = ConstructLinkedHulls(mainHull, linkedHulls, hullContainer, worldBorders);
Vector2 relativeHullPos = new Vector2(
(data.Bounds.X - worldBorders.X) / worldBorders.Width,
(worldBorders.Y - data.Bounds.Y) / worldBorders.Height);
Vector2 relativeHullSize = new Vector2(data.Bounds.Width / worldBorders.Width, data.Bounds.Height / worldBorders.Height);
Color color = Color.DarkCyan * 0.8f;
float highestY = 0f,
highestX = 0f;
foreach (var (r, _) in data.RectDatas)
{
float y = r.Y - -r.Height,
x = r.X;
if (y > highestY) { highestY = y; }
if (x > highestX) { highestX = x; }
}
HashSet<GUIFrame> frames = new HashSet<GUIFrame>();
foreach (var (snappredRect, hull) in data.RectDatas)
{
RectangleF rect = snappredRect;
rect.Height = -rect.Height;
rect.Y -= rect.Height;
var (parentW, parentH) = hullContainer.Rect.Size.ToVector2();
Vector2 size = new Vector2(rect.Width / parentW, rect.Height / parentH);
// TODO this won't be required if we some day switch RectTransform to use RectangleF
const float padding = 0.001f;
size.X += padding;
size.Y += padding;
Vector2 pos = new Vector2(rect.X / parentW, rect.Y / parentH);
GUIFrame hullFrame = new GUIFrame(new RectTransform(size, hullContainer.RectTransform) { RelativeOffset = pos }, style: "ScanLinesSeamless", color: color)
{
UserData = hull,
UVOffset = new Vector2(highestX - rect.X, highestY - rect.Y)
};
frames.Add(hullFrame);
}
new GUICustomComponent(new RectTransform(relativeHullSize, hullContainer.RectTransform) { RelativeOffset = relativeHullPos }, (spriteBatch, component) =>
{
foreach (List<Vector2> list in data.Polygon)
{
spriteBatch.DrawPolygonInner(hullContainer.Rect.Location.ToVector2(), list, component.Color, 2f);
}
}, (deltaTime, component) =>
{
if (component.Parent.Rect.Size != data.ParentSize)
{
data = ConstructLinkedHulls(mainHull, linkedHulls, hullContainer, worldBorders);
}
})
{
UserData = frames,
Color = color,
CanBeFocused = false
};
}
if (pointsOfInterest != null)
@@ -453,6 +557,64 @@ namespace Barotrauma
}
}
public static MiniMapHullData ConstructLinkedHulls(Hull mainHull, HashSet<Hull> linkedHulls, GUIComponent parent, Rectangle worldBorders)
{
Rectangle parentRect = parent.Rect;
Dictionary<Hull, Rectangle> rects = new Dictionary<Hull, Rectangle>();
Rectangle worldRect = mainHull.WorldRect;
worldRect.Y = -worldRect.Y;
rects.Add(mainHull, worldRect);
foreach (Hull hull in linkedHulls)
{
Rectangle rect = hull.WorldRect;
rect.Y = -rect.Y;
worldRect = Rectangle.Union(worldRect, rect);
rects.Add(hull, rect);
}
worldRect.Y = -worldRect.Y;
List<RectangleF> normalizedRects = new List<RectangleF>();
List<Hull> hullRefs = new List<Hull>();
foreach (var (hull, rect) in rects)
{
Rectangle wRect = rect;
wRect.Y = -wRect.Y;
var (posX, posY) = new Vector2(
(wRect.X - worldBorders.X) / (float)worldBorders.Width,
(worldBorders.Y - wRect.Y) / (float)worldBorders.Height);
var (scaleX, scaleY) = new Vector2(wRect.Width / (float)worldBorders.Width, wRect.Height / (float)worldBorders.Height);
RectangleF newRect = new RectangleF(posX * parentRect.Width, posY * parentRect.Height, scaleX * parentRect.Width, scaleY * parentRect.Height);
normalizedRects.Add(newRect);
hullRefs.Add(hull);
}
ImmutableArray<RectangleF> snappedRectangles = ToolBox.SnapRectangles(normalizedRects, treshold: 1);
List<List<Vector2>> polygon = ToolBox.CombineRectanglesIntoShape(snappedRectangles);
List<List<Vector2>> scaledPolygon = new List<List<Vector2>>();
foreach (List<Vector2> list in polygon)
{
var (polySizeX, polySizeY) = ToolBox.GetPolygonBoundingBoxSize(list);
float sizeX = polySizeX - 1f,
sizeY = polySizeY - 1f;
scaledPolygon.Add(ToolBox.ScalePolygon(list, new Vector2(sizeX / polySizeX, sizeY / polySizeY)));
}
return new MiniMapHullData(scaledPolygon, worldRect, parentRect.Size, snappedRectangles, hullRefs.ToImmutableArray());
}
public void CheckForErrors()
{
List<string> errorMsgs = new List<string>();

View File

@@ -64,7 +64,10 @@ namespace Barotrauma.Networking
string orderOption = orderMessageInfo.OrderOption;
orderOption ??= orderMessageInfo.OrderOptionIndex.HasValue && orderMessageInfo.OrderOptionIndex >= 0 && orderMessageInfo.OrderOptionIndex < orderPrefab.Options.Length ?
orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value] : "";
txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter, orderOption: orderOption);
txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName,
givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter,
orderOption: orderOption,
priority: orderMessageInfo.Priority);
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{

View File

@@ -550,6 +550,13 @@ namespace Barotrauma.Networking
okButton.OnClicked += msgBox.Close;
var cancelButton = msgBox.Buttons[1];
cancelButton.OnClicked += msgBox.Close;
passwordBox.OnEnterPressed += (GUITextBox textBox, string text) =>
{
msgBox.Close();
clientPeer?.SendPassword(passwordBox.Text);
requiresPw = false;
return true;
};
okButton.OnClicked += (GUIButton button, object obj) =>
{
@@ -565,6 +572,8 @@ namespace Barotrauma.Networking
GameMain.ServerListScreen.Select();
return true;
};
yield return CoroutineStatus.Running;
passwordBox.Select();
while (GUIMessageBox.MessageBoxes.Contains(msgBox))
{

View File

@@ -110,12 +110,7 @@ namespace Barotrauma.Networking
if (frame == null) { return; }
var previewContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.98f), frame.RectTransform, Anchor.Center))
{
Stretch = true
};
var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), previewContainer.RectTransform, Anchor.CenterLeft), ServerName, font: GUI.LargeFont)
var title = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform), ServerName, font: GUI.LargeFont)
{
ToolTip = ServerName
};
@@ -141,41 +136,30 @@ namespace Barotrauma.Networking
}
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), previewContainer.RectTransform),
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("ServerListVersion"), string.IsNullOrEmpty(GameVersion) ? TextManager.Get("Unknown") : GameVersion));
bool hidePlaystyleBanner = previewContainer.Rect.Height < 380 || !PlayStyle.HasValue;
bool hidePlaystyleBanner = !PlayStyle.HasValue;
if (!hidePlaystyleBanner)
{
PlayStyle playStyle = PlayStyle ?? Networking.PlayStyle.Serious;
Sprite playStyleBannerSprite = ServerListScreen.PlayStyleBanners[(int)playStyle];
float playStyleBannerAspectRatio = playStyleBannerSprite.SourceRect.Width / playStyleBannerSprite.SourceRect.Height;
var playStyleBanner = new GUIImage(new RectTransform(new Point(previewContainer.Rect.Width, (int)(previewContainer.Rect.Width / playStyleBannerAspectRatio)), previewContainer.RectTransform),
var playStyleBanner = new GUIImage(new RectTransform(new Point(frame.Rect.Width, (int)(frame.Rect.Width / playStyleBannerAspectRatio)), frame.RectTransform),
playStyleBannerSprite, null, true);
var playStyleName = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.0f), playStyleBanner.RectTransform) { RelativeOffset = new Vector2(0.01f, 0.06f) },
var playStyleName = new GUITextBlock(new RectTransform(new Vector2(0.15f, 0.0f), playStyleBanner.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.06f) },
TextManager.AddPunctuation(':', TextManager.Get("serverplaystyle"), TextManager.Get("servertag."+ playStyle)), textColor: Color.White,
font: GUI.SmallFont, textAlignment: Alignment.Center,
color: ServerListScreen.PlayStyleColors[(int)playStyle], style: "GUISlopedHeader");
playStyleName.RectTransform.NonScaledSize = (playStyleName.Font.MeasureString(playStyleName.Text) + new Vector2(20, 5) * GUI.Scale).ToPoint();
playStyleName.RectTransform.IsFixedSize = true;
var serverTypeContainer = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.2f), playStyleBanner.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft),
"MainMenuNotifBackground", Color.Black)
{
CanBeFocused = false,
};
var serverType = new GUITextBlock(new RectTransform(Vector2.One, serverTypeContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.CenterLeft);
}
else
{
var serverType = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), previewContainer.RectTransform, Anchor.CenterLeft),
TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.CenterLeft);
}
var serverType = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), frame.RectTransform),
TextManager.Get((OwnerID != 0 || LobbyID != 0) ? "SteamP2PServer" : "DedicatedServer"), textAlignment: Alignment.TopLeft);
serverType.RectTransform.MinSize = new Point(0, (int)(serverType.Rect.Height * 1.5f));
var content = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), previewContainer.RectTransform))
var content = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.6f), frame.RectTransform))
{
Stretch = true
};
@@ -285,7 +269,6 @@ namespace Barotrauma.Networking
else
usingWhiteList.Selected = UsingWhiteList.Value;
content.RectTransform.SizeChanged += () =>
{
GUITextBlock.AutoScaleAndNormalize(allowSpectating.TextBlock, allowRespawn.TextBlock, usingWhiteList.TextBlock);
@@ -294,7 +277,7 @@ namespace Barotrauma.Networking
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
TextManager.Get("ServerListContentPackages"), textAlignment: Alignment.Center, font: GUI.SubHeadingFont);
var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.2f), content.RectTransform)) { ScrollBarVisible = true };
var contentPackageList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.3f), frame.RectTransform)) { ScrollBarVisible = true };
if (ContentPackageNames.Count == 0)
{
new GUITextBlock(new RectTransform(Vector2.One, contentPackageList.Content.RectTransform), TextManager.Get("Unknown"), textAlignment: Alignment.Center)
@@ -309,7 +292,7 @@ namespace Barotrauma.Networking
var packageText = new GUITickBox(new RectTransform(new Vector2(1.0f, 0.15f), contentPackageList.Content.RectTransform) { MinSize = new Point(0, 15) },
ContentPackageNames[i])
{
Enabled = false
CanBeFocused = false
};
if (i < ContentPackageHashes.Count)
{
@@ -322,7 +305,7 @@ namespace Barotrauma.Networking
//workshop download link found
if (i < ContentPackageWorkshopIds.Count && ContentPackageWorkshopIds[i] != 0)
{
packageText.TextColor = Color.Yellow;
packageText.TextColor = GUI.Style.Yellow;
packageText.ToolTip = TextManager.GetWithVariable("ServerListIncompatibleContentPackageWorkshopAvailable", "[contentpackage]", ContentPackageNames[i]);
}
else //no package or workshop download link found, tough luck

View File

@@ -1207,7 +1207,7 @@ namespace Barotrauma.Steam
foreach (string file in allPackageFiles)
{
if (file == metaDataFilePath) { continue; }
string relativePath = UpdaterUtil.GetRelativePath(file, item.Directory);
string relativePath = Path.GetRelativePath(item.Directory, file);
string fullPath = Path.GetFullPath(relativePath);
if (contentPackage.Files.Any(f => { string fp = Path.GetFullPath(f.Path); return fp == fullPath; })) { continue; }
nonContentFiles.Add(relativePath);

View File

@@ -3,7 +3,6 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Reflection.Metadata;
namespace Barotrauma.Particles
{
@@ -16,6 +15,8 @@ namespace Barotrauma.Particles
public delegate void OnChangeHullHandler(Vector2 position, Hull currentHull);
public OnChangeHullHandler OnChangeHull;
public OnChangeHullHandler OnCollision;
private Vector2 position;
private Vector2 prevPosition;
@@ -166,6 +167,7 @@ namespace Barotrauma.Particles
HighQualityCollisionDetection = false;
OnChangeHull = null;
OnCollision = null;
subEmitters.Clear();
hasSubEmitters = false;
@@ -340,12 +342,20 @@ namespace Barotrauma.Particles
Vector2 collisionNormal = Vector2.Zero;
if (velocity.Y < 0.0f && position.Y - prefab.CollisionRadius * size.Y < hullRect.Y - hullRect.Height)
{
if (prefab.DeleteOnCollision) { return UpdateResult.Delete; }
if (prefab.DeleteOnCollision)
{
OnCollision?.Invoke(position, currentHull);
return UpdateResult.Delete;
}
collisionNormal = new Vector2(0.0f, 1.0f);
}
else if (velocity.Y > 0.0f && position.Y + prefab.CollisionRadius * size.Y > hullRect.Y)
{
if (prefab.DeleteOnCollision) { return UpdateResult.Delete; }
if (prefab.DeleteOnCollision)
{
OnCollision?.Invoke(position, currentHull);
return UpdateResult.Delete;
}
collisionNormal = new Vector2(0.0f, -1.0f);
}
@@ -487,6 +497,8 @@ namespace Barotrauma.Particles
velocity.Y = Math.Sign(collisionNormal.Y) * Math.Abs(velocity.Y) * prefab.Restitution;
}
OnCollision?.Invoke(position, currentHull);
velocity += subVel;
}
@@ -523,6 +535,8 @@ namespace Barotrauma.Particles
velocity.Y *= (1.0f - prefab.Friction);
}
OnCollision?.Invoke(position, currentHull);
velocity *= prefab.Restitution;
}

View File

@@ -138,6 +138,8 @@ namespace Barotrauma.Particles
public void Emit(float deltaTime, Vector2 position, Hull hullGuess = null, float angle = 0.0f, float particleRotation = 0.0f, float velocityMultiplier = 1.0f, float sizeMultiplier = 1.0f, float amountMultiplier = 1.0f, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, Tuple<Vector2, Vector2> tracerPoints = null)
{
if (GameMain.Client?.MidRoundSyncing ?? false) { return; }
if (initialDelay < Prefab.Properties.InitialDelay)
{
initialDelay += deltaTime;

View File

@@ -133,22 +133,41 @@ namespace Barotrauma.Particles
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
{
if (particleCount >= MaxParticles || prefab == null || prefab.Sprites.Count == 0) { return null; }
// this should be optimized for tracers after prototyping
if (tracerPoints == null)
if (prefab == null || prefab.Sprites.Count == 0) { return null; }
if (particleCount >= MaxParticles)
{
Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity);
Vector2 minPos = new Vector2(Math.Min(position.X, particleEndPos.X), Math.Min(position.Y, particleEndPos.Y));
Vector2 maxPos = new Vector2(Math.Max(position.X, particleEndPos.X), Math.Max(position.Y, particleEndPos.Y));
Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
for (int i = 0; i < particleCount; i++)
{
if (particles[i].Prefab.Priority < prefab.Priority)
{
RemoveParticle(i);
break;
}
}
if (particleCount >= MaxParticles) { return null; }
}
Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity);
Vector2 minPos = new Vector2(Math.Min(position.X, particleEndPos.X), Math.Min(position.Y, particleEndPos.Y));
Vector2 maxPos = new Vector2(Math.Max(position.X, particleEndPos.X), Math.Max(position.Y, particleEndPos.Y));
if (tracerPoints != null)
{
minPos = new Vector2(
Math.Min(Math.Min(minPos.X, tracerPoints.Item1.X), tracerPoints.Item2.X),
Math.Min(Math.Min(minPos.Y, tracerPoints.Item1.Y), tracerPoints.Item2.Y));
maxPos = new Vector2(
Math.Max(Math.Max(maxPos.X, tracerPoints.Item1.X), tracerPoints.Item2.X),
Math.Max(Math.Max(maxPos.Y, tracerPoints.Item1.Y), tracerPoints.Item2.Y));
}
Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
if (particles[particleCount] == null) particles[particleCount] = new Particle();
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer, tracerPoints: tracerPoints);

View File

@@ -180,16 +180,13 @@ namespace Barotrauma.Particles
[Editable, Serialize("1.0,1.0", false, description: "The maximum initial size of the particle.")]
public Vector2 StartSizeMax { get; private set; }
[Editable]
[Serialize("0.0,0.0", false, description: "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
[Editable, Serialize("0.0,0.0", false, description: "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
public Vector2 SizeChangeMin { get; private set; }
[Editable]
[Serialize("0.0,0.0", false, description: "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
[Editable, Serialize("0.0,0.0", false, description: "How much the size of the particle changes per second. The rate of growth for each particle is randomize between SizeChangeMin and SizeChangeMax.")]
public Vector2 SizeChangeMax { get; private set; }
[Editable]
[Serialize(0.0f, false, description: "How many seconds it takes for the particle to grow to it's initial size.")]
[Editable, Serialize(0.0f, false, description: "How many seconds it takes for the particle to grow to it's initial size.")]
public float GrowTime { get; private set; }
//rendering -----------------------------------------
@@ -215,6 +212,9 @@ namespace Barotrauma.Particles
[Editable, Serialize(ParticleBlendState.AlphaBlend, false, description: "The type of blending to use when rendering the particle.")]
public ParticleBlendState BlendState { get; private set; }
[Editable, Serialize(0, false, description: "Particles with a higher priority can replace lower-priority ones if the maximum number of active particles has been reached.")]
public int Priority { get; private set; }
//animation -----------------------------------------
[Editable(0.0f, float.MaxValue), Serialize(1.0f, false, description: "The duration of the particle's animation cycle (if it's animated).")]

View File

@@ -54,9 +54,11 @@ namespace Barotrauma
executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
Directory.SetCurrentDirectory(executableDir);
SteamManager.Initialize();
EnableNvOptimus();
Game = new GameMain(args);
Game.Run();
Game.Dispose();
FreeNvOptimus();
CrossThread.ProcessTasks();
}
@@ -263,6 +265,27 @@ namespace Barotrauma
" if you'd like to help fix this bug, you may post it on Barotrauma's GitHub issue tracker: https://github.com/Regalis11/Barotrauma/issues/", filePath);
}
}
}
private static IntPtr nvApi64Dll = IntPtr.Zero;
private static void EnableNvOptimus()
{
#if WINDOWS && X64
// We force load nvapi64.dll so nvidia gives us the dedicated GPU on optimus laptops.
// This is not a method for getting optimus that is documented by nvidia, but it works, so...
if (NativeLibrary.TryLoad("nvapi64.dll", out nvApi64Dll))
{
DebugConsole.Log("Loaded nvapi64.dll successfully");
}
#endif
}
private static void FreeNvOptimus()
{
#warning TODO: determine if we can do this safely
//NativeLibrary.Free(nvApi64Dll);
}
}
#endif
}

View File

@@ -466,7 +466,7 @@ namespace Barotrauma
{
var sub = child.UserData as SubmarineInfo;
if (sub == null) { return; }
child.Visible = string.IsNullOrEmpty(filter) ? true : sub.DisplayName.ToLower().Contains(filter.ToLower());
child.Visible = string.IsNullOrEmpty(filter) || sub.DisplayName.ToLower().Contains(filter.ToLower());
}
}

View File

@@ -628,7 +628,7 @@ namespace Barotrauma
{
TextGetter = () =>
{
return TextManager.AddPunctuation(':', TextManager.Get("Missions"), $"{Campaign.NumberOfMissionsAtLocation(destination)}/{Campaign.Settings.MaxMissionCount}");
return TextManager.AddPunctuation(':', TextManager.Get("Missions"), $"{Campaign.NumberOfMissionsAtLocation(destination)}/{Campaign.Settings.TotalMaxMissionCount}");
}
};
@@ -735,7 +735,7 @@ namespace Barotrauma
private void UpdateMaxMissions(Location location)
{
hasMaxMissions = Campaign.NumberOfMissionsAtLocation(location) >= Campaign.Settings.MaxMissionCount;
hasMaxMissions = Campaign.NumberOfMissionsAtLocation(location) >= Campaign.Settings.TotalMaxMissionCount;
}
}
}

View File

@@ -262,7 +262,7 @@ namespace Barotrauma.CharacterEditor
{
FileSelection.OnFileSelected = (file) =>
{
string relativePath = UpdaterUtil.GetRelativePath(Path.GetFullPath(file), Environment.CurrentDirectory);
string relativePath = Path.GetRelativePath(Environment.CurrentDirectory, Path.GetFullPath(file));
string destinationPath = relativePath;
//copy file to XML path if it's not located relative to the game's files

View File

@@ -541,7 +541,7 @@ namespace Barotrauma
private XElement? ExportXML()
{
XElement mainElement = new XElement("ScriptedEvent", new XAttribute("identifier", projectName.RemoveWhitespace().ToLower()));
XElement mainElement = new XElement("ScriptedEvent", new XAttribute("identifier", projectName.RemoveWhitespace().ToLowerInvariant()));
EditorNode? startNode = null;
foreach (EditorNode eventNode in nodeList.Where(node => node is EventNode || node is SpecialNode))
{

View File

@@ -25,6 +25,7 @@ namespace Barotrauma
public Effect PostProcessEffect { get; private set; }
public Effect GradientEffect { get; private set; }
public Effect GrainEffect { get; private set; }
public Effect BlueprintEffect { get; set; }
public GameScreen(GraphicsDevice graphics, ContentManager content)
{
@@ -43,12 +44,14 @@ namespace Barotrauma
PostProcessEffect = content.Load<Effect>("Effects/postprocess_opengl");
GradientEffect = content.Load<Effect>("Effects/gradientshader_opengl");
GrainEffect = content.Load<Effect>("Effects/grainshader_opengl");
BlueprintEffect = content.Load<Effect>("Effects/blueprintshader_opengl");
#else
//var blurEffect = content.Load<Effect>("Effects/blurshader");
damageEffect = content.Load<Effect>("Effects/damageshader");
PostProcessEffect = content.Load<Effect>("Effects/postprocess");
GradientEffect = content.Load<Effect>("Effects/gradientshader");
GrainEffect = content.Load<Effect>("Effects/grainshader");
BlueprintEffect = content.Load<Effect>("Effects/blueprintshader");
#endif
damageStencil = TextureLoader.FromFile("Content/Map/walldamage.png");

View File

@@ -2020,7 +2020,8 @@ namespace Barotrauma
var playerFrame = (GUITextBlock)PlayerList.Content.FindChild(client);
if (playerFrame == null) { return; }
playerFrame.Text = client.Name;
playerFrame.ToolTip = "";
Color color = Color.White;
if (SelectedMode == GameModePreset.PvP)
{
@@ -2028,15 +2029,28 @@ namespace Barotrauma
{
case CharacterTeamType.Team1:
color = new Color(0, 110, 150, 255);
playerFrame.ToolTip = TextManager.GetWithVariable("teampreference", "[team]", TextManager.Get("teampreference.team1"));
break;
case CharacterTeamType.Team2:
color = new Color(150, 110, 0, 255);
playerFrame.ToolTip = TextManager.GetWithVariable("teampreference", "[team]", TextManager.Get("teampreference.team2"));
break;
default:
playerFrame.ToolTip = TextManager.GetWithVariable("teampreference", "[team]", TextManager.Get("none"));
break;
}
}
else if (JobPrefab.Prefabs.ContainsKey(client.PreferredJob))
else
{
color = JobPrefab.Prefabs[client.PreferredJob].UIColor;
if (JobPrefab.Prefabs.ContainsKey(client.PreferredJob))
{
color = JobPrefab.Prefabs[client.PreferredJob].UIColor;
playerFrame.ToolTip = TextManager.GetWithVariable("jobpreference", "[job]", JobPrefab.Prefabs[client.PreferredJob].Name);
}
else
{
playerFrame.ToolTip = TextManager.GetWithVariable("jobpreference", "[job]", TextManager.Get("none"));
}
}
playerFrame.Color = color * 0.4f;
playerFrame.HoverColor = color * 0.6f;

View File

@@ -24,7 +24,8 @@ namespace Barotrauma
private GUIFrame menu;
private GUIListBox serverList;
private GUIFrame serverPreview;
private GUIFrame serverPreviewContainer;
private GUIListBox serverPreview;
private GUIButton joinButton;
private ServerInfo selectedServer;
@@ -340,11 +341,11 @@ namespace Barotrauma
void RecalculateHolder()
{
float listContainerSubtract = filtersHolder.Visible ? sidebarWidth : 0.0f;
listContainerSubtract += serverPreview.Visible ? sidebarWidth : 0.0f;
listContainerSubtract += serverPreviewContainer.Visible ? sidebarWidth : 0.0f;
float toggleButtonsSubtract = 1.1f * filterToggle.Rect.Width / serverListHolder.Rect.Width;
listContainerSubtract += filterToggle.Visible ? toggleButtonsSubtract : 0.0f;
listContainerSubtract += serverPreviewToggleButton.Visible ? toggleButtonsSubtract : 0.0f;
listContainerSubtract += serverPreviewContainer.Visible ? toggleButtonsSubtract : 0.0f;
serverListContainer.RectTransform.RelativeSize = new Vector2(1.0f - listContainerSubtract, 1.0f);
serverListHolder.Recalculate();
@@ -567,17 +568,17 @@ namespace Barotrauma
{
joinButton.Enabled = true;
selectedServer = serverInfo;
if (!serverPreview.Visible)
if (!serverPreviewContainer.Visible)
{
serverPreview.RectTransform.RelativeSize = new Vector2(sidebarWidth, 1.0f);
serverPreviewContainer.RectTransform.RelativeSize = new Vector2(sidebarWidth, 1.0f);
serverPreviewToggleButton.Visible = true;
serverPreviewToggleButton.IgnoreLayoutGroups = false;
serverPreview.Visible = true;
serverPreview.IgnoreLayoutGroups = false;
serverPreviewContainer.Visible = true;
serverPreviewContainer.IgnoreLayoutGroups = false;
RecalculateHolder();
}
serverInfo.CreatePreviewWindow(serverPreview);
btn.Children.ForEach(c => c.SpriteEffects = serverPreview.Visible ? SpriteEffects.None : SpriteEffects.FlipHorizontally);
serverInfo.CreatePreviewWindow(serverPreview.Content);
btn.Children.ForEach(c => c.SpriteEffects = serverPreviewContainer.Visible ? SpriteEffects.None : SpriteEffects.FlipHorizontally);
}
return true;
}
@@ -592,24 +593,28 @@ namespace Barotrauma
Visible = false,
OnClicked = (btn, userdata) =>
{
serverPreview.RectTransform.RelativeSize = new Vector2(0.2f, 1.0f);
serverPreview.Visible = !serverPreview.Visible;
serverPreview.IgnoreLayoutGroups = !serverPreview.Visible;
serverPreviewContainer.RectTransform.RelativeSize = new Vector2(0.2f, 1.0f);
serverPreviewContainer.Visible = !serverPreviewContainer.Visible;
serverPreviewContainer.IgnoreLayoutGroups = !serverPreviewContainer.Visible;
RecalculateHolder();
btn.Children.ForEach(c => c.SpriteEffects = serverPreview.Visible ? SpriteEffects.None : SpriteEffects.FlipHorizontally);
btn.Children.ForEach(c => c.SpriteEffects = serverPreviewContainer.Visible ? SpriteEffects.None : SpriteEffects.FlipHorizontally);
return true;
}
};
serverPreview = new GUIFrame(new RectTransform(new Vector2(sidebarWidth, 1.0f), serverListHolder.RectTransform, Anchor.Center), style: null)
serverPreviewContainer = new GUIFrame(new RectTransform(new Vector2(sidebarWidth, 1.0f), serverListHolder.RectTransform, Anchor.Center), style: null)
{
Color = new Color(12, 14, 15, 255) * 0.5f,
OutlineColor = Color.Black,
IgnoreLayoutGroups = true,
Visible = false
};
serverPreview = new GUIListBox(new RectTransform(Vector2.One, serverPreviewContainer.RectTransform, Anchor.Center))
{
Padding = Vector4.One * 10 * GUI.Scale
};
// Spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.02f), bottomRow.RectTransform), style: null);
@@ -1697,7 +1702,7 @@ namespace Barotrauma
UpdateFriendsList();
serverList.ClearChildren();
serverPreview.ClearChildren();
serverPreview.Content.ClearChildren();
joinButton.Enabled = false;
selectedServer = null;

View File

@@ -1604,7 +1604,7 @@ namespace Barotrauma
if (string.IsNullOrEmpty(file) || !File.Exists(file)) { continue; }
string modFolder = Path.GetDirectoryName(itemContentPackage.Path);
string filePathRelativeToModFolder = UpdaterUtil.GetRelativePath(file, Path.Combine(Environment.CurrentDirectory, modFolder));
string filePathRelativeToModFolder = Path.GetRelativePath(Path.Combine(Environment.CurrentDirectory, modFolder), file);
//file is not inside the mod folder, we need to move it
if (filePathRelativeToModFolder.StartsWith("..") ||

View File

@@ -0,0 +1,125 @@
#nullable enable
using System;
using System.Linq;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
/*
* This screen only exists because I'm going mental without access to EnC on Linux.
* This is fucking stupid and horrible.
* Remember to remove this crap eventually.
* - Markus
*/
namespace Barotrauma
{
class TestScreen : EditorScreen
{
public override Camera Cam { get; }
private Item? miniMapItem;
private Submarine? submarine;
private Character? dummyCharacter;
public static Effect BlueprintEffect;
public TestScreen()
{
Cam = new Camera();
BlueprintEffect = GameMain.GameScreen.BlueprintEffect;
new GUIButton(new RectTransform(new Point(256, 256), Frame.RectTransform), "Reload shader")
{
OnClicked = (button, o) =>
{
BlueprintEffect.Dispose();
GameMain.Instance.Content.Unload();
BlueprintEffect = GameMain.Instance.Content.Load<Effect>("Effects/blueprintshader_opengl");
GameMain.GameScreen.BlueprintEffect = BlueprintEffect;
return true;
}
};
}
public override void Select()
{
base.Select();
if (dummyCharacter is { Removed: false })
{
dummyCharacter?.Remove();
}
// ????????
submarine = new Submarine(SubmarineInfo.SavedSubmarines.FirstOrDefault(info => info.Name.Equals("Kastrull", StringComparison.OrdinalIgnoreCase)));
miniMapItem = new Item(ItemPrefab.Find(null, "statusmonitor"), Vector2.Zero, submarine);
MiniMap miniMap = miniMapItem.GetComponent<MiniMap>();
miniMap.PowerConsumption = 0;
dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.DummyID, hasAi: false);
dummyCharacter.Info.Name = "Galldren";
dummyCharacter.Inventory.CreateSlots();
Character.Controlled = dummyCharacter;
GameMain.World.ProcessChanges();
}
public override void AddToGUIUpdateList()
{
Frame.AddToGUIUpdateList();
CharacterHUD.AddToGUIUpdateList(dummyCharacter);
dummyCharacter?.SelectedConstruction?.AddToGUIUpdateList();
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
if (dummyCharacter is { } dummy && miniMapItem is { } item)
{
if (dummy.SelectedConstruction != item)
{
dummy.SelectedConstruction = item;
}
dummy.SelectedConstruction?.UpdateHUD(Cam, dummy, (float)deltaTime);
Vector2 pos = FarseerPhysics.ConvertUnits.ToSimUnits(item.Position);
foreach (Limb limb in dummy.AnimController.Limbs)
{
limb.body.SetTransform(pos, 0.0f);
}
if (dummy.AnimController?.Collider is { } collider)
{
collider.SetTransform(pos, 0);
}
dummy.ControlLocalPlayer((float)deltaTime, Cam, false);
dummy.Control((float)deltaTime, Cam);
dummy.Submarine = submarine;
}
}
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
base.Draw(deltaTime, graphics, spriteBatch);
graphics.Clear(BackgroundColor);
spriteBatch.Begin(SpriteSortMode.BackToFront, transformMatrix: Cam.Transform);
miniMapItem?.Draw(spriteBatch, false);
if (dummyCharacter is { } dummy)
{
dummyCharacter.DrawFront(spriteBatch, Cam);
dummyCharacter.Draw(spriteBatch, Cam);
}
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState);
GUI.Draw(Cam, spriteBatch);
dummyCharacter?.DrawHUD(spriteBatch, Cam, false);
spriteBatch.End();
}
}
}

View File

@@ -38,6 +38,7 @@ namespace Barotrauma
public readonly string File;
public readonly string Type;
public readonly bool DuckVolume;
public readonly float Volume;
public readonly Vector2 IntensityRange;
@@ -52,6 +53,7 @@ namespace Barotrauma
this.Type = element.GetAttributeString("type", "").ToLowerInvariant();
this.IntensityRange = element.GetAttributeVector2("intensityrange", new Vector2(0.0f, 100.0f));
this.DuckVolume = element.GetAttributeBool("duckvolume", false);
this.Volume = element.GetAttributeFloat("volume", 1.0f);
this.ContinueFromPreviousTime = element.GetAttributeBool("continuefromprevioustime", false);
this.Element = element;
}
@@ -816,6 +818,8 @@ namespace Barotrauma
}
}
int noiseLoopIndex = 1;
updateMusicTimer -= deltaTime;
if (updateMusicTimer <= 0.0f)
{
@@ -851,7 +855,6 @@ namespace Barotrauma
}
}
int noiseLoopIndex = 1;
if (Level.Loaded?.Type == LevelData.LevelType.LocationConnection)
{
// Find background noise loop for the current biome
@@ -917,7 +920,7 @@ namespace Barotrauma
{
//mute the channel
musicChannel[i].Gain = MathHelper.Lerp(musicChannel[i].Gain, 0.0f, MusicLerpSpeed * deltaTime);
if (musicChannel[i].Gain < 0.01f) DisposeMusicChannel(i);
if (musicChannel[i].Gain < 0.01f) { DisposeMusicChannel(i); }
}
}
//something should be playing, but the targetMusic is invalid
@@ -932,7 +935,7 @@ namespace Barotrauma
if (musicChannel[i] != null && musicChannel[i].IsPlaying)
{
musicChannel[i].Gain = MathHelper.Lerp(musicChannel[i].Gain, 0.0f, MusicLerpSpeed * deltaTime);
if (musicChannel[i].Gain < 0.01f) DisposeMusicChannel(i);
if (musicChannel[i].Gain < 0.01f) { DisposeMusicChannel(i); }
}
//channel free now, start playing the correct clip
if (currentMusic[i] == null || (musicChannel[i] == null || !musicChannel[i].IsPlaying))
@@ -949,7 +952,7 @@ namespace Barotrauma
targetMusic[i] = null;
break;
}
musicChannel[i] = currentMusic[i].Play(0.0f, "music");
musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "" : "music");
if (targetMusic[i].ContinueFromPreviousTime)
{
musicChannel[i].StreamSeekPos = targetMusic[i].PreviousTime;
@@ -963,13 +966,13 @@ namespace Barotrauma
if (musicChannel[i] == null || !musicChannel[i].IsPlaying)
{
musicChannel[i]?.Dispose();
musicChannel[i] = currentMusic[i].Play(0.0f, "music");
musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "" : "music");
musicChannel[i].Looping = true;
}
float targetGain = 1.0f;
float targetGain = targetMusic[i].Volume;
if (targetMusic[i].DuckVolume)
{
targetGain = (float)Math.Sqrt(1.0f / activeTrackCount);
targetGain *= (float)Math.Sqrt(1.0f / activeTrackCount);
}
musicChannel[i].Gain = MathHelper.Lerp(musicChannel[i].Gain, targetGain, MusicLerpSpeed * deltaTime);
}

View File

@@ -1,9 +1,10 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Color = Microsoft.Xna.Framework.Color;
namespace Barotrauma
{
@@ -54,6 +55,225 @@ namespace Barotrauma
return isInside;
}
public static Vector2 GetPolygonBoundingBoxSize(List<Vector2> verticess)
{
float minX = verticess[0].X;
float maxX = verticess[0].X;
float minY = verticess[0].Y;
float maxY = verticess[0].Y;
foreach (var (vertX, vertY) in verticess)
{
minX = Math.Min(vertX, minX);
maxX = Math.Max(vertX, maxX);
minY = Math.Min(vertY, minY);
maxY = Math.Max(vertY, maxY);
}
return new Vector2(maxX - minX, maxY - minY);
}
public static List<Vector2> ScalePolygon(List<Vector2> vertices, Vector2 scale)
{
List<Vector2> newVertices = new List<Vector2>();
Vector2 center = GetPolygonCentroid(vertices);
foreach (Vector2 vert in vertices)
{
Vector2 centerVector = vert - center;
Vector2 centerVectorScale = centerVector * scale;
Vector2 scaledVector = centerVectorScale + center;
newVertices.Add(scaledVector);
}
return newVertices;
}
public static Vector2 GetPolygonCentroid(List<Vector2> poly)
{
float accumulatedArea = 0.0f;
float centerX = 0.0f;
float centerY = 0.0f;
for (int i = 0, j = poly.Count - 1; i < poly.Count; j = i++)
{
float temp = poly[i].X * poly[j].Y - poly[j].X * poly[i].Y;
accumulatedArea += temp;
centerX += (poly[i].X + poly[j].X) * temp;
centerY += (poly[i].Y + poly[j].Y) * temp;
}
if (Math.Abs(accumulatedArea) < 1E-7f) { return Vector2.Zero; } // Avoid division by zero
accumulatedArea *= 3f;
return new Vector2(centerX / accumulatedArea, centerY / accumulatedArea);
}
public static List<Vector2> SnapVertices(List<Vector2> points, int treshold = 1)
{
Stack<Vector2> toCheck = new Stack<Vector2>();
List<Vector2> newPoints = new List<Vector2>();
foreach (Vector2 point in points)
{
toCheck.Push(point);
}
while (toCheck.TryPop(out Vector2 point))
{
Vector2 newPoint = new Vector2(point.X, point.Y);
foreach (Vector2 otherPoint in toCheck.Concat(newPoints))
{
float diffX = Math.Abs(newPoint.X - otherPoint.X),
diffY = Math.Abs(newPoint.Y - otherPoint.Y);
if (diffX <= treshold)
{
newPoint.X = Math.Max(newPoint.X, otherPoint.X);
}
if (diffY <= treshold)
{
newPoint.Y = Math.Max(newPoint.Y, otherPoint.Y);
}
}
newPoints.Add(newPoint);
}
return newPoints;
}
public static ImmutableArray<RectangleF> SnapRectangles(IEnumerable<RectangleF> rects, int treshold = 1)
{
List<RectangleF> list = new List<RectangleF>();
List<Vector2> points = new List<Vector2>();
foreach (RectangleF rect in rects)
{
points.Add(new Vector2(rect.Left, rect.Top));
points.Add(new Vector2(rect.Right, rect.Top));
points.Add(new Vector2(rect.Right, rect.Bottom));
points.Add(new Vector2(rect.Left, rect.Bottom));
}
points = SnapVertices(points, treshold);
for (int i = 0; i < points.Count; i += 4)
{
Vector2 topLeft = points[i];
Vector2 bottomRight = points[i + 2];
list.Add(new RectangleF(topLeft, bottomRight - topLeft));
}
return list.ToImmutableArray();
}
public static List<List<Vector2>> CombineRectanglesIntoShape(IEnumerable<RectangleF> rectangles)
{
List<Vector2> points =
(from point in rectangles.SelectMany(RectangleToPoints)
group point by point
into g
where g.Count() % 2 == 1
select g.Key)
.ToList();
List<Vector2> sortedY = points.OrderBy(p => p.Y).ThenByDescending(p => p.X).ToList();
List<Vector2> sortedX = points.OrderBy(p => p.X).ThenByDescending(p => p.Y).ToList();
Dictionary<Vector2, Vector2> edgesH = new Dictionary<Vector2, Vector2>();
Dictionary<Vector2, Vector2> edgesV = new Dictionary<Vector2, Vector2>();
int i = 0;
while (i < points.Count)
{
float currY = sortedY[i].Y;
while (i < points.Count && Math.Abs(sortedY[i].Y - currY) < 0.01f)
{
edgesH[sortedY[i]] = sortedY[i + 1];
edgesH[sortedY[i + 1]] = sortedY[i];
i += 2;
}
}
i = 0;
while (i < points.Count)
{
float currX = sortedX[i].X;
while (i < points.Count && Math.Abs(sortedX[i].X - currX) < 0.01f)
{
edgesV[sortedX[i]] = sortedX[i + 1];
edgesV[sortedX[i + 1]] = sortedX[i];
i += 2;
}
}
List<List<Vector2>> polygons = new List<List<Vector2>>();
while (edgesH.Any())
{
var (key, _) = edgesH.First();
List<(Vector2 Point, int Direction)> polygon = new List<(Vector2 Point, int Direction)> { (key, 0) };
edgesH.Remove(key);
while (true)
{
var (curr, direction) = polygon[^1];
if (direction == 0)
{
Vector2 nextVertex = edgesV[curr];
edgesV.Remove(curr);
polygon.Add((nextVertex, 1));
}
else
{
Vector2 nextVertex = edgesH[curr];
edgesH.Remove(curr);
polygon.Add((nextVertex, 0));
}
if (polygon[^1] == polygon[0])
{
polygon.Remove(polygon[^1]);
break;
}
}
List<Vector2> poly = polygon.Select(t => t.Point).ToList();
foreach (Vector2 vertex in poly)
{
if (edgesH.ContainsKey(vertex))
{
edgesH.Remove(vertex);
}
if (edgesV.ContainsKey(vertex))
{
edgesV.Remove(vertex);
}
}
polygons.Add(poly);
}
return polygons;
static IEnumerable<Vector2> RectangleToPoints(RectangleF rect)
{
(float x1, float y1, float x2, float y2) = (rect.Left, rect.Top, rect.Right, rect.Bottom);
Vector2[] pts = { new Vector2(x1, y1), new Vector2(x2, y1), new Vector2(x2, y2), new Vector2(x1, y2) };
return pts;
}
}
// Convert an RGB value into an HLS value.
public static Vector3 RgbToHLS(this Color color)

View File

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

View File

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

View File

@@ -67,3 +67,9 @@
/processorParam:DebugMode=Auto
/build:grainshader.fx
#begin blueprintshader.fx
/importer:EffectImporter
/processor:EffectProcessor
/processorParam:DebugMode=Auto
/build:blueprintshader.fx

View File

@@ -67,3 +67,8 @@
/processorParam:DebugMode=Auto
/build:grainshader_opengl.fx
#begin blueprintshader_opengl.fx
/importer:EffectImporter
/processor:EffectProcessor
/processorParam:DebugMode=Auto
/build:blueprintshader_opengl.fx

View File

@@ -0,0 +1,48 @@
// vim:ft=hlsl
sampler TextureSampler : register(s0);
float width;
float height;
float3 sobel(float2 uv)
{
float x = 0;
float y = 0;
float w = 1.0 / width;
float h = 1.0 / height;
x += tex2D(TextureSampler, uv + float2(-w, -h)) * -1.0;
x += tex2D(TextureSampler, uv + float2(-w, 0)) * -2.0;
x += tex2D(TextureSampler, uv + float2(-w, h)) * -1.0;
x += tex2D(TextureSampler, uv + float2( w, -h)) * 1.0;
x += tex2D(TextureSampler, uv + float2( w, 0)) * 2.0;
x += tex2D(TextureSampler, uv + float2( w, h)) * 1.0;
y += tex2D(TextureSampler, uv + float2(-w, -h)) * -1.0;
y += tex2D(TextureSampler, uv + float2( 0, -h)) * -2.0;
y += tex2D(TextureSampler, uv + float2( w, -h)) * -1.0;
y += tex2D(TextureSampler, uv + float2(-w, h)) * 1.0;
y += tex2D(TextureSampler, uv + float2( 0, h)) * 2.0;
y += tex2D(TextureSampler, uv + float2( w, h)) * 1.0;
return sqrt(x * x + y * y);
}
float4 blueprint(float4 position : SV_POSITION, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
float3 s = sobel(texCoord);
float a = tex2D(TextureSampler, texCoord).a;
a *= clr.a;
return float4(clr.r + s.r, clr.g + s.g, clr.b + s.b, a);
}
technique Blueprint
{
pass Pass1
{
PixelShader = compile ps_4_0_level_9_1 blueprint();
}
}

View File

@@ -0,0 +1,48 @@
// vim:ft=hlsl
sampler TextureSampler : register(s0);
float width;
float height;
float3 sobel(float2 uv)
{
float x = 0;
float y = 0;
float w = 1.0 / width;
float h = 1.0 / height;
x += tex2D(TextureSampler, uv + float2(-w, -h)) * -1.0;
x += tex2D(TextureSampler, uv + float2(-w, 0)) * -2.0;
x += tex2D(TextureSampler, uv + float2(-w, h)) * -1.0;
x += tex2D(TextureSampler, uv + float2( w, -h)) * 1.0;
x += tex2D(TextureSampler, uv + float2( w, 0)) * 2.0;
x += tex2D(TextureSampler, uv + float2( w, h)) * 1.0;
y += tex2D(TextureSampler, uv + float2(-w, -h)) * -1.0;
y += tex2D(TextureSampler, uv + float2( 0, -h)) * -2.0;
y += tex2D(TextureSampler, uv + float2( w, -h)) * -1.0;
y += tex2D(TextureSampler, uv + float2(-w, h)) * 1.0;
y += tex2D(TextureSampler, uv + float2( 0, h)) * 2.0;
y += tex2D(TextureSampler, uv + float2( w, h)) * 1.0;
return sqrt(x * x + y * y);
}
float4 blueprint(float4 position : SV_POSITION, float4 clr : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0
{
float3 s = sobel(texCoord);
float a = tex2D(TextureSampler, texCoord).a;
a *= clr.a;
return float4(clr.r + s.r, clr.g + s.g, clr.b + s.b, a);
}
technique Blueprint
{
pass Pass1
{
PixelShader = compile ps_3_0 blueprint();
}
}

View File

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

View File

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

View File

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

View File

@@ -46,5 +46,10 @@ namespace Barotrauma
}
}
}
partial void OnMoneyChanged(int prevAmount, int newAmount)
{
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateMoney });
}
}
}

View File

@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
@@ -22,6 +23,14 @@ namespace Barotrauma
}
}
partial void OnExperienceChanged(int prevAmount, int newAmount, Vector2 textPopupPos)
{
if (Math.Abs(prevAmount - newAmount) > 0)
{
GameMain.NetworkMember.CreateEntityEvent(Character, new object[] { NetEntityEvent.Type.UpdateExperience });
}
}
public void ServerWrite(IWriteMessage msg)
{
msg.Write(ID);
@@ -53,6 +62,17 @@ namespace Barotrauma
msg.Write((byte)0);
}
// TODO: animations
msg.Write((byte)savedStatValues.SelectMany(s => s.Value).Count());
foreach (var savedStatValuePair in savedStatValues)
{
foreach (var savedStatValue in savedStatValuePair.Value)
{
msg.Write((byte)savedStatValuePair.Key);
msg.Write(savedStatValue.StatIdentifier);
msg.Write(savedStatValue.StatValue);
msg.Write(savedStatValue.RemoveOnDeath);
}
}
}
}
}

View File

@@ -238,7 +238,7 @@ namespace Barotrauma
break;
case ClientNetObject.ENTITY_STATE:
int eventType = msg.ReadRangedInteger(0, 3);
int eventType = msg.ReadRangedInteger(0, 4);
switch (eventType)
{
case 0:
@@ -268,8 +268,35 @@ namespace Barotrauma
if (IsIncapacitated)
{
var causeOfDeath = CharacterHealth.GetCauseOfDeath();
Kill(causeOfDeath.First, causeOfDeath.Second);
Kill(causeOfDeath.type, causeOfDeath.affliction);
}
break;
case 3: // NetEntityEvent.Type.UpdateTalents
if (c.Character != this)
{
#if DEBUG
DebugConsole.Log("Received a character update message from a client who's not controlling the character");
#endif
return;
}
// get the full list of talents from the player, only give the ones
// that are not already given (or otherwise not viable)
ushort talentCount = msg.ReadUInt16();
List<string> talentSelection = new List<string>();
for (int i = 0; i < talentCount; i++)
{
UInt32 talentIdentifier = msg.ReadUInt32();
var prefab = TalentPrefab.TalentPrefabs.Find(p => p.UIntIdentifier == talentIdentifier);
if (prefab != null) { talentSelection.Add(prefab.Identifier); }
}
talentSelection = TalentTree.CheckTalentSelection(this, talentSelection);
foreach (string talent in talentSelection)
{
GiveTalent(talent);
}
break;
}
break;
@@ -283,7 +310,7 @@ namespace Barotrauma
if (extraData != null)
{
const int min = 0, max = 9;
const int min = 0, max = 12;
switch ((NetEntityEvent.Type)extraData[0])
{
case NetEntityEvent.Type.InventoryState:
@@ -394,6 +421,22 @@ namespace Barotrauma
msg.Write(inventoryItemIDs[i]);
}
break;
case NetEntityEvent.Type.UpdateExperience:
msg.WriteRangedInteger(10, min, max);
msg.Write(Info.ExperiencePoints);
break;
case NetEntityEvent.Type.UpdateTalents:
msg.WriteRangedInteger(11, min, max);
msg.Write((ushort)characterTalents.Count);
foreach (var unlockedTalent in characterTalents)
{
msg.Write(unlockedTalent.Prefab.UIntIdentifier);
}
break;
case NetEntityEvent.Type.UpdateMoney:
msg.WriteRangedInteger(12, min, max);
msg.Write(GameMain.GameSession.Campaign.Money);
break;
default:
DebugConsole.ThrowError("Invalid NetworkEvent type for entity " + ToString() + " (" + (NetEntityEvent.Type)extraData[0] + ")");
break;
@@ -499,7 +542,7 @@ namespace Barotrauma
if (writeStatus)
{
WriteStatus(tempBuffer);
(AIController as EnemyAIController)?.PetBehavior?.ServerWrite(tempBuffer);
AIController?.ServerWrite(tempBuffer);
HealthUpdatePending = false;
}

View File

@@ -1187,6 +1187,7 @@ namespace Barotrauma
NewMessage("*****************", Color.Lime);
GameServer.Log("Console command \"restart\" executed: closing the server...", ServerLog.MessageType.ServerMessage);
GameMain.Instance.CloseServer();
GameMain.Instance.TryStartChildServerRelay();
GameMain.Instance.StartServer();
}));

View File

@@ -18,7 +18,17 @@ namespace Barotrauma
{
public static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
public static World World;
private static World world;
public static World World
{
get
{
if (world == null) { world = new World(new Vector2(0, -9.82f)); }
return world;
}
set { world = value; }
}
public static GameSettings Config;
public static GameServer Server;
@@ -123,6 +133,8 @@ namespace Barotrauma
ItemAssemblyPrefab.LoadAll();
LevelObjectPrefab.LoadAll();
BallastFloraPrefab.LoadAll(GetFilesOfType(ContentType.MapCreature));
TalentPrefab.LoadAll(GetFilesOfType(ContentType.Talents));
TalentTree.LoadAll(GetFilesOfType(ContentType.TalentTrees));
GameModePreset.Init();
DecalManager = new DecalManager();
@@ -179,6 +191,20 @@ namespace Barotrauma
}
}
public bool TryStartChildServerRelay()
{
for (int i = 0; i < CommandLineArgs.Length; i++)
{
switch (CommandLineArgs[i].Trim())
{
case "-pipes":
ChildServerRelay.Start(CommandLineArgs[i + 2], CommandLineArgs[i + 1]);
return true;
}
}
return false;
}
public void StartServer()
{
string name = "Server";
@@ -264,7 +290,7 @@ namespace Barotrauma
i++;
break;
case "-pipes":
ChildServerRelay.Start(CommandLineArgs[i + 2], CommandLineArgs[i + 1]);
//handled in TryStartChildServerRelay
i += 2;
break;
}
@@ -323,6 +349,7 @@ namespace Barotrauma
Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Items.Components.ItemComponent));
Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(typeof(Hull));
TryStartChildServerRelay();
Init();
StartServer();

View File

@@ -56,6 +56,6 @@ namespace Barotrauma
public void ApplyOrderData(Character character)
{
CharacterInfo.ApplyOrderData(character, OrderData);
}
}
}
}

View File

@@ -16,6 +16,12 @@ namespace Barotrauma.Items.Components
set { unsentChanges = value; }
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
pathFinder = null;
}
public void ServerRead(ClientNetObject type, IReadMessage msg, Barotrauma.Networking.Client c)
{

View File

@@ -289,6 +289,14 @@ namespace Barotrauma
teamID = (byte)wifiComponent.TeamID;
break;
}
if (teamID == 0)
{
foreach (IdCard idCard in GetComponents<IdCard>())
{
teamID = (byte)idCard.TeamID;
break;
}
}
msg.Write(teamID);
bool tagsChanged = tags.Count != prefab.Tags.Count || !tags.All(t => prefab.Tags.Contains(t));

View File

@@ -1,12 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Barotrauma.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
using System.IO.Pipes;
namespace Barotrauma.Networking
{

View File

@@ -2354,6 +2354,8 @@ namespace Barotrauma.Networking
characterData.ApplyHealthData(spawnedCharacter);
characterData.ApplyOrderData(spawnedCharacter);
spawnedCharacter.GiveIdCardTags(mainSubWaypoints[i]);
spawnedCharacter.LoadTalents();
characterData.HasSpawned = true;
}
spawnedCharacter.OwnerClientEndPoint = teamClients[i].Connection.EndPointString;
@@ -2366,6 +2368,8 @@ namespace Barotrauma.Networking
spawnedCharacter.TeamID = teamID;
spawnedCharacter.GiveJobItems(mainSubWaypoints[i]);
spawnedCharacter.GiveIdCardTags(mainSubWaypoints[i]);
// talents are only avilable for players in online sessions, but modders or someone else might want to have them loaded anyway
spawnedCharacter.LoadTalents();
}
}
@@ -2431,6 +2435,7 @@ namespace Barotrauma.Networking
roundStartTime = DateTime.Now;
startGameCoroutine = null;
yield return CoroutineStatus.Success;
}
@@ -2619,8 +2624,8 @@ namespace Barotrauma.Networking
}
}
Submarine.Unload();
entityEventManager.Clear();
Submarine.Unload();
GameMain.NetLobbyScreen.Select();
Log("Round ended.", ServerLog.MessageType.ServerMessage);
@@ -3145,28 +3150,19 @@ namespace Barotrauma.Networking
public void SendOrderChatMessage(OrderChatMessage message)
{
if (message.Sender == null || message.Sender.SpeechImpediment >= 100.0f) { return; }
//ChatMessageType messageType = ChatMessage.CanUseRadio(message.Sender) ? ChatMessageType.Radio : ChatMessageType.Default;
//check which clients can receive the message and apply distance effects
foreach (Client client in ConnectedClients)
{
string modifiedMessage = message.Text;
if (message.Sender != null &&
client.Character != null && !client.Character.IsDead)
if (message.Sender != null && client.Character != null && !client.Character.IsDead)
{
//too far to hear the msg -> don't send
if (!client.Character.CanHearCharacter(message.Sender)) { continue; }
}
SendDirectChatMessage(new OrderChatMessage(message.Order, message.OrderOption, message.OrderPriority, message.TargetEntity, message.TargetCharacter, message.Sender), client);
}
string myReceivedMessage = message.Text;
if (!string.IsNullOrWhiteSpace(myReceivedMessage))
if (!string.IsNullOrWhiteSpace(message.Text))
{
AddChatMessage(new OrderChatMessage(message.Order, message.OrderOption, message.OrderPriority, myReceivedMessage, message.TargetEntity, message.TargetCharacter, message.Sender));
AddChatMessage(new OrderChatMessage(message.Order, message.OrderOption, message.OrderPriority, message.Text, message.TargetEntity, message.TargetCharacter, message.Sender));
}
}

View File

@@ -44,11 +44,11 @@ namespace Barotrauma.Networking
class ServerEntityEventManager : NetEntityEventManager
{
private List<ServerEntityEvent> events;
private readonly List<ServerEntityEvent> events;
//list of unique events (i.e. !IsDuplicate) created during the round
//used for syncing clients who join mid-round
private List<ServerEntityEvent> uniqueEvents;
private readonly List<ServerEntityEvent> uniqueEvents;
private UInt16 lastSentToAll;
private UInt16 lastSentToAnyone;
@@ -90,11 +90,11 @@ namespace Barotrauma.Networking
}
}
private List<BufferedEvent> bufferedEvents;
private readonly List<BufferedEvent> bufferedEvents;
private UInt16 ID;
private GameServer server;
private readonly GameServer server;
private double lastEventCountHighWarning;

View File

@@ -284,6 +284,8 @@ namespace Barotrauma.Networking
partial void RespawnCharactersProjSpecific(Vector2? shuttlePos)
{
respawnedCharacters.Clear();
var respawnSub = RespawnShuttle ?? Submarine.MainSub;
MultiPlayerCampaign campaign = GameMain.GameSession.GameMode as MultiPlayerCampaign;
@@ -300,7 +302,7 @@ namespace Barotrauma.Networking
if (matchingData != null && !matchingData.HasSpawned)
{
c.CharacterInfo = matchingData.CharacterInfo;
}
}
//all characters are in Team 1 in game modes/missions with only one team.
//if at some point we add a game mode with multiple teams where respawning is possible, this needs to be reworked
@@ -355,8 +357,21 @@ namespace Barotrauma.Networking
characterInfos[i].ClearCurrentOrders();
var character = Character.Create(characterInfos[i], shuttleSpawnPoints[i].WorldPosition, characterInfos[i].Name, isRemotePlayer: !bot, hasAi: bot);
bool forceSpawnInMainSub = false;
if (!bot && campaign != null)
{
var matchingData = campaign?.GetClientCharacterData(clients[i]);
if (matchingData != null && !matchingData.HasSpawned)
{
forceSpawnInMainSub = true;
}
}
var character = Character.Create(characterInfos[i], (forceSpawnInMainSub ? mainSubSpawnPoints[i] : shuttleSpawnPoints[i]).WorldPosition, characterInfos[i].Name, isRemotePlayer: !bot, hasAi: bot);
character.TeamID = CharacterTeamType.Team1;
character.LoadTalents();
respawnedCharacters.Add(character);
if (bot)
{

View File

@@ -185,7 +185,10 @@ namespace Barotrauma
{
existingItems.Add(item);
}
Entity.Spawner.AddToSpawnQueue(targetPrefab, targetContainer.OwnInventory);
Entity.Spawner.AddToSpawnQueue(targetPrefab, targetContainer.OwnInventory, null, item =>
{
item.AddTag("traitormissionitem");
});
target = null;
}
else if (allowExisting)

View File

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

View File

@@ -63,6 +63,11 @@
<Item file="Content/Items/Gardening/gardeningtools.xml" />
<Item file="Content/Items/Gardening/plantproducts.xml" />
<Item file="Content/Map/Pirates/PirateItems.xml" />
<Item file="Content/Items/Jobgear/Mechanic/mechanic_talent_items.xml" />
<Item file="Content/Items/Jobgear/Security/securityofficer_talent_items.xml" />
<Item file="Content/Items/Jobgear/Engineer/engineer_talent_items.xml" />
<Item file="Content/Items/Jobgear/Medic/medic_talent_items.xml" />
<Item file="Content/Items/Jobgear/Captain/captain_talent_items.xml" />
<Character file="Content/Characters/Balloon/Balloon.xml" />
<Character file="Content/Characters/Carrier/Carrier.xml" />
<Character file="Content/Characters/Charybdis/Charybdis.xml" />
@@ -147,6 +152,12 @@
<Text file="Content/Texts/Korean/KoreanVanilla.xml" />
<UIStyle file="Content/UI/style.xml" />
<Afflictions file="Content/Afflictions.xml" />
<Afflictions file="Content/Talents/Assistant/AfflictionsAssistant.xml" />
<Afflictions file="Content/Talents/Captain/AfflictionsCaptain.xml" />
<Afflictions file="Content/Talents/Doctor/AfflictionsDoctor.xml" />
<Afflictions file="Content/Talents/Engineer/AfflictionsEngineer.xml" />
<Afflictions file="Content/Talents/Mechanic/AfflictionsMechanic.xml" />
<Afflictions file="Content/Talents/Security/AfflictionsSecurity.xml" />
<Structure file="Content/Map/StructurePrefabs.xml" />
<Structure file="Content/Map/Outposts/OutpostStructurePrefabs.xml" />
<Structure file="Content/Map/Pirates/PirateStructures.xml" />
@@ -261,4 +272,11 @@
<EnemySubmarine file="Content/Map/EnemySubmarines/DugongPirate.sub"/>
<EnemySubmarine file="Content/Map/EnemySubmarines/HumpbackPirate.sub"/>
<EnemySubmarine file="Content/Map/EnemySubmarines/Typhon2Pirate.sub"/>
<Talents file="Content/Talents/Assistant/TalentsAssistant.xml"/>
<Talents file="Content/Talents/Security/TalentsSecurity.xml"/>
<Talents file="Content/Talents/Engineer/TalentsEngineer.xml"/>
<Talents file="Content/Talents/Mechanic/TalentsMechanic.xml"/>
<Talents file="Content/Talents/Captain/TalentsCaptain.xml"/>
<Talents file="Content/Talents/Doctor/TalentsDoctor.xml"/>
<TalentTrees file="Content/Talents/TalentTrees.xml"/>
</contentpackage>

View File

@@ -1,7 +1,8 @@
using Microsoft.Xna.Framework;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Barotrauma.Items.Components;
using System.Linq;
namespace Barotrauma
@@ -328,5 +329,8 @@ namespace Barotrauma
protected virtual void OnStateChanged(AIState from, AIState to) { }
protected virtual void OnTargetChanged(AITarget previousTarget, AITarget newTarget) { }
public virtual void ClientRead(IReadMessage msg) { }
public virtual void ServerWrite(IWriteMessage msg) { }
}
}

View File

@@ -1,5 +1,6 @@
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
@@ -37,6 +38,12 @@ namespace Barotrauma
PreviousState = _state;
OnStateChanged(_state, value);
_state = value;
if (_state == AIState.Attack)
{
#if CLIENT
Character.PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
#endif
}
}
}
@@ -50,6 +57,8 @@ namespace Barotrauma
private readonly float updateTargetsInterval = 1;
private readonly float updateMemoriesInverval = 1;
private readonly float attackLimbResetInterval = 2;
// Min priority for the memorized targets. The actual value fades gradually, unless kept fresh by selecting the target.
private const float minPriority = 10;
private readonly float avoidLookAheadDistance;
@@ -394,7 +403,7 @@ namespace Barotrauma
public void SelectTarget(AITarget target, float priority)
{
SelectedAiTarget = target;
selectedTargetMemory = GetTargetMemory(target, true);
selectedTargetMemory = GetTargetMemory(target, addIfNotFound: true);
selectedTargetMemory.Priority = priority;
ignoredTargets.Remove(target);
}
@@ -641,7 +650,7 @@ namespace Barotrauma
{
Character c = a.Character;
if (c.IsDead || c.Removed) { return false; }
if (!IsFriendly(Character, c)) { return true; }
if (!Character.IsFriendly(c)) { return true; }
// Only apply the threshold to friendly characters
return a.Damage >= selectedTargetingParams.DamageThreshold;
}
@@ -976,7 +985,7 @@ namespace Barotrauma
Character owner = GetOwner(item);
if (owner != null)
{
if (IsFriendly(Character, owner))
if (Character.IsFriendly(owner))
{
ResetAITarget();
State = AIState.Idle;
@@ -1337,7 +1346,7 @@ namespace Barotrauma
}
else
{
canAttack = Character.CharacterList.All(c => c == Character || !IsFriendly(Character, c) || IsFarEnough(c));
canAttack = Character.CharacterList.All(c => c == Character || !Character.IsFriendly(c) || IsFarEnough(c));
}
if (canAttack)
{
@@ -1356,7 +1365,7 @@ namespace Barotrauma
{
hitTarget = limb.character;
}
if (hitTarget != null && !hitTarget.IsDead && IsFriendly(Character, hitTarget))
if (hitTarget != null && !hitTarget.IsDead && Character.IsFriendly(hitTarget))
{
return true;
}
@@ -1764,7 +1773,7 @@ namespace Barotrauma
Character.AnimController.ReleaseStuckLimbs();
LatchOntoAI?.DeattachFromBody(reset: true, cooldown: 1);
if (attacker == null || attacker.AiTarget == null || attacker.Removed || attacker.IsDead) { return; }
bool isFriendly = IsFriendly(Character, attacker);
bool isFriendly = Character.IsFriendly(attacker);
if (wasLatched)
{
State = AIState.Escape;
@@ -1840,7 +1849,7 @@ namespace Barotrauma
}
}
AITargetMemory targetMemory = GetTargetMemory(attacker.AiTarget, true);
AITargetMemory targetMemory = GetTargetMemory(attacker.AiTarget, addIfNotFound: true);
targetMemory.Priority += GetRelativeDamage(attackResult.Damage, Character.Vitality) * AIParams.AggressionHurt;
// Only allow to react once. Otherwise would attack the target with only a fraction of a cooldown
@@ -1884,16 +1893,15 @@ namespace Barotrauma
private bool UpdateLimbAttack(float deltaTime, Limb attackingLimb, Vector2 attackSimPos, float distance = -1, Limb targetLimb = null)
{
if (SelectedAiTarget?.Entity == null) { return false; }
ActiveAttack = attackingLimb?.attack;
if (attackingLimb?.attack == null) { return false; }
ActiveAttack = attackingLimb.attack;
if (wallTarget != null)
{
// If the selected target is not the wall target, make the wall target the selected target.
var aiTarget = wallTarget.Structure.AiTarget;
if (aiTarget != null && SelectedAiTarget != aiTarget)
{
SelectTarget(aiTarget, GetTargetMemory(SelectedAiTarget, true).Priority);
SelectTarget(aiTarget, GetTargetMemory(SelectedAiTarget, addIfNotFound: true).Priority);
State = AIState.Attack;
}
}
@@ -1902,23 +1910,35 @@ namespace Barotrauma
{
//simulate attack input to get the character to attack client-side
Character.SetInput(InputType.Attack, true, true);
#if SERVER
GameMain.NetworkMember.CreateEntityEvent(Character, new object[]
if (!ActiveAttack.IsRunning)
{
#if SERVER
GameMain.NetworkMember.CreateEntityEvent(Character, new object[]
{
Networking.NetEntityEvent.Type.SetAttackTarget,
attackingLimb,
(damageTarget as Entity)?.ID ?? Entity.NullEntityID,
damageTarget is Character character && targetLimb != null ? Array.IndexOf(character.AnimController.Limbs, targetLimb) : 0,
SimPosition.X,
SimPosition.Y
});
});
#else
Character.PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
#endif
}
if (attackingLimb.UpdateAttack(deltaTime, attackSimPos, damageTarget, out AttackResult attackResult, distance, targetLimb))
{
if (damageTarget.Health > 0 && attackResult.Damage > 0)
{
// Managed to hit a living/non-destroyed target. Increase the priority more if the target is low in health -> dies easily/soon
selectedTargetMemory.Priority += GetRelativeDamage(attackResult.Damage, damageTarget.Health) * AIParams.AggressionGreed;
float greed = AIParams.AggressionGreed;
if (!(damageTarget is Character))
{
// Halve the greed for attacking non-characters.
greed /= 2;
}
selectedTargetMemory.Priority += GetRelativeDamage(attackResult.Damage, damageTarget.Health) * greed;
}
else
{
@@ -2125,6 +2145,9 @@ namespace Barotrauma
string targetingTag = null;
if (targetCharacter != null)
{
// ignore if target is tagged to be explicitly ignored (Feign Death)
if (targetCharacter.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { continue; }
if (targetCharacter.IsDead)
{
targetingTag = "dead";
@@ -2139,7 +2162,7 @@ namespace Barotrauma
}
else
{
if (IsFriendly(Character, targetCharacter))
if (Character.IsFriendly(targetCharacter))
{
continue;
}
@@ -2449,7 +2472,7 @@ namespace Barotrauma
{
if (otherCharacter == character) { continue; }
if (otherCharacter.AIController?.SelectedAiTarget != aiTarget) { continue; }
if (!IsFriendly(character, otherCharacter)) { continue; }
if (!character.IsFriendly(otherCharacter)) { continue; }
valueModifier /= 2;
}
}
@@ -2469,7 +2492,7 @@ namespace Barotrauma
// -> just ignore the distance and attack whatever has the highest priority
dist = Math.Max(dist, 100.0f);
AITargetMemory targetMemory = GetTargetMemory(aiTarget, true);
AITargetMemory targetMemory = GetTargetMemory(aiTarget, addIfNotFound: true);
if (Character.CurrentHull != null && Math.Abs(toTarget.Y) > Character.CurrentHull.Size.Y)
{
// Inside the sub, treat objects that are up or down, as they were farther away.
@@ -2527,7 +2550,7 @@ namespace Barotrauma
// Don't target items that we own.
// This is a rare case, and almost entirely related to Humanhusks, so let's check it last to reduce unnecessary checks (although the check shouldn't be expensive)
if (owner == character) { continue; }
if (owner != null && (IsFriendly(Character, owner) || owner.AiTarget != null && ignoredTargets.Contains(owner.AiTarget)))
if (owner != null && (Character.IsFriendly(owner) || owner.AiTarget != null && ignoredTargets.Contains(owner.AiTarget)))
{
continue;
}
@@ -2599,7 +2622,7 @@ namespace Barotrauma
wall = wallTarget?.Structure;
}
// The target is not a wall or it's not the same as we are attached to -> release
bool releaseTarget = wall == null || (!wall.Bodies.Contains(LatchOntoAI.AttachJoints[0].BodyB) && wall.Submarine?.PhysicsBody?.FarseerBody != LatchOntoAI.AttachJoints[0].BodyB);
bool releaseTarget = wall?.Bodies == null || (!wall.Bodies.Contains(LatchOntoAI.AttachJoints[0].BodyB) && wall.Submarine?.PhysicsBody?.FarseerBody != LatchOntoAI.AttachJoints[0].BodyB);
if (!releaseTarget)
{
for (int i = 0; i < wall.Sections.Length; i++)
@@ -2847,10 +2870,15 @@ namespace Barotrauma
{
if (addIfNotFound)
{
memory = new AITargetMemory(target, 10);
memory = new AITargetMemory(target, minPriority);
targetMemories.Add(target, memory);
}
}
if (addIfNotFound)
{
// Keep the memory alive.
memory.Priority = Math.Max(memory.Priority, minPriority);
}
return memory;
}
@@ -3014,7 +3042,7 @@ namespace Barotrauma
{
if (!onlyExisting && !tempParams.ContainsKey(tag))
{
if (AIParams.TryAddNewTarget(tag, state, priority ?? 100, out targetParams))
if (AIParams.TryAddNewTarget(tag, state, priority ?? minPriority, out targetParams))
{
tempParams.Add(tag, targetParams);
}
@@ -3051,6 +3079,7 @@ namespace Barotrauma
ChangeParams(target.SpeciesName, state, priority);
if (target.IsHuman)
{
priority = GetTargetParams("human")?.Priority;
// Target also items, because if we are blind and the target doesn't move, we can only perceive the target when it uses items
if (state == AIState.Attack || state == AIState.Escape)
{
@@ -3061,20 +3090,19 @@ namespace Barotrauma
{
// If the target is shooting from the submarine, we might not perceive it because it doesn't move.
// --> Target the submarine too.
if (target.Submarine != null && (canAttackDoors || canAttackWalls))
if (target.Submarine != null && Character.Submarine == null && (canAttackDoors || canAttackWalls))
{
ChangeParams("room", state, priority);
ChangeParams("room", state, priority * 0.1f);
if (canAttackWalls)
{
ChangeParams("wall", state, priority);
ChangeParams("wall", state, priority * 0.1f);
}
if (canAttackDoors)
{
ChangeParams("door", state, priority);
ChangeParams("door", state, priority * 0.1f);
}
}
ChangeParams("provocative", state, priority, onlyExisting: true);
ChangeParams("light", state, priority, onlyExisting: true);
}
}
}
@@ -3306,7 +3334,17 @@ namespace Barotrauma
return null;
}
public static bool IsFriendly(Character me, Character other) => other.SpeciesName == me.SpeciesName || other.Params.CompareGroup(me.Params.Group);
public override void ServerWrite(IWriteMessage msg)
{
msg.Write((byte)State);
PetBehavior?.ServerWrite(msg);
}
public override void ClientRead(IReadMessage msg)
{
State = (AIState)msg.ReadByte();
PetBehavior?.ClientRead(msg);
}
}
//the "memory" of the Character

View File

@@ -915,10 +915,10 @@ namespace Barotrauma
return false;
}
public static void ReportProblem(Character reporter, Order order)
public static void ReportProblem(Character reporter, Order order, Hull targetHull = null)
{
if (reporter == null || order == null) { return; }
var visibleHulls = new List<Hull>(reporter.GetVisibleHulls());
var visibleHulls = targetHull is null ? new List<Hull>(reporter.GetVisibleHulls()) : new List<Hull> { targetHull };
foreach (var hull in visibleHulls)
{
PropagateHullSafety(reporter, hull);
@@ -1415,7 +1415,7 @@ namespace Barotrauma
if (GameMain.GameSession?.Campaign?.Map?.CurrentLocation != null)
{
var reputationLoss = damageAmount * Reputation.ReputationLossPerWallDamage;
GameMain.GameSession.Campaign.Map.CurrentLocation.Reputation.Value -= reputationLoss;
GameMain.GameSession.Campaign.Map.CurrentLocation.Reputation.AddReputation(-reputationLoss);
}
if (accumulatedDamage <= WarningThreshold) { return; }
@@ -1510,7 +1510,7 @@ namespace Barotrauma
var reputationLoss = MathHelper.Clamp(
(item.Prefab.GetMinPrice() ?? 0) * Reputation.ReputationLossPerStolenItemPrice,
Reputation.MinReputationLossPerStolenItem, Reputation.MaxReputationLossPerStolenItem);
GameMain.GameSession.Campaign.Map.CurrentLocation.Reputation.Value -= reputationLoss;
GameMain.GameSession.Campaign.Map.CurrentLocation.Reputation.AddReputation(-reputationLoss);
}
item.StolenDuringRound = true;
otherCharacter.Speak(TextManager.Get("dialogstealwarning"), null, Rand.Range(0.5f, 1.0f), "thief", 10.0f);
@@ -1971,13 +1971,13 @@ namespace Barotrauma
if (c.Removed) { continue; }
if (c.TeamID != Character.TeamID) { continue; }
if (c.IsIncapacitated) { continue; }
other = c;
if (c.IsPlayer)
{
if (c.SelectedConstruction == target.Item)
{
// If the other character is player, don't try to operate
return true;
other = c;
break;
}
}
else if (c.AIController is HumanAIController operatingAI)
@@ -1991,7 +1991,8 @@ namespace Barotrauma
if (!isOrder && isTargetOrdered)
{
// If the other bot is ordered to operate the item, let him do it, unless we are ordered too
return true;
other = c;
break;
}
else
{
@@ -2012,18 +2013,20 @@ namespace Barotrauma
// Steering is hard-coded -> cannot use the required skills collection defined in the xml
if (Character.GetSkillLevel("helm") <= c.GetSkillLevel("helm"))
{
return true;
other = c;
break;
}
}
else if (target.DegreeOfSuccess(Character) <= target.DegreeOfSuccess(c))
{
return true;
other = c;
break;
}
}
}
}
}
return false;
return other != null;
bool IsOrderedToOperateThis(AIController ai) => ai is HumanAIController humanAI && humanAI.ObjectiveManager.CurrentOrder is AIObjectiveOperateItem operateOrder && operateOrder.Component.Item == target.Item;
}

View File

@@ -161,12 +161,13 @@ namespace Barotrauma
private Vector2 CalculateSteeringSeek(Vector2 target, float weight, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null, Func<PathNode, bool> nodeFilter = null, bool checkVisibility = true)
{
Vector2 targetDiff = target - currentTarget;
if (currentPath != null && currentPath.Nodes.Any())
if (currentPath != null && currentPath.Nodes.Any() && character.Submarine != null)
{
//current path calculated relative to a different sub than where the character is now
//target in a different sub than where the character is now
//take that into account when calculating if the target has moved
Submarine currentPathSub = currentPath?.Nodes.First().Submarine;
if (currentPathSub != character.Submarine && character.Submarine != null)
Submarine currentPathSub = currentPath?.CurrentNode?.Submarine;
if (currentPathSub == character.Submarine) { currentPathSub = currentPath?.Nodes.LastOrDefault()?.Submarine; }
if (currentPathSub != character.Submarine && targetDiff.LengthSquared() > 1 && currentPathSub != null)
{
Vector2 subDiff = character.Submarine.SimPosition - currentPathSub.SimPosition;
targetDiff += subDiff;

View File

@@ -101,11 +101,11 @@ namespace Barotrauma
public enum CombatMode
{
Defensive,
Offensive,
Arrest,
Retreat,
None
Defensive, // Use weapons against the enemy, but try to retreat to a safe place
Offensive, // Engage the enemy and keep attacking it
Arrest, // Try to arrest the enemy without using lethal weapons (stunning + handcuffs)
Retreat, // Run to a safe place without attacking the target
None // Don't use
}
public CombatMode Mode { get; private set; }
@@ -958,14 +958,15 @@ namespace Barotrauma
}
if (reloadTimer > 0) { return; }
if (holdFireCondition != null && holdFireCondition()) { return; }
float sqrDist = Vector2.DistanceSquared(character.Position, Enemy.Position);
sqrDistance = Vector2.DistanceSquared(character.WorldPosition, Enemy.WorldPosition);
distanceTimer = distanceCheckInterval;
if (WeaponComponent is MeleeWeapon meleeWeapon)
{
bool closeEnough = true;
float sqrRange = meleeWeapon.Range * meleeWeapon.Range;
if (character.AnimController.InWater)
{
if (sqrDist > sqrRange)
if (sqrDistance > sqrRange)
{
closeEnough = false;
}
@@ -1003,7 +1004,7 @@ namespace Barotrauma
{
if (WeaponComponent is RepairTool repairTool)
{
if (sqrDist > repairTool.Range * repairTool.Range) { return; }
if (sqrDistance > repairTool.Range * repairTool.Range) { return; }
}
float aimFactor = MathHelper.PiOver2 * (1 - AimAccuracy);
if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - Weapon.Position) < MathHelper.PiOver4 + aimFactor)

View File

@@ -53,9 +53,13 @@ namespace Barotrauma
distanceFactor = 1;
}
float severity = AIObjectiveExtinguishFires.GetFireSeverity(targetHull);
if (severity > 0.5f && !isOrder)
if (severity > 0.75f && !isOrder &&
targetHull.RoomName != null &&
!targetHull.RoomName.Contains("reactor", StringComparison.OrdinalIgnoreCase) &&
!targetHull.RoomName.Contains("engine", StringComparison.OrdinalIgnoreCase) &&
!targetHull.RoomName.Contains("command", StringComparison.OrdinalIgnoreCase))
{
// Ignore severe fires unless ordered. (Let the fire drain all the oxygen instead).
// Ignore severe fires to prevent casualities unless ordered to extinguish.
Priority = 0;
Abandon = true;
}

View File

@@ -25,7 +25,7 @@ namespace Barotrauma
/// <summary>
/// 0-1 based on the horizontal size of all of the fires in the hull.
/// </summary>
public static float GetFireSeverity(Hull hull) => MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, Math.Min(hull.Rect.Width, 1000), hull.FireSources.Sum(fs => fs.Size.X)));
public static float GetFireSeverity(Hull hull) => MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, 500, hull.FireSources.Sum(fs => fs.Size.X)));
protected override IEnumerable<Hull> GetList() => Hull.hullList;

View File

@@ -63,6 +63,7 @@ namespace Barotrauma
if (target.CurrentHull == null) { return false; }
if (HumanAIController.IsFriendly(character, target)) { return false; }
if (!character.Submarine.IsConnectedTo(target.Submarine)) { return false; }
if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI)) { return false; }
return true;
}
}

View File

@@ -57,7 +57,20 @@ namespace Barotrauma
};
},
onAbandon: () => Abandon = true,
onCompleted: () => RemoveSubObjective(ref getDivingGear));
onCompleted: () =>
{
RemoveSubObjective(ref getDivingGear);
if (gearTag == HEAVY_DIVING_GEAR && HumanAIController.HasItem(character, LIGHT_DIVING_GEAR, out IEnumerable<Item> masks, requireEquipped: true))
{
foreach (Item mask in masks)
{
if (mask != targetItem)
{
character.Inventory.TryPutItem(mask, character, CharacterInventory.anySlot);
}
}
}
});
}
else
{
@@ -71,9 +84,13 @@ namespace Barotrauma
{
if (character.IsOnPlayerTeam)
{
if (HumanAIController.HasItem(character, "oxygensource", out _, conditionPercentage: min))
if (HumanAIController.HasItem(character, OXYGEN_SOURCE, out _, conditionPercentage: min))
{
character.Speak(TextManager.Get("dialogswappingoxygentank"), null, 0, "swappingoxygentank", 30.0f);
if (character.Inventory.FindAllItems(i => i.HasTag(OXYGEN_SOURCE) && i.Condition > min).Count == 1)
{
character.Speak(TextManager.Get("dialoglastoxygentank"), null, 0.0f, "dialoglastoxygentank", 30.0f);
}
}
else
{
@@ -105,7 +122,7 @@ namespace Barotrauma
onAbandon: () =>
{
Abandon = true;
if (remainingTanks > 0 && !HumanAIController.HasItem(character, "oxygensource", out _, conditionPercentage: 0.01f))
if (remainingTanks > 0 && !HumanAIController.HasItem(character, OXYGEN_SOURCE, out _, conditionPercentage: 0.01f))
{
character.Speak(TextManager.Get("dialogcantfindtoxygen"), null, 0, "cantfindoxygen", 30.0f);
}
@@ -121,7 +138,7 @@ namespace Barotrauma
int ReportOxygenTankCount()
{
if (character.Submarine != Submarine.MainSub) { return 1; }
int remainingOxygenTanks = Submarine.MainSub.GetItems(false).Count(i => i.HasTag("oxygensource") && i.Condition > 1);
int remainingOxygenTanks = Submarine.MainSub.GetItems(false).Count(i => i.HasTag(OXYGEN_SOURCE) && i.Condition > 1);
if (remainingOxygenTanks == 0)
{
character.Speak(TextManager.Get("DialogOutOfOxygenTanks"), null, 0.0f, "outofoxygentanks", 30.0f);
@@ -136,17 +153,6 @@ namespace Barotrauma
}
}
/// <summary>
/// Returns false only when no inventory can be found from the item.
/// </summary>
public static bool EjectEmptyTanks(Character actor, Item target, out IEnumerable<Item> containedItems)
{
containedItems = target.OwnInventory?.AllItems;
if (containedItems == null) { return false; }
AIController.UnequipEmptyItems(actor, target);
return true;
}
public override void Reset()
{
base.Reset();

View File

@@ -242,9 +242,8 @@ namespace Barotrauma
if (!searchingNewHull)
{
//find all available hulls first
FindTargetHulls();
searchingNewHull = true;
return;
FindTargetHulls();
}
else if (targetHulls.Any())
{
@@ -255,11 +254,10 @@ namespace Barotrauma
var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, errorMsgStr: null, nodeFilter: node =>
{
if (node.Waypoint.CurrentHull == null) { return false; }
// Check that there is no unsafe or forbidden hulls on the way to the target
// Check that there is no unsafe hulls on the way to the target
if (node.Waypoint.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(node.Waypoint.CurrentHull)) { return false; }
if (isCurrentHullAllowed && IsForbidden(node.Waypoint.CurrentHull)) { return false; }
return true;
});
}, endNodeFilter: node => !isCurrentHullAllowed | !IsForbidden(node.Waypoint.CurrentHull));
if (path.Unreachable)
{
//can't go to this room, remove it from the list and try another room
@@ -271,30 +269,19 @@ namespace Barotrauma
SetTargetTimerLow();
return;
}
character.AIController.SelectTarget(currentTarget.AiTarget);
PathSteering.SetPath(path);
SetTargetTimerNormal();
searchingNewHull = false;
}
else
{
// Couldn't find a target for some reason -> reset
// Couldn't find a valid hull
SetTargetTimerHigh();
searchingNewHull = false;
}
if (currentTarget != null)
{
character.AIController.SelectTarget(currentTarget.AiTarget);
string errorMsg = null;
#if DEBUG
bool isRoomNameFound = currentTarget.DisplayName != null;
errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.DisplayName : currentTarget.ToString()) + ")";
#endif
var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, errorMsgStr: errorMsg, nodeFilter: node => node.Waypoint.CurrentHull != null);
PathSteering.SetPath(path);
}
SetTargetTimerNormal();
}
newTargetTimer -= deltaTime;
if (!character.IsClimbing && IsSteeringFinished())
{
Wander(deltaTime);

View File

@@ -141,6 +141,7 @@ namespace Barotrauma
public Entity TargetEntity;
public ItemComponent TargetItemComponent;
public readonly bool UseController;
public readonly string[] ControllerTags;
public Controller ConnectedController;
public Character OrderGiver;
@@ -309,6 +310,7 @@ namespace Barotrauma
color = orderElement.GetAttributeColor("color");
FadeOutTime = orderElement.GetAttributeFloat("fadeouttime", 0.0f);
UseController = orderElement.GetAttributeBool("usecontroller", false);
ControllerTags = orderElement.GetAttributeStringArray("controllertags", new string[0]);
TargetAllCharacters = orderElement.GetAttributeBool("targetallcharacters", false);
AppropriateJobs = orderElement.GetAttributeStringArray("appropriatejobs", new string[0]);
Options = orderElement.GetAttributeStringArray("options", new string[0]);
@@ -380,6 +382,7 @@ namespace Barotrauma
SymbolSprite = prefab.SymbolSprite;
Color = prefab.Color;
UseController = prefab.UseController;
ControllerTags = prefab.ControllerTags;
TargetAllCharacters = prefab.TargetAllCharacters;
AppropriateJobs = prefab.AppropriateJobs;
FadeOutTime = prefab.FadeOutTime;
@@ -399,7 +402,7 @@ namespace Barotrauma
{
if (UseController)
{
ConnectedController = targetItem.Item?.FindController();
ConnectedController = targetItem.Item?.FindController(tags: ControllerTags);
if (ConnectedController == null)
{
DebugConsole.AddWarning("AI: Tried to use a controller for operating an item, but couldn't find any.");
@@ -450,19 +453,37 @@ namespace Barotrauma
return false;
}
public string GetChatMessage(string targetCharacterName, string targetRoomName, bool givingOrderToSelf, string orderOption = "")
public string GetChatMessage(string targetCharacterName, string targetRoomName, bool givingOrderToSelf, string orderOption = "", int? priority = null)
{
orderOption ??= "";
string messageTag = (givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf." : "OrderDialog.") + Identifier;
if (Identifier != "dismissed" && !string.IsNullOrEmpty(orderOption)) { messageTag += "." + orderOption; }
if (targetCharacterName == null) { targetCharacterName = ""; }
if (targetRoomName == null) { targetRoomName = ""; }
string msg = TextManager.GetWithVariables(messageTag, new string[2] { "[name]", "[roomname]" }, new string[2] { targetCharacterName, targetRoomName }, new bool[2] { false, true }, true);
if (msg == null) { return ""; }
return msg;
priority ??= CharacterInfo.HighestManualOrderPriority;
// If the order has a lesser priority, it means we are rearranging character orders
if (!TargetAllCharacters && priority != CharacterInfo.HighestManualOrderPriority && Identifier != "dismissed")
{
return TextManager.GetWithVariable("rearrangedorders", "[name]", targetCharacterName ?? string.Empty, returnNull: true) ?? string.Empty;
}
string messageTag = $"{(givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf" : "OrderDialog")}";
messageTag += $".{Identifier}";
if (!string.IsNullOrEmpty(orderOption))
{
if (Identifier != "dismissed")
{
messageTag += $".{orderOption}";
}
else
{
string[] splitOption = orderOption.Split('.');
if (splitOption.Length > 0)
{
messageTag += $".{splitOption[0]}";
}
}
}
string msg = TextManager.GetWithVariables(messageTag,
new string[2] { "[name]", "[roomname]" },
new string[2] { targetCharacterName ?? string.Empty, targetRoomName ?? string.Empty },
formatCapitals: new bool[2] { false, true },
returnNull: true);
return msg ?? string.Empty;
}
/// <summary>
@@ -505,7 +526,7 @@ namespace Barotrauma
if (item.NonInteractable) { continue; }
if (ItemComponentType != null && item.Components.None(c => c.GetType() == ItemComponentType)) { continue; }
Controller controller = null;
if (UseController && !item.TryFindController(out controller)) { continue; }
if (UseController && !item.TryFindController(out controller, tags: ControllerTags)) { continue; }
if (interactableFor != null && (!item.IsInteractable(interactableFor) || (UseController && !controller.Item.IsInteractable(interactableFor)))) { continue; }
matchingItems.Add(item);
}

Some files were not shown because too many files have changed in this diff Show More