(965c31410) v0.10.4.0

This commit is contained in:
Joonas Rikkonen
2020-07-30 13:00:09 +03:00
parent eeac247a8e
commit 4978af3d60
539 changed files with 45803 additions and 25359 deletions

View File

@@ -163,7 +163,8 @@ namespace Barotrauma
position = Vector2.Zero;
CreateMatrices();
GameMain.Instance.OnResolutionChanged += () => { CreateMatrices(); };
// TODO: Needs to unregister if ever destroy cameras.
GameMain.Instance.ResolutionChanged += CreateMatrices;
UpdateTransform(false);
}
@@ -260,27 +261,30 @@ namespace Barotrauma
if (targetPos == Vector2.Zero)
{
Vector2 moveInput = Vector2.Zero;
if (allowMove && GUI.KeyboardDispatcher.Subscriber == null)
if (allowMove)
{
if (PlayerInput.KeyDown(Keys.LeftShift)) moveSpeed *= 2.0f;
if (PlayerInput.KeyDown(Keys.LeftControl)) moveSpeed *= 0.5f;
if (GameMain.Config.KeyBind(InputType.Left).IsDown()) moveInput.X -= 1.0f;
if (GameMain.Config.KeyBind(InputType.Right).IsDown()) moveInput.X += 1.0f;
if (GameMain.Config.KeyBind(InputType.Down).IsDown()) moveInput.Y -= 1.0f;
if (GameMain.Config.KeyBind(InputType.Up).IsDown()) moveInput.Y += 1.0f;
}
velocity = Vector2.Lerp(velocity, moveInput, deltaTime * 10.0f);
moveCam = velocity * moveSpeed * deltaTime * 60.0f;
if (Screen.Selected == GameMain.GameScreen && FollowSub)
{
var closestSub = Submarine.FindClosest(WorldViewCenter);
if (closestSub != null)
if (GUI.KeyboardDispatcher.Subscriber == null)
{
moveCam += FarseerPhysics.ConvertUnits.ToDisplayUnits(closestSub.Velocity * deltaTime);
if (PlayerInput.KeyDown(Keys.LeftShift)) moveSpeed *= 2.0f;
if (PlayerInput.KeyDown(Keys.LeftControl)) moveSpeed *= 0.5f;
if (GameMain.Config.KeyBind(InputType.Left).IsDown()) moveInput.X -= 1.0f;
if (GameMain.Config.KeyBind(InputType.Right).IsDown()) moveInput.X += 1.0f;
if (GameMain.Config.KeyBind(InputType.Down).IsDown()) moveInput.Y -= 1.0f;
if (GameMain.Config.KeyBind(InputType.Up).IsDown()) moveInput.Y += 1.0f;
}
velocity = Vector2.Lerp(velocity, moveInput, deltaTime * 10.0f);
moveCam = velocity * moveSpeed * deltaTime * 60.0f;
if (Screen.Selected == GameMain.GameScreen && FollowSub)
{
var closestSub = Submarine.FindClosest(WorldViewCenter);
if (closestSub != null)
{
moveCam += FarseerPhysics.ConvertUnits.ToDisplayUnits(closestSub.Velocity * deltaTime);
}
}
}
if (allowZoom && GUI.MouseOn == null)
@@ -311,7 +315,7 @@ namespace Barotrauma
{
Freeze = true;
}
if (CharacterHealth.OpenHealthWindow != null || CrewManager.IsCommandInterfaceOpen)
if (CharacterHealth.OpenHealthWindow != null || CrewManager.IsCommandInterfaceOpen || ConversationAction.IsDialogOpen)
{
offset *= 0;
Freeze = false;

View File

@@ -6,6 +6,8 @@ namespace Barotrauma
{
partial class HumanAIController : AIController
{
public static bool debugai;
partial void InitProjSpecific()
{
/*if (GameMain.GameSession != null && GameMain.GameSession.CrewManager != null)
@@ -18,6 +20,8 @@ namespace Barotrauma
public override void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
{
if (Character == Character.Controlled) { return; }
if (!debugai) { return; }
Vector2 pos = Character.WorldPosition;
pos.Y = -pos.Y;
Vector2 textOffset = new Vector2(-40, -160);

View File

@@ -268,7 +268,7 @@ namespace Barotrauma
}
else if (body.UserData is Limb || body == Collider.FarseerBody)
{
if (!character.IsRemotePlayer && impact > ImpactTolerance)
if (!character.IsRemotelyControlled && impact > ImpactTolerance)
{
SoundPlayer.PlayDamageSound("LimbBlunt", strongestImpact, Collider);
}
@@ -460,21 +460,14 @@ namespace Barotrauma
/// </summary>
public float GetDepthOffset()
{
float maxDepth = 0.0f;
float minDepth = 1.0f;
float depthOffset = 0.0f;
var ladder = character.SelectedConstruction?.GetComponent<Ladder>();
if (ladder != null)
{
float maxDepth = 0.0f;
float minDepth = 1.0f;
foreach (Limb limb in Limbs)
{
var activeSprite = limb.ActiveSprite;
if (activeSprite != null)
{
maxDepth = Math.Max(activeSprite.Depth, maxDepth);
minDepth = Math.Min(activeSprite.Depth, minDepth);
}
}
CalculateLimbDepths();
if (character.WorldPosition.X < character.SelectedConstruction.WorldPosition.X)
{
//at the left side of the ladder, needs to be drawn in front of the rungs
@@ -486,6 +479,36 @@ namespace Barotrauma
depthOffset = Math.Max(ladder.BackgroundSpriteDepth + 0.01f - minDepth, 0.0f);
}
}
else
{
CalculateLimbDepths();
var controller = character.SelectedConstruction?.GetComponent<Controller>();
if (controller != null && controller.ControlCharacterPose && controller.User == character)
{
if (controller.Item.SpriteDepth > maxDepth)
{
depthOffset = Math.Max(controller.Item.SpriteDepth - 0.0001f - maxDepth, 0.0f);
}
else
{
depthOffset = Math.Max(controller.Item.SpriteDepth + 0.0001f - minDepth, -minDepth);
}
}
}
void CalculateLimbDepths()
{
foreach (Limb limb in Limbs)
{
var activeSprite = limb.ActiveSprite;
if (activeSprite != null)
{
maxDepth = Math.Max(activeSprite.Depth, maxDepth);
minDepth = Math.Min(activeSprite.Depth, minDepth);
}
}
}
return depthOffset;
}
@@ -498,10 +521,15 @@ namespace Barotrauma
{
if (limb.PullJointEnabled)
{
Vector2 pos = ConvertUnits.ToDisplayUnits(limb.PullJointWorldAnchorA);
Vector2 pos = ConvertUnits.ToDisplayUnits(limb.PullJointWorldAnchorB);
if (currentHull?.Submarine != null) pos += currentHull.Submarine.DrawPosition;
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), GUI.Style.Red, true, 0.01f);
pos = ConvertUnits.ToDisplayUnits(limb.PullJointWorldAnchorA);
if (currentHull?.Submarine != null) pos += currentHull.Submarine.DrawPosition;
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), Color.Cyan, true, 0.01f);
}
limb.body.DebugDraw(spriteBatch, inWater ? (currentHull == null ? Color.Blue : Color.Cyan) : Color.White);

View File

@@ -22,8 +22,8 @@ namespace Barotrauma
protected float soundTimer;
protected float soundInterval;
protected float hudInfoTimer;
protected bool hudInfoVisible;
protected float hudInfoTimer = 1.0f;
protected bool hudInfoVisible = false;
private float pressureParticleTimer;
@@ -31,7 +31,7 @@ namespace Barotrauma
protected float lastRecvPositionUpdateTime;
private float hudInfoHeight;
private float hudInfoHeight = 100.0f;
private List<CharacterSound> sounds;
@@ -264,7 +264,7 @@ namespace Barotrauma
}
else if (Lights.LightManager.ViewTarget is Item item && item.Prefab.FocusOnSelected)
{
cam.OffsetAmount = targetOffsetAmount = item.Prefab.OffsetOnSelected;
cam.OffsetAmount = targetOffsetAmount = item.Prefab.OffsetOnSelected * item.OffsetOnSelectedMultiplier;
}
else if (SelectedConstruction != null && ViewTarget == null &&
SelectedConstruction.Components.Any(ic => ic?.GuiFrame != null && ic.ShouldDrawHUD(this)))
@@ -549,11 +549,12 @@ namespace Barotrauma
public bool ShouldLockHud()
{
if (this != controlled) { return false; }
if (GameMain.GameSession?.Campaign != null && GameMain.GameSession.Campaign.ShowCampaignUI) { return true; }
var controller = SelectedConstruction?.GetComponent<Controller>();
//lock if using a controller, except if we're also using a connection panel in the same item
return
SelectedConstruction != null &&
SelectedConstruction?.GetComponent<Controller>()?.User == this &&
controller?.User == this && controller.HideHUD &&
SelectedConstruction?.GetComponent<ConnectionPanel>()?.User != this;
}
@@ -661,7 +662,7 @@ namespace Barotrauma
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, bool drawHealth = true)
{
CharacterHUD.Draw(spriteBatch, this, cam);
if (drawHealth) CharacterHealth.DrawHUD(spriteBatch);
if (drawHealth && !CharacterHUD.IsCampaignInterfaceOpen) { CharacterHealth.DrawHUD(spriteBatch); }
}
public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam)
@@ -672,8 +673,8 @@ namespace Barotrauma
{
AnimController.DebugDraw(spriteBatch);
}
if (GUI.DisableHUD) return;
if (GUI.DisableHUD) { return; }
if (Controlled != null &&
Controlled != this &&
@@ -756,19 +757,23 @@ namespace Barotrauma
float hoverRange = 300.0f;
float fadeOutRange = 200.0f;
float cursorDist = Vector2.Distance(WorldPosition, cam.ScreenToWorld(PlayerInput.MousePosition));
float hudInfoAlpha = MathHelper.Clamp(1.0f - (cursorDist - (hoverRange - fadeOutRange)) / fadeOutRange, 0.2f, 1.0f);
float hudInfoAlpha =
CampaignInteractionType == CampaignMode.InteractionType.None ?
MathHelper.Clamp(1.0f - (cursorDist - (hoverRange - fadeOutRange)) / fadeOutRange, 0.2f, 1.0f) :
1.0f;
if (!GUI.DisableCharacterNames && hudInfoVisible && info != null &&
(controlled == null || this != controlled.FocusedCharacter))
(controlled == null || this != controlled.FocusedCharacter) && cam.Zoom > 0.4f)
{
string name = Info.DisplayName;
if (controlled == null && name != Info.Name) name += " " + TextManager.Get("Disguised");
if (controlled == null && name != Info.Name) { name += " " + TextManager.Get("Disguised"); }
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - GUI.Font.MeasureString(name) * 0.5f / cam.Zoom;
Vector2 nameSize = GUI.Font.MeasureString(name);
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - nameSize * 0.5f / cam.Zoom;
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
namePos *= screenSize / viewportSize;
namePos.X = (float)Math.Floor(namePos.X); namePos.Y = (float)Math.Floor(namePos.Y);
namePos *= viewportSize / screenSize;
@@ -779,16 +784,30 @@ namespace Barotrauma
{
nameColor = TeamID == TeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
}
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
{
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionIcon." + CampaignInteractionType);
if (iconStyle != null)
{
Vector2 headPos = AnimController.GetLimb(LimbType.Head)?.WorldPosition ?? WorldPosition + Vector2.UnitY * 100.0f;
Vector2 iconPos = headPos;
iconPos.Y = -iconPos.Y;
nameColor = iconStyle.Color;
var icon = iconStyle.Sprites[GUIComponent.ComponentState.None].First();
float iconScale = 30.0f / icon.Sprite.size.X / cam.Zoom;
icon.Sprite.Draw(spriteBatch, iconPos + new Vector2(-35.0f, -25.0f), iconStyle.Color * hudInfoAlpha, scale: iconScale);
}
}
GUI.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
GUI.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
if (GameMain.DebugDraw)
{
GUI.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
}
}
if (IsDead) return;
if (IsDead) { return; }
if (CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
{

View File

@@ -41,13 +41,21 @@ namespace Barotrauma
private static bool shouldRecreateHudTexts = true;
private static bool heldDownShiftWhenGotHudTexts;
public static bool IsCampaignInterfaceOpen =>
GameMain.GameSession?.Campaign != null &&
(GameMain.GameSession.Campaign.ShowCampaignUI || GameMain.GameSession.Campaign.ForceMapUI);
private static bool ShouldDrawInventory(Character character)
{
var controller = character.SelectedConstruction?.GetComponent<Controller>();
return
character?.Inventory != null &&
character.AllowInput &&
!character.LockHands &&
character.SelectedConstruction?.GetComponent<Controller>()?.User != character;
!character.LockHands &&
(controller?.User != character || !controller.HideHUD) &&
!IsCampaignInterfaceOpen &&
!ConversationAction.FadeScreenToBlack;
}
private static string GetCachedHudText(string textTag, string keyBind)
@@ -65,7 +73,7 @@ namespace Barotrauma
{
if (GUI.DisableHUD) return;
if (!character.IsIncapacitated && character.Stun <= 0.0f)
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen)
{
if (character.Inventory != null)
{
@@ -92,9 +100,17 @@ namespace Barotrauma
public static void Update(float deltaTime, Character character, Camera cam)
{
if (GUI.DisableHUD) { return; }
if (!character.IsIncapacitated && character.Stun <= 0.0f)
if (GUI.DisableHUD)
{
if (character.Inventory != null && !LockInventory(character))
{
character.Inventory.UpdateSlotInput();
}
return;
}
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen)
{
if (character.Info != null && !character.ShouldLockHud() && character.SelectedCharacter == null)
{
@@ -163,7 +179,7 @@ namespace Barotrauma
foreach (Item item in Item.ItemList)
{
if (item.Submarine == null || item.Submarine.TeamID != character.TeamID || item.Submarine.Info.IsWreck) { continue; }
if (!item.Repairables.Any(r => item.ConditionPercentage <= r.RepairThreshold)) { continue; }
if (!item.Repairables.Any(r => item.ConditionPercentage <= r.RepairIconThreshold)) { continue; }
if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; }
Vector2 diff = item.WorldPosition - character.WorldPosition;
@@ -211,7 +227,7 @@ namespace Barotrauma
Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
}
if (!character.IsIncapacitated && character.Stun <= 0.0f)
if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen)
{
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
{
@@ -299,6 +315,8 @@ namespace Barotrauma
character.SelectedConstruction.DrawHUD(spriteBatch, cam, Character.Controlled);
}
if (IsCampaignInterfaceOpen) { return; }
if (character.Inventory != null)
{
for (int i = 0; i < character.Inventory.Items.Length - 1; i++)
@@ -308,10 +326,11 @@ namespace Barotrauma
foreach (ItemComponent ic in item.Components)
{
if (ic.DrawHudWhenEquipped) ic.DrawHUD(spriteBatch, character);
if (ic.DrawHudWhenEquipped) { ic.DrawHUD(spriteBatch, character); }
}
}
}
bool mouseOnPortrait = false;
if (character.Stun <= 0.1f && !character.IsDead)
{
@@ -421,7 +440,7 @@ namespace Barotrauma
GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
textPos.Y += textSize.Y;
}
if (!string.IsNullOrEmpty(character.FocusedCharacter.customInteractHUDText))
if (!string.IsNullOrEmpty(character.FocusedCharacter.customInteractHUDText) && character.FocusedCharacter.AllowCustomInteract)
{
GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.customInteractHUDText, GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
textPos.Y += textSize.Y;
@@ -430,7 +449,7 @@ namespace Barotrauma
private static bool LockInventory(Character character)
{
if (character?.Inventory == null || !character.AllowInput || character.LockHands) { return true; }
if (character?.Inventory == null || !character.AllowInput || character.LockHands || IsCampaignInterfaceOpen) { return true; }
return character.ShouldLockHud();
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -12,9 +13,11 @@ namespace Barotrauma
{
private static Sprite infoAreaPortraitBG;
public bool LastControlled;
public static void Init()
{
infoAreaPortraitBG = GUI.Style.GetComponentStyle("InfoAreaPortraitBG")?.Sprites[GUIComponent.ComponentState.None][0].Sprite;
infoAreaPortraitBG = GUI.Style.GetComponentStyle("InfoAreaPortraitBG")?.GetDefaultSprite();
new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(833, 298, 142, 98), null, 0);
}
@@ -117,6 +120,7 @@ namespace Barotrauma
private void DrawInfoFrameCharacterIcon(SpriteBatch sb, Rectangle componentRect)
{
if (headSprite == null) { return; }
Vector2 targetAreaSize = componentRect.Size.ToVector2();
float scale = Math.Min(targetAreaSize.X / headSprite.size.X, targetAreaSize.Y / headSprite.size.Y);
DrawIcon(sb, componentRect.Location.ToVector2() + headSprite.size / 2 * scale, targetAreaSize);
@@ -140,6 +144,9 @@ namespace Barotrauma
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos)
{
if (TeamID == Character.TeamType.FriendlyNPC) { return; }
if (Character.Controlled != null && Character.Controlled.TeamID != TeamID) { return; }
if (newLevel - prevLevel > 0.1f)
{
GUI.AddMessage(

View File

@@ -75,8 +75,15 @@ namespace Barotrauma
states = newInput,
intAim = intAngle
};
if (focusedItem != null && !CharacterInventory.DraggingItemToWorld &&
(!newMem.states.HasFlag(InputNetFlags.Grab) && !newMem.states.HasFlag(InputNetFlags.Health)))
if (FocusedCharacter != null &&
FocusedCharacter.CampaignInteractionType != CampaignMode.InteractionType.None &&
newMem.states.HasFlag(InputNetFlags.Use))
{
newMem.interact = FocusedCharacter.ID;
}
else if (focusedItem != null && !CharacterInventory.DraggingItemToWorld &&
!newMem.states.HasFlag(InputNetFlags.Grab) && !newMem.states.HasFlag(InputNetFlags.Health))
{
newMem.interact = focusedItem.ID;
}
@@ -278,10 +285,10 @@ namespace Barotrauma
break;
case ServerNetObject.ENTITY_EVENT:
int eventType = msg.ReadRangedInteger(0, 4);
int eventType = msg.ReadRangedInteger(0, 5);
switch (eventType)
{
case 0:
case 0: //NetEntityEvent.Type.InventoryState
if (Inventory == null)
{
string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")";
@@ -301,7 +308,7 @@ namespace Barotrauma
Inventory.ClientRead(type, msg, sendingTime);
}
break;
case 1:
case 1: //NetEntityEvent.Type.Control
byte ownerID = msg.ReadByte();
ResetNetState();
if (ownerID == GameMain.Client.ID)
@@ -326,10 +333,10 @@ namespace Barotrauma
}
}
break;
case 2:
case 2: //NetEntityEvent.Type.Status
ReadStatus(msg);
break;
case 3:
case 3: //NetEntityEvent.Type.UpdateSkills
int skillCount = msg.ReadByte();
for (int i = 0; i < skillCount; i++)
{
@@ -338,17 +345,17 @@ namespace Barotrauma
info?.SetSkillLevel(skillIdentifier, skillLevel, WorldPosition + Vector2.UnitY * 150.0f);
}
break;
case 4:
case 4: //NetEntityEvent.Type.ExecuteAttack
int attackLimbIndex = msg.ReadByte();
UInt16 targetEntityID = msg.ReadUInt16();
int targetLimbIndex = msg.ReadByte();
//255 = entity already removed, no need to do anything
if (attackLimbIndex == 255) { break; }
if (attackLimbIndex == 255 || Removed) { break; }
if (attackLimbIndex >= AnimController.Limbs.Length)
{
DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Limb index out of bounds ({attackLimbIndex})");
DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
break;
}
Limb attackLimb = AnimController.Limbs[attackLimbIndex];
@@ -362,7 +369,7 @@ namespace Barotrauma
{
if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
{
DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Target limb index out of bounds ({targetLimbIndex})");
DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})");
break;
}
targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
@@ -372,6 +379,10 @@ namespace Barotrauma
attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
}
break;
case 5: //NetEntityEvent.Type.AssignCampaignInteraction
byte campaignInteractionType = msg.ReadByte();
(GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
break;
}
msg.ReadPadBits();
break;
@@ -398,7 +409,7 @@ namespace Barotrauma
Character character = null;
if (noInfo)
{
character = Create(speciesName, position, seed, null, true);
character = Create(speciesName, position, seed, null, false);
character.ID = id;
bool containsStatusData = inc.ReadBoolean();
if (containsStatusData)
@@ -416,9 +427,14 @@ namespace Barotrauma
CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc);
character = Create(speciesName, position, seed, info, GameMain.Client.ID != ownerId, hasAi);
character = Create(speciesName, position, seed, info, ownerId > 0 && GameMain.Client.ID != ownerId, hasAi);
character.ID = id;
character.TeamID = (TeamType)teamID;
character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte();
if (character.CampaignInteractionType != CampaignMode.InteractionType.None)
{
(GameMain.GameSession.GameMode as CampaignMode)?.AssignNPCMenuInteraction(character, character.CampaignInteractionType);
}
// Check if the character has a current order
if (inc.ReadBoolean())

View File

@@ -525,6 +525,7 @@ namespace Barotrauma
},
TextManager.Get("GiveInButton"), style: "GUIButtonLarge")
{
Visible = false,
ToolTip = TextManager.Get(GameMain.NetworkMember == null ? "GiveInHelpSingleplayer" : "GiveInHelpMultiplayer"),
OnClicked = (button, userData) =>
{
@@ -944,6 +945,22 @@ namespace Barotrauma
suicideButton.Visible = Character == Character.Controlled && !Character.IsDead && Character.IsIncapacitated;
if (GameMain.GameSession?.Campaign is { } campaign)
{
RectTransform endRoundButton = campaign?.EndRoundButton.RectTransform;
if (endRoundButton != null)
{
if (suicideButton.Visible)
{
endRoundButton.ScreenSpaceOffset = new Point(0, suicideButton.Rect.Height);
}
else if (endRoundButton.ScreenSpaceOffset != Point.Zero)
{
endRoundButton.ScreenSpaceOffset = Point.Zero;
}
}
}
cprButton.Visible =
Character == Character.Controlled?.SelectedCharacter
&& (Character.IsUnconscious || Character.Stun > 0.0f)
@@ -965,23 +982,29 @@ namespace Barotrauma
public void AddToGUIUpdateList()
{
if (GUI.DisableHUD) return;
if (GUI.DisableHUD) { return; }
if (OpenHealthWindow == this)
{
healthInterfaceFrame.AddToGUIUpdateList();
afflictionTooltip?.AddToGUIUpdateList();
}
else if (Character.Controlled == Character)
else if (Character.Controlled == Character && !CharacterHUD.IsCampaignInterfaceOpen)
{
healthBarHolder.AddToGUIUpdateList();
}
if (suicideButton.Visible && Character == Character.Controlled) suicideButton.AddToGUIUpdateList();
if (cprButton != null && cprButton.Visible) cprButton.AddToGUIUpdateList();
if (suicideButton.Visible && Character == Character.Controlled)
{
suicideButton.AddToGUIUpdateList();
}
if (cprButton != null && cprButton.Visible)
{
cprButton.AddToGUIUpdateList();
}
}
public void DrawHUD(SpriteBatch spriteBatch)
{
if (GUI.DisableHUD) return;
if (GUI.DisableHUD) { return; }
if (GameMain.GraphicsWidth != screenResolution.X ||
GameMain.GraphicsHeight != screenResolution.Y ||
Math.Abs(inventoryScale - Inventory.UIScale) > 0.01f ||

View File

@@ -56,7 +56,6 @@ namespace Barotrauma
return frameHolder;
}
public class OutfitPreview
{
/// <summary>

View File

@@ -50,7 +50,12 @@ namespace Barotrauma
}
private static bool isOpen;
public static bool IsOpen => isOpen;
public static bool IsOpen
{
get => isOpen;
set => isOpen = value;
}
public static bool Paused = false;
private static GUITextBlock activeQuestionText;
@@ -66,6 +71,8 @@ namespace Barotrauma
public static void Init()
{
OpenAL.Alc.SetErrorReasonCallback((string msg) => NewMessage(msg, Color.Orange));
frame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.45f), GUI.Canvas) { MinSize = new Point(400, 300), AbsoluteOffset = new Point(10, 10) },
color: new Color(0.4f, 0.4f, 0.4f, 0.8f));
@@ -263,12 +270,31 @@ namespace Barotrauma
try
{
var textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform),
msg.Text, font: GUI.SmallFont, wrap: true)
if (msg.IsError)
{
CanBeFocused = false,
TextColor = msg.Color
};
var textContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform), style: "InnerFrame", color: Color.White)
{
CanBeFocused = false
};
var textBlock = new GUITextBlock(new RectTransform(new Point(listBox.Content.Rect.Width - 5, 0), textContainer.RectTransform, Anchor.TopLeft) { AbsoluteOffset = new Point(2, 2) },
msg.Text, textAlignment: Alignment.TopLeft, font: GUI.SmallFont, wrap: true)
{
CanBeFocused = false,
TextColor = msg.Color
};
textContainer.RectTransform.NonScaledSize = new Point(textContainer.RectTransform.NonScaledSize.X, textBlock.RectTransform.NonScaledSize.Y + 5);
textBlock.SetTextPos();
}
else
{
var textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), listBox.Content.RectTransform),
msg.Text, font: GUI.SmallFont, wrap: true)
{
CanBeFocused = false,
TextColor = msg.Color
};
}
listBox.UpdateScrollBarSize();
listBox.BarScroll = 1.0f;
}
@@ -446,6 +472,11 @@ namespace Barotrauma
{
GameMain.SpriteEditorScreen.Select();
}));
commands.Add(new Command("editevents|eventeditor", "editevents/eventeditor: Switch to the Event Editor to edit scripted events.", (string[] args) =>
{
GameMain.EventEditorScreen.Select();
}));
commands.Add(new Command("editcharacters|charactereditor", "editcharacters/charactereditor: Switch to the Character Editor to edit/create the ragdolls and animations of characters.", (string[] args) =>
{
@@ -456,9 +487,11 @@ namespace Barotrauma
GameMain.CharacterEditorScreen.Select();
}));
commands.Add(new Command("steamnetdebug", "steamnetdebug: Toggles Steamworks debug logging.", (string[] args) =>
commands.Add(new Command("steamnetdebug", "steamnetdebug: Toggles Steamworks networking debug logging.", (string[] args) =>
{
SteamManager.NetworkingDebugLog = !SteamManager.NetworkingDebugLog;
SteamManager.SetSteamworksNetworkingDebugLog(SteamManager.NetworkingDebugLog);
}));
AssignRelayToServer("kick", false);
@@ -493,6 +526,7 @@ namespace Barotrauma
commands.Add(new Command("traitorlist", "", (string[] args) => { }));
AssignRelayToServer("traitorlist", true);
AssignRelayToServer("money", true);
AssignRelayToServer("setskill", true);
AssignOnExecute("control", (string[] args) =>
{
@@ -568,16 +602,46 @@ namespace Barotrauma
AssignOnExecute("ambientlight", (string[] args) =>
{
Color color = XMLExtensions.ParseColor(string.Join(",", args));
bool add = string.Equals(args.LastOrDefault(), "add");
string colorString = string.Join(",", add ? args.SkipLast(1) : args);
if (colorString.Equals("restore", StringComparison.OrdinalIgnoreCase))
{
foreach (Hull hull in Hull.hullList)
{
if (hull.OriginalAmbientLight != null)
{
hull.AmbientLight = hull.OriginalAmbientLight.Value;
hull.OriginalAmbientLight = null;
}
}
NewMessage("Restored all hull ambient lights", Color.White);
return;
}
Color color = XMLExtensions.ParseColor(colorString);
if (Level.Loaded != null)
{
Level.Loaded.GenerationParams.AmbientLightColor = color;
}
else
{
GameMain.LightManager.AmbientLight = color;
GameMain.LightManager.AmbientLight = add ? GameMain.LightManager.AmbientLight.Add(color) : color;
}
foreach (Hull hull in Hull.hullList)
{
hull.OriginalAmbientLight ??= hull.AmbientLight;
hull.AmbientLight = add ? hull.AmbientLight.Add(color) : color;
}
if (add)
{
NewMessage($"Set ambient light color to {color}.", Color.White);
}
else
{
NewMessage($"Increased ambient light by {color}.", Color.White);
}
NewMessage("Set ambient light color to " + color + ".", Color.White);
});
AssignRelayToServer("ambientlight", false);
@@ -849,17 +913,6 @@ namespace Barotrauma
TutorialMode.StartTutorial(Tutorials.Tutorial.Tutorials[0]);
}));
commands.Add(new Command("lobby|lobbyscreen", "", (string[] args) =>
{
if (GameMain.Client != null)
{
ThrowError("This command cannot be used in multiplayer.");
return;
}
GameMain.LobbyScreen.Select();
}));
commands.Add(new Command("save|savesub", "save [submarine name]: Save the currently loaded submarine using the specified name.", (string[] args) =>
{
if (args.Length < 1) { return; }
@@ -1011,9 +1064,39 @@ namespace Barotrauma
});
AssignRelayToServer("toggleaitargets|aitargets", false);
AssignOnExecute("debugai", (string[] args) =>
{
HumanAIController.debugai = !HumanAIController.debugai;
if (HumanAIController.debugai)
{
GameMain.DebugDraw = true;
GameMain.LightManager.LightingEnabled = false;
GameMain.LightManager.LosEnabled = false;
}
else
{
GameMain.DebugDraw = false;
GameMain.LightManager.LightingEnabled = true;
GameMain.LightManager.LosEnabled = true;
}
NewMessage(HumanAIController.debugai ? "AI debug info visible" : "AI debug info hidden", Color.White);
});
AssignRelayToServer("debugai", false);
AssignRelayToServer("water|editwater", false);
AssignRelayToServer("fire|editfire", false);
commands.Add(new Command("togglecampaignteleport", "togglecampaignteleport: Toggle on/off teleportation between campaign locations by double clicking on the campaign map.", (string[] args) =>
{
if (GameMain.GameSession?.Campaign == null)
{
ThrowError("No campaign active.");
return;
}
GameMain.GameSession.Map.AllowDebugTeleport = !GameMain.GameSession.Map.AllowDebugTeleport;
NewMessage((GameMain.GameSession.Map.AllowDebugTeleport ? "Enabled" : "Disabled") + " teleportation on the campaign map.", Color.White);
}, isCheat: true));
commands.Add(new Command("mute", "mute [name]: Prevent the client from speaking to anyone through the voice chat. Using this command requires a permission from the server host.",
null,
() =>
@@ -1044,7 +1127,7 @@ namespace Barotrauma
}
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
{
int? minCost = itemPrefab.GetPrices()?.Min(p => p.BuyPrice);
int? minCost = itemPrefab.GetMinPrice();
int? fabricationCost = null;
int? deconstructProductCost = null;
@@ -1053,7 +1136,7 @@ namespace Barotrauma
{
foreach (var ingredient in fabricationRecipe.RequiredItems)
{
int? ingredientPrice = ingredient.ItemPrefab.GetPrices()?.Min(p => p.BuyPrice);
int? ingredientPrice = ingredient.ItemPrefab.GetMinPrice();
if (ingredientPrice.HasValue)
{
if (!fabricationCost.HasValue) { fabricationCost = 0; }
@@ -1071,7 +1154,7 @@ namespace Barotrauma
continue;
}
int? deconstructProductPrice = targetItem.GetPrices()?.Min(p => p.BuyPrice);
int? deconstructProductPrice = targetItem.GetMinPrice();
if (deconstructProductPrice.HasValue)
{
if (!deconstructProductCost.HasValue) { deconstructProductCost = 0; }
@@ -1300,7 +1383,7 @@ namespace Barotrauma
commands.Add(new Command("eventstats", "", (string[] args) =>
{
var debugLines = ScriptedEventSet.GetDebugStatistics();
var debugLines = EventSet.GetDebugStatistics();
string filePath = "eventstats.txt";
File.WriteAllLines(filePath, debugLines);
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
@@ -1537,6 +1620,76 @@ namespace Barotrauma
File.WriteAllLines(filePath, lines);
}));
commands.Add(new Command("dumpeventtexts", "dumpeventtexts [filepath]: gets the texts from event files and and writes them into a file along with xml tags that can be used in translation files. If the filepath is omitted, the file is written to Content/Texts/EventTexts.txt", (string[] args) =>
{
string filePath = args.Length > 0 ? args[0] : "Content/Texts/EventTexts.txt";
List<string> lines = new List<string>();
HashSet<XDocument> docs = new HashSet<XDocument>();
HashSet<string> textIds = new HashSet<string>();
foreach (EventPrefab eventPrefab in EventSet.GetAllEventPrefabs())
{
if (string.IsNullOrEmpty(eventPrefab.Identifier))
{
continue;
}
docs.Add(eventPrefab.ConfigElement.Document);
getTextsFromElement(eventPrefab.ConfigElement, lines, eventPrefab.Identifier);
}
File.WriteAllLines(filePath, lines);
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
{
Indent = true,
NewLineOnAttributes = false
};
foreach (XDocument doc in docs)
{
using (var writer = XmlWriter.Create(new System.Uri(doc.BaseUri).LocalPath, settings))
{
doc.WriteTo(writer);
writer.Flush();
}
}
void getTextsFromElement(XElement element, List<string> list, string parentName)
{
string text = element.GetAttributeString("text", null);
string textId = $"EventText.{parentName}";
if (!string.IsNullOrEmpty(text) && !text.Contains("EventText.", StringComparison.OrdinalIgnoreCase))
{
list.Add($"<{textId}>{text}</{textId}>");
element.SetAttributeValue("text", textId);
}
int i = 1;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "conversationaction":
while (textIds.Contains(parentName+".c"+i))
{
i++;
}
parentName += ".c" + i;
break;
case "option":
while (textIds.Contains(parentName.Substring(0, parentName.Length - 3) + ".o" + i))
{
i++;
}
parentName = parentName.Substring(0, parentName.Length - 3) + ".o" + i;
break;
}
textIds.Add(parentName);
getTextsFromElement(subElement, list, parentName);
}
}
}));
commands.Add(new Command("itemcomponentdocumentation", "", (string[] args) =>
{

View File

@@ -0,0 +1,365 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
partial class ConversationAction : EventAction
{
private GUIMessageBox dialogBox;
private static ConversationAction lastActiveAction;
private static GUIMessageBox lastMessageBox;
public static bool IsDialogOpen
{
get
{
return GUIMessageBox.MessageBoxes.Any(mb =>
mb.UserData as string == "ConversationAction" ||
(mb.UserData is Pair<string, UInt16> pair && pair.First == "ConversationAction"));
}
}
public static bool FadeScreenToBlack
{
get { return IsDialogOpen && shouldFadeToBlack; }
}
private static bool shouldFadeToBlack;
private bool IsBlockedByAnotherConversation(IEnumerable<Entity> _)
{
return
lastActiveAction != null &&
lastActiveAction.ParentEvent != ParentEvent &&
Timing.TotalTime < lastActiveAction.lastActiveTime + BlockOtherConversationsDuration;
}
partial void ShowDialog(Character speaker, Character targetCharacter)
{
CreateDialog(Text, speaker, Options.Select(opt => opt.Text), GetEndingOptions(), actionInstance: this, spriteIdentifier: EventSprite, fadeToBlack: FadeToBlack, dialogType: DialogType, continueConversation: ContinueConversation);
}
public static void CreateDialog(string text, Character speaker, IEnumerable<string> options, int[] closingOptions, string eventSprite, UInt16 actionId, bool fadeToBlack, DialogTypes dialogType, bool continueConversation = false)
{
CreateDialog(text, speaker, options, closingOptions, actionInstance: null, actionId: actionId, spriteIdentifier: eventSprite, fadeToBlack: fadeToBlack, dialogType: dialogType, continueConversation: continueConversation);
}
private static void CreateDialog(string text, Character speaker, IEnumerable<string> options, int[] closingOptions, string spriteIdentifier = null,
ConversationAction actionInstance = null, UInt16? actionId = null, bool fadeToBlack = false, DialogTypes dialogType = DialogTypes.Regular, bool continueConversation = false)
{
Debug.Assert(actionInstance == null || actionId == null);
shouldFadeToBlack = fadeToBlack;
if (lastMessageBox != null && !lastMessageBox.Closed && GUIMessageBox.MessageBoxes.Contains(lastMessageBox))
{
if (actionId != null && lastMessageBox.UserData is Pair<string, ushort> userData)
{
if (userData.Second == actionId) { return; }
lastMessageBox.UserData = new Pair<string, ushort>("ConversationAction", actionId.Value);
}
GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox;
Debug.Assert(conversationList != null);
// gray out the last text block
if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement)
{
if (lastElement.FindChild("text", true) is GUITextBlock textLayout)
{
textLayout.OverrideTextColor(Color.DarkGray * 0.8f);
}
}
List<GUIButton> extraButtons = CreateConversation(conversationList, text, speaker, options, string.IsNullOrWhiteSpace(spriteIdentifier));
AssignActionsToButtons(extraButtons, lastMessageBox);
RecalculateLastMessage(conversationList, true);
conversationList.ScrollToEnd(0.5f);
lastMessageBox.SetBackgroundIcon(EventSet.GetEventSprite(spriteIdentifier));
return;
}
var (relative, min) = GetSizes(dialogType);
GUIMessageBox messageBox = new GUIMessageBox(string.Empty, string.Empty, new string[0],
relativeSize: relative, minSize: min,
type: GUIMessageBox.Type.InGame, backgroundIcon: EventSet.GetEventSprite(spriteIdentifier))
{
UserData = "ConversationAction"
};
lastMessageBox = messageBox;
messageBox.InnerFrame.ClearChildren();
messageBox.AutoClose = false;
GUI.Style.Apply(messageBox.InnerFrame, "DialogBox");
if (actionInstance != null)
{
lastActiveAction = actionInstance;
actionInstance.dialogBox = messageBox;
}
else
{
messageBox.UserData = new Pair<string, UInt16>("ConversationAction", actionId.Value);
}
int padding = GUI.IntScale(16);
GUIListBox listBox = new GUIListBox(new RectTransform(messageBox.InnerFrame.Rect.Size - new Point(padding * 2), messageBox.InnerFrame.RectTransform, Anchor.Center), style: null)
{
KeepSpaceForScrollBar = true,
HoverCursor = CursorState.Default,
UserData = "conversationlist"
};
List<GUIButton> buttons = CreateConversation(listBox, text, speaker, options, string.IsNullOrWhiteSpace(spriteIdentifier));
AssignActionsToButtons(buttons, messageBox);
RecalculateLastMessage(listBox, false);
messageBox.InnerFrame.RectTransform.MinSize = new Point(0, Math.Max(listBox.RectTransform.MinSize.Y + padding * 2, (int)(100 * GUI.yScale)));
var shadow = new GUIFrame(new RectTransform(messageBox.InnerFrame.Rect.Size + new Point(padding * 4), messageBox.InnerFrame.RectTransform, Anchor.Center), style: "OuterGlow")
{
Color = Color.Black * 0.7f
};
shadow.SetAsFirstChild();
void RecalculateLastMessage(GUIListBox conversationList, bool append)
{
if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement)
{
GUILayoutGroup textLayout = lastElement.GetChild<GUILayoutGroup>();
if (lastElement.Rect.Size.Y < textLayout.Rect.Size.Y && !append)
{
lastElement.RectTransform.MinSize = textLayout.Rect.Size;
}
if (textLayout != null)
{
int textHeight = textLayout.Children.Sum(c => c.Rect.Height);
textLayout.RectTransform.MaxSize = new Point(lastElement.RectTransform.MaxSize.X, textHeight);
textLayout.Recalculate();
}
int sumHeight = lastElement.Children.Sum(c => c.Rect.Height);
lastElement.RectTransform.MaxSize = new Point(lastElement.RectTransform.MaxSize.X, sumHeight);
lastElement.Recalculate();
conversationList.RecalculateChildren();
if (!append || textLayout == null) { return; }
foreach (GUIComponent child in textLayout.Children)
{
conversationList.UpdateScrollBarSize();
float wait = conversationList.BarSize < 1.0f ? 0.5f : 0.0f;
if (child is GUITextBlock) { child.FadeIn(wait, 0.5f); }
if (child is GUIButton btn)
{
btn.FadeIn(wait, 1.0f);
btn.TextBlock.FadeIn(wait, 0.5f);
}
}
}
}
void AssignActionsToButtons(List<GUIButton> optionButtons, GUIMessageBox target)
{
if (!options.Any())
{
GUIButton closeButton = new GUIButton(new RectTransform(Vector2.One, target.InnerFrame.RectTransform, Anchor.BottomRight, scaleBasis: ScaleBasis.Smallest)
{
MaxSize = new Point(GUI.IntScale(24)),
MinSize = new Point(24),
AbsoluteOffset = new Point(GUI.IntScale(48), GUI.IntScale(16))
}, style: "GUIButtonVerticalArrow")
{
UserData = "ContinueButton",
IgnoreLayoutGroups = true,
Bounce = true,
OnClicked = (btn, userdata) =>
{
if (actionInstance != null)
{
actionInstance.selectedOption = 0;
}
else if (actionId.HasValue)
{
SendResponse(actionId.Value, 0);
}
if (!continueConversation)
{
target.Close();
}
else
{
btn.Frame.FadeOut(0.33f, true);
}
return true;
}
};
closeButton.Children.ForEach(child => child.SpriteEffects = SpriteEffects.FlipVertically);
closeButton.Frame.FadeIn(0.5f, 0.5f);
closeButton.SlideIn(0.5f, 0.33f, 16, SlideDirection.Down);
}
for (int i = 0; i < optionButtons.Count; i++)
{
optionButtons[i].UserData = i;
optionButtons[i].OnClicked += (btn, userdata) =>
{
int selectedOption = (userdata as int?) ?? 0;
if (actionInstance != null)
{
actionInstance.selectedOption = selectedOption;
foreach (GUIButton otherButton in optionButtons)
{
otherButton.CanBeFocused = false;
if (otherButton != btn)
{
otherButton.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
}
}
btn.ExternalHighlight = true;
return true;
}
if (actionId.HasValue)
{
SendResponse(actionId.Value, selectedOption);
btn.CanBeFocused = false;
btn.ExternalHighlight = true;
foreach (GUIButton otherButton in optionButtons)
{
otherButton.CanBeFocused = false;
if (otherButton != btn)
{
otherButton.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
}
}
return true;
}
//should not happen
return false;
};
if (closingOptions.Contains(i)) { optionButtons[i].OnClicked += target.Close; }
}
}
}
private static Tuple<Vector2, Point> GetSizes(DialogTypes dialogTypes)
{
return dialogTypes switch
{
DialogTypes.Regular => Tuple.Create(new Vector2(0.3f, 0.2f), new Point(512, 256)),
_ => Tuple.Create(new Vector2(0.3f, 0.15f), new Point(512, 128))
};
}
private static List<GUIButton> CreateConversation(GUIListBox parentBox, string text, Character speaker, IEnumerable<string> options, bool drawChathead = true)
{
var content = new GUILayoutGroup(new RectTransform(Vector2.One, parentBox.Content.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
{
Stretch = true,
CanBeFocused = true,
AlwaysOverrideCursor = true
};
string translatedText = TextManager.Get(text, returnNull: true) ?? text;
if (speaker?.Info != null && drawChathead)
{
// chathead
new GUICustomComponent(new RectTransform(new Vector2(0.15f, 0.8f), content.RectTransform), onDraw: (sb, customComponent) =>
{
speaker.Info.DrawIcon(sb, customComponent.Rect.Center.ToVector2(), customComponent.Rect.Size.ToVector2());
});
}
var textContent = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), content.RectTransform), childAnchor: Anchor.TopCenter)
{
AbsoluteSpacing = GUI.IntScale(5)
};
var textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform), translatedText, wrap: true)
{
AlwaysOverrideCursor = true,
UserData = "text"
};
List<GUIButton> buttons = new List<GUIButton>();
if (options.Any())
{
foreach (string option in options)
{
var btn = new GUIButton(new RectTransform(new Vector2(0.9f, 0.01f), textContent.RectTransform), TextManager.Get(option, returnNull: true) ?? option, style: "ListBoxElement");
btn.TextBlock.TextAlignment = Alignment.CenterLeft;
btn.TextColor = btn.HoverTextColor = GUI.Style.Green;
btn.TextBlock.Wrap = true;
buttons.Add(btn);
}
}
content.Recalculate();
textContent.Recalculate();
textBlock.CalculateHeightFromText();
textBlock.RectTransform.MinSize = new Point(0, (int)(textBlock.Rect.Height * 1.2f));
foreach (GUIButton btn in buttons)
{
btn.TextBlock.SetTextPos();
btn.TextBlock.CalculateHeightFromText();
btn.RectTransform.MinSize = new Point(0, (int)(btn.TextBlock.Rect.Height * 1.2f));
}
textContent.RectTransform.MinSize = new Point(0, textContent.Children.Sum(c => c.Rect.Height + textContent.AbsoluteSpacing) + GUI.IntScale(16));
// content.RectTransform.MinSize = new Point(0, textContent.Rect.Height);
return buttons;
}
private static void SendResponse(UInt16 actionId, int selectedOption)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.Write((byte)ClientPacketHeader.EVENTMANAGER_RESPONSE);
outmsg.Write(actionId);
outmsg.Write((byte)selectedOption);
GameMain.Client?.ClientPeer?.Send(outmsg, DeliveryMethod.Reliable);
}
// Too broken, left it here if I ever want to come back to it
private static List<RichTextData> GetQuoteHighlights(string text, Color color)
{
char[] quotes = { '“', '”', '\"', '\'', '「', '」'};
List<RichTextData> textColors = new List<RichTextData> { new RichTextData { StartIndex = 0 } };
bool start = true;
for (int i = 0; i < text.Length; i++)
{
char c = text[i];
if (quotes.Contains(c))
{
textColors.Last().EndIndex = i - 1;
textColors.Add(new RichTextData { StartIndex = i, Color = start ? color : (Color?) null });
start = !start;
}
}
if (textColors.LastOrDefault() is { } last && last.EndIndex == 0)
{
last.EndIndex = text.Length;
}
return textColors;
}
}
}

View File

@@ -1,6 +1,12 @@
using Microsoft.Xna.Framework;
#nullable enable
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -11,34 +17,43 @@ namespace Barotrauma
private float intensityGraphUpdateInterval;
private float lastIntensityUpdate;
private Event? pinnedEvent;
private Vector2 pinnedPosition = new Vector2(256, 128);
private bool isDragging;
public void DebugDraw(SpriteBatch spriteBatch)
{
foreach (ScriptedEvent ev in activeEvents)
foreach (Event ev in activeEvents)
{
Vector2 drawPos = ev.DebugDrawPos;
drawPos.Y = -drawPos.Y;
var textOffset = new Vector2(-150, 0);
ShapeExtensions.DrawCircle(spriteBatch, drawPos, 600, 6, Color.White, thickness: 20);
spriteBatch.DrawCircle(drawPos, 600, 6, Color.White, thickness: 20);
GUI.DrawString(spriteBatch, drawPos + textOffset, ev.ToString(), Color.White, Color.Black, 0, GUI.LargeFont);
}
}
public void DebugDrawHUD(SpriteBatch spriteBatch, int y)
{
foreach (ScriptedEvent scriptedEvent in activeEvents.Where(ev => !ev.IsFinished && ev is ScriptedEvent).Cast<ScriptedEvent>())
{
DrawEventTargetTags(spriteBatch, scriptedEvent);
}
GUI.DrawString(spriteBatch, new Vector2(10, y), "EventManager", Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 20), "Event cooldown: " + eventCoolDown, Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 35), "Current intensity: " + (int)Math.Round(currentIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, currentIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 50), "Target intensity: " + (int)Math.Round(targetIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, targetIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 35), "Current intensity: " + (int) Math.Round(currentIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, currentIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 50), "Target intensity: " + (int) Math.Round(targetIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, targetIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 65), "AvgHealth: " + (int)Math.Round(avgCrewHealth * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgCrewHealth), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 80), "AvgHullIntegrity: " + (int)Math.Round(avgHullIntegrity * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgHullIntegrity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 95), "FloodingAmount: " + (int)Math.Round(floodingAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, floodingAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 110), "FireAmount: " + (int)Math.Round(fireAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, fireAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 125), "EnemyDanger: " + (int)Math.Round(enemyDanger * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, enemyDanger), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 65), "AvgHealth: " + (int) Math.Round(avgCrewHealth * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgCrewHealth), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 80), "AvgHullIntegrity: " + (int) Math.Round(avgHullIntegrity * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgHullIntegrity), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 95), "FloodingAmount: " + (int) Math.Round(floodingAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, floodingAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 110), "FireAmount: " + (int) Math.Round(fireAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, fireAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(15, y + 125), "EnemyDanger: " + (int) Math.Round(enemyDanger * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, enemyDanger), Color.Black * 0.6f, 0, GUI.SmallFont);
#if DEBUG
if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt) &&
if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt) &&
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.T))
{
eventCoolDown = 1.0f;
@@ -56,7 +71,7 @@ namespace Barotrauma
{
intensityGraph.Update(currentIntensity);
targetIntensityGraph.Update(targetIntensity);
lastIntensityUpdate = (float)Timing.TotalTime;
lastIntensityUpdate = (float) Timing.TotalTime;
}
Rectangle graphRect = new Rectangle(15, y + 150, 150, 50);
@@ -72,17 +87,20 @@ namespace Barotrauma
y = graphRect.Bottom + 20;
if (eventCoolDown > 0.0f)
{
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "Event cooldown active: " + (int)eventCoolDown, Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "Event cooldown active: " + (int) eventCoolDown, Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
y += 15;
}
else if (currentIntensity > eventThreshold)
{
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y),
"Intensity too high for new events: " + (int)(currentIntensity * 100) + "%/" + (int)(eventThreshold * 100) + "%", Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
"Intensity too high for new events: " + (int) (currentIntensity * 100) + "%/" + (int) (eventThreshold * 100) + "%", Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
y += 15;
}
foreach (ScriptedEventSet eventSet in pendingEventSets)
foreach (EventSet eventSet in pendingEventSets)
{
if (Submarine.MainSub == null) { break; }
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "New event (ID " + eventSet.DebugIdentifier + ") after: ", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
@@ -90,36 +108,421 @@ namespace Barotrauma
roundDuration < eventSet.MinMissionTime)
{
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y),
" " + (int)(eventSet.MinDistanceTraveled * 100.0f) + "% travelled (current: " + (int)(distanceTraveled * 100.0f) + " %)",
" " + (int) (eventSet.MinDistanceTraveled * 100.0f) + "% travelled (current: " + (int) (distanceTraveled * 100.0f) + " %)",
Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
}
if (CurrentIntensity < eventSet.MinIntensity || CurrentIntensity > eventSet.MaxIntensity)
{
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y),
" intensity between " + ((int)eventSet.MinIntensity) + " and " + ((int)eventSet.MaxIntensity),
" intensity between " + ((int) eventSet.MinIntensity) + " and " + ((int) eventSet.MaxIntensity),
Color.Orange * 0.8f, null, 0, GUI.SmallFont);
y += 12;
}
if (roundDuration < eventSet.MinMissionTime)
{
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y),
" " + (int)(eventSet.MinMissionTime - roundDuration) + " s",
" " + (int) (eventSet.MinMissionTime - roundDuration) + " s",
Color.Orange * 0.8f, null, 0, GUI.SmallFont);
}
y += 15;
}
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "Current events: ", Color.White * 0.9f, null, 0, GUI.SmallFont);
y += 12;
foreach (ScriptedEvent scriptedEvent in activeEvents)
y += 15;
foreach (Event ev in activeEvents.Where(ev => !ev.IsFinished || PlayerInput.IsShiftDown()))
{
if (scriptedEvent.IsFinished) { continue; }
GUI.DrawString(spriteBatch, new Vector2(graphRect.X + 5, y), scriptedEvent.ToString(), Color.White * 0.8f, null, 0, GUI.SmallFont);
y += 12;
GUI.DrawString(spriteBatch, new Vector2(graphRect.X + 5, y), ev.ToString(), (!ev.IsFinished ? Color.White : Color.Red) * 0.8f, null, 0, GUI.SmallFont);
Rectangle rect = new Rectangle(new Point(graphRect.X + 5, y), GUI.SmallFont.MeasureString(ev.ToString()).ToPoint());
Rectangle outlineRect = new Rectangle(rect.Location, rect.Size);
outlineRect.Inflate(4, 4);
if (pinnedEvent == ev) { GUI.DrawRectangle(spriteBatch, outlineRect, Color.White); }
if (rect.Contains(PlayerInput.MousePosition))
{
GUI.MouseCursor = CursorState.Hand;
GUI.DrawRectangle(spriteBatch, outlineRect, Color.White);
if (ev != pinnedEvent)
{
DrawEvent(spriteBatch, ev, rect);
}
else if (PlayerInput.SecondaryMouseButtonHeld() || PlayerInput.SecondaryMouseButtonDown())
{
pinnedEvent = null;
}
if (PlayerInput.PrimaryMouseButtonHeld() || PlayerInput.PrimaryMouseButtonDown())
{
pinnedEvent = ev;
}
}
y += 18;
}
}
public void DrawPinnedEvent(SpriteBatch spriteBatch)
{
if (pinnedEvent != null)
{
Rectangle rect = DrawEvent(spriteBatch, pinnedEvent, null);
if (rect != Rectangle.Empty)
{
if (rect.Contains(PlayerInput.MousePosition) && !isDragging)
{
GUI.MouseCursor = CursorState.Move;
if (PlayerInput.PrimaryMouseButtonDown() || PlayerInput.PrimaryMouseButtonHeld())
{
isDragging = true;
}
if (PlayerInput.SecondaryMouseButtonClicked() || PlayerInput.SecondaryMouseButtonHeld())
{
pinnedEvent = null;
isDragging = false;
}
}
}
if (isDragging)
{
GUI.MouseCursor = CursorState.Dragging;
pinnedPosition = PlayerInput.MousePosition - (new Vector2(rect.Width / 2.0f, -24));
if (!PlayerInput.PrimaryMouseButtonHeld())
{
isDragging = false;
}
}
}
}
private static void DrawEventTargetTags(SpriteBatch spriteBatch, ScriptedEvent scriptedEvent)
{
if (Screen.Selected is GameScreen screen)
{
Camera cam = screen.Cam;
Dictionary<Entity, List<string>> tagsDictionary = new Dictionary<Entity, List<string>>();
foreach ((string key, List<Entity> value) in scriptedEvent.Targets)
{
foreach (Entity entity in value)
{
if (tagsDictionary.ContainsKey(entity))
{
tagsDictionary[entity].Add(key);
}
else
{
tagsDictionary.Add(entity, new List<string> { key });
}
}
}
string identifier = scriptedEvent.Prefab.Identifier;
foreach ((Entity entity, List<string> tags) in tagsDictionary)
{
if (entity.Removed) { continue; }
string text = tags.Aggregate("Tags:\n", (current, tag) => current + $" {tag.ColorizeObject()}\n").TrimEnd('\r', '\n');
if (!string.IsNullOrWhiteSpace(identifier)) { text = $"Event: {identifier.ColorizeObject()}\n{text}"; }
List<RichTextData> richTextData = RichTextData.GetRichTextData(text, out text);
Vector2 entityPos = cam.WorldToScreen(entity.WorldPosition);
Vector2 infoSize = GUI.SmallFont.MeasureString(text);
Vector2 infoPos = entityPos + new Vector2(128 * cam.Zoom, -(128 * cam.Zoom));
infoPos.Y -= infoSize.Y / 2;
Rectangle infoRect = new Rectangle(infoPos.ToPoint(), infoSize.ToPoint());
infoRect.Inflate(4, 4);
GUI.DrawRectangle(spriteBatch, infoRect, Color.Black * 0.8f, isFilled: true);
GUI.DrawRectangle(spriteBatch, infoRect, Color.White, isFilled: false);
GUI.DrawStringWithColors(spriteBatch, infoPos, text, Color.White, richTextData, font: GUI.SmallFont);
GUI.DrawLine(spriteBatch, entityPos, new Vector2(infoRect.Location.X, infoRect.Location.Y + infoRect.Height / 2), Color.White);
}
}
}
private readonly struct DebugLine
{
public readonly Vector2 Position;
public readonly Color Color;
public DebugLine(Vector2 position, Color color)
{
Position = position;
Color = color;
}
}
private Rectangle DrawEvent(SpriteBatch spriteBatch, Event ev, Rectangle? parentRect = null)
{
return ev switch
{
ScriptedEvent scriptedEvent => DrawScriptedEvent(spriteBatch, scriptedEvent, parentRect),
ArtifactEvent artifactEvent => DrawArtifactEvent(spriteBatch, artifactEvent, parentRect),
MonsterEvent monsterEvent => DrawMonsterEvent(spriteBatch, monsterEvent, parentRect),
_ => Rectangle.Empty
};
}
private Rectangle DrawScriptedEvent(SpriteBatch spriteBatch, ScriptedEvent scriptedEvent, Rectangle? parentRect = null)
{
EventAction? currentEvent = !scriptedEvent.IsFinished ? scriptedEvent.Actions[scriptedEvent.CurrentActionIndex] : null;
List<DebugLine> positions = new List<DebugLine>();
string text = $"Finished: {scriptedEvent.IsFinished.ColorizeObject()}\n" +
$"Action index: {scriptedEvent.CurrentActionIndex.ColorizeObject()}\n" +
$"Current action: {currentEvent?.ToDebugString() ?? ToolBox.ColorizeObject(null)}\n";
text += "All actions:\n";
text += FindActions(scriptedEvent).Aggregate(string.Empty, (current, action) => current + $"{new string(' ', action.Item1 * 6)}{action.Item2.ToDebugString()}\n");
text += "Targets:\n";
foreach (var (key, value) in scriptedEvent.Targets)
{
text += $" {key.ColorizeObject()}: {value.Aggregate(string.Empty, (current, entity) => current + $"{entity.ColorizeObject()} ")}\n";
}
if (scriptedEvent.Targets != null)
{
foreach ((_, List<Entity> entities) in scriptedEvent.Targets)
{
if (entities == null || !entities.Any()) { continue; }
foreach (var entity in entities)
{
positions.Add(new DebugLine(entity.WorldPosition, Color.White));
}
}
}
return DrawInfoRectangle(spriteBatch, scriptedEvent, text, parentRect, positions);
}
private Rectangle DrawArtifactEvent(SpriteBatch spriteBatch, ArtifactEvent artifactEvent, Rectangle? parentRect = null)
{
List<DebugLine> positions = new List<DebugLine>();
string text = $"Finished: {artifactEvent.IsFinished.ColorizeObject()}\n" +
$"Item: {artifactEvent.Item.ColorizeObject()}\n" +
$"Spawn pending: {artifactEvent.SpawnPending.ColorizeObject()}\n" +
$"Spawn position: {artifactEvent.SpawnPos.ColorizeObject()}\n";
if (artifactEvent.Item != null)
{
Vector2 pos = artifactEvent.Item.WorldPosition;
positions.Add(new DebugLine(pos, Color.White));
}
return DrawInfoRectangle(spriteBatch, artifactEvent, text, parentRect, positions);
}
private Rectangle DrawMonsterEvent(SpriteBatch spriteBatch, MonsterEvent monsterEvent, Rectangle? parentRect = null)
{
List<DebugLine> positions = new List<DebugLine>();
string text = $"Finished: {monsterEvent.IsFinished.ColorizeObject()}\n" +
$"Amount: {monsterEvent.MinAmount.ColorizeObject()} - {monsterEvent.MaxAmount.ColorizeObject()}\n" +
$"Spawn pending: {monsterEvent.SpawnPending.ColorizeObject()}\n" +
$"Spawn position: {monsterEvent.SpawnPos.ColorizeObject()}\n";
if (monsterEvent.SpawnPos != null && Submarine.MainSub != null)
{
Vector2 pos = monsterEvent.SpawnPos.Value;
text += $"Distance from submarine: {Vector2.Distance(pos, Submarine.MainSub.WorldPosition).ColorizeObject()}\n";
positions.Add(new DebugLine(pos, Color.White));
}
if (monsterEvent.Monsters != null)
{
text += !monsterEvent.Monsters.Any() ? $"Monsters: {"None".ColorizeObject()}" : "Monsters:\n";
foreach (Character monster in monsterEvent.Monsters)
{
text += $" {monster.ColorizeObject()} -> (Dead: {monster.IsDead.ColorizeObject()}, Health: {monster.HealthPercentage.ColorizeObject()}%, AIState: {(monster.AIController?.State).ColorizeObject()})\n";
positions.Add(new DebugLine(monster.WorldPosition, Color.Red));
}
}
return DrawInfoRectangle(spriteBatch, monsterEvent, text, parentRect, positions);
}
private Rectangle DrawInfoRectangle(SpriteBatch spriteBatch, Event @event, string text, Rectangle? parentRect = null, List<DebugLine>? drawPoints = null)
{
text = text.TrimEnd('\r', '\n');
string identifier = @event.Prefab.Identifier;
if (!string.IsNullOrWhiteSpace(identifier))
{
text = $"Identifier: {identifier.ColorizeObject()}\n{text}";
}
List<RichTextData> richTextData = RichTextData.GetRichTextData(text, out text);
Vector2 size = GUI.SmallFont.MeasureString(text);
Vector2 pos = pinnedPosition;
Rectangle infoRect;
Rectangle? infoBarRect = null;
if (parentRect != null)
{
Rectangle rect = parentRect.Value;
pos = new Vector2(350, GameMain.GraphicsHeight / 2.0f - size.Y / 2);
infoRect = new Rectangle(pos.ToPoint(), size.ToPoint());
infoRect.Inflate(8, 8);
GUI.DrawLine(spriteBatch, new Vector2(rect.Right, rect.Y + rect.Height / 2), new Vector2(infoRect.X, infoRect.Y + infoRect.Height / 2), Color.White);
}
else
{
infoRect = new Rectangle(pos.ToPoint(), size.ToPoint());
infoRect.Inflate(8, 8);
Rectangle barRect = new Rectangle(infoRect.Left, infoRect.Top - 32, infoRect.Width, 32);
const string titleHeader = "Pinned event";
GUI.DrawRectangle(spriteBatch, barRect, Color.DarkGray * 0.8f, isFilled: true);
GUI.DrawString(spriteBatch, barRect.Location.ToVector2() + barRect.Size.ToVector2() / 2 - GUI.SubHeadingFont.MeasureString(titleHeader) / 2, titleHeader, Color.White);
GUI.DrawRectangle(spriteBatch, barRect, Color.White);
infoBarRect = barRect;
}
if (drawPoints != null && drawPoints.Any() && Screen.Selected?.Cam != null)
{
foreach (DebugLine line in drawPoints)
{
if (line.Position != Vector2.Zero)
{
float xPos = infoRect.Right;
if (parentRect == null && pinnedPosition.X + infoRect.Width / 2.0f > GameMain.GraphicsWidth / 2.0f)
{
xPos = infoRect.Left;
}
GUI.DrawLine(spriteBatch, new Vector2(xPos, infoRect.Top + infoRect.Height / 2), Screen.Selected.Cam.WorldToScreen(line.Position), line.Color);
}
}
}
GUI.DrawRectangle(spriteBatch, infoRect, Color.Black * 0.8f, isFilled: true);
GUI.DrawRectangle(spriteBatch, infoRect, Color.White);
GUI.DrawStringWithColors(spriteBatch, pos, text, Color.White, richTextData, null, 0, GUI.SmallFont);
richTextData.Clear();
return infoBarRect ?? infoRect;
}
public void ClientRead(IReadMessage msg)
{
NetworkEventType eventType = (NetworkEventType)msg.ReadByte();
switch (eventType)
{
case NetworkEventType.STATUSEFFECT:
string eventIdentifier = msg.ReadString();
UInt16 actionIndex = msg.ReadUInt16();
UInt16 targetCount = msg.ReadUInt16();
List<Entity> targets = new List<Entity>();
for (int i = 0; i < targetCount; i++)
{
UInt16 targetID = msg.ReadUInt16();
Entity target = Entity.FindEntityByID(targetID);
if (target != null) { targets.Add(target); }
}
var eventPrefab = EventSet.GetEventPrefab(eventIdentifier);
if (eventPrefab == null) { return; }
int j = 0;
foreach (XElement element in eventPrefab.ConfigElement.Descendants())
{
if (j != actionIndex)
{
j++;
continue;
}
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("statuseffect", StringComparison.OrdinalIgnoreCase)) { continue; }
StatusEffect effect = StatusEffect.Load(subElement, $"EventManager.ClientRead ({eventIdentifier})");
foreach (Entity target in targets)
{
effect.Apply(effect.type, 1.0f, target, target as ISerializableEntity);
}
}
break;
}
break;
case NetworkEventType.CONVERSATION:
UInt16 identifier = msg.ReadUInt16();
string eventSprite = msg.ReadString();
byte dialogType = msg.ReadByte();
bool continueConversation = msg.ReadBoolean();
UInt16 speakerId = msg.ReadUInt16();
string text = msg.ReadString();
bool fadeToBlack = msg.ReadBoolean();
byte optionCount = msg.ReadByte();
List<string> options = new List<string>();
for (int i = 0; i < optionCount; i++)
{
options.Add(msg.ReadString());
}
byte endCount = msg.ReadByte();
int[] endings = new int[endCount];
for (int i = 0; i < endCount; i++)
{
endings[i] = msg.ReadByte();
}
if (string.IsNullOrEmpty(text) && optionCount == 0)
{
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
{
if (mb.UserData is Pair<string, UInt16> pair && pair.First == "ConversationAction" && pair.Second == identifier)
{
(mb as GUIMessageBox)?.Close();
}
});
}
else
{
ConversationAction.CreateDialog(text, Entity.FindEntityByID(speakerId) as Character, options, endings, eventSprite, identifier, fadeToBlack, (ConversationAction.DialogTypes)dialogType, continueConversation);
}
if (Entity.FindEntityByID(speakerId) is Character speaker)
{
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
speaker.SetCustomInteract(null, null);
}
break;
case NetworkEventType.MISSION:
string missionIdentifier = msg.ReadString();
MissionPrefab? prefab = MissionPrefab.List.Find(mp => mp.Identifier.Equals(missionIdentifier, StringComparison.OrdinalIgnoreCase));
if (prefab != null)
{
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("missionunlocked", "[missionname]", prefab.Name),
new string[0], type: GUIMessageBox.Type.InGame, icon: prefab.Icon, relativeSize: new Vector2(0.3f, 0.15f), minSize: new Point(512, 128))
{
IconColor = prefab.IconColor
};
}
break;
}
}
}
}
}

View File

@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using System.Collections.Generic;
namespace Barotrauma
{
@@ -13,10 +14,20 @@ namespace Barotrauma
string header = messageIndex < Headers.Count ? Headers[messageIndex] : "";
string message = messageIndex < Messages.Count ? Messages[messageIndex] : "";
CoroutineManager.StartCoroutine(ShowMessageBoxAfterRoundSummary(header, message));
}
private IEnumerable<object> ShowMessageBoxAfterRoundSummary(string header, string message)
{
while (GUIMessageBox.VisibleBox?.UserData is RoundSummary)
{
yield return new WaitForSeconds(1.0f);
}
new GUIMessageBox(header, message, buttons: new string[0], type: GUIMessageBox.Type.InGame, icon: Prefab.Icon)
{
IconColor = Prefab.IconColor
};
yield return CoroutineStatus.Success;
}
public void ClientRead(IReadMessage msg)

View File

@@ -492,7 +492,7 @@ namespace Barotrauma
}
}
public Vector2 MeasureString(string text)
public Vector2 MeasureString(string text, bool removeExtraSpacing = false)
{
if (text == null)
{
@@ -501,7 +501,16 @@ namespace Barotrauma
float currentLineX = 0.0f;
Vector2 retVal = Vector2.Zero;
retVal.Y = baseHeight * 1.8f;
if (!removeExtraSpacing)
{
retVal.Y = baseHeight * 1.8f;
}
else
{
retVal.Y = baseHeight;
}
for (int i = 0; i < text.Length; i++)
{
if (text[i] == '\n')

View File

@@ -1,6 +1,7 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
@@ -144,6 +145,15 @@ namespace Barotrauma
GetSize(element);
}
public Sprite GetDefaultSprite()
{
return GetSprite(GUIComponent.ComponentState.None);
}
public Sprite GetSprite(GUIComponent.ComponentState state)
{
return Sprites.ContainsKey(state) ? Sprites[state]?.First()?.Sprite : null;
}
public void GetSize(XElement element)
{
Point size = new Point(0, 0);

View File

@@ -0,0 +1,729 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
class CrewManagement
{
private CampaignMode campaign => campaignUI.Campaign;
private readonly CampaignUI campaignUI;
private readonly GUIComponent parentComponent;
private GUIListBox hireableList, pendingList, crewList;
private GUIFrame characterPreviewFrame;
private GUIDropDown sortingDropDown;
private GUITextBlock totalBlock;
private GUIButton validateHiresButton;
private GUIButton clearAllButton;
private List<CharacterInfo> PendingHires => campaign.Map?.CurrentLocation?.HireManager?.PendingHires;
private bool HasPermission => campaignUI.Campaign.AllowedToManageCampaign();
private Point resolutionWhenCreated;
private enum SortingMethod
{
AlphabeticalAsc,
JobAsc,
PriceAsc,
PriceDesc,
SkillAsc,
SkillDesc
}
public CrewManagement(CampaignUI campaignUI, GUIComponent parentComponent)
{
this.campaignUI = campaignUI;
this.parentComponent = parentComponent;
CreateUI();
UpdateLocationView(campaignUI.Campaign.Map.CurrentLocation, true);
campaignUI.Campaign.Map.OnLocationChanged += (prevLocation, newLocation) => UpdateLocationView(newLocation, true, prevLocation);
}
public void RefreshPermissions()
{
RefreshCrewFrames(hireableList);
RefreshCrewFrames(crewList);
RefreshCrewFrames(pendingList);
if (clearAllButton != null) { clearAllButton.Enabled = HasPermission; }
}
private void RefreshCrewFrames(GUIListBox listBox)
{
if (listBox == null) { return; }
listBox.CanBeFocused = HasPermission;
foreach (GUIComponent child in listBox.Content.Children)
{
if (child.FindChild(c => c is GUIButton && c.UserData is CharacterInfo, true) is GUIButton buyButton)
{
buyButton.Enabled = HasPermission;
}
}
}
private void CreateUI()
{
if (parentComponent.FindChild(c => c.UserData as string == "glow") is GUIComponent glowChild)
{
parentComponent.RemoveChild(glowChild);
}
if (parentComponent.FindChild(c => c.UserData as string == "container") is GUIComponent containerChild)
{
parentComponent.RemoveChild(containerChild);
}
new GUIFrame(new RectTransform(new Vector2(1.25f, 1.25f), parentComponent.RectTransform, Anchor.Center),
style: "OuterGlow", color: Color.Black * 0.7f)
{
UserData = "glow"
};
new GUIFrame(new RectTransform(new Vector2(0.95f), parentComponent.RectTransform, anchor: Anchor.Center),
style: null)
{
CanBeFocused = false,
UserData = "container"
};
var availableMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform)
{
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
})
{
Stretch = true,
RelativeSpacing = 0.02f
};
// Header ------------------------------------------------
var headerGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), availableMainGroup.RectTransform), isHorizontal: true)
{
RelativeSpacing = 0.005f
};
var imageWidth = (float)headerGroup.Rect.Height / headerGroup.Rect.Width;
new GUIImage(new RectTransform(new Vector2(imageWidth, 1.0f), headerGroup.RectTransform), "CrewManagementHeaderIcon");
new GUITextBlock(new RectTransform(new Vector2(1.0f - imageWidth, 1.0f), headerGroup.RectTransform), TextManager.Get("campaigncrew.header"), font: GUI.LargeFont)
{
CanBeFocused = false,
ForceUpperCase = true
};
var hireablesGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
parent: new GUIFrame(new RectTransform(new Vector2(1.0f, 13.25f / 14.0f), availableMainGroup.RectTransform)).RectTransform))
{
RelativeSpacing = 0.015f,
Stretch = true
};
var sortGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), hireablesGroup.RectTransform), isHorizontal: true)
{
RelativeSpacing = 0.015f
};
new GUITextBlock(new RectTransform(new Vector2(0.15f, 1.0f), sortGroup.RectTransform), text: TextManager.Get("campaignstore.sortby"));
sortingDropDown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1.0f), sortGroup.RectTransform), elementCount: 5)
{
OnSelected = (child, userData) =>
{
SortCharacters(hireableList, (SortingMethod)userData);
return true;
}
};
var tag = "sortingmethod.";
sortingDropDown.AddItem(TextManager.Get(tag + SortingMethod.JobAsc), userData: SortingMethod.JobAsc);
sortingDropDown.AddItem(TextManager.Get(tag + SortingMethod.SkillAsc), userData: SortingMethod.SkillAsc);
sortingDropDown.AddItem(TextManager.Get(tag + SortingMethod.SkillDesc), userData: SortingMethod.SkillDesc);
sortingDropDown.AddItem(TextManager.Get(tag + SortingMethod.PriceAsc), userData: SortingMethod.PriceAsc);
sortingDropDown.AddItem(TextManager.Get(tag + SortingMethod.PriceDesc), userData: SortingMethod.PriceDesc);
hireableList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.96f),
hireablesGroup.RectTransform,
anchor: Anchor.Center))
{
Spacing = 1
};
var pendingAndCrewMainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).RectTransform, anchor: Anchor.TopRight)
{
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
})
{
Stretch = true,
RelativeSpacing = 0.02f
};
var playerBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), pendingAndCrewMainGroup.RectTransform), childAnchor: Anchor.TopRight)
{
RelativeSpacing = 0.005f
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform),
TextManager.Get("campaignstore.balance"), font: GUI.Font, textAlignment: Alignment.BottomRight)
{
AutoScaleVertical = true,
ForceUpperCase = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), playerBalanceContainer.RectTransform),
"", font: GUI.SubHeadingFont, textAlignment: Alignment.TopRight)
{
AutoScaleVertical = true,
TextScale = 1.1f,
TextGetter = () => FormatCurrency(campaign.Money)
};
var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
parent: new GUIFrame(new RectTransform(new Vector2(1.0f, 13.25f / 14.0f), pendingAndCrewMainGroup.RectTransform)
{
MaxSize = new Point(560, campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Rect.Height)
}).RectTransform));
float height = 0.05f;
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaigncrew.pending"), font: GUI.SubHeadingFont);
pendingList = new GUIListBox(new RectTransform(new Vector2(1.0f, 8 * height), pendingAndCrewGroup.RectTransform))
{
Spacing = 1
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), TextManager.Get("campaignmenucrew"), font: GUI.SubHeadingFont);
crewList = new GUIListBox(new RectTransform(new Vector2(1.0f, (8)* height), pendingAndCrewGroup.RectTransform))
{
Spacing = 1
};
var group = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), isHorizontal: true);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), group.RectTransform), TextManager.Get("campaignstore.total"));
totalBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), group.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
{
TextScale = 1.1f
};
group = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, height), pendingAndCrewGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.TopRight)
{
RelativeSpacing = 0.01f
};
validateHiresButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaigncrew.validate"))
{
ForceUpperCase = true,
OnClicked = (b, o) => ValidatePendingHires(true)
};
clearAllButton = new GUIButton(new RectTransform(new Vector2(1.0f / 3.0f, 1.0f), group.RectTransform), text: TextManager.Get("campaignstore.clearall"))
{
ForceUpperCase = true,
Enabled = HasPermission,
OnClicked = (b, o) => RemoveAllPendingHires()
};
resolutionWhenCreated = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
private void UpdateLocationView(Location location, bool removePending, Location prevLocation = null)
{
if (prevLocation != null && prevLocation == location && GameMain.NetworkMember != null) { return; }
if (characterPreviewFrame != null)
{
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
characterPreviewFrame = null;
}
UpdateHireables(location);
if (pendingList != null)
{
if (removePending)
{
PendingHires?.Clear();
pendingList.Content.ClearChildren();
}
else
{
PendingHires?.ForEach(ci => AddPendingHire(ci));
}
SetTotalHireCost();
}
UpdateCrew();
}
private void UpdateHireables(Location location)
{
if (hireableList != null)
{
hireableList.Content.Children.ToList().ForEach(c => hireableList.RemoveChild(c));
var hireableCharacters = location.GetHireableCharacters();
if (hireableCharacters.None())
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), hireableList.Content.RectTransform), TextManager.Get("HireUnavailable"), textAlignment: Alignment.Center)
{
CanBeFocused = false
};
}
else
{
foreach (CharacterInfo c in hireableCharacters)
{
if (c == null) { continue; }
CreateCharacterFrame(c, hireableList);
}
}
sortingDropDown.SelectItem(SortingMethod.JobAsc);
hireableList.UpdateScrollBarSize();
}
}
public void SetHireables(Location location, List<CharacterInfo> availableHires)
{
HireManager hireManager = location.HireManager;
if (hireManager == null) { return; }
int hireVal = hireManager.AvailableCharacters.Aggregate(0, (curr, hire) => curr + hire.GetIdentifier());
int newVal = availableHires.Aggregate(0, (curr, hire) => curr + hire.GetIdentifier());
if (hireVal != newVal)
{
location.HireManager.AvailableCharacters = availableHires;
UpdateHireables(location);
}
}
public void UpdateCrew()
{
crewList.Content.Children.ToList().ForEach(c => crewList.Content.RemoveChild(c));
foreach (CharacterInfo c in GameMain.GameSession.CrewManager.GetCharacterInfos())
{
if (c == null || !((c.Character?.IsBot ?? true) || campaign is SinglePlayerCampaign)) { continue; }
CreateCharacterFrame(c, crewList);
}
SortCharacters(crewList, SortingMethod.JobAsc);
crewList.UpdateScrollBarSize();
}
private void SortCharacters(GUIListBox list, SortingMethod sortingMethod)
{
if (sortingMethod == SortingMethod.AlphabeticalAsc)
{
list.Content.RectTransform.SortChildren((x, y) =>
(x.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Name.CompareTo((y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Name));
}
else if (sortingMethod == SortingMethod.JobAsc)
{
SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) =>
String.Compare((x.GUIComponent.UserData as Tuple<CharacterInfo, float>)?.Item1.Job.Name, (y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Job.Name, StringComparison.Ordinal));
}
else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
{
SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) =>
(x.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Salary.CompareTo((y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item1.Salary));
if (sortingMethod == SortingMethod.PriceDesc) { list.Content.RectTransform.ReverseChildren(); }
}
else if (sortingMethod == SortingMethod.SkillAsc || sortingMethod == SortingMethod.SkillDesc)
{
SortCharacters(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) =>
(x.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item2.CompareTo((y.GUIComponent.UserData as Tuple<CharacterInfo, float>).Item2));
if (sortingMethod == SortingMethod.SkillDesc) { list.Content.RectTransform.ReverseChildren(); }
}
}
private void CreateCharacterFrame(CharacterInfo characterInfo, GUIListBox listBox)
{
Skill skill = null;
Color? jobColor = null;
if (characterInfo.Job != null)
{
skill = characterInfo.Job?.PrimarySkill ?? characterInfo.Job.Skills.OrderByDescending(s => s.Level).FirstOrDefault();
jobColor = characterInfo.Job.Prefab.UIColor;
}
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, 55), parent: listBox.Content.RectTransform), "ListBoxElement")
{
UserData = new Tuple<CharacterInfo, float>(characterInfo, skill != null ? skill.Level : 0.0f)
};
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), frame.RectTransform, anchor: Anchor.Center), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
Stretch = true
};
float portraitWidth = (0.8f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
new GUICustomComponent(new RectTransform(new Vector2(portraitWidth, 0.8f), mainGroup.RectTransform),
onDraw: (sb, component) => characterInfo.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()))
{
CanBeFocused = false
};
GUILayoutGroup nameAndJobGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f - portraitWidth, 0.8f), mainGroup.RectTransform));
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
characterInfo.Name, textColor: jobColor, textAlignment: Alignment.BottomLeft)
{
CanBeFocused = false
};
nameBlock.Text = ToolBox.LimitString(nameBlock.Text, nameBlock.Font, nameBlock.Rect.Width);
GUITextBlock jobBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), nameAndJobGroup.RectTransform),
characterInfo.Job.Name, textColor: Color.White, font: GUI.SmallFont, textAlignment: Alignment.TopLeft)
{
CanBeFocused = false
};
jobBlock.Text = ToolBox.LimitString(jobBlock.Text, jobBlock.Font, jobBlock.Rect.Width);
float width = 0.6f / 3;
if (characterInfo.Job != null)
{
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(width, 0.6f), mainGroup.RectTransform), isHorizontal: true);
float iconWidth = (float)skillGroup.Rect.Height / skillGroup.Rect.Width;
GUIImage skillIcon = new GUIImage(new RectTransform(new Vector2(iconWidth, 1.0f), skillGroup.RectTransform), skill.Icon)
{
CanBeFocused = false
};
if (jobColor.HasValue) { skillIcon.Color = jobColor.Value; }
new GUITextBlock(new RectTransform(new Vector2(1.0f - iconWidth, 1.0f), skillGroup.RectTransform), ((int)skill.Level).ToString(), textAlignment: Alignment.CenterLeft)
{
CanBeFocused = false
};
}
if (listBox != crewList)
{
new GUITextBlock(new RectTransform(new Vector2(width, 1.0f), mainGroup.RectTransform), FormatCurrency(characterInfo.Salary), textAlignment: Alignment.Center)
{
CanBeFocused = false
};
}
if (listBox == hireableList)
{
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementAddButton")
{
UserData = characterInfo,
Enabled = HasPermission,
OnClicked = (b, o) => AddPendingHire(o as CharacterInfo)
};
}
else if (listBox == pendingList)
{
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementRemoveButton")
{
UserData = characterInfo,
Enabled = HasPermission,
OnClicked = (b, o) => RemovePendingHire(o as CharacterInfo)
};
}
else if (listBox == crewList && campaign != null)
{
var currentCrew = GameMain.GameSession.CrewManager.GetCharacterInfos();
new GUIButton(new RectTransform(new Vector2(width, 0.9f), mainGroup.RectTransform), style: "CrewManagementFireButton")
{
UserData = characterInfo,
//can't fire if there's only one character in the crew
Enabled = currentCrew.Contains(characterInfo) && currentCrew.Count() > 1 && HasPermission,
OnClicked = (btn, obj) =>
{
var confirmDialog = new GUIMessageBox(
TextManager.Get("FireWarningHeader"),
TextManager.GetWithVariable("FireWarningText", "[charactername]", ((CharacterInfo)obj).Name),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
confirmDialog.Buttons[0].UserData = (CharacterInfo)obj;
confirmDialog.Buttons[0].OnClicked = FireCharacter;
confirmDialog.Buttons[0].OnClicked += confirmDialog.Close;
confirmDialog.Buttons[1].OnClicked = confirmDialog.Close;
return true;
}
};
}
}
private void CreateCharacterPreviewFrame(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
{
Pivot pivot = listBox == hireableList ? Pivot.TopLeft : Pivot.TopRight;
Point absoluteOffset = new Point(
pivot == Pivot.TopLeft ? listBox.Parent.Parent.Rect.Right + 5 : listBox.Parent.Parent.Rect.Left - 5,
characterFrame.Rect.Top);
int frameSize = (int)(GUI.Scale * 300);
if (GameMain.GraphicsHeight - (absoluteOffset.Y + frameSize) < 0)
{
pivot = listBox == hireableList ? Pivot.BottomLeft : Pivot.BottomRight;
absoluteOffset.Y = characterFrame.Rect.Bottom;
}
characterPreviewFrame = new GUIFrame(new RectTransform(new Point(frameSize), parent: campaignUI.GetTabContainer(CampaignMode.InteractionType.Crew).Parent.RectTransform, pivot: pivot)
{
AbsoluteOffset = absoluteOffset
}, style: "InnerFrame")
{
UserData = characterInfo
};
GUILayoutGroup mainGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.95f), characterPreviewFrame.RectTransform, anchor: Anchor.Center))
{
RelativeSpacing = 0.01f
};
// Character info
GUILayoutGroup infoGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.475f), mainGroup.RectTransform), isHorizontal: true);
GUILayoutGroup infoLabelGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.4f, 1.0f), infoGroup.RectTransform)) { Stretch = true };
GUILayoutGroup infoValueGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.6f, 1.0f), infoGroup.RectTransform)) { Stretch = true };
float blockHeight = 1.0f / 4;
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("name"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), characterInfo.Name);
if (characterInfo.HasGenders)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("gender"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get(characterInfo.Gender.ToString()));
}
if (characterInfo.Job is Job job)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("tabmenu.job"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), job.Name);
}
if (characterInfo.PersonalityTrait is NPCPersonalityTrait trait)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoLabelGroup.RectTransform), TextManager.Get("PersonalityTrait"));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), infoValueGroup.RectTransform), TextManager.Get("personalitytrait." + trait.Name.Replace(" ", "")));
}
infoLabelGroup.Recalculate();
infoValueGroup.Recalculate();
new GUIImage(new RectTransform(new Vector2(1.0f, 0.05f), mainGroup.RectTransform), "HorizontalLine");
// Skills
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.475f), mainGroup.RectTransform), isHorizontal: true);
GUILayoutGroup skillNameGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), skillGroup.RectTransform));
GUILayoutGroup skillLevelGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 1.0f), skillGroup.RectTransform));
List<Skill> characterSkills = characterInfo.Job.Skills;
blockHeight = 1.0f / characterSkills.Count;
foreach (Skill skill in characterSkills)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), skillNameGroup.RectTransform), TextManager.Get("SkillName." + skill.Identifier));
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), skillLevelGroup.RectTransform), ((int)skill.Level).ToString(), textAlignment: Alignment.Right);
}
}
private bool SelectCharacter(GUIListBox listBox, GUIFrame characterFrame, CharacterInfo characterInfo)
{
if (characterPreviewFrame != null && characterPreviewFrame.UserData != characterInfo)
{
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
characterPreviewFrame = null;
}
if (listBox == null || characterFrame == null || characterInfo == null) { return false; }
if (characterPreviewFrame == null)
{
CreateCharacterPreviewFrame(listBox, characterFrame, characterInfo);
}
return true;
}
private bool AddPendingHire(CharacterInfo characterInfo, bool createNetworkMessage = true)
{
hireableList.Content.RemoveChild(hireableList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo));
hireableList.UpdateScrollBarSize();
if (!PendingHires.Contains(characterInfo)) { PendingHires.Add(characterInfo); }
CreateCharacterFrame(characterInfo, pendingList);
SortCharacters(pendingList, SortingMethod.JobAsc);
pendingList.UpdateScrollBarSize();
SetTotalHireCost();
if (createNetworkMessage) { SendCrewState(true); }
return true;
}
private bool RemovePendingHire(CharacterInfo characterInfo, bool setTotalHireCost = true, bool createNetworkMessage = true)
{
if (PendingHires.Contains(characterInfo)) { PendingHires.Remove(characterInfo); }
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == characterInfo));
pendingList.UpdateScrollBarSize();
CreateCharacterFrame(characterInfo, hireableList);
SortCharacters(hireableList, (SortingMethod)sortingDropDown.SelectedItemData);
hireableList.UpdateScrollBarSize();
if (setTotalHireCost) { SetTotalHireCost(); }
if (createNetworkMessage) { SendCrewState(true); }
return true;
}
private bool RemoveAllPendingHires(bool createNetworkMessage = true)
{
pendingList.Content.Children.ToList().ForEach(c => RemovePendingHire((c.UserData as Tuple<CharacterInfo, float>).Item1, setTotalHireCost: false, createNetworkMessage));
SetTotalHireCost();
return true;
}
private void SetTotalHireCost()
{
if (pendingList == null || totalBlock == null || validateHiresButton == null) { return; }
int total = 0;
pendingList.Content.Children.ForEach(c => total += (c.UserData as Tuple<CharacterInfo, float>).Item1.Salary);
totalBlock.Text = FormatCurrency(total);
bool enoughMoney = campaign != null ? total <= campaign.Money : true;
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
validateHiresButton.Enabled = enoughMoney && pendingList.Content.RectTransform.Children.Any();
}
public bool ValidatePendingHires(bool createNetworkEvent = false)
{
List<CharacterInfo> hires = new List<CharacterInfo>();
int total = 0;
foreach (GUIComponent c in pendingList.Content.Children.ToList())
{
if (c.UserData is Tuple<CharacterInfo, float> info)
{
hires.Add(info.Item1);
total += info.Item1.Salary;
}
}
if (hires.None() || total > campaign.Money) { return false; }
bool atLeastOneHired = false;
foreach (CharacterInfo ci in hires)
{
if (campaign.TryHireCharacter(campaign.Map.CurrentLocation, ci))
{
atLeastOneHired = true;
PendingHires.Remove(ci);
pendingList.Content.RemoveChild(pendingList.Content.FindChild(c => (c.UserData as Tuple<CharacterInfo, float>).Item1 == ci));
}
else
{
break;
}
}
if (atLeastOneHired)
{
UpdateLocationView(campaign.Map.CurrentLocation, true);
SelectCharacter(null, null, null);
var dialog = new GUIMessageBox(
TextManager.Get("newcrewmembers"),
TextManager.GetWithVariable("crewhiredmessage", "[location]", campaignUI?.Campaign?.Map?.CurrentLocation?.Name),
new string[] { TextManager.Get("Ok") });
dialog.Buttons[0].OnClicked += dialog.Close;
}
if (createNetworkEvent)
{
SendCrewState(true, validateHires: true);
}
return false;
}
private bool FireCharacter(GUIButton button, object selection)
{
if (!(selection is CharacterInfo characterInfo)) { return false; }
campaign.CrewManager.FireCharacter(characterInfo);
SelectCharacter(null, null, null);
UpdateCrew();
SendCrewState(false, firedCharacter: characterInfo);
return false;
}
public void Update()
{
if (GameMain.GraphicsWidth != resolutionWhenCreated.X || GameMain.GraphicsHeight != resolutionWhenCreated.Y)
{
CreateUI();
UpdateLocationView(campaign.Map.CurrentLocation, false);
}
if ((GUI.MouseOn?.UserData as Tuple<CharacterInfo, float>)?.Item1 is CharacterInfo characterInfo)
{
if (characterPreviewFrame == null || characterInfo != characterPreviewFrame.UserData)
{
GUIComponent component = GUI.MouseOn;
GUIListBox listBox = null;
do
{
if (component.Parent is GUIListBox)
{
listBox = component.Parent as GUIListBox;
break;
}
else if (component.Parent != null)
{
component = component.Parent;
}
else
{
break;
}
} while (listBox == null);
if (listBox != null)
{
SelectCharacter(listBox, GUI.MouseOn as GUIFrame, characterInfo);
}
}
else
{
// TODO: Reposition the current preview panel if necessary
// Could happen if we scroll a list while hovering?
}
}
else if (characterPreviewFrame != null)
{
characterPreviewFrame.Parent?.RemoveChild(characterPreviewFrame);
characterPreviewFrame = null;
}
}
public void SetPendingHires(List<int> characterInfos, Location location)
{
List<CharacterInfo> oldHires = PendingHires.ToList();
foreach (CharacterInfo pendingHire in oldHires)
{
RemovePendingHire(pendingHire, createNetworkMessage: false);
}
PendingHires.Clear();
foreach (int identifier in characterInfos)
{
CharacterInfo match = location.HireManager.AvailableCharacters.Find(info => info.GetIdentifier() == identifier);
if (match != null)
{
PendingHires.Add(match);
AddPendingHire(match, createNetworkMessage: false);
}
else
{
DebugConsole.ThrowError("Received a hire that doesn't exist.");
}
}
}
/// <summary>
/// Notify the server of crew changes
/// </summary>
/// <param name="updatePending">When set to true will tell the server to update the pending hires</param>
/// <param name="firedCharacter">When not null tell the server to fire this character</param>
/// <param name="validateHires">When set to true will tell the server to validate pending hires</param>
public void SendCrewState(bool updatePending, CharacterInfo firedCharacter = null, bool validateHires = false)
{
if (campaign is MultiPlayerCampaign)
{
IWriteMessage msg = new WriteOnlyMessage();
msg.Write((byte)ClientPacketHeader.CREW);
msg.Write(updatePending);
if (updatePending)
{
msg.Write((ushort)PendingHires.Count);
foreach (CharacterInfo pendingHire in PendingHires)
{
msg.Write(pendingHire.GetIdentifier());
}
}
msg.Write(validateHires);
msg.Write(firedCharacter != null);
if (firedCharacter != null)
{
msg.Write(firedCharacter.GetIdentifier());
}
GameMain.Client.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
}
}
private string FormatCurrency(int currency) => TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", currency));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
@@ -145,6 +146,11 @@ namespace Barotrauma
textBlock.ToolTip = value;
}
}
public bool Pulse { get; set; }
private float pulseTimer;
private float pulseExpand;
private bool flashed;
public GUIButton(RectTransform rectT, string text = "", Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : base(style, rectT)
{
@@ -196,7 +202,14 @@ namespace Barotrauma
protected override void Draw(SpriteBatch spriteBatch)
{
//do nothing
if (Pulse && pulseTimer > 1.0f)
{
Rectangle expandRect = Rect;
float expand = (pulseExpand * 20.0f) * GUI.Scale;
expandRect.Inflate(expand, expand);
GUI.Style.ButtonPulse.Draw(spriteBatch, expandRect, ToolBox.GradientLerp(pulseExpand, Color.White, Color.White, Color.Transparent));
}
}
protected override void Update(float deltaTime)
@@ -244,13 +257,41 @@ namespace Barotrauma
}
else
{
State = Selected ? ComponentState.Selected : ComponentState.None;
if (!ExternalHighlight)
{
State = Selected ? ComponentState.Selected : ComponentState.None;
}
else
{
State = ComponentState.Hover;
}
}
foreach (GUIComponent child in Children)
{
child.State = State;
}
if (Pulse)
{
pulseTimer += deltaTime;
if (pulseTimer > 1.0f)
{
if (!flashed)
{
flashed = true;
Frame.Flash(Color.White * 0.2f, 0.8f, true);
}
pulseExpand += deltaTime;
if (pulseExpand > 1.0f)
{
pulseTimer = 0.0f;
pulseExpand = 0.0f;
flashed = false;
}
}
}
}
}
}

View File

@@ -20,7 +20,7 @@ namespace Barotrauma
_instance = new GUICanvas();
if (GameMain.Instance != null)
{
GameMain.Instance.OnResolutionChanged += RecalculateSize;
GameMain.Instance.ResolutionChanged += RecalculateSize;
}
_instance.ItemComponentHolder = new GUIFrame(new RectTransform(Vector2.One, _instance, Anchor.Center)).RectTransform;
}

View File

@@ -11,12 +11,16 @@ using System.Net;
namespace Barotrauma
{
public enum SlideDirection { Up, Down, Left, Right }
public abstract class GUIComponent
{
#region Hierarchy
public GUIComponent Parent => RectTransform.Parent?.GUIComponent;
public CursorState HoverCursor = CursorState.Default;
public bool AlwaysOverrideCursor = false;
public delegate bool SecondaryButtonDownHandler(GUIComponent component, object userData);
public SecondaryButtonDownHandler OnSecondaryClicked;
@@ -75,7 +79,7 @@ namespace Barotrauma
public virtual void RemoveChild(GUIComponent child)
{
if (child == null) return;
if (child == null) { return; }
child.RectTransform.Parent = null;
}
@@ -139,6 +143,11 @@ namespace Barotrauma
public bool AutoUpdate { get; set; } = true;
public bool AutoDraw { get; set; } = true;
public int UpdateOrder { get; set; }
public bool Bounce { get; set; }
private float bounceTimer;
private float bounceJump;
private bool bounceDown;
public Action<GUIComponent> OnAddedToGUIUpdateList;
@@ -158,6 +167,8 @@ namespace Barotrauma
protected Color disabledColor;
protected Color pressedColor;
public bool GlowOnSelect { get; set; }
private CoroutineHandle pulsateCoroutine;
protected Color flashColor;
@@ -326,6 +337,11 @@ namespace Barotrauma
{
get { return RectTransform.CountChildren; }
}
/// <summary>
/// Currently only used for the fade effect in GUIListBox, should be set to the same value as Color but only assigned once
/// </summary>
public Color DefaultColor { get; set; }
public virtual Color Color
{
@@ -462,6 +478,36 @@ namespace Barotrauma
OnSecondaryClicked?.Invoke(this, userData);
}
}
if (Bounce)
{
if (bounceTimer > 3.0f || bounceDown)
{
RectTransform.ScreenSpaceOffset = new Point(RectTransform.ScreenSpaceOffset.X, (int) -(bounceJump * 10f));
if (!bounceDown)
{
bounceJump += deltaTime * 4;
if (bounceJump > 0.5f)
{
bounceDown = true;
}
}
else
{
bounceJump -= deltaTime * 4;
if (bounceJump <= 0.0f)
{
bounceJump = 0.0f;
bounceTimer = 0.0f;
bounceDown = false;
}
}
}
else
{
bounceTimer += deltaTime;
}
}
if (flashTimer > 0.0f)
{
@@ -526,12 +572,14 @@ namespace Barotrauma
protected virtual Color GetColor(ComponentState state)
{
if (!Enabled) { return DisabledColor; }
if (ExternalHighlight) { return HoverColor; }
return state switch
{
ComponentState.Hover => HoverColor,
ComponentState.HoverSelected => HoverColor,
ComponentState.Pressed => PressedColor,
ComponentState.Selected => SelectedColor,
ComponentState.Selected when !GlowOnSelect => SelectedColor,
_ => Color,
};
}
@@ -619,6 +667,11 @@ namespace Barotrauma
}
}
if (GlowOnSelect && State == ComponentState.Selected)
{
GUI.UIGlow.Draw(spriteBatch, Rect, SelectedColor);
}
if (flashTimer > 0.0f)
{
//the number of flashes depends on the duration, 1 flash per 1 full second
@@ -690,6 +743,7 @@ namespace Barotrauma
protected virtual void SetAlpha(float a)
{
color = new Color(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, a);
hoverColor = new Color(hoverColor.R / 255.0f, hoverColor.G / 255.0f, hoverColor.B / 255.0f, a);;
}
public virtual void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectInflate = null)
@@ -707,10 +761,77 @@ namespace Barotrauma
CoroutineManager.StartCoroutine(LerpAlpha(0.0f, duration, removeAfter));
}
private IEnumerable<object> LerpAlpha(float to, float duration, bool removeAfter)
public void FadeIn(float wait, float duration)
{
SetAlpha(0.0f);
CoroutineManager.StartCoroutine(LerpAlpha(1.0f, duration, false, wait));
}
public void SlideIn(float wait, float duration, int amount, SlideDirection direction)
{
RectTransform.ScreenSpaceOffset = direction switch
{
SlideDirection.Up => new Point(0, amount),
SlideDirection.Down => new Point(0, -amount),
SlideDirection.Left => new Point(amount, 0),
SlideDirection.Right => new Point(-amount, 0),
_ => RectTransform.ScreenSpaceOffset
};
CoroutineManager.StartCoroutine(SlideToPosition(duration, wait, Vector2.Zero));
}
public void SlideOut(float duration, int amount, SlideDirection direction)
{
RectTransform.ScreenSpaceOffset = Point.Zero;
Vector2 targetPos = direction switch
{
SlideDirection.Up => new Vector2(0, amount),
SlideDirection.Down => new Vector2(0, -amount),
SlideDirection.Left => new Vector2(amount, 0),
SlideDirection.Right => new Vector2(-amount, 0),
_ => Vector2.Zero
};
CoroutineManager.StartCoroutine(SlideToPosition(duration, 0.0f, targetPos));
}
private IEnumerable<object> SlideToPosition(float duration, float wait, Vector2 target)
{
float t = 0.0f;
float startA = color.A;
var (startX, startY) = RectTransform.ScreenSpaceOffset.ToVector2();
var (endX, endY) = target;
while (t < wait)
{
t += CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
t = 0.0f;
while (t < duration)
{
t += CoroutineManager.DeltaTime;
RectTransform.ScreenSpaceOffset = new Point((int)MathHelper.Lerp(startX, endX, t / duration), (int)MathHelper.Lerp(startY, endY, t / duration));
yield return CoroutineStatus.Running;
}
RectTransform.ScreenSpaceOffset = new Point(0, 0);
yield return CoroutineStatus.Success;
}
private IEnumerable<object> LerpAlpha(float to, float duration, bool removeAfter, float wait = 0.0f)
{
State = ComponentState.None;
float t = 0.0f;
float startA = color.A / 255.0f;
while (t < wait)
{
t += CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
t = 0.0f;
while (t < duration)
{

View File

@@ -6,6 +6,8 @@ namespace Barotrauma
{
public class GUIFrame : GUIComponent
{
public int OutlineThickness { get; set; }
public GUIFrame(RectTransform rectT, string style = "", Color? color = null) : base(style, rectT)
{
Enabled = true;
@@ -26,7 +28,7 @@ namespace Barotrauma
if (OutlineColor != Color.Transparent)
{
GUI.DrawRectangle(spriteBatch, Rect, OutlineColor * (OutlineColor.A/255.0f), false);
GUI.DrawRectangle(spriteBatch, Rect, OutlineColor * (OutlineColor.A/255.0f), false, thickness: OutlineThickness);
}
}
}

View File

@@ -42,17 +42,32 @@ namespace Barotrauma
{
return crop;
}
set
}
public void SetCrop(bool state, bool center = true)
{
crop = state;
if (crop && sprite != null)
{
crop = value;
if (crop)
{
sourceRect.Width = Math.Min(sprite.SourceRect.Width, Rect.Width);
sourceRect.Height = Math.Min(sprite.SourceRect.Height, Rect.Height);
sourceRect.Width = Math.Min(sprite.SourceRect.Width, (int)(Rect.Width / Scale));
sourceRect.Height = Math.Min(sprite.SourceRect.Height, (int)(Rect.Height / Scale));
if (center)
{
sourceRect.X = (sprite.SourceRect.Width - sourceRect.Width) / 2;
sourceRect.Y = (sprite.SourceRect.Height - sourceRect.Height) / 2;
}
origin = sourceRect.Size.ToVector2() / 2;
}
else
{
origin = sprite == null ? Vector2.Zero : sprite.size / 2;
}
}
private Vector2 origin;
public float Scale
{
get;
@@ -72,7 +87,8 @@ namespace Barotrauma
{
if (sprite == value) return;
sprite = value;
sourceRect = sprite.SourceRect;
sourceRect = value == null ? Rectangle.Empty : value.SourceRect;
origin = value == null ? Vector2.Zero : value.size / 2;
if (scaleToFit) RecalculateScale();
}
}
@@ -134,7 +150,8 @@ namespace Barotrauma
{
loadingTextures = true;
loading = true;
TaskPool.Add(LoadTextureAsync(), (Task) =>
TaskPool.Add("LoadTextureAsync",
LoadTextureAsync(), (Task) =>
{
loading = false;
lazyLoaded = true;
@@ -178,7 +195,7 @@ namespace Barotrauma
}
else if (sprite?.Texture != null)
{
spriteBatch.Draw(sprite.Texture, Rect.Center.ToVector2(), sourceRect, currentColor * (currentColor.A / 255.0f), Rotation, sprite.size / 2,
spriteBatch.Draw(sprite.Texture, Rect.Center.ToVector2(), sourceRect, currentColor * (currentColor.A / 255.0f), Rotation, origin,
Scale, SpriteEffects, 0.0f);
}

View File

@@ -134,7 +134,7 @@ namespace Barotrauma
(RectTransform.Children.Count(c => !c.GUIComponent.IgnoreLayoutGroups) - 1) *
(absoluteSpacing + relativeSpacing * thisSize);
stretchFactor = totalSize <= 0.0f || minSize >= thisSize ?
stretchFactor = totalSize <= 0.0f || minSize >= thisSize || totalSize == minSize ?
1.0f :
(thisSize - minSize) / (totalSize - minSize);
}

View File

@@ -34,7 +34,7 @@ namespace Barotrauma
public GUIScrollBar ScrollBar { get; private set; }
private Dictionary<GUIComponent, bool> childVisible = new Dictionary<GUIComponent, bool>();
private int totalSize;
private bool childrenNeedsRecalculation;
private bool scrollBarNeedsRecalculation;
@@ -59,6 +59,37 @@ namespace Barotrauma
private bool useGridLayout;
private float targetScroll;
private GUIComponent pendingScroll;
public bool AllowMouseWheelScroll { get; set; } = true;
/// <summary>
/// Scrolls the list smoothly
/// </summary>
public bool SmoothScroll { get; set; }
/// <summary>
/// Whether to only allow scrolling from one element to the next when smooth scrolling is enabled
/// </summary>
public bool ClampScrollToElements { get; set; }
/// <summary>
/// When set to true elements at the bottom of the list are gradually faded
/// </summary>
public bool FadeElements { get; set; }
/// <summary>
/// Adds enough extra padding to the bottom so the end of the scroll will only contain the last element
/// </summary>
public bool PadBottom { get; set; }
/// <summary>
/// When set to true always selects the topmost item on the list
/// </summary>
public bool SelectTop { get; set; }
public bool UseGridLayout
{
get { return useGridLayout; }
@@ -189,10 +220,13 @@ namespace Barotrauma
private Point draggedReferenceOffset;
public GUIComponent DraggedElement => draggedElement;
private bool scheduledScroll = false;
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT)
{
HoverCursor = CursorState.Hand;
CanBeFocused = true;
selected = new List<GUIComponent>();
this.useMouseDownToSelect = useMouseDownToSelect;
@@ -363,6 +397,49 @@ namespace Barotrauma
}
}
}
/// <summary>
/// Scrolls the list to the specific element, currently only works when smooth scrolling and PadBottom are enabled.
/// </summary>
/// <param name="component"></param>
public void ScrollToElement(GUIComponent component)
{
GUI.PlayUISound(GUISoundType.Click);
List<GUIComponent> children = Content.Children.ToList();
int index = children.IndexOf(component);
if (index < 0) { return; }
targetScroll = MathHelper.Clamp(MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, (children.Count - 0.9f), index)), ScrollBar.MinValue, ScrollBar.MaxValue);
}
public void ScrollToEnd(float duration)
{
CoroutineManager.StartCoroutine(ScrollCoroutine());
IEnumerable<object> ScrollCoroutine()
{
if (BarSize >= 1.0f)
{
yield return CoroutineStatus.Success;
}
float t = 0.0f;
float startScroll = BarScroll * BarSize;
float distanceToTravel = ScrollBar.MaxValue - startScroll;
float progress = startScroll;
float speed = distanceToTravel / duration;
while (t < duration && !MathUtils.NearlyEqual(ScrollBar.MaxValue, progress))
{
t += CoroutineManager.DeltaTime;
progress += speed * CoroutineManager.DeltaTime;
BarScroll = progress;
yield return CoroutineStatus.Running;
}
yield return CoroutineStatus.Success;
}
}
private void UpdateChildrenRect()
{
@@ -404,6 +481,32 @@ namespace Barotrauma
}
}
if (SelectTop)
{
foreach (GUIComponent child in Content.Children)
{
child.CanBeFocused = !selected.Contains(child);
if (!child.CanBeFocused)
{
child.State = ComponentState.None;
}
}
}
if (SelectTop && Content.Children.Any() && pendingScroll == null)
{
GUIComponent component = Content.Children.FirstOrDefault(c => (c.Rect.Y - Content.Rect.Y) / (float)c.Rect.Height > -0.1f);
if (component != null && !selected.Contains(component))
{
int index = Content.Children.ToList().IndexOf(component);
if (index >= 0)
{
Select(index, false, false, takeKeyBoardFocus: true);
}
}
}
for (int i = 0; i < Content.CountChildren; i++)
{
var child = Content.RectTransform.GetChild(i)?.GUIComponent;
@@ -418,7 +521,16 @@ namespace Barotrauma
if (mouseDown)
{
Select(i, autoScroll: false);
if (SelectTop)
{
pendingScroll = child;
ScrollToElement(child);
Select(i, autoScroll: false, takeKeyBoardFocus: true);
}
else
{
Select(i, autoScroll: false, takeKeyBoardFocus: true);
}
}
if (CanDragElements && PlayerInput.LeftButtonDown() && GUI.MouseOn == child)
@@ -538,10 +650,93 @@ namespace Barotrauma
{
UpdateScrollBarSize();
}
if ((GUI.IsMouseOn(this) || GUI.IsMouseOn(ScrollBar)) && PlayerInput.ScrollWheelSpeed != 0)
if (FadeElements)
{
ScrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * BarSize;
foreach (var (component, _) in childVisible)
{
float lerp = 0;
float y = component.Rect.Y;
float contentY = Content.Rect.Y;
float height = component.Rect.Height;
if (y < Content.Rect.Y)
{
float distance = (contentY - y) / height;
lerp = distance;
}
float centerY = Content.Rect.Y + Content.Rect.Height / 2.0f;
if (y > centerY)
{
float distance = (y - centerY) / (centerY - height);
lerp = distance;
}
component.Color = component.HoverColor = ToolBox.GradientLerp(lerp, component.DefaultColor, Color.Transparent);
component.DisabledColor = ToolBox.GradientLerp(lerp, component.Style.DisabledColor, Color.Transparent);
component.HoverColor = ToolBox.GradientLerp(lerp, component.Style.HoverColor, Color.Transparent);
foreach (var child in component.GetAllChildren())
{
Color gradient = ToolBox.GradientLerp(lerp, child.DefaultColor, Color.Transparent);
child.Color = child.HoverColor = gradient;
if (child is GUITextBlock block)
{
block.TextColor = block.HoverTextColor = gradient;
}
}
}
}
if (SmoothScroll)
{
if (targetScroll > -1)
{
float distance = Math.Abs(targetScroll - BarScroll);
float speed = Math.Max(distance * BarSize, 0.1f);
BarScroll = (1.0f - speed) * BarScroll + speed * targetScroll;
if (MathUtils.NearlyEqual(BarScroll, targetScroll) || GUIScrollBar.DraggingBar != null)
{
targetScroll = -1;
pendingScroll = null;
}
}
}
if ((GUI.IsMouseOn(this) || GUI.IsMouseOn(ScrollBar)) && AllowMouseWheelScroll && PlayerInput.ScrollWheelSpeed != 0)
{
float speed = PlayerInput.ScrollWheelSpeed / 500.0f * BarSize;
if (SmoothScroll)
{
if (ClampScrollToElements)
{
bool scrollDown = Math.Clamp(PlayerInput.ScrollWheelSpeed, 0, 1) > 0;
if (scrollDown)
{
SelectPrevious(takeKeyBoardFocus: true);
}
else
{
SelectNext(takeKeyBoardFocus: true);
}
}
else
{
pendingScroll = null;
if (targetScroll < 0) { targetScroll = BarScroll; }
targetScroll -= speed;
targetScroll = Math.Clamp(targetScroll, ScrollBar.MinValue, ScrollBar.MaxValue);
}
}
else
{
ScrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * BarSize;
}
}
ScrollBar.Enabled = ScrollBarEnabled && BarSize < 1.0f;
if (AutoHideScrollBar)
{
@@ -553,35 +748,47 @@ namespace Barotrauma
}
}
public void SelectNext(bool force = false, bool autoScroll = true)
public void SelectNext(bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
{
int index = SelectedIndex + 1;
while (index < Content.CountChildren)
{
if (Content.GetChild(index).Visible)
GUIComponent child = Content.GetChild(index);
if (child.Visible)
{
Select(index, force, autoScroll);
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
if (SmoothScroll)
{
pendingScroll = child;
ScrollToElement(child);
}
break;
}
index++;
}
}
public void SelectPrevious(bool force = false, bool autoScroll = true)
public void SelectPrevious(bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
{
int index = SelectedIndex - 1;
while (index >= 0)
{
if (Content.GetChild(index).Visible)
GUIComponent child = Content.GetChild(index);
if (child.Visible)
{
Select(index, force, autoScroll);
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
if (SmoothScroll)
{
pendingScroll = child;
ScrollToElement(child);
}
break;
}
index--;
}
}
public void Select(int childIndex, bool force = false, bool autoScroll = true)
public void Select(int childIndex, bool force = false, bool autoScroll = true, bool takeKeyBoardFocus = false)
{
if (childIndex >= Content.CountChildren || childIndex < 0) { return; }
@@ -646,7 +853,7 @@ namespace Barotrauma
}
// If one of the children is the subscriber, we don't want to register, because it will unregister the child.
if (RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
if (takeKeyBoardFocus && RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
{
Selected = true;
GUI.KeyboardDispatcher.Subscriber = this;
@@ -712,6 +919,14 @@ namespace Barotrauma
totalSize += (ScrollBar.IsHorizontal) ? child.Rect.Width : child.Rect.Height;
}
totalSize += Content.CountChildren * Spacing;
if (PadBottom)
{
GUIComponent last = Content.Children.LastOrDefault();
if (last != null)
{
totalSize += Rect.Height - last.Rect.Height;
}
}
}
float minScrollBarSize = 20.0f;

View File

@@ -57,7 +57,7 @@ namespace Barotrauma
public GUIMessage(string text, Color color, float lifeTime, ScalableFont font = null)
{
coloredText = new ColoredText(text, color, false);
coloredText = new ColoredText(text, color, false, false);
this.lifeTime = lifeTime;
Timer = lifeTime;
@@ -69,7 +69,7 @@ namespace Barotrauma
public GUIMessage(string text, Color color, Vector2 worldPosition, Vector2 velocity, float lifeTime, Alignment textAlignment = Alignment.Center, ScalableFont font = null)
{
coloredText = new ColoredText(text, color, false);
coloredText = new ColoredText(text, color, false, false);
WorldSpace = true;
pos = worldPosition;
Timer = lifeTime;

View File

@@ -30,6 +30,7 @@ namespace Barotrauma
public GUITextBlock Header { get; private set; }
public GUITextBlock Text { get; private set; }
public string Tag { get; private set; }
public bool Closed { get; private set; }
public GUIImage Icon
{
@@ -47,9 +48,16 @@ namespace Barotrauma
}
}
private bool alwaysVisible;
public GUIImage BackgroundIcon { get; private set; }
private GUIImage newBackgroundIcon;
public bool AutoClose;
private readonly bool alwaysVisible;
private float openState;
private float iconState;
private bool iconSwitching;
private bool closing;
private Type type;
@@ -62,7 +70,7 @@ namespace Barotrauma
this.Buttons[0].OnClicked = Close;
}
public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null)
public GUIMessageBox(string headerText, string text, string[] buttons, Vector2? relativeSize = null, Point? minSize = null, Alignment textAlignment = Alignment.TopLeft, Type type = Type.Default, string tag = "", Sprite icon = null, string iconStyle = "", Sprite backgroundIcon = null)
: base(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: GUI.Style.GetComponentStyle("GUIMessageBox." + type) != null ? "GUIMessageBox." + type : "GUIMessageBox")
{
int width = (int)(DefaultWidth * (type == Type.Default ? 1.0f : 1.5f)), height = 0;
@@ -80,6 +88,15 @@ namespace Barotrauma
}
}
if (backgroundIcon != null)
{
BackgroundIcon = new GUIImage(new RectTransform(backgroundIcon.size.ToPoint(), RectTransform), backgroundIcon)
{
IgnoreLayoutGroups = true,
Color = Color.Transparent
};
}
InnerFrame = new GUIFrame(new RectTransform(new Point(width, height), RectTransform, type == Type.InGame ? Anchor.TopCenter : Anchor.Center) { IsFixedSize = false }, style: null);
GUI.Style.Apply(InnerFrame, "", this);
this.type = type;
@@ -145,6 +162,7 @@ namespace Barotrauma
InnerFrame.RectTransform.AbsoluteOffset = new Point(0, GameMain.GraphicsHeight);
alwaysVisible = true;
CanBeFocused = false;
AutoClose = true;
GUI.Style.Apply(InnerFrame, "", this);
var horizontalLayoutGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.98f, 0.95f), InnerFrame.RectTransform, Anchor.Center),
@@ -157,6 +175,10 @@ namespace Barotrauma
{
Icon = new GUIImage(new RectTransform(new Vector2(0.2f, 0.95f), horizontalLayoutGroup.RectTransform), icon, scaleToFit: true);
}
else if (iconStyle != string.Empty)
{
Icon = new GUIImage(new RectTransform(new Vector2(0.2f, 0.95f), horizontalLayoutGroup.RectTransform), iconStyle, scaleToFit: true);
}
Content = new GUILayoutGroup(new RectTransform(new Vector2(icon != null ? 0.65f : 0.85f, 1.0f), horizontalLayoutGroup.RectTransform));
@@ -182,6 +204,10 @@ namespace Barotrauma
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
new Point(Text.Rect.Width, Text.Rect.Height);
Text.RectTransform.IsFixedSize = true;
if (string.IsNullOrWhiteSpace(headerText))
{
Content.ChildAnchor = Anchor.Center;
}
}
if (height == 0)
@@ -226,6 +252,23 @@ namespace Barotrauma
}
}
public void SetBackgroundIcon(Sprite icon)
{
if (icon == null) { return; }
GUIImage newIcon = new GUIImage(new RectTransform(icon.size.ToPoint(), RectTransform), icon)
{
IgnoreLayoutGroups = true,
Color = Color.Transparent
};
if (newBackgroundIcon != null)
{
RemoveChild(newBackgroundIcon);
newBackgroundIcon = null;
}
newBackgroundIcon = newIcon;
}
protected override void Update(float deltaTime)
{
if (type == Type.InGame)
@@ -246,10 +289,19 @@ namespace Barotrauma
if (!closing)
{
InnerFrame.RectTransform.AbsoluteOffset = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
Point step = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (BackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - BackgroundIcon.Rect.Size.Y / 2);
if (!MathUtils.NearlyEqual(openState, 1.0f))
{
BackgroundIcon.Color = ToolBox.GradientLerp(openState, Color.Transparent, Color.White);
}
}
openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn))
if (GUI.MouseOn != InnerFrame && !InnerFrame.IsParentOf(GUI.MouseOn) && AutoClose)
{
inGameCloseTimer += deltaTime;
}
@@ -262,13 +314,55 @@ namespace Barotrauma
else
{
openState += deltaTime * 2.0f;
InnerFrame.RectTransform.AbsoluteOffset = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
Point step = Vector2.SmoothStep(defaultPos, endPos, openState - 1.0f).ToPoint();
InnerFrame.RectTransform.AbsoluteOffset = step;
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
}
if (openState >= 2.0f)
{
if (Parent != null) { Parent.RemoveChild(this); }
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
}
if (newBackgroundIcon != null)
{
if (!iconSwitching)
{
if (BackgroundIcon != null)
{
BackgroundIcon.Color *= 0.9f;
if (BackgroundIcon.Color.A == 0)
{
BackgroundIcon = null;
iconSwitching = true;
RemoveChild(BackgroundIcon);
}
}
else
{
iconSwitching = true;
}
iconState = 0;
}
else
{
newBackgroundIcon.SetAsFirstChild();
newBackgroundIcon.RectTransform.AbsoluteOffset = new Point(InnerFrame.Rect.Location.X - (int) (newBackgroundIcon.Rect.Size.X / 1.25f), (int)defaultPos.Y - newBackgroundIcon.Rect.Size.Y / 2);
newBackgroundIcon.Color = ToolBox.GradientLerp(iconState, Color.Transparent, Color.White);
if (newBackgroundIcon.Color.A == 255)
{
BackgroundIcon = newBackgroundIcon;
BackgroundIcon.SetAsFirstChild();
newBackgroundIcon = null;
iconSwitching = false;
}
iconState = Math.Min(iconState + deltaTime * 2.0f, 1.0f);
}
}
}
}
@@ -284,6 +378,8 @@ namespace Barotrauma
if (Parent != null) { Parent.RemoveChild(this); }
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
Closed = true;
}
public bool Close(GUIButton button, object obj)

View File

@@ -179,7 +179,7 @@ namespace Barotrauma
private float pressedDelay = 0.5f;
private bool IsPressedTimerRunning { get { return pressedTimer > 0; } }
public GUINumberInput(RectTransform rectT, NumberType inputType, string style = "", Alignment textAlignment = Alignment.Center, float? relativeButtonAreaWidth = null) : base(style, rectT)
public GUINumberInput(RectTransform rectT, NumberType inputType, string style = "", Alignment textAlignment = Alignment.Center, float? relativeButtonAreaWidth = null, bool hidePlusMinusButtons = false) : base(style, rectT)
{
LayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, rectT), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
@@ -235,7 +235,7 @@ namespace Barotrauma
return true;
};
if (inputType != NumberType.Int)
if (inputType != NumberType.Int || hidePlusMinusButtons)
{
HidePlusMinusButtons();
}

View File

@@ -121,7 +121,7 @@ namespace Barotrauma
(int)(Rect.Width - style.Padding.X + style.Padding.Z),
(int)(Rect.Height - style.Padding.Y + style.Padding.W));
frame.Visible = showFrame;
slider.Visible = true;
slider.Visible = BarSize > 0.0f;
if (showFrame)
{

View File

@@ -37,6 +37,8 @@ namespace Barotrauma
public UISprite UIGlow { get; private set; }
public UISprite UIGlowCircular { get; private set; }
public UISprite ButtonPulse { get; private set; }
public SpriteSheet FocusIndicator { get; private set; }
/// <summary>
@@ -206,6 +208,9 @@ namespace Barotrauma
case "uiglowcircular":
UIGlowCircular = new UISprite(subElement);
break;
case "endroundbuttonpulse":
ButtonPulse = new UISprite(subElement);
break;
case "focusindicator":
FocusIndicator = new SpriteSheet(subElement);
break;
@@ -255,7 +260,8 @@ namespace Barotrauma
DebugConsole.NewMessage("Global font not defined in the current UI style file. The global font is used to render western symbols when using Chinese/Japanese/Korean localization. Using default font instead...", Color.Orange);
}
GameMain.Instance.OnResolutionChanged += () => { RescaleElements(); };
// TODO: Needs to unregister if we ever remove GUIStyles.
GameMain.Instance.ResolutionChanged += RescaleElements;
}
/// <summary>

View File

@@ -271,6 +271,8 @@ namespace Barotrauma
public OnClickDelegate OnClick;
}
public List<ClickableArea> ClickableAreas { get; private set; } = new List<ClickableArea>();
public bool Shadow { get; set; }
/// <summary>
/// This is the new constructor.
@@ -320,10 +322,10 @@ namespace Barotrauma
hasColorHighlight = richTextData != null;
}
public void CalculateHeightFromText(int padding = 0)
public void CalculateHeightFromText(int padding = 0, bool removeExtraSpacing = false)
{
if (wrappedText == null) { return; }
RectTransform.Resize(new Point(RectTransform.Rect.Width, (int)Font.MeasureString(wrappedText).Y + padding));
RectTransform.Resize(new Point(RectTransform.Rect.Width, (int)Font.MeasureString(wrappedText, removeExtraSpacing).Y + padding));
}
public override void ApplyStyle(GUIComponentStyle componentStyle)
@@ -443,8 +445,8 @@ namespace Barotrauma
protected override void SetAlpha(float a)
{
base.SetAlpha(a);
textColor = new Color(textColor.R, textColor.G, textColor.B, a);
// base.SetAlpha(a);
textColor = new Color(TextColor.R / 255.0f, TextColor.G / 255.0f, TextColor.B / 255.0f, a);
}
/// <summary>
@@ -626,12 +628,17 @@ namespace Barotrauma
if (!hasColorHighlight)
{
Font.DrawString(spriteBatch,
Censor ? censoredText : (Wrap ? wrappedText : text),
pos,
currentTextColor * (currentTextColor.A / 255.0f),
0.0f, origin, TextScale,
SpriteEffects.None, textDepth);
string textToShow = Censor ? censoredText : (Wrap ? wrappedText : text);
Color colorToShow = currentTextColor * (currentTextColor.A / 255.0f);
if (Shadow)
{
Vector2 shadowOffset = new Vector2(GUI.IntScale(2));
Font.DrawString(spriteBatch, textToShow, pos + shadowOffset, Color.Black, 0.0f, origin, TextScale, SpriteEffects.None, textDepth);
}
Font.DrawString(spriteBatch, textToShow, pos, colorToShow, 0.0f, origin, TextScale, SpriteEffects.None, textDepth);
}
else
{

View File

@@ -134,6 +134,10 @@ namespace Barotrauma
{
textBlock.OverflowClip = value != null;
maxTextLength = value;
if (Text.Length > MaxTextLength)
{
SetText(textBlock.Text.Substring(0, (int)maxTextLength));
}
}
}
@@ -360,6 +364,7 @@ namespace Barotrauma
}
else
{
CaretIndex = Math.Min(CaretIndex, textDrawn.Length);
textDrawn = Censor ? textBlock.CensoredText : textBlock.Text;
Vector2 textSize = Font.MeasureString(textDrawn.Substring(0, CaretIndex));
caretPos = new Vector2(textSize.X, 0) + textBlock.TextPos - textBlock.Origin;

View File

@@ -75,6 +75,11 @@ namespace Barotrauma
get; private set;
}
public static Rectangle VotingArea
{
get; private set;
}
public static int Padding
{
get; private set;
@@ -84,7 +89,7 @@ namespace Barotrauma
{
if (GameMain.Instance != null)
{
GameMain.Instance.OnResolutionChanged += CreateAreas;
GameMain.Instance.ResolutionChanged += CreateAreas;
GameMain.Config.OnHUDScaleChanged += CreateAreas;
CreateAreas();
CharacterInfo.Init();
@@ -144,6 +149,13 @@ namespace Barotrauma
int healthWindowY = GameMain.GraphicsHeight / 2 - healthWindowHeight / 2;
HealthWindowAreaLeft = new Rectangle(healthWindowX, healthWindowY, healthWindowWidth, healthWindowHeight);
int votingAreaWidth = (int)(400 * GUI.Scale);
int votingAreaX = GameMain.GraphicsWidth - Padding - votingAreaWidth;
int votingAreaY = Padding + ButtonAreaTop.Height;
// Height is based on text content
VotingArea = new Rectangle(votingAreaX, votingAreaY, votingAreaWidth, 0);
}
public static void Draw(SpriteBatch spriteBatch)

View File

@@ -165,7 +165,7 @@ namespace Barotrauma
float noiseStrength = (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0);
float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 4.0f;
noiseSprite.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight),
startOffset: new Point(Rand.Range(0, noiseSprite.SourceRect.Width), Rand.Range(0, noiseSprite.SourceRect.Height)),
startOffset: new Vector2(Rand.Range(0.0f, noiseSprite.SourceRect.Width), Rand.Range(0.0f, noiseSprite.SourceRect.Height)),
color: Color.White * noiseStrength * 0.1f,
textureScale: Vector2.One * noiseScale);
@@ -185,7 +185,7 @@ namespace Barotrauma
if (LoadState == 100.0f)
{
#if DEBUG
if (GameMain.Config.AutomaticQuickStartEnabled && GameMain.FirstLoad)
if (GameMain.Config.AutomaticQuickStartEnabled || GameMain.Config.AutomaticCampaignLoadEnabled && GameMain.FirstLoad)
{
loadText = "QUICKSTARTING ...";
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,683 @@
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using System.Linq;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
class SubmarineSelection
{
private const int submarinesPerPage = 4;
private int currentPage = 1;
private int pageCount;
private bool transferService, purchaseService, initialized;
private int deliveryFee;
private string deliveryLocationName;
public GUIFrame GuiFrame;
private GUIFrame pageIndicatorHolder;
private GUICustomComponent selectedSubmarineIndicator;
private GUILayoutGroup submarineHorizontalGroup, submarineControlsGroup;
private GUIButton browseLeftButton, browseRightButton, confirmButton, confirmButtonAlt;
private GUIListBox specsFrame;
private GUIImage[] pageIndicators;
private GUITextBlock descriptionTextBlock;
private int selectionIndicatorThickness;
private GUIImage listBackground;
private List<SubmarineInfo> subsToShow;
private SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage];
private SubmarineInfo selectedSubmarine = null;
private string purchaseAndSwitchText, purchaseOnlyText, deliveryText, currentSubText, deliveryFeeText, priceText, switchText, missingPreviewText, currencyShorthandText, currencyLongText;
private RectTransform parent;
private Action closeAction;
private Sprite pageIndicator;
public static readonly string[] DeliveryTextVariables = new string[] { "[submarinename1]", "[location1]", "[location2]", "[submarinename2]", "[amount]", "[currencyname]" };
public static readonly string[] SwitchTextVariables = new string[] { "[submarinename1]", "[submarinename2]" };
public static readonly string[] PurchaseAndSwitchTextVariables = new string[] { "[submarinename1]", "[amount]", "[currencyname]", "[submarinename2]" };
public static readonly string[] PurchaseTextVariables = new string[] { "[submarinename]", "[amount]", "[currencyname]" };
private static readonly string[] notEnoughCreditsDeliveryTextVariables = new string[] { "[currencyname]", "[submarinename]", "[location1]", "[location2]" };
private static readonly string[] notEnoughCreditsPurchaseTextVariables = new string[] { "[currencyname]", "[submarinename]" };
private string[] messageBoxOptions;
public const int DeliveryFeePerDistanceTravelled = 1000;
public static bool ContentRefreshRequired = false;
private static readonly Color indicatorColor = new Color(112, 149, 129);
private Point createdForResolution;
private struct SubmarineDisplayContent
{
public GUIFrame background;
public GUIImage submarineImage;
public SubmarineInfo displayedSubmarine;
public GUITextBlock submarineName;
public GUITextBlock submarineClass;
public GUITextBlock submarineFee;
public GUIButton selectSubmarineButton;
public GUITextBlock middleTextBlock;
}
public SubmarineSelection(bool transfer, Action closeAction, RectTransform parent)
{
if (GameMain.GameSession.Campaign == null) return;
transferService = transfer;
purchaseService = !transfer;
this.parent = parent;
this.closeAction = closeAction;
subsToShow = new List<SubmarineInfo>();
if (GameMain.Client == null)
{
messageBoxOptions = new string[2] { TextManager.Get("Yes"), TextManager.Get("Cancel") };
}
else
{
messageBoxOptions = new string[2] { TextManager.Get("Yes") + " " + TextManager.Get("initiatevoting"), TextManager.Get("Cancel") };
}
if (Submarine.MainSub?.Info == null) return;
Initialize();
}
private void Initialize()
{
initialized = true;
if (transferService)
{
deliveryFee = CalculateDeliveryFee();
currentSubText = TextManager.Get("currentsub");
deliveryFeeText = TextManager.Get("deliveryfee");
deliveryText = TextManager.Get("requestdeliverybutton");
switchText = TextManager.Get("switchtosubmarinebutton");
}
else
{
purchaseAndSwitchText = TextManager.Get("purchaseandswitch");
purchaseOnlyText = TextManager.Get("purchase");
priceText = TextManager.Get("price");
}
currencyShorthandText = TextManager.Get("currencyformat");
currencyLongText = TextManager.Get("credit").ToLower();
UpdateSubmarines();
missingPreviewText = TextManager.Get("SubPreviewImageNotFound");
CreateGUI();
}
private int CalculateDeliveryFee()
{
int distanceToOutpost = GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
deliveryLocationName = endLocation.Name;
return DeliveryFeePerDistanceTravelled * distanceToOutpost;
}
private void CreateGUI()
{
createdForResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
GUILayoutGroup content;
GuiFrame = new GUIFrame(new RectTransform(new Vector2(0.75f, 0.7f), parent, Anchor.TopCenter, Pivot.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.02f) });
selectionIndicatorThickness = HUDLayoutSettings.Padding / 2;
GUIFrame background = new GUIFrame(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center), color: Color.Black * 0.9f)
{
CanBeFocused = false
};
content = new GUILayoutGroup(new RectTransform(new Point(background.Rect.Width - HUDLayoutSettings.Padding * 4, background.Rect.Height - HUDLayoutSettings.Padding * 4), background.RectTransform, Anchor.Center)) { AbsoluteSpacing = (int)(HUDLayoutSettings.Padding * 1.5f) };
GUITextBlock header = new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), content.RectTransform), transferService ? TextManager.Get("switchsubmarineheader") : TextManager.GetWithVariable("outpostshipyard", "[location]", GameMain.GameSession.Map.CurrentLocation.Name), font: GUI.LargeFont);
header.CalculateHeightFromText(0, true);
GUITextBlock credits = new GUITextBlock(new RectTransform(Vector2.One, header.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.CenterRight)
{
TextGetter = CampaignUI.GetMoney
};
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), content.RectTransform), style: "HorizontalLine");
GUILayoutGroup submarineContentGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.4f), content.RectTransform)) { AbsoluteSpacing = HUDLayoutSettings.Padding, Stretch = true };
submarineHorizontalGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), submarineContentGroup.RectTransform)) { IsHorizontal = true, AbsoluteSpacing = HUDLayoutSettings.Padding, Stretch = true };
submarineControlsGroup = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.1f), submarineContentGroup.RectTransform), true, Anchor.TopCenter);
GUILayoutGroup infoFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.4f), content.RectTransform)) { IsHorizontal = true, Stretch = true, AbsoluteSpacing = HUDLayoutSettings.Padding };
new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform), style: null, new Color(8, 13, 19)) { IgnoreLayoutGroups = true };
listBackground = new GUIImage(new RectTransform(new Vector2(0.59f, 1f), infoFrame.RectTransform, Anchor.CenterRight), style: null, true)
{
IgnoreLayoutGroups = true
};
new GUIListBox(new RectTransform(Vector2.One, infoFrame.RectTransform)) { IgnoreLayoutGroups = true, CanBeFocused = false };
specsFrame = new GUIListBox(new RectTransform(new Vector2(0.39f, 1f), infoFrame.RectTransform), style: null) { Spacing = GUI.IntScale(5), Padding = new Vector4(HUDLayoutSettings.Padding / 2f, HUDLayoutSettings.Padding, 0, 0) };
new GUIFrame(new RectTransform(new Vector2(0.02f, 0.8f), infoFrame.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.1f) }, style: "VerticalLine");
GUIListBox descriptionFrame = new GUIListBox(new RectTransform(new Vector2(0.59f, 1f), infoFrame.RectTransform), style: null) { Padding = new Vector4(HUDLayoutSettings.Padding / 2f, HUDLayoutSettings.Padding * 1.5f, HUDLayoutSettings.Padding * 1.5f, HUDLayoutSettings.Padding / 2f) };
descriptionTextBlock = new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionFrame.Content.RectTransform), string.Empty, font: GUI.Font, wrap: true) { CanBeFocused = false };
GUILayoutGroup buttonFrame = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.075f), content.RectTransform), childAnchor: Anchor.CenterRight) { IsHorizontal = true, AbsoluteSpacing = HUDLayoutSettings.Padding };
if (closeAction != null)
{
GUIButton closeButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), TextManager.Get("Close"), style: "GUIButtonFreeScale")
{
OnClicked = (button, userData) =>
{
closeAction();
return true;
}
};
}
if (purchaseService) confirmButtonAlt = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), purchaseOnlyText, style: "GUIButtonFreeScale");
confirmButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1f), buttonFrame.RectTransform), purchaseService ? purchaseAndSwitchText : deliveryFee > 0 ? deliveryText : switchText, style: "GUIButtonFreeScale");
SetConfirmButtonState(false);
pageIndicatorHolder = new GUIFrame(new RectTransform(new Vector2(1f, 1.5f), submarineControlsGroup.RectTransform), style: null);
pageIndicator = GUI.Style.GetComponentStyle("GUIPageIndicator").GetDefaultSprite();
UpdatePaging();
for (int i = 0; i < submarineDisplays.Length; i++)
{
SubmarineDisplayContent submarineDisplayElement = new SubmarineDisplayContent();
submarineDisplayElement.background = new GUIFrame(new RectTransform(new Vector2(1f / submarinesPerPage, 1f), submarineHorizontalGroup.RectTransform), style: null, new Color(8, 13, 19));
submarineDisplayElement.submarineImage = new GUIImage(new RectTransform(new Vector2(0.8f, 1f), submarineDisplayElement.background.RectTransform, Anchor.Center), null, true);
submarineDisplayElement.middleTextBlock = new GUITextBlock(new RectTransform(new Vector2(0.8f, 1f), submarineDisplayElement.background.RectTransform, Anchor.Center), string.Empty, textAlignment: Alignment.Center);
submarineDisplayElement.submarineName = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUI.SubHeadingFont);
submarineDisplayElement.submarineClass = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding + (int)GUI.Font.MeasureString(submarineDisplayElement.submarineName.Text).Y) }, string.Empty, textAlignment: Alignment.Center);
submarineDisplayElement.submarineFee = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.BottomCenter, Pivot.BottomCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUI.SubHeadingFont);
submarineDisplayElement.selectSubmarineButton = new GUIButton(new RectTransform(Vector2.One, submarineDisplayElement.background.RectTransform), style: null);
submarineDisplays[i] = submarineDisplayElement;
}
selectedSubmarineIndicator = new GUICustomComponent(new RectTransform(Point.Zero, submarineHorizontalGroup.RectTransform), onDraw: (sb, component) => DrawSubmarineIndicator(sb, component.Rect)) { IgnoreLayoutGroups = true, CanBeFocused = false };
}
private void UpdatePaging()
{
if (pageIndicatorHolder == null) return;
pageIndicatorHolder.ClearChildren();
if (currentPage > pageCount) currentPage = pageCount;
if (pageCount < 2) return;
browseLeftButton = new GUIButton(new RectTransform(new Vector2(1.15f, 1.15f), pageIndicatorHolder.RectTransform, Anchor.CenterLeft, Pivot.CenterRight) { AbsoluteOffset = new Point(-HUDLayoutSettings.Padding * 3, 0) }, string.Empty, style: "GUIButtonToggleLeft")
{
IgnoreLayoutGroups = true,
OnClicked = (button, userData) =>
{
ChangePage(-1);
return true;
}
};
Point indicatorSize = new Point(GUI.IntScale(pageIndicator.SourceRect.Width * 1.5f), GUI.IntScale(pageIndicator.SourceRect.Height * 1.5f));
pageIndicatorHolder.RectTransform.NonScaledSize = new Point(pageCount * indicatorSize.X + HUDLayoutSettings.Padding * (pageCount - 1), pageIndicatorHolder.RectTransform.NonScaledSize.Y);
int xPos = 0;
int yPos = pageIndicatorHolder.Rect.Height / 2 - indicatorSize.Y / 2;
pageIndicators = new GUIImage[pageCount];
for (int i = 0; i < pageCount; i++)
{
pageIndicators[i] = new GUIImage(new RectTransform(indicatorSize, pageIndicatorHolder.RectTransform) { AbsoluteOffset = new Point(xPos, yPos) }, pageIndicator, null, true);
xPos += indicatorSize.X + HUDLayoutSettings.Padding;
}
for (int i = 0; i < pageIndicators.Length; i++)
{
pageIndicators[i].Color = i == currentPage - 1 ? Color.White : Color.Gray;
}
browseRightButton = new GUIButton(new RectTransform(new Vector2(1.15f, 1.15f), pageIndicatorHolder.RectTransform, Anchor.CenterRight, Pivot.CenterLeft) { AbsoluteOffset = new Point(-HUDLayoutSettings.Padding * 3, 0) }, string.Empty, style: "GUIButtonToggleRight")
{
IgnoreLayoutGroups = true,
OnClicked = (button, userData) =>
{
ChangePage(1);
return true;
}
};
browseLeftButton.Enabled = currentPage > 1;
browseRightButton.Enabled = currentPage < pageCount;
}
private void DrawSubmarineIndicator(SpriteBatch spriteBatch, Rectangle area)
{
if (area == Rectangle.Empty) return;
GUI.DrawRectangle(spriteBatch, area, indicatorColor, thickness: selectionIndicatorThickness);
}
public void Update()
{
if (ContentRefreshRequired)
{
RefreshSubmarineDisplay(true);
}
// Input
if (PlayerInput.KeyHit(Keys.Left))
{
SelectSubmarine(subsToShow.IndexOf(selectedSubmarine), -1);
}
else if (PlayerInput.KeyHit(Keys.Right))
{
SelectSubmarine(subsToShow.IndexOf(selectedSubmarine), 1);
}
}
public void RefreshSubmarineDisplay(bool updateSubs)
{
if (!initialized) Initialize();
if (GameMain.GraphicsWidth != createdForResolution.X || GameMain.GraphicsHeight != createdForResolution.Y) CreateGUI();
if (updateSubs) UpdateSubmarines();
if (pageIndicators != null)
{
for (int i = 0; i < pageIndicators.Length; i++)
{
pageIndicators[i].Color = i == currentPage - 1 ? Color.White : Color.Gray;
}
}
int submarineIndex = (currentPage - 1) * submarinesPerPage;
for (int i = 0; i < submarineDisplays.Length; i++)
{
SubmarineInfo subToDisplay = GetSubToDisplay(submarineIndex);
if (subToDisplay == null)
{
submarineDisplays[i].submarineImage.Sprite = null;
submarineDisplays[i].submarineName.Text = string.Empty;
submarineDisplays[i].submarineFee.Text = string.Empty;
submarineDisplays[i].submarineClass.Text = string.Empty;
submarineDisplays[i].selectSubmarineButton.Enabled = false;
submarineDisplays[i].selectSubmarineButton.OnClicked = null;
submarineDisplays[i].displayedSubmarine = null;
submarineDisplays[i].middleTextBlock.AutoDraw = false;
}
else
{
submarineDisplays[i].displayedSubmarine = subToDisplay;
Sprite previewImage = GetPreviewImage(subToDisplay);
if (previewImage != null)
{
submarineDisplays[i].submarineImage.Sprite = previewImage;
submarineDisplays[i].middleTextBlock.AutoDraw = false;
}
else
{
submarineDisplays[i].submarineImage.Sprite = null;
submarineDisplays[i].middleTextBlock.Text = missingPreviewText;
submarineDisplays[i].middleTextBlock.AutoDraw = true;
}
submarineDisplays[i].selectSubmarineButton.Enabled = true;
int index = i;
submarineDisplays[i].selectSubmarineButton.OnClicked = (button, userData) =>
{
SelectSubmarine(subToDisplay, submarineDisplays[index].background.Rect);
return true;
};
submarineDisplays[i].submarineName.Text = subToDisplay.DisplayName;
submarineDisplays[i].submarineClass.Text = $"{TextManager.GetWithVariable("submarineclass.classsuffixformat", "[type]", TextManager.Get($"submarineclass.{subToDisplay.SubmarineClass}"))}";
if (!GameMain.GameSession.IsSubmarineOwned(subToDisplay))
{
string amountString = currencyShorthandText.Replace("[credits]", subToDisplay.Price.ToString());
submarineDisplays[i].submarineFee.Text = priceText.Replace("[amount]", amountString).Replace("[currencyname]", string.Empty).TrimEnd();
}
else
{
if (subToDisplay.Name != CurrentOrPendingSubmarine().Name)
{
if (deliveryFee > 0)
{
string amountString = currencyShorthandText.Replace("[credits]", deliveryFee.ToString());
submarineDisplays[i].submarineFee.Text = deliveryFeeText.Replace("[amount]", amountString).Replace("[currencyname]", string.Empty).TrimEnd();
}
else
{
submarineDisplays[i].submarineFee.Text = string.Empty;
}
}
else
{
submarineDisplays[i].submarineFee.Text = currentSubText;
}
}
if (transferService && subToDisplay.Name == CurrentOrPendingSubmarine().Name && updateSubs)
{
if (selectedSubmarine == null)
{
CoroutineManager.StartCoroutine(SelectOwnSubmarineWithDelay(subToDisplay, submarineDisplays[i]));
}
else
{
SelectSubmarine(subToDisplay, submarineDisplays[i].background.Rect);
}
}
else if (!transferService && selectedSubmarine == null || !transferService && GameMain.GameSession.IsSubmarineOwned(selectedSubmarine) || subToDisplay == selectedSubmarine)
{
SelectSubmarine(subToDisplay, submarineDisplays[i].background.Rect);
}
}
submarineIndex++;
}
if (subsToShow.Count == 0)
{
SelectSubmarine(null, Rectangle.Empty);
}
}
private void UpdateSubmarines()
{
subsToShow.Clear();
if (transferService)
{
subsToShow.AddRange(GameMain.GameSession.OwnedSubmarines);
subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass));
string currentSubName = CurrentOrPendingSubmarine().Name;
int currentIndex = subsToShow.FindIndex(s => s.Name == currentSubName);
if (currentIndex != -1)
{
currentPage = (int)Math.Ceiling((currentIndex + 1) / (float)submarinesPerPage);
}
}
else
{
if (GameMain.Client == null)
{
subsToShow.AddRange(SubmarineInfo.SavedSubmarines.Where(s => s.IsCampaignCompatible && !GameMain.GameSession.OwnedSubmarines.Any(os => os.Name == s.Name)));
}
else
{
subsToShow.AddRange(GameMain.NetLobbyScreen.CampaignSubmarines.Where(s => !GameMain.GameSession.OwnedSubmarines.Any(os => os.Name == s.Name)));
}
subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass));
}
if (transferService) SetConfirmButtonState(selectedSubmarine != null && selectedSubmarine.Name != CurrentOrPendingSubmarine().Name);
subsToShow.Sort((x, y) => x.SubmarineClass.CompareTo(y.SubmarineClass));
pageCount = Math.Max(1, (int)Math.Ceiling(subsToShow.Count / (float)submarinesPerPage));
UpdatePaging();
ContentRefreshRequired = false;
}
private SubmarineInfo GetSubToDisplay(int index)
{
if (subsToShow.Count <= index || index < 0) return null;
return subsToShow[index];
}
private Sprite GetPreviewImage(SubmarineInfo info)
{
Sprite preview = info.PreviewImage;
if (preview == null)
{
SubmarineInfo potentialMatch;
if (GameMain.Client == null)
{
potentialMatch = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.EqualityCheckVal == info.EqualityCheckVal);
}
else
{
potentialMatch = GameMain.NetLobbyScreen.CampaignSubmarines.FirstOrDefault(s => s.EqualityCheckVal == info.EqualityCheckVal);
}
preview = potentialMatch?.PreviewImage;
// Try from savedsubmarines with name comparison as a backup
if (preview == null)
{
potentialMatch = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == info.Name);
preview = potentialMatch?.PreviewImage;
}
}
return preview;
}
// Initial submarine selection needs a slight wait to allow the layoutgroups to place content properly
private IEnumerable<object> SelectOwnSubmarineWithDelay(SubmarineInfo info, SubmarineDisplayContent display)
{
yield return new WaitForSeconds(0.05f);
SelectSubmarine(info, display.background.Rect);
}
// Selection based on key input
private void SelectSubmarine(int index, int direction)
{
SubmarineInfo nextSub = GetSubToDisplay(index + direction);
if (nextSub == null) return;
for (int i = 0; i < submarineDisplays.Length; i++)
{
if (submarineDisplays[i].displayedSubmarine == nextSub)
{
SelectSubmarine(nextSub, submarineDisplays[i].background.Rect);
return;
}
}
ChangePage(direction);
for (int i = 0; i < submarineDisplays.Length; i++)
{
if (submarineDisplays[i].displayedSubmarine == nextSub)
{
SelectSubmarine(nextSub, submarineDisplays[i].background.Rect);
return;
}
}
}
private void SelectSubmarine(SubmarineInfo info, Rectangle backgroundRect)
{
#if !DEBUG
if (selectedSubmarine == info) return;
#endif
specsFrame.Content.ClearChildren();
selectedSubmarine = info;
if (info != null)
{
bool owned = GameMain.GameSession.IsSubmarineOwned(info);
if (owned)
{
confirmButton.Text = deliveryFee > 0 ? deliveryText : switchText;
confirmButton.OnClicked = (button, userData) =>
{
ShowTransferPrompt();
return true;
};
}
else
{
confirmButton.Text = purchaseAndSwitchText;
confirmButton.OnClicked = (button, userData) =>
{
ShowBuyPrompt(false);
return true;
};
confirmButtonAlt.Text = purchaseOnlyText;
confirmButtonAlt.OnClicked = (button, userData) =>
{
ShowBuyPrompt(true);
return true;
};
}
SetConfirmButtonState(selectedSubmarine.Name != CurrentOrPendingSubmarine().Name);
selectedSubmarineIndicator.RectTransform.NonScaledSize = backgroundRect.Size;
selectedSubmarineIndicator.RectTransform.AbsoluteOffset = new Point(backgroundRect.Left - submarineHorizontalGroup.Rect.Left, 0);
Sprite previewImage = GetPreviewImage(info);
listBackground.Sprite = previewImage;
listBackground.SetCrop(true);
ScalableFont font = GUI.Font;
info.CreateSpecsWindow(specsFrame, font);
descriptionTextBlock.Text = info.Description;
descriptionTextBlock.CalculateHeightFromText();
}
else
{
listBackground.Sprite = null;
listBackground.SetCrop(false);
descriptionTextBlock.Text = string.Empty;
selectedSubmarineIndicator.RectTransform.NonScaledSize = Point.Zero;
SetConfirmButtonState(false);
}
}
private void SetConfirmButtonState(bool state)
{
if (confirmButtonAlt != null)
{
confirmButtonAlt.Enabled = state;
}
if (confirmButton != null)
{
confirmButton.Enabled = state;
}
}
public static SubmarineInfo CurrentOrPendingSubmarine()
{
if (GameMain.GameSession?.Campaign?.PendingSubmarineSwitch == null)
{
return Submarine.MainSub.Info;
}
else
{
return GameMain.GameSession.Campaign.PendingSubmarineSwitch;
}
}
private void ChangePage(int pageChangeDirection)
{
SelectSubmarine(null, Rectangle.Empty);
if (pageChangeDirection < 0 && currentPage > 1) currentPage--;
if (pageChangeDirection > 0 && currentPage < pageCount) currentPage++;
browseLeftButton.Enabled = currentPage > 1;
browseRightButton.Enabled = currentPage < pageCount;
RefreshSubmarineDisplay(false);
}
private void ShowTransferPrompt()
{
if (GameMain.GameSession.Campaign.Money < deliveryFee && deliveryFee > 0)
{
new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("notenoughmoneyfordeliverytext", notEnoughCreditsDeliveryTextVariables,
new string[] { currencyLongText, selectedSubmarine.DisplayName, deliveryLocationName, GameMain.GameSession.Map.CurrentLocation.Name }));
return;
}
GUIMessageBox msgBox;
if (deliveryFee > 0)
{
msgBox = new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("deliveryrequesttext", DeliveryTextVariables,
new string[6] { selectedSubmarine.DisplayName, deliveryLocationName, GameMain.GameSession.Map.CurrentLocation.Name, CurrentOrPendingSubmarine().DisplayName, deliveryFee.ToString(), currencyLongText }), messageBoxOptions);
}
else
{
msgBox = new GUIMessageBox(TextManager.Get("switchsubmarineheader"), TextManager.GetWithVariables("switchsubmarinetext", SwitchTextVariables,
new string[2] { CurrentOrPendingSubmarine().DisplayName, selectedSubmarine.DisplayName }), messageBoxOptions);
}
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
if (GameMain.Client == null)
{
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(selectedSubmarine);
RefreshSubmarineDisplay(true);
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.SwitchSub);
}
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
private void ShowBuyPrompt(bool purchaseOnly)
{
if (GameMain.GameSession.Campaign.Money < selectedSubmarine.Price)
{
new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("notenoughmoneyforpurchasetext", notEnoughCreditsPurchaseTextVariables,
new string[2] { currencyLongText, selectedSubmarine.DisplayName }));
return;
}
GUIMessageBox msgBox;
if (!purchaseOnly)
{
msgBox = new GUIMessageBox(TextManager.Get("purchaseandswitchsubmarineheader"), TextManager.GetWithVariables("purchaseandswitchsubmarinetext", PurchaseAndSwitchTextVariables,
new string[4] { selectedSubmarine.DisplayName, selectedSubmarine.Price.ToString(), currencyLongText, CurrentOrPendingSubmarine().DisplayName }), messageBoxOptions);
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
if (GameMain.Client == null)
{
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(selectedSubmarine);
RefreshSubmarineDisplay(true);
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.PurchaseAndSwitchSub);
}
return true;
};
}
else
{
msgBox = new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("purchasesubmarinetext", PurchaseTextVariables,
new string[3] { selectedSubmarine.DisplayName, selectedSubmarine.Price.ToString(), currencyLongText }), messageBoxOptions);
msgBox.Buttons[0].OnClicked = (applyButton, obj) =>
{
if (GameMain.Client == null)
{
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
RefreshSubmarineDisplay(true);
}
else
{
GameMain.Client.InitiateSubmarineChange(selectedSubmarine, Networking.VoteType.PurchaseSub);
}
return true;
};
}
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = msgBox.Close;
}
}
}

View File

@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
using Barotrauma.Networking;
using System.Globalization;
namespace Barotrauma
{
@@ -13,7 +14,7 @@ namespace Barotrauma
private static bool initialized = false;
private static UISprite spectateIcon, deadIcon, disconnectedIcon;
private static UISprite spectateIcon, disconnectedIcon;
private static Sprite ownerIcon, moderatorIcon;
private enum InfoFrameTab { Crew, Mission, MyCharacter, Traitor };
@@ -31,7 +32,7 @@ namespace Barotrauma
private List<Character.TeamType> teamIDs;
private const string inLobbyString = "\u2022 \u2022 \u2022";
private static Color ownCharacterBGColor = Color.Gold * 0.7f;
public static Color OwnCharacterBGColor = Color.Gold * 0.7f;
private class LinkedGUI
{
@@ -115,10 +116,9 @@ namespace Barotrauma
public void Initialize()
{
spectateIcon = GUI.Style.GetComponentStyle("SpectateIcon").Sprites[GUIComponent.ComponentState.None][0];
deadIcon = GUI.Style.GetComponentStyle("DeadIcon").Sprites[GUIComponent.ComponentState.None][0];
disconnectedIcon = GUI.Style.GetComponentStyle("DisconnectedIcon").Sprites[GUIComponent.ComponentState.None][0];
ownerIcon = GUI.Style.GetComponentStyle("OwnerIcon").Sprites[GUIComponent.ComponentState.None][0].Sprite;
moderatorIcon = GUI.Style.GetComponentStyle("ModeratorIcon").Sprites[GUIComponent.ComponentState.None][0].Sprite;
ownerIcon = GUI.Style.GetComponentStyle("OwnerIcon").GetDefaultSprite();
moderatorIcon = GUI.Style.GetComponentStyle("ModeratorIcon").GetDefaultSprite();
initialized = true;
}
@@ -279,7 +279,7 @@ namespace Barotrauma
teamIDs = crew.Select(c => c.TeamID).Distinct().ToList();
// Show own team first when there's more than one team
if (teamIDs.Count > 1 && GameMain.Client.Character != null)
if (teamIDs.Count > 1 && GameMain.Client?.Character != null)
{
Character.TeamType ownTeam = GameMain.Client.Character.TeamID;
teamIDs = teamIDs.OrderBy(i => i != ownTeam).ThenBy(i => i).ToList();
@@ -408,7 +408,7 @@ namespace Barotrauma
GUIFrame frame = new GUIFrame(new RectTransform(new Point(crewListArray[i].Content.Rect.Width, GUI.IntScale(33f)), crewListArray[i].Content.RectTransform), style: "ListBoxElement")
{
UserData = character,
Color = (Character.Controlled == character) ? ownCharacterBGColor : Color.Transparent
Color = (Character.Controlled == character) ? OwnCharacterBGColor : Color.Transparent
};
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
@@ -486,7 +486,7 @@ namespace Barotrauma
GUIFrame frame = new GUIFrame(new RectTransform(new Point(crewListArray[i].Content.Rect.Width, GUI.IntScale(33f)), crewListArray[i].Content.RectTransform), style: "ListBoxElement")
{
UserData = character,
Color = (GameMain.NetworkMember != null && GameMain.Client.Character == character) ? ownCharacterBGColor : Color.Transparent
Color = (GameMain.NetworkMember != null && GameMain.Client.Character == character) ? OwnCharacterBGColor : Color.Transparent
};
frame.OnSecondaryClicked += (component, data) =>
@@ -631,7 +631,7 @@ namespace Barotrauma
{
if (GameMain.NetworkMember == null || client == null || !client.HasPermissions) return null;
if (!client.AllowKicking) // Owner cannot be kicked
if (client.IsOwner) // Owner cannot be kicked
{
return ownerIcon;
}
@@ -649,7 +649,10 @@ namespace Barotrauma
}
else if (client.Character != null && client.Character.IsDead)
{
client.Character.Info.DrawJobIcon(spriteBatch, area);
if (client.Character.Info != null)
{
client.Character.Info.DrawJobIcon(spriteBatch, area);
}
}
else
{
@@ -780,7 +783,7 @@ namespace Barotrauma
public static void StorePlayerConnectionChangeMessage(ChatMessage message)
{
if (!GameMain.GameSession?.GameMode?.IsRunning ?? true) { return; }
if (!GameMain.GameSession?.IsRunning ?? true) { return; }
string msg = ChatMessage.GetTimeStamp() + message.TextWithSender;
storedMessages.Add(new Pair<string, PlayerConnectionChangeType>(msg, message.ChangeType));
@@ -847,15 +850,15 @@ namespace Barotrauma
infoFrame.ClearChildren();
GUIFrame missionFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
int padding = (int)(0.0245f * missionFrame.Rect.Height);
Location endLocation = GameMain.GameSession.EndLocation;
Sprite portrait = endLocation.Type.GetPortrait(endLocation.PortraitId);
Location location = GameMain.GameSession.EndLocation != null ? GameMain.GameSession.EndLocation : GameMain.GameSession.StartLocation;
Sprite portrait = location.Type.GetPortrait(location.PortraitId);
bool hasPortrait = portrait != null && portrait.SourceRect.Width > 0 && portrait.SourceRect.Height > 0;
int contentWidth = hasPortrait ? (int)(missionFrame.Rect.Width * 0.951f) : missionFrame.Rect.Width - padding * 2;
Vector2 locationNameSize = GUI.LargeFont.MeasureString(endLocation.Name);
Vector2 locationTypeSize = GUI.SubHeadingFont.MeasureString(endLocation.Name);
GUITextBlock locationNameText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationNameSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, padding) }, endLocation.Name, font: GUI.LargeFont);
GUITextBlock locationTypeText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationTypeSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationNameText.Rect.Height + padding) }, endLocation.Type.Name, font: GUI.SubHeadingFont);
Vector2 locationNameSize = GUI.LargeFont.MeasureString(location.Name);
Vector2 locationTypeSize = GUI.SubHeadingFont.MeasureString(location.Name);
GUITextBlock locationNameText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationNameSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, padding) }, location.Name, font: GUI.LargeFont);
GUITextBlock locationTypeText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationTypeSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationNameText.Rect.Height + padding) }, location.Type.Name, font: GUI.SubHeadingFont);
int locationInfoYOffset = locationNameText.Rect.Height + locationTypeText.Rect.Height + padding * 2;
@@ -881,7 +884,8 @@ namespace Barotrauma
string missionNameString = ToolBox.WrapText(mission.Name, missionTextGroup.Rect.Width, GUI.LargeFont);
string missionDescriptionString = ToolBox.WrapText(mission.Description, missionTextGroup.Rect.Width, GUI.Font);
string missionRewardString = ToolBox.WrapText(TextManager.GetWithVariable("MissionReward", "[reward]", mission.Reward.ToString()), missionTextGroup.Rect.Width, GUI.Font);
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", mission.Reward));
string missionRewardString = ToolBox.WrapText(TextManager.GetWithVariable("MissionReward", "[reward]", rewardText), missionTextGroup.Rect.Width, GUI.Font);
Vector2 missionNameSize = GUI.LargeFont.MeasureString(missionNameString);
Vector2 missionDescriptionSize = GUI.Font.MeasureString(missionDescriptionString);
@@ -890,13 +894,15 @@ namespace Barotrauma
missionDescriptionHolder.RectTransform.NonScaledSize = new Point(missionDescriptionHolder.RectTransform.NonScaledSize.X, (int)(missionNameSize.Y + missionDescriptionSize.Y + missionRewardSize.Y));
missionTextGroup.RectTransform.NonScaledSize = new Point(missionTextGroup.RectTransform.NonScaledSize.X, missionDescriptionHolder.RectTransform.NonScaledSize.Y);
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight);
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) { Color = mission.Prefab.IconColor };
if (mission.Prefab.Icon != null)
{
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight);
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true) { Color = mission.Prefab.IconColor };
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionNameString, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionRewardString);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextGroup.RectTransform), missionDescriptionString);

View File

@@ -36,6 +36,12 @@ namespace Barotrauma
get;
private set;
}
public bool MaintainBorderAspectRatio
{
get;
private set;
}
/// <summary>
/// How much the borders of a sliced sprite are allowed to scale
@@ -52,6 +58,7 @@ namespace Barotrauma
{
Sprite = new Sprite(element);
MaintainAspectRatio = element.GetAttributeBool("maintainaspectratio", false);
MaintainBorderAspectRatio = element.GetAttributeBool("maintainborderaspectratio", false);
Tile = element.GetAttributeBool("tile", true);
CrossFadeIn = element.GetAttributeBool("crossfadein", CrossFadeIn);
CrossFadeOut = element.GetAttributeBool("crossfadeout", CrossFadeOut);
@@ -120,13 +127,16 @@ namespace Barotrauma
{
Vector2 pos = new Vector2(rect.X, rect.Y);
float scale = GetSliceBorderScale(rect.Size);
float scale = MaintainBorderAspectRatio ? 1.0f : GetSliceBorderScale(rect.Size);
float aspectScale = MaintainBorderAspectRatio ? Math.Min((float)rect.Width / Sprite.SourceRect.Width, (float)rect.Height / Sprite.SourceRect.Height) : 1.0f;
int centerHeight = rect.Height - (int)((Slices[0].Height + Slices[6].Height) * scale);
int centerWidth = rect.Width - (int)((Slices[0].Width + Slices[2].Width) * scale);
int centerWidth = rect.Width - (int)((Slices[0].Width + Slices[2].Width) * scale * aspectScale);
for (int x = 0; x < 3; x++)
{
int width = (int)(x == 1 ? centerWidth : Slices[x].Width * scale);
int width = (int)(x == 1 ? centerWidth : Slices[x].Width * scale * aspectScale);
if (width <= 0) { continue; }
for (int y = 0; y < 3; y++)
{
@@ -147,7 +157,7 @@ namespace Barotrauma
else if (Tile)
{
Vector2 startPos = new Vector2(rect.X, rect.Y);
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), null, color);
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), color);
}
else
{

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,235 @@
using System;
using System.Collections.Generic;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
class VotingInterface
{
public bool VoteRunning = false;
private GUIFrame frame;
private GUITextBlock votingTextBlock, votedTextBlock, voteCounter;
private GUIProgressBar votingTimer;
private GUIButton yesVoteButton, noVoteButton;
private Action onVoteEnd;
private int yesVotes, noVotes, maxVotes;
private Func<int> getYesVotes, getNoVotes, getMaxVotes;
private bool votePassed;
private string votingOnText;
private List<RichTextData> votingOnTextData;
private float votingTime = 100f;
private float timer;
private VoteType currentVoteType;
private Color submarineColor => GUI.Style.Orange;
private Point createdForResolution;
public VotingInterface(Client starter, SubmarineInfo info, VoteType type, float votingTime)
{
if (starter == null || info == null) return;
SetSubmarineVotingText(starter, info, type);
this.votingTime = votingTime;
getYesVotes = SubmarineYesVotes;
getNoVotes = SubmarineNoVotes;
getMaxVotes = SubmarineMaxVotes;
onVoteEnd = () => SendSubmarineVoteEndMessage(info, type);
Initialize(starter, type);
}
private void Initialize(Client starter, VoteType type)
{
currentVoteType = type;
CreateVotingGUI();
if (starter.ID == GameMain.Client.ID) SetGUIToVotedState(2);
VoteRunning = true;
}
private void CreateVotingGUI()
{
createdForResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
if (frame != null) frame.Parent.RemoveChild(frame);
frame = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.VotingArea, GameMain.Client.InGameHUD.RectTransform), style: "");
int padding = HUDLayoutSettings.Padding * 2;
int spacing = HUDLayoutSettings.Padding;
int yOffset = padding;
int paddedWidth = frame.Rect.Width - padding * 2;
votingTextBlock = new GUITextBlock(new RectTransform(new Point(paddedWidth, 0), frame.RectTransform), votingOnTextData, votingOnText, wrap: true);
votingTextBlock.RectTransform.NonScaledSize = votingTextBlock.RectTransform.MinSize = votingTextBlock.RectTransform.MaxSize = new Point(votingTextBlock.Rect.Width, votingTextBlock.Rect.Height);
votingTextBlock.RectTransform.IsFixedSize = true;
votingTextBlock.RectTransform.AbsoluteOffset = new Point(padding, yOffset);
yOffset += votingTextBlock.Rect.Height + spacing;
voteCounter = new GUITextBlock(new RectTransform(new Point(paddedWidth, 0), frame.RectTransform), "(0/0)", GUI.Style.Green, textAlignment: Alignment.Center);
voteCounter.RectTransform.NonScaledSize = voteCounter.RectTransform.MinSize = voteCounter.RectTransform.MaxSize = new Point(voteCounter.Rect.Width, voteCounter.Rect.Height);
voteCounter.RectTransform.IsFixedSize = true;
voteCounter.RectTransform.AbsoluteOffset = new Point(padding, yOffset);
yOffset += voteCounter.Rect.Height + spacing;
votingTimer = new GUIProgressBar(new RectTransform(new Point(paddedWidth, Math.Max(spacing, 8)), frame.RectTransform) { AbsoluteOffset = new Point(padding, yOffset) }, HUDLayoutSettings.Padding);
votingTimer.RectTransform.IsFixedSize = true;
yOffset += votingTimer.Rect.Height + spacing;
int buttonWidth = (int)(paddedWidth * 0.3f);
yesVoteButton = new GUIButton(new RectTransform(new Point(buttonWidth, 0), frame.RectTransform) { AbsoluteOffset = new Point((int)(frame.Rect.Width / 2f - buttonWidth - spacing), yOffset) }, TextManager.Get("yes"))
{
OnClicked = (applyButton, obj) =>
{
SetGUIToVotedState(2);
GameMain.Client.Vote(currentVoteType, 2);
return true;
}
};
noVoteButton = new GUIButton(new RectTransform(new Point(buttonWidth, 0), frame.RectTransform) { AbsoluteOffset = new Point(yesVoteButton.RectTransform.AbsoluteOffset.X + yesVoteButton.Rect.Width + padding, yOffset) }, TextManager.Get("no"))
{
OnClicked = (applyButton, obj) =>
{
SetGUIToVotedState(1);
GameMain.Client.Vote(currentVoteType, 1);
return true;
}
};
votedTextBlock = new GUITextBlock(new RectTransform(new Point(paddedWidth, yesVoteButton.Rect.Height), frame.RectTransform), string.Empty, textAlignment: Alignment.Center);
votedTextBlock.RectTransform.IsFixedSize = true;
votedTextBlock.RectTransform.AbsoluteOffset = new Point(padding, yOffset);
votedTextBlock.Visible = false;
yOffset += yesVoteButton.Rect.Height;
frame.RectTransform.NonScaledSize = new Point(frame.Rect.Width, yOffset + padding);
}
private void SetGUIToVotedState(int vote)
{
yesVoteButton.Visible = noVoteButton.Visible = false;
votedTextBlock.Text = TextManager.Get(vote == 2 ? "yesvoted" : "novoted");
votedTextBlock.Visible = true;
}
public void Update(float deltaTime)
{
if (!VoteRunning) return;
if (GameMain.GraphicsWidth != createdForResolution.X || GameMain.GraphicsHeight != createdForResolution.Y) CreateVotingGUI();
yesVotes = getYesVotes();
noVotes = getNoVotes();
maxVotes = getMaxVotes();
voteCounter.Text = $"({yesVotes + noVotes}/{maxVotes})";
timer += deltaTime;
votingTimer.BarSize = timer / votingTime;
}
public void EndVote(bool passed, int yesVoteFinal, int noVoteFinal)
{
VoteRunning = false;
votePassed = passed;
yesVotes = yesVoteFinal;
noVotes = noVoteFinal;
onVoteEnd?.Invoke();
}
#region Submarine Voting
private void SetSubmarineVotingText(Client starter, SubmarineInfo info, VoteType type)
{
string name = starter.Name;
JobPrefab prefab = starter?.Character?.Info?.Job?.Prefab;
Color nameColor = prefab != null ? prefab.UIColor : Color.White;
string characterRichString = $"‖color:{nameColor.R},{nameColor.G},{nameColor.B}‖{name}‖color:end‖";
string submarineRichString = $"‖color:{submarineColor.R},{submarineColor.G},{submarineColor.B}‖{info.DisplayName}‖color:end‖";
switch (type)
{
case VoteType.PurchaseAndSwitchSub:
votingOnText = TextManager.GetWithVariables("submarinepurchaseandswitchvote", new string[] { "[playername]", "[submarinename]", "[amount]", "[currencyname]" }, new string[] { characterRichString, submarineRichString, info.Price.ToString(), TextManager.Get("credit").ToLower() });
break;
case VoteType.PurchaseSub:
votingOnText = TextManager.GetWithVariables("submarinepurchasevote", new string[] { "[playername]", "[submarinename]", "[amount]", "[currencyname]" }, new string[] { characterRichString, submarineRichString, info.Price.ToString(), TextManager.Get("credit").ToLower() });
break;
case VoteType.SwitchSub:
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
if (deliveryFee > 0)
{
votingOnText = TextManager.GetWithVariables("submarineswitchfeevote", new string[] { "[playername]", "[submarinename]", "[locationname]", "[amount]", "[currencyname]" }, new string[] { characterRichString, submarineRichString, endLocation.Name, deliveryFee.ToString(), TextManager.Get("credit").ToLower() });
}
else
{
votingOnText = TextManager.GetWithVariables("submarineswitchnofeevote", new string[] { "[playername]", "[submarinename]" }, new string[] { characterRichString, submarineRichString });
}
break;
}
votingOnTextData = RichTextData.GetRichTextData(votingOnText, out votingOnText);
}
private int SubmarineYesVotes()
{
return GameMain.NetworkMember.SubmarineVoteYesCount;
}
private int SubmarineNoVotes()
{
return GameMain.NetworkMember.SubmarineVoteNoCount;
}
private int SubmarineMaxVotes()
{
return GameMain.NetworkMember.SubmarineVoteMax;
}
private void SendSubmarineVoteEndMessage(SubmarineInfo info, VoteType type)
{
GameMain.NetworkMember.AddChatMessage(GetSubmarineVoteResultMessage(info, type, yesVotes.ToString(), noVotes.ToString(), votePassed), ChatMessageType.Server);
}
public static string GetSubmarineVoteResultMessage(SubmarineInfo info, VoteType type, string yesVoteString, string noVoteString, bool votePassed)
{
string result = string.Empty;
switch (type)
{
case VoteType.PurchaseAndSwitchSub:
result = TextManager.GetWithVariables(votePassed ? "submarinepurchaseandswitchvotepassed" : "submarinepurchaseandswitchvotefailed", new string[] { "[submarinename]", "[amount]", "[currencyname]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, info.Price.ToString(), TextManager.Get("credit").ToLower(), yesVoteString, noVoteString });
break;
case VoteType.PurchaseSub:
result = TextManager.GetWithVariables(votePassed ? "submarinepurchasevotepassed" : "submarinepurchasevotefailed", new string[] { "[submarinename]", "[amount]", "[currencyname]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, info.Price.ToString(), TextManager.Get("credit").ToLower(), yesVoteString, noVoteString });
break;
case VoteType.SwitchSub:
int deliveryFee = SubmarineSelection.DeliveryFeePerDistanceTravelled * GameMain.GameSession.Map.DistanceToClosestLocationWithOutpost(GameMain.GameSession.Map.CurrentLocation, out Location endLocation);
if (deliveryFee > 0)
{
result = TextManager.GetWithVariables(votePassed ? "submarineswitchfeevotepassed" : "submarineswitchfeevotefailed", new string[] { "[submarinename]", "[locationname]", "[amount]", "[currencyname]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, endLocation.Name, deliveryFee.ToString(), TextManager.Get("credit").ToLower(), yesVoteString, noVoteString });
}
else
{
result = TextManager.GetWithVariables(votePassed ? "submarineswitchnofeevotepassed" : "submarineswitchnofeevotefailed", new string[] { "[submarinename]", "[yesvotecount]", "[novotecount]" }, new string[] { info.DisplayName, yesVoteString, noVoteString });
}
break;
default:
break;
}
return result;
}
#endregion
public void Remove()
{
if (frame != null)
{
frame.Parent.RemoveChild(frame);
frame = null;
}
}
}
}

View File

@@ -17,6 +17,7 @@ using System.Threading;
using Barotrauma.Tutorials;
using Barotrauma.Media;
using Barotrauma.Extensions;
using System.Threading.Tasks;
namespace Barotrauma
{
@@ -35,7 +36,6 @@ namespace Barotrauma
public static GameScreen GameScreen;
public static MainMenuScreen MainMenuScreen;
public static LobbyScreen LobbyScreen;
public static NetLobbyScreen NetLobbyScreen;
public static ServerListScreen ServerListScreen;
@@ -45,8 +45,11 @@ namespace Barotrauma
public static ParticleEditorScreen ParticleEditorScreen;
public static LevelEditorScreen LevelEditorScreen;
public static SpriteEditorScreen SpriteEditorScreen;
public static EventEditorScreen EventEditorScreen;
public static CharacterEditor.CharacterEditorScreen CharacterEditorScreen;
public static CampaignEndScreen CampaignEndScreen;
public static Lights.LightManager LightManager;
public static Sounds.SoundManager SoundManager;
@@ -79,6 +82,10 @@ namespace Barotrauma
set
{
if (gameSession == value) { return; }
if (value == null && Screen.Selected == GameScreen && gameSession.GameMode is CampaignMode)
{
DebugConsole.AddWarning("GameSession set to null while in the game screen\n" + Environment.StackTrace);
}
if (gameSession?.GameMode != null && gameSession.GameMode != value?.GameMode)
{
gameSession.GameMode.Remove();
@@ -100,7 +107,7 @@ namespace Barotrauma
private CoroutineHandle loadingCoroutine;
private bool hasLoaded;
private GameTime fixedTime;
private readonly GameTime fixedTime;
public string ConnectName;
public string ConnectEndpoint;
@@ -110,7 +117,7 @@ namespace Barotrauma
private Viewport defaultViewport;
public event Action OnResolutionChanged;
public event Action ResolutionChanged;
private bool exiting;
@@ -190,7 +197,6 @@ namespace Barotrauma
public GameMain(string[] args)
{
Content.RootDirectory = "Content";
#if DEBUG && WINDOWS
GraphicsAdapter.UseDebugLayers = true;
#endif
@@ -273,7 +279,7 @@ namespace Barotrauma
defaultViewport = GraphicsDevice.Viewport;
OnResolutionChanged?.Invoke();
ResolutionChanged?.Invoke();
}
public void SetWindowMode(WindowMode windowMode)
@@ -455,9 +461,10 @@ namespace Barotrauma
{
bool waitingForWorkshopUpdates = true;
bool result = false;
TaskPool.Add(SteamManager.AutoUpdateWorkshopItemsAsync(), (task) =>
TaskPool.Add("AutoUpdateWorkshopItemsAsync",
SteamManager.AutoUpdateWorkshopItemsAsync(), (task) =>
{
result = task.Result;
result = ((Task<bool>)task).Result;
waitingForWorkshopUpdates = false;
});
@@ -521,6 +528,10 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
TaskPool.Add("InitRelayNetworkAccess", SteamManager.InitRelayNetworkAccess(), (t) => { });
FactionPrefab.LoadFactions();
NPCSet.LoadSets();
CharacterPrefab.LoadAll();
MissionPrefab.Init();
TraitorMissionPrefab.Init();
@@ -528,8 +539,9 @@ namespace Barotrauma
Tutorials.Tutorial.Init();
MapGenerationParams.Init();
LevelGenerationParams.LoadPresets();
OutpostGenerationParams.LoadPresets();
WreckAIConfig.LoadAll();
ScriptedEventSet.LoadPrefabs();
EventSet.LoadPrefabs();
AfflictionPrefab.LoadAll(GetFilesOfType(ContentType.Afflictions));
SkillSettings.Load(GetFilesOfType(ContentType.SkillSettings));
Order.Init();
@@ -544,6 +556,10 @@ namespace Barotrauma
ItemPrefab.LoadAll(GetFilesOfType(ContentType.Item));
TitleScreen.LoadState = 55.0f;
yield return CoroutineStatus.Running;
UpgradePrefab.LoadAll(GetFilesOfType(ContentType.UpgradeModules));
TitleScreen.LoadState = 56.0f;
yield return CoroutineStatus.Running;
JobPrefab.LoadAll(GetFilesOfType(ContentType.Jobs));
CorpsePrefab.LoadAll(GetFilesOfType(ContentType.Corpses));
@@ -567,7 +583,6 @@ namespace Barotrauma
yield return CoroutineStatus.Running;
MainMenuScreen = new MainMenuScreen(this);
LobbyScreen = new LobbyScreen();
ServerListScreen = new ServerListScreen();
TitleScreen.LoadState = 70.0f;
@@ -594,7 +609,9 @@ namespace Barotrauma
LevelEditorScreen = new LevelEditorScreen();
SpriteEditorScreen = new SpriteEditorScreen();
EventEditorScreen = new EventEditorScreen();
CharacterEditorScreen = new CharacterEditor.CharacterEditorScreen();
CampaignEndScreen = new CampaignEndScreen();
yield return CoroutineStatus.Running;
@@ -633,7 +650,7 @@ namespace Barotrauma
foreach (ContentPackage contentPackage in Config.SelectedContentPackages)
{
var exePaths = contentPackage.GetFilesOfType(ContentType.Executable);
if (exePaths.Any() && AppDomain.CurrentDomain.FriendlyName != exePaths.First())
if (exePaths.Any() && AppDomain.CurrentDomain.FriendlyName != Path.GetFileNameWithoutExtension(exePaths.First()))
{
var msgBox = new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("IncorrectExe",
new string[2] { "[selectedpackage]", "[exename]" }, new string[2] { contentPackage.Name, exePaths.First() }),
@@ -763,7 +780,7 @@ namespace Barotrauma
//reset accumulator if loading
// -> less choppy loading screens because the screen is rendered after each update
// -> no pause caused by leftover time in the accumulator when starting a new shift
GameMain.ResetFrameTime();
ResetFrameTime();
if (!TitleScreen.PlayingSplashScreen)
{
@@ -777,11 +794,33 @@ namespace Barotrauma
}
#if DEBUG
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && Config.AutomaticQuickStartEnabled && FirstLoad)
if (TitleScreen.LoadState >= 100.0f && !TitleScreen.PlayingSplashScreen && (Config.AutomaticQuickStartEnabled || Config.AutomaticCampaignLoadEnabled) && FirstLoad && !PlayerInput.KeyDown(Keys.LeftShift))
{
loadingScreenOpen = false;
FirstLoad = false;
MainMenuScreen.QuickStart();
if (Config.AutomaticQuickStartEnabled)
{
MainMenuScreen.QuickStart();
}
else if (Config.AutomaticCampaignLoadEnabled)
{
IEnumerable<string> saveFiles = SaveUtil.GetSaveFiles(SaveUtil.SaveType.Singleplayer);
if (saveFiles.Count() > 0)
{
saveFiles = saveFiles.OrderBy(file => File.GetLastWriteTime(file));
try
{
SaveUtil.LoadGame(saveFiles.Last());
}
catch (Exception e)
{
DebugConsole.ThrowError("Loading save \"" + saveFiles.Last() + "\" failed", e);
return;
}
}
}
}
#endif
@@ -845,6 +884,12 @@ namespace Barotrauma
{
((GUIMessageBox)GUIMessageBox.VisibleBox).Close();
}
else if (GUIMessageBox.VisibleBox?.UserData is RoundSummary roundSummary &&
roundSummary.ContinueButton != null &&
roundSummary.ContinueButton.Visible)
{
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
}
else if (Tutorial.Initialized && Tutorial.ContentRunning)
{
(GameSession.GameMode as TutorialMode).Tutorial.CloseActiveContentGUI();
@@ -868,7 +913,7 @@ namespace Barotrauma
GUI.TogglePauseMenu();
}
bool itemHudActive()
static bool itemHudActive()
{
if (Character.Controlled?.SelectedConstruction == null) { return false; }
return
@@ -878,7 +923,7 @@ namespace Barotrauma
}
#if DEBUG
if (GameMain.NetworkMember == null)
if (NetworkMember == null)
{
if (PlayerInput.KeyHit(Keys.P) && !(GUI.KeyboardDispatcher.Subscriber is GUITextBox))
{
@@ -890,6 +935,10 @@ namespace Barotrauma
GUI.ClearUpdateList();
Paused = (DebugConsole.IsOpen || GUI.PauseMenuOpen || GUI.SettingsMenuOpen || Tutorial.ContentRunning || DebugConsole.Paused) &&
(NetworkMember == null || !NetworkMember.GameStarted);
if (GameSession?.GameMode != null && GameSession.GameMode.Paused)
{
Paused = true;
}
#if !DEBUG
if (NetworkMember == null && !WindowActive && !Paused && true && Screen.Selected != MainMenuScreen && Config.PauseOnFocusLost)
@@ -921,7 +970,7 @@ namespace Barotrauma
{
(GameSession.GameMode as TutorialMode).Update((float)Timing.Step);
}
else if (DebugConsole.Paused)
else
{
if (Screen.Selected.Cam == null)
{
@@ -929,7 +978,7 @@ namespace Barotrauma
}
else
{
Screen.Selected.Cam.MoveCamera((float)Timing.Step);
Screen.Selected.Cam.MoveCamera((float)Timing.Step, allowMove: DebugConsole.Paused, allowZoom: DebugConsole.Paused);
}
}
@@ -1027,41 +1076,53 @@ namespace Barotrauma
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
}
}
public static void QuitToMainMenu(bool save)
{
if (save)
{
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
if (GameSession.Submarine != null && !GameSession.Submarine.Removed)
{
GameSession.SubmarineInfo = new SubmarineInfo(GameSession.Submarine);
}
// Update store stock when saving and quitting in an outpost (normally updated when CampaignMode.End() is called)
if (GameSession?.Campaign is SinglePlayerCampaign campaign && Level.IsLoadedOutpost && campaign.Map?.CurrentLocation != null && campaign.CargoManager != null)
{
campaign.Map.CurrentLocation.AddToStock(campaign.CargoManager.SoldItems);
campaign.CargoManager.ClearSoldItemsProjSpecific();
campaign.Map.CurrentLocation.RemoveFromStock(campaign.CargoManager.PurchasedItems);
}
SaveUtil.SaveGame(GameSession.SavePath);
}
if (GameMain.Client != null)
if (Client != null)
{
GameMain.Client.Disconnect();
GameMain.Client = null;
Client.Disconnect();
Client = null;
}
CoroutineManager.StopCoroutines("EndCinematic");
if (GameMain.GameSession != null)
if (GameSession != null)
{
if (Tutorial.Initialized)
{
((TutorialMode)GameMain.GameSession.GameMode).Tutorial?.Stop();
((TutorialMode)GameSession.GameMode).Tutorial?.Stop();
}
if (GameSettings.SendUserStatistics)
{
Mission mission = GameMain.GameSession.Mission;
Mission mission = GameSession.Mission;
GameAnalyticsManager.AddDesignEvent("QuitRound:" + (save ? "Save" : "NoSave"));
GameAnalyticsManager.AddDesignEvent("EndRound:" + (mission == null ? "NoMission" : (mission.Completed ? "MissionCompleted" : "MissionFailed")));
}
GameMain.GameSession = null;
}
GUIMessageBox.CloseAll();
GameMain.MainMenuScreen.Select();
MainMenuScreen.Select();
GameSession = null;
}
public void ShowCampaignDisclaimer(Action onContinue = null)
@@ -1071,12 +1132,7 @@ namespace Barotrauma
msgBox.Buttons[0].OnClicked = (btn, userdata) =>
{
var roadMap = new GUIMessageBox(TextManager.Get("CampaignRoadMapTitle"), TextManager.Get("CampaignRoadMapText"),
new string[] { TextManager.Get("Back"), TextManager.Get("OK") });
roadMap.Buttons[0].OnClicked += roadMap.Close;
roadMap.Buttons[0].OnClicked += (_, __) => { ShowCampaignDisclaimer(onContinue); return true; };
roadMap.Buttons[1].OnClicked += roadMap.Close;
roadMap.Buttons[1].OnClicked += (_, __) => { onContinue?.Invoke(); return true; };
ShowOpenUrlInWebBrowserPrompt("https://trello.com/b/hBXI8ltN/barotrauma-roadmap-known-issues");
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
@@ -1094,9 +1150,9 @@ namespace Barotrauma
linkHolder.RectTransform.MaxSize = new Point(int.MaxValue, linkHolder.Rect.Height);
List<Pair<string, string>> links = new List<Pair<string, string>>()
{
new Pair<string, string>(TextManager.Get("EditorDisclaimerWikiLink"),TextManager.Get("EditorDisclaimerWikiUrl")),
new Pair<string, string>(TextManager.Get("EditorDisclaimerDiscordLink"),TextManager.Get("EditorDisclaimerDiscordUrl")),
new Pair<string, string>(TextManager.Get("EditorDisclaimerForumLink"),TextManager.Get("EditorDisclaimerForumUrl")),
new Pair<string, string>(TextManager.Get("EditorDisclaimerWikiLink"), TextManager.Get("EditorDisclaimerWikiUrl")),
new Pair<string, string>(TextManager.Get("EditorDisclaimerDiscordLink"), TextManager.Get("EditorDisclaimerDiscordUrl")),
new Pair<string, string>(TextManager.Get("EditorDisclaimerForumLink"), TextManager.Get("EditorDisclaimerForumUrl")),
};
foreach (var link in links)
{
@@ -1124,8 +1180,10 @@ namespace Barotrauma
return;
}
var msgBox = new GUIMessageBox(TextManager.Get("bugreportbutton"), "");
msgBox.UserData = "bugreporter";
var msgBox = new GUIMessageBox(TextManager.Get("bugreportbutton"), "")
{
UserData = "bugreporter"
};
var linkHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 1.0f), msgBox.Content.RectTransform)) { Stretch = true, RelativeSpacing = 0.025f };
linkHolder.RectTransform.MaxSize = new Point(int.MaxValue, linkHolder.Rect.Height);
@@ -1189,7 +1247,7 @@ namespace Barotrauma
DebugConsole.ThrowError("Error while cleaning unnecessary save files", e);
}
if (GameSettings.SendUserStatistics){ GameAnalytics.OnQuit(); }
if (GameSettings.SendUserStatistics) { GameAnalytics.OnQuit(); }
if (GameSettings.SaveDebugConsoleLogs) { DebugConsole.SaveLogs(); }
base.OnExiting(sender, args);

View File

@@ -0,0 +1,173 @@
using Barotrauma.Extensions;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class CargoManager
{
private class SoldEntity
{
public enum SellStatus
{
Confirmed,
Unconfirmed,
Local
}
public Item Item { get; }
public SellStatus Status { get; set; }
private SoldEntity(Item item, SellStatus status)
{
Item = item;
Status = status;
}
public static SoldEntity CreateInSinglePlayer(Item item) => new SoldEntity(item, SellStatus.Confirmed);
public static SoldEntity CreateInMultiPlayer(Item item) => new SoldEntity(item, SellStatus.Local);
}
private List<SoldEntity> SoldEntities { get; } = new List<SoldEntity>();
public List<Item> GetSellableItems(Character character)
{
if (character == null) { return new List<Item>(); }
// Only consider items which have been:
// a) sold in singleplayer or confirmed by server (SellStatus.Confirmed); or
// b) sold locally in multiplier (SellStatus.Local), but the client has not received a campaing state update yet after selling them
var soldEntities = SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
var sellables = Item.ItemList.FindAll(i => i?.Prefab != null && !i.Removed &&
i.GetRootInventoryOwner() == character &&
!i.SpawnedInOutpost &&
(i.ContainedItems == null || i.ContainedItems.None() || i.ContainedItems.All(ci => soldEntities.Any(se => se.Item == ci))) &&
i.IsFullCondition && soldEntities.None(se => se.Item == i));
// Prevent selling things like battery cells from headsets and oxygen tanks from diving masks
var slots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.OuterClothes, InvSlotType.Headset };
foreach (InvSlotType slot in slots)
{
var index = character.Inventory.FindLimbSlot(slot);
if (character.Inventory.Items[index] is Item item && item.ContainedItems != null)
{
foreach (Item containedItem in item.ContainedItems)
{
if (containedItem != null)
{
sellables.Remove(containedItem);
}
}
}
}
return sellables;
}
public void SetItemsInBuyCrate(List<PurchasedItem> items)
{
ItemsInBuyCrate.Clear();
ItemsInBuyCrate.AddRange(items);
OnItemsInBuyCrateChanged?.Invoke();
}
public void SetSoldItems(List<SoldItem> items)
{
SoldItems.Clear();
SoldItems.AddRange(items);
foreach (SoldEntity se in SoldEntities)
{
if (se.Status == SoldEntity.SellStatus.Confirmed) { continue; }
if (SoldItems.Any(si => si.ID == se.Item.ID && si.ItemPrefab == se.Item.Prefab && (GameMain.Client == null || GameMain.Client.ID == si.SellerID)))
{
se.Status = SoldEntity.SellStatus.Confirmed;
}
else
{
se.Status = SoldEntity.SellStatus.Unconfirmed;
}
}
OnSoldItemsChanged?.Invoke();
}
public void ModifyItemQuantityInSellCrate(ItemPrefab itemPrefab, int changeInQuantity)
{
PurchasedItem itemToSell = ItemsInSellCrate.Find(i => i.ItemPrefab == itemPrefab);
if (itemToSell != null)
{
itemToSell.Quantity += changeInQuantity;
if (itemToSell.Quantity < 1)
{
ItemsInSellCrate.Remove(itemToSell);
}
}
else if (changeInQuantity > 0)
{
itemToSell = new PurchasedItem(itemPrefab, changeInQuantity);
ItemsInSellCrate.Add(itemToSell);
}
OnItemsInSellCrateChanged?.Invoke();
}
public void SellItems(List<PurchasedItem> itemsToSell)
{
var itemsInInventory = GetSellableItems(Character.Controlled);
var canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
var sellerId = GameMain.Client?.ID ?? 0;
foreach (PurchasedItem item in itemsToSell)
{
var itemValue = GetSellValueAtCurrentLocation(item.ItemPrefab, quantity: item.Quantity);
// check if the store can afford the item
if (location.StoreCurrentBalance < itemValue) { continue; }
var matchingItems = itemsInInventory.FindAll(i => i.Prefab == item.ItemPrefab);
if (matchingItems.Count <= item.Quantity)
{
foreach (Item i in matchingItems)
{
SoldItems.Add(new SoldItem(i.Prefab, i.ID, canAddToRemoveQueue, sellerId));
SoldEntities.Add(campaign.IsSinglePlayer ? SoldEntity.CreateInSinglePlayer(i) : SoldEntity.CreateInMultiPlayer(i));
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(i); }
}
}
else
{
for (int i = 0; i < item.Quantity; i++)
{
var matchingItem = matchingItems[i];
SoldItems.Add(new SoldItem(matchingItem.Prefab, matchingItem.ID, canAddToRemoveQueue, sellerId));
SoldEntities.Add(campaign.IsSinglePlayer ? SoldEntity.CreateInSinglePlayer(matchingItem) : SoldEntity.CreateInMultiPlayer(matchingItem));
if (canAddToRemoveQueue) { Entity.Spawner.AddToRemoveQueue(matchingItem); }
}
}
// Exchange money
campaign.Map.CurrentLocation.StoreCurrentBalance -= itemValue;
campaign.Money += itemValue;
// Remove from the sell crate
if (ItemsInSellCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } itemToSell)
{
itemToSell.Quantity -= item.Quantity;
if (itemToSell.Quantity < 1)
{
ItemsInSellCrate.Remove(itemToSell);
}
}
}
OnSoldItemsChanged?.Invoke();
}
public void ClearSoldItemsProjSpecific()
{
SoldItems.Clear();
SoldEntities.Clear();
}
}
}

View File

@@ -20,9 +20,6 @@ namespace Barotrauma
/// </summary>
const float CharacterWaitOnSwitch = 10.0f;
private readonly List<CharacterInfo> characterInfos = new List<CharacterInfo>();
private readonly List<Character> characters = new List<Character>();
private Point screenResolution;
#region UI
@@ -30,6 +27,7 @@ namespace Barotrauma
public GUIComponent ReportButtonFrame { get; set; }
private GUIFrame guiFrame;
private GUIComponent crewAreaWithButtons;
private GUIFrame crewArea;
private GUIListBox crewList;
private GUIButton commandButton, toggleCrewButton;
@@ -72,19 +70,7 @@ namespace Barotrauma
public CrewManager(XElement element, bool isSinglePlayer)
: this(isSinglePlayer)
{
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("character", StringComparison.OrdinalIgnoreCase)) { continue; }
var characterInfo = new CharacterInfo(subElement);
characterInfos.Add(characterInfo);
foreach (XElement invElement in subElement.Elements())
{
if (!invElement.Name.ToString().Equals("inventory", StringComparison.OrdinalIgnoreCase)) { continue; }
characterInfo.InventoryData = invElement;
break;
}
}
AddCharacterElements(element);
}
partial void InitProjectSpecific()
@@ -96,7 +82,7 @@ namespace Barotrauma
#region Crew Area
var crewAreaWithButtons = new GUIFrame(
crewAreaWithButtons = new GUIFrame(
HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.CrewArea, guiFrame.RectTransform),
style: null,
color: Color.Transparent)
@@ -326,43 +312,6 @@ namespace Barotrauma
return characterInfos;
}
public void AddCharacter(Character character)
{
if (character.Removed)
{
DebugConsole.ThrowError("Tried to add a removed character to CrewManager!\n" + Environment.StackTrace);
return;
}
if (character.IsDead)
{
DebugConsole.ThrowError("Tried to add a dead character to CrewManager!\n" + Environment.StackTrace);
return;
}
if (!characters.Contains(character))
{
characters.Add(character);
}
if (!characterInfos.Contains(character.Info))
{
characterInfos.Add(character.Info);
}
AddCharacterToCrewList(character);
DisplayCharacterOrder(character, character.CurrentOrder, character.CurrentOrderOption);
}
public void AddCharacterInfo(CharacterInfo characterInfo)
{
if (characterInfos.Contains(characterInfo))
{
DebugConsole.ThrowError("Tried to add the same character info to CrewManager twice.\n" + Environment.StackTrace);
return;
}
characterInfos.Add(characterInfo);
}
/// <summary>
/// Remove the character from the crew (and crew menus).
/// </summary>
@@ -379,15 +328,6 @@ namespace Barotrauma
if (removeInfo) { characterInfos.Remove(character.Info); }
}
/// <summary>
/// Remove info of a selected character. The character will not be visible in any menus or the round summary.
/// </summary>
/// <param name="characterInfo"></param>
public void RemoveCharacterInfo(CharacterInfo characterInfo)
{
characterInfos.Remove(characterInfo);
}
private void AddCharacterToCrewList(Character character)
{
if (character == null) { return; }
@@ -516,7 +456,7 @@ namespace Barotrauma
};
new GUIImage(
new RectTransform(Vector2.One, soundIcons.RectTransform),
GUI.Style.GetComponentStyle("GUISoundIcon").Sprites[GUIComponent.ComponentState.None].FirstOrDefault().Sprite,
GUI.Style.GetComponentStyle("GUISoundIcon").GetDefaultSprite(),
scaleToFit: true)
{
CanBeFocused = false,
@@ -630,6 +570,22 @@ namespace Barotrauma
ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender));
}
public void AddSinglePlayerChatMessage(ChatMessage message)
{
if (!IsSinglePlayer)
{
DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace);
return;
}
if (string.IsNullOrEmpty(message.Text)) { return; }
if (message.Sender != null)
{
GameMain.GameSession.CrewManager.SetCharacterSpeaking(message.Sender);
}
ChatBox.AddMessage(message);
}
private WifiComponent GetHeadset(Character character, bool requireEquipped)
{
if (character?.Inventory == null) return null;
@@ -861,27 +817,6 @@ namespace Barotrauma
return characterComponent?.FindChild(c => c?.UserData is OrderInfo orderInfo && orderInfo.ComponentIdentifier == "previousorder");
}
private struct OrderInfo
{
public string ComponentIdentifier { get; set; }
public Order Order { get; private set; }
public string OrderOption { get; private set; }
public OrderInfo(Order order, string orderOption)
{
ComponentIdentifier = "currentorder";
Order = order;
OrderOption = orderOption;
}
public OrderInfo(OrderInfo orderInfo)
{
ComponentIdentifier = "previousorder";
Order = orderInfo.Order;
OrderOption = orderInfo.OrderOption;
}
}
#endregion
#region Updating and drawing the UI
@@ -1123,6 +1058,7 @@ namespace Barotrauma
public void AddToGUIUpdateList()
{
if (GUI.DisableHUD) { return; }
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || CoroutineManager.IsCoroutineRunning("SubmarineTransition")) { return; }
commandFrame?.AddToGUIUpdateList();
@@ -1145,6 +1081,8 @@ namespace Barotrauma
}
}
crewAreaWithButtons.Visible = !(GameMain.GameSession?.GameMode is CampaignMode campaign) || (!campaign.ForceMapUI && !campaign.ShowCampaignUI);
guiFrame.AddToGUIUpdateList();
contextMenu?.AddToGUIUpdateList(false, 1);
subContextMenu?.AddToGUIUpdateList(false, 1);
@@ -1170,6 +1108,7 @@ namespace Barotrauma
private void SelectCharacter(Character character)
{
if (ConversationAction.IsDialogOpen) { return; }
if (!AllowCharacterSwitch) { return; }
//make the previously selected character wait in place for some time
//(so they don't immediately start idling and walking away from their station)
@@ -1256,7 +1195,7 @@ namespace Barotrauma
WasCommandInterfaceDisabledThisUpdate = false;
if (PlayerInput.KeyDown(InputType.Command) && (GUI.KeyboardDispatcher.Subscriber == null || GUI.KeyboardDispatcher.Subscriber == crewList) &&
commandFrame == null && !clicklessSelectionActive && CanIssueOrders)
commandFrame == null && !clicklessSelectionActive && CanIssueOrders && !(GameMain.GameSession?.Campaign?.ShowCampaignUI ?? false))
{
if (PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift))
{
@@ -1574,32 +1513,32 @@ namespace Barotrauma
get
{
#if DEBUG
return Character.Controlled == null || Character.Controlled.Info != null && Character.Controlled.SpeechImpediment < 100.0f;
#else
return Character.Controlled?.Info != null && Character.Controlled.SpeechImpediment < 100.0f;
if (Character.Controlled == null) { return true; }
#endif
return Character.Controlled?.Info != null && Character.Controlled.SpeechImpediment < 100.0f;
}
}
private bool CanSomeoneHearCharacter()
{
#if DEBUG
return true;
#else
return Character.Controlled != null && characters.Any(c => c != Character.Controlled && c.CanHearCharacter(Character.Controlled));
if (Character.Controlled == null) { return true; }
#endif
return Character.Controlled != null && characters.Any(c => c != Character.Controlled && c.CanHearCharacter(Character.Controlled));
}
private Entity FindEntityContext()
{
if (Character.Controlled?.FocusedCharacter != null)
if (Character.Controlled?.FocusedCharacter is Character focusedCharacter && !focusedCharacter.IsDead &&
HumanAIController.IsFriendly(Character.Controlled, focusedCharacter) && Character.Controlled.TeamID == focusedCharacter.TeamID)
{
if (Character.Controlled?.FocusedItem != null)
{
Vector2 mousePos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
if (Vector2.Distance(mousePos, Character.Controlled.FocusedCharacter.WorldPosition) < Vector2.Distance(mousePos, Character.Controlled.FocusedItem.WorldPosition))
if (Vector2.Distance(mousePos, focusedCharacter.WorldPosition) < Vector2.Distance(mousePos, Character.Controlled.FocusedItem.WorldPosition))
{
return Character.Controlled.FocusedCharacter;
return focusedCharacter;
}
else
{
@@ -1608,7 +1547,7 @@ namespace Barotrauma
}
else
{
return Character.Controlled.FocusedCharacter;
return focusedCharacter;
}
}
@@ -1677,24 +1616,22 @@ namespace Barotrauma
"CommandNodeContainer",
scaleToFit: true)
{
Color = characterContext.Info.Job.Prefab.UIColor * nodeColorMultiplier,
HoverColor = characterContext.Info.Job.Prefab.UIColor,
Color = characterContext.Info?.Job?.Prefab != null ? characterContext.Info.Job.Prefab.UIColor * nodeColorMultiplier : Color.White,
HoverColor = characterContext.Info?.Job?.Prefab != null ? characterContext.Info.Job.Prefab.UIColor : Color.White,
UserData = "colorsource"
};
// Character icon
new GUICustomComponent(
var characterIcon = new GUICustomComponent(
new RectTransform(Vector2.One, startNode.RectTransform, anchor: Anchor.Center),
(spriteBatch, _) =>
{
if (!(entityContext is Character character)) { return; }
if (!(entityContext is Character character) || character?.Info == null) { return; }
var node = startNode;
character.Info.DrawJobIcon(spriteBatch,
new Rectangle((int)(node.Rect.X + node.Rect.Width * 0.5f), (int)(node.Rect.Y + node.Rect.Height * 0.1f), (int)(node.Rect.Width * 0.6f), (int)(node.Rect.Height * 0.8f)));
character.Info.DrawIcon(spriteBatch, new Vector2(node.Rect.X + node.Rect.Width * 0.35f, node.Center.Y), node.Rect.Size.ToVector2() * 0.7f);
})
{
ToolTip = characterContext.Info.DisplayName + " (" + characterContext.Info.Job.Name + ")"
};
});
SetCharacterTooltip(characterIcon, entityContext as Character);
}
SetCenterNode(startNode);
@@ -1934,7 +1871,7 @@ namespace Barotrauma
c.HoverColor = c.Color;
c.PressedColor = c.Color;
c.SelectedColor = c.Color;
c.ToolTip = characterContext != null ? characterContext.Info.DisplayName + " (" + characterContext.Info.Job.Name + ")" : null;
SetCharacterTooltip(c, characterContext);
}
node.OnClicked = null;
centerNode = node;
@@ -2040,7 +1977,7 @@ namespace Barotrauma
var reactorOutput = -reactor.CurrPowerConsumption;
// If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor
// ---> Create shortcut node for "Operate Reactor" order's "Power Up" option
if ((Character.Controlled == null || Character.Controlled.Info.Job.Prefab != JobPrefab.Get("engineer")) &&
if ((Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("engineer")) &&
reactorOutput < float.Epsilon && characters.None(c => c.SelectedConstruction == reactor.Item))
{
var order = new Order(Order.GetPrefab("operatereactor"), reactor.Item, reactor, Character.Controlled);
@@ -2053,7 +1990,7 @@ namespace Barotrauma
// TODO: Reconsider the conditions as bot captain can have the nav term selected without operating it
// If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up
// --> Create shortcut node for Steer order
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info.Job.Prefab != JobPrefab.Get("captain")) &&
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("captain")) &&
sub.GetItems(false).Find(i => i.HasTag("navterminal") && !i.NonInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) &&
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
{
@@ -2063,7 +2000,7 @@ namespace Barotrauma
// If player is not a security officer AND invaders are reported
// --> Create shorcut node for Fight Intruders order
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info.Job.Prefab != JobPrefab.Get("securityofficer")) &&
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("securityofficer")) &&
(Order.GetPrefab("reportintruders") is Order reportIntruders && ActiveOrders.Any(o => o.First.Prefab == reportIntruders)))
{
shortcutNodes.Add(
@@ -2072,7 +2009,7 @@ namespace Barotrauma
// If player is not a mechanic AND a breach has been reported
// --> Create shorcut node for Fix Leaks order
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info.Job.Prefab != JobPrefab.Get("mechanic")) &&
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("mechanic")) &&
(Order.GetPrefab("reportbreach") is Order reportBreach && ActiveOrders.Any(o => o.First.Prefab == reportBreach)))
{
shortcutNodes.Add(
@@ -2081,7 +2018,7 @@ namespace Barotrauma
// If player is not an engineer AND broken devices have been reported
// --> Create shortcut node for Repair Damaged Systems order
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info.Job.Prefab != JobPrefab.Get("engineer")) &&
if (shortcutNodes.Count < maxShorcutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("engineer")) &&
(Order.GetPrefab("reportbrokendevices") is Order reportBrokenDevices && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices)))
{
shortcutNodes.Add(
@@ -2741,11 +2678,11 @@ namespace Barotrauma
};
}
#if DEBUG
bool canHear = true;
#else
bool canHear = character.CanHearCharacter(Character.Controlled);
#if DEBUG
if (Character.Controlled == null) { canHear = true; }
#endif
if (!canHear)
{
node.CanBeFocused = orderIcon.CanBeFocused = false;
@@ -2904,18 +2841,29 @@ namespace Barotrauma
return sub;
}
private void SetCharacterTooltip(GUIComponent component, Character character)
{
if (component == null) { return; }
var tooltip = character?.Info != null ? characterContext.Info.DisplayName : null;
if (string.IsNullOrWhiteSpace(tooltip)) { component.ToolTip = tooltip; return; }
if (character.Info?.Job != null && !string.IsNullOrWhiteSpace(characterContext.Info.Job.Name)) { tooltip += " (" + characterContext.Info.Job.Name + ")"; }
component.ToolTip = tooltip;
}
#region Crew Member Assignment Logic
private Character GetCharacterForQuickAssignment(Order order)
{
var controllingCharacter = Character.Controlled != null;
#if !DEBUG
if (Character.Controlled == null) { return null; }
if (!controllingCharacter) { return null; }
#endif
if (order.Category == OrderCategory.Operate && HumanAIController.IsItemOperatedByAnother(null, order.TargetItemComponent, out Character operatingCharacter))
if (order.Category == OrderCategory.Operate && HumanAIController.IsItemOperatedByAnother(null, order.TargetItemComponent, out Character operatingCharacter) &&
(!controllingCharacter || operatingCharacter.CanHearCharacter(Character.Controlled)))
{
return operatingCharacter;
}
return GetCharactersSortedForOrder(order, false).FirstOrDefault() ?? Character.Controlled;
return GetCharactersSortedForOrder(order, false).FirstOrDefault(c => !controllingCharacter || c.CanHearCharacter(Character.Controlled)) ?? Character.Controlled;
}
private List<Character> GetCharactersForManualAssignment(Order order)
@@ -2934,11 +2882,17 @@ namespace Barotrauma
private IEnumerable<Character> GetCharactersSortedForOrder(Order order, bool includeSelf)
{
return characters.FindAll(c => Character.Controlled == null || ((includeSelf || c != Character.Controlled) && c.TeamID == Character.Controlled.TeamID))
// 1. Prioritize those who are already ordered to operate the item target of the new 'operate' order
.OrderByDescending(c => c.CurrentOrder != null && order.Category == OrderCategory.Operate && c.CurrentOrder.Identifier == order.Identifier && c.CurrentOrder.TargetEntity == order.TargetEntity)
// 2. Prioritize those who are currently dismissed
.ThenByDescending(c => c.CurrentOrder == null || c.CurrentOrder.Identifier == dismissedOrderPrefab.Identifier)
// 3. Prioritize those who are not currently assigned with the same type of order (for example, when giving a 'Fix Leak' order, prioritize those who have a different order)
.ThenBy(c => c.CurrentOrder != null && c.CurrentOrder.Identifier == order.Identifier && c.CurrentOrder.TargetEntity == order.TargetEntity)
// 4. Prioritize those with the appropriate job for the order
.ThenByDescending(c => order.HasAppropriateJob(c))
// 5. Prioritize those with the lowest "weight" of the current order
.ThenBy(c => c.CurrentOrder?.Weight)
// 6. Prioritize those with the best skill for the order
.ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
}
@@ -3013,40 +2967,7 @@ namespace Barotrauma
public void InitSinglePlayerRound()
{
crewList.ClearChildren();
characters.Clear();
WayPoint[] waypoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSub);
for (int i = 0; i < waypoints.Length; i++)
{
Character character;
character = Character.Create(characterInfos[i], waypoints[i].WorldPosition, characterInfos[i].Name);
if (character.Info != null)
{
if (!character.Info.StartItemsGiven && character.Info.InventoryData != null)
{
DebugConsole.ThrowError($"Error when initializing a single player round: character \"{character.Name}\" has not been given their initial items but has saved inventory data. Using the saved inventory data instead of giving the character new items.");
}
if (character.Info.InventoryData != null)
{
character.Info.SpawnInventoryItems(character.Inventory, character.Info.InventoryData);
}
else if (!character.Info.StartItemsGiven)
{
character.GiveJobItems(waypoints[i]);
}
character.Info.StartItemsGiven = true;
}
AddCharacter(character);
if (i == 0)
{
Character.Controlled = character;
}
}
conversationTimer = Rand.Range(5.0f, 10.0f);
InitRound();
}
public void EndRound()
@@ -3072,10 +2993,9 @@ namespace Barotrauma
foreach (CharacterInfo ci in characterInfos)
{
var infoElement = ci.Save(element);
if (ci.InventoryData != null)
{
infoElement.Add(ci.InventoryData);
}
if (ci.InventoryData != null) { infoElement.Add(ci.InventoryData); }
if (ci.HealthData != null) { infoElement.Add(ci.HealthData); }
if (ci.LastControlled) { infoElement.Add(new XAttribute("lastcontrolled", true)); }
}
parentElement.Add(element);
}

View File

@@ -1,10 +1,65 @@
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Barotrauma
{
abstract partial class CampaignMode : GameMode
{
protected bool crewDead;
protected Color overlayColor;
protected string overlayText, overlayTextBottom;
protected Color overlayTextColor;
protected Sprite overlaySprite;
protected GUIButton endRoundButton;
public GUIButton EndRoundButton => endRoundButton;
protected GUIFrame campaignUIContainer;
public CampaignUI CampaignUI;
public bool ForceMapUI
{
get;
protected set;
}
public override bool Paused
{
get { return ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"); }
}
private bool showCampaignUI;
private bool wasChatBoxOpen;
public bool ShowCampaignUI
{
get { return showCampaignUI; }
set
{
if (value == showCampaignUI) { return; }
var chatBox = CrewManager?.ChatBox ?? GameMain.Client?.ChatBox;
if (value)
{
if (chatBox != null)
{
wasChatBoxOpen = chatBox.ToggleOpen;
chatBox.ToggleOpen = false;
}
}
else if (chatBox != null)
{
chatBox.ToggleOpen = wasChatBoxOpen;
}
showCampaignUI = value;
}
}
public override void ShowStartMessage()
{
if (Mission == null) return;
@@ -15,5 +70,216 @@ namespace Barotrauma
UserData = "missionstartmessage"
};
}
/// <summary>
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
/// </summary>
public bool AllowedToEndRound()
{
//allow ending the round if the client has permissions, is the owner, the only client in the server
//or if no-one has management permissions
if (GameMain.Client == null) { return true; }
return
GameMain.Client.HasPermission(ClientPermissions.ManageRound) ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Client.ConnectedClients.Count == 1 ||
GameMain.Client.IsServerOwner ||
GameMain.Client.ConnectedClients.None(c =>
c.InGame && (c.IsOwner || c.HasPermission(ClientPermissions.ManageRound) || c.HasPermission(ClientPermissions.ManageCampaign)));
}
/// <summary>
/// There is a server-side implementation of the method in <see cref="MultiPlayerCampaign"/>
/// </summary>
public bool AllowedToManageCampaign()
{
//allow ending the round if the client has permissions, is the owner, the only client in the server,
//or if no-one has management permissions
if (GameMain.Client == null) { return true; }
return
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Client.ConnectedClients.Count == 1 ||
GameMain.Client.IsServerOwner ||
GameMain.Client.ConnectedClients.None(c =>
c.InGame && (c.IsOwner || c.HasPermission(ClientPermissions.ManageCampaign)));
}
public override void Draw(SpriteBatch spriteBatch)
{
if (overlayColor.A > 0)
{
if (overlaySprite != null)
{
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.Black * (overlayColor.A / 255.0f), isFilled: true);
float scale = Math.Max(GameMain.GraphicsWidth / overlaySprite.size.X, GameMain.GraphicsHeight / overlaySprite.size.Y);
overlaySprite.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2, overlayColor, overlaySprite.size / 2, scale: scale);
}
else
{
GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), overlayColor, isFilled: true);
}
if (!string.IsNullOrEmpty(overlayText) && overlayTextColor.A > 0)
{
var backgroundSprite = GUI.Style.GetComponentStyle("CommandBackground").GetDefaultSprite();
Vector2 centerPos = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2;
backgroundSprite.Draw(spriteBatch,
centerPos,
Color.White * (overlayTextColor.A / 255.0f),
origin: backgroundSprite.size / 2,
rotate: 0.0f,
scale: new Vector2(1.5f, 0.7f) * (GameMain.GraphicsWidth / 3 / backgroundSprite.size.X));
string wrappedText = ToolBox.WrapText(overlayText, GameMain.GraphicsWidth / 3, GUI.Font);
Vector2 textSize = GUI.Font.MeasureString(wrappedText);
Vector2 textPos = centerPos - textSize / 2;
GUI.DrawString(spriteBatch, textPos + Vector2.One, wrappedText, Color.Black * (overlayTextColor.A / 255.0f));
GUI.DrawString(spriteBatch, textPos, wrappedText, overlayTextColor);
if (!string.IsNullOrEmpty(overlayTextBottom))
{
Vector2 bottomTextPos = centerPos + new Vector2(0.0f, textSize.Y + 30 * GUI.Scale) - GUI.Font.MeasureString(overlayTextBottom) / 2;
GUI.DrawString(spriteBatch, bottomTextPos + Vector2.One, overlayTextBottom, Color.Black * (overlayTextColor.A / 255.0f));
GUI.DrawString(spriteBatch, bottomTextPos, overlayTextBottom, overlayTextColor);
}
}
}
if (GUI.DisableHUD || GUI.DisableUpperHUD || ForceMapUI || CoroutineManager.IsCoroutineRunning("LevelTransition"))
{
endRoundButton.Visible = false;
return;
}
if (Submarine.MainSub == null) { return; }
endRoundButton.Visible = false;
var availableTransition = GetAvailableTransition(out _, out Submarine leavingSub);
string buttonText = "";
switch (availableTransition)
{
case TransitionType.ProgressToNextLocation:
case TransitionType.ProgressToNextEmptyLocation:
if (Level.Loaded.EndOutpost == null || !Level.Loaded.EndOutpost.DockedTo.Contains(leavingSub))
{
buttonText = TextManager.GetWithVariable("EnterLocation", "[locationname]", Level.Loaded.EndLocation?.Name ?? "[ERROR]");
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
}
break;
case TransitionType.LeaveLocation:
// not sure why this can happen at an outpost but it apparently can in multiplayer
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
break;
case TransitionType.ReturnToPreviousLocation:
case TransitionType.ReturnToPreviousEmptyLocation:
if (Level.Loaded.StartOutpost == null || !Level.Loaded.StartOutpost.DockedTo.Contains(leavingSub))
{
buttonText = TextManager.GetWithVariable("EnterLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
}
break;
case TransitionType.None:
default:
if (Level.Loaded.Type == LevelData.LevelType.Outpost &&
(Character.Controlled?.Submarine?.Info.Type == SubmarineType.Player || (Character.Controlled?.CurrentHull?.OutpostModuleTags?.Contains("airlock") ?? false)))
{
buttonText = TextManager.GetWithVariable("LeaveLocation", "[locationname]", Level.Loaded.StartLocation?.Name ?? "[ERROR]");
endRoundButton.Visible = !ForceMapUI && !ShowCampaignUI;
}
else
{
endRoundButton.Visible = false;
}
break;
}
if (endRoundButton.Visible)
{
endRoundButton.Text = ToolBox.LimitString(buttonText, endRoundButton.Font, endRoundButton.Rect.Width - 5);
if (endRoundButton.Text != buttonText)
{
endRoundButton.ToolTip = buttonText;
}
endRoundButton.Enabled = AllowedToEndRound();
}
endRoundButton.DrawManually(spriteBatch);
}
public Task SelectSummaryScreen(RoundSummary roundSummary, LevelData newLevel, bool mirror, Action action)
{
var roundSummaryScreen = RoundSummaryScreen.Select(overlaySprite, roundSummary);
GUI.ClearCursorWait();
var loadTask = Task.Run(async () =>
{
await Task.Yield();
Rand.ThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror);
Rand.ThreadId = 0;
});
TaskPool.Add("AsyncCampaignStartRound", loadTask, (t) =>
{
overlayColor = Color.Transparent;
action?.Invoke();
});
return loadTask;
}
partial void NPCInteractProjSpecific(Character npc, Character interactor)
{
if (npc == null || interactor == null) { return; }
switch (npc.CampaignInteractionType)
{
case InteractionType.None:
case InteractionType.Talk:
return;
case InteractionType.Upgrade when !UpgradeManager.CanUpgradeSub():
UpgradeManager.CreateUpgradeErrorMessage(TextManager.Get("Dialog.CantUpgrade"), IsSinglePlayer, npc);
return;
case InteractionType.Crew when GameMain.NetworkMember != null:
CampaignUI.CrewManagement.SendCrewState(false);
goto default;
default:
ShowCampaignUI = true;
CampaignUI.SelectTab(npc.CampaignInteractionType);
CampaignUI.UpgradeStore?.RefreshAll();
break;
}
}
public override void AddToGUIUpdateList()
{
if (ShowCampaignUI || ForceMapUI)
{
campaignUIContainer?.AddToGUIUpdateList();
if (CampaignUI?.UpgradeStore?.HoveredItem != null)
{
if (CampaignUI.SelectedTab != InteractionType.Upgrade) { return; }
CampaignUI?.UpgradeStore?.ItemInfoFrame.AddToGUIUpdateList(order: 1);
}
}
base.AddToGUIUpdateList();
CrewManager.AddToGUIUpdateList();
endRoundButton.AddToGUIUpdateList();
}
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
{
GUIMessageBox.MessageBoxes.RemoveAll(mb => mb.UserData is RoundSummary);
}
if (ShowCampaignUI || ForceMapUI)
{
CampaignUI?.Update(deltaTime);
}
}
}
}

View File

@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Barotrauma
{
internal partial class CampaignMetadata
{
private const int MaxDrawnElements = 12;
public void DebugDraw(SpriteBatch spriteBatch, Vector2 pos, int debugDrawMetadataOffset, string[] ignoredMetadataInfo)
{
var campaignData = data;
foreach (string ignored in ignoredMetadataInfo)
{
if (!string.IsNullOrWhiteSpace(ignored))
{
campaignData = campaignData.Where(pair => !pair.Key.StartsWith(ignored)).ToDictionary(i => i.Key, i => i.Value);
}
}
int offset = 0;;
if (campaignData.Count > 0)
{
offset = debugDrawMetadataOffset % campaignData.Count;
if (offset < 0) { offset += campaignData.Count; }
}
var text = "Campaign metadata:\n";
int max = 0;
for (int i = offset; i < campaignData.Count + offset; i++)
{
int index = i;
if (index >= campaignData.Count) { index -= campaignData.Count; }
var (key, value) = campaignData.ElementAt(index);
if (max < MaxDrawnElements)
{
text += $"{key.ColorizeObject()}: {value.ColorizeObject()}\n";
max++;
}
else
{
text += "Use arrow keys to scroll";
break;
}
}
text = text.TrimEnd('\n');
List<RichTextData> richTextDatas = RichTextData.GetRichTextData(text, out text) ?? new List<RichTextData>();
Vector2 size = GUI.SmallFont.MeasureString(text);
Vector2 infoPos = new Vector2(GameMain.GraphicsWidth - size.X - 16, pos.Y + 8);
Rectangle infoRect = new Rectangle(infoPos.ToPoint(), size.ToPoint());
infoRect.Inflate(8, 8);
GUI.DrawRectangle(spriteBatch, infoRect, Color.Black * 0.8f, isFilled: true);
GUI.DrawRectangle(spriteBatch, infoRect, Color.White * 0.8f);
if (richTextDatas.Any())
{
GUI.DrawStringWithColors(spriteBatch, infoPos, text, Color.White, richTextDatas, font: GUI.SmallFont);
}
else
{
GUI.DrawString(spriteBatch, infoPos, text, Color.White, font: GUI.SmallFont);
}
float y = infoRect.Bottom + 16;
if (Campaign.Factions != null)
{
const string factionHeader = "Reputations";
Vector2 factionHeaderSize = GUI.SubHeadingFont.MeasureString(factionHeader);
Vector2 factionPos = new Vector2(GameMain.GraphicsWidth - (264 / 2) - factionHeaderSize.X / 2, y);
GUI.DrawString(spriteBatch, factionPos, factionHeader, Color.White, font: GUI.SubHeadingFont);
y += factionHeaderSize.Y + 8;
foreach (Faction faction in Campaign.Factions)
{
string name = faction.Prefab.Name;
Vector2 nameSize = GUI.SmallFont.MeasureString(name);
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 264, y), name, Color.White, font: GUI.SmallFont);
y += nameSize.Y + 5;
Color color = ToolBox.GradientLerp(faction.Reputation.NormalizedValue, Color.Red, Color.Yellow, Color.LightGreen);
GUI.DrawRectangle(spriteBatch, new Rectangle(GameMain.GraphicsWidth - 264, (int) y, (int)(faction.Reputation.NormalizedValue * 255), 10), color, isFilled: true);
GUI.DrawRectangle(spriteBatch, new Rectangle(GameMain.GraphicsWidth - 264, (int) y, 256, 10), Color.White);
y += 15;
}
}
Location location = Campaign.Map?.CurrentLocation;
if (location?.Reputation != null)
{
string name = Campaign.Map?.CurrentLocation.Name;
Vector2 nameSize = GUI.SmallFont.MeasureString(name);
GUI.DrawString(spriteBatch, new Vector2(GameMain.GraphicsWidth - 264, y), name, Color.White, font: GUI.SmallFont);
y += nameSize.Y + 5;
float normalizedReputation = MathUtils.InverseLerp(location.Reputation.MinReputation, location.Reputation.MaxReputation, location.Reputation.Value);
Color color = ToolBox.GradientLerp(normalizedReputation, Color.Red, Color.Yellow, Color.LightGreen);
GUI.DrawRectangle(spriteBatch, new Rectangle(GameMain.GraphicsWidth - 264, (int) y, (int)(normalizedReputation * 255), 10), color, isFilled: true);
GUI.DrawRectangle(spriteBatch, new Rectangle(GameMain.GraphicsWidth - 264, (int) y, 256, 10), Color.White);
}
richTextDatas.Clear();
}
}
}

View File

@@ -1,7 +1,9 @@
using Barotrauma.Networking;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
@@ -11,14 +13,30 @@ namespace Barotrauma
{
public bool SuppressStateSending = false;
private UInt16 startWatchmanID, endWatchmanID;
private UInt16 pendingSaveID = 1;
public UInt16 PendingSaveID
{
get
{
return pendingSaveID;
}
set
{
pendingSaveID = value;
//pending save ID 0 means "no save received yet"
//save IDs are always above 0, so we should never be waiting for 0
if (pendingSaveID == 0) { pendingSaveID++; }
}
}
public static void StartCampaignSetup(IEnumerable<string> saveFiles)
{
var parent = GameMain.NetLobbyScreen.CampaignSetupFrame;
parent.ClearChildren();
parent.Visible = true;
GameMain.NetLobbyScreen.HighlightMode(2);
GameMain.NetLobbyScreen.HighlightMode(
GameMain.NetLobbyScreen.ModeList.Content.GetChildIndex(GameMain.NetLobbyScreen.ModeList.Content.GetChildByUserData(GameModePreset.MultiPlayerCampaign)));
var layout = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform, Anchor.Center))
{
@@ -38,7 +56,7 @@ namespace Barotrauma
var newCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null);
var loadCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null);
var campaignSetupUI = new CampaignSetupUI(true, newCampaignContainer, loadCampaignContainer, null, saveFiles);
GameMain.NetLobbyScreen.CampaignSetupUI = new CampaignSetupUI(true, newCampaignContainer, loadCampaignContainer, null, saveFiles);
var newCampaignButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainer.RectTransform),
TextManager.Get("NewCampaign"), style: "GUITabButton")
@@ -68,94 +86,488 @@ namespace Barotrauma
loadCampaignContainer.Visible = false;
GUITextBlock.AutoScaleAndNormalize(newCampaignButton.TextBlock, loadCampaignButton.TextBlock);
GameMain.NetLobbyScreen.CampaignSetupUI.StartNewGame = GameMain.Client.SetupNewCampaign;
GameMain.NetLobbyScreen.CampaignSetupUI.LoadGame = GameMain.Client.SetupLoadCampaign;
}
partial void InitProjSpecific()
{
var buttonContainer = new GUILayoutGroup(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ButtonAreaTop, GUICanvas.Instance),
isHorizontal: true, childAnchor: Anchor.CenterRight)
{
CanBeFocused = false
};
campaignSetupUI.StartNewGame = GameMain.Client.SetupNewCampaign;
campaignSetupUI.LoadGame = GameMain.Client.SetupLoadCampaign;
int buttonHeight = (int)(GUI.Scale * 40);
int buttonWidth = GUI.IntScale(200);
endRoundButton = new GUIButton(HUDLayoutSettings.ToRectTransform(new Rectangle((GameMain.GraphicsWidth / 2) - (buttonWidth / 2), HUDLayoutSettings.ButtonAreaTop.Center.Y - (buttonHeight / 2), buttonWidth, buttonHeight), GUICanvas.Instance),
TextManager.Get("EndRound"), textAlignment: Alignment.Center, style: "EndRoundButton")
{
Pulse = true,
TextBlock =
{
Shadow = true,
AutoScaleHorizontal = true
},
OnClicked = (btn, userdata) =>
{
var availableTransition = GetAvailableTransition(out _, out _);
if (Character.Controlled != null &&
availableTransition == TransitionType.ReturnToPreviousLocation &&
Character.Controlled?.Submarine == Level.Loaded?.StartOutpost)
{
GameMain.Client.RequestStartRound();
}
else if (Character.Controlled != null &&
availableTransition == TransitionType.ProgressToNextLocation &&
Character.Controlled?.Submarine == Level.Loaded?.EndOutpost)
{
GameMain.Client.RequestStartRound();
}
else
{
ShowCampaignUI = true;
if (CampaignUI == null) { InitCampaignUI(); }
CampaignUI.SelectTab(InteractionType.Map);
}
return true;
}
};
buttonContainer.Recalculate();
}
private void InitCampaignUI()
{
campaignUIContainer = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black);
CampaignUI = new CampaignUI(this, campaignUIContainer)
{
StartRound = () =>
{
GameMain.Client.RequestStartRound();
}
};
}
public override void Start()
{
base.Start();
CoroutineManager.StartCoroutine(DoInitialCameraTransition(), "MultiplayerCampaign.DoInitialCameraTransition");
}
protected override void LoadInitialLevel()
{
//clients should never call this
throw new InvalidOperationException("");
}
private IEnumerable<object> DoInitialCameraTransition()
{
while (GameMain.Instance.LoadingScreenOpen)
{
yield return CoroutineStatus.Running;
}
if (GameMain.Client.LateCampaignJoin)
{
GameMain.Client.LateCampaignJoin = false;
yield return CoroutineStatus.Success;
}
Character prevControlled = Character.Controlled;
if (prevControlled?.AIController != null)
{
prevControlled.AIController.Enabled = false;
}
GUI.DisableHUD = true;
if (IsFirstRound)
{
Character.Controlled = null;
if (prevControlled != null)
{
prevControlled.ClearInputs();
}
overlayColor = Color.LightGray;
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
overlayTextColor = Color.Transparent;
overlayText = TextManager.GetWithVariables("campaignstart",
new string[] { "xxxx", "yyyy" },
new string[] { Map.CurrentLocation.Name, TextManager.Get("submarineclass." + Submarine.MainSub.Info.SubmarineClass) });
float fadeInDuration = 1.0f;
float textDuration = 10.0f;
float timer = 0.0f;
while (timer < textDuration)
{
// Try to grab the controlled here to prevent inputs, assigned late on multiplayer
if (Character.Controlled != null)
{
prevControlled = Character.Controlled;
Character.Controlled = null;
prevControlled.ClearInputs();
}
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
yield return CoroutineStatus.Running;
}
var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
null, null,
fadeOut: false,
duration: 5,
startZoom: 1.5f, endZoom: 1.0f)
{
AllowInterrupt = true,
RemoveControlFromCharacter = false
};
fadeInDuration = 1.0f;
timer = 0.0f;
overlayTextColor = Color.Transparent;
overlayText = "";
while (timer < fadeInDuration)
{
overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
timer += CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running;
}
overlayColor = Color.Transparent;
while (transition.Running)
{
yield return CoroutineStatus.Running;
}
if (prevControlled != null)
{
Character.Controlled = prevControlled;
}
}
else
{
var transition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam,
null, null,
fadeOut: false,
duration: 5,
startZoom: 0.5f, endZoom: 1.0f)
{
AllowInterrupt = true,
RemoveControlFromCharacter = true
};
while (transition.Running)
{
yield return CoroutineStatus.Running;
}
}
if (prevControlled != null)
{
prevControlled.SelectedConstruction = null;
if (prevControlled.AIController != null)
{
prevControlled.AIController.Enabled = true;
}
}
GUI.DisableHUD = false;
yield return CoroutineStatus.Success;
}
protected override IEnumerable<object> DoLevelTransition(TransitionType transitionType, LevelData newLevel, Submarine leavingSub, bool mirror, List<TraitorMissionResult> traitorResults = null)
{
yield return CoroutineStatus.Success;
}
private IEnumerable<object> DoLevelTransition()
{
SoundPlayer.OverrideMusicType = CrewManager.GetCharacters().Any(c => !c.IsDead) ? "endround" : "crewdead";
SoundPlayer.OverrideMusicDuration = 18.0f;
Level prevLevel = Level.Loaded;
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
crewDead = false;
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
if (continueButton != null)
{
continueButton.Visible = false;
}
Character.Controlled = null;
yield return new WaitForSeconds(0.1f);
GameMain.Client.EndCinematic?.Stop();
var endTransition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null,
Alignment.Center,
fadeOut: false,
duration: EndTransitionDuration);
GameMain.Client.EndCinematic = endTransition;
Location portraitLocation = Map?.SelectedLocation ?? Map?.CurrentLocation ?? Level.Loaded?.StartLocation;
if (portraitLocation != null)
{
overlaySprite = portraitLocation.Type.GetPortrait(portraitLocation.PortraitId);
}
float fadeOutDuration = endTransition.Duration;
float t = 0.0f;
while (t < fadeOutDuration || endTransition.Running)
{
t += CoroutineManager.UnscaledDeltaTime;
overlayColor = Color.Lerp(Color.Transparent, Color.White, t / fadeOutDuration);
yield return CoroutineStatus.Running;
}
overlayColor = Color.White;
yield return CoroutineStatus.Running;
//--------------------------------------
//wait for the new level to be loaded
DateTime timeOut = DateTime.Now + new TimeSpan(0, 0, seconds: 30);
while (Level.Loaded == prevLevel || Level.Loaded == null)
{
if (DateTime.Now > timeOut || Screen.Selected != GameMain.GameScreen) { break; }
yield return CoroutineStatus.Running;
}
endTransition.Stop();
overlayColor = Color.Transparent;
if (DateTime.Now > timeOut) { GameMain.NetLobbyScreen.Select(); }
if (!(Screen.Selected is RoundSummaryScreen))
{
if (continueButton != null)
{
continueButton.Visible = true;
}
}
yield return CoroutineStatus.Success;
}
public override void Update(float deltaTime)
{
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || Level.Loaded == null) { return; }
if (ShowCampaignUI || ForceMapUI)
{
if (CampaignUI == null) { InitCampaignUI(); }
Character.DisableControls = true;
}
base.Update(deltaTime);
if (startWatchmanID > 0 && startWatchman == null)
if (PlayerInput.RightButtonClicked() ||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
{
startWatchman = Entity.FindEntityByID(startWatchmanID) as Character;
if (startWatchman != null) { InitializeWatchman(startWatchman); }
ShowCampaignUI = false;
if (GUIMessageBox.VisibleBox?.UserData is RoundSummary roundSummary &&
roundSummary.ContinueButton != null &&
roundSummary.ContinueButton.Visible)
{
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
}
}
if (endWatchmanID > 0 && endWatchman == null)
if (!GUI.DisableHUD && !GUI.DisableUpperHUD)
{
endWatchman = Entity.FindEntityByID(endWatchmanID) as Character;
if (endWatchman != null) { InitializeWatchman(endWatchman); }
endRoundButton.UpdateManually(deltaTime);
if (CoroutineManager.IsCoroutineRunning("LevelTransition") || ForceMapUI) { return; }
}
if (Level.Loaded.Type == LevelData.LevelType.Outpost)
{
if (wasDocked)
{
var connectedSubs = Submarine.MainSub.GetConnectedSubs();
bool isDocked = Level.Loaded.StartOutpost != null && connectedSubs.Contains(Level.Loaded.StartOutpost);
if (!isDocked)
{
//undocked from outpost, need to choose a destination
ForceMapUI = true;
if (CampaignUI == null) { InitCampaignUI(); }
CampaignUI.SelectTab(InteractionType.Map);
}
}
else
{
//wasn't initially docked (sub doesn't have a docking port?)
// -> choose a destination when the sub is far enough from the start outpost
if (!Submarine.MainSub.AtStartPosition)
{
ForceMapUI = true;
if (CampaignUI == null) { InitCampaignUI(); }
CampaignUI.SelectTab(InteractionType.Map);
}
}
if (CampaignUI == null) { InitCampaignUI(); }
}
}
protected override void WatchmanInteract(Character watchman, Character interactor)
public override void End(TransitionType transitionType = TransitionType.None)
{
if ((watchman.Submarine == Level.Loaded.StartOutpost && !Submarine.MainSub.AtStartPosition) ||
(watchman.Submarine == Level.Loaded.EndOutpost && !Submarine.MainSub.AtEndPosition))
base.End(transitionType);
ForceMapUI = ShowCampaignUI = false;
UpgradeManager.CanUpgrade = true;
// remove all event dialogue boxes
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
{
return;
if (mb is GUIMessageBox msgBox)
{
if (mb.UserData is Pair<string, ushort> pair && pair.First.Equals("conversationaction", StringComparison.OrdinalIgnoreCase))
{
msgBox.Close();
}
}
});
if (transitionType == TransitionType.End)
{
EndCampaign();
}
else
{
IsFirstRound = false;
CoroutineManager.StartCoroutine(DoLevelTransition(), "LevelTransition");
}
}
protected override void EndCampaignProjSpecific()
{
if (GUIMessageBox.VisibleBox?.UserData is RoundSummary roundSummary)
{
GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.VisibleBox);
}
CoroutineManager.StartCoroutine(DoEndCampaignCameraTransition(), "DoEndCampaignCameraTransition");
GameMain.CampaignEndScreen.OnFinished = () =>
{
GameMain.NetLobbyScreen.Select();
if (GameMain.NetLobbyScreen.ContinueCampaignButton != null) { GameMain.NetLobbyScreen.ContinueCampaignButton.Enabled = false; }
if (GameMain.NetLobbyScreen.QuitCampaignButton != null) { GameMain.NetLobbyScreen.QuitCampaignButton.Enabled = false; }
};
}
private IEnumerable<object> DoEndCampaignCameraTransition()
{
Character controlled = Character.Controlled;
if (controlled != null)
{
controlled.AIController.Enabled = false;
}
if (GUIMessageBox.MessageBoxes.Any(mbox => mbox.UserData as string == "watchmanprompt"))
{
return;
}
GUI.DisableHUD = true;
ISpatialEntity endObject = Level.Loaded.LevelObjectManager.GetAllObjects().FirstOrDefault(obj => obj.Prefab.SpawnPos == LevelObjectPrefab.SpawnPosType.LevelEnd);
var transition = new CameraTransition(endObject ?? Submarine.MainSub, GameMain.GameScreen.Cam,
null, Alignment.Center,
fadeOut: true,
duration: 10,
startZoom: null, endZoom: 0.2f);
if (GameMain.Client != null && interactor == Character.Controlled)
while (transition.Running)
{
var msgBox = new GUIMessageBox("", TextManager.GetWithVariable("CampaignEnterOutpostPrompt", "[locationname]",
Submarine.MainSub.AtStartPosition ? Map.CurrentLocation.Name : Map.SelectedLocation.Name),
new string[] { TextManager.Get("Yes"), TextManager.Get("No") })
{
UserData = "watchmanprompt"
};
msgBox.Buttons[0].OnClicked = (btn, userdata) =>
{
GameMain.Client.RequestRoundEnd();
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
yield return CoroutineStatus.Running;
}
GameMain.CampaignEndScreen.Select();
GUI.DisableHUD = false;
yield return CoroutineStatus.Success;
}
public void ClientWrite(IWriteMessage msg)
{
System.Diagnostics.Debug.Assert(map.Locations.Count < UInt16.MaxValue);
msg.Write(map.CurrentLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.CurrentLocationIndex);
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
msg.Write(map.SelectedMissionIndex == -1 ? byte.MaxValue : (byte)map.SelectedMissionIndex);
msg.Write(PurchasedHullRepairs);
msg.Write(PurchasedItemRepairs);
msg.Write(PurchasedLostShuttles);
msg.Write((UInt16)CargoManager.ItemsInBuyCrate.Count);
foreach (PurchasedItem pi in CargoManager.ItemsInBuyCrate)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, 100);
}
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
{
msg.Write(pi.ItemPrefab.Identifier);
msg.WriteRangedInteger(pi.Quantity, 0, 100);
}
msg.Write((UInt16)CargoManager.SoldItems.Count);
foreach (SoldItem si in CargoManager.SoldItems)
{
msg.Write(si.ItemPrefab.Identifier);
msg.Write((UInt16)si.ID);
msg.Write(si.Removed);
msg.Write(si.SellerID);
}
msg.Write((ushort)UpgradeManager.PurchasedUpgrades.Count);
foreach (var (prefab, category, level) in UpgradeManager.PurchasedUpgrades)
{
msg.Write(prefab.Identifier);
msg.Write(category.Identifier);
msg.Write((byte)level);
}
}
//static because we may need to instantiate the campaign if it hasn't been done yet
public static void ClientRead(IReadMessage msg)
{
byte campaignID = msg.ReadByte();
UInt16 updateID = msg.ReadUInt16();
UInt16 saveID = msg.ReadUInt16();
string mapSeed = msg.ReadString();
UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16();
byte selectedMissionIndex = msg.ReadByte();
bool isFirstRound = msg.ReadBoolean();
byte campaignID = msg.ReadByte();
UInt16 updateID = msg.ReadUInt16();
UInt16 saveID = msg.ReadUInt16();
string mapSeed = msg.ReadString();
UInt16 currentLocIndex = msg.ReadUInt16();
UInt16 selectedLocIndex = msg.ReadUInt16();
byte selectedMissionIndex = msg.ReadByte();
float? reputation = null;
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
Dictionary<string, float> factionReps = new Dictionary<string, float>();
byte factionsCount = msg.ReadByte();
for (int i = 0; i < factionsCount; i++)
{
factionReps.Add(msg.ReadString(), msg.ReadSingle());
}
UInt16 startWatchmanID = msg.ReadUInt16();
UInt16 endWatchmanID = msg.ReadUInt16();
bool forceMapUI = msg.ReadBoolean();
int money = msg.ReadInt32();
bool purchasedHullRepairs = msg.ReadBoolean();
bool purchasedItemRepairs = msg.ReadBoolean();
bool purchasedLostShuttles = msg.ReadBoolean();
bool purchasedHullRepairs = msg.ReadBoolean();
bool purchasedItemRepairs = msg.ReadBoolean();
bool purchasedLostShuttles = msg.ReadBoolean();
byte missionCount = msg.ReadByte();
List<Pair<string, byte>> availableMissions = new List<Pair<string, byte>>();
for (int i = 0; i < missionCount; i++)
{
string missionIdentifier = msg.ReadString();
byte connectionIndex = msg.ReadByte();
availableMissions.Add(new Pair<string, byte>(missionIdentifier, connectionIndex));
}
UInt16? storeBalance = null;
if (msg.ReadBoolean())
{
storeBalance = msg.ReadUInt16();
}
UInt16 buyCrateItemCount = msg.ReadUInt16();
List<PurchasedItem> buyCrateItems = new List<PurchasedItem>();
for (int i = 0; i < buyCrateItemCount; i++)
{
string itemPrefabIdentifier = msg.ReadString();
int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
}
UInt16 purchasedItemCount = msg.ReadUInt16();
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
@@ -166,65 +578,129 @@ namespace Barotrauma
purchasedItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
}
UInt16 soldItemCount = msg.ReadUInt16();
List<SoldItem> soldItems = new List<SoldItem>();
for (int i = 0; i < soldItemCount; i++)
{
string itemPrefabIdentifier = msg.ReadString();
UInt16 id = msg.ReadUInt16();
bool removed = msg.ReadBoolean();
byte sellerId = msg.ReadByte();
soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId));
}
ushort pendingUpgradeCount = msg.ReadUInt16();
List<PurchasedUpgrade> pendingUpgrades = new List<PurchasedUpgrade>();
for (int i = 0; i < pendingUpgradeCount; i++)
{
string upgradeIdentifier = msg.ReadString();
UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier);
string categoryIdentifier = msg.ReadString();
UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier);
int upgradeLevel = msg.ReadByte();
if (prefab == null || category == null) { continue; }
pendingUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel));
}
bool hasCharacterData = msg.ReadBoolean();
CharacterInfo myCharacterInfo = null;
if (hasCharacterData)
{
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
}
MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
if (campaign == null || campaignID != campaign.CampaignID)
if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaignID != campaign.CampaignID)
{
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
GameMain.GameSession = new GameSession(null, savePath,
GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign"));
campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode);
GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, mapSeed);
campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
campaign.CampaignID = campaignID;
campaign.GenerateMap(mapSeed);
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
//server has a newer save file
if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID))
{
/*//stop any active campaign save transfers, they're outdated now
List<FileReceiver.FileTransferIn> saveTransfers =
GameMain.Client.FileReceiver.ActiveTransfers.FindAll(t => t.FileType == FileTransferType.CampaignSave);
foreach (var transfer in saveTransfers)
{
GameMain.Client.FileReceiver.StopTransfer(transfer);
}
GameMain.Client.RequestFile(FileTransferType.CampaignSave, null, null);*/
campaign.PendingSaveID = saveID;
}
if (NetIdUtils.IdMoreRecent(updateID, campaign.lastUpdateID))
{
campaign.SuppressStateSending = true;
campaign.IsFirstRound = isFirstRound;
//we need to have the latest save file to display location/mission/store
if (campaign.LastSaveID == saveID)
{
campaign.ForceMapUI = forceMapUI;
UpgradeStore.WaitForServerUpdate = false;
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
campaign.Map.SelectMission(selectedMissionIndex);
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
campaign.CargoManager.SetPurchasedItems(purchasedItems);
campaign.CargoManager.SetSoldItems(soldItems);
if (storeBalance.HasValue) { campaign.Map.CurrentLocation.StoreCurrentBalance = storeBalance.Value; }
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
campaign.UpgradeManager.PurchasedUpgrades.Clear();
foreach (var (identifier, rep) in factionReps)
{
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
if (faction?.Reputation != null)
{
faction.Reputation.Value = rep;
}
else
{
DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\".");
}
}
if (reputation.HasValue)
{
campaign.Map.CurrentLocation.Reputation.Value = reputation.Value;
campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
}
foreach (var availableMission in availableMissions)
{
MissionPrefab missionPrefab = MissionPrefab.List.Find(mp => mp.Identifier == availableMission.First);
if (missionPrefab == null)
{
DebugConsole.ThrowError($"Error when receiving campaign data from the server: mission prefab \"{availableMission.First}\" not found.");
continue;
}
if (availableMission.Second < 0 || availableMission.Second >= campaign.Map.CurrentLocation.Connections.Count)
{
DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.First}\" out of range (index: {availableMission.Second}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
continue;
}
LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.Second];
campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
}
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
}
campaign.startWatchmanID = startWatchmanID;
campaign.endWatchmanID = endWatchmanID;
bool shouldRefresh = campaign.Money != money ||
campaign.PurchasedHullRepairs != purchasedHullRepairs ||
campaign.PurchasedItemRepairs != purchasedItemRepairs ||
campaign.PurchasedLostShuttles != purchasedLostShuttles;
campaign.Money = money;
campaign.PurchasedHullRepairs = purchasedHullRepairs;
campaign.PurchasedItemRepairs = purchasedItemRepairs;
campaign.PurchasedLostShuttles = purchasedLostShuttles;
if (shouldRefresh)
{
campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
}
if (myCharacterInfo != null)
{
GameMain.Client.CharacterInfo = myCharacterInfo;
@@ -240,9 +716,68 @@ namespace Barotrauma
}
}
public void ClientReadCrew(IReadMessage msg)
{
ushort availableHireLength = msg.ReadUInt16();
List<CharacterInfo> availableHires = new List<CharacterInfo>();
for (int i = 0; i < availableHireLength; i++)
{
CharacterInfo hire = CharacterInfo.ClientRead("human", msg);
hire.Salary = msg.ReadInt32();
availableHires.Add(hire);
}
ushort pendingHireLength = msg.ReadUInt16();
List<int> pendingHires = new List<int>();
for (int i = 0; i < pendingHireLength; i++)
{
pendingHires.Add(msg.ReadInt32());
}
bool validateHires = msg.ReadBoolean();
bool fireCharacter = msg.ReadBoolean();
int firedIdentifier = -1;
if (fireCharacter) { firedIdentifier = msg.ReadInt32(); }
if (fireCharacter)
{
CharacterInfo firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
// this one might and is allowed to be null since the character is already fired on the original sender's game
if (firedCharacter != null) { CrewManager.FireCharacter(firedCharacter); }
}
if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null)
{
CampaignUI?.CrewManagement?.SetHireables(map.CurrentLocation, availableHires);
if (validateHires) { CampaignUI?.CrewManagement.ValidatePendingHires(); }
CampaignUI?.CrewManagement?.SetPendingHires(pendingHires, map?.CurrentLocation);
if (fireCharacter) { CampaignUI?.CrewManagement.UpdateCrew(); }
}
}
public override void Save(XElement element)
{
//do nothing, the clients get the save files from the server
}
public void LoadState(string filePath)
{
DebugConsole.Log($"Loading save file for an existing game session ({filePath})");
SaveUtil.DecompressToDirectory(filePath, SaveUtil.TempPath, null);
string gamesessionDocPath = Path.Combine(SaveUtil.TempPath, "gamesession.xml");
XDocument doc = XMLExtensions.TryLoadXml(gamesessionDocPath);
if (doc == null)
{
DebugConsole.ThrowError($"Failed to load the state of a multiplayer campaign. Could not open the file \"{gamesessionDocPath}\".");
return;
}
Load(doc.Root.Element("MultiPlayerCampaign"));
SubmarineInfo selectedSub;
GameMain.GameSession.OwnedSubmarines = SaveUtil.LoadOwnedSubmarines(doc, out selectedSub);
GameMain.GameSession.SubmarineInfo = selectedSub;
}
}
}

View File

@@ -1,80 +0,0 @@
using Barotrauma.Tutorials;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class SubTestMode : GameMode
{
public SubTestMode(GameModePreset preset, object param)
: base(preset, param)
{
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
{
for (int i = 0; i < jobPrefab.InitialCount; i++)
{
var variant = Rand.Range(0, jobPrefab.Variants);
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: jobPrefab, variant: variant));
}
}
}
public override void Start()
{
base.Start();
isRunning = true;
CrewManager.InitSinglePlayerRound();
Submarine.MainSub.SetPosition(Vector2.Zero);
}
public override void Draw(SpriteBatch spriteBatch)
{
if (!isRunning|| GUI.DisableHUD || GUI.DisableUpperHUD) return;
if (Submarine.MainSub == null) return;
}
public override void AddToGUIUpdateList()
{
if (!isRunning) return;
base.AddToGUIUpdateList();
CrewManager.AddToGUIUpdateList();
}
public override void Update(float deltaTime)
{
if (!isRunning) { return; }
base.Update(deltaTime);
}
public override void End(string endMessage = "")
{
isRunning = false;
GameMain.GameSession.EndRound("");
CrewManager.EndRound();
Submarine.Unload();
GameMain.SubEditorScreen.Select();
}
private bool EndRound(Submarine leavingSub)
{
isRunning = false;
End("");
return true;
}
}
}

View File

@@ -0,0 +1,34 @@
using Microsoft.Xna.Framework;
using System;
namespace Barotrauma
{
class TestGameMode : GameMode
{
public Action OnRoundEnd;
public TestGameMode(GameModePreset preset) : base(preset)
{
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
{
for (int i = 0; i < jobPrefab.InitialCount; i++)
{
var variant = Rand.Range(0, jobPrefab.Variants);
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: jobPrefab, variant: variant));
}
}
}
public override void Start()
{
base.Start();
CrewManager.InitSinglePlayerRound();
}
public override void End(CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
{
OnRoundEnd?.Invoke();
}
}
}

View File

@@ -614,7 +614,7 @@ namespace Barotrauma.Tutorials
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
GameMain.LightManager.LosEnabled = false;
var cinematic = new RoundEndCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, 5.0f);
var cinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, Alignment.CenterLeft, Alignment.CenterRight, duration: 5.0f);
while (cinematic.Running)
{

View File

@@ -101,6 +101,7 @@ namespace Barotrauma.Tutorials
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("medicaldoctor"));
captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor");
captain_medic.TeamID = Character.TeamType.Team1;
captain_medic.GiveJobItems(null);
captain_medic.CanSpeak = captain_medic.AIController.Enabled = false;
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, false);
@@ -123,14 +124,17 @@ namespace Barotrauma.Tutorials
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("mechanic"));
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic");
captain_mechanic.TeamID = Character.TeamType.Team1;
captain_mechanic.GiveJobItems();
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer");
captain_security.TeamID = Character.TeamType.Team1;
captain_security.GiveJobItems();
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer");
captain_engineer.TeamID = Character.TeamType.Team1;
captain_engineer.GiveJobItems();
captain_mechanic.CanSpeak = captain_security.CanSpeak = captain_engineer.CanSpeak = false;

View File

@@ -80,6 +80,7 @@ namespace Barotrauma.Tutorials
var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant"));
patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1");
patient1.TeamID = Character.TeamType.Team1;
patient1.GiveJobItems(null);
patient1.CanSpeak = false;
patient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 45.0f) }, stun: 0, playSound: false);
@@ -87,22 +88,26 @@ namespace Barotrauma.Tutorials
assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant"));
patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2");
patient2.TeamID = Character.TeamType.Team1;
patient2.GiveJobItems(null);
patient2.CanSpeak = false;
patient2.AIController.Enabled = false;
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3");
subPatient1.TeamID = Character.TeamType.Team1;
subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient1);
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3");
subPatient2.TeamID = Character.TeamType.Team1;
subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient2);
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3");
subPatient3.TeamID = Character.TeamType.Team1;
subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false);
subPatients.Add(subPatient3);

View File

@@ -376,7 +376,7 @@ namespace Barotrauma.Tutorials
}
}
yield return null;
} while (!engineer_brokenJunctionBox.IsFullCondition); // Wait until repaired
} while (engineer_brokenJunctionBox.Condition < repairableJunctionBoxComponent.RepairThreshold); // Wait until repaired
SetHighlight(engineer_brokenJunctionBox, false);
RemoveCompletedObjective(segments[2]);
SetDoorAccess(engineer_thirdDoor, engineer_thirdDoorLight, true);
@@ -408,15 +408,20 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(2f, false);
TriggerTutorialSegment(4); // Repair junction box
while (ContentRunning) yield return null;
SetHighlight(engineer_submarineJunctionBox_1, true);
SetHighlight(engineer_submarineJunctionBox_2, true);
SetHighlight(engineer_submarineJunctionBox_3, true);
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_1, engineer_repairIcon, engineer_repairIconColor);
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_2, engineer_repairIcon, engineer_repairIconColor);
engineer.AddActiveObjectiveEntity(engineer_submarineJunctionBox_3, engineer_repairIcon, engineer_repairIconColor);
SetHighlight(engineer_submarineJunctionBox_1, true);
SetHighlight(engineer_submarineJunctionBox_2, true);
SetHighlight(engineer_submarineJunctionBox_3, true);
Repairable repairableJunctionBoxComponent1 = engineer_submarineJunctionBox_1.GetComponent<Repairable>();
Repairable repairableJunctionBoxComponent2 = engineer_submarineJunctionBox_2.GetComponent<Repairable>();
Repairable repairableJunctionBoxComponent3 = engineer_submarineJunctionBox_3.GetComponent<Repairable>();
// Remove highlights when each individual machine is repaired
do { CheckJunctionBoxHighlights(); yield return null; } while (!engineer_submarineJunctionBox_1.IsFullCondition || !engineer_submarineJunctionBox_2.IsFullCondition || !engineer_submarineJunctionBox_3.IsFullCondition);
CheckJunctionBoxHighlights();
do { CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3); yield return null; } while (engineer_submarineJunctionBox_1.Condition < repairableJunctionBoxComponent1.RepairThreshold || engineer_submarineJunctionBox_2.Condition < repairableJunctionBoxComponent2.RepairThreshold || engineer_submarineJunctionBox_3.Condition < repairableJunctionBoxComponent3.RepairThreshold);
CheckJunctionBoxHighlights(repairableJunctionBoxComponent1, repairableJunctionBoxComponent2, repairableJunctionBoxComponent3);
RemoveCompletedObjective(segments[4]);
yield return new WaitForSeconds(2f, false);
@@ -557,19 +562,19 @@ namespace Barotrauma.Tutorials
}
}
private void CheckJunctionBoxHighlights()
private void CheckJunctionBoxHighlights(Repairable comp1, Repairable comp2, Repairable comp3)
{
if (engineer_submarineJunctionBox_1.IsFullCondition && engineer_submarineJunctionBox_1.ExternalHighlight)
if (engineer_submarineJunctionBox_1.Condition > comp1.RepairThreshold && engineer_submarineJunctionBox_1.ExternalHighlight)
{
SetHighlight(engineer_submarineJunctionBox_1, false);
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_1);
}
if (engineer_submarineJunctionBox_2.IsFullCondition && engineer_submarineJunctionBox_2.ExternalHighlight)
if (engineer_submarineJunctionBox_2.Condition > comp2.RepairThreshold && engineer_submarineJunctionBox_2.ExternalHighlight)
{
SetHighlight(engineer_submarineJunctionBox_2, false);
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_2);
}
if (engineer_submarineJunctionBox_3.IsFullCondition && engineer_submarineJunctionBox_3.ExternalHighlight)
if (engineer_submarineJunctionBox_3.Condition > comp3.RepairThreshold && engineer_submarineJunctionBox_3.ExternalHighlight)
{
SetHighlight(engineer_submarineJunctionBox_3, false);
engineer.RemoveActiveObjectiveEntity(engineer_submarineJunctionBox_3);

View File

@@ -385,7 +385,8 @@ namespace Barotrauma.Tutorials
}
}
if (!gotOxygenTank && mechanic.Inventory.FindItemByIdentifier("oxygentank") != null)
if (!gotOxygenTank && (mechanic.Inventory.FindItemByIdentifier("oxygentank") != null ||
mechanic_deconstructor.InputContainer.Inventory.FindItemByIdentifier("oxygentank") != null))
{
gotOxygenTank = true;
}
@@ -551,7 +552,7 @@ namespace Barotrauma.Tutorials
do
{
yield return null;
if (!mechanic_brokenPump.Item.IsFullCondition)
if (mechanic_brokenPump.Item.Condition < repairablePumpComponent.RepairThreshold)
{
if (!mechanic.HasEquippedItem("wrench"))
{
@@ -575,7 +576,7 @@ namespace Barotrauma.Tutorials
}
}
}
} while (!mechanic_brokenPump.Item.IsFullCondition || mechanic_brokenPump.FlowPercentage >= 0 || !mechanic_brokenPump.IsActive);
} while (mechanic_brokenPump.Item.Condition < repairablePumpComponent.RepairThreshold || mechanic_brokenPump.FlowPercentage >= 0 || !mechanic_brokenPump.IsActive);
RemoveCompletedObjective(segments[9]);
SetHighlight(mechanic_brokenPump.Item, false);
do { yield return null; } while (mechanic_brokenhull_2.WaterPercentage > waterVolumeBeforeOpening);
@@ -592,9 +593,14 @@ namespace Barotrauma.Tutorials
SetHighlight(mechanic_ballastPump_1.Item, true);
SetHighlight(mechanic_ballastPump_2.Item, true);
SetHighlight(mechanic_submarineEngine.Item, true);
Repairable repairablePumpComponent1 = mechanic_ballastPump_1.Item.GetComponent<Repairable>();
Repairable repairablePumpComponent2 = mechanic_ballastPump_2.Item.GetComponent<Repairable>();
Repairable repairableEngineComponent = mechanic_submarineEngine.Item.GetComponent<Repairable>();
// Remove highlights when each individual machine is repaired
do { CheckHighlights(); yield return null; } while (!mechanic_ballastPump_1.Item.IsFullCondition || !mechanic_ballastPump_2.Item.IsFullCondition || !mechanic_submarineEngine.Item.IsFullCondition);
CheckHighlights();
do { CheckHighlights(repairablePumpComponent1, repairablePumpComponent2, repairableEngineComponent); yield return null; } while (mechanic_ballastPump_1.Item.Condition < repairablePumpComponent1.RepairThreshold || mechanic_ballastPump_2.Item.Condition < repairablePumpComponent2.RepairThreshold || mechanic_submarineEngine.Item.Condition < repairableEngineComponent.RepairThreshold);
CheckHighlights(repairablePumpComponent1, repairablePumpComponent2, repairableEngineComponent);
RemoveCompletedObjective(segments[10]);
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Complete"), ChatMessageType.Radio, null);
@@ -617,19 +623,19 @@ namespace Barotrauma.Tutorials
return false;
}
private void CheckHighlights()
private void CheckHighlights(Repairable comp1, Repairable comp2, Repairable comp3)
{
if (mechanic_ballastPump_1.Item.IsFullCondition && mechanic_ballastPump_1.Item.ExternalHighlight)
if (mechanic_ballastPump_1.Item.Condition > comp1.RepairThreshold && mechanic_ballastPump_1.Item.ExternalHighlight)
{
SetHighlight(mechanic_ballastPump_1.Item, false);
mechanic.RemoveActiveObjectiveEntity(mechanic_ballastPump_1.Item);
}
if (mechanic_ballastPump_2.Item.IsFullCondition && mechanic_ballastPump_2.Item.ExternalHighlight)
if (mechanic_ballastPump_2.Item.Condition > comp2.RepairThreshold && mechanic_ballastPump_2.Item.ExternalHighlight)
{
SetHighlight(mechanic_ballastPump_2.Item, false);
mechanic.RemoveActiveObjectiveEntity(mechanic_ballastPump_2.Item);
}
if (mechanic_submarineEngine.Item.IsFullCondition && mechanic_submarineEngine.Item.ExternalHighlight)
if (mechanic_submarineEngine.Item.Condition > comp3.RepairThreshold && mechanic_submarineEngine.Item.ExternalHighlight)
{
SetHighlight(mechanic_submarineEngine.Item, false);
mechanic.RemoveActiveObjectiveEntity(mechanic_submarineEngine.Item);

View File

@@ -322,7 +322,7 @@ namespace Barotrauma.Tutorials
do
{
float distance = Vector2.Distance(officer_coilgunPeriscope.WorldPosition, officer_hammerhead.WorldPosition);
if (distance > originalDistance * 1.5f)
if (distance > originalDistance * 1.5f || officer_hammerhead.WorldPosition.Y > officer_coilgunPeriscope.WorldPosition.Y)
{
// Don't let the Hammerhead go too far.
officer_hammerhead.TeleportTo(officer_hammerheadSpawnPos + new Vector2(0, -1000));

View File

@@ -58,30 +58,31 @@ namespace Barotrauma.Tutorials
{
SubmarineInfo subInfo = new SubmarineInfo(submarinePath);
LevelGenerationParams generationParams = LevelGenerationParams.LevelParams.Find(p => p.Name == levelParams);
LevelGenerationParams generationParams = LevelGenerationParams.LevelParams.Find(p => p.Identifier.Equals(levelParams, StringComparison.OrdinalIgnoreCase));
yield return CoroutineStatus.Running;
GameMain.GameSession = new GameSession(subInfo, "",
GameModePreset.List.Find(g => g.Identifier == "tutorial"));
GameMain.GameSession = new GameSession(subInfo, GameModePreset.Tutorial, missionPrefab: null);
(GameMain.GameSession.GameMode as TutorialMode).Tutorial = this;
if (generationParams != null)
{
Biome biome = LevelGenerationParams.GetBiomes().Find(b => generationParams.AllowedBiomes.Contains(b));
Biome biome =
LevelGenerationParams.GetBiomes().FirstOrDefault(b => generationParams.AllowedBiomes.Contains(b)) ??
LevelGenerationParams.GetBiomes().First();
if (startOutpostPath != string.Empty)
if (!string.IsNullOrEmpty(startOutpostPath))
{
startOutpost = new SubmarineInfo(startOutpostPath);
}
if (endOutpostPath != string.Empty)
if (!string.IsNullOrEmpty(endOutpostPath))
{
endOutpost = new SubmarineInfo(endOutpostPath);
}
Level tutorialLevel = new Level(levelSeed, 0, 0, generationParams, biome, startOutpost, endOutpost);
GameMain.GameSession.StartRound(tutorialLevel);
LevelData tutorialLevel = new LevelData(levelSeed, 0, 0, generationParams, biome);
GameMain.GameSession.StartRound(tutorialLevel, startOutpost: startOutpost, endOutpost: endOutpost);
}
else
{
@@ -100,6 +101,13 @@ namespace Barotrauma.Tutorials
base.Start();
Submarine.MainSub.GodMode = true;
foreach (Structure wall in Structure.WallList)
{
if (wall.Submarine != null && wall.Submarine.Info.IsOutpost)
{
wall.Indestructible = true;
}
}
CharacterInfo charInfo = configElement.Element("Character") == null ?
new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")) :
@@ -114,6 +122,7 @@ namespace Barotrauma.Tutorials
}
character = Character.Create(charInfo, wayPoint.WorldPosition, "", false, false);
character.TeamID = Character.TeamType.Team1;
Character.Controlled = character;
character.GiveJobItems(null);
@@ -126,19 +135,14 @@ namespace Barotrauma.Tutorials
idCard.AddTag("com");
idCard.AddTag("eng");
List<Entity> entities = Entity.GetEntityList();
for (int i = 0; i < entities.Count; i++)
foreach (Item item in Item.ItemList)
{
if (entities[i] is Item)
Door door = item.GetComponent<Door>();
if (door != null)
{
Door door = (entities[i] as Item).GetComponent<Door>();
if (door != null)
{
door.CanBeWelded = false;
}
door.CanBeWelded = false;
}
}
}
tutorialCoroutine = CoroutineManager.StartCoroutine(UpdateState());
}
@@ -284,7 +288,7 @@ namespace Barotrauma.Tutorials
yield return new WaitForSeconds(waitBeforeFade);
var endCinematic = new RoundEndCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, fadeOutTime);
var endCinematic = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam, null, Alignment.Center, duration: fadeOutTime);
currentTutorialCompleted = Completed = true;
while (endCinematic.Running) yield return null;
Stop();

View File

@@ -26,6 +26,7 @@ namespace Barotrauma.Tutorials
protected enum TutorialContentTypes { None = 0, Video = 1, ManualVideo = 2, TextOnly = 3 };
protected string playableContentPath;
protected Point screenResolution;
protected WindowMode windowMode;
protected float prevUIScale;
private GUIFrame holderFrame, objectiveFrame;
@@ -207,7 +208,7 @@ namespace Barotrauma.Tutorials
public virtual void AddToGUIUpdateList()
{
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale)
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale || GameMain.Config.WindowMode != windowMode)
{
CreateObjectiveFrame();
}
@@ -340,6 +341,7 @@ namespace Barotrauma.Tutorials
}
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
windowMode = GameMain.Config.WindowMode;
prevUIScale = GUI.Scale;
}

View File

@@ -11,8 +11,8 @@ namespace Barotrauma
tutorial.Initialize();
}
public TutorialMode(GameModePreset preset, object param)
: base(preset, param)
public TutorialMode(GameModePreset preset)
: base(preset)
{
}
@@ -21,6 +21,11 @@ namespace Barotrauma
base.Start();
GameMain.GameSession.CrewManager = new CrewManager(true);
Tutorial.Start();
foreach (Item item in Item.ItemList)
{
//don't consider the items to belong in the outpost to prevent the stealing icon from showing
item.SpawnedInOutpost = false;
}
}
public override void AddToGUIUpdateList()

View File

@@ -1,14 +1,19 @@
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
partial class GameSession
{
public RoundSummary RoundSummary { get; private set; }
public RoundSummary RoundSummary
{
get;
private set;
}
public static bool IsTabMenuOpen => GameMain.GameSession?.tabMenu != null;
public static TabMenu TabMenuInstance => GameMain.GameSession?.tabMenu;
private TabMenu tabMenu;
public bool ToggleTabMenu()
@@ -46,30 +51,20 @@ namespace Barotrauma
partial void UpdateProjSpecific(float deltaTime)
{
if (GUI.DisableHUD) return;
if (GUI.DisableHUD) { return; }
if (GameMode.IsRunning)
if (tabMenu == null)
{
if (tabMenu == null)
if (PlayerInput.KeyHit(InputType.InfoTab) && GUI.KeyboardDispatcher.Subscriber is GUITextBox == false)
{
if (PlayerInput.KeyHit(InputType.InfoTab) && GUI.KeyboardDispatcher.Subscriber is GUITextBox == false)
{
ToggleTabMenu();
}
}
else
{
tabMenu.Update();
if (PlayerInput.KeyHit(InputType.InfoTab) && GUI.KeyboardDispatcher.Subscriber is GUITextBox == false)
{
ToggleTabMenu();
}
ToggleTabMenu();
}
}
else
{
if (tabMenu != null)
tabMenu.Update();
if (PlayerInput.KeyHit(InputType.InfoTab) && GUI.KeyboardDispatcher.Subscriber is GUITextBox == false)
{
ToggleTabMenu();
}
@@ -97,7 +92,6 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch)
{
if (GUI.DisableHUD) return;
GameMode?.Draw(spriteBatch);
}
}

View File

@@ -1,185 +1,654 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Barotrauma
{
class RoundSummary
{
private Location startLocation, endLocation;
private const float jobColumnWidthPercentage = 0.11f;
private const float characterColumnWidthPercentage = 0.44f;
private const float statusColumnWidthPercentage = 0.45f;
private GameSession gameSession;
private int jobColumnWidth, characterColumnWidth, statusColumnWidth;
private Mission selectedMission;
public RoundSummary(GameSession gameSession)
private readonly SubmarineInfo sub;
private readonly Mission selectedMission;
private readonly Location startLocation, endLocation;
private readonly GameMode gameMode;
private readonly float initialLocationReputation;
private readonly Dictionary<Faction, float> initialFactionReputations = new Dictionary<Faction, float>();
public GUILayoutGroup ButtonArea { get; private set; }
public GUIButton ContinueButton { get; private set; }
public GUIComponent Frame { get; private set; }
public RoundSummary(SubmarineInfo sub, GameMode gameMode, Mission selectedMission, Location startLocation, Location endLocation)
{
this.gameSession = gameSession;
startLocation = gameSession.StartLocation;
endLocation = gameSession.EndLocation;
selectedMission = gameSession.Mission;
this.sub = sub;
this.gameMode = gameMode;
this.selectedMission = selectedMission;
this.startLocation = startLocation;
this.endLocation = endLocation;
initialLocationReputation = startLocation?.Reputation?.Value ?? 0.0f;
if (gameMode is CampaignMode campaignMode)
{
foreach (Faction faction in campaignMode.Factions)
{
initialFactionReputations.Add(faction, faction.Reputation.Value);
}
}
}
public GUIFrame CreateSummaryFrame(string endMessage)
public GUIFrame CreateSummaryFrame(GameSession gameSession, string endMessage, List<TraitorMissionResult> traitorResults, CampaignMode.TransitionType transitionType = CampaignMode.TransitionType.None)
{
bool singleplayer = GameMain.NetworkMember == null;
bool gameOver = gameSession.CrewManager.GetCharacters().All(c => c.IsDead || c.IsIncapacitated);
bool progress = Submarine.MainSub.AtEndPosition;
bool gameOver =
gameSession.GameMode.IsSinglePlayer ?
gameSession.CrewManager.GetCharacters().All(c => c.IsDead || c.IsIncapacitated) :
gameSession.CrewManager.GetCharacters().All(c => c.IsDead || c.IsIncapacitated || c.IsBot);
if (!singleplayer)
{
SoundPlayer.OverrideMusicType = gameOver ? "crewdead" : "endround";
SoundPlayer.OverrideMusicDuration = 18.0f;
}
GUIFrame background = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: "GUIBackgroundBlocker");
GUIFrame frame = new GUIFrame(new RectTransform(Vector2.One, background.RectTransform, Anchor.Center), style: null)
GUIFrame background = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, GUI.Canvas, Anchor.Center), style: "GUIBackgroundBlocker")
{
UserData = "roundsummary"
UserData = this
};
int width = 760, height = 500;
GUIFrame innerFrame = new GUIFrame(new RectTransform(new Vector2(0.4f, 0.5f), frame.RectTransform, Anchor.Center, minSize: new Point(width, height)));
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), innerFrame.RectTransform, Anchor.Center))
List<GUIComponent> rightPanels = new List<GUIComponent>();
int minWidth = 400, minHeight = 350;
int padding = GUI.IntScale(25.0f);
//crew panel -------------------------------------------------------------------------------
GUIFrame crewFrame = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.55f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
GUIFrame crewFrameInner = new GUIFrame(new RectTransform(new Point(crewFrame.Rect.Width - padding * 2, crewFrame.Rect.Height - padding * 2), crewFrame.RectTransform, Anchor.Center), style: "InnerFrame");
var crewContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), crewFrameInner.RectTransform, Anchor.Center))
{
Stretch = true,
RelativeSpacing = 0.03f
Stretch = true
};
GUIListBox infoTextBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.7f), paddedFrame.RectTransform))
var crewHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewContent.RectTransform),
TextManager.Get("crew"), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
crewHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader.Rect.Height * 2.0f));
CreateCrewList(crewContent, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID != Character.TeamType.Team2));
//another crew frame for the 2nd team in combat missions
if (gameSession.Mission is CombatMission)
{
Spacing = (int)(5 * GUI.Scale)
};
//spacing
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), infoTextBox.Content.RectTransform), style: null);
string summaryText = TextManager.GetWithVariables(gameOver ? "RoundSummaryGameOver" :
(progress ? "RoundSummaryProgress" : "RoundSummaryReturn"), new string[2] { "[sub]", "[location]" },
new string[2] { Submarine.MainSub.Info.Name, progress ? GameMain.GameSession.EndLocation.Name : GameMain.GameSession.StartLocation.Name });
var infoText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform),
summaryText, wrap: true);
GUIComponent endText = null;
if (!string.IsNullOrWhiteSpace(endMessage))
{
endText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform),
TextManager.GetServerMessage(endMessage), wrap: true);
crewHeader.Text = CombatMission.GetTeamName(Character.TeamType.Team1);
GUIFrame crewFrame2 = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.55f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
rightPanels.Add(crewFrame2);
GUIFrame crewFrameInner2 = new GUIFrame(new RectTransform(new Point(crewFrame2.Rect.Width - padding * 2, crewFrame2.Rect.Height - padding * 2), crewFrame2.RectTransform, Anchor.Center), style: "InnerFrame");
var crewContent2 = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), crewFrameInner2.RectTransform, Anchor.Center))
{
Stretch = true
};
var crewHeader2 = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), crewContent2.RectTransform),
CombatMission.GetTeamName(Character.TeamType.Team2), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
crewHeader2.RectTransform.MinSize = new Point(0, GUI.IntScale(crewHeader2.Rect.Height * 2.0f));
CreateCrewList(crewContent2, gameSession.CrewManager.GetCharacterInfos().Where(c => c.TeamID == Character.TeamType.Team2));
}
//don't show the mission info if the mission was not completed and there's no localized "mission failed" text available
if (GameMain.GameSession.Mission != null)
//header -------------------------------------------------------------------------------
string headerText = GetHeaderText(gameOver, transitionType);
GUITextBlock headerTextBlock = null;
if (!string.IsNullOrEmpty(headerText))
{
string message = GameMain.GameSession.Mission.Completed ? GameMain.GameSession.Mission.SuccessMessage : GameMain.GameSession.Mission.FailureMessage;
if (!string.IsNullOrEmpty(message))
{
//spacing
var spacingTransform = new RectTransform(new Vector2(1.0f, 0.1f), infoTextBox.Content.RectTransform);
new GUIFrame(spacingTransform, style: null);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("Mission"), GameMain.GameSession.Mission.Name),
font: GUI.LargeFont);
var missionInfo = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform),
message, wrap: true);
if (GameMain.GameSession.Mission.Completed && singleplayer)
{
var missionReward = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoTextBox.Content.RectTransform),
TextManager.GetWithVariable("MissionReward", "[reward]", GameMain.GameSession.Mission.Reward.ToString()));
}
}
headerTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), crewFrame.RectTransform, Anchor.TopLeft, Pivot.BottomLeft),
headerText, textAlignment: Alignment.BottomLeft, font: GUI.LargeFont, wrap: true);
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
TextManager.Get("RoundSummaryCrewStatus"), font: GUI.LargeFont);
GUIListBox characterListBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), paddedFrame.RectTransform, minSize: new Point(0, 75)), isHorizontal: true);
foreach (CharacterInfo characterInfo in gameSession.CrewManager.GetCharacterInfos())
//traitor panel -------------------------------------------------------------------------------
if (traitorResults != null && traitorResults.Any())
{
if (GameMain.GameSession.Mission is CombatMission &&
characterInfo.TeamID != GameMain.GameSession.WinningTeam)
{
continue;
}
GUIFrame traitorframe = new GUIFrame(new RectTransform(crewFrame.RectTransform.RelativeSize, background.RectTransform, Anchor.TopCenter, minSize: crewFrame.RectTransform.MinSize));
rightPanels.Add(traitorframe);
GUIFrame traitorframeInner = new GUIFrame(new RectTransform(new Point(traitorframe.Rect.Width - padding * 2, traitorframe.Rect.Height - padding * 2), traitorframe.RectTransform, Anchor.Center), style: "InnerFrame");
var characterFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 1.0f), characterListBox.Content.RectTransform, minSize: new Point(170, 0)))
var traitorContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), traitorframeInner.RectTransform, Anchor.Center))
{
CanBeFocused = false,
Stretch = true
};
characterInfo.CreateCharacterFrame(characterFrame,
characterInfo.Job != null ? (characterInfo.Name + '\n' + "(" + characterInfo.Job.Name + ")") : characterInfo.Name, null);
var traitorHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), traitorContent.RectTransform),
TextManager.Get("traitors"), font: GUI.SubHeadingFont);
traitorHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(traitorHeader.Rect.Height * 2.0f));
string statusText = TextManager.Get("StatusOK");
Color statusColor = Color.DarkGreen;
GUIListBox listBox = CreateCrewList(traitorContent, traitorResults.SelectMany(tr => tr.Characters.Select(c => c.Info)));
Character character = characterInfo.Character;
if (character == null || character.IsDead)
foreach (var traitorResult in traitorResults)
{
if (characterInfo.CauseOfDeath == null)
var traitorMission = TraitorMissionPrefab.List.Find(t => t.Identifier == traitorResult.MissionIdentifier);
if (traitorMission == null) { continue; }
//spacing
new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, GUI.IntScale(25)), listBox.Content.RectTransform), style: null);
var traitorResultHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), listBox.Content.RectTransform), childAnchor: Anchor.CenterLeft, isHorizontal: true)
{
statusText = TextManager.Get("CauseOfDeathDescription.Unknown");
RelativeSpacing = 0.05f,
Stretch = true
};
new GUIImage(new RectTransform(new Point(traitorResultHorizontal.Rect.Height), traitorResultHorizontal.RectTransform), traitorMission.Icon, scaleToFit: true)
{
Color = traitorMission.IconColor
};
string traitorMessage = TextManager.GetServerMessage(traitorResult.EndMessage);
if (!string.IsNullOrEmpty(traitorMessage))
{
var textContent = new GUILayoutGroup(new RectTransform(Vector2.One, traitorResultHorizontal.RectTransform))
{
RelativeSpacing = 0.025f
};
var traitorStatusText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
TextManager.Get(traitorResult.Success ? "missioncompleted" : "missionfailed"),
textColor: traitorResult.Success ? GUI.Style.Green : GUI.Style.Red, font: GUI.SubHeadingFont);
var traitorMissionInfo = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
traitorMessage, font: GUI.SmallFont, wrap: true);
traitorResultHorizontal.Recalculate();
traitorStatusText.CalculateHeightFromText();
traitorMissionInfo.CalculateHeightFromText();
traitorStatusText.RectTransform.MinSize = new Point(0, traitorStatusText.Rect.Height);
traitorMissionInfo.RectTransform.MinSize = new Point(0, traitorMissionInfo.Rect.Height);
textContent.RectTransform.MaxSize = new Point(int.MaxValue, (int)((traitorStatusText.Rect.Height + traitorMissionInfo.Rect.Height) * 1.2f));
traitorResultHorizontal.RectTransform.MinSize = new Point(0, traitorStatusText.RectTransform.MinSize.Y + traitorMissionInfo.RectTransform.MinSize.Y);
}
else if (characterInfo.CauseOfDeath.Type == CauseOfDeathType.Affliction && characterInfo.CauseOfDeath.Affliction == null)
}
}
//reputation panel -------------------------------------------------------------------------------
if (gameMode is CampaignMode campaignMode)
{
GUIFrame reputationframe = new GUIFrame(new RectTransform(crewFrame.RectTransform.RelativeSize, background.RectTransform, Anchor.TopCenter, minSize: crewFrame.RectTransform.MinSize));
rightPanels.Add(reputationframe);
GUIFrame reputationframeInner = new GUIFrame(new RectTransform(new Point(reputationframe.Rect.Width - padding * 2, reputationframe.Rect.Height - padding * 2), reputationframe.RectTransform, Anchor.Center), style: "InnerFrame");
var reputationContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), reputationframeInner.RectTransform, Anchor.Center))
{
Stretch = true
};
var reputationHeader = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), reputationContent.RectTransform),
TextManager.Get("reputation"), textAlignment: Alignment.TopLeft, font: GUI.SubHeadingFont);
reputationHeader.RectTransform.MinSize = new Point(0, GUI.IntScale(reputationHeader.Rect.Height * 2.0f));
GUIListBox reputationList = new GUIListBox(new RectTransform(Vector2.One, reputationContent.RectTransform))
{
Padding = new Vector4(2, 5, 0, 0)
};
reputationList.ContentBackground.Color = Color.Transparent;
if (startLocation.Type.HasOutpost && startLocation.Reputation != null)
{
var iconStyle = GUI.Style.GetComponentStyle("LocationReputationIcon");
CreateReputationElement(
reputationList.Content,
startLocation.Name,
startLocation.Reputation.Value, startLocation.Reputation.NormalizedValue, initialLocationReputation,
startLocation.Type.Name, "",
iconStyle?.GetDefaultSprite(), startLocation.Type.GetPortrait(0), iconStyle?.Color ?? Color.White);
}
foreach (Faction faction in campaignMode.Factions)
{
float initialReputation = faction.Reputation.Value;
if (initialFactionReputations.ContainsKey(faction))
{
string errorMsg = "Character \"" + character.Name + "\" had an invalid cause of death (the type of the cause of death was Affliction, but affliction was not specified).";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("RoundSummary:InvalidCauseOfDeath", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
statusText = TextManager.Get("CauseOfDeathDescription.Unknown");
initialReputation = initialFactionReputations[faction];
}
else
{
statusText = characterInfo.CauseOfDeath.Type == CauseOfDeathType.Affliction ?
characterInfo.CauseOfDeath.Affliction.CauseOfDeathDescription :
TextManager.Get("CauseOfDeathDescription." + characterInfo.CauseOfDeath.Type.ToString());
DebugConsole.AddWarning($"Could not determine reputation change for faction \"{faction.Prefab.Name}\" (faction was not present at the start of the round).");
}
CreateReputationElement(
reputationList.Content,
faction.Prefab.Name,
faction.Reputation.Value, faction.Reputation.NormalizedValue, initialReputation,
faction.Prefab.ShortDescription, faction.Prefab.Description,
faction.Prefab.Icon, faction.Prefab.BackgroundPortrait, faction.Prefab.IconColor);
}
float otherElementHeight = 0.0f;
float maxDescriptionHeight = 0.0f;
foreach (GUIComponent child in reputationList.Content.Children)
{
var descriptionElement = child.FindChild("description", recursive: true) as GUITextBlock;
maxDescriptionHeight = Math.Max(maxDescriptionHeight, descriptionElement.TextSize.Y * 1.1f);
otherElementHeight = Math.Max(otherElementHeight, descriptionElement.Parent.Rect.Height - descriptionElement.TextSize.Y);
}
foreach (GUIComponent child in reputationList.Content.Children)
{
var descriptionElement = child.FindChild("description", recursive: true) as GUITextBlock;
descriptionElement.RectTransform.MaxSize = new Point(int.MaxValue, (int)(maxDescriptionHeight));
child.RectTransform.MaxSize = new Point(int.MaxValue, (int)((maxDescriptionHeight + otherElementHeight) * 1.2f));
(descriptionElement?.Parent as GUILayoutGroup).Recalculate();
}
}
//mission panel -------------------------------------------------------------------------------
GUIFrame missionframe = new GUIFrame(new RectTransform(new Vector2(0.39f, 0.22f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight / 4)));
GUIFrame missionframeInner = new GUIFrame(new RectTransform(new Point(missionframe.Rect.Width - padding * 2, missionframe.Rect.Height - padding * 2), missionframe.RectTransform, Anchor.Center), style: "InnerFrame");
var missionContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), missionframeInner.RectTransform, Anchor.Center))
{
RelativeSpacing = 0.05f,
Stretch = true
};
if (!string.IsNullOrWhiteSpace(endMessage))
{
var endText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionContent.RectTransform),
TextManager.GetServerMessage(endMessage), wrap: true);
endText.RectTransform.MinSize = new Point(0, endText.Rect.Height);
var line = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.1f), missionContent.RectTransform), style: "HorizontalLine");
line.RectTransform.NonScaledSize = new Point(line.Rect.Width, GUI.IntScale(5.0f));
}
var missionContentHorizontal = new GUILayoutGroup(new RectTransform(Vector2.One, missionContent.RectTransform), childAnchor: Anchor.TopLeft, isHorizontal: true)
{
RelativeSpacing = 0.025f,
Stretch = true
};
Mission displayedMission = selectedMission ?? startLocation.SelectedMission;
string missionMessage = "";
GUIImage missionIcon;
if (displayedMission != null)
{
missionMessage =
displayedMission == selectedMission ?
displayedMission.Completed ? displayedMission.SuccessMessage : displayedMission.FailureMessage :
displayedMission.Description;
missionIcon = new GUIImage(new RectTransform(new Point(missionContentHorizontal.Rect.Height), missionContentHorizontal.RectTransform), displayedMission.Prefab.Icon, scaleToFit: true)
{
Color = displayedMission.Prefab.IconColor
};
if (displayedMission == selectedMission)
{
new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), displayedMission.Completed ? "MissionCompletedIcon" : "MissionFailedIcon", scaleToFit: true);
}
}
else
{
missionIcon = new GUIImage(new RectTransform(new Point(missionContentHorizontal.Rect.Height), missionContentHorizontal.RectTransform), style: "NoMissionIcon", scaleToFit: true);
}
var missionTextContent = new GUILayoutGroup(new RectTransform(Vector2.One, missionContentHorizontal.RectTransform))
{
RelativeSpacing = 0.05f
};
missionContentHorizontal.Recalculate();
missionContent.Recalculate();
missionIcon.RectTransform.MinSize = new Point(0, missionContentHorizontal.Rect.Height);
missionTextContent.RectTransform.MaxSize = new Point(int.MaxValue, missionIcon.Rect.Width);
GUITextBlock missionDescription = null;
if (displayedMission == null)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
TextManager.Get("nomission"), font: GUI.LargeFont);
}
else
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
TextManager.AddPunctuation(':', TextManager.Get("Mission"), displayedMission.Name), font: GUI.SubHeadingFont);
missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
missionMessage, wrap: true);
if (displayedMission == selectedMission && displayedMission.Completed)
{
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", displayedMission.Reward));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
TextManager.GetWithVariable("MissionReward", "[reward]", rewardText));
}
}
ButtonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), missionContent.RectTransform, Anchor.BottomCenter), isHorizontal: true, childAnchor: Anchor.BottomRight)
{
IgnoreLayoutGroups = true,
RelativeSpacing = 0.025f
};
ContinueButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), ButtonArea.RectTransform), TextManager.Get("Close"));
ButtonArea.RectTransform.NonScaledSize = new Point(ButtonArea.Rect.Width, ContinueButton.Rect.Height);
ButtonArea.RectTransform.IsFixedSize = true;
missionContent.Recalculate();
//description overlapping with the buttons -> switch to small font
if (missionDescription != null && missionDescription.Rect.Y + missionDescription.TextSize.Y > ButtonArea.Rect.Y)
{
missionDescription.Font = GUI.Style.SmallFont;
//still overlapping -> shorten the text
if (missionDescription.Rect.Y + missionDescription.TextSize.Y > ButtonArea.Rect.Y && missionDescription.WrappedText.Contains('\n'))
{
missionDescription.ToolTip = missionDescription.Text;
missionDescription.Text = missionDescription.WrappedText.Split('\n').First() + "...";
}
}
// set layout -------------------------------------------------------------------
int panelSpacing = GUI.IntScale(20);
int totalHeight = crewFrame.Rect.Height + panelSpacing + missionframe.Rect.Height;
int totalWidth = crewFrame.Rect.Width;
crewFrame.RectTransform.AbsoluteOffset = new Point(0, (GameMain.GraphicsHeight - totalHeight) / 2);
missionframe.RectTransform.AbsoluteOffset = new Point(0, crewFrame.Rect.Bottom + panelSpacing);
if (rightPanels.Any())
{
totalWidth = crewFrame.Rect.Width * 2 + panelSpacing;
if (headerTextBlock != null)
{
headerTextBlock.RectTransform.MinSize = new Point(totalWidth, 0);
}
crewFrame.RectTransform.AbsoluteOffset = new Point(-(crewFrame.Rect.Width + panelSpacing) / 2, crewFrame.RectTransform.AbsoluteOffset.Y);
foreach (var rightPanel in rightPanels)
{
rightPanel.RectTransform.AbsoluteOffset = new Point((rightPanel.Rect.Width + panelSpacing) / 2, crewFrame.RectTransform.AbsoluteOffset.Y);
}
}
if (!(gameSession.GameMode is CampaignMode))
{
var shadow = new GUIFrame(new RectTransform(new Point((int)(totalWidth * 1.2f), GameMain.GraphicsHeight * 2), background.RectTransform, Anchor.Center), style: "OuterGlow")
{
Color = Color.Black
};
shadow.RectTransform.SetAsFirstChild();
}
Frame = background;
return background;
}
private string GetHeaderText(bool gameOver, CampaignMode.TransitionType transitionType)
{
string locationName = Submarine.MainSub.AtEndPosition ? endLocation?.Name : startLocation?.Name;
string textTag;
if (gameOver)
{
textTag = "RoundSummaryGameOver";
}
else
{
switch (transitionType)
{
case CampaignMode.TransitionType.LeaveLocation:
locationName = startLocation?.Name;
textTag = "RoundSummaryLeaving";
break;
case CampaignMode.TransitionType.ProgressToNextLocation:
case CampaignMode.TransitionType.ProgressToNextEmptyLocation:
locationName = endLocation?.Name;
textTag = "RoundSummaryProgress";
break;
case CampaignMode.TransitionType.ReturnToPreviousLocation:
case CampaignMode.TransitionType.ReturnToPreviousEmptyLocation:
locationName = startLocation?.Name;
textTag = "RoundSummaryReturn";
break;
default:
textTag = Submarine.MainSub.AtEndPosition ? "RoundSummaryProgress" : "RoundSummaryReturn";
break;
}
}
if (textTag == null) { return ""; }
if (locationName == null)
{
DebugConsole.ThrowError($"Error while creating round summary: could not determine destination location. Start location: {startLocation?.Name ?? "null"}, end location: {endLocation?.Name ?? "null"}");
locationName = "[UNKNOWN]";
}
string subName = string.Empty;
SubmarineInfo currentOrPending = SubmarineSelection.CurrentOrPendingSubmarine();
if (currentOrPending != null)
{
subName = currentOrPending.DisplayName;
}
return TextManager.GetWithVariables(textTag, new string[2] { "[sub]", "[location]" }, new string[2] { subName, locationName });
}
private GUIListBox CreateCrewList(GUIComponent parent, IEnumerable<CharacterInfo> characterInfos)
{
var headerFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.0f), parent.RectTransform, Anchor.TopCenter, minSize: new Point(0, (int)(30 * GUI.Scale))) { }, isHorizontal: true)
{
AbsoluteSpacing = 2
};
GUIButton jobButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("tabmenu.job"), style: "GUIButtonSmallFreeScale");
GUIButton characterButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("name"), style: "GUIButtonSmallFreeScale");
GUIButton statusButton = new GUIButton(new RectTransform(new Vector2(0f, 1f), headerFrame.RectTransform), TextManager.Get("label.statuslabel"), style: "GUIButtonSmallFreeScale");
float sizeMultiplier = 1.0f;
//sizeMultiplier = (headerFrame.Rect.Width - headerFrame.AbsoluteSpacing * (headerFrame.CountChildren - 1)) / (float)headerFrame.Rect.Width;
jobButton.RectTransform.RelativeSize = new Vector2(jobColumnWidthPercentage * sizeMultiplier, 1f);
characterButton.RectTransform.RelativeSize = new Vector2(characterColumnWidthPercentage * sizeMultiplier, 1f);
statusButton.RectTransform.RelativeSize = new Vector2(statusColumnWidthPercentage * sizeMultiplier, 1f);
jobButton.TextBlock.Font = characterButton.TextBlock.Font = statusButton.TextBlock.Font = GUI.HotkeyFont;
jobButton.CanBeFocused = characterButton.CanBeFocused = statusButton.CanBeFocused = false;
jobButton.TextBlock.ForceUpperCase = characterButton.TextBlock.ForceUpperCase = statusButton.ForceUpperCase = true;
jobColumnWidth = jobButton.Rect.Width;
characterColumnWidth = characterButton.Rect.Width;
statusColumnWidth = statusButton.Rect.Width;
GUIListBox crewList = new GUIListBox(new RectTransform(Vector2.One, parent.RectTransform))
{
Padding = new Vector4(2, 5, 0, 0),
AutoHideScrollBar = false
};
crewList.ContentBackground.Color = Color.Transparent;
headerFrame.RectTransform.RelativeSize -= new Vector2(crewList.ScrollBar.RectTransform.RelativeSize.X, 0.0f);
foreach (CharacterInfo characterInfo in characterInfos)
{
if (characterInfo == null) { continue; }
CreateCharacterElement(characterInfo, crewList);
}
return crewList;
}
private void CreateCharacterElement(CharacterInfo characterInfo, GUIListBox listBox)
{
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, GUI.IntScale(45)), listBox.Content.RectTransform), style: "ListBoxElement")
{
CanBeFocused = false,
UserData = characterInfo,
Color = (Character.Controlled?.Info == characterInfo) ? TabMenu.OwnCharacterBGColor : Color.Transparent
};
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
{
AbsoluteSpacing = 2,
Stretch = true
};
new GUICustomComponent(new RectTransform(new Point(jobColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform, Anchor.Center), onDraw: (sb, component) => characterInfo.DrawJobIcon(sb, component.Rect))
{
ToolTip = characterInfo.Job.Name ?? "",
HoverColor = Color.White,
SelectedColor = Color.White
};
GUITextBlock characterNameBlock = new GUITextBlock(new RectTransform(new Point(characterColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
ToolBox.LimitString(characterInfo.Name, GUI.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: characterInfo.Job.Prefab.UIColor);
string statusText = TextManager.Get("StatusOK");
Color statusColor = GUI.Style.Green;
Character character = characterInfo.Character;
if (character == null || character.IsDead)
{
if (characterInfo.IsNewHire)
{
statusText = TextManager.Get("CampaignCrew.NewHire");
statusColor = GUI.Style.Blue;
}
else if (characterInfo.CauseOfDeath == null)
{
statusText = TextManager.Get("CauseOfDeathDescription.Unknown");
statusColor = Color.DarkRed;
}
else if (characterInfo.CauseOfDeath.Type == CauseOfDeathType.Affliction && characterInfo.CauseOfDeath.Affliction == null)
{
string errorMsg = "Character \"" + characterInfo.Name + "\" had an invalid cause of death (the type of the cause of death was Affliction, but affliction was not specified).";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("RoundSummary:InvalidCauseOfDeath", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
statusText = TextManager.Get("CauseOfDeathDescription.Unknown");
statusColor = GUI.Style.Red;
}
else
{
if (character.IsUnconscious)
{
statusText = TextManager.Get("Unconscious");
statusColor = Color.DarkOrange;
}
else if (character.Vitality / character.MaxVitality < 0.8f)
{
statusText = TextManager.Get("Injured");
statusColor = Color.DarkOrange;
}
statusText = characterInfo.CauseOfDeath.Type == CauseOfDeathType.Affliction ?
characterInfo.CauseOfDeath.Affliction.CauseOfDeathDescription :
TextManager.Get("CauseOfDeathDescription." + characterInfo.CauseOfDeath.Type.ToString());
statusColor = Color.DarkRed;
}
}
else
{
if (character.IsUnconscious)
{
statusText = TextManager.Get("Unconscious");
statusColor = Color.DarkOrange;
}
else if (character.Vitality / character.MaxVitality < 0.8f)
{
statusText = TextManager.Get("Injured");
statusColor = Color.DarkOrange;
}
var textHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.2f), characterFrame.RectTransform, Anchor.BottomCenter), style: "InnerGlow", color: statusColor);
new GUITextBlock(new RectTransform(Vector2.One, textHolder.RectTransform, Anchor.Center),
statusText, Color.White,
textAlignment: Alignment.Center,
wrap: true, font: GUI.SmallFont, style: null);
}
new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), isHorizontal: true, childAnchor: Anchor.BottomRight)
GUITextBlock statusBlock = new GUITextBlock(new RectTransform(new Point(statusColumnWidth, paddedFrame.Rect.Height), paddedFrame.RectTransform),
ToolBox.LimitString(statusText, GUI.Font, characterColumnWidth), textAlignment: Alignment.Center, textColor: statusColor);
}
private void CreateReputationElement(GUIComponent parent,
string name, float reputation, float normalizedReputation, float initialReputation,
string shortDescription, string fullDescription, Sprite icon, Sprite backgroundPortrait, Color iconColor)
{
var factionFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.3f), parent.RectTransform), style: null);
if (backgroundPortrait != null)
{
RelativeSpacing = 0.05f,
UserData = "buttonarea"
new GUICustomComponent(new RectTransform(Vector2.One, factionFrame.RectTransform), onDraw: (sb, customComponent) =>
{
backgroundPortrait.Draw(sb, customComponent.Rect.Center.ToVector2(), customComponent.Color, backgroundPortrait.size / 2, scale: customComponent.Rect.Width / backgroundPortrait.size.X);
})
{
HideElementsOutsideFrame = true,
IgnoreLayoutGroups = true,
Color = iconColor * 0.2f
};
}
var factionInfoHorizontal = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), factionFrame.RectTransform, Anchor.Center), childAnchor: Anchor.CenterLeft, isHorizontal: true)
{
RelativeSpacing = 0.02f,
Stretch = true
};
paddedFrame.Recalculate();
foreach (GUIComponent child in infoTextBox.Content.Children)
var factionTextContent = new GUILayoutGroup(new RectTransform(Vector2.One, factionInfoHorizontal.RectTransform))
{
child.CanBeFocused = false;
if (child is GUITextBlock textBlock)
{
textBlock.CalculateHeightFromText();
}
RelativeSpacing = 0.05f,
Stretch = true
};
var factionIcon = new GUIImage(new RectTransform(new Point((int)(factionInfoHorizontal.Rect.Height * 0.7f)), factionInfoHorizontal.RectTransform, scaleBasis: ScaleBasis.Smallest), icon, scaleToFit: true)
{
Color = iconColor
};
factionInfoHorizontal.Recalculate();
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), factionTextContent.RectTransform),
name, font: GUI.SubHeadingFont)
{
Padding = Vector4.Zero
};
var factionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.6f), factionTextContent.RectTransform),
shortDescription, font: GUI.SmallFont, wrap: true)
{
UserData = "description",
Padding = Vector4.Zero
};
if (shortDescription != fullDescription && !string.IsNullOrEmpty(fullDescription))
{
factionDescription.ToolTip = fullDescription;
}
return frame;
var sliderHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), factionTextContent.RectTransform),
childAnchor: Anchor.CenterLeft, isHorizontal: true)
{
RelativeSpacing = 0.05f,
Stretch = true
};
sliderHolder.RectTransform.MaxSize = new Point(int.MaxValue, GUI.IntScale(25.0f));
factionTextContent.Recalculate();
new GUICustomComponent(new RectTransform(new Vector2(0.8f, 1.0f), sliderHolder.RectTransform), onDraw: (sb, customComponent) =>
{
GUI.DrawRectangle(sb, customComponent.Rect, GUI.Style.ColorInventoryBackground, isFilled: true);
if (normalizedReputation < 0.5f)
{
int barWidth = (int)((0.5f - normalizedReputation) * customComponent.Rect.Width);
GUI.DrawRectangle(sb, new Rectangle(customComponent.Rect.Center.X - barWidth, customComponent.Rect.Y, barWidth, customComponent.Rect.Height), GUI.Style.Red, isFilled: true);
}
else if (normalizedReputation > 0.5f)
{
int barWidth = (int)((normalizedReputation - 0.5f) * customComponent.Rect.Width);
GUI.DrawRectangle(sb, new Rectangle(customComponent.Rect.Center.X, customComponent.Rect.Y, barWidth, customComponent.Rect.Height), GUI.Style.Green, isFilled: true);
}
GUI.DrawLine(sb, new Vector2(customComponent.Rect.Center.X, customComponent.Rect.Y - 2), new Vector2(customComponent.Rect.Center.X, customComponent.Rect.Bottom + 2), factionDescription.TextColor, width: 1);
});
string reputationText = ((int)Math.Round(reputation)).ToString();
int reputationChange = (int)Math.Round( reputation - initialReputation);
if (Math.Abs(reputationChange) > 0)
{
string changeText = $"{(reputationChange > 0 ? "+" : "") + reputationChange}";
string colorStr = XMLExtensions.ColorToString(reputationChange > 0 ? GUI.Style.Green : GUI.Style.Red);
var rtData = RichTextData.GetRichTextData($"{reputationText} (‖color:{colorStr}‖{changeText}‖color:end‖)", out string sanitizedText);
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
rtData, sanitizedText,
textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont);
}
else
{
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
reputationText,
textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont);
}
}
}
}

View File

@@ -0,0 +1,81 @@
#nullable enable
using System;
using System.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
partial class UpgradeManager
{
partial void UpgradeNPCSpeak(string text, bool isSinglePlayer, Character? character)
{
if (Level.Loaded?.StartOutpost?.Info?.OutpostNPCs == null) { return; }
if (character != null)
{
Speak(character);
return;
}
foreach (Character npc in Level.Loaded.StartOutpost.Info.OutpostNPCs.SelectMany(kpv => kpv.Value))
{
if (npc.CampaignInteractionType == CampaignMode.InteractionType.Upgrade)
{
Speak(npc);
break;
}
}
void Speak(Character npc)
{
ChatMessage message = ChatMessage.Create(npc.Name, text, ChatMessageType.Default, npc);
if (!isSinglePlayer)
{
GameMain.Client?.AddChatMessage(message);
}
else
{
GameMain.GameSession?.CrewManager?.AddSinglePlayerChatMessage(message);
}
}
}
/// <summary>
/// Server has notified us that upgrades were reset.
/// </summary>
/// <param name="inc"></param>
/// <see cref="UpgradeManager.SendUpgradeResetMessage"/>
public void ClientRead(IReadMessage inc)
{
bool shouldReset = inc.ReadBoolean();
int money = inc.ReadInt32();
// uint length = inc.ReadUInt32();
//
// for (int i = 0; i < length; i++)
// {
// string key = inc.ReadString();
// byte value = inc.ReadByte();
// Metadata.SetValue(key, value);
// }
Campaign.Money = money;
if (shouldReset)
{
ResetUpgrades();
}
// spentMoney is local, so this message box should only appear for those who have spent money on upgrades
if (spentMoney > 0)
{
GUIMessageBox msgBox = new GUIMessageBox(TextManager.Get("UpgradeRefundTitle"), TextManager.Get("UpgradeRefundBody"), new [] { TextManager.Get("Ok") });
msgBox.Buttons[0].OnClicked += msgBox.Close;
}
spentMoney = 0;
PendingUpgrades.Clear();
PurchasedUpgrades.Clear();
CanUpgrade = false;
}
}
}

View File

@@ -761,6 +761,44 @@ namespace Barotrauma
RelativeSpacing = 0.01f
};
#if (!OSX)
AudioDeviceNames = Alc.GetStringList((IntPtr)null, Alc.AllDevicesSpecifier);
if (string.IsNullOrEmpty(AudioOutputDevice))
{
AudioOutputDevice = Alc.GetString((IntPtr)null, Alc.DefaultDeviceSpecifier);
if (AudioDeviceNames.Any() && !AudioDeviceNames.Any(n => n.Equals(AudioOutputDevice, StringComparison.OrdinalIgnoreCase)))
{
AudioOutputDevice = AudioDeviceNames[0];
}
}
var outputDeviceList = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.15f), audioContent.RectTransform), TrimAudioDeviceName(AudioOutputDevice), AudioDeviceNames.Count);
if (AudioDeviceNames?.Count > 0)
{
foreach (string name in AudioDeviceNames)
{
outputDeviceList.AddItem(TrimAudioDeviceName(name), name);
}
outputDeviceList.OnSelected = (GUIComponent selected, object obj) =>
{
string name = obj as string;
if (!GameMain.SoundManager.Disconnected && AudioOutputDevice == name) { return true; }
AudioOutputDevice = name;
GameMain.SoundManager.InitializeAlcDevice(AudioOutputDevice);
return true;
};
}
else
{
outputDeviceList.AddItem(TextManager.Get("AudioNoDevices") ?? "N/A", null);
outputDeviceList.ButtonTextColor = GUI.Style.Red;
outputDeviceList.ButtonEnabled = false;
outputDeviceList.Select(0);
}
#endif
GUITextBlock soundVolumeText = new GUITextBlock(new RectTransform(textBlockScale, audioContent.RectTransform), TextManager.Get("SoundVolume"), font: GUI.SubHeadingFont);
GUIScrollBar soundScrollBar = new GUIScrollBar(new RectTransform(textBlockScale, audioContent.RectTransform),
style: "GUISlider", barSize: 0.05f)
@@ -893,7 +931,7 @@ namespace Barotrauma
deviceList.OnSelected = (GUIComponent selected, object obj) =>
{
string name = obj as string;
if (VoiceCaptureDevice == name) { return true; }
if (!(VoipCapture.Instance?.Disconnected ?? true) && VoiceCaptureDevice == name) { return true; }
VoipCapture.ChangeCaptureDevice(name);
return true;
@@ -1179,12 +1217,16 @@ namespace Barotrauma
var inputName = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), inputContainer.RectTransform, Anchor.TopLeft) { MinSize = new Point(100, 0) },
TextManager.Get("InputType." + ((InputType)i)), font: GUI.SmallFont) { ForceUpperCase = true };
inputNameBlocks.Add(inputName);
string keyText = KeyBindText((InputType)i);
var keyBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), inputContainer.RectTransform),
text: KeyBindText((InputType)i), font: GUI.SmallFont, style: "GUITextBoxNoIcon")
text: keyText, font: GUI.SmallFont, style: "GUITextBoxNoIcon")
{
UserData = i
};
keyBox.Text = ToolBox.LimitString(keyBox.Text, keyBox.Font, (int)(keyBox.Rect.Width - keyBox.Padding.X - keyBox.Padding.Z));
keyBox.RectTransform.SizeChanged += () =>
{
keyBox.Text = ToolBox.LimitString(keyText, keyBox.Font, (int)(keyBox.Rect.Width - keyBox.Padding.X - keyBox.Padding.Z));
};
keyBox.OnSelected += KeyBoxSelected;
keyBox.SelectedColor = Color.Gold * 0.3f;
}
@@ -1337,6 +1379,16 @@ namespace Barotrauma
return true;
};
var automaticCampaignLoadTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Automatic campaign load enabled", style: "GUITickBox");
automaticCampaignLoadTickBox.Selected = AutomaticCampaignLoadEnabled;
automaticCampaignLoadTickBox.ToolTip = "Will the game automatically load the latest campaign save when the game is launched";
automaticCampaignLoadTickBox.OnSelected = (tickBox) =>
{
AutomaticCampaignLoadEnabled = tickBox.Selected;
UnsavedSettings = true;
return true;
};
var showSplashScreenTickBox = new GUITickBox(new RectTransform(tickBoxScale / 0.18f, debugTickBoxes.RectTransform, scaleBasis: ScaleBasis.BothHeight), "Splash screen enabled", style: "GUITickBox");
showSplashScreenTickBox.Selected = EnableSplashScreen;
showSplashScreenTickBox.ToolTip = "Are the splash screens shown when the game is launched";
@@ -1520,8 +1572,7 @@ namespace Barotrauma
{
return GameMain.Client == null &&
(ContentPackage.IngameModSwap ||
(Screen.Selected != GameMain.GameScreen &&
Screen.Selected != GameMain.LobbyScreen) &&
Screen.Selected != GameMain.GameScreen &&
Screen.Selected != GameMain.SubEditorScreen) &&
(!core ||
(Screen.Selected != GameMain.CharacterEditorScreen &&

View File

@@ -35,6 +35,26 @@ namespace Barotrauma
}
private static Dictionary<InvSlotType, Sprite> limbSlotIcons;
public static Dictionary<InvSlotType, Sprite> LimbSlotIcons
{
get
{
if (limbSlotIcons == null)
{
limbSlotIcons = new Dictionary<InvSlotType, Sprite>();
int margin = 2;
limbSlotIcons.Add(InvSlotType.Headset, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(384 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.InnerClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(512 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.Card, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(640 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.Head, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(896 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.LeftHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(634, 0, 128, 128)));
limbSlotIcons.Add(InvSlotType.RightHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(762, 0, 128, 128)));
limbSlotIcons.Add(InvSlotType.OuterClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
}
return limbSlotIcons;
}
}
public const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head;
@@ -88,7 +108,7 @@ namespace Barotrauma
indicatorGroup = new GUILayoutGroup(new RectTransform(Point.Zero, hideButton.RectTransform)) { IsHorizontal = false };
indicatorGroup.ChildAnchor = Anchor.TopCenter;
indicatorSpriteSize = GUI.Style.GetComponentStyle("EquipmentIndicatorDivingSuit").Sprites[GUIComponent.ComponentState.None][0].Sprite.size;
indicatorSpriteSize = GUI.Style.GetComponentStyle("EquipmentIndicatorDivingSuit").GetDefaultSprite().size;
indicators[0] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorDivingSuit");
indicators[1] = new GUIImage(new RectTransform(Point.Zero, indicatorGroup.RectTransform), "EquipmentIndicatorID");
@@ -115,20 +135,6 @@ namespace Barotrauma
hidePersonalSlots = false;
if (limbSlotIcons == null)
{
limbSlotIcons = new Dictionary<InvSlotType, Sprite>();
int margin = 2;
limbSlotIcons.Add(InvSlotType.Headset, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(384 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.InnerClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(512 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.Card, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(640 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.Head, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(896 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
limbSlotIcons.Add(InvSlotType.LeftHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(634, 0, 128, 128)));
limbSlotIcons.Add(InvSlotType.RightHand, new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(762, 0, 128, 128)));
limbSlotIcons.Add(InvSlotType.OuterClothes, new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(256 + margin, 128 + margin, 128 - margin * 2, 128 - margin * 2)));
}
SlotPositions = new Vector2[SlotTypes.Length];
CurrentLayout = Layout.Default;
SetSlotPositions(layout);
@@ -522,14 +528,7 @@ namespace Barotrauma
if (hoverOnInventory) { HideTimer = 0.5f; }
if (HideTimer > 0.0f) { HideTimer -= deltaTime; }
for (int i = 0; i < capacity; i++)
{
if (Items[i] != null && Items[i] != draggingItem && Character.Controlled?.Inventory == this &&
GUI.KeyboardDispatcher.Subscriber == null && !CrewManager.IsCommandInterfaceOpen && PlayerInput.InventoryKeyHit(slots[i].InventoryKeyIndex))
{
QuickUseItem(Items[i], true, false, true);
}
}
UpdateSlotInput();
//force personal slots open if an item is running out of battery/fuel/oxygen/etc
if (hidePersonalSlots)
@@ -685,6 +684,18 @@ namespace Barotrauma
doubleClickedItem = null;
}
public void UpdateSlotInput()
{
for (int i = 0; i < capacity; i++)
{
if (Items[i] != null && Items[i] != draggingItem && Character.Controlled?.Inventory == this &&
GUI.KeyboardDispatcher.Subscriber == null && !CrewManager.IsCommandInterfaceOpen && PlayerInput.InventoryKeyHit(slots[i].InventoryKeyIndex))
{
QuickUseItem(Items[i], true, false, true);
}
}
}
private void HandleButtonEquipStates(Item item, InventorySlot slot, float deltaTime)
{
slot.EquipButtonState = slot.EquipButtonRect.Contains(PlayerInput.MousePosition) ?
@@ -1084,9 +1095,9 @@ namespace Barotrauma
!Items[i].AllowedSlots.Any(a => a != InvSlotType.Any))
{
//draw limb icons on empty slots
if (limbSlotIcons.ContainsKey(SlotTypes[i]))
if (LimbSlotIcons.ContainsKey(SlotTypes[i]))
{
var icon = limbSlotIcons[SlotTypes[i]];
var icon = LimbSlotIcons[SlotTypes[i]];
icon.Draw(spriteBatch, slots[i].Rect.Center.ToVector2() + slots[i].DrawOffset, GUI.Style.EquipmentSlotIconColor, origin: icon.size / 2, scale: slots[i].Rect.Width / icon.size.X);
}
continue;
@@ -1096,12 +1107,12 @@ namespace Barotrauma
//draw hand icons if the item is equipped in a hand slot
if (IsInLimbSlot(Items[i], InvSlotType.LeftHand))
{
var icon = limbSlotIcons[InvSlotType.LeftHand];
var icon = LimbSlotIcons[InvSlotType.LeftHand];
icon.Draw(spriteBatch, new Vector2(slots[i].Rect.X, slots[i].Rect.Bottom) + slots[i].DrawOffset, Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.35f, icon.size.Y * 0.75f), scale: slots[i].Rect.Width / icon.size.X * 0.7f);
}
if (IsInLimbSlot(Items[i], InvSlotType.RightHand))
{
var icon = limbSlotIcons[InvSlotType.RightHand];
var icon = LimbSlotIcons[InvSlotType.RightHand];
icon.Draw(spriteBatch, new Vector2(slots[i].Rect.Right, slots[i].Rect.Bottom) + slots[i].DrawOffset, Color.White * 0.6f, origin: new Vector2(icon.size.X * 0.65f, icon.size.Y * 0.75f), scale: slots[i].Rect.Width / icon.size.X * 0.7f);
}

View File

@@ -48,6 +48,8 @@ namespace Barotrauma.Items.Components
private void UpdateConvexHulls()
{
if (item.Removed) { return; }
doorRect = new Rectangle(
item.Rect.Center.X - (int)(doorSprite.size.X / 2 * item.Scale),
item.Rect.Y - item.Rect.Height / 2 + (int)(doorSprite.size.Y / 2.0f * item.Scale),
@@ -92,8 +94,8 @@ namespace Barotrauma.Items.Components
}
}
}
if (convexHull == null) return;
if (convexHull == null) { return; }
if (rect.Height == 0 || rect.Width == 0)
{
@@ -128,7 +130,7 @@ namespace Barotrauma.Items.Components
if (brokenSprite == null)
{
//broken doors turn black if no broken sprite has been configured
color *= (item.Condition / item.Prefab.Health);
color *= (item.Condition / item.MaxCondition);
color.A = 255;
}
@@ -162,10 +164,10 @@ namespace Barotrauma.Items.Components
color, 0.0f, doorSprite.Origin, item.Scale, SpriteEffects.None, doorSprite.Depth);
}
if (brokenSprite != null && item.Health < item.Prefab.Health)
if (brokenSprite != null && item.Health < item.MaxCondition)
{
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f, 1.0f - item.Health / item.Prefab.Health) : Vector2.One;
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.Prefab.Health : 1.0f;
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f, 1.0f - item.Health / item.MaxCondition) : Vector2.One;
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.MaxCondition : 1.0f;
spriteBatch.Draw(brokenSprite.Texture, pos,
new Rectangle((int)(brokenSprite.SourceRect.X + brokenSprite.size.X * openState), brokenSprite.SourceRect.Y,
(int)(brokenSprite.size.X * (1.0f - openState)), (int)brokenSprite.size.Y),
@@ -188,10 +190,10 @@ namespace Barotrauma.Items.Components
color, 0.0f, doorSprite.Origin, item.Scale, SpriteEffects.None, doorSprite.Depth);
}
if (brokenSprite != null && item.Health < item.Prefab.Health)
if (brokenSprite != null && item.Health < item.MaxCondition)
{
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - item.Health / item.Prefab.Health, 1.0f) : Vector2.One;
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.Prefab.Health : 1.0f;
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - item.Health / item.MaxCondition, 1.0f) : Vector2.One;
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.MaxCondition : 1.0f;
spriteBatch.Draw(brokenSprite.Texture, pos,
new Rectangle(brokenSprite.SourceRect.X, (int)(brokenSprite.SourceRect.Y + brokenSprite.size.Y * openState),
(int)brokenSprite.size.X, (int)(brokenSprite.size.Y * (1.0f - openState))),

View File

@@ -114,6 +114,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
crosshairSprite?.Remove();
crosshairSprite = null;
crosshairPointerSprite?.Remove();

View File

@@ -186,7 +186,6 @@ namespace Barotrauma.Items.Components
}
}
private bool shouldMuffleLooping;
private float lastMuffleCheckTime;
private ItemSound loopingSound;
@@ -295,8 +294,6 @@ namespace Barotrauma.Items.Components
PlaySound(matchingSounds[index], item.WorldPosition);
}
}
private void PlaySound(ItemSound itemSound, Vector2 position)
{
if (Vector2.DistanceSquared(new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y), position) > itemSound.Range * itemSound.Range)
@@ -387,7 +384,6 @@ namespace Barotrauma.Items.Components
return true;
}
public ItemComponent GetLinkUIToComponent()
{
if (string.IsNullOrEmpty(LinkUIToComponent))
@@ -431,13 +427,8 @@ namespace Barotrauma.Items.Components
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - GUIFrame defined as rect, use RectTransform instead.");
break;
}
Color? color = null;
if (subElement.Attribute("color") != null) color = subElement.GetAttributeColor("color", Color.White);
string style = subElement.Attribute("style") == null ?
null : subElement.GetAttributeString("style", "");
GuiFrame = new GUIFrame(RectTransform.Load(subElement, GUI.Canvas.ItemComponentHolder, Anchor.Center), style, color);
DefaultLayout = GUILayoutSettings.Load(subElement);
GuiFrameSource = subElement;
ReloadGuiFrame();
break;
case "alternativelayout":
AlternativeLayout = GUILayoutSettings.Load(subElement);
@@ -501,6 +492,42 @@ namespace Barotrauma.Items.Components
return true; //element processed
}
private XElement GuiFrameSource;
protected void ReleaseGuiFrame()
{
if (GuiFrame != null)
{
GuiFrame.RectTransform.Parent = null;
}
}
protected void ReloadGuiFrame()
{
if (GuiFrame != null)
{
ReleaseGuiFrame();
}
Color? color = null;
if (GuiFrameSource.Attribute("color") != null)
{
color = GuiFrameSource.GetAttributeColor("color", Color.White);
}
string style = GuiFrameSource.Attribute("style") == null ? null : GuiFrameSource.GetAttributeString("style", "");
GuiFrame = new GUIFrame(RectTransform.Load(GuiFrameSource, GUI.Canvas.ItemComponentHolder, Anchor.Center), style, color);
DefaultLayout = GUILayoutSettings.Load(GuiFrameSource);
if (GuiFrame != null)
{
GuiFrame.RectTransform.ParentChanged += OnGUIParentChanged;
}
GameMain.Instance.ResolutionChanged += OnResolutionChanged;
}
/// <summary>
/// Overload this method and implement. The method is automatically called when the resolution changes.
/// </summary>
protected virtual void CreateGUI() { }
//Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero.
protected void StartDelayedCorrection(ServerNetObject type, IReadMessage buffer, float sendingTime, bool waitForMidRoundSync = false)
{
@@ -530,5 +557,29 @@ namespace Barotrauma.Items.Components
yield return CoroutineStatus.Success;
}
/// <summary>
/// Launches when the parent of the GuiFrame is changed.
/// </summary>
protected void OnGUIParentChanged(RectTransform newParent)
{
if (newParent == null)
{
// Make sure to unregister. It doesn't matter if we haven't ever registered to the event.
GameMain.Instance.ResolutionChanged -= OnResolutionChangedPrivate;
}
}
protected virtual void OnResolutionChanged() { }
private void OnResolutionChangedPrivate()
{
if (RecreateGUIOnResolutionChange)
{
ReloadGuiFrame();
CreateGUI();
}
OnResolutionChanged();
}
}
}

View File

@@ -13,8 +13,6 @@ namespace Barotrauma.Items.Components
private GUICustomComponent guiCustomComponent;
private Point prevResolution;
public Sprite InventoryTopSprite
{
get { return inventoryTopSprite; }
@@ -115,16 +113,16 @@ namespace Barotrauma.Items.Components
{
CanBeFocused = false
};
GuiFrame.RectTransform.ParentChanged += OnGUIParentChanged;
}
else
{
//if a GUIFrame has been defined, draw the inventory inside it
CreateGUI();
prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
}
private void CreateGUI()
protected override void CreateGUI()
{
var content = new GUIFrame(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset },
style: null)
@@ -167,16 +165,6 @@ namespace Barotrauma.Items.Components
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{
if (hideItems || (item.body != null && !item.body.Enabled)) { return; }
if ((prevResolution.X > 0 && prevResolution.Y > 0) &&
(prevResolution.X != GameMain.GraphicsWidth || prevResolution.Y != GameMain.GraphicsHeight))
{
GuiFrame.ClearChildren();
CreateGUI();
prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
DrawContainedItems(spriteBatch, itemDepth);
}
@@ -238,7 +226,7 @@ namespace Barotrauma.Items.Components
if (item.FlippedY) { origin.Y = containedItem.Sprite.SourceRect.Height - origin.Y; }
float containedSpriteDepth = ContainedSpriteDepth < 0.0f ? containedItem.Sprite.Depth : ContainedSpriteDepth;
containedSpriteDepth = itemDepth + (containedSpriteDepth - item.SpriteDepth) / 10000.0f;
containedSpriteDepth = itemDepth + (containedSpriteDepth - (item.Sprite?.Depth ?? item.SpriteDepth)) / 10000.0f;
containedItem.Sprite.Draw(
spriteBatch,

View File

@@ -20,7 +20,7 @@ namespace Barotrauma.Items.Components
private float[] charWidths;
[Serialize("0,0,0,0", true, description: "The amount of padding around the text in pixels (left,top,right,bottom). ")]
[Serialize("0,0,0,0", true, description: "The amount of padding around the text in pixels (left,top,right,bottom).")]
public Vector4 Padding
{
get { return TextBlock.Padding; }
@@ -34,7 +34,7 @@ namespace Barotrauma.Items.Components
get { return text; }
set
{
if (value == text || item.Rect.Width < 5) return;
if (value == text || item.Rect.Width < 5) { return; }
if (TextBlock.Rect.Width != item.Rect.Width || textBlock.Rect.Height != item.Rect.Height)
{
@@ -64,7 +64,7 @@ namespace Barotrauma.Items.Components
get { return textColor; }
set
{
if (textBlock != null) textBlock.TextColor = value;
if (textBlock != null) { textBlock.TextColor = value; }
textColor = value;
}
}
@@ -75,7 +75,7 @@ namespace Barotrauma.Items.Components
get { return textBlock == null ? 1.0f : textBlock.TextScale; }
set
{
if (textBlock != null) textBlock.TextScale = MathHelper.Clamp(value, 0.1f, 10.0f);
if (textBlock != null) { textBlock.TextScale = MathHelper.Clamp(value, 0.1f, 10.0f); }
}
}
@@ -107,7 +107,7 @@ namespace Barotrauma.Items.Components
if (textBlock == null)
{
textBlock = new GUITextBlock(new RectTransform(item.Rect.Size), "",
textColor: textColor, font: GUI.UnscaledSmallFont, textAlignment: Alignment.Center, wrap: true, style: null)
textColor: textColor, font: GUI.UnscaledSmallFont, textAlignment: scrollable ? Alignment.CenterLeft : Alignment.Center, wrap: true, style: null)
{
TextDepth = item.SpriteDepth - 0.00001f,
RoundToNearestPixel = false,

View File

@@ -27,8 +27,9 @@ namespace Barotrauma.Items.Components
if (backgroundSprite == null) { return; }
backgroundSprite.DrawTiled(spriteBatch,
new Vector2(item.DrawPosition.X - item.Rect.Width / 2, -(item.DrawPosition.Y + item.Rect.Height / 2)) - backgroundSprite.Origin,
new Vector2(backgroundSprite.size.X, item.Rect.Height), color: item.Color,
new Vector2(item.DrawPosition.X - item.Rect.Width / 2 * item.Scale, -(item.DrawPosition.Y + item.Rect.Height / 2)) - backgroundSprite.Origin * item.Scale,
new Vector2(backgroundSprite.size.X * item.Scale, item.Rect.Height), color: item.Color,
textureScale: Vector2.One * item.Scale,
depth: BackgroundSpriteDepth);
}

View File

@@ -1,5 +1,6 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework.Graphics;
using System.ComponentModel;
namespace Barotrauma.Items.Components
{
@@ -74,6 +75,21 @@ namespace Barotrauma.Items.Components
}
}
#if DEBUG
public override void CreateEditingHUD(SerializableEntityEditor editor)
{
base.CreateEditingHUD(editor);
foreach (LimbPos limbPos in limbPositions)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(limbPos);
PropertyDescriptor limbPosProperty = properties.Find("Position", false);
editor.CreateVector2Field(limbPos, new SerializableProperty(limbPosProperty), limbPos.Position, limbPos.LimbType.ToString(), "");
}
}
#endif
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
State = msg.ReadBoolean();

View File

@@ -23,17 +23,15 @@ namespace Barotrauma.Items.Components
partial void InitProjSpecific(XElement element)
{
CreateGUI();
GameMain.Instance.OnResolutionChanged += RecreateGUI;
}
private void RecreateGUI()
protected override void OnResolutionChanged()
{
GuiFrame.ClearChildren();
CreateGUI();
base.OnResolutionChanged();
OnItemLoadedProjSpecific();
}
private void CreateGUI()
protected override void CreateGUI()
{
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.90f, 0.80f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
@@ -170,10 +168,5 @@ namespace Barotrauma.Items.Components
SetActive(msg.ReadBoolean());
progressTimer = msg.ReadSingle();
}
protected override void RemoveComponentSpecific()
{
GameMain.Instance.OnResolutionChanged -= RecreateGUI;
}
}
}

View File

@@ -42,17 +42,15 @@ namespace Barotrauma.Items.Components
partial void InitProjSpecific()
{
CreateGUI();
GameMain.Instance.OnResolutionChanged += RecreateGUI;
}
private void RecreateGUI()
protected override void OnResolutionChanged()
{
GuiFrame.ClearChildren();
CreateGUI();
base.OnResolutionChanged();
OnItemLoadedProjSpecific();
}
private void CreateGUI()
protected override void CreateGUI()
{
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter);
@@ -242,8 +240,8 @@ namespace Barotrauma.Items.Components
var item1 = c1.GUIComponent.UserData as FabricationRecipe;
var item2 = c2.GUIComponent.UserData as FabricationRecipe;
bool hasSkills1 = DegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f;
bool hasSkills2 = DegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f;
bool hasSkills1 = FabricationDegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f;
bool hasSkills2 = FabricationDegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f;
if (hasSkills1 != hasSkills2)
{
@@ -267,7 +265,7 @@ namespace Barotrauma.Items.Components
AutoScaleHorizontal = true,
CanBeFocused = false
};
var firstinSufficient = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe fabricableItem && DegreeOfSuccess(character, fabricableItem.RequiredSkills) < 0.5f);
var firstinSufficient = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe fabricableItem && FabricationDegreeOfSuccess(character, fabricableItem.RequiredSkills) < 0.5f);
if (firstinSufficient != null)
{
insufficientSkillsText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstinSufficient.RectTransform));
@@ -476,7 +474,7 @@ namespace Barotrauma.Items.Components
List<Skill> inadequateSkills = new List<Skill>();
if (user != null)
{
inadequateSkills = selectedItem.RequiredSkills.FindAll(skill => user.GetSkillLevel(skill.Identifier) < skill.Level);
inadequateSkills = selectedItem.RequiredSkills.FindAll(skill => user.GetSkillLevel(skill.Identifier) < Math.Round(skill.Level * SkillRequirementMultiplier));
}
if (selectedItem.RequiredSkills.Any())
@@ -489,13 +487,13 @@ namespace Barotrauma.Items.Components
};
foreach (Skill skill in selectedItem.RequiredSkills)
{
text += TextManager.Get("SkillName." + skill.Identifier) + " " + TextManager.Get("Lvl").ToLower() + " " + skill.Level;
text += TextManager.Get("SkillName." + skill.Identifier) + " " + TextManager.Get("Lvl").ToLower() + " " + Math.Round(skill.Level * SkillRequirementMultiplier);
if (skill != selectedItem.RequiredSkills.Last()) { text += "\n"; }
}
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedReqFrame.RectTransform), text, font: GUI.SmallFont);
}
float degreeOfSuccess = user == null ? 0.0f : DegreeOfSuccess(user, selectedItem.RequiredSkills);
float degreeOfSuccess = user == null ? 0.0f : FabricationDegreeOfSuccess(user, selectedItem.RequiredSkills);
if (degreeOfSuccess > 0.5f) { degreeOfSuccess = 1.0f; }
float requiredTime = user == null ? selectedItem.RequiredTime : GetRequiredTime(selectedItem, user);
@@ -621,10 +619,5 @@ namespace Barotrauma.Items.Components
StartFabricating(fabricationRecipes[itemIndex], user);
}
}
protected override void RemoveComponentSpecific()
{
GameMain.Instance.OnResolutionChanged -= RecreateGUI;
}
}
}

View File

@@ -24,7 +24,17 @@ namespace Barotrauma.Items.Components
partial void InitProjSpecific(XElement element)
{
noPowerTip = TextManager.Get("SteeringNoPowerTip");
CreateGUI();
}
protected override void OnResolutionChanged()
{
base.OnResolutionChanged();
CreateHUD();
}
protected override void CreateGUI()
{
GuiFrame.RectTransform.RelativeOffset = new Vector2(0.05f, 0.0f);
GuiFrame.CanBeFocused = true;
new GUICustomComponent(new RectTransform(GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center) { AbsoluteOffset = GUIStyle.ItemFrameOffset },
@@ -53,7 +63,11 @@ namespace Barotrauma.Items.Components
hullAirQualityText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), hullInfoContainer.RectTransform), "") { Wrap = true };
hullWaterText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), hullInfoContainer.RectTransform), "") { Wrap = true };
hullInfoFrame.Children.ForEach(c => { c.CanBeFocused = false; c.Children.ForEach(c2 => c2.CanBeFocused = false); });
hullInfoFrame.Children.ForEach(c =>
{
c.CanBeFocused = false;
c.Children.ForEach(c2 => c2.CanBeFocused = false);
});
}
public override void AddToGUIUpdateList()
@@ -72,7 +86,7 @@ namespace Barotrauma.Items.Components
{
submarineContainer.ClearChildren();
if (item.Submarine == null) return;
if (item.Submarine == null) { return; }
item.Submarine.CreateMiniMap(submarineContainer);
displayedSubs.Clear();
@@ -125,10 +139,8 @@ namespace Barotrauma.Items.Components
GUI.Style.Orange * (float)Math.Abs(Math.Sin(Timing.TotalTime)), Color.Black * 0.8f, font: GUI.SubHeadingFont);
return;
}
if (!submarineContainer.Children.Any()) { return; }
foreach (GUIComponent child in submarineContainer.Children.First().Children)
foreach (GUIComponent child in submarineContainer.Children.FirstOrDefault()?.Children)
{
if (child.UserData is Hull hull)
{
@@ -177,9 +189,19 @@ namespace Barotrauma.Items.Components
HashSet<Submarine> subs = new HashSet<Submarine>();
foreach (Hull hull in Hull.hullList)
{
if (hull.Submarine == null) continue;
if (hull.Submarine == null) { continue; }
var hullFrame = submarineContainer.Children.FirstOrDefault()?.FindChild(hull);
if (hullFrame == null) continue;
if (hullFrame == null) { continue; }
hullFrame.Visible = true;
if (!submarineContainer.Rect.Contains(hullFrame.Rect))
{
if (hull.Submarine.Info.Type != SubmarineType.Player)
{
hullFrame.Visible = false;
continue;
}
}
hullDatas.TryGetValue(hull, out HullData hullData);
if (hullData == null)
@@ -294,7 +316,7 @@ namespace Barotrauma.Items.Components
foreach (Submarine sub in subs)
{
if (sub.HullVertices == null) { continue; }
if (sub.HullVertices == null || sub.Info.IsOutpost) { continue; }
Rectangle worldBorders = sub.GetDockedBorders();
worldBorders.Location += sub.WorldPosition.ToPoint();

View File

@@ -0,0 +1,42 @@
namespace Barotrauma.Items.Components
{
partial class OutpostTerminal : ItemComponent
{
private SubmarineSelection selectionUI;
public override bool Select(Character character)
{
if (GameMain.GameSession?.Campaign == null)
{
return false;
}
if (selectionUI == null)
{
selectionUI = new SubmarineSelection(true, null, GUICanvas.Instance.ItemComponentHolder);
}
GuiFrame = selectionUI.GuiFrame;
selectionUI.RefreshSubmarineDisplay(true);
IsActive = true;
return base.Select(character);
}
public override void Update(float deltaTime, Camera cam)
{
if (Character.Controlled?.SelectedConstruction != item)
{
IsActive = false;
return;
}
base.Update(deltaTime, cam);
if (selectionUI != null)
{
selectionUI.Update();
}
}
}
}

View File

@@ -157,16 +157,15 @@ namespace Barotrauma.Items.Components
}
}
CreateGUI();
GameMain.Instance.OnResolutionChanged += RecreateGUI;
}
private void RecreateGUI()
protected override void OnResolutionChanged()
{
GuiFrame.ClearChildren();
CreateGUI();
base.OnResolutionChanged();
UpdateGUIElements();
}
private void CreateGUI()
protected override void CreateGUI()
{
bool isConnectedToSteering = item.GetComponent<Steering>() != null;
Vector2 size = isConnectedToSteering ? controlBoxSize : new Vector2(controlBoxSize.X * 2.0f, controlBoxSize.Y);
@@ -667,23 +666,27 @@ namespace Barotrauma.Items.Components
signalWarningText.Visible = false;
}
if (GameMain.GameSession == null) { return; }
if (GameMain.GameSession == null || Level.Loaded == null) { return; }
if (Level.Loaded == null) { return; }
if (Level.Loaded.StartLocation != null)
{
DrawMarker(spriteBatch,
Level.Loaded.StartLocation.Name,
"outpost",
Level.Loaded.StartLocation.Name,
Level.Loaded.StartPosition, transducerCenter,
displayScale, center, DisplayRadius);
}
DrawMarker(spriteBatch,
GameMain.GameSession.StartLocation.Name,
"outpost",
GameMain.GameSession.StartLocation.Name,
Level.Loaded.StartPosition, transducerCenter,
displayScale, center, DisplayRadius);
DrawMarker(spriteBatch,
GameMain.GameSession.EndLocation.Name,
"outpost",
GameMain.GameSession.EndLocation.Name,
Level.Loaded.EndPosition, transducerCenter,
displayScale, center, DisplayRadius);
if (Level.Loaded.EndLocation != null && Level.Loaded.Type == LevelData.LevelType.LocationConnection)
{
DrawMarker(spriteBatch,
Level.Loaded.EndLocation.Name,
"outpost",
Level.Loaded.EndLocation.Name,
Level.Loaded.EndPosition, transducerCenter,
displayScale, center, DisplayRadius);
}
foreach (AITarget aiTarget in AITarget.List)
{
@@ -1444,8 +1447,6 @@ namespace Barotrauma.Items.Components
sprite.Remove();
}
targetIcons.Clear();
GameMain.Instance.OnResolutionChanged -= RecreateGUI;
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)

View File

@@ -53,6 +53,8 @@ namespace Barotrauma.Items.Components
private bool? swapDestinationOrder;
private GUIMessageBox enterOutpostPrompt;
private bool levelStartSelected;
public bool LevelStartSelected
{
@@ -105,10 +107,9 @@ namespace Barotrauma.Items.Components
}
}
CreateGUI();
GameMain.Instance.OnResolutionChanged += RecreateGUI;
}
private void CreateGUI()
protected override void CreateGUI()
{
controlContainer = new GUIFrame(new RectTransform(new Vector2(Sonar.controlBoxSize.X, 1 - Sonar.controlBoxSize.Y * 2), GuiFrame.RectTransform, Anchor.CenterLeft), "ItemUI");
var paddedControlContainer = new GUIFrame(new RectTransform(controlContainer.Rect.Size - GUIStyle.ItemFrameMargin, controlContainer.RectTransform, Anchor.Center)
@@ -220,11 +221,12 @@ namespace Barotrauma.Items.Components
};
levelEndTickBox = new GUITickBox(new RectTransform(new Vector2(1, 0.333f), paddedAutoPilotControls.RectTransform, Anchor.BottomCenter),
GameMain.GameSession?.EndLocation == null ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.Name, textLimit),
(GameMain.GameSession?.EndLocation == null || Level.IsLoadedOutpost) ? "" : ToolBox.LimitString(GameMain.GameSession.EndLocation.Name, textLimit),
font: GUI.SmallFont, style: "GUIRadioButton")
{
Enabled = autoPilot,
Selected = levelEndSelected,
Visible = GameMain.GameSession?.EndLocation != null,
OnSelected = tickBox =>
{
if (levelEndSelected != tickBox.Selected)
@@ -321,7 +323,7 @@ namespace Barotrauma.Items.Components
};
break;
}
new GUITextBlock(new RectTransform(Vector2.One, left.RectTransform), leftText, font: GUI.SubHeadingFont, wrap: true, textAlignment: Alignment.CenterRight);
new GUITextBlock(new RectTransform(Vector2.One, left.RectTransform), leftText, font: GUI.SubHeadingFont, wrap: leftText.Contains(' '), textAlignment: Alignment.CenterRight);
new GUITextBlock(new RectTransform(Vector2.One, center.RectTransform), centerText, font: GUI.Font, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
var digitalFrame = new GUIFrame(new RectTransform(Vector2.One, right.RectTransform), style: "DigitalFrameDark");
new GUITextBlock(new RectTransform(Vector2.One * 0.85f, digitalFrame.RectTransform, Anchor.Center), "12345", GUI.Style.TextColorDark, GUI.DigitalFont, Alignment.CenterRight)
@@ -347,18 +349,47 @@ namespace Barotrauma.Items.Components
{
OnClicked = (btn, userdata) =>
{
if (GameMain.Client == null)
if (GameMain.GameSession?.Campaign != null)
{
item.SendSignal(0, "1", "toggle_docking", sender: null);
}
else
{
dockingNetworkMessagePending = true;
item.CreateClientEvent(this);
if (Level.IsLoadedOutpost &&
DockingSources.Any(d => d.Docked && (d.DockingTarget?.Item.Submarine?.Info?.IsOutpost ?? false)))
{
GameMain.GameSession.Campaign.CampaignUI.SelectTab(CampaignMode.InteractionType.Map);
GameMain.GameSession.Campaign.ShowCampaignUI = true;
return false;
}
else if (!Level.IsLoadedOutpost && DockingModeEnabled && ActiveDockingSource != null &&
!ActiveDockingSource.Docked && (DockingTarget?.Item?.Submarine?.Info.IsOutpost ?? false))
{
enterOutpostPrompt = new GUIMessageBox("", TextManager.GetWithVariable("campaignenteroutpostprompt", "[locationname]", DockingTarget.Item.Submarine.Info.Name), new string[] { TextManager.Get("yes"), TextManager.Get("no") });
enterOutpostPrompt.Buttons[0].OnClicked += (btn, userdata) =>
{
SendDockingSignal();
enterOutpostPrompt.Close();
return true;
};
enterOutpostPrompt.Buttons[1].OnClicked += enterOutpostPrompt.Close;
return false;
}
}
SendDockingSignal();
return true;
}
};
void SendDockingSignal()
{
if (GameMain.Client == null)
{
item.SendSignal(0, "1", "toggle_docking", sender: null);
}
else
{
dockingNetworkMessagePending = true;
item.CreateClientEvent(this);
}
}
dockingButton.Font = GUI.SubHeadingFont;
dockingButton.TextBlock.RectTransform.MaxSize = new Point((int)(dockingButton.Rect.Width * 0.7f), int.MaxValue);
dockingButton.TextBlock.AutoScaleHorizontal = true;
@@ -413,10 +444,9 @@ namespace Barotrauma.Items.Components
GameMain.GameSession?.EndLocation == null ? "End" : GameMain.GameSession.EndLocation.Name);
}
private void RecreateGUI()
protected override void OnResolutionChanged()
{
GuiFrame.ClearChildren();
CreateGUI();
base.OnResolutionChanged();
UpdateGUIElements();
}
@@ -600,6 +630,10 @@ namespace Barotrauma.Items.Components
dockingContainer.Visible = DockingModeEnabled;
statusContainer.Visible = !DockingModeEnabled;
if (!DockingModeEnabled)
{
enterOutpostPrompt?.Close();
}
if (DockingModeEnabled && ActiveDockingSource != null)
{
@@ -613,6 +647,10 @@ namespace Barotrauma.Items.Components
dockingButton.Pulsate(Vector2.One, Vector2.One * 1.2f, dockingButton.FlashTimer);
}
}
else
{
enterOutpostPrompt?.Close();
}
}
else if (DockingSources.Any(d => d.Docked))
{
@@ -663,7 +701,8 @@ namespace Barotrauma.Items.Components
if (Vector2.DistanceSquared(PlayerInput.MousePosition, steerArea.Rect.Center.ToVector2()) < steerRadius * steerRadius)
{
if (PlayerInput.PrimaryMouseButtonHeld() && !CrewManager.IsCommandInterfaceOpen && !GameSession.IsTabMenuOpen)
if (PlayerInput.PrimaryMouseButtonHeld() && !CrewManager.IsCommandInterfaceOpen && !GameSession.IsTabMenuOpen &&
(!GameMain.GameSession?.Campaign?.ShowCampaignUI ?? true) && !GUIMessageBox.MessageBoxes.Any())
{
Vector2 inputPos = PlayerInput.MousePosition - steerArea.Rect.Center.ToVector2();
inputPos.Y = -inputPos.Y;
@@ -800,8 +839,7 @@ namespace Barotrauma.Items.Components
maintainPosIndicator?.Remove();
maintainPosOriginIndicator?.Remove();
steeringIndicator?.Remove();
GameMain.Instance.OnResolutionChanged -= RecreateGUI;
enterOutpostPrompt?.Close();
}
public void ClientWrite(IWriteMessage msg, object[] extraData = null)

View File

@@ -111,27 +111,28 @@ namespace Barotrauma.Items.Components
if (item.FlippedX && item.Prefab.CanSpriteFlipX) { indicatorPos.X = -indicatorPos.X - indicatorSize.X * item.Scale; }
if (item.FlippedY && item.Prefab.CanSpriteFlipY) { indicatorPos.Y = -indicatorPos.Y - indicatorSize.Y * item.Scale; }
if (charge > 0)
if (charge > 0 && capacity > 0)
{
Color indicatorColor = ToolBox.GradientLerp(charge / capacity, Color.Red, Color.Orange, Color.Green);
float chargeRatio = MathHelper.Clamp(charge / capacity, 0.0f, 1.0f);
Color indicatorColor = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
if (!isHorizontal)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y + ((indicatorSize.Y * item.Scale) * (1.0f - charge / capacity))) + indicatorPos,
new Vector2(indicatorSize.X * item.Scale, (indicatorSize.Y * item.Scale) * (charge / capacity)), indicatorColor, true,
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y + ((indicatorSize.Y * item.Scale) * (1.0f - chargeRatio))) + indicatorPos,
new Vector2(indicatorSize.X * item.Scale, (indicatorSize.Y * item.Scale) * chargeRatio), indicatorColor, true,
depth: item.SpriteDepth - 0.00001f);
}
else
{
GUI.DrawRectangle(spriteBatch,
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y) + indicatorPos,
new Vector2((indicatorSize.X * item.Scale) * (charge / capacity), indicatorSize.Y * item.Scale), indicatorColor, true,
new Vector2((indicatorSize.X * item.Scale) * chargeRatio, indicatorSize.Y * item.Scale), indicatorColor, true,
depth: item.SpriteDepth - 0.00001f);
}
}
GUI.DrawRectangle(spriteBatch,
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y) + indicatorPos,
indicatorSize * item.Scale, Color.Black, depth: item.SpriteDepth - 0.00001f);
indicatorSize * item.Scale, Color.Black, depth: item.SpriteDepth - 0.000015f);
}
public void ClientWrite(IWriteMessage msg, object[] extraData)

View File

@@ -83,7 +83,7 @@ namespace Barotrauma.Items.Components
var progressBar = user.UpdateHUDProgressBar(
targetStructure.ID * 1000 + sectionIndex, //unique "identifier" for each wall section
progressBarPos,
1.0f - targetStructure.SectionDamage(sectionIndex) / targetStructure.Health,
MathUtils.InverseLerp(targetStructure.Prefab.MinHealth, targetStructure.Health, targetStructure.Health - targetStructure.SectionDamage(sectionIndex)),
GUI.Style.Red, GUI.Style.Green);
if (progressBar != null) progressBar.Size = new Vector2(60.0f, 20.0f);

View File

@@ -1,4 +1,5 @@
using Barotrauma.Networking;
using System;
using Barotrauma.Networking;
using Barotrauma.Particles;
using Barotrauma.Sounds;
using Microsoft.Xna.Framework;
@@ -49,6 +50,42 @@ namespace Barotrauma.Items.Components
}
partial void InitProjSpecific(XElement element)
{
CreateGUI();
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "emitter":
case "particleemitter":
particleEmitters.Add(new ParticleEmitter(subElement));
float minCondition = subElement.GetAttributeFloat("mincondition", 0.0f);
float maxCondition = subElement.GetAttributeFloat("maxcondition", 100.0f);
if (maxCondition < minCondition)
{
DebugConsole.ThrowError("Invalid damage particle configuration in the Repairable component of " + item.Name + ". MaxCondition needs to be larger than MinCondition.");
float temp = maxCondition;
maxCondition = minCondition;
minCondition = temp;
}
particleEmitterConditionRanges.Add(new Vector2(minCondition, maxCondition));
break;
}
}
}
private void RecreateGUI()
{
if (GuiFrame != null)
{
GuiFrame.ClearChildren();
CreateGUI();
}
}
private void CreateGUI()
{
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.75f), GuiFrame.RectTransform, Anchor.Center), childAnchor: Anchor.TopCenter)
{
@@ -56,7 +93,7 @@ namespace Barotrauma.Items.Components
RelativeSpacing = 0.05f,
CanBeFocused = true
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.15f), paddedFrame.RectTransform),
header, textAlignment: Alignment.TopCenter, font: GUI.LargeFont);
@@ -68,7 +105,7 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < requiredSkills.Count; i++)
{
var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + requiredSkills[i].Identifier), ((int) requiredSkills[i].Level).ToString()),
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + requiredSkills[i].Identifier), ((int) Math.Round(requiredSkills[i].Level * SkillRequirementMultiplier)).ToString()),
font: GUI.SmallFont)
{
UserData = requiredSkills[i]
@@ -111,43 +148,27 @@ namespace Barotrauma.Items.Components
return true;
}
};
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "emitter":
case "particleemitter":
particleEmitters.Add(new ParticleEmitter(subElement));
float minCondition = subElement.GetAttributeFloat("mincondition", 0.0f);
float maxCondition = subElement.GetAttributeFloat("maxcondition", 100.0f);
if (maxCondition < minCondition)
{
DebugConsole.ThrowError("Invalid damage particle configuration in the Repairable component of " + item.Name + ". MaxCondition needs to be larger than MinCondition.");
float temp = maxCondition;
maxCondition = minCondition;
minCondition = temp;
}
particleEmitterConditionRanges.Add(new Vector2(minCondition, maxCondition));
break;
}
}
}
partial void UpdateProjSpecific(float deltaTime)
{
if (Character.Controlled == null || (Character.Controlled.CharacterHealth.GetAffliction("psychosis")?.Strength ?? 0.0f) <= 0.0f)
if (FakeBrokenTimer > 0.0f)
{
FakeBrokenTimer = 0.0f;
item.FakeBroken = true;
if (Character.Controlled == null || (Character.Controlled.CharacterHealth.GetAffliction("psychosis")?.Strength ?? 0.0f) <= 0.0f)
{
FakeBrokenTimer = 0.0f;
}
else
{
FakeBrokenTimer -= deltaTime;
}
}
else
{
FakeBrokenTimer -= deltaTime;
item.FakeBroken = false;
}
item.FakeBroken = FakeBrokenTimer > 0.0f;
if (!GameMain.IsMultiplayer)
{
@@ -211,7 +232,7 @@ namespace Barotrauma.Items.Components
if (!(c.UserData is Skill skill)) continue;
GUITextBlock textBlock = (GUITextBlock)c;
if (character.GetSkillLevel(skill.Identifier) < skill.Level)
if (character.GetSkillLevel(skill.Identifier) < (skill.Level * SkillRequirementMultiplier))
{
textBlock.TextColor = GUI.Style.Red;
}
@@ -247,6 +268,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
repairSoundChannel?.FadeOutAndDispose();
repairSoundChannel = null;
}

View File

@@ -289,7 +289,7 @@ namespace Barotrauma.Items.Components
flashColor * (float)Math.Sin(FlashTimer % flashCycleDuration / flashCycleDuration * MathHelper.Pi * 0.8f), scale: connectorSpriteScale);
}
if (Wires.Any(w => w != null && w != DraggingConnected))
if (Wires.Any(w => w != null && w != DraggingConnected && !w.Hidden))
{
int screwIndex = (int)Math.Floor(position.Y / 30.0f) % screwSprites.Count;
screwSprites[screwIndex].Draw(spriteBatch, position, scale: connectorSpriteScale);

View File

@@ -18,17 +18,9 @@ namespace Barotrauma.Items.Components
partial void InitProjSpecific(XElement element)
{
CreateGUI();
GameMain.Instance.OnResolutionChanged += RecreateGUI;
}
private void RecreateGUI()
{
GuiFrame.ClearChildren();
CreateGUI();
UpdateLabelsProjSpecific();
}
private void CreateGUI()
protected override void CreateGUI()
{
uiElements.Clear();
var visibleElements = customInterfaceElementList.Where(ciElement => !string.IsNullOrEmpty(ciElement.Label));
@@ -309,10 +301,5 @@ namespace Barotrauma.Items.Components
}
}
}
protected override void RemoveComponentSpecific()
{
GameMain.Instance.OnResolutionChanged -= RecreateGUI;
}
}
}

View File

@@ -187,7 +187,7 @@ namespace Barotrauma.Items.Components
}
if (IsActive && item.ParentInventory?.Owner is Character user && user == Character.Controlled)// && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance)
{
if (user.CanInteract)
if (user.CanInteract && currLength < MaxLength)
{
Vector2 gridPos = Character.Controlled.Position;
Vector2 roundedGridPos = new Vector2(

View File

@@ -135,7 +135,7 @@ namespace Barotrauma.Items.Components
Entity targetEntity = Entity.FindEntityByID(dockingTargetID);
if (targetEntity == null || !(targetEntity is Item))
{
DebugConsole.ThrowError("Invalid docking port network event (can't dock to " + targetEntity?.ToString() ?? "null" + ")");
DebugConsole.ThrowError("Invalid docking port network event (can't dock to " + (targetEntity?.ToString() ?? "null") + ")");
return;
}

View File

@@ -260,7 +260,11 @@ namespace Barotrauma
item.Name :
item.Name + '\n' + description;
}
if (item.SpawnedInOutpost)
{
string colorStr = XMLExtensions.ColorToString(GUI.Style.Red);
toolTip = $"‖color:{colorStr}‖{toolTip}‖color:end‖";
}
return toolTip;
}
}
@@ -1127,6 +1131,8 @@ namespace Barotrauma
public static void DrawFront(SpriteBatch spriteBatch)
{
if (GUI.PauseMenuOpen || GUI.SettingsMenuOpen) { return; }
if (GameMain.GameSession?.Campaign != null &&
(GameMain.GameSession.Campaign.ShowCampaignUI || GameMain.GameSession.Campaign.ForceMapUI)) { return; }
subInventorySlotsToDraw.Clear();
subInventorySlotsToDraw.AddRange(highlightedSubInventorySlots);
@@ -1377,6 +1383,17 @@ namespace Barotrauma
sprite.Draw(spriteBatch, itemPos + Vector2.One * 2, Color.Black * 0.6f, rotate: rotation, scale: scale);
}
sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale);
if (item.SpawnedInOutpost && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
{
var stealIcon = CharacterInventory.LimbSlotIcons[InvSlotType.LeftHand];
Vector2 iconSize = new Vector2(25 * GUI.Scale);
stealIcon.Draw(
spriteBatch,
new Vector2(rect.X + iconSize.X * 0.2f, rect.Bottom - iconSize.Y * 1.2f),
color: GUI.Style.Red,
scale: iconSize.X / stealIcon.size.X);
}
}
if (inventory != null &&

View File

@@ -246,7 +246,7 @@ namespace Barotrauma
float fadeInBrokenSpriteAlpha = 0.0f;
float displayCondition = FakeBroken ? 0.0f : condition;
Vector2 drawOffset = Vector2.Zero;
if (displayCondition < Prefab.Health)
if (displayCondition < MaxCondition)
{
for (int i = 0; i < Prefab.BrokenSprites.Count; i++)
{
@@ -299,22 +299,23 @@ namespace Barotrauma
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
decorativeSprite.Sprite.DrawTiled(spriteBatch,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
new Vector2(rect.Width, rect.Height), color: color,
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
size, color: color,
textureScale: Vector2.One * Scale,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
}
}
else
{
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, SpriteRotation, Scale, activeSprite.effects, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, SpriteRotation, Scale, activeSprite.effects, depth - 0.000001f);
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, SpriteRotation + rotation, Scale, activeSprite.effects, depth);
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, SpriteRotation + rotation, Scale, activeSprite.effects, depth - 0.000001f);
foreach (var decorativeSprite in Prefab.DecorativeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
float rot = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + offset.X, -(DrawPosition.Y + offset.Y)), color,
SpriteRotation + rotation, decorativeSprite.Scale * Scale, activeSprite.effects,
SpriteRotation + rotation + rot, decorativeSprite.Scale * Scale, activeSprite.effects,
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
}
}
@@ -328,7 +329,7 @@ namespace Barotrauma
if (holdable.Picker.SelectedItems[0] == this)
{
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand);
if (holdLimb != null)
if (holdLimb?.ActiveSprite != null)
{
depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() + depthStep * 2;
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
@@ -340,7 +341,7 @@ namespace Barotrauma
else if (holdable.Picker.SelectedItems[1] == this)
{
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand);
if (holdLimb != null)
if (holdLimb?.ActiveSprite != null)
{
depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() - depthStep * 2;
foreach (WearableSprite wearableSprite in holdLimb.WearingItems)
@@ -368,6 +369,23 @@ namespace Barotrauma
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
foreach (var upgrade in Upgrades)
{
var upgradeSprites = GetUpgradeSprites(upgrade);
foreach (var decorativeSprite in upgradeSprites)
{
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
var (xOff, yOff) = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + xOff, -(DrawPosition.Y + yOff)), color,
rotation, decorativeSprite.Scale * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
}
activeSprite.effects = oldEffects;
if (fadeInBrokenSprite != null && fadeInBrokenSprite.Sprite != activeSprite)
@@ -437,6 +455,21 @@ namespace Barotrauma
}
}
private void DrawDecorativeSprite(SpriteBatch spriteBatch, DecorativeSprite decorativeSprite, Color color, float depth)
{
if (!spriteAnimState[decorativeSprite].IsActive) { return; }
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
var ca = (float)Math.Cos(-body.Rotation);
var sa = (float)Math.Sin(-body.Rotation);
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X + transformedOffset.X, -(DrawPosition.Y + transformedOffset.Y)), color,
-body.Rotation + rotation, decorativeSprite.Scale * Scale, activeSprite.effects,
depth: depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth));
}
partial void OnCollisionProjSpecific(float impact)
{
if (impact > 1.0f &&
@@ -451,6 +484,22 @@ namespace Barotrauma
public void UpdateSpriteStates(float deltaTime)
{
DecorativeSprite.UpdateSpriteStates(Prefab.DecorativeSpriteGroups, spriteAnimState, ID, deltaTime, ConditionalMatches);
foreach (var upgrade in Upgrades)
{
var upgradeSprites = GetUpgradeSprites(upgrade);
foreach (var decorativeSprite in upgradeSprites)
{
var spriteState = spriteAnimState[decorativeSprite];
spriteState.IsActive = true;
foreach (var _ in decorativeSprite.IsActiveConditionals.Where(conditional => !ConditionalMatches(conditional)))
{
spriteState.IsActive = false;
break;
}
}
}
}
public override void UpdateEditing(Camera cam)
@@ -541,12 +590,14 @@ namespace Barotrauma
linkText.TextColor = GUI.Style.Orange;
itemsText.TextColor = GUI.Style.Orange;
}
var buttonContainer = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)), isHorizontal: true)
{
Stretch = true,
RelativeSpacing = 0.02f,
CanBeFocused = true
};
new GUIButton(new RectTransform(new Vector2(0.23f, 1.0f), buttonContainer.RectTransform), TextManager.Get("MirrorEntityX"), style: "GUIButtonSmall")
{
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
@@ -588,6 +639,22 @@ namespace Barotrauma
buttonContainer.RectTransform.IsFixedSize = true;
itemEditor.AddCustomContent(buttonContainer, itemEditor.ContentCount);
GUITextBlock.AutoScaleAndNormalize(buttonContainer.Children.Select(b => ((GUIButton)b).TextBlock));
if (Submarine.MainSub?.Info?.Type == SubmarineType.OutpostModule)
{
GUITickBox tickBox = new GUITickBox(new RectTransform(new Point(listBox.Content.Rect.Width, 10)), TextManager.Get("sp.structure.removeiflinkedoutpostdoorinuse.name"))
{
Font = GUI.SmallFont,
Selected = RemoveIfLinkedOutpostDoorInUse,
ToolTip = TextManager.Get("sp.structure.removeiflinkedoutpostdoorinuse.description"),
OnSelected = (tickBox) =>
{
RemoveIfLinkedOutpostDoorInUse = tickBox.Selected;
return true;
}
};
itemEditor.AddCustomContent(tickBox, 1);
}
}
foreach (ItemComponent ic in components)
@@ -667,6 +734,39 @@ namespace Barotrauma
return editingHUD;
}
private List<DecorativeSprite> GetUpgradeSprites(Upgrade upgrade)
{
var upgradeSprites = upgrade.Prefab.DecorativeSprites;
if (Prefab.UpgradeOverrideSprites.ContainsKey(upgrade.Prefab.Identifier))
{
upgradeSprites = Prefab.UpgradeOverrideSprites[upgrade.Prefab.Identifier];
}
return upgradeSprites;
}
public override bool AddUpgrade(Upgrade upgrade, bool createNetworkEvent = false)
{
if (upgrade.Prefab.IsWallUpgrade) { return false; }
bool result = base.AddUpgrade(upgrade, createNetworkEvent);
if (result && !upgrade.Disposed)
{
List<DecorativeSprite> upgradeSprites = GetUpgradeSprites(upgrade);
if (upgradeSprites.Any())
{
foreach (DecorativeSprite decorativeSprite in upgradeSprites)
{
decorativeSprite.Sprite.EnsureLazyLoaded();
spriteAnimState.Add(decorativeSprite, new DecorativeSprite.State());
}
UpdateSpriteStates(0.0f);
}
}
return result;
}
private void CreateTagPicker(GUITextBox textBox, IEnumerable<string> availableTags)
{
var msgBox = new GUIMessageBox("", "", new string[] { TextManager.Get("Cancel") }, new Vector2(0.2f, 0.5f), new Point(300, 400));
@@ -756,6 +856,10 @@ namespace Barotrauma
private readonly List<Rectangle> debugInitialHudPositions = new List<Rectangle>();
private readonly List<ItemComponent> prevActiveHUDs = new List<ItemComponent>();
private readonly List<ItemComponent> activeComponents = new List<ItemComponent>();
private readonly List<ItemComponent> maxPriorityHUDs = new List<ItemComponent>();
public void UpdateHUD(Camera cam, Character character, float deltaTime)
{
bool editingHUDCreated = false;
@@ -774,8 +878,11 @@ namespace Barotrauma
editingHUDRefreshTimer -= deltaTime;
}
List<ItemComponent> prevActiveHUDs = new List<ItemComponent>(activeHUDs);
List<ItemComponent> activeComponents = new List<ItemComponent>(components);
prevActiveHUDs.Clear();
prevActiveHUDs.AddRange(activeHUDs);
activeComponents.Clear();
activeComponents.AddRange(components);
foreach (MapEntity entity in linkedTo)
{
if (prefab.IsLinkAllowed(entity.prefab) && entity is Item i)
@@ -788,12 +895,11 @@ namespace Barotrauma
activeHUDs.Clear();
//the HUD of the component with the highest priority will be drawn
//if all components have a priority of 0, all of them are drawn
List<ItemComponent> maxPriorityHUDs = new List<ItemComponent>();
maxPriorityHUDs.Clear();
bool DrawHud(ItemComponent ic) => ic.ShouldDrawHUD(character) && (ic.CanBeSelected && ic.HasRequiredItems(character, addMessage: false) || (character.HasEquippedItem(this) && ic.DrawHudWhenEquipped));
foreach (ItemComponent ic in activeComponents)
{
if (ic.HudPriority > 0 && ic.ShouldDrawHUD(character) &&
(ic.CanBeSelected || (character.HasEquippedItem(this) && ic.DrawHudWhenEquipped)) &&
(maxPriorityHUDs.Count == 0 || ic.HudPriority >= maxPriorityHUDs[0].HudPriority))
if (ic.HudPriority > 0 && DrawHud(ic) && (maxPriorityHUDs.Count == 0 || ic.HudPriority >= maxPriorityHUDs[0].HudPriority))
{
if (maxPriorityHUDs.Count > 0 && ic.HudPriority > maxPriorityHUDs[0].HudPriority) { maxPriorityHUDs.Clear(); }
maxPriorityHUDs.Add(ic);
@@ -808,8 +914,7 @@ namespace Barotrauma
{
foreach (ItemComponent ic in activeComponents)
{
if (ic.ShouldDrawHUD(character) &&
(ic.CanBeSelected || (character.HasEquippedItem(this) && ic.DrawHudWhenEquipped)))
if (DrawHud(ic))
{
activeHUDs.Add(ic);
}
@@ -914,11 +1019,11 @@ namespace Barotrauma
color = Color.Cyan;
}
}
texts.Add(new ColoredText(ic.DisplayMsg, color, false));
texts.Add(new ColoredText(ic.DisplayMsg, color, false, false));
}
if ((PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift)) && CrewManager.DoesItemHaveContextualOrders(this))
{
texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")), Color.Cyan, false));
texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")), Color.Cyan, false, false));
}
return texts;
}
@@ -1048,6 +1153,29 @@ namespace Barotrauma
ReadPropertyChange(msg, false);
editingHUDRefreshPending = true;
break;
case NetEntityEvent.Type.Upgrade:
{
string identifier = msg.ReadString();
byte level = msg.ReadByte();
if (UpgradePrefab.Find(identifier) is { } upgradePrefab)
{
Upgrade upgrade = new Upgrade(this, upgradePrefab, level);
byte targetCount = msg.ReadByte();
for (int i = 0; i < targetCount; i++)
{
byte propertyCount = msg.ReadByte();
for (int j = 0; j < propertyCount; j++)
{
float value = msg.ReadSingle();
upgrade.TargetComponents.ElementAt(i).Value[j].SetOriginalValue(value);
}
}
AddUpgrade(upgrade, false);
}
break;
}
case NetEntityEvent.Type.Invalid:
break;
}
@@ -1220,7 +1348,7 @@ namespace Barotrauma
ushort itemId = msg.ReadUInt16();
ushort inventoryId = msg.ReadUInt16();
DebugConsole.Log("Received entity spawn message for item " + itemName + ".");
DebugConsole.Log($"Received entity spawn message for item \"{itemName}\" (identifier: {itemIdentifier}, id: {itemId})");
Vector2 pos = Vector2.Zero;
Submarine sub = null;
@@ -1243,10 +1371,10 @@ namespace Barotrauma
}
}
byte bodyType = msg.ReadByte();
byte teamID = msg.ReadByte();
bool tagsChanged = msg.ReadBoolean();
byte bodyType = msg.ReadByte();
bool spawnedInOutpost = msg.ReadBoolean();
byte teamID = msg.ReadByte();
bool tagsChanged = msg.ReadBoolean();
string tags = "";
if (tagsChanged)
{
@@ -1289,6 +1417,7 @@ namespace Barotrauma
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
errorMsg);
DebugConsole.ThrowError(errorMsg);
inventory = parentItem.GetComponent<ItemContainer>()?.Inventory;
}
else if (parentItem.components[itemContainerIndex] is ItemContainer container)
{
@@ -1307,7 +1436,8 @@ namespace Barotrauma
var item = new Item(itemPrefab, pos, sub)
{
ID = itemId
ID = itemId,
SpawnedInOutpost = spawnedInOutpost
};
if (item.body != null)

View File

@@ -105,7 +105,7 @@ namespace Barotrauma
}
else
{
Vector2 placeSize = size;
Vector2 placeSize = size * Scale;
if (placePosition == Vector2.Zero)
{
@@ -161,7 +161,14 @@ namespace Barotrauma
}
else
{
sprite?.DrawTiled(spriteBatch, new Vector2(position.X, -position.Y), size, color: SpriteColor);
Vector2 placeSize = size * Scale;
if (placePosition != Vector2.Zero)
{
if (ResizeHorizontal) { placeSize.X = Math.Max(position.X - placePosition.X, placeSize.X); }
if (ResizeVertical) { placeSize.Y = Math.Max(placePosition.Y - position.Y, placeSize.Y); }
position = placePosition;
}
sprite?.DrawTiled(spriteBatch, new Vector2(position.X, -position.Y), placeSize, color: SpriteColor);
}
}
@@ -173,7 +180,15 @@ namespace Barotrauma
}
else
{
if (sprite != null) sprite.DrawTiled(spriteBatch, new Vector2(placeRect.X, -placeRect.Y), placeRect.Size.ToVector2(), null, SpriteColor * 0.8f);
Vector2 position = Submarine.MouseToWorldGrid(Screen.Selected.Cam, Submarine.MainSub);
Vector2 placeSize = size * Scale;
if (placePosition != Vector2.Zero)
{
if (ResizeHorizontal) { placeSize.X = Math.Max(position.X - placePosition.X, placeSize.X); }
if (ResizeVertical) { placeSize.Y = Math.Max(placePosition.Y - position.Y, placeSize.Y); }
position = placePosition;
}
sprite?.DrawTiled(spriteBatch, new Vector2(position.X, -position.Y), placeSize, color: SpriteColor);
}
}
}

View File

@@ -83,7 +83,7 @@ namespace Barotrauma
{
Vector2 dir = IsHorizontal ?
new Vector2(Math.Sign(linkedTo[i].Rect.Center.X - rect.Center.X), 0.0f)
: new Vector2(0.0f, Math.Sign((linkedTo[i].Rect.Y - linkedTo[i].Rect.Height / 2.0f) - (rect.Y - rect.Height / 2.0f)));
: new Vector2(0.0f, Math.Sign((rect.Y - rect.Height / 2.0f) - (linkedTo[i].Rect.Y - linkedTo[i].Rect.Height / 2.0f)));
Vector2 arrowPos = new Vector2(WorldRect.Center.X, -(WorldRect.Y - WorldRect.Height / 2));
arrowPos += new Vector2(dir.X * (WorldRect.Width / 2), dir.Y * (WorldRect.Height / 2));

View File

@@ -201,7 +201,7 @@ namespace Barotrauma
for (int i = 1; i < waveY.Length - 1; i++)
{
float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i]));
if (maxDelta > Rand.Range(1.0f, 10.0f))
if (maxDelta > 1.0f && maxDelta > Rand.Range(1.0f, 10.0f))
{
var particlePos = new Vector2(rect.X + WaveWidth * i, surface + waveY[i]);
if (Submarine != null) particlePos += Submarine.Position;

View File

@@ -22,7 +22,7 @@ namespace Barotrauma
HashSet<Texture2D> uniqueTextures = new HashSet<Texture2D>();
HashSet<Sprite> uniqueSprites = new HashSet<Sprite>();
var allLevelObjects = levelObjectManager.GetAllObjects();
var allLevelObjects = LevelObjectManager.GetAllObjects();
foreach (var levelObj in allLevelObjects)
{
foreach (Sprite sprite in levelObj.Prefab.Sprites)
@@ -56,7 +56,7 @@ namespace Barotrauma
if (GameMain.DebugDraw && Screen.Selected.Cam.Zoom > 0.1f)
{
foreach (InterestingPosition pos in positionsOfInterest)
foreach (InterestingPosition pos in PositionsOfInterest)
{
Color color = Color.Yellow;
if (pos.PositionType == PositionType.Cave)
@@ -71,7 +71,7 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch, new Vector2(pos.Position.X - 15.0f, -pos.Position.Y - 15.0f), new Vector2(30.0f, 30.0f), color, true);
}
foreach (RuinGeneration.Ruin ruin in ruins)
foreach (RuinGeneration.Ruin ruin in Ruins)
{
Rectangle ruinArea = ruin.Area;
ruinArea.Y = -ruinArea.Y - ruinArea.Height;
@@ -113,7 +113,7 @@ namespace Barotrauma
public void DrawBack(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam)
{
float brightness = MathHelper.Clamp(1.1f + (cam.Position.Y - Size.Y) / 100000.0f, 0.1f, 1.0f);
var lightColorHLS = generationParams.AmbientLightColor.RgbToHLS();
var lightColorHLS = GenerationParams.AmbientLightColor.RgbToHLS();
lightColorHLS.Y *= brightness;
GameMain.LightManager.AmbientLight = ToolBox.HLSToRGB(lightColorHLS);
@@ -121,12 +121,12 @@ namespace Barotrauma
graphics.Clear(BackgroundColor);
if (renderer == null) return;
renderer.DrawBackground(spriteBatch, cam, levelObjectManager, backgroundCreatureManager);
renderer.DrawBackground(spriteBatch, cam, LevelObjectManager, backgroundCreatureManager);
}
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
foreach (LevelWall levelWall in extraWalls)
foreach (LevelWall levelWall in ExtraWalls)
{
if (levelWall.Body.BodyType == BodyType.Static) continue;

View File

@@ -9,8 +9,12 @@ namespace Barotrauma
{
partial class LevelObjectManager
{
private List<LevelObject> visibleObjectsBack = new List<LevelObject>();
private List<LevelObject> visibleObjectsFront = new List<LevelObject>();
private readonly List<LevelObject> visibleObjectsBack = new List<LevelObject>();
private readonly List<LevelObject> visibleObjectsFront = new List<LevelObject>();
//Maximum number of visible objects drawn at once. Should be large enough to not have an effect during normal gameplay,
//but small enough to prevent wrecking performance when zooming out very far
const int MaxVisibleObjects = 500;
private Rectangle currentGridIndices;
@@ -43,7 +47,7 @@ namespace Barotrauma
{
for (int y = currentIndices.Y; y <= currentIndices.Height; y++)
{
if (objectGrid[x, y] == null) continue;
if (objectGrid[x, y] == null) { continue; }
foreach (LevelObject obj in objectGrid[x, y])
{
var objectList = obj.Position.Z >= 0 ? visibleObjectsBack : visibleObjectsFront;
@@ -69,6 +73,7 @@ namespace Barotrauma
if (drawOrderIndex >= 0)
{
objectList.Insert(drawOrderIndex, obj);
if (objectList.Count >= MaxVisibleObjects) { break; }
}
}
}

View File

@@ -209,7 +209,7 @@ namespace Barotrauma
level.GenerationParams.WaterParticles.DrawTiled(
spriteBatch, origin + offsetS,
new Vector2(cam.WorldView.Width - offsetS.X, cam.WorldView.Height - offsetS.Y),
rect: srcRect, color: Color.White * alpha, textureScale: new Vector2(texScale));
color: Color.White * alpha, textureScale: new Vector2(texScale));
}
}

View File

@@ -170,10 +170,12 @@ namespace Barotrauma.Lights
isHorizontal = BoundingBox.Width > BoundingBox.Height;
if (ParentEntity is Structure structure)
{
System.Diagnostics.Debug.Assert(!structure.Removed);
isHorizontal = structure.IsHorizontal;
}
else if (ParentEntity is Item item)
{
System.Diagnostics.Debug.Assert(!item.Removed);
var door = item.GetComponent<Door>();
if (door != null) { isHorizontal = door.IsHorizontal; }
}
@@ -444,7 +446,7 @@ namespace Barotrauma.Lights
CalculateDimensions();
if (ParentEntity == null) return;
if (ParentEntity == null) { return; }
var chList = HullLists.Find(h => h.Submarine == ParentEntity.Submarine);
if (chList != null)

View File

@@ -55,7 +55,9 @@ namespace Barotrauma.Lights
public bool ObstructVision;
private readonly Texture2D visionCircle;
private Vector2 losOffset;
public IEnumerable<LightSource> Lights
{
get { return lights; }
@@ -70,7 +72,7 @@ namespace Barotrauma.Lights
visionCircle = Sprite.LoadTexture("Content/Lights/visioncircle.png");
highlightRaster = Sprite.LoadTexture("Content/UI/HighlightRaster.png");
GameMain.Instance.OnResolutionChanged += () =>
GameMain.Instance.ResolutionChanged += () =>
{
CreateRenderTargets(graphics);
};
@@ -279,7 +281,7 @@ namespace Barotrauma.Lights
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
foreach (Character character in Character.CharacterList)
{
if (character.CurrentHull == null || !character.Enabled) { continue; }
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
if (Character.Controlled?.FocusedCharacter == character) { continue; }
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
@@ -297,7 +299,7 @@ namespace Barotrauma.Lights
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform);
foreach (Character character in Character.CharacterList)
{
if (character.CurrentHull == null || !character.Enabled) { continue; }
if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; }
if (Character.Controlled?.FocusedCharacter == character) { continue; }
Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ?
Color.Black :
@@ -335,20 +337,34 @@ namespace Barotrauma.Lights
if (Character.Controlled != null)
{
Vector2 haloDrawPos = Character.Controlled.DrawPosition;
DrawHalo(Character.Controlled);
}
else
{
foreach (Character character in Character.CharacterList)
{
if (character.Submarine == null || character.IsDead || !character.IsHuman) { continue; }
DrawHalo(character);
}
}
void DrawHalo(Character character)
{
Vector2 haloDrawPos = character.DrawPosition;
haloDrawPos.Y = -haloDrawPos.Y;
//ambient light decreases the brightness of the halo (no need for a bright halo if the ambient light is bright enough)
float ambientBrightness = (AmbientLight.R + AmbientLight.B + AmbientLight.G) / 255.0f / 3.0f;
Color haloColor = Color.White.Multiply(0.3f - ambientBrightness);
Color haloColor = Color.White.Multiply(0.3f - ambientBrightness);
if (haloColor.A > 0)
{
float scale = 512.0f / LightSource.LightTexture.Width;
spriteBatch.Draw(
LightSource.LightTexture, haloDrawPos, null, haloColor, 0.0f,
new Vector2(LightSource.LightTexture.Width, LightSource.LightTexture.Height) / 2, scale, SpriteEffects.None, 0.0f);
}
}
}
spriteBatch.End();
//draw the actual light volumes, additive particles, hull ambient lights and the halo around the player
@@ -477,10 +493,11 @@ namespace Barotrauma.Lights
graphics.Clear(Color.Black);
Vector2 diff = lookAtPosition - ViewTarget.WorldPosition;
diff.Y = -diff.Y;
float rotation = MathUtils.VectorToAngle(diff);
if (diff.LengthSquared() > 30.0f) { losOffset = diff; }
float rotation = MathUtils.VectorToAngle(losOffset);
Vector2 scale = new Vector2(
MathHelper.Clamp(diff.Length() / 256.0f, 2.0f, 5.0f), 2.0f);
MathHelper.Clamp(losOffset.Length() / 256.0f, 2.0f, 5.0f), 2.0f);
spriteBatch.Draw(visionCircle, new Vector2(ViewTarget.WorldPosition.X, -ViewTarget.WorldPosition.Y), null, Color.White, rotation,
new Vector2(visionCircle.Width * 0.2f, visionCircle.Height / 2), scale, SpriteEffects.None, 0.0f);

View File

@@ -1,50 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class Location
{
private HireManager hireManager;
public void RemoveHireableCharacter(CharacterInfo character)
{
if (!Type.HasHireableCharacters)
{
DebugConsole.ThrowError("Cannot hire a character from location \"" + Name + "\" - the location has no hireable characters.\n" + Environment.StackTrace);
return;
}
if (hireManager == null)
{
DebugConsole.ThrowError("Cannot hire a character from location \"" + Name + "\" - hire manager has not been instantiated.\n" + Environment.StackTrace);
return;
}
hireManager.RemoveCharacter(character);
}
public IEnumerable<CharacterInfo> GetHireableCharacters()
{
if (!Type.HasHireableCharacters)
{
return Enumerable.Empty<CharacterInfo>();
}
if (hireManager == null)
{
hireManager = new HireManager();
}
if (!hireManager.AvailableCharacters.Any())
{
hireManager.GenerateCharacters(location: this, amount: HireManager.MaxAvailableCharacters);
}
return hireManager.AvailableCharacters;
}
partial void RemoveProjSpecific()
{
hireManager?.Remove();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,14 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
namespace Barotrauma
{
abstract partial class MapEntityPrefab : IPrefab, IDisposable
{
public readonly Dictionary<string, List<DecorativeSprite>> UpgradeOverrideSprites = new Dictionary<string, List<DecorativeSprite>>();
public virtual void UpdatePlacing(Camera cam)
{
if (PlayerInput.SecondaryMouseButtonClicked())

View File

@@ -94,7 +94,23 @@ namespace Barotrauma
editingHUD = new GUIFrame(new RectTransform(new Vector2(0.3f, 0.25f), GUI.Canvas, Anchor.CenterRight) { MinSize = new Point(400, 0) }) { UserData = this };
GUIListBox listBox = new GUIListBox(new RectTransform(new Vector2(0.95f, 0.8f), editingHUD.RectTransform, Anchor.Center), style: null);
var editor = new SerializableEntityEditor(listBox.Content.RectTransform, this, inGame, showName: true, titleFont: GUI.LargeFont);
if (Submarine.MainSub?.Info?.Type == SubmarineType.OutpostModule)
{
GUITickBox tickBox = new GUITickBox(new RectTransform(new Point(listBox.Content.Rect.Width, 10)), TextManager.Get("sp.structure.removeiflinkedoutpostdoorinuse.name"))
{
Font = GUI.SmallFont,
Selected = RemoveIfLinkedOutpostDoorInUse,
ToolTip = TextManager.Get("sp.structure.removeiflinkedoutpostdoorinuse.description"),
OnSelected = (tickBox) =>
{
RemoveIfLinkedOutpostDoorInUse = tickBox.Selected;
return true;
}
};
editor.AddCustomContent(tickBox, 1);
}
var buttonContainer = new GUILayoutGroup(new RectTransform(new Point(listBox.Content.Rect.Width, heightScaled)), isHorizontal: true)
{
Stretch = true,
@@ -261,7 +277,7 @@ namespace Barotrauma
SpriteEffects oldEffects = Prefab.BackgroundSprite.effects;
Prefab.BackgroundSprite.effects ^= SpriteEffects;
Point backGroundOffset = new Point(
Vector2 backGroundOffset = new Vector2(
MathUtils.PositiveModulo((int)-textureOffset.X, Prefab.BackgroundSprite.SourceRect.Width),
MathUtils.PositiveModulo((int)-textureOffset.Y, Prefab.BackgroundSprite.SourceRect.Height));
@@ -299,7 +315,7 @@ namespace Barotrauma
{
if (damageEffect != null)
{
float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / Prefab.Health);
float newCutoff = MathHelper.Lerp(0.0f, 0.65f, Sections[i].damage / MaxHealth);
if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.01f || color != Submarine.DamageEffectColor)
{
@@ -314,7 +330,7 @@ namespace Barotrauma
}
}
Point sectionOffset = new Point(
Vector2 sectionOffset = new Vector2(
Math.Abs(rect.Location.X - Sections[i].rect.Location.X),
Math.Abs(rect.Location.Y - Sections[i].rect.Location.Y));
@@ -371,7 +387,7 @@ namespace Barotrauma
{
var textPos = SectionPosition(i, true);
textPos.Y = -textPos.Y;
GUI.DrawString(spriteBatch, textPos, "Damage: " + (int)((GetSection(i).damage / Health) * 100f) + "%", Color.Yellow);
GUI.DrawString(spriteBatch, textPos, "Damage: " + (int)((GetSection(i).damage / MaxHealth) * 100f) + "%", Color.Yellow);
}
}
}
@@ -448,7 +464,7 @@ namespace Barotrauma
for (int i = 0; i < sectionCount; i++)
{
float damage = msg.ReadRangedSingle(0.0f, 1.0f, 8) * Health;
float damage = msg.ReadRangedSingle(0.0f, 1.0f, 8) * MaxHealth;
if (i < Sections.Length)
{
SetDamage(i, damage);

View File

@@ -5,6 +5,7 @@ using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
@@ -306,9 +307,9 @@ namespace Barotrauma
public static void DrawGrid(SpriteBatch spriteBatch, int gridCells, Vector2 gridCenter, Vector2 roundedGridCenter, float alpha = 1.0f)
{
var horizontalLine = GUI.Style.GetComponentStyle("HorizontalLine").Sprites[GUIComponent.ComponentState.None].First();
var verticalLine = GUI.Style.GetComponentStyle("VerticalLine").Sprites[GUIComponent.ComponentState.None].First();
var horizontalLine = GUI.Style.GetComponentStyle("HorizontalLine").GetDefaultSprite();
var verticalLine = GUI.Style.GetComponentStyle("VerticalLine").GetDefaultSprite();
Vector2 topLeft = roundedGridCenter - Vector2.One * GridSize * gridCells / 2;
Vector2 bottomRight = roundedGridCenter + Vector2.One * GridSize * gridCells / 2;
@@ -324,19 +325,19 @@ namespace Barotrauma
float expandY = MathHelper.Lerp(30.0f, 0.0f, normalizedDistY);
GUI.DrawLine(spriteBatch,
horizontalLine.Sprite,
horizontalLine,
new Vector2(topLeft.X - expandX, -bottomRight.Y + i * GridSize.Y),
new Vector2(bottomRight.X + expandX, -bottomRight.Y + i * GridSize.Y),
Color.White * (1.0f - normalizedDistY) * alpha, depth: 0.6f, width: 3);
GUI.DrawLine(spriteBatch,
verticalLine.Sprite,
verticalLine,
new Vector2(topLeft.X + i * GridSize.X, -topLeft.Y + expandY),
new Vector2(topLeft.X + i * GridSize.X, -bottomRight.Y - expandY),
Color.White * (1.0f - normalizedDistX) * alpha, depth: 0.6f, width: 3);
}
}
public void CreateMiniMap(GUIComponent parent, IEnumerable<Entity> pointsOfInterest = null)
public void CreateMiniMap(GUIComponent parent, IEnumerable<Entity> pointsOfInterest = null, bool ignoreOutpost = false)
{
Rectangle worldBorders = GetDockedBorders();
worldBorders.Location += WorldPosition.ToPoint();
@@ -354,7 +355,8 @@ namespace Barotrauma
foreach (Hull hull in Hull.hullList)
{
if (hull.Submarine != this && !DockedTo.Contains(hull.Submarine)) continue;
if (hull.Submarine != this && !(DockedTo.Contains(hull.Submarine))) continue;
if (ignoreOutpost && !IsEntityFoundOnThisSub(hull, true)) { continue; }
Vector2 relativeHullPos = new Vector2(
(hull.WorldRect.X - worldBorders.X) / (float)worldBorders.Width,
@@ -393,35 +395,61 @@ namespace Barotrauma
errorMsgs.Add(TextManager.Get("NoHullsWarning"));
}
foreach (Item item in Item.ItemList)
if (Info.Type != SubmarineType.OutpostModule ||
(Info.OutpostModuleInfo?.ModuleFlags.Any(f => !f.Equals("hallwayvertical", StringComparison.OrdinalIgnoreCase) && !f.Equals("hallwayhorizontal", StringComparison.OrdinalIgnoreCase)) ?? true))
{
if (item.GetComponent<Items.Components.Vent>() == null) continue;
if (!item.linkedTo.Any())
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Path))
{
errorMsgs.Add(TextManager.Get("DisconnectedVentsWarning"));
break;
errorMsgs.Add(TextManager.Get("NoWaypointsWarning"));
}
}
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Human))
if (Info.Type == SubmarineType.Player)
{
errorMsgs.Add(TextManager.Get("NoHumanSpawnpointWarning"));
}
foreach (Item item in Item.ItemList)
{
if (item.GetComponent<Items.Components.Vent>() == null) { continue; }
if (!item.linkedTo.Any())
{
errorMsgs.Add(TextManager.Get("DisconnectedVentsWarning"));
break;
}
}
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Path))
{
errorMsgs.Add(TextManager.Get("NoWaypointsWarning"));
if (!WayPoint.WayPointList.Any(wp => wp.ShouldBeSaved && wp.SpawnType == SpawnType.Human))
{
errorMsgs.Add(TextManager.Get("NoHumanSpawnpointWarning"));
}
if (WayPoint.WayPointList.Find(wp => wp.SpawnType == SpawnType.Cargo) == null)
{
errorMsgs.Add(TextManager.Get("NoCargoSpawnpointWarning"));
}
if (!Item.ItemList.Any(it => it.GetComponent<Items.Components.Pump>() != null && it.HasTag("ballast")))
{
errorMsgs.Add(TextManager.Get("NoBallastTagsWarning"));
}
}
if (WayPoint.WayPointList.Find(wp => wp.SpawnType == SpawnType.Cargo) == null)
else if (Info.Type == SubmarineType.OutpostModule)
{
errorMsgs.Add(TextManager.Get("NoCargoSpawnpointWarning"));
}
if (!Item.ItemList.Any(it => it.GetComponent<Items.Components.Pump>() != null && it.HasTag("ballast")))
{
errorMsgs.Add(TextManager.Get("NoBallastTagsWarning"));
foreach (Item item in Item.ItemList)
{
var junctionBox = item.GetComponent<PowerTransfer>();
if (junctionBox == null) { continue; }
int doorLinks =
item.linkedTo.Count(lt => lt is Gap || (lt is Item it2 && it2.GetComponent<Door>() != null)) +
Item.ItemList.Count(it2 => it2.linkedTo.Contains(item) && !item.linkedTo.Contains(it2));
for (int i = 0; i < item.Connections.Count; i++)
{
int wireCount = item.Connections[i].Wires.Count(w => w != null);
if (doorLinks + wireCount > Connection.MaxLinked)
{
errorMsgs.Add(TextManager.GetWithVariables("InsufficientFreeConnectionsWarning",
new string[] { "[doorcount]", "[freeconnectioncount]" },
new string[] { doorLinks.ToString(), (Connection.MaxLinked - wireCount).ToString() }));
break;
}
}
}
}
if (Gap.GapList.Any(g => g.linkedTo.Count == 0))

View File

@@ -1,17 +1,13 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma
{
partial class SubmarineInfo : IDisposable
{
public Sprite PreviewImage;
partial void InitProjectSpecific()
{
string previewImageData = SubmarineElement.GetAttributeString("previewimage", "");
@@ -57,78 +53,10 @@ namespace Barotrauma
ScrollBarVisible = true,
Spacing = 5
};
ScalableFont font = parent.Rect.Width < 350 ? GUI.SmallFont : GUI.Font;
//space
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.03f), descriptionBox.Content.RectTransform), style: null);
new GUITextBlock(new RectTransform(new Vector2(1, 0), descriptionBox.Content.RectTransform), TextManager.Get("submarine.name." + Name, true) ?? Name, font: GUI.LargeFont, wrap: true) { ForceUpperCase = true, CanBeFocused = false };
float leftPanelWidth = 0.6f;
float rightPanelWidth = 0.4f / leftPanelWidth;
ScalableFont font = descriptionBox.Rect.Width < 350 ? GUI.SmallFont : GUI.Font;
Vector2 realWorldDimensions = Dimensions * Physics.DisplayToRealWorldRatio;
if (realWorldDimensions != Vector2.Zero)
{
string dimensionsStr = TextManager.GetWithVariables("DimensionsFormat", new string[2] { "[width]", "[height]" }, new string[2] { ((int)realWorldDimensions.X).ToString(), ((int)realWorldDimensions.Y).ToString() });
var dimensionsText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("Dimensions"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), dimensionsText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
dimensionsStr, textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
dimensionsText.RectTransform.MinSize = new Point(0, dimensionsText.Children.First().Rect.Height);
}
if (RecommendedCrewSizeMax > 0)
{
var crewSizeText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("RecommendedCrewSize"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewSizeText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
RecommendedCrewSizeMin + " - " + RecommendedCrewSizeMax, textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
crewSizeText.RectTransform.MinSize = new Point(0, crewSizeText.Children.First().Rect.Height);
}
if (!string.IsNullOrEmpty(RecommendedCrewExperience))
{
var crewExperienceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("RecommendedCrewExperience"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewExperienceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
TextManager.Get(RecommendedCrewExperience), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
crewExperienceText.RectTransform.MinSize = new Point(0, crewExperienceText.Children.First().Rect.Height);
}
if (RequiredContentPackages.Any())
{
var contentPackagesText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("RequiredContentPackages"), textAlignment: Alignment.TopLeft, font: font)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), contentPackagesText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
string.Join(", ", RequiredContentPackages), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
contentPackagesText.RectTransform.MinSize = new Point(0, contentPackagesText.Children.First().Rect.Height);
}
// show what game version the submarine was created on
if (!IsVanillaSubmarine() && GameVersion != null)
{
var versionText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), descriptionBox.Content.RectTransform),
TextManager.Get("serverlistversion"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), versionText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
GameVersion.ToString(), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
versionText.RectTransform.MinSize = new Point(0, versionText.Children.First().Rect.Height);
}
GUITextBlock.AutoScaleAndNormalize(descriptionBox.Content.Children.Where(c => c is GUITextBlock).Cast<GUITextBlock>());
CreateSpecsWindow(descriptionBox, font);
//space
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), descriptionBox.Content.RectTransform), style: null);
@@ -145,5 +73,84 @@ namespace Barotrauma
CanBeFocused = false
};
}
public void CreateSpecsWindow(GUIListBox parent, ScalableFont font)
{
float leftPanelWidth = 0.6f;
float rightPanelWidth = 0.4f / leftPanelWidth;
string className = !HasTag(SubmarineTag.Shuttle) ? TextManager.Get($"submarineclass.{SubmarineClass}") : TextManager.Get("shuttle");
int nameHeight = (int)GUI.LargeFont.MeasureString(DisplayName, true).Y;
int classHeight = (int)GUI.SubHeadingFont.MeasureString(className).Y;
int leftPanelWidthInt = (int)(parent.Rect.Width * leftPanelWidth);
var submarineNameText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, nameHeight + HUDLayoutSettings.Padding / 2), parent.Content.RectTransform), DisplayName, textAlignment: Alignment.CenterLeft, font: GUI.LargeFont) { CanBeFocused = false };
submarineNameText.RectTransform.MinSize = new Point(0, (int)submarineNameText.TextSize.Y);
var submarineClassText = new GUITextBlock(new RectTransform(new Point(leftPanelWidthInt, classHeight), parent.Content.RectTransform), className, textAlignment: Alignment.CenterLeft, font: GUI.SubHeadingFont) { CanBeFocused = false };
submarineClassText.RectTransform.MinSize = new Point(0, (int)submarineClassText.TextSize.Y);
Vector2 realWorldDimensions = Dimensions * Physics.DisplayToRealWorldRatio;
if (realWorldDimensions != Vector2.Zero)
{
string dimensionsStr = TextManager.GetWithVariables("DimensionsFormat", new string[2] { "[width]", "[height]" }, new string[2] { ((int)realWorldDimensions.X).ToString(), ((int)realWorldDimensions.Y).ToString() });
var dimensionsText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
TextManager.Get("Dimensions"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), dimensionsText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
dimensionsStr, textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
dimensionsText.RectTransform.MinSize = new Point(0, dimensionsText.Children.First().Rect.Height);
}
if (RecommendedCrewSizeMax > 0)
{
var crewSizeText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
TextManager.Get("RecommendedCrewSize"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewSizeText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
RecommendedCrewSizeMin + " - " + RecommendedCrewSizeMax, textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
crewSizeText.RectTransform.MinSize = new Point(0, crewSizeText.Children.First().Rect.Height);
}
if (!string.IsNullOrEmpty(RecommendedCrewExperience))
{
var crewExperienceText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
TextManager.Get("RecommendedCrewExperience"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), crewExperienceText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
TextManager.Get(RecommendedCrewExperience), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
crewExperienceText.RectTransform.MinSize = new Point(0, crewExperienceText.Children.First().Rect.Height);
}
if (RequiredContentPackages.Any())
{
var contentPackagesText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
TextManager.Get("RequiredContentPackages"), textAlignment: Alignment.TopLeft, font: font)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), contentPackagesText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
string.Join(", ", RequiredContentPackages), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
contentPackagesText.RectTransform.MinSize = new Point(0, contentPackagesText.Children.First().Rect.Height);
}
// show what game version the submarine was created on
if (!IsVanillaSubmarine() && GameVersion != null)
{
var versionText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
TextManager.Get("serverlistversion"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), versionText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
GameVersion.ToString(), textAlignment: Alignment.TopLeft, font: font, wrap: true)
{ CanBeFocused = false };
versionText.RectTransform.MinSize = new Point(0, versionText.Children.First().Rect.Height);
}
submarineNameText.AutoScaleHorizontal = true;
GUITextBlock.AutoScaleAndNormalize(parent.Content.Children.Where(c => c is GUITextBlock && c != submarineNameText).Cast<GUITextBlock>());
}
}
}

View File

@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
@@ -22,7 +23,6 @@ namespace Barotrauma
get { return !IsHidden(); }
}
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
if (!editing && (!GameMain.DebugDraw || Screen.Selected.Cam.Zoom < 0.1f)) { return; }
@@ -37,7 +37,7 @@ namespace Barotrauma
public void Draw(SpriteBatch spriteBatch, Vector2 drawPos)
{
Color clr = currentHull == null ? Color.CadetBlue : GUI.Style.Green;
Color clr = CurrentHull == null ? Color.DodgerBlue : GUI.Style.Green;
if (spawnType != SpawnType.Path) { clr = Color.Gray; }
if (isObstructed)
{
@@ -46,7 +46,7 @@ namespace Barotrauma
if (IsHighlighted || IsHighlighted) { clr = Color.Lerp(clr, Color.White, 0.8f); }
int iconSize = spawnType == SpawnType.Path ? WaypointSize : SpawnPointSize;
if (ConnectedGap != null || Ladders != null || Stairs != null || SpawnType != SpawnType.Path) { iconSize = (int)(iconSize * 1.5f); }
if (ConnectedDoor != null || Ladders != null || Stairs != null || SpawnType != SpawnType.Path) { iconSize = (int)(iconSize * 1.5f); }
if (IsSelected || IsHighlighted)
{
@@ -83,6 +83,20 @@ namespace Barotrauma
new Vector2(e.DrawPosition.X, -e.DrawPosition.Y),
(isObstructed ? Color.Gray : GUI.Style.Green) * 0.7f, width: 5, depth: 0.002f);
}
if (ConnectedGap != null)
{
GUI.DrawLine(spriteBatch,
drawPos,
new Vector2(ConnectedGap.WorldPosition.X, -ConnectedGap.WorldPosition.Y),
GUI.Style.Green * 0.5f, width: 1);
}
if (Ladders != null)
{
GUI.DrawLine(spriteBatch,
drawPos,
new Vector2(Ladders.Item.WorldPosition.X, -Ladders.Item.WorldPosition.Y),
GUI.Style.Green * 0.5f, width: 1);
}
GUI.SmallFont.DrawString(spriteBatch,
ID.ToString(),
@@ -117,7 +131,7 @@ namespace Barotrauma
editingHUD = CreateEditingHUD();
}
if (IsSelected && PlayerInput.PrimaryMouseButtonClicked())
if (IsSelected && PlayerInput.PrimaryMouseButtonClicked() && GUI.MouseOn == null)
{
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
@@ -144,6 +158,7 @@ namespace Barotrauma
}
else
{
FindHull();
// Update gaps, ladders, and stairs
UpdateLinkedEntity(position, Gap.GapList, gap => ConnectedGap = gap, gap =>
{
@@ -170,6 +185,7 @@ namespace Barotrauma
}
}
}, inflate: 5);
FindStairs();
// TODO: Cannot check the rectangle, since the rectangle is not rotated -> Need to use the collider.
//var stairList = mapEntityList.Where(me => me is Structure s && s.StairDirection != Direction.None).Select(me => me as Structure);
//UpdateLinkedEntity(position, stairList, s =>
@@ -240,7 +256,16 @@ namespace Barotrauma
textBox.Deselect();
return true;
}
private bool EnterTags(GUITextBox textBox, string text)
{
tags = text.Split(',').ToList();
textBox.Text = string.Join(",", Tags);
textBox.Flash(GUI.Style.Green);
textBox.Deselect();
return true;
}
private bool TextBoxChanged(GUITextBox textBox, string text)
{
textBox.Color = GUI.Style.Red;
@@ -298,7 +323,7 @@ namespace Barotrauma
var descText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
TextManager.Get("IDCardDescription"), font: GUI.SmallFont);
GUITextBox propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), descText.RectTransform, Anchor.CenterRight), idCardDesc)
GUITextBox propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), descText.RectTransform, Anchor.CenterRight), IdCardDesc)
{
MaxTextLength = 150,
OnEnterPressed = EnterIDCardDesc,
@@ -306,9 +331,9 @@ namespace Barotrauma
};
propertyBox.OnTextChanged += TextBoxChanged;
var tagsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
var idCardTagsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
TextManager.Get("IDCardTags"), font: GUI.SmallFont);
propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), tagsText.RectTransform, Anchor.CenterRight), string.Join(", ", idCardTags))
propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), idCardTagsText.RectTransform, Anchor.CenterRight), string.Join(", ", idCardTags))
{
MaxTextLength = 60,
OnEnterPressed = EnterIDCardTags,
@@ -327,7 +352,7 @@ namespace Barotrauma
ToolTip = TextManager.Get("SpawnpointJobsTooltip"),
OnSelected = (selected, userdata) =>
{
assignedJob = userdata as JobPrefab;
AssignedJob = userdata as JobPrefab;
return true;
}
};
@@ -336,7 +361,17 @@ namespace Barotrauma
{
jobDropDown.AddItem(jobPrefab.Name, jobPrefab);
}
jobDropDown.SelectItem(assignedJob);
jobDropDown.SelectItem(AssignedJob);
var tagsText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform),
TextManager.Get("spawnpointtags"), font: GUI.SmallFont);
propertyBox = new GUITextBox(new RectTransform(new Vector2(0.5f, 1.0f), tagsText.RectTransform, Anchor.CenterRight), string.Join(", ", tags))
{
MaxTextLength = 60,
OnEnterPressed = EnterTags,
ToolTip = TextManager.Get("spawnpointtagstooltip")
};
propertyBox.OnTextChanged += TextBoxChanged;
}
PositionEditingHUD();

View File

@@ -19,6 +19,7 @@ namespace Barotrauma.Networking
ChatMessageType type = (ChatMessageType)msg.ReadByte();
PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None;
string txt = "";
string styleSetting = string.Empty;
if (type != ChatMessageType.Order)
{
@@ -38,59 +39,65 @@ namespace Barotrauma.Networking
}
}
if (type == ChatMessageType.ServerMessageBox)
switch (type)
{
txt = TextManager.GetServerMessage(txt);
}
else if (type == ChatMessageType.Order)
{
int orderIndex = msg.ReadByte();
UInt16 targetCharacterID = msg.ReadUInt16();
Character targetCharacter = Entity.FindEntityByID(targetCharacterID) as Character;
Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16());
int optionIndex = msg.ReadByte();
case ChatMessageType.Default:
break;
case ChatMessageType.Order:
int orderIndex = msg.ReadByte();
UInt16 targetCharacterID = msg.ReadUInt16();
Character targetCharacter = Entity.FindEntityByID(targetCharacterID) as Character;
Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16());
int optionIndex = msg.ReadByte();
Order order = null;
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
{
DebugConsole.ThrowError("Invalid order message - order index out of bounds.");
if (NetIdUtils.IdMoreRecent(ID, LastID)) LastID = ID;
Order order = null;
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
{
DebugConsole.ThrowError("Invalid order message - order index out of bounds.");
if (NetIdUtils.IdMoreRecent(ID, LastID)) LastID = ID;
return;
}
else
{
order = Order.PrefabList[orderIndex];
}
string orderOption = "";
if (optionIndex >= 0 && optionIndex < order.Options.Length)
{
orderOption = order.Options[optionIndex];
}
txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{
if (order.TargetAllCharacters)
{
GameMain.GameSession?.CrewManager?.AddOrder(
new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
order.Prefab.FadeOutTime);
}
else if (targetCharacter != null)
{
targetCharacter.SetOrder(
new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
orderOption, senderCharacter);
}
}
if (NetIdUtils.IdMoreRecent(ID, LastID))
{
GameMain.Client.AddChatMessage(
new OrderChatMessage(order, orderOption, txt, targetEntity, targetCharacter, senderCharacter));
LastID = ID;
}
return;
}
else
{
order = Order.PrefabList[orderIndex];
}
string orderOption = "";
if (optionIndex >= 0 && optionIndex < order.Options.Length)
{
orderOption = order.Options[optionIndex];
}
txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
{
if (order.TargetAllCharacters)
{
GameMain.GameSession?.CrewManager?.AddOrder(
new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
order.Prefab.FadeOutTime);
}
else if (targetCharacter != null)
{
targetCharacter.SetOrder(
new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
orderOption, senderCharacter);
}
}
if (NetIdUtils.IdMoreRecent(ID, LastID))
{
GameMain.Client.AddChatMessage(
new OrderChatMessage(order, orderOption, txt, targetEntity, targetCharacter, senderCharacter));
LastID = ID;
}
return;
case ChatMessageType.ServerMessageBox:
txt = TextManager.GetServerMessage(txt);
break;
case ChatMessageType.ServerMessageBoxInGame:
styleSetting = msg.ReadString();
txt = TextManager.GetServerMessage(txt);
break;
}
if (NetIdUtils.IdMoreRecent(ID, LastID))
@@ -105,6 +112,9 @@ namespace Barotrauma.Networking
new GUIMessageBox("", txt);
}
break;
case ChatMessageType.ServerMessageBoxInGame:
new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting);
break;
case ChatMessageType.Console:
DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
break;
@@ -120,7 +130,7 @@ namespace Barotrauma.Networking
break;
}
LastID = ID;
}
}
}
}
}

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