Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaClient/ClientSource/Map/Map/Map.cs
Joonas Rikkonen 694cdfee7b v0.12.0.2
2021-02-10 17:08:21 +02:00

876 lines
44 KiB
C#

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Barotrauma
{
partial class Map
{
class MapAnim
{
public Location StartLocation;
public Location EndLocation;
public string StartMessage;
public string EndMessage;
/// <summary>
/// Initial zoom (0 - 1, from min zoom to max)
/// </summary>
public float? StartZoom;
/// <summary>
/// Initial zoom (0 - 1, from min zoom to max)
/// </summary>
public float? EndZoom;
private float startDelay;
public float StartDelay
{
get { return startDelay; }
set
{
startDelay = value;
Timer = -startDelay;
}
}
public Vector2? StartPos;
public float Duration;
public float Timer;
public bool Finished;
}
private readonly Queue<MapAnim> mapAnimQueue = new Queue<MapAnim>();
public Location HighlightedLocation { get; private set; }
private static Sprite noiseOverlay;
public Vector2 DrawOffset;
private Vector2 drawOffsetNoise;
private Vector2 currLocationIndicatorPos;
private float zoom = 3.0f;
private float targetZoom;
private Rectangle borders;
private Sprite[,] mapTiles;
private bool[,] tileDiscovered;
private Pair<Rectangle, string> connectionTooltip;
#if DEBUG
private GUIComponent editor;
private void CreateEditor()
{
editor = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f), GUI.Canvas, Anchor.TopRight, minSize: new Point(400, 0)));
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), editor.RectTransform, Anchor.Center))
{
Stretch = true,
RelativeSpacing = 0.02f,
CanBeFocused = false
};
var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.95f), paddedFrame.RectTransform, Anchor.Center));
new SerializableEntityEditor(listBox.Content.RectTransform, generationParams, false, true);
new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), paddedFrame.RectTransform), "Generate")
{
OnClicked = (btn, userData) =>
{
Rand.SetSyncedSeed(ToolBox.StringToInt(this.Seed));
Generate();
InitProjectSpecific();
return true;
}
};
}
#endif
public Location CurrentDisplayLocation
{
get
{
return GameMain.GameSession.Campaign.CurrentDisplayLocation;
}
}
partial void InitProjectSpecific()
{
noiseOverlay ??= new Sprite("Content/UI/noise.png", Vector2.Zero);
OnLocationChanged = LocationChanged;
borders = new Rectangle(
(int)Locations.Min(l => l.MapPosition.X),
(int)Locations.Min(l => l.MapPosition.Y),
(int)Locations.Max(l => l.MapPosition.X),
(int)Locations.Max(l => l.MapPosition.Y));
borders.Width -= borders.X;
borders.Height -= borders.Y;
if (CurrentLocation != null)
{
DrawOffset = -CurrentLocation.MapPosition;
}
Vector2 tileSize = generationParams.MapTiles.Values.First().First().size * generationParams.MapTileScale;
int tilesX = (int)Math.Ceiling(Width / tileSize.X);
int tilesY = (int)Math.Ceiling(Height / tileSize.Y);
mapTiles = new Sprite[tilesX, tilesY];
tileDiscovered = new bool[tilesX, tilesY];
for (int x = 0; x < tilesX; x++)
{
for (int y = 0; y < tilesY; y++)
{
var biome = GetBiome(x * tileSize.X);
var tileList = generationParams.MapTiles.ContainsKey(biome.Identifier) ?
generationParams.MapTiles[biome.Identifier] :
generationParams.MapTiles.Values.First();
mapTiles[x, y] = tileList[x % tileList.Count];
}
}
RemoveFogOfWar(StartLocation);
GenerateLocationConnectionVisuals();
}
partial void GenerateLocationConnectionVisuals()
{
foreach (LocationConnection connection in Connections)
{
Vector2 connectionStart = connection.Locations[0].MapPosition;
Vector2 connectionEnd = connection.Locations[1].MapPosition;
float connectionLength = Vector2.Distance(connectionStart, connectionEnd);
int iterations = Math.Min((int)Math.Sqrt(connectionLength * generationParams.ConnectionIndicatorIterationMultiplier), 5);
connection.CrackSegments.Clear();
connection.CrackSegments.AddRange(MathUtils.GenerateJaggedLine(
connectionStart, connectionEnd,
iterations, connectionLength * generationParams.ConnectionIndicatorDisplacementMultiplier));
}
}
private void LocationChanged(Location prevLocation, Location newLocation)
{
if (prevLocation == newLocation) return;
//focus on starting location
if (prevLocation != null)
{
mapAnimQueue.Enqueue(new MapAnim()
{
EndZoom = 1.0f,
EndLocation = prevLocation,
Duration = MathHelper.Clamp(Vector2.Distance(-DrawOffset, prevLocation.MapPosition) / 1000.0f, 0.1f, 0.5f)
});
mapAnimQueue.Enqueue(new MapAnim()
{
EndZoom = 0.5f,
StartLocation = prevLocation,
EndLocation = newLocation,
Duration = 2.0f,
StartDelay = 0.5f
});
}
else
{
currLocationIndicatorPos = CurrentLocation.MapPosition;
}
RemoveFogOfWar(newLocation);
}
private void RemoveFogOfWar(Location location, bool removeFromAdjacentLocations = true)
{
if (location == null) { return; }
Vector2 mapTileSize = mapTiles[0, 0].size * generationParams.MapTileScale;
int startX = (int)Math.Max(Math.Floor(location.MapPosition.X / mapTileSize.X - 0.25f), 0);
int startY = (int)Math.Max(Math.Floor(location.MapPosition.Y / mapTileSize.Y - 0.25f), 0);
int endX = (int)Math.Min(Math.Floor(location.MapPosition.X / mapTileSize.X + 0.25f), mapTiles.GetLength(0));
int endY = (int)Math.Min(Math.Floor(location.MapPosition.Y / mapTileSize.Y + 0.25f), mapTiles.GetLength(1));
for (int x = startX; x <= endX; x++)
{
for (int y = startY; y <= endY; y++)
{
tileDiscovered[x, y] = true;
}
}
if (removeFromAdjacentLocations)
{
foreach (LocationConnection c in location.Connections)
{
var otherLocation = c.OtherLocation(location);
RemoveFogOfWar(otherLocation, removeFromAdjacentLocations: false);
}
}
}
private bool IsInFogOfWar(Location location)
{
if (GameMain.DebugDraw) { return false; }
Vector2 mapTileSize = mapTiles[0, 0].size * generationParams.MapTileScale;
int x = (int)Math.Floor(location.MapPosition.X / mapTileSize.X);
int y = (int)Math.Floor(location.MapPosition.Y / mapTileSize.Y);
return !tileDiscovered[MathHelper.Clamp(x, 0, tileDiscovered.Length), MathHelper.Clamp(y, 0, tileDiscovered.Length)];
}
partial void ChangeLocationTypeProjSpecific(Location location, string prevName, LocationTypeChange change)
{
if (change.Messages.Any())
{
string msg = change.Messages[Rand.Range(0, change.Messages.Count)]
.Replace("[previousname]", prevName)
.Replace("[name]", location.Name);
location.LastTypeChangeMessage = msg;
if (GameMain.Client != null)
{
GameMain.Client.AddChatMessage(msg, Networking.ChatMessageType.Default, TextManager.Get("RadioAnnouncerName"));
}
else
{
GameMain.GameSession?.GameMode.CrewManager.AddSinglePlayerChatMessage(
TextManager.Get("RadioAnnouncerName"),
msg,
Networking.ChatMessageType.Default,
sender: null);
}
}
}
partial void ClearAnimQueue()
{
mapAnimQueue.Clear();
}
public void Update(float deltaTime, GUICustomComponent mapContainer)
{
Rectangle rect = mapContainer.Rect;
if (CurrentDisplayLocation != null)
{
if (!CurrentDisplayLocation.Discovered)
{
RemoveFogOfWar(CurrentDisplayLocation);
CurrentDisplayLocation.Discovered = true;
if (CurrentDisplayLocation.MapPosition.X > furthestDiscoveredLocation.MapPosition.X)
{
furthestDiscoveredLocation = CurrentDisplayLocation;
}
}
}
currLocationIndicatorPos = Vector2.Lerp(currLocationIndicatorPos, CurrentDisplayLocation.MapPosition, deltaTime);
#if DEBUG
if (GameMain.DebugDraw)
{
if (editor == null) CreateEditor();
editor.AddToGUIUpdateList(order: 1);
}
#endif
if (mapAnimQueue.Count > 0)
{
hudVisibility = Math.Max(hudVisibility - deltaTime, 0.0f);
UpdateMapAnim(mapAnimQueue.Peek(), deltaTime);
if (mapAnimQueue.Peek().Finished)
{
mapAnimQueue.Dequeue();
}
return;
}
hudVisibility = Math.Min(hudVisibility + deltaTime, 0.75f + (float)Math.Sin(Timing.TotalTime * 3.0f) * 0.25f);
Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y);
Vector2 viewOffset = DrawOffset + drawOffsetNoise;
float closestDist = 0.0f;
HighlightedLocation = null;
if (GUI.MouseOn == null || GUI.MouseOn == mapContainer)
{
for (int i = 0; i < Locations.Count; i++)
{
Location location = Locations[i];
if (IsInFogOfWar(location) && !(CurrentDisplayLocation?.Connections.Any(c => c.Locations.Contains(location)) ?? false) && !GameMain.DebugDraw) { continue; }
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
if (!rect.Contains(pos)) { continue; }
float iconScale = generationParams.LocationIconSize / location.Type.Sprite.size.X;
if (location == CurrentDisplayLocation) { iconScale *= 1.2f; }
Rectangle drawRect = location.Type.Sprite.SourceRect;
drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f);
drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f);
drawRect.X = (int)pos.X - drawRect.Width / 2;
drawRect.Y = (int)pos.Y - drawRect.Width / 2;
if (!drawRect.Contains(PlayerInput.MousePosition)) { continue; }
float dist = Vector2.Distance(PlayerInput.MousePosition, pos);
if (HighlightedLocation == null || dist < closestDist)
{
closestDist = dist;
HighlightedLocation = location;
}
}
}
if (GUI.KeyboardDispatcher.Subscriber == null)
{
float moveSpeed = 1000.0f;
Vector2 moveAmount = Vector2.Zero;
if (PlayerInput.KeyDown(InputType.Left)) { moveAmount += Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Right)) { moveAmount -= Vector2.UnitX; }
if (PlayerInput.KeyDown(InputType.Up)) { moveAmount += Vector2.UnitY; }
if (PlayerInput.KeyDown(InputType.Down)) { moveAmount -= Vector2.UnitY; }
DrawOffset += moveAmount * moveSpeed / zoom * deltaTime;
}
targetZoom = MathHelper.Clamp(targetZoom, generationParams.MinZoom, generationParams.MaxZoom);
zoom = MathHelper.Lerp(zoom, targetZoom, 0.1f);
if (GUI.MouseOn == mapContainer)
{
foreach (LocationConnection connection in Connections)
{
if (HighlightedLocation != CurrentDisplayLocation &&
connection.Locations.Contains(HighlightedLocation) && connection.Locations.Contains(CurrentDisplayLocation))
{
if (PlayerInput.PrimaryMouseButtonClicked() &&
SelectedLocation != HighlightedLocation && HighlightedLocation != null)
{
//clients aren't allowed to select the location without a permission
if ((GameMain.GameSession?.GameMode as CampaignMode)?.AllowedToManageCampaign() ?? false)
{
SelectedConnection = connection;
SelectedLocation = HighlightedLocation;
OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection);
GameMain.Client?.SendCampaignState();
}
}
}
}
targetZoom += PlayerInput.ScrollWheelSpeed / 500.0f;
if (PlayerInput.MidButtonHeld() || (HighlightedLocation == null && PlayerInput.PrimaryMouseButtonHeld()))
{
DrawOffset += PlayerInput.MouseSpeed / zoom;
}
if (AllowDebugTeleport)
{
if (PlayerInput.DoubleClicked() && HighlightedLocation != null)
{
var passedConnection = CurrentDisplayLocation.Connections.Find(c => c.OtherLocation(CurrentDisplayLocation) == HighlightedLocation);
if (passedConnection != null)
{
passedConnection.Passed = true;
}
Location prevLocation = CurrentDisplayLocation;
CurrentLocation = HighlightedLocation;
Level.Loaded.DebugSetStartLocation(CurrentLocation);
Level.Loaded.DebugSetEndLocation(null);
CurrentLocation.Discovered = true;
OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
SelectLocation(-1);
if (GameMain.Client == null)
{
CurrentLocation.CreateStore();
ProgressWorld();
}
else
{
GameMain.Client.SendCampaignState();
}
}
if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift) && PlayerInput.PrimaryMouseButtonClicked() && HighlightedLocation != null)
{
int distance = DistanceToClosestLocationWithOutpost(HighlightedLocation, out Location foundLocation);
DebugConsole.NewMessage($"Distance to closest outpost from {HighlightedLocation.Name} to {foundLocation?.Name} is {distance}");
}
if (PlayerInput.PrimaryMouseButtonClicked() && HighlightedLocation == null)
{
SelectLocation(-1);
}
}
}
}
public void Draw(SpriteBatch spriteBatch, GUICustomComponent mapContainer)
{
connectionTooltip = null;
Rectangle rect = mapContainer.Rect;
Vector2 viewSize = new Vector2(rect.Width / zoom, rect.Height / zoom);
Vector2 edgeBuffer = rect.Size.ToVector2() / 2;
DrawOffset.X = MathHelper.Clamp(DrawOffset.X, -Width - edgeBuffer.X + viewSize.X / 2.0f, edgeBuffer.X - viewSize.X / 2.0f);
DrawOffset.Y = MathHelper.Clamp(DrawOffset.Y, -Height - edgeBuffer.Y + viewSize.Y / 2.0f, edgeBuffer.Y - viewSize.Y / 2.0f);
drawOffsetNoise = new Vector2(
(float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.1f % 255, Timing.TotalTime * 0.1f % 255, 0) - 0.5f,
(float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.2f % 255, Timing.TotalTime * 0.2f % 255, 0.5f) - 0.5f) * 10.0f;
Vector2 viewOffset = DrawOffset + drawOffsetNoise;
Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y);
Rectangle prevScissorRect = GameMain.Instance.GraphicsDevice.ScissorRectangle;
spriteBatch.End();
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, rect);
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
Vector2 topLeft = rectCenter + viewOffset;
Vector2 bottomRight = rectCenter + (viewOffset + new Vector2(Width, Height));
Vector2 mapTileSize = mapTiles[0, 0].size * generationParams.MapTileScale;
int startX = (int)Math.Floor(-topLeft.X / mapTileSize.X) - 1;
int startY = (int)Math.Floor(-topLeft.Y / mapTileSize.Y) - 1;
int endX = (int)Math.Ceiling((-topLeft.X + rect.Width) / mapTileSize.X);
int endY = (int)Math.Ceiling((-topLeft.Y + rect.Height) / mapTileSize.Y);
float noiseT = (float)(Timing.TotalTime * 0.01f);
cameraNoiseStrength = (float)PerlinNoise.CalculatePerlin(noiseT, noiseT * 0.5f, noiseT * 0.2f);
float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 5.0f;
for (int x = startX; x <= endX; x++)
{
for (int y = startY; y <= endY; y++)
{
int tileX = Math.Abs(x) % mapTiles.GetLength(0);
int tileY = Math.Abs(y) % mapTiles.GetLength(1);
Vector2 tilePos = rectCenter + (viewOffset + new Vector2(x, y) * mapTileSize) * zoom;
mapTiles[tileX, tileY].Draw(spriteBatch, tilePos, Color.White, origin: Vector2.Zero, scale: generationParams.MapTileScale * zoom);
if (GameMain.DebugDraw) { continue; }
if (!tileDiscovered[tileX, tileY] || x < 0 || y < 0 || x >= tileDiscovered.GetLength(0) || y >= tileDiscovered.GetLength(1))
{
generationParams.FogOfWarSprite?.Draw(spriteBatch, tilePos, Color.White * cameraNoiseStrength, origin: Vector2.Zero, scale: generationParams.MapTileScale * zoom);
noiseOverlay.DrawTiled(spriteBatch, tilePos, mapTileSize * zoom,
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
color: Color.White * cameraNoiseStrength * 0.2f,
textureScale: Vector2.One * noiseScale);
}
}
}
if (GameMain.DebugDraw)
{
if (topLeft.X > rect.X)
GUI.DrawRectangle(spriteBatch, new Rectangle(rect.X, rect.Y, (int)(topLeft.X - rect.X), rect.Height), Color.Black * 0.5f, true);
if (topLeft.Y > rect.Y)
GUI.DrawRectangle(spriteBatch, new Rectangle((int)topLeft.X, rect.Y, (int)(bottomRight.X - topLeft.X), (int)(topLeft.Y - rect.Y)), Color.Black * 0.5f, true);
if (bottomRight.X < rect.Right)
GUI.DrawRectangle(spriteBatch, new Rectangle((int)bottomRight.X, rect.Y, (int)(rect.Right - bottomRight.X), rect.Height), Color.Black * 0.5f, true);
if (bottomRight.Y < rect.Bottom)
GUI.DrawRectangle(spriteBatch, new Rectangle((int)topLeft.X, (int)bottomRight.Y, (int)(bottomRight.X - topLeft.X), (int)(rect.Bottom - bottomRight.Y)), Color.Black * 0.5f, true);
}
float rawNoiseScale = 1.0f + PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1));
cameraNoiseStrength = PerlinNoise.GetPerlin((int)(Timing.TotalTime * 1 - 1), (int)(Timing.TotalTime * 1 - 1));
noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(),
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
color : Color.White * cameraNoiseStrength * 0.1f,
textureScale: Vector2.One * rawNoiseScale);
noiseOverlay.DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(),
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
color: new Color(20,20,20,50),
textureScale: Vector2.One * rawNoiseScale * 2);
noiseOverlay.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight),
startOffset: new Vector2(Rand.Range(0.0f, noiseOverlay.SourceRect.Width), Rand.Range(0.0f, noiseOverlay.SourceRect.Height)),
color: Color.White * cameraNoiseStrength * 0.1f,
textureScale: Vector2.One * noiseScale);
Pair<Rectangle, string> tooltip = null;
if (generationParams.ShowLocations)
{
foreach (LocationConnection connection in Connections)
{
if (IsInFogOfWar(connection.Locations[0]) && IsInFogOfWar(connection.Locations[1])) { continue; }
DrawConnection(spriteBatch, connection, rect, viewOffset);
}
for (int i = 0; i < Locations.Count; i++)
{
Location location = Locations[i];
if (IsInFogOfWar(location)) { continue; }
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
Rectangle drawRect = location.Type.Sprite.SourceRect;
drawRect.X = (int)pos.X - drawRect.Width / 2;
drawRect.Y = (int)pos.Y - drawRect.Width / 2;
if (!rect.Intersects(drawRect)) { continue; }
if (location == CurrentDisplayLocation )
{
generationParams.CurrentLocationIndicator.Draw(spriteBatch,
rectCenter + (currLocationIndicatorPos + viewOffset) * zoom,
generationParams.IndicatorColor,
generationParams.CurrentLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.CurrentLocationIndicator.size.X) * 1.7f * zoom);
}
if (location == SelectedLocation)
{
generationParams.SelectedLocationIndicator.Draw(spriteBatch,
rectCenter + (location.MapPosition + viewOffset) * zoom,
generationParams.IndicatorColor,
generationParams.SelectedLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.SelectedLocationIndicator.size.X) * 1.7f * zoom);
}
Color color = location.Type.SpriteColor;
if (!location.Discovered) { color = Color.White; }
if (location.Connections.Find(c => c.Locations.Contains(CurrentDisplayLocation)) == null)
{
color *= 0.5f;
}
float iconScale = location == CurrentDisplayLocation ? 1.2f : 1.0f;
if (location == HighlightedLocation)
{
iconScale *= 1.2f;
}
location.Type.Sprite.Draw(spriteBatch, pos, color,
scale: generationParams.LocationIconSize / location.Type.Sprite.size.X * iconScale * zoom);
if (location.TimeSinceLastTypeChange < 1 && !string.IsNullOrEmpty(location.LastTypeChangeMessage) && generationParams.TypeChangeIcon != null)
{
Vector2 typeChangeIconPos = pos + new Vector2(1.35f, -0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
float typeChangeIconScale = 18.0f / generationParams.TypeChangeIcon.SourceRect.Width;
generationParams.TypeChangeIcon.Draw(spriteBatch, typeChangeIconPos, GUI.Style.Red, scale: typeChangeIconScale * zoom);
if (Vector2.Distance(PlayerInput.MousePosition, typeChangeIconPos) < generationParams.TypeChangeIcon.SourceRect.Width * zoom)
{
tooltip = new Pair<Rectangle, string>(
new Rectangle(typeChangeIconPos.ToPoint(), new Point(30)),
location.LastTypeChangeMessage);
}
}
if (location != CurrentLocation && CurrentLocation.AvailableMissions.Any(m => m.Locations.Contains(location)) && 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)
{
var availableMissions = CurrentLocation.AvailableMissions.Where(m => m.Locations.Contains(location));
tooltip = new Pair<Rectangle, string>(
new Rectangle(missionIconPos.ToPoint(), new Point(30)),
TextManager.Get("mission") + '\n'+ string.Join('\n', availableMissions.Select(m => "- " + m.Name)));
}
}
if (GameMain.DebugDraw && location == HighlightedLocation && (!location.Discovered || !location.Type.HasOutpost))
{
if (location.Reputation != null)
{
Vector2 dPos = pos;
dPos.Y += 48;
string name = $"Reputation: {location.Name}";
Vector2 nameSize = GUI.SmallFont.MeasureString(name);
GUI.DrawString(spriteBatch, dPos, name, Color.White, Color.Black * 0.8f, 4, font: GUI.SmallFont);
dPos.Y += nameSize.Y + 16;
Rectangle bgRect = new Rectangle((int)dPos.X, (int)dPos.Y, 256, 32);
bgRect.Inflate(8,8);
Color barColor = ToolBox.GradientLerp(location.Reputation.NormalizedValue, Color.Red, Color.Yellow, Color.LightGreen);
GUI.DrawRectangle(spriteBatch, bgRect, Color.Black * 0.8f, isFilled: true);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)dPos.X, (int)dPos.Y, (int)(location.Reputation.NormalizedValue * 255), 32), barColor, isFilled: true);
string reputationValue = ((int)location.Reputation.Value).ToString();
Vector2 repValueSize = GUI.SubHeadingFont.MeasureString(reputationValue);
GUI.DrawString(spriteBatch, dPos + (new Vector2(256, 32) / 2) - (repValueSize / 2), reputationValue, Color.White, Color.Black, font: GUI.SubHeadingFont);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)dPos.X, (int)dPos.Y, 256, 32), Color.White);
}
}
}
}
DrawDecorativeHUD(spriteBatch, rect);
if (HighlightedLocation != null)
{
Vector2 pos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom;
pos.X += 50 * zoom;
Vector2 nameSize = GUI.LargeFont.MeasureString(HighlightedLocation.Name);
Vector2 typeSize = GUI.Font.MeasureString(HighlightedLocation.Type.Name);
Vector2 size = new Vector2(Math.Max(nameSize.X, typeSize.X), nameSize.Y + typeSize.Y);
bool showReputation = hudVisibility > 0.0f && HighlightedLocation.Discovered && HighlightedLocation.Type.HasOutpost && HighlightedLocation.Reputation != null;
string repLabelText = null, repValueText = null;
Vector2 repLabelSize = Vector2.Zero, repBarSize = Vector2.Zero;
if (showReputation)
{
repLabelText = TextManager.Get("reputation");
repLabelSize = GUI.Font.MeasureString(repLabelText);
size.X = Math.Max(size.X, repLabelSize.X);
repBarSize = new Vector2(Math.Max(0.75f * size.X, 100), repLabelSize.Y);
size.X = Math.Max(size.X, (4.0f / 3.0f) * repBarSize.X);
size.Y += 2 * repLabelSize.Y + 4 + repBarSize.Y;
repValueText = ((int)HighlightedLocation.Reputation.Value).ToString();
}
GUI.Style.GetComponentStyle("OuterGlow").Sprites[GUIComponent.ComponentState.None][0].Draw(
spriteBatch, new Rectangle((int)(pos.X - 60 * GUI.Scale), (int)(pos.Y - size.Y), (int)(size.X + 120 * GUI.Scale), (int)(size.Y * 2.2f)), Color.Black * hudVisibility);
var topLeftPos = pos - new Vector2(0.0f, size.Y / 2);
GUI.DrawString(spriteBatch, topLeftPos, HighlightedLocation.Name, GUI.Style.TextColor * hudVisibility * 1.5f, font: GUI.LargeFont);
topLeftPos += new Vector2(0.0f, nameSize.Y);
GUI.DrawString(spriteBatch, topLeftPos, HighlightedLocation.Type.Name, GUI.Style.TextColor * hudVisibility * 1.5f);
if (showReputation)
{
topLeftPos += new Vector2(0.0f, typeSize.Y + repLabelSize.Y);
GUI.DrawString(spriteBatch, topLeftPos, repLabelText, GUI.Style.TextColor * hudVisibility * 1.5f);
topLeftPos += new Vector2(0.0f, repLabelSize.Y + 4);
Rectangle repBarRect = new Rectangle(new Point((int)topLeftPos.X, (int)topLeftPos.Y), new Point((int)repBarSize.X, (int)repBarSize.Y));
RoundSummary.DrawReputationBar(spriteBatch, repBarRect, HighlightedLocation.Reputation.NormalizedValue);
GUI.DrawString(spriteBatch, new Vector2(repBarRect.Right + 4, repBarRect.Top), repValueText, GUI.Style.TextColor);
}
}
if (tooltip != null)
{
GUIComponent.DrawToolTip(spriteBatch, tooltip.Second, tooltip.First);
}
if (connectionTooltip != null)
{
GUIComponent.DrawToolTip(spriteBatch, connectionTooltip.Second, connectionTooltip.First);
}
spriteBatch.End();
GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
}
private void DrawConnection(SpriteBatch spriteBatch, LocationConnection connection, Rectangle viewArea, Vector2 viewOffset, Color? overrideColor = null)
{
Color connectionColor;
if (GameMain.DebugDraw)
{
float sizeFactor = MathUtils.InverseLerp(
generationParams.SmallLevelConnectionLength,
generationParams.LargeLevelConnectionLength,
connection.Length);
connectionColor = ToolBox.GradientLerp(sizeFactor, Color.LightGreen, GUI.Style.Orange, GUI.Style.Red);
}
else if (overrideColor.HasValue)
{
connectionColor = overrideColor.Value;
}
else
{
connectionColor = connection.Passed ? generationParams.ConnectionColor : generationParams.UnvisitedConnectionColor;
}
int width = (int)(generationParams.LocationConnectionWidth * zoom);
if (Level.Loaded?.LevelData == connection.LevelData)
{
connectionColor = generationParams.HighlightedConnectionColor;
width = (int)(width * 1.5f);
}
if (SelectedLocation != CurrentDisplayLocation &&
(connection.Locations.Contains(SelectedLocation) && connection.Locations.Contains(CurrentDisplayLocation)))
{
connectionColor = generationParams.HighlightedConnectionColor;
width *= 2;
}
else if (HighlightedLocation != CurrentDisplayLocation &&
(connection.Locations.Contains(HighlightedLocation) && connection.Locations.Contains(CurrentDisplayLocation)))
{
connectionColor = generationParams.HighlightedConnectionColor;
width *= 2;
}
Vector2 rectCenter = viewArea.Center.ToVector2();
int startIndex = connection.CrackSegments.Count > 2 ? 1 : 0;
int endIndex = connection.CrackSegments.Count > 2 ? connection.CrackSegments.Count - 1 : connection.CrackSegments.Count;
Vector2? connectionStart = null;
Vector2? connectionEnd = null;
for (int i = startIndex; i < endIndex; i++)
{
var segment = connection.CrackSegments[i];
Vector2 start = rectCenter + (segment[0] + viewOffset) * zoom;
if (!connectionStart.HasValue) { connectionStart = start; }
Vector2 end = rectCenter + (segment[1] + viewOffset) * zoom;
connectionEnd = end;
if (!viewArea.Contains(start) && !viewArea.Contains(end))
{
continue;
}
else
{
if (MathUtils.GetLineRectangleIntersection(start, end, new Rectangle(viewArea.X, viewArea.Y + viewArea.Height, viewArea.Width, viewArea.Height), out Vector2 intersection))
{
if (!viewArea.Contains(start))
{
start = intersection;
}
else
{
end = intersection;
}
}
}
float a = 1.0f;
if (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered)
{
if (IsInFogOfWar(connection.Locations[0]))
{
a = (float)i / connection.CrackSegments.Count;
}
else if (IsInFogOfWar(connection.Locations[1]))
{
a = 1.0f - (float)i / connection.CrackSegments.Count;
}
}
float dist = Vector2.Distance(start, end);
var connectionSprite = connection.Passed ? generationParams.PassedConnectionSprite : generationParams.ConnectionSprite;
spriteBatch.Draw(connectionSprite.Texture,
new Rectangle((int)start.X, (int)start.Y, (int)(dist - 1 * zoom), width),
connectionSprite.SourceRect, connectionColor * a, MathUtils.VectorToAngle(end - start),
new Vector2(0, connectionSprite.size.Y / 2), SpriteEffects.None, 0.01f);
}
if (connectionStart.HasValue && connectionEnd.HasValue)
{
GUIComponentStyle crushDepthWarningIconStyle = null;
string tooltip = null;
var subCrushDepth = Submarine.MainSub?.RealWorldCrushDepth ?? Level.DefaultRealWorldCrushDepth;
if (GameMain.GameSession?.Campaign?.UpgradeManager != null)
{
var hullUpgradePrefab = UpgradePrefab.Find("increasewallhealth");
if (hullUpgradePrefab != null)
{
int pendingLevel = GameMain.GameSession.Campaign.UpgradeManager.GetUpgradeLevel(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First());
int currentLevel = GameMain.GameSession.Campaign.UpgradeManager.GetRealUpgradeLevel(hullUpgradePrefab, hullUpgradePrefab.UpgradeCategories.First());
if (pendingLevel > currentLevel)
{
string updateValueStr = hullUpgradePrefab.SourceElement?.Element("Structure")?.GetAttributeString("crushdepth", null);
if (!string.IsNullOrEmpty(updateValueStr))
{
subCrushDepth = PropertyReference.CalculateUpgrade(subCrushDepth, pendingLevel - currentLevel, updateValueStr);
}
}
}
}
if (connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio > subCrushDepth)
{
crushDepthWarningIconStyle = GUI.Style.GetComponentStyle("CrushDepthWarningHighIcon");
tooltip = "crushdepthwarninghigh";
}
else if ((connection.LevelData.InitialDepth + connection.LevelData.Size.Y) * Physics.DisplayToRealWorldRatio > subCrushDepth)
{
crushDepthWarningIconStyle = GUI.Style.GetComponentStyle("CrushDepthWarningLowIcon");
tooltip = "crushdepthwarninglow";
}
if (crushDepthWarningIconStyle != null)
{
Vector2 iconPos = (connectionStart.Value + connectionEnd.Value) / 2;
float iconSize = 32.0f * GUI.Scale;
bool mouseOn = HighlightedLocation == null && Vector2.DistanceSquared(iconPos, PlayerInput.MousePosition) < iconSize * iconSize;
Sprite crushDepthWarningIcon = crushDepthWarningIconStyle.GetDefaultSprite();
crushDepthWarningIcon.Draw(spriteBatch, iconPos,
mouseOn ? crushDepthWarningIconStyle.HoverColor : crushDepthWarningIconStyle.Color,
scale: iconSize / crushDepthWarningIcon.size.X);
if (mouseOn)
{
connectionTooltip = new Pair<Rectangle, string>(
new Rectangle(iconPos.ToPoint(), new Point((int)iconSize)),
TextManager.Get(tooltip)
.Replace("[initialdepth]", ((int)(connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio)).ToString())
.Replace("[submarinecrushdepth]", ((int)subCrushDepth).ToString()));
}
}
}
if (GameMain.DebugDraw && zoom > 1.0f && generationParams.ShowLevelTypeNames)
{
Vector2 center = rectCenter + (connection.CenterPos + viewOffset) * zoom;
if (viewArea.Contains(center) && connection.Biome != null)
{
GUI.DrawString(spriteBatch, center, connection.Biome.Identifier + " (" + connection.Difficulty + ")", Color.White);
}
}
}
private float hudVisibility;
private float cameraNoiseStrength;
private void DrawDecorativeHUD(SpriteBatch spriteBatch, Rectangle rect)
{
generationParams.DecorativeGraphSprite.Draw(spriteBatch, (int)((Timing.TotalTime * 5.0f) % generationParams.DecorativeGraphSprite.FrameCount),
new Vector2(rect.Left, rect.Top), Color.White, Vector2.Zero, 0, Vector2.One * GUI.Scale);
GUI.DrawString(spriteBatch,
new Vector2(rect.Right - GUI.IntScale(170), rect.Y + GUI.IntScale(5)),
"JOVIAN FLUX " + ((cameraNoiseStrength + Rand.Range(-0.02f, 0.02f)) * 500), generationParams.IndicatorColor * hudVisibility, font: GUI.SmallFont);
GUI.DrawString(spriteBatch,
new Vector2(rect.X + GUI.IntScale(15), rect.Bottom - GUI.IntScale(25)),
"LAT " + (-DrawOffset.Y / 100.0f) + " LON " + (-DrawOffset.X / 100.0f), generationParams.IndicatorColor * hudVisibility, font: GUI.SmallFont);
}
private void UpdateMapAnim(MapAnim anim, float deltaTime)
{
//pause animation while there are messageboxes on screen
if (GUIMessageBox.MessageBoxes.Count > 0) return;
if (!string.IsNullOrEmpty(anim.StartMessage))
{
new GUIMessageBox("", anim.StartMessage);
anim.StartMessage = null;
return;
}
if (anim.StartZoom == null) { anim.StartZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, zoom); }
if (anim.EndZoom == null) { anim.EndZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, zoom); }
anim.StartPos = (anim.StartLocation == null) ? -DrawOffset : anim.StartLocation.MapPosition;
anim.Timer = Math.Min(anim.Timer + deltaTime, anim.Duration);
float t = anim.Duration <= 0.0f ? 1.0f : Math.Max(anim.Timer / anim.Duration, 0.0f);
DrawOffset = -Vector2.SmoothStep(anim.StartPos.Value, anim.EndLocation.MapPosition, t);
DrawOffset += new Vector2(
(float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.3f % 255, Timing.TotalTime * 0.4f % 255, 0) - 0.5f,
(float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.4f % 255, Timing.TotalTime * 0.3f % 255, 0.5f) - 0.5f) * 50.0f * (float)Math.Sin(t * MathHelper.Pi);
zoom =
MathHelper.Lerp(generationParams.MinZoom, generationParams.MaxZoom,
MathHelper.SmoothStep(anim.StartZoom.Value, anim.EndZoom.Value, t));
if (anim.Timer >= anim.Duration)
{
if (!string.IsNullOrEmpty(anim.EndMessage))
{
new GUIMessageBox("", anim.EndMessage);
anim.EndMessage = null;
return;
}
anim.Finished = true;
}
}
partial void RemoveProjSpecific()
{
noiseOverlay?.Remove();
noiseOverlay = null;
}
}
}