4 using Microsoft.Xna.Framework;
5 using Microsoft.Xna.Framework.Graphics;
7 using System.Collections.Generic;
8 using System.Collections.Immutable;
9 using System.Globalization;
11 using System.Threading.Tasks;
12 using System.Xml.Linq;
22 private Task loadTask;
23 private (Vector2 Min, Vector2 Max) bounds;
25 private volatile bool isDisposed;
29 private sealed
class LoadedHull
32 public readonly ImmutableList<UInt16> LinkedHulls;
34 public readonly Identifier NameIdentifier;
36 public LoadedHull(XElement element)
39 NameIdentifier = element.GetAttributeIdentifier(
"roomname",
"");
40 Rect = element.GetAttributeRect(
"rect",
Rectangle.Empty);
42 LinkedHulls = element.GetAttributeUshortArray(
"linked", Array.Empty<ushort>()).ToImmutableList();
46 private sealed
class HullCollection
48 public readonly List<LoadedHull> Hulls =
new List<LoadedHull>();
49 public readonly List<Rectangle> Rects =
new List<Rectangle>();
52 public HullCollection(LoadedHull hull)
54 Name = TextManager.Get(hull.NameIdentifier).
Fallback(hull.NameIdentifier.ToString());
58 public void AddHull(LoadedHull hull)
64 private bool Contains(UInt16 hullId)
66 return Hulls.Any(h => h.ID == hullId);
69 public bool IsLinkedTo(HullCollection other)
72 Hulls.Any(h => h.LinkedHulls.Any(
id => other.Contains(
id))) ||
73 other.Hulls.Any(h => h.LinkedHulls.Any(
id => Contains(
id)));
77 private readonly
struct Door
88 private readonly List<HullCollection> hullCollections =
new List<HullCollection>();
89 private readonly List<Door> doors;
101 instance?.
Dispose(); instance =
null;
107 submarineInfo = subInfo;
112 hullCollections =
new List<HullCollection>();
113 doors =
new List<Door>();
120 OnClicked = (btn, obj) => {
Dispose();
return false; }
123 var innerFrame =
new GUIFrame(
new RectTransform(Vector2.One * 0.9f, previewFrame.
RectTransform,
Anchor.Center));
124 int innerPadding = GUI.IntScale(100f);
125 var innerPadded =
new GUIFrame(
new RectTransform(
new Point(innerFrame.Rect.Width - innerPadding, innerFrame.Rect.Height - innerPadding), previewFrame.
RectTransform,
Anchor.Center), style:
null)
127 OutlineColor = Color.Black,
131 GUITextBlock titleText =
null;
132 GUIListBox specsContainer =
null;
134 new GUICustomComponent(
new RectTransform(Vector2.One, innerPadded.RectTransform,
Anchor.Center),
135 (spriteBatch, component) =>
137 if (isDisposed) { return; }
139 Rectangle drawRect =
new Rectangle(component.Rect.X + 1, component.Rect.Y + 1, component.Rect.Width - 2, component.Rect.Height - 2);
140 RenderSubmarine(spriteBatch, drawRect, component);
142 (deltaTime, component) =>
144 if (isDisposed) {
return; }
145 bool isMouseOnComponent = GUI.MouseOn == component;
146 camera.
MoveCamera(deltaTime, allowZoom: isMouseOnComponent, followSub:
false);
147 if (isMouseOnComponent &&
148 (PlayerInput.MidButtonHeld() || PlayerInput.PrimaryMouseButtonHeld()))
150 Vector2 moveSpeed = PlayerInput.MouseSpeed * (float)deltaTime * 60.0f / camera.
Zoom;
151 moveSpeed.X = -moveSpeed.X;
155 if (titleText !=
null && specsContainer !=
null)
157 specsContainer.Visible = GUI.IsMouseOn(titleText);
159 if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape))
165 var topContainer =
new GUIFrame(
new RectTransform(
new Vector2(1f, 0.07f), innerPadded.RectTransform,
Anchor.TopLeft), style:
null)
167 Color = Color.Black * 0.65f
169 var topLayout =
new GUILayoutGroup(
new RectTransform(
new Vector2(0.97f, 5f / 7f), topContainer.RectTransform,
Anchor.Center), isHorizontal:
true, childAnchor:
Anchor.CenterLeft);
171 titleText =
new GUITextBlock(
new RectTransform(
new Vector2(0.95f, 1f), topLayout.RectTransform), subInfo.
DisplayName, font: GUIStyle.LargeFont);
172 new GUIButton(
new RectTransform(
new Vector2(0.05f, 1f), topLayout.RectTransform), TextManager.Get(
"Close"))
174 OnClicked = (btn, obj) => {
Dispose();
return false; }
177 specsContainer =
new GUIListBox(
new RectTransform(
new Vector2(0.4f, 1f), innerPadded.RectTransform,
Anchor.TopLeft) { RelativeOffset = new Vector2(0.015f, 0.07f) })
179 CurrentSelectMode = GUIListBox.SelectMode.None,
180 Color = Color.Black * 0.65f,
181 ScrollBarEnabled =
false,
182 ScrollBarVisible =
false,
183 Spacing = GUI.IntScale(5)
187 includeDescription:
true);
188 int width = specsContainer.Rect.Width;
189 void recalculateSpecsContainerHeight()
192 var children = specsContainer.Content.Children.Where(c => c.Visible);
193 foreach (GUIComponent child
in children)
195 totalSize += child.Rect.Height;
197 totalSize += specsContainer.Content.CountChildren * specsContainer.Spacing;
198 if (specsContainer.PadBottom)
200 GUIComponent last = specsContainer.Content.Children.LastOrDefault();
203 totalSize += specsContainer.Rect.Height - last.Rect.Height;
206 specsContainer.RectTransform.Resize(
new Point(width, totalSize),
true);
207 specsContainer.RecalculateChildren();
210 recalculateSpecsContainerHeight();
211 specsContainer.Content.GetAllChildren<GUITextBlock>().ForEach(c =>
213 var firstChild = c.Children.FirstOrDefault() as GUITextBlock;
214 if (firstChild !=
null)
216 firstChild.CalculateHeightFromText(); firstChild.SetTextPos();
217 c.RectTransform.MinSize =
new Point(0, firstChild.Rect.Height);
219 c.CalculateHeightFromText(); c.SetTextPos();
221 recalculateSpecsContainerHeight();
225 if (isDisposed) {
return; }
230 camera.
Position = (bounds.Min + bounds.Max) * (0.5f, -0.5f);
231 Vector2 span2d = bounds.Max - bounds.Min;
232 Vector2 scaledSpan2d = span2d / camera.
Resolution.ToVector2();
233 float scaledSpan = Math.Max(scaledSpan2d.X, scaledSpan2d.Y);
234 camera.
MinZoom = Math.Min(0.1f, 0.4f / scaledSpan);
235 camera.
Zoom = 0.7f / scaledSpan;
248 if (loadTask !=
null) {
throw new InvalidOperationException(
"Tried to start SubmarinePreview loadTask more than once!"); }
249 loadTask = Task.Run(GeneratePreviewMeshesInternal);
253 private async Task GeneratePreviewMeshesInternal()
256 spriteRecorder.
Begin(SpriteSortMode.BackToFront);
258 HashSet<int> toIgnore =
new HashSet<int>();
259 HashSet<int> wires =
new HashSet<int>();
261 foreach (var subElement
in submarineInfo.SubmarineElement.Elements())
263 switch (subElement.Name.LocalName.ToLowerInvariant())
266 foreach (var component
in subElement.Elements())
268 switch (component.Name.LocalName.ToLowerInvariant())
270 case "itemcontainer":
271 ExtractItemContainerIds(component, toIgnore);
273 case "connectionpanel":
274 ExtractConnectionPanelLinks(component, wires);
280 if (isDisposed) {
return; }
284 var wireNodes =
new List<XElement>();
285 List<LoadedHull> loadedHulls =
new List<LoadedHull>();
286 foreach (var subElement
in submarineInfo.SubmarineElement.Elements())
288 if (subElement.GetAttributeBool(
"hiddeningame",
false)) {
continue; }
289 switch (subElement.Name.LocalName.ToLowerInvariant())
293 var
id = subElement.GetAttributeInt(
"ID", 0);
294 if (wires.Contains(
id))
296 wireNodes.Add(subElement);
298 else if (!toIgnore.Contains(
id))
300 BakeMapEntity(subElement);
304 Identifier identifier = subElement.GetAttributeIdentifier(
"roomname",
"");
305 if (!identifier.IsEmpty)
307 loadedHulls.Add(
new LoadedHull(subElement));
311 if (isDisposed) {
return; }
316 foreach (LoadedHull hull
in loadedHulls)
318 hullCollections.Add(
new HullCollection(hull));
321 bool intersectionFound;
324 intersectionFound =
false;
325 for (
int i = 0; i < hullCollections.Count; i++)
327 for (
int j = i + 1; j < hullCollections.Count; j++)
329 var collection1 = hullCollections[i];
330 var collection2 = hullCollections[j];
331 if (collection1.IsLinkedTo(collection2))
333 collection2.Hulls.ForEach(h => collection1.AddHull(h));
334 hullCollections.Remove(collection2);
335 intersectionFound =
true;
339 if (intersectionFound) {
break; }
341 }
while (intersectionFound);
343 bounds = (spriteRecorder.
Min, spriteRecorder.
Max);
344 wireNodes.ForEach(BakeWireNodes);
346 spriteRecorder.
End();
349 private static void ExtractItemContainerIds(XElement component, HashSet<int> ids)
351 string containedString = component.GetAttributeString(
"contained",
"");
352 string[] itemIdStrings = containedString.Split(
',');
353 for (
int i = 0; i < itemIdStrings.Length; i++)
355 foreach (
string idStr
in itemIdStrings[i].Split(
';'))
357 if (!
int.TryParse(idStr, NumberStyles.Any, CultureInfo.InvariantCulture, out
int id)) {
continue; }
358 if (
id != 0 && !ids.Contains(
id)) { ids.Add(
id); }
363 private static void ExtractConnectionPanelLinks(XElement component, HashSet<int> ids)
365 var pins = component.Elements(
"input").Concat(component.Elements(
"output"));
366 foreach (var pin
in pins)
368 var links = pin.Elements(
"link");
369 foreach (var link
in links)
371 int id = link.GetAttributeInt(
"w", 0);
372 if (
id != 0 && !ids.Contains(
id)) { ids.Add(
id); }
377 private void BakeWireNodes(XElement element)
379 var prefabIdentifier = element.GetAttributeIdentifier(
"identifier",
"");
380 if (prefabIdentifier.IsEmpty) {
return; }
381 if (!ItemPrefab.Prefabs.TryGet(prefabIdentifier, out var prefab)) {
return; }
383 var prefabWireComponentElement = prefab.ConfigElement.GetChildElement(
"wire");
384 if (prefabWireComponentElement is
null) {
return; }
386 var wireComponent = element.GetChildElement(
"wire");
387 if (wireComponent is
null) {
return; }
389 var color = element.GetAttributeColor(
"spritecolor") ?? Color.White;
394 var useSpriteDepth = element.GetAttributeBool(
"usespritedepth",
false);
397 ? element.GetAttributeFloat(
"spritedepth", 1.0f)
400 var width = prefabWireComponentElement.GetAttributeFloat(
"width", 0.3f);
402 for (
int i = 0; i < nodes.Length - 1; i++)
404 var line = (
Start: nodes[i], End: nodes[i + 1]);
406 wireSegment.
Draw(spriteRecorder, wireSprite, color, Vector2.Zero, depth, width);
410 private void BakeMapEntity(XElement element)
412 Identifier identifier = element.GetAttributeIdentifier(
"identifier", Identifier.Empty);
413 if (identifier.IsEmpty) {
return; }
415 if (rect.Equals(
Rectangle.Empty)) {
return; }
417 float depth = element.GetAttributeFloat(
"spritedepth", 1f);
418 bool flippedX = element.GetAttributeBool(
"flippedx",
false);
419 bool flippedY = element.GetAttributeBool(
"flippedy",
false);
421 float scale = element.GetAttributeFloat(
"scale", 1f);
422 Color color = element.GetAttributeColor(
"spritecolor", Color.White);
424 float rotationRad = MathHelper.ToRadians(element.GetAttributeFloat(
"rotation", 0f));
426 MapEntityPrefab prefab;
427 if (element.NameAsIdentifier() ==
"item"
428 && ItemPrefab.Prefabs.TryGet(identifier, out ItemPrefab ip))
434 prefab = MapEntityPrefab.FindByIdentifier(identifier);
436 if (prefab ==
null) {
return; }
438 flippedX &= prefab.CanSpriteFlipX;
439 flippedY &= prefab.CanSpriteFlipY;
441 SpriteEffects spriteEffects = SpriteEffects.None;
444 spriteEffects |= SpriteEffects.FlipHorizontally;
448 spriteEffects |= SpriteEffects.FlipVertically;
451 var prevEffects = prefab.Sprite.effects;
452 prefab.Sprite.effects ^= spriteEffects;
454 bool overrideSprite =
false;
455 ItemPrefab itemPrefab = prefab as ItemPrefab;
456 if (itemPrefab !=
null)
458 BakeItemComponents(itemPrefab, rect, color, scale, rotationRad, depth, out overrideSprite);
463 if (prefab is StructurePrefab structurePrefab)
465 ParseUpgrades(structurePrefab.ConfigElement, ref scale);
467 if (!prefab.ResizeVertical)
469 rect.Height = (int)(rect.Height * scale / prefab.Scale);
471 if (!prefab.ResizeHorizontal)
473 rect.Width = (int)(rect.Width * scale / prefab.Scale);
475 var textureScale = element.GetAttributeVector2(
"texturescale", Vector2.One);
477 Vector2 backGroundOffset = Vector2.Zero;
479 Vector2 textureOffset = element.GetAttributeVector2(
"textureoffset", Vector2.Zero);
481 textureOffset =
Structure.UpgradeTextureOffset(
482 targetSize: rect.Size.ToVector2(),
483 originalTextureOffset: textureOffset,
484 submarineInfo: submarineInfo,
485 sourceRect: prefab.Sprite.SourceRect,
486 scale: textureScale * scale,
490 backGroundOffset =
new Vector2(
491 MathUtils.PositiveModulo(-textureOffset.X, prefab.Sprite.SourceRect.Width * textureScale.X * scale),
492 MathUtils.PositiveModulo(-textureOffset.Y, prefab.Sprite.SourceRect.Height * textureScale.Y * scale));
494 prefab.Sprite.DrawTiled(
495 spriteBatch: spriteRecorder,
496 position:
new Vector2(rect.X + rect.Width / 2, -(rect.Y - rect.Height / 2)),
497 targetSize: rect.Size.ToVector2(),
498 origin: rect.Size.ToVector2() *
new Vector2(0.5f, 0.5f),
500 startOffset: backGroundOffset,
501 textureScale: textureScale * scale,
503 rotation: rotationRad);
505 else if (itemPrefab !=
null)
507 bool usePrefabValues = element.GetAttributeBool(
"isoverride",
false) != itemPrefab.IsOverride;
510 scale = itemPrefab.ConfigElement.GetAttributeFloat(scale,
"scale",
"Scale");
513 ParseUpgrades(itemPrefab.ConfigElement, ref scale);
515 if (prefab.ResizeVertical || prefab.ResizeHorizontal)
517 if (!prefab.ResizeHorizontal)
519 rect.Width = (int)(prefab.Sprite.size.X * scale);
521 if (!prefab.ResizeVertical)
523 rect.Height = (int)(prefab.Sprite.size.Y * scale);
526 var spritePos = rect.Center.ToVector2();
529 prefab.Sprite.DrawTiled(
531 rect.Location.ToVector2() *
new Vector2(1f, -1f),
532 rect.Size.ToVector2(),
534 textureScale: Vector2.One * scale,
537 foreach (var decorativeSprite
in itemPrefab.DecorativeSprites)
539 float offsetState = 0f;
540 Vector2 offset = decorativeSprite.GetOffset(ref offsetState, Vector2.Zero) * scale;
541 if (flippedX) { offset.X = -offset.X; }
542 if (flippedY) { offset.Y = -offset.Y; }
543 decorativeSprite.Sprite.DrawTiled(spriteRecorder,
544 new Vector2(spritePos.X + offset.X - rect.Width / 2, -(spritePos.Y + offset.Y + rect.Height / 2)),
545 rect.Size.ToVector2(), color: color,
546 textureScale: Vector2.One * scale,
547 depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - prefab.Sprite.Depth), 0.999f));
552 rect.Width = (int)(rect.Width * scale / prefab.Scale);
553 rect.Height = (int)(rect.Height * scale / prefab.Scale);
555 var spritePos = rect.Center.ToVector2();
556 spritePos.Y -= rect.Height;
561 spritePos *
new Vector2(1f, -1f),
563 prefab.Sprite.Origin,
566 prefab.Sprite.effects, depth);
568 foreach (var decorativeSprite
in itemPrefab.DecorativeSprites)
570 float rotationState = 0f;
float offsetState = 0f;
571 float rot = decorativeSprite.GetRotation(ref rotationState, 0f);
572 Vector2 offset = decorativeSprite.GetOffset(ref offsetState, Vector2.Zero) * scale;
573 if (flippedX) { offset.X = -offset.X; }
574 if (flippedY) { offset.Y = -offset.Y; }
575 float throwAway = 0.0f;
576 decorativeSprite.Sprite.Draw(spriteRecorder,
new Vector2(spritePos.X + offset.X, -(spritePos.Y + offset.Y)), color, decorativeSprite.Sprite.Origin,
577 rotationRad + rot, decorativeSprite.GetScale(ref throwAway, 0f) * scale, prefab.Sprite.effects,
578 depth: Math.Min(depth + (decorativeSprite.Sprite.Depth - prefab.Sprite.Depth), 0.999f));
584 prefab.Sprite.effects = prevEffects;
587 private void BakeItemComponents(
589 Rectangle rect, Color color,
590 float scale,
float rotationRad,
float depth,
591 out
bool overrideSprite)
593 overrideSprite =
false;
595 float relativeScale = scale / prefab.Scale;
596 foreach (var subElement
in prefab.ConfigElement.Elements())
598 switch (subElement.Name.LocalName.ToLowerInvariant())
601 Sprite barrelSprite =
null;
602 Sprite railSprite =
null;
603 foreach (var turretSubElem
in subElement.Elements())
605 switch (turretSubElem.Name.ToString().ToLowerInvariant())
608 barrelSprite =
new Sprite(turretSubElem);
611 railSprite =
new Sprite(turretSubElem);
616 Vector2 barrelPos = subElement.GetAttributeVector2(
"barrelpos", Vector2.Zero);
617 Vector2 relativeBarrelPos = barrelPos * prefab.Scale -
new Vector2(rect.Width / 2, rect.Height / 2);
618 var transformedBarrelPos = MathUtils.RotatePoint(
622 Vector2 drawPos =
new Vector2(rect.X + rect.Width * relativeScale / 2 + transformedBarrelPos.X * relativeScale, rect.Y - rect.Height * relativeScale / 2 - transformedBarrelPos.Y * relativeScale);
623 drawPos.Y = -drawPos.Y;
625 railSprite?.Draw(spriteRecorder,
629 SpriteEffects.None, depth + (railSprite.Depth - prefab.Sprite.Depth));
631 barrelSprite?.Draw(spriteRecorder,
635 SpriteEffects.None, depth + (barrelSprite.Depth - prefab.Sprite.Depth));
639 var scaledRect = rect with { Size = (rect.Size.ToVector2() * relativeScale).ToPoint() };
641 doors.Add(
new Door(scaledRect));
643 var doorSpriteElem = subElement.Elements().FirstOrDefault(e => e.Name.LocalName.Equals(
"sprite", StringComparison.OrdinalIgnoreCase));
644 if (doorSpriteElem !=
null)
646 string texturePath = doorSpriteElem.GetAttributeStringUnrestricted(
"texture",
"");
647 Vector2 pos = scaledRect.Location.ToVector2() *
new Vector2(1f, -1f);
648 if (subElement.GetAttributeBool(
"horizontal",
false))
650 pos.Y += (float)scaledRect.Height * 0.5f;
654 pos.X += (float)scaledRect.Width * 0.5f;
656 Sprite doorSprite =
new Sprite(doorSpriteElem, texturePath.Contains(
"/") ?
"" : Path.GetDirectoryName(prefab.FilePath));
657 spriteRecorder.Draw(doorSprite.Texture, pos,
658 new Rectangle((
int)doorSprite.SourceRect.X,
659 (
int)doorSprite.SourceRect.Y,
660 (
int)doorSprite.size.X, (
int)doorSprite.size.Y),
661 color, 0.0f, doorSprite.Origin,
new Vector2(scale), SpriteEffects.None, doorSprite.Depth);
665 var backgroundSprElem = subElement.Elements().FirstOrDefault(e => e.Name.LocalName.Equals(
"backgroundsprite", StringComparison.OrdinalIgnoreCase));
666 if (backgroundSprElem !=
null)
668 Sprite backgroundSprite =
new Sprite(backgroundSprElem);
669 backgroundSprite.DrawTiled(spriteRecorder,
670 new Vector2(rect.Left, -rect.Top) - backgroundSprite.Origin * scale,
671 new Vector2(backgroundSprite.size.X * scale, rect.Height), color: color,
672 textureScale: Vector2.One * scale,
673 depth: depth + 0.1f);
680 private void ParseUpgrades(XElement prefabConfigElement, ref
float scale)
682 foreach (var upgrade
in prefabConfigElement.Elements(
"Upgrade"))
684 var upgradeVersion =
new Version(upgrade.GetAttributeString(
"gameversion",
"0.0.0.0"));
685 if (upgradeVersion >= submarineInfo.GameVersion)
687 string scaleModifier = upgrade.GetAttributeString(
"scale",
"*1");
689 if (scaleModifier.StartsWith(
"*"))
691 if (
float.TryParse(scaleModifier.Substring(1), NumberStyles.Any, CultureInfo.InvariantCulture, out
float parsedScale))
693 scale *= parsedScale;
698 if (
float.TryParse(scaleModifier, NumberStyles.Any, CultureInfo.InvariantCulture, out
float parsedScale))
707 private void RenderSubmarine(SpriteBatch spriteBatch, Rectangle scissorRectangle, GUIComponent component)
709 if (spriteRecorder ==
null) {
return; }
711 GUI.DrawRectangle(spriteBatch, scissorRectangle,
new Color(0.051f, 0.149f, 0.271f, 1.0f), isFilled:
true);
713 if (!spriteRecorder.ReadyToRender)
715 LocalizedString waitText = !loadTask.IsCompleted ?
716 TextManager.Get(
"generatingsubmarinepreview",
"loading") :
717 (loadTask.Exception?.ToString() ??
"Task completed without marking as ready to render");
718 Vector2 origin = (GUIStyle.Font.MeasureString(waitText) * 0.5f);
719 origin.X = MathF.Round(origin.X);
720 origin.Y = MathF.Round(origin.Y);
721 GUIStyle.Font.DrawString(
724 scissorRectangle.Center.ToVector2(),
735 var prevScissorRect = GameMain.Instance.GraphicsDevice.ScissorRectangle;
736 GameMain.Instance.GraphicsDevice.ScissorRectangle = scissorRectangle;
737 var prevRasterizerState = GameMain.Instance.GraphicsDevice.RasterizerState;
738 GameMain.Instance.GraphicsDevice.RasterizerState = GameMain.ScissorTestEnable;
740 spriteRecorder.Render(camera);
742 var mousePos = camera.
ScreenToWorld(PlayerInput.MousePosition);
743 mousePos.Y = -mousePos.Y;
745 spriteBatch.Begin(SpriteSortMode.BackToFront, rasterizerState: GameMain.ScissorTestEnable, transformMatrix: camera.
Transform);
746 GameMain.Instance.GraphicsDevice.ScissorRectangle = scissorRectangle;
747 foreach (var hullCollection
in hullCollections)
749 bool mouseOver =
false;
750 if (GUI.MouseOn ==
null || GUI.MouseOn == component)
752 foreach (var rect
in hullCollection.Rects)
754 mouseOver = rect.Contains(mousePos);
755 if (mouseOver) {
break; }
759 foreach (var rect
in hullCollection.Rects)
761 GUI.DrawRectangle(spriteBatch, rect, mouseOver ? Color.Red : Color.Blue, depth: mouseOver ? 0.45f : 0.5f, thickness: (mouseOver ? 4f : 2f) / camera.
Zoom);
766 LocalizedString str = hullCollection.Name;
767 Vector2 strSize = GUIStyle.Font.MeasureString(str) / camera.
Zoom;
768 Vector2 padding =
new Vector2(30, 30) / camera.
Zoom;
769 Vector2 shift =
new Vector2(10, 0) / camera.
Zoom;
771 GUI.DrawRectangle(spriteBatch, mousePos + shift, strSize + padding, Color.Black, isFilled:
true, depth: 0.25f);
772 GUIStyle.Font.DrawString(spriteBatch, str, mousePos + shift + (strSize + padding) * 0.5f, Color.White, 0f, strSize * camera.
Zoom * 0.5f, 1f / camera.
Zoom, SpriteEffects.None, 0f);
775 foreach (var door
in doors)
777 GUI.DrawRectangle(spriteBatch, door.Rect, GUIStyle.Green * 0.5f, isFilled:
true, depth: 0.4f);
781 GameMain.Instance.GraphicsDevice.ScissorRectangle = prevScissorRect;
782 GameMain.Instance.GraphicsDevice.RasterizerState = prevRasterizerState;
783 spriteBatch.Begin(SpriteSortMode.Deferred);
788 if (previewFrame !=
null)
793 spriteRecorder?.Dispose(); spriteRecorder =
null;
void MoveCamera(float deltaTime, bool allowMove=true, bool allowZoom=true, bool allowInput=true, bool? followSub=null)
Vector2 ScreenToWorld(Vector2 coords)
void UpdateTransform(bool interpolate=true, bool updateListener=true)
const ushort NullEntityID
virtual void AddToGUIUpdateList(bool ignoreChildren=false, int order=0)
RectTransform RectTransform
void Draw(ISpriteBatch spriteBatch, Sprite wireSprite, Color color, Vector2 offset, float depth, float width=0.3f)
static Sprite ExtractWireSprite(ContentXElement element)
static IEnumerable< Vector2 > ExtractNodes(XElement element)
LocalizedString Fallback(LocalizedString fallback, bool useDefaultLanguageIfFound=true)
Use this text instead if the original text cannot be found.
void Begin(SpriteSortMode sortMode)
LocalizedString DisplayName
void CreateSpecsWindow(GUIListBox parent, GUIFont font, bool includeTitle=true, bool includeClass=true, bool includeDescription=false, bool includeCrushDepth=false)
static void Create(SubmarineInfo submarineInfo)
Task GeneratePreviewMeshes()
static void AddToGUIUpdateList()
@ Structure
Structures and hulls, but also items (for backwards support)!