1 using Microsoft.Xna.Framework;
2 using Microsoft.Xna.Framework.Graphics;
4 using System.Collections.Generic;
5 using System.Collections.Immutable;
7 using Microsoft.Xna.Framework.Input;
17 public Location EndLocation;
18 public string StartMessage;
19 public string EndMessage;
24 public float? StartZoom;
28 public float? EndZoom;
30 private float startDelay;
31 public float StartDelay
33 get {
return startDelay; }
41 public Vector2? StartPos;
43 public float Duration;
49 private readonly Queue<MapAnim> mapAnimQueue =
new Queue<MapAnim>();
53 private static Sprite noiseOverlay;
56 private Vector2 drawOffsetNoise;
58 private Vector2 currLocationIndicatorPos;
60 private float zoom = 3.0f;
61 private float targetZoom;
65 private Sprite[,] mapTiles;
66 private bool[,] tileDiscovered;
68 private float connectionHighlightState;
74 private RichString beaconStationActiveText, beaconStationInactiveText;
86 private void CreateEditor()
92 RelativeSpacing = 0.02f,
101 OnClicked = (btn, userData) =>
103 Rand.SetSyncedSeed(ToolBox.StringToInt(
this.Seed));
105 InitProjectSpecific();
112 partial
void InitProjectSpecific()
114 noiseOverlay ??=
new Sprite(
"Content/UI/noise.png", Vector2.Zero);
117 "Map.InitProjSpecific".ToIdentifier(),
118 (locationChangeInfo) => LocationChanged(locationChangeInfo.PrevLocation, locationChangeInfo.NewLocation));
121 (
int)
Locations.Min(l => l.MapPosition.X),
122 (
int)
Locations.Min(l => l.MapPosition.Y),
123 (
int)
Locations.Max(l => l.MapPosition.X),
124 (
int)
Locations.Max(l => l.MapPosition.Y));
125 borders.Width -= borders.X;
126 borders.Height -= borders.Y;
133 Vector2 tileSize = generationParams.
MapTiles.Values.First().First().size * generationParams.
MapTileScale;
134 int tilesX = (int)Math.Ceiling(
Width / tileSize.X);
135 int tilesY = (int)Math.Ceiling(
Height / tileSize.Y);
136 mapTiles =
new Sprite[tilesX, tilesY];
137 tileDiscovered =
new bool[tilesX, tilesY];
138 HashSet<Biome> missingBiomes =
new HashSet<Biome>();
139 for (
int x = 0; x < tilesX; x++)
141 for (
int y = 0; y < tilesY; y++)
143 var biome =
GetBiome(x * tileSize.X);
144 ImmutableArray<Sprite> tileList;
145 if (generationParams.
MapTiles.ContainsKey(biome.Identifier))
147 tileList = generationParams.
MapTiles[biome.Identifier];
151 tileList = generationParams.
MapTiles.Values.First();
152 missingBiomes.Add(biome);
154 mapTiles[x, y] = tileList[x % tileList.Length];
158 foreach (var missingBiome
in missingBiomes)
160 DebugConsole.ThrowError($
"Could not find campaign map sprites for the biome \"{missingBiome.Identifier}\". Using the sprites of the first biome instead...");
163 beaconStationActiveText = RichString.
Rich(TextManager.Get(
"BeaconStationActiveTooltip"));
164 beaconStationInactiveText = RichString.
Rich(TextManager.Get(
"BeaconStationInactiveTooltip"));
168 GenerateAllLocationConnectionVisuals();
171 partial
void GenerateAllLocationConnectionVisuals()
173 foreach (LocationConnection connection
in Connections)
175 GenerateLocationConnectionVisuals(connection);
178 partial
void GenerateLocationConnectionVisuals(LocationConnection connection)
180 Vector2 connectionStart = connection.Locations[0].MapPosition;
181 Vector2 connectionEnd = connection.Locations[1].MapPosition;
182 float connectionLength = Vector2.Distance(connectionStart, connectionEnd);
184 connection.CrackSegments.Clear();
185 connection.CrackSegments.AddRange(MathUtils.GenerateJaggedLine(
186 connectionStart, connectionEnd,
188 rng: Rand.GetRNG(Rand.RandSync.ServerAndClient)));
191 private void LocationChanged(Location prevLocation, Location newLocation)
193 if (prevLocation == newLocation) {
return; }
195 if (prevLocation !=
null)
197 mapAnimQueue.Enqueue(
new MapAnim()
200 EndLocation = prevLocation,
201 Duration = MathHelper.Clamp(Vector2.Distance(-
DrawOffset, prevLocation.MapPosition) / 1000.0f, 0.1f, 0.5f)
203 mapAnimQueue.Enqueue(
new MapAnim()
207 EndLocation = newLocation,
217 if (newLocation.Visited)
219 RemoveFogOfWar(newLocation);
223 partial
void RemoveFogOfWarProjSpecific(Location location) => RemoveFogOfWar(location);
225 private void RemoveFogOfWar(Location location,
bool removeFromAdjacentLocations =
true)
227 if (mapTiles ==
null) {
return; }
228 if (location ==
null) {
return; }
230 var mapTile = generationParams.
MapTiles.Values.FirstOrDefault().FirstOrDefault();
231 if (mapTile ==
null) {
return; }
233 Vector2 mapTileSize = mapTile.size * generationParams.
MapTileScale;
234 int startX = (int)Math.Max(Math.Floor(location.MapPosition.X / mapTileSize.X - 0.25f), 0);
235 int startY = (int)Math.Max(Math.Floor(location.MapPosition.Y / mapTileSize.Y - 0.25f), 0);
236 int endX = (int)Math.Min(Math.Floor(location.MapPosition.X / mapTileSize.X + 0.25f), mapTiles.GetLength(0) - 1);
237 int endY = (int)Math.Min(Math.Floor(location.MapPosition.Y / mapTileSize.Y + 0.25f), mapTiles.GetLength(1) - 1);
238 for (
int x = startX; x <= endX; x++)
240 for (
int y = startY; y <= endY; y++)
242 tileDiscovered[x, y] =
true;
245 if (removeFromAdjacentLocations)
247 foreach (LocationConnection c
in location.Connections)
249 var otherLocation = c.OtherLocation(location);
250 RemoveFogOfWar(otherLocation, removeFromAdjacentLocations:
false);
255 private bool IsInFogOfWar(Location location)
257 if (GameMain.DebugDraw) {
return false; }
259 int x = (int)Math.Floor(location.MapPosition.X / mapTileSize.X);
260 int y = (int)Math.Floor(location.MapPosition.Y / mapTileSize.Y);
262 return !tileDiscovered[MathHelper.Clamp(x, 0, tileDiscovered.Length), MathHelper.Clamp(y, 0, tileDiscovered.Length)];
265 private class MapNotification
267 public readonly RichString Text;
268 public readonly GUIFont Font;
270 public readonly Vector2 TextSize;
272 public int TimesShown;
276 public readonly Location RelatedLocation;
278 public bool IsCurrentlyVisible;
280 public MapNotification(
string text, GUIFont font, List<MapNotification> existingNotifications, Location relatedLocation)
282 Text = RichString.Rich(text);
284 TextSize = Font.MeasureString(Font.ForceUpperCase ? Text.SanitizedValue.ToUpper() : Text.SanitizedValue);
285 if (existingNotifications.Any())
287 Offset = existingNotifications.Max(n => n.Offset + n.TextSize.X + GUI.IntScale(60));
289 RelatedLocation = relatedLocation;
293 private readonly List<MapNotification> mapNotifications =
new List<MapNotification>();
295 partial
void ChangeLocationTypeProjSpecific(Location location, LocalizedString prevName, LocationTypeChange change)
297 var messages = change.GetMessages(location.Faction);
298 if (!messages.Any()) {
return; }
300 string msg = messages.GetRandom(Rand.RandSync.Unsynced)
301 .Replace(
"[previousname]", $
"‖color:gui.yellow‖{prevName}‖end‖")
302 .Replace(
"[name]", $
"‖color:gui.yellow‖{location.DisplayName}‖end‖");
303 location.LastTypeChangeMessage = msg;
305 mapNotifications.Add(
new MapNotification(msg, GUIStyle.SubHeadingFont, mapNotifications, location));
310 Vector2 pos =
new Vector2(container.
Rect.Right, container.
Rect.Center.Y);
311 foreach (var notification
in mapNotifications)
313 Vector2 textPos = pos +
new Vector2(notification.Offset, -notification.TextSize.Y / 2);
315 notification.Font.DrawStringWithColors(
317 notification.Text.SanitizedValue,
319 Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0,
320 notification.Text.RichTextData);
322 int margin = container.Rect.Width / 5;
323 notification.IsCurrentlyVisible =
324 textPos.X < container.Rect.Right - margin &&
325 textPos.X + notification.TextSize.X > container.Rect.X + margin;
331 if (mapNotifications.Count < 5)
334 while (TextManager.ContainsTag(
"randomnews" + maxIndex))
338 string textTag =
"randomnews" + Rand.Range(0, maxIndex);
339 if (TextManager.ContainsTag(textTag))
341 mapNotifications.Add(
new MapNotification(TextManager.Get(textTag).Value, GUIStyle.SubHeadingFont, mapNotifications, relatedLocation:
null));
345 for (
int i = mapNotifications.Count - 1; i >= 0; i--)
347 var notification = mapNotifications[i];
348 notification.Offset -= deltaTime * 75.0f;
349 if (notification.Offset < -notification.TextSize.X - mapContainer.
Rect.Width)
351 notification.Offset = Math.Max(mapNotifications.Max(n => n.Offset + n.TextSize.X) + GUI.IntScale(60), 0);
352 notification.TimesShown++;
353 if (mapNotifications.Count > 5)
355 mapNotifications.RemoveAt(i);
357 else if (mapNotifications.Count > 3 && notification.TimesShown > 2)
359 mapNotifications.RemoveAt(i);
365 private void CreateLocationInfoOverlay(Location location)
367 locationInfoOverlay =
new GUIFrame(
new RectTransform(
new Point(GUI.IntScale(350), GUI.IntScale(350)), GUI.Canvas), style:
"GUIToolTip")
371 locationInfoOverlay.
Color *= 0.8f;
373 var content =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.9f, 0.85f), locationInfoOverlay.
RectTransform,
Anchor.Center))
376 RelativeSpacing = 0.02f
379 bool showReputation = hudVisibility > 0.0f && location.Type.HasOutpost && location.Reputation !=
null;
381 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), content.RectTransform), location.DisplayName, font: GUIStyle.LargeFont) { Padding = Vector4.Zero };
382 if (!location.Type.Name.IsNullOrEmpty())
384 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), content.RectTransform), location.Type.Name, font: GUIStyle.SubHeadingFont) { Padding = Vector4.Zero };
389 if (!location.Type.Description.IsNullOrEmpty())
391 CreateTextWithIcon(location.Type.Description, location.Type.Sprite);
394 int highestSubTier = location.HighestSubmarineTierAvailable();
396 if (location.CanHaveSubsForSale())
398 overrideTiers =
new List<(SubmarineClass subClass, int tier)>();
402 int highestClassTier = location.HighestSubmarineTierAvailable(subClass);
403 if (highestClassTier > 0 && highestClassTier > highestSubTier)
405 overrideTiers.Add((subClass, highestClassTier));
409 if (highestSubTier > 0)
411 CreateTextWithIcon(TextManager.GetWithVariable(
"advancedsub.all",
"[tiernumber]", highestSubTier.ToString()), icon:
null, style:
"LocationOverlaySubmarineIcon");
413 if (overrideTiers !=
null)
415 foreach (var (subClass, tier) in overrideTiers)
417 CreateTextWithIcon(TextManager.GetWithVariable($
"advancedsub.{subClass}",
"[tiernumber]", tier.ToString()), icon:
null, style:
"LocationOverlaySubmarineIcon");
423 void CreateTextWithIcon(LocalizedString text, Sprite icon,
string style =
null)
425 var textHolder =
new GUILayoutGroup(
new RectTransform(
new Point(content.Rect.Width, (
int)GUIStyle.Font.MeasureString(text).Y), content.RectTransform), isHorizontal:
true)
432 new GUIImage(
new RectTransform(Vector2.One * 1.25f, textHolder.RectTransform, scaleBasis:
ScaleBasis.BothHeight), icon) :
433 new GUIImage(new RectTransform(Vector2.One * 1.25f, textHolder.RectTransform, scaleBasis:
ScaleBasis.
BothHeight), style);
434 var textBlock =
new GUITextBlock(
new RectTransform(
new Vector2(0.9f, 1.0f), textHolder.RectTransform), text);
435 textBlock.RectTransform.MinSize =
new Point((
int)textBlock.TextSize.X, 0);
436 textHolder.RectTransform.MinSize =
new Point((
int)textBlock.TextSize.X + guiIcon.Rect.Width, 0);
439 void CreateSpacing(
int height)
441 new GUIFrame(
new RectTransform(
new Point(content.Rect.Width, GUI.IntScale(height)), content.RectTransform), style:
null);
444 if (location.Faction !=
null)
446 new GUITextBlock(
new RectTransform(
new Vector2(1.0f, 0.0f), content.RectTransform),
447 RichString.Rich(TextManager.GetWithVariables(
"reputationgainnotification",
448 (
"[value]",
string.Empty),
449 (
"[reputationname]", $
"‖color:{XMLExtensions.ToStringHex(location.Faction.Prefab.IconColor)}‖{location.Faction.Prefab.Name}‖end‖"))))
451 Padding = Vector4.Zero
456 var repBarHolder =
new GUILayoutGroup(
new RectTransform(
new Point(content.Rect.Width, GUI.IntScale(25)), content.RectTransform), isHorizontal:
true)
459 RelativeSpacing = 0.05f
461 new GUICustomComponent(
new RectTransform(
new Vector2(0.6f, 1.0f), repBarHolder.RectTransform), onDraw: (sb, component) =>
463 if (location.Reputation == null) { return; }
464 RoundSummary.DrawReputationBar(sb, component.Rect,
465 location.Reputation.NormalizedValue,
466 location.Reputation.MinReputation, location.Reputation.MaxReputation);
469 new GUITextBlock(
new RectTransform(
new Vector2(0.4f, 1.0f), repBarHolder.RectTransform),
470 location.Reputation.GetFormattedReputationText(), textAlignment: Alignment.CenterRight);
472 new GUIImage(
new RectTransform(
new Vector2(0.25f, 0.5f), locationInfoOverlay.
RectTransform,
Anchor.BottomRight) { RelativeOffset = new Vector2(0.05f) },
473 location.Faction.Prefab.Icon, scaleToFit:
true)
475 Color = location.Faction.Prefab.IconColor * 0.5f
482 Math.Max(locationInfoOverlay.
Rect.Width, (
int)(content.Children.Max(c => c is GUITextBlock textBlock ? textBlock.TextSize.X : c.RectTransform.MinSize.X) * 1.2f)),
483 (
int)(content.Children.Sum(c => c.Rect.Height) / content.RectTransform.RelativeSize.Y));
486 partial
void ClearAnimQueue()
488 mapAnimQueue.Clear();
495 UpdateNotifications(deltaTime, mapContainer);
498 if (currentDisplayLocation !=
null)
500 if (!currentDisplayLocation.Discovered)
502 RemoveFogOfWar(currentDisplayLocation);
503 Discover(currentDisplayLocation);
504 if (currentDisplayLocation.MapPosition.X > furthestDiscoveredLocation.MapPosition.X)
506 furthestDiscoveredLocation = currentDisplayLocation;
511 Vector2 currentPosition = currentDisplayLocation.MapPosition;
518 currentPosition = startPos +
519 Vector2.Normalize(diff) * Math.Min(100, diff.Length() * 0.2f) * moveDir;
523 currentPosition += Vector2.UnitY * 35;
526 currLocationIndicatorPos = Vector2.Lerp(currLocationIndicatorPos, currentPosition, deltaTime);
530 if (editor ==
null) CreateEditor();
540 Radiation?.MapUpdate(deltaTime);
542 if (mapAnimQueue.Count > 0)
544 hudVisibility = Math.Max(hudVisibility - deltaTime, 0.0f);
545 UpdateMapAnim(mapAnimQueue.Peek(), deltaTime);
546 if (mapAnimQueue.Peek().Finished)
548 mapAnimQueue.Dequeue();
553 hudVisibility = Math.Min(hudVisibility + deltaTime, 0.75f + (
float)Math.Sin(Timing.TotalTime * 3.0f) * 0.25f);
555 Vector2 rectCenter =
new Vector2(rect.Center.X, rect.Center.Y);
556 Vector2 viewOffset = DrawOffset + drawOffsetNoise;
557 if (HighlightedLocation !=
null)
559 Vector2 highlightedLocationDrawPos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom;
560 if (locationInfoOverlay ==
null || locationInfoOverlay.
UserData != HighlightedLocation)
562 CreateLocationInfoOverlay(HighlightedLocation);
565 Point offsetFromLocationIcon =
new Point(GUI.IntScale(25));
567 if (locationInfoRt.Pivot ==
Pivot.BottomLeft || locationInfoRt.Pivot ==
Pivot.BottomRight)
569 offsetFromLocationIcon.Y = -offsetFromLocationIcon.Y;
571 if (locationInfoRt.Pivot ==
Pivot.TopRight || locationInfoRt.Pivot ==
Pivot.BottomRight)
573 offsetFromLocationIcon.X = -offsetFromLocationIcon.X;
575 locationInfoRt.
ScreenSpaceOffset = highlightedLocationDrawPos.ToPoint() + offsetFromLocationIcon;
576 if (locationInfoOverlay.
Rect.Bottom > rect.Bottom)
578 locationInfoRt.Pivot =
Pivot.BottomLeft;
580 if (locationInfoOverlay.
Rect.Right > rect.Right)
582 locationInfoRt.Pivot = locationInfoRt.Pivot ==
Pivot.TopLeft ?
Pivot.TopRight :
Pivot.BottomRight;
587 float closestDist = 0.0f;
588 HighlightedLocation =
null;
589 if ((GUI.MouseOn ==
null || GUI.MouseOn == mapContainer))
591 for (
int i = 0; i < Locations.Count; i++)
594 if (IsInFogOfWar(location) && !(currentDisplayLocation?.Connections.Any(c => c.Locations.Contains(location)) ??
false) && !
GameMain.
DebugDraw) {
continue; }
596 Vector2 pos = rectCenter + (location.
MapPosition + viewOffset) * zoom;
597 if (!rect.Contains(pos)) {
continue; }
600 float iconScale = generationParams.LocationIconSize / locationSprite.
size.X;
601 if (location == currentDisplayLocation) { iconScale *= 1.2f; }
604 drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f);
605 drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f);
606 drawRect.X = (int)pos.X - drawRect.Width / 2;
607 drawRect.Y = (
int)pos.Y - drawRect.Width / 2;
612 if (HighlightedLocation ==
null || dist < closestDist)
615 HighlightedLocation = location;
620 if (SelectedConnection !=
null)
622 connectionHighlightState = Math.Min(connectionHighlightState + deltaTime, 1.0f);
626 connectionHighlightState = 0.0f;
629 if (GUI.KeyboardDispatcher.Subscriber ==
null)
631 float moveSpeed = 1000.0f;
632 Vector2 moveAmount = Vector2.Zero;
637 DrawOffset += moveAmount * moveSpeed / zoom * deltaTime;
640 targetZoom = MathHelper.Clamp(targetZoom, generationParams.MinZoom, generationParams.MaxZoom);
641 zoom = MathHelper.Lerp(zoom, targetZoom * GUI.Scale, 0.1f);
643 if (GUI.MouseOn == mapContainer)
647 if (HighlightedLocation != currentDisplayLocation &&
648 connection.
Locations.Contains(HighlightedLocation) &&
649 connection.
Locations.Contains(currentDisplayLocation))
652 SelectedLocation != HighlightedLocation && HighlightedLocation !=
null)
656 new GUIMessageBox(
string.Empty, TextManager.Get(
"LockedPathTooltip"));
661 connectionHighlightState = 0.0f;
662 SelectedConnection = connection;
663 SelectedLocation = HighlightedLocation;
665 OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection);
678 if (AllowDebugTeleport)
682 var passedConnection = currentDisplayLocation.Connections.Find(c => c.OtherLocation(currentDisplayLocation) == HighlightedLocation);
683 if (passedConnection !=
null)
685 passedConnection.Passed =
true;
688 Location prevLocation = currentDisplayLocation;
689 CurrentLocation = HighlightedLocation;
693 Discover(CurrentLocation);
694 Visit(CurrentLocation);
699 CurrentLocation.CreateStores();
700 ProgressWorld(campaign);
701 Radiation?.OnStep(1);
724 Vector2 viewSize =
new Vector2(rect.Width / zoom, rect.Height / zoom);
725 Vector2 edgeBuffer =
new Vector2(rect.Width * 0.05f);
726 DrawOffset.X = MathHelper.Clamp(DrawOffset.X, -Width - edgeBuffer.X + viewSize.X / 2.0f, edgeBuffer.X - viewSize.X / 2.0f);
727 DrawOffset.Y = MathHelper.Clamp(DrawOffset.Y, -Height - edgeBuffer.Y + viewSize.Y / 2.0f, edgeBuffer.Y - viewSize.Y / 2.0f);
729 drawOffsetNoise =
new Vector2(
730 (
float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.1f % 255, Timing.TotalTime * 0.1f % 255, 0) - 0.5f,
731 (
float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.2f % 255, Timing.TotalTime * 0.2f % 255, 0.5f) - 0.5f) * 10.0f;
733 Vector2 viewOffset = DrawOffset + drawOffsetNoise;
735 Vector2 rectCenter =
new Vector2(rect.Center.X, rect.Center.Y);
737 float missionIconScale = generationParams.MissionIcon !=
null ? 18.0f / generationParams.MissionIcon.SourceRect.Width : 1.0f;
741 spriteBatch.GraphicsDevice.ScissorRectangle =
Rectangle.Intersect(prevScissorRect, rect);
744 Vector2 topLeft = rectCenter + viewOffset - rect.Location.ToVector2();
745 Vector2 bottomRight = topLeft +
new Vector2(Width, Height);
746 Vector2 mapTileSize = mapTiles[0, 0].
size * generationParams.MapTileScale;
748 int startX = (int)Math.Floor(-topLeft.X / mapTileSize.X) - 1;
749 int startY = (int)Math.Floor(-topLeft.Y / mapTileSize.Y) - 1;
750 int endX = (int)Math.Ceiling((-topLeft.X + rect.Width) / mapTileSize.X);
751 int endY = (int)Math.Ceiling((-topLeft.Y + rect.Height) / mapTileSize.Y);
753 float noiseT = (float)(Timing.TotalTime * 0.01f);
754 cameraNoiseStrength = (float)PerlinNoise.CalculatePerlin(noiseT, noiseT * 0.5f, noiseT * 0.2f);
755 float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 5.0f;
757 for (
int x = startX; x <= endX; x++)
759 for (
int y = startY; y <= endY; y++)
761 int tileX = Math.Abs(x) % mapTiles.GetLength(0);
762 int tileY = Math.Abs(y) % mapTiles.GetLength(1);
763 Vector2 tilePos = rectCenter + (viewOffset +
new Vector2(x, y) * mapTileSize) * zoom;
764 mapTiles[tileX, tileY].
Draw(spriteBatch, tilePos, Color.White, origin: Vector2.Zero, scale: generationParams.MapTileScale * zoom);
767 if (!tileDiscovered[tileX, tileY] || x < 0 || y < 0 || x >= tileDiscovered.GetLength(0) || y >= tileDiscovered.GetLength(1))
769 generationParams.FogOfWarSprite?.Draw(spriteBatch, tilePos, Color.White * cameraNoiseStrength, origin: Vector2.Zero, scale: generationParams.MapTileScale * zoom);
770 noiseOverlay.
DrawTiled(spriteBatch, tilePos, mapTileSize * zoom,
771 startOffset:
new Vector2(Rand.Range(0.0f, noiseOverlay.
SourceRect.Width), Rand.Range(0.0f, noiseOverlay.
SourceRect.Height)),
772 color: Color.White * cameraNoiseStrength * 0.2f,
773 textureScale: Vector2.One * noiseScale);
780 if (topLeft.X > rect.X)
781 GUI.DrawRectangle(spriteBatch,
new Rectangle(rect.X, rect.Y, (
int)(topLeft.X - rect.X), rect.Height), Color.Black * 0.5f,
true);
782 if (topLeft.Y > rect.Y)
783 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);
784 if (bottomRight.X < rect.Right)
785 GUI.DrawRectangle(spriteBatch,
new Rectangle((
int)bottomRight.X, rect.Y, (
int)(rect.Right - bottomRight.X), rect.Height), Color.Black * 0.5f,
true);
786 if (bottomRight.Y < rect.Bottom)
787 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);
790 float rawNoiseScale = 1.0f + PerlinNoise.GetPerlin((
int)(Timing.TotalTime * 1 - 1), (
int)(Timing.TotalTime * 1 - 1));
791 DrawNoise(spriteBatch, rect, rawNoiseScale);
793 Radiation?.Draw(spriteBatch, rect, zoom);
795 if (generationParams.ShowLocations)
799 if (IsInFogOfWar(connection.
Locations[0]) && IsInFogOfWar(connection.
Locations[1])) {
continue; }
800 DrawConnection(spriteBatch, connection, rect, viewOffset, currentDisplayLocation);
803 for (
int i = 0; i < Locations.Count; i++)
806 if (!location.
Discovered && IsInFogOfWar(location)) {
continue; }
807 bool isEndLocation = endLocations.Contains(location);
808 if (!
GameMain.
DebugDraw && isEndLocation && location != endLocations.First()) {
continue; }
809 Vector2 pos = rectCenter + (location.
MapPosition + viewOffset) * zoom;
813 Rectangle drawRect = locationSprite.SourceRect;
814 drawRect.X = (int)pos.X - drawRect.Width / 2;
815 drawRect.Y = (
int)pos.Y - drawRect.Width / 2;
817 if (drawRect.X > rect.Right - GUI.IntScale(100) && generationParams.MissionIcon !=
null && location.
AvailableMissions.Any())
819 Vector2 offScreenMissionIconPos =
new Vector2(rect.Right - GUI.IntScale(50), drawRect.Center.Y);
820 generationParams.MissionIcon.Draw(spriteBatch,
821 offScreenMissionIconPos,
822 generationParams.IndicatorColor, scale: missionIconScale * zoom);
823 GUI.Arrow.Draw(spriteBatch,
824 offScreenMissionIconPos + Vector2.UnitX * generationParams.MissionIcon.size.X * missionIconScale * zoom,
825 generationParams.IndicatorColor, MathHelper.PiOver2, scale: 0.5f);
829 if (!rect.Intersects(drawRect)) {
continue; }
832 if (!location.
Visited) { color = Color.White; }
833 if (location.
Connections.Find(c => c.Locations.Contains(currentDisplayLocation)) ==
null)
838 float iconScale = location == currentDisplayLocation ? 1.2f : 1.0f;
839 if (location == HighlightedLocation) { iconScale *= 1.2f; }
840 if (isEndLocation) { iconScale *= 2.0f; }
842 float notificationPulseAmount = 1.0f;
843 float notificationColorLerp = 0.0f;
844 if (mapNotifications.Any(n => n.RelatedLocation == location && n.IsCurrentlyVisible))
846 float sin = MathF.Sin((
float)Timing.TotalTime * 2.0f);
847 notificationPulseAmount = Math.Max(sin + 0.5f, 1.0f);
848 notificationColorLerp = (notificationPulseAmount - 1.0f) * 4.0f;
849 color = Color.Lerp(color, GUIStyle.Yellow, notificationColorLerp);
850 iconScale *= notificationPulseAmount;
853 locationSprite.Draw(spriteBatch, pos, color,
854 scale: generationParams.LocationIconSize / locationSprite.size.X * iconScale * zoom);
858 float factionIconScale = iconScale * 0.7f;
860 Color factionIconColor = Color.Lerp(color, location.
Faction.
Prefab.IconColor, notificationColorLerp);
861 factionIcon.
Draw(spriteBatch, pos +
new Vector2(-15, 15) * zoom, factionIconColor,
862 scale: generationParams.LocationIconSize / factionIcon.
size.X * factionIconScale * zoom);
865 if (location == currentDisplayLocation)
867 if (SelectedLocation !=
null)
869 Vector2 dir = Vector2.Normalize(SelectedLocation.MapPosition - currLocationIndicatorPos);
870 GUI.Arrow.Draw(spriteBatch,
871 rectCenter + (currLocationIndicatorPos + viewOffset) * zoom + dir * generationParams.LocationIconSize * 0.6f * zoom,
872 generationParams.IndicatorColor,
874 rotate: MathUtils.VectorToAngle(dir) + MathHelper.PiOver2,
875 new Vector2(0.5f, 1.0f) * zoom);
877 generationParams.CurrentLocationIndicator.Draw(spriteBatch,
878 rectCenter + (currLocationIndicatorPos + viewOffset) * zoom,
879 generationParams.IndicatorColor,
880 generationParams.CurrentLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.CurrentLocationIndicator.size.X) * 0.8f * zoom);
884 if (location == SelectedLocation)
886 generationParams.SelectedLocationIndicator.Draw(spriteBatch,
887 rectCenter + (location.
MapPosition + viewOffset) * zoom,
888 generationParams.IndicatorColor,
889 generationParams.SelectedLocationIndicator.Origin, 0, Vector2.One * (generationParams.LocationIconSize / generationParams.SelectedLocationIndicator.size.X) * 1.7f * zoom);
894 Vector2 typeChangeIconPos = pos +
new Vector2(1.35f, -0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
895 float typeChangeIconScale = 18.0f / generationParams.TypeChangeIcon.SourceRect.Width;
896 Color iconColor = GUIStyle.Red;
897 color = Color.Lerp(color, GUIStyle.Yellow, notificationColorLerp);
898 iconScale *= notificationPulseAmount;
899 generationParams.TypeChangeIcon.Draw(spriteBatch, typeChangeIconPos, iconColor, scale: typeChangeIconScale * zoom);
900 if (Vector2.Distance(
PlayerInput.
MousePosition, typeChangeIconPos) < generationParams.TypeChangeIcon.SourceRect.Width * zoom &&
901 (tooltip ==
null || IsPreferredTooltip(typeChangeIconPos)))
906 if (location != CurrentLocation && generationParams.MissionIcon !=
null)
908 if ((CurrentLocation == currentDisplayLocation && CurrentLocation.AvailableMissions.Any(m => m.Locations.Contains(location))) ||
911 Vector2 missionIconPos = pos +
new Vector2(1.35f, 0.35f) * generationParams.LocationIconSize * 0.5f * zoom;
912 generationParams.MissionIcon.Draw(spriteBatch, missionIconPos, generationParams.IndicatorColor, scale: missionIconScale * zoom);
913 if (Vector2.Distance(
PlayerInput.
MousePosition, missionIconPos) < generationParams.MissionIcon.SourceRect.Width * zoom && IsPreferredTooltip(missionIconPos))
915 var availableMissions = CurrentLocation.AvailableMissions
916 .Where(m => m.Locations.Contains(location))
919 tooltip = (
new Rectangle(missionIconPos.ToPoint(),
new Point(30)), TextManager.Get(
"mission") +
'\n'+
string.Join(
'\n', availableMissions.Select(m =>
"- " + m.Name)));
927 if (location == HighlightedLocation)
930 GUI.DrawString(spriteBatch, dPos +
new Vector2(15, 32),
"Faction: " + (location.
Faction?.
Prefab.Name ??
"none"), Color.White, Color.Black, font: GUIStyle.SubHeadingFont);
931 GUI.DrawString(spriteBatch, dPos +
new Vector2(15, 50),
"Secondary Faction: " + (location.
SecondaryFaction?.
Prefab.Name ??
"none"), Color.White, Color.Black, font: GUIStyle.SubHeadingFont);
936 GUI.DrawString(spriteBatch,
new Vector2(150,150),
"Dist: " +
937 GetDistanceToClosestLocationOrConnection(CurrentLocation,
int.MaxValue, loc => loc == location), Color.White, Color.Black, font: GUIStyle.SubHeadingFont);
942 GUI.DrawString(spriteBatch, dPos, $
"Difficulty: {location.LevelData.Difficulty.FormatSingleDecimal()}", Color.White, Color.Black * 0.8f, 4, font: GUIStyle.SmallFont);
947 DrawDecorativeHUD(spriteBatch, rect);
949 bool drawRadiationTooltip =
true;
954 drawRadiationTooltip =
false;
957 if (drawRadiationTooltip)
959 Radiation?.DrawFront(spriteBatch);
967 public static void DrawNoise(SpriteBatch spriteBatch, Rectangle rect,
float strength)
969 noiseOverlay ??=
new Sprite(
"Content/UI/noise.png", Vector2.Zero);
971 float noiseT = (float)(Timing.TotalTime * 0.01f);
972 float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 5.0f;
974 float rawNoiseScale = 1.0f + GetPerlinNoise();
976 noiseOverlay.
DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(),
977 startOffset:
new Vector2(Rand.Range(0.0f, noiseOverlay.
SourceRect.Width), Rand.Range(0.0f, noiseOverlay.
SourceRect.Height)),
978 color : Color.White * strength * 0.1f,
979 textureScale: Vector2.One * rawNoiseScale);
981 noiseOverlay.
DrawTiled(spriteBatch, rect.Location.ToVector2(), rect.Size.ToVector2(),
982 startOffset:
new Vector2(Rand.Range(0.0f, noiseOverlay.
SourceRect.Width), Rand.Range(0.0f, noiseOverlay.
SourceRect.Height)),
983 color:
new Color(20,20,20,50),
984 textureScale: Vector2.One * rawNoiseScale * 2);
987 startOffset:
new Vector2(Rand.Range(0.0f, noiseOverlay.
SourceRect.Width), Rand.Range(0.0f, noiseOverlay.
SourceRect.Height)),
988 color: Color.White * strength * 0.1f,
989 textureScale: Vector2.One * noiseScale);
992 private static float GetPerlinNoise() => PerlinNoise.GetPerlin((
int)(Timing.TotalTime * 1 - 1), (
int)(Timing.TotalTime * 1 - 1));
994 private void DrawConnection(SpriteBatch spriteBatch,
LocationConnection connection, Rectangle viewArea, Vector2 viewOffset,
Location currentDisplayLocation, Color? overrideColor =
null)
996 Color connectionColor;
999 float sizeFactor = MathUtils.InverseLerp(
1000 generationParams.SmallLevelConnectionLength,
1001 generationParams.LargeLevelConnectionLength,
1003 connectionColor = ToolBox.GradientLerp(sizeFactor, Color.LightGreen, GUIStyle.Orange, GUIStyle.Red);
1005 else if (overrideColor.HasValue)
1007 connectionColor = overrideColor.Value;
1011 connectionColor = connection.
Passed ? generationParams.ConnectionColor : generationParams.UnvisitedConnectionColor;
1014 int width = (int)(generationParams.LocationConnectionWidth * zoom);
1017 if (Level.Loaded?.LevelData == connection.
LevelData)
1019 connectionColor = generationParams.HighlightedConnectionColor;
1020 width = (int)(width * 1.5f);
1023 if (SelectedLocation != currentDisplayLocation &&
1024 connection.
Locations.Contains(SelectedLocation) && connection.
Locations.Contains(currentDisplayLocation))
1026 connectionColor = generationParams.HighlightedConnectionColor;
1030 else if (HighlightedLocation != currentDisplayLocation &&
1031 connection.
Locations.Contains(HighlightedLocation) && connection.
Locations.Contains(currentDisplayLocation))
1033 connectionColor = generationParams.HighlightedConnectionColor;
1037 Vector2 rectCenter = viewArea.Center.ToVector2();
1039 int startIndex = connection.
CrackSegments.Count > 2 ? 1 : 0;
1042 Vector2? connectionStart =
null;
1043 Vector2? connectionEnd =
null;
1044 for (
int i = startIndex; i < endIndex; i++)
1048 Vector2 start = rectCenter + (segment[0] + viewOffset) * zoom;
1049 if (!connectionStart.HasValue) { connectionStart = start; }
1050 Vector2 end = rectCenter + (segment[1] + viewOffset) * zoom;
1051 connectionEnd = end;
1053 if (!viewArea.Contains(start) && !viewArea.Contains(end))
1059 if (MathUtils.GetLineRectangleIntersection(start, end,
new Rectangle(viewArea.X, viewArea.Y + viewArea.Height, viewArea.Width, viewArea.Height), out Vector2 intersection))
1061 if (!viewArea.Contains(start))
1063 start = intersection;
1075 if (IsInFogOfWar(connection.
Locations[0]))
1079 else if (IsInFogOfWar(connection.
Locations[1]))
1084 float dist = Vector2.Distance(start, end);
1085 var connectionSprite = connection.
Passed ? generationParams.PassedConnectionSprite : generationParams.ConnectionSprite;
1086 if (connectionSprite?.Texture ==
null) {
continue; }
1088 Color segmentColor = connectionColor;
1089 int segmentWidth = width;
1090 if (connection == SelectedConnection)
1092 float t = (i - startIndex) / (
float)(endIndex - startIndex - 1);
1093 if (currentDisplayLocation == connection.
Locations[1]) { t = 1.0f - t; }
1094 if (t > connectionHighlightState)
1097 segmentColor = connection.
Passed ? generationParams.ConnectionColor : generationParams.UnvisitedConnectionColor;
1101 spriteBatch.Draw(connectionSprite.Texture,
1102 new Rectangle((
int)start.X, (int)start.Y, (
int)(dist - 1 * zoom), segmentWidth),
1103 connectionSprite.SourceRect, segmentColor * a,
1104 MathUtils.VectorToAngle(end - start),
1105 new Vector2(0, connectionSprite.size.Y / 2), SpriteEffects.None, 0.01f);
1108 int iconCount = 0, iconIndex = 0;
1109 if (connectionStart.HasValue && connectionEnd.HasValue)
1113 if (connection.
Locked) { iconCount++; }
1114 string tooltip =
null;
1116 float subCrushDepth = SubmarineInfo.GetSubCrushDepth(SubmarineSelection.CurrentOrPendingSubmarine(), ref pendingSubInfo);
1117 string crushDepthWarningIconStyle =
null;
1123 levelData.Size.Y * Math.Max(levelData.GenerationParams.StartPosition.Y, levelData.GenerationParams.EndPosition.Y);
1126 if (spawnDepth * Physics.DisplayToRealWorldRatio > subCrushDepth)
1129 crushDepthWarningIconStyle =
"CrushDepthWarningHighIcon";
1130 tooltip =
"crushdepthwarninghigh";
1134 else if ((spawnDepth + connection.
LevelData.
Size.Y) * Physics.DisplayToRealWorldRatio > subCrushDepth)
1137 crushDepthWarningIconStyle =
"CrushDepthWarningLowIcon";
1138 tooltip =
"crushdepthwarninglow";
1145 (Level.Loaded?.LevelData == connection.
LevelData && Level.Loaded.CheckBeaconActive());
1146 var beaconStationIconStyle = beaconActive ?
"BeaconStationActive" :
"BeaconStationInactive";
1147 DrawIcon(beaconStationIconStyle, (
int)(28 * zoom), beaconActive ? beaconStationActiveText : beaconStationInactiveText);
1153 var unlockEvent = EventPrefab.GetUnlockPathEvent(gateLocation.LevelData.Biome.Identifier, gateLocation.Faction);
1155 if (unlockEvent !=
null)
1157 Reputation unlockReputation = CurrentLocation.
Reputation;
1158 Faction unlockFaction =
null;
1159 if (!unlockEvent.Faction.IsEmpty)
1161 unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.Faction);
1162 unlockReputation = unlockFaction?.
Reputation;
1164 if (unlockReputation !=
null)
1167 "LockedLocationConnection", (
int)(28 * zoom),
1168 RichString.Rich(TextManager.GetWithVariables(unlockEvent.UnlockPathTooltip ??
"LockedPathTooltip",
1169 (
"[requiredreputation]", Reputation.GetFormattedReputationText(MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation), unlockEvent.UnlockPathReputation, addColorTags:
true)),
1170 (
"[currentreputation]", unlockReputation.GetFormattedReputationText(addColorTags:
true)))));
1175 DrawIcon(
"LockedLocationConnection", (
int)(28 * zoom), TextManager.Get(
"LockedPathTooltip"));
1182 DrawIcon(
"HuntingGrounds", (
int)(28 * zoom), RichString.Rich(TextManager.Get(
"HuntingGroundsTooltip")));
1185 if (crushDepthWarningIconStyle !=
null)
1187 DrawIcon(crushDepthWarningIconStyle, (
int)(32 * zoom),
1188 RichString.Rich(TextManager.GetWithVariables(tooltip,
1189 (
"[initialdepth]", $
"‖color:gui.orange‖{(int)(connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio)}‖end‖"),
1190 (
"[submarinecrushdepth]", $
"‖color:gui.orange‖{(int)subCrushDepth}‖end‖"))));
1194 if (GameMain.DebugDraw && zoom > (1.0f * GUI.Scale) && generationParams.ShowLevelTypeNames)
1196 Vector2 center = rectCenter + (connection.
CenterPos + viewOffset) * zoom;
1197 if (viewArea.Contains(center) && connection.
Biome !=
null)
1203 void DrawIcon(
string iconStyle,
int iconSize, RichString tooltipText)
1205 Vector2 iconPos = (connectionStart.Value + connectionEnd.Value) / 2;
1206 Vector2 iconDiff = Vector2.Normalize(connectionEnd.Value - connectionStart.Value) * iconSize;
1208 iconPos += (iconDiff * -(iconCount - 1) / 2.0f) + iconDiff * iconIndex;
1210 var style = GUIStyle.GetComponentStyle(iconStyle);
1211 bool mouseOn = Vector2.DistanceSquared(iconPos, PlayerInput.MousePosition) < iconSize * iconSize && IsPreferredTooltip(iconPos);
1212 Sprite iconSprite = style.GetDefaultSprite();
1213 iconSprite.Draw(spriteBatch, iconPos, (mouseOn ? style.HoverColor : style.Color) * 0.7f,
1214 scale: iconSize / iconSprite.size.X);
1217 tooltip = (
new Rectangle((iconPos - Vector2.One * iconSize / 2).ToPoint(),
new Point(iconSize)), tooltipText);
1223 private bool IsPreferredTooltip(Vector2 tooltipPos)
1225 return tooltip ==
null || Vector2.DistanceSquared(tooltipPos, PlayerInput.MousePosition) < Vector2.DistanceSquared(tooltip.Value.targetArea.Center.ToVector2(), PlayerInput.MousePosition);
1228 private float hudVisibility;
1229 private float cameraNoiseStrength;
1231 private void DrawDecorativeHUD(SpriteBatch spriteBatch, Rectangle rect)
1233 generationParams.DecorativeGraphSprite.Draw(spriteBatch, (
int)((Timing.TotalTime * 5.0f) % generationParams.DecorativeGraphSprite.FrameCount),
1234 new Vector2(rect.X, rect.Bottom - (generationParams.DecorativeGraphSprite.FrameSize.Y + 30) * GUI.Scale),
1235 Color.White, Vector2.Zero, 0, Vector2.One * GUI.Scale, SpriteEffects.FlipVertically);
1237 GUI.DrawString(spriteBatch,
1238 new Vector2(rect.Right - GUI.IntScale(170), rect.Y + GUI.IntScale(5)),
1239 "JOVIAN FLUX " + ((cameraNoiseStrength + Rand.Range(-0.02f, 0.02f)) * 500), generationParams.IndicatorColor * hudVisibility, font: GUIStyle.SmallFont);
1240 GUI.DrawString(spriteBatch,
1241 new Vector2(rect.X + GUI.IntScale(5), rect.Y + GUI.IntScale(5)),
1242 "LAT " + (-DrawOffset.Y / 100.0f) +
" LON " + (-DrawOffset.X / 100.0f), generationParams.IndicatorColor * hudVisibility, font: GUIStyle.SmallFont);
1245 private void UpdateMapAnim(MapAnim anim,
float deltaTime)
1248 if (GUIMessageBox.MessageBoxes.Count(c => !(c is GUIMessageBox mb) || mb.MessageBoxType != GUIMessageBox.Type.Hint) > 0) {
return; }
1250 if (!
string.IsNullOrEmpty(anim.StartMessage))
1252 new GUIMessageBox(
"", anim.StartMessage);
1253 anim.StartMessage =
null;
1257 float unscaledZoom = zoom / GUI.Scale;
1258 if (anim.StartZoom ==
null) { anim.StartZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, unscaledZoom); }
1259 if (anim.EndZoom ==
null) { anim.EndZoom = MathUtils.InverseLerp(generationParams.MinZoom, generationParams.MaxZoom, unscaledZoom); }
1261 anim.StartPos = (anim.StartLocation ==
null) ? -DrawOffset : anim.StartLocation.MapPosition;
1263 anim.Timer = Math.Min(anim.Timer + deltaTime, anim.Duration);
1264 float t = anim.Duration <= 0.0f ? 1.0f : Math.Max(anim.Timer / anim.Duration, 0.0f);
1265 DrawOffset = -Vector2.SmoothStep(anim.StartPos.Value, anim.EndLocation.MapPosition, t);
1266 DrawOffset +=
new Vector2(
1267 (
float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.3f % 255, Timing.TotalTime * 0.4f % 255, 0) - 0.5f,
1268 (
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);
1271 MathHelper.Lerp(generationParams.MinZoom, generationParams.MaxZoom,
1272 MathHelper.SmoothStep(anim.StartZoom.Value, anim.EndZoom.Value, t))
1275 if (anim.Timer >= anim.Duration)
1277 if (!
string.IsNullOrEmpty(anim.EndMessage))
1279 new GUIMessageBox(
"", anim.EndMessage);
1280 anim.EndMessage =
null;
1283 anim.Finished =
true;
1295 partial
void RemoveProjSpecific()
1298 noiseOverlay =
null;
static bool AllowedToManageCampaign(ClientPermissions permissions)
There is a server-side implementation of the method in MultiPlayerCampaign
Location GetCurrentDisplayLocation()
The location that's displayed as the "current one" in the map screen. Normally the current outpost or...
virtual void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
void DrawToolTip(SpriteBatch spriteBatch)
Creates and draws a tooltip.
RectTransform RectTransform
GUIComponent that can be used to render custom content on the UI
static GameSession?? GameSession
static RasterizerState ScissorTestEnable
static int GraphicsHeight
LevelGenerationParams GenerationParams
readonly int InitialDepth
The depth at which the level starts at, in in-game coordinates. E.g. if this was set to 100 000 (= 10...
void DebugSetEndLocation(Location newEndLocation)
void DebugSetStartLocation(Location newStartLocation)
readonly List< Vector2[]> CrackSegments
int TimeSinceLastTypeChange
readonly List< LocationConnection > Connections
bool IsCriticallyRadiated()
IEnumerable< Mission > AvailableMissions
string LastTypeChangeMessage
readonly ImmutableDictionary< Identifier, ImmutableArray< Sprite > > MapTiles
float ConnectionIndicatorDisplacementMultiplier
float ConnectionIndicatorIterationMultiplier
readonly NamedEvent< LocationChangeInfo > OnLocationChanged
From -> To
void Update(CampaignMode campaign, float deltaTime, GUICustomComponent mapContainer)
void DrawNotifications(SpriteBatch spriteBatch, GUICustomComponent container)
List< Location > Locations
Biome GetBiome(Vector2 mapPos)
List< LocationConnection > Connections
Location HighlightedLocation
static void DrawNoise(SpriteBatch spriteBatch, Rectangle rect, float strength)
void Draw(CampaignMode campaign, SpriteBatch spriteBatch, GUICustomComponent mapContainer)
void ResetPendingSub()
Resets pendingSubInfo and forces crush depth to be calculated again for icon displaying purposes
readonly Identifier Identifier
Reputation(CampaignMetadata metadata, Location location, Identifier identifier, int minReputation, int maxReputation, int initialReputation)
static RichString Rich(LocalizedString str, Func< string, string >? postProcess=null)
void Draw(ISpriteBatch spriteBatch, Vector2 pos, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect=SpriteEffects.None)
void DrawTiled(ISpriteBatch spriteBatch, Vector2 position, Vector2 targetSize, float rotation=0f, Vector2? origin=null, Color? color=null, Vector2? startOffset=null, Vector2? textureScale=null, float? depth=null, SpriteEffects? spriteEffects=null)
readonly record struct PendingSubInfo(SubmarineInfo PendingSub=null, bool StructuresDefineRealWorldCrushDepth=false, float RealWorldCrushDepth=Level.DefaultRealWorldCrushDepth)