v0.14.6.0
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -40,9 +40,5 @@ Libraries/webm_mem_playback/opus_x64_linux/
|
||||
# Win
|
||||
desktop.ini
|
||||
|
||||
# Merge script
|
||||
#Merge script
|
||||
temp.txt
|
||||
|
||||
# Private assets
|
||||
Barotrauma/BarotraumaShared/Content/*
|
||||
.github/ISSUE_TEMPLATE/release-checklist.md
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma
|
||||
{
|
||||
public override void DebugDraw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (Character.IsDead) return;
|
||||
if (Character.IsUnconscious || !Character.Enabled || !Enabled) { return; }
|
||||
|
||||
Vector2 pos = Character.WorldPosition;
|
||||
pos.Y = -pos.Y;
|
||||
@@ -38,7 +38,7 @@ namespace Barotrauma
|
||||
}
|
||||
targetPos.Y = -targetPos.Y;
|
||||
GUI.DrawLine(spriteBatch, pos, targetPos, GUI.Style.Red * 0.5f, 0, 4);
|
||||
if (wallTarget != null && (State == AIState.Attack || State == AIState.Aggressive || State == AIState.PassiveAggressive))
|
||||
if (wallTarget != null)
|
||||
{
|
||||
Vector2 wallTargetPos = wallTarget.Position;
|
||||
if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.Position; }
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Barotrauma
|
||||
{
|
||||
abstract partial class AIObjective
|
||||
{
|
||||
public static Sprite GetSprite(string identifier, string option, Entity targetEntity)
|
||||
{
|
||||
if (string.IsNullOrEmpty(identifier))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
identifier = identifier.RemoveWhitespace();
|
||||
if (Order.Prefabs.TryGetValue(identifier, out Order orderPrefab))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(option) && orderPrefab.OptionSprites.TryGetValue(option, out var optionSprite))
|
||||
{
|
||||
return optionSprite;
|
||||
}
|
||||
if (targetEntity is Item targetItem && targetItem.Prefab.MinimapIcon != null)
|
||||
{
|
||||
return targetItem.Prefab.MinimapIcon;
|
||||
}
|
||||
return orderPrefab.SymbolSprite;
|
||||
}
|
||||
return GUI.Style.GetComponentStyle($"{identifier}objectiveicon")?.GetDefaultSprite();
|
||||
}
|
||||
|
||||
public Sprite GetSprite()
|
||||
{
|
||||
return GetSprite(Identifier, Option, (this as AIObjectiveOperateItem)?.OperateTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,10 +39,7 @@ namespace Barotrauma
|
||||
|
||||
partial void DamageParticles(float deltaTime, Vector2 worldPosition)
|
||||
{
|
||||
if (particleEmitter != null)
|
||||
{
|
||||
particleEmitter.Emit(deltaTime, worldPosition);
|
||||
}
|
||||
particleEmitter?.Emit(deltaTime, worldPosition);
|
||||
|
||||
if (sound != null)
|
||||
{
|
||||
|
||||
@@ -102,11 +102,13 @@ namespace Barotrauma
|
||||
set { chromaticAberrationStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
}
|
||||
|
||||
public Color GrainColor { get; set; }
|
||||
|
||||
private float grainStrength;
|
||||
public float GrainStrength
|
||||
{
|
||||
get => grainStrength;
|
||||
set => grainStrength = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
set => grainStrength = Math.Max(0, value);
|
||||
}
|
||||
|
||||
private readonly List<ParticleEmitter> bloodEmitters = new List<ParticleEmitter>();
|
||||
@@ -199,7 +201,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
|
||||
{
|
||||
if (DisableControls || GUI.PauseMenuOpen || GUI.SettingsMenuOpen)
|
||||
if (DisableControls || GUI.InputBlockingMenuOpen)
|
||||
{
|
||||
foreach (Key key in keys)
|
||||
{
|
||||
@@ -321,7 +323,7 @@ namespace Barotrauma
|
||||
DoInteractionUpdate(deltaTime, mouseSimPos);
|
||||
}
|
||||
|
||||
if (!GUI.PauseMenuOpen && !GUI.SettingsMenuOpen)
|
||||
if (!GUI.InputBlockingMenuOpen)
|
||||
{
|
||||
if (SelectedConstruction != null &&
|
||||
(SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)) ||
|
||||
@@ -505,14 +507,16 @@ namespace Barotrauma
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.body != null && !item.body.Enabled) continue;
|
||||
if (item.ParentInventory != null) continue;
|
||||
if (ignoredItems != null && ignoredItems.Contains(item)) continue;
|
||||
if (item.body != null && !item.body.Enabled) { continue; }
|
||||
if (item.ParentInventory != null) { continue; }
|
||||
if (ignoredItems != null && ignoredItems.Contains(item)) { continue; }
|
||||
if (item.Prefab.RequireCampaignInteract && item.CampaignInteractionType == CampaignMode.InteractionType.None) { continue; }
|
||||
if (Screen.Selected is SubEditorScreen editor && editor.WiringMode && item.GetComponent<ConnectionPanel>() == null) { continue; }
|
||||
|
||||
if (draggingItemToWorld)
|
||||
{
|
||||
if (item.OwnInventory == null ||
|
||||
!item.OwnInventory.Container.AllowDragAndDrop ||
|
||||
!item.OwnInventory.CanBePut(CharacterInventory.DraggingItems.First()) ||
|
||||
!CanAccessInventory(item.OwnInventory))
|
||||
{
|
||||
@@ -677,7 +681,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
//Ideally it shouldn't send the character entirely if we can't see them but /shrug, this isn't the most hacker-proof game atm
|
||||
hudInfoVisible = controlled.CanSeeCharacter(this, controlled.ViewTarget == null ? controlled.WorldPosition : controlled.ViewTarget.WorldPosition);
|
||||
hudInfoVisible = controlled.CanSeeTarget(this, controlled.ViewTarget);
|
||||
}
|
||||
hudInfoTimer = Rand.Range(0.5f, 1.0f);
|
||||
}
|
||||
@@ -859,7 +863,14 @@ namespace Barotrauma
|
||||
Color nameColor = Color.White;
|
||||
if (Controlled != null && TeamID != Controlled.TeamID)
|
||||
{
|
||||
nameColor = TeamID == CharacterTeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
|
||||
if (TeamID == CharacterTeamType.FriendlyNPC)
|
||||
{
|
||||
nameColor = UniqueNameColor ?? Color.SkyBlue;
|
||||
}
|
||||
else
|
||||
{
|
||||
nameColor = GUI.Style.Red;
|
||||
}
|
||||
}
|
||||
if (CampaignInteractionType != CampaignMode.InteractionType.None && AllowCustomInteract)
|
||||
{
|
||||
|
||||
@@ -296,7 +296,7 @@ namespace Barotrauma
|
||||
float alpha = GetDistanceBasedIconAlpha(brokenItem);
|
||||
if (alpha <= 0.0f) continue;
|
||||
GUI.DrawIndicator(spriteBatch, brokenItem.DrawPosition, cam, 100.0f, GUI.BrokenIcon,
|
||||
Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
|
||||
Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
|
||||
}
|
||||
|
||||
float GetDistanceBasedIconAlpha(ISpatialEntity target, float maxDistance = 1000.0f)
|
||||
@@ -341,7 +341,7 @@ namespace Barotrauma
|
||||
|
||||
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
|
||||
{
|
||||
bool shiftDown = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift);
|
||||
bool shiftDown = PlayerInput.IsShiftDown();
|
||||
if (shouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown)
|
||||
{
|
||||
shouldRecreateHudTexts = true;
|
||||
@@ -391,7 +391,16 @@ namespace Barotrauma
|
||||
if (npc.CampaignInteractionType == CampaignMode.InteractionType.None || npc.Submarine != character.Submarine || npc.IsDead || npc.IsIncapacitated) { continue; }
|
||||
|
||||
var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionIcon." + npc.CampaignInteractionType);
|
||||
GUI.DrawIndicator(spriteBatch, npc.WorldPosition, cam, 500.0f, iconStyle.GetDefaultSprite(), iconStyle.Color);
|
||||
GUI.DrawIndicator(spriteBatch, npc.WorldPosition, cam, npc.CurrentHull == Character.Controlled.CurrentHull ? 500.0f : 100.0f, iconStyle.GetDefaultSprite(), iconStyle.Color);
|
||||
}
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.IconStyle is null || item.Submarine != character.Submarine) { continue; }
|
||||
if (Vector2.DistanceSquared(character.Position, item.Position) > 500f*500f) { continue; }
|
||||
var body = Submarine.CheckVisibility(character.SimPosition, item.SimPosition, ignoreLevel: true);
|
||||
if (body != null && body.UserData as Item != item) { continue; }
|
||||
GUI.DrawIndicator(spriteBatch, item.WorldPosition + new Vector2(0f, item.RectHeight * 0.65f), cam, new Vector2(-100f, 500.0f), item.IconStyle.GetDefaultSprite(), item.IconStyle.Color, createOffset: false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -291,8 +291,7 @@ namespace Barotrauma
|
||||
|
||||
break;
|
||||
case ServerNetObject.ENTITY_EVENT:
|
||||
|
||||
int eventType = msg.ReadRangedInteger(0, 6);
|
||||
int eventType = msg.ReadRangedInteger(0, 9);
|
||||
switch (eventType)
|
||||
{
|
||||
case 0: //NetEntityEvent.Type.InventoryState
|
||||
@@ -303,9 +302,9 @@ namespace Barotrauma
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
|
||||
//read anyway to prevent messing up reading the rest of the message
|
||||
UInt16 lastEventID = msg.ReadUInt16();
|
||||
byte itemCount = msg.ReadByte();
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
_ = msg.ReadUInt16();
|
||||
byte inventoryItemCount = msg.ReadByte();
|
||||
for (int i = 0; i < inventoryItemCount; i++)
|
||||
{
|
||||
msg.ReadUInt16();
|
||||
}
|
||||
@@ -354,56 +353,102 @@ namespace Barotrauma
|
||||
info?.SetSkillLevel(skillIdentifier, skillLevel, Position + Vector2.UnitY * 150.0f);
|
||||
}
|
||||
break;
|
||||
case 4: //NetEntityEvent.Type.ExecuteAttack
|
||||
case 4: // NetEntityEvent.Type.SetAttackTarget
|
||||
case 5: //NetEntityEvent.Type.ExecuteAttack
|
||||
int attackLimbIndex = msg.ReadByte();
|
||||
UInt16 targetEntityID = msg.ReadUInt16();
|
||||
int targetLimbIndex = msg.ReadByte();
|
||||
|
||||
Vector2 targetSimPos = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
//255 = entity already removed, no need to do anything
|
||||
if (attackLimbIndex == 255 || Removed) { break; }
|
||||
|
||||
if (attackLimbIndex >= AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
|
||||
DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
|
||||
break;
|
||||
}
|
||||
Limb attackLimb = AnimController.Limbs[attackLimbIndex];
|
||||
Limb targetLimb = null;
|
||||
if (!(FindEntityByID(targetEntityID) is IDamageable targetEntity))
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Target entity not found (ID {targetEntityID})");
|
||||
DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Target entity not found (ID {targetEntityID})");
|
||||
break;
|
||||
}
|
||||
if (targetEntity is Character targetCharacter)
|
||||
{
|
||||
if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
|
||||
{
|
||||
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})");
|
||||
DebugConsole.ThrowError($"Received invalid SetAttack/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];
|
||||
}
|
||||
if (attackLimb?.attack != null)
|
||||
{
|
||||
attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
|
||||
if (eventType == 4)
|
||||
{
|
||||
SetAttackTarget(attackLimb, targetEntity, targetSimPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 5: //NetEntityEvent.Type.AssignCampaignInteraction
|
||||
case 6: //NetEntityEvent.Type.AssignCampaignInteraction
|
||||
byte campaignInteractionType = msg.ReadByte();
|
||||
bool requireConsciousness = msg.ReadBoolean();
|
||||
(GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
|
||||
RequireConsciousnessForCustomInteract = requireConsciousness;
|
||||
break;
|
||||
case 6: //NetEntityEvent.Type.ObjectiveManagerOrderState
|
||||
bool properData = msg.ReadBoolean();
|
||||
if (!properData) { break; }
|
||||
int orderIndex = msg.ReadRangedInteger(0, Order.PrefabList.Count);
|
||||
var orderPrefab = Order.PrefabList[orderIndex];
|
||||
string option = null;
|
||||
if (orderPrefab.HasOptions)
|
||||
case 7: //NetEntityEvent.Type.ObjectiveManagerState
|
||||
// 1 = order, 2 = objective
|
||||
int msgType = msg.ReadRangedInteger(0, 2);
|
||||
if (msgType == 0) { break; }
|
||||
bool validData = msg.ReadBoolean();
|
||||
if (!validData) { break; }
|
||||
if (msgType == 1)
|
||||
{
|
||||
int optionIndex = msg.ReadRangedInteger(0, orderPrefab.Options.Length);
|
||||
option = orderPrefab.Options[optionIndex];
|
||||
int orderIndex = msg.ReadRangedInteger(0, Order.PrefabList.Count);
|
||||
var orderPrefab = Order.PrefabList[orderIndex];
|
||||
string option = null;
|
||||
if (orderPrefab.HasOptions)
|
||||
{
|
||||
int optionIndex = msg.ReadRangedInteger(-1, orderPrefab.AllOptions.Length);
|
||||
if (optionIndex > -1)
|
||||
{
|
||||
option = orderPrefab.AllOptions[optionIndex];
|
||||
}
|
||||
}
|
||||
GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option);
|
||||
}
|
||||
else if (msgType == 2)
|
||||
{
|
||||
string identifier = msg.ReadString();
|
||||
string option = msg.ReadString();
|
||||
ushort objectiveTargetEntityId = msg.ReadUInt16();
|
||||
var objectiveTargetEntity = FindEntityByID(objectiveTargetEntityId);
|
||||
GameMain.GameSession?.CrewManager?.CreateObjectiveIcon(this, identifier, option, objectiveTargetEntity);
|
||||
}
|
||||
break;
|
||||
case 8: //NetEntityEvent.Type.TeamChange
|
||||
byte newTeamId = msg.ReadByte();
|
||||
ChangeTeam((CharacterTeamType)newTeamId);
|
||||
break;
|
||||
case 9: //NetEntityEvent.Type.AddToCrew
|
||||
GameMain.GameSession.CrewManager.AddCharacter(this);
|
||||
CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte();
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
{
|
||||
ushort itemID = msg.ReadUInt16();
|
||||
if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; }
|
||||
item.AllowStealing = true;
|
||||
var wifiComponent = item.GetComponent<Items.Components.WifiComponent>();
|
||||
if (wifiComponent != null)
|
||||
{
|
||||
wifiComponent.TeamID = teamID;
|
||||
}
|
||||
}
|
||||
GameMain.GameSession.CrewManager.SetHighlightedOrderIcon(this, orderPrefab.Identifier, option);
|
||||
break;
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
@@ -415,7 +460,7 @@ namespace Barotrauma
|
||||
{
|
||||
DebugConsole.Log("Reading character spawn data");
|
||||
|
||||
if (GameMain.Client == null) return null;
|
||||
if (GameMain.Client == null) { return null; }
|
||||
|
||||
bool noInfo = inc.ReadBoolean();
|
||||
ushort id = inc.ReadUInt16();
|
||||
@@ -431,7 +476,15 @@ namespace Barotrauma
|
||||
Character character = null;
|
||||
if (noInfo)
|
||||
{
|
||||
character = Create(speciesName, position, seed, characterInfo: null, id: id, isRemotePlayer: false);
|
||||
try
|
||||
{
|
||||
character = Create(speciesName, position, seed, characterInfo: null, id: id, isRemotePlayer: false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to spawn character {speciesName}", e);
|
||||
throw;
|
||||
}
|
||||
bool containsStatusData = inc.ReadBoolean();
|
||||
if (containsStatusData)
|
||||
{
|
||||
@@ -447,8 +500,15 @@ namespace Barotrauma
|
||||
string infoSpeciesName = inc.ReadString();
|
||||
|
||||
CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc);
|
||||
|
||||
character = Create(speciesName, position, seed, characterInfo: info, id: id, isRemotePlayer: ownerId > 0 && GameMain.Client.ID != ownerId, hasAi: hasAi);
|
||||
try
|
||||
{
|
||||
character = Create(speciesName, position, seed, characterInfo: info, id: id, isRemotePlayer: ownerId > 0 && GameMain.Client.ID != ownerId, hasAi: hasAi);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to spawn character {speciesName}", e);
|
||||
throw;
|
||||
}
|
||||
character.TeamID = (CharacterTeamType)teamID;
|
||||
character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte();
|
||||
if (character.CampaignInteractionType != CampaignMode.InteractionType.None)
|
||||
@@ -471,7 +531,7 @@ namespace Barotrauma
|
||||
var x = inc.ReadSingle();
|
||||
var y = inc.ReadSingle();
|
||||
var hull = FindEntityByID(inc.ReadUInt16()) as Hull;
|
||||
targetPosition = new OrderTarget(new Vector2(x, y), hull, true);
|
||||
targetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true);
|
||||
}
|
||||
|
||||
if (orderPrefabIndex >= 0 && orderPrefabIndex < Order.PrefabList.Count)
|
||||
@@ -485,7 +545,7 @@ namespace Barotrauma
|
||||
new Order(orderPrefab, targetPosition, orderGiver: orderGiver);
|
||||
character.SetOrder(order,
|
||||
orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderOptionIndex] : null,
|
||||
orderPriority, orderGiver, speak: false);
|
||||
orderPriority, orderGiver, speak: false, force: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -63,6 +63,8 @@ namespace Barotrauma
|
||||
|
||||
private GUIListBox afflictionTooltip;
|
||||
|
||||
private static readonly Color oxygenLowGrainColor = new Color(0.1f, 0.1f, 0.1f, 1f);
|
||||
|
||||
private struct HeartratePosition
|
||||
{
|
||||
public float Time;
|
||||
@@ -671,17 +673,19 @@ namespace Barotrauma
|
||||
bloodParticleTimer -= deltaTime * (affliction.Strength / 10.0f);
|
||||
if (bloodParticleTimer <= 0.0f)
|
||||
{
|
||||
var emitter = Character.BloodEmitters.FirstOrDefault();
|
||||
float particleMinScale = emitter != null ? emitter.Prefab.ScaleMin : 0.5f;
|
||||
float particleMaxScale = emitter != null ? emitter.Prefab.ScaleMax : 1;
|
||||
bool inWater = Character.AnimController.InWater;
|
||||
var drawTarget = inWater ? Particles.ParticlePrefab.DrawTargetType.Water : Particles.ParticlePrefab.DrawTargetType.Air;
|
||||
var emitter = Character.BloodEmitters.FirstOrDefault(e => e.Prefab.ParticlePrefab.DrawTarget == drawTarget || e.Prefab.ParticlePrefab.DrawTarget == Particles.ParticlePrefab.DrawTargetType.Both);
|
||||
float particleMinScale = emitter?.Prefab.Properties.ScaleMin ?? 0.5f;
|
||||
float particleMaxScale = emitter?.Prefab.Properties.ScaleMax ?? 1;
|
||||
float severity = Math.Min(affliction.Strength / affliction.Prefab.MaxStrength * Character.Params.BleedParticleMultiplier, 1);
|
||||
float bloodParticleSize = MathHelper.Lerp(particleMinScale, particleMaxScale, severity);
|
||||
bool inWater = Character.AnimController.InWater;
|
||||
if (!inWater)
|
||||
{
|
||||
bloodParticleSize *= 2.0f;
|
||||
}
|
||||
|
||||
// TODO: use the blood emitter?
|
||||
var blood = GameMain.ParticleManager.CreateParticle(
|
||||
inWater ? Character.Params.BleedParticleWater : Character.Params.BleedParticleAir,
|
||||
targetLimb.WorldPosition, Rand.Vector(affliction.Strength), 0.0f, Character.AnimController.CurrentHull);
|
||||
@@ -694,6 +698,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsMouseOnHealthBar()
|
||||
{
|
||||
if (Character.Controlled?.CharacterHealth == null) { return false; }
|
||||
return Character.Controlled.CharacterHealth.healthBar.State == GUIComponent.ComponentState.Hover;
|
||||
}
|
||||
|
||||
public void UpdateHUD(float deltaTime)
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
@@ -742,7 +752,9 @@ namespace Barotrauma
|
||||
float radialDistortStrength = 0.0f;
|
||||
float chromaticAberrationStrength = 0.0f;
|
||||
float grainStrength = 0.0f;
|
||||
Color grainColor = Color.Transparent;
|
||||
|
||||
float oxygenLowStrength = 0.0f;
|
||||
if (Character.IsUnconscious)
|
||||
{
|
||||
blurStrength = 1.0f;
|
||||
@@ -750,10 +762,14 @@ namespace Barotrauma
|
||||
}
|
||||
else if (OxygenAmount < 100.0f)
|
||||
{
|
||||
blurStrength = MathHelper.Lerp(0.5f, 1.0f, 1.0f - Vitality / MaxVitality);
|
||||
distortStrength = blurStrength;
|
||||
distortSpeed = (blurStrength + 1.0f);
|
||||
oxygenLowStrength = Math.Min(1.0f - (OxygenAmount - LowOxygenThreshold) / LowOxygenThreshold, 1.0f);
|
||||
blurStrength = MathHelper.Lerp(0.5f, 1.0f, 1.0f - Vitality / MaxVitality) * oxygenLowStrength;
|
||||
distortStrength = blurStrength * oxygenLowStrength;
|
||||
distortSpeed = blurStrength + 1.0f;
|
||||
distortSpeed *= distortSpeed * distortSpeed * distortSpeed;
|
||||
|
||||
grainStrength = MathHelper.Lerp(0.5f, 10.0f, oxygenLowStrength);
|
||||
grainColor = oxygenLowGrainColor;
|
||||
}
|
||||
|
||||
foreach (Affliction affliction in afflictions)
|
||||
@@ -762,7 +778,12 @@ namespace Barotrauma
|
||||
blurStrength = Math.Max(blurStrength, affliction.GetScreenBlurStrength());
|
||||
radialDistortStrength = Math.Max(radialDistortStrength, affliction.GetRadialDistortStrength());
|
||||
chromaticAberrationStrength = Math.Max(chromaticAberrationStrength, affliction.GetChromaticAberrationStrength());
|
||||
grainStrength = Math.Max(grainStrength, affliction.GetScreenGrainStrength());
|
||||
float afflictionGrainStrength = affliction.GetScreenGrainStrength();
|
||||
if (afflictionGrainStrength > 0.0f)
|
||||
{
|
||||
grainStrength = Math.Max(grainStrength, affliction.GetScreenGrainStrength());
|
||||
grainColor = Color.Lerp(grainColor, Color.White, (float)Math.Pow(1.0f - oxygenLowStrength, 2));
|
||||
}
|
||||
}
|
||||
foreach (LimbHealth limbHealth in limbHealths)
|
||||
{
|
||||
@@ -778,6 +799,7 @@ namespace Barotrauma
|
||||
Character.RadialDistortStrength = radialDistortStrength;
|
||||
Character.ChromaticAberrationStrength = chromaticAberrationStrength;
|
||||
Character.GrainStrength = grainStrength;
|
||||
Character.GrainColor = grainColor;
|
||||
if (blurStrength > 0.0f)
|
||||
{
|
||||
distortTimer = (distortTimer + deltaTime * distortSpeed) % MathHelper.TwoPi;
|
||||
@@ -955,11 +977,9 @@ namespace Barotrauma
|
||||
highlightedLimbIndex = -1;
|
||||
}
|
||||
|
||||
Rectangle hoverArea = Rectangle.Union(HUDLayoutSettings.AfflictionAreaLeft, HUDLayoutSettings.HealthBarArea);
|
||||
|
||||
healthBarHolder.CanBeFocused = healthBar.CanBeFocused = healthBarShadow.CanBeFocused = !Character.ShouldLockHud();
|
||||
if (Character.AllowInput && UseHealthWindow && healthBar.Enabled && healthBar.CanBeFocused &&
|
||||
hoverArea.Contains(PlayerInput.MousePosition) && Inventory.SelectedSlot == null)
|
||||
(GUI.IsMouseOn(healthBar) || highlightedAfflictionIcon != null) && Inventory.SelectedSlot == null)
|
||||
{
|
||||
healthBar.State = GUIComponent.ComponentState.Hover;
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
@@ -1076,8 +1096,11 @@ namespace Barotrauma
|
||||
DrawStatusHUD(spriteBatch);
|
||||
}
|
||||
|
||||
|
||||
private Pair<Affliction, string> highlightedAfflictionIcon = null;
|
||||
public void DrawStatusHUD(SpriteBatch spriteBatch)
|
||||
{
|
||||
highlightedAfflictionIcon = null;
|
||||
//Rectangle interactArea = healthBar.Rect;
|
||||
if (Character.Controlled?.SelectedCharacter == null && openHealthWindow == null)
|
||||
{
|
||||
@@ -1092,7 +1115,6 @@ namespace Barotrauma
|
||||
statusIcons.Add(new Pair<Affliction, string>(affliction, affliction.Prefab.Name));
|
||||
}
|
||||
|
||||
Pair<Affliction, string> highlightedIcon = null;
|
||||
Vector2 highlightedIconPos = Vector2.Zero;
|
||||
Rectangle afflictionArea = HUDLayoutSettings.AfflictionAreaLeft;
|
||||
|
||||
@@ -1113,9 +1135,9 @@ namespace Barotrauma
|
||||
AfflictionPrefab afflictionPrefab = affliction.Prefab;
|
||||
|
||||
Rectangle afflictionIconRect = new Rectangle(pos, new Point(iconSize));
|
||||
if (afflictionIconRect.Contains(PlayerInput.MousePosition) && !Character.ShouldLockHud())
|
||||
if (afflictionIconRect.Contains(PlayerInput.MousePosition) && !Character.ShouldLockHud() && GUI.MouseOn == null)
|
||||
{
|
||||
highlightedIcon = statusIcon;
|
||||
highlightedAfflictionIcon = statusIcon;
|
||||
highlightedIconPos = afflictionIconRect.Location.ToVector2();
|
||||
}
|
||||
|
||||
@@ -1135,7 +1157,7 @@ namespace Barotrauma
|
||||
highlightedIcon == statusIcon ? slot.HoverColor : slot.Color);*/
|
||||
|
||||
|
||||
float alphaMultiplier = highlightedIcon == statusIcon ? 1f : 0.8f;
|
||||
float alphaMultiplier = highlightedAfflictionIcon == statusIcon ? 1f : 0.8f;
|
||||
|
||||
afflictionPrefab.Icon?.Draw(spriteBatch,
|
||||
pos.ToVector2(),
|
||||
@@ -1150,9 +1172,9 @@ namespace Barotrauma
|
||||
pos.Y += iconSize + (int)(5 * GUI.Scale);
|
||||
}
|
||||
|
||||
if (highlightedIcon != null)
|
||||
if (highlightedAfflictionIcon != null)
|
||||
{
|
||||
string nameTooltip = highlightedIcon.Second;
|
||||
string nameTooltip = highlightedAfflictionIcon.Second;
|
||||
Vector2 offset = GUI.Font.MeasureString(nameTooltip);
|
||||
|
||||
GUI.DrawString(spriteBatch,
|
||||
@@ -1315,7 +1337,7 @@ namespace Barotrauma
|
||||
child.Recalculate();
|
||||
}
|
||||
|
||||
if (buttonToSelect != null) { buttonToSelect.OnClicked(buttonToSelect, "selectaffliction"); }
|
||||
buttonToSelect?.OnClicked(buttonToSelect, "selectaffliction");
|
||||
|
||||
afflictionIconContainer.RecalculateChildren();
|
||||
|
||||
@@ -2004,7 +2026,7 @@ namespace Barotrauma
|
||||
existingAffliction.PeriodicEffectTimers[periodicEffect.First] = periodicEffect.Second;
|
||||
foreach (StatusEffect effect in periodicEffect.First.StatusEffects)
|
||||
{
|
||||
existingAffliction.ApplyStatusEffect(effect, deltaTime: 1.0f, this, targetLimb: null);
|
||||
existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2071,7 +2093,7 @@ namespace Barotrauma
|
||||
foreach (StatusEffect effect in periodicEffect.First.StatusEffects)
|
||||
{
|
||||
Limb targetLimb = Character.AnimController.Limbs.FirstOrDefault(l => l.HealthIndex == limbHealths.IndexOf(newAffliction.First));
|
||||
existingAffliction.ApplyStatusEffect(effect, deltaTime: 1.0f, this, targetLimb: targetLimb);
|
||||
existingAffliction.ApplyStatusEffect(ActionType.OnActive, effect, deltaTime: 1.0f, this, targetLimb: targetLimb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -997,7 +997,7 @@ namespace Barotrauma
|
||||
}
|
||||
wearableColor = wearableItemComponent.Item.GetSpriteColor();
|
||||
}
|
||||
float textureScale = wearable.InheritTextureScale ? TextureScale : 1;
|
||||
float textureScale = wearable.InheritTextureScale ? TextureScale : wearable.Scale;
|
||||
|
||||
wearable.Sprite.Draw(spriteBatch,
|
||||
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
|
||||
|
||||
@@ -15,6 +15,7 @@ using Barotrauma.Extensions;
|
||||
using Barotrauma.Steam;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.MapCreatures.Behavior;
|
||||
using static Barotrauma.FabricationRecipe;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -240,6 +241,7 @@ namespace Barotrauma
|
||||
case "toggleupperhud":
|
||||
case "togglecharacternames":
|
||||
case "fpscounter":
|
||||
case "showperf":
|
||||
case "dumptofile":
|
||||
case "findentityids":
|
||||
case "setfreecamspeed":
|
||||
@@ -1367,6 +1369,241 @@ namespace Barotrauma
|
||||
}
|
||||
}, isCheat: false));
|
||||
|
||||
commands.Add(new Command("analyzeitem", "analyzeitem: Analyzes one item for exploits.", (string[] args) =>
|
||||
{
|
||||
if (args.Length < 1) return;
|
||||
|
||||
List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>();
|
||||
foreach (ItemPrefab iPrefab in ItemPrefab.Prefabs)
|
||||
{
|
||||
fabricableItems.AddRange(iPrefab.FabricationRecipes);
|
||||
}
|
||||
|
||||
string itemNameOrId = args[0].ToLowerInvariant();
|
||||
|
||||
ItemPrefab itemPrefab =
|
||||
(MapEntityPrefab.Find(itemNameOrId, identifier: null, showErrorMessages: false) ??
|
||||
MapEntityPrefab.Find(null, identifier: itemNameOrId, showErrorMessages: false)) as ItemPrefab;
|
||||
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
NewMessage("Item not found for analyzing.");
|
||||
return;
|
||||
}
|
||||
NewMessage("Analyzing item " + itemPrefab.Name + " with base cost " + itemPrefab.DefaultPrice.Price);
|
||||
|
||||
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == itemPrefab);
|
||||
// omega nesting incoming
|
||||
if (fabricationRecipe != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, PriceInfo> itemLocationPrice in itemPrefab.GetSellPricesOver(0))
|
||||
{
|
||||
NewMessage(" If bought at " + itemLocationPrice.Key + " it costs " + itemLocationPrice.Value.Price);
|
||||
int totalPrice = 0;
|
||||
int? totalBestPrice = 0;
|
||||
foreach (var ingredient in fabricationRecipe.RequiredItems)
|
||||
{
|
||||
foreach (ItemPrefab ingredientItemPrefab in ingredient.ItemPrefabs)
|
||||
{
|
||||
NewMessage(" Its ingredient " + ingredientItemPrefab.Name + " has base cost " + ingredientItemPrefab.DefaultPrice.Price);
|
||||
totalPrice += ingredientItemPrefab.DefaultPrice.Price;
|
||||
totalBestPrice += ingredientItemPrefab.GetMinPrice();
|
||||
int basePrice = ingredientItemPrefab.DefaultPrice.Price;
|
||||
foreach (KeyValuePair<string, PriceInfo> ingredientItemLocationPrice in ingredientItemPrefab.GetBuyPricesUnder())
|
||||
{
|
||||
if (basePrice > ingredientItemLocationPrice.Value.Price)
|
||||
{
|
||||
NewMessage(" Location " + ingredientItemLocationPrice.Key + " sells ingredient " + ingredientItemPrefab.Name + " for cheaper, " + ingredientItemLocationPrice.Value.Price, Color.Yellow);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewMessage(" Location " + ingredientItemLocationPrice.Key + " sells ingredient " + ingredientItemPrefab.Name + " for more, " + ingredientItemLocationPrice.Value.Price, Color.Teal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int costDifference = itemPrefab.DefaultPrice.Price - totalPrice;
|
||||
NewMessage(" Constructing the item from store-bought items provides " + costDifference + " profit with default values.");
|
||||
|
||||
if (totalBestPrice.HasValue)
|
||||
{
|
||||
int? bestDifference = itemLocationPrice.Value.Price - totalBestPrice;
|
||||
NewMessage(" Constructing the item from store-bought items provides " + bestDifference + " profit with best-case scenario values.");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
() =>
|
||||
{
|
||||
return new string[][] { ItemPrefab.Prefabs.SelectMany(p => p.Aliases).Concat(ItemPrefab.Prefabs.Select(p => p.Identifier)).ToArray() };
|
||||
}, isCheat: false));
|
||||
|
||||
commands.Add(new Command("checkcraftingexploits", "checkcraftingexploits: Finds outright item exploits created by buying store-bought ingredients and constructing them into sellable items.", (string[] args) =>
|
||||
{
|
||||
List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>();
|
||||
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
|
||||
{
|
||||
fabricableItems.AddRange(itemPrefab.FabricationRecipes);
|
||||
}
|
||||
List<Tuple<string, int>> costDifferences = new List<Tuple<string, int>>();
|
||||
|
||||
int maximumAllowedCost = 5;
|
||||
|
||||
if (args.Length > 0)
|
||||
{
|
||||
Int32.TryParse(args[0], out maximumAllowedCost);
|
||||
}
|
||||
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
|
||||
{
|
||||
int? defaultCost = itemPrefab.DefaultPrice?.Price;
|
||||
int? fabricationCostStore = null;
|
||||
|
||||
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == itemPrefab);
|
||||
if (fabricationRecipe == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool canBeBought = true;
|
||||
|
||||
foreach (var ingredient in fabricationRecipe.RequiredItems)
|
||||
{
|
||||
int? ingredientPrice = ingredient.ItemPrefabs.Where(p => p.CanBeBought).Min(ip => ip.DefaultPrice?.Price);
|
||||
if (ingredientPrice.HasValue)
|
||||
{
|
||||
if (!fabricationCostStore.HasValue) { fabricationCostStore = 0; }
|
||||
float useAmount = ingredient.UseCondition ? ingredient.MinCondition : 1.0f;
|
||||
fabricationCostStore += (int)(ingredientPrice.Value * ingredient.Amount * useAmount);
|
||||
}
|
||||
else
|
||||
{
|
||||
canBeBought = false;
|
||||
}
|
||||
}
|
||||
if (fabricationCostStore.HasValue && defaultCost.HasValue && canBeBought)
|
||||
{
|
||||
int costDifference = defaultCost.Value - fabricationCostStore.Value;
|
||||
if (costDifference > maximumAllowedCost || costDifference < 0f)
|
||||
{
|
||||
float ratio = (float)fabricationCostStore.Value / defaultCost.Value;
|
||||
string message = "Fabricating \"" + itemPrefab.Name + "\" costs " + (int)(ratio * 100) + "% of the price of the item, or " + costDifference + " more. Item price: " + defaultCost.Value + ", ingredient prices: " + fabricationCostStore.Value;
|
||||
costDifferences.Add(new Tuple<string, int>(message, costDifference));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
costDifferences.Sort((x, y) => x.Item2.CompareTo(y.Item2));
|
||||
|
||||
foreach (Tuple<string, int> costDifference in costDifferences)
|
||||
{
|
||||
Color color = Color.Yellow;
|
||||
NewMessage(costDifference.Item1, color);
|
||||
}
|
||||
}, isCheat: false));
|
||||
|
||||
commands.Add(new Command("adjustprice", "adjustprice: Recursively prints out expected price adjustments for items derived from this item.", (string[] args) =>
|
||||
{
|
||||
List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>();
|
||||
foreach (ItemPrefab iP in ItemPrefab.Prefabs)
|
||||
{
|
||||
fabricableItems.AddRange(iP.FabricationRecipes);
|
||||
}
|
||||
if (args.Length < 2)
|
||||
{
|
||||
NewMessage("Item or value not defined.");
|
||||
return;
|
||||
}
|
||||
string itemNameOrId = args[0].ToLowerInvariant();
|
||||
|
||||
ItemPrefab materialPrefab =
|
||||
(MapEntityPrefab.Find(itemNameOrId, identifier: null, showErrorMessages: false) ??
|
||||
MapEntityPrefab.Find(null, identifier: itemNameOrId, showErrorMessages: false)) as ItemPrefab;
|
||||
|
||||
if (materialPrefab == null)
|
||||
{
|
||||
NewMessage("Item not found for price adjustment.");
|
||||
return;
|
||||
}
|
||||
|
||||
AdjustItemTypes adjustItemType = AdjustItemTypes.NoAdjustment;
|
||||
if (args.Length > 2)
|
||||
{
|
||||
switch (args[2].ToLowerInvariant())
|
||||
{
|
||||
case "add":
|
||||
adjustItemType = AdjustItemTypes.Additive;
|
||||
break;
|
||||
case "mult":
|
||||
adjustItemType = AdjustItemTypes.Multiplicative;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Int32.TryParse(args[1].ToLowerInvariant(), out int newPrice))
|
||||
{
|
||||
Dictionary<ItemPrefab, int> newPrices = new Dictionary<ItemPrefab, int>();
|
||||
PrintItemCosts(newPrices, materialPrefab, fabricableItems, newPrice, true, adjustItemType: adjustItemType);
|
||||
PrintItemCosts(newPrices, materialPrefab, fabricableItems, newPrice, false, adjustItemType: adjustItemType);
|
||||
}
|
||||
|
||||
}, isCheat: false));
|
||||
|
||||
commands.Add(new Command("deconstructvalue", "deconstructvalue: Views and compares deconstructed component prices for this item.", (string[] args) =>
|
||||
{
|
||||
List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>();
|
||||
foreach (ItemPrefab iP in ItemPrefab.Prefabs)
|
||||
{
|
||||
fabricableItems.AddRange(iP.FabricationRecipes);
|
||||
}
|
||||
if (args.Length < 1)
|
||||
{
|
||||
NewMessage("Item not defined.");
|
||||
return;
|
||||
}
|
||||
string itemNameOrId = args[0].ToLowerInvariant();
|
||||
|
||||
ItemPrefab parentItem =
|
||||
(MapEntityPrefab.Find(itemNameOrId, identifier: null, showErrorMessages: false) ??
|
||||
MapEntityPrefab.Find(null, identifier: itemNameOrId, showErrorMessages: false)) as ItemPrefab;
|
||||
|
||||
if (parentItem == null)
|
||||
{
|
||||
NewMessage("Item not found for price adjustment.");
|
||||
return;
|
||||
}
|
||||
|
||||
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == parentItem);
|
||||
int totalValue = 0;
|
||||
NewMessage(parentItem.Name + " has the price " + parentItem.DefaultPrice.Price);
|
||||
if (fabricationRecipe != null)
|
||||
{
|
||||
NewMessage(" It constructs from:");
|
||||
|
||||
foreach (RequiredItem requiredItem in fabricationRecipe.RequiredItems)
|
||||
{
|
||||
foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
NewMessage(" " + itemPrefab.Name + " has the price " + itemPrefab.DefaultPrice.Price);
|
||||
totalValue += itemPrefab.DefaultPrice.Price;
|
||||
}
|
||||
}
|
||||
NewMessage("Its total value was: " + totalValue);
|
||||
totalValue = 0;
|
||||
}
|
||||
NewMessage(" The item deconstructs into:");
|
||||
foreach (DeconstructItem deconstructItem in parentItem.DeconstructItems)
|
||||
{
|
||||
ItemPrefab itemPrefab =
|
||||
(MapEntityPrefab.Find(deconstructItem.ItemIdentifier, identifier: null, showErrorMessages: false) ??
|
||||
MapEntityPrefab.Find(null, identifier: itemNameOrId, showErrorMessages: false)) as ItemPrefab;
|
||||
|
||||
NewMessage(" " + itemPrefab.Name + " has the price " + itemPrefab.DefaultPrice.Price);
|
||||
totalValue += itemPrefab.DefaultPrice.Price;
|
||||
}
|
||||
NewMessage("Its deconstruct value was: " + totalValue);
|
||||
|
||||
}, isCheat: false));
|
||||
|
||||
commands.Add(new Command("setentityproperties", "setentityproperties [property name] [value]: Sets the value of some property on all selected items/structures in the sub editor.", (string[] args) =>
|
||||
{
|
||||
if (args.Length != 2 || Screen.Selected != GameMain.SubEditorScreen) { return; }
|
||||
@@ -2935,5 +3172,155 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum AdjustItemTypes
|
||||
{
|
||||
NoAdjustment,
|
||||
Additive,
|
||||
Multiplicative
|
||||
}
|
||||
|
||||
private static void PrintItemCosts(Dictionary<ItemPrefab, int> newPrices, ItemPrefab materialPrefab, List<FabricationRecipe> fabricableItems, int newPrice, bool adjustDown, string depth = "", AdjustItemTypes adjustItemType = AdjustItemTypes.NoAdjustment)
|
||||
{
|
||||
if (newPrice < 1)
|
||||
{
|
||||
NewMessage(depth + materialPrefab.Name + " cannot be adjusted to this price, because it would become less than 1.");
|
||||
return;
|
||||
}
|
||||
|
||||
depth += " ";
|
||||
|
||||
if (newPrice > 0)
|
||||
{
|
||||
newPrices.TryAdd(materialPrefab, newPrice);
|
||||
}
|
||||
|
||||
int componentCost = 0;
|
||||
int newComponentCost = 0;
|
||||
|
||||
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == materialPrefab);
|
||||
|
||||
if (fabricationRecipe != null)
|
||||
{
|
||||
foreach (RequiredItem requiredItem in fabricationRecipe.RequiredItems)
|
||||
{
|
||||
foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
GetAdjustedPrice(itemPrefab, ref componentCost, ref newComponentCost, newPrices);
|
||||
}
|
||||
}
|
||||
}
|
||||
string componentCostMultiplier = "";
|
||||
if (componentCost > 0)
|
||||
{
|
||||
componentCostMultiplier = $" (Relative difference to component cost {GetComponentCostDifference(materialPrefab.DefaultPrice.Price, componentCost)} => {GetComponentCostDifference(newPrice, newComponentCost)}, or flat profit {(int)(materialPrefab.DefaultPrice.Price - (int)componentCost)} => {newPrice - newComponentCost})";
|
||||
}
|
||||
string priceAdjustment = "";
|
||||
if (newPrice != materialPrefab.DefaultPrice.Price)
|
||||
{
|
||||
priceAdjustment = ", Suggested price adjustment is " + materialPrefab.DefaultPrice.Price + " => " + newPrice;
|
||||
}
|
||||
NewMessage(depth + materialPrefab.Name + "(" + materialPrefab.DefaultPrice.Price + ") " + priceAdjustment + componentCostMultiplier);
|
||||
|
||||
if (adjustDown)
|
||||
{
|
||||
if (componentCost > 0)
|
||||
{
|
||||
double newPriceMult = (double)newPrice / (double)(materialPrefab.DefaultPrice.Price);
|
||||
int newPriceDiff = componentCost + newPrice - materialPrefab.DefaultPrice.Price;
|
||||
|
||||
switch (adjustItemType)
|
||||
{
|
||||
case AdjustItemTypes.Additive:
|
||||
NewMessage(depth + materialPrefab.Name + "'s components should be adjusted " + componentCost + " => " + newPriceDiff);
|
||||
break;
|
||||
case AdjustItemTypes.Multiplicative:
|
||||
NewMessage(depth + materialPrefab.Name + "'s components should be adjusted " + componentCost + " => " + Math.Round(newPriceMult * componentCost));
|
||||
break;
|
||||
}
|
||||
|
||||
if (fabricationRecipe != null)
|
||||
{
|
||||
foreach (RequiredItem requiredItem in fabricationRecipe.RequiredItems)
|
||||
{
|
||||
foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
if (itemPrefab.DefaultPrice != null)
|
||||
{
|
||||
switch (adjustItemType)
|
||||
{
|
||||
case AdjustItemTypes.NoAdjustment:
|
||||
PrintItemCosts(newPrices, itemPrefab, fabricableItems, itemPrefab.DefaultPrice.Price, adjustDown, depth, adjustItemType);
|
||||
break;
|
||||
case AdjustItemTypes.Additive:
|
||||
PrintItemCosts(newPrices, itemPrefab, fabricableItems, itemPrefab.DefaultPrice.Price + (int)((newPrice - materialPrefab.DefaultPrice.Price) / (double)fabricationRecipe.RequiredItems.Count), adjustDown, depth, adjustItemType);
|
||||
break;
|
||||
case AdjustItemTypes.Multiplicative:
|
||||
PrintItemCosts(newPrices, itemPrefab, fabricableItems, (int)(itemPrefab.DefaultPrice.Price * newPriceMult), adjustDown, depth, adjustItemType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var fabricationRecipes = fabricableItems.Where(f => f.RequiredItems.Any(x => x.ItemPrefabs.Contains(materialPrefab)));
|
||||
|
||||
foreach (FabricationRecipe fabricationRecipeParent in fabricationRecipes)
|
||||
{
|
||||
if (fabricationRecipeParent.TargetItem.DefaultPrice != null)
|
||||
{
|
||||
int targetComponentCost = 0;
|
||||
int newTargetComponentCost = 0;
|
||||
|
||||
foreach (RequiredItem requiredItem in fabricationRecipeParent.RequiredItems)
|
||||
{
|
||||
foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
GetAdjustedPrice(itemPrefab, ref targetComponentCost, ref newTargetComponentCost, newPrices);
|
||||
}
|
||||
}
|
||||
switch (adjustItemType)
|
||||
{
|
||||
case AdjustItemTypes.NoAdjustment:
|
||||
PrintItemCosts(newPrices, fabricationRecipeParent.TargetItem, fabricableItems, fabricationRecipeParent.TargetItem.DefaultPrice.Price, adjustDown, depth, adjustItemType);
|
||||
break;
|
||||
case AdjustItemTypes.Additive:
|
||||
PrintItemCosts(newPrices, fabricationRecipeParent.TargetItem, fabricableItems, fabricationRecipeParent.TargetItem.DefaultPrice.Price + newPrice - materialPrefab.DefaultPrice.Price, adjustDown, depth, adjustItemType);
|
||||
break;
|
||||
case AdjustItemTypes.Multiplicative:
|
||||
double maintainedMultiplier = GetComponentCostDifference(fabricationRecipeParent.TargetItem.DefaultPrice.Price, targetComponentCost);
|
||||
PrintItemCosts(newPrices, fabricationRecipeParent.TargetItem, fabricableItems, (int)(newTargetComponentCost * maintainedMultiplier), adjustDown, depth, adjustItemType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static double GetComponentCostDifference(int itemCost, int componentCost)
|
||||
{
|
||||
return Math.Round((double)(itemCost / (double)componentCost), 2);
|
||||
}
|
||||
|
||||
private static void GetAdjustedPrice(ItemPrefab itemPrefab, ref int componentCost, ref int newComponentCost, Dictionary<ItemPrefab, int> newPrices)
|
||||
{
|
||||
if (newPrices.TryGetValue(itemPrefab, out int newPrice))
|
||||
{
|
||||
newComponentCost += newPrice;
|
||||
}
|
||||
else if (itemPrefab.DefaultPrice != null)
|
||||
{
|
||||
newComponentCost += itemPrefab.DefaultPrice.Price;
|
||||
}
|
||||
if (itemPrefab.DefaultPrice != null)
|
||||
{
|
||||
componentCost += itemPrefab.DefaultPrice.Price;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Barotrauma
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Hull hull, float depth)
|
||||
{
|
||||
if (Sprite.Texture == null) { return; }
|
||||
|
||||
Vector2 drawPos = position + hull.Rect.Location.ToVector2();
|
||||
if (hull.Submarine != null) { drawPos += hull.Submarine.DrawPosition; }
|
||||
drawPos.Y = -drawPos.Y;
|
||||
|
||||
@@ -55,6 +55,12 @@ namespace Barotrauma
|
||||
{
|
||||
Debug.Assert(actionInstance == null || actionId == null);
|
||||
|
||||
if (GUI.InputBlockingMenuOpen)
|
||||
{
|
||||
if (actionId.HasValue) { SendIgnore(actionId.Value); }
|
||||
return;
|
||||
}
|
||||
|
||||
shouldFadeToBlack = fadeToBlack;
|
||||
|
||||
if (lastMessageBox != null && !lastMessageBox.Closed && GUIMessageBox.MessageBoxes.Contains(lastMessageBox))
|
||||
@@ -368,6 +374,15 @@ namespace Barotrauma
|
||||
GameMain.Client?.ClientPeer?.Send(outmsg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
private static void SendIgnore(UInt16 actionId)
|
||||
{
|
||||
IWriteMessage outmsg = new WriteOnlyMessage();
|
||||
outmsg.Write((byte)ClientPacketHeader.EVENTMANAGER_RESPONSE);
|
||||
outmsg.Write(actionId);
|
||||
outmsg.Write(byte.MaxValue);
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Barotrauma
|
||||
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 + 140), "MonsterTotalStrength: " + (int)Math.Round(monsterTotalStrength), Color.Lerp(GUI.Style.Green, GUI.Style.Red, monsterTotalStrength / 5000f), Color.Black * 0.6f, 0, GUI.SmallFont);
|
||||
|
||||
#if DEBUG
|
||||
if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt) &&
|
||||
@@ -75,7 +76,7 @@ namespace Barotrauma
|
||||
lastIntensityUpdate = (float) Timing.TotalTime;
|
||||
}
|
||||
|
||||
Rectangle graphRect = new Rectangle(15, y + 150, 150, 50);
|
||||
Rectangle graphRect = new Rectangle(15, y + 165, 150, 50);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, graphRect, Color.Black * 0.5f, true);
|
||||
intensityGraph.Draw(spriteBatch, graphRect, 1.0f, 0.0f, Color.Lerp(Color.White, GUI.Style.Red, currentIntensity));
|
||||
|
||||
@@ -22,6 +22,13 @@ namespace Barotrauma
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
ushort targetItemCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < targetItemCount; i++)
|
||||
{
|
||||
var item = Item.ReadSpawnData(msg);
|
||||
items.Add(item);
|
||||
}
|
||||
|
||||
byte characterCount = msg.ReadByte();
|
||||
|
||||
for (int i = 0; i < characterCount; i++)
|
||||
|
||||
@@ -1,9 +1,28 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class CargoMission : Mission
|
||||
{
|
||||
public override string GetMissionRewardText(Submarine sub)
|
||||
{
|
||||
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetReward(sub)));
|
||||
|
||||
if (rewardPerCrate.HasValue)
|
||||
{
|
||||
string rewardPerCrateText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", rewardPerCrate.Value));
|
||||
return TextManager.GetWithVariables("missionrewardcargopercrate",
|
||||
new string[] { "[rewardpercrate]", "[itemcount]", "[maxitemcount]", "[totalreward]" },
|
||||
new string[] { rewardPerCrateText, itemsToSpawn.Count.ToString(), maxItemCount.ToString(), $"‖color:gui.orange‖{rewardText}‖end‖" });
|
||||
}
|
||||
else
|
||||
{
|
||||
return TextManager.GetWithVariables("missionrewardcargo",
|
||||
new string[] { "[totalreward]", "[itemcount]", "[maxitemcount]" },
|
||||
new string[] { $"‖color:gui.orange‖{rewardText}‖end‖", itemsToSpawn.Count.ToString(), maxItemCount.ToString() });
|
||||
}
|
||||
}
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
items.Clear();
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class EscortMission : Mission
|
||||
{
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
byte characterCount = msg.ReadByte();
|
||||
|
||||
for (int i = 0; i < characterCount; i++)
|
||||
{
|
||||
Character character = Character.ReadSpawnData(msg);
|
||||
characters.Add(character);
|
||||
if (msg.ReadBoolean())
|
||||
{
|
||||
terroristCharacters.Add(character);
|
||||
}
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int j = 0; j < itemCount; j++)
|
||||
{
|
||||
Item.ReadSpawnData(msg);
|
||||
}
|
||||
}
|
||||
if (characters.Contains(null))
|
||||
{
|
||||
throw new System.Exception("Error in EscortMission.ClientReadInitial: character list contains null (mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
|
||||
if (characters.Count != characterCount)
|
||||
{
|
||||
throw new System.Exception("Error in EscortMission.ClientReadInitial: character count does not match the server count (" + characterCount + " != " + characters.Count + "mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
InitCharacters();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,9 @@ namespace Barotrauma
|
||||
return ToolBox.GradientLerp(t, GUI.Style.Green, GUI.Style.Orange, GUI.Style.Red);
|
||||
}
|
||||
|
||||
public string GetMissionRewardText()
|
||||
public virtual string GetMissionRewardText(Submarine sub)
|
||||
{
|
||||
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", Reward));
|
||||
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", GetReward(sub)));
|
||||
return TextManager.GetWithVariable("missionreward", "[reward]", $"‖color:gui.orange‖{rewardText}‖end‖");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class OutpostDestroyMission : AbandonedOutpostMission
|
||||
{
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
{
|
||||
var item = Item.ReadSpawnData(msg);
|
||||
items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class PirateMission : Mission
|
||||
{
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
// duplicate code from escortmission, should possibly be combined, though additional loot items might be added so maybe not
|
||||
byte characterCount = msg.ReadByte();
|
||||
|
||||
for (int i = 0; i < characterCount; i++)
|
||||
{
|
||||
characters.Add(Character.ReadSpawnData(msg));
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int j = 0; j < itemCount; j++)
|
||||
{
|
||||
Item.ReadSpawnData(msg);
|
||||
}
|
||||
}
|
||||
if (characters.Contains(null))
|
||||
{
|
||||
throw new System.Exception("Error in PirateMission.ClientReadInitial: character list contains null (mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
|
||||
if (characters.Count != characterCount)
|
||||
{
|
||||
throw new System.Exception("Error in PirateMission.ClientReadInitial: character count does not match the server count (" + characterCount + " != " + characters.Count + "mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,6 +208,19 @@ namespace Barotrauma
|
||||
get { return pauseMenuOpen; }
|
||||
}
|
||||
|
||||
public static bool InputBlockingMenuOpen
|
||||
{
|
||||
get
|
||||
{
|
||||
return PauseMenuOpen ||
|
||||
SettingsMenuOpen ||
|
||||
DebugConsole.IsOpen ||
|
||||
GameSession.IsTabMenuOpen ||
|
||||
(GameMain.GameSession?.GameMode?.Paused ?? false) ||
|
||||
CharacterHUD.IsCampaignInterfaceOpen;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool PreventPauseMenuToggle = false;
|
||||
|
||||
public static Color ScreenOverlayColor
|
||||
@@ -226,6 +239,9 @@ namespace Barotrauma
|
||||
private static SavingIndicatorState savingIndicatorState = SavingIndicatorState.None;
|
||||
private static float? timeUntilSavingIndicatorDisabled;
|
||||
|
||||
private static string loadedSpritesText;
|
||||
private static DateTime loadedSpritesUpdateTime;
|
||||
|
||||
private enum SavingIndicatorState
|
||||
{
|
||||
None,
|
||||
@@ -441,9 +457,12 @@ namespace Barotrauma
|
||||
"Particle count: " + GameMain.ParticleManager.ParticleCount + "/" + GameMain.ParticleManager.MaxParticles,
|
||||
Color.Lerp(GUI.Style.Green, GUI.Style.Red, (GameMain.ParticleManager.ParticleCount / (float)GameMain.ParticleManager.MaxParticles)), Color.Black * 0.5f, 0, SmallFont);
|
||||
|
||||
DrawString(spriteBatch, new Vector2(10, 115),
|
||||
"Loaded sprites: " + Sprite.LoadedSprites.Count() + "\n(" + Sprite.LoadedSprites.Select(s => s.FilePath).Distinct().Count() + " unique textures)",
|
||||
Color.White, Color.Black * 0.5f, 0, SmallFont);
|
||||
if (loadedSpritesText == null || DateTime.Now > loadedSpritesUpdateTime)
|
||||
{
|
||||
loadedSpritesText = "Loaded sprites: " + Sprite.LoadedSprites.Count() + "\n(" + Sprite.LoadedSprites.Select(s => s.FilePath).Distinct().Count() + " unique textures)";
|
||||
loadedSpritesUpdateTime = DateTime.Now + new TimeSpan(0, 0, seconds: 5);
|
||||
}
|
||||
DrawString(spriteBatch, new Vector2(10, 115), loadedSpritesText, Color.White, Color.Black * 0.5f, 0, SmallFont);
|
||||
|
||||
if (debugDrawSounds)
|
||||
{
|
||||
@@ -978,8 +997,7 @@ namespace Barotrauma
|
||||
return editor.GetMouseCursorState();
|
||||
// Portrait area during gameplay
|
||||
case GameScreen _ when !(Character.Controlled?.ShouldLockHud() ?? true):
|
||||
if (HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) ||
|
||||
Rectangle.Union(HUDLayoutSettings.AfflictionAreaLeft, HUDLayoutSettings.HealthBarArea).Contains(PlayerInput.MousePosition))
|
||||
if (HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) || CharacterHealth.IsMouseOnHealthBar())
|
||||
{
|
||||
return CursorState.Hand;
|
||||
}
|
||||
@@ -1331,7 +1349,7 @@ namespace Barotrauma
|
||||
|
||||
/// <param name="createOffset">Should the indicator move based on the camera position?</param>
|
||||
/// <param name="overrideAlpha">Override the distance-based alpha value with the specified alpha value</param>
|
||||
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color,
|
||||
public static void DrawIndicator(SpriteBatch spriteBatch, in Vector2 worldPosition, Camera cam, in Vector2 visibleRange, Sprite sprite, in Color color,
|
||||
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null)
|
||||
{
|
||||
Vector2 diff = worldPosition - cam.WorldViewCenter;
|
||||
@@ -1339,9 +1357,9 @@ namespace Barotrauma
|
||||
|
||||
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier * Scale;
|
||||
|
||||
if (overrideAlpha.HasValue || dist > hideDist)
|
||||
if (overrideAlpha.HasValue || (dist > visibleRange.X && dist < visibleRange.Y))
|
||||
{
|
||||
float alpha = overrideAlpha ?? Math.Min((dist - hideDist) / 100.0f, 1.0f);
|
||||
float alpha = overrideAlpha ?? MathUtils.Min((dist - visibleRange.X) / 100.0f, 1.0f - ((dist - visibleRange.Y + 100f) / 100.0f), 1.0f);
|
||||
Vector2 targetScreenPos = cam.WorldToScreen(worldPosition);
|
||||
|
||||
if (!createOffset)
|
||||
@@ -1352,8 +1370,9 @@ namespace Barotrauma
|
||||
|
||||
float screenDist = Vector2.Distance(cam.WorldToScreen(cam.WorldViewCenter), targetScreenPos);
|
||||
float angle = MathUtils.VectorToAngle(diff);
|
||||
float originalAngle = angle;
|
||||
|
||||
float minAngleDiff = 0.05f;
|
||||
const float minAngleDiff = 0.05f;
|
||||
bool overlapFound = true;
|
||||
int iterations = 0;
|
||||
while (overlapFound && iterations < 10)
|
||||
@@ -1375,18 +1394,24 @@ namespace Barotrauma
|
||||
|
||||
usedIndicatorAngles.Add(angle);
|
||||
|
||||
Vector2 unclampedDiff = new Vector2(
|
||||
(float)Math.Cos(angle) * screenDist,
|
||||
(float)-Math.Sin(angle) * screenDist);
|
||||
|
||||
Vector2 iconDiff = new Vector2(
|
||||
(float)Math.Cos(angle) * Math.Min(GameMain.GraphicsWidth * 0.4f, screenDist + 10),
|
||||
(float)-Math.Sin(angle) * Math.Min(GameMain.GraphicsHeight * 0.4f, screenDist + 10));
|
||||
|
||||
angle = MathHelper.Lerp(originalAngle, angle, MathHelper.Clamp(((screenDist + 10f) - iconDiff.Length()) / 10f, 0f, 1f));
|
||||
|
||||
/*Vector2 unclampedDiff = new Vector2(
|
||||
(float)Math.Cos(angle) * screenDist,
|
||||
(float)-Math.Sin(angle) * screenDist);*/
|
||||
|
||||
iconDiff = new Vector2(
|
||||
(float)Math.Cos(angle) * Math.Min(GameMain.GraphicsWidth * 0.4f, screenDist),
|
||||
(float)-Math.Sin(angle) * Math.Min(GameMain.GraphicsHeight * 0.4f, screenDist));
|
||||
|
||||
Vector2 iconPos = cam.WorldToScreen(cam.WorldViewCenter) + iconDiff;
|
||||
sprite.Draw(spriteBatch, iconPos, color * alpha, rotate: 0.0f, scale: symbolScale);
|
||||
|
||||
if (unclampedDiff.Length() - 10 > iconDiff.Length())
|
||||
if (/*unclampedDiff.Length()*/ screenDist - 10 > iconDiff.Length())
|
||||
{
|
||||
Vector2 normalizedDiff = Vector2.Normalize(targetScreenPos - iconPos);
|
||||
Vector2 arrowOffset = normalizedDiff * sprite.size.X * symbolScale * 0.7f;
|
||||
@@ -1395,6 +1420,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color,
|
||||
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null)
|
||||
{
|
||||
DrawIndicator(spriteBatch, worldPosition, cam, new Vector2(hideDist, float.PositiveInfinity), sprite, color, createOffset, scaleMultiplier, overrideAlpha);
|
||||
}
|
||||
|
||||
public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, float width = 1)
|
||||
{
|
||||
DrawLine(sb, t, start, end, clr, depth, (int)width);
|
||||
@@ -1495,6 +1526,22 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawFilledRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, float depth = 0.0f)
|
||||
{
|
||||
if (size.X < 0)
|
||||
{
|
||||
start.X += size.X;
|
||||
size.X = -size.X;
|
||||
}
|
||||
if (size.Y < 0)
|
||||
{
|
||||
start.Y += size.Y;
|
||||
size.Y = -size.Y;
|
||||
}
|
||||
|
||||
sb.Draw(t, start, null, clr, 0f, Vector2.Zero, size, SpriteEffects.None, depth);
|
||||
}
|
||||
|
||||
public static void DrawRectangle(SpriteBatch sb, Vector2 center, float width, float height, float rotation, Color clr, float depth = 0.0f, float thickness = 1)
|
||||
{
|
||||
Matrix rotate = Matrix.CreateRotationZ(rotation);
|
||||
@@ -1971,6 +2018,30 @@ namespace Barotrauma
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
public static GUIMessageBox AskForConfirmation(string header, string body, Action onConfirm, Action onDeny = null)
|
||||
{
|
||||
string[] buttons = { TextManager.Get("Ok"), TextManager.Get("Cancel") };
|
||||
GUIMessageBox msgBox = new GUIMessageBox(header, body, buttons, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
|
||||
|
||||
// Cancel button
|
||||
msgBox.Buttons[1].OnClicked = delegate
|
||||
{
|
||||
onDeny?.Invoke();
|
||||
msgBox.Close();
|
||||
return true;
|
||||
};
|
||||
|
||||
// Ok button
|
||||
msgBox.Buttons[0].OnClicked = delegate
|
||||
{
|
||||
onConfirm.Invoke();
|
||||
msgBox.Close();
|
||||
return true;
|
||||
};
|
||||
return msgBox;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Element positioning
|
||||
@@ -2098,7 +2169,7 @@ namespace Barotrauma
|
||||
for (int j = i + 1; j < elements.Count; j++)
|
||||
{
|
||||
Rectangle rect2 = elements[j].Rect;
|
||||
if (!rect1.Intersects(rect2)) continue;
|
||||
if (!rect1.Intersects(rect2)) { continue; }
|
||||
|
||||
intersections = true;
|
||||
Point centerDiff = rect1.Center - rect2.Center;
|
||||
@@ -2127,10 +2198,10 @@ namespace Barotrauma
|
||||
elements[j].RectTransform.ScreenSpaceOffset += moveAmount2.ToPoint();
|
||||
}
|
||||
|
||||
if (disallowedAreas == null) continue;
|
||||
if (disallowedAreas == null) { continue; }
|
||||
foreach (Rectangle rect2 in disallowedAreas)
|
||||
{
|
||||
if (!rect1.Intersects(rect2)) continue;
|
||||
if (!rect1.Intersects(rect2)) { continue; }
|
||||
intersections = true;
|
||||
|
||||
Point centerDiff = rect1.Center - rect2.Center;
|
||||
@@ -2148,7 +2219,7 @@ namespace Barotrauma
|
||||
iterations++;
|
||||
}
|
||||
|
||||
Vector2 ClampMoveAmount(Rectangle Rect, Rectangle clampTo, Vector2 moveAmount)
|
||||
static Vector2 ClampMoveAmount(Rectangle Rect, Rectangle clampTo, Vector2 moveAmount)
|
||||
{
|
||||
if (Rect.Y < clampTo.Y)
|
||||
{
|
||||
@@ -2221,6 +2292,7 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
|
||||
bool IsOutpostLevel() => GameMain.GameSession != null && Level.IsLoadedOutpost;
|
||||
if (Screen.Selected == GameMain.GameScreen && GameMain.GameSession != null)
|
||||
{
|
||||
if (GameMain.GameSession.GameMode is SinglePlayerCampaign spMode)
|
||||
@@ -2253,45 +2325,22 @@ namespace Barotrauma
|
||||
};
|
||||
return true;
|
||||
};
|
||||
var saveAndQuitButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuSaveQuit"))
|
||||
if (IsOutpostLevel())
|
||||
{
|
||||
UserData = "save"
|
||||
};
|
||||
saveAndQuitButton.OnClicked += (btn, userdata) =>
|
||||
{
|
||||
//Only allow saving mid-round in outpost levels. Quitting in the middle of a mission reset progress to the start of the round.
|
||||
if (GameMain.GameSession == null)
|
||||
var saveAndQuitButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuSaveQuit"))
|
||||
{
|
||||
pauseMenuOpen = false;
|
||||
|
||||
}
|
||||
else if (GameMain.GameSession?.Campaign == null || Level.IsLoadedOutpost)
|
||||
{
|
||||
pauseMenuOpen = false;
|
||||
GameMain.QuitToMainMenu(save: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
var msgBox = new GUIMessageBox("", TextManager.Get("PauseMenuSaveAndQuitVerification", fallBackTag: "pausemenuquitverification"), new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") })
|
||||
{
|
||||
UserData = "verificationprompt"
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked = (_, userdata) =>
|
||||
UserData = "save",
|
||||
OnClicked = (btn, userData) =>
|
||||
{
|
||||
pauseMenuOpen = false;
|
||||
GameMain.QuitToMainMenu(save: false);
|
||||
if (IsOutpostLevel())
|
||||
{
|
||||
GameMain.QuitToMainMenu(save: true);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
msgBox.Buttons[1].OnClicked = (_, userdata) =>
|
||||
{
|
||||
pauseMenuOpen = false;
|
||||
msgBox.Close();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (GameMain.GameSession.GameMode is TestGameMode)
|
||||
{
|
||||
@@ -2313,7 +2362,7 @@ namespace Barotrauma
|
||||
OnClicked = (btn, userdata) =>
|
||||
{
|
||||
if (!GameMain.Client.HasPermission(ClientPermissions.ManageRound)) { return false; }
|
||||
if (GameMain.GameSession.GameMode is CampaignMode || (!Submarine.MainSub.AtStartExit && !Submarine.MainSub.AtEndExit))
|
||||
if (GameMain.GameSession.GameMode is CampaignMode && !IsOutpostLevel() || (!Submarine.MainSub.AtStartExit && !Submarine.MainSub.AtEndExit))
|
||||
{
|
||||
var msgBox = new GUIMessageBox("",
|
||||
TextManager.Get(GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd"),
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace Barotrauma
|
||||
{
|
||||
base.ApplyStyle(style);
|
||||
|
||||
if (frame != null) { frame.ApplyStyle(style); }
|
||||
frame?.ApplyStyle(style);
|
||||
}
|
||||
|
||||
public override void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectInflate = null)
|
||||
|
||||
@@ -59,9 +59,7 @@ namespace Barotrauma
|
||||
|
||||
private bool useGridLayout;
|
||||
|
||||
private float targetScroll;
|
||||
|
||||
private GUIComponent pendingScroll;
|
||||
private GUIComponent scrollToElement;
|
||||
|
||||
public bool AllowMouseWheelScroll { get; set; } = true;
|
||||
|
||||
@@ -238,8 +236,6 @@ namespace Barotrauma
|
||||
|
||||
public GUIComponent DraggedElement => draggedElement;
|
||||
|
||||
private bool scheduledScroll = false;
|
||||
|
||||
private readonly bool isHorizontal;
|
||||
|
||||
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
|
||||
@@ -429,7 +425,14 @@ namespace Barotrauma
|
||||
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);
|
||||
if (!Content.Children.Contains(component) || !component.Visible)
|
||||
{
|
||||
scrollToElement = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
scrollToElement = component;
|
||||
}
|
||||
}
|
||||
|
||||
public void ScrollToEnd(float duration)
|
||||
@@ -533,7 +536,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectTop && Content.Children.Any() && pendingScroll == null)
|
||||
if (SelectTop && Content.Children.Any() && scrollToElement == null)
|
||||
{
|
||||
GUIComponent component = Content.Children.FirstOrDefault(c => (c.Rect.Y - Content.Rect.Y) / (float)c.Rect.Height > -0.1f);
|
||||
|
||||
@@ -563,7 +566,6 @@ namespace Barotrauma
|
||||
{
|
||||
if (SelectTop)
|
||||
{
|
||||
pendingScroll = child;
|
||||
ScrollToElement(child);
|
||||
Select(i, autoScroll: false, takeKeyBoardFocus: true);
|
||||
}
|
||||
@@ -728,25 +730,29 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (scrollToElement != null)
|
||||
{
|
||||
if (!scrollToElement.Visible || !Content.Children.Contains(scrollToElement))
|
||||
{
|
||||
scrollToElement = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
float diff = isHorizontal ? scrollToElement.Rect.X - Content.Rect.X : scrollToElement.Rect.Y - Content.Rect.Y;
|
||||
float speed = MathHelper.Clamp(Math.Abs(diff) * 0.1f, 5.0f, 100.0f);
|
||||
System.Diagnostics.Debug.WriteLine(speed);
|
||||
if (Math.Abs(diff) < speed || GUIScrollBar.DraggingBar != null)
|
||||
{
|
||||
speed = Math.Abs(diff);
|
||||
scrollToElement = null;
|
||||
}
|
||||
BarScroll += speed * Math.Sign(diff) / TotalSize;
|
||||
}
|
||||
}
|
||||
|
||||
if ((GUI.IsMouseOn(this) || GUI.IsMouseOn(ScrollBar)) && AllowMouseWheelScroll && PlayerInput.ScrollWheelSpeed != 0)
|
||||
{
|
||||
float speed = PlayerInput.ScrollWheelSpeed / 500.0f * BarSize;
|
||||
if (SmoothScroll)
|
||||
{
|
||||
if (ClampScrollToElements)
|
||||
@@ -762,13 +768,6 @@ namespace Barotrauma
|
||||
SelectNext(takeKeyBoardFocus: true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingScroll = null;
|
||||
if (targetScroll < 0) { targetScroll = BarScroll; }
|
||||
targetScroll -= speed;
|
||||
targetScroll = Math.Clamp(targetScroll, ScrollBar.MinValue, ScrollBar.MaxValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -799,7 +798,6 @@ namespace Barotrauma
|
||||
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
|
||||
if (SmoothScroll)
|
||||
{
|
||||
pendingScroll = child;
|
||||
ScrollToElement(child);
|
||||
}
|
||||
break;
|
||||
@@ -819,7 +817,6 @@ namespace Barotrauma
|
||||
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
|
||||
if (SmoothScroll)
|
||||
{
|
||||
pendingScroll = child;
|
||||
ScrollToElement(child);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -551,7 +551,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (openState >= 2.0f)
|
||||
{
|
||||
if (Parent != null) { Parent.RemoveChild(this); }
|
||||
Parent?.RemoveChild(this);
|
||||
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
|
||||
}
|
||||
}
|
||||
@@ -604,7 +604,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Parent != null) { Parent.RemoveChild(this); }
|
||||
Parent?.RemoveChild(this);
|
||||
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (value == intValue) { return; }
|
||||
intValue = value;
|
||||
ClampIntValue();
|
||||
UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,10 +658,7 @@ namespace Barotrauma
|
||||
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, RichTextData);
|
||||
}
|
||||
|
||||
if (Strikethrough != null)
|
||||
{
|
||||
Strikethrough.Draw(spriteBatch, (int)Math.Ceiling(TextSize.X / 2f), pos.X, ForceUpperCase ? pos.Y : pos.Y + GUI.Scale * 2f);
|
||||
}
|
||||
Strikethrough?.Draw(spriteBatch, (int)Math.Ceiling(TextSize.X / 2f), pos.X, ForceUpperCase ? pos.Y : pos.Y + GUI.Scale * 2f);
|
||||
}
|
||||
|
||||
if (overflowClipActive)
|
||||
|
||||
@@ -49,8 +49,11 @@ namespace Barotrauma
|
||||
get { return _caretIndex; }
|
||||
set
|
||||
{
|
||||
_caretIndex = value;
|
||||
caretPosDirty = true;
|
||||
if (value >= 0)
|
||||
{
|
||||
_caretIndex = value;
|
||||
caretPosDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool caretPosDirty;
|
||||
@@ -454,7 +457,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (!isSelecting)
|
||||
{
|
||||
isSelecting = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift);
|
||||
isSelecting = PlayerInput.IsShiftDown();
|
||||
}
|
||||
|
||||
if (mouseHeldInside && !PlayerInput.PrimaryMouseButtonHeld())
|
||||
@@ -879,15 +882,22 @@ namespace Barotrauma
|
||||
selectionEndIndex = Math.Min(CaretIndex, textDrawn.Length);
|
||||
selectionEndPos = caretPos;
|
||||
selectedCharacters = Math.Abs(selectionStartIndex - selectionEndIndex);
|
||||
if (IsLeftToRight)
|
||||
try
|
||||
{
|
||||
selectedText = Text.Substring(selectionStartIndex, selectedCharacters);
|
||||
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionStartIndex, selectedCharacters)) * TextBlock.TextScale;
|
||||
if (IsLeftToRight)
|
||||
{
|
||||
selectedText = Text.Substring(selectionStartIndex, Math.Min(selectedCharacters, Text.Length));
|
||||
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionStartIndex, Math.Min(selectedCharacters, textDrawn.Length))) * TextBlock.TextScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedText = Text.Substring(selectionEndIndex, Math.Min(selectedCharacters, Text.Length));
|
||||
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionEndIndex, Math.Min(selectedCharacters, textDrawn.Length))) * TextBlock.TextScale;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (ArgumentOutOfRangeException exception)
|
||||
{
|
||||
selectedText = Text.Substring(selectionEndIndex, Math.Min(selectedCharacters, textDrawn.Length - selectionEndIndex));
|
||||
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionEndIndex, selectedCharacters)) * TextBlock.TextScale;
|
||||
DebugConsole.ThrowError($"GUITextBox: Invalid selection: ({exception})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,47 +17,63 @@ namespace Barotrauma
|
||||
private readonly Dictionary<StoreTab, GUIListBox> tabLists = new Dictionary<StoreTab, GUIListBox>();
|
||||
private readonly Dictionary<StoreTab, SortingMethod> tabSortingMethods = new Dictionary<StoreTab, SortingMethod>();
|
||||
private readonly List<PurchasedItem> itemsToSell = new List<PurchasedItem>();
|
||||
private readonly List<PurchasedItem> itemsToSellFromSub = new List<PurchasedItem>();
|
||||
|
||||
private StoreTab activeTab = StoreTab.Buy;
|
||||
private MapEntityCategory? selectedItemCategory;
|
||||
private bool suppressBuySell;
|
||||
private int buyTotal, sellTotal;
|
||||
private int buyTotal, sellTotal, sellFromSubTotal;
|
||||
|
||||
private GUITextBlock merchantBalanceBlock;
|
||||
private GUITextBlock currentSellValueBlock, newSellValueBlock;
|
||||
private GUIImage sellValueChangeArrow;
|
||||
private GUIDropDown sortingDropDown;
|
||||
private GUITextBox searchBox;
|
||||
private GUIListBox storeBuyList, storeSellList;
|
||||
private GUIListBox storeBuyList, storeSellList, storeSellFromSubList;
|
||||
/// <summary>
|
||||
/// Can be null when there are no deals at the current location
|
||||
/// </summary>
|
||||
private GUILayoutGroup storeDailySpecialsGroup, storeRequestedGoodGroup;
|
||||
private GUILayoutGroup storeDailySpecialsGroup, storeRequestedGoodGroup, storeRequestedSubGoodGroup;
|
||||
private Color storeSpecialColor;
|
||||
|
||||
private GUIListBox shoppingCrateBuyList, shoppingCrateSellList;
|
||||
private GUIListBox shoppingCrateBuyList, shoppingCrateSellList, shoppingCrateSellFromSubList;
|
||||
private GUITextBlock shoppingCrateTotal;
|
||||
private GUIButton clearAllButton, confirmButton;
|
||||
|
||||
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh;
|
||||
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh, needsSellingFromSubRefresh, needsItemsToSellFromSubRefresh;
|
||||
|
||||
private Point resolutionWhenCreated;
|
||||
private bool hadPermissions;
|
||||
|
||||
private Dictionary<ItemPrefab, int> OwnedItems { get; } = new Dictionary<ItemPrefab, int>();
|
||||
private Dictionary<ItemPrefab, int> OwnedItems { get; } = new Dictionary<ItemPrefab, int>();
|
||||
|
||||
private CargoManager CargoManager => campaignUI.Campaign.CargoManager;
|
||||
private Location CurrentLocation => campaignUI.Campaign.Map?.CurrentLocation;
|
||||
private int PlayerMoney => campaignUI.Campaign.Money;
|
||||
private bool HasPermissions => campaignUI.Campaign.AllowedToManageCampaign();
|
||||
private bool IsBuying => activeTab != StoreTab.Sell;
|
||||
private bool IsSelling => activeTab == StoreTab.Sell;
|
||||
private GUIListBox ActiveShoppingCrateList => IsBuying ? shoppingCrateBuyList : shoppingCrateSellList;
|
||||
private bool IsBuying => activeTab switch
|
||||
{
|
||||
StoreTab.Buy => true,
|
||||
StoreTab.Sell => false,
|
||||
StoreTab.SellFromSub => false,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
private bool IsSelling => !IsBuying;
|
||||
private GUIListBox ActiveShoppingCrateList => activeTab switch
|
||||
{
|
||||
StoreTab.Buy => shoppingCrateBuyList,
|
||||
StoreTab.Sell => shoppingCrateSellList,
|
||||
StoreTab.SellFromSub => shoppingCrateSellFromSubList,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
private enum StoreTab
|
||||
private bool IsTabUnavailable(StoreTab tab) => !tabLists.ContainsKey(tab);
|
||||
|
||||
public enum StoreTab
|
||||
{
|
||||
Buy,
|
||||
Sell
|
||||
Sell,
|
||||
SellFromSub
|
||||
}
|
||||
|
||||
private enum SortingMethod
|
||||
@@ -73,11 +89,8 @@ namespace Barotrauma
|
||||
{
|
||||
this.campaignUI = campaignUI;
|
||||
this.parentComponent = parentComponent;
|
||||
|
||||
hadPermissions = HasPermissions;
|
||||
|
||||
CreateUI();
|
||||
|
||||
campaignUI.Campaign.Map.OnLocationChanged += UpdateLocation;
|
||||
if (CurrentLocation?.Reputation != null)
|
||||
{
|
||||
@@ -89,8 +102,10 @@ namespace Barotrauma
|
||||
campaignUI.Campaign.CargoManager.OnSoldItemsChanged += () =>
|
||||
{
|
||||
needsItemsToSellRefresh = true;
|
||||
needsItemsToSellFromSubRefresh = true;
|
||||
needsRefresh = true;
|
||||
};
|
||||
campaignUI.Campaign.CargoManager.OnItemsInSellFromSubCrateChanged += () => { needsSellingFromSubRefresh = true; };
|
||||
}
|
||||
|
||||
public void Refresh(bool updateOwned = true)
|
||||
@@ -99,6 +114,7 @@ namespace Barotrauma
|
||||
if (updateOwned) { UpdateOwnedItems(); }
|
||||
RefreshBuying(updateOwned: false);
|
||||
RefreshSelling(updateOwned: false);
|
||||
RefreshSellingFromSub(updateOwned: false);
|
||||
needsRefresh = false;
|
||||
}
|
||||
|
||||
@@ -124,6 +140,20 @@ namespace Barotrauma
|
||||
needsSellingRefresh = false;
|
||||
}
|
||||
|
||||
private void RefreshSellingFromSub(bool updateOwned = true, bool updateItemsToSellFromSub = true)
|
||||
{
|
||||
if (IsTabUnavailable(StoreTab.SellFromSub)) { return; }
|
||||
if (updateOwned) { UpdateOwnedItems(); }
|
||||
if (updateItemsToSellFromSub) RefreshItemsToSellFromSub();
|
||||
RefreshShoppingCrateSellFromSubList();
|
||||
RefreshStoreSellFromSubList();
|
||||
// TODO: Separate permissions from regular campaign permissions
|
||||
var hasPermissions = HasPermissions;
|
||||
storeSellFromSubList.Enabled = hasPermissions;
|
||||
shoppingCrateSellFromSubList.Enabled = hasPermissions;
|
||||
needsSellingFromSubRefresh = false;
|
||||
}
|
||||
|
||||
private void CreateUI()
|
||||
{
|
||||
if (parentComponent.FindChild(c => c.UserData as string == "glow") is GUIComponent glowChild)
|
||||
@@ -236,9 +266,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (CurrentLocation != null)
|
||||
{
|
||||
int balanceAfterTransaction = IsBuying ?
|
||||
CurrentLocation.StoreCurrentBalance + buyTotal :
|
||||
CurrentLocation.StoreCurrentBalance - sellTotal;
|
||||
int balanceAfterTransaction = activeTab switch
|
||||
{
|
||||
StoreTab.Buy => CurrentLocation.StoreCurrentBalance + buyTotal,
|
||||
StoreTab.Sell => CurrentLocation.StoreCurrentBalance - sellTotal,
|
||||
StoreTab.SellFromSub => CurrentLocation.StoreCurrentBalance - sellFromSubTotal,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
if (balanceAfterTransaction != CurrentLocation.StoreCurrentBalance)
|
||||
{
|
||||
var newStatus = Location.GetStoreBalanceStatus(balanceAfterTransaction);
|
||||
@@ -300,8 +334,14 @@ namespace Barotrauma
|
||||
tabSortingMethods.Clear();
|
||||
foreach (StoreTab tab in tabs)
|
||||
{
|
||||
if (tab == StoreTab.SellFromSub && GameMain.IsMultiplayer) { continue; }
|
||||
string text = tab switch
|
||||
{
|
||||
StoreTab.SellFromSub => TextManager.Get("submarine"),
|
||||
_ => TextManager.Get("campaignstoretab." + tab)
|
||||
};
|
||||
var tabButton = new GUIButton(new RectTransform(new Vector2(1.0f / (tabs.Length + 1), 1.0f), modeButtonContainer.RectTransform),
|
||||
text: TextManager.Get("campaignstoretab." + tab), style: "GUITabButton")
|
||||
text: text, style: "GUITabButton")
|
||||
{
|
||||
UserData = tab,
|
||||
OnClicked = (button, userData) =>
|
||||
@@ -416,6 +456,17 @@ namespace Barotrauma
|
||||
storeRequestedGoodGroup = CreateDealsGroup(storeSellList);
|
||||
tabLists.Add(StoreTab.Sell, storeSellList);
|
||||
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
storeSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
|
||||
{
|
||||
AutoHideScrollBar = false,
|
||||
Visible = false
|
||||
};
|
||||
storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList);
|
||||
tabLists.Add(StoreTab.SellFromSub, storeSellFromSubList);
|
||||
}
|
||||
|
||||
// Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
var shoppingCrateContent = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Store).RectTransform, anchor: Anchor.TopRight)
|
||||
@@ -475,6 +526,10 @@ namespace Barotrauma
|
||||
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.85f), shoppingCrateInventoryContainer.RectTransform), style: null);
|
||||
shoppingCrateBuyList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
||||
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
|
||||
}
|
||||
|
||||
var totalContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true)
|
||||
{
|
||||
@@ -504,7 +559,13 @@ namespace Barotrauma
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
if (!HasPermissions) { return false; }
|
||||
var itemsToRemove = new List<PurchasedItem>(IsBuying ? CargoManager.ItemsInBuyCrate : CargoManager.ItemsInSellCrate);
|
||||
var itemsToRemove = activeTab switch
|
||||
{
|
||||
StoreTab.Buy => new List<PurchasedItem>(CargoManager.ItemsInBuyCrate),
|
||||
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
|
||||
StoreTab.SellFromSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
itemsToRemove.ForEach(i => ClearFromShoppingCrate(i));
|
||||
return true;
|
||||
}
|
||||
@@ -560,6 +621,7 @@ namespace Barotrauma
|
||||
|
||||
private void ChangeStoreTab(StoreTab tab)
|
||||
{
|
||||
if (IsTabUnavailable(tab)) { return; }
|
||||
activeTab = tab;
|
||||
foreach (GUIButton tabButton in storeTabButtons)
|
||||
{
|
||||
@@ -571,24 +633,56 @@ namespace Barotrauma
|
||||
SetConfirmButtonBehavior();
|
||||
SetConfirmButtonStatus();
|
||||
FilterStoreItems();
|
||||
if (tab == StoreTab.Buy)
|
||||
switch (tab)
|
||||
{
|
||||
storeSellList.Visible = false;
|
||||
storeBuyList.Visible = true;
|
||||
shoppingCrateSellList.Visible = false;
|
||||
shoppingCrateBuyList.Visible = true;
|
||||
}
|
||||
else if (tab == StoreTab.Sell)
|
||||
{
|
||||
storeBuyList.Visible = false;
|
||||
storeSellList.Visible = true;
|
||||
shoppingCrateBuyList.Visible = false;
|
||||
shoppingCrateSellList.Visible = true;
|
||||
case StoreTab.Buy:
|
||||
storeSellList.Visible = false;
|
||||
if (storeSellFromSubList != null)
|
||||
{
|
||||
storeSellFromSubList.Visible = false;
|
||||
}
|
||||
storeBuyList.Visible = true;
|
||||
shoppingCrateSellList.Visible = false;
|
||||
if (shoppingCrateSellFromSubList != null)
|
||||
{
|
||||
shoppingCrateSellFromSubList.Visible = false;
|
||||
}
|
||||
shoppingCrateBuyList.Visible = true;
|
||||
break;
|
||||
case StoreTab.Sell:
|
||||
storeBuyList.Visible = false;
|
||||
if (storeSellFromSubList != null)
|
||||
{
|
||||
storeSellFromSubList.Visible = false;
|
||||
}
|
||||
storeSellList.Visible = true;
|
||||
shoppingCrateBuyList.Visible = false;
|
||||
if (shoppingCrateSellFromSubList != null)
|
||||
{
|
||||
shoppingCrateSellFromSubList.Visible = false;
|
||||
}
|
||||
shoppingCrateSellList.Visible = true;
|
||||
break;
|
||||
case StoreTab.SellFromSub:
|
||||
storeBuyList.Visible = false;
|
||||
storeSellList.Visible = false;
|
||||
if (storeSellFromSubList != null)
|
||||
{
|
||||
storeSellFromSubList.Visible = true;
|
||||
}
|
||||
shoppingCrateBuyList.Visible = false;
|
||||
shoppingCrateSellList.Visible = false;
|
||||
if (shoppingCrateSellFromSubList != null)
|
||||
{
|
||||
shoppingCrateSellFromSubList.Visible = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void FilterStoreItems(MapEntityCategory? category, string filter)
|
||||
{
|
||||
if (IsTabUnavailable(activeTab)) { return; }
|
||||
selectedItemCategory = category;
|
||||
var list = tabLists[activeTab];
|
||||
filter = filter?.ToLower();
|
||||
@@ -668,7 +762,7 @@ namespace Barotrauma
|
||||
if (itemFrame == null)
|
||||
{
|
||||
var parentComponent = isDailySpecial ? storeDailySpecialsGroup : storeBuyList as GUIComponent;
|
||||
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, quantity), priceInfo, parentComponent, forceDisable: !hasPermissions);
|
||||
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, quantity), parentComponent, StoreTab.Buy, forceDisable: !hasPermissions);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -688,7 +782,7 @@ namespace Barotrauma
|
||||
removedItemFrames.AddRange(storeDailySpecialsGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
|
||||
}
|
||||
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
|
||||
if (IsBuying) { FilterStoreItems(); }
|
||||
if (activeTab == StoreTab.Buy) { FilterStoreItems(); }
|
||||
SortItems(StoreTab.Buy);
|
||||
|
||||
storeBuyList.BarScroll = prevBuyListScroll;
|
||||
@@ -743,7 +837,7 @@ namespace Barotrauma
|
||||
if (itemFrame == null)
|
||||
{
|
||||
var parentComponent = isRequestedGood ? storeRequestedGoodGroup : storeSellList as GUIComponent;
|
||||
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), priceInfo, parentComponent, forceDisable: !hasPermissions);
|
||||
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), parentComponent, StoreTab.Sell, forceDisable: !hasPermissions);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -766,13 +860,91 @@ namespace Barotrauma
|
||||
removedItemFrames.AddRange(storeRequestedGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
|
||||
}
|
||||
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
|
||||
if (IsSelling) { FilterStoreItems(); }
|
||||
if (activeTab == StoreTab.Sell) { FilterStoreItems(); }
|
||||
SortItems(StoreTab.Sell);
|
||||
|
||||
storeSellList.BarScroll = prevSellListScroll;
|
||||
shoppingCrateSellList.BarScroll = prevShoppingCrateScroll;
|
||||
}
|
||||
|
||||
private void RefreshStoreSellFromSubList()
|
||||
{
|
||||
float prevSellListScroll = storeSellFromSubList.BarScroll;
|
||||
float prevShoppingCrateScroll = shoppingCrateSellFromSubList.BarScroll;
|
||||
bool hasPermissions = HasPermissions;
|
||||
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
||||
|
||||
if ((storeRequestedSubGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
|
||||
{
|
||||
if (storeRequestedSubGoodGroup == null)
|
||||
{
|
||||
storeRequestedSubGoodGroup = CreateDealsGroup(storeSellList);
|
||||
storeRequestedSubGoodGroup.Parent.SetAsFirstChild();
|
||||
}
|
||||
else
|
||||
{
|
||||
storeSellFromSubList.RemoveChild(storeRequestedSubGoodGroup.Parent);
|
||||
storeRequestedSubGoodGroup = null;
|
||||
}
|
||||
storeSellFromSubList.RecalculateChildren();
|
||||
}
|
||||
|
||||
foreach (PurchasedItem item in itemsToSellFromSub)
|
||||
{
|
||||
CreateOrUpdateItemFrame(item.ItemPrefab, item.Quantity);
|
||||
}
|
||||
|
||||
foreach (var requestedGood in CurrentLocation.RequestedGoods)
|
||||
{
|
||||
if (itemsToSellFromSub.Any(pi => pi.ItemPrefab == requestedGood)) { continue; }
|
||||
CreateOrUpdateItemFrame(requestedGood, 0);
|
||||
}
|
||||
|
||||
void CreateOrUpdateItemFrame(ItemPrefab itemPrefab, int itemQuantity)
|
||||
{
|
||||
PriceInfo priceInfo = itemPrefab.GetPriceInfo(CurrentLocation);
|
||||
if (priceInfo == null) { return; }
|
||||
var isRequestedGood = CurrentLocation.RequestedGoods.Contains(itemPrefab);
|
||||
var itemFrame = isRequestedGood ?
|
||||
storeRequestedSubGoodGroup.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab) :
|
||||
storeSellFromSubList.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab);
|
||||
if (CargoManager.ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem itemInSellFromSubCrate)
|
||||
{
|
||||
itemQuantity = Math.Max(itemQuantity - itemInSellFromSubCrate.Quantity, 0);
|
||||
}
|
||||
if (itemFrame == null)
|
||||
{
|
||||
var parentComponent = isRequestedGood ? storeRequestedSubGoodGroup : storeSellFromSubList as GUIComponent;
|
||||
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), parentComponent, StoreTab.SellFromSub, forceDisable: !hasPermissions);
|
||||
}
|
||||
else
|
||||
{
|
||||
(itemFrame.UserData as PurchasedItem).Quantity = itemQuantity;
|
||||
SetQuantityLabelText(StoreTab.SellFromSub, itemFrame);
|
||||
SetOwnedLabelText(itemFrame);
|
||||
SetPriceGetters(itemFrame, false);
|
||||
}
|
||||
SetItemFrameStatus(itemFrame, hasPermissions && itemQuantity > 0);
|
||||
if (itemQuantity < 1 && !isRequestedGood)
|
||||
{
|
||||
itemFrame.Visible = false;
|
||||
}
|
||||
existingItemFrames.Add(itemFrame);
|
||||
}
|
||||
|
||||
var removedItemFrames = storeSellFromSubList.Content.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList();
|
||||
if (storeRequestedSubGoodGroup != null)
|
||||
{
|
||||
removedItemFrames.AddRange(storeRequestedSubGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
|
||||
}
|
||||
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
|
||||
if (activeTab == StoreTab.SellFromSub) { FilterStoreItems(); }
|
||||
SortItems(StoreTab.SellFromSub);
|
||||
|
||||
storeSellFromSubList.BarScroll = prevSellListScroll;
|
||||
shoppingCrateSellFromSubList.BarScroll = prevShoppingCrateScroll;
|
||||
}
|
||||
|
||||
private void SetPriceGetters(GUIComponent itemFrame, bool buying)
|
||||
{
|
||||
if (itemFrame == null || !(itemFrame.UserData is PurchasedItem pi)) { return; }
|
||||
@@ -834,7 +1006,38 @@ namespace Barotrauma
|
||||
needsItemsToSellRefresh = false;
|
||||
}
|
||||
|
||||
private void RefreshShoppingCrateList(List<PurchasedItem> items, GUIListBox listBox)
|
||||
public void RefreshItemsToSellFromSub()
|
||||
{
|
||||
itemsToSellFromSub.Clear();
|
||||
var subItems = CargoManager.GetSellableItemsFromSub();
|
||||
foreach (Item subItem in subItems)
|
||||
{
|
||||
if (itemsToSellFromSub.FirstOrDefault(i => i.ItemPrefab == subItem.Prefab) is PurchasedItem item)
|
||||
{
|
||||
item.Quantity += 1;
|
||||
}
|
||||
else if (subItem.Prefab.GetPriceInfo(CurrentLocation) != null)
|
||||
{
|
||||
itemsToSellFromSub.Add(new PurchasedItem(subItem.Prefab, 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove items from sell crate if they aren't on the sub anymore
|
||||
var itemsInCrate = new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate);
|
||||
foreach (PurchasedItem crateItem in itemsInCrate)
|
||||
{
|
||||
var subItem = itemsToSellFromSub.Find(i => i.ItemPrefab == crateItem.ItemPrefab);
|
||||
var subItemQuantity = subItem != null ? subItem.Quantity : 0;
|
||||
if (crateItem.Quantity > subItemQuantity)
|
||||
{
|
||||
CargoManager.ModifyItemQuantityInSellFromSubCrate(crateItem.ItemPrefab, subItemQuantity - crateItem.Quantity);
|
||||
}
|
||||
}
|
||||
sellableItemsFromSubUpdateTimer = 0.0f;
|
||||
needsItemsToSellFromSubRefresh = false;
|
||||
}
|
||||
|
||||
private void RefreshShoppingCrateList(List<PurchasedItem> items, GUIListBox listBox, StoreTab tab)
|
||||
{
|
||||
bool hasPermissions = HasPermissions;
|
||||
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
|
||||
@@ -848,7 +1051,7 @@ namespace Barotrauma
|
||||
GUINumberInput numInput = null;
|
||||
if (itemFrame == null)
|
||||
{
|
||||
itemFrame = CreateItemFrame(item, priceInfo, listBox, forceDisable: !hasPermissions);
|
||||
itemFrame = CreateItemFrame(item, listBox, tab, forceDisable: !hasPermissions);
|
||||
numInput = itemFrame.FindChild(c => c is GUINumberInput, recursive: true) as GUINumberInput;
|
||||
}
|
||||
else
|
||||
@@ -859,6 +1062,7 @@ namespace Barotrauma
|
||||
{
|
||||
numInput.UserData = item;
|
||||
numInput.Enabled = hasPermissions;
|
||||
numInput.MaxValueInt = GetMaxAvailable(item.ItemPrefab, tab);
|
||||
}
|
||||
SetOwnedLabelText(itemFrame);
|
||||
SetItemFrameStatus(itemFrame, hasPermissions);
|
||||
@@ -873,7 +1077,7 @@ namespace Barotrauma
|
||||
}
|
||||
suppressBuySell = false;
|
||||
|
||||
var price = listBox == shoppingCrateBuyList ?
|
||||
var price = tab == StoreTab.Buy ?
|
||||
CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo) :
|
||||
CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
|
||||
totalPrice += item.Quantity * price;
|
||||
@@ -883,24 +1087,32 @@ namespace Barotrauma
|
||||
removedItemFrames.ForEach(f => listBox.Content.RemoveChild(f));
|
||||
|
||||
SortItems(listBox, SortingMethod.CategoryAsc);
|
||||
listBox.UpdateScrollBarSize();
|
||||
if (listBox == shoppingCrateBuyList)
|
||||
listBox.UpdateScrollBarSize();
|
||||
switch (tab)
|
||||
{
|
||||
buyTotal = totalPrice;
|
||||
if (IsBuying) { SetShoppingCrateTotalText(); }
|
||||
case StoreTab.Buy:
|
||||
buyTotal = totalPrice;
|
||||
break;
|
||||
case StoreTab.Sell:
|
||||
sellTotal = totalPrice;
|
||||
break;
|
||||
case StoreTab.SellFromSub:
|
||||
sellFromSubTotal = totalPrice;
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (activeTab == tab)
|
||||
{
|
||||
sellTotal = totalPrice;
|
||||
if(IsSelling) { SetShoppingCrateTotalText(); }
|
||||
SetShoppingCrateTotalText();
|
||||
}
|
||||
SetClearAllButtonStatus();
|
||||
SetConfirmButtonStatus();
|
||||
}
|
||||
|
||||
private void RefreshShoppingCrateBuyList() => RefreshShoppingCrateList(CargoManager.ItemsInBuyCrate, shoppingCrateBuyList);
|
||||
private void RefreshShoppingCrateBuyList() => RefreshShoppingCrateList(CargoManager.ItemsInBuyCrate, shoppingCrateBuyList, StoreTab.Buy);
|
||||
|
||||
private void RefreshShoppingCrateSellList() => RefreshShoppingCrateList(CargoManager.ItemsInSellCrate, shoppingCrateSellList);
|
||||
private void RefreshShoppingCrateSellList() => RefreshShoppingCrateList(CargoManager.ItemsInSellCrate, shoppingCrateSellList, StoreTab.Sell);
|
||||
|
||||
private void RefreshShoppingCrateSellFromSubList() => RefreshShoppingCrateList(CargoManager.ItemsInSellFromSubCrate, shoppingCrateSellFromSubList, StoreTab.SellFromSub);
|
||||
|
||||
private void SortItems(GUIListBox list, SortingMethod sortingMethod)
|
||||
{
|
||||
@@ -932,7 +1144,7 @@ namespace Barotrauma
|
||||
else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
|
||||
{
|
||||
SortItems(list, SortingMethod.AlphabeticalAsc);
|
||||
if (list == storeSellList || list == shoppingCrateSellList)
|
||||
if (list != storeBuyList && list != shoppingCrateBuyList)
|
||||
{
|
||||
list.Content.RectTransform.SortChildren(CompareBySellPrice);
|
||||
if (GetSpecialsGroup() is GUILayoutGroup specialsGroup)
|
||||
@@ -1014,6 +1226,10 @@ namespace Barotrauma
|
||||
{
|
||||
return storeRequestedGoodGroup;
|
||||
}
|
||||
else if (list == storeSellFromSubList)
|
||||
{
|
||||
return storeRequestedSubGoodGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
@@ -1045,15 +1261,20 @@ namespace Barotrauma
|
||||
|
||||
private void SortItems(StoreTab tab, SortingMethod sortingMethod)
|
||||
{
|
||||
if (IsTabUnavailable(tab)) { return; }
|
||||
tabSortingMethods[tab] = sortingMethod;
|
||||
SortItems(tabLists[tab], sortingMethod);
|
||||
}
|
||||
|
||||
private void SortItems(StoreTab tab) => SortItems(tab, tabSortingMethods[tab]);
|
||||
private void SortItems(StoreTab tab)
|
||||
{
|
||||
if (IsTabUnavailable(tab)) { return; }
|
||||
SortItems(tab, tabSortingMethods[tab]);
|
||||
}
|
||||
|
||||
private void SortActiveTabItems(SortingMethod sortingMethod) => SortItems(activeTab, sortingMethod);
|
||||
|
||||
private GUIComponent CreateItemFrame(PurchasedItem pi, PriceInfo priceInfo, GUIComponent parentComponent, bool forceDisable = false)
|
||||
private GUIComponent CreateItemFrame(PurchasedItem pi, GUIComponent parentComponent, StoreTab containingTab, bool forceDisable = false)
|
||||
{
|
||||
var tooltip = pi.ItemPrefab.Name;
|
||||
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
|
||||
@@ -1114,8 +1335,8 @@ namespace Barotrauma
|
||||
CanBeFocused = false,
|
||||
Stretch = true
|
||||
};
|
||||
var isSellingRelatedList = parentComponent == storeSellList || parentComponent == storeRequestedGoodGroup || parentComponent == shoppingCrateSellList;
|
||||
var locationHasDealOnItem = isSellingRelatedList ?
|
||||
bool isSellingRelatedList = containingTab != StoreTab.Buy;
|
||||
bool locationHasDealOnItem = isSellingRelatedList ?
|
||||
CurrentLocation.RequestedGoods.Contains(pi.ItemPrefab) : CurrentLocation.DailySpecials.Contains(pi.ItemPrefab);
|
||||
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), nameAndQuantityGroup.RectTransform),
|
||||
pi.ItemPrefab.Name, font: GUI.SubHeadingFont, textAlignment: Alignment.BottomLeft)
|
||||
@@ -1140,14 +1361,15 @@ namespace Barotrauma
|
||||
};
|
||||
dealIcon.SetAsFirstChild();
|
||||
}
|
||||
var isParentOnLeftSideOfInterface = parentComponent == storeBuyList || parentComponent == storeDailySpecialsGroup ||
|
||||
parentComponent == storeSellList || parentComponent == storeRequestedGoodGroup;
|
||||
bool isParentOnLeftSideOfInterface = parentComponent == storeBuyList || parentComponent == storeDailySpecialsGroup ||
|
||||
parentComponent == storeSellList || parentComponent == storeRequestedGoodGroup ||
|
||||
parentComponent == storeSellFromSubList || parentComponent == storeRequestedSubGoodGroup;
|
||||
GUILayoutGroup shoppingCrateAmountGroup = null;
|
||||
GUINumberInput amountInput = null;
|
||||
if (isParentOnLeftSideOfInterface)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), nameAndQuantityGroup.RectTransform),
|
||||
CreateQuantityLabelText(isSellingRelatedList ? StoreTab.Sell : StoreTab.Buy, pi.Quantity), font: GUI.Font, textAlignment: Alignment.BottomLeft)
|
||||
CreateQuantityLabelText(containingTab, pi.Quantity), font: GUI.Font, textAlignment: Alignment.BottomLeft)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Shadow = locationHasDealOnItem,
|
||||
@@ -1156,7 +1378,7 @@ namespace Barotrauma
|
||||
UserData = "quantitylabel"
|
||||
};
|
||||
}
|
||||
else if (!isParentOnLeftSideOfInterface)
|
||||
else
|
||||
{
|
||||
var relativePadding = nameBlock.Padding.X / nameBlock.Rect.Width;
|
||||
shoppingCrateAmountGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - relativePadding, 0.6f), nameAndQuantityGroup.RectTransform) { RelativeOffset = new Vector2(relativePadding, 0) },
|
||||
@@ -1167,7 +1389,7 @@ namespace Barotrauma
|
||||
amountInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), shoppingCrateAmountGroup.RectTransform), GUINumberInput.NumberType.Int)
|
||||
{
|
||||
MinValueInt = 0,
|
||||
MaxValueInt = GetMaxAvailable(pi.ItemPrefab, isSellingRelatedList ? StoreTab.Sell : StoreTab.Buy),
|
||||
MaxValueInt = GetMaxAvailable(pi.ItemPrefab, containingTab),
|
||||
UserData = pi,
|
||||
IntValue = pi.Quantity
|
||||
};
|
||||
@@ -1394,7 +1616,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private string CreateQuantityLabelText(StoreTab mode, int quantity) => mode == StoreTab.Sell ?
|
||||
private string CreateQuantityLabelText(StoreTab mode, int quantity) => mode != StoreTab.Buy ?
|
||||
TextManager.GetWithVariable("campaignstore.quantity", "[amount]", quantity.ToString()) :
|
||||
TextManager.GetWithVariable("campaignstore.instock", "[amount]", quantity.ToString());
|
||||
|
||||
@@ -1417,10 +1639,16 @@ namespace Barotrauma
|
||||
|
||||
private int GetMaxAvailable(ItemPrefab itemPrefab, StoreTab mode)
|
||||
{
|
||||
var list = mode == StoreTab.Sell ? itemsToSell : CurrentLocation.StoreStock;
|
||||
var list = mode switch
|
||||
{
|
||||
StoreTab.Buy => CurrentLocation.StoreStock,
|
||||
StoreTab.Sell => itemsToSell,
|
||||
StoreTab.SellFromSub => itemsToSellFromSub,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
if (list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
|
||||
{
|
||||
if (mode != StoreTab.Sell)
|
||||
if (mode == StoreTab.Buy)
|
||||
{
|
||||
var purchasedItem = CargoManager.PurchasedItems.Find(i => i.ItemPrefab == item.ItemPrefab);
|
||||
if (purchasedItem != null) { return Math.Max(item.Quantity - purchasedItem.Quantity, 0); }
|
||||
@@ -1469,11 +1697,37 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool AddToShoppingCrate(PurchasedItem item, int quantity = 1) => IsBuying ?
|
||||
ModifyBuyQuantity(item, quantity) : ModifySellQuantity(item, quantity);
|
||||
private bool ModifySellFromSubQuantity(PurchasedItem item, int quantity)
|
||||
{
|
||||
if (item == null || item.ItemPrefab == null) { return false; }
|
||||
if (!HasPermissions) { return false; }
|
||||
if (quantity > 0)
|
||||
{
|
||||
// Make sure there's enough available to sell
|
||||
var itemToSell = CargoManager.ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == item.ItemPrefab);
|
||||
var totalQuantityToSell = itemToSell != null ? itemToSell.Quantity + quantity : quantity;
|
||||
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.SellFromSub)) { return false; }
|
||||
}
|
||||
CargoManager.ModifyItemQuantityInSellFromSubCrate(item.ItemPrefab, quantity);
|
||||
// TODO: GameMain.Client?.SendCampaignState();
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ClearFromShoppingCrate(PurchasedItem item) => IsBuying ?
|
||||
ModifyBuyQuantity(item, -item.Quantity) : ModifySellQuantity(item, -item.Quantity);
|
||||
private bool AddToShoppingCrate(PurchasedItem item, int quantity = 1) => activeTab switch
|
||||
{
|
||||
StoreTab.Buy => ModifyBuyQuantity(item, quantity),
|
||||
StoreTab.Sell => ModifySellQuantity(item, quantity),
|
||||
StoreTab.SellFromSub => ModifySellFromSubQuantity(item, quantity),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
private bool ClearFromShoppingCrate(PurchasedItem item) => activeTab switch
|
||||
{
|
||||
StoreTab.Buy => ModifyBuyQuantity(item, -item.Quantity),
|
||||
StoreTab.Sell => ModifySellQuantity(item, -item.Quantity),
|
||||
StoreTab.SellFromSub => ModifySellFromSubQuantity(item, -item.Quantity),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
private bool BuyItems()
|
||||
{
|
||||
@@ -1510,18 +1764,17 @@ namespace Barotrauma
|
||||
private bool SellItems()
|
||||
{
|
||||
if (!HasPermissions) { return false; }
|
||||
|
||||
var itemsToSell = new List<PurchasedItem>(CargoManager.ItemsInSellCrate);
|
||||
var itemsToSell = activeTab switch
|
||||
{
|
||||
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
|
||||
StoreTab.SellFromSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
var itemsToRemove = new List<PurchasedItem>();
|
||||
var totalValue = 0;
|
||||
foreach (PurchasedItem item in itemsToSell)
|
||||
{
|
||||
if (item?.ItemPrefab == null)
|
||||
{
|
||||
itemsToRemove.Add(item);
|
||||
continue;
|
||||
}
|
||||
if (item.ItemPrefab.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo)
|
||||
if (item?.ItemPrefab?.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo)
|
||||
{
|
||||
totalValue += item.Quantity * CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
|
||||
}
|
||||
@@ -1531,12 +1784,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
itemsToRemove.ForEach(i => itemsToSell.Remove(i));
|
||||
|
||||
if (itemsToSell.None() || totalValue > CurrentLocation.StoreCurrentBalance) { return false; }
|
||||
|
||||
CargoManager.SellItems(itemsToSell);
|
||||
GameMain.Client?.SendCampaignState();
|
||||
|
||||
CargoManager.SellItems(itemsToSell, activeTab);
|
||||
if (activeTab == StoreTab.Sell)
|
||||
{
|
||||
// TODO: Implement selling sub items in multiplayer
|
||||
GameMain.Client?.SendCampaignState();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1549,8 +1803,14 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
shoppingCrateTotal.Text = GetCurrencyFormatted(sellTotal);
|
||||
shoppingCrateTotal.TextColor = CurrentLocation != null && sellTotal > CurrentLocation.StoreCurrentBalance ? Color.Red : Color.White;
|
||||
int total = activeTab switch
|
||||
{
|
||||
StoreTab.Sell => sellTotal,
|
||||
StoreTab.SellFromSub => sellFromSubTotal,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
shoppingCrateTotal.Text = GetCurrencyFormatted(total);
|
||||
shoppingCrateTotal.TextColor = CurrentLocation != null && total > CurrentLocation.StoreCurrentBalance ? Color.Red : Color.White;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1580,13 +1840,19 @@ namespace Barotrauma
|
||||
|
||||
private void SetConfirmButtonStatus() => confirmButton.Enabled =
|
||||
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any() &&
|
||||
((IsBuying && buyTotal <= PlayerMoney) || (IsSelling && CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance));
|
||||
activeTab switch
|
||||
{
|
||||
StoreTab.Buy => buyTotal <= PlayerMoney,
|
||||
StoreTab.Sell => CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance,
|
||||
StoreTab.SellFromSub => CurrentLocation != null && sellFromSubTotal <= CurrentLocation.StoreCurrentBalance,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
private void SetClearAllButtonStatus() => clearAllButton.Enabled =
|
||||
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any();
|
||||
|
||||
private float ownedItemsUpdateTimer = 0.0f;
|
||||
private readonly float ownedItemsUpdateInterval = 1.5f;
|
||||
private float ownedItemsUpdateTimer = 0.0f, sellableItemsFromSubUpdateTimer = 0.0f;
|
||||
private readonly float timerUpdateInterval = 1.5f;
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
@@ -1598,7 +1864,7 @@ namespace Barotrauma
|
||||
{
|
||||
// Update the owned items at short intervals and check if the interface should be refreshed
|
||||
ownedItemsUpdateTimer += deltaTime;
|
||||
if (ownedItemsUpdateTimer >= ownedItemsUpdateInterval)
|
||||
if (ownedItemsUpdateTimer >= timerUpdateInterval)
|
||||
{
|
||||
var prevOwnedItems = new Dictionary<ItemPrefab, int>(OwnedItems);
|
||||
UpdateOwnedItems();
|
||||
@@ -1612,12 +1878,21 @@ namespace Barotrauma
|
||||
needsRefresh = true;
|
||||
}
|
||||
}
|
||||
// Update the sellable sub items at short intervals and check if the interface should be refreshed
|
||||
sellableItemsFromSubUpdateTimer += deltaTime;
|
||||
if (sellableItemsFromSubUpdateTimer >= timerUpdateInterval)
|
||||
{
|
||||
needsItemsToSellFromSubRefresh = true;
|
||||
needsRefresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsItemsToSellRefresh) { RefreshItemsToSell(); }
|
||||
if (needsItemsToSellFromSubRefresh) { RefreshItemsToSellFromSub(); }
|
||||
if (needsRefresh || hadPermissions != HasPermissions) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
|
||||
if (needsBuyingRefresh) { RefreshBuying(); }
|
||||
if (needsSellingRefresh) { RefreshSelling(); }
|
||||
if (needsSellingFromSubRefresh) { RefreshSellingFromSub(updateItemsToSellFromSub: sellableItemsFromSubUpdateTimer > 0.0f); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace Barotrauma
|
||||
private const int submarinesPerPage = 4;
|
||||
private int currentPage = 1;
|
||||
private int pageCount;
|
||||
private bool transferService, purchaseService, initialized;
|
||||
private readonly bool transferService, purchaseService;
|
||||
private bool initialized;
|
||||
private int deliveryFee;
|
||||
private string deliveryLocationName;
|
||||
|
||||
@@ -27,12 +28,12 @@ namespace Barotrauma
|
||||
private int selectionIndicatorThickness;
|
||||
private GUIImage listBackground;
|
||||
|
||||
private List<SubmarineInfo> subsToShow;
|
||||
private SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage];
|
||||
private readonly List<SubmarineInfo> subsToShow;
|
||||
private readonly 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 readonly RectTransform parent;
|
||||
private readonly Action closeAction;
|
||||
private Sprite pageIndicator;
|
||||
|
||||
public static readonly string[] DeliveryTextVariables = new string[] { "[submarinename1]", "[location1]", "[location2]", "[submarinename2]", "[amount]", "[currencyname]" };
|
||||
@@ -42,7 +43,7 @@ namespace Barotrauma
|
||||
|
||||
private static readonly string[] notEnoughCreditsDeliveryTextVariables = new string[] { "[currencyname]", "[submarinename]", "[location1]", "[location2]" };
|
||||
private static readonly string[] notEnoughCreditsPurchaseTextVariables = new string[] { "[currencyname]", "[submarinename]" };
|
||||
private string[] messageBoxOptions;
|
||||
private readonly string[] messageBoxOptions;
|
||||
|
||||
public const int DeliveryFeePerDistanceTravelled = 1000;
|
||||
public static bool ContentRefreshRequired = false;
|
||||
@@ -65,7 +66,7 @@ namespace Barotrauma
|
||||
|
||||
public SubmarineSelection(bool transfer, Action closeAction, RectTransform parent)
|
||||
{
|
||||
if (GameMain.GameSession.Campaign == null) return;
|
||||
if (GameMain.GameSession.Campaign == null) { return; }
|
||||
|
||||
transferService = transfer;
|
||||
purchaseService = !transfer;
|
||||
@@ -83,7 +84,7 @@ namespace Barotrauma
|
||||
messageBoxOptions = new string[2] { TextManager.Get("Yes") + " " + TextManager.Get("initiatevoting"), TextManager.Get("Cancel") };
|
||||
}
|
||||
|
||||
if (Submarine.MainSub?.Info == null) return;
|
||||
if (Submarine.MainSub?.Info == null) { return; }
|
||||
Initialize();
|
||||
}
|
||||
|
||||
@@ -184,8 +185,10 @@ namespace Barotrauma
|
||||
|
||||
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));
|
||||
SubmarineDisplayContent submarineDisplayElement = new SubmarineDisplayContent
|
||||
{
|
||||
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);
|
||||
@@ -433,7 +436,7 @@ namespace Barotrauma
|
||||
|
||||
private SubmarineInfo GetSubToDisplay(int index)
|
||||
{
|
||||
if (subsToShow.Count <= index || index < 0) return null;
|
||||
if (subsToShow.Count <= index || index < 0) { return null; }
|
||||
return subsToShow[index];
|
||||
}
|
||||
|
||||
@@ -626,7 +629,6 @@ namespace Barotrauma
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
|
||||
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub);
|
||||
RefreshSubmarineDisplay(true);
|
||||
}
|
||||
else
|
||||
@@ -661,7 +663,6 @@ namespace Barotrauma
|
||||
{
|
||||
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
|
||||
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
|
||||
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub);
|
||||
RefreshSubmarineDisplay(true);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -228,7 +228,10 @@ namespace Barotrauma
|
||||
|
||||
var crewButton = createTabButton(InfoFrameTab.Crew, "crew");
|
||||
|
||||
var missionButton = createTabButton(InfoFrameTab.Mission, "mission");
|
||||
if (!(GameMain.GameSession?.GameMode is TestGameMode))
|
||||
{
|
||||
createTabButton(InfoFrameTab.Mission, "mission");
|
||||
}
|
||||
|
||||
if (GameMain.GameSession?.GameMode is CampaignMode campaignMode)
|
||||
{
|
||||
@@ -903,51 +906,68 @@ 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 location = GameMain.GameSession.EndLocation != null ? GameMain.GameSession.EndLocation : GameMain.GameSession.StartLocation;
|
||||
GUIFrame missionFrameContent = new GUIFrame(new RectTransform(new Point(missionFrame.Rect.Width - padding * 2, missionFrame.Rect.Height - padding * 2), infoFrame.RectTransform, Anchor.Center), style: null);
|
||||
Location location = GameMain.GameSession.EndLocation ?? GameMain.GameSession.StartLocation;
|
||||
|
||||
GUILayoutGroup locationInfoContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), missionFrameContent.RectTransform))
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(10)
|
||||
};
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Name, font: GUI.LargeFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Type.Name, font: GUI.SubHeadingFont);
|
||||
|
||||
var biomeLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
|
||||
TextManager.Get("Biome", fallBackTag: "location"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), biomeLabel.RectTransform), Level.Loaded.LevelData.Biome.DisplayName, textAlignment: Alignment.CenterRight);
|
||||
var difficultyLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
|
||||
TextManager.Get("LevelDifficulty"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), difficultyLabel.RectTransform), ((int)Level.Loaded.LevelData.Difficulty) + " %", textAlignment: Alignment.CenterRight);
|
||||
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), missionFrameContent.RectTransform) { AbsoluteOffset = new Point(0, locationInfoContainer.Rect.Height + padding) }, style: "HorizontalLine")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
int locationInfoYOffset = locationInfoContainer.Rect.Height + padding * 2;
|
||||
|
||||
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(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;
|
||||
|
||||
GUIListBox missionList;
|
||||
int contentWidth = missionFrameContent.Rect.Width;
|
||||
|
||||
if (hasPortrait)
|
||||
{
|
||||
GUIFrame portraitHolder = new GUIFrame(new RectTransform(new Point(contentWidth, (int)(missionFrame.Rect.Height * 0.588f)), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
|
||||
float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height;
|
||||
GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(1.0f, 1f), portraitHolder.RectTransform), portrait, scaleToFit: true);
|
||||
portraitHolder.RectTransform.NonScaledSize = new Point(portraitImage.Rect.Size.X, (int)(portraitImage.Rect.Size.X / portraitAspectRatio));
|
||||
GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(0.5f, 1f), locationInfoContainer.RectTransform, Anchor.CenterRight), portrait, scaleToFit: true)
|
||||
{
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
locationInfoContainer.Recalculate();
|
||||
portraitImage.RectTransform.NonScaledSize = new Point(Math.Min((int)(portraitImage.Rect.Size.Y * portraitAspectRatio), portraitImage.Rect.Width), portraitImage.Rect.Size.Y);
|
||||
}
|
||||
|
||||
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Bottom - portraitHolder.Rect.Bottom - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, portraitHolder.RectTransform.AbsoluteOffset.Y + portraitHolder.Rect.Height + padding) });
|
||||
}
|
||||
else
|
||||
{
|
||||
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Height - locationInfoYOffset - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
|
||||
}
|
||||
GUIListBox missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrameContent.Rect.Height - locationInfoYOffset), missionFrameContent.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
|
||||
missionList.ContentBackground.Color = Color.Transparent;
|
||||
missionList.Spacing = GUI.IntScale(15);
|
||||
|
||||
if (GameMain.GameSession?.Missions != null)
|
||||
{
|
||||
int spacing = GUI.IntScale(5);
|
||||
int iconSize = (int)(GUI.LargeFont.MeasureChar('T').Y + GUI.Font.MeasureChar('T').Y * 4 + spacing * 4);
|
||||
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null);
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft)
|
||||
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(iconSize + spacing, 0) }, false, childAnchor: Anchor.TopLeft)
|
||||
{
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
AbsoluteSpacing = spacing
|
||||
};
|
||||
string descriptionText = mission.Description;
|
||||
foreach (string missionMessage in mission.ShownMessages)
|
||||
{
|
||||
descriptionText += "\n\n" + missionMessage;
|
||||
}
|
||||
string rewardText = mission.GetMissionRewardText();
|
||||
string rewardText = mission.GetMissionRewardText(Submarine.MainSub);
|
||||
string reputationText = mission.GetReputationRewardText(mission.Locations[0]);
|
||||
|
||||
var missionNameRichTextData = RichTextData.GetRichTextData(mission.Name, out string missionNameString);
|
||||
@@ -974,12 +994,12 @@ namespace Barotrauma
|
||||
|
||||
if (mission.Prefab.Icon != null)
|
||||
{
|
||||
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
|
||||
/*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);
|
||||
Point iconSize = new Point(iconWidth, iconHeight);*/
|
||||
|
||||
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
|
||||
new GUIImage(new RectTransform(new Point(iconSize), missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
|
||||
{
|
||||
Color = mission.Prefab.IconColor,
|
||||
HoverColor = mission.Prefab.IconColor,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@ namespace Barotrauma
|
||||
public static bool ShowFPS = false;
|
||||
public static bool ShowPerf = false;
|
||||
public static bool DebugDraw;
|
||||
public static bool IsSingleplayer => NetworkMember == null;
|
||||
public static bool IsMultiplayer => NetworkMember != null;
|
||||
|
||||
public static PerformanceCounter PerformanceCounter;
|
||||
@@ -245,6 +246,23 @@ namespace Barotrauma
|
||||
FarseerPhysics.Settings.PositionIterations = 1;
|
||||
|
||||
MainThread = Thread.CurrentThread;
|
||||
|
||||
Window.FileDropped += OnFileDropped;
|
||||
}
|
||||
|
||||
public static void OnFileDropped(object sender, FileDropEventArgs args)
|
||||
{
|
||||
if (!(Screen.Selected is { } screen)) { return; }
|
||||
|
||||
string filePath = args.FilePath;
|
||||
if (string.IsNullOrWhiteSpace(filePath)) { return; }
|
||||
|
||||
string extension = Path.GetExtension(filePath).ToLower();
|
||||
|
||||
System.IO.FileInfo info = new System.IO.FileInfo(args.FilePath);
|
||||
if (!info.Exists) { return; }
|
||||
|
||||
screen.OnFileDropped(filePath, extension);
|
||||
}
|
||||
|
||||
public void ApplyGraphicsSettings()
|
||||
@@ -933,10 +951,7 @@ namespace Barotrauma
|
||||
|
||||
Screen.Selected.AddToGUIUpdateList();
|
||||
|
||||
if (Client != null)
|
||||
{
|
||||
Client.AddToGUIUpdateList();
|
||||
}
|
||||
Client?.AddToGUIUpdateList();
|
||||
|
||||
SubmarinePreview.AddToGUIUpdateList();
|
||||
|
||||
@@ -966,10 +981,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (NetworkMember != null)
|
||||
{
|
||||
NetworkMember.Update((float)Timing.Step);
|
||||
}
|
||||
NetworkMember?.Update((float)Timing.Step);
|
||||
|
||||
GUI.Update((float)Timing.Step);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
@@ -42,10 +43,7 @@ namespace Barotrauma
|
||||
public IEnumerable<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 multiplayer (SellStatus.Local), but the client has not received a campaing state update yet after selling them
|
||||
var confirmedSoldEntities = SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
|
||||
var confirmedSoldEntities = GetConfirmedSoldEntities();
|
||||
// The bag slot is intentionally left out since we want to be able to sell items from there
|
||||
var equipmentSlots = new List<InvSlotType>() { InvSlotType.Head, InvSlotType.InnerClothes, InvSlotType.OuterClothes, InvSlotType.Headset, InvSlotType.Card };
|
||||
return character.Inventory.FindAllItems(item =>
|
||||
@@ -72,6 +70,43 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Item> GetSellableItemsFromSub()
|
||||
{
|
||||
if (Submarine.MainSub == null) { return new List<Item>(); }
|
||||
var confirmedSoldEntities = GetConfirmedSoldEntities();
|
||||
return Submarine.MainSub.GetItems(true).FindAll(item =>
|
||||
{
|
||||
if (!item.Prefab.CanBeSold) { return false; }
|
||||
if (item.SpawnedInOutpost) { return false; }
|
||||
if (!item.Prefab.AllowSellingWhenBroken && item.ConditionPercentage < 90.0f) { return false; }
|
||||
if (!item.Components.All(c => !(c is Holdable h) || !h.Attachable || !h.Attached)) { return false; }
|
||||
if (!item.Components.All(c => !(c is Wire w) || w.Connections.All(c => c == null))) { return false; }
|
||||
if (!ItemAndAllContainersInteractable(item)) { return false; }
|
||||
if (confirmedSoldEntities.Any(it => it.Item == item)) { return false; }
|
||||
// There must be no contained items or the contained items must be confirmed as sold
|
||||
if (!item.ContainedItems.All(it => confirmedSoldEntities.Any(se => se.Item == it))) { return false; }
|
||||
return true;
|
||||
}).Distinct();
|
||||
|
||||
static bool ItemAndAllContainersInteractable(Item item)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (!item.IsPlayerTeamInteractable) { return false; }
|
||||
item = item.Container;
|
||||
} while (item != null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<SoldEntity> GetConfirmedSoldEntities()
|
||||
{
|
||||
// Only consider items which have been:
|
||||
// a) sold in singleplayer or confirmed by server (SellStatus.Confirmed); or
|
||||
// b) sold locally in multiplayer (SellStatus.Local), but the client has not received a campaing state update yet after selling them
|
||||
return SoldEntities.Where(se => se.Status != SoldEntity.SellStatus.Unconfirmed);
|
||||
}
|
||||
|
||||
public void SetItemsInBuyCrate(List<PurchasedItem> items)
|
||||
{
|
||||
ItemsInBuyCrate.Clear();
|
||||
@@ -119,10 +154,34 @@ namespace Barotrauma
|
||||
OnItemsInSellCrateChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void SellItems(List<PurchasedItem> itemsToSell)
|
||||
public void ModifyItemQuantityInSellFromSubCrate(ItemPrefab itemPrefab, int changeInQuantity)
|
||||
{
|
||||
var itemsInInventory = GetSellableItems(Character.Controlled);
|
||||
var canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
|
||||
var itemToSell = ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == itemPrefab);
|
||||
if (itemToSell != null)
|
||||
{
|
||||
itemToSell.Quantity += changeInQuantity;
|
||||
if (itemToSell.Quantity < 1)
|
||||
{
|
||||
ItemsInSellFromSubCrate.Remove(itemToSell);
|
||||
}
|
||||
}
|
||||
else if (changeInQuantity > 0)
|
||||
{
|
||||
itemToSell = new PurchasedItem(itemPrefab, changeInQuantity);
|
||||
ItemsInSellFromSubCrate.Add(itemToSell);
|
||||
}
|
||||
OnItemsInSellFromSubCrateChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void SellItems(List<PurchasedItem> itemsToSell, Store.StoreTab sellingMode)
|
||||
{
|
||||
var sellableItems = sellingMode switch
|
||||
{
|
||||
Store.StoreTab.Sell => GetSellableItems(Character.Controlled),
|
||||
Store.StoreTab.SellFromSub => GetSellableItemsFromSub(),
|
||||
_ => throw new System.NotImplementedException(),
|
||||
};
|
||||
bool canAddToRemoveQueue = campaign.IsSinglePlayer && Entity.Spawner != null;
|
||||
var sellerId = GameMain.Client?.ID ?? 0;
|
||||
|
||||
// Check all the prices before starting the transaction
|
||||
@@ -137,7 +196,7 @@ namespace Barotrauma
|
||||
if (Location.StoreCurrentBalance < itemValue) { continue; }
|
||||
|
||||
// TODO: Write logic for prioritizing certain items over others (e.g. lone Battery Cell should be preferred over one inside a Stun Baton)
|
||||
var matchingItems = itemsInInventory.Where(i => i.Prefab == item.ItemPrefab);
|
||||
var matchingItems = sellableItems.Where(i => i.Prefab == item.ItemPrefab);
|
||||
if (matchingItems.Count() <= item.Quantity)
|
||||
{
|
||||
foreach (Item i in matchingItems)
|
||||
@@ -163,12 +222,21 @@ namespace Barotrauma
|
||||
campaign.Money += itemValue;
|
||||
|
||||
// Remove from the sell crate
|
||||
if (ItemsInSellCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } itemToSell)
|
||||
// TODO: Simplify duplicate logic?
|
||||
if (sellingMode == Store.StoreTab.Sell && ItemsInSellCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } inventoryItem)
|
||||
{
|
||||
itemToSell.Quantity -= item.Quantity;
|
||||
if (itemToSell.Quantity < 1)
|
||||
inventoryItem.Quantity -= item.Quantity;
|
||||
if (inventoryItem.Quantity < 1)
|
||||
{
|
||||
ItemsInSellCrate.Remove(itemToSell);
|
||||
ItemsInSellCrate.Remove(inventoryItem);
|
||||
}
|
||||
}
|
||||
else if(sellingMode == Store.StoreTab.SellFromSub && ItemsInSellFromSubCrate.Find(pi => pi.ItemPrefab == item.ItemPrefab) is { } subItem)
|
||||
{
|
||||
subItem.Quantity -= item.Quantity;
|
||||
if (subItem.Quantity < 1)
|
||||
{
|
||||
ItemsInSellFromSubCrate.Remove(subItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace Barotrauma
|
||||
: this(isSinglePlayer)
|
||||
{
|
||||
AddCharacterElements(element);
|
||||
ActiveOrdersElement = element.GetChildElement("activeorders");
|
||||
}
|
||||
|
||||
partial void InitProjectSpecific()
|
||||
@@ -192,7 +193,8 @@ namespace Barotrauma
|
||||
{
|
||||
AbsoluteSpacing = (int)(5 * GUI.Scale),
|
||||
UserData = "reportbuttons",
|
||||
CanBeFocused = false
|
||||
CanBeFocused = false,
|
||||
Visible = false
|
||||
};
|
||||
|
||||
ReportButtonFrame.RectTransform.AbsoluteOffset = new Point(0, -chatBox.ToggleButton.Rect.Height);
|
||||
@@ -433,13 +435,20 @@ namespace Barotrauma
|
||||
Stretch = false
|
||||
};
|
||||
|
||||
var soundIcons = new GUIFrame(new RectTransform(new Vector2(0.8f * iconRelativeWidth, 0.8f), layoutGroup.RectTransform), style: null)
|
||||
var extraIconFrame = new GUIFrame(new RectTransform(new Vector2(0.8f * iconRelativeWidth, 0.8f), layoutGroup.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
UserData = "soundicons"
|
||||
UserData = "extraicons"
|
||||
};
|
||||
|
||||
var soundIconParent = new GUIFrame(new RectTransform(Vector2.One, extraIconFrame.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
UserData = "soundicons",
|
||||
Visible = character.IsPlayer
|
||||
};
|
||||
new GUIImage(
|
||||
new RectTransform(Vector2.One, soundIcons.RectTransform),
|
||||
new RectTransform(Vector2.One, soundIconParent.RectTransform),
|
||||
GUI.Style.GetComponentStyle("GUISoundIcon").GetDefaultSprite(),
|
||||
scaleToFit: true)
|
||||
{
|
||||
@@ -448,7 +457,7 @@ namespace Barotrauma
|
||||
Visible = true
|
||||
};
|
||||
new GUIImage(
|
||||
new RectTransform(Vector2.One, soundIcons.RectTransform),
|
||||
new RectTransform(Vector2.One, soundIconParent.RectTransform),
|
||||
"GUISoundIconDisabled",
|
||||
scaleToFit: true)
|
||||
{
|
||||
@@ -457,6 +466,16 @@ namespace Barotrauma
|
||||
Visible = false
|
||||
};
|
||||
|
||||
if (character.IsBot)
|
||||
{
|
||||
new GUIFrame(new RectTransform(Vector2.One, extraIconFrame.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false,
|
||||
UserData = "objectiveicon",
|
||||
Visible = false
|
||||
};
|
||||
}
|
||||
|
||||
new GUIButton(new RectTransform(new Point((int)commandButtonAbsoluteHeight), background.RectTransform), style: "CrewListCommandButton")
|
||||
{
|
||||
ToolTip = TextManager.Get("inputtype.command"),
|
||||
@@ -624,9 +643,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (client?.Character == null) { return; }
|
||||
|
||||
if (crewList.Content.GetChildByUserData(client.Character)?
|
||||
.FindChild(c => c is GUILayoutGroup)?
|
||||
.GetChildByUserData("soundicons") is GUIComponent soundIcons)
|
||||
if (GetSoundIconParent(client.Character) is GUIComponent soundIcons)
|
||||
{
|
||||
var soundIcon = soundIcons.FindChild(c => c.UserData is Pair<string, float> pair && pair.First == "soundicon");
|
||||
var soundIconDisabled = soundIcons.FindChild("soundicondisabled");
|
||||
@@ -638,15 +655,17 @@ namespace Barotrauma
|
||||
|
||||
public void SetClientSpeaking(Client client)
|
||||
{
|
||||
if (client?.Character != null) { SetCharacterSpeaking(client.Character); }
|
||||
if (client?.Character != null)
|
||||
{
|
||||
SetCharacterSpeaking(client.Character);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCharacterSpeaking(Character character)
|
||||
{
|
||||
if (crewList.Content.GetChildByUserData(character)?
|
||||
.FindChild(c => c is GUILayoutGroup)?
|
||||
.GetChildByUserData("soundicons")?
|
||||
.FindChild(c => c.UserData is Pair<string, float> pair && pair.First == "soundicon") is GUIComponent soundIcon)
|
||||
if (character == null || character.IsBot) { return; }
|
||||
|
||||
if (GetSoundIconParent(character)?.FindChild(c => c.UserData is Pair<string, float> pair && pair.First == "soundicon") is GUIComponent soundIcon)
|
||||
{
|
||||
soundIcon.Color = Color.White;
|
||||
Pair<string, float> userdata = soundIcon.UserData as Pair<string, float>;
|
||||
@@ -654,6 +673,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private GUIComponent GetSoundIconParent(GUIComponent characterComponent)
|
||||
{
|
||||
return characterComponent?
|
||||
.FindChild(c => c is GUILayoutGroup)?
|
||||
.GetChildByUserData("extraicons")?
|
||||
.GetChildByUserData("soundicons");
|
||||
}
|
||||
|
||||
private GUIComponent GetSoundIconParent(Character character)
|
||||
{
|
||||
return GetSoundIconParent(crewList?.Content.GetChildByUserData(character));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Crew List Order Displayment
|
||||
@@ -770,6 +802,7 @@ namespace Barotrauma
|
||||
if (icon is GUIImage image)
|
||||
{
|
||||
image.Sprite = GetOrderIconSprite(order, option);
|
||||
image.ToolTip = CreateOrderTooltip(order, option);
|
||||
}
|
||||
updatedExistingIcon = true;
|
||||
}
|
||||
@@ -835,7 +868,7 @@ namespace Barotrauma
|
||||
Visible = false
|
||||
};
|
||||
|
||||
int hierarchyIndex = GetOrderIconHierarchyIndex(priority);
|
||||
int hierarchyIndex = Math.Clamp(CharacterInfo.HighestManualOrderPriority - priority, 0, Math.Max(currentOrderIconList.Content.CountChildren - 1, 0));
|
||||
if (hierarchyIndex != currentOrderIconList.Content.GetChildIndex(nodeIcon))
|
||||
{
|
||||
nodeIcon.RectTransform.RepositionChildInHierarchy(hierarchyIndex);
|
||||
@@ -878,11 +911,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int GetOrderIconHierarchyIndex(int priority)
|
||||
{
|
||||
return CharacterInfo.HighestManualOrderPriority - priority;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddCurrentOrderIcon(Character character, OrderInfo? orderInfo)
|
||||
@@ -1020,29 +1048,36 @@ namespace Barotrauma
|
||||
SetCharacterOrder(character, orderInfo.Order, orderInfo.OrderOption, priority, Character.Controlled);
|
||||
}
|
||||
|
||||
private string CreateOrderTooltip(Order order, string option)
|
||||
private string CreateOrderTooltip(Order orderPrefab, string option, Entity targetEntity)
|
||||
{
|
||||
if (order == null) { return ""; }
|
||||
if (orderPrefab == null) { return ""; }
|
||||
if (!string.IsNullOrEmpty(option))
|
||||
{
|
||||
return TextManager.GetWithVariables("crewlistordericontooltip",
|
||||
new string[2] { "[ordername]", "[orderoption]" },
|
||||
new string[2] { order.Name, order.GetOptionName(option) });
|
||||
new string[2] { orderPrefab.Name, orderPrefab.GetOptionName(option) });
|
||||
}
|
||||
else if (order.TargetEntity is Item targetItem && order.MinimapIcons.ContainsKey(targetItem.Prefab.Identifier))
|
||||
else if (targetEntity is Item targetItem && targetItem.Prefab.MinimapIcon != null)
|
||||
{
|
||||
return TextManager.GetWithVariables("crewlistordericontooltip",
|
||||
new string[2] { "[ordername]", "[orderoption]" },
|
||||
new string[2] { order.Name, targetItem.Name });
|
||||
new string[2] { orderPrefab.Name, targetItem.Name });
|
||||
}
|
||||
else
|
||||
{
|
||||
return order.Name;
|
||||
return orderPrefab.Name;
|
||||
}
|
||||
}
|
||||
|
||||
private string CreateOrderTooltip(OrderInfo orderInfo) =>
|
||||
CreateOrderTooltip(orderInfo.Order, orderInfo.OrderOption);
|
||||
private string CreateOrderTooltip(Order order, string option)
|
||||
{
|
||||
return CreateOrderTooltip(order?.Prefab ?? order, option, order?.TargetEntity);
|
||||
}
|
||||
|
||||
private string CreateOrderTooltip(OrderInfo orderInfo)
|
||||
{
|
||||
return CreateOrderTooltip(orderInfo.Order?.Prefab ?? orderInfo.Order, orderInfo.OrderOption, orderInfo.Order?.TargetEntity);
|
||||
}
|
||||
|
||||
private Sprite GetOrderIconSprite(Order order, string option)
|
||||
{
|
||||
@@ -1052,9 +1087,9 @@ namespace Barotrauma
|
||||
{
|
||||
order.Prefab.OptionSprites.TryGetValue(option, out sprite);
|
||||
}
|
||||
if (sprite == null && order.TargetEntity is Item targetItem && order.MinimapIcons.Any())
|
||||
if (sprite == null && order.TargetEntity is Item targetItem && targetItem.Prefab.MinimapIcon != null)
|
||||
{
|
||||
order.MinimapIcons.TryGetValue(targetItem.Prefab.Identifier, out sprite);
|
||||
sprite = targetItem.Prefab.MinimapIcon;
|
||||
}
|
||||
return sprite ?? order.SymbolSprite;
|
||||
}
|
||||
@@ -1244,10 +1279,7 @@ namespace Barotrauma
|
||||
//make the previously selected character wait in place for some time
|
||||
//(so they don't immediately start idling and walking away from their station)
|
||||
var aiController = Character.Controlled?.AIController;
|
||||
if (aiController != null)
|
||||
{
|
||||
aiController.Reset();
|
||||
}
|
||||
aiController?.Reset();
|
||||
DisableCommandUI();
|
||||
Character.Controlled = character;
|
||||
HintManager.OnChangeCharacter();
|
||||
@@ -1255,24 +1287,34 @@ namespace Barotrauma
|
||||
|
||||
private int TryAdjustIndex(int amount)
|
||||
{
|
||||
int index = Character.Controlled == null ? 0 :
|
||||
crewList.Content.GetChildIndex(crewList.Content.GetChildByUserData(Character.Controlled)) + amount;
|
||||
if (Character.Controlled == null) { return 0; }
|
||||
|
||||
int currentIndex = crewList.Content.GetChildIndex(crewList.Content.GetChildByUserData(Character.Controlled));
|
||||
if (currentIndex == -1) { return 0; }
|
||||
|
||||
int lastIndex = crewList.Content.CountChildren - 1;
|
||||
if (index > lastIndex)
|
||||
|
||||
int index = currentIndex + amount;
|
||||
for (int i = 0; i < crewList.Content.CountChildren; i++)
|
||||
{
|
||||
index = 0;
|
||||
if (index > lastIndex) { index = 0; }
|
||||
if (index < 0) { index = lastIndex; }
|
||||
|
||||
if ((crewList.Content.GetChild(index)?.UserData as Character)?.IsOnPlayerTeam ?? false)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
index += amount;
|
||||
}
|
||||
if (index < 0)
|
||||
{
|
||||
index = lastIndex;
|
||||
}
|
||||
return index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
partial void UpdateProjectSpecific(float deltaTime)
|
||||
{
|
||||
// Quick selection
|
||||
if (!GameMain.IsMultiplayer && GUI.KeyboardDispatcher.Subscriber == null)
|
||||
if (GameMain.IsSingleplayer && GUI.KeyboardDispatcher.Subscriber == null)
|
||||
{
|
||||
if (PlayerInput.KeyHit(InputType.SelectNextCharacter))
|
||||
{
|
||||
@@ -1294,7 +1336,7 @@ namespace Barotrauma
|
||||
(GUI.KeyboardDispatcher.Subscriber == null || (GUI.KeyboardDispatcher.Subscriber is GUIComponent component && (component == crewList || component.IsChildOf(crewList)))) &&
|
||||
commandFrame == null && !clicklessSelectionActive && CanIssueOrders && !(GameMain.GameSession?.Campaign?.ShowCampaignUI ?? false))
|
||||
{
|
||||
if (PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift))
|
||||
if (PlayerInput.IsShiftDown())
|
||||
{
|
||||
CreateCommandUI(FindEntityContext(), true);
|
||||
}
|
||||
@@ -1521,19 +1563,44 @@ namespace Barotrauma
|
||||
{
|
||||
crewList.Select(character, force: true);
|
||||
}
|
||||
if (character.AIController is HumanAIController controller)
|
||||
if (GameMain.IsSingleplayer && character.IsBot && character.AIController is HumanAIController controller &&
|
||||
controller.ObjectiveManager is AIObjectiveManager objectiveManager)
|
||||
{
|
||||
OrderInfo? currentOrderInfo = controller.ObjectiveManager?.GetCurrentOrderInfo();
|
||||
if (currentOrderInfo.HasValue)
|
||||
// In multiplayer, these are set through character networking (the server lets the clients now when these are updated)
|
||||
if (objectiveManager.CurrentObjective is AIObjective currentObjective)
|
||||
{
|
||||
SetHighlightedOrderIcon(characterComponent, currentOrderInfo.Value.Order?.Identifier, currentOrderInfo.Value.OrderOption);
|
||||
if (objectiveManager.IsOrder(currentObjective))
|
||||
{
|
||||
var orderInfo = objectiveManager.CurrentOrders.FirstOrDefault(o => o.Objective == currentObjective);
|
||||
SetOrderHighlight(characterComponent, orderInfo.Order?.Identifier, orderInfo.OrderOption);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateObjectiveIcon(characterComponent, currentObjective);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (characterComponent.GetChild<GUILayoutGroup>().GetChildByUserData("soundicons") is GUIComponent soundIconParent)
|
||||
// Order highlighting and objective icons are intended to communicate bot behavior so they should be disabled for player characters
|
||||
if (character.IsPlayer)
|
||||
{
|
||||
DisableOrderHighlight(characterComponent);
|
||||
RemoveObjectiveIcon(characterComponent);
|
||||
}
|
||||
if (GetSoundIconParent(characterComponent) is GUIComponent soundIconParent)
|
||||
{
|
||||
if (soundIconParent.FindChild(c => c.UserData is Pair<string, float> pair && pair.First == "soundicon") is GUIImage soundIcon)
|
||||
{
|
||||
VoipClient.UpdateVoiceIndicator(soundIcon, 0.0f, deltaTime);
|
||||
if (character.IsPlayer)
|
||||
{
|
||||
soundIconParent.Visible = true;
|
||||
VoipClient.UpdateVoiceIndicator(soundIcon, 0.0f, deltaTime);
|
||||
}
|
||||
else if(soundIcon.Visible)
|
||||
{
|
||||
var userdata = soundIcon.UserData as Pair<string, float>;
|
||||
userdata.Second = 0.0f;
|
||||
soundIconParent.Visible = soundIcon.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1559,31 +1626,127 @@ namespace Barotrauma
|
||||
UpdateReports();
|
||||
}
|
||||
|
||||
private void SetHighlightedOrderIcon(GUIComponent characterComponent, string orderIdentifier, string orderOption)
|
||||
private void SetOrderHighlight(GUIComponent characterComponent, string orderIdentifier, string orderOption)
|
||||
{
|
||||
var currentOrderIconList = GetCurrentOrderIconList(characterComponent);
|
||||
if (currentOrderIconList == null) { return; }
|
||||
bool foundMatch = false;
|
||||
foreach (var orderIcon in currentOrderIconList.Content.Children)
|
||||
if (characterComponent == null) { return; }
|
||||
RemoveObjectiveIcon(characterComponent);
|
||||
if (GetCurrentOrderIconList(characterComponent) is GUIListBox currentOrderIconList)
|
||||
{
|
||||
var glowComponent = orderIcon.GetChildByUserData("glow");
|
||||
if (glowComponent == null) { continue; }
|
||||
if (foundMatch)
|
||||
bool foundMatch = false;
|
||||
foreach (var orderIcon in currentOrderIconList.Content.Children)
|
||||
{
|
||||
glowComponent.Visible = false;
|
||||
continue;
|
||||
var glowComponent = orderIcon.GetChildByUserData("glow");
|
||||
if (glowComponent == null) { continue; }
|
||||
if (foundMatch)
|
||||
{
|
||||
glowComponent.Visible = false;
|
||||
continue;
|
||||
}
|
||||
var orderInfo = (OrderInfo)orderIcon.UserData;
|
||||
foundMatch = orderInfo.MatchesOrder(orderIdentifier, orderOption);
|
||||
glowComponent.Visible = foundMatch;
|
||||
}
|
||||
var orderInfo = (OrderInfo)orderIcon.UserData;
|
||||
foundMatch = orderInfo.MatchesOrder(orderIdentifier, orderOption);
|
||||
glowComponent.Visible = foundMatch;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetHighlightedOrderIcon(Character character, string orderIdentifier, string orderOption)
|
||||
public void SetOrderHighlight(Character character, string orderIdentifier, string orderOption)
|
||||
{
|
||||
if (crewList == null) { return; }
|
||||
var characterComponent = crewList.Content.GetChildByUserData(character);
|
||||
SetHighlightedOrderIcon(characterComponent, orderIdentifier, orderOption);
|
||||
SetOrderHighlight(characterComponent, orderIdentifier, orderOption);
|
||||
}
|
||||
|
||||
private void DisableOrderHighlight(GUIComponent characterComponent)
|
||||
{
|
||||
if (GetCurrentOrderIconList(characterComponent) is GUIListBox currentOrderIconList)
|
||||
{
|
||||
foreach (var orderIcon in currentOrderIconList.Content.Children)
|
||||
{
|
||||
var glowComponent = orderIcon.GetChildByUserData("glow");
|
||||
if (glowComponent == null) { continue; }
|
||||
glowComponent.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateObjectiveIcon(GUIComponent characterComponent, Sprite sprite, string tooltip)
|
||||
{
|
||||
if (characterComponent == null || !(characterComponent.UserData is Character character) || character.IsPlayer) { return; }
|
||||
DisableOrderHighlight(characterComponent);
|
||||
if (GetObjectiveIconParent(characterComponent) is GUIFrame objectiveIconFrame)
|
||||
{
|
||||
var existingObjectiveIcon = objectiveIconFrame.GetChild<GUIImage>();
|
||||
if (existingObjectiveIcon == null || existingObjectiveIcon.Sprite != sprite || existingObjectiveIcon.ToolTip != tooltip)
|
||||
{
|
||||
objectiveIconFrame.ClearChildren();
|
||||
if (sprite != null)
|
||||
{
|
||||
var objectiveIcon = CreateNodeIcon(Vector2.One, objectiveIconFrame.RectTransform, sprite, Color.LightGray, tooltip: tooltip);
|
||||
new GUIFrame(new RectTransform(new Vector2(1.5f), objectiveIcon.RectTransform, anchor: Anchor.Center), style: "OuterGlowCircular")
|
||||
{
|
||||
CanBeFocused = false,
|
||||
Color = Color.LightGray
|
||||
};
|
||||
objectiveIconFrame.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
objectiveIconFrame.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateObjectiveIcon(Character character, string identifier, string option, Entity targetEntity)
|
||||
{
|
||||
CreateObjectiveIcon(crewList?.Content.GetChildByUserData(character),
|
||||
AIObjective.GetSprite(identifier, option, targetEntity),
|
||||
GetObjectiveIconTooltip(identifier, option, targetEntity));
|
||||
}
|
||||
|
||||
private void CreateObjectiveIcon(GUIComponent characterComponent, AIObjective objective)
|
||||
{
|
||||
CreateObjectiveIcon(characterComponent,
|
||||
objective?.GetSprite(),
|
||||
GetObjectiveIconTooltip(objective));
|
||||
}
|
||||
|
||||
private string GetObjectiveIconTooltip(string identifier, string option, Entity targetEntity)
|
||||
{
|
||||
string variableValue;
|
||||
identifier = identifier.RemoveWhitespace();
|
||||
if (Order.Prefabs.TryGetValue(identifier, out Order orderPrefab))
|
||||
{
|
||||
variableValue = CreateOrderTooltip(orderPrefab, option, targetEntity);
|
||||
}
|
||||
else
|
||||
{
|
||||
variableValue = TextManager.Get($"objective.{identifier}", returnNull: true) ?? "";
|
||||
}
|
||||
return string.IsNullOrEmpty(variableValue) ? variableValue : TextManager.GetWithVariable("crewlistobjectivetooltip", "[objective]", variableValue);
|
||||
}
|
||||
|
||||
private string GetObjectiveIconTooltip(AIObjective objective)
|
||||
{
|
||||
return objective == null ? "" :
|
||||
GetObjectiveIconTooltip(objective.Identifier, objective.Option, (objective as AIObjectiveOperateItem)?.OperateTarget);
|
||||
}
|
||||
|
||||
private GUIComponent GetObjectiveIconParent(GUIComponent characterComponent)
|
||||
{
|
||||
return characterComponent?
|
||||
.GetChild<GUILayoutGroup>()?
|
||||
.GetChildByUserData("extraicons")?
|
||||
.GetChildByUserData("objectiveicon");
|
||||
}
|
||||
|
||||
private void RemoveObjectiveIcon(GUIComponent characterComponent)
|
||||
{
|
||||
if (GetObjectiveIconParent(characterComponent) is GUIFrame objectiveIconFrame)
|
||||
{
|
||||
objectiveIconFrame.ClearChildren();
|
||||
objectiveIconFrame.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1853,7 +2016,10 @@ namespace Barotrauma
|
||||
{
|
||||
nodeConnectors = new GUICustomComponent(
|
||||
new RectTransform(Vector2.One, commandFrame.RectTransform),
|
||||
onDraw: DrawNodeConnectors);
|
||||
onDraw: DrawNodeConnectors)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
nodeConnectors.SetAsFirstChild();
|
||||
background.SetAsFirstChild();
|
||||
}
|
||||
@@ -1862,10 +2028,27 @@ namespace Barotrauma
|
||||
{
|
||||
if (centerNode == null || optionNodes == null) { return; }
|
||||
var startNodePos = centerNode.Rect.Center.ToVector2();
|
||||
// Don't draw connectors for mini map options or assignment nodes
|
||||
if ((targetFrame == null || !targetFrame.Visible) && !(optionNodes.FirstOrDefault()?.Item1.UserData is Character))
|
||||
// Don't draw connectors for assignment nodes
|
||||
if (!(optionNodes.FirstOrDefault()?.Item1.UserData is Character))
|
||||
{
|
||||
optionNodes.ForEach(n => DrawNodeConnector(startNodePos, centerNodeMargin, n.Item1, optionNodeMargin, spriteBatch));
|
||||
// Regular option nodes
|
||||
if (targetFrame == null || !targetFrame.Visible)
|
||||
{
|
||||
optionNodes.ForEach(n => DrawNodeConnector(startNodePos, centerNodeMargin, n.Item1, optionNodeMargin, spriteBatch));
|
||||
}
|
||||
// Minimap item nodes for single-option orders
|
||||
else if(optionNodes.FirstOrDefault()?.Item1?.UserData is Tuple<Order, string> userData && string.IsNullOrEmpty(userData.Item2))
|
||||
{
|
||||
foreach (var node in optionNodes)
|
||||
{
|
||||
float iconRadius = 0.5f * optionNodeMargin;
|
||||
Vector2 itemPosition = node.Item1.Parent.Rect.Center.ToVector2();
|
||||
if (Vector2.Distance(node.Item1.Center, itemPosition) <= iconRadius) { continue; }
|
||||
DrawNodeConnector(itemPosition, 0.0f, node.Item1, iconRadius, spriteBatch, widthMultiplier: 0.5f);
|
||||
GUI.DrawFilledRectangle(spriteBatch, itemPosition - Vector2.One, new Vector2(3),
|
||||
node.Item1.GetChildByUserData("colorsource")?.Color ?? Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
DrawNodeConnector(startNodePos, centerNodeMargin, returnNode, returnNodeMargin, spriteBatch);
|
||||
if (shortcutCenterNode == null || !shortcutCenterNode.Visible) { return; }
|
||||
@@ -1874,7 +2057,7 @@ namespace Barotrauma
|
||||
shortcutNodes.ForEach(n => DrawNodeConnector(startNodePos, shortcutCenterNodeMargin, n, shortcutNodeMargin, spriteBatch));
|
||||
}
|
||||
|
||||
private void DrawNodeConnector(Vector2 startNodePos, float startNodeMargin, GUIComponent endNode, float endNodeMargin, SpriteBatch spriteBatch)
|
||||
private void DrawNodeConnector(Vector2 startNodePos, float startNodeMargin, GUIComponent endNode, float endNodeMargin, SpriteBatch spriteBatch, float widthMultiplier = 1.0f)
|
||||
{
|
||||
if (endNode == null || !endNode.Visible) { return; }
|
||||
var endNodePos = endNode.Rect.Center.ToVector2();
|
||||
@@ -1885,11 +2068,11 @@ namespace Barotrauma
|
||||
if ((selectedNode == null && endNode != shortcutCenterNode && GUI.IsMouseOn(endNode)) ||
|
||||
(isSelectionHighlighted && (endNode == selectedNode || (endNode == shortcutCenterNode && shortcutNodes.Any(n => GUI.IsMouseOn(n))))))
|
||||
{
|
||||
GUI.DrawLine(spriteBatch, start, end, colorSource != null ? colorSource.HoverColor : Color.White, width: 4);
|
||||
GUI.DrawLine(spriteBatch, start, end, colorSource?.HoverColor ?? Color.White, width: Math.Max(widthMultiplier * 4.0f, 1.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.DrawLine(spriteBatch, start, end, colorSource != null ? colorSource.Color : Color.White * nodeColorMultiplier, width: 2);
|
||||
GUI.DrawLine(spriteBatch, start, end, colorSource?.Color ?? Color.White * nodeColorMultiplier, width: Math.Max(widthMultiplier * 2.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1966,7 +2149,12 @@ namespace Barotrauma
|
||||
{
|
||||
if (commandFrame == null) { return false; }
|
||||
RemoveOptionNodes();
|
||||
if (targetFrame != null) { targetFrame.Visible = false; }
|
||||
if (targetFrame != null)
|
||||
{
|
||||
targetFrame.Visible = false;
|
||||
nodeConnectors.RectTransform.Parent = commandFrame.RectTransform;
|
||||
nodeConnectors.RectTransform.RepositionChildInHierarchy(1);
|
||||
}
|
||||
// TODO: Center node could move to option node instead of being removed
|
||||
commandFrame.RemoveChild(centerNode);
|
||||
SetCenterNode(node);
|
||||
@@ -2126,6 +2314,8 @@ namespace Barotrauma
|
||||
|
||||
private void CreateShortcutNodes()
|
||||
{
|
||||
bool HasAppropriateJob(Character c, string jobId) => c.Info?.Job != null && c.Info.Job.Prefab.AppropriateOrders.Contains(jobId);
|
||||
|
||||
Submarine sub = GetTargetSubmarine();
|
||||
|
||||
if (sub == null) { return; }
|
||||
@@ -2138,7 +2328,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 || !HasAppropriateJob(Character.Controlled, "operatereactor")) &&
|
||||
reactorOutput < float.Epsilon && characters.None(c => c.SelectedConstruction == reactor.Item))
|
||||
{
|
||||
var order = new Order(Order.GetPrefab("operatereactor"), reactor.Item, reactor, Character.Controlled);
|
||||
@@ -2151,7 +2341,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 < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("captain")) &&
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, "steer")) &&
|
||||
sub.GetItems(false).Find(i => i.HasTag("navterminal") && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) &&
|
||||
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
|
||||
{
|
||||
@@ -2161,8 +2351,8 @@ namespace Barotrauma
|
||||
|
||||
// If player is not a security officer AND invaders are reported
|
||||
// --> Create shorcut node for Fight Intruders order
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("securityofficer")) &&
|
||||
(Order.GetPrefab("reportintruders") is Order reportIntruders && ActiveOrders.Any(o => o.First.Prefab == reportIntruders)))
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, "fightintruders")) &&
|
||||
Order.GetPrefab("reportintruders") is Order reportIntruders && ActiveOrders.Any(o => o.First.Prefab == reportIntruders))
|
||||
{
|
||||
shortcutNodes.Add(
|
||||
CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab("fightintruders"), -1));
|
||||
@@ -2170,25 +2360,43 @@ namespace Barotrauma
|
||||
|
||||
// If player is not a mechanic AND a breach has been reported
|
||||
// --> Create shorcut node for Fix Leaks order
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("mechanic")) &&
|
||||
(Order.GetPrefab("reportbreach") is Order reportBreach && ActiveOrders.Any(o => o.First.Prefab == reportBreach)))
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, "fixleaks")) &&
|
||||
Order.GetPrefab("reportbreach") is Order reportBreach && ActiveOrders.Any(o => o.First.Prefab == reportBreach))
|
||||
{
|
||||
shortcutNodes.Add(
|
||||
CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab("fixleaks"), -1));
|
||||
}
|
||||
|
||||
// If player is not an engineer AND broken devices have been reported
|
||||
// --> Create shortcut node for Repair Damaged Systems order
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || Character.Controlled.Info?.Job?.Prefab != JobPrefab.Get("engineer")) &&
|
||||
(Order.GetPrefab("reportbrokendevices") is Order reportBrokenDevices && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices)))
|
||||
// --> Create shortcut nodes for the Repair orders
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && Order.GetPrefab("reportbrokendevices") is Order reportBrokenDevices && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices))
|
||||
{
|
||||
shortcutNodes.Add(
|
||||
CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab("repairsystems"), -1));
|
||||
// TODO: Doesn't work for player issued reports, because they don't have a target.
|
||||
int repairNodes = 0;
|
||||
string tag = "repairelectrical";
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, tag)) && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices && o.First.TargetItemComponent is Repairable r && r.requiredSkills.Any(s => s.Identifier == "electrical")))
|
||||
{
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab(tag), -1));
|
||||
repairNodes++;
|
||||
}
|
||||
tag = "repairmechanical";
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, tag)) && ActiveOrders.Any(o => o.First.Prefab == reportBrokenDevices && o.First.TargetItemComponent is Repairable r && r.requiredSkills.Any(s => s.Identifier == "mechanical")))
|
||||
{
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab(tag), -1));
|
||||
repairNodes++;
|
||||
}
|
||||
if (repairNodes == 0 && shortcutNodes.Count < maxShortCutNodeCount)
|
||||
{
|
||||
tag = "repairsystems";
|
||||
if (Character.Controlled == null || !HasAppropriateJob(Character.Controlled, tag))
|
||||
{
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab(tag), -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If fire is reported
|
||||
// --> Create shortcut node for Extinguish Fires order
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && ActiveOrders.Any(o=> o.First.Prefab == Order.GetPrefab("reportfire")))
|
||||
if (shortcutNodes.Count < maxShortCutNodeCount && ActiveOrders.Any(o => o.First.Prefab == Order.GetPrefab("reportfire")))
|
||||
{
|
||||
shortcutNodes.Add(
|
||||
CreateOrderNode(shortcutNodeSize, null, Point.Zero, Order.GetPrefab("extinguishfires"), -1));
|
||||
@@ -2555,20 +2763,22 @@ namespace Barotrauma
|
||||
if (itemTargetFrame == null) { continue; }
|
||||
|
||||
var anchor = Anchor.TopLeft;
|
||||
if (itemTargetFrame.RectTransform.RelativeOffset.X < 0.5f && itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f)
|
||||
if (itemTargetFrame.RectTransform.RelativeOffset.X < 0.5f)
|
||||
{
|
||||
anchor = Anchor.BottomRight;
|
||||
if (itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f)
|
||||
{
|
||||
anchor = Anchor.BottomRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
anchor = Anchor.TopRight;
|
||||
}
|
||||
}
|
||||
else if (itemTargetFrame.RectTransform.RelativeOffset.X > 0.5f && itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f)
|
||||
else if (itemTargetFrame.RectTransform.RelativeOffset.Y < 0.5f)
|
||||
{
|
||||
anchor = Anchor.BottomLeft;
|
||||
}
|
||||
|
||||
else if (itemTargetFrame.RectTransform.RelativeOffset.X < 0.5f && itemTargetFrame.RectTransform.RelativeOffset.Y > 0.5f)
|
||||
{
|
||||
anchor = Anchor.TopRight;
|
||||
}
|
||||
|
||||
GUIComponent optionElement;
|
||||
if (order.Options.Length > 1)
|
||||
{
|
||||
@@ -2631,7 +2841,6 @@ namespace Barotrauma
|
||||
{
|
||||
UserData = userData,
|
||||
Font = GUI.SmallFont,
|
||||
ToolTip = item?.Name ?? GetOrderNameBasedOnContextuality(order),
|
||||
OnClicked = (_, userData) =>
|
||||
{
|
||||
if (!CanIssueOrders) { return false; }
|
||||
@@ -2645,21 +2854,31 @@ namespace Barotrauma
|
||||
{
|
||||
optionElement.OnSecondaryClicked = (button, _) => CreateAssignmentNodes(button);
|
||||
}
|
||||
Sprite icon = null;
|
||||
order.MinimapIcons?.TryGetValue(item.Prefab.Identifier, out icon);
|
||||
if (item.Prefab.MinimapIcon != null)
|
||||
{
|
||||
icon = item.Prefab.MinimapIcon;
|
||||
}
|
||||
var colorMultiplier = characters.Any(c => c.CurrentOrders.Any(o => o.Order != null &&
|
||||
o.Order.Identifier == userData.Item1.Identifier &&
|
||||
o.Order.TargetEntity == userData.Item1.TargetEntity)) ? 0.5f : 1f;
|
||||
CreateNodeIcon(Vector2.One, optionElement.RectTransform, icon ?? order.SymbolSprite, order.Color * colorMultiplier);
|
||||
CreateNodeIcon(Vector2.One, optionElement.RectTransform, item.Prefab.MinimapIcon ?? order.SymbolSprite, order.Color * colorMultiplier, tooltip: item.Name);
|
||||
optionNodes.Add(new Tuple<GUIComponent, Keys>(optionElement, Keys.None));
|
||||
}
|
||||
optionElements.Add(optionElement);
|
||||
}
|
||||
GUI.PreventElementOverlap(optionElements, clampArea: new Rectangle(10, 10, GameMain.GraphicsWidth - 20, GameMain.GraphicsHeight - 20));
|
||||
|
||||
Rectangle clampArea = new Rectangle(10, 10, GameMain.GraphicsWidth - 20, GameMain.GraphicsHeight - 20);
|
||||
if (order.Identifier == "operateweapons")
|
||||
{
|
||||
Rectangle disallowedArea = targetFrame.GetChild<GUIFrame>().Rect;
|
||||
Point originalSize = disallowedArea.Size;
|
||||
disallowedArea.Size = disallowedArea.MultiplySize(0.9f);
|
||||
disallowedArea.X += (originalSize.X - disallowedArea.Size.X) / 2;
|
||||
disallowedArea.Y += (originalSize.Y - disallowedArea.Size.Y) / 2;
|
||||
GUI.PreventElementOverlap(optionElements, new List<Rectangle>() { disallowedArea }, clampArea);
|
||||
nodeConnectors.RectTransform.Parent = targetFrame.RectTransform;
|
||||
nodeConnectors.RectTransform.SetAsFirstChild();
|
||||
}
|
||||
else
|
||||
{
|
||||
GUI.PreventElementOverlap(optionElements, clampArea: clampArea);
|
||||
}
|
||||
|
||||
var shadow = new GUIFrame(
|
||||
new RectTransform(targetFrame.Rect.Size + new Point((int)(200 * GUI.Scale)), targetFrame.RectTransform, anchor: Anchor.Center),
|
||||
@@ -2731,6 +2950,12 @@ namespace Barotrauma
|
||||
|
||||
private bool CreateAssignmentNodes(GUIComponent node)
|
||||
{
|
||||
if (centerNode == null)
|
||||
{
|
||||
DisableCommandUI();
|
||||
return false;
|
||||
}
|
||||
|
||||
var order = (node.UserData is Order) ?
|
||||
new Tuple<Order, string>(node.UserData as Order, null) :
|
||||
node.UserData as Tuple<Order, string>;
|
||||
@@ -2777,6 +3002,8 @@ namespace Barotrauma
|
||||
node = null;
|
||||
}
|
||||
targetFrame.Visible = false;
|
||||
nodeConnectors.RectTransform.Parent = commandFrame.RectTransform;
|
||||
nodeConnectors.RectTransform.RepositionChildInHierarchy(1);
|
||||
}
|
||||
if (shortcutCenterNode != null)
|
||||
{
|
||||
@@ -3182,16 +3409,7 @@ namespace Barotrauma
|
||||
|
||||
private Character GetCharacterForQuickAssignment(Order order)
|
||||
{
|
||||
var controllingCharacter = Character.Controlled != null;
|
||||
#if !DEBUG
|
||||
if (!controllingCharacter) { return null; }
|
||||
#endif
|
||||
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(c => !controllingCharacter || c.CanHearCharacter(Character.Controlled)) ?? Character.Controlled;
|
||||
return GetCharacterForQuickAssignment(order, Character.Controlled, characters);
|
||||
}
|
||||
|
||||
private List<Character> GetCharactersForManualAssignment(Order order)
|
||||
@@ -3203,34 +3421,15 @@ namespace Barotrauma
|
||||
{
|
||||
return characters.Union(GetOrderableFriendlyNPCs()).Where(c => !c.IsDismissed).OrderBy(c => c.Info.DisplayName).ToList();
|
||||
}
|
||||
return GetCharactersSortedForOrder(order, order.Identifier != "follow").ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<Character> GetCharactersSortedForOrder(Order order, bool includeSelf)
|
||||
{
|
||||
return characters.Where(c => Character.Controlled == null || ((includeSelf || c != Character.Controlled) && c.TeamID == Character.Controlled.TeamID)).Union(GetOrderableFriendlyNPCs())
|
||||
// 1. Prioritize those who are on the same submarine than the controlled character
|
||||
.OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine)
|
||||
// 2. Prioritize those who have been given the same maintenance or operate order as now issued
|
||||
.ThenByDescending(c => c.CurrentOrders.Any(o =>
|
||||
o.Order != null && o.Order.Identifier == order.Identifier &&
|
||||
(order.Category == OrderCategory.Maintenance || order.Category == OrderCategory.Operate)))
|
||||
// 3. Prioritize those with the appropriate job for the order
|
||||
.ThenByDescending(c => order.HasAppropriateJob(c))
|
||||
// 4. Prioritize bots over player controlled characters
|
||||
.ThenByDescending(c => c.IsBot)
|
||||
// 5. Use the priority value of the current objective
|
||||
.ThenBy(c => c.AIController is HumanAIController humanAI ? humanAI.ObjectiveManager.CurrentObjective?.Priority : 0)
|
||||
// 6. Prioritize those with the best skill for the order
|
||||
.ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
|
||||
return GetCharactersSortedForOrder(order, characters, Character.Controlled, order.Identifier != "follow", extraCharacters: GetOrderableFriendlyNPCs()).ToList();
|
||||
}
|
||||
|
||||
private IEnumerable<Character> GetOrderableFriendlyNPCs()
|
||||
{
|
||||
// TODO: change this so that we can get the data without having to rely on ui elements.
|
||||
return crewList.Content.Children.Where(c => c.UserData is Character character && character.TeamID == CharacterTeamType.FriendlyNPC).Select(c => (Character)c.UserData);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
@@ -3325,15 +3524,76 @@ namespace Barotrauma
|
||||
public void Save(XElement parentElement)
|
||||
{
|
||||
XElement element = new XElement("crew");
|
||||
|
||||
foreach (CharacterInfo ci in characterInfos)
|
||||
{
|
||||
var infoElement = ci.Save(element);
|
||||
if (ci.InventoryData != null) { infoElement.Add(ci.InventoryData); }
|
||||
if (ci.HealthData != null) { infoElement.Add(ci.HealthData); }
|
||||
if (ci.OrderData != null) { infoElement.Add(ci.OrderData); }
|
||||
if (ci.LastControlled) { infoElement.Add(new XAttribute("lastcontrolled", true)); }
|
||||
}
|
||||
SaveActiveOrders(element);
|
||||
parentElement.Add(element);
|
||||
}
|
||||
|
||||
public static void ClientReadActiveOrders(IReadMessage inc)
|
||||
{
|
||||
ushort count = inc.ReadUInt16();
|
||||
if (count < 1) { return; }
|
||||
var activeOrders = new List<(Order, float?)>();
|
||||
for (ushort i = 0; i < count; i++)
|
||||
{
|
||||
var orderMessageInfo = OrderChatMessage.ReadOrder(inc);
|
||||
Character orderGiver = null;
|
||||
if (inc.ReadBoolean())
|
||||
{
|
||||
ushort orderGiverId = inc.ReadUInt16();
|
||||
orderGiver = orderGiverId != Entity.NullEntityID ? Entity.FindEntityByID(orderGiverId) as Character : null;
|
||||
}
|
||||
if (orderMessageInfo.OrderIndex < 0 || orderMessageInfo.OrderIndex >= Order.PrefabList.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid active order - order index out of bounds.");
|
||||
continue;
|
||||
}
|
||||
Order orderPrefab = orderMessageInfo.OrderPrefab ?? Order.PrefabList[orderMessageInfo.OrderIndex];
|
||||
Order order = orderMessageInfo.TargetType switch
|
||||
{
|
||||
Order.OrderTargetType.Entity =>
|
||||
new Order(orderPrefab, orderMessageInfo.TargetEntity, orderPrefab.GetTargetItemComponent(orderMessageInfo.TargetEntity as Item), orderGiver: orderGiver),
|
||||
Order.OrderTargetType.Position =>
|
||||
new Order(orderPrefab, orderMessageInfo.TargetPosition, orderGiver: orderGiver),
|
||||
Order.OrderTargetType.WallSection =>
|
||||
new Order(orderPrefab, orderMessageInfo.TargetEntity as Structure, orderMessageInfo.WallSectionIndex, orderGiver: orderGiver),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
if (order != null && order.TargetAllCharacters)
|
||||
{
|
||||
var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null;
|
||||
activeOrders.Add((order, fadeOutTime));
|
||||
}
|
||||
}
|
||||
foreach (var (order, fadeOutTime) in activeOrders)
|
||||
{
|
||||
if (order.IsIgnoreOrder)
|
||||
{
|
||||
switch (order.TargetType)
|
||||
{
|
||||
case Order.OrderTargetType.Entity:
|
||||
if (!(order.TargetEntity is IIgnorable ignorableEntity)) { break; }
|
||||
ignorableEntity.OrderedToBeIgnored = order.Identifier == "ignorethis";
|
||||
break;
|
||||
case Order.OrderTargetType.Position:
|
||||
throw new NotImplementedException();
|
||||
case Order.OrderTargetType.WallSection:
|
||||
if (!order.WallSectionIndex.HasValue) { break; }
|
||||
if (!(order.TargetEntity is Structure s)) { break; }
|
||||
if (!(s.GetSection(order.WallSectionIndex.Value) is IIgnorable ignorableWall)) { break; }
|
||||
ignorableWall.OrderedToBeIgnored = order.Identifier == "ignorethis";
|
||||
break;
|
||||
}
|
||||
}
|
||||
GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace Barotrauma
|
||||
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)))
|
||||
(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;
|
||||
@@ -287,6 +287,7 @@ namespace Barotrauma
|
||||
{
|
||||
case InteractionType.None:
|
||||
case InteractionType.Talk:
|
||||
case InteractionType.Examine:
|
||||
return;
|
||||
case InteractionType.Upgrade when !UpgradeManager.CanUpgradeSub():
|
||||
UpgradeManager.CreateUpgradeErrorMessage(TextManager.Get("Dialog.CantUpgrade"), IsSinglePlayer, npc);
|
||||
|
||||
@@ -211,10 +211,7 @@ namespace Barotrauma
|
||||
{
|
||||
Character.Controlled = null;
|
||||
|
||||
if (prevControlled != null)
|
||||
{
|
||||
prevControlled.ClearInputs();
|
||||
}
|
||||
prevControlled?.ClearInputs();
|
||||
|
||||
overlayColor = Color.LightGray;
|
||||
overlaySprite = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
|
||||
@@ -326,7 +323,6 @@ namespace Barotrauma
|
||||
Level prevLevel = Level.Loaded;
|
||||
|
||||
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
|
||||
GUI.SetSavingIndicatorState(success);
|
||||
crewDead = false;
|
||||
|
||||
var continueButton = GameMain.GameSession.RoundSummary?.ContinueButton;
|
||||
@@ -484,6 +480,8 @@ namespace Barotrauma
|
||||
{
|
||||
IsFirstRound = false;
|
||||
CoroutineManager.StartCoroutine(DoLevelTransition(), "LevelTransition");
|
||||
bool success = CrewManager.GetCharacters().Any(c => !c.IsDead);
|
||||
GUI.SetSavingIndicatorState(success && (Level.IsLoadedOutpost || transitionType != TransitionType.None));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,7 +532,13 @@ namespace Barotrauma
|
||||
|
||||
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);
|
||||
|
||||
var selectedMissionIndices = map.GetSelectedMissionIndices();
|
||||
msg.Write((byte)selectedMissionIndices.Count());
|
||||
foreach (int selectedMissionIndex in selectedMissionIndices)
|
||||
{
|
||||
msg.Write((byte)selectedMissionIndex);
|
||||
}
|
||||
msg.Write(PurchasedHullRepairs);
|
||||
msg.Write(PurchasedItemRepairs);
|
||||
msg.Write(PurchasedLostShuttles);
|
||||
@@ -569,6 +573,13 @@ namespace Barotrauma
|
||||
msg.Write(category.Identifier);
|
||||
msg.Write((byte)level);
|
||||
}
|
||||
|
||||
msg.Write((ushort)UpgradeManager.PurchasedItemSwaps.Count);
|
||||
foreach (var itemSwap in UpgradeManager.PurchasedItemSwaps)
|
||||
{
|
||||
msg.Write(itemSwap.ItemToRemove.ID);
|
||||
msg.Write(itemSwap.ItemToInstall?.Identifier ?? string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
//static because we may need to instantiate the campaign if it hasn't been done yet
|
||||
@@ -581,8 +592,15 @@ namespace Barotrauma
|
||||
string mapSeed = msg.ReadString();
|
||||
UInt16 currentLocIndex = msg.ReadUInt16();
|
||||
UInt16 selectedLocIndex = msg.ReadUInt16();
|
||||
byte selectedMissionIndex = msg.ReadByte();
|
||||
bool allowDebugTeleport = msg.ReadBoolean();
|
||||
|
||||
byte selectedMissionCount = msg.ReadByte();
|
||||
List<int> selectedMissionIndices = new List<int>();
|
||||
for (int i = 0; i < selectedMissionCount; i++)
|
||||
{
|
||||
selectedMissionIndices.Add(msg.ReadByte());
|
||||
}
|
||||
|
||||
bool allowDebugTeleport = msg.ReadBoolean();
|
||||
float? reputation = null;
|
||||
if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); }
|
||||
|
||||
@@ -657,6 +675,21 @@ namespace Barotrauma
|
||||
pendingUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel));
|
||||
}
|
||||
|
||||
ushort purchasedItemSwapCount = msg.ReadUInt16();
|
||||
List<PurchasedItemSwap> purchasedItemSwaps = new List<PurchasedItemSwap>();
|
||||
for (int i = 0; i < purchasedItemSwapCount; i++)
|
||||
{
|
||||
UInt16 itemToRemoveID = msg.ReadUInt16();
|
||||
Item itemToRemove = Entity.FindEntityByID(itemToRemoveID) as Item;
|
||||
|
||||
string itemToInstallIdentifier = msg.ReadString();
|
||||
ItemPrefab itemToInstall = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);
|
||||
|
||||
if (itemToRemove == null) { continue; }
|
||||
|
||||
purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
|
||||
}
|
||||
|
||||
bool hasCharacterData = msg.ReadBoolean();
|
||||
CharacterInfo myCharacterInfo = null;
|
||||
if (hasCharacterData)
|
||||
@@ -694,7 +727,7 @@ namespace Barotrauma
|
||||
|
||||
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
|
||||
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
|
||||
campaign.Map.SelectMission(selectedMissionIndex);
|
||||
campaign.Map.SelectMission(selectedMissionIndices);
|
||||
campaign.Map.AllowDebugTeleport = allowDebugTeleport;
|
||||
campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
|
||||
campaign.CargoManager.SetPurchasedItems(purchasedItems);
|
||||
@@ -703,6 +736,26 @@ namespace Barotrauma
|
||||
campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
|
||||
campaign.UpgradeManager.PurchasedUpgrades.Clear();
|
||||
|
||||
campaign.UpgradeManager.PurchasedUpgrades.Clear();
|
||||
foreach (var purchasedItemSwap in purchasedItemSwaps)
|
||||
{
|
||||
if (purchasedItemSwap.ItemToInstall == null)
|
||||
{
|
||||
campaign.UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove, force: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
campaign.UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall, force: true);
|
||||
}
|
||||
}
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
|
||||
{
|
||||
item.PendingItemSwap = null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (identifier, rep) in factionReps)
|
||||
{
|
||||
Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
@@ -101,7 +101,8 @@ namespace Barotrauma
|
||||
case "cargo":
|
||||
CargoManager.LoadPurchasedItems(subElement);
|
||||
break;
|
||||
case "pendingupgrades":
|
||||
case "pendingupgrades": //backwards compatibility
|
||||
case "upgrademanager":
|
||||
UpgradeManager = new UpgradeManager(this, subElement, isSingleplayer: true);
|
||||
break;
|
||||
case "pets":
|
||||
@@ -229,6 +230,7 @@ namespace Barotrauma
|
||||
{
|
||||
PetBehavior.LoadPets(petsElement);
|
||||
}
|
||||
CrewManager.LoadActiveOrders();
|
||||
|
||||
GUI.DisableSavingIndicatorDelayed();
|
||||
}
|
||||
@@ -264,10 +266,7 @@ namespace Barotrauma
|
||||
prevControlled.AIController.Enabled = false;
|
||||
}
|
||||
Character.Controlled = null;
|
||||
if (prevControlled != null)
|
||||
{
|
||||
prevControlled.ClearInputs();
|
||||
}
|
||||
prevControlled?.ClearInputs();
|
||||
|
||||
GUI.DisableHUD = true;
|
||||
while (GameMain.Instance.LoadingScreenOpen)
|
||||
@@ -303,7 +302,7 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
|
||||
timer = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
|
||||
timer = Math.Min(timer + CoroutineManager.UnscaledDeltaTime, textDuration);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
var outpost = GameMain.GameSession.Level.StartOutpost;
|
||||
@@ -331,7 +330,7 @@ namespace Barotrauma
|
||||
while (timer < fadeInDuration)
|
||||
{
|
||||
overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
|
||||
timer += CoroutineManager.DeltaTime;
|
||||
timer += CoroutineManager.UnscaledDeltaTime;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
overlayColor = Color.Transparent;
|
||||
@@ -446,9 +445,13 @@ namespace Barotrauma
|
||||
{
|
||||
Submarine.MainSub = leavingSub;
|
||||
GameMain.GameSession.Submarine = leavingSub;
|
||||
GameMain.GameSession.SubmarineInfo = leavingSub.Info;
|
||||
leavingSub.Info.FilePath = System.IO.Path.Combine(SaveUtil.TempPath, leavingSub.Info.Name + ".sub");
|
||||
var subsToLeaveBehind = GetSubsToLeaveBehind(leavingSub);
|
||||
GameMain.GameSession.OwnedSubmarines.Add(leavingSub.Info);
|
||||
foreach (Submarine sub in subsToLeaveBehind)
|
||||
{
|
||||
GameMain.GameSession.OwnedSubmarines.RemoveAll(s => s != leavingSub.Info && s.Name == sub.Info.Name);
|
||||
MapEntity.mapEntityList.RemoveAll(e => e.Submarine == sub && e is LinkedSubmarine);
|
||||
LinkedSubmarine.CreateDummy(leavingSub, sub);
|
||||
}
|
||||
@@ -480,6 +483,8 @@ namespace Barotrauma
|
||||
EnableRoundSummaryGameOverState();
|
||||
}
|
||||
|
||||
CrewManager?.ClearCurrentOrders();
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
SelectSummaryScreen(roundSummary, newLevel, mirror, () =>
|
||||
@@ -558,7 +563,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.R))
|
||||
if (GUI.KeyboardDispatcher.Subscriber == null && PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.M))
|
||||
{
|
||||
if (GUIMessageBox.MessageBoxes.Any()) { GUIMessageBox.MessageBoxes.Remove(GUIMessageBox.MessageBoxes.Last()); }
|
||||
|
||||
@@ -735,9 +740,10 @@ namespace Barotrauma
|
||||
if (c.Inventory != null)
|
||||
{
|
||||
c.Info.InventoryData = new XElement("inventory");
|
||||
c.SaveInventory(c.Inventory, c.Info.InventoryData);
|
||||
c.SaveInventory();
|
||||
c.Inventory?.DeleteAllItems();
|
||||
}
|
||||
c.Info.SaveOrderData();
|
||||
}
|
||||
|
||||
petsElement = new XElement("pets");
|
||||
@@ -748,7 +754,7 @@ namespace Barotrauma
|
||||
CampaignMetadata.Save(modeElement);
|
||||
Map.Save(modeElement);
|
||||
CargoManager?.SavePurchasedItems(modeElement);
|
||||
UpgradeManager?.SavePendingUpgrades(modeElement, UpgradeManager?.PendingUpgrades);
|
||||
UpgradeManager?.Save(modeElement);
|
||||
element.Add(modeElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,17 +123,17 @@ namespace Barotrauma.Tutorials
|
||||
SetDoorAccess(tutorial_lockedDoor_2, null, false);
|
||||
|
||||
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("mechanic"));
|
||||
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic");
|
||||
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "mechanic");
|
||||
captain_mechanic.TeamID = CharacterTeamType.Team1;
|
||||
captain_mechanic.GiveJobItems();
|
||||
|
||||
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("securityofficer"));
|
||||
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer");
|
||||
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "securityofficer");
|
||||
captain_security.TeamID = CharacterTeamType.Team1;
|
||||
captain_security.GiveJobItems();
|
||||
|
||||
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
|
||||
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer");
|
||||
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "engineer");
|
||||
captain_engineer.TeamID = CharacterTeamType.Team1;
|
||||
captain_engineer.GiveJobItems();
|
||||
|
||||
|
||||
@@ -94,19 +94,19 @@ namespace Barotrauma.Tutorials
|
||||
patient2.AIController.Enabled = false;
|
||||
|
||||
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: JobPrefab.Get("engineer"));
|
||||
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient1.TeamID = CharacterTeamType.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: JobPrefab.Get("securityofficer"));
|
||||
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient2.TeamID = CharacterTeamType.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: JobPrefab.Get("engineer"));
|
||||
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job?.Prefab, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient3.TeamID = CharacterTeamType.Team1;
|
||||
subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false);
|
||||
subPatients.Add(subPatient3);
|
||||
|
||||
@@ -531,7 +531,7 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
}
|
||||
yield return null;
|
||||
} while (!mechanic.HasEquippedItem("divingsuit"));
|
||||
} while (!mechanic.HasEquippedItem("divingsuit", slotType: InvSlotType.OuterClothes));
|
||||
SetHighlight(mechanic_divingSuitContainer.Item, false);
|
||||
RemoveCompletedObjective(segments[8]);
|
||||
SetDoorAccess(tutorial_mechanicFinalDoor, tutorial_mechanicFinalDoorLight, true);
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
}
|
||||
|
||||
return WayPoint.GetRandom(spawnPointType, charInfo.Job, spawnSub);
|
||||
return WayPoint.GetRandom(spawnPointType, charInfo.Job?.Prefab, spawnSub);
|
||||
}
|
||||
|
||||
protected bool HasOrder(Character character, string identifier, string option = null)
|
||||
|
||||
@@ -224,10 +224,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
public virtual void Update(float deltaTime)
|
||||
{
|
||||
if (videoPlayer != null)
|
||||
{
|
||||
videoPlayer.Update();
|
||||
}
|
||||
videoPlayer?.Update();
|
||||
|
||||
if (activeObjectives != null)
|
||||
{
|
||||
|
||||
@@ -524,7 +524,7 @@ namespace Barotrauma
|
||||
private static void CheckIfDivingGearOutOfOxygen()
|
||||
{
|
||||
if (!CanDisplayHints()) { return; }
|
||||
var divingGear = Character.Controlled.GetEquippedItem("diving");
|
||||
var divingGear = Character.Controlled.GetEquippedItem("diving", InvSlotType.OuterClothes);
|
||||
if (divingGear?.OwnInventory == null) { return; }
|
||||
if (divingGear.GetContainedItemConditionPercentage() > 0.0f) { return; }
|
||||
DisplayHint("ondivinggearoutofoxygen", onUpdate: () =>
|
||||
|
||||
@@ -218,12 +218,15 @@ namespace Barotrauma
|
||||
};
|
||||
|
||||
List<Mission> missionsToDisplay = new List<Mission>(selectedMissions);
|
||||
if (!selectedMissions.Any() && startLocation?.SelectedMission != null)
|
||||
{
|
||||
if (startLocation.SelectedMission.Locations[0] == startLocation.SelectedMission.Locations[1] ||
|
||||
startLocation.SelectedMission.Locations.Contains(campaignMode?.Map.SelectedLocation))
|
||||
if (!selectedMissions.Any() && startLocation != null)
|
||||
{
|
||||
foreach (Mission mission in startLocation.SelectedMissions)
|
||||
{
|
||||
missionsToDisplay.Add(startLocation.SelectedMission);
|
||||
if (mission.Locations[0] == mission.Locations[1] ||
|
||||
mission.Locations.Contains(campaignMode?.Map.SelectedLocation))
|
||||
{
|
||||
missionsToDisplay.Add(mission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,10 +287,11 @@ namespace Barotrauma
|
||||
new GUIImage(new RectTransform(Vector2.One, missionIcon.RectTransform), displayedMission.Completed ? "MissionCompletedIcon" : "MissionFailedIcon", scaleToFit: true);
|
||||
}
|
||||
|
||||
var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 0.8f), missionContentHorizontal.RectTransform))
|
||||
var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), missionContentHorizontal.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
};
|
||||
missionContentHorizontal.Recalculate();
|
||||
var missionNameTextBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
displayedMission.Name, font: GUI.SubHeadingFont);
|
||||
if (displayedMission.Difficulty.HasValue)
|
||||
@@ -309,12 +313,13 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
|
||||
missionMessage, wrap: true, parseRichText: true);
|
||||
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && displayedMission.Reward > 0)
|
||||
int reward = displayedMission.GetReward(Submarine.MainSub);
|
||||
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && reward > 0)
|
||||
{
|
||||
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), displayedMission.GetMissionRewardText(), parseRichText: true);
|
||||
string rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", reward));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), displayedMission.GetMissionRewardText(Submarine.MainSub), parseRichText: true);
|
||||
}
|
||||
|
||||
if (displayedMission != missionsToDisplay.Last())
|
||||
@@ -322,6 +327,13 @@ namespace Barotrauma
|
||||
var spacing = new GUIFrame(new RectTransform(new Vector2(1.0f, 1.0f), missionList.Content.RectTransform) { MaxSize = new Point(int.MaxValue, GUI.IntScale(15)) }, style: null);
|
||||
new GUIFrame(new RectTransform(new Vector2(0.8f, 1.0f), spacing.RectTransform, Anchor.Center) { RelativeOffset = new Vector2(0.1f, 0.0f) }, "HorizontalLine");
|
||||
}
|
||||
|
||||
foreach (GUIComponent child in missionTextContent.Children)
|
||||
{
|
||||
child.RectTransform.IsFixedSize = true;
|
||||
}
|
||||
missionTextContent.RectTransform.MinSize = new Point(0, missionTextContent.Children.Sum(c => c.Rect.Height + missionTextContent.AbsoluteSpacing));
|
||||
missionContentHorizontal.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Rect.Height / missionTextContent.RectTransform.RelativeSize.Y));
|
||||
}
|
||||
|
||||
if (!missionsToDisplay.Any())
|
||||
|
||||
@@ -39,43 +39,5 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1077,8 +1077,8 @@ namespace Barotrauma
|
||||
new RectTransform(new Vector2(0.5f, 1.0f), voiceInputContainerHorizontal.RectTransform),
|
||||
isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), voiceInputContainer.RectTransform), TextManager.Get("InputType.Voice"), font: GUI.SubHeadingFont);
|
||||
var voiceKeyBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), voiceInputContainer.RectTransform, Anchor.TopRight), text: KeyBindText(InputType.Voice))
|
||||
var voiceKeybindLabel = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), voiceInputContainer.RectTransform), TextManager.Get("InputType.Voice"), font: GUI.SubHeadingFont);
|
||||
var voiceKeyBox = new GUITextBox(new RectTransform(new Vector2(0.3f, 1.0f), voiceInputContainer.RectTransform, Anchor.TopRight), text: KeyBindText(InputType.Voice))
|
||||
{
|
||||
SelectedColor = Color.Gold * 0.3f,
|
||||
UserData = InputType.Voice
|
||||
@@ -1089,14 +1089,16 @@ namespace Barotrauma
|
||||
new RectTransform(new Vector2(0.5f, 1.0f), voiceInputContainerHorizontal.RectTransform),
|
||||
isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), localVoiceInputContainer.RectTransform), TextManager.Get("InputType.LocalVoice"), font: GUI.SubHeadingFont);
|
||||
var localVoiceKeyBox = new GUITextBox(new RectTransform(new Vector2(0.4f, 1.0f), localVoiceInputContainer.RectTransform, Anchor.TopRight), text: KeyBindText(InputType.LocalVoice))
|
||||
var localVoiceKeybindLabel = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), localVoiceInputContainer.RectTransform), TextManager.Get("InputType.LocalVoice"), font: GUI.SubHeadingFont);
|
||||
var localVoiceKeyBox = new GUITextBox(new RectTransform(new Vector2(0.3f, 1.0f), localVoiceInputContainer.RectTransform, Anchor.TopRight), text: KeyBindText(InputType.LocalVoice))
|
||||
{
|
||||
SelectedColor = Color.Gold * 0.3f,
|
||||
UserData = InputType.LocalVoice
|
||||
};
|
||||
localVoiceKeyBox.OnSelected += KeyBoxSelected;
|
||||
|
||||
voiceKeybindLabel.RectTransform.SizeChanged += () => { GUITextBlock.AutoScaleAndNormalize(voiceKeybindLabel, localVoiceKeybindLabel); };
|
||||
|
||||
var cutoffPreventionText = new GUITextBlock(new RectTransform(textBlockScale, voiceChatContent.RectTransform), TextManager.Get("CutoffPrevention"), font: GUI.SubHeadingFont)
|
||||
{
|
||||
ToolTip = TextManager.Get("CutoffPreventionTooltip")
|
||||
@@ -1326,24 +1328,41 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
|
||||
GUITickBox disableInGameHintsBox = new GUITickBox(new RectTransform(tickBoxScale, gameplaySettingsGroup.RectTransform),
|
||||
TextManager.Get("DisableInGameHints"))
|
||||
new GUITickBox(new RectTransform(tickBoxScale, gameplaySettingsGroup.RectTransform), TextManager.Get("DisableInGameHints"))
|
||||
{
|
||||
Selected = DisableInGameHints,
|
||||
ToolTip = TextManager.Get("DisableInGameHintsToolTip"),
|
||||
OnSelected = (tickBox) =>
|
||||
{
|
||||
DisableInGameHints = tickBox.Selected;
|
||||
if (!DisableInGameHints && GameMain.Config?.IgnoredHints != null)
|
||||
{
|
||||
// Reset the ignored hints when the hints are re-enabled (to-be-replaced by a separate button)
|
||||
GameMain.Config.IgnoredHints.Clear();
|
||||
}
|
||||
UnsavedSettings = true;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform),
|
||||
text: TextManager.Get("ResetInGameHints"),
|
||||
style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ResetInGameHints"),
|
||||
TextManager.Get("ResetInGameHintsTooltip"),
|
||||
new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") })
|
||||
{
|
||||
UserData = "verificationprompt"
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked = (button, userData) =>
|
||||
{
|
||||
GameMain.Config.IgnoredHints.Clear();
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
msgBox.Buttons[1].OnClicked = msgBox.Close;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
GUITextBlock HUDScaleText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform), TextManager.Get("HUDScale"), font: GUI.SubHeadingFont, wrap: true);
|
||||
GUIScrollBar HUDScaleScrollBar = new GUIScrollBar(new RectTransform(new Vector2(1.0f, 0.05f), gameplaySettingsGroup.RectTransform),
|
||||
style: "GUISlider", barSize: 0.1f)
|
||||
|
||||
@@ -297,11 +297,11 @@ namespace Barotrauma
|
||||
SlotSize = !isFourByThree ? (SlotSpriteSmall.size * UIScale).ToPoint() : (SlotSpriteSmall.size * UIScale * .925f).ToPoint();
|
||||
int bottomOffset = SlotSize.Y + Spacing * 2 + ContainedIndicatorHeight;
|
||||
|
||||
hideButton.Visible = false;
|
||||
|
||||
if (visualSlots == null) { CreateSlots(); }
|
||||
if (visualSlots.None()) { return; }
|
||||
|
||||
hideButton.Visible = false;
|
||||
|
||||
switch (layout)
|
||||
{
|
||||
case Layout.Default:
|
||||
@@ -537,9 +537,12 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
List<SlotReference> hideSubInventories = new List<SlotReference>();
|
||||
//remove highlighted subinventory slots that can no longer be accessed
|
||||
highlightedSubInventorySlots.RemoveWhere(s =>
|
||||
s.ParentInventory == this &&
|
||||
((s.SlotIndex < 0 || s.SlotIndex >= slots.Length || slots[s.SlotIndex] == null) || (Character.Controlled != null && !Character.Controlled.CanAccessInventory(s.Inventory))));
|
||||
//remove highlighted subinventory slots that refer to items no longer in this inventory
|
||||
highlightedSubInventorySlots.RemoveWhere(s => s.Item != null && s.ParentInventory == this && s.Item.ParentInventory != this);
|
||||
foreach (var highlightedSubInventorySlot in highlightedSubInventorySlots)
|
||||
{
|
||||
if (highlightedSubInventorySlot.ParentInventory == this)
|
||||
@@ -910,7 +913,7 @@ namespace Barotrauma
|
||||
else if (allowEquip) //doubleclicked and no other inventory is selected
|
||||
{
|
||||
//not equipped -> attempt to equip
|
||||
if (!character.HasEquippedItem(item))
|
||||
if (!character.HasEquippedItem(item) || item.GetComponents<Pickable>().Count() > 1)
|
||||
{
|
||||
return QuickUseAction.Equip;
|
||||
}
|
||||
@@ -1100,6 +1103,7 @@ namespace Barotrauma
|
||||
public void DrawOwn(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!AccessibleWhenAlive && !character.IsDead) { return; }
|
||||
if (capacity == 0) { return; }
|
||||
if (visualSlots == null) { CreateSlots(); }
|
||||
if (GameMain.GraphicsWidth != screenResolution.X ||
|
||||
GameMain.GraphicsHeight != screenResolution.Y ||
|
||||
|
||||
@@ -281,10 +281,10 @@ namespace Barotrauma.Items.Components
|
||||
foreach (ParticleEmitter particleEmitter in particleEmitters)
|
||||
{
|
||||
float particleAngle = item.body.Rotation + ((item.body.Dir > 0.0f) ? 0.0f : MathHelper.Pi);
|
||||
float particleRange = particleEmitter.Prefab.VelocityMax * particleEmitter.Prefab.ParticlePrefab.LifeTime;
|
||||
float particleRange = particleEmitter.Prefab.Properties.VelocityMax * particleEmitter.Prefab.ParticlePrefab.LifeTime;
|
||||
particleEmitter.Emit(
|
||||
deltaTime, particleStartPos,
|
||||
item.CurrentHull, particleAngle, particleEmitter.Prefab.CopyEntityAngle ? -particleAngle : 0, velocityMultiplier: dist / particleRange * 1.5f,
|
||||
item.CurrentHull, particleAngle, particleEmitter.Prefab.Properties.CopyEntityAngle ? -particleAngle : 0, velocityMultiplier: dist / particleRange * 1.5f,
|
||||
colorMultiplier: new Color(color.R, color.G, color.B, (byte)255));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,5 +611,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
OnResolutionChanged();
|
||||
}
|
||||
public virtual void AddTooltipInfo(ref string description) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private GUICustomComponent guiCustomComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Can be used to set the sprite depth individually for each contained item
|
||||
/// </summary>
|
||||
private float[] containedSpriteDepths;
|
||||
|
||||
public Sprite InventoryTopSprite
|
||||
{
|
||||
get { return inventoryTopSprite; }
|
||||
@@ -153,6 +158,8 @@ namespace Barotrauma.Items.Components
|
||||
//if a GUIFrame has been defined, draw the inventory inside it
|
||||
CreateGUI();
|
||||
}
|
||||
|
||||
containedSpriteDepths = element.GetAttributeFloatArray("containedspritedepths", new float[0]);
|
||||
}
|
||||
|
||||
protected override void CreateGUI()
|
||||
@@ -316,6 +323,10 @@ namespace Barotrauma.Items.Components
|
||||
if (item.FlippedY) { origin.Y = containedItem.Sprite.SourceRect.Height - origin.Y; }
|
||||
|
||||
float containedSpriteDepth = ContainedSpriteDepth < 0.0f ? containedItem.Sprite.Depth : ContainedSpriteDepth;
|
||||
if (i < containedSpriteDepths.Length)
|
||||
{
|
||||
containedSpriteDepth = containedSpriteDepths[i];
|
||||
}
|
||||
containedSpriteDepth = itemDepth + (containedSpriteDepth - (item.Sprite?.Depth ?? item.SpriteDepth)) / 10000.0f;
|
||||
|
||||
containedItem.Sprite.Draw(
|
||||
|
||||
@@ -16,44 +16,43 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public Vector2 DrawSize
|
||||
{
|
||||
get { return new Vector2(light.Range * 2, light.Range * 2); }
|
||||
get { return new Vector2(Light.Range * 2, Light.Range * 2); }
|
||||
}
|
||||
|
||||
private LightSource light;
|
||||
public LightSource Light
|
||||
{
|
||||
get { return light; }
|
||||
}
|
||||
public LightSource Light { get; }
|
||||
|
||||
public override void OnScaleChanged()
|
||||
{
|
||||
light.SpriteScale = Vector2.One * item.Scale;
|
||||
light.Position = ParentBody != null ? ParentBody.Position : item.Position;
|
||||
Light.SpriteScale = Vector2.One * item.Scale;
|
||||
Light.Position = ParentBody != null ? ParentBody.Position : item.Position;
|
||||
}
|
||||
|
||||
partial void SetLightSourceState(bool enabled, float brightness)
|
||||
{
|
||||
if (light == null) { return; }
|
||||
light.Enabled = enabled;
|
||||
light.Color = LightColor.Multiply(brightness);
|
||||
if (Light == null) { return; }
|
||||
Light.Enabled = enabled;
|
||||
if (enabled)
|
||||
{
|
||||
Light.Color = LightColor.Multiply(brightness);
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
|
||||
{
|
||||
if (light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn)
|
||||
if (Light.LightSprite != null && (item.body == null || item.body.Enabled) && lightBrightness > 0.0f && IsOn)
|
||||
{
|
||||
Vector2 origin = light.LightSprite.Origin;
|
||||
if ((light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = light.LightSprite.SourceRect.Width - origin.X; }
|
||||
if ((light.LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y = light.LightSprite.SourceRect.Height - origin.Y; }
|
||||
light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, origin, -light.Rotation, item.Scale, light.LightSpriteEffect, itemDepth - 0.0001f);
|
||||
Vector2 origin = Light.LightSprite.Origin;
|
||||
if ((Light.LightSpriteEffect & SpriteEffects.FlipHorizontally) == SpriteEffects.FlipHorizontally) { origin.X = Light.LightSprite.SourceRect.Width - origin.X; }
|
||||
if ((Light.LightSpriteEffect & SpriteEffects.FlipVertically) == SpriteEffects.FlipVertically) { origin.Y = Light.LightSprite.SourceRect.Height - origin.Y; }
|
||||
Light.LightSprite.Draw(spriteBatch, new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), lightColor * lightBrightness, origin, -Light.Rotation, item.Scale, Light.LightSpriteEffect, itemDepth - 0.0001f);
|
||||
}
|
||||
}
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
if (light?.LightSprite != null && item.Prefab.CanSpriteFlipX && item.body == null)
|
||||
if (Light?.LightSprite != null && item.Prefab.CanSpriteFlipX && item.body == null)
|
||||
{
|
||||
light.LightSpriteEffect = light.LightSpriteEffect == SpriteEffects.None ?
|
||||
Light.LightSpriteEffect = Light.LightSpriteEffect == SpriteEffects.None ?
|
||||
SpriteEffects.FlipHorizontally : SpriteEffects.None;
|
||||
}
|
||||
}
|
||||
@@ -93,7 +92,7 @@ namespace Barotrauma.Items.Components
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
light.Remove();
|
||||
Light.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,26 +178,26 @@ namespace Barotrauma.Items.Components
|
||||
ToolTip = fi.TargetItem.Description
|
||||
};
|
||||
|
||||
var container = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform),
|
||||
childAnchor: Anchor.CenterLeft, isHorizontal: true) { RelativeSpacing = 0.02f };
|
||||
var container = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform),
|
||||
childAnchor: Anchor.CenterLeft, isHorizontal: true) { RelativeSpacing = 0.02f };
|
||||
|
||||
var itemIcon = fi.TargetItem.InventoryIcon ?? fi.TargetItem.sprite;
|
||||
if (itemIcon != null)
|
||||
{
|
||||
new GUIImage(new RectTransform(new Point(frame.Rect.Height,frame.Rect.Height), container.RectTransform),
|
||||
itemIcon, scaleToFit: true)
|
||||
{
|
||||
Color = fi.TargetItem.InventoryIconColor,
|
||||
ToolTip = fi.TargetItem.Description
|
||||
};
|
||||
}
|
||||
var itemIcon = fi.TargetItem.InventoryIcon ?? fi.TargetItem.sprite;
|
||||
if (itemIcon != null)
|
||||
{
|
||||
new GUIImage(new RectTransform(new Point(frame.Rect.Height,frame.Rect.Height), container.RectTransform),
|
||||
itemIcon, scaleToFit: true)
|
||||
{
|
||||
Color = fi.TargetItem.InventoryIconColor,
|
||||
ToolTip = fi.TargetItem.Description
|
||||
};
|
||||
}
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), container.RectTransform), GetRecipeNameAndAmount(fi))
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
AutoScaleVertical = true,
|
||||
ToolTip = fi.TargetItem.Description
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.85f, 1f), container.RectTransform), GetRecipeNameAndAmount(fi))
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
AutoScaleVertical = true,
|
||||
ToolTip = fi.TargetItem.Description
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,6 +367,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
toolTipText += " " + (int)Math.Round(requiredItem.MinCondition * 100) + "%";
|
||||
}
|
||||
else if (requiredItem.MaxCondition <= 0.0f)
|
||||
{
|
||||
toolTipText = TextManager.GetWithVariable("displayname.emptyitem", "[itemname]", toolTipText);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(requiredItem.ItemPrefabs.First().Description))
|
||||
{
|
||||
toolTipText += '\n' + requiredItem.ItemPrefabs.First().Description;
|
||||
|
||||
@@ -199,13 +199,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
Color neutralColor = Color.DarkCyan;
|
||||
if (hull.RoomName != null)
|
||||
if (hull.IsWetRoom)
|
||||
{
|
||||
if (hull.RoomName.Contains("ballast") || hull.RoomName.Contains("Ballast") ||
|
||||
hull.RoomName.Contains("airlock") || hull.RoomName.Contains("Airlock"))
|
||||
{
|
||||
neutralColor = new Color(9, 80, 159);
|
||||
}
|
||||
neutralColor = new Color(9, 80, 159);
|
||||
}
|
||||
|
||||
if (hullData.Distort)
|
||||
|
||||
@@ -33,10 +33,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
if (selectionUI != null)
|
||||
{
|
||||
selectionUI.Update();
|
||||
}
|
||||
selectionUI?.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@ namespace Barotrauma.Items.Components
|
||||
private GUITickBox powerLight;
|
||||
private GUITickBox autoControlIndicator;
|
||||
|
||||
private List<Pair<Vector2, ParticleEmitter>> pumpOutEmitters = new List<Pair<Vector2, ParticleEmitter>>();
|
||||
private List<Pair<Vector2, ParticleEmitter>> pumpInEmitters = new List<Pair<Vector2, ParticleEmitter>>();
|
||||
private readonly List<(Vector2 position, ParticleEmitter emitter)> pumpOutEmitters = new List<(Vector2 position, ParticleEmitter emitter)>();
|
||||
private readonly List<(Vector2 position, ParticleEmitter emitter)> pumpInEmitters = new List<(Vector2 position, ParticleEmitter emitter)>();
|
||||
|
||||
public float CurrentBrokenVolume
|
||||
{
|
||||
@@ -35,14 +35,10 @@ namespace Barotrauma.Items.Components
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "pumpoutemitter":
|
||||
pumpOutEmitters.Add(new Pair<Vector2, ParticleEmitter>(
|
||||
subElement.GetAttributeVector2("position", Vector2.Zero),
|
||||
new ParticleEmitter(subElement)));
|
||||
pumpOutEmitters.Add((subElement.GetAttributeVector2("position", Vector2.Zero), new ParticleEmitter(subElement)));
|
||||
break;
|
||||
case "pumpinemitter":
|
||||
pumpInEmitters.Add(new Pair<Vector2, ParticleEmitter>(
|
||||
subElement.GetAttributeVector2("position", Vector2.Zero),
|
||||
new ParticleEmitter(subElement)));
|
||||
pumpInEmitters.Add((subElement.GetAttributeVector2("position", Vector2.Zero), new ParticleEmitter(subElement)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -148,21 +144,43 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (FlowPercentage < 0.0f)
|
||||
{
|
||||
foreach (Pair<Vector2, ParticleEmitter> pumpOutEmitter in pumpOutEmitters)
|
||||
foreach (var (position, emitter) in pumpOutEmitters)
|
||||
{
|
||||
//only emit "pump out" particles when underwater
|
||||
Vector2 particlePos = item.Rect.Location.ToVector2() + pumpOutEmitter.First;
|
||||
if (item.CurrentHull != null && item.CurrentHull.Surface < particlePos.Y) continue;
|
||||
if (item.CurrentHull != null && item.CurrentHull.Surface < item.Rect.Location.Y + position.Y) { continue; }
|
||||
|
||||
pumpOutEmitter.Second.Emit(deltaTime, item.WorldRect.Location.ToVector2() + pumpOutEmitter.First * item.Scale, item.CurrentHull,
|
||||
//only emit "pump out" particles when underwater
|
||||
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
|
||||
float angle = 0.0f;
|
||||
if (item.FlippedX)
|
||||
{
|
||||
relativeParticlePos.X = -relativeParticlePos.X;
|
||||
angle = MathHelper.Pi;
|
||||
}
|
||||
if (item.FlippedY)
|
||||
{
|
||||
relativeParticlePos.Y = -relativeParticlePos.Y;
|
||||
}
|
||||
|
||||
emitter.Emit(deltaTime, item.WorldPosition + relativeParticlePos, item.CurrentHull, angle,
|
||||
velocityMultiplier: MathHelper.Lerp(0.5f, 1.0f, -FlowPercentage / 100.0f));
|
||||
}
|
||||
}
|
||||
else if (FlowPercentage > 0.0f)
|
||||
{
|
||||
foreach (Pair<Vector2, ParticleEmitter> pumpInEmitter in pumpInEmitters)
|
||||
foreach (var (position, emitter) in pumpInEmitters)
|
||||
{
|
||||
pumpInEmitter.Second.Emit(deltaTime, item.WorldRect.Location.ToVector2() + pumpInEmitter.First * item.Scale, item.CurrentHull,
|
||||
Vector2 relativeParticlePos = (item.WorldRect.Location.ToVector2() + position * item.Scale) - item.WorldPosition;
|
||||
float angle = 0.0f;
|
||||
if (item.FlippedX)
|
||||
{
|
||||
relativeParticlePos.X = -relativeParticlePos.X;
|
||||
angle = MathHelper.Pi;
|
||||
}
|
||||
if (item.FlippedY)
|
||||
{
|
||||
relativeParticlePos.Y = -relativeParticlePos.Y;
|
||||
}
|
||||
emitter.Emit(deltaTime, item.WorldPosition + relativeParticlePos, item.CurrentHull, angle,
|
||||
velocityMultiplier: MathHelper.Lerp(0.5f, 1.0f, FlowPercentage / 100.0f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private GUIFrame inventoryContainer;
|
||||
|
||||
private GUILayoutGroup paddedFrame;
|
||||
|
||||
private readonly Dictionary<string, GUIButton> warningButtons = new Dictionary<string, GUIButton>();
|
||||
|
||||
private static readonly string[] warningTexts = new string[]
|
||||
@@ -74,7 +76,7 @@ namespace Barotrauma.Items.Components
|
||||
tempRangeIndicator = new Sprite(element.GetChildElement("temprangeindicator")?.GetChildElement("sprite"));
|
||||
graphLine = new Sprite(element.GetChildElement("graphline")?.GetChildElement("sprite"));
|
||||
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(
|
||||
paddedFrame = new GUILayoutGroup(new RectTransform(
|
||||
GuiFrame.Rect.Size - GUIStyle.ItemFrameMargin, GuiFrame.RectTransform, Anchor.Center)
|
||||
{ AbsoluteOffset = GUIStyle.ItemFrameOffset },
|
||||
isHorizontal: true)
|
||||
@@ -128,27 +130,27 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
|
||||
Point maxIndicatorSize = new Point(int.MaxValue, (int)(40 * GUI.Scale));
|
||||
criticalHeatWarning = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
|
||||
criticalHeatWarning = new GUITickBox(new RectTransform(new Vector2(0.3f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
|
||||
TextManager.Get("ReactorWarningCriticalTemp"), font: GUI.SubHeadingFont, style: "IndicatorLightRed")
|
||||
{
|
||||
Selected = false,
|
||||
Enabled = false,
|
||||
ToolTip = TextManager.Get("ReactorHeatTip")
|
||||
};
|
||||
lowTemperatureWarning = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
|
||||
TextManager.Get("ReactorWarningCriticalLowTemp"), font: GUI.SubHeadingFont, style: "IndicatorLightRed")
|
||||
{
|
||||
Selected = false,
|
||||
Enabled = false,
|
||||
ToolTip = TextManager.Get("ReactorTempTip")
|
||||
};
|
||||
criticalOutputWarning = new GUITickBox(new RectTransform(new Vector2(0.33f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
|
||||
criticalOutputWarning = new GUITickBox(new RectTransform(new Vector2(0.3f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
|
||||
TextManager.Get("ReactorWarningCriticalOutput"), font: GUI.SubHeadingFont, style: "IndicatorLightRed")
|
||||
{
|
||||
Selected = false,
|
||||
Enabled = false,
|
||||
ToolTip = TextManager.Get("ReactorOutputTip")
|
||||
};
|
||||
lowTemperatureWarning = new GUITickBox(new RectTransform(new Vector2(0.4f, 1.0f), topLeftArea.RectTransform) { MaxSize = maxIndicatorSize },
|
||||
TextManager.Get("ReactorWarningCriticalLowTemp"), font: GUI.SubHeadingFont, style: "IndicatorLightRed")
|
||||
{
|
||||
Selected = false,
|
||||
Enabled = false,
|
||||
ToolTip = TextManager.Get("ReactorTempTip")
|
||||
};
|
||||
List<GUITickBox> indicatorLights = new List<GUITickBox>() { criticalHeatWarning, lowTemperatureWarning, criticalOutputWarning };
|
||||
indicatorLights.ForEach(l => l.TextBlock.OverrideTextColor(GUI.Style.TextColor));
|
||||
topLeftArea.Recalculate();
|
||||
@@ -334,8 +336,9 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
|
||||
topRightArea.Recalculate();
|
||||
autoTempLight.TextBlock.Wrap = true;
|
||||
indicatorLights.Add(autoTempLight);
|
||||
autoTempLight.TextBlock.Padding = new Vector4(autoTempLight.TextBlock.Padding.X, 0.0f, 0.0f, 0.0f);
|
||||
autoTempLight.TextBlock.Text = autoTempLight.TextBlock.Text.Replace(' ', '\n');
|
||||
autoTempLight.TextBlock.AutoScaleHorizontal = true;
|
||||
GUITextBlock.AutoScaleAndNormalize(indicatorLights.Select(l => l.TextBlock));
|
||||
|
||||
// right bottom (graph area) -----------------------
|
||||
@@ -533,8 +536,7 @@ namespace Barotrauma.Items.Components
|
||||
warningButtons["ReactorWarningMeltdown"].Selected = meltDownTimer > MeltdownDelay * 0.5f || item.Condition == 0.0f && lightOn;
|
||||
warningButtons["ReactorWarningSCRAM"].Selected = temperature > 0.1f && !PowerOn;
|
||||
|
||||
if ((FissionRateScrollBar.Rect.Contains(PlayerInput.MousePosition) || FissionRateScrollBar.Children.Contains(GUIScrollBar.DraggingBar) ||
|
||||
TurbineOutputScrollBar.Rect.Contains(PlayerInput.MousePosition) || TurbineOutputScrollBar.Children.Contains(GUIScrollBar.DraggingBar)) &&
|
||||
if (paddedFrame.Rect.Contains(PlayerInput.MousePosition) &&
|
||||
!PlayerInput.KeyDown(InputType.Deselect) && !PlayerInput.KeyHit(InputType.Deselect))
|
||||
{
|
||||
Character.DisableControls = true;
|
||||
|
||||
@@ -87,21 +87,6 @@ namespace Barotrauma.Items.Components
|
||||
//float = strength of the disruption, between 0-1
|
||||
private readonly List<Pair<Vector2, float>> disruptedDirections = new List<Pair<Vector2, float>>();
|
||||
|
||||
class CachedDistance
|
||||
{
|
||||
public readonly Vector2 TransducerWorldPos;
|
||||
public readonly Vector2 WorldPos;
|
||||
public readonly float Distance;
|
||||
public double RecalculationTime;
|
||||
|
||||
public CachedDistance(Vector2 transducerWorldPos, Vector2 worldPos, float dist)
|
||||
{
|
||||
TransducerWorldPos = transducerWorldPos;
|
||||
WorldPos = worldPos;
|
||||
Distance = dist;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<object, CachedDistance> markerDistances = new Dictionary<object, CachedDistance>();
|
||||
|
||||
private readonly Color positiveColor = Color.Green;
|
||||
@@ -451,7 +436,9 @@ namespace Barotrauma.Items.Components
|
||||
connectedSubUpdateTimer = ConnectedSubUpdateInterval;
|
||||
}
|
||||
|
||||
if (sonarView.Rect.Contains(PlayerInput.MousePosition))
|
||||
Steering steering = item.GetComponent<Steering>();
|
||||
if (sonarView.Rect.Contains(PlayerInput.MousePosition) &&
|
||||
(GUI.MouseOn == null || GUI.MouseOn == sonarView || sonarView.IsParentOf(GUI.MouseOn) || GUI.MouseOn == steering?.GuiFrame || (steering?.GuiFrame?.IsParentOf(GUI.MouseOn) ?? false)))
|
||||
{
|
||||
float scrollSpeed = PlayerInput.ScrollWheelSpeed / 1000.0f;
|
||||
if (Math.Abs(scrollSpeed) > 0.0001f)
|
||||
@@ -459,8 +446,13 @@ namespace Barotrauma.Items.Components
|
||||
zoomSlider.BarScroll += PlayerInput.ScrollWheelSpeed / 1000.0f;
|
||||
zoomSlider.OnMoved(zoomSlider, zoomSlider.BarScroll);
|
||||
}
|
||||
|
||||
if (PlayerInput.KeyHit(InputType.Run))
|
||||
{
|
||||
SonarModeSwitch.OnClicked(SonarModeSwitch, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float distort = 1.0f - item.Condition / item.MaxCondition;
|
||||
for (int i = sonarBlips.Count - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -623,7 +615,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
Steering steering = item.GetComponent<Steering>();
|
||||
if (steering != null && steering.DockingModeEnabled && steering.ActiveDockingSource != null)
|
||||
{
|
||||
float dockingDist = Vector2.Distance(steering.ActiveDockingSource.Item.WorldPosition, steering.DockingTarget.Item.WorldPosition);
|
||||
@@ -790,10 +781,7 @@ namespace Barotrauma.Items.Components
|
||||
DisplayRadius = (rect.Width / 2.0f) * (1.0f - displayBorderSize);
|
||||
displayScale = DisplayRadius / range * zoom;
|
||||
|
||||
if (screenBackground != null)
|
||||
{
|
||||
screenBackground.Draw(spriteBatch, center, 0.0f, rect.Width / screenBackground.size.X);
|
||||
}
|
||||
screenBackground?.Draw(spriteBatch, center, 0.0f, rect.Width / screenBackground.size.X);
|
||||
|
||||
if (useDirectionalPing)
|
||||
{
|
||||
@@ -881,10 +869,7 @@ namespace Barotrauma.Items.Components
|
||||
GUI.DrawString(spriteBatch, rect.Location.ToVector2(), sonarBlips.Count.ToString(), Color.White);
|
||||
}
|
||||
|
||||
if (screenOverlay != null)
|
||||
{
|
||||
screenOverlay.Draw(spriteBatch, center, 0.0f, rect.Width / screenOverlay.size.X);
|
||||
}
|
||||
screenOverlay?.Draw(spriteBatch, center, 0.0f, rect.Width / screenOverlay.size.X);
|
||||
|
||||
if (signalStrength <= 0.5f)
|
||||
{
|
||||
@@ -947,21 +932,25 @@ namespace Barotrauma.Items.Components
|
||||
cave.StartPos.ToVector2(), transducerCenter,
|
||||
displayScale, center, DisplayRadius);
|
||||
}
|
||||
|
||||
|
||||
int missionIndex = 0;
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(mission.SonarLabel))
|
||||
{
|
||||
int i = 0;
|
||||
foreach (Vector2 sonarPosition in mission.SonarPositions)
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
mission.SonarLabel,
|
||||
mission.SonarIconIdentifier,
|
||||
mission,
|
||||
sonarPosition, transducerCenter,
|
||||
"mission" + missionIndex + ":" + i,
|
||||
sonarPosition, transducerCenter,
|
||||
displayScale, center, DisplayRadius * 0.95f);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
missionIndex++;
|
||||
}
|
||||
|
||||
if (AllowUsingMineralScanner && useMineralScanner && CurrentMode == Mode.Active && MineralClusters != null)
|
||||
@@ -974,7 +963,7 @@ namespace Barotrauma.Items.Components
|
||||
var i = unobtainedMinerals.FirstOrDefault();
|
||||
if (i == null) { continue; }
|
||||
DrawMarker(spriteBatch,
|
||||
i.Name, "mineral", i,
|
||||
i.Name, "mineral", "mineralcluster" + i,
|
||||
c.center, transducerCenter,
|
||||
displayScale, center, DisplayRadius * 0.95f,
|
||||
onlyShowTextOnMouseOver: true);
|
||||
@@ -987,14 +976,14 @@ namespace Barotrauma.Items.Components
|
||||
if (connectedSubs.Contains(sub)) { continue; }
|
||||
if (sub.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
|
||||
if (item.Submarine != null)
|
||||
if (item.Submarine != null || Character.Controlled != null)
|
||||
{
|
||||
//hide enemy team
|
||||
if (sub.TeamID == CharacterTeamType.Team1 && (item.Submarine.TeamID == CharacterTeamType.Team2 || Character.Controlled?.TeamID == CharacterTeamType.Team2))
|
||||
if (sub.TeamID == CharacterTeamType.Team1 && (item.Submarine?.TeamID == CharacterTeamType.Team2 || Character.Controlled?.TeamID == CharacterTeamType.Team2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (sub.TeamID == CharacterTeamType.Team2 && (item.Submarine.TeamID == CharacterTeamType.Team1 || Character.Controlled?.TeamID == CharacterTeamType.Team1))
|
||||
else if (sub.TeamID == CharacterTeamType.Team2 && (item.Submarine?.TeamID == CharacterTeamType.Team1 || Character.Controlled?.TeamID == CharacterTeamType.Team1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1101,19 +1090,17 @@ namespace Barotrauma.Items.Components
|
||||
if (Level.Loaded != null && dockingPort.Item.Submarine.WorldPosition.Y > Level.Loaded.Size.Y) { continue; }
|
||||
if (dockingPort.Item.Submarine == null) { continue; }
|
||||
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
|
||||
if (!dockingPort.Item.Submarine.ShowSonarMarker && !dockingPort.Item.Submarine.Info.IsOutpost) { continue; }
|
||||
// docking ports should be shown even if defined as not, if the submarine is the same as the sonar's
|
||||
if (!dockingPort.Item.Submarine.ShowSonarMarker && dockingPort.Item.Submarine != item.Submarine && !dockingPort.Item.Submarine.Info.IsOutpost) { continue; }
|
||||
|
||||
//don't show the docking ports of the opposing team on the sonar
|
||||
if (item.Submarine != null)
|
||||
if (item.Submarine != null &&
|
||||
item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
|
||||
dockingPort.Item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
|
||||
dockingPort.Item.Submarine.Info.Type != SubmarineType.Outpost)
|
||||
{
|
||||
if (dockingPort.Item.Submarine.TeamID == CharacterTeamType.Team1 && (item.Submarine.TeamID == CharacterTeamType.Team2 || Character.Controlled?.TeamID == CharacterTeamType.Team2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (dockingPort.Item.Submarine.TeamID == CharacterTeamType.Team2 && (item.Submarine.TeamID == CharacterTeamType.Team1 || Character.Controlled?.TeamID == CharacterTeamType.Team1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// specifically checking for friendlyNPC seems more logical here
|
||||
if (dockingPort.Item.Submarine.TeamID != item.Submarine.TeamID && dockingPort.Item.Submarine.TeamID != CharacterTeamType.FriendlyNPC) { continue; }
|
||||
}
|
||||
|
||||
Vector2 offset = (dockingPort.Item.WorldPosition - transducerCenter) * scale;
|
||||
@@ -1629,9 +1616,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (markerDistances.TryGetValue(targetIdentifier, out CachedDistance cachedDistance))
|
||||
{
|
||||
if (Timing.TotalTime > cachedDistance.RecalculationTime &&
|
||||
(Vector2.DistanceSquared(cachedDistance.TransducerWorldPos, transducerPosition) > 500 * 500 ||
|
||||
Vector2.DistanceSquared(cachedDistance.WorldPos, worldPosition) > 500 * 500))
|
||||
if (cachedDistance.ShouldUpdateDistance(transducerPosition, worldPosition))
|
||||
{
|
||||
markerDistances.Remove(targetIdentifier);
|
||||
CalculateDistance();
|
||||
@@ -1653,10 +1638,7 @@ namespace Barotrauma.Items.Components
|
||||
var path = pathFinder.FindPath(ConvertUnits.ToSimUnits(transducerPosition), ConvertUnits.ToSimUnits(worldPosition));
|
||||
if (!path.Unreachable)
|
||||
{
|
||||
var cachedDistance = new CachedDistance(transducerPosition, worldPosition, path.TotalLength)
|
||||
{
|
||||
RecalculationTime = Timing.TotalTime + Rand.Range(1.0f, 5.0f)
|
||||
};
|
||||
var cachedDistance = new CachedDistance(transducerPosition, worldPosition, path.TotalLength, Timing.TotalTime + Rand.Range(1.0f, 5.0f));
|
||||
markerDistances.Add(targetIdentifier, cachedDistance);
|
||||
dist = path.TotalLength;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Particles;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Projectile : ItemComponent
|
||||
{
|
||||
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
bool launch = msg.ReadBoolean();
|
||||
@@ -96,5 +102,30 @@ namespace Barotrauma.Items.Components
|
||||
Unstick();
|
||||
}
|
||||
}
|
||||
|
||||
partial void LaunchProjSpecific(Vector2 startLocation, Vector2 endLocation)
|
||||
{
|
||||
Vector2 particlePos = item.WorldPosition;
|
||||
float rotation = -item.body.Rotation;
|
||||
if (item.body.Dir < 0.0f) { rotation += MathHelper.Pi; }
|
||||
Tuple<Vector2, Vector2> tracerPoints = new Tuple<Vector2, Vector2>(startLocation, endLocation);
|
||||
foreach (ParticleEmitter emitter in particleEmitters)
|
||||
{
|
||||
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: rotation, particleRotation: rotation, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier, tracerPoints: tracerPoints);
|
||||
}
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "particleemitter":
|
||||
particleEmitters.Add(new ParticleEmitter(subElement));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
particleEmitter.Emit(
|
||||
deltaTime, ConvertUnits.ToDisplayUnits(raystart),
|
||||
item.CurrentHull, particleAngle, particleEmitter.Prefab.CopyEntityAngle ? -particleAngle : 0);
|
||||
item.CurrentHull, particleAngle, particleEmitter.Prefab.Properties.CopyEntityAngle ? -particleAngle : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,12 +36,30 @@ namespace Barotrauma.Items.Components
|
||||
get
|
||||
{
|
||||
if (target == null || source == null) { return Vector2.Zero; }
|
||||
|
||||
Vector2 sourcePos = GetSourcePos();
|
||||
|
||||
return new Vector2(
|
||||
Math.Abs(target.DrawPosition.X - source.DrawPosition.X),
|
||||
Math.Abs(target.DrawPosition.Y - source.DrawPosition.Y)) * 1.5f;
|
||||
Math.Abs(target.DrawPosition.X - sourcePos.X),
|
||||
Math.Abs(target.DrawPosition.Y - sourcePos.Y)) * 1.5f;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 GetSourcePos()
|
||||
{
|
||||
Vector2 sourcePos = source.WorldPosition;
|
||||
if (source is Item sourceItem)
|
||||
{
|
||||
sourcePos = sourceItem.DrawPosition;
|
||||
}
|
||||
else if (source is Limb sourceLimb && sourceLimb.body != null)
|
||||
{
|
||||
sourcePos = sourceLimb.body.DrawPosition;
|
||||
}
|
||||
return sourcePos;
|
||||
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
@@ -65,14 +83,18 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (target == null) { return; }
|
||||
|
||||
Vector2 startPos = new Vector2(source.DrawPosition.X, -source.DrawPosition.Y);
|
||||
var turret = source?.GetComponent<Turret>();
|
||||
if (turret != null)
|
||||
Vector2 startPos = GetSourcePos();
|
||||
startPos.Y = -startPos.Y;
|
||||
if (source is Item sourceItem)
|
||||
{
|
||||
startPos = new Vector2(source.WorldRect.X + turret.TransformedBarrelPos.X, -(source.WorldRect.Y - turret.TransformedBarrelPos.Y));
|
||||
if (turret.BarrelSprite != null)
|
||||
var turret = sourceItem?.GetComponent<Turret>();
|
||||
if (turret != null)
|
||||
{
|
||||
startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * item.Scale * 0.9f;
|
||||
startPos = new Vector2(sourceItem.WorldRect.X + turret.TransformedBarrelPos.X, -(sourceItem.WorldRect.Y - turret.TransformedBarrelPos.Y));
|
||||
if (turret.BarrelSprite != null)
|
||||
{
|
||||
startPos += new Vector2((float)Math.Cos(turret.Rotation), (float)Math.Sin(turret.Rotation)) * turret.BarrelSprite.size.Y * turret.BarrelSprite.RelativeOrigin.Y * item.Scale * 0.9f;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vector2 endPos = new Vector2(target.DrawPosition.X, -target.DrawPosition.Y);
|
||||
@@ -80,7 +102,7 @@ namespace Barotrauma.Items.Components
|
||||
if (Snapped)
|
||||
{
|
||||
float snapState = 1.0f - snapTimer / SnapAnimDuration;
|
||||
Vector2 diff = target.DrawPosition - source.DrawPosition;
|
||||
Vector2 diff = target.DrawPosition - new Vector2(startPos.X, -startPos.Y);
|
||||
diff.Y = -diff.Y;
|
||||
|
||||
int width = (int)(SpriteWidth * snapState);
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
Wire equippedWire = null;
|
||||
|
||||
bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring || panel.AlwaysAllowRewiring;
|
||||
if (allowRewiring && (!panel.Locked || Screen.Selected == GameMain.SubEditorScreen))
|
||||
if (allowRewiring && (!panel.Locked && !panel.TemporarilyLocked || Screen.Selected == GameMain.SubEditorScreen))
|
||||
{
|
||||
//if the Character using the panel has a wire item equipped
|
||||
//and the wire hasn't been connected yet, draw it on the panel
|
||||
@@ -365,7 +365,7 @@ namespace Barotrauma.Items.Components
|
||||
ConnectionPanel.HighlightedWire = wire;
|
||||
|
||||
bool allowRewiring = GameMain.NetworkMember?.ServerSettings == null || GameMain.NetworkMember.ServerSettings.AllowRewiring || panel.AlwaysAllowRewiring;
|
||||
if (allowRewiring && (!wire.Locked && !panel.Locked || Screen.Selected == GameMain.SubEditorScreen))
|
||||
if (allowRewiring && (!wire.Locked && !panel.Locked && !panel.TemporarilyLocked || Screen.Selected == GameMain.SubEditorScreen))
|
||||
{
|
||||
//start dragging the wire
|
||||
if (PlayerInput.PrimaryMouseButtonHeld()) { DraggingConnected = wire; }
|
||||
|
||||
@@ -367,9 +367,9 @@ namespace Barotrauma.Items.Components
|
||||
//a wire has been selected -> check if we should start dragging one of the nodes
|
||||
float nodeSelectDist = 10, sectionSelectDist = 5;
|
||||
highlightedNodeIndex = null;
|
||||
if (MapEntity.SelectedList.Count == 1 && MapEntity.SelectedList[0] is Item)
|
||||
if (MapEntity.SelectedList.Count == 1 && MapEntity.SelectedList.FirstOrDefault() is Item selectedItem)
|
||||
{
|
||||
Wire selectedWire = ((Item)MapEntity.SelectedList[0]).GetComponent<Wire>();
|
||||
Wire selectedWire = selectedItem.GetComponent<Wire>();
|
||||
|
||||
if (selectedWire != null)
|
||||
{
|
||||
|
||||
@@ -17,15 +17,6 @@ namespace Barotrauma.Items.Components
|
||||
TextManager.Get("CatastrophicBleeding")
|
||||
};
|
||||
|
||||
private static readonly string[] HealthTexts =
|
||||
{
|
||||
TextManager.Get("NoInjuries"),
|
||||
TextManager.Get("MinorInjuries"),
|
||||
TextManager.Get("Injuries"),
|
||||
TextManager.Get("MajorInjuries"),
|
||||
TextManager.Get("CriticalInjuries")
|
||||
};
|
||||
|
||||
private static readonly string[] OxygenTexts =
|
||||
{
|
||||
TextManager.Get("OxygenNormal"),
|
||||
@@ -55,6 +46,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Character equipper;
|
||||
|
||||
private bool isEquippable;
|
||||
|
||||
public IEnumerable<Character> VisibleCharacters
|
||||
{
|
||||
get
|
||||
@@ -64,14 +57,28 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
isEquippable = item.GetComponent<Pickable>() != null;
|
||||
if (!isEquippable) { IsActive = true; }
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
if (equipper == null || equipper.Removed)
|
||||
Entity refEntity = equipper;
|
||||
if (isEquippable)
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
if (equipper == null || equipper.Removed)
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
refEntity = item;
|
||||
}
|
||||
|
||||
if (updateTimer > 0.0f)
|
||||
@@ -85,11 +92,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (c == equipper || !c.Enabled || c.Removed) { continue; }
|
||||
|
||||
float dist = Vector2.DistanceSquared(equipper.WorldPosition, c.WorldPosition);
|
||||
float dist = Vector2.DistanceSquared(refEntity.WorldPosition, c.WorldPosition);
|
||||
if (dist < Range * Range)
|
||||
{
|
||||
Vector2 diff = c.WorldPosition - equipper.WorldPosition;
|
||||
if (Submarine.CheckVisibility(equipper.SimPosition, equipper.SimPosition + ConvertUnits.ToSimUnits(diff)) == null)
|
||||
Vector2 diff = c.WorldPosition - refEntity.WorldPosition;
|
||||
if (Submarine.CheckVisibility(refEntity.SimPosition, refEntity.SimPosition + ConvertUnits.ToSimUnits(diff)) == null)
|
||||
{
|
||||
visibleCharacters.Add(c);
|
||||
}
|
||||
@@ -147,9 +154,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
List<string> texts = new List<string>();
|
||||
List<Color> textColors = new List<Color>();
|
||||
|
||||
texts.Add(target.Info == null ? target.DisplayName : target.Info.DisplayName);
|
||||
textColors.Add(GUI.Style.TextColor);
|
||||
Color nameColor = GUI.Style.TextColor;
|
||||
if (Character.Controlled != null && target.TeamID != Character.Controlled.TeamID)
|
||||
{
|
||||
nameColor = target.TeamID == CharacterTeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
|
||||
}
|
||||
textColors.Add(nameColor);
|
||||
|
||||
if (target.IsDead)
|
||||
{
|
||||
|
||||
@@ -34,7 +34,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private RoundSound startMoveSound, endMoveSound, moveSound;
|
||||
|
||||
private SoundChannel moveSoundChannel;
|
||||
private RoundSound chargeSound;
|
||||
|
||||
private SoundChannel moveSoundChannel, chargeSoundChannel;
|
||||
private Vector2 oldRotation = Vector2.Zero;
|
||||
|
||||
private Vector2 crosshairPos, crosshairPointerPos;
|
||||
@@ -43,11 +45,16 @@ namespace Barotrauma.Items.Components
|
||||
private float prevAngle;
|
||||
|
||||
private bool flashLowPower;
|
||||
private bool flashNoAmmo;
|
||||
private bool flashNoAmmo, flashLoaderBroken;
|
||||
private float flashTimer;
|
||||
private float flashLength = 1;
|
||||
private readonly float flashLength = 1;
|
||||
|
||||
private const float MaxCircle = 360f;
|
||||
private const float HalfCircle = 180f;
|
||||
private const float QuarterCircle = 90f;
|
||||
|
||||
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
||||
private readonly List<ParticleEmitter> particleEmitterCharges = new List<ParticleEmitter>();
|
||||
|
||||
[Editable, Serialize("0,0,0,0", true, description: "Optional screen tint color when the item is being operated (R,G,B,A).")]
|
||||
public Color HudTint
|
||||
@@ -77,6 +84,21 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "The distance in which the spinning barrels rotate. Only used if spinning barrels are created.")]
|
||||
public float SpinningBarrelDistance
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Use firing offset for muzzleflash? This field shouldn't be needed but I'm using it for prototyping")]
|
||||
public bool UseFiringOffsetForMuzzleFlash
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
public Vector2 DrawSize
|
||||
{
|
||||
get
|
||||
@@ -124,9 +146,15 @@ namespace Barotrauma.Items.Components
|
||||
case "movesound":
|
||||
moveSound = Submarine.LoadRoundSound(subElement, false);
|
||||
break;
|
||||
case "chargesound":
|
||||
chargeSound = Submarine.LoadRoundSound(subElement, false);
|
||||
break;
|
||||
case "particleemitter":
|
||||
particleEmitters.Add(new ParticleEmitter(subElement));
|
||||
break;
|
||||
case "particleemittercharge":
|
||||
particleEmitterCharges.Add(new ParticleEmitter(subElement));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +178,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
recoilTimer = RetractionTime;
|
||||
PlaySound(ActionType.OnUse);
|
||||
Vector2 particlePos = new Vector2(item.WorldRect.X + transformedBarrelPos.X, item.WorldRect.Y - transformedBarrelPos.Y);
|
||||
Vector2 particlePos = GetRelativeFiringPosition(UseFiringOffsetForMuzzleFlash);
|
||||
foreach (ParticleEmitter emitter in particleEmitters)
|
||||
{
|
||||
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: -rotation, particleRotation: rotation);
|
||||
@@ -215,12 +243,55 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
float chargeRatio = currentChargeTime / MaxChargeTime;
|
||||
currentBarrelSpin = (currentBarrelSpin + MaxCircle * chargeRatio * deltaTime * 3f) % MaxCircle;
|
||||
|
||||
switch (currentChargingState)
|
||||
{
|
||||
case ChargingState.WindingUp:
|
||||
Vector2 particlePos = GetRelativeFiringPosition();
|
||||
float sizeMultiplier = Math.Clamp(chargeRatio, 0.1f, 1f);
|
||||
foreach (ParticleEmitter emitter in particleEmitterCharges)
|
||||
{
|
||||
// color is currently not connected to ammo type, should be updated when ammo is changed
|
||||
emitter.Emit(deltaTime, particlePos, hullGuess: null, angle: -rotation, particleRotation: rotation, sizeMultiplier: sizeMultiplier, colorMultiplier: emitter.Prefab.Properties.ColorMultiplier);
|
||||
}
|
||||
|
||||
if (chargeSoundChannel == null || !chargeSoundChannel.IsPlaying)
|
||||
{
|
||||
if (chargeSound != null)
|
||||
{
|
||||
chargeSoundChannel = SoundPlayer.PlaySound(chargeSound.Sound, item.WorldPosition, chargeSound.Volume, chargeSound.Range, ignoreMuffling: chargeSound.IgnoreMuffling);
|
||||
if (chargeSoundChannel != null) chargeSoundChannel.Looping = true;
|
||||
}
|
||||
}
|
||||
else if (chargeSoundChannel != null)
|
||||
{
|
||||
chargeSoundChannel.FrequencyMultiplier = MathHelper.Lerp(0.5f, 1.5f, chargeRatio);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (chargeSoundChannel != null)
|
||||
{
|
||||
if (chargeSoundChannel.IsPlaying)
|
||||
{
|
||||
chargeSoundChannel.FadeOutAndDispose();
|
||||
chargeSoundChannel.Looping = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
chargeSoundChannel = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (moveSoundChannel != null && moveSoundChannel.IsPlaying)
|
||||
{
|
||||
moveSoundChannel.Gain = MathHelper.Clamp(Math.Abs(angularVelocity), 0.5f, 1.0f);
|
||||
}
|
||||
|
||||
if (flashLowPower || flashNoAmmo)
|
||||
if (flashLowPower || flashNoAmmo || flashLoaderBroken)
|
||||
{
|
||||
flashTimer += deltaTime;
|
||||
if (flashTimer >= flashLength)
|
||||
@@ -228,6 +299,7 @@ namespace Barotrauma.Items.Components
|
||||
flashTimer = 0;
|
||||
flashLowPower = false;
|
||||
flashNoAmmo = false;
|
||||
flashLoaderBroken = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,6 +361,42 @@ namespace Barotrauma.Items.Components
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (barrelSprite.Depth - item.Sprite.Depth));
|
||||
|
||||
float chargeRatio = currentChargeTime / MaxChargeTime;
|
||||
|
||||
foreach ((Sprite chargeSprite, Vector2 position) in chargeSprites)
|
||||
{
|
||||
chargeSprite?.Draw(spriteBatch,
|
||||
drawPos - MathUtils.RotatePoint(new Vector2(position.X * chargeRatio, position.Y * chargeRatio) * item.Scale, rotation + MathHelper.PiOver2),
|
||||
item.SpriteColor,
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, item.SpriteDepth + (chargeSprite.Depth - item.Sprite.Depth));
|
||||
}
|
||||
|
||||
int spinningBarrelCount = spinningBarrelSprites.Count;
|
||||
|
||||
for (int i = 0; i < spinningBarrelCount; i++)
|
||||
{
|
||||
// this block is messy since I was debugging it with a bunch of values, should be cleaned up / optimized if prototype is accepted
|
||||
Sprite spinningBarrel = spinningBarrelSprites[i];
|
||||
float barrelCirclePosition = (MaxCircle * i / spinningBarrelCount + currentBarrelSpin) % MaxCircle;
|
||||
|
||||
float newDepth = item.SpriteDepth + (spinningBarrel.Depth - item.Sprite.Depth) + (barrelCirclePosition > HalfCircle ? 0.0f : 0.001f);
|
||||
|
||||
float barrelColorPosition = (barrelCirclePosition + QuarterCircle) % MaxCircle;
|
||||
float colorOffset = Math.Abs(barrelColorPosition - HalfCircle) / HalfCircle;
|
||||
Color newColorModifier = Color.Lerp(Color.Black, Color.Gray, colorOffset);
|
||||
|
||||
float barrelHalfCirclePosition = Math.Abs(barrelCirclePosition - HalfCircle);
|
||||
float barrelPositionModifier = MathUtils.SmoothStep(barrelHalfCirclePosition / HalfCircle);
|
||||
float newPositionOffset = barrelPositionModifier * SpinningBarrelDistance;
|
||||
|
||||
spinningBarrel.Draw(spriteBatch,
|
||||
drawPos - MathUtils.RotatePoint(new Vector2(newPositionOffset, 0f) * item.Scale, rotation + MathHelper.PiOver2),
|
||||
Color.Lerp(item.SpriteColor, newColorModifier, 0.8f),
|
||||
rotation + MathHelper.PiOver2, item.Scale,
|
||||
SpriteEffects.None, newDepth);
|
||||
}
|
||||
|
||||
if (!editing || GUI.DisableHUD || !item.IsSelected) { return; }
|
||||
|
||||
const float widgetRadius = 60.0f;
|
||||
@@ -552,14 +660,20 @@ namespace Barotrauma.Items.Components
|
||||
new VisualSlot(new Rectangle(invSlotPos + new Point((i % slotsPerRow) * (slotSize.X + spacing.X), (int)Math.Floor(i / (float)slotsPerRow) * (slotSize.Y + spacing.Y)), slotSize)),
|
||||
availableAmmo[i], -1, true);
|
||||
}
|
||||
Rectangle rect = new Rectangle(invSlotPos.X, invSlotPos.Y, totalWidth, slotSize.Y);
|
||||
float inflate = MathHelper.Lerp(3, 8, (float)Math.Abs(Math.Sin(flashTimer * 5)));
|
||||
rect.Inflate(inflate, inflate);
|
||||
Color color = GUI.Style.Red * Math.Max(0.5f, (float)Math.Sin(flashTimer * 12));
|
||||
if (flashNoAmmo)
|
||||
{
|
||||
Rectangle rect = new Rectangle(invSlotPos.X, invSlotPos.Y, totalWidth, slotSize.Y);
|
||||
float inflate = MathHelper.Lerp(3, 8, (float)Math.Abs(1 * Math.Sin(flashTimer * 5)));
|
||||
rect.Inflate(inflate, inflate);
|
||||
Color color = GUI.Style.Red * MathHelper.Max(0.5f, (float)Math.Sin(flashTimer * 12));
|
||||
GUI.DrawRectangle(spriteBatch, rect, color, thickness: 3);
|
||||
}
|
||||
else if (flashLoaderBroken)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, rect, color, thickness: 3);
|
||||
GUI.BrokenIcon.Draw(spriteBatch, rect.Center.ToVector2(), color, scale: rect.Height / GUI.BrokenIcon.size.Y);
|
||||
GUIComponent.DrawToolTip(spriteBatch, TextManager.Get("turretloaderbroken"), new Rectangle(invSlotPos.X + totalWidth + GUI.IntScale(10), invSlotPos.Y + slotSize.Y / 2 - GUI.IntScale(9), 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
float zoom = cam == null ? 1.0f : (float)Math.Sqrt(cam.Zoom);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Wearable
|
||||
{
|
||||
private void GetDamageModifierText(ref string description, float damageMultiplier, string afflictionIdentifier)
|
||||
{
|
||||
int roundedValue = (int)Math.Round((1 - damageMultiplier) * 100);
|
||||
if (roundedValue == 0) { return; }
|
||||
string colorStr = XMLExtensions.ColorToString(GUI.Style.Green);
|
||||
description += $"\n ‖color:{colorStr}‖{roundedValue.ToString("-0;+#")}%‖color:end‖ {AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier.Equals(afflictionIdentifier, StringComparison.OrdinalIgnoreCase))?.Name ?? afflictionIdentifier}";
|
||||
}
|
||||
|
||||
public override void AddTooltipInfo(ref string description)
|
||||
{
|
||||
if (damageModifiers.Any(d => !MathUtils.NearlyEqual(d.DamageMultiplier, 1f)) || SkillModifiers.Any())
|
||||
{
|
||||
description += "\n";
|
||||
}
|
||||
|
||||
if (damageModifiers.Any())
|
||||
{
|
||||
foreach (DamageModifier damageModifier in damageModifiers)
|
||||
{
|
||||
if (MathUtils.NearlyEqual(damageModifier.DamageMultiplier, 1f))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (string afflictionIdentifier in damageModifier.ParsedAfflictionIdentifiers)
|
||||
{
|
||||
GetDamageModifierText(ref description, damageModifier.DamageMultiplier, afflictionIdentifier);
|
||||
}
|
||||
foreach (string afflictionIdentifier in damageModifier.ParsedAfflictionTypes)
|
||||
{
|
||||
GetDamageModifierText(ref description, damageModifier.DamageMultiplier, afflictionIdentifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (SkillModifiers.Any())
|
||||
{
|
||||
foreach (var skillModifier in SkillModifiers)
|
||||
{
|
||||
string colorStr = XMLExtensions.ColorToString(GUI.Style.Green);
|
||||
int roundedValue = (int)Math.Round(skillModifier.Value);
|
||||
if (roundedValue == 0) { continue; }
|
||||
description += $"\n ‖color:{colorStr}‖{roundedValue.ToString("+0;-#")}‖color:end‖ {TextManager.Get("SkillName." + skillModifier.Key, true) ?? skillModifier.Key}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,6 +296,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ItemComponent component in item.Components)
|
||||
{
|
||||
component.AddTooltipInfo(ref description);
|
||||
}
|
||||
|
||||
if (item.Prefab.ShowContentsInTooltip && item.OwnInventory != null)
|
||||
{
|
||||
foreach (string itemName in item.OwnInventory.AllItems.Select(it => it.Name).Distinct())
|
||||
@@ -320,6 +326,11 @@ namespace Barotrauma
|
||||
toolTip += $"‖color:{conditionColorStr}‖ ({(int)item.ConditionPercentage} %)‖color:end‖";
|
||||
}
|
||||
if (!string.IsNullOrEmpty(description)) { toolTip += '\n' + description; }
|
||||
if (item.prefab.ContentPackage != GameMain.VanillaContent && item.prefab.ContentPackage != null)
|
||||
{
|
||||
colorStr = XMLExtensions.ColorToString(Color.MediumPurple);
|
||||
toolTip += $"\n‖color:{colorStr}‖{item.prefab.ContentPackage.Name}‖color:end‖";
|
||||
}
|
||||
}
|
||||
if (itemsInSlot.Count() > 1)
|
||||
{
|
||||
@@ -945,6 +956,15 @@ namespace Barotrauma
|
||||
{
|
||||
return CursorState.Hand;
|
||||
}
|
||||
var container = item?.GetComponent<ItemContainer>();
|
||||
if (container == null) { continue; }
|
||||
if (container.Inventory.visualSlots != null)
|
||||
{
|
||||
if (container.Inventory.visualSlots.Any(slot => slot.IsHighlighted))
|
||||
{
|
||||
return CursorState.Hand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1120,9 +1140,10 @@ namespace Barotrauma
|
||||
if (selectedSlot == null)
|
||||
{
|
||||
if (DraggingItemToWorld &&
|
||||
Character.Controlled.FocusedItem?.OwnInventory != null &&
|
||||
(Character.Controlled.FocusedItem.GetComponent<ItemContainer>()?.HasRequiredItems(Character.Controlled, addMessage: false) ?? false) &&
|
||||
Character.Controlled.FocusedItem.OwnInventory.CanBePut(DraggingItems.FirstOrDefault()))
|
||||
Character.Controlled.FocusedItem is { OwnInventory: { } inventory } item && item.GetComponent<ItemContainer>() is { } container &&
|
||||
container.HasRequiredItems(Character.Controlled, addMessage: false) &&
|
||||
container.AllowDragAndDrop &&
|
||||
inventory.CanBePut(DraggingItems.FirstOrDefault()))
|
||||
{
|
||||
bool anySuccess = false;
|
||||
foreach (Item it in DraggingItems)
|
||||
@@ -1575,7 +1596,7 @@ namespace Barotrauma
|
||||
}
|
||||
sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale);
|
||||
|
||||
if (!item.AllowStealing && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
|
||||
if ((!item.AllowStealing || (inventory != null && inventory.slots[slotIndex].Items.Any(it => !it.AllowStealing))) && CharacterInventory.LimbSlotIcons.ContainsKey(InvSlotType.LeftHand))
|
||||
{
|
||||
var stealIcon = CharacterInventory.LimbSlotIcons[InvSlotType.LeftHand];
|
||||
Vector2 iconSize = new Vector2(25 * GUI.Scale);
|
||||
|
||||
@@ -22,6 +22,19 @@ namespace Barotrauma
|
||||
|
||||
private readonly List<ItemComponent> activeHUDs = new List<ItemComponent>();
|
||||
|
||||
public GUIComponentStyle IconStyle { get; private set; } = null;
|
||||
partial void AssignCampaignInteractionTypeProjSpecific(CampaignMode.InteractionType interactionType)
|
||||
{
|
||||
if (interactionType == CampaignMode.InteractionType.None)
|
||||
{
|
||||
IconStyle = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
IconStyle = GUI.Style.GetComponentStyle($"CampaignInteractionIcon.{interactionType}");
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<ItemComponent> ActiveHUDs => activeHUDs;
|
||||
|
||||
public float LastImpactSoundTime;
|
||||
@@ -252,7 +265,8 @@ namespace Barotrauma
|
||||
else if (!ShowItems) { return; }
|
||||
}
|
||||
|
||||
Color color = IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange : GetSpriteColor();
|
||||
Color color = IsIncludedInSelection && editing ? GUI.Style.Blue : IsHighlighted && !GUI.DisableItemHighlights && Screen.Selected != GameMain.GameScreen ? GUI.Style.Orange * Math.Max(GetSpriteColor().A / (float) byte.MaxValue, 0.1f) : GetSpriteColor();
|
||||
|
||||
//if (IsSelected && editing) color = Color.Lerp(color, Color.Gold, 0.5f);
|
||||
|
||||
bool isWiringMode = editing && SubEditorScreen.TransparentWiringMode && SubEditorScreen.IsWiringMode() && !isWire && parentInventory == null;
|
||||
@@ -305,24 +319,27 @@ namespace Barotrauma
|
||||
if (prefab.ResizeHorizontal || prefab.ResizeVertical)
|
||||
{
|
||||
Vector2 size = new Vector2(rect.Width, rect.Height);
|
||||
activeSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + drawOffset,
|
||||
size, color: color,
|
||||
textureScale: Vector2.One * Scale,
|
||||
depth: depth);
|
||||
fadeInBrokenSprite?.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + fadeInBrokenSprite.Offset.ToVector2() * Scale, size, color: color * fadeInBrokenSpriteAlpha,
|
||||
textureScale: Vector2.One * Scale,
|
||||
depth: depth - 0.000001f);
|
||||
foreach (var decorativeSprite in Prefab.DecorativeSprites)
|
||||
if (color.A > 0)
|
||||
{
|
||||
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
||||
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
|
||||
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
|
||||
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
|
||||
decorativeSprite.Sprite.DrawTiled(spriteBatch,
|
||||
new Vector2(DrawPosition.X + offset.X - rect.Width / 2, -(DrawPosition.Y + offset.Y + rect.Height / 2)),
|
||||
activeSprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + drawOffset,
|
||||
size, color: color,
|
||||
textureScale: Vector2.One * Scale,
|
||||
depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - activeSprite.Depth), 0.999f));
|
||||
depth: depth);
|
||||
fadeInBrokenSprite?.Sprite.DrawTiled(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)) + fadeInBrokenSprite.Offset.ToVector2() * Scale, size, color: color * fadeInBrokenSpriteAlpha,
|
||||
textureScale: Vector2.One * Scale,
|
||||
depth: depth - 0.000001f);
|
||||
foreach (var decorativeSprite in Prefab.DecorativeSprites)
|
||||
{
|
||||
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
||||
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState, spriteAnimState[decorativeSprite].RandomOffsetMultiplier, -rotationRad) * Scale;
|
||||
if (flippedX && Prefab.CanSpriteFlipX) { offset.X = -offset.X; }
|
||||
if (flippedY && Prefab.CanSpriteFlipY) { offset.Y = -offset.Y; }
|
||||
decorativeSprite.Sprite.DrawTiled(spriteBatch,
|
||||
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
|
||||
@@ -336,8 +353,11 @@ namespace Barotrauma
|
||||
{
|
||||
origin.Y = activeSprite.SourceRect.Height - origin.Y;
|
||||
}
|
||||
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth);
|
||||
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f);
|
||||
if (color.A > 0)
|
||||
{
|
||||
activeSprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, origin, rotationRad, Scale, activeSprite.effects, depth);
|
||||
fadeInBrokenSprite?.Sprite.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + fadeInBrokenSprite.Offset.ToVector2() * Scale, color * fadeInBrokenSpriteAlpha, origin, rotationRad, Scale, activeSprite.effects, depth - 0.000001f);
|
||||
}
|
||||
if (Infector != null && (Infector.ParentBallastFlora.HasBrokenThrough || BallastFloraBehavior.AlwaysShowBallastFloraSprite))
|
||||
{
|
||||
Prefab.InfectedSprite?.Draw(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y) + drawOffset, color, Prefab.InfectedSprite.Origin, rotationRad, Scale, activeSprite.effects, depth - 0.001f);
|
||||
@@ -365,7 +385,7 @@ namespace Barotrauma
|
||||
float depthStep = 0.000001f;
|
||||
if (holdable.Picker.Inventory?.GetItemInLimbSlot(InvSlotType.RightHand) == this)
|
||||
{
|
||||
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightHand);
|
||||
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.RightArm);
|
||||
if (holdLimb?.ActiveSprite != null)
|
||||
{
|
||||
depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() + depthStep * 2;
|
||||
@@ -377,7 +397,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (holdable.Picker.Inventory?.GetItemInLimbSlot(InvSlotType.LeftHand) == this)
|
||||
{
|
||||
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftHand);
|
||||
Limb holdLimb = holdable.Picker.AnimController.GetLimb(LimbType.LeftArm);
|
||||
if (holdLimb?.ActiveSprite != null)
|
||||
{
|
||||
depth = holdLimb.ActiveSprite.Depth + holdable.Picker.AnimController.GetDepthOffset() - depthStep * 2;
|
||||
@@ -442,10 +462,7 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
if (body != null)
|
||||
{
|
||||
body.DebugDraw(spriteBatch, Color.White);
|
||||
}
|
||||
body?.DebugDraw(spriteBatch, Color.White);
|
||||
}
|
||||
|
||||
if (editing && IsSelected && PlayerInput.KeyDown(Keys.Space))
|
||||
@@ -463,7 +480,10 @@ namespace Barotrauma
|
||||
|
||||
if (IsSelected || IsHighlighted)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2)), new Vector2(rect.Width, rect.Height),
|
||||
Vector2 drawPos = new Vector2(DrawPosition.X - rect.Width / 2, -(DrawPosition.Y + rect.Height / 2));
|
||||
Vector2 drawSize = new Vector2(MathF.Ceiling(rect.Width + Math.Abs(drawPos.X - (int)drawPos.X)), MathF.Ceiling(rect.Height + Math.Abs(drawPos.Y - (int)drawPos.Y)));
|
||||
drawPos = new Vector2(MathF.Floor(drawPos.X), MathF.Floor(drawPos.Y));
|
||||
GUI.DrawRectangle(spriteBatch, drawPos, drawSize,
|
||||
Color.White, false, 0, thickness: Math.Max(1, (int)(2 / Screen.Selected.Cam.Zoom)));
|
||||
|
||||
foreach (Rectangle t in Prefab.Triggers)
|
||||
@@ -675,7 +695,11 @@ namespace Barotrauma
|
||||
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
FlipX(relativeToSub: false);
|
||||
foreach (MapEntity me in SelectedList)
|
||||
{
|
||||
me.FlipX(relativeToSub: false);
|
||||
}
|
||||
if (!SelectedList.Contains(this)) { FlipX(relativeToSub: false); }
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -684,7 +708,11 @@ namespace Barotrauma
|
||||
ToolTip = TextManager.Get("MirrorEntityYToolTip"),
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
FlipY(relativeToSub: false);
|
||||
foreach (MapEntity me in SelectedList)
|
||||
{
|
||||
me.FlipY(relativeToSub: false);
|
||||
}
|
||||
if (!SelectedList.Contains(this)) { FlipY(relativeToSub: false); }
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -694,7 +722,7 @@ namespace Barotrauma
|
||||
reloadTextureButton.OnClicked += (button, data) =>
|
||||
{
|
||||
Sprite.ReloadXML();
|
||||
Sprite.ReloadTexture();
|
||||
Sprite.ReloadTexture(updateAllSprites: true);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
@@ -702,7 +730,12 @@ namespace Barotrauma
|
||||
{
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
Reset();
|
||||
foreach (MapEntity me in SelectedList)
|
||||
{
|
||||
(me as Item)?.Reset();
|
||||
(me as Structure)?.Reset();
|
||||
}
|
||||
if (!SelectedList.Contains(this)) { Reset(); }
|
||||
CreateEditingHUD();
|
||||
return true;
|
||||
}
|
||||
@@ -734,7 +767,11 @@ namespace Barotrauma
|
||||
if (inGame)
|
||||
{
|
||||
if (!ic.AllowInGameEditing) { continue; }
|
||||
if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0) { continue; }
|
||||
if (SerializableProperty.GetProperties<InGameEditable>(ic).Count == 0 &&
|
||||
!SerializableProperty.GetProperties<ConditionallyEditable>(ic).Any(p => p.GetAttribute<ConditionallyEditable>().IsEditable(ic)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1093,29 +1130,39 @@ namespace Barotrauma
|
||||
nameText += $" ({idName})";
|
||||
}
|
||||
}
|
||||
texts.Add(new ColoredText(nameText, GUI.Style.TextColor, false, false));
|
||||
texts.Add(new ColoredText(nameText, GUI.Style.TextColor, false, false));
|
||||
|
||||
foreach (ItemComponent ic in components)
|
||||
bool noComponentText = true;
|
||||
|
||||
if (CampaignInteractionType != CampaignMode.InteractionType.None)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ic.DisplayMsg)) { continue; }
|
||||
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
|
||||
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
|
||||
|
||||
Color color = Color.Gray;
|
||||
if (ic.HasRequiredItems(character, false))
|
||||
{
|
||||
if (ic is Repairable)
|
||||
{
|
||||
if (!IsFullCondition) { color = Color.Cyan; }
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.Cyan;
|
||||
}
|
||||
}
|
||||
texts.Add(new ColoredText(ic.DisplayMsg, color, false, false));
|
||||
texts.Add(new ColoredText(TextManager.GetWithVariable($"CampaignInteraction.{CampaignInteractionType}", "[key]", GameMain.Config.KeyBindText(InputType.Use)), Color.Cyan, false, false));
|
||||
}
|
||||
if ((PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift)) && CrewManager.DoesItemHaveContextualOrders(this))
|
||||
else
|
||||
{
|
||||
foreach (ItemComponent ic in components)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ic.DisplayMsg)) { continue; }
|
||||
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
|
||||
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
|
||||
|
||||
Color color = Color.Gray;
|
||||
if (ic.HasRequiredItems(character, false))
|
||||
{
|
||||
if (ic is Repairable)
|
||||
{
|
||||
if (!IsFullCondition) { color = Color.Cyan; }
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.Cyan;
|
||||
}
|
||||
}
|
||||
texts.Add(new ColoredText(ic.DisplayMsg, color, false, false));
|
||||
noComponentText = false;
|
||||
}
|
||||
}
|
||||
if (PlayerInput.IsShiftDown() && CrewManager.DoesItemHaveContextualOrders(this))
|
||||
{
|
||||
texts.Add(new ColoredText(TextManager.ParseInputTypes(TextManager.Get("itemmsgcontextualorders")), Color.Cyan, false, false));
|
||||
}
|
||||
@@ -1213,6 +1260,9 @@ namespace Barotrauma
|
||||
}
|
||||
SetActiveSprite();
|
||||
break;
|
||||
case NetEntityEvent.Type.AssignCampaignInteraction:
|
||||
CampaignInteractionType = (CampaignMode.InteractionType)msg.ReadByte();
|
||||
break;
|
||||
case NetEntityEvent.Type.ApplyStatusEffect:
|
||||
{
|
||||
ActionType actionType = (ActionType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length - 1);
|
||||
@@ -1327,6 +1377,11 @@ namespace Barotrauma
|
||||
|
||||
isActive = true;
|
||||
|
||||
if (positionBuffer.Count > 0)
|
||||
{
|
||||
transformDirty = true;
|
||||
}
|
||||
|
||||
body.CorrectPosition(positionBuffer, out Vector2 newPosition, out Vector2 newVelocity, out float newRotation, out float newAngularVelocity);
|
||||
body.LinearVelocity = newVelocity;
|
||||
body.AngularVelocity = newAngularVelocity;
|
||||
@@ -1534,11 +1589,20 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
var item = new Item(itemPrefab, pos, sub, id: itemId)
|
||||
Item item = null;
|
||||
try
|
||||
{
|
||||
SpawnedInOutpost = spawnedInOutpost,
|
||||
AllowStealing = allowStealing
|
||||
};
|
||||
item = new Item(itemPrefab, pos, sub, id: itemId)
|
||||
{
|
||||
SpawnedInOutpost = spawnedInOutpost,
|
||||
AllowStealing = allowStealing
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to spawn item {itemPrefab.Name}", e);
|
||||
throw;
|
||||
}
|
||||
|
||||
if (item.body != null)
|
||||
{
|
||||
|
||||
@@ -63,9 +63,12 @@ namespace Barotrauma
|
||||
public Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
|
||||
public Sprite InventoryIcon;
|
||||
public Sprite MinimapIcon;
|
||||
public Sprite UpgradePreviewSprite;
|
||||
public Sprite InfectedSprite;
|
||||
public Sprite DamagedInfectedSprite;
|
||||
|
||||
public float UpgradePreviewScale = 1.0f;
|
||||
|
||||
//only used to display correct color in the sub editor, item instances have their own property that can be edited on a per-item basis
|
||||
[Serialize("1.0,1.0,1.0,1.0", false)]
|
||||
public Color InventoryIconColor
|
||||
|
||||
@@ -71,15 +71,15 @@ namespace Barotrauma
|
||||
if (sparks)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("spark", worldPosition,
|
||||
Rand.Vector(Rand.Range(500.0f, 800.0f)), 0.0f, hull);
|
||||
Rand.Vector(Rand.Range(1200.0f, 2400.0f)), 0.0f, hull);
|
||||
}
|
||||
}
|
||||
|
||||
if (flash)
|
||||
{
|
||||
float displayRange = flashRange.HasValue ? flashRange.Value : Attack.Range;
|
||||
float displayRange = flashRange ?? Attack.Range;
|
||||
if (displayRange < 0.1f) { return; }
|
||||
var light = new LightSource(worldPosition, displayRange, Color.LightYellow, null);
|
||||
var light = new LightSource(worldPosition, displayRange, flashColor, null);
|
||||
CoroutineManager.StartCoroutine(DimLight(light));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,40 +169,43 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (EditWater)
|
||||
if (!IdFreed)
|
||||
{
|
||||
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
if (Submarine.RectContains(WorldRect, position))
|
||||
if (EditWater)
|
||||
{
|
||||
if (PlayerInput.PrimaryMouseButtonHeld())
|
||||
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
if (Submarine.RectContains(WorldRect, position))
|
||||
{
|
||||
WaterVolume += 1500.0f;
|
||||
networkUpdatePending = true;
|
||||
serverUpdateDelay = 0.5f;
|
||||
if (PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
WaterVolume += 1500.0f;
|
||||
networkUpdatePending = true;
|
||||
serverUpdateDelay = 0.5f;
|
||||
}
|
||||
else if (PlayerInput.SecondaryMouseButtonHeld())
|
||||
{
|
||||
WaterVolume -= 1500.0f;
|
||||
networkUpdatePending = true;
|
||||
serverUpdateDelay = 0.5f;
|
||||
}
|
||||
}
|
||||
else if (PlayerInput.SecondaryMouseButtonHeld())
|
||||
}
|
||||
else if (EditFire)
|
||||
{
|
||||
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
if (Submarine.RectContains(WorldRect, position))
|
||||
{
|
||||
WaterVolume -= 1500.0f;
|
||||
networkUpdatePending = true;
|
||||
serverUpdateDelay = 0.5f;
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
new FireSource(position, this, isNetworkMessage: true);
|
||||
networkUpdatePending = true;
|
||||
serverUpdateDelay = 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (EditFire)
|
||||
{
|
||||
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
if (Submarine.RectContains(WorldRect, position))
|
||||
{
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
new FireSource(position, this, isNetworkMessage: true);
|
||||
networkUpdatePending = true;
|
||||
serverUpdateDelay = 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (waterVolume < 1.0f) return;
|
||||
|
||||
if (waterVolume < 1.0f) { return; }
|
||||
for (int i = 1; i < waveY.Length - 1; i++)
|
||||
{
|
||||
float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i]));
|
||||
@@ -629,9 +632,9 @@ namespace Barotrauma
|
||||
PowerConsumptionTimer = message.ReadSingle()
|
||||
};
|
||||
}
|
||||
else if (BallastFlora != null)
|
||||
else
|
||||
{
|
||||
BallastFlora.ClientRead(message, header);
|
||||
BallastFlora?.ClientRead(message, header);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace Barotrauma
|
||||
if (ParticleEmitterTriggers[i] != null && !ParticleEmitterTriggers[i].IsTriggered) { continue; }
|
||||
Vector2 emitterPos = LocalToWorld(Prefab.EmitterPositions[i]);
|
||||
ParticleEmitters[i].Emit(deltaTime, emitterPos, hullGuess: null,
|
||||
angle: ParticleEmitters[i].Prefab.CopyEntityAngle ? -CurrentRotation + MathHelper.PiOver2 : 0.0f);
|
||||
angle: ParticleEmitters[i].Prefab.Properties.CopyEntityAngle ? -CurrentRotation + MathHelper.Pi : 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,6 +293,12 @@ namespace Barotrauma
|
||||
public void ClientRead(IReadMessage msg)
|
||||
{
|
||||
if (Triggers == null) { return; }
|
||||
|
||||
if (Prefab.TakeLevelWallDamage)
|
||||
{
|
||||
float newHealth = msg.ReadRangedSingle(0.0f, Prefab.Health, 8);
|
||||
AddDamage(Health - newHealth, 1.0f, null, isNetworkEvent: true);
|
||||
}
|
||||
for (int i = 0; i < Triggers.Count; i++)
|
||||
{
|
||||
if (!Triggers[i].UseNetworkSyncing) { continue; }
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace Barotrauma
|
||||
const int MaxVisibleObjects = 500;
|
||||
|
||||
private Rectangle currentGridIndices;
|
||||
|
||||
public bool ForceRefreshVisibleObjects;
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
{
|
||||
@@ -60,6 +62,8 @@ namespace Barotrauma
|
||||
if (objectGrid[x, y] == null) { continue; }
|
||||
foreach (LevelObject obj in objectGrid[x, y])
|
||||
{
|
||||
if (obj.Prefab.HideWhenBroken && obj.Health <= 0.0f) { continue; }
|
||||
|
||||
if (zoom < 0.05f)
|
||||
{
|
||||
//hide if the sprite is very small when zoomed this far out
|
||||
@@ -154,9 +158,10 @@ namespace Barotrauma
|
||||
indices.Height = Math.Min(indices.Height, objectGrid.GetLength(1) - 1);
|
||||
|
||||
float z = 0.0f;
|
||||
if (currentGridIndices != indices && Timing.TotalTime > NextRefreshTime)
|
||||
if (ForceRefreshVisibleObjects || (currentGridIndices != indices && Timing.TotalTime > NextRefreshTime))
|
||||
{
|
||||
RefreshVisibleObjects(indices, cam.Zoom);
|
||||
ForceRefreshVisibleObjects = false;
|
||||
if (cam.Zoom < 0.1f)
|
||||
{
|
||||
//when zoomed very far out, refresh a little less often
|
||||
|
||||
@@ -361,6 +361,7 @@ namespace Barotrauma.Lights
|
||||
|
||||
void DrawHalo(Character character)
|
||||
{
|
||||
if (character == null || character.Removed) { return; }
|
||||
Vector2 haloDrawPos = character.DrawPosition;
|
||||
haloDrawPos.Y = -haloDrawPos.Y;
|
||||
|
||||
@@ -404,7 +405,7 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.IsHighlighted && !highlightedEntities.Contains(item))
|
||||
if ((item.IsHighlighted || item.IconStyle != null) && !highlightedEntities.Contains(item))
|
||||
{
|
||||
highlightedEntities.Add(item);
|
||||
}
|
||||
@@ -425,7 +426,14 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
if (highlighted is Item item)
|
||||
{
|
||||
item.Draw(spriteBatch, false, true);
|
||||
if (item.IconStyle != null && (item != Character.Controlled.FocusedItem || Character.Controlled.FocusedItem == null))
|
||||
{
|
||||
//wait until next pass
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Draw(spriteBatch, false, true);
|
||||
}
|
||||
}
|
||||
else if (highlighted is Character character)
|
||||
{
|
||||
@@ -434,6 +442,22 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
//draw items with iconstyles in the style's color
|
||||
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive, samplerState: SamplerState.LinearWrap, effect: SolidColorEffect, transformMatrix: spriteBatchTransform);
|
||||
foreach (Entity highlighted in highlightedEntities)
|
||||
{
|
||||
if (highlighted is Item item)
|
||||
{
|
||||
if (item.IconStyle != null && (item != Character.Controlled.FocusedItem || Character.Controlled.FocusedItem == null))
|
||||
{
|
||||
SolidColorEffect.Parameters["color"].SetValue(item.IconStyle.Color.ToVector4());
|
||||
SolidColorEffect.CurrentTechnique.Passes[0].Apply();
|
||||
item.Draw(spriteBatch, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
//draw characters in black with a bit of blur, leaving the white edges visible
|
||||
float phase = (float)(Math.Sin(Timing.TotalTime * 3.0f) + 1.0f) / 2.0f; //phase oscillates between 0 and 1
|
||||
Vector4 overlayColor = Color.Black.ToVector4() * MathHelper.Lerp(0.5f, 0.9f, phase);
|
||||
|
||||
@@ -607,15 +607,18 @@ namespace Barotrauma
|
||||
tooltip = (new Rectangle(typeChangeIconPos.ToPoint(), new Point(30)), location.LastTypeChangeMessage);
|
||||
}
|
||||
}
|
||||
if (location != CurrentLocation && CurrentLocation.AvailableMissions.Any(m => m.Locations.Contains(location)) && generationParams.MissionIcon != null)
|
||||
if (location != CurrentLocation && generationParams.MissionIcon != null)
|
||||
{
|
||||
Vector2 missionIconPos = pos + new Vector2(1.35f, 0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
|
||||
float missionIconScale = 18.0f / generationParams.MissionIcon.SourceRect.Width;
|
||||
generationParams.MissionIcon.Draw(spriteBatch, missionIconPos, generationParams.IndicatorColor, scale: missionIconScale * zoom);
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, missionIconPos) < generationParams.MissionIcon.SourceRect.Width * zoom && IsPreferredTooltip(missionIconPos))
|
||||
if ((CurrentLocation == currentDisplayLocation && CurrentLocation.AvailableMissions.Any(m => m.Locations.Contains(location))) || location.AvailableMissions.Any(m => m.Prefab.Type == MissionType.GoTo))
|
||||
{
|
||||
var availableMissions = CurrentLocation.AvailableMissions.Where(m => m.Locations.Contains(location));
|
||||
tooltip = (new Rectangle(missionIconPos.ToPoint(), new Point(30)), TextManager.Get("mission") + '\n'+ string.Join('\n', availableMissions.Select(m => "- " + m.Name)));
|
||||
Vector2 missionIconPos = pos + new Vector2(1.35f, 0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
|
||||
float missionIconScale = 18.0f / generationParams.MissionIcon.SourceRect.Width;
|
||||
generationParams.MissionIcon.Draw(spriteBatch, missionIconPos, generationParams.IndicatorColor, scale: missionIconScale * zoom);
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, missionIconPos) < generationParams.MissionIcon.SourceRect.Width * zoom && IsPreferredTooltip(missionIconPos))
|
||||
{
|
||||
var availableMissions = CurrentLocation.AvailableMissions.Where(m => m.Locations.Contains(location)).Concat(location.AvailableMissions.Where(m => m.Prefab.Type == MissionType.GoTo)).Distinct();
|
||||
tooltip = (new Rectangle(missionIconPos.ToPoint(), new Point(30)), TextManager.Get("mission") + '\n'+ string.Join('\n', availableMissions.Select(m => "- " + m.Name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,20 +29,14 @@ namespace Barotrauma
|
||||
public static bool SelectionChanged;
|
||||
|
||||
//which entities have been selected for editing
|
||||
private static List<MapEntity> selectedList = new List<MapEntity>();
|
||||
public static List<MapEntity> SelectedList
|
||||
{
|
||||
get
|
||||
{
|
||||
return selectedList;
|
||||
}
|
||||
}
|
||||
private static List<MapEntity> copiedList = new List<MapEntity>();
|
||||
public static HashSet<MapEntity> SelectedList { get; private set; } = new HashSet<MapEntity>();
|
||||
|
||||
public static List<MapEntity> CopiedList = new List<MapEntity>();
|
||||
|
||||
private static List<MapEntity> highlightedList = new List<MapEntity>();
|
||||
|
||||
// Test feature. Not yet saved.
|
||||
public static Dictionary<MapEntity, List<MapEntity>> SelectionGroups { get; private set; } = new Dictionary<MapEntity, List<MapEntity>>();
|
||||
public static Dictionary<MapEntity, HashSet<MapEntity>> SelectionGroups { get; private set; } = new Dictionary<MapEntity, HashSet<MapEntity>>();
|
||||
|
||||
private static float highlightTimer;
|
||||
|
||||
@@ -78,25 +72,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool SelectableInEditor
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public virtual bool SelectableInEditor => true;
|
||||
|
||||
public static bool SelectedAny
|
||||
{
|
||||
get { return selectedList.Count > 0; }
|
||||
}
|
||||
public static bool SelectedAny => SelectedList.Count > 0;
|
||||
|
||||
public static IEnumerable<MapEntity> CopiedList
|
||||
{
|
||||
get { return copiedList; }
|
||||
}
|
||||
public bool IsSelected => SelectedList.Contains(this);
|
||||
|
||||
public bool IsSelected
|
||||
{
|
||||
get { return selectedList.Contains(this); }
|
||||
}
|
||||
public bool IsIncludedInSelection { get; set; }
|
||||
|
||||
public virtual bool IsVisible(Rectangle worldView)
|
||||
{
|
||||
@@ -129,7 +111,10 @@ namespace Barotrauma
|
||||
{
|
||||
if (resizing)
|
||||
{
|
||||
if (selectedList.Count == 0) resizing = false;
|
||||
if (!SelectedAny)
|
||||
{
|
||||
resizing = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -157,19 +142,19 @@ namespace Barotrauma
|
||||
if (MapEntityPrefab.Selected != null)
|
||||
{
|
||||
selectionPos = Vector2.Zero;
|
||||
selectedList.Clear();
|
||||
SelectedList.Clear();
|
||||
return;
|
||||
}
|
||||
if (GUI.KeyboardDispatcher.Subscriber == null)
|
||||
{
|
||||
if (PlayerInput.KeyHit(Keys.Delete))
|
||||
{
|
||||
if (selectedList.Any())
|
||||
if (SelectedAny)
|
||||
{
|
||||
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(selectedList, true));
|
||||
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(SelectedList), true));
|
||||
}
|
||||
selectedList.ForEach(e => { if (!e.Removed) { e.Remove(); } });
|
||||
selectedList.Clear();
|
||||
SelectedList.ForEach(e => { if (!e.Removed) { e.Remove(); } });
|
||||
SelectedList.Clear();
|
||||
}
|
||||
|
||||
if (PlayerInput.IsCtrlDown())
|
||||
@@ -178,7 +163,7 @@ namespace Barotrauma
|
||||
if (PlayerInput.KeyHit(Keys.D))
|
||||
{
|
||||
bool terminate = false;
|
||||
foreach (MapEntity entity in selectedList)
|
||||
foreach (MapEntity entity in SelectedList)
|
||||
{
|
||||
if (entity is Item item && item.GetComponent<Planter>() is { } planter)
|
||||
{
|
||||
@@ -201,11 +186,11 @@ namespace Barotrauma
|
||||
#endif
|
||||
if (PlayerInput.KeyHit(Keys.C))
|
||||
{
|
||||
Copy(selectedList);
|
||||
Copy(SelectedList.ToList());
|
||||
}
|
||||
else if (PlayerInput.KeyHit(Keys.X))
|
||||
{
|
||||
Cut(selectedList);
|
||||
Cut(SelectedList.ToList());
|
||||
}
|
||||
else if (PlayerInput.KeyHit(Keys.V))
|
||||
{
|
||||
@@ -213,21 +198,21 @@ namespace Barotrauma
|
||||
}
|
||||
else if (PlayerInput.KeyHit(Keys.G))
|
||||
{
|
||||
if (selectedList.Any())
|
||||
if (SelectedList.Any())
|
||||
{
|
||||
if (SelectionGroups.ContainsKey(selectedList.Last()))
|
||||
if (SelectionGroups.ContainsKey(SelectedList.Last()))
|
||||
{
|
||||
// Ungroup all selected
|
||||
selectedList.ForEach(e => SelectionGroups.Remove(e));
|
||||
SelectedList.ForEach(e => SelectionGroups.Remove(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var entity in selectedList)
|
||||
foreach (var entity in SelectedList)
|
||||
{
|
||||
// Remove the old group, if any
|
||||
SelectionGroups.Remove(entity);
|
||||
// Create a group that can be accessed with any member
|
||||
SelectionGroups.Add(entity, selectedList);
|
||||
SelectionGroups.Add(entity, SelectedList);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,7 +262,7 @@ namespace Barotrauma
|
||||
Vector2 nudge = GetNudgeAmount();
|
||||
if (nudge != Vector2.Zero)
|
||||
{
|
||||
foreach (MapEntity entityToNudge in selectedList) { entityToNudge.Move(nudge); }
|
||||
foreach (MapEntity entityToNudge in SelectedList) { entityToNudge.Move(nudge); }
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -290,7 +275,7 @@ namespace Barotrauma
|
||||
//started moving selected entities
|
||||
if (startMovingPos != Vector2.Zero)
|
||||
{
|
||||
Item targetContainer = GetPotentialContainer(position, selectedList);
|
||||
Item targetContainer = GetPotentialContainer(position, SelectedList);
|
||||
|
||||
if (targetContainer != null) { targetContainer.IsHighlighted = true; }
|
||||
|
||||
@@ -313,16 +298,16 @@ namespace Barotrauma
|
||||
//clone
|
||||
if (PlayerInput.IsCtrlDown())
|
||||
{
|
||||
var clones = Clone(selectedList).Where(c => c != null).ToList();
|
||||
selectedList = clones;
|
||||
selectedList.ForEach(c => c.Move(moveAmount));
|
||||
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(clones, false));
|
||||
HashSet<MapEntity> clones = Clone(SelectedList.ToList()).Where(c => c != null).ToHashSet();
|
||||
SelectedList = clones;
|
||||
SelectedList.ForEach(c => c.Move(moveAmount));
|
||||
SubEditorScreen.StoreCommand(new AddOrDeleteCommand(new List<MapEntity>(clones), false));
|
||||
}
|
||||
else // move
|
||||
{
|
||||
var oldRects = selectedList.Select(e => e.Rect).ToList();
|
||||
var oldRects = SelectedList.Select(e => e.Rect).ToList();
|
||||
List<MapEntity> deposited = new List<MapEntity>();
|
||||
foreach (MapEntity e in selectedList)
|
||||
foreach (MapEntity e in SelectedList)
|
||||
{
|
||||
e.Move(moveAmount);
|
||||
|
||||
@@ -340,14 +325,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity>(selectedList),selectedList.Select(entity => entity.Rect).ToList(), oldRects, false));
|
||||
SubEditorScreen.StoreCommand(new TransformCommand(new List<MapEntity>(SelectedList),SelectedList.Select(entity => entity.Rect).ToList(), oldRects, false));
|
||||
if (deposited.Any() && deposited.Any(entity => entity is Item))
|
||||
{
|
||||
var depositedItems = deposited.Where(entity => entity is Item).Cast<Item>().ToList();
|
||||
SubEditorScreen.StoreCommand(new InventoryPlaceCommand(targetContainer.OwnInventory, depositedItems, false));
|
||||
}
|
||||
|
||||
deposited.ForEach(entity => { selectedList.Remove(entity); });
|
||||
deposited.ForEach(entity => { SelectedList.Remove(entity); });
|
||||
}
|
||||
}
|
||||
startMovingPos = Vector2.Zero;
|
||||
@@ -360,7 +345,12 @@ namespace Barotrauma
|
||||
selectionSize.X = position.X - selectionPos.X;
|
||||
selectionSize.Y = selectionPos.Y - position.Y;
|
||||
|
||||
List<MapEntity> newSelection = new List<MapEntity>();// FindSelectedEntities(selectionPos, selectionSize);
|
||||
foreach (MapEntity entity in mapEntityList)
|
||||
{
|
||||
entity.IsIncludedInSelection = false;
|
||||
}
|
||||
|
||||
HashSet<MapEntity> newSelection = new HashSet<MapEntity>();// FindSelectedEntities(selectionPos, selectionSize);
|
||||
if (Math.Abs(selectionSize.X) > Submarine.GridSize.X || Math.Abs(selectionSize.Y) > Submarine.GridSize.Y)
|
||||
{
|
||||
newSelection = FindSelectedEntities(selectionPos, selectionSize);
|
||||
@@ -369,13 +359,22 @@ namespace Barotrauma
|
||||
{
|
||||
if (highLightedEntity != null)
|
||||
{
|
||||
if (SelectionGroups.TryGetValue(highLightedEntity, out List<MapEntity> group))
|
||||
if (SelectionGroups.TryGetValue(highLightedEntity, out HashSet<MapEntity> group))
|
||||
{
|
||||
newSelection.AddRange(group);
|
||||
foreach (MapEntity entity in group.Where(e => !newSelection.Contains(e)))
|
||||
{
|
||||
newSelection.Add(entity);
|
||||
}
|
||||
|
||||
foreach (MapEntity entity in group)
|
||||
{
|
||||
entity.IsIncludedInSelection = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newSelection.Add(highLightedEntity);
|
||||
highLightedEntity.IsIncludedInSelection = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,7 +385,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (MapEntity e in newSelection)
|
||||
{
|
||||
if (selectedList.Contains(e))
|
||||
if (SelectedList.Contains(e))
|
||||
{
|
||||
RemoveSelection(e);
|
||||
}
|
||||
@@ -398,7 +397,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedList = new List<MapEntity>(newSelection);
|
||||
SelectedList = new HashSet<MapEntity>(newSelection);
|
||||
//selectedList.Clear();
|
||||
//newSelection.ForEach(e => AddSelection(e));
|
||||
foreach (var entity in newSelection)
|
||||
@@ -407,23 +406,23 @@ namespace Barotrauma
|
||||
onGapFound: (door, gap) =>
|
||||
{
|
||||
door.RefreshLinkedGap();
|
||||
if (!selectedList.Contains(gap))
|
||||
if (!SelectedList.Contains(gap))
|
||||
{
|
||||
selectedList.Add(gap);
|
||||
SelectedList.Add(gap);
|
||||
}
|
||||
},
|
||||
onDoorFound: (door, gap) =>
|
||||
{
|
||||
if (!selectedList.Contains(door.Item))
|
||||
if (!SelectedList.Contains(door.Item))
|
||||
{
|
||||
selectedList.Add(door.Item);
|
||||
SelectedList.Add(door.Item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//select wire if both items it's connected to are selected
|
||||
var selectedItems = selectedList.Where(e => e is Item).Cast<Item>().ToList();
|
||||
var selectedItems = SelectedList.Where(e => e is Item).Cast<Item>().ToList();
|
||||
foreach (Item item in selectedItems)
|
||||
{
|
||||
if (item.Connections == null) continue;
|
||||
@@ -431,11 +430,11 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Wire w in c.Wires)
|
||||
{
|
||||
if (w == null || selectedList.Contains(w.Item)) continue;
|
||||
if (w == null || SelectedList.Contains(w.Item)) continue;
|
||||
|
||||
if (w.OtherConnection(c) != null && selectedList.Contains(w.OtherConnection(c).Item))
|
||||
if (w.OtherConnection(c) != null && SelectedList.Contains(w.OtherConnection(c).Item))
|
||||
{
|
||||
selectedList.Add(w.Item);
|
||||
SelectedList.Add(w.Item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -443,6 +442,10 @@ namespace Barotrauma
|
||||
|
||||
selectionPos = Vector2.Zero;
|
||||
selectionSize = Vector2.Zero;
|
||||
foreach (MapEntity entity in mapEntityList)
|
||||
{
|
||||
entity.IsIncludedInSelection = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
//default, not doing anything specific yet
|
||||
@@ -455,7 +458,7 @@ namespace Barotrauma
|
||||
(highlightedListBox == null || (GUI.MouseOn != highlightedListBox && !highlightedListBox.IsParentOf(GUI.MouseOn))))
|
||||
{
|
||||
//if clicking a selected entity, start moving it
|
||||
foreach (MapEntity e in selectedList)
|
||||
foreach (MapEntity e in SelectedList)
|
||||
{
|
||||
if (e.IsMouseOn(position)) startMovingPos = position;
|
||||
}
|
||||
@@ -503,7 +506,7 @@ namespace Barotrauma
|
||||
return ReplacedBy?.GetReplacementOrThis() ?? this;
|
||||
}
|
||||
|
||||
public static Item GetPotentialContainer(Vector2 position, List<MapEntity> entities = null)
|
||||
public static Item GetPotentialContainer(Vector2 position, HashSet<MapEntity> entities = null)
|
||||
{
|
||||
Item targetContainer = null;
|
||||
bool isShiftDown = PlayerInput.IsShiftDown();
|
||||
@@ -638,7 +641,7 @@ namespace Barotrauma
|
||||
|
||||
if (PlayerInput.IsCtrlDown() && !wiringMode)
|
||||
{
|
||||
if (selectedList.Contains(entity))
|
||||
if (SelectedList.Contains(entity))
|
||||
{
|
||||
RemoveSelection(entity);
|
||||
}
|
||||
@@ -657,56 +660,60 @@ namespace Barotrauma
|
||||
|
||||
public static void AddSelection(MapEntity entity)
|
||||
{
|
||||
if (selectedList.Contains(entity)) { return; }
|
||||
selectedList.Add(entity);
|
||||
if (SelectedList.Contains(entity)) { return; }
|
||||
SelectedList.Add(entity);
|
||||
HandleDoorGapLinks(entity,
|
||||
onGapFound: (door, gap) =>
|
||||
{
|
||||
door.RefreshLinkedGap();
|
||||
if (!selectedList.Contains(gap))
|
||||
if (!SelectedList.Contains(gap))
|
||||
{
|
||||
selectedList.Add(gap);
|
||||
SelectedList.Add(gap);
|
||||
}
|
||||
},
|
||||
onDoorFound: (door, gap) =>
|
||||
{
|
||||
if (!selectedList.Contains(door.Item))
|
||||
if (!SelectedList.Contains(door.Item))
|
||||
{
|
||||
selectedList.Add(door.Item);
|
||||
SelectedList.Add(door.Item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void HandleDoorGapLinks(MapEntity entity, Action<Door, Gap> onGapFound, Action<Door, Gap> onDoorFound)
|
||||
{
|
||||
if (entity is Item i)
|
||||
switch (entity)
|
||||
{
|
||||
var door = i.GetComponent<Door>();
|
||||
if (door != null)
|
||||
case Item i:
|
||||
{
|
||||
var gap = door.LinkedGap;
|
||||
var door = i.GetComponent<Door>();
|
||||
var gap = door?.LinkedGap;
|
||||
if (gap != null)
|
||||
{
|
||||
onGapFound(door, gap);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (entity is Gap gap)
|
||||
{
|
||||
var door = gap.ConnectedDoor;
|
||||
if (door != null)
|
||||
case Gap gap:
|
||||
{
|
||||
onDoorFound(door, gap);
|
||||
var door = gap.ConnectedDoor;
|
||||
if (door != null)
|
||||
{
|
||||
onDoorFound(door, gap);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveSelection(MapEntity entity)
|
||||
{
|
||||
selectedList.Remove(entity);
|
||||
SelectedList.Remove(entity);
|
||||
HandleDoorGapLinks(entity,
|
||||
onGapFound: (door, gap) => selectedList.Remove(gap),
|
||||
onDoorFound: (door, gap) => selectedList.Remove(door.Item));
|
||||
onGapFound: (door, gap) => SelectedList.Remove(gap),
|
||||
onDoorFound: (door, gap) => SelectedList.Remove(door.Item));
|
||||
}
|
||||
|
||||
static partial void UpdateAllProjSpecific(float deltaTime)
|
||||
@@ -751,7 +758,7 @@ namespace Barotrauma
|
||||
//started moving the selected entities
|
||||
if (Math.Abs(moveAmount.X) >= Submarine.GridSize.X || Math.Abs(moveAmount.Y) >= Submarine.GridSize.Y || isShiftDown)
|
||||
{
|
||||
foreach (MapEntity e in selectedList)
|
||||
foreach (MapEntity e in SelectedList)
|
||||
{
|
||||
SpriteEffects spriteEffects = SpriteEffects.None;
|
||||
switch (e)
|
||||
@@ -800,7 +807,32 @@ namespace Barotrauma
|
||||
}
|
||||
if (selectionPos != null && selectionPos != Vector2.Zero)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(selectionPos.X, -selectionPos.Y), selectionSize, Color.DarkRed, false, 0, 2f / GameScreen.Selected.Cam.Zoom);
|
||||
var (sizeX, sizeY) = selectionSize;
|
||||
var (posX, posY) = selectionPos;
|
||||
|
||||
posY = -posY;
|
||||
|
||||
Vector2[] corners =
|
||||
{
|
||||
new Vector2(posX, posY),
|
||||
new Vector2(posX + sizeX, posY),
|
||||
new Vector2(posX + sizeX, posY + sizeY),
|
||||
new Vector2(posX, posY + sizeY)
|
||||
};
|
||||
|
||||
Color selectionColor = GUI.Style.Blue;
|
||||
float thickness = Math.Max(2f, 2f / Screen.Selected.Cam.Zoom);
|
||||
|
||||
GUI.DrawFilledRectangle(spriteBatch, corners[0], selectionSize, selectionColor * 0.1f);
|
||||
|
||||
Vector2 offset = new Vector2(0f, thickness / 2f);
|
||||
|
||||
if (sizeY < 0) { offset.Y = -offset.Y; }
|
||||
|
||||
spriteBatch.DrawLine(corners[0], corners[1], selectionColor, thickness);
|
||||
spriteBatch.DrawLine(corners[1] - offset, corners[2] + offset, selectionColor, thickness);
|
||||
spriteBatch.DrawLine(corners[2], corners[3], selectionColor, thickness);
|
||||
spriteBatch.DrawLine(corners[3] + offset, corners[0] - offset, selectionColor, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -824,8 +856,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
FilteredSelectedList.Clear();
|
||||
if (selectedList.Count == 0) return;
|
||||
foreach (var e in selectedList)
|
||||
if (SelectedList.Count == 0) return;
|
||||
foreach (var e in SelectedList)
|
||||
{
|
||||
if (e is Gap gap && gap.ConnectedDoor != null) { continue; }
|
||||
FilteredSelectedList.Add(e);
|
||||
@@ -844,15 +876,19 @@ namespace Barotrauma
|
||||
{
|
||||
if (PlayerInput.KeyHit(Keys.N))
|
||||
{
|
||||
float minX = selectedList[0].WorldRect.X, maxX = selectedList[0].WorldRect.Right;
|
||||
for (int i = 0; i < selectedList.Count; i++)
|
||||
MapEntity firstSelected = SelectedList.First();
|
||||
|
||||
float minX = firstSelected.WorldRect.X,
|
||||
maxX = firstSelected.WorldRect.Right;
|
||||
|
||||
foreach (MapEntity entity in SelectedList)
|
||||
{
|
||||
minX = Math.Min(minX, selectedList[i].WorldRect.X);
|
||||
maxX = Math.Max(maxX, selectedList[i].WorldRect.Right);
|
||||
minX = Math.Min(minX, entity.WorldRect.X);
|
||||
maxX = Math.Max(maxX, entity.WorldRect.Right);
|
||||
}
|
||||
|
||||
float centerX = (minX + maxX) / 2.0f;
|
||||
foreach (MapEntity me in selectedList)
|
||||
foreach (MapEntity me in SelectedList)
|
||||
{
|
||||
me.FlipX(false);
|
||||
me.Move(new Vector2((centerX - me.WorldPosition.X) * 2.0f, 0.0f));
|
||||
@@ -860,15 +896,20 @@ namespace Barotrauma
|
||||
}
|
||||
else if (PlayerInput.KeyHit(Keys.M))
|
||||
{
|
||||
float minY = selectedList[0].WorldRect.Y - selectedList[0].WorldRect.Height, maxY = selectedList[0].WorldRect.Y;
|
||||
for (int i = 0; i < selectedList.Count; i++)
|
||||
MapEntity firstSelected = SelectedList.First();
|
||||
|
||||
float minY = firstSelected.WorldRect.Y - firstSelected.WorldRect.Height,
|
||||
maxY = firstSelected.WorldRect.Y;
|
||||
|
||||
foreach (MapEntity entity in SelectedList)
|
||||
{
|
||||
minY = Math.Min(minY, selectedList[i].WorldRect.Y - selectedList[i].WorldRect.Height);
|
||||
maxY = Math.Max(maxY, selectedList[i].WorldRect.Y);
|
||||
|
||||
minY = Math.Min(minY, entity.WorldRect.Y - entity.WorldRect.Height);
|
||||
maxY = Math.Max(maxY, entity.WorldRect.Y);
|
||||
}
|
||||
|
||||
float centerY = (minY + maxY) / 2.0f;
|
||||
foreach (MapEntity me in selectedList)
|
||||
foreach (MapEntity me in SelectedList)
|
||||
{
|
||||
me.FlipY(false);
|
||||
me.Move(new Vector2(0.0f, (centerY - me.WorldPosition.Y) * 2.0f));
|
||||
@@ -879,19 +920,20 @@ namespace Barotrauma
|
||||
|
||||
public static void DrawEditor(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
if (selectedList.Count == 1)
|
||||
if (SelectedList.Count == 1)
|
||||
{
|
||||
selectedList[0].DrawEditing(spriteBatch, cam);
|
||||
if (selectedList[0].ResizeHorizontal || selectedList[0].ResizeVertical)
|
||||
MapEntity firstSelected = SelectedList.First();
|
||||
firstSelected.DrawEditing(spriteBatch, cam);
|
||||
if (firstSelected.ResizeHorizontal || firstSelected.ResizeVertical)
|
||||
{
|
||||
selectedList[0].DrawResizing(spriteBatch, cam);
|
||||
firstSelected.DrawResizing(spriteBatch, cam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeselectAll()
|
||||
{
|
||||
selectedList.Clear();
|
||||
SelectedList.Clear();
|
||||
}
|
||||
|
||||
public static void SelectEntity(MapEntity entity)
|
||||
@@ -926,10 +968,10 @@ namespace Barotrauma
|
||||
|
||||
public static void Paste(Vector2 position)
|
||||
{
|
||||
if (copiedList.Count == 0) { return; }
|
||||
if (CopiedList.Count == 0) { return; }
|
||||
|
||||
List<MapEntity> prevEntities = new List<MapEntity>(mapEntityList);
|
||||
Clone(copiedList);
|
||||
Clone(CopiedList);
|
||||
|
||||
var clones = mapEntityList.Except(prevEntities).ToList();
|
||||
var nonWireClones = clones.Where(c => !(c is Item item) || item.GetComponent<Wire>() == null);
|
||||
@@ -941,8 +983,8 @@ namespace Barotrauma
|
||||
|
||||
Vector2 moveAmount = Submarine.VectorToWorldGrid(position - center);
|
||||
|
||||
selectedList = new List<MapEntity>(clones);
|
||||
foreach (MapEntity clone in selectedList)
|
||||
SelectedList = new HashSet<MapEntity>(clones);
|
||||
foreach (MapEntity clone in SelectedList)
|
||||
{
|
||||
clone.Move(moveAmount);
|
||||
clone.Submarine = Submarine.MainSub;
|
||||
@@ -958,7 +1000,7 @@ namespace Barotrauma
|
||||
{
|
||||
List<MapEntity> prevEntities = new List<MapEntity>(mapEntityList);
|
||||
|
||||
copiedList = Clone(entities);
|
||||
CopiedList = Clone(entities);
|
||||
|
||||
//find all new entities created during cloning
|
||||
var newEntities = mapEntityList.Except(prevEntities).ToList();
|
||||
@@ -1131,9 +1173,9 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Find entities whose rect intersects with the "selection rect"
|
||||
/// </summary>
|
||||
public static List<MapEntity> FindSelectedEntities(Vector2 pos, Vector2 size)
|
||||
public static HashSet<MapEntity> FindSelectedEntities(Vector2 pos, Vector2 size)
|
||||
{
|
||||
List<MapEntity> foundEntities = new List<MapEntity>();
|
||||
HashSet<MapEntity> foundEntities = new HashSet<MapEntity>();
|
||||
|
||||
Rectangle selectionRect = Submarine.AbsRect(pos, size);
|
||||
|
||||
@@ -1141,7 +1183,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (!e.SelectableInEditor) continue;
|
||||
|
||||
if (Submarine.RectsOverlap(selectionRect, e.rect)) foundEntities.Add(e);
|
||||
if (Submarine.RectsOverlap(selectionRect, e.rect))
|
||||
{
|
||||
foundEntities.Add(e);
|
||||
e.IsIncludedInSelection = true;
|
||||
}
|
||||
}
|
||||
|
||||
return foundEntities;
|
||||
|
||||
@@ -127,7 +127,11 @@ namespace Barotrauma
|
||||
ToolTip = TextManager.Get("MirrorEntityXToolTip"),
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
FlipX(relativeToSub: false);
|
||||
foreach (MapEntity me in SelectedList)
|
||||
{
|
||||
me.FlipX(relativeToSub: false);
|
||||
}
|
||||
if (!SelectedList.Contains(this)) { FlipX(relativeToSub: false); }
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -136,7 +140,11 @@ namespace Barotrauma
|
||||
ToolTip = TextManager.Get("MirrorEntityYToolTip"),
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
FlipY(relativeToSub: false);
|
||||
foreach (MapEntity me in SelectedList)
|
||||
{
|
||||
me.FlipY(relativeToSub: false);
|
||||
}
|
||||
if (!SelectedList.Contains(this)) { FlipY(relativeToSub: false); }
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -145,7 +153,7 @@ namespace Barotrauma
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
Sprite.ReloadXML();
|
||||
Sprite.ReloadTexture();
|
||||
Sprite.ReloadTexture(updateAllSprites: true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -153,7 +161,12 @@ namespace Barotrauma
|
||||
{
|
||||
OnClicked = (button, data) =>
|
||||
{
|
||||
Reset();
|
||||
foreach (MapEntity me in SelectedList)
|
||||
{
|
||||
(me as Item)?.Reset();
|
||||
(me as Structure)?.Reset();
|
||||
}
|
||||
if (!SelectedList.Contains(this)) { Reset(); }
|
||||
CreateEditingHUD();
|
||||
return true;
|
||||
}
|
||||
@@ -247,7 +260,7 @@ namespace Barotrauma
|
||||
}
|
||||
else if (HiddenInGame) { return; }
|
||||
|
||||
Color color = IsHighlighted ? GUI.Style.Orange : spriteColor;
|
||||
Color color = IsIncludedInSelection && editing ? GUI.Style.Blue : IsHighlighted ? GUI.Style.Orange * Math.Max(spriteColor.A / (float) byte.MaxValue, 0.1f) : spriteColor;
|
||||
|
||||
if (IsSelected && editing)
|
||||
{
|
||||
|
||||
@@ -114,7 +114,6 @@ namespace Barotrauma
|
||||
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 };
|
||||
@@ -124,6 +123,15 @@ namespace Barotrauma
|
||||
dimensionsText.RectTransform.MinSize = new Point(0, dimensionsText.Children.First().Rect.Height);
|
||||
}
|
||||
|
||||
string cargoCapacityStr = CargoCapacity < 0 ? TextManager.Get("unknown") : TextManager.GetWithVariables("cargocapacityformat", new string[1] { "[cratecount]" }, new string[1] {CargoCapacity.ToString() });
|
||||
var cargoCapacityText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
|
||||
TextManager.Get("cargocapacity"), textAlignment: Alignment.TopLeft, font: font, wrap: true)
|
||||
{ CanBeFocused = false };
|
||||
new GUITextBlock(new RectTransform(new Vector2(rightPanelWidth, 0.0f), cargoCapacityText.RectTransform, Anchor.TopRight, Pivot.TopLeft),
|
||||
cargoCapacityStr, textAlignment: Alignment.TopLeft, font: font, wrap: true)
|
||||
{ CanBeFocused = false };
|
||||
cargoCapacityText.RectTransform.MinSize = new Point(0, cargoCapacityText.Children.First().Rect.Height);
|
||||
|
||||
if (RecommendedCrewSizeMax > 0)
|
||||
{
|
||||
var crewSizeText = new GUITextBlock(new RectTransform(new Vector2(leftPanelWidth, 0), parent.Content.RectTransform),
|
||||
|
||||
@@ -46,7 +46,10 @@ namespace Barotrauma
|
||||
if (IsHighlighted || IsHighlighted) { clr = Color.Lerp(clr, Color.White, 0.8f); }
|
||||
|
||||
int iconSize = spawnType == SpawnType.Path ? WaypointSize : SpawnPointSize;
|
||||
if (ConnectedDoor != 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)
|
||||
{
|
||||
@@ -98,10 +101,32 @@ namespace Barotrauma
|
||||
GUI.Style.Green * 0.5f, width: 1);
|
||||
}
|
||||
|
||||
var color = Color.WhiteSmoke;
|
||||
if (spawnType == SpawnType.Path)
|
||||
{
|
||||
if (linkedTo.Count < 2)
|
||||
{
|
||||
if (linkedTo.Count == 0)
|
||||
{
|
||||
color = Color.Red;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CurrentHull == null)
|
||||
{
|
||||
color = Ladders == null ? Color.Red : Color.Yellow;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.Yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GUI.SmallFont.DrawString(spriteBatch,
|
||||
ID.ToString(),
|
||||
new Vector2(DrawPosition.X - 10, -DrawPosition.Y - 30),
|
||||
Color.WhiteSmoke);
|
||||
color);
|
||||
}
|
||||
|
||||
public override bool IsMouseOn(Vector2 position)
|
||||
|
||||
@@ -53,100 +53,32 @@ namespace Barotrauma.Networking
|
||||
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());
|
||||
|
||||
Order orderPrefab = null;
|
||||
int? optionIndex = null;
|
||||
string orderOption = null;
|
||||
|
||||
// The option of a Dismiss order is written differently so we know what order we target
|
||||
// now that the game supports multiple current orders simultaneously
|
||||
if (orderIndex >= 0 && orderIndex < Order.PrefabList.Count)
|
||||
{
|
||||
orderPrefab = Order.PrefabList[orderIndex];
|
||||
if (orderPrefab.Identifier != "dismissed")
|
||||
{
|
||||
optionIndex = msg.ReadByte();
|
||||
}
|
||||
// Does the dismiss order have a specified target?
|
||||
else if (msg.ReadBoolean())
|
||||
{
|
||||
int identifierCount = msg.ReadByte();
|
||||
if (identifierCount > 0)
|
||||
{
|
||||
int dismissedOrderIndex = msg.ReadByte();
|
||||
Order dismissedOrderPrefab = null;
|
||||
if (dismissedOrderIndex >= 0 && dismissedOrderIndex < Order.PrefabList.Count)
|
||||
{
|
||||
dismissedOrderPrefab = Order.PrefabList[dismissedOrderIndex];
|
||||
orderOption = dismissedOrderPrefab.Identifier;
|
||||
}
|
||||
if (identifierCount > 1)
|
||||
{
|
||||
int dismissedOrderOptionIndex = msg.ReadByte();
|
||||
if (dismissedOrderPrefab != null)
|
||||
{
|
||||
var options = dismissedOrderPrefab.Options;
|
||||
if (options != null && dismissedOrderOptionIndex >= 0 && dismissedOrderOptionIndex < options.Length)
|
||||
{
|
||||
orderOption += $".{options[dismissedOrderOptionIndex]}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
optionIndex = msg.ReadByte();
|
||||
}
|
||||
|
||||
int orderPriority = msg.ReadByte();
|
||||
OrderTarget orderTargetPosition = null;
|
||||
Order.OrderTargetType orderTargetType = (Order.OrderTargetType)msg.ReadByte();
|
||||
int wallSectionIndex = 0;
|
||||
if (msg.ReadBoolean())
|
||||
{
|
||||
var x = msg.ReadSingle();
|
||||
var y = msg.ReadSingle();
|
||||
var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull;
|
||||
orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true);
|
||||
}
|
||||
else if(orderTargetType == Order.OrderTargetType.WallSection)
|
||||
{
|
||||
wallSectionIndex = msg.ReadByte();
|
||||
}
|
||||
|
||||
if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
|
||||
var orderMessageInfo = OrderChatMessage.ReadOrder(msg);
|
||||
if (orderMessageInfo.OrderIndex < 0 || orderMessageInfo.OrderIndex >= Order.PrefabList.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid order message - order index out of bounds.");
|
||||
if (NetIdUtils.IdMoreRecent(id, LastID)) { LastID = id; }
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
orderPrefab ??= Order.PrefabList[orderIndex];
|
||||
}
|
||||
|
||||
orderOption ??= optionIndex.HasValue && optionIndex >= 0 && optionIndex < orderPrefab.Options.Length ? orderPrefab.Options[optionIndex.Value] : "";
|
||||
txt = orderPrefab.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);
|
||||
var orderPrefab = orderMessageInfo.OrderPrefab ?? Order.PrefabList[orderMessageInfo.OrderIndex];
|
||||
string orderOption = orderMessageInfo.OrderOption;
|
||||
orderOption ??= orderMessageInfo.OrderOptionIndex.HasValue && orderMessageInfo.OrderOptionIndex >= 0 && orderMessageInfo.OrderOptionIndex < orderPrefab.Options.Length ?
|
||||
orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value] : "";
|
||||
txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter, orderOption: orderOption);
|
||||
|
||||
if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
|
||||
{
|
||||
Order order = null;
|
||||
switch (orderTargetType)
|
||||
switch (orderMessageInfo.TargetType)
|
||||
{
|
||||
case Order.OrderTargetType.Entity:
|
||||
order = new Order(orderPrefab, targetEntity, orderPrefab.GetTargetItemComponent(targetEntity as Item), orderGiver: senderCharacter);
|
||||
order = new Order(orderPrefab, orderMessageInfo.TargetEntity, orderPrefab.GetTargetItemComponent(orderMessageInfo.TargetEntity as Item), orderGiver: senderCharacter);
|
||||
break;
|
||||
case Order.OrderTargetType.Position:
|
||||
order = new Order(orderPrefab, orderTargetPosition, orderGiver: senderCharacter);
|
||||
order = new Order(orderPrefab, orderMessageInfo.TargetPosition, orderGiver: senderCharacter);
|
||||
break;
|
||||
case Order.OrderTargetType.WallSection:
|
||||
order = new Order(orderPrefab, targetEntity as Structure, wallSectionIndex, orderGiver: senderCharacter);
|
||||
order = new Order(orderPrefab, orderMessageInfo.TargetEntity as Structure, orderMessageInfo.WallSectionIndex, orderGiver: senderCharacter);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -157,9 +89,9 @@ namespace Barotrauma.Networking
|
||||
var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null;
|
||||
GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime);
|
||||
}
|
||||
else if (targetCharacter != null)
|
||||
else
|
||||
{
|
||||
targetCharacter.SetOrder(order, orderOption, orderPriority, senderCharacter);
|
||||
orderMessageInfo.TargetCharacter?.SetOrder(order, orderOption, orderMessageInfo.Priority, senderCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,7 +99,7 @@ namespace Barotrauma.Networking
|
||||
if (NetIdUtils.IdMoreRecent(id, LastID))
|
||||
{
|
||||
GameMain.Client.AddChatMessage(
|
||||
new OrderChatMessage(orderPrefab, orderOption, orderPriority, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter));
|
||||
new OrderChatMessage(orderPrefab, orderOption, orderMessageInfo.Priority, txt, orderMessageInfo.TargetPosition ?? orderMessageInfo.TargetEntity as ISpatialEntity, orderMessageInfo.TargetCharacter, senderCharacter));
|
||||
LastID = id;
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -527,7 +527,7 @@ namespace Barotrauma.Networking
|
||||
string pwMsg = TextManager.Get("PasswordRequired");
|
||||
|
||||
var msgBox = new GUIMessageBox(pwMsg, "", new string[] { TextManager.Get("OK"), TextManager.Get("Cancel") },
|
||||
relativeSize: new Vector2(0.25f, 0.1f), minSize: new Point(400, (int)(170 * Math.Max(1.0f, GUI.Scale))));
|
||||
relativeSize: new Vector2(0.25f, 0.1f), minSize: new Point(400, GUI.IntScale(170)));
|
||||
var passwordHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), msgBox.Content.RectTransform), childAnchor: Anchor.TopCenter);
|
||||
var passwordBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1f), passwordHolder.RectTransform) { MinSize = new Point(0, 20) })
|
||||
{
|
||||
@@ -537,7 +537,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (wrongPassword)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1f, 0.33f), passwordHolder.RectTransform), TextManager.Language == "English" ? TextManager.Get("incorrectpassword") : "Incorrect password", GUI.Style.Red, GUI.Font, textAlignment: Alignment.Center);
|
||||
var incorrectPasswordText = new GUITextBlock(new RectTransform(new Vector2(1f, 0.0f), passwordHolder.RectTransform), TextManager.Get("incorrectpassword"), GUI.Style.Red, GUI.Font, textAlignment: Alignment.Center);
|
||||
incorrectPasswordText.RectTransform.MinSize = new Point(0, (int)incorrectPasswordText.TextSize.Y);
|
||||
passwordHolder.Recalculate();
|
||||
}
|
||||
|
||||
@@ -643,7 +644,7 @@ namespace Barotrauma.Networking
|
||||
DebugConsole.ThrowError("Error while reading a message from server.", e);
|
||||
new GUIMessageBox(TextManager.Get("Error"), TextManager.GetWithVariables("MessageReadError", new string[2] { "[message]", "[targetsite]" }, new string[2] { e.Message, e.TargetSite.ToString() }));
|
||||
Disconnect();
|
||||
GameMain.MainMenuScreen.Select();
|
||||
GameMain.ServerListScreen.Select();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -659,10 +660,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
EndVoteTickBox.Visible = serverSettings.Voting.AllowEndVoting && HasSpawned && !(GameMain.GameSession?.GameMode is CampaignMode);
|
||||
|
||||
if (respawnManager != null)
|
||||
{
|
||||
respawnManager.Update(deltaTime);
|
||||
}
|
||||
respawnManager?.Update(deltaTime);
|
||||
|
||||
if (updateTimer <= DateTime.Now)
|
||||
{
|
||||
@@ -936,9 +934,6 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ServerPacketHeader.RESET_UPGRADES:
|
||||
campaign?.UpgradeManager.ClientRead(inc);
|
||||
break;
|
||||
case ServerPacketHeader.CREW:
|
||||
campaign?.ClientReadCrew(inc);
|
||||
break;
|
||||
@@ -993,15 +988,21 @@ namespace Barotrauma.Networking
|
||||
string errorMsg = $"Mission equality check failed. Mission count doesn't match the server (server: {missionCount}, client: {GameMain.GameSession.Missions.Count()})";
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
foreach (Mission mission in GameMain.GameSession.Missions)
|
||||
List<string> serverMissionIdentifiers = new List<string>();
|
||||
for (int i = 0; i < missionCount; i++)
|
||||
{
|
||||
string missionIdentifier = inc.ReadString() ?? "";
|
||||
if (missionIdentifier != mission.Prefab.Identifier)
|
||||
serverMissionIdentifiers.Add(inc.ReadString() ?? "");
|
||||
}
|
||||
|
||||
if (missionCount > 0)
|
||||
{
|
||||
if (!GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier).OrderBy(id => id).SequenceEqual(serverMissionIdentifiers.OrderBy(id => id)))
|
||||
{
|
||||
string errorMsg = $"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server (server: {missionIdentifier ?? "null"}, client: {mission.Prefab.Identifier})";
|
||||
string errorMsg = $"Mission equality check failed. The mission selected at your end doesn't match the one loaded by the server (server: {string.Join(", ", serverMissionIdentifiers)}, client: {string.Join(", ", GameMain.GameSession.Missions.Select(m => m.Prefab.Identifier))})";
|
||||
GameAnalyticsManager.AddErrorEventOnce("GameClient.StartGame:MissionsDontMatch" + Level.Loaded.Seed, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
GameMain.GameSession.EnforceMissionOrder(serverMissionIdentifiers);
|
||||
}
|
||||
|
||||
byte equalityCheckValueCount = inc.ReadByte();
|
||||
@@ -1046,6 +1047,11 @@ namespace Barotrauma.Networking
|
||||
mission.ClientReadInitial(inc);
|
||||
}
|
||||
|
||||
if (inc.ReadBoolean())
|
||||
{
|
||||
CrewManager.ClientReadActiveOrders(inc);
|
||||
}
|
||||
|
||||
roundInitStatus = RoundInitStatus.Started;
|
||||
}
|
||||
|
||||
@@ -1308,10 +1314,7 @@ namespace Barotrauma.Networking
|
||||
Client.ReadPermissions(inc, out permissions, out permittedCommands);
|
||||
|
||||
Client targetClient = ConnectedClients.Find(c => c.ID == clientID);
|
||||
if (targetClient != null)
|
||||
{
|
||||
targetClient.SetPermissions(permissions, permittedCommands);
|
||||
}
|
||||
targetClient?.SetPermissions(permissions, permittedCommands);
|
||||
if (clientID == myID)
|
||||
{
|
||||
SetMyPermissions(permissions, permittedCommands.Select(command => command.names[0]));
|
||||
@@ -1422,7 +1425,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
while (CoroutineManager.IsCoroutineRunning("EndGame"))
|
||||
{
|
||||
if (EndCinematic != null) { EndCinematic.Stop(); }
|
||||
EndCinematic?.Stop();
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
@@ -1609,6 +1612,9 @@ namespace Barotrauma.Networking
|
||||
DateTime? timeOut = null;
|
||||
DateTime requestFinalizeTime = DateTime.Now;
|
||||
TimeSpan requestFinalizeInterval = new TimeSpan(0, 0, 2);
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.REQUEST_STARTGAMEFINALIZE);
|
||||
clientPeer.Send(msg, DeliveryMethod.Unreliable);
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -1618,7 +1624,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (DateTime.Now > requestFinalizeTime)
|
||||
{
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.REQUEST_STARTGAMEFINALIZE);
|
||||
clientPeer.Send(msg, DeliveryMethod.Unreliable);
|
||||
requestFinalizeTime = DateTime.Now + requestFinalizeInterval;
|
||||
@@ -1648,12 +1654,11 @@ namespace Barotrauma.Networking
|
||||
break;
|
||||
}
|
||||
|
||||
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize) { break; }
|
||||
|
||||
clientPeer.Update((float)Timing.Step);
|
||||
|
||||
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize) { break; }
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -2089,6 +2094,7 @@ namespace Barotrauma.Networking
|
||||
float autoRestartTimer = autoRestartEnabled ? inc.ReadSingle() : 0.0f;
|
||||
|
||||
bool radiationEnabled = inc.ReadBoolean();
|
||||
byte maxMissionCount = inc.ReadByte();
|
||||
|
||||
//ignore the message if we already a more up-to-date one
|
||||
//or if we're still waiting for the initial update
|
||||
@@ -2154,6 +2160,7 @@ namespace Barotrauma.Networking
|
||||
GameMain.NetLobbyScreen.SetRadiationEnabled(radiationEnabled);
|
||||
GameMain.NetLobbyScreen.SetBotSpawnMode(botSpawnMode);
|
||||
GameMain.NetLobbyScreen.SetBotCount(botCount);
|
||||
GameMain.NetLobbyScreen.SetMaxMissionCount(maxMissionCount);
|
||||
GameMain.NetLobbyScreen.SetAutoRestart(autoRestartEnabled, autoRestartTimer);
|
||||
|
||||
serverSettings.VoiceChatEnabled = voiceChatEnabled;
|
||||
@@ -2752,6 +2759,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void Vote(VoteType voteType, object data)
|
||||
{
|
||||
if (clientPeer == null) return;
|
||||
|
||||
IWriteMessage msg = new WriteOnlyMessage();
|
||||
msg.Write((byte)ClientPacketHeader.UPDATE_LOBBY);
|
||||
msg.Write((byte)ClientNetObject.VOTE);
|
||||
@@ -3289,16 +3298,24 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
string respawnText = string.Empty;
|
||||
Color textColor = Color.White;
|
||||
bool canChooseRespawn =
|
||||
GameMain.GameSession.GameMode is CampaignMode &&
|
||||
Character.Controlled == null &&
|
||||
bool canChooseRespawn =
|
||||
GameMain.GameSession.GameMode is CampaignMode &&
|
||||
Character.Controlled == null &&
|
||||
Level.Loaded?.Type != LevelData.LevelType.Outpost &&
|
||||
(characterInfo == null || HasSpawned);
|
||||
if (respawnManager.CurrentState == RespawnManager.State.Waiting &&
|
||||
respawnManager.RespawnCountdownStarted)
|
||||
if (respawnManager.CurrentState == RespawnManager.State.Waiting)
|
||||
{
|
||||
float timeLeft = (float)(respawnManager.RespawnTime - DateTime.Now).TotalSeconds;
|
||||
respawnText = TextManager.GetWithVariable(respawnManager.UsingShuttle ? "RespawnShuttleDispatching" : "RespawningIn", "[time]", ToolBox.SecondsToReadableTime(timeLeft));
|
||||
if (respawnManager.RespawnCountdownStarted)
|
||||
{
|
||||
float timeLeft = (float)(respawnManager.RespawnTime - DateTime.Now).TotalSeconds;
|
||||
respawnText = TextManager.GetWithVariable(respawnManager.UsingShuttle ? "RespawnShuttleDispatching" : "RespawningIn", "[time]", ToolBox.SecondsToReadableTime(timeLeft));
|
||||
}
|
||||
else if (respawnManager.PendingRespawnCount > 0)
|
||||
{
|
||||
respawnText = TextManager.GetWithVariables("RespawnWaitingForMoreDeadPlayers",
|
||||
new string[] { "[deadplayers]", "[requireddeadplayers]" },
|
||||
new string[] { respawnManager.PendingRespawnCount.ToString(), respawnManager.RequiredRespawnCount.ToString() });
|
||||
}
|
||||
}
|
||||
else if (respawnManager.CurrentState == RespawnManager.State.Transporting &&
|
||||
respawnManager.ReturnCountdownStarted)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
@@ -8,6 +6,17 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
private DateTime lastShuttleLeavingWarningTime;
|
||||
|
||||
public int PendingRespawnCount
|
||||
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public int RequiredRespawnCount
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
partial void UpdateTransportingProjSpecific(float deltaTime)
|
||||
{
|
||||
if (GameMain.Client?.Character == null || GameMain.Client.Character.Submarine != RespawnShuttle) { return; }
|
||||
@@ -41,6 +50,8 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
break;
|
||||
case State.Waiting:
|
||||
PendingRespawnCount = msg.ReadUInt16();
|
||||
RequiredRespawnCount = msg.ReadUInt16();
|
||||
RespawnCountdownStarted = msg.ReadBoolean();
|
||||
ResetShuttle();
|
||||
float newRespawnTime = msg.ReadSingle();
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? radiationEnabled = null, bool? useRespawnShuttle = null)
|
||||
public void ClientAdminWrite(NetFlags dataToSend, int? missionTypeOr = null, int? missionTypeAnd = null, float? levelDifficulty = null, bool? autoRestart = null, int traitorSetting = 0, int botCount = 0, int botSpawnMode = 0, bool? radiationEnabled = null, bool? useRespawnShuttle = null, int maxMissionCount = 0)
|
||||
{
|
||||
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings)) return;
|
||||
|
||||
@@ -217,6 +217,8 @@ namespace Barotrauma.Networking
|
||||
outMsg.Write(autoRestart != null);
|
||||
outMsg.Write(autoRestart ?? false);
|
||||
outMsg.Write(radiationEnabled ?? RadiationEnabled);
|
||||
outMsg.Write((byte)maxMissionCount + 1);
|
||||
|
||||
outMsg.WritePadBits();
|
||||
}
|
||||
|
||||
@@ -745,6 +747,10 @@ namespace Barotrauma.Networking
|
||||
TextManager.Get("ServerSettingsAllowRewiring"));
|
||||
GetPropertyData("AllowRewiring").AssignGUIComponent(allowRewiring);
|
||||
|
||||
var allowWifiChatter = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowWifiChat"));
|
||||
GetPropertyData("AllowLinkingWifiToChat").AssignGUIComponent(allowWifiChatter);
|
||||
|
||||
var allowDisguises = new GUITickBox(new RectTransform(new Vector2(0.48f, 0.05f), tickBoxContainer.Content.RectTransform),
|
||||
TextManager.Get("ServerSettingsAllowDisguises"));
|
||||
GetPropertyData("AllowDisguises").AssignGUIComponent(allowDisguises);
|
||||
|
||||
@@ -26,8 +26,6 @@ namespace Barotrauma.Particles
|
||||
|
||||
private float angularVelocity;
|
||||
|
||||
private Vector2 dragVec = Vector2.Zero;
|
||||
private float dragWait = 0;
|
||||
private float collisionIgnoreTimer = 0;
|
||||
|
||||
private Vector2 size;
|
||||
@@ -35,6 +33,7 @@ namespace Barotrauma.Particles
|
||||
|
||||
private Color color;
|
||||
private bool changeColor;
|
||||
private bool UseMiddleColor;
|
||||
|
||||
private int spriteIndex;
|
||||
|
||||
@@ -66,7 +65,7 @@ namespace Barotrauma.Particles
|
||||
public Vector4 ColorMultiplier;
|
||||
|
||||
public bool DrawOnTop { get; private set; }
|
||||
|
||||
|
||||
public ParticlePrefab.DrawTargetType DrawTarget
|
||||
{
|
||||
get { return prefab.DrawTarget; }
|
||||
@@ -103,8 +102,7 @@ namespace Barotrauma.Particles
|
||||
{
|
||||
return debugName;
|
||||
}
|
||||
|
||||
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f)
|
||||
public void Init(ParticlePrefab prefab, Vector2 position, Vector2 speed, float rotation, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
{
|
||||
this.prefab = prefab;
|
||||
debugName = $"Particle ({prefab.Name})";
|
||||
@@ -113,11 +111,19 @@ namespace Barotrauma.Particles
|
||||
|
||||
animState = 0;
|
||||
animFrame = 0;
|
||||
dragWait = 0;
|
||||
dragVec = Vector2.Zero;
|
||||
|
||||
|
||||
currentHull = Hull.FindHull(position, hullGuess);
|
||||
|
||||
size = prefab.StartSizeMin + (prefab.StartSizeMax - prefab.StartSizeMin) * Rand.Range(0.0f, 1.0f);
|
||||
|
||||
if (tracerPoints != null)
|
||||
{
|
||||
size = new Vector2(Vector2.Distance(tracerPoints.Item1, tracerPoints.Item2), size.Y);
|
||||
position = (tracerPoints.Item1 + tracerPoints.Item2) / 2;
|
||||
}
|
||||
|
||||
sizeChange = prefab.SizeChangeMin + (prefab.SizeChangeMax - prefab.SizeChangeMin) * Rand.Range(0.0f, 1.0f);
|
||||
|
||||
this.position = position;
|
||||
prevPosition = position;
|
||||
|
||||
@@ -135,14 +141,21 @@ namespace Barotrauma.Particles
|
||||
|
||||
angularVelocity = Rand.Range(prefab.AngularVelocityMinRad, prefab.AngularVelocityMaxRad);
|
||||
|
||||
totalLifeTime = prefab.LifeTime;
|
||||
lifeTime = prefab.LifeTime;
|
||||
|
||||
if (prefab.LifeTimeMin <= 0.0f)
|
||||
{
|
||||
totalLifeTime = prefab.LifeTime;
|
||||
lifeTime = prefab.LifeTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
totalLifeTime = Rand.Range(prefab.LifeTimeMin, prefab.LifeTime);
|
||||
lifeTime = totalLifeTime;
|
||||
}
|
||||
|
||||
startDelay = Rand.Range(prefab.StartDelayMin, prefab.StartDelayMax);
|
||||
|
||||
size = prefab.StartSizeMin + (prefab.StartSizeMax - prefab.StartSizeMin) * Rand.Range(0.0f, 1.0f);
|
||||
|
||||
sizeChange = prefab.SizeChangeMin + (prefab.SizeChangeMax - prefab.SizeChangeMin) * Rand.Range(0.0f, 1.0f);
|
||||
|
||||
UseMiddleColor = prefab.UseMiddleColor;
|
||||
color = prefab.StartColor;
|
||||
changeColor = prefab.StartColor != prefab.EndColor;
|
||||
ColorMultiplier = Vector4.One;
|
||||
@@ -238,13 +251,27 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
|
||||
size.X += sizeChange.X * deltaTime;
|
||||
size.Y += sizeChange.Y * deltaTime;
|
||||
size.Y += sizeChange.Y * deltaTime;
|
||||
|
||||
if (changeColor)
|
||||
if (UseMiddleColor)
|
||||
{
|
||||
color = Color.Lerp(prefab.EndColor, prefab.StartColor, lifeTime / prefab.LifeTime);
|
||||
if (lifeTime > totalLifeTime * 0.5f)
|
||||
{
|
||||
color = Color.Lerp(prefab.MiddleColor, prefab.StartColor, (lifeTime / totalLifeTime - 0.5f) * 2.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.Lerp(prefab.EndColor, prefab.MiddleColor, lifeTime / totalLifeTime * 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (changeColor)
|
||||
{
|
||||
color = Color.Lerp(prefab.EndColor, prefab.StartColor, lifeTime / totalLifeTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (prefab.Sprites[spriteIndex] is SpriteSheet)
|
||||
{
|
||||
animState += deltaTime;
|
||||
@@ -394,29 +421,35 @@ namespace Barotrauma.Particles
|
||||
|
||||
private void ApplyDrag(float dragCoefficient, float deltaTime)
|
||||
{
|
||||
if (velocity.LengthSquared() < dragVec.LengthSquared())
|
||||
Vector2 relativeVel = velocity;
|
||||
if (currentHull?.Submarine != null)
|
||||
{
|
||||
velocity = Vector2.Zero;
|
||||
return;
|
||||
}
|
||||
if (Math.Abs(velocity.X) < 0.0001f && Math.Abs(velocity.Y) < 0.0001f) return;
|
||||
|
||||
//TODO: some better way to handle particle drag
|
||||
//this doesn't work that well because the drag vector is only updated every 0.5 seconds, allowing the particle to accelerate way more than it should
|
||||
//(e.g. a falling particle can freely accelerate for 0.5 seconds before the drag takes effect)
|
||||
dragWait-=deltaTime;
|
||||
if (dragWait <= 0f)
|
||||
{
|
||||
dragWait = 0.5f;
|
||||
|
||||
float speed = velocity.Length();
|
||||
|
||||
dragVec = (velocity / speed) * Math.Min(speed * speed * dragCoefficient * deltaTime, 1.0f);
|
||||
relativeVel = velocity - ConvertUnits.ToDisplayUnits(currentHull.Submarine.Velocity);
|
||||
}
|
||||
|
||||
velocity -= dragVec;
|
||||
float speed = relativeVel.Length();
|
||||
|
||||
relativeVel /= speed;
|
||||
|
||||
float drag = speed * speed * dragCoefficient * 0.01f * deltaTime;
|
||||
if (drag > speed)
|
||||
{
|
||||
relativeVel = Vector2.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
speed -= drag;
|
||||
relativeVel *= speed;
|
||||
}
|
||||
|
||||
velocity = relativeVel;
|
||||
if (currentHull?.Submarine != null)
|
||||
{
|
||||
velocity += ConvertUnits.ToDisplayUnits(currentHull.Submarine.Velocity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnWallCollisionInside(Hull prevHull, Vector2 collisionNormal)
|
||||
{
|
||||
if (prevHull == null) { return; }
|
||||
|
||||
@@ -1,14 +1,127 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Particles
|
||||
{
|
||||
class ParticleEmitterProperties : ISerializableEntity
|
||||
{
|
||||
private const float MinValue = int.MinValue,
|
||||
MaxValue = int.MaxValue;
|
||||
|
||||
public string Name => nameof(ParticleEmitter);
|
||||
|
||||
private float angleMin, angleMax;
|
||||
|
||||
public float AngleMinRad { get; private set; }
|
||||
public float AngleMaxRad { get; private set; }
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 360, MinValueFloat = -360f), Serialize(0f, true)]
|
||||
public float AngleMin
|
||||
{
|
||||
get => angleMin;
|
||||
set
|
||||
{
|
||||
angleMin = value;
|
||||
AngleMinRad = MathHelper.ToRadians(MathHelper.Clamp(value, -360.0f, 360.0f));
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 360, MinValueFloat = -360f), Serialize(0f, true)]
|
||||
public float AngleMax
|
||||
{
|
||||
get => angleMax;
|
||||
set
|
||||
{
|
||||
angleMax = value;
|
||||
AngleMaxRad = MathHelper.ToRadians(MathHelper.Clamp(value, -360.0f, 360.0f));
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(0f, true)]
|
||||
public float DistanceMin { get; set; }
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(0f, true)]
|
||||
public float DistanceMax { get; set; }
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(0f, true)]
|
||||
public float VelocityMin { get; set; }
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = MaxValue, MinValueFloat = MinValue), Serialize(0f, true)]
|
||||
public float VelocityMax { get; set; }
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 100.0f, MinValueFloat = 0.0f), Serialize(1f, true)]
|
||||
public float ScaleMin { get; set; }
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 100.0f, MinValueFloat = 0.0f), Serialize(1f, true)]
|
||||
public float ScaleMax { get; set; }
|
||||
|
||||
|
||||
[Editable(), Serialize("1,1", true)]
|
||||
public Vector2 ScaleMultiplier { get; set; }
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 100.0f, MinValueFloat = 0.0f), Serialize(0f, true)]
|
||||
public float EmitInterval { get; set; }
|
||||
|
||||
[Editable(ValueStep = 1, MinValueInt = 0, MaxValueInt = 1000), Serialize(0, true, description: "The number of particles to spawn per frame, or every x seconds if EmitInterval is set.")]
|
||||
public int ParticleAmount { get; set; }
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 1000.0f, MinValueFloat = 0.0f), Serialize(0f, true)]
|
||||
public float ParticlesPerSecond { get; set; }
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 10.0f, MinValueFloat = 0.0f), Serialize(0f, true, description: "If larger than 0, a particle is spawned every x pixels across the ray cast by a hitscan weapon.")]
|
||||
public float EmitAcrossRayInterval { get; set; }
|
||||
|
||||
[Editable(ValueStep = 1, DecimalCount = 2, MaxValueFloat = 100.0f, MinValueFloat = 0.0f), Serialize(0f, true, description: "Delay before the emitter becomes active after being created.")]
|
||||
public float InitialDelay { get; set; }
|
||||
|
||||
[Editable, Serialize(false, true)]
|
||||
public bool HighQualityCollisionDetection { get; set; }
|
||||
|
||||
[Editable, Serialize(false, true)]
|
||||
public bool CopyEntityAngle { get; set; }
|
||||
|
||||
[Editable, Serialize("1,1,1,1", true)]
|
||||
public Color ColorMultiplier { get; set; }
|
||||
|
||||
[Editable, Serialize(false, true)]
|
||||
public bool DrawOnTop { get; set; }
|
||||
|
||||
[Serialize(0f, true)]
|
||||
public float Angle
|
||||
{
|
||||
get => AngleMin;
|
||||
set => AngleMin = AngleMax = value;
|
||||
}
|
||||
|
||||
[Serialize(0f, true)]
|
||||
public float Distance
|
||||
{
|
||||
get => DistanceMin;
|
||||
set => DistanceMin = DistanceMax = value;
|
||||
}
|
||||
|
||||
[Serialize(0f, true)]
|
||||
public float Velocity
|
||||
{
|
||||
get => VelocityMin;
|
||||
set => VelocityMin = VelocityMax = value;
|
||||
}
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; }
|
||||
|
||||
public ParticleEmitterProperties(XElement element)
|
||||
{
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
}
|
||||
}
|
||||
|
||||
class ParticleEmitter
|
||||
{
|
||||
private float emitTimer;
|
||||
private float burstEmitTimer;
|
||||
private float initialDelay;
|
||||
|
||||
public readonly ParticleEmitterPrefab Prefab;
|
||||
|
||||
@@ -23,51 +136,76 @@ namespace Barotrauma.Particles
|
||||
Prefab = prefab;
|
||||
}
|
||||
|
||||
public void Emit(float deltaTime, Vector2 position, Hull hullGuess = null, float angle = 0.0f, float particleRotation = 0.0f, float velocityMultiplier = 1.0f, float sizeMultiplier = 1.0f, float amountMultiplier = 1.0f, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null)
|
||||
public void Emit(float deltaTime, Vector2 position, Hull hullGuess = null, float angle = 0.0f, float particleRotation = 0.0f, float velocityMultiplier = 1.0f, float sizeMultiplier = 1.0f, float amountMultiplier = 1.0f, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
{
|
||||
if (initialDelay < Prefab.Properties.InitialDelay)
|
||||
{
|
||||
initialDelay += deltaTime;
|
||||
return;
|
||||
}
|
||||
|
||||
emitTimer += deltaTime * amountMultiplier;
|
||||
burstEmitTimer -= deltaTime;
|
||||
|
||||
if (Prefab.ParticlesPerSecond > 0)
|
||||
if (Prefab.Properties.EmitAcrossRayInterval > 0.0f && tracerPoints != null)
|
||||
{
|
||||
float emitInterval = 1.0f / Prefab.ParticlesPerSecond;
|
||||
Vector2 dir = tracerPoints.Item2 - tracerPoints.Item1;
|
||||
if (dir.LengthSquared() > 0.001f)
|
||||
{
|
||||
float dist = dir.Length();
|
||||
dir /= dist;
|
||||
for (float z = 0.0f; z < dist; z += Prefab.Properties.EmitAcrossRayInterval)
|
||||
{
|
||||
Vector2 pos = tracerPoints.Item1 + dir * z;
|
||||
Emit(pos, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, tracerPoints: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Prefab.Properties.ParticlesPerSecond > 0)
|
||||
{
|
||||
float emitInterval = 1.0f / Prefab.Properties.ParticlesPerSecond;
|
||||
while (emitTimer > emitInterval)
|
||||
{
|
||||
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle);
|
||||
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, tracerPoints: tracerPoints);
|
||||
emitTimer -= emitInterval;
|
||||
}
|
||||
}
|
||||
|
||||
if (burstEmitTimer > 0.0f) { return; }
|
||||
|
||||
burstEmitTimer = Prefab.EmitInterval;
|
||||
for (int i = 0; i < Prefab.ParticleAmount * amountMultiplier; i++)
|
||||
|
||||
burstEmitTimer = Prefab.Properties.EmitInterval;
|
||||
for (int i = 0; i < Prefab.Properties.ParticleAmount * amountMultiplier; i++)
|
||||
{
|
||||
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle);
|
||||
Emit(position, hullGuess, angle, particleRotation, velocityMultiplier, sizeMultiplier, colorMultiplier, overrideParticle, tracerPoints: tracerPoints);
|
||||
}
|
||||
}
|
||||
|
||||
private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null)
|
||||
private void Emit(Vector2 position, Hull hullGuess, float angle, float particleRotation, float velocityMultiplier, float sizeMultiplier, Color? colorMultiplier = null, ParticlePrefab overrideParticle = null, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
{
|
||||
angle += Rand.Range(Prefab.AngleMin, Prefab.AngleMax);
|
||||
var particlePrefab = overrideParticle ?? Prefab.ParticlePrefab;
|
||||
if (particlePrefab == null) { return; }
|
||||
|
||||
angle += Rand.Range(Prefab.Properties.AngleMinRad, Prefab.Properties.AngleMaxRad);
|
||||
|
||||
Vector2 dir = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
|
||||
Vector2 velocity = dir * Rand.Range(Prefab.VelocityMin, Prefab.VelocityMax) * velocityMultiplier;
|
||||
position += dir * Rand.Range(Prefab.DistanceMin, Prefab.DistanceMax);
|
||||
Vector2 velocity = dir * Rand.Range(Prefab.Properties.VelocityMin, Prefab.Properties.VelocityMax) * velocityMultiplier;
|
||||
position += dir * Rand.Range(Prefab.Properties.DistanceMin, Prefab.Properties.DistanceMax);
|
||||
|
||||
var particle = GameMain.ParticleManager.CreateParticle(overrideParticle ?? Prefab.ParticlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop);
|
||||
var particle = GameMain.ParticleManager.CreateParticle(particlePrefab, position, velocity, particleRotation, hullGuess, Prefab.DrawOnTop, tracerPoints: tracerPoints);
|
||||
|
||||
if (particle != null)
|
||||
{
|
||||
particle.Size *= Rand.Range(Prefab.ScaleMin, Prefab.ScaleMax) * sizeMultiplier;
|
||||
particle.HighQualityCollisionDetection = Prefab.HighQualityCollisionDetection;
|
||||
if (colorMultiplier.HasValue)
|
||||
{
|
||||
particle.ColorMultiplier = colorMultiplier.Value.ToVector4();
|
||||
}
|
||||
else if (Prefab.ColorMultiplier != Color.White)
|
||||
particle.Size *= Rand.Range(Prefab.Properties.ScaleMin, Prefab.Properties.ScaleMax) * sizeMultiplier;
|
||||
particle.Size *= Prefab.Properties.ScaleMultiplier;
|
||||
particle.HighQualityCollisionDetection = Prefab.Properties.HighQualityCollisionDetection;
|
||||
if (colorMultiplier.HasValue)
|
||||
{
|
||||
particle.ColorMultiplier = Prefab.ColorMultiplier.ToVector4();
|
||||
particle.ColorMultiplier = colorMultiplier.Value.ToVector4();
|
||||
}
|
||||
else if (Prefab.Properties.ColorMultiplier != Color.White)
|
||||
{
|
||||
particle.ColorMultiplier = Prefab.Properties.ColorMultiplier.ToVector4();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,9 +214,9 @@ namespace Barotrauma.Particles
|
||||
{
|
||||
Rectangle bounds = new Rectangle((int)startPosition.X, (int)startPosition.Y, (int)startPosition.X, (int)startPosition.Y);
|
||||
|
||||
for (float angle = Prefab.AngleMin; angle <= Prefab.AngleMax; angle += 0.1f)
|
||||
for (float angle = Prefab.Properties.AngleMinRad; angle <= Prefab.Properties.AngleMaxRad; angle += 0.1f)
|
||||
{
|
||||
Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Prefab.VelocityMax;
|
||||
Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Prefab.Properties.VelocityMax;
|
||||
Vector2 endPosition = Prefab.ParticlePrefab.CalculateEndPosition(startPosition, velocity);
|
||||
|
||||
Vector2 endSize = Prefab.ParticlePrefab.CalculateEndSize();
|
||||
@@ -91,15 +229,15 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteExtent = Math.Max(spriteExtent, Math.Max(sprite.size.X * endSize.X, sprite.size.Y * endSize.Y));
|
||||
spriteExtent = Math.Max(spriteExtent, Math.Max(sprite.size.X * endSize.X, sprite.size.Y * endSize.Y));
|
||||
}
|
||||
}
|
||||
|
||||
bounds = new Rectangle(
|
||||
(int)Math.Min(bounds.X, endPosition.X - Prefab.DistanceMax - spriteExtent / 2),
|
||||
(int)Math.Min(bounds.Y, endPosition.Y - Prefab.DistanceMax - spriteExtent / 2),
|
||||
(int)Math.Max(bounds.X, endPosition.X + Prefab.DistanceMax + spriteExtent / 2),
|
||||
(int)Math.Max(bounds.Y, endPosition.Y + Prefab.DistanceMax + spriteExtent / 2));
|
||||
(int)Math.Min(bounds.X, endPosition.X - Prefab.Properties.DistanceMax - spriteExtent / 2),
|
||||
(int)Math.Min(bounds.Y, endPosition.Y - Prefab.Properties.DistanceMax - spriteExtent / 2),
|
||||
(int)Math.Max(bounds.X, endPosition.X + Prefab.Properties.DistanceMax + spriteExtent / 2),
|
||||
(int)Math.Max(bounds.Y, endPosition.Y + Prefab.Properties.DistanceMax + spriteExtent / 2));
|
||||
}
|
||||
|
||||
bounds = new Rectangle(bounds.X, bounds.Y, bounds.Width - bounds.X, bounds.Height - bounds.Y);
|
||||
@@ -109,9 +247,7 @@ namespace Barotrauma.Particles
|
||||
}
|
||||
|
||||
class ParticleEmitterPrefab
|
||||
{
|
||||
public readonly string Name;
|
||||
|
||||
{
|
||||
private string particlePrefabName;
|
||||
|
||||
private ParticlePrefab particlePrefab;
|
||||
@@ -122,103 +258,30 @@ namespace Barotrauma.Particles
|
||||
if (particlePrefab == null && particlePrefabName != null)
|
||||
{
|
||||
particlePrefab = GameMain.ParticleManager?.FindPrefab(particlePrefabName);
|
||||
if (particlePrefab == null) { particlePrefabName = null; }
|
||||
if (particlePrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find particle prefab \"{particlePrefabName}\".");
|
||||
particlePrefabName = null;
|
||||
}
|
||||
}
|
||||
return particlePrefab;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly float AngleMin, AngleMax;
|
||||
public readonly ParticleEmitterProperties Properties;
|
||||
|
||||
public readonly float DistanceMin, DistanceMax;
|
||||
|
||||
public readonly float VelocityMin, VelocityMax;
|
||||
|
||||
public readonly float ScaleMin, ScaleMax;
|
||||
|
||||
public readonly float EmitInterval;
|
||||
public readonly int ParticleAmount;
|
||||
|
||||
public readonly float ParticlesPerSecond;
|
||||
|
||||
public readonly bool HighQualityCollisionDetection;
|
||||
|
||||
public readonly bool CopyEntityAngle;
|
||||
|
||||
public readonly Color ColorMultiplier;
|
||||
|
||||
public bool DrawOnTop => forceDrawOnTop || ParticlePrefab.DrawOnTop;
|
||||
private readonly bool forceDrawOnTop;
|
||||
public bool DrawOnTop => Properties.DrawOnTop || ParticlePrefab.DrawOnTop;
|
||||
|
||||
public ParticleEmitterPrefab(XElement element)
|
||||
{
|
||||
Name = element.Name.ToString();
|
||||
Properties = new ParticleEmitterProperties(element);
|
||||
particlePrefabName = element.GetAttributeString("particle", "");
|
||||
}
|
||||
|
||||
if (element.Attribute("startrotation") == null)
|
||||
{
|
||||
AngleMin = element.GetAttributeFloat("anglemin", 0.0f);
|
||||
AngleMax = element.GetAttributeFloat("anglemax", 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
AngleMin = element.GetAttributeFloat("angle", 0.0f);
|
||||
AngleMax = AngleMin;
|
||||
}
|
||||
|
||||
AngleMin = MathHelper.ToRadians(MathHelper.Clamp(AngleMin, -360.0f, 360.0f));
|
||||
AngleMax = MathHelper.ToRadians(MathHelper.Clamp(AngleMax, -360.0f, 360.0f));
|
||||
|
||||
if (element.Attribute("scalemin") == null)
|
||||
{
|
||||
ScaleMin = 1.0f;
|
||||
ScaleMax = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
ScaleMin = element.GetAttributeFloat("scalemin", 1.0f);
|
||||
ScaleMax = Math.Max(ScaleMin, element.GetAttributeFloat("scalemax", 1.0f));
|
||||
}
|
||||
|
||||
if (element.Attribute("distance") == null)
|
||||
{
|
||||
DistanceMin = element.GetAttributeFloat("distancemin", 0.0f);
|
||||
DistanceMax = element.GetAttributeFloat("distancemax", 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
DistanceMin = DistanceMax = element.GetAttributeFloat("distance", 0.0f);
|
||||
}
|
||||
if (DistanceMax < DistanceMin)
|
||||
{
|
||||
var temp = DistanceMin;
|
||||
DistanceMin = DistanceMax;
|
||||
DistanceMax = temp;
|
||||
}
|
||||
|
||||
if (element.Attribute("velocity") == null)
|
||||
{
|
||||
VelocityMin = element.GetAttributeFloat("velocitymin", 0.0f);
|
||||
VelocityMax = element.GetAttributeFloat("velocitymax", 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
VelocityMin = VelocityMax = element.GetAttributeFloat("velocity", 0.0f);
|
||||
}
|
||||
if (VelocityMax < VelocityMin)
|
||||
{
|
||||
var temp = VelocityMin;
|
||||
VelocityMin = VelocityMax;
|
||||
VelocityMax = temp;
|
||||
}
|
||||
|
||||
EmitInterval = element.GetAttributeFloat("emitinterval", 0.0f);
|
||||
ParticlesPerSecond = element.GetAttributeFloat("particlespersecond", 0);
|
||||
ParticleAmount = element.GetAttributeInt("particleamount", 0);
|
||||
HighQualityCollisionDetection = element.GetAttributeBool("highqualitycollisiondetection", false);
|
||||
CopyEntityAngle = element.GetAttributeBool("copyentityangle", false);
|
||||
forceDrawOnTop = element.GetAttributeBool("drawontop", false);
|
||||
ColorMultiplier = element.GetAttributeColor("colormultiplier", Color.White);
|
||||
public ParticleEmitterPrefab(ParticlePrefab prefab, ParticleEmitterProperties properties)
|
||||
{
|
||||
Properties = properties;
|
||||
particlePrefab = prefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,13 +114,12 @@ namespace Barotrauma.Particles
|
||||
{
|
||||
Prefabs.RemoveByFile(configFile);
|
||||
}
|
||||
|
||||
public Particle CreateParticle(string prefabName, Vector2 position, float angle, float speed, Hull hullGuess = null, float collisionIgnoreTimer = 0f)
|
||||
public Particle CreateParticle(string prefabName, Vector2 position, float angle, float speed, Hull hullGuess = null, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
{
|
||||
return CreateParticle(prefabName, position, new Vector2((float)Math.Cos(angle), (float)-Math.Sin(angle)) * speed, angle, hullGuess, collisionIgnoreTimer);
|
||||
return CreateParticle(prefabName, position, new Vector2((float)Math.Cos(angle), (float)-Math.Sin(angle)) * speed, angle, hullGuess, collisionIgnoreTimer, tracerPoints: tracerPoints);
|
||||
}
|
||||
|
||||
public Particle CreateParticle(string prefabName, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, float collisionIgnoreTimer = 0f)
|
||||
public Particle CreateParticle(string prefabName, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
{
|
||||
ParticlePrefab prefab = FindPrefab(prefabName);
|
||||
|
||||
@@ -129,27 +128,30 @@ namespace Barotrauma.Particles
|
||||
DebugConsole.ThrowError("Particle prefab \"" + prefabName + "\" not found!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return CreateParticle(prefab, position, velocity, rotation, hullGuess, collisionIgnoreTimer: collisionIgnoreTimer);
|
||||
return CreateParticle(prefab, position, velocity, rotation, hullGuess, collisionIgnoreTimer: collisionIgnoreTimer, tracerPoints:tracerPoints);
|
||||
}
|
||||
|
||||
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f)
|
||||
public Particle CreateParticle(ParticlePrefab prefab, Vector2 position, Vector2 velocity, float rotation = 0.0f, Hull hullGuess = null, bool drawOnTop = false, float collisionIgnoreTimer = 0f, Tuple<Vector2, Vector2> tracerPoints = null)
|
||||
{
|
||||
if (particleCount >= MaxParticles || prefab == null || prefab.Sprites.Count == 0) { return null; }
|
||||
|
||||
// this should be optimized for tracers after prototyping
|
||||
if (tracerPoints == null)
|
||||
{
|
||||
Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity);
|
||||
|
||||
Vector2 particleEndPos = prefab.CalculateEndPosition(position, velocity);
|
||||
Vector2 minPos = new Vector2(Math.Min(position.X, particleEndPos.X), Math.Min(position.Y, particleEndPos.Y));
|
||||
Vector2 maxPos = new Vector2(Math.Max(position.X, particleEndPos.X), Math.Max(position.Y, particleEndPos.Y));
|
||||
|
||||
Vector2 minPos = new Vector2(Math.Min(position.X, particleEndPos.X), Math.Min(position.Y, particleEndPos.Y));
|
||||
Vector2 maxPos = new Vector2(Math.Max(position.X, particleEndPos.X), Math.Max(position.Y, particleEndPos.Y));
|
||||
Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
|
||||
|
||||
Rectangle expandedViewRect = MathUtils.ExpandRect(cam.WorldView, MaxOutOfViewDist);
|
||||
|
||||
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
|
||||
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
|
||||
if (minPos.X > expandedViewRect.Right || maxPos.X < expandedViewRect.X) { return null; }
|
||||
if (minPos.Y > expandedViewRect.Y || maxPos.Y < expandedViewRect.Y - expandedViewRect.Height) { return null; }
|
||||
}
|
||||
|
||||
if (particles[particleCount] == null) particles[particleCount] = new Particle();
|
||||
|
||||
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer);
|
||||
particles[particleCount].Init(prefab, position, velocity, rotation, hullGuess, drawOnTop, collisionIgnoreTimer, tracerPoints: tracerPoints);
|
||||
|
||||
particleCount++;
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ namespace Barotrauma.Particles
|
||||
[Editable(0.0f, float.MaxValue), Serialize(5.0f, false, description: "How many seconds the particle remains alive.")]
|
||||
public float LifeTime { get; private set; }
|
||||
|
||||
[Editable(0.0f, float.MaxValue), Serialize(0.0f, false, description: "Will randomize lifetime value between lifetime and lifetimeMin. If left to 0 will use only lifetime value.")]
|
||||
public float LifeTimeMin { get; private set; }
|
||||
|
||||
|
||||
[Editable, Serialize(0.0f, false, description: "How long it takes for the particle to appear after spawning it.")]
|
||||
public float StartDelayMin { get; private set; }
|
||||
[Editable, Serialize(0.0f, false, description: "How long it takes for the particle to appear after spawning it.")]
|
||||
@@ -118,10 +122,10 @@ namespace Barotrauma.Particles
|
||||
[Editable, Serialize(false, false, description: "Should the particle face the direction it's moving towards.")]
|
||||
public bool RotateToDirection { get; private set; }
|
||||
|
||||
[Editable, Serialize(0.0f, false, description: "Drag applied to the particle when it's moving through air.")]
|
||||
[Editable(0.0f, float.MaxValue, DecimalCount = 3), Serialize(0.0f, false, description: "Drag applied to the particle when it's moving through air.")]
|
||||
public float Drag { get; private set; }
|
||||
|
||||
[Editable, Serialize(0.0f, false, description: "Drag applied to the particle when it's moving through water.")]
|
||||
[Editable(0.0f, float.MaxValue, DecimalCount = 3), Serialize(0.0f, false, description: "Drag applied to the particle when it's moving through water.")]
|
||||
public float WaterDrag { get; private set; }
|
||||
|
||||
private Vector2 velocityChange;
|
||||
@@ -193,8 +197,14 @@ namespace Barotrauma.Particles
|
||||
[Editable, Serialize("1.0,1.0,1.0,1.0", false, description: "The initial color of the particle.")]
|
||||
public Color StartColor { get; private set; }
|
||||
|
||||
[Editable, Serialize("1.0,1.0,1.0,1.0", false, description: "The initial color of the particle.")]
|
||||
public Color MiddleColor { get; private set; }
|
||||
|
||||
[Editable, Serialize("1.0,1.0,1.0,1.0", false, description: "The color of the particle at the end of its lifetime.")]
|
||||
public Color EndColor { get; private set; }
|
||||
|
||||
[Editable, Serialize(false, false, description: "If true the color will go from StartColor to EndcColor and back to StartColor.")]
|
||||
public bool UseMiddleColor { get; private set; }
|
||||
|
||||
[Editable, Serialize(DrawTargetType.Air, false, description: "Should the particle be rendered in air, water or both.")]
|
||||
public DrawTargetType DrawTarget { get; private set; }
|
||||
@@ -289,7 +299,7 @@ namespace Barotrauma.Particles
|
||||
StartRotationMin = element.GetAttributeFloat("startrotation", 0.0f);
|
||||
StartRotationMax = StartRotationMin;
|
||||
}
|
||||
|
||||
|
||||
if (CollisionRadius <= 0.0f) CollisionRadius = Sprites.Count > 0 ? 1 : Sprites[0].SourceRect.Width / 2.0f;
|
||||
}
|
||||
|
||||
|
||||
@@ -443,7 +443,7 @@ namespace Barotrauma
|
||||
|
||||
public static bool KeyHit(Keys button)
|
||||
{
|
||||
return (AllowInput && oldKeyboardState.IsKeyDown(button) && keyboardState.IsKeyUp(button));
|
||||
return AllowInput && oldKeyboardState.IsKeyUp(button) && keyboardState.IsKeyDown(button);
|
||||
}
|
||||
|
||||
public static bool InventoryKeyHit(int index)
|
||||
@@ -454,7 +454,7 @@ namespace Barotrauma
|
||||
|
||||
public static bool KeyDown(Keys button)
|
||||
{
|
||||
return (AllowInput && keyboardState.IsKeyDown(button));
|
||||
return AllowInput && keyboardState.IsKeyDown(button);
|
||||
}
|
||||
|
||||
public static bool KeyUp(Keys button)
|
||||
|
||||
@@ -6,6 +6,7 @@ using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Globalization;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -42,6 +43,12 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public GUITickBox EnableRadiationToggle { get; set; }
|
||||
public GUILayoutGroup CampaignSettingsContent { get; set; }
|
||||
|
||||
public GUIButton CampaignCustomizeButton { get; set; }
|
||||
public GUIMessageBox CampaignCustomizeSettings { get; set; }
|
||||
|
||||
public GUITextBlock MaxMissionCountText;
|
||||
|
||||
private readonly bool isMultiplayer;
|
||||
|
||||
@@ -177,10 +184,19 @@ namespace Barotrauma
|
||||
if (isMultiplayer)
|
||||
{
|
||||
settings.RadiationEnabled = GameMain.NetLobbyScreen.IsRadiationEnabled();
|
||||
settings.MaxMissionCount = GameMain.NetLobbyScreen.GetMaxMissionCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.RadiationEnabled = EnableRadiationToggle?.Selected ?? false;
|
||||
if (MaxMissionCountText != null && Int32.TryParse(MaxMissionCountText.Text, out int missionCount))
|
||||
{
|
||||
settings.MaxMissionCount = missionCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.MaxMissionCount = CampaignSettings.DefaultMaxMissionCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedSub.HasTag(SubmarineTag.Shuttle) || !hasRequiredContentPackages)
|
||||
@@ -265,14 +281,14 @@ namespace Barotrauma
|
||||
|
||||
if (!isMultiplayer)
|
||||
{
|
||||
if (MapGenerationParams.Instance.RadiationParams != null)
|
||||
CampaignCustomizeButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1f), buttonContainer.RectTransform, Anchor.CenterLeft), TextManager.Get("SettingsButton"))
|
||||
{
|
||||
EnableRadiationToggle = new GUITickBox(new RectTransform(new Vector2(0.3f, 1f), buttonContainer.RectTransform), TextManager.Get("CampaignOption.EnableRadiation"), font: GUI.Style.Font)
|
||||
OnClicked = (tb, userdata) =>
|
||||
{
|
||||
Selected = true,
|
||||
ToolTip = TextManager.Get("campaignoption.enableradiation.tooltip")
|
||||
};
|
||||
}
|
||||
CreateCustomizeWindow();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(1.0f, 0.8f), rightColumn.RectTransform, Anchor.TopRight) { AbsoluteOffset = new Point(5) }, style: "GUINotificationButton")
|
||||
{
|
||||
@@ -290,6 +306,52 @@ namespace Barotrauma
|
||||
UpdateLoadMenu(saveFiles);
|
||||
}
|
||||
|
||||
private void CreateCustomizeWindow()
|
||||
{
|
||||
CampaignCustomizeSettings = new GUIMessageBox("", "", new string[] { TextManager.Get("OK") }, new Vector2(0.2f, 0.2f));
|
||||
CampaignCustomizeSettings.Buttons[0].OnClicked += CampaignCustomizeSettings.Close;
|
||||
|
||||
CampaignSettingsContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), CampaignCustomizeSettings.Content.RectTransform, Anchor.TopCenter))
|
||||
{
|
||||
RelativeSpacing = 0.1f
|
||||
};
|
||||
|
||||
if (MapGenerationParams.Instance.RadiationParams != null)
|
||||
{
|
||||
EnableRadiationToggle = new GUITickBox(new RectTransform(new Vector2(0.3f, 0.3f), CampaignSettingsContent.RectTransform), TextManager.Get("CampaignOption.EnableRadiation"), font: GUI.Style.Font)
|
||||
{
|
||||
Selected = true,
|
||||
ToolTip = TextManager.Get("campaignoption.enableradiation.tooltip")
|
||||
};
|
||||
}
|
||||
var maxMissionCountSettingHolder = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.3f), CampaignSettingsContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
Stretch = true,
|
||||
ToolTip = TextManager.Get("maxmissioncounttooltip")
|
||||
};
|
||||
var maxMissionCountDescription = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.0f), maxMissionCountSettingHolder.RectTransform), TextManager.Get("maxmissioncount", fallBackTag: "missions"), wrap: true);
|
||||
var maxMissionCountContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), maxMissionCountSettingHolder.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { RelativeSpacing = 0.05f, Stretch = true };
|
||||
var maxMissionCountButtons = new GUIButton[2];
|
||||
maxMissionCountButtons[0] = new GUIButton(new RectTransform(new Vector2(0.15f, 0.8f), maxMissionCountContainer.RectTransform), style: "GUIButtonToggleLeft")
|
||||
{
|
||||
OnClicked = (button, obj) =>
|
||||
{
|
||||
MaxMissionCountText.Text = Math.Clamp(Int32.Parse(MaxMissionCountText.Text) - 1, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit).ToString();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
MaxMissionCountText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), maxMissionCountContainer.RectTransform), CampaignSettings.DefaultMaxMissionCount.ToString(), textAlignment: Alignment.Center, style: "GUITextBox");
|
||||
maxMissionCountButtons[1] = new GUIButton(new RectTransform(new Vector2(0.15f, 0.8f), maxMissionCountContainer.RectTransform), style: "GUIButtonToggleRight")
|
||||
{
|
||||
OnClicked = (button, obj) =>
|
||||
{
|
||||
MaxMissionCountText.Text = Math.Clamp(Int32.Parse(MaxMissionCountText.Text) + 1, CampaignSettings.MinMissionCountLimit, CampaignSettings.MaxMissionCountLimit).ToString();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
maxMissionCountContainer.Children.ForEach(c => c.ToolTip = maxMissionCountSettingHolder.ToolTip);
|
||||
}
|
||||
|
||||
private void CreateMultiplayerCampaignSubList(RectTransform parent)
|
||||
{
|
||||
GUILayoutGroup subHolder = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.725f), parent))
|
||||
|
||||
@@ -21,6 +21,9 @@ namespace Barotrauma
|
||||
private GUIComponent locationInfoPanel;
|
||||
|
||||
private GUIListBox missionList;
|
||||
private readonly List<GUITickBox> missionTickBoxes = new List<GUITickBox>();
|
||||
|
||||
private bool hasMaxMissions;
|
||||
|
||||
private GUIButton repairHullsButton, replaceShuttlesButton, repairItemsButton;
|
||||
|
||||
@@ -51,9 +54,18 @@ namespace Barotrauma
|
||||
CreateUI(container);
|
||||
|
||||
campaign.Map.OnLocationSelected += SelectLocation;
|
||||
campaign.Map.OnMissionSelected += (connection, mission) =>
|
||||
campaign.Map.OnMissionsSelected += (connection, missions) =>
|
||||
{
|
||||
missionList?.Select(mission);
|
||||
if (missionList?.Content != null)
|
||||
{
|
||||
foreach (GUIComponent missionElement in missionList.Content.Children)
|
||||
{
|
||||
if (missionElement.FindChild(c => c is GUITickBox, recursive: true) is GUITickBox tickBox)
|
||||
{
|
||||
tickBox.Selected = missions.Contains(tickBox.UserData as Mission);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -311,6 +323,20 @@ namespace Barotrauma
|
||||
map.SelectLocation(-1);
|
||||
}
|
||||
map.Update(deltaTime, mapContainer);
|
||||
foreach (GUITickBox tickBox in missionTickBoxes)
|
||||
{
|
||||
bool disable = hasMaxMissions && !tickBox.Selected;
|
||||
tickBox.Enabled = Campaign.AllowedToManageCampaign() && !disable;
|
||||
tickBox.Box.DisabledColor = disable ? tickBox.Box.Color * 0.5f : tickBox.Box.Color * 0.8f;
|
||||
foreach (GUIComponent child in tickBox.Parent.Parent.Children)
|
||||
{
|
||||
if (child is GUITextBlock textBlock)
|
||||
{
|
||||
textBlock.SelectedTextColor = textBlock.HoverTextColor = textBlock.TextColor =
|
||||
disable ? new Color(textBlock.TextColor, 0.5f) : new Color(textBlock.TextColor, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
@@ -341,6 +367,7 @@ namespace Barotrauma
|
||||
|
||||
public void SelectLocation(Location location, LocationConnection connection)
|
||||
{
|
||||
missionTickBoxes.Clear();
|
||||
locationInfoPanel.ClearChildren();
|
||||
//don't select the map panel if we're looking at some other tab
|
||||
if (selectedTab == CampaignMode.InteractionType.Map)
|
||||
@@ -436,6 +463,19 @@ namespace Barotrauma
|
||||
{
|
||||
Spacing = (int)(5 * GUI.yScale)
|
||||
};
|
||||
missionList.OnSelected = (GUIComponent selected, object userdata) =>
|
||||
{
|
||||
var tickBox = selected.FindChild(c => c is GUITickBox, recursive: true) as GUITickBox;
|
||||
if (GUI.MouseOn == tickBox) { return false; }
|
||||
if (tickBox != null)
|
||||
{
|
||||
if (Campaign.AllowedToManageCampaign() && tickBox.Enabled)
|
||||
{
|
||||
tickBox.Selected = !tickBox.Selected;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
SelectedLevel = connection?.LevelData;
|
||||
Location currentDisplayLocation = Campaign.GetCurrentDisplayLocation();
|
||||
@@ -444,9 +484,6 @@ namespace Barotrauma
|
||||
List<Mission> availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).ToList();
|
||||
if (!availableMissions.Contains(null)) { availableMissions.Insert(0, null); }
|
||||
|
||||
Mission selectedMission = currentDisplayLocation.SelectedMission != null && availableMissions.Contains(currentDisplayLocation.SelectedMission) ?
|
||||
currentDisplayLocation.SelectedMission : null;
|
||||
|
||||
missionList.Content.ClearChildren();
|
||||
|
||||
foreach (Mission mission in availableMissions)
|
||||
@@ -458,67 +495,93 @@ namespace Barotrauma
|
||||
var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), missionPanel.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
CanBeFocused = true
|
||||
CanBeFocused = true,
|
||||
AbsoluteSpacing = GUI.IntScale(5)
|
||||
};
|
||||
|
||||
var missionName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission?.Name ?? TextManager.Get("NoMission"), font: GUI.SubHeadingFont, wrap: true);
|
||||
// missionName.RectTransform.MinSize = new Point(0, (int)(missionName.Rect.Height * 1.5f));
|
||||
if (mission != null)
|
||||
{
|
||||
if (MapGenerationParams.Instance?.MissionIcon != null)
|
||||
{
|
||||
var tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.X, 0) }, label: string.Empty)
|
||||
{
|
||||
var icon = new GUIImage(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.X, 0) },
|
||||
MapGenerationParams.Instance.MissionIcon, scaleToFit: true)
|
||||
{
|
||||
Color = MapGenerationParams.Instance.IndicatorColor * 0.5f,
|
||||
SelectedColor = MapGenerationParams.Instance.IndicatorColor,
|
||||
HoverColor = Color.Lerp(MapGenerationParams.Instance.IndicatorColor, Color.White, 0.5f)
|
||||
};
|
||||
icon.RectTransform.IsFixedSize = true;
|
||||
|
||||
GUILayoutGroup difficultyIndicatorGroup = null;
|
||||
if (mission.Difficulty.HasValue)
|
||||
{
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) },
|
||||
isHorizontal: true, childAnchor: Anchor.CenterRight)
|
||||
{
|
||||
AbsoluteSpacing = 1,
|
||||
UserData = "difficulty"
|
||||
};
|
||||
var difficultyColor = mission.GetDifficultyColor();
|
||||
for (int i = 0; i < mission.Difficulty; i++)
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
|
||||
{
|
||||
Color = difficultyColor * 0.5f,
|
||||
SelectedColor = difficultyColor,
|
||||
HoverColor = Color.Lerp(difficultyColor, Color.White, 0.5f)
|
||||
};
|
||||
}
|
||||
}
|
||||
UserData = mission,
|
||||
Selected = Campaign.Map.CurrentLocation?.SelectedMissions.Contains(mission) ?? false
|
||||
};
|
||||
tickBox.RectTransform.MinSize = new Point(tickBox.Rect.Height, 0);
|
||||
tickBox.RectTransform.IsFixedSize = true;
|
||||
tickBox.Enabled = Campaign.AllowedToManageCampaign();
|
||||
tickBox.OnSelected += (GUITickBox tb) =>
|
||||
{
|
||||
if (!Campaign.AllowedToManageCampaign()) { return false; }
|
||||
|
||||
float extraPadding = 0.5f * icon.Rect.Width;
|
||||
float extraZPadding = difficultyIndicatorGroup != null ? mission.Difficulty.Value * (difficultyIndicatorGroup.Children.First().Rect.Width + difficultyIndicatorGroup.AbsoluteSpacing) : 0;
|
||||
missionName.Padding = new Vector4(missionName.Padding.X + icon.Rect.Width + extraPadding,
|
||||
missionName.Padding.Y,
|
||||
missionName.Padding.Z + extraZPadding + extraPadding,
|
||||
missionName.Padding.W);
|
||||
missionName.CalculateHeightFromText();
|
||||
if (tb.Selected)
|
||||
{
|
||||
Campaign.Map.CurrentLocation.SelectMission(mission);
|
||||
}
|
||||
else
|
||||
{
|
||||
Campaign.Map.CurrentLocation.DeselectMission(mission);
|
||||
}
|
||||
|
||||
UpdateMaxMissions(connection.OtherLocation(currentDisplayLocation));
|
||||
|
||||
if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending &&
|
||||
Campaign.AllowedToManageCampaign())
|
||||
{
|
||||
GameMain.Client?.SendCampaignState();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
missionTickBoxes.Add(tickBox);
|
||||
|
||||
GUILayoutGroup difficultyIndicatorGroup = null;
|
||||
if (mission.Difficulty.HasValue)
|
||||
{
|
||||
difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, missionName.RectTransform, anchor: Anchor.CenterRight, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionName.Padding.Z, 0) },
|
||||
isHorizontal: true, childAnchor: Anchor.CenterRight)
|
||||
{
|
||||
AbsoluteSpacing = 1,
|
||||
UserData = "difficulty"
|
||||
};
|
||||
var difficultyColor = mission.GetDifficultyColor();
|
||||
for (int i = 0; i < mission.Difficulty; i++)
|
||||
{
|
||||
new GUIImage(new RectTransform(Vector2.One, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
|
||||
{
|
||||
Color = difficultyColor,
|
||||
SelectedColor = difficultyColor,
|
||||
HoverColor = difficultyColor
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.GetMissionRewardText(), wrap: true, parseRichText: true);
|
||||
float extraPadding = 0;// 0.8f * tickBox.Rect.Width;
|
||||
float extraZPadding = difficultyIndicatorGroup != null ? mission.Difficulty.Value * (difficultyIndicatorGroup.Children.First().Rect.Width + difficultyIndicatorGroup.AbsoluteSpacing) : 0;
|
||||
missionName.Padding = new Vector4(missionName.Padding.X + tickBox.Rect.Width * 1.2f + extraPadding,
|
||||
missionName.Padding.Y,
|
||||
missionName.Padding.Z + extraZPadding + extraPadding,
|
||||
missionName.Padding.W);
|
||||
missionName.CalculateHeightFromText();
|
||||
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(10)) }, style: null);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.GetMissionRewardText(Submarine.MainSub), wrap: true, parseRichText: true);
|
||||
|
||||
string reputationText = mission.GetReputationRewardText(mission.Locations[0]);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), reputationText, wrap: true, parseRichText: true);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), mission.Description, wrap: true, parseRichText: true);
|
||||
}
|
||||
missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(20));
|
||||
missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height + missionTextContent.AbsoluteSpacing) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(0));
|
||||
foreach (GUIComponent child in missionTextContent.Children)
|
||||
{
|
||||
var textBlock = child as GUITextBlock;
|
||||
textBlock.Color = textBlock.SelectedColor = textBlock.HoverColor = Color.Transparent;
|
||||
textBlock.HoverTextColor = textBlock.TextColor;
|
||||
textBlock.TextColor *= 0.5f;
|
||||
if (child is GUITextBlock textBlock)
|
||||
{
|
||||
textBlock.Color = textBlock.SelectedColor = textBlock.HoverColor = Color.Transparent;
|
||||
textBlock.SelectedTextColor = textBlock.HoverTextColor = textBlock.TextColor;
|
||||
}
|
||||
}
|
||||
missionPanel.OnAddedToGUIUpdateList = (c) =>
|
||||
{
|
||||
@@ -537,36 +600,33 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
}
|
||||
missionList.Select(selectedMission);
|
||||
if (prevSelectedLocation == selectedLocation)
|
||||
{
|
||||
missionList.BarScroll = prevMissionListScroll;
|
||||
}
|
||||
|
||||
if (Campaign.AllowedToManageCampaign())
|
||||
{
|
||||
missionList.OnSelected = (component, userdata) =>
|
||||
{
|
||||
Mission mission = userdata as Mission;
|
||||
if (Campaign.Map.CurrentLocation.SelectedMission == mission) { return false; }
|
||||
Campaign.Map.CurrentLocation.SelectedMission = mission;
|
||||
//RefreshMissionInfo(mission);
|
||||
if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending &&
|
||||
Campaign.AllowedToManageCampaign())
|
||||
{
|
||||
GameMain.Client?.SendCampaignState();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
missionList.UpdateDimensions();
|
||||
missionList.UpdateScrollBarSize();
|
||||
}
|
||||
}
|
||||
var destination = connection.OtherLocation(currentDisplayLocation);
|
||||
UpdateMaxMissions(destination);
|
||||
|
||||
StartButton = new GUIButton(new RectTransform(new Vector2(0.5f, 0.1f), content.RectTransform),
|
||||
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), content.RectTransform), isHorizontal: true);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), buttonArea.RectTransform), "", font: GUI.Style.SubHeadingFont)
|
||||
{
|
||||
TextGetter = () =>
|
||||
{
|
||||
return TextManager.AddPunctuation(':', TextManager.Get("Missions"), $"{Campaign.NumberOfMissionsAtLocation(destination)}/{Campaign.Settings.MaxMissionCount}");
|
||||
}
|
||||
};
|
||||
|
||||
StartButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonArea.RectTransform),
|
||||
TextManager.Get("StartCampaignButton"), style: "GUIButtonLarge")
|
||||
{
|
||||
OnClicked = (GUIButton btn, object obj) =>
|
||||
{
|
||||
if (missionList.Content.Children.Any(c => c.UserData is Mission) && !(missionList.SelectedData is Mission))
|
||||
if (missionList.Content.FindChild(c => c is GUITickBox tickBox && tickBox.Selected, recursive: true) == null &&
|
||||
missionList.Content.Children.Any(c => c.UserData is Mission))
|
||||
{
|
||||
var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), new string[] { TextManager.Get("yes"), TextManager.Get("no") });
|
||||
noMissionVerification.Buttons[0].OnClicked = (btn, userdata) =>
|
||||
@@ -587,6 +647,8 @@ namespace Barotrauma
|
||||
Visible = Campaign.AllowedToEndRound()
|
||||
};
|
||||
|
||||
buttonArea.RectTransform.MinSize = new Point(0, StartButton.RectTransform.MinSize.Y);
|
||||
|
||||
if (Level.Loaded != null &&
|
||||
connection?.LevelData == Level.Loaded.LevelData &&
|
||||
currentDisplayLocation == Campaign.Map?.CurrentLocation)
|
||||
@@ -594,6 +656,7 @@ namespace Barotrauma
|
||||
StartButton.Visible = false;
|
||||
missionList.Enabled = false;
|
||||
}
|
||||
//locationInfoPanel?.UpdateAuto(1.0f);
|
||||
}
|
||||
|
||||
public void SelectTab(CampaignMode.InteractionType tab)
|
||||
@@ -657,5 +720,10 @@ namespace Barotrauma
|
||||
{
|
||||
return TextManager.GetWithVariable("PlayerCredits", "[credits]", (GameMain.GameSession?.Campaign == null) ? "0" : string.Format(CultureInfo.InvariantCulture, "{0:N0}", GameMain.GameSession.Campaign.Money));
|
||||
}
|
||||
|
||||
private void UpdateMaxMissions(Location location)
|
||||
{
|
||||
hasMaxMissions = Campaign.NumberOfMissionsAtLocation(location) >= Campaign.Settings.MaxMissionCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4731,7 +4731,7 @@ namespace Barotrauma.CharacterEditor
|
||||
rotation: 0,
|
||||
origin: orig,
|
||||
sourceRectangle: wearable.InheritSourceRect ? limb.ActiveSprite.SourceRect : wearable.Sprite.SourceRect,
|
||||
scale: (wearable.InheritTextureScale ? 1 : 1 / RagdollParams.TextureScale) * spriteSheetZoom,
|
||||
scale: (wearable.InheritTextureScale ? 1 : wearable.Scale / RagdollParams.TextureScale) * spriteSheetZoom,
|
||||
effects: SpriteEffects.None,
|
||||
color: Color.White,
|
||||
layerDepth: 0);
|
||||
|
||||
@@ -305,7 +305,7 @@ namespace Barotrauma.CharacterEditor
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.3f, 1), mainElement.RectTransform, Anchor.CenterLeft), TextManager.Get("ContentPackage"));
|
||||
var rightContainer = new GUIFrame(new RectTransform(new Vector2(0.7f, 1), mainElement.RectTransform, Anchor.CenterRight), style: null);
|
||||
contentPackageDropDown = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.5f), rightContainer.RectTransform, Anchor.TopRight));
|
||||
foreach (ContentPackage cp in ContentPackage.AllPackages)
|
||||
foreach (ContentPackage cp in GameMain.Config.AllEnabledPackages)
|
||||
{
|
||||
#if !DEBUG
|
||||
if (cp == GameMain.VanillaContent) { continue; }
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class EditorScreen : Screen
|
||||
{
|
||||
public static Color BackgroundColor = GameSettings.SubEditorBackgroundColor;
|
||||
|
||||
public void CreateBackgroundColorPicker()
|
||||
{
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("CharacterEditor.EditBackgroundColor"), "", new[] { TextManager.Get("Reset"), TextManager.Get("OK")}, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
|
||||
|
||||
var rgbLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), msgBox.Content.RectTransform), isHorizontal: true);
|
||||
|
||||
// Generate R,G,B labels and parent elements
|
||||
var layoutParents = new GUILayoutGroup[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var colorContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.33f, 1), rgbLayout.RectTransform), isHorizontal: true) { Stretch = true };
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1), colorContainer.RectTransform, Anchor.CenterLeft) { MinSize = new Point(15, 0) }, GUI.colorComponentLabels[i], font: GUI.SmallFont, textAlignment: Alignment.Center);
|
||||
layoutParents[i] = colorContainer;
|
||||
}
|
||||
|
||||
// attach number inputs to our generated parent elements
|
||||
var rInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1f), layoutParents[0].RectTransform), GUINumberInput.NumberType.Int) { IntValue = BackgroundColor.R };
|
||||
var gInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1f), layoutParents[1].RectTransform), GUINumberInput.NumberType.Int) { IntValue = BackgroundColor.G };
|
||||
var bInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1f), layoutParents[2].RectTransform), GUINumberInput.NumberType.Int) { IntValue = BackgroundColor.B };
|
||||
|
||||
rInput.MinValueInt = gInput.MinValueInt = bInput.MinValueInt = 0;
|
||||
rInput.MaxValueInt = gInput.MaxValueInt = bInput.MaxValueInt = 255;
|
||||
|
||||
rInput.OnValueChanged = gInput.OnValueChanged = bInput.OnValueChanged = delegate
|
||||
{
|
||||
var color = new Color(rInput.IntValue, gInput.IntValue, bInput.IntValue);
|
||||
BackgroundColor = color;
|
||||
GameSettings.SubEditorBackgroundColor = color;
|
||||
};
|
||||
|
||||
// Reset button
|
||||
msgBox.Buttons[0].OnClicked = (button, o) =>
|
||||
{
|
||||
rInput.IntValue = 13;
|
||||
gInput.IntValue = 37;
|
||||
bInput.IntValue = 69;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Ok button
|
||||
msgBox.Buttons[1].OnClicked = (button, o) =>
|
||||
{
|
||||
msgBox.Close();
|
||||
GameMain.Config.SaveNewPlayerConfig();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,7 +227,7 @@ namespace Barotrauma
|
||||
public static GUIMessageBox AskForConfirmation(string header, string body, Func<bool> onConfirm)
|
||||
{
|
||||
string[] buttons = { TextManager.Get("Ok"), TextManager.Get("Cancel") };
|
||||
GUIMessageBox msgBox = new GUIMessageBox(header, body, buttons, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
|
||||
GUIMessageBox msgBox = new GUIMessageBox(header, body, buttons);
|
||||
|
||||
// Cancel button
|
||||
msgBox.Buttons[1].OnClicked = delegate
|
||||
|
||||
@@ -364,13 +364,15 @@ namespace Barotrauma
|
||||
Quad.Render();
|
||||
}
|
||||
|
||||
float grainStrength = Character.Controlled?.GrainStrength ?? 0;
|
||||
if (grainStrength > 0)
|
||||
if (Character.Controlled is { } character)
|
||||
{
|
||||
float grainStrength = character.GrainStrength;
|
||||
Rectangle screenRect = new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: GrainEffect);
|
||||
GUI.DrawRectangle(spriteBatch, screenRect, Color.White * grainStrength, isFilled: true);
|
||||
GUI.DrawRectangle(spriteBatch, screenRect, Color.White, isFilled: true);
|
||||
GrainEffect.Parameters["seed"].SetValue(Rand.Range(0f, 1f, Rand.RandSync.Unsynced));
|
||||
GrainEffect.Parameters["intensity"].SetValue(grainStrength);
|
||||
GrainEffect.Parameters["grainColor"].SetValue(character.GrainColor.ToVector4());
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
|
||||
@@ -469,18 +469,17 @@ namespace Barotrauma
|
||||
|
||||
this.game = game;
|
||||
|
||||
menuTabs[(int)Tab.Credits] = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null);
|
||||
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, menuTabs[(int)Tab.Credits].RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
menuTabs[(int)Tab.Credits] = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, menuTabs[(int)Tab.Credits].RectTransform, Anchor.Center), style: "GUIBackgroundBlocker")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
var creditsContainer = new GUIFrame(new RectTransform(new Vector2(0.75f, 1.5f), menuTabs[(int)Tab.Credits].RectTransform, Anchor.CenterRight), style: "OuterGlow", color: Color.Black * 0.8f);
|
||||
creditsPlayer = new CreditsPlayer(new RectTransform(Vector2.One, creditsContainer.RectTransform), "Content/Texts/Credits.xml");
|
||||
|
||||
new GUIButton(new RectTransform(new Vector2(0.1f, 0.05f), menuTabs[(int)Tab.Credits].RectTransform, Anchor.BottomLeft) { RelativeOffset = new Vector2(0.25f, 0.02f) },
|
||||
TextManager.Get("Back"), style: "GUIButtonLarge")
|
||||
{
|
||||
OnClicked = SelectTab
|
||||
};
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -867,9 +866,7 @@ namespace Barotrauma
|
||||
{
|
||||
int.TryParse(maxPlayersBox.Text, out int currMaxPlayers);
|
||||
currMaxPlayers = (int)MathHelper.Clamp(currMaxPlayers + (int)button.UserData, 1, NetConfig.MaxPlayers);
|
||||
|
||||
maxPlayersBox.Text = currMaxPlayers.ToString();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1167,15 +1164,18 @@ namespace Barotrauma
|
||||
StartNewGame = StartGame
|
||||
};
|
||||
|
||||
var startButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), innerNewGame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.BottomRight);
|
||||
var startButtonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), innerNewGame.RectTransform, Anchor.Center), isHorizontal: true, childAnchor: Anchor.BottomRight)
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
campaignSetupUI.StartButton.RectTransform.Parent = startButtonContainer.RectTransform;
|
||||
campaignSetupUI.StartButton.RectTransform.MinSize = new Point(
|
||||
(int)(campaignSetupUI.StartButton.TextBlock.TextSize.X * 1.5f),
|
||||
campaignSetupUI.StartButton.RectTransform.MinSize.Y);
|
||||
startButtonContainer.RectTransform.MinSize = new Point(0, campaignSetupUI.StartButton.RectTransform.MinSize.Y);
|
||||
if (campaignSetupUI.EnableRadiationToggle != null)
|
||||
if (campaignSetupUI.CampaignCustomizeButton != null)
|
||||
{
|
||||
campaignSetupUI.EnableRadiationToggle.RectTransform.Parent = startButtonContainer.RectTransform;
|
||||
campaignSetupUI.CampaignCustomizeButton.RectTransform.Parent = startButtonContainer.RectTransform;
|
||||
}
|
||||
campaignSetupUI.InitialMoneyText.RectTransform.Parent = startButtonContainer.RectTransform;
|
||||
}
|
||||
@@ -1322,8 +1322,18 @@ namespace Barotrauma
|
||||
};
|
||||
maxPlayersBox = new GUITextBox(new RectTransform(new Vector2(0.6f, 1.0f), buttonContainer.RectTransform), textAlignment: Alignment.Center)
|
||||
{
|
||||
Text = maxPlayers.ToString(),
|
||||
CanBeFocused = false
|
||||
Text = maxPlayers.ToString()
|
||||
};
|
||||
maxPlayersBox.OnEnterPressed += (GUITextBox sender, string text) =>
|
||||
{
|
||||
maxPlayersBox.Deselect();
|
||||
return true;
|
||||
};
|
||||
maxPlayersBox.OnDeselected += (GUITextBox sender, Microsoft.Xna.Framework.Input.Keys key) =>
|
||||
{
|
||||
int.TryParse(maxPlayersBox.Text, out int currMaxPlayers);
|
||||
currMaxPlayers = (int)MathHelper.Clamp(currMaxPlayers, 1, NetConfig.MaxPlayers);
|
||||
maxPlayersBox.Text = currMaxPlayers.ToString();
|
||||
};
|
||||
new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "GUIPlusButton", textAlignment: Alignment.Center)
|
||||
{
|
||||
|
||||
@@ -45,6 +45,10 @@ namespace Barotrauma
|
||||
|
||||
private readonly GUITickBox radiationEnabledTickBox;
|
||||
|
||||
private readonly GUIButton[] maxMissionCountButtons;
|
||||
private readonly GUITextBlock maxMissionCountText;
|
||||
private readonly GUITextBlock maxMissionCountDescription;
|
||||
|
||||
private readonly GUIButton[] traitorProbabilityButtons;
|
||||
private readonly GUITextBlock traitorProbabilityText;
|
||||
|
||||
@@ -929,7 +933,7 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
QuitCampaignButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.3f), campaignContent.RectTransform),
|
||||
TextManager.Get("pausemenusavequit"), textAlignment: Alignment.Center)
|
||||
TextManager.Get("quitbutton"), textAlignment: Alignment.Center)
|
||||
{
|
||||
OnClicked = (_, __) =>
|
||||
{
|
||||
@@ -969,6 +973,16 @@ namespace Barotrauma
|
||||
UserData = missionType,
|
||||
};
|
||||
|
||||
if (MissionPrefab.HiddenMissionClasses.Contains(missionType))
|
||||
{
|
||||
missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform), string.Empty)
|
||||
{
|
||||
UserData = (int)missionType,
|
||||
Visible = false,
|
||||
CanBeFocused = false
|
||||
};
|
||||
continue;
|
||||
}
|
||||
missionTypeTickBoxes[index] = new GUITickBox(new RectTransform(Vector2.One, frame.RectTransform),
|
||||
TextManager.Get("MissionType." + missionType.ToString()))
|
||||
{
|
||||
@@ -1148,6 +1162,31 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
var maxMissionCountSettingHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.1f), settingsContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
maxMissionCountDescription = new GUITextBlock(new RectTransform(new Vector2(0.7f, 0.0f), maxMissionCountSettingHolder.RectTransform), TextManager.Get("maxmissioncount", fallBackTag: "missions"), wrap: true)
|
||||
{
|
||||
ToolTip = TextManager.Get("maxmissioncounttooltip")
|
||||
};
|
||||
var maxMissionCountContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), maxMissionCountSettingHolder.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { RelativeSpacing = 0.05f, Stretch = true };
|
||||
maxMissionCountButtons = new GUIButton[2];
|
||||
maxMissionCountButtons[0] = new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), maxMissionCountContainer.RectTransform), style: "GUIButtonToggleLeft")
|
||||
{
|
||||
OnClicked = (button, obj) =>
|
||||
{
|
||||
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, maxMissionCount: -1);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
maxMissionCountText = new GUITextBlock(new RectTransform(new Vector2(0.7f, 1.0f), maxMissionCountContainer.RectTransform), "0", textAlignment: Alignment.Center, style: "GUITextBox");
|
||||
maxMissionCountButtons[1] = new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), maxMissionCountContainer.RectTransform), style: "GUIButtonToggleRight")
|
||||
{
|
||||
OnClicked = (button, obj) =>
|
||||
{
|
||||
GameMain.Client.ServerSettings.ClientAdminWrite(ServerSettings.NetFlags.Misc, maxMissionCount: 1);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
maxMissionCountSettingHolder.Children.ForEach(c => c.ToolTip = maxMissionCountSettingHolder.ToolTip);
|
||||
|
||||
List<GUIComponent> settingsElements = settingsContent.Children.ToList();
|
||||
for (int i = 0; i < settingsElements.Count; i++)
|
||||
@@ -1301,6 +1340,13 @@ namespace Barotrauma
|
||||
{
|
||||
radiationEnabledTickBox.Enabled = CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
|
||||
}
|
||||
maxMissionCountDescription.Enabled = CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
|
||||
maxMissionCountText.Enabled = CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
|
||||
foreach (var button in maxMissionCountButtons)
|
||||
{
|
||||
button.Enabled = CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
|
||||
}
|
||||
|
||||
traitorProbabilityButtons[0].Enabled = traitorProbabilityButtons[1].Enabled = traitorProbabilityText.Enabled =
|
||||
!CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
|
||||
botCountButtons[0].Enabled = botCountButtons[1].Enabled = GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
|
||||
@@ -1322,7 +1368,7 @@ namespace Barotrauma
|
||||
roundControlsHolder.Children.ForEach(c => c.IgnoreLayoutGroups = !c.Visible);
|
||||
roundControlsHolder.Recalculate();
|
||||
|
||||
ReadyToStartBox.Parent.Visible = !GameMain.Client.GameStarted && SelectedMode != GameModePreset.MultiPlayerCampaign;
|
||||
ReadyToStartBox.Parent.Visible = !GameMain.Client.GameStarted;
|
||||
|
||||
RefreshGameModeContent();
|
||||
}
|
||||
@@ -3214,7 +3260,12 @@ namespace Barotrauma
|
||||
{
|
||||
for (int i = 0; i < missionTypeTickBoxes.Length; i++)
|
||||
{
|
||||
MissionType missionType = (MissionType)((int)missionTypeTickBoxes[i].UserData);
|
||||
MissionType missionType = (MissionType)(int)missionTypeTickBoxes[i].UserData;
|
||||
if (MissionPrefab.HiddenMissionClasses.Contains(missionType))
|
||||
{
|
||||
missionTypeTickBoxes[i].Parent.Visible = false;
|
||||
continue;
|
||||
}
|
||||
if (SelectedMode == GameModePreset.Mission)
|
||||
{
|
||||
missionTypeTickBoxes[i].Parent.Visible = MissionPrefab.CoOpMissionClasses.ContainsKey(missionType);
|
||||
@@ -3269,7 +3320,7 @@ namespace Barotrauma
|
||||
CampaignFrame.Visible = CampaignSetupFrame.Visible = false;
|
||||
}
|
||||
|
||||
ReadyToStartBox.Parent.Visible = !GameMain.Client.GameStarted && SelectedMode != GameModePreset.MultiPlayerCampaign;
|
||||
ReadyToStartBox.Parent.Visible = !GameMain.Client.GameStarted;
|
||||
|
||||
StartButton.Visible =
|
||||
GameMain.Client.HasPermission(ClientPermissions.ManageRound) &&
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using System.Text;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics;
|
||||
#if DEBUG
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
@@ -15,57 +16,8 @@ using Barotrauma.IO;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class ParticleEditorScreen : Screen
|
||||
class ParticleEditorScreen : EditorScreen
|
||||
{
|
||||
class Emitter : ISerializableEntity
|
||||
{
|
||||
public float EmitTimer;
|
||||
|
||||
public float BurstTimer;
|
||||
|
||||
[Editable(), Serialize("0.0,360.0", false)]
|
||||
public Vector2 AngleRange { get; private set; }
|
||||
|
||||
[Editable(), Serialize("0.0,0.0", false)]
|
||||
public Vector2 VelocityRange { get; private set; }
|
||||
|
||||
[Editable(), Serialize("1.0,1.0", false)]
|
||||
public Vector2 ScaleRange { get; private set; }
|
||||
|
||||
[Editable(), Serialize(0, false)]
|
||||
public int ParticleBurstAmount { get; private set; }
|
||||
|
||||
[Editable(), Serialize(1.0f, false)]
|
||||
public float ParticleBurstInterval { get; private set; }
|
||||
|
||||
[Editable(), Serialize(1.0f, false)]
|
||||
public float ParticlesPerSecond { get; private set; }
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return TextManager.Get("particleeditor.emitter");
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Emitter()
|
||||
{
|
||||
ScaleRange = Vector2.One;
|
||||
AngleRange = new Vector2(0.0f, 360.0f);
|
||||
ParticleBurstAmount = 1;
|
||||
ParticleBurstInterval = 1.0f;
|
||||
|
||||
SerializableProperties = SerializableProperty.GetProperties(this);
|
||||
}
|
||||
}
|
||||
|
||||
private GUIComponent rightPanel, leftPanel;
|
||||
private GUIListBox prefabList;
|
||||
private GUITextBox filterBox;
|
||||
@@ -73,23 +25,38 @@ namespace Barotrauma
|
||||
|
||||
private ParticlePrefab selectedPrefab;
|
||||
|
||||
private Emitter emitter;
|
||||
private readonly ParticleEmitterProperties emitterProperties = new ParticleEmitterProperties(null)
|
||||
{
|
||||
ScaleMax = 1f,
|
||||
ScaleMin = 1f,
|
||||
AngleMax = 360f,
|
||||
AngleMin = 0,
|
||||
ParticlesPerSecond = 1f
|
||||
};
|
||||
|
||||
private ParticleEmitterPrefab emitterPrefab;
|
||||
private ParticleEmitter emitter;
|
||||
|
||||
private readonly Camera cam;
|
||||
|
||||
public override Camera Cam
|
||||
{
|
||||
get
|
||||
{
|
||||
return cam;
|
||||
}
|
||||
}
|
||||
public override Camera Cam => cam;
|
||||
|
||||
private const string sizeRefFilePath = "Content/size_reference.png";
|
||||
private readonly Texture2D sizeReference;
|
||||
private Vector2 sizeRefPosition = Vector2.Zero;
|
||||
private readonly Vector2 sizeRefOrigin;
|
||||
private bool sizeRefEnabled;
|
||||
|
||||
public ParticleEditorScreen()
|
||||
{
|
||||
cam = new Camera();
|
||||
GameMain.Instance.ResolutionChanged += CreateUI;
|
||||
CreateUI();
|
||||
if (File.Exists(sizeRefFilePath))
|
||||
{
|
||||
sizeReference = TextureLoader.FromFile(sizeRefFilePath, compress: false);
|
||||
sizeRefOrigin = new Vector2(sizeReference.Width / 2f, sizeReference.Height / 2f);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUI()
|
||||
@@ -122,8 +89,8 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
|
||||
var serializeToClipBoardButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform),
|
||||
TextManager.Get("editor.copytoclipboard"))
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform),
|
||||
TextManager.Get("ParticleEditor.CopyPrefabToClipboard"))
|
||||
{
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
@@ -132,11 +99,18 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
|
||||
emitter = new Emitter();
|
||||
var emitterEditorContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.25f), paddedRightPanel.RectTransform), style: null);
|
||||
var emitterEditor = new SerializableEntityEditor(emitterEditorContainer.RectTransform, emitter, false, true, elementHeight: 20, titleFont: GUI.SubHeadingFont);
|
||||
emitterEditor.RectTransform.RelativeSize = Vector2.One;
|
||||
emitterEditorContainer.RectTransform.Resize(new Point(emitterEditorContainer.RectTransform.NonScaledSize.X, emitterEditor.ContentHeight), false);
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 0.03f), paddedRightPanel.RectTransform),
|
||||
TextManager.Get("ParticleEditor.CopyEmitterToClipboard"))
|
||||
{
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
SerializeEmitterToClipboard();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var emitterListBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.25f), paddedRightPanel.RectTransform));
|
||||
new SerializableEntityEditor(emitterListBox.Content.RectTransform, emitterProperties, false, true, elementHeight: 20, titleFont: GUI.SubHeadingFont);
|
||||
|
||||
var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.6f), paddedRightPanel.RectTransform));
|
||||
|
||||
@@ -157,7 +131,10 @@ namespace Barotrauma
|
||||
prefabList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.8f), paddedLeftPanel.RectTransform));
|
||||
prefabList.OnSelected += (GUIComponent component, object obj) =>
|
||||
{
|
||||
cam.Position = Vector2.Zero;
|
||||
selectedPrefab = obj as ParticlePrefab;
|
||||
emitterPrefab = new ParticleEmitterPrefab(selectedPrefab, emitterProperties);
|
||||
emitter = new ParticleEmitter(emitterPrefab);
|
||||
listBox.ClearChildren();
|
||||
new SerializableEntityEditor(listBox.Content.RectTransform, selectedPrefab, false, true, elementHeight: 20, titleFont: GUI.SubHeadingFont);
|
||||
//listBox.Content.RectTransform.NonScaledSize = particlePrefabEditor.RectTransform.NonScaledSize;
|
||||
@@ -198,19 +175,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void Emit(Vector2 position)
|
||||
{
|
||||
float angle = MathHelper.ToRadians(Rand.Range(emitter.AngleRange.X, emitter.AngleRange.Y));
|
||||
Vector2 velocity = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Rand.Range(emitter.VelocityRange.X, emitter.VelocityRange.Y);
|
||||
|
||||
var particle = GameMain.ParticleManager.CreateParticle(selectedPrefab, position, velocity, 0.0f);
|
||||
|
||||
if (particle != null)
|
||||
{
|
||||
particle.Size *= Rand.Range(emitter.ScaleRange.X, emitter.ScaleRange.Y);
|
||||
}
|
||||
}
|
||||
|
||||
private void FilterEmitters(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
@@ -263,9 +227,34 @@ namespace Barotrauma
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
}
|
||||
|
||||
private void SerializeEmitterToClipboard()
|
||||
{
|
||||
XElement element = new XElement(nameof(ParticleEmitter));
|
||||
if (selectedPrefab is { } prefab)
|
||||
{
|
||||
element.Add(new XAttribute("particle", prefab.Identifier));
|
||||
}
|
||||
|
||||
SerializableProperty.SerializeProperties(emitterProperties, element, saveIfDefault: false, ignoreEditable: true);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
|
||||
{
|
||||
OmitXmlDeclaration = true
|
||||
};
|
||||
|
||||
using (var writer = System.Xml.XmlWriter.Create(sb, settings))
|
||||
{
|
||||
element.WriteTo(writer);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
Clipboard.SetText(sb.ToString());
|
||||
}
|
||||
|
||||
private void SerializeToClipboard(ParticlePrefab prefab)
|
||||
{
|
||||
#if WINDOWS
|
||||
if (prefab == null) { return; }
|
||||
|
||||
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
|
||||
@@ -308,43 +297,39 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Clipboard.SetText(sb.ToString());
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
cam.MoveCamera((float)deltaTime, allowMove: true, allowZoom: GUI.MouseOn == null);
|
||||
|
||||
if (selectedPrefab != null)
|
||||
|
||||
if (GUI.MouseOn is null && PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
emitter.EmitTimer += (float)deltaTime;
|
||||
emitter.BurstTimer += (float)deltaTime;
|
||||
sizeRefPosition = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
}
|
||||
|
||||
if (PlayerInput.SecondaryMouseButtonClicked())
|
||||
{
|
||||
CreateContextMenu();
|
||||
}
|
||||
|
||||
if (emitter.ParticlesPerSecond > 0)
|
||||
{
|
||||
float emitInterval = 1.0f / emitter.ParticlesPerSecond;
|
||||
while (emitter.EmitTimer > emitInterval)
|
||||
{
|
||||
Emit(Vector2.Zero);
|
||||
emitter.EmitTimer -= emitInterval;
|
||||
}
|
||||
}
|
||||
|
||||
if (emitter.BurstTimer > emitter.ParticleBurstInterval)
|
||||
{
|
||||
for (int i = 0; i < emitter.ParticleBurstAmount; i++)
|
||||
{
|
||||
Emit(Vector2.Zero);
|
||||
}
|
||||
emitter.BurstTimer = 0.0f;
|
||||
}
|
||||
|
||||
if (selectedPrefab != null && emitter != null)
|
||||
{
|
||||
emitter.Emit((float) deltaTime, Vector2.Zero);
|
||||
}
|
||||
|
||||
GameMain.ParticleManager.Update((float)deltaTime);
|
||||
}
|
||||
|
||||
private void CreateContextMenu()
|
||||
{
|
||||
GUIContextMenu.CreateContextMenu
|
||||
(
|
||||
new ContextMenuOption("subeditor.editbackgroundcolor", true, CreateBackgroundColorPicker),
|
||||
new ContextMenuOption("editor.togglereferencecharacter", true, delegate { sizeRefEnabled = !sizeRefEnabled; })
|
||||
);
|
||||
}
|
||||
|
||||
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
|
||||
{
|
||||
cam.UpdateTransform();
|
||||
@@ -357,7 +342,7 @@ namespace Barotrauma
|
||||
null, null, null, null,
|
||||
cam.Transform);
|
||||
|
||||
graphics.Clear(new Color(0.051f, 0.149f, 0.271f, 1.0f));
|
||||
graphics.Clear(BackgroundColor);
|
||||
|
||||
GameMain.ParticleManager.Draw(spriteBatch, false, false, ParticleBlendState.AlphaBlend);
|
||||
GameMain.ParticleManager.Draw(spriteBatch, true, false, ParticleBlendState.AlphaBlend);
|
||||
@@ -374,6 +359,20 @@ namespace Barotrauma
|
||||
|
||||
spriteBatch.End();
|
||||
|
||||
if (sizeRefEnabled && !(sizeReference is null))
|
||||
{
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred,
|
||||
BlendState.NonPremultiplied,
|
||||
null, null, null, null,
|
||||
cam.Transform);
|
||||
|
||||
Vector2 pos = sizeRefPosition;
|
||||
pos.Y = -pos.Y;
|
||||
spriteBatch.Draw(sizeReference, pos, null, Color.White, 0f, sizeRefOrigin, new Vector2(0.4f), SpriteEffects.None, 0f);
|
||||
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, null, GUI.SamplerState, null, GameMain.ScissorTestEnable);
|
||||
|
||||
@@ -66,6 +66,8 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public virtual void OnFileDropped(string filePath, string extension) { }
|
||||
|
||||
public virtual void Release()
|
||||
{
|
||||
frame.RectTransform.Parent = null;
|
||||
|
||||
@@ -363,7 +363,10 @@ namespace Barotrauma
|
||||
"VineSprite",
|
||||
"LeafSprite",
|
||||
"FlowerSprite",
|
||||
"DecorativeSprite"
|
||||
"DecorativeSprite",
|
||||
"BarrelSprite",
|
||||
"RailSprite",
|
||||
"SchematicSprite"
|
||||
};
|
||||
|
||||
foreach (string spriteElementName in spriteElementNames)
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace Barotrauma
|
||||
//listbox that shows the files included in the item being created
|
||||
private GUIListBox createItemFileList;
|
||||
|
||||
private GUIImage previewIcon;
|
||||
|
||||
private System.IO.FileSystemWatcher createItemWatcher;
|
||||
|
||||
private readonly List<GUIButton> tabButtons = new List<GUIButton>();
|
||||
@@ -277,6 +279,24 @@ namespace Barotrauma
|
||||
SelectTab(Tab.Mods);
|
||||
}
|
||||
|
||||
public override void OnFileDropped(string filePath, string extension)
|
||||
{
|
||||
switch (extension)
|
||||
{
|
||||
case ".png": // workshop preview
|
||||
case ".jpg":
|
||||
case ".jpeg":
|
||||
if (previewIcon == null || itemContentPackage == null) { break; }
|
||||
|
||||
OnPreviewImageSelected(previewIcon, filePath);
|
||||
break;
|
||||
|
||||
default:
|
||||
DebugConsole.ThrowError($"Could not drag and drop the file. \"{extension}\" is not a valid file extension! (expected .png, .jpg or .jpeg)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemInstalled(ulong itemId)
|
||||
{
|
||||
RefreshSubscribedItems();
|
||||
@@ -1263,7 +1283,7 @@ namespace Barotrauma
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), topLeftColumn.RectTransform), TextManager.Get("WorkshopItemPreviewImage"), font: GUI.SubHeadingFont);
|
||||
|
||||
var previewIcon = new GUIImage(new RectTransform(new Vector2(1.0f, 0.7f), topLeftColumn.RectTransform), SteamManager.DefaultPreviewImage, scaleToFit: true);
|
||||
previewIcon = new GUIImage(new RectTransform(new Vector2(1.0f, 0.7f), topLeftColumn.RectTransform), SteamManager.DefaultPreviewImage, scaleToFit: true);
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 0.2f), topLeftColumn.RectTransform), TextManager.Get("WorkshopItemBrowse"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (btn, userdata) =>
|
||||
|
||||
@@ -19,7 +19,7 @@ using Barotrauma.IO;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class SubEditorScreen : Screen
|
||||
class SubEditorScreen : EditorScreen
|
||||
{
|
||||
private static readonly string[] crewExperienceLevels =
|
||||
{
|
||||
@@ -177,8 +177,6 @@ namespace Barotrauma
|
||||
|
||||
private Mode mode;
|
||||
|
||||
private Color backgroundColor = GameSettings.SubEditorBackgroundColor;
|
||||
|
||||
private Vector2 MeasurePositionStart = Vector2.Zero;
|
||||
|
||||
// Prevent the mode from changing
|
||||
@@ -1007,6 +1005,12 @@ namespace Barotrauma
|
||||
string name = legacy ? TextManager.GetWithVariable("legacyitemformat", "[name]", ep.Name) : ep.Name;
|
||||
frame.ToolTip = string.IsNullOrEmpty(ep.Description) ? name : name + '\n' + ep.Description;
|
||||
|
||||
if (ep.ContentPackage != GameMain.VanillaContent && ep.ContentPackage != null)
|
||||
{
|
||||
frame.Color = Color.Magenta;
|
||||
string colorStr = XMLExtensions.ColorToString(Color.MediumPurple);
|
||||
frame.ToolTip += $"\n‖color:{colorStr}‖{ep.ContentPackage?.Name}‖color:end‖";
|
||||
}
|
||||
if (ep.HideInMenus)
|
||||
{
|
||||
frame.Color = Color.Red;
|
||||
@@ -1225,6 +1229,50 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnFileDropped(string filePath, string extension)
|
||||
{
|
||||
switch (extension)
|
||||
{
|
||||
case ".sub": // Submarine
|
||||
SubmarineInfo info = new SubmarineInfo(filePath);
|
||||
if (info.IsFileCorrupted)
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not drag and drop the file. File \"{filePath}\" is corrupted!");
|
||||
info.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
string body = TextManager.GetWithVariable("SubEditor.LoadConfirmBody", "[submarine]", info.Name);
|
||||
GUI.AskForConfirmation(TextManager.Get("Load"), body, onConfirm: () => LoadSub(info), onDeny: () => info.Dispose());
|
||||
break;
|
||||
|
||||
case ".xml": // Item Assembly
|
||||
string text = File.ReadAllText(filePath);
|
||||
// PlayerInput.MousePosition doesn't update while the window is not active so we need to use this method
|
||||
Vector2 mousePos = Mouse.GetState().Position.ToVector2();
|
||||
PasteAssembly(text, cam.ScreenToWorld(mousePos));
|
||||
break;
|
||||
|
||||
case ".png": // submarine preview
|
||||
case ".jpg":
|
||||
case ".jpeg":
|
||||
if (saveFrame == null) { break; }
|
||||
|
||||
Texture2D texture = Sprite.LoadTexture(filePath);
|
||||
previewImage.Sprite = new Sprite(texture, null, null);
|
||||
if (Submarine.MainSub != null)
|
||||
{
|
||||
Submarine.MainSub.Info.PreviewImage = previewImage.Sprite;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
DebugConsole.ThrowError($"Could not drag and drop the file. \"{extension}\" is not a valid file extension! (expected .xml, .sub, .png or .jpg)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that waits 5 minutes and then runs itself recursively again to save the submarine into a temporary file
|
||||
/// </summary>
|
||||
@@ -1466,6 +1514,9 @@ namespace Barotrauma
|
||||
case SubmarineType.Wreck:
|
||||
contentType = ContentType.Wreck;
|
||||
break;
|
||||
case SubmarineType.EnemySubmarine:
|
||||
contentType = ContentType.EnemySubmarine;
|
||||
break;
|
||||
}
|
||||
if (contentType != ContentType.Submarine)
|
||||
{
|
||||
@@ -1504,8 +1555,13 @@ namespace Barotrauma
|
||||
if (!string.IsNullOrEmpty(specialSavePath) &&
|
||||
(string.IsNullOrEmpty(Submarine.MainSub?.Info.FilePath) || Path.GetFileNameWithoutExtension(Submarine.MainSub.Info.Name) != nameBox.Text || Path.GetDirectoryName(Submarine.MainSub?.Info.FilePath) != specialSavePath))
|
||||
{
|
||||
string submarineTypeTag = "SubmarineType." + Submarine.MainSub.Info.Type;
|
||||
if (Submarine.MainSub.Info.Type == SubmarineType.EnemySubmarine && !TextManager.ContainsTag(submarineTypeTag))
|
||||
{
|
||||
submarineTypeTag = "MissionType.Pirate";
|
||||
}
|
||||
var msgBox = new GUIMessageBox("", TextManager.GetWithVariables("savesubtospecialfolderprompt",
|
||||
new string[] { "[type]", "[outpostpath]" }, new string[] { TextManager.Get("submarinetype." + Submarine.MainSub.Info.Type), specialSavePath }),
|
||||
new string[] { "[type]", "[outpostpath]" }, new string[] { TextManager.Get(submarineTypeTag), specialSavePath }),
|
||||
new string[] { TextManager.Get("yes"), TextManager.Get("no") });
|
||||
msgBox.Buttons[0].OnClicked = (bt, userdata) =>
|
||||
{
|
||||
@@ -1781,7 +1837,12 @@ namespace Barotrauma
|
||||
subTypeContainer.RectTransform.MinSize = new Point(0, subTypeContainer.RectTransform.Children.Max(c => c.MinSize.Y));
|
||||
foreach (SubmarineType subType in Enum.GetValues(typeof(SubmarineType)))
|
||||
{
|
||||
subTypeDropdown.AddItem(TextManager.Get("submarinetype."+subType.ToString().ToLowerInvariant()), subType);
|
||||
string textTag = "SubmarineType." + subType;
|
||||
if (subType == SubmarineType.EnemySubmarine && !TextManager.ContainsTag(textTag))
|
||||
{
|
||||
textTag = "MissionType.Pirate";
|
||||
}
|
||||
subTypeDropdown.AddItem(TextManager.Get(textTag), subType);
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
@@ -2020,30 +2081,27 @@ namespace Barotrauma
|
||||
Submarine.MainSub.Info.Price = Math.Max(Submarine.MainSub.Info.Price, basePrice);
|
||||
}
|
||||
|
||||
if (!Submarine.MainSub.Info.HasTag(SubmarineTag.Shuttle))
|
||||
var classGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true)
|
||||
{
|
||||
var classGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), classGroup.RectTransform),
|
||||
TextManager.Get("submarineclass"), textAlignment: Alignment.CenterLeft, wrap: true);
|
||||
GUIDropDown classDropDown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1.0f), classGroup.RectTransform));
|
||||
classDropDown.RectTransform.MinSize = new Point(0, subTypeContainer.RectTransform.Children.Max(c => c.MinSize.Y));
|
||||
classDropDown.AddItem(TextManager.Get("submarineclass.undefined"), SubmarineClass.Undefined);
|
||||
classDropDown.AddItem(TextManager.Get("submarineclass.scout"), SubmarineClass.Scout);
|
||||
classDropDown.AddItem(TextManager.Get("submarineclass.attack"), SubmarineClass.Attack);
|
||||
classDropDown.AddItem(TextManager.Get("submarineclass.transport"), SubmarineClass.Transport);
|
||||
classDropDown.AddItem(TextManager.Get("submarineclass.deepdiver"), SubmarineClass.DeepDiver);
|
||||
classDropDown.OnSelected += (selected, userdata) =>
|
||||
{
|
||||
SubmarineClass submarineClass = (SubmarineClass)userdata;
|
||||
Submarine.MainSub.Info.SubmarineClass = submarineClass;
|
||||
return true;
|
||||
};
|
||||
|
||||
classDropDown.SelectItem(Submarine.MainSub.Info.SubmarineClass);
|
||||
}
|
||||
Stretch = true
|
||||
};
|
||||
var classText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), classGroup.RectTransform),
|
||||
TextManager.Get("submarineclass"), textAlignment: Alignment.CenterLeft, wrap: true);
|
||||
GUIDropDown classDropDown = new GUIDropDown(new RectTransform(new Vector2(0.4f, 1.0f), classGroup.RectTransform));
|
||||
classDropDown.RectTransform.MinSize = new Point(0, subTypeContainer.RectTransform.Children.Max(c => c.MinSize.Y));
|
||||
classDropDown.AddItem(TextManager.Get("submarineclass.undefined"), SubmarineClass.Undefined);
|
||||
classDropDown.AddItem(TextManager.Get("submarineclass.scout"), SubmarineClass.Scout);
|
||||
classDropDown.AddItem(TextManager.Get("submarineclass.attack"), SubmarineClass.Attack);
|
||||
classDropDown.AddItem(TextManager.Get("submarineclass.transport"), SubmarineClass.Transport);
|
||||
classDropDown.AddItem(TextManager.Get("submarineclass.deepdiver"), SubmarineClass.DeepDiver);
|
||||
classDropDown.OnSelected += (selected, userdata) =>
|
||||
{
|
||||
SubmarineClass submarineClass = (SubmarineClass)userdata;
|
||||
Submarine.MainSub.Info.SubmarineClass = submarineClass;
|
||||
return true;
|
||||
};
|
||||
classDropDown.SelectItem(Submarine.MainSub.Info.SubmarineClass);
|
||||
classText.Enabled = classDropDown.ButtonEnabled = !Submarine.MainSub.Info.HasTag(SubmarineTag.Shuttle);
|
||||
|
||||
var crewSizeArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), subSettingsContainer.RectTransform), isHorizontal: true)
|
||||
{
|
||||
@@ -2216,17 +2274,29 @@ namespace Barotrauma
|
||||
{
|
||||
Selected = Submarine.MainSub != null && Submarine.MainSub.Info.HasTag(tag),
|
||||
UserData = tag,
|
||||
|
||||
OnSelected = (GUITickBox tickBox) =>
|
||||
{
|
||||
if (Submarine.MainSub == null) return false;
|
||||
SubmarineTag tag = (SubmarineTag)tickBox.UserData;
|
||||
if (tag == SubmarineTag.Shuttle)
|
||||
{
|
||||
if (tickBox.Selected)
|
||||
{
|
||||
classDropDown.SelectItem(SubmarineClass.Undefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
classDropDown.SelectItem(Submarine.MainSub.Info.SubmarineClass);
|
||||
}
|
||||
classText.Enabled = classDropDown.ButtonEnabled = !tickBox.Selected;
|
||||
}
|
||||
if (tickBox.Selected)
|
||||
{
|
||||
Submarine.MainSub.Info.AddTag((SubmarineTag)tickBox.UserData);
|
||||
Submarine.MainSub.Info.AddTag(tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
Submarine.MainSub.Info.RemoveTag((SubmarineTag)tickBox.UserData);
|
||||
Submarine.MainSub.Info.RemoveTag(tag);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -2305,7 +2375,6 @@ namespace Barotrauma
|
||||
if (quickSave) { SaveSub(saveButton, saveButton.UserData); }
|
||||
}
|
||||
|
||||
|
||||
private void CreateSaveAssemblyScreen()
|
||||
{
|
||||
SetMode(Mode.Default);
|
||||
@@ -2317,10 +2386,10 @@ namespace Barotrauma
|
||||
|
||||
new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, saveFrame.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
|
||||
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.25f, 0.3f), saveFrame.RectTransform, Anchor.Center) { MinSize = new Point(400, 300) });
|
||||
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.25f, 0.35f), saveFrame.RectTransform, Anchor.Center) { MinSize = new Point(400, 350) });
|
||||
GUILayoutGroup paddedSaveFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), innerFrame.RectTransform, Anchor.Center))
|
||||
{
|
||||
AbsoluteSpacing = 5,
|
||||
AbsoluteSpacing = GUI.IntScale(5),
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
@@ -2337,15 +2406,22 @@ namespace Barotrauma
|
||||
};
|
||||
#endif
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedSaveFrame.RectTransform),
|
||||
TextManager.Get("SaveItemAssemblyDialogDescription"));
|
||||
descriptionBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.3f), paddedSaveFrame.RectTransform))
|
||||
var descriptionContainer = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), paddedSaveFrame.RectTransform));
|
||||
descriptionBox = new GUITextBox(new RectTransform(Vector2.One, descriptionContainer.Content.RectTransform, Anchor.TopLeft),
|
||||
font: GUI.SmallFont, style: "GUITextBoxNoBorder", wrap: true, textAlignment: Alignment.TopLeft)
|
||||
{
|
||||
UserData = "description",
|
||||
Wrap = true,
|
||||
Text = ""
|
||||
Padding = new Vector4(10 * GUI.Scale)
|
||||
};
|
||||
|
||||
|
||||
descriptionBox.OnTextChanged += (textBox, text) =>
|
||||
{
|
||||
Vector2 textSize = textBox.Font.MeasureString(descriptionBox.WrappedText);
|
||||
textBox.RectTransform.NonScaledSize = new Point(textBox.RectTransform.NonScaledSize.X, Math.Max(descriptionContainer.Content.Rect.Height, (int)textSize.Y + 10));
|
||||
descriptionContainer.UpdateScrollBarSize();
|
||||
descriptionContainer.BarScroll = 1.0f;
|
||||
return true;
|
||||
};
|
||||
|
||||
var buttonArea = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), paddedSaveFrame.RectTransform), style: null);
|
||||
new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonArea.RectTransform, Anchor.BottomLeft),
|
||||
TextManager.Get("Cancel"))
|
||||
@@ -2361,6 +2437,7 @@ namespace Barotrauma
|
||||
{
|
||||
OnClicked = SaveAssembly
|
||||
};
|
||||
buttonArea.RectTransform.MinSize = new Point(0, buttonArea.Children.First().RectTransform.MinSize.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2404,7 +2481,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
bool hideInMenus = !(nameBox.Parent.GetChildByUserData("hideinmenus") is GUITickBox hideInMenusTickBox) ? false : hideInMenusTickBox.Selected;
|
||||
bool hideInMenus = nameBox.Parent.GetChildByUserData("hideinmenus") is GUITickBox hideInMenusTickBox && hideInMenusTickBox.Selected;
|
||||
#if DEBUG
|
||||
string saveFolder = ItemAssemblyPrefab.VanillaSaveFolder;
|
||||
#else
|
||||
@@ -2423,7 +2500,6 @@ namespace Barotrauma
|
||||
}
|
||||
#endif
|
||||
string filePath = Path.Combine(saveFolder, nameBox.Text + ".xml");
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("Warning"), TextManager.Get("ItemAssemblyFileExistsWarning"), new[] { TextManager.Get("Yes"), TextManager.Get("No") });
|
||||
@@ -2438,18 +2514,27 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
Save();
|
||||
var identifier = nameBox.Text.ToLowerInvariant().Replace(" ", "");
|
||||
var existingPrefab = MapEntityPrefab.Find(null, identifier, showErrorMessages: false);
|
||||
if (existingPrefab != null && System.IO.Path.GetDirectoryName(existingPrefab.FilePath) == ItemAssemblyPrefab.VanillaSaveFolder)
|
||||
{
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("Warning"), TextManager.Get("ItemAssemblyVanillaFileExistsWarning"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
void Save()
|
||||
{
|
||||
XDocument doc = new XDocument(ItemAssemblyPrefab.Save(MapEntity.SelectedList, nameBox.Text, descriptionBox.Text, hideInMenus));
|
||||
XDocument doc = new XDocument(ItemAssemblyPrefab.Save(MapEntity.SelectedList.ToList(), nameBox.Text, descriptionBox.Text, hideInMenus));
|
||||
#if DEBUG
|
||||
doc.Save(filePath);
|
||||
#else
|
||||
doc.SaveSafe(filePath);
|
||||
#endif
|
||||
new ItemAssemblyPrefab(filePath);
|
||||
new ItemAssemblyPrefab(filePath, allowOverwrite: true);
|
||||
UpdateEntityList();
|
||||
}
|
||||
|
||||
@@ -2519,8 +2604,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (prevSub == null || prevSub.Type != sub.Type)
|
||||
{
|
||||
string textTag = "SubmarineType." + sub.Type;
|
||||
if (sub.Type == SubmarineType.EnemySubmarine && !TextManager.ContainsTag(textTag))
|
||||
{
|
||||
textTag = "MissionType.Pirate";
|
||||
}
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), subList.Content.RectTransform) { MinSize = new Point(0, 35) },
|
||||
TextManager.Get("SubmarineType." + sub.Type), font: GUI.LargeFont, textAlignment: Alignment.Center, style: "ListBoxElement")
|
||||
TextManager.Get(textTag), font: GUI.LargeFont, textAlignment: Alignment.Center, style: "ListBoxElement")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
@@ -2712,8 +2802,15 @@ namespace Barotrauma
|
||||
if (subList.SelectedComponent == null) { return false; }
|
||||
if (!(subList.SelectedComponent.UserData is SubmarineInfo selectedSubInfo)) { return false; }
|
||||
|
||||
LoadSub(selectedSubInfo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void LoadSub(SubmarineInfo info)
|
||||
{
|
||||
Submarine.Unload();
|
||||
var selectedSub = new Submarine(selectedSubInfo);
|
||||
var selectedSub = new Submarine(info);
|
||||
Submarine.MainSub = selectedSub;
|
||||
Submarine.MainSub.UpdateTransform(interpolate: false);
|
||||
ClearUndoBuffer();
|
||||
@@ -2744,8 +2841,6 @@ namespace Barotrauma
|
||||
};
|
||||
adjustLightsPrompt.Buttons[1].OnClicked += adjustLightsPrompt.Close;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void TryDeleteSub(SubmarineInfo sub)
|
||||
@@ -2822,7 +2917,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entityFilterBox.Text) || dummyCharacter?.SelectedConstruction?.OwnInventory != null)
|
||||
if (!string.IsNullOrEmpty(entityFilterBox.Text))
|
||||
{
|
||||
FilterEntities(entityFilterBox.Text);
|
||||
}
|
||||
@@ -2835,7 +2930,7 @@ namespace Barotrauma
|
||||
|
||||
private void FilterEntities(string filter)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filter) && dummyCharacter?.SelectedConstruction?.OwnInventory == null)
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
{
|
||||
allEntityList.Visible = false;
|
||||
categorizedEntityList.Visible = true;
|
||||
@@ -2862,11 +2957,7 @@ namespace Barotrauma
|
||||
{
|
||||
child.Visible =
|
||||
(!selectedCategory.HasValue || ((MapEntityPrefab)child.UserData).Category.HasFlag(selectedCategory)) &&
|
||||
((MapEntityPrefab)child.UserData).Name.ToLower().Contains(filter); ;
|
||||
if (child.Visible && dummyCharacter?.SelectedConstruction?.OwnInventory != null)
|
||||
{
|
||||
child.Visible = child.UserData is MapEntityPrefab item && IsItemPrefab(item);
|
||||
}
|
||||
((MapEntityPrefab)child.UserData).Name.ToLower().Contains(filter);
|
||||
}
|
||||
allEntityList.UpdateScrollBarSize();
|
||||
allEntityList.BarScroll = 0.0f;
|
||||
@@ -2942,11 +3033,14 @@ namespace Barotrauma
|
||||
new ContextMenuOption("SubEditor.EditBackgroundColor", isEnabled: true, onSelected: CreateBackgroundColorPicker),
|
||||
new ContextMenuOption("SubEditor.ToggleTransparency", isEnabled: true, onSelected: () => TransparentWiringMode = !TransparentWiringMode),
|
||||
new ContextMenuOption("SubEditor.ToggleGrid", isEnabled: true, onSelected: () => ShouldDrawGrid = !ShouldDrawGrid),
|
||||
new ContextMenuOption("SubEditor.PasteAssembly", isEnabled: true, PasteAssembly),
|
||||
new ContextMenuOption("SubEditor.PasteAssembly", isEnabled: true, () => PasteAssembly()),
|
||||
new ContextMenuOption("Editor.SelectSame", isEnabled: targets.Count > 0, onSelected: delegate
|
||||
{
|
||||
IEnumerable<MapEntity> matching = MapEntity.mapEntityList.Where(e => e.prefab != null && targets.Any(t => t.prefab?.Identifier == e.prefab.Identifier) && !MapEntity.SelectedList.Contains(e));
|
||||
MapEntity.SelectedList.AddRange(matching);
|
||||
foreach (MapEntity match in MapEntity.mapEntityList.Where(e => e.prefab != null && targets.Any(t => t.prefab?.Identifier == e.prefab.Identifier) && !MapEntity.SelectedList.Contains(e)))
|
||||
{
|
||||
if (MapEntity.SelectedList.Contains(match)) { continue; }
|
||||
MapEntity.SelectedList.Add(match);
|
||||
}
|
||||
}),
|
||||
new ContextMenuOption("SubEditor.AddImage", isEnabled: true, onSelected: ImageManager.CreateImageWizard),
|
||||
new ContextMenuOption("SubEditor.ToggleImageEditing", isEnabled: true, onSelected: delegate
|
||||
@@ -2973,10 +3067,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void PasteAssembly()
|
||||
private void PasteAssembly(string text = null, Vector2? pos = null)
|
||||
{
|
||||
string clipboard = Clipboard.GetText();
|
||||
if (string.IsNullOrWhiteSpace(clipboard))
|
||||
pos ??= cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
text ??= Clipboard.GetText();
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
DebugConsole.ThrowError("Unable to paste assembly: Clipboard content is empty.");
|
||||
return;
|
||||
@@ -2986,7 +3081,7 @@ namespace Barotrauma
|
||||
|
||||
try
|
||||
{
|
||||
element = XDocument.Parse(clipboard).Root;
|
||||
element = XDocument.Parse(text).Root;
|
||||
}
|
||||
catch (Exception) { /* ignored */ }
|
||||
|
||||
@@ -2996,12 +3091,11 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 pos = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
Submarine sub = Submarine.MainSub;
|
||||
List<MapEntity> entities;
|
||||
try
|
||||
{
|
||||
entities = ItemAssemblyPrefab.PasteEntities(pos, sub, element, selectInstance: true);
|
||||
entities = ItemAssemblyPrefab.PasteEntities(pos.Value, sub, element, selectInstance: true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -3143,6 +3237,8 @@ namespace Barotrauma
|
||||
|
||||
Color newColor = SetColor(null);
|
||||
|
||||
if (!IsSubEditor()) { return true; }
|
||||
|
||||
Dictionary<object, List<ISerializableEntity>> oldProperties = new Dictionary<object, List<ISerializableEntity>>();
|
||||
|
||||
foreach (var (sEntity, color, _) in entities)
|
||||
@@ -3274,57 +3370,6 @@ namespace Barotrauma
|
||||
|
||||
static string ColorToHex(Color color) => $"#{(color.R << 16 | color.G << 8 | color.B):X6}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a color picker that can be used to change the submarine editor's background color
|
||||
/// </summary>
|
||||
private void CreateBackgroundColorPicker()
|
||||
{
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("CharacterEditor.EditBackgroundColor"), "", new[] { TextManager.Get("Reset"), TextManager.Get("OK")}, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
|
||||
|
||||
var rgbLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), msgBox.Content.RectTransform), isHorizontal: true);
|
||||
|
||||
// Generate R,G,B labels and parent elements
|
||||
var layoutParents = new GUILayoutGroup[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var colorContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.33f, 1), rgbLayout.RectTransform), isHorizontal: true) { Stretch = true };
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.2f, 1), colorContainer.RectTransform, Anchor.CenterLeft) { MinSize = new Point(15, 0) }, GUI.colorComponentLabels[i], font: GUI.SmallFont, textAlignment: Alignment.Center);
|
||||
layoutParents[i] = colorContainer;
|
||||
}
|
||||
|
||||
// attach number inputs to our generated parent elements
|
||||
var rInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1f), layoutParents[0].RectTransform), GUINumberInput.NumberType.Int) { IntValue = backgroundColor.R };
|
||||
var gInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1f), layoutParents[1].RectTransform), GUINumberInput.NumberType.Int) { IntValue = backgroundColor.G };
|
||||
var bInput = new GUINumberInput(new RectTransform(new Vector2(0.7f, 1f), layoutParents[2].RectTransform), GUINumberInput.NumberType.Int) { IntValue = backgroundColor.B };
|
||||
|
||||
rInput.MinValueInt = gInput.MinValueInt = bInput.MinValueInt = 0;
|
||||
rInput.MaxValueInt = gInput.MaxValueInt = bInput.MaxValueInt = 255;
|
||||
|
||||
rInput.OnValueChanged = gInput.OnValueChanged = bInput.OnValueChanged = delegate
|
||||
{
|
||||
var color = new Color(rInput.IntValue, gInput.IntValue, bInput.IntValue);
|
||||
backgroundColor = color;
|
||||
GameSettings.SubEditorBackgroundColor = color;
|
||||
};
|
||||
|
||||
// Reset button
|
||||
msgBox.Buttons[0].OnClicked = (button, o) =>
|
||||
{
|
||||
rInput.IntValue = 13;
|
||||
gInput.IntValue = 37;
|
||||
bInput.IntValue = 69;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Ok button
|
||||
msgBox.Buttons[1].OnClicked = (button, o) =>
|
||||
{
|
||||
msgBox.Close();
|
||||
GameMain.Config.SaveNewPlayerConfig();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
private GUIFrame CreateWiringPanel()
|
||||
{
|
||||
@@ -3495,27 +3540,7 @@ namespace Barotrauma
|
||||
|
||||
submarineDescriptionCharacterCount.Text = text.Length + " / " + submarineDescriptionLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the prefab is an item or if it only consists of items
|
||||
/// </summary>
|
||||
/// <param name="mapPrefab">The prefab to check</param>
|
||||
/// <returns>True if the the prefab is an item or it contains only items</returns>
|
||||
private bool IsItemPrefab(MapEntityPrefab mapPrefab)
|
||||
{
|
||||
if (dummyCharacter?.SelectedConstruction == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return mapPrefab switch
|
||||
{
|
||||
ItemPrefab iPrefab => true,
|
||||
ItemAssemblyPrefab aPrefab => aPrefab.DisplayEntities.All(pair => pair.First is ItemPrefab),
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private bool SelectPrefab(GUIComponent component, object obj)
|
||||
{
|
||||
allEntityList.Deselect();
|
||||
@@ -3983,9 +4008,9 @@ namespace Barotrauma
|
||||
{
|
||||
loadFrame.AddToGUIUpdateList();
|
||||
}
|
||||
else if (saveFrame != null)
|
||||
else
|
||||
{
|
||||
saveFrame.AddToGUIUpdateList();
|
||||
saveFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4728,9 +4753,9 @@ namespace Barotrauma
|
||||
CloseItem();
|
||||
}
|
||||
}
|
||||
else if (MapEntity.SelectedList.Count == 1 && WiringMode)
|
||||
else if (MapEntity.SelectedList.Count == 1 && WiringMode && MapEntity.SelectedList.FirstOrDefault() is Item item)
|
||||
{
|
||||
(MapEntity.SelectedList[0] as Item)?.UpdateHUD(cam, dummyCharacter, (float)deltaTime);
|
||||
item.UpdateHUD(cam, dummyCharacter, (float)deltaTime);
|
||||
}
|
||||
|
||||
CharacterHUD.Update((float)deltaTime, dummyCharacter, cam);
|
||||
@@ -4753,7 +4778,7 @@ namespace Barotrauma
|
||||
sub.UpdateTransform();
|
||||
}
|
||||
|
||||
graphics.Clear(backgroundColor);
|
||||
graphics.Clear(BackgroundColor);
|
||||
|
||||
ImageManager.Draw(spriteBatch, cam);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user