Updated to MonoGame 3.6 + Directory refactor

- Barotrauma's projects are in the Barotrauma directory
- All libraries are in the Libraries directory
- MonoGame is now managed by NuGet, rather than referenced from the installed files (TODO: consider using PCL for easier cross-platform development?)
- NuGet libraries are not included in the repo, as getting the latest versions automatically should be preferred
- Removed Content/effects.mgfx as it didn't seem to be used anywhere
- Removed some references to Subsurface directory
- Renamed Launcher2 to Launcher
This commit is contained in:
juanjp600
2017-06-27 09:52:57 -03:00
parent 330b24bcf6
commit 4d225c65f2
1301 changed files with 169 additions and 77952 deletions
@@ -0,0 +1,73 @@
using Microsoft.Xna.Framework;
using Barotrauma.Lights;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Barotrauma.Networking;
using FarseerPhysics;
namespace Barotrauma
{
partial class Explosion
{
partial void ExplodeProjSpecific(Vector2 worldPosition,Hull hull)
{
if (shockwave)
{
GameMain.ParticleManager.CreateParticle("shockwave", worldPosition,
Vector2.Zero, 0.0f, hull);
}
for (int i = 0; i < attack.Range * 0.1f; i++)
{
Vector2 bubblePos = Rand.Vector(attack.Range * 0.5f);
GameMain.ParticleManager.CreateParticle("bubbles", worldPosition + bubblePos,
bubblePos, 0.0f, hull);
if (sparks)
{
GameMain.ParticleManager.CreateParticle("spark", worldPosition,
Rand.Vector(Rand.Range(500.0f, 800.0f)), 0.0f, hull);
}
if (flames)
{
GameMain.ParticleManager.CreateParticle("explosionfire", ClampParticlePos(worldPosition + Rand.Vector(50f), hull),
Rand.Vector(Rand.Range(50.0f, 100.0f)), 0.0f, hull);
}
if (smoke)
{
GameMain.ParticleManager.CreateParticle("smoke", ClampParticlePos(worldPosition + Rand.Vector(50f), hull),
Rand.Vector(Rand.Range(1.0f, 10.0f)), 0.0f, hull);
}
}
float displayRange = attack.Range;
if (displayRange < 0.1f) return;
var light = new LightSource(worldPosition, displayRange, Color.LightYellow, null);
CoroutineManager.StartCoroutine(DimLight(light));
}
private IEnumerable<object> DimLight(LightSource light)
{
float currBrightness = 1.0f;
float startRange = light.Range;
while (light.Color.A > 0.0f)
{
light.Color = new Color(light.Color.R, light.Color.G, light.Color.B, currBrightness);
light.Range = startRange * currBrightness;
currBrightness -= CoroutineManager.DeltaTime * 20.0f;
yield return CoroutineStatus.Running;
}
light.Remove();
yield return CoroutineStatus.Success;
}
}
}
@@ -0,0 +1,86 @@
using Barotrauma.Lights;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Barotrauma.Networking;
namespace Barotrauma
{
partial class FireSource
{
static Sound fireSoundBasic, fireSoundLarge;
private LightSource lightSource;
partial void UpdateProjSpecific(float growModifier)
{
if (hull.FireSources.Any(fs => fs != this && fs.size.X > size.X))
{
if (basicSoundIndex > 0)
{
Sounds.SoundManager.Stop(basicSoundIndex);
basicSoundIndex = -1;
}
if (largeSoundIndex > 0)
{
Sounds.SoundManager.Stop(largeSoundIndex);
largeSoundIndex = -1;
}
}
else
{
if (fireSoundBasic != null)
{
basicSoundIndex = fireSoundBasic.Loop(basicSoundIndex,
Math.Min(size.X / 100.0f, 1.0f), WorldPosition + size / 2.0f, 1000.0f);
}
if (fireSoundLarge != null)
{
largeSoundIndex = fireSoundLarge.Loop(largeSoundIndex,
MathHelper.Clamp((size.X - 200.0f) / 100.0f, 0.0f, 1.0f), WorldPosition + size / 2.0f, 1000.0f);
}
}
float count = Rand.Range(0.0f, size.X / 50.0f);
for (int i = 0; i < count; i++)
{
Vector2 particlePos = new Vector2(
WorldPosition.X + Rand.Range(0.0f, size.X),
Rand.Range(WorldPosition.Y - size.Y, WorldPosition.Y + 20.0f));
Vector2 particleVel = new Vector2(
(particlePos.X - (WorldPosition.X + size.X / 2.0f)),
(float)Math.Sqrt(size.X) * Rand.Range(0.0f, 15.0f) * growModifier);
var particle = GameMain.ParticleManager.CreateParticle("flame",
particlePos, particleVel, 0.0f, hull);
if (particle == null) continue;
//make some of the particles create another firesource when they enter another hull
if (Rand.Int(20) == 1) particle.OnChangeHull = OnChangeHull;
particle.Size *= MathHelper.Clamp(size.X / 60.0f * Math.Max(hull.Oxygen / hull.FullVolume, 0.4f), 0.5f, 1.0f);
if (Rand.Int(5) == 1)
{
var smokeParticle = GameMain.ParticleManager.CreateParticle("smoke",
particlePos, new Vector2(particleVel.X, particleVel.Y * 0.1f), 0.0f, hull);
if (smokeParticle != null)
{
smokeParticle.Size *= MathHelper.Clamp(size.X / 100.0f * Math.Max(hull.Oxygen / hull.FullVolume, 0.4f), 0.5f, 1.0f);
}
}
}
lightSource.Range = Math.Max(size.X, size.Y) * 10.0f / 2.0f;
lightSource.Color = new Color(1.0f, 0.45f, 0.3f) * Rand.Range(0.8f, 1.0f);
lightSource.Position = position + Vector2.UnitY * 30.0f;
}
}
}
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.ObjectModel;
using Barotrauma.Items.Components;
namespace Barotrauma
{
partial class Gap : MapEntity
{
public override void Draw(SpriteBatch sb, bool editing, bool back = true)
{
if (GameMain.DebugDraw)
{
Vector2 center = new Vector2(WorldRect.X + rect.Width / 2.0f, -(WorldRect.Y - rect.Height / 2.0f));
GUI.DrawLine(sb, center, center + new Vector2(flowForce.X, -flowForce.Y) / 10.0f, Color.Red);
GUI.DrawLine(sb, center + Vector2.One * 5.0f, center + new Vector2(lerpedFlowForce.X, -lerpedFlowForce.Y) / 10.0f + Vector2.One * 5.0f, Color.Orange);
}
if (!editing || !ShowGaps) return;
Color clr = (open == 0.0f) ? Color.Red : Color.Cyan;
if (isHighlighted) clr = Color.Gold;
float depth = (ID % 255) * 0.000001f;
GUI.DrawRectangle(
sb, new Rectangle(WorldRect.X, -WorldRect.Y, rect.Width, rect.Height),
clr * 0.5f, true,
depth,
(int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f));
for (int i = 0; i < linkedTo.Count; i++)
{
Vector2 dir = isHorizontal ?
new Vector2(Math.Sign(linkedTo[i].Rect.Center.X - rect.Center.X), 0.0f)
: new Vector2(0.0f, Math.Sign((linkedTo[i].Rect.Y - linkedTo[i].Rect.Height / 2.0f) - (rect.Y - rect.Height / 2.0f)));
Vector2 arrowPos = new Vector2(WorldRect.Center.X, -(WorldRect.Y - WorldRect.Height / 2));
arrowPos += new Vector2(dir.X * (WorldRect.Width / 2 + 10), dir.Y * (WorldRect.Height / 2 + 10));
GUI.Arrow.Draw(sb,
arrowPos, clr * 0.8f,
GUI.Arrow.Origin, MathUtils.VectorToAngle(dir) + MathHelper.PiOver2,
isHorizontal ? new Vector2(rect.Height / 16.0f, 1.0f) : new Vector2(rect.Width / 16.0f, 1.0f),
SpriteEffects.None, depth);
}
if (IsSelected)
{
GUI.DrawRectangle(sb,
new Vector2(WorldRect.X - 5, -WorldRect.Y - 5),
new Vector2(rect.Width + 10, rect.Height + 10),
Color.Red,
false,
depth,
(int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f));
}
}
public override XElement Save(XElement parentElement)
{
XElement element = new XElement("Gap");
element.Add(
new XAttribute("ID", ID),
new XAttribute("horizontal", isHorizontal ? "true" : "false"));
element.Add(new XAttribute("rect",
(int)(rect.X - Submarine.HiddenSubPosition.X) + "," +
(int)(rect.Y - Submarine.HiddenSubPosition.Y) + "," +
rect.Width + "," + rect.Height));
//if (linkedTo != null)
//{
// int i = 0;
// foreach (Entity e in linkedTo)
// {
// if (e == null) continue;
// element.Add(new XAttribute("linkedto" + i, e.ID));
// i += 1;
// }
//}
parentElement.Add(element);
return element;
}
}
}
@@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Lidgren.Network;
using Barotrauma.Networking;
namespace Barotrauma
{
partial class Hull : MapEntity, IPropertyObject, IServerSerializable
{
public static WaterRenderer renderer;
private Sound currentFlowSound;
private int soundIndex;
private float soundVolume;
public override bool IsMouseOn(Vector2 position)
{
if (!GameMain.DebugDraw && !ShowHulls) return false;
return (Submarine.RectContains(WorldRect, position) &&
!Submarine.RectContains(MathUtils.ExpandRect(WorldRect, -8), position));
}
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
//if (back) return;
Rectangle drawRect;
if (!Visible)
{
drawRect =
Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height);
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y),
new Vector2(rect.Width, rect.Height),
Color.Black, true,
0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f));
}
if (!ShowHulls && !GameMain.DebugDraw) return;
if (!editing && !GameMain.DebugDraw) return;
if (aiTarget != null) aiTarget.Draw(spriteBatch);
drawRect =
Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height);
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y),
new Vector2(rect.Width, rect.Height),
Color.Blue, false, (ID % 255) * 0.000001f, (int)Math.Max((1.5f / Screen.Selected.Cam.Zoom), 1.0f));
GUI.DrawRectangle(spriteBatch,
new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height),
Color.Red * ((100.0f - OxygenPercentage) / 400.0f), true, 0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f));
if (GameMain.DebugDraw)
{
GUI.SmallFont.DrawString(spriteBatch, "Pressure: " + ((int)pressure - rect.Y).ToString() +
" - Oxygen: " + ((int)OxygenPercentage), new Vector2(drawRect.X + 5, -drawRect.Y + 5), Color.White);
GUI.SmallFont.DrawString(spriteBatch, volume + " / " + FullVolume, new Vector2(drawRect.X + 5, -drawRect.Y + 20), Color.White);
foreach (FireSource fs in fireSources)
{
GUI.DrawRectangle(spriteBatch, new Rectangle((int)fs.WorldPosition.X, (int)-fs.WorldPosition.Y, (int)fs.Size.X, (int)fs.Size.Y), Color.Orange, false);
}
}
if ((IsSelected || isHighlighted) && editing)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X + 5, -drawRect.Y + 5),
new Vector2(rect.Width - 10, rect.Height - 10),
isHighlighted ? Color.LightBlue * 0.5f : Color.Red * 0.5f, true, 0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f));
}
}
public void Render(GraphicsDevice graphicsDevice, Camera cam)
{
if (renderer.PositionInBuffer > renderer.vertices.Length - 6) return;
Vector2 submarinePos = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
//calculate where the surface should be based on the water volume
float top = rect.Y + submarinePos.Y;
float bottom = top - rect.Height;
float drawSurface = surface + submarinePos.Y;
Matrix transform = cam.Transform * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
if (bottom > cam.WorldView.Y || top < cam.WorldView.Y - cam.WorldView.Height) return;
if (!update)
{
// create the four corners of our triangle.
Vector3[] corners = new Vector3[4];
corners[0] = new Vector3(rect.X, rect.Y, 0.0f);
corners[1] = new Vector3(rect.X + rect.Width, rect.Y, 0.0f);
corners[2] = new Vector3(corners[1].X, rect.Y - rect.Height, 0.0f);
corners[3] = new Vector3(corners[0].X, corners[2].Y, 0.0f);
Vector2[] uvCoords = new Vector2[4];
for (int i = 0; i < 4; i++)
{
corners[i] += new Vector3(submarinePos, 0.0f);
uvCoords[i] = Vector2.Transform(new Vector2(corners[i].X, -corners[i].Y), transform);
}
renderer.vertices[renderer.PositionInBuffer] = new VertexPositionTexture(corners[0], uvCoords[0]);
renderer.vertices[renderer.PositionInBuffer + 1] = new VertexPositionTexture(corners[1], uvCoords[1]);
renderer.vertices[renderer.PositionInBuffer + 2] = new VertexPositionTexture(corners[2], uvCoords[2]);
renderer.vertices[renderer.PositionInBuffer + 3] = new VertexPositionTexture(corners[0], uvCoords[0]);
renderer.vertices[renderer.PositionInBuffer + 4] = new VertexPositionTexture(corners[2], uvCoords[2]);
renderer.vertices[renderer.PositionInBuffer + 5] = new VertexPositionTexture(corners[3], uvCoords[3]);
renderer.PositionInBuffer += 6;
return;
}
float x = rect.X + Submarine.DrawPosition.X;
int start = (int)Math.Floor((cam.WorldView.X - x) / WaveWidth);
start = Math.Max(start, 0);
int end = (waveY.Length - 1)
- (int)Math.Floor((float)((x + rect.Width) - (cam.WorldView.X + cam.WorldView.Width)) / WaveWidth);
end = Math.Min(end, waveY.Length - 1);
x += start * WaveWidth;
for (int i = start; i < end; i++)
{
if (renderer.PositionInBuffer > renderer.vertices.Length - 6) return;
Vector3[] corners = new Vector3[4];
corners[0] = new Vector3(x, top, 0.0f);
corners[3] = new Vector3(corners[0].X, drawSurface + waveY[i], 0.0f);
//skip adjacent "water rects" if the surface of the water is roughly at the same position
int width = WaveWidth;
while (i < end - 1 && Math.Abs(waveY[i + 1] - waveY[i]) < 1.0f)
{
width += WaveWidth;
i++;
}
corners[1] = new Vector3(x + width, top, 0.0f);
corners[2] = new Vector3(corners[1].X, drawSurface + waveY[i + 1], 0.0f);
Vector2[] uvCoords = new Vector2[4];
for (int n = 0; n < 4; n++)
{
uvCoords[n] = Vector2.Transform(new Vector2(corners[n].X, -corners[n].Y), transform);
}
renderer.vertices[renderer.PositionInBuffer] = new VertexPositionTexture(corners[0], uvCoords[0]);
renderer.vertices[renderer.PositionInBuffer + 1] = new VertexPositionTexture(corners[1], uvCoords[1]);
renderer.vertices[renderer.PositionInBuffer + 2] = new VertexPositionTexture(corners[2], uvCoords[2]);
renderer.vertices[renderer.PositionInBuffer + 3] = new VertexPositionTexture(corners[0], uvCoords[0]);
renderer.vertices[renderer.PositionInBuffer + 4] = new VertexPositionTexture(corners[2], uvCoords[2]);
renderer.vertices[renderer.PositionInBuffer + 5] = new VertexPositionTexture(corners[3], uvCoords[3]);
renderer.PositionInBuffer += 6;
x += width;
}
}
public override XElement Save(XElement parentElement)
{
XElement element = new XElement("Hull");
element.Add
(
new XAttribute("ID", ID),
new XAttribute("rect",
(int)(rect.X - Submarine.HiddenSubPosition.X) + "," +
(int)(rect.Y - Submarine.HiddenSubPosition.Y) + "," +
rect.Width + "," + rect.Height),
new XAttribute("water", volume)
);
parentElement.Add(element);
return element;
}
}
}
@@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Voronoi2;
using Microsoft.Xna.Framework.Graphics;
using FarseerPhysics;
using FarseerPhysics.Common;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
namespace Barotrauma
{
static partial class CaveGenerator
{
public static List<VertexPositionTexture> GenerateRenderVerticeList(List<Vector2[]> triangles)
{
var verticeList = new List<VertexPositionTexture>();
for (int i = 0; i < triangles.Count; i++)
{
foreach (Vector2 vertex in triangles[i])
{
//shift the coordinates around a bit to make the texture repetition less obvious
Vector2 uvCoords = new Vector2(
vertex.X / 2000.0f + (float)Math.Sin(vertex.X / 500.0f) * 0.15f,
vertex.Y / 2000.0f + (float)Math.Sin(vertex.Y / 700.0f) * 0.15f);
verticeList.Add(new VertexPositionTexture(new Vector3(vertex, 1.0f), uvCoords));
}
}
return verticeList;
}
public static VertexPositionTexture[] GenerateWallShapes(List<VoronoiCell> cells)
{
float inwardThickness = 500.0f, outWardThickness = 30.0f;
List<VertexPositionTexture> verticeList = new List<VertexPositionTexture>();
foreach (VoronoiCell cell in cells)
{
//if (cell.body == null) continue;
foreach (GraphEdge edge in cell.edges)
{
if (edge.cell1 != null && edge.cell1.body == null && edge.cell1.CellType != CellType.Empty) edge.cell1 = null;
if (edge.cell2 != null && edge.cell2.body == null && edge.cell2.CellType != CellType.Empty) edge.cell2 = null;
CompareCCW compare = new CompareCCW(cell.Center);
if (compare.Compare(edge.point1, edge.point2) == -1)
{
var temp = edge.point1;
edge.point1 = edge.point2;
edge.point2 = temp;
}
}
}
foreach (VoronoiCell cell in cells)
{
//if (cell.body == null) continue;
foreach (GraphEdge edge in cell.edges)
{
if (!edge.isSolid) continue;
GraphEdge leftEdge = cell.edges.Find(e => e != edge && (edge.point1 == e.point1 || edge.point1 == e.point2));
GraphEdge rightEdge = cell.edges.Find(e => e != edge && (edge.point2 == e.point1 || edge.point2 == e.point2));
Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero;
if (leftEdge == null)
{
leftNormal = GetEdgeNormal(edge, cell);
}
else
{
leftNormal = (leftEdge.isSolid) ?
Vector2.Normalize(GetEdgeNormal(leftEdge) + GetEdgeNormal(edge, cell)) :
Vector2.Normalize(leftEdge.Center - edge.point1);
}
if (!MathUtils.IsValid(leftNormal))
{
#if DEBUG
DebugConsole.ThrowError("Invalid left normal");
#endif
if (cell.body != null)
{
GameMain.World.RemoveBody(cell.body);
cell.body = null;
}
leftNormal = Vector2.UnitX;
break;
}
if (rightEdge == null)
{
rightNormal = GetEdgeNormal(edge, cell);
}
else
{
rightNormal = (rightEdge.isSolid) ?
Vector2.Normalize(GetEdgeNormal(rightEdge) + GetEdgeNormal(edge, cell)) :
Vector2.Normalize(rightEdge.Center - edge.point2);
}
if (!MathUtils.IsValid(rightNormal))
{
#if DEBUG
DebugConsole.ThrowError("Invalid right normal");
#endif
if (cell.body != null)
{
GameMain.World.RemoveBody(cell.body);
cell.body = null;
}
rightNormal = Vector2.UnitX;
break;
}
for (int i = 0; i < 2; i++)
{
Vector2[] verts = new Vector2[3];
VertexPositionTexture[] vertPos = new VertexPositionTexture[3];
if (i == 0)
{
verts[0] = edge.point1 - leftNormal * outWardThickness;
verts[1] = edge.point2 - rightNormal * outWardThickness;
verts[2] = edge.point1 + leftNormal * inwardThickness;
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), Vector2.Zero);
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), Vector2.UnitX);
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(0, 0.5f));
}
else
{
verts[0] = edge.point1 + leftNormal * inwardThickness;
verts[1] = edge.point2 - rightNormal * outWardThickness;
verts[2] = edge.point2 + rightNormal * inwardThickness;
vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(0.0f, 0.5f));
vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), Vector2.UnitX);
vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(1.0f, 0.5f));
}
var comparer = new CompareCCW((verts[0] + verts[1] + verts[2]) / 3.0f);
Array.Sort(verts, vertPos, comparer);
for (int j = 0; j < 3; j++)
{
verticeList.Add(vertPos[j]);
}
}
}
}
return verticeList.ToArray();
}
}
}
@@ -0,0 +1,59 @@
using FarseerPhysics;
using FarseerPhysics.Common;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using Voronoi2;
using Barotrauma.RuinGeneration;
namespace Barotrauma
{
partial class Level
{
private LevelRenderer renderer;
public void DrawFront(SpriteBatch spriteBatch)
{
if (renderer == null) return;
renderer.Draw(spriteBatch);
if (GameMain.DebugDraw)
{
foreach (InterestingPosition pos in positionsOfInterest)
{
Color color = Color.Yellow;
if (pos.PositionType == PositionType.Cave)
{
color = Color.DarkOrange;
}
else if (pos.PositionType == PositionType.Ruin)
{
color = Color.LightGray;
}
GUI.DrawRectangle(spriteBatch, new Vector2(pos.Position.X - 15.0f, -pos.Position.Y - 15.0f), new Vector2(30.0f, 30.0f), color, true);
}
}
}
public void DrawBack(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, BackgroundCreatureManager backgroundSpriteManager = null)
{
float brightness = MathHelper.Clamp(50.0f + (cam.Position.Y - Size.Y) / 2000.0f, 10.0f, 40.0f);
float avgValue = (backgroundColor.R + backgroundColor.G + backgroundColor.G) / 3;
GameMain.LightManager.AmbientLight = new Color(backgroundColor * (brightness / avgValue), 1.0f);
graphics.Clear(backgroundColor);
if (renderer == null) return;
renderer.DrawBackground(spriteBatch, cam, backgroundSpriteManager);
}
}
}
@@ -0,0 +1,253 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Voronoi2;
namespace Barotrauma
{
class LevelRenderer : IDisposable
{
private static BasicEffect wallEdgeEffect, wallCenterEffect;
private static Sprite background, backgroundTop;
private static Sprite dustParticles;
private static Texture2D shaftTexture;
private static BackgroundSpriteManager backgroundSpriteManager;
Vector2 dustOffset;
private Level level;
private VertexBuffer wallVertices, bodyVertices;
//public VertexPositionTexture[] WallVertices;
//public VertexPositionColor[] BodyVertices;
public LevelRenderer(Level level)
{
if (shaftTexture == null) shaftTexture = TextureLoader.FromFile("Content/Map/iceWall.png");
if (background==null)
{
background = new Sprite("Content/Map/background2.png", Vector2.Zero);
backgroundTop = new Sprite("Content/Map/background.png", Vector2.Zero);
dustParticles = new Sprite("Content/Map/dustparticles.png", Vector2.Zero);
}
if (wallEdgeEffect == null)
{
wallEdgeEffect = new BasicEffect(GameMain.Instance.GraphicsDevice)
{
DiffuseColor = new Vector3(0.8f, 0.8f, 0.8f),
VertexColorEnabled = false,
TextureEnabled = true,
Texture = shaftTexture
};
wallEdgeEffect.CurrentTechnique = wallEdgeEffect.Techniques["BasicEffect_Texture"];
}
if (wallCenterEffect == null)
{
wallCenterEffect = new BasicEffect(GameMain.Instance.GraphicsDevice)
{
VertexColorEnabled = false,
TextureEnabled = true,
Texture = backgroundTop.Texture
};
wallCenterEffect.CurrentTechnique = wallCenterEffect.Techniques["BasicEffect_Texture"];
}
if (backgroundSpriteManager==null)
{
var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.BackgroundSpritePrefabs);
if (files.Count > 0)
backgroundSpriteManager = new BackgroundSpriteManager(files);
else
backgroundSpriteManager = new BackgroundSpriteManager("Content/BackgroundSprites/BackgroundSpritePrefabs.xml");
}
this.level = level;
}
public void PlaceSprites(int amount)
{
backgroundSpriteManager.PlaceSprites(level, amount);
}
public void Update(float deltaTime)
{
dustOffset -= Vector2.UnitY * 10.0f * deltaTime;
backgroundSpriteManager.Update(deltaTime);
}
public void SetWallVertices(VertexPositionTexture[] vertices)
{
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionTexture.VertexDeclaration, vertices.Length,BufferUsage.WriteOnly);
wallVertices.SetData(vertices);
}
public void SetBodyVertices(VertexPositionTexture[] vertices)
{
bodyVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
bodyVertices.SetData(vertices);
}
public void DrawBackground(SpriteBatch spriteBatch, Camera cam, BackgroundCreatureManager backgroundCreatureManager = null)
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearWrap);
Vector2 backgroundPos = cam.WorldViewCenter;
backgroundPos.Y = -backgroundPos.Y;
backgroundPos /= 20.0f;
if (backgroundPos.Y < 1024)
{
if (backgroundPos.Y < 0)
{
backgroundTop.SourceRect = new Rectangle((int)backgroundPos.X, (int)backgroundPos.Y, 1024, (int)Math.Min(-backgroundPos.Y, 1024));
backgroundTop.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, Math.Min(-backgroundPos.Y, GameMain.GraphicsHeight)),
Vector2.Zero, level.BackgroundColor);
}
if (backgroundPos.Y > -1024)
{
background.SourceRect = new Rectangle((int)backgroundPos.X, (int)Math.Max(backgroundPos.Y, 0), 1024, 1024);
background.DrawTiled(spriteBatch,
(backgroundPos.Y < 0) ? new Vector2(0.0f, (int)-backgroundPos.Y) : Vector2.Zero,
new Vector2(GameMain.GraphicsWidth, (int)Math.Ceiling(1024 - backgroundPos.Y)),
Vector2.Zero, level.BackgroundColor);
}
}
spriteBatch.End();
spriteBatch.Begin(SpriteSortMode.BackToFront,
BlendState.AlphaBlend,
SamplerState.LinearWrap, DepthStencilState.Default, null, null,
cam.Transform);
backgroundSpriteManager.DrawSprites(spriteBatch, cam);
if (backgroundCreatureManager!=null) backgroundCreatureManager.Draw(spriteBatch);
for (int i = 0; i < 4; i++)
{
float scale = 1.0f - i * 0.2f;
//alpha goes from 1.0 to 0.0 when scale is in the range of 0.2-0.1
float alpha = (cam.Zoom * scale) < 0.2f ? (cam.Zoom * scale - 0.1f) * 10.0f : 1.0f;
if (alpha <= 0.0f) continue;
Vector2 offset = (new Vector2(cam.WorldViewCenter.X, cam.WorldViewCenter.Y) + dustOffset) * scale;
Vector3 origin = new Vector3(cam.WorldView.Width, cam.WorldView.Height, 0.0f) * 0.5f;
dustParticles.SourceRect = new Rectangle(
(int)((offset.X - origin.X + (i * 256)) / scale),
(int)((-offset.Y - origin.Y + (i * 256)) / scale),
(int)((cam.WorldView.Width) / scale),
(int)((cam.WorldView.Height) / scale));
spriteBatch.Draw(dustParticles.Texture,
new Vector2(cam.WorldViewCenter.X, -cam.WorldViewCenter.Y),
dustParticles.SourceRect, Color.White * alpha, 0.0f,
new Vector2(cam.WorldView.Width, cam.WorldView.Height) * 0.5f / scale, scale, SpriteEffects.None, 1.0f - scale);
}
spriteBatch.End();
RenderWalls(GameMain.Instance.GraphicsDevice, cam);
}
public void Draw(SpriteBatch spriteBatch)
{
if (GameMain.DebugDraw)
{
var cells = level.GetCells(GameMain.GameScreen.Cam.WorldViewCenter, 2);
foreach (VoronoiCell cell in cells)
{
GUI.DrawRectangle(spriteBatch, new Vector2(cell.Center.X - 10.0f, -cell.Center.Y-10.0f), new Vector2(20.0f, 20.0f), Color.Cyan, true);
GUI.DrawLine(spriteBatch,
new Vector2(cell.edges[0].point1.X, -cell.edges[0].point1.Y),
new Vector2(cell.Center.X, -cell.Center.Y),
Color.Blue*0.5f);
foreach (GraphEdge edge in cell.edges)
{
GUI.DrawLine(spriteBatch, new Vector2(edge.point1.X, -edge.point1.Y),
new Vector2(edge.point2.X, -edge.point2.Y), cell.body==null ? Color.Cyan*0.5f : Color.White);
}
foreach (Vector2 point in cell.bodyVertices)
{
GUI.DrawRectangle(spriteBatch, new Vector2(point.X, -point.Y), new Vector2(10.0f, 10.0f), Color.White, true);
}
}
}
Vector2 pos = new Vector2(0.0f, -level.Size.Y);
if (GameMain.GameScreen.Cam.WorldView.Y < -pos.Y - 1024) return;
pos.X = GameMain.GameScreen.Cam.WorldView.X -1024;
int width = (int)(Math.Ceiling(GameMain.GameScreen.Cam.WorldView.Width / 1024 + 4.0f) * 1024);
GUI.DrawRectangle(spriteBatch,new Rectangle((int)(MathUtils.Round(pos.X, 1024)), (int)-GameMain.GameScreen.Cam.WorldView.Y, width, (int)(GameMain.GameScreen.Cam.WorldView.Y + pos.Y) - 30),Color.Black, true);
spriteBatch.Draw(shaftTexture,
new Rectangle((int)(MathUtils.Round(pos.X, 1024)), (int)pos.Y-1000, width, 1024),
new Rectangle(0, 0, width, -1024),
level.BackgroundColor, 0.0f,
Vector2.Zero,
SpriteEffects.None, 0.0f);
}
public void RenderWalls(GraphicsDevice graphicsDevice, Camera cam)
{
if (wallVertices == null) return;
wallEdgeEffect.World = cam.ShaderTransform
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 100) * 0.5f;
wallCenterEffect.World = wallEdgeEffect.World;
//render the solid center of the wall cells
graphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
graphicsDevice.SetVertexBuffer(bodyVertices);
wallCenterEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(bodyVertices.VertexCount / 3.0f));
//render the edges of the wall cells
graphicsDevice.SetVertexBuffer(wallVertices);
wallEdgeEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)Math.Floor(wallVertices.VertexCount / 3.0f));
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (wallVertices!=null) wallVertices.Dispose();
if (bodyVertices != null) bodyVertices.Dispose();
}
}
}
@@ -0,0 +1,30 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Voronoi2;
namespace Barotrauma.RuinGeneration
{
partial class Ruin
{
public void Draw(SpriteBatch spriteBatch)
{
//foreach (BTRoom room in leaves)
//{
// GUI.DrawRectangle(spriteBatch, room.Rect, Color.White);
//}
//foreach (Corridor corr in corridors)
//{
// GUI.DrawRectangle(spriteBatch, corr.Rect, Color.Blue);
//}
foreach (Line line in walls)
{
GUI.DrawLine(spriteBatch, new Vector2(line.A.X, -line.A.Y), new Vector2(line.B.X, -line.B.Y), Color.Red, 0.0f, 10);
}
}
}
}
@@ -0,0 +1,124 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
namespace Barotrauma
{
class WaterRenderer : IDisposable
{
const int DefaultBufferSize = 1500;
private Vector2 wavePos;
public VertexPositionTexture[] vertices = new VertexPositionTexture[DefaultBufferSize];
private Effect waterEffect;
private BasicEffect basicEffect;
public int PositionInBuffer = 0;
private Texture2D waterTexture;
public Texture2D WaterTexture
{
get { return waterTexture; }
}
public WaterRenderer(GraphicsDevice graphicsDevice, ContentManager content)
{
#if WINDOWS
waterEffect = content.Load<Effect>("watershader");
#endif
#if LINUX
waterEffect = content.Load<Effect>("watershader_opengl");
#endif
waterTexture = TextureLoader.FromFile("Content/waterbump.png");
waterEffect.Parameters["xWaveWidth"].SetValue(0.05f);
waterEffect.Parameters["xWaveHeight"].SetValue(0.05f);
#if WINDOWS
//waterEffect.Parameters["xTexture"].SetValue(waterTexture);
#endif
#if LINUX
waterEffect.Parameters["xWaterBumpMap"].SetValue(waterTexture);
#endif
if (basicEffect == null)
{
basicEffect = new BasicEffect(GameMain.Instance.GraphicsDevice);
basicEffect.VertexColorEnabled = false;
basicEffect.TextureEnabled = true;
}
}
public void RenderBack(SpriteBatch spriteBatch, RenderTarget2D texture, float blurAmount = 0.0f)
{
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, SamplerState.LinearWrap, null, null, waterEffect);
waterEffect.CurrentTechnique = waterEffect.Techniques["WaterShader"];
waterEffect.Parameters["xWavePos"].SetValue(wavePos);
waterEffect.Parameters["xBlurDistance"].SetValue(blurAmount);
//waterEffect.CurrentTechnique.Passes[0].Apply();
#if WINDOWS
waterEffect.Parameters["xTexture"].SetValue(texture);
spriteBatch.Draw(waterTexture, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
#elif LINUX
spriteBatch.Draw(texture, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
#endif
spriteBatch.End();
}
public void ScrollWater(float deltaTime)
{
wavePos.X += 0.006f * deltaTime;
wavePos.Y += 0.006f * deltaTime;
}
public void Render(GraphicsDevice graphicsDevice, Camera cam, RenderTarget2D texture, Matrix transform)
{
if (vertices == null) return;
if (vertices.Length < 0) return;
basicEffect.Texture = texture;
basicEffect.View = Matrix.Identity;
basicEffect.World = transform
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
basicEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
graphicsDevice.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleList, vertices, 0, vertices.Length / 3);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (waterEffect != null)
{
waterEffect.Dispose();
waterEffect = null;
}
if (basicEffect != null)
{
basicEffect.Dispose();
basicEffect = null;
}
}
}
}
@@ -0,0 +1,39 @@
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Voronoi2;
namespace Barotrauma
{
partial class WrappingWall : IDisposable
{
private VertexBuffer wallVertices, bodyVertices;
public VertexBuffer WallVertices
{
get { return wallVertices; }
}
public VertexBuffer BodyVertices
{
get { return bodyVertices; }
}
public void SetWallVertices(VertexPositionTexture[] vertices)
{
wallVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionTexture.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
wallVertices.SetData(vertices);
}
public void SetBodyVertices(VertexPositionColor[] vertices)
{
bodyVertices = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColor.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
bodyVertices.SetData(vertices);
}
}
}
@@ -0,0 +1,677 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Barotrauma.Lights
{
/*class CachedShadow : IDisposable
{
public VertexBuffer ShadowBuffer;
public Vector2 LightPos;
public int ShadowVertexCount, PenumbraVertexCount;
public CachedShadow(VertexPositionColor[] shadowVertices, Vector2 lightPos, int shadowVertexCount, int penumbraVertexCount)
{
//var ShadowVertices = new VertexPositionColor [shadowVertices.Count()];
//shadowVertices.CopyTo(ShadowVertices, 0);
ShadowBuffer = new VertexBuffer(GameMain.CurrGraphicsDevice, VertexPositionColor.VertexDeclaration, 6*2, BufferUsage.None);
ShadowBuffer.SetData(shadowVertices, 0, shadowVertices.Length);
ShadowVertexCount = shadowVertexCount;
PenumbraVertexCount = penumbraVertexCount;
LightPos = lightPos;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
ShadowBuffer.Dispose();
}
}*/
class ConvexHullList
{
private List<ConvexHull> list;
public readonly Submarine Submarine;
public List<ConvexHull> List
{
get { return list; }
set
{
Debug.Assert(value != null);
Debug.Assert(!list.Contains(null));
list = value;
}
}
public ConvexHullList(Submarine submarine)
{
Submarine = submarine;
list = new List<ConvexHull>();
}
}
class Segment
{
public SegmentPoint Start;
public SegmentPoint End;
public bool IsHorizontal;
public Segment(SegmentPoint start, SegmentPoint end)
{
Start = start;
End = end;
start.Segment = this;
end.Segment = this;
IsHorizontal = Math.Abs(start.Pos.X - end.Pos.X) > Math.Abs(start.Pos.Y - end.Pos.Y);
}
}
struct SegmentPoint
{
public Vector2 Pos;
public Vector2 WorldPos;
public Segment Segment;
public SegmentPoint(Vector2 pos)
{
Pos = pos;
WorldPos = pos;
Segment = null;
}
public override string ToString()
{
return Pos.ToString();
}
}
class ConvexHull
{
public static List<ConvexHullList> HullLists = new List<ConvexHullList>();
static BasicEffect shadowEffect;
static BasicEffect penumbraEffect;
//private Dictionary<LightSource, CachedShadow> cachedShadows;
public VertexBuffer ShadowBuffer;
Segment[] segments = new Segment[4];
SegmentPoint[] vertices = new SegmentPoint[4];
SegmentPoint[] losVertices = new SegmentPoint[4];
//private Vector2[] vertices;
//private Vector2[] losVertices;
private bool[] backFacing;
private bool[] ignoreEdge;
private VertexPositionColor[] shadowVertices;
private VertexPositionTexture[] penumbraVertices;
int shadowVertexCount;
private Entity parentEntity;
private Rectangle boundingBox;
public Entity ParentEntity
{
get { return parentEntity; }
}
private bool enabled;
public bool Enabled
{
get
{
return enabled;
}
set
{
if (enabled == value) return;
enabled = value;
LastVertexChangeTime = (float)Timing.TotalTime;
}
}
/// <summary>
/// The elapsed gametime when the vertices of this hull last changed
/// </summary>
public float LastVertexChangeTime
{
get;
private set;
}
public Rectangle BoundingBox
{
get { return boundingBox; }
}
public ConvexHull(Vector2[] points, Color color, Entity parent)
{
if (shadowEffect == null)
{
shadowEffect = new BasicEffect(GameMain.Instance.GraphicsDevice);
shadowEffect.VertexColorEnabled = true;
}
if (penumbraEffect == null)
{
penumbraEffect = new BasicEffect(GameMain.Instance.GraphicsDevice);
penumbraEffect.TextureEnabled = true;
//shadowEffect.VertexColorEnabled = true;
penumbraEffect.LightingEnabled = false;
penumbraEffect.Texture = TextureLoader.FromFile("Content/Lights/penumbra.png");
}
parentEntity = parent;
//cachedShadows = new Dictionary<LightSource, CachedShadow>();
shadowVertices = new VertexPositionColor[6 * 2];
penumbraVertices = new VertexPositionTexture[6];
//vertices = points;
SetVertices(points);
//CalculateDimensions();
backFacing = new bool[4];
ignoreEdge = new bool[4];
Enabled = true;
var chList = HullLists.Find(x => x.Submarine == parent.Submarine);
if (chList == null)
{
chList = new ConvexHullList(parent.Submarine);
HullLists.Add(chList);
}
foreach (ConvexHull ch in chList.List)
{
UpdateIgnoredEdges(ch);
ch.UpdateIgnoredEdges(this);
}
chList.List.Add(this);
}
private void UpdateIgnoredEdges(ConvexHull ch)
{
if (ch == this) return;
//ignore edges that are inside some other convex hull
for (int i = 0; i < vertices.Length; i++)
{
if (vertices[i].Pos.X >= ch.boundingBox.X && vertices[i].Pos.X <= ch.boundingBox.Right &&
vertices[i].Pos.Y >= ch.boundingBox.Y && vertices[i].Pos.Y <= ch.boundingBox.Bottom)
{
Vector2 p = vertices[(i + 1) % vertices.Length].Pos;
if (p.X >= ch.boundingBox.X && p.X <= ch.boundingBox.Right &&
p.Y >= ch.boundingBox.Y && p.Y <= ch.boundingBox.Bottom)
{
ignoreEdge[i] = true;
}
}
}
}
private void CalculateDimensions()
{
float minX = vertices[0].Pos.X, minY = vertices[0].Pos.Y, maxX = vertices[0].Pos.X, maxY = vertices[0].Pos.Y;
for (int i = 1; i < vertices.Length; i++)
{
if (vertices[i].Pos.X < minX) minX = vertices[i].Pos.X;
if (vertices[i].Pos.Y < minY) minY = vertices[i].Pos.Y;
if (vertices[i].Pos.X > maxX) maxX = vertices[i].Pos.X;
if (vertices[i].Pos.Y > minY) maxY = vertices[i].Pos.Y;
}
boundingBox = new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
}
public void Move(Vector2 amount)
{
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].Pos += amount;
losVertices[i].Pos += amount;
segments[i].Start.Pos += amount;
segments[i].End.Pos += amount;
}
LastVertexChangeTime = (float)Timing.TotalTime;
CalculateDimensions();
}
public void SetVertices(Vector2[] points)
{
Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported");
LastVertexChangeTime = (float)Timing.TotalTime;
for (int i = 0; i < 4; i++)
{
vertices[i] = new SegmentPoint(points[i]);
losVertices[i] = new SegmentPoint(points[i]);
}
for (int i = 0; i < 4; i++)
{
segments[i] = new Segment(vertices[i], vertices[(i + 1) % 4]);
}
int margin = 0;
if (Math.Abs(points[0].X - points[2].X) < Math.Abs(points[0].Y - points[1].Y))
{
losVertices[0].Pos = new Vector2(points[0].X + margin, points[0].Y);
losVertices[1].Pos = new Vector2(points[1].X + margin, points[1].Y);
losVertices[2].Pos = new Vector2(points[2].X - margin, points[2].Y);
losVertices[3].Pos = new Vector2(points[3].X - margin, points[3].Y);
}
else
{
losVertices[0].Pos = new Vector2(points[0].X, points[0].Y + margin);
losVertices[1].Pos = new Vector2(points[1].X, points[1].Y - margin);
losVertices[2].Pos = new Vector2(points[2].X, points[2].Y - margin);
losVertices[3].Pos = new Vector2(points[3].X, points[3].Y + margin);
}
CalculateDimensions();
if (parentEntity == null || ignoreEdge == null) return;
for (int i = 0; i<4; i++)
{
ignoreEdge[i] = false;
}
var chList = HullLists.Find(x => x.Submarine == parentEntity.Submarine);
if (chList != null)
{
foreach (ConvexHull ch in chList.List)
{
UpdateIgnoredEdges(ch);
}
}
}
/*private void RemoveCachedShadow(Lights.LightSource light)
{
CachedShadow shadow = null;
cachedShadows.TryGetValue(light, out shadow);
if (shadow != null)
{
shadow.Dispose();
cachedShadows.Remove(light);
}
}
private void ClearCachedShadows()
{
foreach (KeyValuePair<LightSource, CachedShadow> cachedShadow in cachedShadows)
{
cachedShadow.Key.NeedsHullUpdate = true;
cachedShadow.Value.Dispose();
}
cachedShadows.Clear();
}*/
public bool Intersects(Rectangle rect)
{
if (!Enabled) return false;
Rectangle transformedBounds = boundingBox;
if (parentEntity != null && parentEntity.Submarine != null)
{
transformedBounds.X += (int)parentEntity.Submarine.Position.X;
transformedBounds.Y += (int)parentEntity.Submarine.Position.Y;
}
return transformedBounds.Intersects(rect);
}
/// <summary>
/// Returns the segments that are facing towards viewPosition
/// </summary>
public List<Segment> GetVisibleSegments(Vector2 viewPosition)
{
List<Segment> visibleFaces = new List<Segment>();
for (int i = 0; i < 4; i++)
{
if (ignoreEdge[i]) continue;
Vector2 pos1 = vertices[i].WorldPos;
Vector2 pos2 = vertices[(i + 1) % 4].WorldPos;
Vector2 middle = (pos1 + pos2) / 2;
Vector2 L = viewPosition - middle;
Vector2 N = new Vector2(
-(pos2.Y - pos1.Y),
pos2.X - pos1.X);
if (Vector2.Dot(N, L) > 0)
{
visibleFaces.Add(segments[i]);
}
}
return visibleFaces;
}
public void RefreshWorldPositions()
{
if (parentEntity == null || parentEntity.Submarine == null) return;
for (int i = 0; i < 4; i++)
{
vertices[i].WorldPos = vertices[i].Pos + parentEntity.Submarine.DrawPosition;
segments[i].Start.WorldPos = segments[i].Start.Pos + parentEntity.Submarine.DrawPosition;
segments[i].End.WorldPos = segments[i].End.Pos + parentEntity.Submarine.DrawPosition;
}
}
private void CalculateShadowVertices(Vector2 lightSourcePos, bool los = true)
{
shadowVertexCount = 0;
var vertices = los ? losVertices : this.vertices;
//compute facing of each edge, using N*L
for (int i = 0; i < 4; i++)
{
if (ignoreEdge[i])
{
backFacing[i] = false;
continue;
}
Vector2 firstVertex = vertices[i].Pos;
Vector2 secondVertex = vertices[(i+1) % 4].Pos;
Vector2 L = lightSourcePos - ((firstVertex + secondVertex) / 2.0f);
Vector2 N = new Vector2(
-(secondVertex.Y - firstVertex.Y),
secondVertex.X - firstVertex.X);
backFacing[i] = (Vector2.Dot(N, L) < 0) == los;
}
//find beginning and ending vertices which
//belong to the shadow
int startingIndex = 0;
int endingIndex = 0;
for (int i = 0; i < 4; i++)
{
int currentEdge = i;
int nextEdge = (i + 1) % 4;
if (backFacing[currentEdge] && !backFacing[nextEdge])
endingIndex = nextEdge;
if (!backFacing[currentEdge] && backFacing[nextEdge])
startingIndex = nextEdge;
}
//nr of vertices that are in the shadow
if (endingIndex > startingIndex)
shadowVertexCount = endingIndex - startingIndex + 1;
else
shadowVertexCount = 4 + 1 - startingIndex + endingIndex;
//shadowVertices = new VertexPositionColor[shadowVertexCount * 2];
//create a triangle strip that has the shape of the shadow
int currentIndex = startingIndex;
int svCount = 0;
while (svCount != shadowVertexCount * 2)
{
Vector3 vertexPos = new Vector3(vertices[currentIndex].Pos, 0.0f);
int i = los ? svCount : svCount + 1;
int j = los ? svCount + 1 : svCount;
//one vertex on the hull
shadowVertices[i] = new VertexPositionColor();
shadowVertices[i].Color = los ? Color.Black : Color.Transparent;
shadowVertices[i].Position = vertexPos;
//one extruded by the light direction
shadowVertices[j] = new VertexPositionColor();
shadowVertices[j].Color = shadowVertices[i].Color;
Vector3 L2P = vertexPos - new Vector3(lightSourcePos, 0);
L2P.Normalize();
shadowVertices[j].Position = new Vector3(lightSourcePos, 0) + L2P * 9000;
svCount += 2;
currentIndex = (currentIndex + 1) % 4;
}
if (los)
{
CalculatePenumbraVertices(startingIndex, endingIndex, lightSourcePos, los);
}
}
private void CalculatePenumbraVertices(int startingIndex, int endingIndex, Vector2 lightSourcePos, bool los)
{
for (int n = 0; n < 4; n += 3)
{
Vector3 penumbraStart = new Vector3((n == 0) ? vertices[startingIndex].Pos : vertices[endingIndex].Pos, 0.0f);
penumbraVertices[n] = new VertexPositionTexture();
penumbraVertices[n].Position = penumbraStart;
penumbraVertices[n].TextureCoordinate = new Vector2(0.0f, 1.0f);
//penumbraVertices[0].te = fow ? Color.Black : Color.Transparent;
for (int i = 0; i < 2; i++)
{
penumbraVertices[n + i + 1] = new VertexPositionTexture();
Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
vertexDir.Normalize();
Vector3 normal = (i == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
if (n > 0) normal = -normal;
vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) - normal * 20.0f);
vertexDir.Normalize();
penumbraVertices[n + i + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000;
if (los)
{
penumbraVertices[n + i + 1].TextureCoordinate = (i == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
}
else
{
penumbraVertices[n + i + 1].TextureCoordinate = (i == 0) ? new Vector2(1.0f, 0.0f) : Vector2.Zero;
}
}
if (n > 0)
{
var temp = penumbraVertices[4];
penumbraVertices[4] = penumbraVertices[5];
penumbraVertices[5] = temp;
}
}
}
public static List<ConvexHull> GetHullsInRange(Vector2 position, float range, Submarine ParentSub)
{
List<ConvexHull> list = new List<ConvexHull>();
foreach (ConvexHullList chList in ConvexHull.HullLists)
{
Vector2 lightPos = position;
if (ParentSub == null)
{
//light and the convexhull are both outside
if (chList.Submarine == null)
{
list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
}
//light is outside, convexhull inside a sub
else
{
if (!MathUtils.CircleIntersectsRectangle(lightPos - chList.Submarine.WorldPosition, range, chList.Submarine.Borders)) continue;
lightPos -= (chList.Submarine.WorldPosition - chList.Submarine.HiddenSubPosition);
list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
}
}
else
{
//light is inside, convexhull outside
if (chList.Submarine == null) continue;
//light and convexhull are both inside the same sub
if (chList.Submarine == ParentSub)
{
list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
}
//light and convexhull are inside different subs
else
{
lightPos -= (chList.Submarine.Position - ParentSub.Position);
Rectangle subBorders = chList.Submarine.Borders;
subBorders.Location += chList.Submarine.HiddenSubPosition.ToPoint() - new Point(0, chList.Submarine.Borders.Height);
if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders)) continue;
list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)));
}
}
}
return list;
}
public void DrawShadows(GraphicsDevice graphicsDevice, Camera cam, LightSource light, Matrix transform, bool los = true)
{
if (!Enabled) return;
Vector2 lightSourcePos = light.Position;
if (parentEntity != null && parentEntity.Submarine != null)
{
if (light.ParentSub == null)
{
lightSourcePos -= parentEntity.Submarine.Position;
}
else if (light.ParentSub != parentEntity.Submarine)
{
lightSourcePos += (light.ParentSub.Position-parentEntity.Submarine.Position);
}
}
CalculateShadowVertices(lightSourcePos, los);
ShadowBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColor.VertexDeclaration, 6 * 2, BufferUsage.None);
ShadowBuffer.SetData(shadowVertices, 0, shadowVertices.Length);
graphicsDevice.SetVertexBuffer(ShadowBuffer);
shadowVertexCount = shadowVertices.Length;
DrawShadows(graphicsDevice, cam, transform, los);
}
public void DrawShadows(GraphicsDevice graphicsDevice, Camera cam, Vector2 lightSourcePos, Matrix transform, bool los = true)
{
if (!Enabled) return;
if (parentEntity != null && parentEntity.Submarine != null) lightSourcePos -= parentEntity.Submarine.Position;
CalculateShadowVertices(lightSourcePos, los);
DrawShadows(graphicsDevice, cam, transform, los);
}
private void DrawShadows(GraphicsDevice graphicsDevice, Camera cam, Matrix transform, bool los = true)
{
Vector3 offset = Vector3.Zero;
if (parentEntity != null && parentEntity.Submarine != null)
{
offset = new Vector3(parentEntity.Submarine.DrawPosition.X, parentEntity.Submarine.DrawPosition.Y, 0.0f);
}
if (shadowVertexCount>0)
{
shadowEffect.World = Matrix.CreateTranslation(offset) * transform;
if (los)
{
shadowEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, shadowVertices, 0, shadowVertexCount * 2 - 2, VertexPositionColor.VertexDeclaration);
}
else
{
shadowEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, shadowVertexCount * 2 - 2);
}
}
if (los)
{
penumbraEffect.World = shadowEffect.World;
penumbraEffect.CurrentTechnique.Passes[0].Apply();
#if WINDOWS
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, penumbraVertices, 0, 2, VertexPositionTexture.VertexDeclaration);
#endif
}
}
public void Remove()
{
var chList = HullLists.Find(x => x.Submarine == parentEntity.Submarine);
if (chList != null)
{
chList.List.Remove(this);
if (chList.List.Count == 0)
{
HullLists.Remove(chList);
}
}
}
}
}
@@ -0,0 +1,392 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Barotrauma.Lights
{
class LightManager
{
private const float AmbientLightUpdateInterval = 0.2f;
private const float AmbientLightFalloff = 0.8f;
private static Entity viewTarget;
public static Entity ViewTarget
{
get { return viewTarget; }
set {
if (viewTarget == value) return;
viewTarget = value;
}
}
public Color AmbientLight;
RenderTarget2D lightMap, losTexture;
BasicEffect lightEffect;
private static Texture2D alphaClearTexture;
private List<LightSource> lights;
public bool LosEnabled = true;
public bool LightingEnabled = true;
public bool ObstructVision;
private Texture2D visionCircle;
private Dictionary<Hull, Color> hullAmbientLights;
private Dictionary<Hull, Color> smoothedHullAmbientLights;
private float ambientLightUpdateTimer;
public LightManager(GraphicsDevice graphics)
{
lights = new List<LightSource>();
AmbientLight = new Color(20, 20, 20, 255);
visionCircle = Sprite.LoadTexture("Content/Lights/visioncircle.png");
var pp = graphics.PresentationParameters;
lightMap = new RenderTarget2D(graphics,
GameMain.GraphicsWidth, GameMain.GraphicsHeight, false,
pp.BackBufferFormat, pp.DepthStencilFormat, pp.MultiSampleCount,
RenderTargetUsage.DiscardContents);
losTexture = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
if (lightEffect == null)
{
lightEffect = new BasicEffect(GameMain.Instance.GraphicsDevice);
lightEffect.VertexColorEnabled = false;
lightEffect.TextureEnabled = true;
lightEffect.Texture = LightSource.LightTexture;
}
hullAmbientLights = new Dictionary<Hull, Color>();
smoothedHullAmbientLights = new Dictionary<Hull, Color>();
if (alphaClearTexture == null)
{
alphaClearTexture = TextureLoader.FromFile("Content/Lights/alphaOne.png");
}
}
public void AddLight(LightSource light)
{
lights.Add(light);
}
public void RemoveLight(LightSource light)
{
lights.Remove(light);
}
public void OnMapLoaded()
{
foreach (LightSource light in lights)
{
light.NeedsHullCheck = true;
light.NeedsRecalculation = true;
}
}
public void Update(float deltaTime)
{
if (ambientLightUpdateTimer > 0.0f)
{
ambientLightUpdateTimer -= deltaTime;
}
else
{
CalculateAmbientLights();
ambientLightUpdateTimer = AmbientLightUpdateInterval;
}
foreach (Hull hull in hullAmbientLights.Keys)
{
if (!smoothedHullAmbientLights.ContainsKey(hull))
{
smoothedHullAmbientLights.Add(hull, Color.TransparentBlack);
}
}
foreach (Hull hull in smoothedHullAmbientLights.Keys.ToList())
{
Color targetColor = Color.TransparentBlack;
hullAmbientLights.TryGetValue(hull, out targetColor);
smoothedHullAmbientLights[hull] = Color.Lerp(smoothedHullAmbientLights[hull], targetColor, deltaTime);
}
}
public void UpdateLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Effect blur)
{
if (!LightingEnabled) return;
graphics.SetRenderTarget(lightMap);
Rectangle viewRect = cam.WorldView;
viewRect.Y -= cam.WorldView.Height;
//clear to some small ambient light
graphics.Clear(AmbientLight);
graphics.BlendState = BlendState.Additive;
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, null, null, null, cam.Transform);
Matrix transform = cam.ShaderTransform
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
Vector3 offset = Vector3.Zero;// new Vector3(Submarine.MainSub.DrawPosition.X, Submarine.MainSub.DrawPosition.Y, 0.0f);
foreach (LightSource light in lights)
{
if (light.Color.A < 1 || light.Range < 1.0f || !light.CastShadows) continue;
if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.Range, viewRect)) continue;
light.Draw(spriteBatch, lightEffect, transform);
}
lightEffect.World = Matrix.CreateTranslation(offset) * transform;
GameMain.ParticleManager.Draw(spriteBatch, false, Particles.ParticleBlendState.Additive);
if (Character.Controlled != null)
{
if (Character.Controlled.ClosestItem != null)
{
Character.Controlled.ClosestItem.IsHighlighted = true;
Character.Controlled.ClosestItem.Draw(spriteBatch, false, true);
Character.Controlled.ClosestItem.IsHighlighted = true;
}
else if (Character.Controlled.ClosestCharacter != null)
{
Character.Controlled.ClosestCharacter.Draw(spriteBatch);
}
}
foreach (Hull hull in smoothedHullAmbientLights.Keys)
{
if (smoothedHullAmbientLights[hull].A < 0.01f) continue;
var drawRect =
hull.Submarine == null ?
hull.Rect :
new Rectangle((int)(hull.Submarine.DrawPosition.X + hull.Rect.X), (int)(hull.Submarine.DrawPosition.Y + hull.Rect.Y), hull.Rect.Width, hull.Rect.Height);
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y),
new Vector2(hull.Rect.Width, hull.Rect.Height),
smoothedHullAmbientLights[hull] * 0.5f, true);
}
spriteBatch.End();
//clear alpha, to avoid messing stuff up later
//ClearAlphaToOne(graphics, spriteBatch);
graphics.SetRenderTarget(null);
graphics.BlendState = BlendState.AlphaBlend;
}
public void UpdateObstructVision(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Vector2 lookAtPosition)
{
if (!LosEnabled && !ObstructVision) return;
graphics.SetRenderTarget(losTexture);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, cam.Transform);
if (ObstructVision)
{
//graphics.Clear(Color.Black);
Vector2 diff = lookAtPosition - ViewTarget.WorldPosition;
diff.Y = -diff.Y;
float rotation = MathUtils.VectorToAngle(diff);
Vector2 scale = new Vector2(MathHelper.Clamp(diff.Length()/256.0f, 2.0f, 5.0f), 2.0f);
spriteBatch.Draw(visionCircle, new Vector2(ViewTarget.WorldPosition.X, -ViewTarget.WorldPosition.Y), null, Color.White, rotation,
new Vector2(LightSource.LightTexture.Width*0.2f, LightSource.LightTexture.Height/2), scale, SpriteEffects.None, 0.0f);
}
else
{
graphics.Clear(Color.White);
}
spriteBatch.End();
//--------------------------------------
if (LosEnabled && ViewTarget != null)
{
Vector2 pos = ViewTarget.WorldPosition;
Rectangle camView = new Rectangle(cam.WorldView.X, cam.WorldView.Y - cam.WorldView.Height, cam.WorldView.Width, cam.WorldView.Height);
Matrix shadowTransform = cam.ShaderTransform
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
var convexHulls = ConvexHull.GetHullsInRange(viewTarget.Position, cam.WorldView.Width*0.75f, viewTarget.Submarine);
if (convexHulls != null)
{
foreach (ConvexHull convexHull in convexHulls)
{
if (!convexHull.Intersects(camView)) continue;
//if (!camView.Intersects(convexHull.BoundingBox)) continue;
convexHull.DrawShadows(graphics, cam, pos, shadowTransform);
}
}
}
graphics.SetRenderTarget(null);
}
private void CalculateAmbientLights()
{
hullAmbientLights.Clear();
foreach (LightSource light in lights)
{
if (light.Color.A < 1f || light.Range < 1.0f) continue;
var newAmbientLights = AmbientLightHulls(light);
foreach (Hull hull in newAmbientLights.Keys)
{
if (hullAmbientLights.ContainsKey(hull))
{
//hull already lit by some other light source -> add the ambient lights up
hullAmbientLights[hull] = new Color(
hullAmbientLights[hull].R + newAmbientLights[hull].R,
hullAmbientLights[hull].G + newAmbientLights[hull].G,
hullAmbientLights[hull].B + newAmbientLights[hull].B,
hullAmbientLights[hull].A + newAmbientLights[hull].A);
}
else
{
hullAmbientLights.Add(hull, newAmbientLights[hull]);
}
}
}
}
/// <summary>
/// Add ambient light to the hull the lightsource is inside + all adjacent hulls connected by a gap
/// </summary>
private Dictionary<Hull, Color> AmbientLightHulls(LightSource light)
{
Dictionary<Hull, Color> hullAmbientLight = new Dictionary<Hull, Color>();
var hull = Hull.FindHull(light.WorldPosition);
if (hull == null) return hullAmbientLight;
return AmbientLightHulls(hull, hullAmbientLight, light.Color * (light.Range/2000.0f));
}
/// <summary>
/// A flood fill algorithm that adds ambient light to all hulls the starting hull is connected to
/// </summary>
private Dictionary<Hull, Color> AmbientLightHulls(Hull hull, Dictionary<Hull, Color> hullAmbientLight, Color currColor)
{
if (hullAmbientLight.ContainsKey(hull))
{
if (hullAmbientLight[hull].A > currColor.A)
return hullAmbientLight;
else
hullAmbientLight[hull] = currColor;
}
else
{
hullAmbientLight.Add(hull, currColor);
}
Color nextHullLight = currColor * AmbientLightFalloff;
//light getting too dark to notice -> no need to spread further
if (nextHullLight.A < 20) return hullAmbientLight;
//use hashset to make sure that each hull is only included once
HashSet<Hull> hulls = new HashSet<Hull>();
foreach (Gap g in hull.ConnectedGaps)
{
if (!g.IsRoomToRoom || !g.PassAmbientLight || g.Open < 0.5f) continue;
hulls.Add((g.linkedTo[0] == hull ? g.linkedTo[1] : g.linkedTo[0]) as Hull);
}
foreach (Hull h in hulls)
{
hullAmbientLight = AmbientLightHulls(h, hullAmbientLight, nextHullLight);
}
return hullAmbientLight;
}
public void DrawLightMap(SpriteBatch spriteBatch, Effect effect)
{
if (!LightingEnabled) return;
spriteBatch.Begin(SpriteSortMode.Deferred, CustomBlendStates.Multiplicative, null, null, null, effect);
spriteBatch.Draw(lightMap, Vector2.Zero, Color.White);
spriteBatch.End();
}
public void DrawLOS(SpriteBatch spriteBatch, Effect effect,bool renderingBackground)
{
if (!LosEnabled || ViewTarget == null) return;
spriteBatch.Begin(SpriteSortMode.Deferred, renderingBackground ? CustomBlendStates.LOS : CustomBlendStates.Multiplicative, null, null, null, effect);
spriteBatch.Draw(losTexture, Vector2.Zero, Color.White);
spriteBatch.End();
if (!renderingBackground) ObstructVision = false;
}
public void ClearLights()
{
lights.Clear();
}
}
class CustomBlendStates
{
static CustomBlendStates()
{
Multiplicative = new BlendState();
Multiplicative.ColorSourceBlend = Multiplicative.AlphaSourceBlend = Blend.Zero;
Multiplicative.ColorDestinationBlend = Multiplicative.AlphaDestinationBlend = Blend.SourceColor;
Multiplicative.ColorBlendFunction = Multiplicative.AlphaBlendFunction = BlendFunction.Add;
WriteToAlpha = new BlendState();
WriteToAlpha.ColorWriteChannels = ColorWriteChannels.Alpha;
MultiplyWithAlpha = new BlendState();
MultiplyWithAlpha.ColorDestinationBlend = MultiplyWithAlpha.AlphaDestinationBlend = Blend.One;
MultiplyWithAlpha.ColorSourceBlend = MultiplyWithAlpha.AlphaSourceBlend = Blend.DestinationAlpha;
LOS = new BlendState();
LOS.ColorSourceBlend = LOS.AlphaSourceBlend = Blend.Zero;
LOS.ColorDestinationBlend = LOS.AlphaDestinationBlend = Blend.InverseSourceColor;
LOS.ColorBlendFunction = LOS.AlphaBlendFunction = BlendFunction.Add;
}
public static BlendState Multiplicative { get; private set; }
public static BlendState WriteToAlpha { get; private set; }
public static BlendState MultiplyWithAlpha { get; private set; }
public static BlendState LOS { get; private set; }
}
}
@@ -0,0 +1,612 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Barotrauma.Lights
{
class LightSource
{
private static Texture2D lightTexture;
private List<ConvexHullList> hullsInRange;
private Color color;
private float range;
private Sprite overrideLightTexture;
private Texture2D texture;
public Sprite LightSprite;
public Submarine ParentSub;
public bool CastShadows;
//what was the range of the light when lightvolumes were last calculated
private float prevCalculatedRange;
private Vector2 prevCalculatedPosition;
//do we need to recheck which convex hulls are within range
//(e.g. position or range of the lightsource has changed)
public bool NeedsHullCheck = true;
//do we need to recalculate the vertices of the light volume
public bool NeedsRecalculation = true;
//when were the vertices of the light volume last calculated
private float lastRecalculationTime;
private Dictionary<Submarine, Vector2> diffToSub;
private DynamicVertexBuffer lightVolumeBuffer;
private DynamicIndexBuffer lightVolumeIndexBuffer;
private int vertexCount;
private int indexCount;
private Vector2 position;
public Vector2 Position
{
get { return position; }
set
{
if (position == value) return;
position = value;
if (Vector2.Distance(prevCalculatedPosition, position) < 5.0f) return;
NeedsHullCheck = true;
NeedsRecalculation = true;
prevCalculatedPosition = position;
}
}
private float rotation;
public float Rotation
{
get { return rotation; }
set
{
if (rotation == value) return;
rotation = value;
NeedsHullCheck = true;
NeedsRecalculation = true;
}
}
public Vector2 WorldPosition
{
get { return (ParentSub == null) ? position : position + ParentSub.Position; }
}
public static Texture2D LightTexture
{
get
{
if (lightTexture == null)
{
lightTexture = TextureLoader.FromFile("Content/Lights/light.png");
}
return lightTexture;
}
}
public Color Color
{
get { return color; }
set { color = value; }
}
public float Range
{
get { return range; }
set
{
range = MathHelper.Clamp(value, 0.0f, 2048.0f);
if (Math.Abs(prevCalculatedRange - range) < 10.0f) return;
NeedsHullCheck = true;
NeedsRecalculation = true;
prevCalculatedRange = range;
}
}
public LightSource (XElement element)
: this(Vector2.Zero, 100.0f, Color.White, null)
{
range = ToolBox.GetAttributeFloat(element, "range", 100.0f);
color = new Color(ToolBox.GetAttributeVector4(element, "color", Vector4.One));
CastShadows = ToolBox.GetAttributeBool(element, "castshadows", true);
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
LightSprite = new Sprite(subElement);
break;
case "lighttexture":
overrideLightTexture = new Sprite(subElement);
break;
}
}
}
public LightSource(Vector2 position, float range, Color color, Submarine submarine)
{
hullsInRange = new List<ConvexHullList>();
this.ParentSub = submarine;
this.position = position;
this.range = range;
this.color = color;
CastShadows = true;
texture = LightTexture;
diffToSub = new Dictionary<Submarine, Vector2>();
GameMain.LightManager.AddLight(this);
}
/*public void DrawShadows(GraphicsDevice graphics, Camera cam, Matrix shadowTransform)
{
if (!CastShadows) return;
if (range < 1.0f || color.A < 0.01f) return;
foreach (Submarine sub in Submarine.Loaded)
{
var hulls = GetHullsInRange(sub);
if (hulls == null) continue;
foreach ( ConvexHull ch in hulls)
{
ch.DrawShadows(graphics, cam, this, shadowTransform, false);
}
}
var outsideHulls = GetHullsInRange(null);
NeedsHullUpdate = false;
if (outsideHulls == null) return;
foreach (ConvexHull ch in outsideHulls)
{
ch.DrawShadows(graphics, cam, this, shadowTransform, false);
}
}*/
/// <summary>
/// Update the contents of ConvexHullList and check if we need to recalculate vertices
/// </summary>
private void RefreshConvexHullList(ConvexHullList chList, Vector2 lightPos, Submarine sub)
{
var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub);
if (fullChList == null) return;
chList.List = fullChList.List.FindAll(ch => ch.Enabled && MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox));
NeedsHullCheck = true;
}
/// <summary>
/// Recheck which convex hulls are in range (if needed),
/// and check if we need to recalculate vertices due to changes in the convex hulls
/// </summary>
private void CheckHullsInRange()
{
List<Submarine> subs = new List<Submarine>(Submarine.Loaded);
subs.Add(null);
foreach (Submarine sub in subs)
{
//find the list of convexhulls that belong to the sub
var chList = hullsInRange.Find(x => x.Submarine == sub);
//not found -> create one
if (chList == null)
{
chList = new ConvexHullList(sub);
hullsInRange.Add(chList);
NeedsRecalculation = true;
}
if (chList.List.Any(ch => ch.LastVertexChangeTime > lastRecalculationTime))
{
NeedsRecalculation = true;
}
Vector2 lightPos = position;
if (ParentSub == null)
{
//light and the convexhulls are both outside
if (sub == null)
{
if (NeedsHullCheck)
{
RefreshConvexHullList(chList, lightPos, null);
}
}
//light is outside, convexhulls inside a sub
else
{
lightPos -= sub.Position;
Rectangle subBorders = sub.Borders;
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//only draw if the light overlaps with the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders))
{
if (chList.List.Count > 0) NeedsRecalculation = true;
chList.List.Clear();
continue;
}
RefreshConvexHullList(chList, lightPos, sub);
}
}
else
{
//light is inside, convexhull outside
if (sub == null) continue;
//light and convexhull are both inside the same sub
if (sub == ParentSub)
{
if (NeedsHullCheck)
{
RefreshConvexHullList(chList, lightPos, sub);
}
}
//light and convexhull are inside different subs
else
{
if (sub.DockedTo.Contains(ParentSub) && !NeedsHullCheck) continue;
lightPos -= (sub.Position - ParentSub.Position);
Rectangle subBorders = sub.Borders;
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
//don't draw any shadows if the light doesn't overlap with the borders of the sub
if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders))
{
if (chList.List.Count > 0) NeedsRecalculation = true;
chList.List.Clear();
continue;
}
//recalculate vertices if the subs have moved > 5 px relative to each other
Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition;
Vector2 prevDiff;
if (!diffToSub.TryGetValue(sub, out prevDiff))
{
diffToSub.Add(sub, diff);
NeedsRecalculation = true;
}
else if (Vector2.DistanceSquared(diff, prevDiff) > 5.0f*5.0f)
{
diffToSub[sub] = diff;
NeedsRecalculation = true;
}
RefreshConvexHullList(chList, lightPos, sub);
}
}
}
}
private List<Vector2> FindRaycastHits()
{
if (!CastShadows) return null;
if (range < 1.0f || color.A < 0.01f) return null;
Vector2 drawPos = position;
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
var hulls = new List<ConvexHull>();// ConvexHull.GetHullsInRange(position, range, ParentSub);
foreach (ConvexHullList chList in hullsInRange)
{
hulls.AddRange(chList.List);
}
//find convexhull segments that are close enough and facing towards the light source
List<Segment> visibleSegments = new List<Segment>();
List<SegmentPoint> points = new List<SegmentPoint>();
foreach (ConvexHull hull in hulls)
{
hull.RefreshWorldPositions();
var visibleHullSegments = hull.GetVisibleSegments(drawPos);
visibleSegments.AddRange(visibleHullSegments);
foreach (Segment s in visibleHullSegments)
{
points.Add(s.Start);
points.Add(s.End);
}
}
//add a square-shaped boundary to make sure we've got something to construct the triangles from
//even if there aren't enough hull segments around the light source
//(might be more effective to calculate if we actually need these extra points)
var boundaryCorners = new List<SegmentPoint> {
new SegmentPoint(new Vector2(drawPos.X + range*2, drawPos.Y + range*2)),
new SegmentPoint(new Vector2(drawPos.X + range*2, drawPos.Y - range*2)),
new SegmentPoint(new Vector2(drawPos.X - range*2, drawPos.Y - range*2)),
new SegmentPoint(new Vector2(drawPos.X - range*2, drawPos.Y + range*2))
};
points.AddRange(boundaryCorners);
var compareCCW = new CompareSegmentPointCW(drawPos);
try
{
points.Sort(compareCCW);
}
catch (Exception e)
{
StringBuilder sb = new StringBuilder("Constructing light volumes failed! Light pos: "+drawPos+", Hull verts:\n");
foreach (SegmentPoint sp in points)
{
sb.AppendLine(sp.Pos.ToString());
}
DebugConsole.ThrowError(sb.ToString(), e);
}
List<Vector2> output = new List<Vector2>();
//remove points that are very close to each other
for (int i = 0; i < points.Count - 1; i++)
{
if (Math.Abs(points[i].WorldPos.X - points[i + 1].WorldPos.X) < 3 &&
Math.Abs(points[i].WorldPos.Y - points[i + 1].WorldPos.Y) < 3)
{
points.RemoveAt(i + 1);
}
}
foreach (SegmentPoint p in points)
{
Vector2 dir = Vector2.Normalize(p.WorldPos - drawPos);
Vector2 dirNormal = new Vector2(-dir.Y, dir.X)*3;
//do two slightly offset raycasts to hit the segment itself and whatever's behind it
Vector2 intersection1 = RayCast(drawPos, drawPos + dir * range * 2 - dirNormal, visibleSegments);
Vector2 intersection2 = RayCast(drawPos, drawPos + dir * range * 2 + dirNormal, visibleSegments);
//hit almost the same position -> only add one vertex to output
if ((Math.Abs(intersection1.X - intersection2.X) < 5 &&
Math.Abs(intersection1.Y - intersection2.Y) < 5))
{
output.Add(intersection1);
}
else
{
output.Add(intersection1);
output.Add(intersection2);
}
}
return output;
}
private Vector2 RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments)
{
float closestDist = 0.0f;
Vector2? closestIntersection = null;
foreach (Segment s in segments)
{
Vector2? intersection = MathUtils.GetAxisAlignedLineIntersection(rayStart, rayEnd, s.Start.WorldPos, s.End.WorldPos, s.IsHorizontal);
if (intersection != null)
{
float dist = Vector2.Distance((Vector2)intersection, rayStart);
if (closestIntersection == null || dist < closestDist)
{
closestDist = dist;
closestIntersection = intersection;
}
}
}
return closestIntersection == null ? rayEnd : (Vector2)closestIntersection;
}
private void CalculateLightVertices(List<Vector2> rayCastHits)
{
List<VertexPositionTexture> vertices = new List<VertexPositionTexture>();
Vector2 drawPos = position;
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
float cosAngle = (float)Math.Cos(Rotation);
float sinAngle = -(float)Math.Sin(Rotation);
Vector2 uvOffset = Vector2.Zero;
Vector2 overrideTextureDims = Vector2.One;
if (overrideLightTexture != null)
{
overrideTextureDims = new Vector2(overrideLightTexture.SourceRect.Width, overrideLightTexture.SourceRect.Height);
uvOffset = (overrideLightTexture.Origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
}
// Add a vertex for the center of the mesh
vertices.Add(new VertexPositionTexture(new Vector3(position.X, position.Y, 0),
new Vector2(0.5f, 0.5f) + uvOffset));
// Add all the other encounter points as vertices
// storing their world position as UV coordinates
foreach (Vector2 vertex in rayCastHits)
{
Vector2 rawDiff = vertex - drawPos;
Vector2 diff = rawDiff;
diff /= range*2.0f;
if (overrideLightTexture != null)
{
Vector2 originDiff = diff;
diff.X = originDiff.X * cosAngle - originDiff.Y * sinAngle;
diff.Y = originDiff.X * sinAngle + originDiff.Y * cosAngle;
diff *= (overrideTextureDims / overrideLightTexture.size) * 2.0f;
diff += uvOffset;
}
vertices.Add(new VertexPositionTexture(new Vector3(position.X + rawDiff.X, position.Y + rawDiff.Y, 0),
new Vector2(0.5f, 0.5f) + diff));
}
// Compute the indices to form triangles
List<short> indices = new List<short>();
for (int i = 0; i < rayCastHits.Count - 1; i++)
{
indices.Add(0);
indices.Add((short)((i + 2) % vertices.Count));
indices.Add((short)((i + 1) % vertices.Count));
}
indices.Add(0);
indices.Add((short)(1));
indices.Add((short)(vertices.Count - 1));
vertexCount = vertices.Count;
indexCount = indices.Count;
//TODO: a better way to determine the size of the vertex buffer and handle changes in size?
//now we just create a buffer for 64 verts and make it larger if needed
if (lightVolumeBuffer == null)
{
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionTexture.VertexDeclaration, Math.Max(64, (int)(vertexCount*1.5)), BufferUsage.None);
lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.Instance.GraphicsDevice, typeof(short), Math.Max(64*3, (int)(indexCount * 1.5)), BufferUsage.None);
}
else if (vertexCount > lightVolumeBuffer.VertexCount)
{
lightVolumeBuffer.Dispose();
lightVolumeIndexBuffer.Dispose();
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionTexture.VertexDeclaration, (int)(vertexCount*1.5), BufferUsage.None);
lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.Instance.GraphicsDevice, typeof(short), (int)(indexCount * 1.5), BufferUsage.None);
}
lightVolumeBuffer.SetData<VertexPositionTexture>(vertices.ToArray());
lightVolumeIndexBuffer.SetData<short>(indices.ToArray());
}
public void Draw(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform)
{
CheckHullsInRange();
Vector3 offset = ParentSub == null ? Vector3.Zero :
new Vector3(ParentSub.DrawPosition.X, ParentSub.DrawPosition.Y, 0.0f);
lightEffect.World = Matrix.CreateTranslation(offset) * transform;
Vector2 drawPos = position;
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
drawPos.Y = -drawPos.Y;
/*if (range > 1.0f)
{
if (overrideLightTexture == null)
{
Vector2 center = new Vector2(LightTexture.Width / 2, LightTexture.Height / 2);
float scale = range / (lightTexture.Width / 2.0f);
spriteBatch.Draw(lightTexture, drawPos, null, color * (color.A / 255.0f), 0, center, scale, SpriteEffects.None, 1);
}
else
{
overrideLightTexture.Draw(spriteBatch,
drawPos, color * (color.A / 255.0f),
overrideLightTexture.Origin, -Rotation,
new Vector2(overrideLightTexture.size.X / overrideLightTexture.SourceRect.Width, overrideLightTexture.size.Y / overrideLightTexture.SourceRect.Height));
}
}
if (LightSprite != null)
{
LightSprite.Draw(spriteBatch, drawPos, Color, LightSprite.Origin, -Rotation, 1);
}*/
if (NeedsRecalculation)
{
var verts = FindRaycastHits();
CalculateLightVertices(verts);
lastRecalculationTime = (float)Timing.TotalTime;
NeedsRecalculation = false;
}
if (vertexCount == 0) return;
lightEffect.DiffuseColor = (new Vector3(color.R, color.G, color.B) * (color.A / 255.0f)) / 255.0f;// color.ToVector3();
if (overrideLightTexture != null)
{
lightEffect.Texture = overrideLightTexture.Texture;
}
else
{
lightEffect.Texture = LightTexture;
}
lightEffect.CurrentTechnique.Passes[0].Apply();
GameMain.Instance.GraphicsDevice.SetVertexBuffer(lightVolumeBuffer);
GameMain.Instance.GraphicsDevice.Indices = lightVolumeIndexBuffer;
GameMain.Instance.GraphicsDevice.DrawIndexedPrimitives
(
PrimitiveType.TriangleList, 0, 0, indexCount / 3
);
}
/*public void FlipX()
{
if (LightSprite != null)
{
Vector2 lightOrigin = LightSprite.Origin;
lightOrigin.X = LightSprite.SourceRect.Width - lightOrigin.X;
LightSprite.Origin = lightOrigin;
}
if (overrideLightTexture != null)
{
Vector2 lightOrigin = overrideLightTexture.Origin;
lightOrigin.X = overrideLightTexture.SourceRect.Width - lightOrigin.X;
overrideLightTexture.Origin = lightOrigin;
}
}*/
public void Remove()
{
if (LightSprite != null) LightSprite.Remove();
if (lightVolumeBuffer != null)
{
lightVolumeBuffer.Dispose();
lightVolumeBuffer = null;
}
if (lightVolumeIndexBuffer != null)
{
lightVolumeIndexBuffer.Dispose();
lightVolumeIndexBuffer = null;
}
GameMain.LightManager.RemoveLight(this);
}
}
}
@@ -0,0 +1,234 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Barotrauma
{
partial class LinkedSubmarine : MapEntity
{
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
if (!editing || wallVertices == null) return;
Color color = (isHighlighted) ? Color.Orange : Color.Green;
if (IsSelected) color = Color.Red;
Vector2 pos = Position;
for (int i = 0; i < wallVertices.Count; i++)
{
Vector2 startPos = wallVertices[i] + pos;
startPos.Y = -startPos.Y;
Vector2 endPos = wallVertices[(i + 1) % wallVertices.Count] + pos;
endPos.Y = -endPos.Y;
GUI.DrawLine(spriteBatch,
startPos,
endPos,
color, 0.0f, 5);
}
pos.Y = -pos.Y;
GUI.DrawLine(spriteBatch, pos + Vector2.UnitY * 50.0f, pos - Vector2.UnitY * 50.0f, color, 0.0f, 5);
GUI.DrawLine(spriteBatch, pos + Vector2.UnitX * 50.0f, pos - Vector2.UnitX * 50.0f, color, 0.0f, 5);
Rectangle drawRect = rect;
drawRect.Y = -rect.Y;
GUI.DrawRectangle(spriteBatch, drawRect, Color.Red, true);
foreach (MapEntity e in linkedTo)
{
GUI.DrawLine(spriteBatch,
new Vector2(WorldPosition.X, -WorldPosition.Y),
new Vector2(e.WorldPosition.X, -e.WorldPosition.Y),
Color.Red * 0.3f);
}
}
public override void UpdateEditing(Camera cam)
{
if (editingHUD == null || editingHUD.UserData as LinkedSubmarine != this)
{
editingHUD = CreateEditingHUD();
}
editingHUD.Update((float)Timing.Step);
if (!PlayerInput.LeftButtonClicked() || !PlayerInput.KeyDown(Keys.Space)) return;
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
foreach (MapEntity entity in mapEntityList)
{
if (entity == this || !entity.IsHighlighted || !(entity is Item) || !entity.IsMouseOn(position)) continue;
if (((Item)entity).GetComponent<DockingPort>() == null) continue;
if (linkedTo.Contains(entity))
{
linkedTo.Remove(entity);
}
else
{
linkedTo.Add(entity);
}
}
}
public override void DrawEditing(SpriteBatch spriteBatch, Camera cam)
{
if (editingHUD == null) return;
editingHUD.Draw(spriteBatch);
}
private GUIComponent CreateEditingHUD(bool inGame = false)
{
int width = 450;
int x = GameMain.GraphicsWidth / 2 - width / 2, y = 10;
editingHUD = new GUIFrame(new Rectangle(x, y, width, 100), "");
editingHUD.Padding = new Vector4(10, 10, 0, 0);
editingHUD.UserData = this;
new GUITextBlock(new Rectangle(0, 0, 100, 20), "Linked submarine", "",
Alignment.TopLeft, Alignment.TopLeft, editingHUD, false, GUI.LargeFont);
var pathBox = new GUITextBox(new Rectangle(10, 30, 300, 20), "", editingHUD);
pathBox.Font = GUI.SmallFont;
pathBox.Text = filePath;
var reloadButton = new GUIButton(new Rectangle(320, 30, 80, 20), "Refresh", "", editingHUD);
reloadButton.OnClicked = Reload;
reloadButton.UserData = pathBox;
reloadButton.ToolTip = "Reload the linked submarine from the specified file";
y += 20;
if (!inGame)
{
new GUITextBlock(new Rectangle(0, 0, 0, 20), "Hold space to link to a docking port",
"", Alignment.TopRight, Alignment.TopRight, editingHUD, false, GUI.SmallFont);
y += 25;
}
return editingHUD;
}
private bool Reload(GUIButton button, object obj)
{
var pathBox = obj as GUITextBox;
if (!File.Exists(pathBox.Text))
{
new GUIMessageBox("Error", "Submarine file \"" + pathBox.Text + "\" not found!");
pathBox.Flash(Color.Red);
pathBox.Text = filePath;
return false;
}
XDocument doc = Submarine.OpenFile(pathBox.Text);
if (doc == null || doc.Root == null) return false;
pathBox.Flash(Color.Green);
GenerateWallVertices(doc.Root);
saveElement = doc.Root;
saveElement.Name = "LinkedSubmarine";
filePath = pathBox.Text;
return true;
}
public override XElement Save(XElement parentElement)
{
XElement saveElement = null;
if (sub == null)
{
if (this.saveElement == null)
{
var doc = Submarine.OpenFile(filePath);
saveElement = doc.Root;
saveElement.Name = "LinkedSubmarine";
saveElement.Add(new XAttribute("filepath", filePath));
}
else
{
saveElement = this.saveElement;
}
if (saveElement.Attribute("pos") != null) saveElement.Attribute("pos").Remove();
saveElement.Add(new XAttribute("pos", ToolBox.Vector2ToString(Position - Submarine.HiddenSubPosition)));
var linkedPort = linkedTo.FirstOrDefault(lt => (lt is Item) && ((Item)lt).GetComponent<DockingPort>() != null);
if (linkedPort != null)
{
if (saveElement.Attribute("linkedto") != null) saveElement.Attribute("linkedto").Remove();
saveElement.Add(new XAttribute("linkedto", linkedPort.ID));
}
}
else
{
saveElement = new XElement("LinkedSubmarine");
sub.SaveToXElement(saveElement);
}
if (sub != null)
{
bool leaveBehind = false;
if (!sub.DockedTo.Contains(Submarine.MainSub))
{
System.Diagnostics.Debug.Assert(Submarine.MainSub.AtEndPosition || Submarine.MainSub.AtStartPosition);
if (Submarine.MainSub.AtEndPosition)
{
leaveBehind = sub.AtEndPosition != Submarine.MainSub.AtEndPosition;
}
else
{
leaveBehind = sub.AtStartPosition != Submarine.MainSub.AtStartPosition;
}
}
if (leaveBehind)
{
saveElement.SetAttributeValue("location", Level.Loaded.Seed);
saveElement.SetAttributeValue("worldpos", ToolBox.Vector2ToString(sub.SubBody.Position));
}
else
{
if (saveElement.Attribute("location") != null) saveElement.Attribute("location").Remove();
if (saveElement.Attribute("worldpos") != null) saveElement.Attribute("worldpos").Remove();
}
saveElement.SetAttributeValue("pos", ToolBox.Vector2ToString(Position - Submarine.HiddenSubPosition));
}
parentElement.Add(saveElement);
return saveElement;
}
}
}
@@ -0,0 +1,210 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Voronoi2;
namespace Barotrauma
{
partial class Map
{
private static Sprite iceTexture;
private static Texture2D iceCraters;
private static Texture2D iceCrack;
public void Update(float deltaTime, Rectangle rect, float scale = 1.0f)
{
Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y);
Vector2 offset = -currentLocation.MapPosition;
float maxDist = 20.0f;
float closestDist = 0.0f;
highlightedLocation = null;
for (int i = 0; i < locations.Count; i++)
{
Location location = locations[i];
Vector2 pos = rectCenter + (location.MapPosition + offset) * scale;
if (!rect.Contains(pos)) continue;
float dist = Vector2.Distance(PlayerInput.MousePosition, pos);
if (dist < maxDist && (highlightedLocation == null || dist < closestDist))
{
closestDist = dist;
highlightedLocation = location;
}
}
foreach (LocationConnection connection in connections)
{
if (highlightedLocation != currentLocation &&
connection.Locations.Contains(highlightedLocation) && connection.Locations.Contains(currentLocation))
{
if (PlayerInput.LeftButtonClicked() &&
selectedLocation != highlightedLocation && highlightedLocation != null)
{
selectedConnection = connection;
selectedLocation = highlightedLocation;
GameMain.LobbyScreen.SelectLocation(highlightedLocation, connection);
}
}
}
}
public void Draw(SpriteBatch spriteBatch, Rectangle rect, float scale = 1.0f)
{
Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y);
Vector2 offset = -currentLocation.MapPosition;
iceTexture.DrawTiled(spriteBatch, new Vector2(rect.X, rect.Y), new Vector2(rect.Width, rect.Height), Vector2.Zero, Color.White * 0.8f);
foreach (LocationConnection connection in connections)
{
Color crackColor = Color.White * Math.Max(connection.Difficulty / 100.0f, 1.5f);
if (selectedLocation != currentLocation &&
(connection.Locations.Contains(selectedLocation) && connection.Locations.Contains(currentLocation)))
{
crackColor = Color.Red;
}
else if (highlightedLocation != currentLocation &&
(connection.Locations.Contains(highlightedLocation) && connection.Locations.Contains(currentLocation)))
{
crackColor = Color.Red * 0.5f;
}
else if (!connection.Passed)
{
crackColor *= 0.2f;
}
for (int i = 0; i < connection.CrackSegments.Count; i++)
{
var segment = connection.CrackSegments[i];
Vector2 start = rectCenter + (segment[0] + offset) * scale;
Vector2 end = rectCenter + (segment[1] + offset) * scale;
if (!rect.Contains(start) && !rect.Contains(end))
{
continue;
}
else
{
Vector2? intersection = MathUtils.GetLineRectangleIntersection(start, end, new Rectangle(rect.X, rect.Y + rect.Height, rect.Width, rect.Height));
if (intersection != null)
{
if (!rect.Contains(start))
{
start = (Vector2)intersection;
}
else
{
end = (Vector2)intersection;
}
}
}
float dist = Vector2.Distance(start, end);
int width = (int)(MathHelper.Clamp(connection.Difficulty, 2.0f, 20.0f) * scale);
spriteBatch.Draw(iceCrack,
new Rectangle((int)start.X, (int)start.Y, (int)dist + 2, width),
new Rectangle(0, 0, iceCrack.Width, 60), crackColor, MathUtils.VectorToAngle(end - start),
new Vector2(0, 30), SpriteEffects.None, 0.01f);
}
}
rect.Inflate(8, 8);
GUI.DrawRectangle(spriteBatch, rect, Color.Black, false, 0.0f, 8);
GUI.DrawRectangle(spriteBatch, rect, Color.LightGray);
for (int i = 0; i < locations.Count; i++)
{
Location location = locations[i];
Vector2 pos = rectCenter + (location.MapPosition + offset) * scale;
Rectangle drawRect = location.Type.Sprite.SourceRect;
Rectangle sourceRect = drawRect;
drawRect.X = (int)pos.X - drawRect.Width / 2;
drawRect.Y = (int)pos.Y - drawRect.Width / 2;
if (!rect.Intersects(drawRect)) continue;
Color color = location.Connections.Find(c => c.Locations.Contains(currentLocation)) == null ? Color.White : Color.Green;
color *= (location.Discovered) ? 0.8f : 0.2f;
if (location == currentLocation) color = Color.Orange;
if (drawRect.X < rect.X)
{
sourceRect.X += rect.X - drawRect.X;
sourceRect.Width -= sourceRect.X;
drawRect.X = rect.X;
}
else if (drawRect.Right > rect.Right)
{
sourceRect.Width -= (drawRect.Right - rect.Right);
}
if (drawRect.Y < rect.Y)
{
sourceRect.Y += rect.Y - drawRect.Y;
sourceRect.Height -= sourceRect.Y;
drawRect.Y = rect.Y;
}
else if (drawRect.Bottom > rect.Bottom)
{
sourceRect.Height -= drawRect.Bottom - rect.Bottom;
}
drawRect.Width = sourceRect.Width;
drawRect.Height = sourceRect.Height;
spriteBatch.Draw(location.Type.Sprite.Texture, drawRect, sourceRect, color);
}
for (int i = 0; i < 3; i++)
{
Location location = (i == 0) ? highlightedLocation : selectedLocation;
if (i == 2) location = currentLocation;
if (location == null) continue;
Vector2 pos = rectCenter + (location.MapPosition + offset) * scale;
pos.X = (int)(pos.X + location.Type.Sprite.SourceRect.Width * 0.6f);
pos.Y = (int)(pos.Y - 10);
GUI.DrawString(spriteBatch, pos, location.Name, Color.White, Color.Black * 0.8f, 3);
}
}
public void Save(XElement element)
{
XElement mapElement = new XElement("map");
mapElement.Add(new XAttribute("currentlocation", CurrentLocationIndex));
mapElement.Add(new XAttribute("seed", Seed));
mapElement.Add(new XAttribute("size", size));
List<int> discoveredLocations = new List<int>();
for (int i = 0; i < locations.Count; i++)
{
if (locations[i].Discovered) discoveredLocations.Add(i);
}
mapElement.Add(new XAttribute("discovered", string.Join(",", discoveredLocations)));
List<int> passedConnections = new List<int>();
for (int i = 0; i < connections.Count; i++)
{
if (connections[i].Passed) passedConnections.Add(i);
}
mapElement.Add(new XAttribute("passed", string.Join(",", passedConnections)));
element.Add(mapElement);
}
}
}
@@ -0,0 +1,15 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
namespace Barotrauma
{
partial class Location
{
private HireManager hireManager;
public HireManager HireManager
{
get { return hireManager; }
}
}
}
@@ -0,0 +1,637 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Collections.ObjectModel;
using Barotrauma.Items.Components;
namespace Barotrauma
{
abstract partial class MapEntity : Entity
{
protected static Vector2 selectionPos = Vector2.Zero;
protected static Vector2 selectionSize = Vector2.Zero;
protected static Vector2 startMovingPos = Vector2.Zero;
//which entities have been selected for editing
private static List<MapEntity> selectedList = new List<MapEntity>();
public static List<MapEntity> SelectedList
{
get
{
return selectedList;
}
}
private static List<MapEntity> copiedList = new List<MapEntity>();
private static List<MapEntity> highlightedList = new List<MapEntity>();
private static float highlightTimer;
private static GUIListBox highlightedListBox;
public static GUIListBox HighlightedListBox
{
get { return highlightedListBox; }
}
protected static GUIComponent editingHUD;
public static GUIComponent EditingHUD
{
get
{
return editingHUD;
}
}
//protected bool isSelected;
private static bool disableSelect;
public static bool DisableSelect
{
get { return disableSelect; }
set
{
disableSelect = value;
if (disableSelect)
{
startMovingPos = Vector2.Zero;
selectionSize = Vector2.Zero;
selectionPos = Vector2.Zero;
}
}
}
public static bool SelectedAny
{
get { return selectedList.Count > 0; }
}
public bool IsSelected
{
get { return selectedList.Contains(this); }
}
protected bool ResizeHorizontal
{
get { return prefab != null && prefab.ResizeHorizontal; }
}
protected bool ResizeVertical
{
get { return prefab != null && prefab.ResizeVertical; }
}
public virtual void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { }
public virtual void DrawDamage(SpriteBatch spriteBatch, Effect damageEffect) { }
/// <summary>
/// Update the selection logic in submarine editor
/// </summary>
public static void UpdateSelecting(Camera cam)
{
if (resizing)
{
if (selectedList.Count == 0) resizing = false;
return;
}
foreach (MapEntity e in mapEntityList)
{
e.isHighlighted = false;
}
if (DisableSelect)
{
DisableSelect = false;
return;
}
if (GUIComponent.MouseOn != null || !PlayerInput.MouseInsideWindow)
{
if (highlightedListBox == null ||
(GUIComponent.MouseOn != highlightedListBox && !highlightedListBox.IsParentOf(GUIComponent.MouseOn)))
{
UpdateHighlightedListBox(null);
return;
}
}
if (MapEntityPrefab.Selected != null)
{
selectionPos = Vector2.Zero;
selectedList.Clear();
return;
}
if (PlayerInput.KeyDown(Keys.Delete))
{
selectedList.ForEach(e => e.Remove());
selectedList.Clear();
}
if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl))
{
if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.C) &&
PlayerInput.GetOldKeyboardState.IsKeyUp(Keys.C))
{
CopyEntities(selectedList);
}
else if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.X) &&
PlayerInput.GetOldKeyboardState.IsKeyUp(Keys.X))
{
CopyEntities(selectedList);
selectedList.ForEach(e => e.Remove());
selectedList.Clear();
}
else if (copiedList.Count > 0 &&
PlayerInput.GetKeyboardState.IsKeyDown(Keys.V) &&
PlayerInput.GetOldKeyboardState.IsKeyUp(Keys.V))
{
var clones = Clone(copiedList);
Vector2 center = Vector2.Zero;
clones.ForEach(c => center += c.WorldPosition);
center = Submarine.VectorToWorldGrid(center / clones.Count);
Vector2 moveAmount = Submarine.VectorToWorldGrid(cam.WorldViewCenter - center);
selectedList = new List<MapEntity>(clones);
foreach (MapEntity clone in selectedList)
{
clone.Move(moveAmount);
clone.Submarine = Submarine.MainSub;
}
}
}
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
MapEntity highLightedEntity = null;
if (startMovingPos == Vector2.Zero)
{
List<MapEntity> highlightedEntities = new List<MapEntity>();
if (highlightedListBox != null && highlightedListBox.IsParentOf(GUIComponent.MouseOn))
{
highLightedEntity = GUIComponent.MouseOn.UserData as MapEntity;
}
else
{
foreach (MapEntity e in mapEntityList)
{
if (!e.SelectableInEditor) continue;
if (e.IsMouseOn(position))
{
int i = 0;
while (i < highlightedEntities.Count &&
e.Sprite != null &&
(highlightedEntities[i].Sprite == null || highlightedEntities[i].Sprite.Depth < e.Sprite.Depth))
{
i++;
}
highlightedEntities.Insert(i, e);
if (i == 0) highLightedEntity = e;
}
}
if (PlayerInput.MouseSpeed.LengthSquared() > 10)
{
highlightTimer = 0.0f;
}
else
{
bool mouseNearHighlightBox = false;
if (highlightedListBox != null)
{
Rectangle expandedRect = highlightedListBox.Rect;
expandedRect.Inflate(20, 20);
mouseNearHighlightBox = expandedRect.Contains(PlayerInput.MousePosition);
if (!mouseNearHighlightBox) highlightedListBox = null;
}
highlightTimer += (float)Timing.Step;
if (highlightTimer > 1.0f)
{
if (!mouseNearHighlightBox)
{
UpdateHighlightedListBox(highlightedEntities);
highlightTimer = 0.0f;
}
}
}
}
if (highLightedEntity != null) highLightedEntity.isHighlighted = true;
}
//started moving selected entities
if (startMovingPos != Vector2.Zero)
{
if (PlayerInput.LeftButtonReleased())
{
//mouse released -> move the entities to the new position of the mouse
Vector2 moveAmount = position - startMovingPos;
moveAmount = Submarine.VectorToWorldGrid(moveAmount);
if (moveAmount != Vector2.Zero)
{
//clone
if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl))
{
var clones = Clone(selectedList);
selectedList = clones;
selectedList.ForEach(c => c.Move(moveAmount));
}
else // move
{
foreach (MapEntity e in selectedList) e.Move(moveAmount);
}
}
startMovingPos = Vector2.Zero;
}
}
//started dragging a "selection rectangle"
else if (selectionPos != Vector2.Zero)
{
selectionSize.X = position.X - selectionPos.X;
selectionSize.Y = selectionPos.Y - position.Y;
List<MapEntity> newSelection = new List<MapEntity>();// FindSelectedEntities(selectionPos, selectionSize);
if (Math.Abs(selectionSize.X) > Submarine.GridSize.X || Math.Abs(selectionSize.Y) > Submarine.GridSize.Y)
{
newSelection = FindSelectedEntities(selectionPos, selectionSize);
}
else
{
if (highLightedEntity != null) newSelection.Add(highLightedEntity);
}
if (PlayerInput.LeftButtonReleased())
{
if (PlayerInput.KeyDown(Keys.LeftControl) ||
PlayerInput.KeyDown(Keys.RightControl))
{
foreach (MapEntity e in newSelection)
{
if (selectedList.Contains(e))
selectedList.Remove(e);
else
selectedList.Add(e);
}
}
else
{
selectedList = newSelection;
}
//select wire if both items it's connected to are selected
var selectedItems = selectedList.Where(e => e is Item).Cast<Item>().ToList();
foreach (Item item in selectedItems)
{
if (item.Connections == null) continue;
foreach (Connection c in item.Connections)
{
foreach (Wire w in c.Wires)
{
if (w == null || selectedList.Contains(w.Item)) continue;
if (w.OtherConnection(c) != null && selectedList.Contains(w.OtherConnection(c).Item))
{
selectedList.Add(w.Item);
}
}
}
}
selectionPos = Vector2.Zero;
selectionSize = Vector2.Zero;
}
}
//default, not doing anything specific yet
else
{
if (PlayerInput.LeftButtonHeld() &&
PlayerInput.KeyUp(Keys.Space) &&
(highlightedListBox == null || (GUIComponent.MouseOn != highlightedListBox && !highlightedListBox.IsParentOf(GUIComponent.MouseOn))))
{
//if clicking a selected entity, start moving it
foreach (MapEntity e in selectedList)
{
if (e.IsMouseOn(position)) startMovingPos = position;
}
selectionPos = position;
}
}
}
private static void UpdateHighlightedListBox(List<MapEntity> highlightedEntities)
{
if (highlightedEntities == null || highlightedEntities.Count < 2)
{
highlightedListBox = null;
return;
}
if (highlightedListBox != null)
{
if (GUIComponent.MouseOn == highlightedListBox || highlightedListBox.IsParentOf(GUIComponent.MouseOn)) return;
if (highlightedEntities.SequenceEqual(highlightedList)) return;
}
highlightedList = highlightedEntities;
highlightedListBox = new GUIListBox(
new Rectangle((int)PlayerInput.MousePosition.X + 15, (int)PlayerInput.MousePosition.Y + 15, 150, highlightedEntities.Count * 18 + 5),
null, Alignment.TopLeft, "GUIToolTip", null, false);
foreach (MapEntity entity in highlightedEntities)
{
var textBlock = new GUITextBlock(
new Rectangle(0, 0, highlightedListBox.Rect.Width, 18),
ToolBox.LimitString(entity.Name, GUI.SmallFont, 140), "", Alignment.TopLeft, Alignment.CenterLeft, highlightedListBox, false, GUI.SmallFont);
textBlock.UserData = entity;
}
highlightedListBox.OnSelected = (GUIComponent component, object obj) =>
{
MapEntity entity = obj as MapEntity;
if (PlayerInput.KeyDown(Keys.LeftControl) ||
PlayerInput.KeyDown(Keys.RightControl))
{
if (selectedList.Contains(entity))
selectedList.Remove(entity);
else
selectedList.Add(entity);
}
else
{
SelectEntity(entity);
}
return true;
};
}
/// <summary>
/// Draw the "selection rectangle" and outlines of entities that are being dragged (if any)
/// </summary>
public static void DrawSelecting(SpriteBatch spriteBatch, Camera cam)
{
if (GUIComponent.MouseOn != null) return;
Vector2 position = PlayerInput.MousePosition;
position = cam.ScreenToWorld(position);
if (startMovingPos != Vector2.Zero)
{
Vector2 moveAmount = position - startMovingPos;
moveAmount = Submarine.VectorToWorldGrid(moveAmount);
moveAmount.Y = -moveAmount.Y;
//started moving the selected entities
if (moveAmount != Vector2.Zero)
{
foreach (MapEntity e in selectedList)
GUI.DrawRectangle(spriteBatch,
new Vector2(e.WorldRect.X, -e.WorldRect.Y) + moveAmount,
new Vector2(e.rect.Width, e.rect.Height),
Color.DarkRed, false, 0, (int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f));
//stop dragging the "selection rectangle"
selectionPos = Vector2.Zero;
}
}
if (selectionPos != null && selectionPos != Vector2.Zero)
{
GUI.DrawRectangle(spriteBatch, new Vector2(selectionPos.X, -selectionPos.Y), selectionSize, Color.DarkRed, false, 0, (int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f));
}
}
public static void UpdateEditor(Camera cam)
{
if (highlightedListBox != null) highlightedListBox.Update((float)Timing.Step);
if (selectedList.Count == 1)
{
selectedList[0].UpdateEditing(cam);
if (selectedList[0].ResizeHorizontal || selectedList[0].ResizeVertical)
{
selectedList[0].UpdateResizing(cam);
}
}
if (editingHUD != null)
{
if (selectedList.Count == 0 || editingHUD.UserData != selectedList[0])
{
foreach (GUIComponent component in editingHUD.children)
{
var textBox = component as GUITextBox;
if (textBox == null) continue;
textBox.Deselect();
}
editingHUD = null;
}
}
}
public static void DrawEditor(SpriteBatch spriteBatch, Camera cam)
{
if (selectedList.Count == 1)
{
selectedList[0].DrawEditing(spriteBatch, cam);
if (selectedList[0].ResizeHorizontal || selectedList[0].ResizeVertical)
{
selectedList[0].DrawResizing(spriteBatch, cam);
}
}
if (highlightedListBox != null)
{
highlightedListBox.Draw(spriteBatch);
}
}
public static void DeselectAll()
{
selectedList.Clear();
}
public static void SelectEntity(MapEntity entity)
{
DeselectAll();
selectedList.Add(entity);
}
/// <summary>
/// copies a list of entities to the "clipboard" (copiedList)
/// </summary>
private static void CopyEntities(List<MapEntity> entities)
{
List<MapEntity> prevEntities = new List<MapEntity>(mapEntityList);
copiedList = Clone(entities);
//find all new entities created during cloning
var newEntities = mapEntityList.Except(prevEntities).ToList();
//do a "shallow remove" (removes the entities from the game without removing links between them)
// -> items will stay in their containers
newEntities.ForEach(e => e.ShallowRemove());
}
public virtual void AddToGUIUpdateList()
{
if (editingHUD != null && editingHUD.UserData == this) editingHUD.AddToGUIUpdateList();
}
public virtual void UpdateEditing(Camera cam) { }
public virtual void DrawEditing(SpriteBatch spriteBatch, Camera cam) { }
private void UpdateResizing(Camera cam)
{
isHighlighted = true;
int startX = ResizeHorizontal ? -1 : 0;
int StartY = ResizeVertical ? -1 : 0;
for (int x = startX; x < 2; x += 2)
{
for (int y = StartY; y < 2; y += 2)
{
Vector2 handlePos = cam.WorldToScreen(Position + new Vector2(x * (rect.Width * 0.5f + 5), y * (rect.Height * 0.5f + 5)));
bool highlighted = Vector2.Distance(PlayerInput.MousePosition, handlePos) < 5.0f;
if (highlighted && PlayerInput.LeftButtonDown())
{
selectionPos = Vector2.Zero;
resizeDirX = x;
resizeDirY = y;
resizing = true;
}
}
}
if (resizing)
{
Vector2 placePosition = new Vector2(rect.X, rect.Y);
Vector2 placeSize = new Vector2(rect.Width, rect.Height);
Vector2 mousePos = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
if (resizeDirX > 0)
{
mousePos.X = Math.Max(mousePos.X, rect.X + Submarine.GridSize.X);
placeSize.X = mousePos.X - placePosition.X;
}
else if (resizeDirX < 0)
{
mousePos.X = Math.Min(mousePos.X, rect.Right - Submarine.GridSize.X);
placeSize.X = (placePosition.X + placeSize.X) - mousePos.X;
placePosition.X = mousePos.X;
}
if (resizeDirY < 0)
{
mousePos.Y = Math.Min(mousePos.Y, rect.Y - Submarine.GridSize.Y);
placeSize.Y = placePosition.Y - mousePos.Y;
}
else if (resizeDirY > 0)
{
mousePos.Y = Math.Max(mousePos.Y, rect.Y - rect.Height + Submarine.GridSize.X);
placeSize.Y = mousePos.Y - (rect.Y - rect.Height);
placePosition.Y = mousePos.Y;
}
if ((int)placePosition.X != rect.X || (int)placePosition.Y != rect.Y || (int)placeSize.X != rect.Width || (int)placeSize.Y != rect.Height)
{
Rect = new Rectangle((int)placePosition.X, (int)placePosition.Y, (int)placeSize.X, (int)placeSize.Y);
}
if (!PlayerInput.LeftButtonHeld())
{
resizing = false;
}
}
}
private void DrawResizing(SpriteBatch spriteBatch, Camera cam)
{
isHighlighted = true;
int startX = ResizeHorizontal ? -1 : 0;
int StartY = ResizeVertical ? -1 : 0;
for (int x = startX; x < 2; x += 2)
{
for (int y = StartY; y < 2; y += 2)
{
Vector2 handlePos = cam.WorldToScreen(Position + new Vector2(x * (rect.Width * 0.5f + 5), y * (rect.Height * 0.5f + 5)));
bool highlighted = Vector2.Distance(PlayerInput.MousePosition, handlePos) < 5.0f;
GUI.DrawRectangle(spriteBatch,
handlePos - new Vector2(3.0f, 3.0f),
new Vector2(6.0f, 6.0f),
Color.White * (highlighted ? 1.0f : 0.6f),
true, 0,
(int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f));
}
}
}
/// <summary>
/// Find entities whose rect intersects with the "selection rect"
/// </summary>
public static List<MapEntity> FindSelectedEntities(Vector2 pos, Vector2 size)
{
List<MapEntity> foundEntities = new List<MapEntity>();
Rectangle selectionRect = Submarine.AbsRect(pos, size);
foreach (MapEntity e in mapEntityList)
{
if (!e.SelectableInEditor) continue;
if (Submarine.RectsOverlap(selectionRect, e.rect)) foundEntities.Add(e);
}
return foundEntities;
}
public virtual XElement Save(XElement parentElement)
{
DebugConsole.ThrowError("Saving entity " + GetType() + " failed.");
return null;
}
}
}
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Barotrauma
{
partial class MapEntityPrefab
{
public virtual void DrawPlacing(SpriteBatch spriteBatch, Camera cam)
{
Vector2 placeSize = Submarine.GridSize;
if (placePosition == Vector2.Zero)
{
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
GUI.DrawLine(spriteBatch, new Vector2(position.X - GameMain.GraphicsWidth, -position.Y), new Vector2(position.X + GameMain.GraphicsWidth, -position.Y), Color.White, 0, (int)(2.0f / cam.Zoom));
GUI.DrawLine(spriteBatch, new Vector2(position.X, -(position.Y - GameMain.GraphicsHeight)), new Vector2(position.X, -(position.Y + GameMain.GraphicsHeight)), Color.White, 0, (int)(2.0f / cam.Zoom));
}
else
{
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
if (resizeHorizontal) placeSize.X = position.X - placePosition.X;
if (resizeVertical) placeSize.Y = placePosition.Y - position.Y;
Rectangle newRect = Submarine.AbsRect(placePosition, placeSize);
newRect.Width = (int)Math.Max(newRect.Width, Submarine.GridSize.X);
newRect.Height = (int)Math.Max(newRect.Height, Submarine.GridSize.Y);
if (Submarine.MainSub != null)
{
newRect.Location -= Submarine.MainSub.Position.ToPoint();
}
newRect.Y = -newRect.Y;
GUI.DrawRectangle(spriteBatch, newRect, Color.DarkBlue);
}
}
public void DrawListLine(SpriteBatch spriteBatch, Vector2 pos, Color color)
{
GUI.Font.DrawString(spriteBatch, name, pos, color);
}
}
}
@@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Factories;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Networking;
using Barotrauma.Lights;
namespace Barotrauma
{
partial class WallSection
{
public ConvexHull hull;
}
partial class Structure : MapEntity, IDamageable, IServerSerializable
{
List<ConvexHull> convexHulls;
private void GenerateConvexHull()
{
// If not null and not empty , remove the hulls from the system
if (convexHulls != null && convexHulls.Any())
convexHulls.ForEach(x => x.Remove());
// list all of hulls for this structure
convexHulls = new List<ConvexHull>();
var mergedSections = new List<WallSection>();
foreach (var section in sections)
{
if (mergedSections.Count > 5)
{
mergedSections.Add(section);
GenerateMergedHull(mergedSections);
continue;
}
// if there is a gap and we have sections to merge, do it.
if (section.gap != null)
{
GenerateMergedHull(mergedSections);
}
else
{
mergedSections.Add(section);
}
}
// take care of any leftover pieces
if (mergedSections.Count > 0)
{
GenerateMergedHull(mergedSections);
}
}
private void GenerateMergedHull(List<WallSection> mergedSections)
{
if (!mergedSections.Any()) return;
Rectangle mergedRect = GenerateMergedRect(mergedSections);
var h = new ConvexHull(CalculateExtremes(mergedRect), Color.Black, this);
mergedSections.ForEach(x => x.hull = h);
convexHulls.Add(h);
mergedSections.Clear();
}
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
if (prefab.sprite == null) return;
Draw(spriteBatch, editing, back, null);
}
public override void DrawDamage(SpriteBatch spriteBatch, Effect damageEffect)
{
Draw(spriteBatch, false, false, damageEffect);
}
private void Draw(SpriteBatch spriteBatch, bool editing, bool back = true, Effect damageEffect = null)
{
if (prefab.sprite == null) return;
Color color = (isHighlighted) ? Color.Orange : Color.White;
if (IsSelected && editing)
{
color = Color.Red;
GUI.DrawRectangle(spriteBatch, new Rectangle(rect.X, -rect.Y, rect.Width, rect.Height), color);
}
Vector2 drawOffset = Submarine == null ? Vector2.Zero : Submarine.DrawPosition;
float depth = prefab.sprite.Depth;
depth -= (ID % 255) * 0.000001f;
if (back && damageEffect == null)
{
if (prefab.BackgroundSprite != null)
{
prefab.BackgroundSprite.DrawTiled(
spriteBatch,
new Vector2(rect.X + drawOffset.X, -(rect.Y + drawOffset.Y)),
new Vector2(rect.Width, rect.Height),
color, Point.Zero);
}
}
SpriteEffects oldEffects = prefab.sprite.effects;
prefab.sprite.effects ^= SpriteEffects;
if (back == prefab.sprite.Depth > 0.5f || editing)
{
for (int i = 0; i < sections.Length; i++)
{
if (damageEffect != null)
{
float newCutoff = Math.Min((sections[i].damage / prefab.MaxHealth), 0.65f);
if (Math.Abs(newCutoff - Submarine.DamageEffectCutoff) > 0.01f)
{
damageEffect.Parameters["aCutoff"].SetValue(newCutoff);
damageEffect.Parameters["cCutoff"].SetValue(newCutoff * 1.2f);
damageEffect.CurrentTechnique.Passes[0].Apply();
Submarine.DamageEffectCutoff = newCutoff;
}
}
Point textureOffset = new Point(
Math.Abs(rect.Location.X - sections[i].rect.Location.X),
Math.Abs(rect.Location.Y - sections[i].rect.Location.Y));
if (flippedX && isHorizontal)
{
textureOffset.X = rect.Width - textureOffset.X - sections[i].rect.Width;
}
prefab.sprite.DrawTiled(
spriteBatch,
new Vector2(sections[i].rect.X + drawOffset.X, -(sections[i].rect.Y + drawOffset.Y)),
new Vector2(sections[i].rect.Width, sections[i].rect.Height),
color,
textureOffset, depth);
}
}
prefab.sprite.effects = oldEffects;
}
public override XElement Save(XElement parentElement)
{
XElement element = new XElement("Structure");
element.Add(new XAttribute("name", prefab.Name),
new XAttribute("ID", ID),
new XAttribute("rect",
(int)(rect.X - Submarine.HiddenSubPosition.X) + "," +
(int)(rect.Y - Submarine.HiddenSubPosition.Y) + "," +
rect.Width + "," + rect.Height));
for (int i = 0; i < sections.Length; i++)
{
if (sections[i].damage == 0.0f) continue;
var sectionElement =
new XElement("section",
new XAttribute("i", i),
new XAttribute("damage", sections[i].damage));
if (sections[i].gap != null)
{
sectionElement.Add(new XAttribute("gap", sections[i].gap.ID));
}
element.Add(sectionElement);
}
parentElement.Add(element);
return element;
}
}
}
@@ -0,0 +1,44 @@
using System;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;
namespace Barotrauma
{
partial class StructurePrefab : MapEntityPrefab
{
public override void DrawPlacing(SpriteBatch spriteBatch, Camera cam)
{
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
//Vector2 placeSize = size;
Rectangle newRect = new Rectangle((int)position.X, (int)position.Y, (int)size.X, (int)size.Y);
if (placePosition == Vector2.Zero)
{
if (PlayerInput.LeftButtonHeld())
placePosition = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
newRect.X = (int)position.X;
newRect.Y = (int)position.Y;
//sprite.Draw(spriteBatch, new Vector2(position.X, -position.Y), placeSize, Color.White);
}
else
{
Vector2 placeSize = size;
if (resizeHorizontal) placeSize.X = position.X - placePosition.X;
if (resizeVertical) placeSize.Y = placePosition.Y - position.Y;
newRect = Submarine.AbsRect(placePosition, placeSize);
}
sprite.DrawTiled(spriteBatch, new Vector2(newRect.X, -newRect.Y), new Vector2(newRect.Width, newRect.Height), Color.White);
GUI.DrawRectangle(spriteBatch, new Rectangle(newRect.X - GameMain.GraphicsWidth, -newRect.Y, newRect.Width + GameMain.GraphicsWidth * 2, newRect.Height), Color.White);
GUI.DrawRectangle(spriteBatch, new Rectangle(newRect.X, -newRect.Y - GameMain.GraphicsHeight, newRect.Width, newRect.Height + GameMain.GraphicsHeight * 2), Color.White);
}
}
}
@@ -0,0 +1,228 @@
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Common;
using FarseerPhysics.Dynamics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Voronoi2;
namespace Barotrauma
{
partial class Submarine : Entity, IServerSerializable
{
public static void Draw(SpriteBatch spriteBatch, bool editing = false)
{
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.mapEntityList;
foreach (MapEntity e in entitiesToRender)
{
e.Draw(spriteBatch, editing);
}
}
public static void DrawFront(SpriteBatch spriteBatch, bool editing = false, Predicate<MapEntity> predicate = null)
{
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.mapEntityList;
foreach (MapEntity e in entitiesToRender)
{
if (!e.DrawOverWater) continue;
if (predicate != null)
{
if (!predicate(e)) continue;
}
e.Draw(spriteBatch, editing, false);
}
if (GameMain.DebugDraw)
{
foreach (Submarine sub in Submarine.Loaded)
{
Rectangle worldBorders = sub.Borders;
worldBorders.Location += sub.WorldPosition.ToPoint();
worldBorders.Y = -worldBorders.Y;
GUI.DrawRectangle(spriteBatch, worldBorders, Color.White, false, 0, 5);
if (sub.subBody.MemPos.Count < 2) continue;
Vector2 prevPos = ConvertUnits.ToDisplayUnits(sub.subBody.MemPos[0].Position);
prevPos.Y = -prevPos.Y;
for (int i = 1; i < sub.subBody.MemPos.Count; i++)
{
Vector2 currPos = ConvertUnits.ToDisplayUnits(sub.subBody.MemPos[i].Position);
currPos.Y = -currPos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)currPos.X - 10, (int)currPos.Y - 10, 20, 20), Color.Blue * 0.6f, true, 0.01f);
GUI.DrawLine(spriteBatch, prevPos, currPos, Color.Cyan * 0.5f, 0, 5);
prevPos = currPos;
}
}
}
}
public static float DamageEffectCutoff;
public static void DrawDamageable(SpriteBatch spriteBatch, Effect damageEffect, bool editing = false)
{
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.mapEntityList;
foreach (MapEntity e in entitiesToRender)
{
if (e.DrawDamageEffect)
e.DrawDamage(spriteBatch, damageEffect);
}
if (damageEffect != null)
{
damageEffect.Parameters["aCutoff"].SetValue(0.0f);
damageEffect.Parameters["cCutoff"].SetValue(0.0f);
DamageEffectCutoff = 0.0f;
}
}
public static void DrawBack(SpriteBatch spriteBatch, bool editing = false, Predicate<MapEntity> predicate = null)
{
var entitiesToRender = !editing && visibleEntities != null ? visibleEntities : MapEntity.mapEntityList;
foreach (MapEntity e in entitiesToRender)
{
if (!e.DrawBelowWater) continue;
if (predicate != null)
{
if (!predicate(e)) continue;
}
e.Draw(spriteBatch, editing, true);
}
}
public bool Save()
{
return SaveAs(filePath);
}
public bool SaveAs(string filePath)
{
name = System.IO.Path.GetFileNameWithoutExtension(filePath);
XDocument doc = new XDocument(new XElement("Submarine"));
SaveToXElement(doc.Root);
hash = new Md5Hash(doc);
doc.Root.Add(new XAttribute("md5hash", hash.Hash));
try
{
SaveUtil.CompressStringToFile(filePath, doc.ToString());
}
catch (Exception e)
{
DebugConsole.ThrowError("Saving submarine \"" + filePath + "\" failed!", e);
return false;
}
return true;
}
public void SaveToXElement(XElement element)
{
element.Add(new XAttribute("name", name));
element.Add(new XAttribute("description", Description == null ? "" : Description));
element.Add(new XAttribute("tags", tags.ToString()));
foreach (MapEntity e in MapEntity.mapEntityList)
{
if (e.MoveWithLevel || e.Submarine != this) continue;
e.Save(element);
}
}
public static bool SaveCurrent(string filePath)
{
if (Submarine.MainSub == null)
{
Submarine.MainSub = new Submarine(filePath);
// return;
}
Submarine.MainSub.filePath = filePath;
return Submarine.MainSub.SaveAs(filePath);
}
public void CheckForErrors()
{
List<string> errorMsgs = new List<string>();
if (!Hull.hullList.Any())
{
errorMsgs.Add("No hulls found in the submarine. Hulls determine the \"borders\" of an individual room and are required for water and air distribution to work correctly.");
}
foreach (Item item in Item.ItemList)
{
if (item.GetComponent<Items.Components.Vent>() == null) continue;
if (!item.linkedTo.Any())
{
errorMsgs.Add("The submarine contains vents which haven't been linked to an oxygen generator. Select a vent and click an oxygen generator while holding space to link them.");
break;
}
}
if (WayPoint.WayPointList.Find(wp => !wp.MoveWithLevel && wp.SpawnType == SpawnType.Path) == null)
{
errorMsgs.Add("No waypoints found in the submarine. AI controlled crew members won't be able to navigate without waypoints.");
}
if (WayPoint.WayPointList.Find(wp => wp.SpawnType == SpawnType.Cargo) == null)
{
errorMsgs.Add("The submarine doesn't have spawnpoints for cargo (which are used for determining where to place bought items). "
+ "To fix this, create a new spawnpoint and change its \"spawn type\" parameter to \"cargo\".");
}
if (errorMsgs.Any())
{
new GUIMessageBox("Warning", string.Join("\n\n", errorMsgs), 400, 0);
}
foreach (MapEntity e in MapEntity.mapEntityList)
{
if (Vector2.Distance(e.Position, HiddenSubPosition) > 20000)
{
var msgBox = new GUIMessageBox(
"Warning",
"One or more structures have been placed very far from the submarine. Show the structures?",
new string[] { "Yes", "No" });
msgBox.Buttons[0].OnClicked += (btn, obj) =>
{
GameMain.EditMapScreen.Cam.Position = e.WorldPosition;
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked += msgBox.Close;
break;
}
}
}
}
}
@@ -0,0 +1,249 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
//using Microsoft.Xna.Framework.Input;
using System.Collections.ObjectModel;
using Barotrauma.Items.Components;
using FarseerPhysics.Dynamics;
namespace Barotrauma
{
partial class WayPoint : MapEntity
{
private static Texture2D iconTexture;
private const int IconSize = 32;
private static int[] iconIndices = { 3, 0, 1, 2 };
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true)
{
if (!editing && !GameMain.DebugDraw) return;
if (IsHidden()) return;
//Rectangle drawRect =
// Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height);
Vector2 drawPos = Position;
if (Submarine != null) drawPos += Submarine.DrawPosition;
drawPos.Y = -drawPos.Y;
Color clr = currentHull == null ? Color.Blue : Color.White;
if (IsSelected) clr = Color.Red;
if (isHighlighted) clr = Color.DarkRed;
int iconX = iconIndices[(int)spawnType] * IconSize % iconTexture.Width;
int iconY = (int)(Math.Floor(iconIndices[(int)spawnType] * IconSize / (float)iconTexture.Width)) * IconSize;
int iconSize = ConnectedGap == null && Ladders == null ? IconSize : (int)(IconSize * 1.5f);
spriteBatch.Draw(iconTexture,
new Rectangle((int)(drawPos.X - iconSize / 2), (int)(drawPos.Y - iconSize / 2), iconSize, iconSize),
new Rectangle(iconX, iconY, IconSize, IconSize), clr);
//GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height), clr, true);
//GUI.SmallFont.DrawString(spriteBatch, Position.ToString(), new Vector2(Position.X, -Position.Y), Color.White);
foreach (MapEntity e in linkedTo)
{
GUI.DrawLine(spriteBatch,
drawPos,
new Vector2(e.DrawPosition.X, -e.DrawPosition.Y),
Color.Green);
}
}
private bool IsHidden()
{
if (spawnType == SpawnType.Path)
{
return (!GameMain.DebugDraw && !ShowWayPoints);
}
else
{
return (!GameMain.DebugDraw && !ShowSpawnPoints);
}
}
public override void UpdateEditing(Camera cam)
{
if (editingHUD == null || editingHUD.UserData != this)
{
editingHUD = CreateEditingHUD();
}
editingHUD.Update((float)Timing.Step);
if (PlayerInput.LeftButtonClicked())
{
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
foreach (MapEntity e in mapEntityList)
{
if (e.GetType() != typeof(WayPoint)) continue;
if (e == this) continue;
if (!Submarine.RectContains(e.Rect, position)) continue;
linkedTo.Add(e);
e.linkedTo.Add(this);
}
}
}
public override void DrawEditing(SpriteBatch spriteBatch, Camera cam)
{
if (editingHUD != null) editingHUD.Draw(spriteBatch);
}
private bool ChangeSpawnType(GUIButton button, object obj)
{
GUITextBlock spawnTypeText = button.Parent as GUITextBlock;
spawnType += (int)button.UserData;
if (spawnType > SpawnType.Cargo) spawnType = SpawnType.Human;
if (spawnType < SpawnType.Human) spawnType = SpawnType.Cargo;
spawnTypeText.Text = spawnType.ToString();
return true;
}
private bool EnterIDCardTags(GUITextBox textBox, string text)
{
IdCardTags = text.Split(',');
textBox.Text = text;
textBox.Color = Color.Green;
textBox.Deselect();
return true;
}
private bool EnterAssignedJob(GUITextBox textBox, string text)
{
string trimmedName = text.ToLowerInvariant().Trim();
assignedJob = JobPrefab.List.Find(jp => jp.Name.ToLowerInvariant() == trimmedName);
if (assignedJob != null && trimmedName != "none")
{
textBox.Color = Color.Green;
textBox.Text = (assignedJob == null) ? "None" : assignedJob.Name;
}
textBox.Deselect();
return true;
}
private bool TextBoxChanged(GUITextBox textBox, string text)
{
textBox.Color = Color.Red;
return true;
}
private GUIComponent CreateEditingHUD(bool inGame = false)
{
int width = 500;
int height = spawnType == SpawnType.Path ? 100 : 140;
int x = GameMain.GraphicsWidth / 2 - width / 2, y = 10;
editingHUD = new GUIFrame(new Rectangle(x, y, width, height), Color.Black * 0.5f);
editingHUD.Padding = new Vector4(10, 10, 0, 0);
editingHUD.UserData = this;
if (spawnType == SpawnType.Path)
{
new GUITextBlock(new Rectangle(0, 0, 100, 20), "Editing waypoint", "", editingHUD);
new GUITextBlock(new Rectangle(0, 20, 100, 20), "Hold space to link to another waypoint", "", editingHUD);
}
else
{
new GUITextBlock(new Rectangle(0, 0, 100, 20), "Editing spawnpoint", "", editingHUD);
new GUITextBlock(new Rectangle(0, 25, 100, 20), "Spawn type: ", "", editingHUD);
var spawnTypeText = new GUITextBlock(new Rectangle(0, 25, 200, 20), spawnType.ToString(), "", Alignment.Right, Alignment.TopLeft, editingHUD);
var button = new GUIButton(new Rectangle(-30, 0, 20, 20), "-", Alignment.Right, "", spawnTypeText);
button.UserData = -1;
button.OnClicked = ChangeSpawnType;
button = new GUIButton(new Rectangle(0, 0, 20, 20), "+", Alignment.Right, "", spawnTypeText);
button.UserData = 1;
button.OnClicked = ChangeSpawnType;
y = 40 + 20;
new GUITextBlock(new Rectangle(0, y, 100, 20), "ID Card tags:", Color.Transparent, Color.White, Alignment.TopLeft, null, editingHUD);
GUITextBox propertyBox = new GUITextBox(new Rectangle(100, y, 200, 20), "", editingHUD);
propertyBox.Text = string.Join(", ", idCardTags);
propertyBox.OnEnterPressed = EnterIDCardTags;
propertyBox.OnTextChanged = TextBoxChanged;
propertyBox.ToolTip = "Characters spawning at this spawnpoint will have the specified tags added to their ID card. You can, for example, use these tags to limit access to some parts of the sub.";
y = y + 30;
new GUITextBlock(new Rectangle(0, y, 100, 20), "Assigned job:", Color.Transparent, Color.White, Alignment.TopLeft, null, editingHUD);
propertyBox = new GUITextBox(new Rectangle(100, y, 200, 20), "", editingHUD);
propertyBox.Text = (assignedJob == null) ? "None" : assignedJob.Name;
propertyBox.OnEnterPressed = EnterAssignedJob;
propertyBox.OnTextChanged = TextBoxChanged;
propertyBox.ToolTip = "Only characters with the specified job will spawn at this spawnpoint.";
}
//GUI.Font.DrawString(spriteBatch, "Spawnpoint: " + spawnType.ToString() + " +/-", new Vector2(x, y + 40), Color.Black);
y = y + 30;
return editingHUD;
}
public override XElement Save(XElement parentElement)
{
if (MoveWithLevel) return null;
XElement element = new XElement("WayPoint");
element.Add(new XAttribute("ID", ID),
new XAttribute("x", (int)(rect.X - Submarine.HiddenSubPosition.X)),
new XAttribute("y", (int)(rect.Y - Submarine.HiddenSubPosition.Y)),
new XAttribute("spawn", spawnType));
if (idCardTags.Length > 0)
{
element.Add(new XAttribute("idcardtags", string.Join(",", idCardTags)));
}
if (assignedJob != null) element.Add(new XAttribute("job", assignedJob.Name));
if (ConnectedGap != null) element.Add(new XAttribute("gap", ConnectedGap.ID));
if (Ladders != null) element.Add(new XAttribute("ladders", Ladders.Item.ID));
parentElement.Add(element);
if (linkedTo != null)
{
int i = 0;
foreach (MapEntity e in linkedTo)
{
element.Add(new XAttribute("linkedto" + i, e.ID));
i += 1;
}
}
return element;
}
}
}