First commit

This commit is contained in:
Regalis
2015-05-25 01:04:03 +03:00
commit fadb89ae9e
320 changed files with 32186 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
using System.Collections.Generic;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Subsurface.Networking;
namespace Subsurface
{
class Entity
{
public static Dictionary<int, Entity> dictionary = new Dictionary<int, Entity>();
private int id;
protected AITarget aiTarget;
//protected float soundRange;
//protected float sightRange;
public int ID
{
get { return id; }
set
{
dictionary.Remove(id);
//if there's already an entity with the same ID, give it the old ID of this one
Entity existingEntity;
if (dictionary.TryGetValue(value, out existingEntity))
{
dictionary.Remove(value);
dictionary.Add(id, existingEntity);
existingEntity.id = id;
}
id = value;
dictionary.Add(id, this);
}
}
public virtual Vector2 SimPosition
{
get { return Vector2.Zero; }
}
public Entity()
{
//give an unique ID
bool IDfound;
id = 0;
do
{
id += 1;
IDfound = dictionary.ContainsKey(id);
} while (IDfound);
dictionary.Add(id, this);
}
public virtual void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) { }
public virtual void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) { }
/// <summary>
/// Find an entity based on the ID
/// </summary>
public static Entity FindEntityByID(int ID)
{
Entity matchingEntity;
dictionary.TryGetValue(ID, out matchingEntity);
return matchingEntity;
}
public static void RemoveAll()
{
List<Entity> list = new List<Entity>(dictionary.Values);
foreach (Entity e in list)
{
e.Remove();
}
}
public virtual void Remove()
{
dictionary.Remove(this.ID);
}
}
}
+111
View File
@@ -0,0 +1,111 @@
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Subsurface
{
class Explosion
{
Vector2 position;
float range;
float damage;
float structureDamage;
float stun;
float force;
public Explosion(Vector2 position, float range, float damage, float structureDamage, float stun=0.0f, float force=0.0f)
{
this.position = position;
this.range = Math.Max(range,1.0f);
this.damage = damage;
this.structureDamage = structureDamage;
this.stun = stun;
this.force = force;
}
public Explosion(XElement element)
{
range = Math.Max(ToolBox.GetAttributeFloat(element, "range", 1.0f),1.0f);
damage = ToolBox.GetAttributeFloat(element, "damage", 0.0f);
structureDamage = ToolBox.GetAttributeFloat(element, "structuredamage", 0.0f);
stun = ToolBox.GetAttributeFloat(element, "stun", 0.0f);
force = ToolBox.GetAttributeFloat(element, "force", 0.0f);
}
public void Explode()
{
Explode(position);
}
public void Explode(Vector2 position)
{
for (int i = 0; i<range*10; i++)
{
Game1.particleManager.CreateParticle(position,
ToolBox.RandomFloat(0.0f,3.14f),
Vector2.Normalize(new Vector2(ToolBox.RandomFloat(-1.0f, 1.0f), ToolBox.RandomFloat(-1.0f, 1.0f))) * ToolBox.RandomFloat(3.0f,4.0f),
"explosionfire");
}
Vector2 displayPosition = ConvertUnits.ToDisplayUnits(position);
float displayRange = ConvertUnits.ToDisplayUnits(range);
if (structureDamage>0.0f)
{
List<Structure> structureList = new List<Structure>();
float dist = 600.0f;
foreach (MapEntity entity in MapEntity.mapEntityList)
{
Structure structure = entity as Structure;
if (structure == null) continue;
if (structure.HasBody &&
!structure.IsPlatform &&
Vector2.Distance(structure.Position, displayPosition) < dist*3.0f)
{
structureList.Add(structure);
}
}
foreach (Structure structure in structureList)
{
for (int i = 0; i < structure.SectionCount; i++)
{
float distFactor = 1.0f - (Vector2.Distance(structure.SectionPosition(i), displayPosition) / displayRange);
if (distFactor > 0.0f) structure.AddDamage(i, structureDamage*distFactor);
}
}
}
foreach (Character c in Character.characterList)
{
float dist = Vector2.Distance(c.SimPosition, position);
if (dist > range) continue;
float distFactor = 1.0f - dist / range;
foreach (Limb limb in c.animController.limbs)
{
distFactor = 1.0f - Vector2.Distance(limb.SimPosition, position)/range;
c.AddDamage(limb.SimPosition, damage * distFactor, 0.0f, stun * distFactor);
if (force>0.0f)
{
limb.body.ApplyLinearImpulse(Vector2.Normalize(limb.SimPosition-position)*distFactor*force);
}
}
}
}
}
}
+572
View File
@@ -0,0 +1,572 @@
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;
namespace Subsurface
{
class Gap : MapEntity
{
public bool isHorizontal;
//private Sound waterSound;
//a value between 0.0f-1.0f (0.0 = closed, 1.0f = open)
float open;
//the forces of the water flow which are exerted on physics bodies
Vector2 flowForce;
Hull flowTargetHull;
float higherSurface;
float lowerSurface;
private int soundIndex;
float soundVolume;
public float Open
{
get { return open; }
set { open = MathHelper.Clamp(value, 0.0f, 1.0f); }
}
public Vector2 FlowForce
{
get { return flowForce*soundVolume; }
}
public Hull FlowTargetHull
{
get { return flowTargetHull; }
}
public Gap(Rectangle newRect)
{
rect = newRect;
linkedTo = new ObservableCollection<MapEntity>();
//waterSound = new Sound("waterstream", 0.0f);
flowForce = Vector2.Zero;
isHorizontal = (rect.Width < rect.Height);
open = 1.0f;
FindHulls();
mapEntityList.Add(this);
}
public Gap(Rectangle newRect, bool isHorizontal)
{
rect = newRect;
linkedTo = new ObservableCollection<MapEntity>();
flowForce = Vector2.Zero;
this.isHorizontal = isHorizontal;
open = 1.0f;
FindHulls();
mapEntityList.Add(this);
}
public static void UpdateHulls()
{
foreach (MapEntity entity in mapEntityList)
{
Gap g = entity as Gap;
if (g != null) g.FindHulls();
}
}
private void FindHulls()
{
Hull hull1 = null, hull2 = null;
linkedTo.Clear();
foreach (Hull h in Hull.hullList)
{
if (!Map.RectsOverlap(h.Rect, rect)) continue;
//if the gap is inside the hull completely, ignore it
if (rect.X > h.Rect.X && rect.X + rect.Width < h.Rect.X+h.Rect.Width &&
rect.Y < h.Rect.Y && rect.Y - rect.Height > h.Rect.Y - h.Rect.Height) continue;
if (hull1 == null)
{
hull1 = h;
}
else
{
hull2 = h;
break;
}
}
if (hull1 == null && hull2 == null) return;
if (hull1 != null && hull2 != null)
{
if (isHorizontal)
{
//make sure that water1 is the lefthand room
//or that water2 is null if the gap doesn't lead to another room
if (hull1.Rect.X < hull2.Rect.X)
{
linkedTo.Add(hull1);
linkedTo.Add(hull2);
}
else
{
linkedTo.Add(hull2);
linkedTo.Add(hull1);
}
}
else
{
//make sure that water1 is the room on the top
//or that water2 is null if the gap doesn't lead to another room
if (hull1.Rect.Y > hull2.Rect.Y)
{
linkedTo.Add(hull1);
linkedTo.Add(hull2);
}
else
{
linkedTo.Add(hull2);
linkedTo.Add(hull1);
}
}
}
else
{
linkedTo.Add(hull1);
}
}
public override void Draw(SpriteBatch sb, bool editing)
{
//if (linkedTo[0] != null)
// GUI.DrawLine(sb, new Vector2(Position.X, Position.Y),
// new Vector2(linkedTo[0].Position.X, linkedTo[0].Position.Y), Color.Blue);
//if (linkedTo.Count > 1 && linkedTo[1] != null)
// GUI.DrawLine(sb, new Vector2(Position.X, Position.Y),
// new Vector2(linkedTo[1].Position.X, linkedTo[1].Position.Y), Color.Blue);
//GUI.DrawLine(sb, new Vector2(Position.X, -Position.Y), new Vector2(Position.X, -Position.Y)+new Vector2(flowForce.X, -flowForce.Y), Color.LightBlue);
if (!editing) return;
Color clr = (open == 0.0f) ? Color.Red : Color.Cyan;
GUI.DrawRectangle(sb, new Rectangle(rect.X, -rect.Y, rect.Width, rect.Height), clr);
if (isSelected && editing)
{
GUI.DrawRectangle(sb,
new Vector2(rect.X - 5, -rect.Y - 5),
new Vector2(rect.Width + 10, rect.Height + 10),
Color.Red);
}
//HUD.DrawLine(sb, new Vector2(position.X, -position.Y),
// isHorizontal ? new Vector2(position.X, -position.Y + size) : new Vector2(position.X + size, -position.Y),
// clr);
}
public override void Update(Camera cam, float deltaTime)
{
soundVolume = soundVolume + ((flowForce.Length() < 100.0f) ? -deltaTime * 0.5f : deltaTime * 0.5f);
soundVolume = MathHelper.Clamp(soundVolume, 0.0f, 1.0f);
//if (soundVolume < 0.01f)
//{
// if (soundIndex > -1)
// {
// Sound.Stop(soundIndex);
// soundIndex = -1;
// }
//}
//else
{
int index = (int)Math.Floor(flowForce.Length() / 100.0f);
index = Math.Min(index,2);
soundIndex = AmbientSoundManager.flowSounds[index].Loop(soundIndex, soundVolume, Position, 2000.0f);
//soundVolume = Math.Max(0.0f, soundVolume-deltaTime);
//Sound.UpdatePosition(soundIndex, Position, 2000.0f);
}
flowForce = Vector2.Zero;
if (open == 0.0f) return;
UpdateOxygen();
if (linkedTo.Count == 1)
{
//gap leading from a room to outside
UpdateRoomToOut(deltaTime);
}
else
{
//gap leading from a room to another
UpdateRoomToRoom(deltaTime);
}
if (FlowForce.Length() > 150.0f && flowTargetHull!=null && flowTargetHull.Volume < flowTargetHull.FullVolume)
{
//UpdateFlowForce();
Vector2 pos = SimPosition;
if (isHorizontal)
{
pos.Y = ConvertUnits.ToSimUnits(MathHelper.Clamp(lowerSurface, rect.Y-rect.Height, rect.Y));
Game1.particleManager.CreateParticle(new Vector2(pos.X, pos.Y - ToolBox.RandomFloat(0.0f, 0.1f)),
0.0f, new Vector2(flowForce.X * ToolBox.RandomFloat(0.005f, 0.007f), flowForce.Y * ToolBox.RandomFloat(0.005f, 0.007f)), "watersplash");
pos.Y = ConvertUnits.ToSimUnits(ToolBox.RandomFloat(lowerSurface, rect.Y - rect.Height));
Game1.particleManager.CreateParticle(pos, 0.0f, flowForce / 200.0f, "bubbles");
}
else
{
pos.Y += Math.Sign(flowForce.Y) * ConvertUnits.ToSimUnits(rect.Height / 2.0f);
for (int i = 0; i < rect.Width; i += (int)ToolBox.RandomFloat(20, 50))
{
pos.X = ConvertUnits.ToSimUnits(ToolBox.RandomFloat(rect.X, rect.X+rect.Width));
Game1.particleManager.CreateParticle(pos,
0.0f, new Vector2(flowForce.X * ToolBox.RandomFloat(0.005f, 0.008f), flowForce.Y * ToolBox.RandomFloat(0.005f, 0.008f)), "watersplash");
Game1.particleManager.CreateParticle(pos, ToolBox.VectorToAngle(flowForce), flowForce / 200.0f, "bubbles");
}
}
}
}
void UpdateRoomToRoom(float deltaTime)
{
if (linkedTo.Count < 2) return;
Hull hull1 = (Hull)linkedTo[0];
Hull hull2 = (Hull)linkedTo[1];
if (hull1.Volume == 0.0 && hull2.Volume == 0.0) return;
float size = (isHorizontal) ? rect.Height : rect.Width;
//a variable affecting the water flow through the gap
//the larger the gap is, the faster the water flows
float sizeModifier = size / 100.0f * open;
//horizontal gap (such as a regular door)
if (isHorizontal)
{
//higherSurface = Math.Min(hull1.Surface,hull2.Surface);
float delta=0.0f;
//water level is above the lower boundary of the gap
if (Math.Max(hull1.Surface+hull1.WaveY[hull1.WaveY.Length - 1], hull2.Surface+hull2.WaveY[0]) > rect.Y - size)
{
int dir = (hull1.Pressure > hull2.Pressure) ? 1 : -1;
//water flowing from the righthand room to the lefthand room
if (dir == -1)
{
if (!(hull2.Volume > 0.0f)) return;
lowerSurface = hull1.Surface - hull1.WaveY[hull1.WaveY.Length - 1];
//delta = Math.Min((room2.water.pressure - room1.water.pressure) * sizeModifier, Math.Min(room2.water.Volume, room2.Volume));
//delta = Math.Min(delta, room1.Volume - room1.water.Volume + Water.MaxCompress);
flowTargetHull = hull1;
//make sure not to move more than what the room contains
delta = Math.Min((hull2.Pressure - hull1.Pressure) * sizeModifier, Math.Min(hull2.Volume, hull2.FullVolume));
//make sure not to place more water to the target room than it can hold
delta = Math.Min(delta, hull1.FullVolume + Hull.MaxCompress - (hull1.Volume));
hull1.Volume += delta;
hull2.Volume -= delta;
if (hull1.Volume > hull1.FullVolume)
hull1.Pressure = Math.Max(hull1.Pressure, (hull1.Pressure + hull2.Pressure) / 2);
flowForce = new Vector2(-delta, 0.0f);
}
else if (dir == 1)
{
if (!(hull1.Volume > 0.0f)) return;
lowerSurface = hull2.Surface - hull2.WaveY[1];
flowTargetHull = hull2;
//make sure not to move more than what the room contains
delta = Math.Min((hull1.Pressure - hull2.Pressure) * sizeModifier, Math.Min(hull1.Volume, hull1.FullVolume));
//make sure not to place more water to the target room than it can hold
delta = Math.Min(delta, hull2.FullVolume + Hull.MaxCompress - (hull2.Volume));
hull1.Volume -= delta;
hull2.Volume += delta;
if (hull2.Volume > hull2.FullVolume)
hull2.Pressure = Math.Max(hull2.Pressure, (hull1.Pressure + hull2.Pressure) / 2);
flowForce = new Vector2(delta, 0.0f);
}
if (delta>100.0f)
{
float avg = (hull1.Surface + hull2.Surface) / 2.0f;
//float avgVel = (hull2.WaveVel[1] + hull1.WaveVel[hull1.WaveY.Length - 2]) / 2.0f;
if (hull1.Volume < hull1.FullVolume - Hull.MaxCompress &&
hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1] < rect.Y)
{
hull1.WaveVel[hull1.WaveY.Length - 1] = (avg-(hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1]))*0.1f;
hull1.WaveVel[hull1.WaveY.Length - 2] = hull1.WaveVel[hull1.WaveY.Length - 1];
}
if (hull2.Volume < hull2.FullVolume - Hull.MaxCompress &&
hull2.Surface + hull2.WaveY[0] < rect.Y)
{
hull2.WaveVel[0] = (avg - (hull2.Surface + hull2.WaveY[0])) * 0.1f;
hull2.WaveVel[1] = hull2.WaveVel[0];
}
}
}
}
else
{
//lower room is full of water
if (hull2.Pressure > hull1.Pressure)
{
float delta = Math.Min(hull2.Volume - hull2.FullVolume + Hull.MaxCompress / 2.0f, deltaTime * 5000f * sizeModifier);
delta = Math.Max(delta, 0.0f);
hull1.Volume += delta;
hull2.Volume -= delta;
flowTargetHull = hull1;
//delta = (water2.Pressure - water1.Pressure) * 0.1f;
//if (delta > 0.1f)
//{
// int posX = (int)((rect.X + size / 2.0f - water1.Rect.X) / Hull.WaveWidth);
// //water1.WaveY[posX] = delta;
// water1.WaveVel[posX] = delta * 0.01f;
//}
if (hull1.Volume > hull1.FullVolume)
{
hull1.Pressure = Math.Max(hull1.Pressure, (hull1.Pressure + hull2.Pressure) / 2);
}
flowForce = new Vector2(0.0f, delta);
}
//there's water in the upper room, drop to lower
else if (hull1.Volume > 0)
{
flowTargetHull = hull2;
//make sure the amount of water moved isn't more than what the room contains
float delta = Math.Min(hull1.Volume, deltaTime * 10000f * sizeModifier);
//make sure not to place more water to the target room than it can hold
delta = Math.Min(delta, (hull2.FullVolume + Math.Max(hull1.Volume - hull1.FullVolume, 0.0f)) - hull2.Volume + Hull.MaxCompress / 4.0f);
hull1.Volume -= delta;
hull2.Volume += delta;
if (hull2.Volume > hull2.FullVolume)
{
hull2.Pressure = Math.Max(hull2.Pressure, (hull1.Pressure + hull2.Pressure) / 2);
}
flowForce = new Vector2(0.0f,-delta);
//if (water2.Volume < water2.FullVolume - Hull.MaxCompress)
//{
// int posX = (int)((rect.X + size / 2.0f - water1.Rect.X) / Hull.WaveWidth);
// //water1.WaveY[posX] = -delta;
// if (posX > -1 && posX < water2.WaveVel.Length)
// water1.WaveVel[posX] = -delta * 0.01f;
// posX = (int)((rect.X + size / 2.0f - water2.Rect.X) / Hull.WaveWidth);
// //water2.WaveY[posX] = delta;
// if (posX > -1 && posX<water2.WaveVel.Length)
// water2.WaveVel[posX] = delta * 0.01f;
//}
}
}
if (open > 0.0f)
{
if (hull1.Volume>hull1.FullVolume && hull2.Volume>hull2.FullVolume)
{
float avgLethality = (hull1.LethalPressure + hull2.LethalPressure) / 2.0f;
hull1.LethalPressure = avgLethality;
hull2.LethalPressure = avgLethality;
}
else
{
hull1.LethalPressure = 0.0f;
hull2.LethalPressure = 0.0f;
}
}
}
void UpdateRoomToOut(float deltaTime)
{
if (linkedTo.Count != 1) return;
float size = (isHorizontal) ? rect.Height : rect.Width;
Hull hull1 = (Hull)linkedTo[0];
//a variable affecting the water flow through the gap
//the larger the gap is, the faster the water flows
float sizeModifier = size * open;
float delta = Hull.MaxCompress * sizeModifier * deltaTime;
//make sure not to place more water to the target room than it can hold
delta = Math.Min(delta, hull1.FullVolume + Hull.MaxCompress - hull1.Volume);
hull1.Volume += delta;
if (hull1.Volume > hull1.FullVolume) hull1.Pressure += 0.5f;
flowTargetHull = hull1;
if (isHorizontal)
{
//water flowing from right to left
if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f)
{
flowForce = new Vector2(-delta, 0.0f);
}
else
{
flowForce = new Vector2(delta, 0.0f);
}
higherSurface = hull1.Surface;
lowerSurface = rect.Y;
if (hull1.Volume < hull1.FullVolume - Hull.MaxCompress &&
hull1.Surface > -rect.Y)
{
float vel = (rect.Y + hull1.Surface) * 0.03f;
if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f)
{
hull1.WaveVel[hull1.WaveY.Length - 1] += vel;
hull1.WaveVel[hull1.WaveY.Length - 2] += vel;
}
else
{
hull1.WaveVel[0] += vel;
hull1.WaveVel[1] += vel;
}
}
}
else
{
if (rect.Y > hull1.Rect.Y - hull1.Rect.Height / 2.0f)
{
flowForce = new Vector2(0.0f, -delta);
}
else
{
flowForce = new Vector2(0.0f,delta);
}
}
}
private void UpdateOxygen()
{
if (linkedTo.Count < 2) return;
Hull hull1 = (Hull)linkedTo[0];
Hull hull2 = (Hull)linkedTo[1];
float totalOxygen = hull1.Oxygen + hull2.Oxygen;
float totalVolume = (hull1.FullVolume + hull2.FullVolume);
hull1.Oxygen += Math.Sign(totalOxygen*hull1.FullVolume/(totalVolume) - hull1.Oxygen) * Hull.OxygenDistributionSpeed;
hull2.Oxygen += Math.Sign(totalOxygen*hull2.FullVolume/(totalVolume) - hull2.Oxygen) * Hull.OxygenDistributionSpeed;
}
public override void Remove()
{
base.Remove();
if (soundIndex > -1) Sounds.SoundManager.Stop(soundIndex);
}
public override XElement Save(XDocument doc)
{
XElement element = new XElement("Gap");
element.Add(new XAttribute("ID", ID),
new XAttribute("x", rect.X),
new XAttribute("y", rect.Y),
new XAttribute("width", rect.Width),
new XAttribute("height", 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;
// }
//}
doc.Root.Add(element);
return element;
}
public static void Load(XElement element)
{
Rectangle rect = new Rectangle(
int.Parse(element.Attribute("x").Value),
int.Parse(element.Attribute("y").Value),
int.Parse(element.Attribute("width").Value),
int.Parse(element.Attribute("height").Value));
Gap g = new Gap(rect);
g.ID = int.Parse(element.Attribute("ID").Value);
g.linkedToID = new List<int>();
//int i = 0;
//while (element.Attribute("linkedto" + i) != null)
//{
// g.linkedToID.Add(int.Parse(element.Attribute("linkedto" + i).Value));
// i += 1;
//}
}
}
}
+436
View File
@@ -0,0 +1,436 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Subsurface
{
class Hull : MapEntity
{
public static List<Hull> hullList = new List<Hull>();
public static bool EditWater;
public static WaterRenderer renderer;
public static bool DebugDraw;
public const float OxygenDistributionSpeed = 500.0f;
public const float OxygenDetoriationSpeed = 0.3f;
public const float OxygenConsumptionSpeed = 1000.0f;
public const int WaveWidth = 16;
const float WaveStiffness = 0.003f;
const float WaveSpread = 0.05f;
const float WaveDampening = 0.01f;
//how much excess water the room can contain (= more than the volume of the room)
public const float MaxCompress = 10000f;
public readonly Dictionary<string, PropertyDescriptor> properties;
float lethalPressure;
float surface;
float volume;
float pressure;
float oxygen;
bool update;
float[] waveY; //displacement from the surface of the water
float[] waveVel; //velocity of the point
float[] leftDelta;
float[] rightDelta;
public override bool IsLinkable
{
get { return true; }
}
public float LethalPressure
{
get { return lethalPressure; }
set { lethalPressure = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public Vector2 Size
{
get { return new Vector2(rect.Width, rect.Height); }
}
public float Surface
{
get { return surface; }
}
public float Volume
{
get { return volume; }
set
{
volume = MathHelper.Clamp(value, 0.0f, FullVolume + MaxCompress);
if (volume < FullVolume) Pressure = rect.Y - rect.Height + volume / rect.Width;
if (volume > 0.0f) update = true;
}
}
public float Oxygen
{
get { return oxygen; }
set { oxygen = MathHelper.Clamp(value, 0.0f, FullVolume); }
}
public float OxygenPercentage
{
get { return oxygen / FullVolume * 100.0f; }
set { Oxygen = (value / 100.0f) * FullVolume; }
}
public float FullVolume
{
get { return (rect.Width * rect.Height); }
}
public float Pressure
{
get { return pressure; }
set { pressure = value; }
}
public float[] WaveY
{
get { return waveY; }
}
public float[] WaveVel
{
get { return waveVel; }
}
public Hull(Rectangle rectangle)
{
rect = rectangle;
OxygenPercentage = (float)(Game1.random.NextDouble() * 100.0);
properties = TypeDescriptor.GetProperties(this.GetType())
.Cast<PropertyDescriptor>()
.ToDictionary(pr => pr.Name);
int arraySize = (rectangle.Width / WaveWidth + 1);
waveY = new float[arraySize];
waveVel = new float[arraySize];
leftDelta = new float[arraySize];
rightDelta = new float[arraySize];
surface = rect.Y - rect.Height;
aiTarget = new AITarget(this);
aiTarget.SightRange = (rect.Width + rect.Height)*10.0f;
hullList.Add(this);
Item.UpdateHulls();
Gap.UpdateHulls();
Volume = 0.0f;
//add to list of entities as well
mapEntityList.Add(this);
}
public override bool Contains(Vector2 position)
{
return (Map.RectContains(rect, position) &&
!Map.RectContains(new Rectangle(rect.X + 8, rect.Y - 8, rect.Width -16, rect.Height -16), position));
}
public int GetWaveIndex(Vector2 position)
{
int index = (int)(position.X - rect.X) / WaveWidth;
index = MathHelper.Clamp(index, 0, waveY.Length-1);
return index;
}
public override void Move(Vector2 amount)
{
rect.X += (int)amount.X;
rect.Y += (int)amount.Y;
Item.UpdateHulls();
Gap.UpdateHulls();
}
public override void Remove()
{
base.Remove();
Item.UpdateHulls();
Gap.UpdateHulls();
//renderer.Dispose();
hullList.Remove(this);
}
public override void Update(Camera cam, float deltaTime)
{
Oxygen -= OxygenDetoriationSpeed * deltaTime;
if (EditWater)
{
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
if (Map.RectContains(rect, position))
{
if (PlayerInput.LeftButtonDown())
{
waveY[(int)(position.X - rect.X) / Hull.WaveWidth] = 100.0f;
Volume = Volume + 1500.0f;
}
else if (PlayerInput.GetMouseState.RightButton == Microsoft.Xna.Framework.Input.ButtonState.Pressed)
{
Volume = Volume - 1500.0f;
}
}
}
if (!update) return;
float surfaceY = rect.Y - rect.Height + Volume / rect.Width;
for (int i = 0; i < waveY.Length; i++)
{
float maxDelta = Math.Max(Math.Abs(rightDelta[i]), Math.Abs(leftDelta[i]));
if (maxDelta > ToolBox.RandomFloat(0.2f,10.0f))
{
Game1.particleManager.CreateParticle(ConvertUnits.ToSimUnits(new Vector2(rect.X + WaveWidth * i,surface + waveY[i])),
0.0f, new Vector2(0.0f, waveVel[i]/10.0f-1.0f), "mist");
}
waveY[i] = waveY[i] + waveVel[i];
if (surfaceY + waveY[i] > rect.Y)
{
waveY[i] -= (surfaceY + waveY[i]) - rect.Y;
waveVel[i] = waveVel[i] * -0.5f;
}
else if (surfaceY + waveY[i] < rect.Y - rect.Height)
{
waveY[i] -= (surfaceY + waveY[i]) - (rect.Y - rect.Height);
waveVel[i] = waveVel[i] * -0.5f;
}
//acceleration
float a = -WaveStiffness * waveY[i] - waveVel[i] * WaveDampening;
waveVel[i] = waveVel[i] + a;
}
for (int j = 0; j < 2; j++)
{
for (int i = 1; i < waveY.Length - 1; i++)
{
leftDelta[i] = WaveSpread * (waveY[i] - waveY[i - 1]);
waveVel[i - 1] = waveVel[i - 1] + leftDelta[i];
rightDelta[i] = WaveSpread * (waveY[i] - waveY[i + 1]);
waveVel[i + 1] = waveVel[i + 1] + rightDelta[i];
}
for (int i = 1; i < waveY.Length - 1; i++)
{
waveY[i - 1] = waveY[i - 1] + leftDelta[i];
waveY[i + 1] = waveY[i + 1] + rightDelta[i];
}
}
if (volume<FullVolume)
{
LethalPressure -= 0.5f;
if (Volume == 0.0f)
{
for (int i = 1; i < waveY.Length - 1; i++)
{
if (waveY[i] > 0.1f) return;
}
update = false;
}
}
else
{
LethalPressure += 1.0f;
}
}
public override void Draw(SpriteBatch spriteBatch, bool editing)
{
if (!editing && !DebugDraw) return;
GUI.DrawRectangle(spriteBatch,
new Vector2(rect.X, -rect.Y),
new Vector2(rect.Width, rect.Height),
isHighlighted ? Color.Green : Color.Blue);
GUI.DrawRectangle(spriteBatch,
new Rectangle(rect.X, -rect.Y, rect.Width, rect.Height),
Color.Red*((100.0f-OxygenPercentage)/400.0f), true);
spriteBatch.DrawString(GUI.font, "Pressure: " + ((int)pressure - rect.Y).ToString() +
" - Lethality: " + lethalPressure +
" - Oxygen: "+((int)OxygenPercentage).ToString(), new Vector2(rect.X+10, -rect.Y+10), Color.Black);
spriteBatch.DrawString(GUI.font, volume.ToString() +" / "+ FullVolume.ToString(), new Vector2(rect.X+10, -rect.Y+30), Color.Black);
if (isSelected && editing)
{
GUI.DrawRectangle(spriteBatch,
new Vector2(rect.X - 5, -rect.Y - 5),
new Vector2(rect.Width + 10, rect.Height + 10),
Color.Red);
}
}
public void Render(GraphicsDevice graphicsDevice, Camera cam)
{
if (renderer.positionInBuffer > renderer.vertices.Length - 6) return;
//calculate where the surface should be based on the water volume
float top = rect.Y;
float bottom = rect.Y - rect.Height;
float surfaceY = bottom + Volume / rect.Width;
//interpolate the position of the rendered surface towards the "target surface"
surface = surface + (surfaceY - surface) / 10.0f;
Matrix transform =
cam.Transform
* Matrix.CreateOrthographic(Game1.GraphicsWidth, Game1.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 p1 = new Vector3(rect.X, top, 0.0f);
Vector3 p2 = new Vector3(rect.X + rect.Width, top, 0.0f);
Vector3 p3 = new Vector3(p2.X, bottom, 0.0f);
Vector3 p4 = new Vector3(p1.X, bottom, 0.0f);
renderer.vertices[renderer.positionInBuffer] = new WaterVertex(p1, new Vector2(p1.X, -p1.Y), transform);
renderer.vertices[renderer.positionInBuffer + 1] = new WaterVertex(p2, new Vector2(p2.X, -p2.Y), transform);
renderer.vertices[renderer.positionInBuffer + 2] = new WaterVertex(p3, new Vector2(p3.X, -p3.Y), transform);
renderer.vertices[renderer.positionInBuffer + 3] = new WaterVertex(p1, new Vector2(p1.X, -p1.Y), transform);
renderer.vertices[renderer.positionInBuffer + 4] = new WaterVertex(p3, new Vector2(p3.X, -p3.Y), transform);
renderer.vertices[renderer.positionInBuffer + 5] = new WaterVertex(p4, new Vector2(p4.X, -p4.Y), transform);
renderer.positionInBuffer += 6;
return;
}
int x = rect.X;
int start = (int)Math.Floor((float)(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 p1 = new Vector3(x, top, 0.0f);
Vector3 p4 = new Vector3(p1.X, surface + 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++;
}
Vector3 p2 = new Vector3(x + width, top, 0.0f);
Vector3 p3 = new Vector3(p2.X, surface + waveY[i+1], 0.0f);
renderer.vertices[renderer.positionInBuffer] = new WaterVertex(p1, new Vector2(p1.X, -p1.Y), transform);
renderer.vertices[renderer.positionInBuffer + 1] = new WaterVertex(p2, new Vector2(p2.X, -p2.Y), transform);
renderer.vertices[renderer.positionInBuffer + 2] = new WaterVertex(p3, new Vector2(p3.X, -p3.Y), transform);
renderer.vertices[renderer.positionInBuffer + 3] = new WaterVertex(p1, new Vector2(p1.X, -p1.Y), transform);
renderer.vertices[renderer.positionInBuffer + 4] = new WaterVertex(p3, new Vector2(p3.X, -p3.Y), transform);
renderer.vertices[renderer.positionInBuffer + 5] = new WaterVertex(p4, new Vector2(p4.X, -p4.Y), transform);
renderer.positionInBuffer += 6;
x += width;
}
}
//returns the water block which contains the point (or null if it isn't inside any)
public static Hull FindHull(Vector2 position, Hull guess = null)
{
if (guess != null && hullList.Contains(guess))
{
if (Map.RectContains(guess.rect, position)) return guess;
}
foreach (Hull w in hullList)
{
if (Map.RectContains(w.rect, position)) return w;
}
return null;
}
public override XElement Save(XDocument doc)
{
XElement element = new XElement("Hull");
element.Add(new XAttribute("ID", ID),
new XAttribute("x", rect.X),
new XAttribute("y", rect.Y),
new XAttribute("width", rect.Width),
new XAttribute("height", rect.Height),
new XAttribute("water", volume));
doc.Root.Add(element);
return element;
}
public static void Load(XElement element)
{
Rectangle rect = new Rectangle(
int.Parse(element.Attribute("x").Value),
int.Parse(element.Attribute("y").Value),
int.Parse(element.Attribute("width").Value),
int.Parse(element.Attribute("height").Value));
Hull h = new Hull(rect);
h.volume = ToolBox.GetAttributeFloat(element, "pressure", 0.0f);
h.ID = int.Parse(element.Attribute("ID").Value);
}
}
}
+20
View File
@@ -0,0 +1,20 @@
using Microsoft.Xna.Framework;
namespace Subsurface
{
interface IDamageable
{
//float Damage
//{
// get;
// set;
//}
Vector2 SimPosition
{
get;
}
void AddDamage(Vector2 position, float amount, float bleedingAmount, float stun);
}
}
+191
View File
@@ -0,0 +1,191 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Subsurface.Lights
{
class ConvexHull
{
public static List<ConvexHull> list = new List<ConvexHull>();
static BasicEffect drawingEffect;
private VertexPositionColor[] vertices;
private short[] indices;
int primitiveCount;
bool[] backFacing;
VertexPositionColor[] shadowVertices;
public bool Enabled
{
get;
set;
}
//private Vector2 position = Vector2.Zero;
//public Vector2 Position
//{
// get { return position; }
// set { position = value; }
//}
public ConvexHull(Vector2[] points, Color color)
{
int vertexCount = points.Length;
vertices = new VertexPositionColor[vertexCount + 1];
Vector2 center = Vector2.Zero;
for (int i = 0; i < vertexCount; i++)
{
vertices[i] = new VertexPositionColor(new Vector3(points[i], 0), color);
center += points[i];
}
center /= points.Length;
vertices[vertexCount] = new VertexPositionColor(new Vector3(center, 0), color);
primitiveCount = points.Length;
indices = new short[primitiveCount * 3];
for (int i = 0; i < primitiveCount; i++)
{
indices[3 * i] = (short)i;
indices[3 * i + 1] = (short)((i + 1) % vertexCount);
indices[3 * i + 2] = (short)vertexCount;
}
backFacing = new bool[vertexCount];
Enabled = true;
list.Add(this);
}
public void Move(Vector2 amount)
{
for (int i = 0; i < vertices.Count(); i++)
{
vertices[i].Position = new Vector3(vertices[i].Position.X + amount.X, vertices[i].Position.Y + amount.Y, vertices[i].Position.Z);
}
}
public void SetVertices(Vector2[] points)
{
int vertexCount = points.Length;
vertices = new VertexPositionColor[vertexCount + 1];
for (int i = 0; i < vertexCount; i++)
{
vertices[i] = new VertexPositionColor(new Vector3(points[i], 0), Color.Black);
}
}
//public void Draw(GameTime gameTime)
//{
// device.RasterizerState = RasterizerState.CullNone;
// device.BlendState = BlendState.Opaque;
// drawingEffect.World = Matrix.CreateTranslation(position.X, position.Y, 0);
// foreach (EffectPass pass in drawingEffect.CurrentTechnique.Passes)
// {
// pass.Apply();
// device.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, primitiveCount);
// }
//}
public void DrawShadows(GraphicsDevice graphicsDevice, Camera cam, Vector2 lightSourcePos)
{
if (!Enabled) return;
if (drawingEffect == null)
{
drawingEffect = new BasicEffect(graphicsDevice);
drawingEffect.VertexColorEnabled = true;
}
//compute facing of each edge, using N*L
for (int i = 0; i < primitiveCount; i++)
{
Vector2 firstVertex = new Vector2(vertices[i].Position.X, vertices[i].Position.Y);
int secondIndex = (i + 1) % primitiveCount;
Vector2 secondVertex = new Vector2(vertices[secondIndex].Position.X, vertices[secondIndex].Position.Y);
Vector2 middle = (firstVertex + secondVertex) / 2;
Vector2 L = lightSourcePos - middle;
Vector2 N = new Vector2();
N.X = -(secondVertex.Y - firstVertex.Y);
N.Y = secondVertex.X - firstVertex.X;
backFacing[i] = (Vector2.Dot(N, L) < 0);
}
//find beginning and ending vertices which
//belong to the shadow
int startingIndex = 0;
int endingIndex = 0;
for (int i = 0; i < primitiveCount; i++)
{
int currentEdge = i;
int nextEdge = (i + 1) % primitiveCount;
if (backFacing[currentEdge] && !backFacing[nextEdge])
endingIndex = nextEdge;
if (!backFacing[currentEdge] && backFacing[nextEdge])
startingIndex = nextEdge;
}
int shadowVertexCount;
//nr of vertices that are in the shadow
if (endingIndex > startingIndex)
shadowVertexCount = endingIndex - startingIndex + 1;
else
shadowVertexCount = primitiveCount + 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 = vertices[currentIndex].Position;
//one vertex on the hull
shadowVertices[svCount] = new VertexPositionColor();
shadowVertices[svCount].Color = Color.Black;
shadowVertices[svCount].Position = vertexPos;
//one extruded by the light direction
shadowVertices[svCount + 1] = new VertexPositionColor();
shadowVertices[svCount + 1].Color = Color.Black;
Vector3 L2P = vertexPos - new Vector3(lightSourcePos, 0);
L2P.Normalize();
shadowVertices[svCount + 1].Position = new Vector3(lightSourcePos, 0) + L2P * 9000;
svCount += 2;
currentIndex = (currentIndex + 1) % primitiveCount;
}
drawingEffect.World = cam.ShaderTransform
* Matrix.CreateOrthographic(Game1.GraphicsWidth, Game1.GraphicsHeight, -1, 1) * 0.5f;
drawingEffect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleStrip, shadowVertices, 0, shadowVertexCount * 2 - 2);
}
public void Remove()
{
list.Remove(this);
}
}
}
+26
View File
@@ -0,0 +1,26 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Subsurface.Lights
{
class LightManager
{
public static Vector2 viewPos;
public static bool fowEnabled = true;
public static void DrawFow(GraphicsDevice graphics, Camera cam)
{
if (!fowEnabled) return;
foreach (ConvexHull convexHull in ConvexHull.list)
{
convexHull.DrawShadows(graphics, cam, viewPos);
}
}
}
}
+381
View File
@@ -0,0 +1,381 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using FarseerPhysics.Collision;
namespace Subsurface
{
public enum Direction : byte
{
None = 0, Left = 1, Right = 2
}
static class Map
{
public static Vector2 gridSize = new Vector2(16.0f, 16.0f);
private static Vector2 lastPickedPosition;
private static float lastPickedFraction;
private static Rectangle borders;
private static string filePath;
public static Vector2 LastPickedPosition
{
get { return lastPickedPosition; }
}
public static float LastPickedFraction
{
get { return lastPickedFraction; }
}
public static Rectangle Borders
{
get { return borders; }
}
public static string FilePath
{
get { return filePath; }
}
public static void Draw(SpriteBatch spriteBatch, bool editing = false)
{
for (int i = 0; i < MapEntity.mapEntityList.Count(); i++ )
{
MapEntity.mapEntityList[i].Draw(spriteBatch, editing);
}
}
public static void DrawFront(SpriteBatch spriteBatch, bool editing = false)
{
for (int i = 0; i < MapEntity.mapEntityList.Count(); i++)
{
if (MapEntity.mapEntityList[i].sprite == null || MapEntity.mapEntityList[i].sprite.Depth < 0.5f)
MapEntity.mapEntityList[i].Draw(spriteBatch, editing);
}
}
public static void DrawBack(SpriteBatch spriteBatch, bool editing = false)
{
for (int i = 0; i < MapEntity.mapEntityList.Count(); i++)
{
if (MapEntity.mapEntityList[i].sprite == null || MapEntity.mapEntityList[i].sprite.Depth >= 0.5f)
MapEntity.mapEntityList[i].Draw(spriteBatch, editing);
}
}
public static Vector2 MouseToWorldGrid(Camera cam)
{
Vector2 position = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y);
position = cam.ScreenToWorld(position);
return VectorToWorldGrid(position);
}
public static Vector2 VectorToWorldGrid(Vector2 position)
{
position.X = (float)Math.Floor(Convert.ToDouble(position.X / gridSize.X)) * gridSize.X;
position.Y = (float)Math.Ceiling(Convert.ToDouble(position.Y / gridSize.Y)) * gridSize.Y;
return position;
}
public static Rectangle AbsRect(Vector2 pos, Vector2 size)
{
if (size.X < 0.0f)
{
pos.X += size.X;
size.X = -size.X;
}
if (size.Y < 0.0f)
{
pos.Y -= size.Y;
size.Y = -size.Y;
}
return new Rectangle((int)pos.X, (int)pos.Y, (int)size.X, (int)size.Y);
}
public static bool RectContains(Rectangle rect, Vector2 pos)
{
return (pos.X > rect.X && pos.X < rect.X + rect.Width
&& pos.Y < rect.Y && pos.Y > rect.Y - rect.Height);
}
public static bool RectsOverlap(Rectangle rect1, Rectangle rect2)
{
return !(rect1.X > rect2.X + rect2.Width || rect1.X + rect1.Width < rect2.X ||
rect1.Y < rect2.Y - rect2.Height || rect1.Y - rect1.Height > rect2.Y);
}
public static Body PickBody(Vector2 rayStart, Vector2 rayEnd, List<Body> ignoredBodies = null)
{
float closestFraction = 1.0f;
Body closestBody = null;
Game1.world.RayCast((fixture, point, normal, fraction) =>
{
if (fixture == null || fixture.CollisionCategories == Category.None) return -1;
if (ignoredBodies != null && ignoredBodies.Contains(fixture.Body)) return -1;
Structure structure = fixture.Body.UserData as Structure;
if (structure != null && (structure.IsPlatform || !structure.HasBody)) return -1;
if (fraction < closestFraction)
{
closestFraction = fraction;
if (fixture.Body!=null) closestBody = fixture.Body;
}
return fraction;
}
, rayStart, rayEnd);
lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction;
lastPickedFraction = closestFraction;
return closestBody;
}
public static Structure CheckVisibility(Vector2 rayStart, Vector2 rayEnd)
{
Structure closestStructure = null;
float closestFraction = 1.0f;
if (Vector2.Distance(rayStart,rayEnd)<0.01f)
{
closestFraction = 0.01f;
return null;
}
Game1.world.RayCast((fixture, point, normal, fraction) =>
{
if (fixture == null || fixture.CollisionCategories != Physics.CollisionWall) return -1;
Structure structure = fixture.Body.UserData as Structure;
if (structure != null)
{
if (structure.IsPlatform || structure.StairDirection != Direction.None) return -1;
int sectionIndex = structure.FindSectionIndex(ConvertUnits.ToDisplayUnits(point));
if (sectionIndex > -1 && structure.SectionHasHole(sectionIndex)) return -1;
}
if (fraction < closestFraction)
{
if (structure != null) closestStructure = structure;
closestFraction = fraction;
}
return closestFraction;
}
, rayStart, rayEnd);
lastPickedPosition = rayStart + (rayEnd - rayStart) * closestFraction;
lastPickedFraction = closestFraction;
return closestStructure;
}
public static Body PickBody(Vector2 point)
{
Body foundBody = null;
AABB aabb = new AABB(point, point);
Game1.world.QueryAABB(p =>
{
foundBody = p.Body;
return true;
}, ref aabb);
return foundBody;
}
public static bool InsideWall(Vector2 point)
{
Body foundBody = Map.PickBody(point);
if (foundBody==null) return false;
Structure wall = foundBody.UserData as Structure;
if (wall == null || wall.IsPlatform) return false;
return true;
}
public static void Save(string filePath, string fileName)
{
if (fileName==null)
{
DebugConsole.ThrowError("No save file selected");
return;
}
XDocument doc = new XDocument(
new XElement((XName)fileName));
foreach (MapEntity e in MapEntity.mapEntityList)
{
e.Save(doc);
}
try
{
string docString = doc.ToString();
ToolBox.CompressStringToFile(filePath+fileName+".gz", doc.ToString());
}
catch
{
DebugConsole.ThrowError("Saving map ''" + filePath + fileName + "'' failed!");
}
//doc.Save(filePath + fileName);
}
public static string[] GetMapFilePaths()
{
string[] mapFilePaths;
try
{
mapFilePaths = Directory.GetFiles("Content/SavedMaps");
}
catch (Exception e)
{
DebugConsole.ThrowError("Couldn't open directory ''Content/SavedMaps''!", e);
return null;
}
return mapFilePaths;
}
public static void Load(string filePath, string fileName)
{
Load(filePath + fileName);
}
public static void Load(string file)
{
Clear();
filePath = file;
XDocument doc = null;
string extension = Path.GetExtension(file);
if (extension==".gz")
{
Stream stream = ToolBox.DecompressFiletoStream(file);
if (stream == null)
{
DebugConsole.ThrowError("Loading map ''" + file + "'' failed!");
return;
}
try
{
stream.Position = 0;
doc = XDocument.Load(stream); //ToolBox.TryLoadXml(file);
stream.Close();
stream.Dispose();
}
catch
{
DebugConsole.ThrowError("Loading map ''" + file + "'' failed!");
return;
}
}
else if (extension ==".xml")
{
doc = XDocument.Load(file);
}
else
{
DebugConsole.ThrowError("Couldn't load map ''"+file+"! (Unrecognized file extension)");
return;
}
foreach (XElement element in doc.Root.Elements())
{
string typeName = element.Name.ToString();
Type t;
try
{
// Get the type of a specified class.
t = Type.GetType("Subsurface." + typeName + ", Subsurface", true, true);
if (t == null)
{
DebugConsole.ThrowError("Error in " + file + "! Could not find a entity of the type ''" + typeName + "''.");
continue;
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Error in " + file + "! Could not find a entity of the type ''" + typeName + "''.", e);
continue;
}
try
{
MethodInfo loadMethod = t.GetMethod("Load");
loadMethod.Invoke(t, new object[] { element });
}
catch (Exception e)
{
DebugConsole.ThrowError("Could not find the method ''Load'' in " + t + ".", e);
}
}
borders = new Rectangle(0, 0, 1, 1);
foreach (Hull hull in Hull.hullList)
{
if (hull.Rect.X < borders.X || borders.X == 0) borders.X = hull.Rect.X;
if (hull.Rect.Y > borders.Y || borders.Y == 0) borders.Y = hull.Rect.Y;
if (hull.Rect.X + hull.Rect.Width > borders.X + borders.Width) borders.Width = hull.Rect.X + hull.Rect.Width - borders.X;
if (hull.Rect.Y - hull.Rect.Height < borders.Y - borders.Height) borders.Height = borders.Y - (hull.Rect.Y - hull.Rect.Height);
}
MapEntity.LinkAll();
foreach (Item item in Item.itemList)
{
foreach (ItemComponent ic in item.components)
{
ic.OnMapLoaded();
}
}
}
public static void Clear()
{
Map.filePath = "";
if (Game1.gameScreen.Cam != null) Game1.gameScreen.Cam.TargetPos = Vector2.Zero;
Entity.RemoveAll();
if (Game1.gameSession!=null)
Game1.gameSession.crewManager.EndShift();
PhysicsBody.list.Clear();
Ragdoll.list.Clear();
Game1.world.Clear();
}
}
}
+431
View File
@@ -0,0 +1,431 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml.Linq;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System.Collections.ObjectModel;
namespace Subsurface
{
class MapEntity : Entity
{
public static List<MapEntity> mapEntityList = new List<MapEntity>();
//which entities have been selected for editing
protected static List<MapEntity> selectedList = new List<MapEntity>();
protected static GUIComponent editingHUD;
protected static Vector2 selectionPos = Vector2.Zero;
protected static Vector2 selectionSize = Vector2.Zero;
protected static Vector2 startMovingPos = Vector2.Zero;
protected List<int> linkedToID;
//observable collection because some entities may need to be notified when the collection is modified
public ObservableCollection<MapEntity> linkedTo;
//protected float soundRange;
//protected float sightRange;
//is the mouse inside the rect
protected bool isHighlighted;
protected bool isSelected;
//the position and dimensions of the entity
protected Rectangle rect;
public virtual Rectangle Rect {
get { return rect; }
set { rect = value; }
}
public virtual Sprite sprite
{
get { return null; }
}
public virtual bool IsLinkable
{
get { return false; }
}
public Vector2 Position
{
get
{
return new Vector2(
rect.X + rect.Width / 2.0f,
rect.Y - rect.Height / 2.0f);
}
}
public override Vector2 SimPosition
{
get
{
return ConvertUnits.ToSimUnits(Position);
}
}
public float SoundRange
{
get
{
if (aiTarget == null) return 0.0f;
return aiTarget.SoundRange;
}
set { aiTarget.SoundRange = value; }
}
public float SightRange
{
get
{
if (aiTarget == null) return 0.0f;
return aiTarget.SightRange;
}
set { aiTarget.SightRange = value; }
}
public bool IsHighlighted {
get { return isHighlighted; }
set { isHighlighted = value; }
}
public bool IsSelected
{
get { return isSelected; }
set { isSelected = value; }
}
public virtual string Name
{
get { return ""; }
}
public virtual void Move(Vector2 amount)
{
rect.X += (int)amount.X;
rect.Y += (int)amount.Y;
}
public virtual bool Contains(Vector2 position)
{
return (Map.RectContains(rect, position));
}
public virtual void Draw(SpriteBatch spriteBatch, bool editing) {}
public override void Remove()
{
base.Remove();
mapEntityList.Remove(this);
if (aiTarget != null) aiTarget.Remove();
if (linkedTo != null)
{
foreach (MapEntity e in linkedTo)
{
e.RemoveLinked(this);
}
linkedTo.Clear();
}
}
/// <summary>
/// Call Update() on every object in Entity.list
/// </summary>
public static void UpdateAll(Camera cam, float deltaTime)
{
foreach (Item item in Item.itemList)
{
item.Updated = false;
}
for (int i = 0; i < mapEntityList.Count; i++)
{
mapEntityList[i].Update(cam, deltaTime);
}
}
public virtual void Update(Camera cam, float deltaTime) { }
/// <summary>
/// Update the selection logic in editmap-screen
/// </summary>
public static void UpdateSelecting(Camera cam)
{
if (GUIComponent.MouseOn != null) return;
foreach (MapEntity e in mapEntityList)
{
e.isHighlighted = false;
e.isSelected = false;
}
if (MapEntityPrefab.Selected != null)
{
selectionPos = Vector2.Zero;
selectedList.Clear();
return;
}
if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.Delete))
{
foreach (MapEntity e in selectedList)
e.Remove();
selectedList.Clear();
}
Vector2 position = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y);
position = cam.ScreenToWorld(position);
MapEntity highLightedEntity = null;
foreach (MapEntity e in mapEntityList)
{
if (highLightedEntity == null || e.sprite == null ||
(highLightedEntity.sprite!=null && e.sprite.Depth < highLightedEntity.sprite.Depth))
{
if (e.Contains(position)) highLightedEntity = e;
}
e.isSelected = false;
}
if (highLightedEntity!=null)
highLightedEntity.isHighlighted = true;
foreach (MapEntity e in selectedList)
{
e.isSelected = true;
}
//started moving selected entities
if (startMovingPos != Vector2.Zero)
{
if (PlayerInput.GetMouseState.LeftButton == ButtonState.Released)
{
//mouse released -> move the entities to the new position of the mouse
Vector2 moveAmount = position - startMovingPos;
moveAmount = Map.VectorToWorldGrid(moveAmount);
if (moveAmount != Vector2.Zero)
{
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) > Map.gridSize.X || Math.Abs(selectionSize.Y) > Map.gridSize.Y)
{
newSelection = FindSelectedEntities(selectionPos, selectionSize);
}
else
{
if (highLightedEntity != null) newSelection.Add(highLightedEntity);
}
foreach (MapEntity e in newSelection)
{
e.isHighlighted = true;
}
if (PlayerInput.GetMouseState.LeftButton == ButtonState.Released)
{
if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.LeftControl) ||
PlayerInput.GetKeyboardState.IsKeyDown(Keys.RightControl))
{
foreach (MapEntity e in newSelection)
{
bool alreadySelected = false;
foreach (MapEntity e2 in selectedList)
{
Debug.WriteLine(e.ID+", "+e2.ID);
if (e.ID == e2.ID) alreadySelected = true;
}
if (alreadySelected)
selectedList.Remove(e);
else
selectedList.Add(e);
}
}
else
{
selectedList = newSelection;
}
selectionPos = Vector2.Zero;
Debug.WriteLine("zero");
selectionSize = Vector2.Zero;
}
}
//default, not doing anything specific yet
else
{
if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed &&
PlayerInput.GetKeyboardState.IsKeyUp(Keys.Space))
{
//if clicking a selected entity, start moving it
foreach (MapEntity e in selectedList)
{
if (e.Contains(position)) startMovingPos = position;
}
selectionPos = position;
Debug.WriteLine("pos");
}
}
}
/// <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 = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y);
position = cam.ScreenToWorld(position);
if (startMovingPos != Vector2.Zero)
{
Vector2 moveAmount = position - startMovingPos;
moveAmount = Map.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.rect.X, -e.rect.Y) + moveAmount,
new Vector2(e.rect.Width, e.rect.Height),
Color.DarkRed);
//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);
}
}
/// <summary>
/// Call DrawEditing() if only one entity is selected
/// </summary>
public static void Edit(SpriteBatch spriteBatch, Camera cam)
{
if (selectedList.Count == 1)
{
selectedList[0].DrawEditing(spriteBatch, cam);
}
else
{
editingHUD = null;
}
}
public virtual void DrawEditing(SpriteBatch spriteBatch, Camera cam) {}
public static List<MapEntity> FindMapEntities(Vector2 pos)
{
List<MapEntity> foundEntities = new List<MapEntity>();
foreach (MapEntity e in mapEntityList)
{
if (Map.RectContains(e.rect, pos)) foundEntities.Add(e);
}
return foundEntities;
}
public static MapEntity FindMapEntity(Vector2 pos)
{
foreach (MapEntity e in mapEntityList)
{
if (Map.RectContains(e.rect, pos)) return e;
}
return null;
}
/// <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 = Map.AbsRect(pos, size);
foreach (MapEntity e in mapEntityList)
{
if (Map.RectsOverlap(selectionRect, e.rect))
foundEntities.Add(e);
}
return foundEntities;
}
public virtual XElement Save(XDocument doc)
{
DebugConsole.ThrowError("Saving entity " + this.GetType() + " failed.");
return null;
}
/// <summary>
/// Update the linkedTo-lists of the entities based on the linkedToID-lists
/// Has to be done after all the entities have been loaded (an entity can't
/// be linked to some other entity that hasn't been loaded yet)
/// </summary>
public static void LinkAll()
{
foreach (MapEntity e in mapEntityList)
{
if (e.linkedToID == null) continue;
if (e.linkedToID.Count == 0) continue;
e.linkedTo.Clear();
foreach (int i in e.linkedToID)
{
MapEntity linked = FindEntityByID(i) as MapEntity;
if (linked != null)
e.linkedTo.Add(linked);
}
}
}
public void RemoveLinked(MapEntity e)
{
if (linkedTo == null) return;
if (linkedTo.Contains(e)) linkedTo.Remove(e);
}
}
}
+139
View File
@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Subsurface
{
class MapEntityPrefab
{
public static List<MapEntityPrefab> list = new List<MapEntityPrefab>();
protected string name;
protected bool isLinkable;
public Sprite sprite;
//the position where the structure is being placed (needed when stretching the structure)
protected static Vector2 placePosition;
protected ConstructorInfo constructor;
//is it possible to stretch the entity horizontally/vertically
protected bool resizeHorizontal;
protected bool resizeVertical;
//which prefab has been selected for placing
protected static MapEntityPrefab selected;
public string Name
{
get { return name; }
}
public static MapEntityPrefab Selected
{
get { return selected; }
}
public virtual bool IsLinkable
{
get { return isLinkable; }
}
public static void Init()
{
MapEntityPrefab ep = new MapEntityPrefab();
ep.name = "hull";
ep.constructor = typeof(Hull).GetConstructor(new Type[] { typeof(Rectangle) });
ep.resizeHorizontal = true;
ep.resizeVertical = true;
list.Add(ep);
ep = new MapEntityPrefab();
ep.name = "gap";
ep.constructor = typeof(Gap).GetConstructor(new Type[] { typeof(Rectangle) });
ep.resizeHorizontal = true;
ep.resizeVertical = true;
list.Add(ep);
ep = new MapEntityPrefab();
ep.name = "waypoint";
ep.constructor = typeof(WayPoint).GetConstructor(new Type[] { typeof(Rectangle) });
list.Add(ep);
}
public virtual void UpdatePlacing(SpriteBatch spriteBatch, Camera cam)
{
Vector2 placeSize = Map.gridSize;
if (placePosition == null || placePosition == Vector2.Zero)
{
if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed)
placePosition = Map.MouseToWorldGrid(cam);
}
else
{
Vector2 position = Map.MouseToWorldGrid(cam);
if (resizeHorizontal) placeSize.X = position.X - placePosition.X;
if (resizeVertical) placeSize.Y = placePosition.Y - position.Y;
Rectangle newRect = Map.AbsRect(placePosition, placeSize);
newRect.Width = (int)Math.Max(newRect.Width, Map.gridSize.X);
newRect.Height = (int)Math.Max(newRect.Height, Map.gridSize.Y);
if (PlayerInput.GetMouseState.LeftButton == ButtonState.Released)
{
object[] lobject = new object[] { newRect };
constructor.Invoke(lobject);
placePosition = Vector2.Zero;
selected = null;
}
newRect.Y = -newRect.Y;
GUI.DrawRectangle(spriteBatch, newRect, Color.DarkBlue);
}
if (PlayerInput.GetMouseState.RightButton == ButtonState.Pressed)
{
placePosition = Vector2.Zero;
selected = null;
}
}
public static bool SelectPrefab(object selection)
{
if ((selected = selection as MapEntityPrefab) != null)
{
placePosition = Vector2.Zero;
return true;
}
else
{
return false;
}
}
//a method that allows the GUIListBoxes to check through a delegate if the entityprefab is still selected
public static object GetSelected()
{
return (object)selected;
}
public void DrawListLine(SpriteBatch spriteBatch, Vector2 pos, Color color)
{
spriteBatch.DrawString(GUI.font, name, pos, color);
}
}
}
+592
View File
@@ -0,0 +1,592 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
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 Subsurface.Networking;
using Subsurface.Lights;
namespace Subsurface
{
class WallSection
{
public Rectangle rect;
public float damage;
public Gap gap;
public bool isHighLighted;
public WallSection(Rectangle rect)
{
this.rect = rect;
damage = 0.0f;
}
public WallSection(Rectangle rect, float damage)
{
this.rect = rect;
this.damage = 0.0f;
}
}
class Structure : MapEntity, IDamageable
{
static int wallSectionSize = 100;
public static List<Structure> wallList = new List<Structure>();
ConvexHull convexHull;
StructurePrefab prefab;
//farseer physics bodies, separated by gaps
List<Body> bodies;
//sections of the wall that are supposed to be rendered
private WallSection[] sections;
bool isHorizontal;
public override Sprite sprite
{
get { return prefab.sprite; }
}
public bool IsPlatform
{
get { return prefab.IsPlatform; }
}
public Direction StairDirection
{
get { return prefab.StairDirection; }
}
public override string Name
{
get { return "structure"; }
}
public bool HasBody
{
get { return prefab.HasBody; }
}
public bool IsHorizontal
{
get { return isHorizontal; }
}
public int SectionCount
{
get { return sections.Length; }
}
public override void Move(Vector2 amount)
{
base.Move(amount);
for (int i = 0; i < sections.Count(); i++)
{
Rectangle r = sections[i].rect;
r.X += (int)amount.X;
r.Y += (int)amount.Y;
sections[i].rect = r;
}
if (bodies != null)
{
Vector2 simAmount = ConvertUnits.ToSimUnits(amount);
foreach (Body b in bodies)
{
b.SetTransform(b.Position + simAmount, 0.0f);
}
}
if (convexHull!=null)
{
convexHull.Move(amount);
}
//if (gaps != null)
//{
// foreach (Gap g in gaps)
// {
// g.Move(amount);
// //g.position.X += amount.X;
// //g.position.Y -= amount.Y;
// }
//}
}
public Structure(Rectangle rectangle, StructurePrefab sp)
{
if (rectangle.Width == 0 || rectangle.Height == 0) return;
rect = rectangle;
prefab = sp;
isHorizontal = (rect.Width>rect.Height);
if (prefab.HasBody)
{
bodies = new List<Body>();
//gaps = new List<Gap>();
Body newBody = BodyFactory.CreateRectangle(Game1.world,
ConvertUnits.ToSimUnits(rect.Width),
ConvertUnits.ToSimUnits(rect.Height),
1.5f);
newBody.BodyType = BodyType.Static;
newBody.Position = ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width / 2.0f, rect.Y - rect.Height / 2.0f));
newBody.Friction = 0.5f;
newBody.OnCollision += OnWallCollision;
newBody.UserData = this;
newBody.CollisionCategories = (prefab.IsPlatform) ? Physics.CollisionPlatform : Physics.CollisionWall;
bodies.Add(newBody);
wallList.Add(this);
int xsections = 1;
int ysections = 1;
int width, height;
if (isHorizontal)
{
xsections = (int)Math.Ceiling((float)rect.Width / wallSectionSize);
sections = new WallSection[xsections];
width = (int)wallSectionSize;
height = rect.Height;
}
else
{
ysections = (int)Math.Ceiling((float)rect.Height / wallSectionSize);
sections = new WallSection[ysections];
width = rect.Width;
height = (int)wallSectionSize;
}
for (int x = 0; x < xsections; x++ )
{
for (int y = 0; y < ysections; y++)
{
Rectangle sectionRect = new Rectangle(rect.X + x * width, rect.Y - y * height, width, height);
sectionRect.Width -= (int)Math.Max((sectionRect.X + sectionRect.Width) - (rect.X + rect.Width), 0.0f);
sectionRect.Height -= (int)Math.Max((rect.Y - rect.Height)-(sectionRect.Y - sectionRect.Height), 0.0f);
sections[x+y] = new WallSection(sectionRect);
}
}
}
else
{
sections = new WallSection[1];
sections[0] = new WallSection(rect);
if (StairDirection!=Direction.None)
{
bodies = new List<Body>();
Body newBody = BodyFactory.CreateRectangle(Game1.world,
ConvertUnits.ToSimUnits(rect.Width * Math.Sqrt(2.0) - Map.gridSize.X),
ConvertUnits.ToSimUnits(10),
1.5f);
newBody.BodyType = BodyType.Static;
Vector2 stairPos = new Vector2(Position.X, rect.Y - rect.Height + rect.Width / 2.0f);
stairPos += new Vector2(
(StairDirection == Direction.Right) ? -Map.gridSize.X*1.5f : Map.gridSize.X*1.5f,
- Map.gridSize.Y*2.0f);
newBody.Position = ConvertUnits.ToSimUnits(stairPos);
newBody.Rotation = (StairDirection == Direction.Right) ? MathHelper.PiOver4 : -MathHelper.PiOver4;
newBody.Friction = 0.8f;
newBody.CollisionCategories = Physics.CollisionStairs;
newBody.UserData = this;
bodies.Add(newBody);
}
}
if (prefab.CastShadow)
{
Vector2[] corners = new Vector2[4];
corners[0] = new Vector2(rect.X, rect.Y - rect.Height);
corners[1] = new Vector2(rect.X, rect.Y);
corners[2] = new Vector2(rect.Right, rect.Y);
corners[3] = new Vector2(rect.Right, rect.Y - rect.Height);
convexHull = new ConvexHull(corners, Color.Black);
}
mapEntityList.Add(this);
}
public override void Remove()
{
base.Remove();
if (wallList.Contains(this)) wallList.Remove(this);
if (bodies != null)
{
foreach (Body b in bodies)
Game1.world.RemoveBody(b);
}
if (convexHull != null) convexHull.Remove();
}
public override void Draw(SpriteBatch spriteBatch, bool editing)
{
if (prefab.sprite == null) return;
Color color = (isHighlighted) ? Color.Green : Color.White;
if (isSelected && editing) color = Color.Red;
prefab.sprite.DrawTiled(spriteBatch, new Vector2(rect.X, -rect.Y), new Vector2(rect.Width, rect.Height), Vector2.Zero, color);
foreach (WallSection s in sections)
{
if (s.isHighLighted)
GUI.DrawRectangle(spriteBatch,
new Rectangle((int)s.rect.X, (int)-s.rect.Y, (int)s.rect.Width, (int)s.rect.Height),
new Color((s.damage / prefab.MaxHealth), 1.0f - (s.damage / prefab.MaxHealth), 0.0f, 1.0f), true);
s.isHighLighted = false;
if (s.damage == 0.0f) continue;
GUI.DrawRectangle(spriteBatch,
new Rectangle((int)s.rect.X, (int)-s.rect.Y, (int)s.rect.Width, (int)s.rect.Height),
Color.Black * (s.damage / prefab.MaxHealth), true);
}
}
private bool OnWallCollision(Fixture f1, Fixture f2, Contact contact)
{
//Structure structure = f1.Body.UserData as Structure;
//if (f2.Body.UserData as Item != null)
//{
// if (prefab.IsPlatform || prefab.StairDirection != Direction.None) return false;
//}
if (prefab.IsPlatform)
{
Limb limb;
if ((limb = f2.Body.UserData as Limb) != null)
{
if (limb.character.animController.IgnorePlatforms) return false;
}
}
if (!prefab.IsPlatform && prefab.StairDirection == Direction.None)
{
Vector2 pos = ConvertUnits.ToDisplayUnits(f2.Body.Position);
int section = FindSectionIndex(pos);
if (section>0)
{
Vector2 normal = contact.Manifold.LocalNormal;
float impact = Vector2.Dot(f2.Body.LinearVelocity, -normal)*f2.Body.Mass*0.1f;
if (impact < 10.0f) return true;
AmbientSoundManager.PlayDamageSound(DamageSoundType.StructureBlunt, impact,
new Vector2(
sections[section].rect.X + sections[section].rect.Width / 2,
sections[section].rect.Y - sections[section].rect.Height / 2));
AddDamage(section, impact);
}
}
return true;
}
public void HighLightSection(int sectionIndex)
{
sections[sectionIndex].isHighLighted = true;
}
public bool SectionHasHole(int sectionIndex)
{
if (sectionIndex < 0 || sectionIndex >= sections.Length) return false;
return (sections[sectionIndex].damage>=prefab.MaxHealth);
}
public void AddDamage(int sectionIndex, float damage)
{
if (!prefab.HasBody || prefab.IsPlatform) return;
if (Game1.client==null)
SetDamage(sectionIndex, sections[sectionIndex].damage + damage);
}
public int FindSectionIndex(Vector2 pos)
{
int index = (isHorizontal) ?
(int)Math.Floor((pos.X - rect.X) / wallSectionSize) :
(int)Math.Floor((rect.Y - pos.Y) / wallSectionSize);
if (index < 0 || index > sections.Length - 1) return -1;
return index;
}
public float SectionDamage(int sectionIndex)
{
if (sectionIndex < 0 || sectionIndex >= sections.Length) return 0.0f;
return sections[sectionIndex].damage;
}
public Vector2 SectionPosition(int sectionIndex)
{
if (sectionIndex < 0 || sectionIndex >= sections.Length) return Vector2.Zero;
return new Vector2(
sections[sectionIndex].rect.X + sections[sectionIndex].rect.Width / 2.0f,
sections[sectionIndex].rect.Y - sections[sectionIndex].rect.Height / 2.0f);
}
public void AddDamage(Vector2 position, float amount, float bleedingAmount, float stun)
{
if (!prefab.HasBody || prefab.IsPlatform) return;
int i = FindSectionIndex(ConvertUnits.ToDisplayUnits(position));
if (i == -1) return;
Game1.particleManager.CreateParticle(ConvertUnits.ToSimUnits(SectionPosition(i)), 0.0f, 0.0f, "dustcloud");
AddDamage(i, amount);
}
private void SetDamage(int sectionIndex, float damage)
{
if (!prefab.HasBody) return;
if (damage != sections[sectionIndex].damage)
new NetworkEvent(ID, false);
if (damage < prefab.MaxHealth*0.5f)
{
if (sections[sectionIndex].gap != null)
{
//remove existing gap if damage is below 50%
sections[sectionIndex].gap.Remove();
sections[sectionIndex].gap = null;
}
}
else
{
if (sections[sectionIndex].gap == null)
{
sections[sectionIndex].gap = new Gap(sections[sectionIndex].rect, !isHorizontal);
}
}
if (sections[sectionIndex].gap != null)
sections[sectionIndex].gap.Open = (float)Math.Pow(((damage / prefab.MaxHealth)-0.5)*2.0, 2.0);
bool hadHole = SectionHasHole(sectionIndex);
sections[sectionIndex].damage = MathHelper.Clamp(damage, 0.0f, prefab.MaxHealth);
bool hasHole = SectionHasHole(sectionIndex);
if (hadHole != hasHole) UpdateSections();
}
private void UpdateSections()
{
foreach (Body b in bodies)
{
Game1.world.RemoveBody(b);
}
bodies.Clear();
int x = sections[0].rect.X;
int y = sections[0].rect.Y;
int width = sections[0].rect.Width;
int height = sections[0].rect.Height;
bool hasHoles = false;
for (int i = 1; i < sections.Length; i++)
{
bool hasHole = SectionHasHole(i);
if (hasHole) hasHoles = true;
if (hasHole || i == sections.Length - 1)
{
if (width > 0 && height > 0)
{
CreateRectBody(new Rectangle(x, y, width, height));
}
if (isHorizontal)
{
x = sections[i].rect.X+ sections[i].rect.Width;
width = 0;
}
else
{
y = sections[i].rect.Y - sections[i].rect.Height;
height = 0;
}
}
else
{
if (isHorizontal)
{
width += sections[i].rect.Width;
}
else
{
height += sections[i].rect.Height;
}
}
}
if (hasHoles)
{
CreateRectBody(rect).IsSensor = true;
}
}
private Body CreateRectBody(Rectangle rect)
{
Body newBody = BodyFactory.CreateRectangle(Game1.world,
ConvertUnits.ToSimUnits(rect.Width),
ConvertUnits.ToSimUnits(rect.Height),
1.5f);
newBody.BodyType = BodyType.Static;
newBody.Position = ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width / 2.0f, rect.Y - rect.Height / 2.0f));
newBody.Friction = 0.5f;
newBody.OnCollision += OnWallCollision;
newBody.CollisionCategories = Physics.CollisionWall;
newBody.UserData = this;
bodies.Add(newBody);
return newBody;
}
public override XElement Save(XDocument doc)
{
XElement element = new XElement("Structure");
element.Add(new XAttribute("name", prefab.Name),
new XAttribute("ID", ID),
new XAttribute("rect", rect.X + "," + rect.Y+","+rect.Width+","+rect.Height));
for (int i = 0; i < sections.Count(); i++)
{
if (sections[i].damage == 0.0f) continue;
element.Add(new XElement("section",
new XAttribute("i", i),
new XAttribute("damage", sections[i].damage)));
}
doc.Root.Add(element);
return element;
}
public static void Load(XElement element)
{
string rectString = ToolBox.GetAttributeString(element, "rect", "0,0,0,0");
string[] rectValues = rectString.Split(',');
Rectangle rect = new Rectangle(
int.Parse(rectValues[0]),
int.Parse(rectValues[1]),
int.Parse(rectValues[2]),
int.Parse(rectValues[3]));
string name = element.Attribute("name").Value;
Debug.WriteLine(name+" - "+rect);
Structure s = null;
foreach (MapEntityPrefab ep in MapEntityPrefab.list)
{
if (ep.Name == name)
{
s = new Structure(rect, (StructurePrefab)ep);
s.ID = int.Parse(element.Attribute("ID").Value);
break;
}
}
if (s == null)
{
DebugConsole.ThrowError("Structure prefab " + name + " not found.");
return;
}
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString())
{
case "section":
if (subElement.Attribute("i") == null) continue;
s.sections[int.Parse(subElement.Attribute("i").Value)].damage =
ToolBox.GetAttributeFloat(subElement, "damage", 0.0f);
break;
}
}
}
public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data)
{
for (int i = 0; i < sections.Length; i++ )
{
message.Write(sections[i].damage);
}
}
public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message)
{
for (int i = 0; i < sections.Length; i++)
{
float damage = message.ReadFloat();
if (damage != sections[i].damage) SetDamage(i, damage);
}
}
}
}
+157
View File
@@ -0,0 +1,157 @@
using System;
using System.Diagnostics;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Subsurface
{
class StructurePrefab : MapEntityPrefab
{
//public static List<StructurePrefab> list = new List<StructurePrefab>();
//does the structure have a physics body
bool hasBody;
bool castShadow;
bool isPlatform;
Direction stairDirection;
float maxHealth;
//default size
Vector2 size;
public bool HasBody
{
get { return hasBody; }
}
public bool IsPlatform
{
get { return isPlatform; }
}
public float MaxHealth
{
get { return maxHealth; }
}
public bool CastShadow
{
get { return castShadow; }
}
public Direction StairDirection
{
get { return stairDirection; }
}
public static void LoadAll(string filePath)
{
XDocument doc = ToolBox.TryLoadXml(filePath);
if (doc == null) return;
foreach (XElement el in doc.Root.Elements())
{
StructurePrefab sp = Load(el);
Debug.WriteLine(sp.name);
list.Add(sp);
}
}
public static StructurePrefab Load(XElement el)
{
StructurePrefab sp = new StructurePrefab();
sp.name = el.Name.ToString();
Vector4 sourceVector = ToolBox.GetAttributeVector4(el, "sourcerect", new Vector4(0,0,1,1));
Rectangle sourceRect = new Rectangle(
(int)sourceVector.X,
(int)sourceVector.Y,
(int)sourceVector.Z,
(int)sourceVector.W);
if (el.Attribute("sprite") != null)
{
sp.sprite = new Sprite(el.Attribute("sprite").Value, sourceRect, Vector2.Zero);
sp.sprite.Depth = ToolBox.GetAttributeFloat(el, "depth", 0.0f);
if (ToolBox.GetAttributeBool(el, "fliphorizontal", false)) sp.sprite.effects = SpriteEffects.FlipHorizontally;
if (ToolBox.GetAttributeBool(el, "flipvertical", false)) sp.sprite.effects = SpriteEffects.FlipVertically;
}
sp.size = Vector2.Zero;
sp.size.X = ToolBox.GetAttributeFloat(el, "width", 0.0f);
sp.size.Y = ToolBox.GetAttributeFloat(el, "height", 0.0f);
sp.maxHealth = ToolBox.GetAttributeFloat(el, "health", 100.0f);
sp.resizeHorizontal = ToolBox.GetAttributeBool(el, "resizehorizontal", false);
sp.resizeVertical = ToolBox.GetAttributeBool(el, "resizevertical", false);
sp.isPlatform = ToolBox.GetAttributeBool(el, "platform", false);
sp.stairDirection = (Direction)Enum.Parse(typeof(Direction), ToolBox.GetAttributeString(el, "stairdirection", "None"));
sp.castShadow = ToolBox.GetAttributeBool(el, "castshadow", false);
sp.hasBody = ToolBox.GetAttributeBool(el, "body", false);
return sp;
}
public override void UpdatePlacing(SpriteBatch spriteBatch, Camera cam)
{
Vector2 position = Map.MouseToWorldGrid(cam);
//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.GetMouseState.LeftButton == ButtonState.Pressed)
placePosition = Map.MouseToWorldGrid(cam);
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 = Map.AbsRect(placePosition, placeSize);
//newRect.Width = (int)Math.Max(newRect.Width, Map.gridSize.X);
//newRect.Height = (int)Math.Max(newRect.Height, Map.gridSize.Y);
if (PlayerInput.GetMouseState.LeftButton == ButtonState.Released)
{
new Structure(newRect, this);
selected = null;
return;
}
//position = placePosition;
}
sprite.DrawTiled(spriteBatch, new Vector2(newRect.X, -newRect.Y), new Vector2(newRect.Width, newRect.Height), Color.White);
GUI.DrawRectangle(spriteBatch, new Rectangle(newRect.X - Game1.GraphicsWidth, -newRect.Y, newRect.Width + Game1.GraphicsWidth*2, newRect.Height), Color.White);
GUI.DrawRectangle(spriteBatch, new Rectangle(newRect.X, -newRect.Y - Game1.GraphicsHeight, newRect.Width, newRect.Height + Game1.GraphicsHeight*2), Color.White);
if (PlayerInput.GetMouseState.RightButton == ButtonState.Pressed) selected = null;
}
}
}
+162
View File
@@ -0,0 +1,162 @@
using System;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Subsurface
{
struct WaterVertex
{
public Vector3 position;
private Vector2 texCoord;
public WaterVertex(Vector3 position, Vector2 texCoord, Matrix transform)
{
this.position = position;
this.texCoord = Vector2.Transform(texCoord, transform);
}
public WaterVertex(Vector3 position, Vector2 texCoord)
{
this.position = position;
this.texCoord = texCoord;
}
public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
(
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
new VertexElement(sizeof(float) * 3, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
);
public void TransformTexCoord(Matrix transform)
{
this.texCoord = Vector2.Transform(this.texCoord, transform);
}
}
class WaterRenderer : IDisposable
{
const int DefaultBufferSize = 1500;
Effect effect;
Vector2 wavePos;
public WaterVertex[] vertices = new WaterVertex[DefaultBufferSize];
private VertexBuffer vertexBuffer;
public int positionInBuffer = 0;
public WaterRenderer(GraphicsDevice graphicsDevice)
{
//vertexBuffer = new VertexBuffer(graphicsDevice, WaterVertex.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
//vertexBuffer.SetData(vertices);
//effect = Game1.game.Content.Load<Effect>("effects");
byte[] bytecode = File.ReadAllBytes("Content/effects.mgfx");
effect = new Effect(graphicsDevice, bytecode);
//Texture2D waterBumpMap = Game1.textureLoader.FromFile("Content/waterbump.jpg");
//effect.Parameters["xBump"].SetValue(waterBumpMap);
//effect.Parameters["xWaveLength"].SetValue(0.5f);
//effect.Parameters["xWaveHeight"].SetValue(0.03f);
effect.Parameters["xProjection"].SetValue(Matrix.CreateOrthographic(Game1.GraphicsWidth, Game1.GraphicsHeight, -1, 1));
effect.Parameters["xColor"].SetValue(new Vector4(0.75f, 0.8f, 0.9f, 1.0f));
effect.Parameters["xBlurDistance"].SetValue(0.0005f);
effect.Parameters["xWaterBumpMap"].SetValue(Game1.textureLoader.FromFile("Content/waterbump.jpg"));
effect.Parameters["xWaveWidth"].SetValue(0.1f);
effect.Parameters["xWaveHeight"].SetValue(0.1f);
vertexBuffer = new VertexBuffer(graphicsDevice, WaterVertex.VertexDeclaration, DefaultBufferSize, BufferUsage.WriteOnly);
}
public void RenderBack(GraphicsDevice graphicsDevice, RenderTarget2D texture, Matrix transform)
{
WaterVertex[] verts = new WaterVertex[6];
// create the four corners of our triangle.
Vector3 p1 = new Vector3(-graphicsDevice.Viewport.Width / 2.0f, graphicsDevice.Viewport.Height / 2.0f, 0.0f);
Vector3 p2 = new Vector3(-p1.X, p1.Y, 0.0f);
Vector3 p3 = new Vector3(p2.X, -p1.Y, 0.0f);
Vector3 p4 = new Vector3(p1.X, -p1.Y, 0.0f);
verts[0] = new WaterVertex(p1, new Vector2(0, 0));
verts[1] = new WaterVertex(p2, new Vector2(1, 0));
verts[2] = new WaterVertex(p3, new Vector2(1, 1));
verts[3] = new WaterVertex(p1, new Vector2(0, 0));
verts[4] = new WaterVertex(p3, new Vector2(1, 1));
verts[5] = new WaterVertex(p4, new Vector2(0, 1));
vertexBuffer.SetData(verts);
effect.CurrentTechnique = effect.Techniques["WaterShader"];
effect.Parameters["xTexture"].SetValue(texture);
effect.Parameters["xView"].SetValue(Matrix.Identity);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, verts, 0, verts.Length / 3, WaterVertex.VertexDeclaration);
}
}
public void Render(GraphicsDevice graphicsDevice, Camera cam, RenderTarget2D texture, Matrix transform)
{
if (vertices == null) return;
if (vertices.Length < 0) return;
vertexBuffer.SetData(vertices);
wavePos.X += 0.0001f;
wavePos.Y += 0.0001f;
effect.Parameters["xBumpPos"].SetValue(cam.Position/Game1.GraphicsWidth/cam.Zoom);
effect.Parameters["xWavePos"].SetValue(wavePos);
effect.CurrentTechnique = effect.Techniques["EmptyShader"];
effect.Parameters["xTexture"].SetValue(texture);
effect.Parameters["xView"].SetValue(transform);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length / 3, WaterVertex.VertexDeclaration);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (vertexBuffer != null)
{
vertexBuffer.Dispose();
vertexBuffer = null;
}
if (effect != null)
{
effect.Dispose();
effect = null;
}
}
}
}
}
+144
View File
@@ -0,0 +1,144 @@
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;
namespace Subsurface
{
class WayPoint : MapEntity
{
public enum SpawnType { None, Human, Enemy };
private SpawnType spawnType;
public override Vector2 SimPosition
{
get { return ConvertUnits.ToSimUnits(new Vector2(rect.X, rect.Y)); }
}
public WayPoint(Rectangle newRect)
{
rect = newRect;
linkedTo = new ObservableCollection<MapEntity>();
mapEntityList.Add(this);
}
public override void Draw(SpriteBatch spriteBatch, bool editing)
{
if (!editing) return;
Color clr = (isSelected) ? Color.Red : Color.LightGreen;
GUI.DrawRectangle(spriteBatch, new Rectangle(rect.X, -rect.Y, rect.Width, rect.Height), clr, true);
foreach (MapEntity e in linkedTo)
{
GUI.DrawLine(spriteBatch,
new Vector2(rect.X + rect.Width / 2, -rect.Y + rect.Height / 2),
new Vector2(e.Rect.X + e.Rect.Width / 2, -e.Rect.Y + e.Rect.Height / 2),
Color.Green);
}
}
public override void DrawEditing(SpriteBatch spriteBatch, Camera cam)
{
int x = 300, y = 10;
spriteBatch.DrawString(GUI.font, "Editing waypoint", new Vector2(x, y), Color.Black);
spriteBatch.DrawString(GUI.font, "Hold space to link to another entity", new Vector2(x, y + 20), Color.Black);
spriteBatch.DrawString(GUI.font, "Spawnpoint: "+spawnType.ToString()+" +/-", new Vector2(x, y + 40), Color.Black);
if (PlayerInput.KeyHit(Keys.Add)) spawnType += 1;
if (PlayerInput.KeyHit(Keys.Subtract)) spawnType -= 1;
if (spawnType > SpawnType.Enemy) spawnType = SpawnType.None;
if (spawnType < SpawnType.None) spawnType = SpawnType.Enemy;
if (!PlayerInput.LeftButtonClicked()) return;
Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition);
foreach (MapEntity e in mapEntityList)
{
if (e.GetType()!=typeof(WayPoint)) continue;
if (e == this) continue;
if (!Map.RectContains(e.Rect, position)) continue;
linkedTo.Add(e);
e.linkedTo.Add(this);
}
}
public static WayPoint GetRandom(SpawnType spawnType = SpawnType.None)
{
List<WayPoint> wayPoints = new List<WayPoint>();
foreach (MapEntity e in mapEntityList)
{
WayPoint wayPoint = e as WayPoint;
if (wayPoint==null) continue;
if (spawnType != SpawnType.None && wayPoint.spawnType != spawnType) continue;
wayPoints.Add(wayPoint);
}
if (wayPoints.Count() == 0) return null;
return wayPoints[Game1.random.Next(wayPoints.Count())];
}
public override XElement Save(XDocument doc)
{
XElement element = new XElement("WayPoint");
element.Add(new XAttribute("ID", ID),
new XAttribute("x", rect.X),
new XAttribute("y", rect.Y),
new XAttribute("spawn", spawnType));
doc.Root.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;
}
public static void Load(XElement element)
{
Rectangle rect = new Rectangle(
int.Parse(element.Attribute("x").Value),
int.Parse(element.Attribute("y").Value),
(int)Map.gridSize.X, (int)Map.gridSize.Y);
WayPoint w = new WayPoint(rect);
w.ID = int.Parse(element.Attribute("ID").Value);
w.spawnType = (SpawnType)Enum.Parse(typeof(SpawnType),
ToolBox.GetAttributeString(element, "spawn", "None"));
w.linkedToID = new List<int>();
int i = 0;
while (element.Attribute("linkedto" + i) != null)
{
w.linkedToID.Add(int.Parse(element.Attribute("linkedto" + i).Value));
i += 1;
}
}
}
}