commit fadb89ae9e56e89f547c1dc0c567894333c1e30a Author: Regalis Date: Mon May 25 01:04:03 2015 +0300 First commit diff --git a/Subsurface/Camera.cs b/Subsurface/Camera.cs new file mode 100644 index 000000000..0bdcdea9b --- /dev/null +++ b/Subsurface/Camera.cs @@ -0,0 +1,205 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace Subsurface +{ + public class Camera + { + float zoom; + + const float DefaultZoom = 1.0f; + const float ZoomSmoothness = 8.0f; + const float MoveSmoothness = 8.0f; + + float offsetAmount; + + Matrix transform; + Matrix shaderTransform; + + Matrix viewMatrix; + private Vector2 position; + float rotation; + + //the area of the world inside the camera view + //used by the sprite drawing functions to determine whether + //a sprite should be drawn + Rectangle worldView; + + Point resolution; + + private Vector2 targetPos; + + public float Zoom + { + get { return zoom; } + set + { + //prevZoom = zoom; + zoom = value; + if (zoom < 0.1f) zoom = 0.1f; + + //if (prevZoom == zoom) return; + + Vector2 center = WorldViewCenter; + float newWidth = resolution.X / zoom; + float newHeight = resolution.Y / zoom; + + worldView = new Rectangle( + (int)(center.X - newWidth/2.0f), + (int)(center.Y - newHeight/2.0f), + (int)newWidth, + (int)newHeight); + + UpdateTransform(); + } + } + + public float Rotation + { + get { return rotation; } + set { rotation = value; } + } + + public float OffsetAmount + { + get { return offsetAmount; } + set { offsetAmount = value; } + } + + public Point Resolution + { + get { return resolution; } + } + + public Rectangle WorldView + { + get { return worldView; } + } + + public Vector2 WorldViewCenter + { + get + { + return new Vector2( + worldView.X + worldView.Width / 2.0f, + worldView.Y - worldView.Height / 2.0f); + } + } + + public Matrix Transform + { + get { return transform; } + } + + public Matrix ShaderTransform + { + get { return shaderTransform; } + } + + public Camera() + { + zoom = 1.0f; + rotation = 0.0f; + position = Vector2.Zero; + + worldView = new Rectangle(0,0, + Game1.GraphicsWidth, + Game1.GraphicsHeight); + + resolution = new Point(Game1.GraphicsWidth, Game1.GraphicsHeight); + + viewMatrix = + //Matrix.CreateRotationZ(Rotation) * + Matrix.CreateTranslation(new Vector3(Game1.GraphicsWidth / 2.0f, Game1.GraphicsHeight / 2.0f, 0)); + } + + public Vector2 TargetPos + { + get { return targetPos; } + set { targetPos = value; } + } + + // Auxiliary function to move the camera + public void Translate(Vector2 amount) + { + position += amount; + Sound.CameraPos = new Vector3(WorldViewCenter.X, WorldViewCenter.Y, 0.0f); + + UpdateTransform(); + } + + private void UpdateTransform() + { + Vector2 interpolatedPosition = position;//Physics.Interpolate(prevPosition,position); + + float interpolatedZoom = zoom;// Physics.Interpolate(prevZoom, zoom); + + worldView.X = (int)(interpolatedPosition.X - worldView.Width / 2.0); + worldView.Y = (int)(interpolatedPosition.Y + worldView.Height / 2.0); + + transform = Matrix.CreateTranslation( + new Vector3(-interpolatedPosition.X, interpolatedPosition.Y, 0)) * + Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) * + viewMatrix; + + shaderTransform = Matrix.CreateTranslation( + new Vector3( + -interpolatedPosition.X - resolution.X / interpolatedZoom / 2.0f, + -interpolatedPosition.Y - resolution.Y / interpolatedZoom / 2.0f, 0)) * + Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) * + viewMatrix; + } + + public void MoveCamera(float deltaTime) + { + float moveSpeed = 20.0f/zoom; + + Vector2 moveCam = Vector2.Zero; + if (targetPos == Vector2.Zero) + { + if (Keyboard.GetState().IsKeyDown(Keys.Left)) moveCam.X -= moveSpeed; + if (Keyboard.GetState().IsKeyDown(Keys.Right)) moveCam.X += moveSpeed; + if (Keyboard.GetState().IsKeyDown(Keys.Down)) moveCam.Y -= moveSpeed; + if (Keyboard.GetState().IsKeyDown(Keys.Up)) moveCam.Y += moveSpeed; + } + else + { + Vector2 mousePos = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y); + + Vector2 offset = mousePos - new Vector2(resolution.X / 2.0f, resolution.Y / 2.0f); + + offset.X = offset.X / (resolution.X * 0.4f); + offset.Y = -offset.Y / (resolution.Y * 0.3f); + + if (offset.Length() > 1.0f) offset.Normalize(); + + offset = offset * offsetAmount; + + float newZoom = Math.Min(DefaultZoom - Math.Min(offset.Length() / resolution.Y, 1.0f),1.0f); + Zoom += (newZoom - zoom) / ZoomSmoothness; + + moveCam = (targetPos + offset - position) / MoveSmoothness; + } + + Translate(moveCam*deltaTime*60.0f); + } + + public Vector2 Position + { + get { return position; } + } + + public Vector2 ScreenToWorld(Vector2 coords) + { + Vector2 worldCoords = Vector2.Transform(coords, Matrix.Invert(transform)); + return new Vector2(worldCoords.X, -worldCoords.Y); + } + + public Vector2 WorldToScreen(Vector2 coords) + { + Vector2 screenCoords = Vector2.Transform(coords, transform); + return new Vector2(screenCoords.X, screenCoords.Y); + } + } +} diff --git a/Subsurface/Characters/AI/AIController.cs b/Subsurface/Characters/AI/AIController.cs new file mode 100644 index 000000000..f32019c3f --- /dev/null +++ b/Subsurface/Characters/AI/AIController.cs @@ -0,0 +1,54 @@ +using Lidgren.Network; +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + class AIController : ISteerable + { + + public enum AiState { None, Attack, GoTo, Escape } + public enum SteeringState { Wander, Seek, Escape } + + public Character character; + + protected AiState state; + + protected SteeringManager steeringManager; + + public Vector2 Steering + { + get { return character.animController.TargetMovement; } + set { character.animController.TargetMovement = value; } + } + + public Vector2 Position + { + get { return character.animController.limbs[0].SimPosition; } + } + + public Vector2 Velocity + { + get { return character.animController.limbs[0].LinearVelocity; } + } + + public AiState State + { + get { return state; } + } + + public AIController (Character c) + { + character = c; + + steeringManager = new SteeringManager(this); + } + + public virtual void Update(float deltaTime) { } + + //protected Structure lastStructurePicked; + + public virtual void FillNetworkData(NetOutgoingMessage message) { } + public virtual void ReadNetworkData(NetIncomingMessage message) { } + + } +} diff --git a/Subsurface/Characters/AI/AITarget.cs b/Subsurface/Characters/AI/AITarget.cs new file mode 100644 index 000000000..9c10f1851 --- /dev/null +++ b/Subsurface/Characters/AI/AITarget.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + class AITarget + { + public static List list = new List(); + + + protected float soundRange; + protected float sightRange; + + public Entity entity; + + public float SoundRange + { + get + { + return soundRange; + } + set { soundRange = value; } + } + + public float SightRange + { + get { return sightRange; } + set { sightRange = value; } + } + + public Vector2 Position + { + get { return entity.SimPosition; } + } + + public AITarget(Entity e) + { + entity = e; + list.Add(this); + } + + public void Remove() + { + list.Remove(this); + } + + } +} diff --git a/Subsurface/Characters/AI/EnemyAIController.cs b/Subsurface/Characters/AI/EnemyAIController.cs new file mode 100644 index 000000000..da8c085e1 --- /dev/null +++ b/Subsurface/Characters/AI/EnemyAIController.cs @@ -0,0 +1,506 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Xml.Linq; +using FarseerPhysics; +using Lidgren.Network; +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + + class EnemyAIController : AIController + { //the preference to attack a specific type of target (-1.0 - 1.0) + //0.0 = doesn't attack targets of the type + //positive values = attacks targets of this type + //negative values = escapes targets of this type + private float attackRooms; + private float attackHumans; + private float attackWeaker; + private float attackStronger; + + + + private float updateTargetsTimer; + private const float UpdateTargetsInterval = 5.0f; + + private float raycastTimer; + private const float RaycastInterval = 1.0f; + + private Vector2 prevPosition; + private float distanceAccumulator; + + //a timer for attacks such as biting that last for a specific amount of time + //the duration is determined by the attackDuration of the attacking limb + private float attackTimer; + + //a "cooldown time" after an attack during which the character doesn't try to attack again + private float attackCoolDown; + private float coolDownTimer; + + //a point in a wall which the character is currently targeting + private Vector2 wallAttackPos; + //the entity (a wall) which the character is targeting + private MapEntity targetEntity; + + //the limb selected for the current attack + private Limb attackingLimb; + + private AITarget selectedTarget; + private AITargetMemory selectedTargetMemory; + private float targetValue; + + private Dictionary targetMemories; + + //the eyesight of the NPC (0.0 = blind, 1.0 = sees every target within sightRange) + private float sight; + //how far the NPC can hear targets from (0.0 = deaf, 1.0 = hears every target within soundRange) + private float hearing; + + public EnemyAIController(Character c, string file) : base(c) + { + targetMemories = new Dictionary(); + + XDocument doc = ToolBox.TryLoadXml(file); + if (doc == null) return; + + XElement aiElement = doc.Root.Element("ai"); + if (aiElement == null) return; + + attackRooms = ToolBox.GetAttributeFloat(aiElement, "attackrooms", 0.0f) / 100.0f; + attackHumans = ToolBox.GetAttributeFloat(aiElement, "attackhumans", 0.0f) / 100.0f; + attackWeaker = ToolBox.GetAttributeFloat(aiElement, "attackweaker", 0.0f) / 100.0f; + attackStronger = ToolBox.GetAttributeFloat(aiElement, "attackstronger", 0.0f) / 100.0f; + + attackCoolDown = ToolBox.GetAttributeFloat(aiElement, "attackcooldown", 5.0f); + + sight = ToolBox.GetAttributeFloat(aiElement, "sight", 0.0f); + hearing = ToolBox.GetAttributeFloat(aiElement, "hearing", 0.0f); + + state = AiState.None; + } + + public override void Update(float deltaTime) + { + UpdateDistanceAccumulator(); + + character.animController.IgnorePlatforms = (-character.animController.TargetMovement.Y > Math.Abs(character.animController.TargetMovement.X)); + + if (updateTargetsTimer > 0.0) + { + updateTargetsTimer -= deltaTime; + } + else + { + System.Diagnostics.Debug.WriteLine("updatetargets"); + UpdateTargets(character); + updateTargetsTimer = UpdateTargetsInterval; + + if (selectedTarget == null) + { + state = AiState.None; + } + else + { + state = (targetValue > 0.0f) ? AiState.Attack : AiState.Escape; + } + //if (coolDownTimer >= 0.0f) return; + } + + switch (state) + { + case AiState.None: + UpdateNone(deltaTime); + break; + case AiState.Attack: + UpdateAttack(deltaTime); + break; + } + + steeringManager.Update(); + } + + private void UpdateNone(float deltaTime) + { + //wander around randomly + //UpdateSteeringWander(deltaTime, 0.8f); + steeringManager.SteeringWander(0.8f); + steeringManager.SteeringAvoid(deltaTime, 1.0f); + + attackingLimb = null; + attackTimer = 0.0f; + + coolDownTimer -= deltaTime; + } + + private void UpdateDistanceAccumulator() + { + Limb limb = character.animController.limbs[0]; + distanceAccumulator += (limb.SimPosition - prevPosition).Length(); + + prevPosition = limb.body.Position; + } + + private void UpdateAttack(float deltaTime) + { + + if (selectedTarget == null) + { + state = AiState.None; + return; + } + + selectedTargetMemory.Priority -= deltaTime; + + Vector2 attackPosition = selectedTarget.Position; + if (wallAttackPos != Vector2.Zero) attackPosition = wallAttackPos; + + if (coolDownTimer>0.0f) + { + coolDownTimer -= deltaTime; + + //System.Diagnostics.Debug.WriteLine("cooldown"); + + if (selectedTarget.entity is Hull || + Vector2.Distance(attackPosition, character.animController.limbs[0].SimPosition) 0.0) + { + raycastTimer -= deltaTime; + } + else + { + targetEntity = null; + //check if there's a wall between the target and the character + Vector2 rayStart = character.animController.limbs[0].SimPosition; + Vector2 rayEnd = selectedTarget.Position; + Structure closestStructure = Map.CheckVisibility(rayStart, rayEnd); + if (Map.LastPickedFraction == 1.0f || closestStructure == null) + { + wallAttackPos = Vector2.Zero; + } + else + { + + + Structure wall = closestStructure as Structure; + if (wall==null) + { + wallAttackPos = Map.LastPickedPosition; + } + else + { + int sectionIndex = wall.FindSectionIndex(ConvertUnits.ToDisplayUnits(Map.LastPickedPosition)); + + float sectionDamage = wall.SectionDamage(sectionIndex); + for (int i = sectionIndex-2; i<=sectionIndex+2; i++) + { + if (wall.SectionHasHole(i)) + { + sectionIndex = i; + break; + } + if (wall.SectionDamage(i) > sectionDamage) sectionIndex = i; + } + wallAttackPos = wall.SectionPosition(sectionIndex); + wallAttackPos = ConvertUnits.ToSimUnits(wallAttackPos); + } + + + targetEntity = closestStructure; + } + + raycastTimer = RaycastInterval; + } + + steeringManager.SteeringSeek(attackPosition); + + //check if any of the limbs is close enough to attack the target + if (attackingLimb == null) + { + foreach (Limb limb in character.animController.limbs) + { + if (limb.attack==null || limb.attack.type == Attack.Type.None) continue; + if (Vector2.Distance(limb.SimPosition, attackPosition) > limb.attack.range) continue; + + attackingLimb = limb; + break; + } + return; + } + + UpdateLimbAttack(deltaTime, attackingLimb, attackPosition); + + } + + private void UpdateLimbAttack(float deltaTime, Limb limb, Vector2 attackPosition) + { + //DamageType damageType = DamageType.None; + // float damage = 0.0f; + + //bool hasAttacked = false; + + IDamageable damageTarget = null; + + switch (limb.attack.type) + { + case Attack.Type.PinchCW: + case Attack.Type.PinchCCW: + + float dir = (limb.attack.type == Attack.Type.PinchCW) ? 1.0f : -1.0f; + float dist = Vector2.Distance(limb.SimPosition, attackPosition); + + if (wallAttackPos != Vector2.Zero && targetEntity != null) + { + damageTarget = targetEntity as IDamageable; + } + else + { + damageTarget = selectedTarget.entity as IDamageable; + } + + attackTimer += deltaTime*0.05f; + + if (damageTarget == null) + { + attackTimer = limb.attack.duration; + break; + } + + if (dist < limb.attack.range * 0.5f) + { + attackTimer += deltaTime; + limb.body.ApplyTorque(limb.Mass * 50.0f * character.animController.Dir * dir); + + limb.attack.DoDamage(damageTarget, limb.SimPosition, deltaTime, (limb.soundTimer <= 0.0f)); + + limb.soundTimer = Limb.SoundInterval; + } + else + { + //limb.body.ApplyTorque(limb.Mass * -20.0f * character.animController.Dir * dir); + } + + limb.body.ApplyLinearImpulse(limb.Mass * 10.0f * + Vector2.Normalize(attackPosition - limb.SimPosition)); + + steeringManager.SteeringSeek(attackPosition + (limb.SimPosition-Position), 5.0f); + + break; + default: + attackTimer = limb.attack.duration; + break; + } + + if (attackTimer >= limb.attack.duration) + { + attackTimer = 0.0f; + if (Vector2.Distance(limb.SimPosition, attackPosition)<5.0) coolDownTimer = attackCoolDown; + + System.Diagnostics.Debug.WriteLine("cooldown: " + coolDownTimer); + } + } + + //private float GetAttackDamage(Limb limb, float deltaTime) + //{ + // return (limb.attack.duration == 0.0f) ? limb.attack.damage : limb.attack.damage * deltaTime; + //} + + //goes through all the AItargets, evaluates how preferable it is to attack the target, + //whether the character can see/hear the target and chooses the most preferable target within + //sight/hearing range + public void UpdateTargets(Character character) + { + + if (distanceAccumulator<5.0f && Game1.random.Next(1,3)==1) + { + selectedTarget = null; + character.animController.TargetMovement = -character.animController.TargetMovement; + state = AiState.None; + return; + } + distanceAccumulator = 0.0f; + + selectedTarget = null; + selectedTargetMemory = null; + this.targetValue = 0.0f; + + UpdateTargetMemories(); + + foreach (AITarget target in AITarget.list) + { + float valueModifier = 0.0f; + float dist = 0.0f; + + Character targetCharacter = target.entity as Character; + + //ignore the aitarget if it is the character itself + if (targetCharacter == character) continue; + + if (targetCharacter!=null) + { + if (attackHumans == 0.0f || targetCharacter.speciesName != "human") continue; + + valueModifier = attackHumans; + } + else if (target.entity!=null && attackRooms!=0.0f) + { + //skip the target if it's the room the character is inside of + if (character.animController.CurrentHull != null && character.animController.CurrentHull == target.entity as Hull) continue; + + valueModifier = attackRooms; + } + + dist = Vector2.Distance( + character.animController.limbs[0].SimPosition, + target.Position); + dist = ConvertUnits.ToDisplayUnits(dist); + + AITargetMemory targetMemory = FindTargetMemory(target); + + valueModifier *= targetMemory.Priority; + //dist -= targetMemory.Priority; + + if (valueModifier != 0.0f && (dist < target.SightRange * sight || dist < target.SoundRange * hearing)) + { + //if the target is a character, check if it is visible + if (targetCharacter != null) + { + Vector2 rayStart = character.animController.limbs[0].SimPosition; + Vector2 rayEnd = target.Position; + + Structure closestStructure = Map.CheckVisibility(rayStart, rayEnd); + //System.Diagnostics.Debug.WriteLine("closestfraction: " + closestFraction); + //if not visible, ignore this AItarget + if (closestStructure != null) continue; + } + + float newTargetValue = valueModifier/dist; + if (selectedTarget == null || Math.Abs(newTargetValue) > Math.Abs(this.targetValue)) + { + selectedTarget = target; + selectedTargetMemory = targetMemory; + targetValue = newTargetValue; + Debug.WriteLine(selectedTarget); + } + } + } + + //selectedTarget = bestTarget; + //selectedTargetMemory = targetMemory; + //this.targetValue = bestTargetValue; + } + + //find the targetMemory that corresponds to some AItarget or create if there isn't one yet + private AITargetMemory FindTargetMemory(AITarget target) + { + AITargetMemory memory = null; + if (targetMemories.TryGetValue(target, out memory)) + { + return memory; + } + + memory = new AITargetMemory(100.0f); + targetMemories.Add(target, memory); + + return memory; + } + + //go through all the targetmemories and delete ones that don't + //have a corresponding AItarget or whose priority is 0.0f + private void UpdateTargetMemories() + { + + List toBeRemoved = new List(); + foreach(KeyValuePair memory in targetMemories) + { + memory.Value.Priority += 0.5f; + if (memory.Value.Priority == 0.0f || !AITarget.list.Contains(memory.Key)) toBeRemoved.Add(memory.Key); + } + + foreach (AITarget target in toBeRemoved) + { + targetMemories.Remove(target); + } + } + + public override void FillNetworkData(NetOutgoingMessage message) + { + message.Write((byte)state); + + message.Write(wallAttackPos.X); + message.Write(wallAttackPos.Y); + + message.Write(steeringManager.WanderAngle); + message.Write(updateTargetsTimer); + message.Write(raycastTimer); + message.Write(coolDownTimer); + + message.Write(targetEntity==null ? -1 : targetEntity.ID); + } + + public override void ReadNetworkData(NetIncomingMessage message) + { + state = (AiState)(message.ReadByte()); + + wallAttackPos.X = message.ReadFloat(); + wallAttackPos.Y = message.ReadFloat(); + + float wanderAngle = message.ReadFloat(); + float updateTargetsTimer = message.ReadFloat(); + float raycastTimer = message.ReadFloat(); + float coolDownTimer = message.ReadFloat(); + + int targetID = message.ReadInt32(); + + steeringManager.WanderAngle = wanderAngle; + this.updateTargetsTimer = updateTargetsTimer; + this.raycastTimer = raycastTimer; + this.coolDownTimer = coolDownTimer; + + if (targetID>-1) + targetEntity = Entity.FindEntityByID(targetID) as MapEntity; + + } + } + + //the "memory" of the character + //keeps track of how preferable it is to attack a specific target + //(if the character can't inflict much damage the target, the priority decreases + //and if the target attacks the character, the priority increases) + class AITargetMemory + { + //private AITarget target; + private float priority; + + //public AITarget Target + //{ + // get { return target; } + //} + + public float Priority + { + get { return priority; } + set { priority = MathHelper.Clamp(value, 1.0f, 100.0f); } + } + + public AITargetMemory(float priority) + { + this.priority = priority; + } + + } +} diff --git a/Subsurface/Characters/AI/ISteerable.cs b/Subsurface/Characters/AI/ISteerable.cs new file mode 100644 index 000000000..0a814393b --- /dev/null +++ b/Subsurface/Characters/AI/ISteerable.cs @@ -0,0 +1,27 @@ +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + interface ISteerable + { + + Vector2 Steering + { + get; + set; + } + + Vector2 Velocity + { + get; + } + + + + Vector2 Position + { + get; + } + + } +} diff --git a/Subsurface/Characters/AI/SteeringManager.cs b/Subsurface/Characters/AI/SteeringManager.cs new file mode 100644 index 000000000..058cf7efb --- /dev/null +++ b/Subsurface/Characters/AI/SteeringManager.cs @@ -0,0 +1,146 @@ +using System; +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + class SteeringManager + { + private const float CircleDistance = 2.5f; + private const float CircleRadius = 0.3f; + + private const float RayCastInterval = 0.5f; + + private ISteerable host; + + private Vector2 steering; + + //the steering amount when avoiding obstacles + //(needs a separate variable because it's only updated when a raycast is done to detect any nearby obstacles) + private Vector2 avoidSteering; + private float rayCastTimer; + + private float wanderAngle; + + public float WanderAngle + { + get { return wanderAngle; } + set { wanderAngle = value; } + } + + public SteeringManager(ISteerable host) + { + this.host = host; + + wanderAngle = ToolBox.RandomFloat(0.0f, MathHelper.TwoPi); + } + + public void SteeringSeek(Vector2 target, float speed = 1.0f) + { + steering += DoSteeringSeek(target, speed); + } + + public void SteeringWander(float speed = 1.0f) + { + steering += DoSteeringWander(speed); + } + + public void SteeringAvoid(float deltaTime, float speed) + { + steering += DoSteeringAvoid(deltaTime, speed); + } + + public void Update(float speed = 1.0f) + { + float steeringSpeed = steering.Length(); + if (steeringSpeed>speed) + { + steering = Vector2.Normalize(steering) * Math.Abs(speed); + } + + host.Steering = steering; + } + + private Vector2 DoSteeringSeek(Vector2 target, float speed = 1.0f) + { + Vector2 targetVel = target - host.Position; + targetVel = Vector2.Normalize(targetVel) * speed; + Vector2 newSteering = targetVel - host.Steering; + + if (newSteering==Vector2.Zero) return Vector2.Zero; + + float steeringSpeed = (newSteering + host.Steering).Length(); + if (steeringSpeed > Math.Abs(speed)) + { + newSteering = Vector2.Normalize(newSteering)*Math.Abs(speed); + } + + return newSteering; + } + + private Vector2 DoSteeringWander(float speed = 1.0f) + { + Vector2 circleCenter = (host.Velocity == Vector2.Zero) ? new Vector2(speed, 0.0f) : host.Velocity; + circleCenter = Vector2.Normalize(circleCenter) * CircleDistance; + + Vector2 displacement = new Vector2( + (float)Math.Cos(wanderAngle), + (float)Math.Sin(wanderAngle)); + displacement = displacement * CircleRadius; + + float angleChange = 1.5f; + + wanderAngle += ToolBox.RandomFloat(0.0f, 1.0f) * angleChange - angleChange * 0.5f; + + Vector2 newSteering = circleCenter + displacement; + float steeringSpeed = (newSteering + host.Steering).Length(); + if (steeringSpeed > speed) + { + newSteering = Vector2.Normalize(newSteering) * speed; + } + + return newSteering; + } + + private Vector2 DoSteeringAvoid(float deltaTime, float speed = 1.0f) + { + if (steering == Vector2.Zero || host.Steering == Vector2.Zero) return Vector2.Zero; + + float MaxDistance = 2.0f; + + Vector2 ahead = host.Position + Vector2.Normalize(host.Steering)*MaxDistance; + + if (rayCastTimer <= 0.0f) + { + rayCastTimer = RayCastInterval; + Structure closestStructure = Map.CheckVisibility(host.Position, ahead); + if (closestStructure == null) + { + avoidSteering = Vector2.Zero; + return Vector2.Zero; + } + else + { + Vector2 obstaclePosition = Map.LastPickedPosition; + if (closestStructure.IsHorizontal) + { + obstaclePosition.Y = closestStructure.SimPosition.Y; + } + else + { + obstaclePosition.X = closestStructure.SimPosition.X; + } + + avoidSteering = Vector2.Normalize(Map.LastPickedPosition - obstaclePosition); + } + + } + else + { + rayCastTimer -= deltaTime; + } + + return avoidSteering * speed; + + } + } +} diff --git a/Subsurface/Characters/AI/SteeringPath.cs b/Subsurface/Characters/AI/SteeringPath.cs new file mode 100644 index 000000000..e3e8ee835 --- /dev/null +++ b/Subsurface/Characters/AI/SteeringPath.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + class SteeringPath + { + private Queue nodes; + + const float minDistance = 0.1f; + + Vector2 currentNode; + + public SteeringPath() + { + nodes = new Queue(); + } + + public void AddNode(Vector2 node) + { + if (node == Vector2.Zero) return; + nodes.Enqueue(node); + } + + public Vector2 CurrentNode + { + get { return currentNode; } + } + + public Vector2 GetNode(Vector2 pos) + { + if (nodes.Count == 0) return Vector2.Zero; + if (currentNode==null || currentNode==Vector2.Zero || Vector2.Distance(pos, currentNode) characterList = new List(); + + public static Queue newCharacterQueue = new Queue(); + + public static bool disableControls; + + //the character that the player is currently controlling + private static Character controlled; + + public static Character Controlled + { + get { return controlled; } + set { controlled = value; } + } + + public readonly bool IsNetworkPlayer; + + private Inventory inventory; + + public double lastNetworkUpdate; + + public byte largeUpdateTimer; + + public readonly Dictionary properties; + + protected Key selectKeyHit; + protected Key actionKeyHit; + protected Key actionKeyDown; + protected Key secondaryKeyHit; + protected Key secondaryKeyDown; + + private Item selectedConstruction; + private Item[] selectedItems; + + public AnimController animController; + private AIController aiController; + + private Vector2 cursorPosition; + + protected bool needsAir; + protected float oxygen; + protected float drowningTime; + + protected Item closestItem; + + protected bool isDead; + + bool isHumanoid; + + //the name of the species (e.q. human) + public readonly string speciesName; + + public CharacterInfo info; + + protected float soundTimer; + protected float soundInterval; + + private float blood; + + private Sound[] sounds; + //which AIstate each sound is for + private AIController.AiState[] soundStates; + + public Inventory Inventory + { + get { return inventory; } + } + + public Vector2 CursorPosition + { + get { return cursorPosition; } + } + + public float SoundRange + { + get { return aiTarget.SoundRange; } + } + + public float SightRange + { + get { return aiTarget.SightRange; } + } + private float pressureProtection; + public float PressureProtection + { + get { return pressureProtection; } + set + { + //if (!value && pressureProtection) Debug.WriteLine("pp set: " + value); + pressureProtection = MathHelper.Clamp(value, 0.0f, 100.0f); + + } + } + + public float Oxygen + { + get { return oxygen; } + set + { + oxygen = MathHelper.Clamp(value, 0.0f, 100.0f); + if (oxygen == 0.0f) Kill(); + } + } + + public float Blood + { + get { return blood; } + set + { + blood = MathHelper.Clamp(value, 0.0f, 100.0f); + if (blood == 0.0f) Kill(); + } + } + + public Item[] SelectedItems + { + get { return selectedItems; } + } + + public bool HasSelectedItem(Item item) + { + return selectedItems.Contains(item); + } + + public bool TrySelectItem(Item item) + { + bool rightHand = ((CharacterInventory)inventory).IsInLimbSlot(item, LimbSlot.RightHand); + bool leftHand = ((CharacterInventory)inventory).IsInLimbSlot(item, LimbSlot.LeftHand); + + bool selected = false; + if (rightHand && SelectedItems[0] == null) + { + selectedItems[0] = item; + selected = true; + } + if (leftHand && SelectedItems[1] == null) + { + selectedItems[1] = item; + selected = true; + } + + return selected; + } + + public bool TrySelectItem(Item item, int index) + { + if (selectedItems[index] != null) return false; + + selectedItems[index] = item; + return true; + } + + public void DeselectItem(Item item) + { + for (int i = 0; i < selectedItems.Length; i++) + { + if (selectedItems[i] == item) selectedItems[i] = null; + } + } + + public Item SelectedConstruction + { + get { return selectedConstruction; } + set { selectedConstruction = value; } + } + + public Item ClosestItem + { + get { return closestItem; } + } + + public Key SelectKeyHit + { + get { return selectKeyHit; } + } + + public Key ActionKeyHit + { + get { return actionKeyHit; } + } + + public Key ActionKeyDown + { + get { return actionKeyDown; } + } + + public Key SecondaryKeyHit + { + get { return secondaryKeyHit; } + } + + public Key SecondaryKeyDown + { + get { return secondaryKeyDown; } + } + + public bool IsDead + { + get { return isDead; } + } + + public override Vector2 SimPosition + { + get { return animController.limbs[0].SimPosition; } + } + + public Vector2 Position + { + get { return ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition); } + } + + public Character(string file) : this(file, Vector2.Zero, null) + { + } + + public Character(string file, Vector2 position) + : this(file, position, null) + { + } + + public Character(CharacterInfo characterInfo, Vector2 position, bool isNetworkPlayer = false) + : this(characterInfo.file, position, characterInfo, isNetworkPlayer) + { + } + + public Character(string file, Vector2 position, CharacterInfo characterInfo = null, bool isNetworkPlayer = false) + { + selectKeyHit = new Key(false); + actionKeyDown = new Key(true); + actionKeyHit = new Key(false); + secondaryKeyHit = new Key(false); + secondaryKeyDown = new Key(true); + + selectedItems = new Item[2]; + + IsNetworkPlayer = isNetworkPlayer; + + oxygen = 100.0f; + blood = 100.0f; + aiTarget = new AITarget(this); + + properties = ObjectProperty.GetProperties(this); + + XDocument doc = ToolBox.TryLoadXml(file); + if (doc == null) return; + + speciesName = ToolBox.GetAttributeString(doc.Root, "name", "Unknown"); + + isHumanoid = ToolBox.GetAttributeBool(doc.Root, "humanoid", false); + + info = characterInfo ?? new CharacterInfo(file); + + if (isHumanoid) + { + animController = new HumanoidAnimController(this, doc.Root.Element("ragdoll")); + animController.targetDir = Direction.Right; + inventory = new CharacterInventory(10, this); + } + else + { + animController = new FishAnimController(this, doc.Root.Element("ragdoll")); + PressureProtection = 100.0f; + //FishAnimController fishAnim = (FishAnimController)animController; + + aiController = new EnemyAIController(this, file); + } + + foreach (Limb limb in animController.limbs) + { + limb.body.SetTransform(position+limb.SimPosition, 0.0f); + //limb.prevPosition = ConvertUnits.ToDisplayUnits(position); + } + + needsAir = ToolBox.GetAttributeBool(doc.Root, "needsair", false); + drowningTime = ToolBox.GetAttributeFloat(doc.Root, "drowningtime", 10.0f); + + soundInterval = ToolBox.GetAttributeFloat(doc.Root, "soundinterval", 10.0f); + + var xSounds = doc.Root.Elements("sound"); + if (xSounds.Count() > 0) + { + sounds = new Sound[xSounds.Count()]; + soundStates = new AIController.AiState[xSounds.Count()]; + int i = 0; + foreach (XElement xSound in xSounds) + { + sounds[i] = Sound.Load(xSound.Attribute("file").Value); + if (xSound.Attribute("state") == null) + { + soundStates[i] = AIController.AiState.None; + } + else + { + soundStates[i] = (AIController.AiState)Enum.Parse( + typeof(AIController.AiState), xSound.Attribute("state").Value, true); + } + i++; + } + } + + + animController.FindHull(); + + if (this.info.ID >= 0) + { + ID = this.info.ID; + } + + characterList.Add(this); + } + + + /// + /// Control the characte + /// + public void Control(Camera cam, bool forcePick=false) + { + if (isDead) return; + + //find the closest item if selectkey has been hit, or if the character is being + //controlled by the player (in order to highlight it) + //closestItem = null; + if (controlled==this) + { + Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition)); + closestItem = FindClosestItem(mouseSimPos); + + if (closestItem != null) + { + closestItem.IsHighlighted = true; + if (selectKeyHit.State && closestItem.Pick(this, forcePick)) + { + new NetworkEvent(NetworkEventType.PickItem, ID, true, closestItem.ID); + } + } + } + + for (int i = 0; i < selectedItems.Length; i++ ) + { + if (selectedItems[i] == null) continue; + if (i == 1 && selectedItems[0] == selectedItems[1]) continue; + + if (actionKeyDown.State) selectedItems[i].Use(this); + if (secondaryKeyDown.State && selectedItems[i] != null) selectedItems[i].SecondaryUse(this); + + } + + if (selectedConstruction != null) + { + if (actionKeyDown.State) selectedConstruction.Use(this); + if (secondaryKeyDown.State) selectedConstruction.SecondaryUse(this); + } + + if (IsNetworkPlayer) + { + selectKeyHit.Reset(); + actionKeyHit.Reset(); + actionKeyDown.Reset(); + secondaryKeyHit.Reset(); + secondaryKeyDown.Reset(); + } + } + + private Item FindClosestItem(Vector2 mouseSimPos) + { + Limb torso = animController.GetLimb(LimbType.Torso); + Vector2 pos = (torso.body.TargetPosition != Vector2.Zero) ? torso.body.TargetPosition : torso.SimPosition; + + return Item.FindPickable(pos, selectedConstruction == null ? mouseSimPos : selectedConstruction.SimPosition, null, selectedItems); + } + + /// + /// Control the character according to player input + /// + public void ControlLocalPlayer(Camera cam, bool moveCam = true) + { + if (isDead) return; + + Limb head = animController.GetLimb(LimbType.Head); + + Lights.LightManager.viewPos = ConvertUnits.ToDisplayUnits(head.SimPosition); + + Vector2 targetMovement = Vector2.Zero; + + if (!disableControls) + { + if (PlayerInput.KeyDown(Keys.W)) targetMovement.Y += 1.0f; + if (PlayerInput.KeyDown(Keys.S)) targetMovement.Y -= 1.0f; + if (PlayerInput.KeyDown(Keys.A)) targetMovement.X -= 1.0f; + if (PlayerInput.KeyDown(Keys.D)) targetMovement.X += 1.0f; + + //the vertical component is only used for falling through platforms and climbing ladders when not in water, + //so the movement can't be normalized or the character would walk slower when pressing down/up + if (animController.InWater) + { + float length = targetMovement.Length(); + if (length > 0.0f) targetMovement = targetMovement / length; + } + + if (Keyboard.GetState().IsKeyDown(Keys.LeftShift) && Math.Sign(targetMovement.X) == Math.Sign(animController.Dir)) + targetMovement *= 3.0f; + + selectKeyHit.SetState(PlayerInput.KeyHit(Keys.E)); + actionKeyHit.SetState(PlayerInput.LeftButtonClicked()); + actionKeyDown.SetState(PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed); + secondaryKeyHit.SetState(PlayerInput.RightButtonClicked()); + secondaryKeyDown.SetState(PlayerInput.GetMouseState.RightButton == ButtonState.Pressed); + } + else + { + selectKeyHit.SetState(false); + actionKeyHit.SetState(false); + actionKeyDown.SetState(false); + secondaryKeyHit.SetState(false); + secondaryKeyDown.SetState(false); + } + + animController.TargetMovement = targetMovement; + animController.isStanding = true; + + if (moveCam) + { + cam.TargetPos = ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition); + cam.OffsetAmount = 250.0f; + } + + cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition); + Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition); + + if (animController.onGround && + !animController.InWater && + animController.anim != AnimController.Animation.UsingConstruction) + { + if (mouseSimPos.X < head.SimPosition.X-1.0f) + { + animController.targetDir = Direction.Left; + } + else if (mouseSimPos.X > head.SimPosition.X + 1.0f) + { + animController.targetDir = Direction.Right; + } + } + + disableControls = false; + } + + + public static void UpdateAnimAll(float deltaTime) + { + foreach (Character c in characterList) + { + if (c.isDead) continue; + c.animController.UpdateAnim(deltaTime); + } + } + + public static void UpdateAll(Camera cam, float deltaTime) + { + if (newCharacterQueue.Count>0) + { + new Character(newCharacterQueue.Dequeue(), Vector2.Zero); + } + + foreach (Character c in characterList) + { + c.Update(cam, deltaTime); + } + } + + public void Update(Camera cam, float deltaTime) + { + if (isDead) return; + + Debug.WriteLine("pp: " + PressureProtection); + if (PressureProtection==0.0f && + (animController.CurrentHull == null || animController.CurrentHull.LethalPressure >= 100.0f)) + { + Implode(); + return; + } + + if (controlled == this) ControlLocalPlayer(cam); + + Control(cam); + + UpdateSightRange(); + aiTarget.SoundRange = 0.0f; + + if (needsAir) + { + if (animController.HeadInWater) + { + Oxygen -= deltaTime*100.0f / drowningTime; + } + else if (animController.CurrentHull != null) + { + float hullOxygen = animController.CurrentHull.OxygenPercentage; + hullOxygen -= 30.0f; + + Oxygen += deltaTime * 100.0f * (hullOxygen / 500.0f); + + animController.CurrentHull.Oxygen -= Hull.OxygenConsumptionSpeed * deltaTime; + } + PressureProtection -= deltaTime*100.0f; + } + + if (soundTimer > 0) + { + soundTimer -= deltaTime; + } + else + { + PlaySound((aiController == null) ? AIController.AiState.None : aiController.State); + soundTimer = soundInterval; + } + + foreach (Limb limb in animController.limbs) + { + Blood = blood - limb.Bleeding * deltaTime * 0.1f; + } + + if (aiController != null) aiController.Update(deltaTime); + } + + private void UpdateSightRange() + { + aiTarget.SightRange = 0.0f; + + //distance is approximated based on the mass of the character + //(which corresponds to size because all the characters have the same limb density) + foreach (Limb limb in animController.limbs) + { + aiTarget.SightRange += limb.Mass * 1000.0f; + } + //the faster the character is moving, the easier it is to see it + Limb torso = animController.GetLimb(LimbType.Torso); + if (torso !=null) + { + aiTarget.SightRange += torso.LinearVelocity.Length() * 500.0f; + } + } + + public void Draw(SpriteBatch spriteBatch) + { + animController.Draw(spriteBatch); + + if (IsNetworkPlayer) + { + Vector2 pos = new Vector2(Position.X, -Position.Y - 50.0f) - GUI.font.MeasureString(info.name) * 0.5f; + spriteBatch.DrawString(GUI.font, info.name, pos - new Vector2(1.0f, 1.0f), Color.Black); + spriteBatch.DrawString(GUI.font, info.name, pos, Color.White); + } + + //spriteBatch.DrawString(GUI.font, ID.ToString(), ConvertUnits.ToDisplayUnits(animController.limbs[0].Position), Color.White); + //GUI.DrawLine(spriteBatch, ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition.X, animController.limbs[0].SimPosition.Y), + // ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition.X, animController.limbs[0].SimPosition.Y) + + // ConvertUnits.ToDisplayUnits(animController.targetMovement.X, animController.targetMovement.Y), Color.Green); + } + + public void PlaySound(AIController.AiState state) + { + if (sounds == null || sounds.Count()==0) return; + var matchingSoundStates = soundStates.Where(x => x == state).ToList(); + + int selectedSound = Game1.localRandom.Next(matchingSoundStates.Count()); + + int n = 0; + for (int i = 0; i < sounds.Count(); i++) + { + if (soundStates[i] != state) continue; + if (n == selectedSound) + { + sounds[i].Play(1.0f, 2000.0f, + animController.limbs[0].body.FarseerBody); + Debug.WriteLine("playing: " + sounds[i]); + return; + } + n++; + } + } + + public void AddDamage(Vector2 position, float amount, float bleedingAmount, float stun) + { + int bloodAmount = 0; + + animController.StunTimer = Math.Max(animController.StunTimer, stun); + + Limb closestLimb = null; + float closestDistance = 0.0f; + foreach (Limb limb in animController.limbs) + { + float distance = Vector2.Distance(position, limb.SimPosition); + if (closestLimb == null || distance < closestDistance) + { + closestLimb = limb; + closestDistance = distance; + } + } + + Vector2 pull = position - closestLimb.SimPosition; + if (pull != Vector2.Zero) pull = Vector2.Normalize(pull); + closestLimb.body.ApplyForce(pull*Math.Min(amount*100.0f, 100.0f)); + + closestLimb.Bleeding += bleedingAmount; + closestLimb.Damage += amount; + bloodAmount = (int)Math.Min((int)(amount*2.0f),20); + //if (closestLimb.Damage>=100.0f) + //{ + // bloodAmount *= 2; + // foreach (var joint in animController.limbJoints) + // { + // if (!(joint.BodyA == closestLimb.body.FarseerBody) && !(joint.BodyB == closestLimb.body.FarseerBody)) continue; + + // joint.Enabled = false; + // break; + // } + //} + + for (int i = 0; i < bloodAmount; i++) + { + Vector2 particleVel = closestLimb.SimPosition-position; + if (particleVel != Vector2.Zero) particleVel = Vector2.Normalize(particleVel); + + Game1.particleManager.CreateParticle( + closestLimb.SimPosition, + ToolBox.RandomFloat(0.0f, 3.1f), + particleVel * ToolBox.RandomFloat(1.0f,3.0f), + "blood"); + } + + for (int i = 0; i < bloodAmount / 2; i++) + { + Game1.particleManager.CreateParticle(closestLimb.SimPosition, + 0.0f, + Vector2.Zero, "waterblood"); + } + } + + public void Stun() + { + for (int i = 0; i < selectedItems.Length; i++ ) + { + if (selectedItems[i] == null) continue; + selectedItems[i].Drop(); + selectedItems[i] = null; + } + + selectedConstruction = null; + } + + private void Implode() + { + Limb torso= animController.GetLimb(LimbType.Torso); + if (torso == null) torso = animController.GetLimb(LimbType.Head); + + Vector2 centerOfMass = Vector2.Zero; + float totalMass = 0.0f; + foreach (Limb limb in animController.limbs) + { + centerOfMass += limb.Mass * limb.SimPosition; + totalMass += limb.Mass; + } + + centerOfMass /= totalMass; + + foreach (Limb limb in animController.limbs) + { + Vector2 diff = centerOfMass - limb.SimPosition; + if (diff == Vector2.Zero) continue; + limb.body.ApplyLinearImpulse(diff * 10.0f); + limb.Damage = 100.0f; + } + + AmbientSoundManager.PlayDamageSound(DamageSoundType.Implode, 50.0f, torso.body.FarseerBody); + + for (int i = 0; i < 10; i++) + { + Particle p = Game1.particleManager.CreateParticle( + torso.SimPosition + new Vector2(ToolBox.RandomFloat(-0.5f, 0.5f), ToolBox.RandomFloat(-0.5f, 0.5f)), + 0.0f, + Vector2.Zero, "waterblood"); + if (p!=null) p.Size *= 2.0f; + + Game1.particleManager.CreateParticle( + torso.SimPosition, + 0.0f, + new Vector2(ToolBox.RandomFloat(-0.5f, 0.5f), ToolBox.RandomFloat(-1.0f,0.5f)), + "bubbles"); + } + + foreach (var joint in animController.limbJoints) + { + joint.LimitEnabled = false; + } + Kill(true); + } + + public void Kill(bool networkMessage = false) + { + if (isDead) return; + + //if the game is run by a client, characters are only killed when the server says so + if (Game1.client != null) + { + if (networkMessage) + { + new NetworkEvent(NetworkEventType.KillCharacter, ID, true); + } + else + { + return; + } + } + + if (Game1.server != null) + { + new NetworkEvent(NetworkEventType.KillCharacter, ID, false); + } + + if (Game1.gameSession.crewManager!=null) + { + Game1.gameSession.crewManager.KillCharacter(this); + } + + isDead = true; + animController.movement = Vector2.Zero; + animController.TargetMovement = Vector2.Zero; + + for (int i = 0; i < selectedItems.Length; i++ ) + { + if (selectedItems[i] != null) selectedItems[i].Drop(this); + } + + + aiTarget.Remove(); + aiTarget = null; + + foreach (Limb limb in animController.limbs) + { + if (limb.pullJoint == null) continue; + limb.pullJoint.Enabled = false; + } + + foreach (RevoluteJoint joint in animController.limbJoints) + { + joint.MotorEnabled = false; + joint.MaxMotorTorque = 0.0f; + } + } + + public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + { + if (type == NetworkEventType.PickItem) + { + message.Write((int)data); + Debug.WriteLine("pickitem"); + return; + } + else if (type == NetworkEventType.KillCharacter) + { + return; + } + + + //if (type == Networking.NetworkEventType.KeyHit) + //{ + // message.Write(selectKeyHit.Dequeue); + message.Write(actionKeyDown.Dequeue); + message.Write(secondaryKeyDown.Dequeue); + //} + + message.Write(NetTime.Now); + + // Write byte = move direction + message.Write(animController.TargetMovement.X); + message.Write(animController.TargetMovement.Y); + + message.Write(animController.targetDir==Direction.Right); + + message.Write(cursorPosition.X); + message.Write(cursorPosition.Y); + + message.Write(largeUpdateTimer <= 0); + + if (largeUpdateTimer<=0) + { + foreach (Limb limb in animController.limbs) + { + message.Write(limb.body.Position.X); + message.Write(limb.body.Position.Y); + + message.Write(limb.body.LinearVelocity.X); + message.Write(limb.body.LinearVelocity.Y); + + message.Write(limb.body.Rotation); + message.Write(limb.body.AngularVelocity); + } + + message.Write(animController.StunTimer); + + largeUpdateTimer = 5; + } + else + { + Limb torso = animController.GetLimb(LimbType.Torso); + message.Write(torso.body.Position.X); + message.Write(torso.body.Position.Y); + + largeUpdateTimer = (byte)Math.Max(0, largeUpdateTimer-1); + } + + + + if (aiController != null) aiController.FillNetworkData(message); + + } + + public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) + { + if (type == NetworkEventType.PickItem) + { + int itemId = message.ReadInt32(); + Item item = FindEntityByID(itemId) as Item; + if (item != null) + { + Debug.WriteLine("pickitem "+itemId ); + item.Pick(this); + } + else + { + } + + //DebugConsole.ThrowError("pickitem"); + + return; + } + else if (type == NetworkEventType.KillCharacter) + { + Kill(true); + if (Game1.client != null && controlled == this) + { + Game1.client.AddChatMessage("YOU HAVE DIED. Your chat messages will only be visible to other dead players.", ChatMessageType.Dead); + } + return; + } + + //if (type == Networking.NetworkEventType.KeyHit) + //{ + // selectKeyHit.State = message.ReadBoolean(); + + //} + actionKeyDown.State = message.ReadBoolean(); + secondaryKeyDown.State = message.ReadBoolean(); + + double sendingTime = message.ReadDouble(); + + Vector2 targetMovement = Vector2.Zero; + + targetMovement.X = message.ReadFloat(); + targetMovement.Y = message.ReadFloat(); + + animController.isStanding = true; + + bool targetDir = message.ReadBoolean(); + + Vector2 cursorPos = Vector2.Zero; + cursorPos.X = message.ReadFloat(); + cursorPos.Y = message.ReadFloat(); + + if (sendingTime > lastNetworkUpdate) + { + cursorPosition = cursorPos; + + animController.TargetMovement= targetMovement; + animController.targetDir = (targetDir) ? Direction.Right : Direction.Left; + + if (message.ReadBoolean()) + { + foreach (Limb limb in animController.limbs) + { + Vector2 pos = Vector2.Zero; + pos.X = message.ReadFloat(); + pos.Y = message.ReadFloat(); + + Vector2 vel = Vector2.Zero; + vel.X = message.ReadFloat(); + vel.Y = message.ReadFloat(); + + float rotation = message.ReadFloat(); + float angularVel = message.ReadFloat(); + + if (limb.body == null) continue; + + if (vel != Vector2.Zero && vel.Length() > 100.0f) { } + + if (pos != Vector2.Zero && pos.Length() > 100.0f) { } + + limb.body.TargetVelocity = vel; + limb.body.TargetPosition = pos;// +vel * (float)(deltaTime / 60.0); + limb.body.TargetRotation = rotation;// +angularVel * (float)(deltaTime / 60.0); + limb.body.TargetAngularVelocity = angularVel; + } + + animController.StunTimer = message.ReadFloat(); + + largeUpdateTimer = 1; + } + else + { + Vector2 pos = Vector2.Zero; + pos.X = message.ReadFloat(); + pos.Y = message.ReadFloat(); + + Limb torso = animController.GetLimb(LimbType.Torso); + torso.body.TargetPosition = pos; + + largeUpdateTimer = 0; + } + + if (aiController != null) aiController.ReadNetworkData(message); + + lastNetworkUpdate = sendingTime; + } + } + + public override void Remove() + { + base.Remove(); + + characterList.Remove(this); + + if (controlled == this) controlled = null; + + if (Game1.client!=null && Game1.client.Character == this) Game1.client.Character = null; + + if (aiTarget != null) + aiTarget.Remove(); + + if (animController!=null) + animController.Remove(); + } + + } +} diff --git a/Subsurface/Characters/CharacterInfo.cs b/Subsurface/Characters/CharacterInfo.cs new file mode 100644 index 000000000..e6ae2ab79 --- /dev/null +++ b/Subsurface/Characters/CharacterInfo.cs @@ -0,0 +1,76 @@ +using System.Xml.Linq; + +namespace Subsurface +{ + public enum Gender { None, Male, Female }; + + class CharacterInfo + { + //the name of the character (e.q. Urist McEngineer) + public string name; + + public readonly string file; + + public int ID; + + public Gender gender; + + public int salary; + + public string GenderString() + { + return gender.ToString(); + } + + public CharacterInfo(string file, string name = "", Gender gender = Gender.None) + { + this.file = file; + + ID = -1; + + XDocument doc = ToolBox.TryLoadXml(file); + if (doc == null) return; + + salary = 500; + + if (ToolBox.GetAttributeBool(doc.Root, "genders", false)) + { + if (gender==Gender.None) + { + float femaleRatio = ToolBox.GetAttributeFloat(doc.Root, "femaleratio", 0.5f); + this.gender = (Game1.random.NextDouble() < femaleRatio) ? Gender.Female : Gender.Male; + } + else + { + this.gender = gender; + } + } + + if (!string.IsNullOrEmpty(name)) + { + this.name = name; + return; + } + + if (doc.Root.Element("name") != null) + { + string firstNamePath = (ToolBox.GetAttributeString(doc.Root.Element("name"), "firstname", "")); + if (firstNamePath != "") + { + firstNamePath = firstNamePath.Replace("[GENDER]", (this.gender == Gender.Female) ? "f" : ""); + this.name = ToolBox.GetRandomLine(firstNamePath); + } + + string lastNamePath = (ToolBox.GetAttributeString(doc.Root.Element("name"), "lastname", "")); + if (lastNamePath != "") + { + lastNamePath = lastNamePath.Replace("[GENDER]", (this.gender == Gender.Female) ? "f" : ""); + if (this.name != "") this.name += " "; + this.name += ToolBox.GetRandomLine(lastNamePath); + } + } + + + } + } +} diff --git a/Subsurface/Characters/DelayedEffect.cs b/Subsurface/Characters/DelayedEffect.cs new file mode 100644 index 000000000..c4231fff4 --- /dev/null +++ b/Subsurface/Characters/DelayedEffect.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Subsurface +{ + class DelayedEffect : StatusEffect + { + public static List list = new List(); + + float delay; + + float timer; + + private Item item; + + private Character character; + + private Limb limb; + + public float Timer + { + get { return timer; } + } + + public DelayedEffect(XElement element) + : base(element) + { + delay = ToolBox.GetAttributeFloat(element, "delay", 1.0f); + } + + public override void Apply(ActionType type, float deltaTime, Item item, Character character = null, Limb limb = null) + { + if (this.type != type) return; + + this.item = item; + this.character = character; + this.limb = limb; + + this.timer = delay; + + list.Add(this); + } + + public void Update(float deltaTime) + { + timer -= deltaTime; + + if (timer > 0.0f) return; + + base.Apply(1.0f, character, item, limb); + list.Remove(this); + } + + } +} diff --git a/Subsurface/Characters/FishAnimController.cs b/Subsurface/Characters/FishAnimController.cs new file mode 100644 index 000000000..c911bb09e --- /dev/null +++ b/Subsurface/Characters/FishAnimController.cs @@ -0,0 +1,353 @@ +using System; +using System.Linq; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics.Joints; +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + class FishAnimController : AnimController + { + //amplitude and wave length of the "sine wave" swimming animation + //if amplitude = 0, sine wave animation isn't used + private float waveAmplitude; + private float waveLength; + + private bool flip; + + public FishAnimController(Character character, XElement element) + : base(character, element) + { + waveAmplitude = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "waveamplitude", 0.0f)); + waveLength = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "wavelength", 0.0f)); + + flip = ToolBox.GetAttributeBool(element, "flip", false); + + walkSpeed = ToolBox.GetAttributeFloat(element, "walkspeed", 1.0f); + swimSpeed = ToolBox.GetAttributeFloat(element, "swimspeed", 1.0f); + } + + public override void UpdateAnim(float deltaTime) + { + ResetPullJoints(); + + if (strongestImpact > 0.0f) + { + stunTimer = MathHelper.Clamp(strongestImpact * 0.5f, stunTimer, 5.0f); + strongestImpact = 0.0f; + } + + if (stunTimer>0.0f) + { + UpdateStruggling(); + stunTimer -= deltaTime; + return; + } + else + { + if (inWater) + { + UpdateSineAnim(deltaTime); + } + else + { + UpdateWalkAnim(deltaTime); + } + } + + + if (flip) + { + //targetDir = (movement.X > 0.0f) ? Direction.Right : Direction.Left; + if (movement.X > 0.1f && movement.X > Math.Abs(movement.Y)) + { + targetDir = Direction.Right; + } + else if (movement.X < -0.1f && movement.X < -Math.Abs(movement.Y)) + { + targetDir = Direction.Left; + } + } + else + { + Limb head = GetLimb(LimbType.Head); + float rotation = ToolBox.WrapAngleTwoPi(head.Rotation); + rotation = MathHelper.ToDegrees(rotation); + + if (rotation < 0.0f) rotation += 360; + + if (rotation > 20 && rotation < 160) + { + targetDir = Direction.Left; + } + else if (rotation > 200 && rotation < 340) + { + targetDir = Direction.Right; + } + } + + //if (stunTimer > gameTime.TotalGameTime.TotalMilliseconds) return; + + if (targetDir != dir) + { + Flip(); + if (flip) Mirror(); + } + } + + void UpdateSineAnim(float deltaTime) + { + movement = ToolBox.SmoothStep(movement, TargetMovement*swimSpeed, 1.0f); + if (movement == Vector2.Zero) return; + + if (!inWater) movement.Y = Math.Min(0.0f, movement.Y); + + float movementAngle = ToolBox.VectorToAngle(movement)+MathHelper.PiOver2; + + Limb tail = GetLimb(LimbType.Tail); + if (tail != null && waveAmplitude>0.0f) + { + walkPos -= movement.Length(); + + float waveRotation = (float)Math.Sin(walkPos / waveLength)*waveAmplitude; + + float angle = ToolBox.GetShortestAngle(tail.body.Rotation, movementAngle + waveRotation); + + //limbs[tailIndex].body.ApplyTorque((Math.Sign(angle) + Math.Max(Math.Min(angle * 10.0f, 10.0f), -10.0f)) * limbs[tailIndex].body.Mass); + //limbs[tailIndex].body.ApplyTorque(-limbs[tailIndex].body.AngularVelocity * 0.5f * limbs[tailIndex].body.Mass); + } + + Vector2 steerForce = Vector2.Zero; + + Limb head = GetLimb(LimbType.Head); + if (head != null) + { + float angle = ToolBox.GetShortestAngle(head.body.Rotation, movementAngle); + + + head.body.SmoothRotate(head.body.Rotation+angle, 25.0f); + + //rotate head towards the angle of movement + //float torque = (Math.Sign(angle)*10.0f + MathHelper.Clamp(angle * 10.0f, -10.0f, 10.0f)); + //angular drag + //torque -= head.body.AngularVelocity * 0.5f; + //head.body.ApplyTorque(torque * head.body.Mass); + + + //the movement vector if going to the direction of the head + //Vector2 headMovement = new Vector2( + // (float)Math.Cos(head.body.Rotation - MathHelper.PiOver2), + // (float)Math.Sin(head.body.Rotation - MathHelper.PiOver2)); + //headMovement *= movement.Length(); + + //the movement angle is between direction of the head and the direction + //where the character is actually trying to go + + //current * (float)alpha + previous * (1.0f - (float)alpha); + + + steerForce = (movement * 50.0f - head.LinearVelocity * 30.0f); + // force += (headMovement - movement) * Math.Min(head.LinearVelocity.Length()/movement.Length(), 1.0f); + + if (!inWater) steerForce.Y = 0.0f; + } + + for (int i = 0; i < limbs.Count(); i++) + { + if (steerForce!=Vector2.Zero) + limbs[i].body.ApplyForce(steerForce * limbs[i].SteerForce * limbs[i].Mass); + + if (limbs[i].type != LimbType.Torso) continue; + + float dist = (limbs[0].SimPosition - limbs[i].SimPosition).Length(); + + Vector2 limbPos = limbs[0].SimPosition - Vector2.Normalize(movement) * dist; + + limbs[i].body.ApplyForce(((limbPos - limbs[i].SimPosition) * 3.0f - limbs[i].LinearVelocity * 3.0f) * limbs[i].Mass); + } + + if (!inWater) + { + UpdateWalkAnim(deltaTime); + } + else + { + floorY = limbs[0].SimPosition.Y; + } + } + + void UpdateWalkAnim(float deltaTime) + { + movement = ToolBox.SmoothStep(movement, TargetMovement * walkSpeed, 0.2f); + if (movement == Vector2.Zero) return; + + Limb colliderLimb; + float colliderHeight; + + Limb torso = GetLimb(LimbType.Torso); + Limb head = GetLimb(LimbType.Head); + + if (torso!=null) + { + colliderLimb = torso; + colliderHeight = TorsoPosition; + + colliderLimb.body.SmoothRotate(TorsoAngle*Dir, 5.0f); + } + else + { + colliderLimb = head; + colliderHeight = HeadPosition; + + colliderLimb.body.SmoothRotate(HeadAngle*Dir, 10.0f); + } + + Vector2 colliderPos = colliderLimb.SimPosition; + + Vector2 rayStart = colliderPos; + Vector2 rayEnd = rayStart - new Vector2(0.0f, colliderHeight); + if (stairs != null) rayEnd.Y -= 0.5f; + + //do a raytrace straight down from the torso to figure + //out whether the ragdoll is standing on ground + float closestFraction = 1; + //Structure closestStructure = null; + Game1.world.RayCast((fixture, point, normal, fraction) => + { + //other limbs and bodies with no collision detection are ignored + if (fixture == null || + fixture.CollisionCategories == Physics.CollisionCharacter || + fixture.CollisionCategories == Physics.CollisionNone || + fixture.CollisionCategories == Physics.CollisionMisc) return -1; + + Structure structure = fixture.Body.UserData as Structure; + if (structure != null) + { + if (structure.StairDirection != Direction.None && (stairs == null)) return -1; + if (structure.IsPlatform && (IgnorePlatforms || stairs != null)) return -1; + } + + onGround = true; + onFloorTimer = 0.05f; + + if (fraction < closestFraction) closestFraction = fraction; + return 1; + } + , rayStart, rayEnd); + + //the ragdoll "stays on ground" for 50 millisecs after separation + if (onFloorTimer <= 0.0f) + { + onGround = false; + } + else + { + onFloorTimer -= deltaTime; + } + + if (closestFraction == 1) //raycast didn't hit anything + floorY = (currentHull == null || onGround) ? -1000.0f : ConvertUnits.ToSimUnits(currentHull.Rect.Y - currentHull.Rect.Height); + else + floorY = rayStart.Y + (rayEnd.Y - rayStart.Y) * closestFraction; + + if (Math.Abs(colliderPos.Y - floorY)-1) + { + RevoluteJoint refJoint = limbJoints[limb.RefJointIndex]; + footPos.X = refJoint.WorldAnchorA.X; + } + footPos.X += stepOffset.X * Dir; + footPos.Y += stepOffset.Y; + + if (limb.type == LimbType.LeftFoot) + { + limb.Move(footPos +new Vector2( + transformedStepSize.X + movement.X * 0.1f, + (transformedStepSize.Y > 0.0f) ? transformedStepSize.Y : 0.0f), + 8.0f); + } + else if (limb.type == LimbType.RightFoot) + { + limb.Move(footPos +new Vector2( + -transformedStepSize.X + movement.X * 0.1f, + (-transformedStepSize.Y > 0.0f) ? -transformedStepSize.Y : 0.0f), + 8.0f); + } + break; + case LimbType.LeftLeg: + case LimbType.RightLeg: + if (legTorque!=0.0f) limb.body.ApplyTorque(limb.Mass*legTorque*Dir); + break; + } + } + } + + void UpdateStruggling() + { + Limb head = GetLimb(LimbType.Head); + Limb tail = GetLimb(LimbType.Tail); + + head.body.ApplyTorque(head.Mass * Dir * 0.1f); + tail.body.ApplyTorque(tail.Mass * -Dir * 0.1f); + } + + public override void Flip() + { + base.Flip(); + + foreach (Limb l in limbs) + { + if (!l.DoesFlip) continue; + + l.body.SetTransform(l.SimPosition, + -l.body.Rotation); + } + } + + private void Mirror() + { + float leftX = limbs[0].SimPosition.X, rightX = limbs[0].SimPosition.X; + for (int i = 1; i < limbs.Count(); i++ ) + { + if (limbs[i].SimPosition.X < leftX) + { + leftX = limbs[i].SimPosition.X; + } + else if (limbs[i].SimPosition.X > rightX) + { + rightX = limbs[i].SimPosition.X; + } + } + + float midX = (leftX + rightX) / 2.0f; + + foreach (Limb l in limbs) + { + Vector2 newPos = new Vector2(midX - (l.SimPosition.X - midX), l.SimPosition.Y); + l.body.SetTransform(newPos, l.body.Rotation); + } + } + + } +} diff --git a/Subsurface/Characters/HumanoidAnimController.cs b/Subsurface/Characters/HumanoidAnimController.cs new file mode 100644 index 000000000..7cf77748b --- /dev/null +++ b/Subsurface/Characters/HumanoidAnimController.cs @@ -0,0 +1,787 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Xml.Linq; +using FarseerPhysics; +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + class HumanoidAnimController : AnimController + { + public HumanoidAnimController(Character character, XElement element) + : base(character, element) + { + } + + public override void UpdateAnim(float deltaTime) + { + Vector2 colliderPos = GetLimb(LimbType.Torso).SimPosition; + + Vector2 rayStart = colliderPos; // at the bottom of the player sprite + Vector2 rayEnd = rayStart - new Vector2(0.0f, TorsoPosition); + if (stairs != null) rayEnd.Y -= 0.5f; + if (anim != Animation.UsingConstruction) ResetPullJoints(); + + //do a raytrace straight down from the torso to figure + //out whether the ragdoll is standing on ground + float closestFraction = 1; + Structure closestStructure = null; + Game1.world.RayCast((fixture, point, normal, fraction) => + { + switch (fixture.CollisionCategories) + { + case Physics.CollisionStairs: + Structure structure = fixture.Body.UserData as Structure; + if (stairs == null) + { + if (LowestLimb.SimPosition.Y 0.0f) + { + character.Stun(); + stunTimer = MathHelper.Clamp(strongestImpact * 0.5f, stunTimer, 5.0f); + } + strongestImpact = 0.0f; + + if (stunTimer > 0) + { + UpdateStruggling(); + stunTimer -= deltaTime; + return; + } + + switch (anim) + { + case Animation.Climbing: + UpdateClimbing(); + break; + case Animation.UsingConstruction: + break; + default: + if (inWater) + UpdateSwimming(); + else if (isStanding) + UpdateStanding(); + + break; + } + + if (targetDir != dir) Flip(); + + foreach (Limb limb in limbs) + { + limb.Disabled = false; + } + + } + + void UpdateStanding() + { + Vector2 handPos; + + Limb leftFoot = GetLimb(LimbType.LeftFoot); + Limb rightFoot = GetLimb(LimbType.RightFoot); + Limb head = GetLimb(LimbType.Head); + Limb torso = GetLimb(LimbType.Torso); + + Limb leftHand = GetLimb(LimbType.LeftHand); + Limb rightHand = GetLimb(LimbType.RightHand); + + Limb leftLeg = GetLimb(LimbType.LeftLeg); + Limb rightLeg = GetLimb(LimbType.RightLeg); + + float getUpSpeed = 0.3f; + float walkCycleSpeed = head.LinearVelocity.X * 0.08f; + if (stairs != null) + { + if (TargetMovement!=Vector2.Zero && TargetMovement.Length()>3.0f) + { + TargetMovement /= TargetMovement.Length() / 3.0f; + } + walkCycleSpeed *= 1.5f; + } + + Vector2 colliderPos = new Vector2(torso.SimPosition.X, floorY); + + float walkPosX = (float)Math.Cos(walkPos); + float walkPosY = (float)Math.Sin(walkPos); + float runningModifier = (float)Math.Max(Math.Abs(movement.X) / 1.5f, 1.0); + + Vector2 stepSize = new Vector2( + this.stepSize.X * walkPosX * runningModifier, + this.stepSize.Y * walkPosY * runningModifier * runningModifier); + + float footMid = (leftFoot.SimPosition.X + rightFoot.SimPosition.X) / 2.0f; + + movement = ToolBox.SmoothStep(movement, TargetMovement, 0.5f); + movement.Y = 0.0f; + + //place the anchors of the head and the torso to make the ragdoll stand + if (onGround && LowestLimb != null && (LowestLimb.SimPosition.Y-floorY < 0.5f || stairs != null) && head !=null) + { + getUpSpeed = Math.Max(getUpSpeed * (head.SimPosition.Y - colliderPos.Y), 0.25f); + + if (stairs != null) + { + if (LowestLimb.SimPosition.Y < stairs.SimPosition.Y) IgnorePlatforms = true; + + torso.pullJoint.Enabled = true; + torso.pullJoint.WorldAnchorB = new Vector2( + MathHelper.SmoothStep(torso.SimPosition.X, footMid + movement.X * 0.35f, getUpSpeed * 0.8f), + MathHelper.SmoothStep(torso.SimPosition.Y, colliderPos.Y + TorsoPosition - Math.Abs(walkPosX * 0.05f), getUpSpeed * 3.0f)); + + + head.pullJoint.Enabled = true; + head.pullJoint.WorldAnchorB = new Vector2( + MathHelper.SmoothStep(head.SimPosition.X, footMid + movement.X * 0.4f, getUpSpeed * 0.8f), + MathHelper.SmoothStep(head.SimPosition.Y, colliderPos.Y + HeadPosition - Math.Abs(walkPosX * 0.05f), getUpSpeed * 3.0f)); + } + else + { + torso.pullJoint.Enabled = true; + torso.pullJoint.WorldAnchorB = + ToolBox.SmoothStep(torso.SimPosition, + new Vector2(footMid + movement.X * 0.35f, colliderPos.Y + TorsoPosition - Math.Abs(walkPosX * 0.05f)), getUpSpeed); + + head.pullJoint.Enabled = true; + head.pullJoint.WorldAnchorB = + ToolBox.SmoothStep(head.SimPosition, + new Vector2(footMid + movement.X * 0.4f, colliderPos.Y + HeadPosition - Math.Abs(walkPosX * 0.05f)), getUpSpeed); + } + + + //moving horizontally + if (TargetMovement.X != 0.0f) + { + //progress the walking animation + walkPos -= (walkCycleSpeed / runningModifier); + + MoveLimb(leftFoot, + colliderPos + new Vector2( + stepSize.X, + (stepSize.Y > 0.0f) ? stepSize.Y : -0.15f), + 10.0f, true); + + MoveLimb(rightFoot, + colliderPos + new Vector2( + -stepSize.X, + (-stepSize.Y > 0.0f) ? -stepSize.Y : -0.15f), + 10.0f, true); + + if (Math.Sign(stepSize.X) == Math.Sign(Dir)) + { + leftFoot.body.SmoothRotate(leftLeg.body.Rotation + MathHelper.PiOver2 * Dir * 1.3f, 20.0f * runningModifier); + } + else if (Math.Sign(-stepSize.X) == Math.Sign(Dir)) + { + rightFoot.body.SmoothRotate(rightLeg.body.Rotation + MathHelper.PiOver2 * Dir * 1.3f, 20 * runningModifier); + } + + if (walkPosY > 0.0f) + { + GetLimb(LimbType.LeftThigh).body.ApplyTorque(-walkPosY * Dir * Math.Abs(movement.X) * -2.0f); + } + else + { + GetLimb(LimbType.RightThigh).body.ApplyTorque(walkPosY * Dir * Math.Abs(movement.X) * -2.0f); + } + + //calculate the positions of hands + handPos = torso.SimPosition; + handPos.X = -walkPosX * 0.25f; + + float lowerY = -0.4f / runningModifier; + handPos.Y = lowerY + (float)(Math.Abs(Math.Sin(walkPos - Math.PI * 1.5f) * 0.1)) * runningModifier; + + Vector2 posAdditon = new Vector2(movement.X*0.05f, 0.0f); + if (stairs!=null) + { + if ((stairs.StairDirection == Direction.Right && movement.X < 0.0f) || + (stairs.StairDirection == Direction.Left && movement.X > 0.0f)) + { + posAdditon.Y -= 0.1f; + } + else + { + posAdditon.Y += 0.1f; + } + } + + + if (!rightHand.Disabled) + { + rightHand.body.ApplyTorque(walkPosY * runningModifier * Dir); + MoveLimb(rightHand, torso.SimPosition + posAdditon + + new Vector2( + -handPos.X, + (Math.Sign(walkPosX) == Math.Sign(Dir)) ? handPos.Y : lowerY), + 15.0f, true); + } + + if (!leftHand.Disabled) + { + leftHand.body.ApplyTorque(-walkPosY * runningModifier * Dir); + MoveLimb(leftHand, torso.SimPosition + posAdditon + + new Vector2( + handPos.X, + (Math.Sign(walkPosX) == Math.Sign(-Dir)) ? handPos.Y : lowerY), + 15.0f, true); + } + + } + else + { + //add torque to the head to do a subtle "breathing" effect + //head.body.ApplyTorque((float)Math.Sin(gameTime.TotalGameTime.TotalMilliseconds / 300) * 0.2f); + + //standing still -> "attach" the feet to the ground + + float movementFactor = (movement.X / 4.0f) * movement.X * Math.Sign(movement.X); + + Vector2 footPos = new Vector2( + colliderPos.X + movementFactor - Dir * 0.05f, + colliderPos.Y - 0.2f - Math.Abs(movementFactor)); + + MoveLimb(leftFoot, footPos, 2.5f); + MoveLimb(rightFoot, footPos, 2.5f); + + leftFoot.body.SmoothRotate(Dir * MathHelper.PiOver2, 5.0f); + rightFoot.body.SmoothRotate(Dir * MathHelper.PiOver2, 5.0f); + + handPos = torso.SimPosition; + handPos.X += movement.X; + handPos.Y -= 0.4f; + if (!rightHand.Disabled) + { + MoveLimb(rightHand, handPos, 1.5f, true); + } + + if (!leftHand.Disabled) + { + MoveLimb(leftHand, handPos, 1.5f, true); + } + + } + + + } + + for (int i = 0; i < 2; i++) + { + Limb leg = (i == 0) ? rightFoot : leftFoot; + + if (leg.SimPosition.Y < torso.SimPosition.Y) continue; + + leg.body.ApplyTorque(-Dir * leg.Mass * 20.0f); + } + + } + + void UpdateSwimming() + { + IgnorePlatforms = true; + + Vector2 footPos, handPos; + + float surfaceLimiter = 1.0f; + + Limb head = GetLimb(LimbType.Head); + + if (currentHull != null && currentHull.Volume < currentHull.FullVolume) + { + surfaceLimiter = (ConvertUnits.ToDisplayUnits(head.SimPosition.Y)-surfaceY); + surfaceLimiter = Math.Max(1.0f, surfaceLimiter); + if (surfaceLimiter > 20.0f) return; + } + + Limb leftFoot = GetLimb(LimbType.LeftFoot); + Limb rightFoot = GetLimb(LimbType.RightFoot); + Limb torso = GetLimb(LimbType.Torso); + Limb leftHand = GetLimb(LimbType.LeftHand); + Limb rightHand = GetLimb(LimbType.RightHand); + + + float rotation = MathHelper.WrapAngle(torso.Rotation); + rotation = MathHelper.ToDegrees(rotation); + if (rotation < 0.0f) rotation += 360; + + if (!character.IsNetworkPlayer) + { + if (rotation > 20 && rotation < 170) + targetDir = Direction.Left; + else if (rotation > 190 && rotation < 340) + targetDir = Direction.Right; + } + + if (TargetMovement == Vector2.Zero) return; + + float targetSpeed = TargetMovement.Length(); + if (targetSpeed > 0.0f) TargetMovement /= targetSpeed; + + //if trying to head to the opposite direction, apply torque + //to the torso to flip the ragdoll around + if (Math.Sign(TargetMovement.X) != Dir && TargetMovement.X != 0.0f) + { + float torque = torso.Mass * 10.0f; + torque *= (rotation > 90 && rotation < 270) ? -Dir : Dir; + + torso.body.ApplyTorque(torque); + } + + movement = ToolBox.SmoothStep(movement, TargetMovement, 0.3f); + + //dont try to move upwards if head is already out of water + if (surfaceLimiter > 1.0f) + { + if (TargetMovement.X == 0.0f) + { + head.body.ApplyForce(head.Mass * new Vector2(-Dir * 5.1f, -5.0f)); + torso.body.ApplyForce(torso.Mass *new Vector2(-Dir*5.1f, -15.0f)); + leftFoot.body.ApplyForce(leftFoot.Mass *new Vector2(0.0f, -80.0f)); + rightFoot.body.ApplyForce(rightFoot.Mass *new Vector2(0.0f, -80.0f)); + } + else + { + TargetMovement = new Vector2( + (float)Math.Sqrt(targetSpeed * targetSpeed - TargetMovement.Y * TargetMovement.Y) + * Math.Sign(TargetMovement.X), + Math.Max(TargetMovement.Y, TargetMovement.Y * 0.2f)); + + head.body.ApplyTorque(Dir * 0.1f); + } + + movement.Y = movement.Y - (surfaceLimiter - 1.0f) * 0.01f; + } + + head.body.ApplyForce((new Vector2(movement.X, + movement.Y / surfaceLimiter + 0.2f) - head.body.LinearVelocity * 0.2f) * + 20.0f * head.body.Mass); + + torso.body.ApplyForce((new Vector2(movement.X, + movement.Y / surfaceLimiter + 0.2f) - torso.body.LinearVelocity * 0.2f) * 10.0f * torso.body.Mass); + + walkPos += movement.Length() * 0.15f; + footPos = (leftFoot.SimPosition + rightFoot.SimPosition) / 2.0f; + + Vector2 transformedFootPos = new Vector2((float)Math.Sin(walkPos) * 0.3f, 0.0f); + transformedFootPos = Vector2.Transform( + transformedFootPos, + Matrix.CreateRotationZ(torso.body.Rotation)); + + MoveLimb(leftFoot, footPos + transformedFootPos, 2.5f); + MoveLimb(rightFoot, footPos - transformedFootPos, 2.5f); + + Vector2 feetExtendForce = new Vector2( + (float)-Math.Sin(torso.body.Rotation), + (float)Math.Cos(torso.body.Rotation)); + + leftFoot.body.ApplyForce(feetExtendForce); + rightFoot.body.ApplyForce(feetExtendForce); + + leftFoot.body.ApplyTorque(leftFoot.body.Mass * -Dir); + rightFoot.body.ApplyTorque(rightFoot.body.Mass * -Dir); + + handPos = (torso.SimPosition + head.SimPosition) / 2.0f; + + //if (!rightHand.Disabled) rightHand.body.ApplyTorque(leftHand.body.Mass * Dir); + //if (!leftHand.Disabled) leftHand.body.ApplyTorque(leftHand.body.Mass * Dir); + + //at the surface, not moving sideways -> hands just float around + if (!headInWater && TargetMovement.X == 0.0f) + { + handPos.X = handPos.X + Dir * 0.6f; + + float wobbleAmount = 0.05f; + + if (!rightHand.Disabled) + { + MoveLimb(rightHand, new Vector2( + handPos.X + (float)Math.Sin(walkPos / 1.5f) * wobbleAmount, + handPos.Y + (float)Math.Sin(walkPos / 3.5f) * wobbleAmount - 0.0f), 1.5f); + } + + if (!leftHand.Disabled) + { + MoveLimb(leftHand, new Vector2( + handPos.X + (float)Math.Sin(walkPos / 2.0f) * wobbleAmount, + handPos.Y + (float)Math.Sin(walkPos / 3.0f) * wobbleAmount - 0.0f), 1.5f); + } + + return; + } + + handPos += head.LinearVelocity * 0.1f; + + float handCyclePos = walkPos / 2.0f; + float handPosX = (float)Math.Cos(handCyclePos * Dir) * 0.4f; + float handPosY = (float)Math.Sin(handCyclePos * Dir) * 0.7f; + handPosY = MathHelper.Clamp(handPosY, -0.6f, 0.6f); + + Matrix rotationMatrix = Matrix.CreateRotationZ(torso.Rotation); + + if (!rightHand.Disabled) + { + Vector2 rightHandPos = new Vector2(-handPosX, -handPosY); + rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.2f, rightHandPos.X) : Math.Min(-0.2f, rightHandPos.X); + rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix); + + MoveLimb(rightHand, handPos + rightHandPos, 3.5f); + } + + if (!leftHand.Disabled) + { + Vector2 leftHandPos = new Vector2(handPosX, handPosY); + leftHandPos.X = (Dir == 1.0f) ? Math.Max(0.2f, leftHandPos.X) : Math.Min(-0.2f, leftHandPos.X); + leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix); + + MoveLimb(leftHand, handPos + leftHandPos, 3.5f); + } + + } + + + + void UpdateClimbing() + { + if (character.SelectedConstruction == null) + { + anim = Animation.None; + return; + } + + onGround = false; + IgnorePlatforms = true; + + movement = ToolBox.SmoothStep(movement, TargetMovement, 0.3f); + + Vector2 footPos, handPos; + + Limb leftFoot = GetLimb(LimbType.LeftFoot); + Limb rightFoot = GetLimb(LimbType.RightFoot); + Limb head = GetLimb(LimbType.Head); + Limb torso = GetLimb(LimbType.Torso); + + Limb waist = GetLimb(LimbType.Waist); + + Limb leftHand = GetLimb(LimbType.LeftHand); + Limb rightHand = GetLimb(LimbType.RightHand); + + Vector2 ladderSimPos = ConvertUnits.ToSimUnits( + character.SelectedConstruction.Rect.X + character.SelectedConstruction.Rect.Width / 2.0f, + character.SelectedConstruction.Rect.Y); + + MoveLimb(head, new Vector2(ladderSimPos.X - 0.27f * Dir, head.SimPosition.Y + 0.05f), 10.5f); + MoveLimb(torso, new Vector2(ladderSimPos.X - 0.27f * Dir, torso.SimPosition.Y), 10.5f); + MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, waist.SimPosition.Y), 10.5f); + + float stepHeight = ConvertUnits.ToSimUnits(30.0f); + + handPos = new Vector2( + ladderSimPos.X, + head.SimPosition.Y + 0.3f + movement.Y * 0.1f - ladderSimPos.Y); + + MoveLimb(leftHand, + new Vector2(handPos.X, + ToolBox.Round(handPos.Y - stepHeight, stepHeight * 2.0f) + stepHeight + ladderSimPos.Y), + 5.2f); + + MoveLimb(rightHand, + new Vector2(handPos.X, + ToolBox.Round(handPos.Y, stepHeight * 2.0f) + ladderSimPos.Y), + 5.2f); + + leftHand.body.ApplyTorque(Dir * 2.0f); + rightHand.body.ApplyTorque(Dir * 2.0f); + + footPos = new Vector2( + handPos.X + Dir*0.1f, + head.SimPosition.Y - stepHeight * 2.7f - ladderSimPos.Y); + + MoveLimb(leftFoot, + new Vector2(footPos.X, + ToolBox.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight + ladderSimPos.Y), + 7.5f, true); + + MoveLimb(rightFoot, + new Vector2(footPos.X, + ToolBox.Round(footPos.Y, stepHeight * 2.0f) + ladderSimPos.Y), + 7.5f, true); + + //apply torque to the legs to make the knees bend + Limb leftLeg = GetLimb(LimbType.LeftLeg); + Limb rightLeg = GetLimb(LimbType.RightLeg); + + leftLeg.body.ApplyTorque(Dir * -3.0f); + rightLeg.body.ApplyTorque(Dir * -3.0f); + + //apply forces to the head and the torso to move the character up/down + float movementFactor = (handPos.Y / stepHeight) * (float)Math.PI; + movementFactor = 0.8f + (float)Math.Abs(Math.Sin(movementFactor)); + + Vector2 climbForce = new Vector2(0.0f, movement.Y + 0.5f) * movementFactor; + torso.body.ApplyForce(climbForce * 65.0f * torso.Mass); + head.body.SmoothRotate(0.0f); + + Rectangle trigger = character.SelectedConstruction.Prefab.triggers.First(); + trigger = character.SelectedConstruction.TransformTrigger(trigger); + + //stop climbing if: + // - going too fast (can't grab a ladder while falling) + // - moving sideways + // - reached the top or bottom of the ladder + if (Math.Abs(torso.LinearVelocity.Y) > 5.0f || + TargetMovement.X != 0.0f || + (TargetMovement.Y < 0.0f && ConvertUnits.ToSimUnits(trigger.Height) + handPos.Y < HeadPosition*2.0f) || + (TargetMovement.Y > 0.0f && -handPos.Y < ConvertUnits.ToSimUnits(10.0f))) + { + anim = Animation.None; + character.SelectedConstruction = null; + IgnorePlatforms = false; + } + + } + + void UpdateStruggling() + { + Limb leftLeg = GetLimb(LimbType.LeftFoot); + Limb rightLeg = GetLimb(LimbType.RightFoot); + Limb torso = GetLimb(LimbType.Torso); + + walkPos += 0.2f; + + if (inWater) return; + + Vector2 footPos = torso.body.Position+ new Vector2(TorsoPosition*Dir,0.0f); + + MoveLimb(leftLeg, footPos, 0.7f); + MoveLimb(rightLeg, footPos, 0.7f); + } + + public override void HoldItem(float deltaTime, Camera cam, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, float holdAngle) + { + //calculate the handle positions + Matrix itemTransfrom = Matrix.CreateRotationZ(item.body.Rotation); + Vector2[] transformedHandlePos = new Vector2[2]; + transformedHandlePos[0] = Vector2.Transform(handlePos[0], itemTransfrom); + transformedHandlePos[1] = Vector2.Transform(handlePos[1], itemTransfrom); + + Limb head = GetLimb(LimbType.Head); + Limb torso = GetLimb(LimbType.Torso); + Limb leftHand = GetLimb(LimbType.LeftHand); + Limb leftArm = GetLimb(LimbType.LeftArm); + Limb rightHand = GetLimb(LimbType.RightHand); + Limb rightArm = GetLimb(LimbType.RightArm); + + Vector2 itemPos = character.SecondaryKeyDown.State ? aimPos : holdPos; + + float itemAngle; + if (character.SecondaryKeyDown.State && itemPos != Vector2.Zero) + { + Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition); + + Vector2 diff = (mousePos - torso.SimPosition) * Dir; + + holdAngle = ToolBox.VectorToAngle(new Vector2(diff.X, diff.Y * Dir)) - torso.body.Rotation * Dir; + holdAngle = MathHelper.Clamp(ToolBox.WrapAnglePi(holdAngle), -1.3f, 1.0f); + + itemAngle = (torso.body.Rotation + holdAngle * Dir); + + head.body.SmoothRotate(itemAngle); + + if (TargetMovement == Vector2.Zero && inWater) + { + torso.body.SmoothRotate(0.2f * Dir); + torso.body.ApplyForce(torso.body.LinearVelocity * -0.5f); + } + } + else + { + itemAngle = (torso.body.Rotation + holdAngle * Dir); + } + + Vector2 shoulderPos = head.SimPosition + (torso.SimPosition - head.SimPosition) *0.9f; + Vector2 transformedHoldPos = shoulderPos; + + if (itemPos == Vector2.Zero) + { + if (character.SelectedItems[0] == item) + { + transformedHoldPos = rightHand.pullJoint.WorldAnchorA - transformedHandlePos[0]; + itemAngle = (rightHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir); + //rightHand.Disabled = true; + } + if (character.SelectedItems[1] == item) + { + transformedHoldPos = leftHand.pullJoint.WorldAnchorA - transformedHandlePos[1]; + itemAngle = (leftHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir); + //leftHand.Disabled = true; + } + } + else + { + if (character.SelectedItems[0] == item) + { + rightHand.Disabled = true; + } + if (character.SelectedItems[1] == item) + { + leftHand.Disabled = true; + } + + + itemPos.X = itemPos.X * Dir; + + Matrix torsoTransform = Matrix.CreateRotationZ(itemAngle); + + transformedHoldPos += Vector2.Transform(itemPos, torsoTransform); + } + + + Vector2 bodyVelocity = torso.body.LinearVelocity / 60.0f; + + item.body.ResetDynamics(); + item.body.SetTransform(ToolBox.SmoothStep(item.body.Position, transformedHoldPos + bodyVelocity, 0.5f), itemAngle); + + //item.body.SmoothRotate(itemAngle, 50.0f); + + for (int i = 0; i < 2; i++) + { + if (character.SelectedItems[i] != item) continue; + if (itemPos == Vector2.Zero) continue; + + Limb hand = (i == 0) ? rightHand : leftHand; + Limb arm = (i == 0) ? rightArm : leftArm; + + //hand length + float a = 24.0f; + + //arm length + float b = 34.0f; + + //distance from shoulder to holdpos + float c = ConvertUnits.ToDisplayUnits(Vector2.Distance(transformedHoldPos + transformedHandlePos[i], shoulderPos)); + c = MathHelper.Clamp(a + b - 1, b-a, c); + + float ang2 = ToolBox.VectorToAngle((transformedHoldPos + transformedHandlePos[i]) - shoulderPos)+MathHelper.PiOver2; + + float armAngle = ToolBox.SolveTriangleSSS(a, b, c); + float handAngle = ToolBox.SolveTriangleSSS(b, a, c); + + arm.body.SmoothRotate((ang2 - armAngle * Dir), 20.0f); + hand.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f); + + } + + } + + public override void Flip() + { + base.Flip(); + + Limb torso = GetLimb(LimbType.Torso); + + Vector2 difference; + + Matrix torsoTransform = Matrix.CreateRotationZ(torso.Rotation); + + for (int i = 0; i < character.SelectedItems.Length; i++) + { + if (character.SelectedItems[i] != null) + { + difference = character.SelectedItems[i].body.Position - torso.SimPosition; + difference = Vector2.Transform(difference, torsoTransform); + difference.Y = -difference.Y; + + character.SelectedItems[i].body.SetTransform( + torso.SimPosition + Vector2.Transform(difference, -torsoTransform), + ToolBox.WrapAngleTwoPi(-character.SelectedItems[i].body.Rotation)); + } + } + + foreach (Limb l in limbs) + { + switch (l.type) + { + case LimbType.LeftHand: + case LimbType.LeftArm: + case LimbType.RightHand: + case LimbType.RightArm: + difference = l.body.Position - torso.SimPosition; + difference = Vector2.Transform(difference, torsoTransform); + difference.Y = -difference.Y; + + l.body.SetTransform(torso.SimPosition + Vector2.Transform(difference, -torsoTransform), -l.body.Rotation); + break; + default: + if (!inWater) l.body.SetTransform(l.body.Position, + ToolBox.WrapAnglePi(l.body.Rotation * (l.DoesFlip ? -1.0f : 1.0f))); + break; + } + } + + } + + } +} diff --git a/Subsurface/Characters/Job.cs b/Subsurface/Characters/Job.cs new file mode 100644 index 000000000..e1973c788 --- /dev/null +++ b/Subsurface/Characters/Job.cs @@ -0,0 +1,6 @@ +namespace Subsurface.Characters +{ + class Job + { + } +} diff --git a/Subsurface/Characters/Jobs/Job.cs b/Subsurface/Characters/Jobs/Job.cs new file mode 100644 index 000000000..2664e0866 --- /dev/null +++ b/Subsurface/Characters/Jobs/Job.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Subsurface +{ + class Job + { + public static List jobList; + + string name; + string description; + + //names of the items the character spawns with + public List itemNames; + + public string Name + { + get { return name; } + } + + public Job(XElement element) + { + name = element.Name.ToString(); + + description = ToolBox.GetAttributeString(element, "description", ""); + + itemNames = new List(); + + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString()) + { + case "item": + string itemName = ToolBox.GetAttributeString(subElement, "name", ""); + if (!string.IsNullOrEmpty(itemName)) itemNames.Add(itemName); + break; + } + } + } + + + public static void LoadAll(string filePath) + { + jobList = new List(); + + XDocument doc = ToolBox.TryLoadXml(filePath); + + foreach (XElement element in doc.Root.Elements()) + { + Job job = new Job(element); + jobList.Add(job); + } + } + } +} diff --git a/Subsurface/Characters/Limb.cs b/Subsurface/Characters/Limb.cs new file mode 100644 index 000000000..91e2e5407 --- /dev/null +++ b/Subsurface/Characters/Limb.cs @@ -0,0 +1,362 @@ +using System; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Dynamics.Joints; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface +{ + public enum LimbType + { + None, LeftHand, RightHand, LeftArm, RightArm, + LeftLeg, RightLeg, LeftFoot, RightFoot, Head, Torso, Tail, Legs, RightThigh, LeftThigh, Waist + }; + + class Limb + { + private const float LimbDensity = 15; + private const float LimbAngularDamping = 7; + + public readonly Character character; + + //the physics body of the limb + public PhysicsBody body; + private Texture2D bodyShapeTexture; + + private readonly int refJointIndex; + + private readonly float steerForce; + + private readonly bool doesFlip; + + public Sprite sprite; + + public bool inWater; + + public FixedMouseJoint pullJoint; + + public readonly LimbType type; + + public readonly bool ignoreCollisions; + + private float maxHealth; + private float damage; + private float bleeding; + + public readonly float impactTolerance; + + Sound hitSound; + //a timer for delaying when a hitsound/attacksound can be played again + public float soundTimer; + public const float SoundInterval = 0.2f; + + public readonly Attack attack; + + private Direction dir; + + private Item wearingItem; + private Sprite wearingItemSprite; + + private Vector2 animTargetPos; + + public Texture2D BodyShapeTexture + { + get { return bodyShapeTexture; } + } + + public bool DoesFlip + { + get { return doesFlip; } + } + + public Vector2 SimPosition + { + get { return body.Position; } + } + + public float Rotation + { + get { return body.Rotation; } + } + + public Vector2 AnimTargetPos + { + get { return animTargetPos; } + } + + public float SteerForce + { + get { return steerForce; } + } + + public float Mass + { + get { return body.Mass; } + } + + public bool Disabled { get; set; } + + public Sound HitSound + { + get { return hitSound; } + } + + public Vector2 LinearVelocity + { + get { return body.LinearVelocity; } + } + + public float Dir + { + get { return ((dir == Direction.Left) ? -1.0f : 1.0f); } + set { dir = (value==-1.0f) ? Direction.Left : Direction.Right; } + } + + public int RefJointIndex + { + get { return refJointIndex; } + } + + public float Damage + { + get { return damage; } + set + { + damage = Math.Max(value, 0.0f); + if (damage >=maxHealth) character.Kill(); + } + } + + public float Bleeding + { + get { return bleeding; } + set { bleeding = MathHelper.Clamp(value, 0.0f, 100.0f); } + } + + public Item WearingItem + { + get { return wearingItem; } + set { wearingItem = value; } + } + + public Sprite WearingItemSprite + { + get { return wearingItemSprite; } + set { wearingItemSprite = value; } + } + + public Limb (Character character, XElement element) + { + this.character = character; + + dir = Direction.Right; + + doesFlip = ToolBox.GetAttributeBool(element, "flip", false); + + body = new PhysicsBody(element); + + if (ToolBox.GetAttributeBool(element, "ignorecollisions", false)) + { + body.CollisionCategories = Category.None; + body.CollidesWith = Category.None; + + ignoreCollisions = true; + } + else + { + //limbs don't collide with each other + body.CollisionCategories = Physics.CollisionCharacter; + body.CollidesWith = Physics.CollisionAll & ~Physics.CollisionCharacter & ~Physics.CollisionMisc; + } + + impactTolerance = ToolBox.GetAttributeFloat(element, "impacttolerance", 20.0f); + + body.UserData = this; + + refJointIndex = -1; + + if (element.Attribute("type") != null) + { + type = (LimbType)Enum.Parse(typeof(LimbType), element.Attribute("type").Value, true); + + Vector2 jointPos = ToolBox.GetAttributeVector2(element, "pullpos", Vector2.Zero); + + jointPos = ConvertUnits.ToSimUnits(jointPos); + + refJointIndex = ToolBox.GetAttributeInt(element, "refjoint", -1); + + pullJoint = new FixedMouseJoint(body.FarseerBody, jointPos); + pullJoint.Enabled = false; + pullJoint.MaxForce = 150.0f * body.Mass; + + Game1.world.AddJoint(pullJoint); + } + else + { + type = LimbType.None; + } + + steerForce = ToolBox.GetAttributeFloat(element, "steerforce", 0.0f); + + maxHealth = Math.Max(ToolBox.GetAttributeFloat(element, "health", 100.0f),1.0f); + + body.BodyType = BodyType.Dynamic; + body.FarseerBody.AngularDamping = LimbAngularDamping; + + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString()) + { + case "sprite": + string spritePath = subElement.Attribute("texture").Value; + + if (character.info!=null) + spritePath = spritePath.Replace("[GENDER]", (character.info.gender == Gender.Female) ? "f" : ""); + + sprite = new Sprite(subElement, "", spritePath); + break; + case "attack": + attack = new Attack(subElement); + break; + case "sound": + hitSound = Sound.Load(ToolBox.GetAttributeString(subElement, "file", "")); + break; + } + } + } + + public void Move(Vector2 pos, float amount, bool pullFromCenter=false) + { + Vector2 pullPos = body.Position; + if (pullJoint!=null && !pullFromCenter) + { + pullPos = pullJoint.WorldAnchorA; + } + + animTargetPos = pos; + + Vector2 vel = body.LinearVelocity; + Vector2 deltaPos = pos - pullPos; + deltaPos *= amount; + body.ApplyLinearImpulse((deltaPos - vel * 0.5f) * body.Mass, pullPos); + } + + public void Update(float deltaTime) + { + if (LinearVelocity.X>100.0f) + { + DebugConsole.ThrowError("CHARACTER EXPLODED"); + foreach (Limb limb in character.animController.limbs) + { + limb.body.ResetDynamics(); + limb.body.SetTransform(body.Position, 0.0f); + } + } + + if (inWater) + { + //buoyancy + Vector2 buoyancy = new Vector2(0, Mass * 9.6f); + + //drag + Vector2 velDir = Vector2.Normalize(LinearVelocity); + + Vector2 line = new Vector2((float)Math.Cos(body.Rotation), (float)Math.Sin(body.Rotation)); + line *= ConvertUnits.ToSimUnits(sprite.size.Y); + + Vector2 normal = new Vector2(-line.Y, line.X); + normal = Vector2.Normalize(-normal); + + float dragDot = Vector2.Dot(normal, velDir); + Vector2 dragForce = Vector2.Zero; + if (dragDot > 0) + { + float vel = LinearVelocity.Length(); + float drag = dragDot * vel * vel + * ConvertUnits.ToSimUnits(sprite.size.Y); + dragForce = drag * -velDir; + if (dragForce.Length() > 100.0f) { } + } + + body.ApplyForce(dragForce + buoyancy); + body.ApplyTorque(body.AngularVelocity * body.Mass * -0.05f); + } + + if (character.IsDead) return; + + soundTimer -= deltaTime; + + if (ToolBox.RandomFloat(0.0f, 1000.0f) < Bleeding) + { + Game1.particleManager.CreateParticle(SimPosition, + MathHelper.Pi, + ToolBox.RandomFloat(0.0f, 0.0f), !inWater ? "blood" : "waterblood"); + } + } + + + public void Draw(SpriteBatch spriteBatch, bool debugDraw) + { + Color color = new Color(1.0f, 1.0f - damage / maxHealth, 1.0f - damage / maxHealth); + + body.Dir = Dir; + body.Draw(spriteBatch, sprite, color); + + if (wearingItem != null) + { + SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally; + wearingItemSprite.Draw(spriteBatch, + new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), + color, + -body.DrawRotation, + 1.0f, spriteEffect); + } + + if (!debugDraw) return; + + if (pullJoint!=null) + { + Vector2 pos = ConvertUnits.ToDisplayUnits(pullJoint.WorldAnchorB); + GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), Color.Red, true); + } + + if (bodyShapeTexture == null) + { + switch (body.bodyShape) + { + case PhysicsBody.Shape.Rectangle: + bodyShapeTexture = GUI.CreateRectangle( + (int)ConvertUnits.ToDisplayUnits(body.width), + (int)ConvertUnits.ToDisplayUnits(body.height)); + break; + + case PhysicsBody.Shape.Capsule: + bodyShapeTexture = GUI.CreateCapsule( + (int)ConvertUnits.ToDisplayUnits(body.radius), + (int)ConvertUnits.ToDisplayUnits(body.height)); + break; + case PhysicsBody.Shape.Circle: + bodyShapeTexture = GUI.CreateCircle((int)ConvertUnits.ToDisplayUnits(body.radius)); + break; + } + } + spriteBatch.Draw( + bodyShapeTexture, + new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), + null, + Color.White, + -body.DrawRotation, + new Vector2(bodyShapeTexture.Width / 2, bodyShapeTexture.Height / 2), 1.0f, SpriteEffects.None, 0.0f); + } + + + public void Remove() + { + sprite.Remove(); + body.Remove(); + if (hitSound!=null) hitSound.Remove(); + } + } +} diff --git a/Subsurface/Characters/LimbAttack.cs b/Subsurface/Characters/LimbAttack.cs new file mode 100644 index 000000000..11d61f90e --- /dev/null +++ b/Subsurface/Characters/LimbAttack.cs @@ -0,0 +1,94 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Subsurface +{ + + + + class Attack + { + public enum DamageType { None, Blunt, Slash }; + + public enum Type + { + None, PinchCW, PinchCCW + }; + public readonly Type type; + public readonly float range; + public readonly float duration; + + public readonly DamageType damageType; + + public readonly float structureDamage; + public readonly float damage; + public readonly float bleedingDamage; + + public readonly float stun; + + private float priority; + + public Attack(XElement element) + { + try + { + type = (Type)Enum.Parse(typeof(Type), element.Attribute("type").Value, true); + } + catch + { + type = Type.None; + } + + try + { + damageType = (DamageType)Enum.Parse(typeof(DamageType), ToolBox.GetAttributeString(element, "damagetype", "None"), true); + } + catch + { + damageType = DamageType.None; + } + + + damage = ToolBox.GetAttributeFloat(element, "damage", 0.0f); + structureDamage = ToolBox.GetAttributeFloat(element, "damage", 0.0f); + bleedingDamage = ToolBox.GetAttributeFloat(element, "bleedingdamage", 0.0f); + + stun = ToolBox.GetAttributeFloat(element, "stun", 0.0f); + + + range = FarseerPhysics.ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "range", 0.0f)); + + duration = ToolBox.GetAttributeFloat(element, "duration", 0.0f); + + priority = ToolBox.GetAttributeFloat(element, "priority", 1.0f); + } + + public void DoDamage(IDamageable target, Vector2 position, float deltaTime, bool playSound=true) + { + float damageAmount = 0.0f; + DamageSoundType damageSoundType = DamageSoundType.None; + + if (target as Structure == null) + { + damageAmount = damage; + damageSoundType = (damageType == DamageType.Blunt) ? DamageSoundType.LimbBlunt : DamageSoundType.LimbSlash; + } + else + { + damageAmount = structureDamage; + damageSoundType = (damageType == DamageType.Blunt) ? DamageSoundType.StructureBlunt: DamageSoundType.StructureSlash; + } + + if (playSound) AmbientSoundManager.PlayDamageSound(damageSoundType, damageAmount, position); + + if (duration > 0.0f) damageAmount *= deltaTime; + float bleedingAmount = (duration == 0.0f) ? bleedingDamage : bleedingDamage * deltaTime; + + if (damageAmount>0.0f) target.AddDamage(position, damageAmount, bleedingAmount, stun); + } + } +} diff --git a/Subsurface/Characters/Ragdoll.cs b/Subsurface/Characters/Ragdoll.cs new file mode 100644 index 000000000..59cc0e866 --- /dev/null +++ b/Subsurface/Characters/Ragdoll.cs @@ -0,0 +1,643 @@ +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.Dynamics.Joints; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface +{ + class Ragdoll + { + public static List list = new List(); + + public static bool DebugDraw = false; + + protected Hull currentHull; + + public Limb[] limbs; + private Dictionary limbDictionary; + public RevoluteJoint[] limbJoints; + + Character character; + + private Limb lowestLimb; + + protected float strongestImpact; + + float headPosition, headAngle; + float torsoPosition, torsoAngle; + + protected double onFloorTimer; + + //the movement speed of the ragdoll + public Vector2 movement; + //the target speed towards which movement is interpolated + private Vector2 targetMovement; + + //a movement vector that overrides targetmovement if trying to steer + //a character to the position sent by server in multiplayer mode + public Vector2 correctionMovement; + + protected float floorY; + protected float surfaceY; + + protected bool inWater, headInWater; + public bool onGround; + private bool ignorePlatforms; + + protected Structure stairs; + + protected Direction dir; + + //private byte ID; + + public Limb LowestLimb + { + get { return lowestLimb; } + } + + public Vector2 TargetMovement + { + get { return (correctionMovement == Vector2.Zero) ? targetMovement : correctionMovement; } + set { targetMovement = value; } + } + + public float HeadPosition + { + get { return headPosition; } + } + + public float HeadAngle + { + get { return headAngle; } + } + + public float TorsoPosition + { + get { return torsoPosition; } + } + + public float TorsoAngle + { + get { return torsoAngle; } + } + + public float Dir + { + get { return ((dir == Direction.Left) ? -1.0f : 1.0f); } + } + + public bool InWater + { + get { return inWater; } + } + + public bool HeadInWater + { + get { return headInWater; } + } + + public Hull CurrentHull + { + get { return currentHull;} + } + + public bool IgnorePlatforms + { + get { return ignorePlatforms; } + set + { + if (ignorePlatforms == value) return; + ignorePlatforms = value; + + foreach (Limb l in limbs) + { + if (l.ignoreCollisions) continue; + + l.body.CollidesWith = (ignorePlatforms) ? + Physics.CollisionWall | Physics.CollisionProjectile | Physics.CollisionStairs + : Physics.CollisionAll & ~Physics.CollisionCharacter & ~Physics.CollisionMisc; + } + + } + } + + public float StrongestImpact + { + get { return strongestImpact; } + set { strongestImpact = Math.Max(value, strongestImpact); } + } + + public Structure Stairs + { + get { return stairs; } + } + + public Ragdoll(Character character, XElement element) + { + list.Add(this); + + this.character = character; + + dir = Direction.Right; + + //int limbAmount = ; + limbs = new Limb[element.Elements("limb").Count()]; + limbJoints = new RevoluteJoint[element.Elements("joint").Count()]; + limbDictionary = new Dictionary(); + + headPosition = ToolBox.GetAttributeFloat(element, "headposition", 50.0f); + headPosition = ConvertUnits.ToSimUnits(headPosition); + headAngle = MathHelper.ToRadians(ToolBox.GetAttributeFloat(element, "headangle", 0.0f)); + + torsoPosition = ToolBox.GetAttributeFloat(element, "torsoposition", 50.0f); + torsoPosition = ConvertUnits.ToSimUnits(torsoPosition); + torsoAngle = MathHelper.ToRadians(ToolBox.GetAttributeFloat(element, "torsoangle", 0.0f)); + + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString()) + { + case "limb": + byte ID = Convert.ToByte(subElement.Attribute("id").Value); + + Limb limb = new Limb(character, subElement); + + limb.body.FarseerBody.OnCollision += OnLimbCollision; + + limbs[ID] = limb; + if (!limbDictionary.ContainsKey(limb.type)) limbDictionary.Add(limb.type, limb); + break; + case "joint": + Byte limb1ID = Convert.ToByte(subElement.Attribute("limb1").Value); + Byte limb2ID = Convert.ToByte(subElement.Attribute("limb2").Value); + + Vector2 limb1Pos = ToolBox.GetAttributeVector2(subElement, "limb1anchor", Vector2.Zero); + limb1Pos = ConvertUnits.ToSimUnits(limb1Pos); + + Vector2 limb2Pos = ToolBox.GetAttributeVector2(subElement, "limb2anchor", Vector2.Zero); + limb2Pos = ConvertUnits.ToSimUnits(limb2Pos); + + RevoluteJoint joint = new RevoluteJoint(limbs[limb1ID].body.FarseerBody, limbs[limb2ID].body.FarseerBody, limb1Pos, limb2Pos); + + joint.CollideConnected = false; + + if (subElement.Attribute("lowerlimit")!=null) + { + joint.LimitEnabled = true; + joint.LowerLimit = float.Parse(subElement.Attribute("lowerlimit").Value) * ((float)Math.PI / 180.0f); + joint.UpperLimit = float.Parse(subElement.Attribute("upperlimit").Value) * ((float)Math.PI / 180.0f); + } + + joint.MotorEnabled = true; + joint.MaxMotorTorque = 0.25f; + + Game1.world.AddJoint(joint); + + for (int i = 0; i < limbJoints.Length; i++ ) + { + if (limbJoints[i] != null) continue; + + limbJoints[i] = joint; + break; + } + + break; + } + + } + + foreach (var joint in limbJoints) + { + + joint.BodyB.SetTransform( + joint.BodyA.Position+joint.LocalAnchorA-joint.LocalAnchorB, + (joint.LowerLimit+joint.UpperLimit)/2.0f); + } + + float startDepth = 0.1f; + float increment = 0.0001f; + + foreach (Character otherCharacter in Character.characterList) + { + if (otherCharacter==character) continue; + startDepth+=increment; + } + + foreach (Limb limb in limbs) + { + limb.sprite.Depth = startDepth + limb.sprite.Depth * 0.00001f; + } + } + + public bool OnLimbCollision(Fixture f1, Fixture f2, Contact contact) + { + Structure structure = f2.Body.UserData as Structure; + + //always collides with bodies other than structures + if (structure == null) + { + CalculateImpact(f1, f2, contact); + return true; + } + + if (structure.IsPlatform) + { + if (ignorePlatforms) return false; + + //the collision is ignored if the lowest limb is under the platform + if (lowestLimb==null || lowestLimb.SimPosition.Y < f2.Body.Position.Y) return false; + } + else if (structure.StairDirection!=Direction.None) + { + if (inWater || (!(targetMovement.Y>Math.Abs(targetMovement.X/2.0f)) && lowestLimb.body.Position.Y < ConvertUnits.ToSimUnits(structure.Rect.Y - structure.Rect.Height) + 0.5f)) + { + stairs = null; + return false; + } + + Limb limb = f1.Body.UserData as Limb; + if (limb != null && (limb.type == LimbType.LeftFoot || limb.type == LimbType.RightFoot)) + { + Debug.WriteLine(contact.Manifold.LocalNormal.Y); + if (contact.Manifold.LocalNormal.Y >= 0.0f) + { + stairs = structure; + return true; + } + else + { + stairs = null; + return false; + } + } + else + { + return false; + } + } + + + CalculateImpact(f1, f2, contact); + return true; + } + + private void CalculateImpact(Fixture f1, Fixture f2, Contact contact) + { + Vector2 normal = contact.Manifold.LocalNormal; + float impact = Vector2.Dot(f1.Body.LinearVelocity, -normal); + + + Limb l = (Limb)f1.Body.UserData; + + if (impact > 1.0f && l.HitSound != null && l.soundTimer<=0.0f) l.HitSound.Play(Math.Min(impact / 5.0f, 1.0f)); + + if (impact > l.impactTolerance) + { + l.Damage += (impact-l.impactTolerance*0.1f); + strongestImpact = Math.Max(strongestImpact, impact - l.impactTolerance); + } + } + + + public virtual void Draw(SpriteBatch spriteBatch) + { + + + + foreach (Limb limb in limbs) + { + limb.Draw(spriteBatch, DebugDraw); + } + + if (!DebugDraw) return; + + foreach (Limb limb in limbs) + { + + if (limb.pullJoint != null) + { + Vector2 pos = ConvertUnits.ToDisplayUnits(limb.pullJoint.WorldAnchorA); + pos.Y = -pos.Y; + GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), Color.Red, true, 0.01f); + + if (limb.AnimTargetPos == Vector2.Zero) continue; + + Vector2 pos2 = ConvertUnits.ToDisplayUnits(limb.AnimTargetPos); + pos2.Y = -pos2.Y; + GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos2.X, (int)pos2.Y, 5, 5), Color.Blue, true, 0.01f); + + GUI.DrawLine(spriteBatch, pos, pos2, Color.Green); + } + } + + foreach (RevoluteJoint joint in limbJoints) + { + Vector2 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorA); + pos.Y = -pos.Y; + GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true); + + pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorB); + pos.Y = -pos.Y; + GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true); + } + + } + + public virtual void Flip() + { + dir = (dir == Direction.Left) ? Direction.Right : Direction.Left; + + for (int i = 0; i < limbJoints.Count(); i++) + { + float lowerLimit = -limbJoints[i].UpperLimit; + float upperLimit = -limbJoints[i].LowerLimit; + + limbJoints[i].LowerLimit = lowerLimit; + limbJoints[i].UpperLimit = upperLimit; + + limbJoints[i].LocalAnchorA = new Vector2(-limbJoints[i].LocalAnchorA.X, limbJoints[i].LocalAnchorA.Y); + limbJoints[i].LocalAnchorB = new Vector2(-limbJoints[i].LocalAnchorB.X, limbJoints[i].LocalAnchorB.Y); + } + + + for (int i = 0; i < limbs.Count(); i++) + { + if (limbs[i] == null) continue; + + Vector2 spriteOrigin = limbs[i].sprite.Origin; + spriteOrigin.X = limbs[i].sprite.SourceRect.Width - spriteOrigin.X; + limbs[i].sprite.Origin = spriteOrigin; + + limbs[i].Dir = Dir; + + if (limbs[i].pullJoint == null) continue; + + limbs[i].pullJoint.LocalAnchorA = + new Vector2( + -limbs[i].pullJoint.LocalAnchorA.X, + limbs[i].pullJoint.LocalAnchorA.Y); + } + + } + + /// + /// + /// + /// if false, force is applied to the position of pullJoint + protected void MoveLimb(Limb limb, Vector2 pos, float amount, bool pullFromCenter = false) + { + limb.Move(pos, amount, pullFromCenter); + } + + public void ResetPullJoints() + { + for (int i = 0; i < limbs.Count(); i++) + { + if (limbs[i] == null) continue; + if (limbs[i].pullJoint == null) continue; + limbs[i].pullJoint.Enabled = false; + } + } + + public static void UpdateAll(float deltaTime) + { + foreach (Ragdoll r in list) + { + r.Update(deltaTime); + } + } + + public void FindHull() + { + Limb torso = GetLimb(LimbType.Torso); + if (torso==null) torso = GetLimb(LimbType.Head); + + currentHull = Hull.FindHull( + ConvertUnits.ToDisplayUnits(torso.SimPosition), + currentHull); + } + + public void Update(float deltaTime) + { + UpdateNetplayerPosition(); + + Vector2 flowForce = Vector2.Zero; + + FindLowestLimb(); + + FindHull(); + + //ragdoll isn't in any room -> it's in the water + if (currentHull == null) + { + inWater = true; + headInWater = true; + } + else + { + flowForce = GetFlowForce(); + + inWater = false; + headInWater = false; + + if (ConvertUnits.ToSimUnits(currentHull.Surface)-floorY> HeadPosition) + inWater = true; + + } + + foreach (Limb limb in limbs) + { + Vector2 limbPosition = ConvertUnits.ToDisplayUnits(limb.SimPosition); + + //find the room which the limb is in + //the room where the ragdoll is in is used as the "guess", meaning that it's checked first + Hull limbHull = Hull.FindHull(limbPosition, currentHull); + + bool prevInWater = limb.inWater; + limb.inWater = false; + + if (limbHull==null) + { + //limb isn't in any room -> it's in the water + limb.inWater = true; + } + else if (limbHull.Volume>0.0f && Map.RectContains(limbHull.Rect, limbPosition)) + { + + if (limbPosition.Y < limbHull.Surface) + { + limb.inWater = true; + + if (flowForce.Length() > 0.01f) + { + limb.body.ApplyForce(flowForce); + if (flowForce.Length() > 15.0f) surfaceY = limbHull.Surface; + } + + surfaceY = limbHull.Surface; + + if (limb.type == LimbType.Head) + { + headInWater = true; + surfaceY = limbHull.Surface; + } + } + //the limb has gone through the surface of the water + if (Math.Abs(limb.LinearVelocity.Y) > 3.0 && inWater != prevInWater) + { + + //create a splash particle + Game1.particleManager.CreateParticle( + new Vector2(limb.SimPosition.X, ConvertUnits.ToSimUnits(limbHull.Surface)), + 0.0f, + new Vector2(0.0f, Math.Abs(limb.LinearVelocity.Y*0.1f)), + "watersplash"); + Game1.particleManager.CreateParticle( + new Vector2(limb.SimPosition.X, ConvertUnits.ToSimUnits(limbHull.Surface)), + 0.0f, + limb.LinearVelocity*0.001f, + "bubbles"); + + + + //if the character dropped into water, create a wave + if (limb.LinearVelocity.Y<0.0f) + { + //1.0 when the limb is parallel to the surface of the water + // = big splash and a large impact + float parallel = (float)Math.Abs(Math.Sin(limb.Rotation)); + Vector2 impulse = Vector2.Multiply(limb.LinearVelocity, -parallel * limb.Mass); + //limb.body.ApplyLinearImpulse(impulse); + int n = (int)((limbPosition.X - limbHull.Rect.X) / Hull.WaveWidth); + limbHull.WaveVel[n] = Math.Min(impulse.Y * 1.0f, 5.0f); + StrongestImpact = ((impulse.Length() * 0.5f) - limb.impactTolerance); + } + } + + + } + + limb.Update(deltaTime); + + } + + } + + private void UpdateNetplayerPosition() + { + Limb refLimb = GetLimb(LimbType.Torso); + if (refLimb== null) refLimb = GetLimb(LimbType.Head); + + if (refLimb.body.TargetPosition == Vector2.Zero) return; + + //if the limb is further away than resetdistance, all limbs are immediately snapped to their targetpositions + float resetDistance = 1.5f; + + //if the limb is closer than alloweddistance, limb positions aren't updated + float allowedDistance = 0.1f; + + float dist = Vector2.Distance(limbs[0].body.Position, refLimb.body.TargetPosition); + bool resetAll = (dist > resetDistance && character.largeUpdateTimer == 1); + + Vector2 newMovement = (refLimb.body.TargetPosition - refLimb.body.Position); + + if (newMovement == Vector2.Zero || newMovement.Length() < allowedDistance) + { + refLimb.body.TargetPosition = Vector2.Zero; + correctionMovement = Vector2.Zero; + return; + } + else + { + if (inWater) + { + foreach (Limb limb in limbs) + { + if (limb.body.TargetPosition == Vector2.Zero) continue; + + limb.body.SetTransform(limb.SimPosition + newMovement * 0.1f, limb.Rotation); + } + } + else + { + correctionMovement = Vector2.Normalize(newMovement) * ((targetMovement == Vector2.Zero) ? 1.0f : targetMovement.Length()); + } + } + + if (resetAll) + { + System.Diagnostics.Debug.WriteLine("resetall"); + + foreach (Limb limb in limbs) + { + if (limb.body.TargetPosition == Vector2.Zero) continue; + + limb.body.LinearVelocity = limb.body.TargetVelocity; + limb.body.AngularVelocity = limb.body.TargetAngularVelocity; + + limb.body.SetTransform(limb.body.TargetPosition, limb.body.TargetRotation); + limb.body.TargetPosition = Vector2.Zero; + } + } + } + + + private Vector2 GetFlowForce() + { + Vector2 limbPos = ConvertUnits.ToDisplayUnits(limbs[0].SimPosition); + + Vector2 force = Vector2.Zero; + foreach (MapEntity e in MapEntity.mapEntityList) + { + Gap gap = e as Gap; + if (gap == null || gap.FlowTargetHull!=currentHull ||gap.FlowForce == Vector2.Zero) continue; + + Vector2 gapPos = gap.SimPosition; + + float dist = Vector2.Distance(limbPos, gapPos); + + force += Vector2.Normalize(gap.FlowForce)*(Math.Max(gap.FlowForce.Length() - dist, 0.0f)/500.0f); + } + + if (force.Length() > 20.0f) return force; + return force; + } + + public Limb GetLimb(LimbType limbType) + { + Limb limb = null; + limbDictionary.TryGetValue(limbType, out limb); + return limb; + } + + public void FindLowestLimb() + { + //find the lowest limb + lowestLimb = null; + foreach (Limb limb in limbs) + { + if (lowestLimb == null) + lowestLimb = limb; + else if (limb.SimPosition.Y < lowestLimb.SimPosition.Y) + lowestLimb = limb; + } + } + + public void Remove() + { + foreach (Limb l in limbs) l.Remove(); + foreach (RevoluteJoint joint in limbJoints) + { + Game1.world.RemoveJoint(joint); + } + } + + } +} diff --git a/Subsurface/Characters/StatusEffect.cs b/Subsurface/Characters/StatusEffect.cs new file mode 100644 index 000000000..af84101e2 --- /dev/null +++ b/Subsurface/Characters/StatusEffect.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Xml.Linq; + +namespace Subsurface +{ + class StatusEffect + { + + [Flags] + public enum Target + { + This = 1, Parent = 2, Character = 4, Contained = 8, Nearby = 16 + } + + private Target targets; + private string[] targetNames; + + public string[] propertyNames; + private object[] propertyEffects; + + private string[] onContainingNames; + + public readonly ActionType type; + + private Explosion explosion; + + private Sound sound; + + public Target Targets + { + get { return targets; } + } + + public string[] TargetNames + { + get { return targetNames; } + } + + public string[] OnContainingNames + { + get { return onContainingNames; } + } + + public static StatusEffect Load(XElement element) + { + if (element.Attribute("delay")!=null) + { + return new DelayedEffect(element); + } + else + { + return new StatusEffect(element); + } + } + + protected StatusEffect(XElement element) + { + IEnumerable attributes = element.Attributes(); + List propertyAttributes = new List(); + + foreach (XAttribute attribute in attributes) + { + switch (attribute.Name.ToString()) + { + case "type": + try + { + type = (ActionType)Enum.Parse(typeof(ActionType), attribute.Value, true); + } + + catch + { + string[] split = attribute.Value.Split('='); + type = (ActionType)Enum.Parse(typeof(ActionType), split[0], true); + + string[] containingNames = split[1].Split(','); + onContainingNames = new string[containingNames.Count()]; + for (int i =0; i < containingNames.Count(); i++) + { + onContainingNames[i] = containingNames[i].Trim(); + } + } + + break; + case "target": + string[] Flags = attribute.Value.Split(','); + foreach (string s in Flags) + { + targets |= (Target)Enum.Parse(typeof(Target), s, true); + } + + break; + case "targetnames": + string[] names = attribute.Value.Split(','); + targetNames = new string[names.Count()]; + for (int i=0; i < names.Count(); i++ ) + { + targetNames[i] = names[i].Trim(); + } + break; + case "sound": + sound = Sound.Load(attribute.Value.ToString()); + break; + default: + propertyAttributes.Add(attribute); + break; + } + } + + int count = propertyAttributes.Count(); + propertyNames = new string[count]; + propertyEffects = new object[count]; + + int n = 0; + foreach (XAttribute attribute in propertyAttributes) + { + propertyNames[n] = attribute.Name.ToString().ToLower(); + propertyEffects[n] = ToolBox.GetAttributeObject(attribute); + n++; + } + + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLower()) + { + case "explosion": + explosion = new Explosion(subElement); + break; + } + } + + //oxygen = ToolBox.GetAttributeFloat(element, "oxygen", 0.0f); + + + //deteriorateOnActive = ToolBox.GetAttributeFloat(element, "deteriorateonactive", 0.0f); + //deteriorateOnUse = ToolBox.GetAttributeFloat(element, "deteriorateonuse", 0.0f); + } + + + public virtual void Apply(ActionType type, float deltaTime, Item item, Character character = null, Limb limb = null) + { + if (this.type == type) Apply(deltaTime, character, item); + + } + + + protected virtual void Apply(float deltaTime, Character character, Item item, Limb limb = null) + { + if (explosion != null) explosion.Explode(item.SimPosition); + + if (sound != null) sound.Play(1.0f, 1000.0f, item.body.FarseerBody); + + for (int i = 0; i < propertyNames.Count(); i++) + { + ObjectProperty property; + + if (character!=null && character.properties.TryGetValue(propertyNames[i], out property)) + { + ApplyToProperty(property, propertyEffects[i], deltaTime); + } + + if (item == null) continue; + + if (item.properties.TryGetValue(propertyNames[i], out property)) + { + ApplyToProperty(property, propertyEffects[i], deltaTime); + } + + foreach (ItemComponent ic in item.components) + { + if (!ic.properties.TryGetValue(propertyNames[i], out property)) continue; + ApplyToProperty(property, propertyEffects[i], deltaTime); + } + } + } + + protected void ApplyToProperty(ObjectProperty property, object value, float deltaTime) + { + + Type type = value.GetType(); + if (type == typeof(float)) + { + property.TrySetValue((float)property.GetValue() + (float)value * deltaTime); + } + if (type == typeof(int)) + { + property.TrySetValue((int)property.GetValue() + (int)value * deltaTime); + } + else if (type == typeof(bool)) + { + property.TrySetValue((bool)value); + } + else if (type == typeof(string)) + { + property.TrySetValue((string)value); + } + } + + public static void UpdateAll(float deltaTime) + { + for (int i = DelayedEffect.list.Count-1; i>= 0; i--) + { + DelayedEffect.list[i].Update(deltaTime); + } + } + + } +} diff --git a/Subsurface/Content/Characters/Crawler/crawler.png b/Subsurface/Content/Characters/Crawler/crawler.png new file mode 100644 index 000000000..9b4b6753b Binary files /dev/null and b/Subsurface/Content/Characters/Crawler/crawler.png differ diff --git a/Subsurface/Content/Characters/Crawler/crawler.xml b/Subsurface/Content/Characters/Crawler/crawler.xml new file mode 100644 index 000000000..0516f6573 --- /dev/null +++ b/Subsurface/Content/Characters/Crawler/crawler.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Subsurface/Content/Characters/Human/ffirstnames.txt b/Subsurface/Content/Characters/Human/ffirstnames.txt new file mode 100644 index 000000000..c8f23b820 --- /dev/null +++ b/Subsurface/Content/Characters/Human/ffirstnames.txt @@ -0,0 +1,171 @@ +Albertine +Alfredia +Alita +Alla +Alta +Althea +Alyce +Amee +Annette +Ara +Arcelia +Arie +Armanda +Ashly +Astrid +Avril +Barbra +Beaulah +Bell +Bertha +Bettyann +Beverley +Bridgett +Candace +Carie +Carla +Cathleen +Cecille +Christiana +Christine +Cinda +Cleopatra +Codi +Corinne +Creola +Criselda +Dahlia +Danielle +Dannette +Darleen +Deborah +Denise +Denisha +Dona +Doria +Dorthy +Eleanor +Elinor +Emerald +Erline +Ethel +Eugenia +Fanny +Fernanda +Glenda +Hortensia +Ilene +Ina +Inga +Iris +Jacquline +Janee +Janessa +Janie +Jeanene +Jenine +Jina +Jodee +Joy +Joyce +Juana +Julianne +Jutta +Karyn +Katerina +Katherina +Kathy +Kellie +Keri +Kizzie +Kristeen +Kristie +Larissa +Larita +Laurie +Laurinda +Lavinia +Leanne +Leila +Lelah +Leola +Lindy +Liza +Lola +Lora +Lorriane +Lorrie +Mafalda +Maira +Majorie +Marcy +Margeret +Mariela +Marin +Marissa +Marla +Maryann +Marylee +Masako +Maudie +Maybell +Mechelle +Melany +Melba +Michele +Mila +Miranda +Muoi +Natalia +Natashia +Nola +Noriko +Page +Paula +Peggie +Randi +Reatha +Renata +Rhonda +Roberta +Roselle +Rosina +Roslyn +Rowena +Ruthie +Sabrina +Sage +Sanda +Sara +Serena +Sharie +Shayla +Shelly +Sherise +Sherita +Sherry +Shin +Shirlee +Socorro +Stefany +Stephane +Susy +Synthia +Tania +Tanika +Tanya +Tawanda +Tera +Tessie +Thea +Tisha +Tracy +Trista +Trudie +Trudy +Valerie +Vanessa +Velma +Yahaira +Zandra +Zoe \ No newline at end of file diff --git a/Subsurface/Content/Characters/Human/fhead.png b/Subsurface/Content/Characters/Human/fhead.png new file mode 100644 index 000000000..f3312bfd1 Binary files /dev/null and b/Subsurface/Content/Characters/Human/fhead.png differ diff --git a/Subsurface/Content/Characters/Human/firstnames.txt b/Subsurface/Content/Characters/Human/firstnames.txt new file mode 100644 index 000000000..ccb1d0f35 --- /dev/null +++ b/Subsurface/Content/Characters/Human/firstnames.txt @@ -0,0 +1,206 @@ +Abe +Adam +Adan +Alan +Aldo +Allan +Alonso +Alton +Alvaro +Andres +Anton +Antony +Arkadi +Arron +Arturo +Austin +Avery +Barney +Barry +Barton +Ben +Bennie +Bertram +Bill +Boyce +Boyd +Brady +Brain +Britt +Bruno +Bryant +Bryce +Burl +Burt +Carlos +Carlton +Carmen +Carson +Chance +Christoper +Chuck +Claude +Coleman +Dan +Darius +Darrin +Darwin +David +Delbert +Devin +Dewey +Dewitt +Dimitry +Dominick +Donn +Dorsey +Edgar +Edison +Eldridge +Elmer +Erich +Erik +Ernie +Everette +Ezequiel +Filiberto +Frances +Franklin +Freeman +Fritz +Garrett +Gerard +Hal +Harlan +Harris +Harry +Hector +Herman +Hobert +Irwin +Ivan +Jackie +Jaime +Jamey +Jan +Jared +Jarrod +Jarvis +Jc +Jean +Jerald +Jeremiah +Jerrell +Jerrod +Jess +Joe +John +Jonathan +Josef +Joseph +Joshua +Jude +Keith +Kendall +Keneth +Kenton +Kerry +Keven +Kim +Kip +Lamont +Lane +Lanny +Lee +Len +Lenny +Lionel +Lynn +Lynwood +Malcom +Manuel +Marc +Marcel +Marcelino +Marcellus +Marco +Mathew +Matt +Maurice +Mauricio +Mckinley +Mechislav +Merrill +Micheal +Milton +Minh +Mitchel +Mitrofan +Mohammad +Monte +Morton +Mose +Murray +Neal +Neil +Nolan +Norman +Orval +Oscar +Owen +Pete +Peter +Quincy +Ramon +Randall +Raphael +Raymon +Reed +Reggie +Reginald +Renato +Rene +Richard +Rob +Roderick +Roland +Romeo +Rosendo +Roy +Rupert +Sammie +Santiago +Saul +Sebastian +Sergei +Seth +Shelby +Sidney +Son +Sonny +Steve +Stevie +Sylvester +Tad +Terrance +Terrell +Timur +Tod +Todd +Tommie +Tory +Travis +Tyler +Tyson +Ulof +Virgil +Virgilio +Waldo +Walker +Warner +Wilford +Will +Williams +Willy +Winston +Zachary \ No newline at end of file diff --git a/Subsurface/Content/Characters/Human/flegs.png b/Subsurface/Content/Characters/Human/flegs.png new file mode 100644 index 000000000..83d56c70d Binary files /dev/null and b/Subsurface/Content/Characters/Human/flegs.png differ diff --git a/Subsurface/Content/Characters/Human/ftorso.png b/Subsurface/Content/Characters/Human/ftorso.png new file mode 100644 index 000000000..1edfaf0fa Binary files /dev/null and b/Subsurface/Content/Characters/Human/ftorso.png differ diff --git a/Subsurface/Content/Characters/Human/head.png b/Subsurface/Content/Characters/Human/head.png new file mode 100644 index 000000000..3c6f7eb54 Binary files /dev/null and b/Subsurface/Content/Characters/Human/head.png differ diff --git a/Subsurface/Content/Characters/Human/head2.png b/Subsurface/Content/Characters/Human/head2.png new file mode 100644 index 000000000..8056e645a Binary files /dev/null and b/Subsurface/Content/Characters/Human/head2.png differ diff --git a/Subsurface/Content/Characters/Human/human.xml b/Subsurface/Content/Characters/Human/human.xml new file mode 100644 index 000000000..102159d70 --- /dev/null +++ b/Subsurface/Content/Characters/Human/human.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Subsurface/Content/Characters/Human/lastnames.txt b/Subsurface/Content/Characters/Human/lastnames.txt new file mode 100644 index 000000000..8e4e8874b --- /dev/null +++ b/Subsurface/Content/Characters/Human/lastnames.txt @@ -0,0 +1,157 @@ +Aker +Angles +Atterbury +Bartolomeo +Basnett +Baumgardner +Bergman +Berlin +Berner +Blackwell +Blakley +Boman +Bonham +Books +Box +Breeki +Bushman +Callahan +Callanan +Carey +Celina Ryder +Chandler +Chaney +Chapman +Chiu +Clothier +Cobble +Colpitts +Combs +Connors +Cork +Cortese +Danko +Day +Delange +Deloney +Deras +Dews +Dimauro +Dollinger +Dorado +Dudley +Duplantier +Eastman +Feinberg +Feinman +Fesler +Fleig +Flowers +Fonte +Frye +Gadberry +Gallagher +Gallardo +Gears +Geist +Gonzales +Goodin +Gumbs +Hajek +Hallett +Hardy +Harp +Hebert +Hedrick +Hefner +Heuer +Hillard +Hines +Hocker +Holter +House +Jack +Jones +Keller +Kinney +Klein +Knowles +Krall +Lane +Lavey +Leonard +Lester +Linares +Lowe +Malone +Mansfield +Marchi +Mason +Maynard +Maynes +McLeroy +McNeal +Mead +Meals +Meisner +Mell +Mendel +Mulford +Mungo +Navarro +Newton +Nicolosi +Ordway +Ouellette +Palmieri +Parchman +Parker +Parks +Petersen +Phoenix +Pickett +Planck +Pool +Poole +Price +Radebaugh +Radice +Ram +Rhymer +Riddle +Riles +Robertson +Rolfe +Rosewood +Sacks +Santillan +Santistevan +Sawyer +Severance +Smith +Southworth +Sparks +Stalvey +Stanley +Stein +Stogner +Summitt +Sunderland +Swanger +Swanson +Sykes +Tamashiro +Tate +Thompkins +Tootle +Tseng +Tyson +Ulrich +Vaughn +Veach +Velazquez +Welden +Wheeling +Whitely +Wiley +Williams \ No newline at end of file diff --git a/Subsurface/Content/Characters/Human/legs.png b/Subsurface/Content/Characters/Human/legs.png new file mode 100644 index 000000000..83d56c70d Binary files /dev/null and b/Subsurface/Content/Characters/Human/legs.png differ diff --git a/Subsurface/Content/Characters/Human/torso.png b/Subsurface/Content/Characters/Human/torso.png new file mode 100644 index 000000000..2da41f32c Binary files /dev/null and b/Subsurface/Content/Characters/Human/torso.png differ diff --git a/Subsurface/Content/Characters/Jobs.xml b/Subsurface/Content/Characters/Jobs.xml new file mode 100644 index 000000000..83d15703d --- /dev/null +++ b/Subsurface/Content/Characters/Jobs.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/Characters/Scorpion/scorpion.png b/Subsurface/Content/Characters/Scorpion/scorpion.png new file mode 100644 index 000000000..be0e4f02d Binary files /dev/null and b/Subsurface/Content/Characters/Scorpion/scorpion.png differ diff --git a/Subsurface/Content/Characters/Scorpion/scorpion.xml b/Subsurface/Content/Characters/Scorpion/scorpion.xml new file mode 100644 index 000000000..f6b2ebf02 --- /dev/null +++ b/Subsurface/Content/Characters/Scorpion/scorpion.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Subsurface/Content/Characters/Scorpion/scorpionattack1.ogg b/Subsurface/Content/Characters/Scorpion/scorpionattack1.ogg new file mode 100644 index 000000000..e99d3f2a4 Binary files /dev/null and b/Subsurface/Content/Characters/Scorpion/scorpionattack1.ogg differ diff --git a/Subsurface/Content/Characters/Scorpion/scorpionidle1.ogg b/Subsurface/Content/Characters/Scorpion/scorpionidle1.ogg new file mode 100644 index 000000000..6918d61cb Binary files /dev/null and b/Subsurface/Content/Characters/Scorpion/scorpionidle1.ogg differ diff --git a/Subsurface/Content/Characters/Scorpion/scorpionidle2.ogg b/Subsurface/Content/Characters/Scorpion/scorpionidle2.ogg new file mode 100644 index 000000000..89b047eeb Binary files /dev/null and b/Subsurface/Content/Characters/Scorpion/scorpionidle2.ogg differ diff --git a/Subsurface/Content/Characters/TigerThresher/tigerthresher.png b/Subsurface/Content/Characters/TigerThresher/tigerthresher.png new file mode 100644 index 000000000..3400159e1 Binary files /dev/null and b/Subsurface/Content/Characters/TigerThresher/tigerthresher.png differ diff --git a/Subsurface/Content/Characters/TigerThresher/tigerthresher.xml b/Subsurface/Content/Characters/TigerThresher/tigerthresher.xml new file mode 100644 index 000000000..8474606f0 --- /dev/null +++ b/Subsurface/Content/Characters/TigerThresher/tigerthresher.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/Characters/panzershrimp.png b/Subsurface/Content/Characters/panzershrimp.png new file mode 100644 index 000000000..20be6c30d Binary files /dev/null and b/Subsurface/Content/Characters/panzershrimp.png differ diff --git a/Subsurface/Content/Characters/shrimp.xml b/Subsurface/Content/Characters/shrimp.xml new file mode 100644 index 000000000..147ae0cfe --- /dev/null +++ b/Subsurface/Content/Characters/shrimp.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Subsurface/Content/HUD/caret.png b/Subsurface/Content/HUD/caret.png new file mode 100644 index 000000000..32ecaf16e Binary files /dev/null and b/Subsurface/Content/HUD/caret.png differ diff --git a/Subsurface/Content/HUD/style.xml b/Subsurface/Content/HUD/style.xml new file mode 100644 index 000000000..b7458d130 --- /dev/null +++ b/Subsurface/Content/HUD/style.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + Ÿ + + + + diff --git a/Subsurface/Content/SpriteFont1.xnb b/Subsurface/Content/SpriteFont1.xnb new file mode 100644 index 000000000..ab9f04af5 Binary files /dev/null and b/Subsurface/Content/SpriteFont1.xnb differ diff --git a/Subsurface/Content/effects.mgfx b/Subsurface/Content/effects.mgfx new file mode 100644 index 000000000..613c88437 Binary files /dev/null and b/Subsurface/Content/effects.mgfx differ diff --git a/Subsurface/Content/randomevents.xml b/Subsurface/Content/randomevents.xml new file mode 100644 index 000000000..e65489627 --- /dev/null +++ b/Subsurface/Content/randomevents.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/Subsurface/Content/step.ogg b/Subsurface/Content/step.ogg new file mode 100644 index 000000000..33d2a32dd Binary files /dev/null and b/Subsurface/Content/step.ogg differ diff --git a/Subsurface/Content/waterbump.jpg b/Subsurface/Content/waterbump.jpg new file mode 100644 index 000000000..21fbc07b9 Binary files /dev/null and b/Subsurface/Content/waterbump.jpg differ diff --git a/Subsurface/DebugConsole.cs b/Subsurface/DebugConsole.cs new file mode 100644 index 000000000..f49882672 --- /dev/null +++ b/Subsurface/DebugConsole.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using Subsurface.Networking; + +namespace Subsurface +{ + struct ColoredText + { + public string text; + public Color color; + + public ColoredText(string text, Color color) + { + this.text = text; + this.color = color; + } + } + + static class DebugConsole + { + public static List messages = new List(); + + static bool isOpen; + + static GUITextBox textBox; + + //used for keeping track of the message entered when pressing up/down + static int selectedIndex; + + public static bool IsOpen + { + get { return isOpen; } + } + + public static void Init(GameWindow window) + { + textBox = new GUITextBox(new Rectangle(30, 480,780, 30), Color.Black, Color.White, Alignment.Left, Alignment.Left); + NewMessage("Press F3 to open/close the debug console", Color.Green); + } + + public static void Update(Game1 game, float deltaTime) + { + if (PlayerInput.KeyHit(Keys.F3)) + { + isOpen = !isOpen; + if (isOpen) + { + textBox.Select(); + } + else + { + textBox.Deselect(); + } + + //keyboardDispatcher.Subscriber = (isOpen) ? textBox : null; + } + + if (isOpen) + { + Character.disableControls = true; + + if (PlayerInput.KeyHit(Keys.Up)) + { + SelectMessage(-1); + } + else if (PlayerInput.KeyHit(Keys.Down)) + { + SelectMessage(1); + } + + textBox.Update(deltaTime); + + if (PlayerInput.GetKeyboardState.IsKeyDown(Keys.Enter) && textBox.Text != "") + { + messages.Add(new ColoredText(textBox.Text, Color.White)); + ExecuteCommand(textBox.Text, game); + textBox.Text = ""; + + selectedIndex = messages.Count; + } + } + } + + private static void SelectMessage(int direction) + { + int messageCount = messages.Count; + if (messageCount == 0) return; + + direction = Math.Min(Math.Max(-1, direction), 1); + + selectedIndex += direction; + if (selectedIndex < 0) selectedIndex = messageCount - 1; + selectedIndex = selectedIndex % messageCount; + + textBox.Text = messages[selectedIndex].text; + } + + public static void Draw(SpriteBatch spriteBatch) + { + if (!isOpen) return; + + int x = 20, y = 20; + int width = 800, height = 500; + + int margin = 5; + + GUI.DrawRectangle(spriteBatch, + new Vector2(x, y), + new Vector2(width, height), + new Color(0.4f, 0.4f, 0.4f, 0.6f), true); + + GUI.DrawRectangle(spriteBatch, + new Vector2(x + margin, y + margin), + new Vector2(width - margin * 2, height - margin * 2), + new Color(0.0f, 0.0f, 0.0f, 0.6f), true); + + //remove messages that won't fit on the screen + while (messages.Count() * 20 > height-70) + { + messages.RemoveAt(0); + } + + Vector2 messagePos = new Vector2(x + margin * 2, y + height - 70 - messages.Count()*20); + foreach (ColoredText message in messages) + { + spriteBatch.DrawString(GUI.font, message.text, messagePos, message.color); + messagePos.Y += 20; + } + + textBox.Draw(spriteBatch); + } + + public static void ExecuteCommand(string command, Game1 game) + { + if (command == "") return; + string[] commands = command.Split(' '); + + switch (commands[0].ToLower()) + { + case "spawn": + if (commands.Length == 1) return; + + if (commands[1].ToLower()=="human") + { + WayPoint spawnPoint = WayPoint.GetRandom(WayPoint.SpawnType.Human); + Character.Controlled = new Character("Content/Characters/Human/human.xml", (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition); + if (Game1.gameSession != null) Game1.gameSession.crewManager.AddCharacter(Character.Controlled); + if (Game1.gameSession != null) Game1.gameSession.crewManager.SelectCharacter(Character.Controlled); + } + else + { + WayPoint spawnPoint = WayPoint.GetRandom(WayPoint.SpawnType.Enemy); + new Character("Content/Characters/" + commands[1] + "/" + commands[1] + ".xml", (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition); + } + + break; + case "startserver": + if (Game1.server==null) + Game1.server = new GameServer(); + break; + case "kick": + if (Game1.server == null) break; + Game1.server.KickPlayer(commands[1]); + break; + case "startclient": + if (commands.Length == 1) return; + if (Game1.client == null) + { + Game1.client = new GameClient("Name"); + Game1.client.ConnectToServer(commands[1]); + } + break; + case "mainmenuscreen": + case "mainmenu": + case "menu": + Game1.mainMenuScreen.Select(); + break; + case "gamescreen": + case "game": + Game1.gameScreen.Select(); + break; + case "editmapscreen": + case "editmap": + case "edit": + Game1.editMapScreen.Select(); + break; + case "editcharacter": + case "editchar": + Game1.editCharacterScreen.Select(); + break; + case "editwater": + case "water": + if (Game1.client== null) + { + Hull.EditWater = !Hull.EditWater; + } + break; + case "fowenabled": + case "fow": + case "drawfow": + Lights.LightManager.fowEnabled = !Lights.LightManager.fowEnabled; + break; + case "lobbyscreen": + case "lobby": + Game1.lobbyScreen.Select(); + break; + case "savemap": + Map.Save("Content/SavedMaps/", commands[1]); + NewMessage("map saved", Color.Green); + break; + case "loadmap": + Map.Load("Content/SavedMaps/", commands[1]); + break; + case "debugdraw": + Hull.DebugDraw = !Hull.DebugDraw; + Ragdoll.DebugDraw = !Ragdoll.DebugDraw; + break; + default: + NewMessage("Command not found", Color.Red); + break; + } + } + + public static void NewMessage(string msg, Color color) + { + if (String.IsNullOrEmpty((msg))) return; + messages.Add(new ColoredText(msg, color)); + + if (textBox != null && textBox.Text == "") selectedIndex = messages.Count; + } + + public static void ThrowError(string error, Exception e = null) + { + if (e!=null) error += " {"+e.Message+"}"; + NewMessage(error, Color.Red); + isOpen = true; + } + } +} diff --git a/Subsurface/EventInput/EventInput.cs b/Subsurface/EventInput/EventInput.cs new file mode 100644 index 000000000..402be3fd4 --- /dev/null +++ b/Subsurface/EventInput/EventInput.cs @@ -0,0 +1,208 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace EventInput +{ + + public class KeyboardLayout + { + const uint KLF_ACTIVATE = 1; //activate the layout + const int KL_NAMELENGTH = 9; // length of the keyboard buffer + const string LANG_EN_US = "00000409"; + const string LANG_HE_IL = "0001101A"; + + [DllImport("user32.dll")] + private static extern long LoadKeyboardLayout( + string pwszKLID, // input locale identifier + uint Flags // input locale identifier options + ); + + [DllImport("user32.dll")] + private static extern long GetKeyboardLayoutName( + StringBuilder pwszKLID //[out] string that receives the name of the locale identifier + ); + + public static string getName() + { + StringBuilder name = new StringBuilder(KL_NAMELENGTH); + GetKeyboardLayoutName(name); + return name.ToString(); + } + } + + public class CharacterEventArgs : EventArgs + { + private readonly char character; + private readonly int lParam; + + public CharacterEventArgs(char character, int lParam) + { + this.character = character; + this.lParam = lParam; + } + + public char Character + { + get { return character; } + } + + public int Param + { + get { return lParam; } + } + + public int RepeatCount + { + get { return lParam & 0xffff; } + } + + public bool ExtendedKey + { + get { return (lParam & (1 << 24)) > 0; } + } + + public bool AltPressed + { + get { return (lParam & (1 << 29)) > 0; } + } + + public bool PreviousState + { + get { return (lParam & (1 << 30)) > 0; } + } + + public bool TransitionState + { + get { return (lParam & (1 << 31)) > 0; } + } + } + + public class KeyEventArgs : EventArgs + { + private Keys keyCode; + + public KeyEventArgs(Keys keyCode) + { + this.keyCode = keyCode; + } + + public Keys KeyCode + { + get { return keyCode; } + } + } + + public delegate void CharEnteredHandler(object sender, CharacterEventArgs e); + public delegate void KeyEventHandler(object sender, KeyEventArgs e); + + public static class EventInput + { + /// + /// Event raised when a character has been entered. + /// + public static event CharEnteredHandler CharEntered; + + /// + /// Event raised when a key has been pressed down. May fire multiple times due to keyboard repeat. + /// + public static event KeyEventHandler KeyDown; + + /// + /// Event raised when a key has been released. + /// + public static event KeyEventHandler KeyUp; + + delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + + static bool initialized; + static IntPtr prevWndProc; + static WndProc hookProcDelegate; + static IntPtr hIMC; + + //various Win32 constants that we need + const int GWL_WNDPROC = -4; + const int WM_KEYDOWN = 0x100; + const int WM_KEYUP = 0x101; + const int WM_CHAR = 0x102; + const int WM_IME_SETCONTEXT = 0x0281; + const int WM_INPUTLANGCHANGE = 0x51; + const int WM_GETDLGCODE = 0x87; + const int WM_IME_COMPOSITION = 0x10f; + const int DLGC_WANTALLKEYS = 4; + + //Win32 functions that we're using + [DllImport("Imm32.dll", CharSet = CharSet.Unicode)] + static extern IntPtr ImmGetContext(IntPtr hWnd); + + [DllImport("Imm32.dll", CharSet = CharSet.Unicode)] + static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + + /// + /// Initialize the TextInput with the given GameWindow. + /// + /// The XNA window to which text input should be linked. + public static void Initialize(GameWindow window) + { + if (initialized) + throw new InvalidOperationException("TextInput.Initialize can only be called once!"); + + hookProcDelegate = HookProc; + prevWndProc = (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC, + (int)Marshal.GetFunctionPointerForDelegate(hookProcDelegate)); + + hIMC = ImmGetContext(window.Handle); + initialized = true; + } + + static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) + { + IntPtr returnCode = CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam); + + switch (msg) + { + case WM_GETDLGCODE: + returnCode = (IntPtr)(returnCode.ToInt32() | DLGC_WANTALLKEYS); + break; + + case WM_KEYDOWN: + if (KeyDown != null) + KeyDown(null, new KeyEventArgs((Keys)wParam)); + break; + + case WM_KEYUP: + if (KeyUp != null) + KeyUp(null, new KeyEventArgs((Keys)wParam)); + break; + + case WM_CHAR: + if (CharEntered != null) + CharEntered(null, new CharacterEventArgs((char)wParam, lParam.ToInt32())); + break; + + case WM_IME_SETCONTEXT: + if (wParam.ToInt32() == 1) + ImmAssociateContext(hWnd, hIMC); + break; + + case WM_INPUTLANGCHANGE: + ImmAssociateContext(hWnd, hIMC); + returnCode = (IntPtr)1; + break; + } + + return returnCode; + } + } + + +} \ No newline at end of file diff --git a/Subsurface/EventInput/KeyboardDispatcher.cs b/Subsurface/EventInput/KeyboardDispatcher.cs new file mode 100644 index 000000000..9427af6d5 --- /dev/null +++ b/Subsurface/EventInput/KeyboardDispatcher.cs @@ -0,0 +1,85 @@ +using System; +using System.Threading; +using System.Windows; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace EventInput +{ + public interface IKeyboardSubscriber + { + void ReceiveTextInput(char inputChar); + void ReceiveTextInput(string text); + void ReceiveCommandInput(char command); + void ReceiveSpecialInput(Keys key); + + bool Selected { get; set; } //or Focused + } + + public class KeyboardDispatcher + { + public KeyboardDispatcher(GameWindow window) + { + EventInput.Initialize(window); + EventInput.CharEntered += EventInput_CharEntered; + EventInput.KeyDown += EventInput_KeyDown; + } + + void EventInput_KeyDown(object sender, KeyEventArgs e) + { + if (_subscriber == null) + return; + + _subscriber.ReceiveSpecialInput(e.KeyCode); + } + + void EventInput_CharEntered(object sender, CharacterEventArgs e) + { + if (_subscriber == null) + return; + if (char.IsControl(e.Character)) + { + //ctrl-v + if (e.Character == 0x16) + { + //XNA runs in Multiple Thread Apartment state, which cannot recieve clipboard + Thread thread = new Thread(PasteThread); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + _subscriber.ReceiveTextInput(_pasteResult); + } + else + { + _subscriber.ReceiveCommandInput(e.Character); + } + } + else + { + _subscriber.ReceiveTextInput(e.Character); + } + } + + IKeyboardSubscriber _subscriber; + public IKeyboardSubscriber Subscriber + { + get { return _subscriber; } + set + { + if (_subscriber != null) + _subscriber.Selected = false; + _subscriber = value; + if (value != null) + value.Selected = true; + } + } + + //Thread has to be in Single Thread Apartment state in order to receive clipboard + string _pasteResult = ""; + [STAThread] + void PasteThread() + { + _pasteResult = Clipboard.ContainsText() ? Clipboard.GetText() : ""; + } + } +} diff --git a/Subsurface/Events/EventManager.cs b/Subsurface/Events/EventManager.cs new file mode 100644 index 000000000..bf4a7fe7f --- /dev/null +++ b/Subsurface/Events/EventManager.cs @@ -0,0 +1,106 @@ +//using Microsoft.Xna.Framework; +//using Microsoft.Xna.Framework.Graphics; +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; + +//namespace Subsurface +//{ +// static class EventManager +// { +// static Event activeEvent; + +// static GUIFrame infoPanel; + +// const int MaxPreviousEvents = 6; +// const float PreviouslyUsedWeight = 10.0f; + +// static List previousEvents = new List(); + +// public static Event ActiveEvent +// { +// get { return activeEvent; } +// } + +// public static void StartShift() +// { +// int eventCount = Event.list.Count(); + +// //create an array where the probability of an event being selected will be calculated +// float[] eventProbability = new float[eventCount]; + +// float probabilitySum = 0.0f; + +// for (int i = 0; i < eventCount; i++) +// { +// eventProbability[i] = Event.list[i].Commonness; + +// //if the event has been previously selected, it's less likely to be selected now +// int previousEventIndex = previousEvents.FindIndex(x => x == Event.list[i]); +// if (previousEventIndex>=0) +// { +// //how many shifts ago was the event last selected +// int eventDist = eventCount - previousEventIndex; + +// float weighting = (1.0f / eventDist) * PreviouslyUsedWeight; + +// eventProbability[i] *= weighting; +// } + +// probabilitySum += eventProbability[i]; +// } + +// float randomNumber = (float)Game1.random.NextDouble()*probabilitySum; + +// for (int i = 0; i < eventCount; i++) +// { +// if (randomNumber <= eventProbability[i]) +// { +// SelectEvent(Event.list[i]); +// return; +// } + +// randomNumber -= eventProbability[i]; +// } +// } + +// public static void SelectEvent(Event selectedEvent) +// { +// if (selectedEvent == null) return; + +// activeEvent = selectedEvent; +// previousEvents.Add(activeEvent); + +// activeEvent.Init(); +// } + +// public static void Update(GameTime gameTime) +// { +// if (activeEvent==null) return; +// activeEvent.Update(gameTime); +// } + +// public static void DrawInfo(SpriteBatch spriteBatch) +// { +// if (activeEvent==null || !activeEvent.IsStarted) return; + +// if (infoPanel == null) +// { +// infoPanel = new GUIFrame(new Rectangle(Game1.GraphicsWidth - 320, 20, 300, 100), Color.White * 0.8f); +// infoPanel.Padding = GUI.style.smallPadding; +// infoPanel.AddChild(new GUITextBlock(new Rectangle(0, 0, 200, 20), activeEvent.Name, Color.Transparent, Color.Black)); +// infoPanel.AddChild(new GUITextBlock(new Rectangle(0, 0, 200, 50), activeEvent.Description, Color.Transparent, Color.Black)); +// } + + +// //infoPanel.Draw(spriteBatch); +// } + +// public static void EventFinished(Event e) +// { +// if (e != activeEvent) return; +// infoPanel.AddChild(new GUITextBlock(new Rectangle(0, 0, 200, 80), "Finished!", Color.Transparent, Color.Black)); +// } +// } +//} diff --git a/Subsurface/Events/MonsterEvent.cs b/Subsurface/Events/MonsterEvent.cs new file mode 100644 index 000000000..822f3ed05 --- /dev/null +++ b/Subsurface/Events/MonsterEvent.cs @@ -0,0 +1,59 @@ +using System.Xml.Linq; +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + class MonsterEvent : ScriptedEvent + { + private string characterFile; + + private int minAmount, maxAmount; + + private Character[] monsters; + + public MonsterEvent(XElement element) + : base (element) + { + characterFile = ToolBox.GetAttributeString(element, "characterfile", ""); + + minAmount = ToolBox.GetAttributeInt(element, "minamount", 1); + maxAmount = ToolBox.GetAttributeInt(element, "maxamount", 1); + } + + protected override void Start() + { + WayPoint randomWayPoint = WayPoint.GetRandom(WayPoint.SpawnType.Enemy); + + int amount = Game1.random.Next(minAmount, maxAmount); + + monsters = new Character[amount]; + + for (int i = 0; i < amount; i++) + { + monsters[i] = new Character(characterFile, + (randomWayPoint == null) ? Vector2.Zero : randomWayPoint.SimPosition); + } + + } + + public override void Update(float deltaTime) + { + base.Update(deltaTime); + + if (!isStarted) return; + + if (!isFinished) + { + bool monstersDead = true; + for (int i = 0; i < monsters.Length; i++) + { + if (monsters[i].IsDead) continue; + + monstersDead = false; + break; + } + if (monstersDead) Finished(); + } + } + } +} diff --git a/Subsurface/Events/PropertyTask.cs b/Subsurface/Events/PropertyTask.cs new file mode 100644 index 000000000..c972e2a2d --- /dev/null +++ b/Subsurface/Events/PropertyTask.cs @@ -0,0 +1,28 @@ +namespace Subsurface +{ + class PropertyTask : Task + { + Item item; + + + public delegate bool IsFinishedHandler(); + private IsFinishedHandler IsFinishedChecker; + + public PropertyTask(TaskManager taskManager, Item item, IsFinishedHandler isFinished, float priority, string name) + : base(taskManager, priority, name) + { + this.item = item; + IsFinishedChecker = isFinished; + + taskManager.TaskStarted(this); + } + + public override void Update(float deltaTime) + { + if (IsFinishedChecker()) + { + Finished(); + } + } + } +} diff --git a/Subsurface/Events/RepairTask.cs b/Subsurface/Events/RepairTask.cs new file mode 100644 index 000000000..7175c4253 --- /dev/null +++ b/Subsurface/Events/RepairTask.cs @@ -0,0 +1,20 @@ +namespace Subsurface +{ + class RepairTask : Task + { + Item item; + + public RepairTask(TaskManager taskManager, Item item, float priority, string name) + : base(taskManager, priority, name) + { + this.item = item; + + taskManager.TaskStarted(this); + } + + public override void Update(float deltaTime) + { + if (item.Condition > 50.0f) Finished(); + } + } +} diff --git a/Subsurface/Events/ScriptedEvent.cs b/Subsurface/Events/ScriptedEvent.cs new file mode 100644 index 000000000..831aed4fd --- /dev/null +++ b/Subsurface/Events/ScriptedEvent.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Xml.Linq; + +namespace Subsurface +{ + class ScriptedEvent + { + private static string configFile = "Content/randomevents.xml"; + + const int MaxPreviousEvents = 6; + const float PreviouslyUsedWeight = 10.0f; + + static List previousEvents = new List(); + + protected string name; + protected string description; + + protected int commonness; + protected int difficulty; + + //the time after starting a shift after which the event is started + //the time is set to a random value between startTimeMin and startTimeMax at the start of the shift + private int startTimeMin; + private int startTimeMax; + + private double startTimer; + + protected bool isStarted; + protected bool isFinished; + + public string Name + { + get { return name; } + } + + public string Description + { + get { return description; } + } + + public int Commonness + { + get { return commonness; } + } + + public bool IsStarted + { + get { return isStarted; } + } + + public bool IsFinished + { + get { return isFinished; } + } + + public int Difficulty + { + get { return difficulty; } + } + + public ScriptedEvent(XElement element) + { + name = ToolBox.GetAttributeString(element, "name", ""); + description = ToolBox.GetAttributeString(element, "description", ""); + + difficulty = ToolBox.GetAttributeInt(element, "difficulty", 1); + commonness = ToolBox.GetAttributeInt(element, "commonness", 1); + + if (element.Attribute("starttime") != null) + { + startTimeMax = ToolBox.GetAttributeInt(element, "starttime", 1); + startTimeMin = startTimeMax; + } + else + { + startTimeMax = ToolBox.GetAttributeInt(element, "starttimemax", 1); + startTimeMin = ToolBox.GetAttributeInt(element, "starttimemin", 1); + } + } + + + public static ScriptedEvent LoadRandom() + { + XDocument doc = ToolBox.TryLoadXml(configFile); + if (doc == null) return null; + + int eventCount = doc.Root.Elements().Count(); + //int[] commonness = new int[eventCount]; + float[] eventProbability = new float[eventCount]; + + float probabilitySum = 0.0f; + + int i = 0; + foreach (XElement element in doc.Root.Elements()) + { + eventProbability[i] = ToolBox.GetAttributeInt(element, "commonness", 1); + + //if the event has been previously selected, it's less likely to be selected now + int previousEventIndex = previousEvents.FindIndex(x => x == i); + if (previousEventIndex >= 0) + { + //how many shifts ago was the event last selected + int eventDist = eventCount - previousEventIndex; + + float weighting = (1.0f / eventDist) * PreviouslyUsedWeight; + + eventProbability[i] *= weighting; + } + + probabilitySum += eventProbability[i]; + + i++; + } + + float randomNumber = (float)Game1.random.NextDouble() * probabilitySum; + + i = 0; + foreach (XElement element in doc.Root.Elements()) + { + if (randomNumber <= eventProbability[i]) + { + Type t; + string type = element.Name.ToString(); + + try + { + t = Type.GetType("Subsurface." + type + ", Subsurface", true, true); + if (t == null) + { + DebugConsole.ThrowError("Error in " + configFile + "! Could not find a an event class of the type ''" + type + "''."); + continue; + } + } + catch + { + DebugConsole.ThrowError("Error in " + configFile + "! Could not find a an event class of the type ''" + type + "''."); + continue; + } + + ConstructorInfo constructor = t.GetConstructor(new[] { typeof(XElement) }); + object instance = constructor.Invoke(new object[] { element }); + + previousEvents.Add(i); + + return (ScriptedEvent)instance; + } + + randomNumber -= eventProbability[i]; + i++; + } + + return null; + } + + public virtual void Init() + { + isStarted = false; + isFinished = false; + startTimer = Game1.random.Next(startTimeMin, startTimeMax); + } + + protected virtual void Start() + { + } + + public virtual void Update(float deltaTime) + { + if (isStarted) return; + + if (startTimer>0) + { + startTimer -= deltaTime; + } + else + { + Start(); + isStarted = true; + } + } + + public virtual void Finished() + { + isFinished = true; + //EventManager.EventFinished(this); + } + + + } +} diff --git a/Subsurface/Events/ScriptedTask.cs b/Subsurface/Events/ScriptedTask.cs new file mode 100644 index 000000000..45ef1e0de --- /dev/null +++ b/Subsurface/Events/ScriptedTask.cs @@ -0,0 +1,28 @@ +namespace Subsurface +{ + class ScriptedTask : Task + { + private ScriptedEvent scriptedEvent; + + private bool prevStarted; + + public ScriptedTask(TaskManager taskManager, ScriptedEvent scriptedEvent) + : base(taskManager, scriptedEvent.Difficulty, scriptedEvent.Name) + { + this.scriptedEvent = scriptedEvent; + scriptedEvent.Init(); + } + + public override void Update(float deltaTime) + { + if (prevStarted == false && scriptedEvent.IsStarted) + { + taskManager.TaskStarted(this); + prevStarted = true; + } + + scriptedEvent.Update(deltaTime); + if (scriptedEvent.IsFinished) Finished(); + } + } +} diff --git a/Subsurface/Events/Task.cs b/Subsurface/Events/Task.cs new file mode 100644 index 000000000..08dd5dade --- /dev/null +++ b/Subsurface/Events/Task.cs @@ -0,0 +1,50 @@ + +namespace Subsurface +{ + class Task + { + + protected string name; + + private float priority; + + protected TaskManager taskManager; + + protected bool isFinished; + + public string Name + { + get { return name; } + } + + public float Priority + { + get { return priority; } + } + + public bool IsFinished + { + get { return isFinished; } + } + + public Task(TaskManager taskManager, float priority, string name) + { + this.taskManager = taskManager; + this.priority = priority; + this.name = name; + + taskManager.AddTask(this); + } + + public virtual void Update(float deltaTime) + { + + } + + protected virtual void Finished() + { + taskManager.TaskFinished(this); + isFinished = true; + } + } +} diff --git a/Subsurface/Events/TaskManager.cs b/Subsurface/Events/TaskManager.cs new file mode 100644 index 000000000..f039eee2b --- /dev/null +++ b/Subsurface/Events/TaskManager.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface +{ + class TaskManager + { + private List tasks; + + private GUIListBox taskListBox; + + public TaskManager(GameSession session) + { + tasks = new List(); + + taskListBox = new GUIListBox(new Rectangle(Game1.GraphicsWidth - 250, 50, 250, 500), Color.Transparent); + //taskListBox.ScrollBarEnabled = false; + taskListBox.Padding = GUI.style.smallPadding; + } + + public void AddTask(Task newTask) + { + if (tasks.Contains(newTask)) return; + + tasks.Add(newTask); + } + + public void StartShift(int scriptedEventCount) + { + CreateScriptedEvents(scriptedEventCount); + + taskListBox.ClearChildren(); + } + + + public void EndShift() + { + taskListBox.ClearChildren(); + tasks.Clear(); + } + + private void CreateScriptedEvents(int scriptedEventCount) + { + for (int i = 0; i < scriptedEventCount; i++) + { + ScriptedEvent scriptedEvent = ScriptedEvent.LoadRandom(); + AddTask(new ScriptedTask(this, scriptedEvent)); + } + } + + public void TaskStarted(Task task) + { + Color color = Color.Red; + int width = 250; + if (task.Priority < 30.0f) + { + width = 200; + color = Color.Yellow; + } + else if (task.Priority < 60) + { + width = 220; + color = Color.Orange; + } + + GUIFrame frame = new GUIFrame(new Rectangle(0,0,width,40), Color.Transparent, Alignment.Right, taskListBox); + frame.UserData = task; + frame.Padding = new Vector4(0.0f, 5.0f, 5.0f, 5.0f); + + GUIFrame colorFrame = new GUIFrame(new Rectangle(0, 0, 0, 0), color * 0.5f, Alignment.Right, frame); + GUITextBlock textBlock = new GUITextBlock(new Rectangle(5, 5, 0, 20), task.Name, Color.Transparent, Color.Black, Alignment.Right, colorFrame); + //textBlock.Padding = new Vector4(10.0f, 10.0f, 0.0f, 0.0f); + + //colorFrame.AddChild(textBlock); + + taskListBox.children.Sort((x, y) => ((Task)y.UserData).Priority.CompareTo(((Task)x.UserData).Priority)); + } + + public void TaskFinished(Task task) + { + + } + + + + public void Update(float deltaTime) + { + Task removeTask = null; + GUIComponent removeComponent = null; + foreach (Task task in tasks) + { + if (task.IsFinished) + { + foreach (GUIComponent comp in taskListBox.children) + { + if (comp.UserData as Task != task) continue; + comp.Rect = new Rectangle(comp.Rect.X, comp.Rect.Y, comp.Rect.Width, comp.Rect.Height - 1); + comp.children[0].ClearChildren(); + if (comp.Rect.Height < 1) + { + removeComponent = comp; + removeTask = task; + } + break; + } + + } + else + { + task.Update(deltaTime); + } + } + + if (removeComponent!= null) + { + taskListBox.RemoveChild(removeComponent); + tasks.Remove(removeTask); + } + + //endShiftButton.Enabled = finished || Game1.server!=null; + } + + public void Draw(SpriteBatch spriteBatch) + { + taskListBox.Draw(spriteBatch); + } + } +} diff --git a/Subsurface/FarseerPhysics MonoGame.dll b/Subsurface/FarseerPhysics MonoGame.dll new file mode 100644 index 000000000..66d295861 Binary files /dev/null and b/Subsurface/FarseerPhysics MonoGame.dll differ diff --git a/Subsurface/FrameCounter.cs b/Subsurface/FrameCounter.cs new file mode 100644 index 000000000..1674f264c --- /dev/null +++ b/Subsurface/FrameCounter.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Subsurface +{ + public class FrameCounter + { + public long TotalFrames { get; private set; } + public double TotalSeconds { get; private set; } + public double AverageFramesPerSecond { get; private set; } + public double CurrentFramesPerSecond { get; private set; } + + public const int MaximumSamples = 10; + + private Queue _sampleBuffer = new Queue(); + + public bool Update(double deltaTime) + { + //float deltaTime = stopwatch.ElapsedMilliseconds / 1000.0f; + + if (deltaTime == 0.0f) { return false; } + //stopwatch.Restart(); + + CurrentFramesPerSecond = (1.0 / deltaTime); + + _sampleBuffer.Enqueue(CurrentFramesPerSecond); + + if (_sampleBuffer.Count > MaximumSamples) + { + _sampleBuffer.Dequeue(); + AverageFramesPerSecond = _sampleBuffer.Average(i => i); + } + else + { + AverageFramesPerSecond = CurrentFramesPerSecond; + } + + if (AverageFramesPerSecond < 0 || AverageFramesPerSecond > 500) { } + + TotalFrames++; + TotalSeconds += deltaTime; + return true; + } + } + +} diff --git a/Subsurface/GUI/GUI.cs b/Subsurface/GUI/GUI.cs new file mode 100644 index 000000000..bdf708a5c --- /dev/null +++ b/Subsurface/GUI/GUI.cs @@ -0,0 +1,344 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace Subsurface +{ + [Flags] + public enum Alignment { CenterX = 1, Left = 2, Right = 4, CenterY = 8, Top = 16, Bottom = 32 } + + + class GUIMessage + { + ColoredText coloredText; + Vector2 pos; + + float lifeTime; + + Vector2 size; + + + public string Text + { + get { return coloredText.text; } + } + + public Color Color + { + get { return coloredText.color; } + } + + public Vector2 Pos + { + get { return pos; } + set { pos = value; } + } + + public Vector2 Size + { + get { return size; } + } + + + public float LifeTime + { + get { return lifeTime; } + set { lifeTime = value; } + } + + public GUIMessage(string text, Color color, Vector2 pos, float lifeTime) + { + this.coloredText = new ColoredText(text, color); + this.pos = pos; + this.lifeTime = lifeTime; + + this.size = GUI.font.MeasureString(text); + } + } + + class GUI + { + public static GUIStyle style; + + static Texture2D t; + public static SpriteFont font; + + private static GUIProgressBar drowningBar; + + private static GraphicsDevice graphicsDevice; + + + private static List messages = new List(); + + + private static Sound[] sounds; + + + public static void LoadContent(GraphicsDevice graphics) + { + graphicsDevice = graphics; + + sounds = new Sound[2]; + sounds[0] = Sound.Load("Content/Sounds/UI/UImsg.ogg"); + + // create 1x1 texture for line drawing + t = new Texture2D(graphicsDevice, 1, 1); + t.SetData( + new Color[] { Color.White });// fill the texture with white + + int width = 200, height = 20; + drowningBar = new GUIProgressBar(new Rectangle(Game1.GraphicsWidth / 2 - width / 2, 20, width, height), Color.Blue, 1.0f); + + + style = new GUIStyle("Content/HUD/style.xml"); + } + + public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f) + { + Vector2 edge = end - start; + // calculate angle to rotate line + float angle = (float)Math.Atan2(edge.Y, edge.X); + + sb.Draw(t, + new Rectangle(// rectangle defines shape of line and position of start of line + (int)start.X, + (int)start.Y, + (int)edge.Length(), //sb will strech the texture to fill this rectangle + 1), //width of line, change this to make thicker line + null, + clr, //colour of line + angle, //angle of line (calulated above) + new Vector2(0, 0), // point in line about which to rotate + SpriteEffects.None, + depth); + } + + public static void DrawRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, bool isFilled = false, float depth = 0.0f) + { + if (isFilled) + { + sb.Draw(t, new Rectangle((int)start.X,(int)start.Y,(int)size.X,(int)size.Y),null,clr); + } + else + { + Vector2 p2 = new Vector2(start.X + size.X, start.Y); + Vector2 p4 = new Vector2(start.X, start.Y + size.Y); + + DrawLine(sb, start, p2, clr, depth); + DrawLine(sb, p2, start + size, clr, depth); + DrawLine(sb, start + size, p4, clr, depth); + DrawLine(sb, p4, start, clr, depth); + } + } + + public static void DrawRectangle(SpriteBatch sb, Rectangle rect, Color clr, bool isFilled = false, float depth = 0.0f) + { + if (isFilled) + { + sb.Draw(t, rect, null, clr); + } + else + { + Vector2 p1 = new Vector2(rect.X, rect.Y); + Vector2 p2 = new Vector2(rect.X + rect.Width, rect.Y); + Vector2 p3 = new Vector2(rect.X + rect.Width, rect.Y + rect.Height); + Vector2 p4 = new Vector2(rect.X, rect.Y + rect.Height); + + DrawLine(sb, p1, p2, clr, depth); + DrawLine(sb, p2, p3, clr, depth); + DrawLine(sb, p3, p4, clr, depth); + DrawLine(sb, p4, p1, clr, depth); + } + } + + public static Texture2D CreateCircle(int radius) + { + int outerRadius = radius * 2 + 2; // So circle doesn't go out of bounds + Texture2D texture = new Texture2D(graphicsDevice, outerRadius, outerRadius); + + Color[] data = new Color[outerRadius * outerRadius]; + + // Colour the entire texture transparent first. + for (int i = 0; i < data.Length; i++) + data[i] = Color.Transparent; + + // Work out the minimum step necessary using trigonometry + sine approximation. + double angleStep = 1f / radius; + + for (double angle = 0; angle < Math.PI * 2; angle += angleStep) + { + // Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates + int x = (int)Math.Round(radius + radius * Math.Cos(angle)); + int y = (int)Math.Round(radius + radius * Math.Sin(angle)); + + data[y * outerRadius + x + 1] = Color.White; + } + + texture.SetData(data); + return texture; + } + + public static Texture2D CreateCapsule(int radius, int height) + { + int textureWidth = radius * 2, textureHeight = height + radius * 2; + + Texture2D texture = new Texture2D(graphicsDevice, textureWidth, textureHeight); + Color[] data = new Color[textureWidth * textureHeight]; + + // Colour the entire texture transparent first. + for (int i = 0; i < data.Length; i++) + data[i] = Color.Transparent; + + // Work out the minimum step necessary using trigonometry + sine approximation. + double angleStep = 1f / radius; + + for (int i = 0; i < 2; i++ ) + { + for (double angle = 0; angle < Math.PI * 2; angle += angleStep) + { + // Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates + int x = (int)Math.Round(radius + radius * Math.Cos(angle)); + int y = (height-1)*i + (int)Math.Round(radius + radius * Math.Sin(angle)); + + data[y * textureWidth + x] = Color.White; + } + } + + for (int y = radius; y children; + + protected ComponentState state; + + protected float alpha; + + public GUIComponent Parent + { + get { return parent; } + } + + public Rectangle Rect + { + get { return rect; } + set + { + int prevX = rect.X, prevY = rect.Y; + int prevWidth = rect.Width, prevHeight = rect.Height; + + rect = value; + + if (prevX == rect.X && prevY == rect.Y && rect.Width == prevWidth && rect.Height == prevHeight) return; + + foreach (GUIComponent child in children) + { + child.Rect = new Rectangle( + child.rect.X + (rect.X - prevX), + child.rect.Y + (rect.Y - prevY), + Math.Max(child.rect.Width + (rect.Width - prevWidth),0), + Math.Max(child.rect.Height + (rect.Height - prevHeight),0)); + } + } + } + + public ComponentState State + { + get { return state; } + set { state = value; } + } + + public object UserData + { + get { return userData; } + set { userData = value; } + } + + public virtual Vector4 Padding + { + get { return padding; } + set { padding = value; } + } + + public int CountChildren + { + get { return children.Count(); } + } + + public virtual Color Color + { + get { return color; } + set { color = value; } + } + + public Color HoverColor + { + get { return hoverColor; } + set { hoverColor = value; } + } + + public Color SelectedColor + { + get { return selectedColor; } + set { selectedColor = value; } + } + + + public float Alpha + { + get + { + return alpha; + } + set + { + alpha = MathHelper.Clamp(value, 0.0f, 1.0f); + foreach (GUIComponent child in children) + { + child.Alpha = value; + } + } + } + + protected GUIComponent() + { + alpha = 1.0f; + + children = new List(); + } + + public static void Init(GameWindow window) + { + keyboardDispatcher = new KeyboardDispatcher(window); + } + + public GUIComponent GetChild(object obj) + { + foreach (GUIComponent child in children) + { + if (child.UserData == obj) return child; + } + return null; + } + + public virtual void Draw(SpriteBatch spriteBatch) + { + Color newColor = color; + if (state == ComponentState.Selected) newColor = selectedColor; + if (state == ComponentState.Hover) newColor = hoverColor; + + GUI.DrawRectangle(spriteBatch, rect, newColor*alpha, true); + DrawChildren(spriteBatch); + } + + public virtual void Update(float deltaTime) + { + if (rect.Contains(PlayerInput.MousePosition)) + { + MouseOn = this; + } + else + { + if (MouseOn == this) MouseOn = null; + } + + foreach (GUIComponent child in children) + { + child.Update(deltaTime); + } + } + + protected virtual void UpdateDimensions(GUIComponent parent) + { + if (parent!=null) + { + if (rect.Width == 0) rect.Width = parent.Rect.Width - rect.X + - (int)parent.Padding.X - (int)parent.Padding.Z; + + if (rect.Height == 0) rect.Height = parent.Rect.Height - rect.Y + - (int)parent.Padding.Y - (int)parent.Padding.W; + + if (alignment.HasFlag(Alignment.CenterX)) + { + rect.X += parent.Rect.X + (int)parent.Rect.Width/2 - (int)rect.Width/2; + } + else if (alignment.HasFlag(Alignment.Right)) + { + rect.X += parent.Rect.X + (int)parent.Rect.Width - (int)parent.Padding.Z - (int)rect.Width; + } + else + { + rect.X += parent.Rect.X + (int)parent.Padding.X; + } + + if (alignment.HasFlag(Alignment.CenterY)) + { + rect.Y += parent.Rect.Y + (int)parent.Rect.Height / 2 - (int)rect.Height / 2; + } + else if (alignment.HasFlag(Alignment.Bottom)) + { + rect.Y += parent.Rect.Y + (int)parent.Rect.Height - (int)parent.Padding.W - (int)rect.Height; + } + else + { + rect.Y += parent.Rect.Y + (int)parent.Padding.Y; + } + + } + } + + public virtual void DrawChildren(SpriteBatch spriteBatch) + { + foreach (GUIComponent child in children) + { + child.Draw(spriteBatch); + } + } + + public virtual void AddChild(GUIComponent child) + { + child.parent = this; + child.UpdateDimensions(this); + + children.Add(child); + } + + public virtual void RemoveChild(GUIComponent child) + { + if (children.Contains(child)) children.Remove(child); + } + + public void ClearChildren() + { + children.Clear(); + } + } +} diff --git a/Subsurface/GUI/GUIFrame.cs b/Subsurface/GUI/GUIFrame.cs new file mode 100644 index 000000000..33c18c969 --- /dev/null +++ b/Subsurface/GUI/GUIFrame.cs @@ -0,0 +1,31 @@ +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + class GUIFrame : GUIComponent + { + public GUIFrame(Rectangle rect, Color color, Alignment alignment, GUIStyle style, GUIComponent parent = null) + : this(rect, color, alignment, parent) + { + hoverColor = style.hoverColor; + selectedColor = style.selectedColor; + } + + public GUIFrame(Rectangle rect, Color color, GUIComponent parent = null) + : this(rect,color,(Alignment.Left | Alignment.Top), parent) + { + } + + public GUIFrame(Rectangle rect, Color color, Alignment alignment, GUIComponent parent = null) + { + this.rect = rect; + + this.alignment = alignment; + + this.color = color; + if (parent!=null) + parent.AddChild(this); + } + + } +} diff --git a/Subsurface/GUI/GUIImage.cs b/Subsurface/GUI/GUIImage.cs new file mode 100644 index 000000000..f46cc86ff --- /dev/null +++ b/Subsurface/GUI/GUIImage.cs @@ -0,0 +1,84 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface +{ + class GUIImage : GUIComponent + { + Sprite sprite; + + Rectangle sourceRect; + + bool crop; + + public bool Crop + { + get + { + return crop; + } + set + { + crop = value; + if (crop) + { + sourceRect.Width = Math.Min(sprite.SourceRect.Width, Rect.Width); + sourceRect.Height = Math.Min(sprite.SourceRect.Height, Rect.Height); + } + } + } + + public float Scale + { + get; + set; + } + + public Rectangle SourceRect + { + get { return sourceRect; } + set { sourceRect = value; } + } + + public GUIImage(Rectangle rect, string spritePath, Alignment alignment, GUIComponent parent = null) + : this(rect, new Sprite(spritePath, Vector2.Zero), alignment, parent) + { + } + + public GUIImage(Rectangle rect, Sprite sprite, Alignment alignment, GUIComponent parent = null) + { + this.rect = rect; + + this.alignment = alignment; + + color = Color.White; + + alpha = 1.0f; + + Scale = 1.0f; + + this.sprite = sprite; + + if (rect.Width == 0) this.rect.Width = (int)sprite.size.X; + if (rect.Height == 0) this.rect.Height = (int)Math.Min(sprite.size.Y, sprite.size.Y * (this.rect.Width / sprite.size.X)); + + sourceRect = sprite.SourceRect; + + if (parent != null) + parent.AddChild(this); + } + + public override void Draw(SpriteBatch spriteBatch) + { + Color currColor = color; + if (state == ComponentState.Hover) currColor = hoverColor; + if (state == ComponentState.Selected) currColor = selectedColor; + + spriteBatch.Draw(sprite.Texture, new Vector2(rect.X, rect.Y), sourceRect, currColor * alpha, 0.0f, Vector2.Zero, + Scale, SpriteEffects.None, 0.0f); + + DrawChildren(spriteBatch); + } + } +} diff --git a/Subsurface/GUI/GUIListBox.cs b/Subsurface/GUI/GUIListBox.cs new file mode 100644 index 000000000..ff20dd83c --- /dev/null +++ b/Subsurface/GUI/GUIListBox.cs @@ -0,0 +1,257 @@ +using System; +using System.Diagnostics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface +{ + class GUIListBox : GUIComponent + { + protected GUIComponent selected; + + public delegate bool OnSelectedHandler(object obj); + public OnSelectedHandler OnSelected; + + public delegate object CheckSelectedHandler(); + public CheckSelectedHandler CheckSelected; + + private GUIScrollBar scrollBar; + + private int totalSize; + + private int spacing; + + private bool scrollBarEnabled; + private bool scrollBarHidden; + + private bool enabled; + + public object SelectedData + { + get { return (selected == null) ? null : selected.UserData; } + } + + public int SelectedIndex + { + get + { + if (selected == null) return -1; + return children.FindIndex(x => x == selected); + } + } + + public int Spacing + { + get { return spacing; } + set { spacing = value; } + } + + public bool Enabled + { + get { return enabled; } + set { enabled = value; } + } + + public bool ScrollBarEnabled + { + get { return scrollBarEnabled; } + set + { + if (value) + { + if (!scrollBarEnabled && scrollBarHidden) ShowScrollBar(); + } + else + { + if (scrollBarEnabled && !scrollBarHidden) HideScrollBar(); + } + + scrollBarEnabled = value; + } + } + + public GUIListBox(Rectangle rect, GUIStyle style, Alignment alignment, GUIComponent parent = null) + : this(rect, style.foreGroundColor, alignment, parent) + { + } + + public GUIListBox(Rectangle rect, Color color, GUIComponent parent = null) + : this(rect, color, (Alignment.Left | Alignment.Top), parent) + { + } + + public GUIListBox(Rectangle rect, Color color, Alignment alignment, GUIComponent parent = null) + { + this.rect = rect; + this.alignment = alignment; + + this.color = color; + + if (parent != null) + parent.AddChild(this); + + this.rect.Width -= 20; + + scrollBar = new GUIScrollBar( + new Rectangle(this.rect.X + this.rect.Width, this.rect.Y, 20, this.rect.Height), color, 1.0f); + + UpdateScrollBarSize(); + + enabled = true; + + scrollBarEnabled = true; + } + + public void Select(object selection) + { + foreach (GUIComponent child in children) + { + if (child.UserData != selection) continue; + + selected = child; + if (OnSelected != null) OnSelected(selected.UserData); + return; + } + } + + public override void Update(float deltaTime) + { + base.Update(deltaTime); + + scrollBar.Update(deltaTime); + } + + public void Select(int childIndex) + { + if (childIndex >= children.Count || childIndex<0) return; + + selected = children[childIndex]; + if (OnSelected != null) OnSelected(selected.UserData); + } + + public void Deselect() + { + selected = null; + } + + public void UpdateScrollBarSize() + { + totalSize = 0; + foreach (GUIComponent child in children) + { + totalSize += (scrollBar.IsHorizontal) ? child.Rect.Width : child.Rect.Height; + totalSize += spacing; + } + + scrollBar.BarSize = scrollBar.IsHorizontal ? + Math.Min((float)rect.Width / (float)totalSize, 1.0f) : + Math.Min((float)rect.Height / (float)totalSize, 1.0f); + + if (scrollBar.BarSize < 1.0f && scrollBarHidden) ShowScrollBar(); + if (scrollBar.BarSize >= 1.0f && !scrollBarHidden) HideScrollBar(); + } + + public override void AddChild(GUIComponent child) + { + base.AddChild(child); + + float oldScroll = scrollBar.BarScroll; + float oldSize = scrollBar.BarSize; + UpdateScrollBarSize(); + + if (scrollBar.BarSize<1.0f && (oldSize>=1.0f || oldScroll==1.0f)) + { + scrollBar.BarScroll = 1.0f; + } + + } + + public override void RemoveChild(GUIComponent child) + { + base.RemoveChild(child); + + UpdateScrollBarSize(); + + } + + private void ShowScrollBar() + { + scrollBarHidden = false; + Rect = new Rectangle(rect.X, rect.Y, rect.Width - scrollBar.Rect.Width, rect.Height); + } + + private void HideScrollBar() + { + scrollBarHidden = true; + Rect = new Rectangle(rect.X, rect.Y, rect.Width + scrollBar.Rect.Width, rect.Height); + } + + public override void Draw(SpriteBatch spriteBatch) + { + + GUI.DrawRectangle(spriteBatch, rect, color*alpha, true); + + int x = rect.X, y = rect.Y; + + if (!scrollBarHidden) + { + scrollBar.Draw(spriteBatch); + if (scrollBar.IsHorizontal) + { + x -= (int)((totalSize - rect.Height) * scrollBar.BarScroll); + } + else + { + y -= (int)((totalSize - rect.Height) * scrollBar.BarScroll); + } + } + + + for (int i = 0; i < children.Count; i++ ) + { + GUIComponent child = children[i]; + + child.Rect = new Rectangle(child.Rect.X, y, child.Rect.Width, child.Rect.Height); + y += child.Rect.Height + spacing; + + if (child.Rect.Y + child.Rect.Height < rect.Y) continue; + if (child.Rect.Y + child.Rect.Height > rect.Y + rect.Height) break; + + if (child.Rect.Y < rect.Y && child.Rect.Y + child.Rect.Height >= rect.Y) + { + y = rect.Y; + continue; + } + + if (selected == child) + { + child.State = ComponentState.Selected; + + if (CheckSelected != null) + { + if (CheckSelected() != selected.UserData) selected = null; + } + } + else if (child.Rect.Contains(PlayerInput.GetMouseState.Position) && enabled) + { + child.State = ComponentState.Hover; + if (PlayerInput.LeftButtonClicked()) + { + Debug.WriteLine("clicked"); + selected = child; + if (OnSelected != null) + OnSelected(child.UserData); + } + } + else + { + child.State = ComponentState.None; + } + + child.Draw(spriteBatch); + } + + //GUI.DrawRectangle(spriteBatch, rect, Color.Black, false); + } + } +} diff --git a/Subsurface/GUI/GUIProgressBar.cs b/Subsurface/GUI/GUIProgressBar.cs new file mode 100644 index 000000000..63c29d912 --- /dev/null +++ b/Subsurface/GUI/GUIProgressBar.cs @@ -0,0 +1,77 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface +{ + class GUIProgressBar : GUIComponent + { + private bool isHorizontal; + + private GUIFrame frame; + private float barSize; + + private int margin; + + public delegate float ProgressGetterHandler(); + public ProgressGetterHandler ProgressGetter; + + public bool IsHorizontal + { + get { return isHorizontal; } + } + + public float BarSize + { + get { return barSize; } + set + { + float oldBarSize = barSize; + barSize = MathHelper.Clamp(value, 0.0f, 1.0f); + if (barSize!=oldBarSize) UpdateRect(); + } + } + + public GUIProgressBar(Rectangle rect, Color color, float barSize, GUIComponent parent = null) + : this(rect,color,barSize, (Alignment.Left | Alignment.Top), parent) + { + } + + public GUIProgressBar(Rectangle rect, Color color, float barSize, Alignment alignment, GUIComponent parent = null) + { + this.rect = rect; + this.color = color; + isHorizontal = (rect.Width > rect.Height); + + this.alignment = alignment; + + margin = 5; + + if (parent != null) + parent.AddChild(this); + + frame = new GUIFrame(new Rectangle(0,0,0,0), Color.White, this); + + this.barSize = barSize; + UpdateRect(); + } + + private void UpdateRect() + { + rect = new Rectangle( + frame.Rect.X + margin, + frame.Rect.Y + margin, + isHorizontal ? (int)((frame.Rect.Width - margin * 2) * barSize) : (frame.Rect.Width - margin * 2), + isHorizontal ? (frame.Rect.Height - margin * 2) : (int)((frame.Rect.Height - margin * 2) * barSize)); + } + + public override void Draw(SpriteBatch spriteBatch) + { + if (ProgressGetter != null) BarSize = ProgressGetter(); + + DrawChildren(spriteBatch); + + GUI.DrawRectangle(spriteBatch, rect, color*alpha, true); + } + + } +} diff --git a/Subsurface/GUI/GUIScrollBar.cs b/Subsurface/GUI/GUIScrollBar.cs new file mode 100644 index 000000000..48f426d88 --- /dev/null +++ b/Subsurface/GUI/GUIScrollBar.cs @@ -0,0 +1,175 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface +{ + class GUIScrollBar : GUIComponent + { + public static GUIScrollBar draggingBar; + + private bool isHorizontal; + + private GUIFrame frame; + private GUIButton bar; + private float barSize; + private float barScroll; + + private bool enabled; + + public delegate bool OnMovedHandler(object obj); + public OnMovedHandler OnMoved; + + public bool IsHorizontal + { + get { return isHorizontal; } + } + + public bool Enabled + { + get { return enabled; } + set { enabled = value; } + } + + public float BarScroll + { + get { return barScroll; } + set + { + barScroll = MathHelper.Clamp(value, 0.0f, 1.0f); + int newX = bar.Rect.X - frame.Rect.X, newY = bar.Rect.Y - frame.Rect.Y; + if (isHorizontal) + { + newX = (int)(barScroll *(frame.Rect.Width - bar.Rect.Width)); + newX = Math.Max(newX, 0); + newX = Math.Min(newX, frame.Rect.Width - bar.Rect.Width); + + } + else + { + newY = (int)(barScroll * (frame.Rect.Height- bar.Rect.Height)); + newY = Math.Max(newY, 0); + newY = Math.Min(newY, frame.Rect.Height - bar.Rect.Height); + + } + bar.Rect = new Rectangle(newX + frame.Rect.X, newY + frame.Rect.Y, bar.Rect.Width, bar.Rect.Height); + } + } + + public float BarSize + { + get { return barSize; } + set + { + float oldBarSize = barSize; + barSize = Math.Min(Math.Max(value, 0.0f), 1.0f); + if (barSize!=oldBarSize) UpdateRect(); + } + } + + public GUIScrollBar(Rectangle rect, GUIStyle style, float barSize, GUIComponent parent = null) + : this(rect, style.foreGroundColor, barSize, parent) + { + } + + public GUIScrollBar(Rectangle rect, Color color, float barSize, GUIComponent parent = null) + : this(rect, color, barSize, (Alignment.Left | Alignment.Top), parent) + { + } + + + public GUIScrollBar(Rectangle rect, Color color, float barSize, Alignment alignment, GUIComponent parent = null) + { + this.rect = rect; + //GetDimensions(parent); + + this.alignment = alignment; + + if (parent != null) + parent.AddChild(this); + + isHorizontal = (rect.Width > rect.Height); + frame = new GUIFrame(new Rectangle(0,0,0,0), Color.White, this); + //AddChild(frame); + + //System.Diagnostics.Debug.WriteLine(frame.rect); + + bar = new GUIButton(new Rectangle(0, 0, 0, 0), "", color, this); + bar.OnPressed = SelectBar; + //AddChild(bar); + + enabled = true; + + UpdateRect(); + } + + private void UpdateRect() + { + bar.Rect = new Rectangle( + frame.Rect.X, + frame.Rect.Y, + isHorizontal ? (int)(frame.Rect.Width * barSize) : frame.Rect.Width, + isHorizontal ? frame.Rect.Height : (int)(frame.Rect.Height * barSize)); + + foreach (GUIComponent child in bar.children) + { + child.Rect = bar.Rect; + } + } + + public override void Update(float deltaTime) + { + base.Update(deltaTime); + + if (draggingBar != this) return; + if (!PlayerInput.LeftButtonDown()) draggingBar = null; + + MoveButton(); + } + + public override void Draw(SpriteBatch spriteBatch) + { + DrawChildren(spriteBatch); + } + + private bool SelectBar() + { + if (!enabled) return false; + if (barSize == 1.0f) return false; + + draggingBar = this; + + return true; + + } + + private void MoveButton() + { + //if (!enabled) return false; + //if (barSize == 1.0f) return false; + + int newX = bar.Rect.X - frame.Rect.X, newY = bar.Rect.Y - frame.Rect.Y; + int moveAmount; + if (isHorizontal) + { + moveAmount = PlayerInput.GetMouseState.Position.X - PlayerInput.GetOldMouseState.Position.X; + newX = Math.Min(Math.Max(newX + moveAmount, 0), frame.Rect.Width - bar.Rect.Width); + + barScroll = (float)newX / ((float)frame.Rect.Width - (float)bar.Rect.Width); + } + else + { + moveAmount = PlayerInput.GetMouseState.Position.Y - PlayerInput.GetOldMouseState.Position.Y; + newY = Math.Min(Math.Max(newY+moveAmount, 0), frame.Rect.Height - bar.Rect.Height); + + barScroll = (float)newY / ((float)frame.Rect.Height - (float)bar.Rect.Height); + } + + if (moveAmount != 0 && OnMoved != null) OnMoved(moveAmount); + + bar.Rect = new Rectangle(newX + frame.Rect.X, newY + frame.Rect.Y, bar.Rect.Width, bar.Rect.Height); + + } + + } +} diff --git a/Subsurface/GUI/GUIStyle.cs b/Subsurface/GUI/GUIStyle.cs new file mode 100644 index 000000000..0dc23ca2a --- /dev/null +++ b/Subsurface/GUI/GUIStyle.cs @@ -0,0 +1,50 @@ +using System.Xml.Linq; +using Microsoft.Xna.Framework; +using System; + +namespace Subsurface +{ + class GUIStyle + { + public readonly Vector4 smallPadding; + public readonly Vector4 largePadding; + + public readonly Color backGroundColor; + public readonly Color foreGroundColor; + + public readonly Color textColor; + + public readonly Color hoverColor; + public readonly Color selectedColor; + + public GUIStyle(string file) + { + XDocument doc; + try { doc = XDocument.Load(file); } + catch (Exception e) + { + DebugConsole.ThrowError("Loading style ''" + file + "'' failed", e); + return; + } + + smallPadding = ToolBox.GetAttributeVector4(doc.Root, "smallpadding", Vector4.Zero); + largePadding = ToolBox.GetAttributeVector4(doc.Root, "largepadding", Vector4.Zero); + + Vector4 colorVector = ToolBox.GetAttributeVector4(doc.Root, "backgroundcolor", new Vector4(0.0f,0.0f,0.0f,1.0f)); + backGroundColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W); + + colorVector = ToolBox.GetAttributeVector4(doc.Root, "foregroundcolor", new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); + foreGroundColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W); + + colorVector = ToolBox.GetAttributeVector4(doc.Root, "textcolor", new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); + textColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W); + + colorVector = ToolBox.GetAttributeVector4(doc.Root, "hovercolor", new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); + hoverColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W); + + colorVector = ToolBox.GetAttributeVector4(doc.Root, "selectedcolor", new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); + selectedColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W); + } + + } +} diff --git a/Subsurface/GUI/GUITextBlock.cs b/Subsurface/GUI/GUITextBlock.cs new file mode 100644 index 000000000..cd6b8264a --- /dev/null +++ b/Subsurface/GUI/GUITextBlock.cs @@ -0,0 +1,194 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface +{ + class GUITextBlock : GUIComponent + { + protected string text; + + protected Alignment textAlignment; + + protected Vector2 textPos; + protected Vector2 origin; + + protected Vector2 caretPos; + + protected Color textColor; + + public delegate string TextGetterHandler(); + public TextGetterHandler TextGetter; + + private bool wrap; + + public override Vector4 Padding + { + get { return padding; } + set + { + padding = value; + SetTextPos(); + } + } + + public string Text + { + get { return text; } + set + { + text = value; + SetTextPos(); + } + } + + public Vector2 TextPos + { + get { return textPos; } + } + + public Vector2 Origin + { + get { return origin; } + } + + public Color TextColor + { + get { return textColor; } + } + + public Vector2 CaretPos + { + get { return caretPos; } + } + + public GUITextBlock(Rectangle rect, string text, GUIStyle style, Alignment alignment, Alignment textAlignment, GUIComponent parent = null, bool wrap = false) + : this (rect, text, style.foreGroundColor, style.textColor, alignment, textAlignment, parent, wrap) + { + hoverColor = style.hoverColor; + selectedColor = style.selectedColor; + } + + public GUITextBlock(Rectangle rect, string text, Color color, Color textColor, Alignment textAlignment = Alignment.Left, GUIComponent parent = null, bool wrap = false) + : this(rect, text,color, textColor, (Alignment.Left | Alignment.Top), textAlignment, parent, wrap) + { + } + + protected override void UpdateDimensions(GUIComponent parent) + { + base.UpdateDimensions(parent); + + SetTextPos(); + } + + + public GUITextBlock(Rectangle rect, string text, Color color, Color textColor, Alignment alignment, Alignment textAlignment = Alignment.Left, GUIComponent parent = null, bool wrap = false) + { + this.rect = rect; + + this.color = color; + this.textColor = textColor; + this.text = text; + + this.alignment = alignment; + + this.textAlignment = textAlignment; + + if (parent != null) + parent.AddChild(this); + + //if (wrap) + //{ + this.wrap = wrap; + // this.text = ToolBox.WrapText(this.text, rect.Width); + //} + + SetTextPos(); + } + + private void SetTextPos() + { + if (text==null) return; + + + + + + + Vector2 size = MeasureText(); + + if (wrap && rect.Width>0) + { + text = text.Replace("\n"," "); + this.text = ToolBox.WrapText(this.text, rect.Width); + + Vector2 newSize = MeasureText(); + + Rectangle newRect = rect; + + //newRect.Width += (int)(newSize.X-size.X); + newRect.Height += (int)(newSize.Y - size.Y); + + Rect = newRect; + size = newSize; + } + + textPos = new Vector2(rect.Width / 2.0f, rect.Height / 2.0f); + origin = size * 0.5f; + + if (textAlignment.HasFlag(Alignment.Left)) + origin.X += (rect.Width / 2.0f - padding.X) - size.X / 2; + + if (textAlignment.HasFlag(Alignment.Right)) + origin.X -= (rect.Width / 2.0f - padding.Z) - size.X / 2; + + if (textAlignment.HasFlag(Alignment.Top)) + origin.Y += (rect.Height / 2.0f - padding.Y) - size.Y / 2; + + if (textAlignment.HasFlag(Alignment.Bottom)) + origin.Y -= (rect.Height / 2.0f - padding.W) - size.Y / 2; + + origin.X = (int)origin.X; + origin.Y = (int)origin.Y; + + textPos.X = (int)textPos.X; + textPos.Y = (int)textPos.Y; + + caretPos = new Vector2(rect.X + size.X, rect.Y) + textPos - origin; + } + + private Vector2 MeasureText() + { + Vector2 size = Vector2.Zero; + while (size == Vector2.Zero) + { + try { size = GUI.font.MeasureString((text == "") ? " " : text); } + catch { text = text.Substring(0, text.Length - 1); } + } + + return size; + } + + public override void Draw(SpriteBatch spriteBatch) + { + Color currColor = color; + if (state == ComponentState.Hover) currColor = hoverColor; + if (state == ComponentState.Selected) currColor = selectedColor; + + GUI.DrawRectangle(spriteBatch, rect, currColor*alpha, true); + + if (TextGetter != null) text = TextGetter(); + + if (!string.IsNullOrEmpty(text)) + { + spriteBatch.DrawString(GUI.font, + text, + new Vector2(rect.X, rect.Y) + textPos, + textColor * alpha, + 0.0f, origin, 1.0f, + SpriteEffects.None, 0.0f); + } + + DrawChildren(spriteBatch); + } + } +} diff --git a/Subsurface/GUI/GUITextBox.cs b/Subsurface/GUI/GUITextBox.cs new file mode 100644 index 000000000..13530ab53 --- /dev/null +++ b/Subsurface/GUI/GUITextBox.cs @@ -0,0 +1,198 @@ +using System; +using EventInput; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace Subsurface +{ + + delegate void TextBoxEvent(GUITextBox sender); + + class GUITextBox : GUIComponent, IKeyboardSubscriber + { + public event TextBoxEvent Clicked; + + bool caretVisible; + float caretTimer; + + GUITextBlock textBlock; + + public delegate bool OnEnterHandler(GUITextBox textBox, string text); + public OnEnterHandler OnEnter; + + public delegate bool OnTextChangedHandler(GUITextBox textBox, string text); + public OnTextChangedHandler OnTextChanged; + + public override Color Color + { + get { return color; } + set + { + color = value; + textBlock.Color = color; + } + } + + public String Text + { + get + { + return textBlock.Text; + } + set + { + textBlock.Text = value; + if (textBlock.Text == null) textBlock.Text = ""; + + if (textBlock.Text != "") + { + //if you attempt to display a character that is not in your font + //you will get an exception, so we filter the characters + //remove the filtering if you're using a default character in your spritefont + String filtered = ""; + foreach (char c in value) + { + if (GUI.font.Characters.Contains(c)) + filtered += c; + } + + textBlock.Text = filtered; + + if (GUI.font.MeasureString(textBlock.Text).X > rect.Width) + { + //recursion to ensure that text cannot be larger than the box + Text = textBlock.Text.Substring(0, textBlock.Text.Length - 1); + } + } + } + } + + public GUITextBox(Rectangle rect, Color color, Color textColor, Alignment alignment, Alignment textAlignment = Alignment.Left, GUIComponent parent = null) + { + this.rect = rect; + + this.color = color; + + this.alignment = alignment; + + //this.textAlignment = textAlignment; + + if (parent != null) + parent.AddChild(this); + + textBlock = new GUITextBlock(new Rectangle(0,0,0,0), "", color, textColor, textAlignment, this); + textBlock.Padding = new Vector4(10.0f, 0.0f, 10.0f, 0.0f); + + _previousMouse = PlayerInput.GetMouseState; + //SetTextPos(); + } + + public void Select() + { + keyboardDispatcher.Subscriber = this; + if (Clicked != null) Clicked(this); + } + + public void Deselect() + { + if (keyboardDispatcher.Subscriber == this) keyboardDispatcher.Subscriber = null; + } + + MouseState _previousMouse; + public override void Update(float deltaTime) + { + caretTimer += deltaTime; + caretVisible = ((caretTimer*1000.0f) % 1000) < 500; + + if (rect.Contains(PlayerInput.GetMouseState.Position)) + { + state = ComponentState.Hover; + if (PlayerInput.LeftButtonClicked()) + { + Select(); + } + } + else + { + state = ComponentState.None; + } + + if (keyboardDispatcher.Subscriber == this) + { + Character.disableControls = true; + if (OnEnter != null && PlayerInput.KeyHit(Keys.Enter)) + { + string input = Text; + Text = ""; + OnEnter(this, input); + + } + } + } + + public override void Draw(SpriteBatch spriteBatch) + { + DrawChildren(spriteBatch); + + + Vector2 caretPos = textBlock.CaretPos; + + if (caretVisible && Selected) + { + GUI.DrawLine(spriteBatch, + new Vector2((int)caretPos.X + 2, caretPos.Y + 3), + new Vector2((int)caretPos.X + 2, caretPos.Y + rect.Height - 3), + textBlock.TextColor * alpha); + } + + } + + + public void ReceiveTextInput(char inputChar) + { + Text = Text + inputChar; + + if (OnTextChanged!=null) OnTextChanged(this, Text); + } + public void ReceiveTextInput(string text) + { + Text = Text + text; + + if (OnTextChanged != null) OnTextChanged(this, Text); + } + public void ReceiveCommandInput(char command) + { + switch (command) + { + case '\b': //backspace + if (Text.Length > 0) + Text = Text.Substring(0, Text.Length - 1); + break; + case '\r': //return + if (OnEnterPressed != null) + OnEnterPressed(this); + break; + case '\t': //tab + if (OnTabPressed != null) + OnTabPressed(this); + break; + } + + if (OnTextChanged != null) OnTextChanged(this, Text); + } + + public void ReceiveSpecialInput(Keys key) + { + } + + public event TextBoxEvent OnEnterPressed; + public event TextBoxEvent OnTabPressed; + + public bool Selected + { + get; + set; + } + } +} diff --git a/Subsurface/GUI/GUITickBox.cs b/Subsurface/GUI/GUITickBox.cs new file mode 100644 index 000000000..f43069e65 --- /dev/null +++ b/Subsurface/GUI/GUITickBox.cs @@ -0,0 +1,60 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace Subsurface +{ + class GUITickBox : GUIComponent + { + GUIFrame box; + GUITextBlock text; + + public delegate bool OnSelectedHandler(object obj); + public OnSelectedHandler OnSelected; + + bool selected; + + public GUITickBox(Rectangle rect, string label, Alignment alignment, GUIComponent parent) + { + if (parent != null) + parent.AddChild(this); + + box = new GUIFrame(new Rectangle(rect.X, rect.Y, 30, 30), Color.LightGray, this); + text = new GUITextBlock(new Rectangle(rect.X + 40, rect.Y, 200, 30), label, Color.Transparent, Color.White, Alignment.Left | Alignment.CenterY, this); + } + + public override void Update(float deltaTime) + { + base.Update(deltaTime); + + if (box.Rect.Contains(PlayerInput.GetMouseState.Position)) + { + box.State = ComponentState.Hover; + + if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed) + box.State = ComponentState.Selected; + + if (PlayerInput.LeftButtonClicked()) + { + selected = !selected; + if (OnSelected != null) OnSelected(this); + } + + } + else + { + box.State = ComponentState.None; + } + } + + public override void Draw(SpriteBatch spriteBatch) + { + DrawChildren(spriteBatch); + + if (selected) + { + GUI.DrawRectangle(spriteBatch, new Rectangle(box.Rect.X + 5, box.Rect.Y + 5, box.Rect.Width - 10, box.Rect.Height - 10), Color.Green * alpha, true); + } + } + } +} diff --git a/Subsurface/Game1.cs b/Subsurface/Game1.cs new file mode 100644 index 000000000..bf6a79b6a --- /dev/null +++ b/Subsurface/Game1.cs @@ -0,0 +1,220 @@ +using System; +using System.Diagnostics; +using System.Reflection; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using Subsurface.Networking; +using Subsurface.Particles; + +namespace Subsurface +{ + /// + /// This is the main type for your game + /// + class Game1 : Game + { + GraphicsDeviceManager graphics; + static int graphicsWidth, graphicsHeight; + static SpriteBatch spriteBatch; + + public static FrameCounter frameCounter; + + public static readonly Version version = Assembly.GetEntryAssembly().GetName().Version; + + public static GameScreen gameScreen; + public static MainMenuScreen mainMenuScreen; + public static LobbyScreen lobbyScreen; + public static NetLobbyScreen netLobbyScreen; + public static EditMapScreen editMapScreen; + public static EditCharacterScreen editCharacterScreen; + + public static GameSession gameSession; + + public static GameClient client; + public static GameServer server; + + public static ParticleManager particleManager; + + public static TextureLoader textureLoader; + + public static World world; + + public static Random localRandom; + public static Random random; + + + public Camera Cam + { + get { return gameScreen.Cam; } + } + + public static int GraphicsWidth + { + get { return graphicsWidth; } + } + + public static int GraphicsHeight + { + get { return graphicsHeight; } + } + + public Game1() + { + graphics = new GraphicsDeviceManager(this); + + graphicsWidth = 1280; + graphicsHeight = 700; + + //graphics.IsFullScreen = true; + graphics.PreferredBackBufferWidth = graphicsWidth; + graphics.PreferredBackBufferHeight = graphicsHeight; + Content.RootDirectory = "Content"; + + //graphics.SynchronizeWithVerticalRetrace = false; + //graphics.ApplyChanges(); + + frameCounter = new FrameCounter(); + + IsMouseVisible = true; + + IsFixedTimeStep = false; + //TargetElapsedTime = new TimeSpan(0, 0, 0, 0, 55); + + world = new World(new Vector2(0, -9.82f)); + Settings.VelocityIterations = 2; + Settings.PositionIterations = 1; + + random = new Random(); + localRandom = new Random(); + } + + /// + /// Allows the game to perform any initialization it needs to before starting to run. + /// This is where it can query for any required services and load any non-graphic + /// related content. Calling base.Initialize will enumerate through any components + /// and initialize them as well. + /// + protected override void Initialize() + { + // TODO: Add your initialization logic here + base.Initialize(); + + particleManager = new ParticleManager("Content/Particles/prefabs.xml", Cam); + + GameMode.Init(); + GUIComponent.Init(Window); + DebugConsole.Init(Window); + //Event.Init("Content/randomevents.xml"); + } + + /// + /// LoadContent will be called once per game and is the place to load + /// all of your content. + /// + protected override void LoadContent() + { + graphicsWidth = GraphicsDevice.Viewport.Width; + graphicsHeight = GraphicsDevice.Viewport.Height; + + Sound.Init(); + + ConvertUnits.SetDisplayUnitToSimUnitRatio(Physics.DisplayToSimRation); + + spriteBatch = new SpriteBatch(GraphicsDevice); + textureLoader = new TextureLoader(GraphicsDevice); + + Hull.renderer = new WaterRenderer(GraphicsDevice); + + GUI.font = Content.Load("SpriteFont1"); + GUI.LoadContent(GraphicsDevice); + + MapEntityPrefab.Init(); + + Job.LoadAll("Content/Characters/Jobs.xml"); + + StructurePrefab.LoadAll("Content/Map/StructurePrefabs.xml"); + ItemPrefab.LoadAll(); + + AmbientSoundManager.Init("Content/Sounds/Sounds.xml"); + + gameScreen = new GameScreen(graphics.GraphicsDevice); + mainMenuScreen = new MainMenuScreen(this); + lobbyScreen = new LobbyScreen(); + netLobbyScreen = new NetLobbyScreen(); + editMapScreen = new EditMapScreen(); + editCharacterScreen = new EditCharacterScreen(); + + mainMenuScreen.Select(); + } + + /// + /// UnloadContent will be called once per game and is the place to unload + /// all content. + /// + protected override void UnloadContent() + { + Sound.Dispose(); + } + + + /// + /// Allows the game to run logic such as updating the world, + /// checking for collisions, gathering input, and playing audio. + /// + /// Provides a snapshot of timing values. + protected override void Update(GameTime gameTime) + { + base.Update(gameTime); + + double deltaTime = gameTime.ElapsedGameTime.TotalSeconds; + + PlayerInput.Update(deltaTime); + + //if (PlayerInput.KeyDown(Keys.Escape)) Quit(); + + DebugConsole.Update(this, (float)deltaTime); + + if (!DebugConsole.IsOpen || server != null || client != null) Screen.Selected.Update(deltaTime); + + if (server != null) + { + server.Update(); + } + else if (client != null) + { + client.Update(); + } + else + { + NetworkEvent.events.Clear(); + } + } + + + /// + /// This is called when the game should draw itself. + /// + /// elapsed time in seconds + protected override void Draw(GameTime gameTime) + { + //System.Diagnostics.Debug.WriteLine(deltaTime); + //System.Diagnostics.Debug.WriteLine(gameTime.ElapsedGameTime.TotalSeconds); + double deltaTime = gameTime.ElapsedGameTime.TotalSeconds; + + frameCounter.Update(deltaTime); + + Screen.Selected.Draw(deltaTime, GraphicsDevice, spriteBatch); + } + + protected override void OnExiting(object sender, EventArgs args) + { + if (client != null) client.Disconnect(); + + base.OnExiting(sender, args); + } + + } +} diff --git a/Subsurface/GameSession/CrewManager.cs b/Subsurface/GameSession/CrewManager.cs new file mode 100644 index 000000000..db26f0b5a --- /dev/null +++ b/Subsurface/GameSession/CrewManager.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface +{ + class CrewManager + { + public List characters; + public List characterInfos; + + //public static string mapFile; + //public string saveFile; + + private int money; + + private GUIFrame guiFrame; + private GUIListBox listBox; + + public int Money + { + get { return money; } + set { money = (int)Math.Max(value, 0.0f); } + } + + public CrewManager(GameSession session) + { + characters = new List(); + characterInfos = new List(); + + guiFrame = new GUIFrame(new Rectangle(0, 50, 150, 450), Color.Transparent); + + listBox = new GUIListBox(new Rectangle(0, 0, 150, 400), Color.Transparent, guiFrame); + listBox.ScrollBarEnabled = false; + listBox.OnSelected = SelectCharacter; + + money = 10000; + } + + private string CreateSaveFile(string mapName) + { + string path = "Content/Data/Saves/"; + + string name = Path.GetFileNameWithoutExtension(mapName); + + int i = 0; + while (File.Exists(path+name + i)) + { + i++; + } + + return path + name + i; + } + + public bool SelectCharacter(object selection) + { + //listBox.Select(selection); + Character character = selection as Character; + + if (character == null) return false; + + if (characters.Contains(character)) + { + Character.Controlled = character; + return true; + } + + return false; + } + + public void AddCharacter(Character character) + { + characters.Add(character); + if (!characterInfos.Contains(character.info)) + { + characterInfos.Add(character.info); + } + + GUIFrame frame = new GUIFrame(new Rectangle(0,0,0,40), Color.Transparent, listBox); + frame.UserData = character; + frame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); + frame.HoverColor = Color.LightGray * 0.5f; + frame.SelectedColor = Color.Gold * 0.5f; + + string name = character.info.name.Replace(' ','\n'); + + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(40,0,0,25), + name, + Color.Transparent, Color.Black, + Alignment.Left, + Alignment.Left, + frame); + textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); + + GUIImage face = new GUIImage(new Rectangle(-10,-10,0,0), character.animController.limbs[0].sprite, Alignment.Left, frame); + } + + public void KillCharacter(Character killedCharacter) + { + GUIComponent characterBlock = listBox.GetChild(killedCharacter) as GUIComponent; + if (characterBlock != null) characterBlock.Color = Color.DarkRed * 0.5f; + + } + + public void StartShift() + { + foreach (CharacterInfo ci in characterInfos) + { + WayPoint randomWayPoint = WayPoint.GetRandom(WayPoint.SpawnType.Human); + Vector2 position = (randomWayPoint == null) ? Vector2.Zero : randomWayPoint.SimPosition; + + Character character = new Character(ci.file, position, ci); + Character.Controlled = character; + AddCharacter(character); + } + + if (characters.Count>0) SelectCharacter(characters[0]); + } + + public void EndShift() + { + foreach (Character c in characters) + { + if (!c.IsDead) continue; + + CharacterInfo deadInfo = characterInfos.Find(x => c.info == x); + if (deadInfo != null) characterInfos.Remove(deadInfo); + } + + characters.Clear(); + listBox.ClearChildren(); + } + + public void Draw(SpriteBatch spriteBatch) + { + guiFrame.Draw(spriteBatch); + } + } +} diff --git a/Subsurface/GameSession/GameMode.cs b/Subsurface/GameSession/GameMode.cs new file mode 100644 index 000000000..6cbe9a703 --- /dev/null +++ b/Subsurface/GameSession/GameMode.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; + +namespace Subsurface +{ + class GameMode + { + public static List list = new List(); + + //TimeSpan duration; + protected DateTime startTime; + protected DateTime endTime; + + protected bool isRunning; + + protected string name; + + private string endMessage; + + public DateTime StartTime + { + get { return startTime; } + } + + public DateTime EndTime + { + get { return endTime; } + } + + public bool IsRunning + { + get { return isRunning; } + } + + public string Name + { + get { return name; } + } + + public string EndMessage + { + get { return endMessage; } + } + + public GameMode(string name) + { + this.name = name; + + list.Add(this); + } + + public virtual void Start(TimeSpan duration) + { + startTime = DateTime.Now; + endTime = startTime + duration; + + endMessage = "The round has ended!"; + + isRunning = true; + } + + public virtual void Update() + { + if (!isRunning) return; + + if (DateTime.Now >= endTime) + { + End(endMessage); + } + } + + public void End(string endMessage = "") + { + isRunning = false; + + if (endMessage != "" || this.endMessage == null) this.endMessage = endMessage; + + Game1.gameSession.EndShift(null, null); + } + + public static void Init() + { + new GameMode("Sandbox"); + new TraitorMode("Traitor"); + } + } +} diff --git a/Subsurface/GameSession/GameSession.cs b/Subsurface/GameSession/GameSession.cs new file mode 100644 index 000000000..ea39eb087 --- /dev/null +++ b/Subsurface/GameSession/GameSession.cs @@ -0,0 +1,234 @@ +using System; +using System.IO; +using System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace Subsurface +{ + class GameSession + { + public readonly TaskManager taskManager; + public readonly CrewManager crewManager; + public readonly HireManager hireManager; + + protected DateTime startTime; + protected DateTime endTime; + + public readonly GameMode gameMode; + + private GUIListBox chatBox; + private GUITextBox textBox; + + private GUIProgressBar timerBar; + + private GUIButton endShiftButton; + + string saveFile; + + private int day; + + public string SaveFile + { + get { return saveFile; } + } + + public int Day + { + get { return day; } + } + + public GameSession(string selectedMapFile, bool save, TimeSpan gameDuration, GameMode gameMode = null) + { + taskManager = new TaskManager(this); + crewManager = new CrewManager(this); + hireManager = new HireManager(); + + hireManager.GenerateCharacters("Content/Characters/Human/human.xml", 10); + + int width = 350, height = 100; + chatBox = new GUIListBox(new Rectangle( + Game1.GraphicsWidth - (int)GUI.style.smallPadding.X - width, + Game1.GraphicsHeight - (int)GUI.style.smallPadding.W*2 - 25 - height, + width, height), + Color.White * 0.5f); + + endShiftButton = new GUIButton(new Rectangle(Game1.GraphicsWidth - 240, 20, 100, 25), "End shift", Color.White, Alignment.CenterX, null); + endShiftButton.OnClicked = EndShift; + + timerBar = new GUIProgressBar(new Rectangle(Game1.GraphicsWidth - 120, 20, 100, 25), Color.Gold, 0.0f, null); + + textBox = new GUITextBox( + new Rectangle(chatBox.Rect.X, chatBox.Rect.Y + chatBox.Rect.Height + (int)GUI.style.smallPadding.W, chatBox.Rect.Width, 25), + Color.White * 0.5f, Color.Black, Alignment.Bottom, Alignment.Left); + textBox.OnEnter = EnterChatMessage; + + this.gameMode = gameMode; + if (this.gameMode != null) this.gameMode.Start(Game1.netLobbyScreen.GameDuration); + + startTime = DateTime.Now; + endTime = startTime + gameDuration; + + if (!save) return; + + CreateSaveFile(selectedMapFile); + + day = 1; + + } + + public bool TryHireCharacter(CharacterInfo characterInfo) + { + if (crewManager.Money < characterInfo.salary) return false; + + hireManager.availableCharacters.Remove(characterInfo); + crewManager.characterInfos.Add(characterInfo); + + crewManager.Money -= characterInfo.salary; + + return true; + } + + public string GetMoney() + { + return ("Money: " + crewManager.Money); + } + + public void StartShift(int scriptedEventCount = 1) + { + crewManager.StartShift(); + taskManager.StartShift(scriptedEventCount); + } + + public bool EndShift(GUIButton button, object obj) + { + if (Game1.server!=null) + { + string endMessage = gameMode.EndMessage; + + Game1.server.EndGame(endMessage); + + } + else if (Game1.client==null) + { + if (saveFile == null) return false; + + Map.Save(Path.GetDirectoryName(saveFile) + "/", Path.GetFileName(saveFile)); + + crewManager.EndShift(); + + Game1.lobbyScreen.Select(); + + day++; + } + + taskManager.EndShift(); + + return true; + } + + private void CreateSaveFile(string mapName) + { + string path = "Content/Data/Saves/"; + + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + string name = Path.GetFileNameWithoutExtension(mapName); + string extension = Path.GetExtension(mapName); + + int i = 0; + while (File.Exists(path + name + i + extension)) + { + i++; + } + + saveFile = path + name + i+extension; + + + try + { + File.Copy(mapName, saveFile); + } + catch (Exception e) + { + DebugConsole.ThrowError("Copying map file ''" + mapName + "'' to ''" + saveFile + "'' failed", e); + } + } + + public bool EnterChatMessage(GUITextBox textBox, string message) + { + if (string.IsNullOrWhiteSpace(message)) return false; + + if (Game1.server!=null) + { + Game1.server.SendChatMessage(message); + } + else if (Game1.client!=null) + { + Game1.client.SendChatMessage(Game1.client.Name + ": " + message); + } + + textBox.Deselect(); + + return true; + } + + public void NewChatMessage(string text, Color color) + { + GUITextBlock msg = new GUITextBlock(new Rectangle(0, 0, 0, 20), text, + ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f, color, + Alignment.Left, null, true); + + msg.Padding = new Vector4(GUI.style.smallPadding.X, 0, 0, 0); + chatBox.AddChild(msg); + + while (chatBox.CountChildren > 20) + { + chatBox.RemoveChild(chatBox.children.First()); + } + } + + public void Update(float deltaTime) + { + taskManager.Update(deltaTime); + + textBox.Update(deltaTime); + + if (gameMode != null) gameMode.Update(); + + double duration = (endTime - startTime).TotalSeconds; + double elapsedTime = (DateTime.Now-startTime).TotalSeconds; + timerBar.BarSize = (float)(elapsedTime / Math.Max(duration, 1.0)); + + if (PlayerInput.KeyHit(Keys.Tab)) + { + if (textBox.Selected) + { + textBox.Deselect(); + textBox.Text = ""; + } + else + { + textBox.Select(); + } + } + } + + public void Draw(SpriteBatch spriteBatch) + { + crewManager.Draw(spriteBatch); + taskManager.Draw(spriteBatch); + + chatBox.Draw(spriteBatch); + textBox.Draw(spriteBatch); + + timerBar.Draw(spriteBatch); + + if (Game1.client == null) endShiftButton.Draw(spriteBatch); + } + } +} diff --git a/Subsurface/GameSession/HireManager.cs b/Subsurface/GameSession/HireManager.cs new file mode 100644 index 000000000..3c3efd658 --- /dev/null +++ b/Subsurface/GameSession/HireManager.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Subsurface +{ + class HireManager + { + public List availableCharacters; + + const int MaxAvailableCharacters = 10; + + public HireManager() + { + availableCharacters = new List(); + } + + public void GenerateCharacters(string file, int amount) + { + for (int i = 0 ; i= endTime) + { + string endMessage = traitor.character.info.name + " was a traitor! "; + endMessage += (traitor.character.info.gender == Gender.Male) ? "His" : "Her"; + endMessage += " task was to assassinate " + target.character.info.name + ". The task was unsuccesful."; + End(endMessage); + return; + } + + + if (traitor==null || target ==null) + { + int clientCount = Game1.server.connectedClients.Count(); + if (clientCount < 2) return; + + int traitorIndex = Game1.localRandom.Next(clientCount); + traitor = Game1.server.connectedClients[traitorIndex]; + + int targetIndex = 0; + while (targetIndex==traitorIndex) + { + targetIndex = Game1.localRandom.Next(clientCount); + } + target = Game1.server.connectedClients[targetIndex]; + + + Game1.server.NewTraitor(traitor, target); + } + else + { + if (target.character.IsDead) + { + string endMessage = traitor.character.info.name + " was a traitor! "; + endMessage += (traitor.character.info.gender == Gender.Male) ? "his" : "her"; + endMessage += " task was to assassinate " + target.character.info.name + ". The task was succesful."; + End(endMessage); + } + } + } + } +} diff --git a/Subsurface/Icon.ico b/Subsurface/Icon.ico new file mode 100644 index 000000000..7d9dec187 Binary files /dev/null and b/Subsurface/Icon.ico differ diff --git a/Subsurface/Interface1.cs b/Subsurface/Interface1.cs new file mode 100644 index 000000000..6e5970c01 --- /dev/null +++ b/Subsurface/Interface1.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Subsurface +{ + interface IPropertyObject + { + Dictionary ObjectProperties + { + get; + } + } +} diff --git a/Subsurface/Items/CharacterInventory.cs b/Subsurface/Items/CharacterInventory.cs new file mode 100644 index 000000000..e61fce794 --- /dev/null +++ b/Subsurface/Items/CharacterInventory.cs @@ -0,0 +1,241 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace Subsurface +{ + [Flags] + public enum LimbSlot + { + Any = 1, RightHand = 2, LeftHand = 4, Head = 8, Torso = 16, Legs = 32, BothHands = 64 + }; + + class CharacterInventory : Inventory + { + private Character character; + + private static LimbSlot[] limbSlots = new LimbSlot[] { + LimbSlot.Head, LimbSlot.Torso, LimbSlot.LeftHand, LimbSlot.RightHand, LimbSlot.Legs, + LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any }; + + public CharacterInventory(int capacity, Character character) + : base(capacity) + { + this.character = character; + } + + public int FindLimbSlot(LimbSlot limbSlot) + { + for (int i = 0; i < items.Length; i++) + { + if ( limbSlots[i] == limbSlot) return i; + } + return -1; + } + + public bool IsInLimbSlot(Item item, LimbSlot limbSlot) + { + for (int i = 0; i + /// If there is room, puts the item in the inventory and returns true, otherwise returns false + /// + public override bool TryPutItem(Item item, LimbSlot usedSlots, bool createNetworkEvent = true) + { + for (int i = 0; i < capacity; i++) + { + //item is already in the inventory! + if (items[i] == item) return true; + } + + if (usedSlots.HasFlag(LimbSlot.Any)) + { + for (int i = 0; i < capacity; i++) + { + if (items[i] != null) continue; + if (limbSlots[i] != LimbSlot.Any) continue; + PutItem(item, i, createNetworkEvent); + item.Unequip(character); + return true; + } + } + + for (int i = 0; i < capacity; i++) + { + if (usedSlots.HasFlag(limbSlots[i]) && items[i]!=null) return false; + } + + for (int i = 0; i < capacity; i++) + { + if (usedSlots.HasFlag(limbSlots[i]) && items[i] == null) + { + PutItem(item, i, createNetworkEvent); + item.Equip(character); + return true; + } + } + + return false; + } + + public override bool TryPutItem(Item item, int i, bool createNetworkEvent) + { + LimbSlot usedSlots = item.AllowedSlots; + + //there's already an item in the slot + if (items[i] != null) + { + bool combined = false; + if (item.Combine(items[i])) + { + //PutItem(item, i, false, false); + combined = true; + } + else if (items[i].Combine(item)) + { + //PutItem(items[i], i, false, false); + combined = true; + } + + if (!combined) return false; + + if (usedSlots.HasFlag(LimbSlot.BothHands)) + { + if (limbSlots[i] == LimbSlot.LeftHand) + { + PutItem(item, FindLimbSlot(LimbSlot.RightHand), createNetworkEvent, false); + } + else if (limbSlots[i] == LimbSlot.RightHand) + { + PutItem(item, FindLimbSlot(LimbSlot.LeftHand), createNetworkEvent, false); + } + } + if (limbSlots[i] == LimbSlot.Any) item.Unequip(character); + + return true; + } + + if (limbSlots[i]==LimbSlot.Any) + { + if (usedSlots.HasFlag(LimbSlot.Any)) + { + item.Unequip(character); + PutItem(item, i, createNetworkEvent); + return true; + } + else + { + return false; + } + + } + else + { + + if (limbSlots[i] != LimbSlot.Any && usedSlots.HasFlag(limbSlots[i]) && items[i] == null) + { + item.Unequip(character); + PutItem(item, i, createNetworkEvent); + item.Equip(character); + return true; + } + + if (usedSlots.HasFlag(LimbSlot.BothHands) && (limbSlots[i]==LimbSlot.LeftHand || limbSlots[i]==LimbSlot.RightHand)) + { + int rightHandSlot = FindLimbSlot(LimbSlot.LeftHand); + int leftHandSlot = FindLimbSlot(LimbSlot.RightHand); + + if (items[rightHandSlot] != null) return false; + if (items[leftHandSlot] != null) return false; + + PutItem(item, rightHandSlot, true, true); + PutItem(item, leftHandSlot, true, false); + item.Equip(character); + return true; + } + + + return false; + } + + } + + public override void Draw(SpriteBatch spriteBatch) + { + if (doubleClickedItem!=null && doubleClickedItem.inventory!=this) + { + TryPutItem(doubleClickedItem, doubleClickedItem.AllowedSlots, true); + } + doubleClickedItem = null; + + int rectWidth = 40, rectHeight = 40; + + int spacing = 10; + + Rectangle slotRect = new Rectangle(0, 0, rectWidth, rectHeight); + Rectangle draggingItemSlot = slotRect; + + for (int i = 0; i < capacity; i++) + { + int x, y; + switch (i) + { + //head + case 0: + //legs + case 4: + x = spacing * 2 + rectWidth; + y = Game1.GraphicsHeight - (spacing + rectHeight) * ((i == 0) ? 3 : 1); + break; + //lefthand + case 2: + //torso + case 1: + //righthand + case 3: + x = spacing; + if (i == 1) x += (spacing + rectWidth); + if (i == 3) x += (spacing + rectWidth) * 2; + y = Game1.GraphicsHeight - (spacing + rectHeight) * 2; + break; + default: + x = spacing * (4 + (i - 5)) + rectWidth * (3 + (i - 5)); + y = Game1.GraphicsHeight - (spacing + rectHeight); + break; + } + + slotRect.X = x; + slotRect.Y = y; + + UpdateSlot(spriteBatch, slotRect, i, items[i], false); + if (draggingItem!=null && draggingItem == items[i]) draggingItemSlot = slotRect; + + } + + if (draggingItem != null && !draggingItemSlot.Contains(PlayerInput.MousePosition)) + { + if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed) + { + slotRect.X = PlayerInput.GetMouseState.X - slotRect.Width / 2; + slotRect.Y = PlayerInput.GetMouseState.Y - slotRect.Height / 2; + //GUI.DrawRectangle(spriteBatch, rect, Color.White, true); + //draggingItem.sprite.Draw(spriteBatch, new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), Color.White); + + DrawSlot(spriteBatch, slotRect, draggingItem, false, false); + } + else + { + draggingItem.Drop(character); + //draggingItem = null; + } + } + } + + } +} diff --git a/Subsurface/Items/Components/Container.cs b/Subsurface/Items/Components/Container.cs new file mode 100644 index 000000000..038f1343b --- /dev/null +++ b/Subsurface/Items/Components/Container.cs @@ -0,0 +1,240 @@ +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using FarseerPhysics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface.Items.Components +{ + class ItemContainer : ItemComponent + { + List containableItems; + public ItemInventory inventory; + + //how many items can be contained + private int capacity; + + private bool hideItems; + private bool drawInventory; + + //the position of the first item in the container + private Vector2 itemPos; + + //item[i].Pos = itemPos + itemInterval*i + private Vector2 itemInterval; + + private float itemRotation; + + [HasDefaultValue(5, false)] + public int Capacity + { + get { return capacity; } + set { capacity = Math.Max(value, 1); } + } + + [HasDefaultValue(true, false)] + public bool HideItems + { + get { return hideItems; } + set { hideItems = value; } + } + + [HasDefaultValue(false, false)] + public bool DrawInventory + { + get { return drawInventory; } + set { drawInventory = value; } + } + + [HasDefaultValue(0.0f, false)] + public float ItemRotation + { + get { return itemRotation; } + set { itemRotation = value; } + } + + //[Initable(Vector2.Zero)] + //private Vector2 ItemPos + //{ + // set { itemPos = ConvertUnits.ToSimUnits(value); } + //} + + //[Initable(Vector2.Zero)] + //private Vector2 ItemInterval + //{ + // set { itemInterval = ConvertUnits.ToSimUnits(value); } + //} + + public ItemContainer(Item item, XElement element) + : base (item, element) + { + inventory = new ItemInventory(this, capacity); + containableItems = new List(); + + itemPos = ToolBox.GetAttributeVector2(element, "ItemPos", Vector2.Zero); + itemPos = ConvertUnits.ToSimUnits(itemPos); + + itemInterval = ToolBox.GetAttributeVector2(element, "ItemInterval", Vector2.Zero); + itemInterval = ConvertUnits.ToSimUnits(itemInterval); + + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLower()) + { + case "containable": + RelatedItem containable = RelatedItem.Load(subElement); + if (containable!=null) containableItems.Add(containable); + break; + } + } + } + + public void RemoveContained(Item item) + { + inventory.RemoveItem(item); + } + + public bool CanBeContained(Item item) + { + return (containableItems.Find(x => x.MatchesItem(item)) != null); + } + + public override void Update(float deltaTime, Camera cam) + { + foreach (Item contained in inventory.items) + { + if (contained == null) continue; + + if (contained.body!=null) contained.body.Enabled = !hideItems; + + RelatedItem ri = containableItems.Find(x => x.MatchesItem(item)); + if (ri == null) continue; + + foreach (StatusEffect effect in ri.statusEffects) + { + if (effect.Targets.HasFlag(StatusEffect.Target.This)) effect.Apply(ActionType.OnContaining, deltaTime, item); + if (effect.Targets.HasFlag(StatusEffect.Target.Contained)) effect.Apply(ActionType.OnContaining, deltaTime, contained); + } + + contained.ApplyStatusEffects(ActionType.OnContained, deltaTime); + } + + if (hideItems) return; + + Vector2 transformedItemPos; + Vector2 transformedItemInterval = itemInterval; + //float transformedItemRotation = itemRotation; + if (item.body==null) + { + transformedItemPos = new Vector2(item.Rect.X, item.Rect.Y); + transformedItemPos = ConvertUnits.ToSimUnits(transformedItemPos) + itemPos; + } + else + { + Matrix transform = Matrix.CreateRotationZ(item.body.Rotation); + + transformedItemPos = Vector2.Transform(item.body.Position + itemPos, transform); + transformedItemInterval = Vector2.Transform(transformedItemInterval, transform); + //transformedItemRotation += item.body.Rotation; + } + + foreach (Item containedItem in inventory.items) + { + if (containedItem == null) continue; + + Vector2 itemDist = (transformedItemPos - containedItem.body.Position); + Vector2 force = (itemDist - containedItem.body.LinearVelocity * 0.1f) * containedItem.body.Mass * 60.0f; + + containedItem.body.ApplyForce(force); + + containedItem.body.SmoothRotate(itemRotation); + + transformedItemPos += transformedItemInterval; + } + + + } + + public override void DrawHUD(SpriteBatch spriteBatch, Character character) + { + if (!drawInventory && false) return; + + inventory.Draw(spriteBatch); + } + + public override bool Pick(Character picker) + { + if (picker == null) return false; + //picker.SelectedConstruction = item; + + return true; + } + + + public override bool Combine(Item item) + { + if (containableItems.Find(x => x.MatchesItem(item)) == null) return false; + + if (inventory.TryPutItem(item)) + { + isActive = true; + if (hideItems) item.body.Enabled = false; + + item.container = this.item; + + return true; + } + + return false; + } + + public override void OnMapLoaded() + { + for (int i = 0; i < itemIds.Length; i++) + { + Item item = MapEntity.FindEntityByID(itemIds[i]) as Item; + if (item == null) continue; + + inventory.TryPutItem(item, i, false); + } + + itemIds = null; + } + + public override void Load(XElement componentElement) + { + base.Load(componentElement); + + string containedString = ToolBox.GetAttributeString(componentElement, "contained", ""); + + string[] itemIdStrings = containedString.Split(','); + + itemIds = new int[itemIdStrings.Length]; + for (int i = 0; i < itemIdStrings.Length; i++) + { + int id = -1; + if (!int.TryParse(itemIdStrings[i], out id)) continue; + + itemIds[i] = id; + } + } + + int[] itemIds; + + public override XElement Save(XElement parentElement) + { + XElement componentElement = base.Save(parentElement); + + string[] itemIdStrings = new string[inventory.items.Length]; + for (int i = 0; i < inventory.items.Length; i++) + { + itemIdStrings[i] = (inventory.items[i]==null) ? "-1" : inventory.items[i].ID.ToString(); + } + + componentElement.Add(new XAttribute("contained", string.Join(",",itemIdStrings))); + + return componentElement; + } + } +} diff --git a/Subsurface/Items/Components/Controller.cs b/Subsurface/Items/Components/Controller.cs new file mode 100644 index 000000000..dd2b3d65b --- /dev/null +++ b/Subsurface/Items/Components/Controller.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics.Joints; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface.Items.Components +{ + struct LimbPos + { + public LimbType limbType; + public Vector2 position; + } + + class Controller : ItemComponent + { + //where the limbs of the user should be positioned when using the controller + List limbPositions; + + Direction dir; + + //the x-position where the user walks to when using the controller + float userPos; + + Character character; + + [HasDefaultValue(1.0f,false)] + public float UserPos + { + set { userPos = value; } + } + + public Controller(Item item, XElement element) + : base(item, element) + { + limbPositions = new List(); + + dir = (Direction)Enum.Parse(typeof(Direction), ToolBox.GetAttributeString(element, "direction", "None"), true); + + //userPos = ToolBox.GetAttributeInt(element, "userpos", 1); + + //string allowedIdString = ToolBox.GetAttributeString(element, "allowedids", ""); + //if (allowedIdString!="") + //{ + // string[] splitIds = allowedIdString.Split(','); + // allowedIDs = new string[splitIds.Length]; + // for (int i = 0; i 10.0f) + { + character.animController.anim = AnimController.Animation.None; + + character.animController.TargetMovement = + new Vector2( + Math.Min(Math.Max(item.Rect.X + userPos - torsoX, -1.0f), 1.0f), + 0.0f); + character.animController.targetDir = (Math.Sign(torsoX - item.Rect.X + userPos) == 1) ? Direction.Right : Direction.Left; + return; + } + } + + if (limbPositions.Count == 0) return; + + character.animController.anim = AnimController.Animation.UsingConstruction; + character.animController.ResetPullJoints(); + if (dir != 0) character.animController.targetDir = dir; + + foreach (LimbPos lb in limbPositions) + { + Limb limb = character.animController.GetLimb(lb.limbType); + if (limb == null) continue; + + FixedMouseJoint fmj = limb.pullJoint; + if (fmj == null) continue; + + Vector2 position = ConvertUnits.ToSimUnits(lb.position + new Vector2(item.Rect.X, item.Rect.Y)); + fmj.Enabled = true; + fmj.WorldAnchorB = position; + } + + foreach (MapEntity e in item.linkedTo) + { + Item linkedItem = e as Item; + if (linkedItem == null) continue; + linkedItem.Update(cam, deltaTime); + } + } + + public override bool Use(Character activator = null) + { + character = activator; + foreach (MapEntity e in item.linkedTo) + { + Item linkedItem = e as Item; + if (linkedItem == null) continue; + linkedItem.Use(activator); + } + + ApplyStatusEffects(ActionType.OnUse, 1.0f, character); + + return true; + } + + public override void SecondaryUse(Character character = null) + { + if (character == null) return; + + foreach (MapEntity e in item.linkedTo) + { + Item linkedItem = e as Item; + if (linkedItem == null) continue; + linkedItem.SecondaryUse(character); + } + } + + public override bool Pick(Character activator = null) + { + if (character!=null && character.SelectedConstruction == item) + { + character = null; + isActive = false; + if (activator != null) activator.animController.anim = AnimController.Animation.None; + + return false; + } + else + { + character = activator; + if (activator == null) return false; + + isActive = true; + } + + item.SendSignal("1", "signal_out"); + return true; + } + + } +} diff --git a/Subsurface/Items/Components/Door.cs b/Subsurface/Items/Components/Door.cs new file mode 100644 index 000000000..adbd339c2 --- /dev/null +++ b/Subsurface/Items/Components/Door.cs @@ -0,0 +1,267 @@ +using System; +using System.IO; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Factories; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Subsurface.Lights; + +namespace Subsurface.Items.Components +{ + class Door : ItemComponent + { + Gap linkedGap; + + Rectangle window; + + ConvexHull convexHull; + ConvexHull convexHull2; + + Gap LinkedGap + { + get + { + if (linkedGap != null) return linkedGap; + foreach (MapEntity e in item.linkedTo) + { + linkedGap = e as Gap; + if (linkedGap != null) return linkedGap; + } + linkedGap = new Gap(item.Rect); + linkedGap.Open = openState; + item.linkedTo.Add(linkedGap); + return linkedGap; + } + } + + bool isOpen; + float openState; + + [HasDefaultValue("0.0,0.0,0.0,0.0", false)] + public string Window + { + get { return ToolBox.Vector4ToString(new Vector4(window.X, window.Y, window.Width, window.Height)); } + set + { + Vector4 vector = ToolBox.ParseToVector4(value); + if (vector.Z!=0.0f || vector.W !=0.0f) + { + window = new Rectangle((int)vector.X, (int)vector.Y, (int)vector.Z, (int)vector.W); + } + } + } + + [Editable, HasDefaultValue(false, true)] + public bool IsOpen + { + get { return isOpen; } + set + { + isOpen = value; + openState = (isOpen) ? 1.0f : 0.0f; + } + } + + public float OpenState + { + get { return openState; } + set + { + if (openState == value) return; + openState = MathHelper.Clamp(value, 0.0f, 1.0f); + + + + if (window==null) + { + Rectangle rect = item.Rect; + rect.Height = (int)(rect.Height * (1.0f - openState)); + + } + else + { + //Rectangle rect = item.Rect; + //rect.Height = (int)(rect.Height * (1.0f - openState)); + + Rectangle rect1 = item.Rect; + rect1.Height = -window.Y; + + rect1.Y += (int)(item.Rect.Height * openState); + rect1.Height = Math.Max(rect1.Height - (rect1.Y - item.Rect.Y), 0); + rect1.Y = Math.Min(item.Rect.Y, rect1.Y); + + Rectangle rect2 = item.Rect; + rect2.Y = rect2.Y + window.Y - window.Height; + + rect2.Y += (int)(item.Rect.Height * openState); + //rect2.Height = Math.Max(rect2.Height - (rect2.Y - item.Rect.Y), 0); + rect2.Y = Math.Min(item.Rect.Y, rect2.Y); + rect2.Height = rect2.Y - (item.Rect.Y - (int)(item.Rect.Height * (1.0f - openState))); + + convexHull.SetVertices(GetConvexHullCorners(rect1)); + if (convexHull2!=null) convexHull2.SetVertices(GetConvexHullCorners(rect2)); + } + + } + } + + PhysicsBody body; + + Sprite doorSprite; + + public Door(Item item, XElement element) + : base(item, element) + { + //Vector2 position = new Vector2(newRect.X, newRect.Y); + + // isOpen = false; + + body = new PhysicsBody(BodyFactory.CreateRectangle(Game1.world, + ConvertUnits.ToSimUnits(Math.Max(item.Rect.Width, 1)), + ConvertUnits.ToSimUnits(Math.Max(item.Rect.Height, 1)), + 1.5f)); + + body.BodyType = BodyType.Static; + body.SetTransform( + ConvertUnits.ToSimUnits(new Vector2(item.Rect.X + item.Rect.Width / 2, item.Rect.Y - item.Rect.Height / 2)), + 0.0f); + body.Friction = 0.5f; + + + //string spritePath = Path.GetDirectoryName(item.Prefab.ConfigFile) + "\\"+ ToolBox.GetAttributeString(element, "sprite", ""); + + foreach (XElement subElement in element.Elements()) + { + if (subElement.Name.ToString().ToLower() != "sprite") continue; + doorSprite = new Sprite(subElement, Path.GetDirectoryName(item.Prefab.ConfigFile)); + break; + } + + isActive = true; + + Vector2[] corners = GetConvexHullCorners(item.Rect); + + convexHull = new ConvexHull(corners, Color.Black); + if (window!=null) convexHull2 = new ConvexHull(corners, Color.Black); + + OpenState = openState; + //powerConsumption = -100.0f; + + //LinkedGap.Open = openState; + } + + private Vector2[] GetConvexHullCorners(Rectangle rect) + { + 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); + + return corners; + } + + public override void Move(Vector2 amount) + { + base.Move(amount); + + body.SetTransform(body.Position + ConvertUnits.ToSimUnits(amount), 0.0f); + + convexHull.Move(amount); + } + + + public override bool Pick(Character picker) + { + isActive = true; + isOpen = !isOpen; + + return true; + } + + public override void Update(float deltaTime, Camera cam) + { + OpenState += deltaTime * ((isOpen) ? 3.0f : -3.0f); + + item.SendSignal((isOpen) ? "1" : "0", "state_out"); + } + + public override void Draw(SpriteBatch spriteBatch) + { + + LinkedGap.Open = openState; + + Color color = (item.IsSelected) ? Color.Green : Color.White; + + //prefab.sprite.Draw(spriteBatch, new Vector2(rect.X, -rect.Y), new Vector2(rect.Width, rect.Height), color); + + if (openState == 1.0f) + { + body.Enabled = false; + convexHull.Enabled = false; + } + else + { + spriteBatch.Draw(doorSprite.Texture, new Vector2(item.Rect.Center.X, -item.Rect.Y), + new Rectangle(0, (int)(doorSprite.size.Y * openState), + (int)doorSprite.size.X, (int)(doorSprite.size.Y * (1.0f - openState))), + color, 0.0f, doorSprite.Origin, 1.0f, SpriteEffects.None, doorSprite.Depth); + + convexHull.Enabled = true; + + if (openState == 0.0f) + { + body.Enabled = true; + } + else + { + + + //push characters out of the doorway when the door is closing/opening + Vector2 simPos = ConvertUnits.ToSimUnits(new Vector2(item.Rect.X, item.Rect.Y)); + Vector2 simSize = ConvertUnits.ToSimUnits(new Vector2(item.Rect.Width, + item.Rect.Height * (1.0f - openState))); + + foreach (Character c in Character.characterList) + { + int dir = Math.Sign(c.animController.limbs[0].SimPosition.X - simPos.X); + foreach (Limb l in c.animController.limbs) + { + if (l.SimPosition.Y < simPos.Y || l.SimPosition.Y > simPos.Y - simSize.Y) continue; + if (Math.Abs(l.SimPosition.X - simPos.X) > simSize.X * 2.0f) continue; + + l.body.ApplyForce(new Vector2(dir * 10.0f, 0.0f)); + } + } + } + } + } + + public override void Remove() + { + base.Remove(); + + Game1.world.RemoveBody(body.FarseerBody); + + doorSprite.Remove(); + + convexHull.Remove(); + if (convexHull2 != null) convexHull2.Remove(); + } + + public override void ReceiveSignal(string signal, Connection connection, Item sender) + { + isActive = true; + if (connection.name=="toggle") + { + isOpen = !isOpen; + } + else if (connection.name == "set_state") + { + isOpen = (signal!="0"); + } + } + } +} diff --git a/Subsurface/Items/Components/Holdable.cs b/Subsurface/Items/Components/Holdable.cs new file mode 100644 index 000000000..da63930bc --- /dev/null +++ b/Subsurface/Items/Components/Holdable.cs @@ -0,0 +1,220 @@ +using System.Diagnostics; +using System.Xml.Linq; +using FarseerPhysics; +using Microsoft.Xna.Framework; + +namespace Subsurface.Items.Components +{ + class Holdable : ItemComponent + { + //the position(s) in the item that the character grabs + protected Vector2[] handlePos; + + protected Character picker; + + //the distance from the holding characters elbow to center of the physics body of the item + protected Vector2 holdPos; + + protected Vector2 aimPos; + + protected bool aimable; + + private bool attachable; + private bool attached; + private PhysicsBody body; + + //the angle in which the character holds the item + protected float holdAngle; + + [HasDefaultValue(false, true)] + public bool Attached + { + get { return attached; } + set { attached = value; } + } + + [HasDefaultValue(false, false)] + public bool Aimable + { + get { return aimable; } + set { aimable = value; } + } + + [HasDefaultValue(false, false)] + public bool Attachable + { + get { return attachable; } + set { attachable = value; } + } + + [HasDefaultValue("0.0,0.0", false)] + public string HoldPos + { + get { return ToolBox.Vector2ToString(ConvertUnits.ToDisplayUnits(holdPos)); } + set { holdPos = ConvertUnits.ToSimUnits(ToolBox.ParseToVector2(value)); } + } + + [HasDefaultValue("0.0,0.0", false)] + public string AimPos + { + get { return ToolBox.Vector2ToString(ConvertUnits.ToDisplayUnits(aimPos)); } + set { aimPos = ConvertUnits.ToSimUnits(ToolBox.ParseToVector2(value)); } + } + + [HasDefaultValue(0.0f, false)] + private float HoldAngle + { + get { return MathHelper.ToDegrees(holdAngle); } + set { holdAngle = MathHelper.ToRadians(value); } + } + + public Holdable(Item item, XElement element) + : base(item, element) + { + body = item.body; + + handlePos = new Vector2[2]; + + for (int i = 1; i < 3; i++) + { + handlePos[i - 1] = ToolBox.GetAttributeVector2(element, "handle" + i, Vector2.Zero); + + handlePos[i - 1] = ConvertUnits.ToSimUnits(handlePos[i - 1]); + } + + //holdAngle = ToolBox.GetAttributeFloat(element, "holdangle", 0.0f); + //holdAngle = MathHelper.ToRadians(holdAngle); + } + + //public override void Equip(Character picker) + //{ + // if (picker == null) return; + // if (picker.Inventory == null) return; + + // this.picker = picker; + + // for (int i = item.linkedTo.Count - 1; i >= 0; i--) + // item.linkedTo[i].RemoveLinked((MapEntity)item); + // item.linkedTo.Clear(); + + // System.Diagnostics.Debug.WriteLine("picked item"); + + // //this.picker = picker; + // picker.SelectedItem = item; + + // isActive = true; + //} + + public override void Drop(Character dropper) + { + if (picker == null) + { + if (dropper==null) return; + picker = dropper; + } + if (picker.Inventory == null) return; + + item.body.Enabled = true; + isActive = false; + + //item.Unequip(); + + picker.DeselectItem(item); + picker.Inventory.RemoveItem(item); + picker = null; + } + + public override void Equip(Character character) + { + picker = character; + + if (!item.body.Enabled) + { + Limb rightHand = picker.animController.GetLimb(LimbType.RightHand); + item.SetTransform(rightHand.SimPosition, 0.0f); + } + + if (picker.TrySelectItem(item)) + { + item.body.Enabled = true; + isActive = true; + } + } + + public override void Unequip(Character character) + { + if (picker == null) return; + + picker.DeselectItem(item); + + item.body.Enabled = false; + isActive = false; + } + + public override bool Pick(Character picker) + { + if (!attachable) return false; + + //if (item.body==null) + //{ + // DebugConsole.ThrowError("Item " + item + " must have a physics body component to be attachable!"); + // return false; + //} + + item.body = body; + item.body.Enabled = true; + + return true; + } + + public override bool Use(Character character = null) + { + if (!attachable || item.body==null) return false; + + item.Drop(); + item.body.Enabled = false; + item.body = null; + + return true; + } + + public override void UpdateBroken(float deltaTime, Camera cam) + { + Update(deltaTime, cam); + } + + public override void Update(float deltaTime, Camera cam) + { + //if (picker == null)// || picker.animController.selectedItem != item) + //{ + // System.Diagnostics.Debug.WriteLine("drop"); + // //picker = null; + // isActive = false; + // return; + //} + + if (!item.body.Enabled) return; + if (!picker.HasSelectedItem(item)) isActive = false; + + ApplyStatusEffects(ActionType.OnActive, deltaTime, picker); + + if (item.body.Dir != picker.animController.Dir) Flip(item); + + AnimController ac = picker.animController; + + ac.HoldItem(deltaTime, cam, item, handlePos, holdPos, aimPos, holdAngle); + } + + protected void Flip(Item item) + { + handlePos[0].X = -handlePos[0].X; + handlePos[1].X = -handlePos[1].X; + item.body.Dir = -item.body.Dir; + } + + public override void OnMapLoaded() + { + if (attached) Use(); + } + } +} diff --git a/Subsurface/Items/Components/ItemComponent.cs b/Subsurface/Items/Components/ItemComponent.cs new file mode 100644 index 000000000..9e47da6f6 --- /dev/null +++ b/Subsurface/Items/Components/ItemComponent.cs @@ -0,0 +1,492 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Xml.Linq; +using Lidgren.Network; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Subsurface.Networking; +using Subsurface.Items.Components; + +namespace Subsurface +{ + struct ItemSound + { + public readonly byte index; + public readonly ActionType type; + + public readonly float range; + + public ItemSound(int index, ActionType type, float range) + { + this.index = (byte)index; + this.type = type; + this.range = range; + } + } + + /// + /// The base class for components holding the different functionalities of the item + /// + class ItemComponent : IPropertyObject + { + protected Item item; + + protected string name; + + protected bool isActive; + + protected bool characterUsable; + + protected bool canBePicked; + protected bool canBeSelected; + + public List statusEffects; + + protected bool updated; + + public List requiredItems; + + private List sounds; + + public readonly Dictionary properties; + public Dictionary ObjectProperties + { + get { return properties; } + } + //has the component already been updated this frame + public bool Updated + { + get { return updated; } + set { updated = value; } + } + + public virtual bool IsActive + { + get { return isActive; } + set { isActive = value; } + } + + [HasDefaultValue(false, false)] + public bool CanBePicked + { + get { return canBePicked; } + set { canBePicked = value; } + } + + [HasDefaultValue(false, false)] + public bool CanBeSelected + { + get { return canBeSelected; } + set { canBeSelected = value; } + } + + public Item Item + { + get { return item; } + } + + public string Name + { + get { return name; } + } + + [HasDefaultValue("", false)] + public string Msg + { + get { return msg; } + set { msg = value; } + } + + private string msg; + + public ItemComponent(Item item, XElement element) + { + this.item = item; + + properties = ObjectProperty.GetProperties(this); + + //canBePicked = ToolBox.GetAttributeBool(element, "canbepicked", false); + //canBeSelected = ToolBox.GetAttributeBool(element, "canbeselected", false); + + //msg = ToolBox.GetAttributeString(element, "msg", ""); + + requiredItems = new List(); + + sounds = new List(); + + statusEffects = new List(); + + //var initableProperties = ObjectProperty.GetProperties(this); + //foreach (ObjectProperty initableProperty in initableProperties) + //{ + // object value = ToolBox.GetAttributeObject(element, initableProperty.Name.ToLower()); + // if (value==null) + // { + // foreach (var ini in initableProperty.Attributes.OfType()) + // { + // value = ini.defaultValue; + // break; + // } + // } + + // initableProperty.TrySetValue(value); + //} + + + properties = ObjectProperty.InitProperties(this, element); + + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLower()) + { + case "requireditem": + RelatedItem ri = RelatedItem.Load(subElement); + if (ri != null) requiredItems.Add(ri); + break; + case "statuseffect": + statusEffects.Add(StatusEffect.Load(subElement)); + break; + case "sound": + string filePath = ToolBox.GetAttributeString(subElement, "path", ""); + if (filePath=="") continue; + + int index = item.Prefab.sounds.FindIndex(x => x.FilePath == filePath); + + ActionType type; + + try + { + type = (ActionType)Enum.Parse(typeof(ActionType), ToolBox.GetAttributeString(subElement, "type", ""), true); + } + catch (Exception e) + { + DebugConsole.ThrowError("Invalid sound type in "+subElement+"!", e); + break; + } + + float range = ToolBox.GetAttributeFloat(subElement, "range", 800.0f); + + sounds.Add(new ItemSound(index, type, range)); + break; + } + + } + } + + //public List GetProperties() + //{ + // List editableProperties = new List(); + + // foreach (var property in properties.Values) + // { + // if (property.Attributes.OfType().Count() > 0) editableProperties.Add(property); + // } + + // return editableProperties; + //} + + + + private int loopingSoundIndex; + + + public void PlaySound(ActionType type, float volume, Vector2 position, bool loop=false) + { + + if (!loop) + { + List matchingSounds = sounds.FindAll(x=> x.type == type); + + if (matchingSounds.Count == 0 || item.Prefab.sounds.Count == 0) return; + + int index = Game1.localRandom.Next(Math.Min(matchingSounds.Count,item.Prefab.sounds.Count)); + + Sound sound = item.Prefab.sounds[matchingSounds[index].index]; + + sound.Play(volume, matchingSounds[index].range, position); + } + else + { + //todo: get rid of copypaste + if (!Sounds.SoundManager.IsPlaying(loopingSoundIndex)) + { + List matchingSounds = sounds.FindAll(x => x.type == type); + + if (matchingSounds.Count == 0 || item.Prefab.sounds.Count == 0) return; + + int index = Game1.localRandom.Next(Math.Min(matchingSounds.Count, item.Prefab.sounds.Count)); + + Sound sound = item.Prefab.sounds[matchingSounds[index].index]; + + loopingSoundIndex = sound.Loop(loopingSoundIndex, volume, position, matchingSounds[index].range); + } + } + } + + public virtual void Move(Vector2 amount) { } + + /// a character has picked the item + public virtual bool Pick(Character picker) + { + return false; + } + + /// a character has dropped the item + public virtual void Drop(Character dropper) { } + + public virtual void Draw(SpriteBatch spriteBatch) { } + + public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { } + + /// + /// a construction has activated the item (such as a turret shooting a projectile) + /// call the Activate-methods of the components + /// The construction which activated the item + /// A vector that can be used to pass additional information to the components + public virtual void ItemActivate(Item item, Vector2 modifier) { } + + //called when isActive is true and condition > 0.0f + public virtual void Update(float deltaTime, Camera cam) { } + + //called when isActive is true and condition == 0.0f + public virtual void UpdateBroken(float deltaTime, Camera cam) + { + if (loopingSoundIndex <= 0) return; + + if (Sounds.SoundManager.IsPlaying(loopingSoundIndex)) + { + Sounds.SoundManager.Stop(loopingSoundIndex); + } + + } + + //called when the item is equipped and left mouse button is pressed + //returns true if the item was used succesfully (not out of ammo, reloading, etc) + public virtual bool Use(Character character = null) + { + return false; + } + + //called when the item is equipped and right mouse button is pressed + public virtual void SecondaryUse(Character character = null) { } + + //called when the item is placed in a "limbslot" + public virtual void Equip(Character character) { } + + //called then the item is dropped or dragged out of a "limbslot" + public virtual void Unequip(Character character) { } + + public virtual bool UseOtherItem(Item item) + { + return false; + } + + public virtual void ReceiveSignal(string signal, Connection connection, Item sender) { } + + public virtual bool Combine(Item item) + { + return false; + } + + public virtual void Remove() { } + + public bool HasRequiredContainedItems(bool addMessage) + { + if (requiredItems.Count() == 0) return true; + + Item[] containedItems = item.ContainedItems; + if (containedItems == null || containedItems.Count() == 0) return false; + + foreach (RelatedItem ri in requiredItems) + { + if (ri.Type != RelatedItem.RelationType.Contained) continue; + Item containedItem = Array.Find(containedItems, x => x != null && x.Condition > 0.0f && ri.MatchesItem(x)); + if (containedItem == null) + { + //if (addMessage && !String.IsNullOrEmpty(ri.Msg)) GUI.AddMessage(ri.Msg, Color.Red); + return false; + } + } + + return true; + } + + public bool HasRequiredEquippedItems(Character character, bool addMessage) + { + if (requiredItems.Count() == 0) return true; + + foreach (RelatedItem ri in requiredItems) + { + if (ri.Type == RelatedItem.RelationType.Equipped) + { + for (int i = 0; i < character.SelectedItems.Length; i++ ) + { + Item selectedItem = character.SelectedItems[i]; + if (selectedItem !=null && selectedItem.Condition>0.0f && ri.MatchesItem(selectedItem )) + { + return true; + } + } + + //if (addMessage && !String.IsNullOrEmpty(ri.Msg)) GUI.AddMessage(ri.Msg, Color.Red); + return false; + } + else if (ri.Type == RelatedItem.RelationType.Picked) + { + Item pickedItem = character.Inventory.items.FirstOrDefault(x => x!=null && x.Condition>0.0f && ri.MatchesItem(x)); + if (pickedItem == null) + { + //if (addMessage && !String.IsNullOrEmpty(ri.Msg)) GUI.AddMessage(ri.Msg, Color.Red); + return false; + } + } + else + { + continue; + } + } + + return true; + } + + public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb limb = null) + { + foreach (StatusEffect effect in statusEffects) + { + if (effect.type != type) continue; + item.ApplyStatusEffect(effect, type, deltaTime, character, limb); + } + } + + public virtual XElement Save(XElement parentElement) + { + XElement componentElement = new XElement(name); + + foreach (RelatedItem ri in requiredItems) + { + XElement newElement = new XElement("requireditem"); + ri.Save(newElement); + componentElement.Add(newElement); + } + + ObjectProperty.SaveProperties(this, componentElement); + + //var saveProperties = ObjectProperty.GetProperties(this); + //foreach (var property in saveProperties) + //{ + // object value = property.GetValue(); + // if (value == null) continue; + + // bool dontSave = false; + // foreach (var ini in property.Attributes.OfType()) + // { + // if (ini.defaultValue != value) continue; + + // dontSave = true; + // break; + // } + + // if (dontSave) continue; + + // componentElement.Add(new XAttribute(property.Name.ToLower(), value)); + //} + + parentElement.Add(componentElement); + return componentElement; + } + + public virtual void Load(XElement componentElement) + { + if (componentElement == null) return; + + bool requiredItemsCleared = false; + + foreach (XAttribute attribute in componentElement.Attributes()) + { + ObjectProperty property = null; + if (!properties.TryGetValue(attribute.Name.ToString().ToLower(), out property)) continue; + + property.TrySetValue(attribute.Value); + } + + foreach (XElement subElement in componentElement.Elements()) + { + switch (subElement.Name.ToString().ToLower()) + { + case "requireditem": + RelatedItem newRequiredItem = RelatedItem.Load(subElement); + + if (newRequiredItem == null) continue; + + if (!requiredItemsCleared) + { + requiredItems.Clear(); + requiredItemsCleared = true; + } + + requiredItems.Add(newRequiredItem); + break; + } + } + } + + public virtual void OnMapLoaded() { } + + public static ItemComponent Load(XElement element, Item item, string file) + { + Type t; + string type = element.Name.ToString().ToLower(); + try + { + // Get the type of a specified class. + t = Type.GetType("Subsurface.Items.Components." + type + ", Subsurface", true, true); + if (t == null) + { + DebugConsole.ThrowError("Could not find the component ''" + type + "'' (" + file + ")"); + return null; + } + } + catch (Exception e) + { + DebugConsole.ThrowError("Could not find the component ''" + type + "'' (" + file + ")", e); + return null; + } + + ConstructorInfo constructor; + try + { + if (!t.IsSubclassOf(typeof(ItemComponent))) return null; + constructor = t.GetConstructor(new Type[] { typeof(Item), typeof(XElement) }); + if (constructor == null) + { + DebugConsole.ThrowError("Could not find the constructor of the component ''" + type + "'' (" + file + ")"); + return null; + } + } + catch (Exception e) + { + DebugConsole.ThrowError("Could not find the constructor of the component ''" + type + "'' (" + file + ")", e); + return null; + } + + object[] lobject = new object[] { item, element }; + object component = constructor.Invoke(lobject); + + ItemComponent ic = (ItemComponent)component; + ic.name = element.Name.ToString(); + + return ic; + } + + public virtual void FillNetworkData(NetworkEventType type, NetOutgoingMessage message) + { + } + + public virtual void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) + { + } + } +} diff --git a/Subsurface/Items/Components/Ladder.cs b/Subsurface/Items/Components/Ladder.cs new file mode 100644 index 000000000..051fe78db --- /dev/null +++ b/Subsurface/Items/Components/Ladder.cs @@ -0,0 +1,23 @@ +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class Ladder : ItemComponent + { + + public Ladder(Item item, XElement element) + : base(item, element) + { + } + + public override bool Pick(Character picker = null) + { + if (picker == null) return false; + + picker.animController.anim = AnimController.Animation.Climbing; + //picker.SelectedConstruction = item; + + return true; + } + } +} diff --git a/Subsurface/Items/Components/MiniMap.cs b/Subsurface/Items/Components/MiniMap.cs new file mode 100644 index 000000000..90cf3c72f --- /dev/null +++ b/Subsurface/Items/Components/MiniMap.cs @@ -0,0 +1,83 @@ +using System; +using System.Xml.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface.Items.Components +{ + class MiniMap : Powered + { + + public MiniMap(Item item, XElement element) + : base(item, element) + { + isActive = true; + } + + public override void Update(float deltaTime, Camera cam) + { + currPowerConsumption = powerConsumption; + + voltage = 0.0f; + } + + public override bool Pick(Character picker) + { + if (picker == null) return false; + + picker.SelectedConstruction = item; + + return true; + } + + public override void DrawHUD(SpriteBatch spriteBatch, Character character) + { + int width = 500, height = 400; + int x = Game1.GraphicsWidth / 2 - width / 2; + int y = Game1.GraphicsHeight / 2 - height / 2; + + + GUI.DrawRectangle(spriteBatch, new Rectangle(x,y,width,height), Color.Black, true); + + Rectangle miniMap = new Rectangle(x + 20, y + 40, width - 40, height - 60); + + float size = Math.Min((float)miniMap.Width / (float)Map.Borders.Width, (float)miniMap.Height / (float)Map.Borders.Height); + foreach (Hull hull in Hull.hullList) + { + Rectangle hullRect = new Rectangle( + miniMap.X + (int)((hull.Rect.X - Map.Borders.X) * size), + miniMap.Y - (int)((hull.Rect.Y - Map.Borders.Y) * size), + (int)(hull.Rect.Width * size), + (int)(hull.Rect.Height * size)); + + float waterAmount = Math.Min(hull.Volume / hull.FullVolume, 1.0f); + + if (hullRect.Height * waterAmount > 1.0f) + { + Rectangle waterRect = new Rectangle( + hullRect.X, + (int)(hullRect.Y + hullRect.Height * (1.0f - waterAmount)), + hullRect.Width, + (int)(hullRect.Height * waterAmount)); + + GUI.DrawRectangle(spriteBatch, waterRect, Color.DarkBlue, true); + } + + GUI.DrawRectangle(spriteBatch, hullRect, Color.White); + } + + foreach (Character c in Character.characterList) + { + if (c.animController.CurrentHull!=null) continue; + + Rectangle characterRect = new Rectangle( + miniMap.X + (int)((c.Position.X - Map.Borders.X) * size), + miniMap.Y - (int)((c.Position.Y - Map.Borders.Y) * size), + 5, 5); + + GUI.DrawRectangle(spriteBatch, characterRect, Color.White, true); + } + } + + } +} diff --git a/Subsurface/Items/Components/OxygenGenerator.cs b/Subsurface/Items/Components/OxygenGenerator.cs new file mode 100644 index 000000000..1a43e3e52 --- /dev/null +++ b/Subsurface/Items/Components/OxygenGenerator.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class OxygenGenerator : Powered + { + PropertyTask powerUpTask; + + bool running; + + List ventList; + + public bool IsRunning() + { + return running && item.Condition>0.0f; + } + + public OxygenGenerator(Item item, XElement element) + : base(item, element) + { + isActive = true; + + ventList = new List(); + + item.linkedTo.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler( + delegate(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { GetVents(); } + ); + } + + public override void Update(float deltaTime, Camera cam) + { + base.Update(deltaTime, cam); + + currPowerConsumption = powerConsumption; + + if (item.currentHull == null) return; + + if (voltage < minVoltage) + { + running = false; + if (powerUpTask==null || powerUpTask.IsFinished) + { + powerUpTask = new PropertyTask(Game1.gameSession.taskManager, item, IsRunning, 30.0f, "Turn on the oxygen generator"); + } + return; + } + + running = true; + + float deltaOxygen = Math.Min(voltage, 1.0f) * 50000.0f; + item.currentHull.Oxygen += deltaOxygen * deltaTime; + + UpdateVents(deltaOxygen); + + voltage = 0.0f; + } + + private void GetVents() + { + foreach (MapEntity entity in item.linkedTo) + { + Item linkedItem = entity as Item; + if (linkedItem == null) continue; + + Vent vent = linkedItem.GetComponent(); + if (vent != null) ventList.Add(vent); + } + } + + private void UpdateVents(float deltaOxygen) + { + if (ventList.Count == 0) return; + + deltaOxygen = deltaOxygen / ventList.Count; + foreach (Vent v in ventList) + { + v.OxygenFlow = deltaOxygen; + v.IsActive = true; + } + } + } +} diff --git a/Subsurface/Items/Components/Pickable.cs b/Subsurface/Items/Components/Pickable.cs new file mode 100644 index 000000000..5e9d5ed37 --- /dev/null +++ b/Subsurface/Items/Components/Pickable.cs @@ -0,0 +1,94 @@ +using System; +using System.Diagnostics; +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class Pickable : ItemComponent + { + protected Character picker; + + protected LimbSlot allowedSlots; + + public LimbSlot AllowedSlots + { + get { return allowedSlots; } + } + + public Character Picker + { + get { return picker; } + } + + public Pickable(Item item, XElement element) + : base(item, element) + { + string slotString = ToolBox.GetAttributeString(element, "slots", "Any"); + string[] slots = slotString.Split(','); + foreach (string slot in slots) + { + allowedSlots = allowedSlots | (LimbSlot)Enum.Parse(typeof(LimbSlot), slot.Trim()); + } + + canBePicked = true; + } + + public override bool Pick(Character picker) + { + if (picker == null) return false; + if (picker.Inventory == null) return false; + + this.picker = picker; + + for (int i = item.linkedTo.Count - 1; i >= 0; i--) + item.linkedTo[i].RemoveLinked(item); + item.linkedTo.Clear(); + + if (picker.Inventory.TryPutItem(item, allowedSlots)) + { + if (!picker.HasSelectedItem(item) && item.body!=null) item.body.Enabled = false; + this.picker = picker; + + ApplyStatusEffects(ActionType.OnPicked, 1.0f, picker, null); + + //foreach (StatusEffect effect in item.Prefab.statusEffects) + //{ + // effect.OnPicked(picker, null); + //} + + return true; + } + + return false; + } + + public override void Drop(Character dropper) + { + if (picker == null) + { + picker = dropper; + + //foreach (Character c in Character.characterList) + //{ + // if (c.Inventory == null) continue; + // if (c.Inventory.FindIndex(item) == -1) continue; + + // picker = c; + // break; + //} + } + + if (picker==null || picker.Inventory == null) return; + + if (item.body!= null && !item.body.Enabled) + { + Limb rightHand = picker.animController.GetLimb(LimbType.RightHand); + item.SetTransform(rightHand.SimPosition, 0.0f); + item.body.Enabled = true; + } + picker.Inventory.RemoveItem(item); + picker = null; + } + + } +} diff --git a/Subsurface/Items/Components/PowerContainer.cs b/Subsurface/Items/Components/PowerContainer.cs new file mode 100644 index 000000000..61083fa12 --- /dev/null +++ b/Subsurface/Items/Components/PowerContainer.cs @@ -0,0 +1,201 @@ +using System; +using System.Xml.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface.Items.Components +{ + class PowerContainer : Powered + { + float capacity; + + //[power/min] + float charge; + + //how fast the battery can be recharged + float maxRechargeSpeed; + + //how fast it's currently being recharged (can be changed, so that + //charging can be slowed down or disabled if there's a shortage of power) + float rechargeSpeed; + + float maxOutput; + + //public override float Charge + //{ + // get { return charge; } + // set { Math.Max(Math.Min(charge = value, capacity), 0.0f); } + //} + + [Editable, HasDefaultValue(10.0f, true)] + public float MaxOutPut + { + get { return maxOutput; } + } + + [HasDefaultValue(0.0f, true)] + public float Charge + { + get { return charge; } + set { charge = MathHelper.Clamp(value, 0.0f, capacity); } + } + + + [HasDefaultValue(10.0f, false)] + private float Capacity + { + set { capacity = Math.Max(value,1.0f); } + } + + [HasDefaultValue(10.0f, false)] + private float MaxInput + { + set { MaxInput = value; } + } + + [HasDefaultValue(10.0f, false)] + private float MaxOutput + { + set { maxOutput = value; } + } + + public PowerContainer(Item item, XElement element) + : base(item, element) + { + //capacity = ToolBox.GetAttributeFloat(element, "capacity", 10.0f); + //maxRechargeSpeed = ToolBox.GetAttributeFloat(element, "maxinput", 10.0f); + //maxOutput = ToolBox.GetAttributeFloat(element, "maxoutput", 10.0f); + + rechargeSpeed = maxRechargeSpeed; + + isActive = true; + } + + public override bool Pick(Character picker) + { + if (picker == null) return false; + + //picker.SelectedConstruction = (picker.SelectedConstruction == item) ? null : item; + + return true; + } + + public override void Update(float deltaTime, Camera cam) + { + float chargeRate = (float)(Math.Sqrt(charge / capacity)); + //float gridPower = 0.0f; + float gridLoad = 0.0f; + + //if (item.linkedTo.Count == 0) return; + + ApplyStatusEffects(ActionType.OnActive, deltaTime, null); + + + foreach (Connection c in item.Connections) + { + foreach (Connection c2 in c.Recipients) + { + PowerTransfer pt = c2.Item.GetComponent(); + if (pt == null) continue; + + gridLoad += pt.PowerLoad; + } + } + + //foreach (MapEntity e in item.linkedTo) + //{ + // Item it = e as Item; + // if (it == null) continue; + + // PowerTransfer pt = it.GetComponent(); + // if (pt==null) continue; + // //gridPower -= pt.PowerConsumption; + // gridLoad += pt.PowerLoad; + + // //gridPower = -jb.PowerConsumption; + // //gridLoad = jb.load; + // break; + //} + + float gridRate = voltage; + + //recharge + if (gridRate >= chargeRate) + { + if (charge >= capacity) + { + currPowerConsumption = 0.0f; + charge = capacity; + return; + } + + currPowerConsumption = MathHelper.Lerp(currPowerConsumption, rechargeSpeed, 0.05f); + charge += currPowerConsumption*voltage / 3600.0f; + } + //provide power to the grid + else if (gridLoad > 0.0f) + { + if (charge <= 0.0f) + { + currPowerConsumption = 0.0f; + charge = 0.0f; + return; + } + + currPowerConsumption = MathHelper.Lerp( + currPowerConsumption, + -maxOutput * chargeRate, + 0.1f); + + currPowerConsumption = MathHelper.Lerp( + currPowerConsumption, + -Math.Min(maxOutput * chargeRate, gridLoad - (gridLoad * voltage)), + 0.05f); + + //powerConsumption = MathHelper.Lerp( + // powerConsumption, + // -Math.Min(maxOutput * chargeRate, gridLoad - (power)), + // 0.1f); + + //powerConsumption = Math.Min(powerConsumption, 0.0f); + charge -= -currPowerConsumption / chargeRate / 3600.0f; + } + + voltage = 0.0f; + } + + public override void Draw(SpriteBatch spriteBatch) + { + base.Draw(spriteBatch); + + GUI.DrawRectangle(spriteBatch, + new Vector2(item.Rect.X + item.Rect.Width / 2 - 4, -item.Rect.Y + 9), + new Vector2(8, 22), Color.Black); + + if (charge > 0) + GUI.DrawRectangle(spriteBatch, + new Vector2(item.Rect.X + item.Rect.Width / 2 - 3, -item.Rect.Y + 10 + (20.0f * (1.0f - charge / capacity))), + new Vector2(6, 20 * (charge / capacity)), Color.Green, true); + } + + public override void DrawHUD(SpriteBatch spriteBatch, Character character) + { + int width = 300, height = 200; + int x = Game1.GraphicsWidth / 2 - width / 2; + int y = Game1.GraphicsHeight / 2 - height / 2; + + GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black, true); + + spriteBatch.DrawString(GUI.font, + "Charge: " + (int)charge + "/" + (int)capacity + " (" + (int)((charge / capacity) * 100.0f) + " %)", + new Vector2(x + 30, y + 30), Color.White); + + spriteBatch.DrawString(GUI.font, "Recharge rate: " + (rechargeSpeed / maxRechargeSpeed), new Vector2(x + 30, y + 100), Color.White); + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 50, y + 150, 40, 40), "+", true)) + rechargeSpeed = Math.Min(rechargeSpeed + 10.0f, maxRechargeSpeed); + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 250, y + 150, 40, 40), "-", true)) + rechargeSpeed = Math.Max(rechargeSpeed - 10.0f, 0.0f); + } + + } +} diff --git a/Subsurface/Items/Components/PowerTransfer.cs b/Subsurface/Items/Components/PowerTransfer.cs new file mode 100644 index 000000000..47144a969 --- /dev/null +++ b/Subsurface/Items/Components/PowerTransfer.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; +using System.Xml.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; + +namespace Subsurface.Items.Components +{ + class PowerTransfer : Powered + { + static float fullPower; + static float fullLoad; + + //affects how fast changes in power/load are carried over the grid + static float inertia = 5.0f; + + static List connectedList = new List(); + + private float powerLoad; + + public float PowerLoad + { + get { return powerLoad; } + } + + public PowerTransfer(Item item, XElement element) + : base(item, element) + { + isActive = true; + } + + public override void Update(float deltaTime, Camera cam) + { + //reset and recalculate the power generated/consumed + //by the constructions connected to the grid + fullPower = 0.0f; + fullLoad = 0.0f; + connectedList.Clear(); + + if (updated) return; + + CheckJunctions(deltaTime); + + foreach (Powered p in connectedList) + { + PowerTransfer pt = p as PowerTransfer; + if (pt!=null) + { + pt.powerLoad += (fullLoad - pt.powerLoad) / inertia; + pt.currPowerConsumption += (-fullPower - pt.currPowerConsumption) / inertia; + pt.Item.SendSignal((fullPower / Math.Max(fullLoad,1.0f)).ToString(), "power_out"); + } + else + { + //p.Power = fullPower; + } + } + + + } + + public override bool Pick(Character picker) + { + if (picker == null) return false; + + //picker.SelectedConstruction = (picker.SelectedConstruction == item) ? null : item; + + return true; + } + + //a recursive function that goes through all the junctions and adds up + //all the generated/consumed power of the constructions connected to the grid + private void CheckJunctions(float deltaTime) + { + updated = true; + connectedList.Add(this); + + ApplyStatusEffects(ActionType.OnActive, deltaTime, null); + + List connections = item.Connections; + if (connections == null) return; + + foreach (Connection c in connections) + { + foreach (Connection recipient in c.Recipients) + { + if (recipient == null) continue; + + Item it = recipient.Item; + if (it == null) continue; + + //if (it.Updated) continue; + + Powered powered = it.GetComponent(); + if (powered == null || powered.Updated) continue; + + PowerTransfer powerTransfer = powered as PowerTransfer; + if (powerTransfer != null) + { + powerTransfer.CheckJunctions(deltaTime); + } + else + { + connectedList.Add(powered); + //positive power consumption = the construction requires power -> increase load + if (powered.PowerConsumption > 0.0f) + { + fullLoad += powered.PowerConsumption; + } + else if (powered.PowerConsumption < 0.0f) + //negative power consumption = the construction is a + //generator/battery or another junction box + { + fullPower -= powered.PowerConsumption; + } + } + } + } + + } + + public override void DrawHUD(SpriteBatch spriteBatch, Character character) + { + int width = 300, height = 200; + int x = Game1.GraphicsWidth / 2 - width / 2; + int y = Game1.GraphicsHeight / 2 - height / 2; + + GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black, true); + + spriteBatch.DrawString(GUI.font, "Power: " + (int)(-currPowerConsumption), new Vector2(x + 30, y + 30), Color.White); + spriteBatch.DrawString(GUI.font, "Load: " + (int)powerLoad, new Vector2(x + 30, y + 100), Color.White); + } + + } +} diff --git a/Subsurface/Items/Components/Powered.cs b/Subsurface/Items/Components/Powered.cs new file mode 100644 index 000000000..e3651ccc4 --- /dev/null +++ b/Subsurface/Items/Components/Powered.cs @@ -0,0 +1,85 @@ +using System; +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class Powered : ItemComponent + { + //the amount of power CURRENTLY consumed by the item + //negative values mean that the item is providing power to connected items + protected float currPowerConsumption; + + //the amount of power available for the item through connected items + protected float voltage; + + //the amount of power required for the item to work + protected float minVoltage; + + //the maximum amount of power the item can draw from connected items + protected float powerConsumption; + + [Editable, HasDefaultValue(0.5f, true)] + public float MinVoltage + { + get { return minVoltage; } + set { minVoltage = value; } + } + + [Editable, HasDefaultValue(0.0f, true)] + public float PowerConsumption + { + get { return powerConsumption; } + set { powerConsumption = value; } + } + + + [HasDefaultValue(false,true)] + public override bool IsActive + { + get { return isActive; } + set + { + isActive = value; + if (!isActive) currPowerConsumption = 0.0f; + } + } + + [HasDefaultValue(0.0f, true)] + public float CurrPowerConsumption + { + get {return currPowerConsumption; } + set { currPowerConsumption = value; } + } + + [HasDefaultValue(0.0f, true)] + public float Voltage + { + get { return voltage; } + set { voltage = Math.Max(0.0f, value); } + } + + public override void ReceiveSignal(string signal, Connection connection, Item sender) + { + if (connection.name=="power_in") + { + if (!float.TryParse(signal, out voltage)) + { + voltage = 0.0f; + } + } + } + + public override void Update(float deltaTime, Camera cam) + { + if (currPowerConsumption == 0.0f) return; + if (voltage > minVoltage) ApplyStatusEffects(ActionType.OnActive, deltaTime); + } + + public Powered(Item item, XElement element) + : base(item, element) + { + //minVoltage = ToolBox.GetAttributeFloat(element, "minvoltage", 10.0f); + //powerConsumption = ToolBox.GetAttributeFloat(element, "powerconsumption", 15.0f); + } + } +} diff --git a/Subsurface/Items/Components/Projectile.cs b/Subsurface/Items/Components/Projectile.cs new file mode 100644 index 000000000..6bd2ee72f --- /dev/null +++ b/Subsurface/Items/Components/Projectile.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Dynamics.Contacts; +using FarseerPhysics.Dynamics.Joints; +using Microsoft.Xna.Framework; + +namespace Subsurface.Items.Components +{ + class Projectile : ItemComponent + { + private float launchImpulse; + + private bool doesStick; + private PrismaticJoint stickJoint; + private Body stickTarget; + + Attack attack; + + public List ignoredBodies; + + [HasDefaultValue(10.0f, false)] + public float LaunchImpulse + { + get { return launchImpulse; } + set { launchImpulse = value; } + } + + [HasDefaultValue(false, false)] + public bool CharacterUsable + { + get { return characterUsable; } + set { characterUsable = value; } + } + + [HasDefaultValue(false, false)] + public bool DoesStick + { + get { return doesStick; } + set { doesStick = value; } + } + + public Projectile(Item item, XElement element) + : base (item, element) + { + ignoredBodies = new List(); + + //launchImpulse = ToolBox.GetAttributeFloat(element, "launchimpulse", 10.0f); + //characterUsable = ToolBox.GetAttributeBool(element, "characterusable", false); + + foreach (XElement subElement in element.Elements()) + { + if (subElement.Name.ToString().ToLower() != "attack") continue; + attack = new Attack(subElement); + } + + //bleedingDamage = ToolBox.GetAttributeFloat(element, "bleedingdamage", 0.0f); + //bluntDamage = ToolBox.GetAttributeFloat(element, "bluntdamage", 0.0f); + + //doesStick = ToolBox.GetAttributeBool(element, "doesstick", false); + } + + //public override void ConstructionActivate(Construction c, Vector2 modifier) + //{ + // for (int i = 0; i < item.linkedTo.Count; i++) + // item.linkedTo[i].RemoveLinked((MapEntity)item); + // item.linkedTo.Clear(); + + // ApplyStatusEffects(StatusEffect.Type.OnUse, 1.0f, null); + + // Launch(modifier+Vector2.Normalize(modifier)*launchImpulse); + + //} + + public override bool Use(Character character = null) + { + if (character != null && !characterUsable) return false; + + ApplyStatusEffects(ActionType.OnUse, 1.0f, character); + + Debug.WriteLine(item.body.Rotation); + + Launch(new Vector2( + (float)Math.Cos(item.body.Rotation), + (float)Math.Sin(item.body.Rotation))*launchImpulse*item.body.Mass); + + return true; + } + + private void Launch(Vector2 impulse) + { + item.body.Enabled = true; + item.body.ApplyLinearImpulse(impulse); + + item.body.FarseerBody.OnCollision += OnProjectileCollision; + item.body.FarseerBody.IsBullet = true; + + item.body.CollisionCategories = Physics.CollisionProjectile; + item.body.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall; + + item.Drop(); + + if (stickJoint != null && doesStick) + { + if (stickTarget!=null) item.body.FarseerBody.RestoreCollisionWith(stickTarget); + Game1.world.RemoveJoint(stickJoint); + stickJoint = null; + } + } + + public override void Update(float deltaTime, Camera cam) + { + if (stickJoint != null) + { + if (stickJoint.JointTranslation < 0.01f) + { + if (stickTarget!=null) + { + item.body.FarseerBody.RestoreCollisionWith(stickTarget); + } + + Game1.world.RemoveJoint(stickJoint); + stickJoint = null; + + isActive = false; + } + } + else + { + isActive = false; + } + } + + private bool OnProjectileCollision(Fixture f1, Fixture f2, Contact contact) + { + //doesn't collide with items + if (f2.Body.UserData is Item) return false; + + if (ignoredBodies.Contains(f2.Body)) return false; + + //Structure structure = f1.Body.UserData as Structure; + //if (structure!=null && (structure.IsPlatform || structure.StairDirection != Direction.None)) return false; + + //Vector2 force = f1.Body.LinearVelocity * f1.Body.Mass; + //float forceLength = force.Length(); + + //if (forceLength > 20.0f) + //{ + // force = force / forceLength * 20.0f; + //} + + //f2.Body.ApplyLinearImpulse(force); + //f1.Body.ApplyLinearImpulse(-f1.Body.LinearVelocity * f1.Body.Mass); + + float damage = f1.Body.LinearVelocity.Length(); + + Limb limb; + Structure structure; + if ((limb = (f2.Body.UserData as Limb)) != null) + { + attack.DoDamage(limb.character, item.SimPosition, 0.0f); + //limb.Damage += damage; + //limb.Bleeding += bleedingDamage; + + //if (bleedingDamage>0.0f) + //{ + // for (int i = 0; i < 5; i++ ) + // { + // Game1.particleManager.CreateParticle(limb.SimPosition, + // ToolBox.VectorToAngle(-f1.Body.LinearVelocity*0.5f) + ToolBox.RandomFloat(-0.5f, 0.5f), + // ToolBox.RandomFloat(1.0f, 3.0f), "blood"); + // } + + // Game1.particleManager.CreateParticle(limb.SimPosition, + // 0.0f, + // Vector2.Zero, "waterblood"); + //} + + //AmbientSoundManager.PlayDamageSound(DamageType.LimbBlunt, damage, limb.body.FarseerBody); + } + else if ((structure = (f2.Body.UserData as Structure)) != null) + { + attack.DoDamage(structure, item.SimPosition, 0.0f); + + //AmbientSoundManager.PlayDamageSound(DamageType.StructureBlunt, damage, f2.Body); + } + + item.body.FarseerBody.OnCollision -= OnProjectileCollision; + + item.body.FarseerBody.IsBullet = false; + item.body.CollisionCategories = Physics.CollisionMisc; + item.body.CollidesWith = Physics.CollisionWall; + + ignoredBodies.Clear(); + + if (doesStick) + { + Vector2 normal = contact.Manifold.LocalNormal; + Vector2 dir = new Vector2( + (float)Math.Cos(item.body.Rotation), + (float)Math.Sin(item.body.Rotation)); + + if (Vector2.Dot(f1.Body.LinearVelocity, normal)<0 ) return StickToTarget(f2.Body, dir); + } + + return true; + } + + private bool StickToTarget(Body targetBody, Vector2 axis) + { + if (stickJoint != null) return false; + + stickJoint = new PrismaticJoint(targetBody, item.body.FarseerBody, item.body.Position, axis, true); + stickJoint.MotorEnabled = true; + stickJoint.MaxMotorForce = 15.0f; + + stickJoint.LimitEnabled = true; + stickJoint.UpperLimit = ConvertUnits.ToSimUnits(item.sprite.size.X*0.7f); + + item.body.FarseerBody.IgnoreCollisionWith(targetBody); + stickTarget = targetBody; + Game1.world.AddJoint(stickJoint); + + isActive = true; + + return false; + } + } +} diff --git a/Subsurface/Items/Components/Pump.cs b/Subsurface/Items/Components/Pump.cs new file mode 100644 index 000000000..6ffffd386 --- /dev/null +++ b/Subsurface/Items/Components/Pump.cs @@ -0,0 +1,166 @@ +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 Subsurface.Items.Components +{ + class Pump : Powered + { + float flow; + float maxFlow; + + bool flowIn; + + Hull hull1, hull2; + + [HasDefaultValue(100.0f, false)] + private float MaxFlow + { + set { maxFlow = value; } + } + + public Pump(Item item, XElement element) + : base(item, element) + { + //maxFlow = ToolBox.GetAttributeFloat(element, "maxflow", 100.0f); + + item.linkedTo.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler( + delegate(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { GetHulls(); } + ); + } + + public override void Update(float deltaTime, Camera cam) + { + currPowerConsumption = powerConsumption; + + if (voltage < minVoltage) return; + + if (hull2 == null && hull1 == null) return; + + float deltaVolume = flow * ((flowIn) ? 1.0f : -1.0f); + hull1.Volume += deltaVolume; + if (hull2 != null) hull2.Volume -= deltaVolume; + + float powerFactor = (currPowerConsumption==0.0f) ? 1.0f : voltage; + flow = maxFlow * powerFactor; + + voltage = 0.0f; + } + + //public override void DrawHUD(SpriteBatch spriteBatch, Character character) + //{ + // int width = 300, height = 200; + // int x = Game1.GraphicsWidth / 2 - width / 2; + // int y = Game1.GraphicsHeight / 2 - height / 2; + + // GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black, true); + + // spriteBatch.DrawString(GUI.font, "Pumping direction: " + ((flowIn) ? "in" : "out"), new Vector2(x + 30, y + 30), Color.White); + // if (GUI.DrawButton(spriteBatch, new Rectangle(x + 30, y + 50, 80, 40), "TOGGLE")) flowIn = !flowIn; + + // if (GUI.DrawButton(spriteBatch, new Rectangle(x + 30, y + 150, 100, 40), (isActive) ? "TURN OFF" : "TURN ON")) IsActive = !isActive; + + //} + + //public override bool Pick(Character activator = null) + //{ + // //isActive = !isActive; + + // hull1 = null; + // hull2 = null; + + // foreach (MapEntity e in item.linkedTo) + // { + // Hull hull = e as Hull; + // if (hull == null) continue; + + // if (hull1 == null) + // { + // hull1 = hull; + // } + // else if (hull2 == null && hull != hull1) + // { + // hull2 = hull; + // break; + // } + // } + + // return true; + //} + + private void GetHulls() + { + hull1 = null; + hull2 = null; + + foreach (MapEntity e in item.linkedTo) + { + Hull hull = e as Hull; + if (hull == null) continue; + + if (hull1 == null) + { + hull1 = hull; + } + else if (hull2 == null && hull != hull1) + { + hull2 = hull; + break; + } + } + } + + //public override void OnMapLoaded() + //{ + // hull1 = null; + // hull2 = null; + + // foreach (MapEntity e in item.linkedTo) + // { + // Hull hull = e as Hull; + // if (hull == null) continue; + + // if (hull1 == null) + // { + // hull1 = hull; + // } + // else if (hull2 == null && hull != hull1) + // { + // hull2 = hull; + // break; + // } + // } + //} + + public override void ReceiveSignal(string signal, Connection connection, Item sender) + { + base.ReceiveSignal(signal, connection, sender); + + if (connection.name == "toggle") + { + isActive = !isActive; + } + else if (connection.name == "set_state") + { + isActive = (signal != "0"); + } + } + + public override void FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message) + { + message.Write(flowIn); + message.Write(isActive); + } + + public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetIncomingMessage message) + { + flowIn = message.ReadBoolean(); + isActive = message.ReadBoolean(); + } + } +} diff --git a/Subsurface/Items/Components/RangedWeapon.cs b/Subsurface/Items/Components/RangedWeapon.cs new file mode 100644 index 000000000..b82968b7f --- /dev/null +++ b/Subsurface/Items/Components/RangedWeapon.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using Microsoft.Xna.Framework; + +namespace Subsurface.Items.Components +{ + class RangedWeapon : ItemComponent + { + private float reload; + + private Vector2 barrelPos; + + //[Initable(new Vector2(0.0f, 0.0f))] + public Vector2 BarrelPos + { + get { return new Vector2(barrelPos.X * item.body.Dir, barrelPos.Y); } + } + + public Vector2 TransformedBarrelPos + { + get + { + Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation); + return (Vector2.Transform(BarrelPos, bodyTransform) + item.body.Position); + } + } + + public RangedWeapon(Item item, XElement element) + : base(item, element) + { + barrelPos = ToolBox.GetAttributeVector2(element, "barrelpos", Vector2.Zero); + barrelPos = ConvertUnits.ToSimUnits(barrelPos); + } + + public override void Update(float deltaTime, Camera cam) + { + reload -= deltaTime; + + if (reload < 0.0f) + { + reload = 0.0f; + isActive = false; + } + } + + public override bool Use(Character character = null) + { + if (character == null) return false; + if (!character.SecondaryKeyDown.State || reload > 0.0f) return false; + isActive = true; + reload = 1.0f; + + List limbBodies = new List(); + foreach (Limb l in character.animController.limbs) + { + limbBodies.Add(l.body.FarseerBody); + } + + Item[] containedItems = item.ContainedItems; + if (containedItems == null || containedItems.Count()==0) return false; + + foreach (Item projectile in containedItems) + { + if (projectile == null) continue; + //find the projectile-itemcomponent of the projectile, + //and add the limbs of the shooter to the list of bodies to be ignored + //so that the player can't shoot himself + Projectile projectileComponent= projectile.GetComponent(); + if (projectileComponent == null) continue; + + projectileComponent.ignoredBodies = limbBodies; + + projectile.body.ResetDynamics(); + projectile.SetTransform(TransformedBarrelPos, + (item.body.Dir == 1.0f) ? item.body.Rotation : item.body.Rotation - MathHelper.Pi); + + projectile.Use(); + item.RemoveContained(projectile); + + //recoil + item.body.ApplyLinearImpulse( + new Vector2((float)Math.Cos(projectile.body.Rotation), (float)Math.Sin(projectile.body.Rotation)) * item.body.Mass); + + Rope rope = item.GetComponent(); + if (rope != null) rope.Attach(projectile); + + return true; + } + + + return false; + + } + + } +} diff --git a/Subsurface/Items/Components/Reactor.cs b/Subsurface/Items/Components/Reactor.cs new file mode 100644 index 000000000..158c5e0c2 --- /dev/null +++ b/Subsurface/Items/Components/Reactor.cs @@ -0,0 +1,381 @@ +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using Lidgren.Network; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Subsurface.Networking; +using System.Globalization; + +namespace Subsurface.Items.Components +{ + class Reactor : Powered + { + //the rate at which the reactor is being run un + //higher rates generate more power (and heat) + float fissionRate; + + //the rate at which the heat is being dissipated + float coolingRate; + + float temperature; + + //is automatic temperature control on + //(adjusts the cooling rate automatically to keep the + //amount of power generated balanced with the load) + bool autoTemp; + + //the temperature after which fissionrate is automatically + //turned down and cooling increased + float shutDownTemp; + + float meltDownTemp; + + //how much power is provided to the grid per 1 temperature unit + float powerPerTemp; + + int graphSize = 25; + + float graphTimer; + + int updateGraphInterval = 500; + + float[] fissionRateGraph; + float[] coolingRateGraph; + float[] tempGraph; + + private PropertyTask powerUpTask; + + [Editable, HasDefaultValue(9500.0f, true)] + public float MeltDownTemp + { + get { return meltDownTemp; } + set + { + meltDownTemp = Math.Max(0.0f, value); + } + } + + [Editable, HasDefaultValue(1.0f, true)] + public float PowerPerTemp + { + get { return powerPerTemp; } + set + { + powerPerTemp = Math.Max(0.0f, value); + } + } + + public float FissionRate + { + get { return fissionRate; } + set { fissionRate = MathHelper.Clamp(value, 0.0f, 100.0f); } + } + + public float CoolingRate + { + get { return coolingRate; } + set { coolingRate = MathHelper.Clamp(value, 0.0f, 100.0f); } + } + + public float Temperature + { + get { return temperature; } + set { temperature = MathHelper.Clamp(value, 0.0f, 10000.0f); } + } + + public bool IsRunning() + { + return (temperature > 0.0f); + } + + public float ExtraCooling { get; set; } + + public Reactor(Item item, XElement element) + : base(item, element) + { + fissionRateGraph = new float[graphSize]; + coolingRateGraph = new float[graphSize]; + tempGraph = new float[graphSize]; + + meltDownTemp = 9000.0f; + + powerPerTemp = 1.0f; + + isActive = true; + } + + public override void Update(float deltaTime, Camera cam) + { + ApplyStatusEffects(ActionType.OnActive, deltaTime, null); + + + float heat = 100 * fissionRate; + float heatDissipation = 50 * coolingRate + ExtraCooling; + + float deltaTemp = (((heat - heatDissipation) * 5) - temperature) / 1000.0f; + Temperature = temperature + deltaTemp; + + if (temperature > meltDownTemp) + { + MeltDown(); + return; + } + else if (temperature==0.0f) + { + if (powerUpTask==null || powerUpTask.IsFinished) + { + powerUpTask = new PropertyTask(Game1.gameSession.taskManager, item, IsRunning, 20.0f, "Power up the reactor"); + } + } + + item.Condition -= temperature*deltaTime*0.00005f; + + if (temperature > shutDownTemp) + { + CoolingRate += 0.5f; + FissionRate -= 0.5f; + } + else if (autoTemp) + { + float load = 0.0f; + foreach (MapEntity e in item.linkedTo) + { + Item it = e as Item; + if (it == null) continue; + + PowerTransfer pt = it.GetComponent(); + if (pt != null) load += pt.PowerLoad; + } + + fissionRate = Math.Min(load / 200.0f, shutDownTemp); + //float target = Math.Min(targetTemp, load); + CoolingRate = coolingRate + (temperature - Math.Min(load, shutDownTemp) + deltaTemp)*0.1f; + } + + //fission rate can't be lowered below a certain amount if the core is too hot + FissionRate = Math.Max(fissionRate, heat / 200.0f); + + //the power generated by the reactor is equal to the temperature + currPowerConsumption = -temperature; + + if (item.currentHull != null) + { + //the sound can be heard from 20 000 display units away when everything running at 100% + item.currentHull.SoundRange += (coolingRate + fissionRate) * 100; + } + + UpdateGraph(deltaTime); + + ExtraCooling = 0.0f; + } + + public override void UpdateBroken(float deltaTime, Camera cam) + { + base.UpdateBroken(deltaTime, cam); + + Temperature -= deltaTime * 1000.0f; + FissionRate -= deltaTime * 10.0f; + CoolingRate -= deltaTime * 10.0f; + + currPowerConsumption = -temperature; + + UpdateGraph(deltaTime); + + ExtraCooling = 0.0f; + } + + private void UpdateGraph(float deltaTime) + { + graphTimer += deltaTime * 1000.0f; + + if (graphTimer > updateGraphInterval) + { + UpdateGraph(fissionRateGraph, fissionRate); + UpdateGraph(coolingRateGraph, coolingRate); + UpdateGraph(tempGraph, temperature); + graphTimer = 0.0f; + } + } + + private void MeltDown() + { + if (item.Condition <= 0.0f) return; + + new RepairTask(Game1.gameSession.taskManager, item, 50.0f, "Reactor meltdown!"); + item.Condition = 0.0f; + fissionRate = 0.0f; + coolingRate = 0.0f; + + new Explosion(item.SimPosition, 6.0f, 500.0f, 600.0f, 10.0f, 2.0f).Explode(); + + //List structureList = new List(); + + //float dist = 600.0f; + //foreach (MapEntity entity in MapEntity.mapEntityList) + //{ + // Structure structure = entity as Structure; + // if (structure == null) continue; + + // if (structure.HasBody && Vector2.Distance(structure.Position, item.Position) 0.0f) structure.AddDamage(i, damage); + // } + //} + + //if (item.currentHull!=null) + //{ + // item.currentHull.WaveVel[item.currentHull.GetWaveIndex(item.SimPosition)] = 100.0f; + //} + + if (item.ContainedItems!=null) + { + foreach (Item containedItem in item.ContainedItems) + { + if (containedItem == null) continue; + containedItem.Condition = 0.0f; + } + } + + + } + + public override bool Pick(Character picker) + { + if (picker == null) return false; + + //picker.SelectedConstruction = (picker.SelectedConstruction==item) ? null : item; + + return true; + } + + public override void Draw(SpriteBatch spriteBatch) + { + base.Draw(spriteBatch); + + GUI.DrawRectangle(spriteBatch, + new Vector2(item.Rect.X + item.Rect.Width / 2 - 6, -item.Rect.Y + 29), + new Vector2(12, 42), Color.Black); + + if (temperature > 0) + GUI.DrawRectangle(spriteBatch, + new Vector2(item.Rect.X + item.Rect.Width / 2 - 5, -item.Rect.Y + 30 + (40.0f * (1.0f - temperature / 10000.0f))), + new Vector2(10, 40 * (temperature / 10000.0f)), new Color(temperature / 10000.0f, 1.0f - (temperature / 10000.0f), 0.0f, 1.0f), true); + } + + + public override void DrawHUD(SpriteBatch spriteBatch, Character character) + { + isActive = true; + + int width = 500, height = 420; + int x = Game1.GraphicsWidth / 2 - width / 2; + int y = Game1.GraphicsHeight / 2 - height / 2 - 50; + + float xOffset = (graphTimer / (float)updateGraphInterval); + + GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black, true); + + spriteBatch.DrawString(GUI.font, "Temperature: " + (int)temperature + " C", new Vector2(x + 30, y + 30), Color.White); + DrawGraph(tempGraph, spriteBatch, x + 30, y + 50, 10000.0f, xOffset); + + y += 130; + + spriteBatch.DrawString(GUI.font, "Fission rate: " + (int)fissionRate + " %", new Vector2(x + 30, y + 30), Color.White); + DrawGraph(fissionRateGraph, spriteBatch, x + 30, y + 50, 100.0f, xOffset); + + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 280, y + 30, 40, 40), "+", true)) FissionRate += 1.0f; + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 280, y + 80, 40, 40), "-", true)) FissionRate -= 1.0f; + + y += 130; + + spriteBatch.DrawString(GUI.font, "Cooling rate: " + (int)coolingRate + " %", new Vector2(x + 30, y + 30), Color.White); + DrawGraph(coolingRateGraph, spriteBatch, x + 30, y + 50, 100.0f, xOffset); + + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 280, y + 30, 40, 40), "+", true)) CoolingRate += 1.0f; + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 280, y + 80, 40, 40), "-", true)) CoolingRate -= 1.0f; + + y = y - 260; + + spriteBatch.DrawString(GUI.font, "Autotemp: " + ((autoTemp) ? "ON" : "OFF"), new Vector2(x + 400, y + 30), Color.White); + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 400, y + 60, 100, 40), ((autoTemp) ? "TURN OFF" : "TURN ON"))) autoTemp = !autoTemp; + + spriteBatch.DrawString(GUI.font, "Max temperature: " + shutDownTemp, new Vector2(x + 400, y + 150), Color.White); + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 400, y + 180, 40, 40), "+", true)) shutDownTemp += 100.0f; + if (GUI.DrawButton(spriteBatch, new Rectangle(x + 450, y + 180, 40, 40), "-", true)) shutDownTemp -= 100.0f; + + item.NewComponentEvent(this, true); + + + } + + static void UpdateGraph(IList graph, T newValue) + { + for (int i = graph.Count - 1; i > 0; i--) + { + graph[i] = graph[i - 1]; + } + graph[0] = newValue; + } + + static void DrawGraph(IList graph, SpriteBatch spriteBatch, int x, int y, float maxVal, float xOffset) + { + int width = 200; + int height = 100; + + float lineWidth = (float)width / (float)(graph.Count - 2); + float yScale = (float)height / maxVal; + + GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.White); + + Vector2 prevPoint = new Vector2(x, y + height - (graph[1] + (graph[0] - graph[1]) * xOffset) * yScale); + + float currX = x + ((xOffset - 1.0f) * lineWidth); + + for (int i = 1; i < graph.Count - 1; i++) + { + currX += lineWidth; + + Vector2 newPoint = new Vector2(currX, y + height - graph[i] * yScale); + + GUI.DrawLine(spriteBatch, prevPoint, newPoint, Color.White); + + prevPoint = newPoint; + } + + Vector2 lastPoint = new Vector2(x + width, + y + height - (graph[graph.Count - 1] + (graph[graph.Count - 2] - graph[graph.Count - 1]) * xOffset) * yScale); + + GUI.DrawLine(spriteBatch, prevPoint, lastPoint, Color.Red); + } + + public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message) + { + message.Write(autoTemp); + message.Write(temperature); + message.Write(shutDownTemp); + + message.Write(coolingRate); + message.Write(fissionRate); + } + + public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) + { + autoTemp = message.ReadBoolean(); + temperature = message.ReadFloat(); + shutDownTemp = message.ReadFloat(); + + coolingRate = message.ReadFloat(); + fissionRate = message.ReadFloat(); + } + } +} diff --git a/Subsurface/Items/Components/RepairTool.cs b/Subsurface/Items/Components/RepairTool.cs new file mode 100644 index 000000000..be095919f --- /dev/null +++ b/Subsurface/Items/Components/RepairTool.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface.Items.Components +{ + class RepairTool : ItemComponent + { + List fixableEntities; + + float range; + + Vector2 pickedPosition; + + float structureFixAmount, limbFixAmount; + + [HasDefaultValue(100.0f, false)] + private float Range + { + set { range = ConvertUnits.ToSimUnits(value); } + } + + [HasDefaultValue(1.0f, false)] + private float StructureFixAmount + { + set { structureFixAmount = value; } + } + + [HasDefaultValue(1.0f, false)] + private float LimbFixAmount + { + set { limbFixAmount = value; } + } + + public RepairTool(Item item, XElement element) + : base(item, element) + { + this.item = item; + + //range = ToolBox.GetAttributeFloat(element, "range", 100.0f); + //range = ConvertUnits.ToSimUnits(range); + + //structureFixAmount = ToolBox.GetAttributeFloat(element, "structurefixamount", 1.0f); + //limbFixAmount = ToolBox.GetAttributeFloat(element, "limbfixamount", -0.5f); + + + fixableEntities = new List(); + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLower()) + { + case "fixable": + fixableEntities.Add(subElement.Attribute("name").Value); + break; + } + } + } + + //public override void Update(float deltaTime, Camera cam) + //{ + // base.Update(deltaTime, cam); + + //} + + public override bool Use(Character character = null) + { + if (character == null) return false; + + Vector2 targetPosition = item.body.Position; + //targetPosition = targetPosition.X, -targetPosition.Y); + + targetPosition += new Vector2( + (float)Math.Cos(item.body.Rotation) * range, + (float)Math.Sin(item.body.Rotation) * range) * item.body.Dir; + + List ignoredBodies = new List(); + foreach (Limb limb in character.animController.limbs) + { + ignoredBodies.Add(limb.body.FarseerBody); + } + + + Body targetBody = Map.PickBody(item.body.Position, targetPosition, ignoredBodies); + pickedPosition = Map.LastPickedPosition; + + if (targetBody==null || targetBody.UserData==null) return false; + + ApplyStatusEffects(ActionType.OnUse, 1.0f, character); + + + Structure targetStructure; + Limb targetLimb; + Item targetItem; + if ((targetStructure = (targetBody.UserData as Structure)) != null) + { + if (!fixableEntities.Contains(targetStructure.Name)) return false; + + int sectionIndex = targetStructure.FindSectionIndex(ConvertUnits.ToDisplayUnits(pickedPosition)); + if (sectionIndex < 0) return false; + + targetStructure.HighLightSection(sectionIndex); + + if (character.SecondaryKeyDown.State) + { + targetStructure.AddDamage(sectionIndex, -structureFixAmount); + isActive = true; + } + + } + else if ((targetLimb = (targetBody.UserData as Limb)) != null) + { + if (character.SecondaryKeyDown.State) + { + targetLimb.Damage -= limbFixAmount; + isActive = true; + } + } + else if ((targetItem = (targetBody.UserData as Item)) !=null) + { + targetItem.Condition -= structureFixAmount; + } + + return true; + } + + public override void Draw(SpriteBatch spriteBatch) + { + if (!isActive) return; + + Vector2 startPos = ConvertUnits.ToDisplayUnits(item.body.Position); + Vector2 endPos = ConvertUnits.ToDisplayUnits(pickedPosition); + endPos = new Vector2(endPos.X + Game1.localRandom.Next(-2, 2), endPos.Y + Game1.localRandom.Next(-2, 2)); + + GUI.DrawLine(spriteBatch, startPos, endPos, Color.Orange, 0.0f); + + isActive = false; + } + + + } +} diff --git a/Subsurface/Items/Components/Rope.cs b/Subsurface/Items/Components/Rope.cs new file mode 100644 index 000000000..561dbbdf4 --- /dev/null +++ b/Subsurface/Items/Components/Rope.cs @@ -0,0 +1,335 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Collision.Shapes; +using FarseerPhysics.Common; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Dynamics.Joints; +using FarseerPhysics.Factories; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface.Items.Components +{ + class Rope : ItemComponent + { + PhysicsBody[] ropeBodies; + RevoluteJoint[] ropeJoints; + + DistanceJoint gunJoint; + + float pullForce; + + Sprite sprite; + + float reload; + + float prevDir; + + float sectionLength; + + Item projectile; + + Vector2 projectileAnchor; + + private Vector2 BarrelPos + { + get + { + Vector2 barrelPos = Vector2.Zero; + + RangedWeapon weapon = item.GetComponent(); + if (weapon != null) barrelPos = weapon.BarrelPos; + + return barrelPos; + } + } + + private Vector2 TransformedBarrelPos + { + get + { + Vector2 barrelPos = Vector2.Zero; + + RangedWeapon weapon = item.GetComponent(); + if (weapon != null) barrelPos = weapon.TransformedBarrelPos; + + return barrelPos; + } + } + + + public Rope(Item item, XElement element) + : base(item, element) + { + string spritePath = ToolBox.GetAttributeString(element, "sprite", ""); + if (spritePath == "") DebugConsole.ThrowError("Sprite "+spritePath+" in "+element+" not found!"); + + float length = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "length", 200.0f)); + + pullForce = ToolBox.GetAttributeFloat(element, "pullforce", 10.0f); + + projectileAnchor = Vector2.Zero; + projectileAnchor.X = ToolBox.GetAttributeFloat(element, "projectileanchorx", 0.0f); + projectileAnchor.Y = ToolBox.GetAttributeFloat(element, "projectileanchory", 0.0f); + projectileAnchor = ConvertUnits.ToSimUnits(projectileAnchor); + + characterUsable = ToolBox.GetAttributeBool(element, "characterusable", false); + + sprite = new Sprite(spritePath, new Vector2(0.5f,0.5f)); + sectionLength = ConvertUnits.ToSimUnits(sprite.size.X); + + + Path ropePath = new Path(); + ropePath.Add(item.body.Position); + ropePath.Add(item.body.Position + new Vector2(length, 0.0f)); + ropePath.Closed = false; + + Vertices box = PolygonTools.CreateRectangle(sectionLength, 0.05f); + PolygonShape shape = new PolygonShape(box, 5); + + ListropeList = PathManager.EvenlyDistributeShapesAlongPath(Game1.world, ropePath, shape, BodyType.Dynamic, (int)(length/sectionLength)); + + ropeBodies = new PhysicsBody[ropeList.Count()]; + for (int i = 0; i joints = PathManager.AttachBodiesWithRevoluteJoint(Game1.world, ropeList, + new Vector2(-sectionLength/2, 0.0f), new Vector2(sectionLength/2, 0.0f), false, false); + + ropeJoints = new RevoluteJoint[joints.Count+1]; + //ropeJoints[0] = JointFactory.CreateRevoluteJoint(Game1.world, item.body, ropeList[0], new Vector2(0f, -0.0f)); + for (int i = 0; i < joints.Count; i++) + { + var distanceJoint = JointFactory.CreateDistanceJoint(Game1.world, ropeList[i], ropeList[i + 1]); + + distanceJoint.Length = sectionLength; + distanceJoint.DampingRatio = 1.0f; + ropeJoints[i] = joints[i]; + } + + } + + public override void SecondaryUse(Character character = null) + { + if (reload > 0.0f) return; + + bool first = true; + for (int i = 0; i < ropeBodies.Length - 1; i++) + { + if (ropeBodies[i].UserData == null || (bool)ropeBodies[i].UserData) continue; + + if (first) + { + Vector2 dist = gunJoint.WorldAnchorA - ropeJoints[i].WorldAnchorA; + float length = dist.Length(); + + if (gunJoint.Length < 0.011 && length*0.5f0.0f) reload -= deltaTime; + + //for (int i = 0; i < ropeBodies.Length - 1; i++) + //{ + // if (ropeBodies[i].UserData == null || (bool)ropeBodies[i].UserData == true) continue; + // ropeBodies[i].SmoothRotate(item.body.Rotation); + //} + + int len = 1; + for (int i = 0; i < ropeBodies.Length - 1; i++) + { + if (ropeBodies[i].UserData == null || (bool)ropeBodies[i].UserData) continue; + len++; + } + + if (Vector2.Distance(TransformedBarrelPos, projectile.SimPosition)>len*sectionLength) + { + Vector2 stopForce = projectile.SimPosition - ropeBodies[ropeBodies.Length-1].Position; + stopForce = Vector2.Normalize(stopForce); + + float dotProduct = Vector2.Dot(stopForce, Vector2.Normalize(projectile.body.LinearVelocity)); + + if (dotProduct<0) + projectile.body.ApplyLinearImpulse(-stopForce*dotProduct * projectile.body.LinearVelocity.Length() * projectile.body.Mass); + } + + if (item.body.Dir!=prevDir) + { + gunJoint.LocalAnchorA = + new Vector2( + -gunJoint.LocalAnchorA.X, + BarrelPos.Y); + + prevDir = -prevDir; + } + + if (!projectile.body.Enabled || !item.body.Enabled) + { + //attempt to recontain the projectile in the launcher + //eq automatically reload a spear into a speargun when picking the spear up + if (!projectile.body.Enabled) item.Combine(projectile); + + foreach (PhysicsBody b in ropeBodies) + { + b.Enabled = false; + } + foreach (var joint in ropeJoints) + { + if (joint != null) joint.Enabled = false; + } + isActive = false; + } + } + + public override void Draw(SpriteBatch spriteBatch) + { + base.Draw(spriteBatch); + + if (!isActive) return; + + RevoluteJoint firstJoint = null; + + for (int i = 0; i(); + + foreach (PhysicsBody b in ropeBodies) + { + b.SetTransform(item.body.Position, 0.0f); + b.UserData = false; + b.Enabled = true; + } + + foreach (var joint in ropeJoints) + { + if (joint!=null) joint.Enabled = true; + } + + ropeBodies[ropeBodies.Length - 1].SetTransform(projectile.body.Position, projectile.body.Rotation); + + //attach projectile to the last section of the rope + if (ropeJoints[ropeJoints.Length-1] != null) Game1.world.RemoveJoint(ropeJoints[ropeJoints.Length-1]); + ropeJoints[ropeJoints.Length - 1] = JointFactory.CreateRevoluteJoint(Game1.world, + projectile.body.FarseerBody, ropeBodies[ropeBodies.Length - 1].FarseerBody, + projectileAnchor, new Vector2(sectionLength / 2, 0.0f)); + + AttachGunJoint(ropeBodies[0].FarseerBody); + + prevDir = item.body.Dir; + } + + private void AttachGunJoint(Body body) + { + float rotation = (item.body.Dir == -1.0f) ? item.body.Rotation - MathHelper.Pi : item.body.Rotation; + body.SetTransform(TransformedBarrelPos, rotation); + Vector2 axis = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)); + + if (gunJoint != null) Game1.world.RemoveJoint(gunJoint); + gunJoint = new DistanceJoint(item.body.FarseerBody, body, BarrelPos, + new Vector2(sectionLength / 2, 0.0f)); + + gunJoint.Length = sectionLength; + + //gunJoint.LocalAnchorA = BarrelPos; + //gunJoint.LocalAnchorB = new Vector2(sectionLength / 2, 0.0f); + //gunJoint.UpperLimit = sectionLength; + //gunJoint.LowerLimit = 0.0f; + //gunJoint.LimitEnabled = true; + //gunJoint.ReferenceAngle = 0.0f; + + Game1.world.AddJoint(gunJoint); + } + + } +} diff --git a/Subsurface/Items/Components/Signal/AndComponent.cs b/Subsurface/Items/Components/Signal/AndComponent.cs new file mode 100644 index 000000000..cc2dd9a09 --- /dev/null +++ b/Subsurface/Items/Components/Signal/AndComponent.cs @@ -0,0 +1,77 @@ +using System; +using System.Globalization; +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class AndComponent : ItemComponent + { + protected string output; + + //an array to keep track of how long ago a non-zero signal was received on both inputs + protected float[] timeSinceReceived; + + //the output is sent if both inputs have received a signal within the timeframe + protected float timeFrame; + + [InGameEditable, HasDefaultValue(0.1f, true)] + public float TimeFrame + { + get { return timeFrame; } + set + { + timeFrame = Math.Max(0.0f, value); + } + } + + [InGameEditable, HasDefaultValue("1", true)] + public string Output + { + get { return output; } + set { output = value; } + } + + public AndComponent(Item item, XElement element) + : base (item, element) + { + timeSinceReceived = new float[] { timeFrame*2.0f, timeFrame*2.0f}; + + //output = "1"; + } + + public override void Update(float deltaTime, Camera cam) + { + bool sendOutput = true; + for (int i = 0; i timeFrame) sendOutput = false; + timeSinceReceived[i] += deltaTime; + } + + if (sendOutput) + { + item.SendSignal(output, "signal_out"); + } + } + + public override void ReceiveSignal(string signal, Connection connection, Item sender) + { + switch (connection.name) + { + case "signal_in1": + if (signal == "0") return; + timeSinceReceived[0] = 0.0f; + isActive = true; + break; + case "signal_in2": + if (signal == "0") return; + timeSinceReceived[1] = 0.0f; + isActive = true; + break; + case "set_output": + output = signal; + break; + } + } + } +} diff --git a/Subsurface/Items/Components/Signal/Connection.cs b/Subsurface/Items/Components/Signal/Connection.cs new file mode 100644 index 000000000..613ac367c --- /dev/null +++ b/Subsurface/Items/Components/Signal/Connection.cs @@ -0,0 +1,434 @@ +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 Subsurface.Items.Components +{ + + class Connection + { + private static Sprite connector; + private static Sprite wireCorner, wireVertical, wireHorizontal; + + //how many wires can be linked to a single connector + private const int MaxLinked = 5; + + public readonly string name; + + public Wire[] wires; + + private Item item; + + public readonly bool isOutput; + + private static Item draggingConnected; + + int[] wireId; + + public List Recipients + { + get + { + List recipients = new List(); + for (int i = 0; i-1) + { + Inventory.draggingItem = c.wires[linkIndex].Item; + } + } + + //outputs are drawn at the right side of the panel, inputs at the left + if (c.isOutput) + { + c.Draw(spriteBatch, panel.Item, rightPos, + new Vector2(rightPos.X + 20, rightPos.Y), + new Vector2(rightWireX, y + height), mouseInRect); + + rightPos.Y += 30; + rightWireX += wireInterval; + } + else + { + c.Draw(spriteBatch, panel.Item, leftPos, + new Vector2(leftPos.X - 100, leftPos.Y), + new Vector2(leftWireX, y + height), mouseInRect); + + leftPos.Y += 30; + leftWireX -= wireInterval; + } + } + + + //draw a wire for all the items that are linked to this item, but not to any of the signal connections + //foreach (MapEntity entity in panel.Item.linkedTo) + //{ + // Item linked = entity as Item; + // if (linked == null) continue; + + // //if the item is already connected, don't draw it again + // if (panel.connections.Find(c => c.linked.Contains()) != null) continue; + + // DrawWire(spriteBatch, false, linked, + // new Vector2(leftPos.X + (leftPos.Y - y), y + height- 50), + // new Vector2(leftPos.X + (leftPos.Y - y), y + height), mouseInRect); + + + + + + // leftPos.Y += 30.0f; + //} + + //if the character using the panel has a wire item equipped + //and the wire hasn't been connected yet, draw it on the panel + + + for (int i = 0; i < character.SelectedItems.Length; i++ ) + { + Item selectedItem = character.SelectedItems[i]; + + if (selectedItem == null) continue; + + Wire wireComponent = selectedItem.GetComponent(); + + if (wireComponent != null && + panel.connections.Find(c => c.wires.Contains(wireComponent)) == null) + { + DrawWire(spriteBatch, selectedItem, selectedItem, + new Vector2(x + width / 2, y + height - 100), + new Vector2(x + width / 2, y + height), mouseInRect); + + if (draggingConnected == selectedItem) Inventory.draggingItem = selectedItem; + + break; + } + } + + //stop dragging a wire item if cursor is outside the panel + if (mouseInRect) Inventory.draggingItem = null; + + if (draggingConnected != null) + { + if (!PlayerInput.LeftButtonDown()) + { + panel.Item.NewComponentEvent(panel, true); + draggingConnected = null; + } + } + } + + private void Draw(SpriteBatch spriteBatch, Item item, Vector2 position, Vector2 labelPos, Vector2 wirePosition, bool mouseIn) + { + + spriteBatch.DrawString(GUI.font, name, new Vector2(labelPos.X, labelPos.Y-10), Color.White); + + GUI.DrawRectangle(spriteBatch, new Rectangle((int)position.X-10, (int)position.Y-10, 20, 20), Color.White); + + + for (int i = 0; i see if the wire can be connected to this connection + if (draggingConnected != null + && !PlayerInput.LeftButtonDown()) + { + //close enough to the connector -> make a new connection + if (Vector2.Distance(position, PlayerInput.MousePosition) < 10.0f) + { + //find an empty cell for the new connection + int index = FindWireIndex(null); + + Wire wireComponent = draggingConnected.GetComponent(); + + if (index>-1 && wireComponent!=null && !wires.Contains(wireComponent)) + { + wires[index] = wireComponent; + wireComponent.Connect(this); + } + } + //far away -> disconnect if the wire is linked to this connector + else + { + int index = FindWireIndex(draggingConnected); + if (index>-1) + { + wires[index].RemoveConnection(this); + wires[index] = null; + } + } + } + + + } + + private static void DrawWire(SpriteBatch spriteBatch, Item wireItem, Item item, Vector2 end, Vector2 start, bool mouseIn) + { + if (draggingConnected == wireItem) + { + if (!mouseIn) return; + end = PlayerInput.MousePosition; + } + else if (draggingConnected == null) + { + if (Vector2.Distance(end, PlayerInput.MousePosition)<20.0f) + { + item.IsHighlighted = true; + //start dragging the wire + if (PlayerInput.LeftButtonDown()) draggingConnected = wireItem; + } + } + + + int textX = (int)start.X; + float connLength = 10.0f; + + if (Math.Abs(end.X-start.X) start.X) ? -1.0f : 1.0f; + + wireCorner.Draw(spriteBatch, + new Vector2(start.X, end.Y), 0.0f, 1.0f, + (end.X > start.X) ? SpriteEffects.None : SpriteEffects.FlipHorizontally); + + float wireStartX = start.X - wireCorner.size.X / 2 * dir; + float wireEndX = end.X + connLength * dir; + + wireHorizontal.DrawTiled(spriteBatch, new Vector2(Math.Min(wireStartX,wireEndX), end.Y - wireVertical.size.Y / 2), + new Vector2(Math.Abs(wireStartX - wireEndX), wireHorizontal.size.Y), Color.White); + + + connector.Draw(spriteBatch, end, -MathHelper.PiOver2*dir); + } + + spriteBatch.DrawString(GUI.font, item.Name, + new Vector2(textX, start.Y-30), + Color.White, + MathHelper.PiOver2, + GUI.font.MeasureString(item.Name)*0.5f, + 1.0f, SpriteEffects.None, 0.0f); + } + + public void Save(XElement parentElement) + { + XElement newElement = new XElement(isOutput ? "output" : "input", new XAttribute("name", name)); + + for (int i = 0; i < MaxLinked; i++ ) + { + if (wires[i] == null) continue; + + Connection recipient = wires[i].OtherConnection(this); + + //int connectionIndex = recipient.item.Connections.FindIndex(x => x == recipient); + newElement.Add(new XElement("link", + new XAttribute("w", (wires[i] == null) ? "-1" : wires[i].Item.ID.ToString()))); + } + + parentElement.Add(newElement); + } + + + + public void ConnectLinked() + { + if (wireId == null) return; + + for (int i = 0; i < MaxLinked; i++) + { + if (wireId[i] == -1) continue; + + Item wireItem = MapEntity.FindEntityByID(wireId[i]) as Item; + + if (wireItem == null) continue; + + wires[i] = wireItem.GetComponent(); + + if (wires[i]!=null) + { + wires[i].Item.body.Enabled = false; + wires[i].Connect(this, false); + } + } + + wireId = null; + } + + } +} diff --git a/Subsurface/Items/Components/Signal/ConnectionPanel.cs b/Subsurface/Items/Components/Signal/ConnectionPanel.cs new file mode 100644 index 000000000..1724371b9 --- /dev/null +++ b/Subsurface/Items/Components/Signal/ConnectionPanel.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class ConnectionPanel : ItemComponent + { + + public List connections; + + Character user; + + public ConnectionPanel(Item item, XElement element) + : base(item, element) + { + connections = new List(); + + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString()) + { + case "input": + connections.Add(new Connection(subElement, item)); + break; + case "output": + connections.Add(new Connection(subElement, item)); + break; + } + } + } + + public override void DrawHUD(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Character character) + { + if (user!=character) return; + Connection.DrawConnections(spriteBatch, this, character); + } + + public override XElement Save(XElement parentElement) + { + XElement componentElement = base.Save(parentElement); + + foreach (Connection c in connections) + { + //XElement newElement = new XElement(c.isOutput ? "output" : "input", new XAttribute("name", c.name)); + + c.Save(componentElement); + } + + return componentElement; + } + + public override void OnMapLoaded() + { + foreach (Connection c in connections) + { + c.ConnectLinked(); + } + } + + public override void Update(float deltaTime, Camera cam) + { + if (user != null && user.SelectedConstruction != item) user = null; + } + + public override bool Pick(Character picker) + { + user = picker; + isActive = true; + return true; + } + + public override void Load(XElement element) + { + base.Load(element); + + connections.Clear(); + + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString()) + { + case "input": + connections.Add(new Connection(subElement, item)); + break; + case "output": + connections.Add(new Connection(subElement, item)); + break; + } + } + } + + public override void FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message) + { + foreach (Connection c in connections) + { + int wireCount = c.wires.Length; + for (int i = 0 ; i < wireCount; i++) + { + message.Write(c.wires[i]==null ? -1 : c.wires[i].Item.ID); + } + } + } + + public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetIncomingMessage message) + { + System.Diagnostics.Debug.WriteLine("connectionpanel update"); + foreach (Connection c in connections) + { + int wireCount = c.wires.Length; + c.ClearConnections(); + + for (int i = 0; i < wireCount; i++) + { + int wireId = message.ReadInt32(); + if (wireId == -1) continue; + + Item wireItem = MapEntity.FindEntityByID(wireId) as Item; + if (wireItem == null) continue; + + Wire wireComponent = wireItem.GetComponent(); + if (wireComponent == null) continue; + + c.wires[i] = wireComponent; + wireComponent.Connect(c, false); + } + } + } + } +} diff --git a/Subsurface/Items/Components/Signal/LightComponent.cs b/Subsurface/Items/Components/Signal/LightComponent.cs new file mode 100644 index 000000000..4c311b4b8 --- /dev/null +++ b/Subsurface/Items/Components/Signal/LightComponent.cs @@ -0,0 +1,58 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class LightComponent : ItemComponent + { + private Color lightColor; + + private Sprite sprite; + + [InGameEditable, HasDefaultValue("1.0,1.0,1.0,1.0", true)] + public string LightColor + { + get { return ToolBox.Vector4ToString(lightColor.ToVector4()); } + set + { + lightColor = new Color(ToolBox.ParseToVector4(value)); + } + } + + public LightComponent(Item item, XElement element) + : base (item, element) + { + foreach (XElement subElement in element.Elements()) + { + if (subElement.Name.ToString().ToLower() != "sprite") continue; + sprite = new Sprite(subElement); + break; + } + + //lightColor = new Color(ToolBox.GetAttributeVector4(element, "color", Vector4.One)); + } + + public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) + { + if (!isActive || sprite==null) return; + sprite.Draw(spriteBatch, new Vector2(item.Position.X, -item.Position.Y), 0.0f, 1.0f, Microsoft.Xna.Framework.Graphics.SpriteEffects.None); + } + + public override void ReceiveSignal(string signal, Connection connection, Item sender) + { + switch (connection.name) + { + case "toggle": + isActive = !isActive; + break; + case "set_state": + isActive = (signal == "0") ? false : true; + break; + } + } + } +} diff --git a/Subsurface/Items/Components/Signal/NotComponent.cs b/Subsurface/Items/Components/Signal/NotComponent.cs new file mode 100644 index 000000000..c15aaff47 --- /dev/null +++ b/Subsurface/Items/Components/Signal/NotComponent.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class NotComponent : ItemComponent + { + public NotComponent(Item item, XElement element) + : base (item, element) + { + } + + public override void ReceiveSignal(string signal, Connection connection, Item sender) + { + if (connection.name != "signal_in") return; + + item.SendSignal(signal=="0" ? "1" : "0", "signal_out", item); + } + } +} diff --git a/Subsurface/Items/Components/Signal/OrComponent.cs b/Subsurface/Items/Components/Signal/OrComponent.cs new file mode 100644 index 000000000..335096ff5 --- /dev/null +++ b/Subsurface/Items/Components/Signal/OrComponent.cs @@ -0,0 +1,27 @@ +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class OrComponent : AndComponent + { + public OrComponent(Item item, XElement element) + : base (item, element) + { + } + + public override void Update(float deltaTime, Camera cam) + { + bool sendOutput = true; + for (int i = 0; i timeFrame) sendOutput = false; + timeSinceReceived[i] += deltaTime; + } + + if (sendOutput) + { + item.SendSignal(output, "signal_out"); + } + } + } +} diff --git a/Subsurface/Items/Components/Signal/OxygenDetector.cs b/Subsurface/Items/Components/Signal/OxygenDetector.cs new file mode 100644 index 000000000..655584f02 --- /dev/null +++ b/Subsurface/Items/Components/Signal/OxygenDetector.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class OxygenDetector : ItemComponent + { + private Hull hull; + + public OxygenDetector(Item item, XElement element) + : base (item, element) + { + hull = Hull.FindHull(item.Position); + + isActive = true; + } + + public override void OnMapLoaded() + { + hull = Hull.FindHull(item.Position); + } + + public override void Move(Microsoft.Xna.Framework.Vector2 amount) + { + hull = Hull.FindHull(item.Position); + } + + public override void Update(float deltaTime, Camera cam) + { + if (hull == null) return; + + item.SendSignal(((int)hull.OxygenPercentage).ToString(), "signal_out"); + + } + + } +} diff --git a/Subsurface/Items/Components/Signal/RegExFindComponent.cs b/Subsurface/Items/Components/Signal/RegExFindComponent.cs new file mode 100644 index 000000000..2b7847a40 --- /dev/null +++ b/Subsurface/Items/Components/Signal/RegExFindComponent.cs @@ -0,0 +1,67 @@ +using System.Xml.Linq; +using System.Text.RegularExpressions; + +namespace Subsurface.Items.Components +{ + class RegExFindComponent : ItemComponent + { + private string output; + + private string expression; + + [InGameEditable, HasDefaultValue("1", true)] + public string Output + { + get { return output; } + set { output = value; } + } + + [InGameEditable, HasDefaultValue("", true)] + public string Expression + { + get { return expression; } + set { expression = value; } + } + + public RegExFindComponent(Item item, XElement element) + : base(item, element) + { + } + + public override void ReceiveSignal(string signal, Connection connection, Item sender) + { + switch (connection.name) + { + case "signal_in": + if (string.IsNullOrWhiteSpace(expression)) return; + + bool success = false; + try + { + Regex regex = new Regex(@expression); + Match match = regex.Match(signal); + success = match.Success; + } + catch + { + item.SendSignal("ERROR", "signal_out", item); + return; + } + + if (success) + { + item.SendSignal(output, "signal_out", item); + } + else + { + item.SendSignal("0", "signal_out", item); + } + + break; + case "set_output": + output = signal; + break; + } + } + } +} diff --git a/Subsurface/Items/Components/Signal/Wire.cs b/Subsurface/Items/Components/Signal/Wire.cs new file mode 100644 index 000000000..7ec9fb32a --- /dev/null +++ b/Subsurface/Items/Components/Signal/Wire.cs @@ -0,0 +1,294 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class Wire : ItemComponent + { + const float nodeDistance = 128.0f; + + static Sprite wireSprite; + + List nodes; + + Connection[] connections; + + public Wire(Item item, XElement element) + : base(item, element) + { + if (wireSprite==null) + { + wireSprite = new Sprite("Content/Items/wireHorizontal.png",new Vector2(0.5f,0.5f)); + wireSprite.Depth = 0.85f; + } + + nodes = new List(); + + connections = new Connection[2]; + } + + public Connection OtherConnection(Connection connection) + { + if (connection==null) return null; + if (connection==connections[0]) return connections[1]; + if (connection==connections[1]) return connections[0]; + + return null; + } + + public void RemoveConnection(Connection connection) + { + if (connection == connections[0]) connections[0] = null; + if (connection == connections[1]) connections[1] = null; + } + + public void Connect(Connection newConnection, bool addNode = true) + { + for (int i = 0; i < 2; i++) + { + if (connections[i] == newConnection) return; + } + + for (int i = 0; i < 2; i++) + { + if (connections[i] != null) continue; + + connections[i] = newConnection; + + if (!addNode) break; + + if (i==0) + { + nodes.Insert(0, newConnection.Item.Position); + } + else + { + nodes.Add(newConnection.Item.Position); + } + + + break; + } + + if (connections[0]!=null && connections[1]!=null) + { + item.Drop(null, false); + item.body.Enabled = false; + + CleanNodes(); + } + + //new Networking.NetworkEvent(item.ID, true); + + } + + public override void Equip(Character character) + { + ClearConnections(); + + isActive = true; + } + + public override void Unequip(Character character) + { + ClearConnections(); + } + + public override void Update(float deltaTime, Camera cam) + { + if (nodes.Count == 0) return; + + if (Math.Abs(item.Position.X-nodes[nodes.Count-1].X)>nodeDistance) + { + nodes.Add(new Vector2( + ToolBox.Round(item.Position.X, Map.gridSize.X), + nodes[nodes.Count - 1].Y)); + + item.NewComponentEvent(this, true); + } + else if (Math.Abs(item.Position.Y-nodes[nodes.Count-1].Y)>nodeDistance) + { + nodes.Add(new Vector2(nodes[nodes.Count - 1].X, + ToolBox.Round(item.Position.Y, Map.gridSize.Y))); + + item.NewComponentEvent(this, true); + } + } + + //public override bool Use(Character character = null) + //{ + // Vector2 nodePos = item.Position; + // ToolBox.Round(nodePos.X, Map.gridSize.X); + // ToolBox.Round(nodePos.Y, Map.gridSize.Y); + + // nodes.Add(nodePos); + + // return true; + //} + + public override void SecondaryUse(Character character = null) + { + if (nodes.Count > 0) + { + nodes.RemoveAt(nodes.Count - 1); + item.NewComponentEvent(this, true); + } + } + + public override bool Pick(Character picker) + { + ClearConnections(); + + return true; + } + + private void ClearConnections() + { + nodes.Clear(); + + for (int i = 0; i < 2; i++ ) + { + if (connections[i] == null) continue; + int wireIndex = connections[i].FindWireIndex(item); + + if (wireIndex == -1) continue; + connections[i].AddLink(wireIndex, null); + + connections[i] = null; + } + } + + private void CleanNodes() + { + for (int i = nodes.Count - 2; i > 0; i--) + { + if ((nodes[i-1].X == nodes[i].X || nodes[i-1].Y == nodes[i].Y) && + (nodes[i+1].X == nodes[i].X || nodes[i+1].Y == nodes[i].Y)) + { + if (Vector2.Distance(nodes[i - 1], nodes[i]) == Vector2.Distance(nodes[i + 1], nodes[i])) + { + nodes.RemoveAt(i); + } + } + } + + bool removed; + do + { + removed = false; + for (int i = nodes.Count - 2; i > 0; i--) + { + if ((nodes[i - 1].X == nodes[i].X && nodes[i + 1].X == nodes[i].X) + || (nodes[i - 1].Y == nodes[i].Y && nodes[i + 1].Y == nodes[i].Y)) + { + nodes.RemoveAt(i); + removed = true; + } + } + + } while (removed); + + } + + + public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch) + { + for (int i = 0; i < nodes.Count; i++) + { + GUI.DrawRectangle(spriteBatch, new Rectangle((int)nodes[i].X, (int)-nodes[i].Y, 5, 5), Color.DarkGray, true, wireSprite.Depth - 0.01f); + } + + for (int i = 1; i1.0f) + { + item.Drop(); + item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f); + } + + return; + } + } +} diff --git a/Subsurface/Items/Components/Turret.cs b/Subsurface/Items/Components/Turret.cs new file mode 100644 index 000000000..45a133f75 --- /dev/null +++ b/Subsurface/Items/Components/Turret.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System.Globalization; + +namespace Subsurface.Items.Components +{ + class Turret : Powered + { + Sprite barrelSprite; + + Vector2 barrelPos; + + float targetRotation; + float rotation; + + float reload; + float reloadTime; + + float minRotation; + float maxRotation; + + float launchImpulse; + + Camera cam; + + [HasDefaultValue("0,0", false)] + public string BarrelPos + { + get + { + return ToolBox.Vector2ToString(barrelPos); + } + set + { + barrelPos = ToolBox.ParseToVector2(value); + } + } + + [HasDefaultValue(0.0f, false)] + public float LaunchImpulse + { + get { return launchImpulse; } + set { launchImpulse = value; } + } + + [HasDefaultValue(5.0f, false)] + public float Reload + { + get { return reloadTime; } + set { reloadTime = value; } + } + + [HasDefaultValue("0.0,0.0", false)] + public string RotationLimits + { + get + { + return ToolBox.Vector2ToString(barrelPos); + } + set + { + Vector2 vector = ToolBox.ParseToVector2(value); + minRotation = MathHelper.ToRadians(vector.X); + maxRotation = MathHelper.ToRadians(vector.Y); + } + } + + public Turret(Item item, XElement element) + : base(item, element) + { + isActive = true; + + barrelSprite = new Sprite(Path.GetDirectoryName(item.Prefab.ConfigFile) + "\\" +element.Attribute("barrelsprite").Value, + ToolBox.GetAttributeVector2(element, "origin", Vector2.Zero)); + + //barrelPos = ToolBox.GetAttributeVector2(element, "BarrelPos", Vector2.Zero); + + //launchImpulse = ToolBox.GetAttributeFloat(element, "launchimpulse", 0.0f); + + //minRotation = ToolBox.GetAttributeFloat(element, "MinimumRotation", 0.0f); + //maxRotation = ToolBox.GetAttributeFloat(element, "MaximumRotation", MathHelper.TwoPi); + + //reloadTime = ToolBox.GetAttributeFloat(element, "reload", 5.0f); + } + + public override void Draw(SpriteBatch spriteBatch) + { + barrelSprite.Draw(spriteBatch, new Vector2(item.Rect.X, -item.Rect.Y) + barrelPos, rotation + MathHelper.PiOver2, 1.0f); + + //GUI.DrawRectangle(spriteBatch, + // new Rectangle((int)(rect.X + barrelPos.X), (int)(-rect.Y + barrelPos.Y), 10, 10), + // Color.White, true); + } + + public override void Update(float deltaTime, Camera cam) + { + //if (character == null || character.SelectedConstruction != item) + //{ + // character = null; + // isActive = false; + // return; + //} + + this.cam = cam; + + if (reload>0.0f) reload -= deltaTime; + + ApplyStatusEffects(ActionType.OnActive, deltaTime, null); + + if (targetRotation < minRotation || targetRotation > maxRotation) + { + float diff = ToolBox.WrapAngleTwoPi(targetRotation - (minRotation + maxRotation) / 2.0f); + targetRotation = (diff > Math.PI) ? minRotation : maxRotation; + } + + rotation = ToolBox.CurveAngle(rotation, targetRotation, 0.05f); + + //if (!prefab.FocusOnSelected) return; + + //cam.OffsetAmount = prefab.OffsetOnSelected; + } + + public override void SecondaryUse(Character character = null) + { + if (character == null) return; + Vector2 centerPos = new Vector2(item.Rect.X + barrelPos.X, item.Rect.Y - barrelPos.Y); + + Vector2 offset = character.CursorPosition - centerPos; + offset.Y = -offset.Y; + + targetRotation = ToolBox.WrapAngleTwoPi(ToolBox.VectorToAngle(offset)); + + isActive = true; + + if (character == Character.Controlled && cam!=null) + { + Lights.LightManager.viewPos = centerPos; + cam.TargetPos = new Vector2(item.Rect.X + barrelPos.X, item.Rect.Y - barrelPos.Y); + } + } + + public override bool Use(Character character = null) + { + if (reload > 0.0f) return false; + + Projectile projectileComponent = null; + + currPowerConsumption = powerConsumption; + + float availablePower = 0.0f; + List batteries = new List(); + foreach (MapEntity e in item.linkedTo) + { + Item battery = e as Item; + if (battery == null) continue; + + PowerContainer batteryComponent = battery.GetComponent(); + if (batteryComponent == null) continue; + + float batteryPower = Math.Min(batteryComponent.Charge, batteryComponent.MaxOutPut); + float takePower = Math.Min(currPowerConsumption - availablePower, batteryPower); + + batteryComponent.Charge -= takePower; + availablePower += takePower; + } + + reload = reloadTime; + + if (availablePower < currPowerConsumption) return false; + + //search for a projectile from linked containers + Item projectile = null; + foreach (MapEntity e in item.linkedTo) + { + Item container = e as Item; + if (container == null) continue; + + ItemContainer containerComponent = container.GetComponent(); + if (containerComponent == null) continue; + + for (int i = 0; i < containerComponent.inventory.items.Length; i++) + { + if (containerComponent.inventory.items[i] == null) continue; + if ((projectileComponent = containerComponent.inventory.items[i].GetComponent()) != null) + { + projectile = containerComponent.inventory.items[i]; + break; + } + } + + if (projectileComponent != null) break; + } + + if (projectile == null || projectileComponent==null) return false; + + projectile.body.ResetDynamics(); + projectile.body.Enabled = true; + projectile.SetTransform(item.SimPosition, rotation); + + //if (useSounds.Count() > 0) useSounds[Game1.localRandom.Next(useSounds.Count())].Play(1.0f, 800.0f, item.body.FarseerBody); + + projectileComponent.Use(); + item.RemoveContained(projectile); + + return true; + } + } +} + + diff --git a/Subsurface/Items/Components/Vent.cs b/Subsurface/Items/Components/Vent.cs new file mode 100644 index 000000000..eb813b5ee --- /dev/null +++ b/Subsurface/Items/Components/Vent.cs @@ -0,0 +1,32 @@ +using System; +using System.Xml.Linq; + +namespace Subsurface.Items.Components +{ + class Vent : ItemComponent + { + private float oxygenFlow; + + public float OxygenFlow + { + get { return oxygenFlow; } + set { oxygenFlow = Math.Max(value, 0.0f); } + } + + public Vent (Item item, XElement element) + : base(item, element) + { + + } + + public override void Update(float deltaTime, Camera cam) + { + base.Update(deltaTime, cam); + + if (item.currentHull == null) return; + + item.currentHull.Oxygen += oxygenFlow * deltaTime; + OxygenFlow -= deltaTime * 1000.0f; + } + } +} diff --git a/Subsurface/Items/Components/Wearable.cs b/Subsurface/Items/Components/Wearable.cs new file mode 100644 index 000000000..7c4a9a901 --- /dev/null +++ b/Subsurface/Items/Components/Wearable.cs @@ -0,0 +1,139 @@ +using System; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using Microsoft.Xna.Framework; +using System.Diagnostics; + +namespace Subsurface.Items.Components +{ + class Wearable : Pickable + { + Sprite[] sprite; + LimbType[] limbType; + Limb[] limb; + + public Wearable (Item item, XElement element) + : base(item, element) + { + this.item = item; + + var sprites = element.Elements().Where(x => x.Name.ToString() == "sprite").ToList(); + int spriteCount = sprites.Count(); + sprite = new Sprite[spriteCount]; + limbType = new LimbType[spriteCount]; + limb = new Limb[spriteCount]; + + int i = 0; + foreach (XElement subElement in sprites) + { + //Rectangle sourceRect = new Rectangle( + // ToolBox.GetAttributeInt(subElement, "sourcex", 1), + // ToolBox.GetAttributeInt(subElement, "sourcey", 1), + // ToolBox.GetAttributeInt(subElement, "sourcewidth", 1), + // ToolBox.GetAttributeInt(subElement, "sourceheight", 1)); + + if (subElement.Attribute("texture") == null) + { + DebugConsole.ThrowError("Item ''" + item.Name + "'' doesn't have a texture specified!"); + return; + } + + string spritePath = subElement.Attribute("texture").Value; + spritePath = Path.GetDirectoryName( item.Prefab.ConfigFile)+"\\"+spritePath; + + sprite[i] = new Sprite(subElement, "", spritePath); + //sprite[i].origin = new Vector2(sourceRect.Width / 2.0f, sourceRect.Height / 2.0f); + + limbType[i] = (LimbType)Enum.Parse(typeof(LimbType), + ToolBox.GetAttributeString(subElement, "limb", "Head")); + + i++; + } + } + + public override void Equip(Character character) + { + picker = character; + for (int i = 0; i < sprite.Length; i++ ) + { + Limb equipLimb = character.animController.GetLimb(limbType[i]); + if (equipLimb == null) continue; + + //something is already on the limb -> unequip it + if (equipLimb.WearingItem != null && equipLimb.WearingItem != item) + { + equipLimb.WearingItem.Unequip(character); + } + + sprite[i].Depth = equipLimb.sprite.Depth - 0.001f; + + item.body.Enabled = false; + + isActive = true; + + limb[i] = equipLimb; + equipLimb.WearingItem = item; + equipLimb.WearingItemSprite = sprite[i]; + } + } + + public override void Drop(Character dropper) + { + Unequip(picker); + + base.Drop(dropper); + + picker = null; + isActive = false; + } + + public override void Unequip(Character character) + { + if (picker == null) return; + for (int i = 0; i < sprite.Length; i++) + { + Limb equipLimb = character.animController.GetLimb(limbType[i]); + if (equipLimb == null) continue; + + if (equipLimb.WearingItem != item) continue; + + limb[i] = null; + equipLimb.WearingItem = null; + equipLimb.WearingItemSprite = null; + } + + isActive = false; + } + + public override void UpdateBroken(float deltaTime, Camera cam) + { + Update(deltaTime, cam); + } + + public override void Update(float deltaTime, Camera cam) + { + base.Update(deltaTime, cam); + + Item[] containedItems = item.ContainedItems; + + + for (int i = 0; i < limb.Length; i++) + { + if (limb[i] == null) continue; + ApplyStatusEffects(ActionType.OnWearing, deltaTime, picker, limb[i]); + + if (containedItems == null) continue; + for (int j = 0; j= items.Length) return false; + return (items[i] == null); + } + + /// + /// If there is room, puts the item in the inventory and returns true, otherwise returns false + /// + public virtual bool TryPutItem(Item item, LimbSlot usedSlots = 0, bool createNetworkEvent = true) + { + int slot = CanBePut(item); + if (slot < 0) return false; + + PutItem(item, slot, createNetworkEvent); + return true; + } + + public virtual bool TryPutItem(Item item, int i, bool createNetworkEvent = true) + { + if (CanBePut(item,i)) + { + PutItem(item, i, createNetworkEvent); + return true; + } + else + { + return false; + } + } + + protected void PutItem(Item item, int i, bool createNetworkEvent, bool removeItem = true) + { + if (item.inventory != null && removeItem) item.inventory.RemoveItem(item); + items[i] = item; + item.inventory = this; + if (item.body!=null) + { + item.body.Enabled = false; + } + + + if (createNetworkEvent) + { + int[] data = { item.ID, i }; + new NetworkEvent(NetworkEventType.InventoryUpdate, ID, true, data); + } + } + + public void RemoveItem(Item item) + { + //go through the inventory and remove the item from all slots + for (int n = 0; n < capacity; n++) + { + if (items[n] != item) continue; + items[n] = null; + } + } + + //protected virtual void DropItem(Item item) + //{ + // for (int i = 0; i < capacity; i++) + // { + // if (items[i] == item) items[i] = null; + // } + // item.Drop(); + // return; + //} + //public void DropItem(int i) + //{ + // items[i].Drop(); + // items[i] = null; + //} + + public virtual void Draw(SpriteBatch spriteBatch) + { + doubleClickedItem = null; + + int rectWidth = 40, rectHeight = 40; + + int spacing = 10; + + int slotsPerRow = 5; + + int rows = (int)Math.Ceiling((double)capacity / slotsPerRow); + + int startX = Game1.GraphicsWidth / 2 - (rectWidth * slotsPerRow + spacing * (slotsPerRow - 1)) / 2; + int startY = (int)(Game1.GraphicsHeight * 0.9) - rows*(spacing+rectHeight); + + Rectangle slotRect = new Rectangle(startX, startY, rectWidth, rectHeight); + Rectangle draggingItemSlot = slotRect; + + for (int i = 0; i < capacity; i++) + { + slotRect.X = startX + (rectWidth + spacing) * (i % slotsPerRow); + slotRect.Y = startY + (rectHeight + spacing) * ((int)Math.Floor((double)i / slotsPerRow)); + + if (draggingItem == items[i]) draggingItemSlot = slotRect; + + UpdateSlot(spriteBatch, slotRect, i, items[i], false); + } + + if (draggingItem != null && !draggingItemSlot.Contains(PlayerInput.MousePosition)) + { + if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed) + { + slotRect.X = PlayerInput.GetMouseState.X - slotRect.Width / 2; + slotRect.Y = PlayerInput.GetMouseState.Y - slotRect.Height / 2; + //GUI.DrawRectangle(spriteBatch, rect, Color.White, true); + //draggingItem.sprite.Draw(spriteBatch, new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), Color.White); + + DrawSlot(spriteBatch, slotRect, draggingItem, false, false); + } + else + { + draggingItem.Drop(null, false); + + int[] data = { draggingItem.ID, -1 }; + new NetworkEvent(NetworkEventType.InventoryUpdate, ID, true, data); + + //draggingItem = null; + } + } + } + + protected void UpdateSlot(SpriteBatch spriteBatch, Rectangle rect, int slotIndex, Item item, bool isSubSlot) + { + bool mouseOn = rect.Contains(PlayerInput.MousePosition); + + DrawSlot(spriteBatch, rect, (draggingItem == item && !mouseOn) ? null : item, mouseOn, isSubSlot); + + if (mouseOn) + { + if (draggingItem == null) + { + if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed) + { + draggingItem = item; + } + } + else if (PlayerInput.GetMouseState.LeftButton == ButtonState.Released) + { + if (PlayerInput.DoubleClicked()) + { + doubleClickedItem = item; + } + + selectedSlot = slotIndex; + TryPutItem(draggingItem, slotIndex); + draggingItem = null; + } + + if (!isSubSlot) selectedSlot = slotIndex; + } + + if (selectedSlot == slotIndex) + { + if (item == null) return; + + int itemCapacity = item.Capacity; + if (itemCapacity == 0) return; + + Rectangle containerRect = new Rectangle(rect.X - 5, rect.Y - (rect.Height + 10) * itemCapacity - 5, + rect.Width + 10, rect.Height + (rect.Height + 10) * itemCapacity + 10); + + selectedSlot = containerRect.Contains(PlayerInput.MousePosition) ? slotIndex : -1; + + GUI.DrawRectangle(spriteBatch, containerRect, Color.White, false); + + Item[] containedItems = null; + if (items[slotIndex] != null) containedItems = items[slotIndex].ContainedItems; + + if (containedItems == null || containedItems.Count() == 0) return; + + for (int i = 0; i < itemCapacity; i++) + { + rect.Y = rect.Y - rect.Height - 10; + UpdateSlot(spriteBatch, rect, selectedSlot, (i < containedItems.Count()) ? containedItems[i] : null, true); + } + } + } + + protected void DrawSlot(SpriteBatch spriteBatch, Rectangle rect, Item item, bool isHighLighted, bool isSubSlot) + { + GUI.DrawRectangle(spriteBatch, rect, (isHighLighted ? Color.Red : Color.White)*((isSubSlot) ? 0.1f : 0.3f), true); + + if (item == null) return; + + item.sprite.Draw(spriteBatch, new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), Color.White); + + if (isHighLighted) + { + Vector2 pos = new Vector2(rect.X + rect.Width / 2, rect.Y - rect.Height + 20) - GUI.font.MeasureString(item.Name)*0.5f; + pos.X = (int)pos.X; + pos.Y = (int)pos.Y; + spriteBatch.DrawString(GUI.font, item.Name, pos - new Vector2(1.0f,1.0f), Color.Black); + spriteBatch.DrawString(GUI.font, item.Name, pos, Color.White); + } + + if (item.Condition < 100.0f) + spriteBatch.DrawString(GUI.font, (int)item.Condition + " %", new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), Color.Red); + } + + public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + { + int[] dataArray = data as int[]; + if (dataArray == null || dataArray.Length<2) + { + message.Write(-1); + return; + } + //item id + message.Write(dataArray[0]); + //index of the slot which the item was moved to + message.Write(dataArray[1]); + } + + public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) + { + int itemId = message.ReadInt32(); + if (itemId == -1) return; + + int slotIndex = message.ReadInt32(); + + Item item = FindEntityByID(itemId) as Item; + if (item == null) return; + + if (slotIndex==-1) + { + if (item.inventory == this) item.Drop(); + } + else + { + TryPutItem(item, slotIndex, false); + } + + + } + } +} diff --git a/Subsurface/Items/Item.cs b/Subsurface/Items/Item.cs new file mode 100644 index 000000000..63fb7c7ac --- /dev/null +++ b/Subsurface/Items/Item.cs @@ -0,0 +1,1021 @@ +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using FarseerPhysics; +using Lidgren.Network; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using Subsurface.Networking; +using System; +using Subsurface.Items.Components; +using System.ComponentModel; +using System.Collections.ObjectModel; + +namespace Subsurface +{ + + public enum ActionType + { + OnPicked, OnWearing, OnContaining, OnContained, OnActive, OnUse + } + + class Item : MapEntity, IDamageable, IPropertyObject + { + public static List itemList = new List(); + protected ItemPrefab prefab; + + private List tags; + + public Hull currentHull; + + //components that determine the functionality of the item + public List components; + + public readonly Dictionary properties; + public Dictionary ObjectProperties + { + get { return properties; } + } + + public PhysicsBody body; + + private float condition; + + //the inventory in which the item is contained in + public Inventory inventory; + + public Item container; + + public override string Name + { + get { return prefab.Name; } + } + + public override Sprite sprite + { + get { return prefab.sprite; } + } + + + public float Condition + { + get { return condition; } + set { condition = MathHelper.Clamp(value, 0.0f, 100.0f); } + } + + [Editable, HasDefaultValue("", true)] + public string Tags + { + get { return string.Join(",",tags); } + set + { + tags.Clear(); + if (value == null) return; + + string[] newTags = value.Split(','); + foreach (string tag in newTags) + { + string newTag = tag.Trim(); + if (!tags.Contains(newTag)) tags.Add(newTag); + } + + } + } + + public bool Updated + { + set + { + foreach (ItemComponent ic in components) ic.Updated = value; + } + } + + public ItemPrefab Prefab + { + get { return prefab; } + } + + //which type of inventory slots (head, torso, any, etc) the item can be placed in + public LimbSlot AllowedSlots + { + get + { + Pickable p = GetComponent(); + return (p==null) ? LimbSlot.Any : p.AllowedSlots; + } + } + + public int Capacity + { + get + { + ItemContainer c = GetComponent(); + return (c == null) ? 0 : c.Capacity; + } + } + + public List Connections + { + get + { + ConnectionPanel panel = GetComponent(); + if (panel == null) return null; + return panel.connections; + } + } + + public Item[] ContainedItems + { + get + { + ItemContainer c = GetComponent(); + return (c == null) ? null : c.inventory.items; + } + } + + public override bool IsLinkable + { + get { return prefab.IsLinkable; } + } + + List highlightText; + + public List HighlightText + { + get { return highlightText;} + + } + + public Item(Rectangle newRect, ItemPrefab itemPrefab) + { + prefab = itemPrefab; + + linkedTo = new ObservableCollection(); + components = new List(); + + tags = new List(); + + rect = newRect; + //rect.X -= rect.Width / 2; + //rect.Y += rect.Height / 2; + + //dir = 1.0f; + + FindHull(); + + condition = 100.0f; + + XElement element = ToolBox.TryLoadXml(Prefab.ConfigFile).Root; + if (element == null) return; + + if (ToolBox.GetAttributeString(element, "name", "") != Name) + { + foreach (XElement subElement in element.Elements()) + { + if (ToolBox.GetAttributeString(subElement, "name", "") != Name) continue; + + element = subElement; + break; + } + } + + + properties = ObjectProperty.InitProperties(this, element); + + //foreach (XAttribute attribute in element.Attributes()) + //{ + // ObjectProperty property = null; + // if (!properties.TryGetValue(attribute.Name.ToString().ToLower(), out property)) continue; + // if (property.Attributes.OfType().Count() == 0) continue; + // property.TrySetValue(attribute.Value); + //} + + + + highlightText = new List(); + + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLower()) + { + case "body": + body = new PhysicsBody(subElement, ConvertUnits.ToSimUnits(Position)); + break; + case "trigger": + case "sprite": + break; + default: + ItemComponent ic = ItemComponent.Load(subElement, this, prefab.ConfigFile); + if (ic == null) break; + + components.Add(ic); + if (!string.IsNullOrWhiteSpace(ic.Msg)) highlightText.Add(ic.Msg); + + break; + } + } + + + itemList.Add(this); + mapEntityList.Add(this); + } + + public T GetComponent() + { + foreach (ItemComponent ic in components) + { + if (ic is T) return (T)(object)ic; + } + + return default(T); + } + + public void RemoveContained(Item contained) + { + ItemContainer c = GetComponent(); + if (c == null) return; + + c.RemoveContained(contained); + contained.container = null; + } + + + public void SetTransform(Vector2 position, float rotation) + { + body.SetTransform(position, rotation); + + Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.Position); + + rect.X = (int)(displayPos.X - rect.Width / 2.0f); + rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); + } + + public override void Move(Vector2 amount) + { + base.Move(amount); + + if (itemList != null && body!=null) + { + Vector2 pos = new Vector2(rect.X + rect.Width / 2.0f, rect.Y - rect.Height / 2.0f); + body.SetTransform(ConvertUnits.ToSimUnits(pos), 0.0f); + } + foreach (ItemComponent ic in components) + { + ic.Move(amount); + } + + FindHull(); + } + + public Rectangle TransformTrigger(Rectangle trigger) + { + return new Rectangle( + Rect.X + trigger.X, + Rect.Y + trigger.Y, + (trigger.Width == 0) ? (int)Rect.Width : trigger.Width, + (trigger.Height == 0) ? (int)Rect.Height : trigger.Height); + } + + /// + /// goes through every item and re-checks which hull they are in + /// + public static void UpdateHulls() + { + foreach (Item item in itemList) item.FindHull(); + } + + protected virtual Hull FindHull() + { + currentHull = Hull.FindHull((body == null) ? Position : ConvertUnits.ToDisplayUnits(body.Position), currentHull); + return currentHull; + } + + + public bool HasTag(string tag) + { + return (tags.Contains(tag)); + } + + + public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb limb = null) + { + foreach (ItemComponent ic in components) + { + foreach (StatusEffect effect in ic.statusEffects) + { + ApplyStatusEffect(effect, type, deltaTime, character, limb); + } + } + } + + public void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character = null, Limb limb = null) + { + if (condition == 0.0f) return; + + bool hasTargets = (effect.TargetNames == null); + + Item[] containedItems = ContainedItems; + if (effect.OnContainingNames!=null) + { + foreach (string s in effect.OnContainingNames) + { + if (containedItems.FirstOrDefault(x => x!=null && x.Name==s && x.Condition>0.0f) == null) return; + } + } + + if (containedItems!=null) + { + if (effect.Targets.HasFlag(StatusEffect.Target.Contained)) + { + foreach (Item containedItem in containedItems) + { + if (containedItem == null || containedItem.condition==0.0f) continue; + if (effect.TargetNames != null && !effect.TargetNames.Contains(containedItem.Name)) continue; + + hasTargets = true; + effect.Apply(type, deltaTime, containedItem); + //containedItem.ApplyStatusEffect(effect, type, deltaTime, containedItem); + } + } + } + + + if (hasTargets) + { + if (effect.Targets.HasFlag(StatusEffect.Target.This)) + effect.Apply(type, deltaTime, this); + //ApplyStatusEffect(effect, type, deltaTime, this); + + if (effect.Targets.HasFlag(StatusEffect.Target.Character)) + effect.Apply(type, deltaTime, null, character, limb); + //ApplyStatusEffect(effect, type, deltaTime, null, character, limb); + + if (container != null && effect.Targets.HasFlag(StatusEffect.Target.Parent)) + { + effect.Apply(type, deltaTime, container); + //container.ApplyStatusEffect(effect, type, deltaTime, container); + } + } + } + + + public void AddDamage(Vector2 position, float amount, float bleedingAmount, float stun) + { + Condition -= amount; + } + + + public override void Update(Camera cam, float deltaTime) + { + foreach (ItemComponent ic in components) + { + if (!ic.IsActive) continue; + if (condition > 0.0f) + { + ic.Update(deltaTime, cam); + + + ic.PlaySound(ActionType.OnActive, 1.0f, Position, true); + ic.ApplyStatusEffects(ActionType.OnActive, 1.0f, null); + } + else + { + ic.UpdateBroken(deltaTime, cam); + } + } + + + if (body == null) return; + + if (body.LinearVelocity.Length()>0.001f) FindHull(); + + + Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.Position); + + rect.X = (int)(displayPos.X - rect.Width / 2.0f); + rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); + + body.SetToTargetPosition(); + + if (currentHull != null) + { + float surfaceY = ConvertUnits.ToSimUnits(currentHull.Surface); + if (surfaceY > body.Position.Y) return; + + //the item has gone through the surface of the water -> apply an impulse which serves as surface tension + if ((body.Position.Y - (body.LinearVelocity.Y / 60.0f)) < surfaceY) + { + Vector2 impulse = -body.LinearVelocity * (body.Mass / body.Density); + body.ApplyLinearImpulse(impulse); + int n = (int)((displayPos.X - currentHull.Rect.X) / Hull.WaveWidth); + currentHull.WaveVel[n] = impulse.Y * 10.0f; + } + } + + //calculate (a rough approximation of) buoyancy + float volume = body.Mass / body.Density; + Vector2 buoyancy = new Vector2(0, volume * 20.0f); + + //apply buoyancy and drag + try + { + //if ((buoyancy - body.LinearVelocity * volume) == Vector2.Zero) DebugConsole.ThrowError("v.zero "); + if (body.LinearVelocity != Vector2.Zero && body.LinearVelocity.Length() > 1000.0f) + { + body.ResetDynamics(); + if (body.Position.Length() > 1000.0f) + { + this.Remove(); + return; + } + } + body.ApplyForce(buoyancy - body.LinearVelocity * volume); + + //apply simple angular drag + body.ApplyTorque(body.AngularVelocity * volume * -0.05f); + } + + catch + { + DebugConsole.ThrowError("something bad happened with the physics"); + } + } + + public override void Draw(SpriteBatch spriteBatch, bool editing) + { + Color color = (isSelected && editing) ? color = Color.Red : Color.White; + if (isHighlighted) + { + color = Color.Orange; + + float stringWidth = GUI.font.MeasureString(prefab.Name).X; + Vector2 textPos = new Vector2(Position.X, -Position.Y); + textPos -= new Vector2(stringWidth / 2, 40); + spriteBatch.DrawString(GUI.font, prefab.Name, textPos, Color.Black); + spriteBatch.DrawString(GUI.font, prefab.Name, textPos + new Vector2(1, -1), Color.Orange); + + + textPos = new Vector2(Position.X, -Position.Y+50); + foreach (string text in highlightText) + { + textPos.X = Position.X - GUI.font.MeasureString(text).X/2; + + spriteBatch.DrawString(GUI.font, text, textPos, Color.Black); + spriteBatch.DrawString(GUI.font, text, textPos + new Vector2(1, -1), Color.Orange); + + textPos.Y += 25; + } + } + + if (body==null) + { + prefab.sprite.DrawTiled(spriteBatch, new Vector2(rect.X, -rect.Y), new Vector2(rect.Width, rect.Height), color); + } + else if (body.Enabled) + { + body.Draw(spriteBatch, prefab.sprite, color); + } + + foreach (ItemComponent component in components) component.Draw(spriteBatch); + + if (!editing || (body!=null && !body.Enabled)) + { + isHighlighted = false; + return; + } + + GUI.DrawRectangle(spriteBatch, new Vector2(rect.X, -rect.Y), new Vector2(rect.Width, rect.Height), Color.Green); + + foreach (Rectangle t in prefab.triggers) + { + Rectangle transformedTrigger = TransformTrigger(t); + GUI.DrawRectangle(spriteBatch, + new Vector2(transformedTrigger.X, -transformedTrigger.Y), + new Vector2(transformedTrigger.Width, transformedTrigger.Height), + Color.Green); + } + + 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.Red); + } + } + + public override void DrawEditing(SpriteBatch spriteBatch, Camera cam) + { + if (editingHUD==null || editingHUD.UserData as Item != this) + { + editingHUD = CreateEditingHUD(); + } + + editingHUD.Draw(spriteBatch); + editingHUD.Update((float)Physics.step); + + if (!prefab.IsLinkable) return; + + if (!PlayerInput.LeftButtonClicked() || !PlayerInput.KeyDown(Keys.Space)) return; + + Vector2 position = cam.ScreenToWorld(PlayerInput.MousePosition); + + foreach (MapEntity entity in mapEntityList) + { + if (entity == this || !entity.IsHighlighted) continue; + if (linkedTo.Contains(entity)) continue; + if (!entity.Contains(position)) continue; + + linkedTo.Add(entity); + if (entity.IsLinkable && entity.linkedTo != null) entity.linkedTo.Add(this); + } + } + + private GUIComponent CreateEditingHUD(bool inGame=false) + { + int width = 500; + int x = Game1.GraphicsWidth/2-width/2, y = 10; + + List editableProperties = inGame ? GetProperties() : GetProperties(); + + int requiredItemCount = 0; + if (!inGame) + { + foreach (ItemComponent ic in components) + { + requiredItemCount += ic.requiredItems.Count; + } + } + + editingHUD = new GUIFrame(new Rectangle(x, y, width, 110 + (editableProperties.Count()+requiredItemCount) * 30), Color.Black * 0.5f); + editingHUD.Padding = new Vector4(10, 10, 0, 0); + editingHUD.UserData = this; + + new GUITextBlock(new Rectangle(0, 0, 100, 20), prefab.Name, Color.Transparent, Color.White, Alignment.Left, editingHUD); + + y += 20; + + if (!inGame) + { + if (prefab.IsLinkable) + { + new GUITextBlock(new Rectangle(0, 20, 100, 20), "Hold space to link to another construction", Color.Transparent, Color.White, Alignment.Left, editingHUD); + y += 25; + } + foreach (ItemComponent ic in components) + { + foreach (RelatedItem relatedItem in ic.requiredItems) + { + + new GUITextBlock(new Rectangle(0, y, 100, 20),ic.Name+ ": "+relatedItem.Type.ToString()+" required", Color.Transparent, Color.White, Alignment.Left, editingHUD); + GUITextBox namesBox = new GUITextBox(new Rectangle(0, y, 200, 20), Color.White, Color.Black, Alignment.Right, Alignment.Left, editingHUD); + + PropertyDescriptorCollection properties = TypeDescriptor.GetProperties (relatedItem); + PropertyDescriptor property = properties.Find("JoinedNames", false); + + namesBox.Text = relatedItem.JoinedNames; + namesBox.UserData = new ObjectProperty(property, relatedItem); + namesBox.OnEnter = EnterProperty; + namesBox.OnTextChanged = PropertyChanged; + + y += 30; + } + } + + } + + foreach (var objectProperty in editableProperties) + { + new GUITextBlock(new Rectangle(0, y, 100, 20), objectProperty.Name, Color.Transparent, Color.White, Alignment.Left, editingHUD); + GUITextBox propertyBox = new GUITextBox(new Rectangle(100, y, 200, 20), Color.White, Color.Black, Alignment.Left, Alignment.Left, editingHUD); + + object value = objectProperty.GetValue(); + if (value != null) + { + propertyBox.Text = value.ToString(); + } + + propertyBox.UserData = objectProperty; + propertyBox.OnEnter = EnterProperty; + propertyBox.OnTextChanged = PropertyChanged; + y = y + 30; + } + return editingHUD; + } + + public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) + { + if (editingHUD==null || editingHUD.UserData as Item != this) + { + editingHUD = CreateEditingHUD(true); + } + + editingHUD.Draw(spriteBatch); + + foreach (ItemComponent ic in components) + { + ic.DrawHUD(spriteBatch, character); + } + } + + + public void SendSignal(string signal, string connectionName, Item ignoredReceiver = null) + { + ConnectionPanel panel = GetComponent(); + if (panel == null) return; + foreach (Connection c in panel.connections) + { + if (c.name != connectionName) continue; + + c.SendSignal(signal, this); + } + + } + + /// Position of the character doing the pick, only items that are close enough to this are checked + /// the item closest to pickPosition is returned + /// If a hull is specified, only items within that hull are checked + public static Item FindPickable(Vector2 position, Vector2 pickPosition, Hull hull = null, Item[] ignoredItems=null) + { + float closestDist = 0.0f, dist; + Item closest = null; + + + Vector2 displayPos = ConvertUnits.ToDisplayUnits(position); + Vector2 displayPickPos = ConvertUnits.ToDisplayUnits(pickPosition); + + foreach (Item item in itemList) + { + if (ignoredItems!=null && ignoredItems.Contains(item)) continue; + if (hull != null && item.currentHull != hull) continue; + if (item.body != null && !item.body.Enabled) continue; + + Pickable pickableComponent = item.GetComponent(); + if (pickableComponent != null && (pickableComponent.Picker != null && !pickableComponent.Picker.IsDead)) continue; + + foreach (Rectangle trigger in item.prefab.triggers) + { + Rectangle transformedTrigger = item.TransformTrigger(trigger); + + if (!Map.RectContains(transformedTrigger, displayPos))continue; + + + Vector2 triggerCenter = + new Vector2( + transformedTrigger.X + transformedTrigger.Width / 2.0f, + transformedTrigger.Y - transformedTrigger.Height / 2.0f); + + dist = MathHelper.Min(Math.Abs(triggerCenter.X - displayPos.X), Math.Abs(triggerCenter.Y-displayPos.Y)); + dist = ConvertUnits.ToSimUnits(dist); + if (dist > closestDist && closest!=null) continue; + + dist = MathHelper.Min(Math.Abs(triggerCenter.X - displayPickPos.X), Math.Abs(triggerCenter.Y - displayPickPos.Y)); + dist = ConvertUnits.ToSimUnits(dist); + if (closest == null || dist < closestDist) + { + closest = item; + closestDist = dist; + } + } + + + if (item.prefab.PickDistance == 0.0f) continue; + if (Vector2.Distance(position, item.SimPosition) > item.prefab.PickDistance) continue; + + dist = Vector2.Distance(pickPosition, item.SimPosition); + if (closest == null || dist < closestDist) + { + closest = item; + closestDist = dist; + } + } + + return closest; + } + + public bool Pick(Character picker, bool forcePick=false) + { + + bool picked = false, selected = false; + foreach (ItemComponent ic in components) + { + if ((ic.CanBePicked || ic.CanBeSelected) + && (ic.HasRequiredEquippedItems(picker, picker == Character.Controlled) || forcePick) + && ic.Pick(picker)) + { + picked = true; + ic.ApplyStatusEffects(ActionType.OnPicked, 1.0f, picker); + if (ic.CanBeSelected) selected = true; + } + } + + if (!picked) return false; + if (selected) + { + picker.SelectedConstruction = (picker.SelectedConstruction == this) ? null : this; + } + + if (container!=null) container.RemoveContained(this); + + return true; + } + + + public void Use(Character character = null) + { + if (condition == 0.0f) return; + + foreach (ItemComponent ic in components) + { + if (!ic.HasRequiredContainedItems(character == Character.Controlled)) continue; + if (ic.Use(character)) + { + ic.PlaySound(ActionType.OnUse, 1.0f, Position); + + ic.ApplyStatusEffects(ActionType.OnUse, 1.0f, character); + } + } + } + + public void SecondaryUse(Character character = null) + { + foreach (ItemComponent ic in components) + { + if (!ic.HasRequiredContainedItems(character == Character.Controlled)) continue; + ic.SecondaryUse(character); + } + } + + public bool Combine(Item item) + { + bool isCombined = false; + foreach (ItemComponent ic in components) + { + if (ic.Combine(item)) isCombined = true; + } + return isCombined; + } + + public void Drop(Character dropper = null, bool createNetworkEvent = true) + { + if (dropper == Character.Controlled) + new NetworkEvent(NetworkEventType.DropItem, ID, true); + + foreach (ItemComponent ic in components) ic.Drop(dropper); + + if (container != null) container.RemoveContained(this); + } + + public void Equip(Character character) + { + foreach (ItemComponent ic in components) ic.Equip(character); + } + + public void Unequip(Character character) + { + character.DeselectItem(this); + foreach (ItemComponent ic in components) ic.Unequip(character); + } + + + public List GetProperties() + { + + List editableProperties = ObjectProperty.GetProperties(this); + + foreach (ItemComponent ic in components) + { + List componentProperties = ObjectProperty.GetProperties(ic); + foreach (var property in componentProperties) + { + editableProperties.Add(property); + } + } + + return editableProperties; + } + + private bool EnterProperty(GUITextBox textBox, string text) + { + textBox.Color = Color.White; + + var objectProperty = textBox.UserData as ObjectProperty; + if (objectProperty == null) return false; + + object prevValue = objectProperty.GetValue(); + + if (objectProperty.TrySetValue(text)) + { + textBox.Text = text; + return true; + } + else + { + if (prevValue!=null) + { + textBox.Text = prevValue.ToString(); + } + return false; + } + } + + private bool PropertyChanged(GUITextBox textBox, string text) + { + textBox.Color = Color.Red; + + return true; + } + + + //private void Init() + //{ + + //} + + public override XElement Save(XDocument doc) + { + XElement element = new XElement("Item"); + + element.Add(new XAttribute("name", prefab.Name), + new XAttribute("ID", ID), + new XAttribute("rect", rect.X + "," + rect.Y+","+rect.Width+","+rect.Height)); + + if (linkedTo != null && linkedTo.Count>0) + { + string[] linkedToIDs = new string[linkedTo.Count]; + + for (int i = 0; i < linkedTo.Count; i++ ) + { + linkedToIDs[i] = linkedTo[i].ID.ToString(); + } + + element.Add(new XAttribute("linked", string.Join(",", linkedToIDs))); + } + + + ObjectProperty.SaveProperties(this, element); + + //var saveProperties = ObjectProperty.GetProperties(this); + //foreach (var property in saveProperties) + //{ + // object value = property.GetValue(); + // if (value == null) continue; + + // bool dontSave=false; + // foreach (var ini in property.Attributes.OfType()) + // { + // if (ini.defaultValue==value) + // { + // dontSave = true; + // break; + // } + // } + + // if (dontSave) continue; + + // element.Add(new XAttribute(property.Name.ToLower(), value)); + //} + + //if (tags.Count>0) + //{ + // element.Add(new XAttribute("tags",string.Join(", ",tags))); + //} + + foreach (ItemComponent ic in components) + { + ic.Save(element); + } + + 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; + + foreach (MapEntityPrefab ep in MapEntityPrefab.list) + { + ItemPrefab ip = ep as ItemPrefab; + if (ip == null) continue; + + if (ip.Name != name) continue; + + Item item = new Item(rect, ip); + item.ID = int.Parse(element.Attribute("ID").Value); + + item.linkedToID = new List(); + + foreach (XAttribute attribute in element.Attributes()) + { + ObjectProperty property = null; + if (!item.properties.TryGetValue(attribute.Name.ToString(), out property)) continue; + + bool shouldBeLoaded = false; + + foreach (var propertyAttribute in property.Attributes.OfType()) + { + if (propertyAttribute.isSaveable) + { + shouldBeLoaded = true; + break; + } + } + + if (shouldBeLoaded) property.TrySetValue(attribute.Value); + } + + string linkedToString = ToolBox.GetAttributeString(element, "linked", ""); + if (linkedToString!="") + { + string[] linkedToIds = linkedToString.Split(','); + for (int i = 0; i x.Name == subElement.Name.ToString()); + + if (component == null) continue; + + component.Load(subElement); + } + + break; + } + + } + + + public void NewComponentEvent(ItemComponent ic, bool isClient) + { + int index = components.IndexOf(ic); + + new NetworkEvent(NetworkEventType.UpdateComponent, this.ID, isClient, index); + } + + public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data) + { + switch (type) + { + case NetworkEventType.DropItem: + if (body != null) body.FillNetworkData(type, message); + break; + case NetworkEventType.UpdateComponent: + message.Write((int)data); + components[(int)data].FillNetworkData(type, message); + break; + default: + break; + } + } + + public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) + { + switch (type) + { + case NetworkEventType.DropItem: + if (body != null) body.ReadNetworkData(type, message); + Drop(null, false); + break; + case NetworkEventType.UpdateComponent: + int componentIndex = message.ReadInt32(); + if (componentIndex < 0 || componentIndex > components.Count - 1) return; + components[componentIndex].ReadNetworkData(type, message); + break; + default: + break; + } + } + + public override void Remove() + { + base.Remove(); + + //sprite.Remove(); + if (body!=null) body.Remove(); + + foreach (ItemComponent ic in components) + { + ic.Remove(); + } + + itemList.Remove(this); + } + + } +} diff --git a/Subsurface/Items/ItemInventory.cs b/Subsurface/Items/ItemInventory.cs new file mode 100644 index 000000000..5cdf85cf7 --- /dev/null +++ b/Subsurface/Items/ItemInventory.cs @@ -0,0 +1,59 @@ +using Subsurface.Items.Components; + +namespace Subsurface +{ + class ItemInventory : Inventory + { + ItemContainer container; + + public ItemInventory(ItemContainer container, int capacity) + : base(capacity) + { + this.container = container; + } + + public override int CanBePut(Item item) + { + for (int i = 0; i < capacity; i++) + { + //item is already in the inventory! + if (items[i] == item) return -1; + } + + if (!container.CanBeContained(item)) return -1; + + for (int i = 0; i < capacity; i++) + { + if (items[i] == null) return i; + } + + return -1; + } + + public override bool CanBePut(Item item, int i) + { + if (i < 0 || i >= items.Length) return false; + return (item!=null && items[i]==null && container.CanBeContained(item)); + } + + public override bool TryPutItem(Item item, int i, bool createNetworkEvent) + { + bool wasPut = base.TryPutItem(item, i, createNetworkEvent); + + if (wasPut) + { + foreach (Character c in Character.characterList) + { + if (!c.HasSelectedItem(item)) continue; + + item.Unequip(c); + break; + } + item.container = container.Item; + container.IsActive = true; + } + return wasPut; + } + + } +} diff --git a/Subsurface/Items/ItemPrefab.cs b/Subsurface/Items/ItemPrefab.cs new file mode 100644 index 000000000..ed5e30a9b --- /dev/null +++ b/Subsurface/Items/ItemPrefab.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml.Linq; +using FarseerPhysics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace Subsurface +{ + class ItemPrefab : MapEntityPrefab + { + static string contentFolder = "Content/Items/"; + + string configFile; + + //should the camera focus on the construction when selected + protected bool focusOnSelected; + //the amount of "camera offset" when selecting the construction + protected float offsetOnSelected; + //default size + protected Vector2 size; + + //how close the character has to be to the item to pick it up + float pickDistance; + + + public List sounds; + + //an area next to the construction + //the construction can be Activated() by a character inside the area + public List triggers; + + public string ConfigFile + { + get { return configFile; } + } + + public float PickDistance + { + get { return pickDistance; } + } + + + public override bool IsLinkable + { + get { return isLinkable; } + } + + public bool FocusOnSelected + { + get { return focusOnSelected; } + } + + public float OffsetOnSelected + { + get { return offsetOnSelected; } + } + + public override void UpdatePlacing(SpriteBatch spriteBatch, Camera cam) + { + Vector2 position = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y); + position = cam.ScreenToWorld(position); + + if (!resizeHorizontal && !resizeVertical) + { + if (PlayerInput.LeftButtonClicked()) + { + new Item(new Rectangle((int)position.X, (int)position.Y, (int)sprite.size.X, (int)sprite.size.Y), this); + //constructor.Invoke(lobject); + + placePosition = Vector2.Zero; + + selected = null; + return; + } + + sprite.Draw(spriteBatch, new Vector2(position.X + sprite.size.X / 2.0f, -position.Y + sprite.size.Y / 2.0f)); + } + else + { + Vector2 placeSize = size; + + if (placePosition == Vector2.Zero) + { + if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed) + placePosition = position; + } + else + { + if (resizeHorizontal) + placeSize.X = Math.Max(position.X - placePosition.X, size.X); + if (resizeVertical) + placeSize.Y = Math.Max(placePosition.Y - position.Y, size.Y); + + if (PlayerInput.GetMouseState.LeftButton == ButtonState.Released) + { + new Item(new Rectangle((int)placePosition.X, (int)placePosition.Y, (int)placeSize.X, (int)placeSize.Y), this); + + selected = null; + return; + } + + position = placePosition; + } + + sprite.DrawTiled(spriteBatch, new Vector2(position.X, -position.Y), placeSize, Color.White); + } + + if (PlayerInput.GetMouseState.RightButton == ButtonState.Pressed) selected = null; + + } + + public static void LoadAll() + { + string[] files = Directory.GetFiles(contentFolder, "*.xml", SearchOption.AllDirectories); + + foreach (string filePath in files) + { + XDocument doc = ToolBox.TryLoadXml(filePath); + if (doc == null) return; + + if (doc.Root.Name.ToString().ToLower() == "item") + { + new ItemPrefab(doc.Root, filePath); + } + else + { + foreach (XElement element in doc.Root.Elements()) + { + if (element.Name.ToString().ToLower() != "item") continue; + + new ItemPrefab(element, filePath); + } + } + } + } + + public ItemPrefab (XElement element, string filePath) + { + + configFile = filePath; + + name = ToolBox.GetAttributeString(element, "name", ""); + if (name == "") DebugConsole.ThrowError("Unnamed item in "+filePath+"!"); + + //if (element.Attribute("sprite") != null) + //{ + // sprite = new Sprite(Path.GetDirectoryName(filePath) + "/" + element.Attribute("sprite").Value, new Vector2(0.5f, 0.5f)); + // sprite.Depth = 0.5f; + //} + + //var initableProperties = GetProperties(); + //foreach (ObjectProperty initableProperty in initableProperties) + //{ + // object value = ToolBox.GetAttributeObject(element, initableProperty.Name.ToLower()); + // if (value == null) + // { + // foreach (var ini in initableProperty.Attributes.OfType()) + // { + // value = ini.defaultValue; + // break; + // } + // } + + // initableProperty.TrySetValue(value); + //} + + pickDistance = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "pickdistance", 0.0f)); + + isLinkable = ToolBox.GetAttributeBool(element, "linkable", false); + + resizeHorizontal = ToolBox.GetAttributeBool(element, "resizehorizontal", false); + resizeVertical = ToolBox.GetAttributeBool(element, "resizevertical", false); + + focusOnSelected = ToolBox.GetAttributeBool(element, "focusonselected", false); + + offsetOnSelected = ToolBox.GetAttributeFloat(element, "offsetonselected", 0.0f); + + triggers = new List(); + foreach (XElement subElement in element.Elements()) + { + switch (subElement.Name.ToString().ToLower()) + { + case "sprite": + sprite = new Sprite(subElement, Path.GetDirectoryName(filePath) + "/"); + size = sprite.size; + break; + case "trigger": + Rectangle trigger = new Rectangle(0, 0, 10,10); + + trigger.X = ToolBox.GetAttributeInt(subElement, "x", 0); + trigger.Y = ToolBox.GetAttributeInt(subElement, "y", 0); + + trigger.Width = ToolBox.GetAttributeInt(subElement, "width", 0); + trigger.Height = ToolBox.GetAttributeInt(subElement, "height", 0); + + triggers.Add(trigger); + + break; + } + } + + sounds = new List(); + var soundElements = element.Descendants("Sound"); + foreach (XElement soundElement in soundElements) + { + string soundPath = ToolBox.GetAttributeString(soundElement, "path", ""); + if (soundPath == "") continue; + + Sound sound = Sound.Load(soundPath); + if (sound != null) sounds.Add(sound); + } + + list.Add(this); + } + } +} diff --git a/Subsurface/Items/RelatedItem.cs b/Subsurface/Items/RelatedItem.cs new file mode 100644 index 000000000..5ee8c0c54 --- /dev/null +++ b/Subsurface/Items/RelatedItem.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; + +namespace Subsurface +{ + class RelatedItem + { + [Flags] + public enum RelationType + { + None = 0, + Contained = 1, + Equipped = 2, + Picked = 3 + } + + string[] names; + + RelationType type; + + public readonly List statusEffects; + + //public string[] Names + //{ + // get { return names; } + //} + + public RelationType Type + { + get { return type; } + } + + public bool MatchesItem(Item item) + { + if (item == null) return false; + return names.Any(name => item.Name == name || item.HasTag(name)); + } + + public string JoinedNames + { + get { return string.Join(",", names); } + set + { + if (value == null) return; + + names = value.Split(','); + for (int i = 0; i < names.Length;i++ ) + { + names[i] = names[i].Trim(); + } + } + } + + + public string[] Names + { + get { return names; } + } + + public RelatedItem(string[] names) + { + for (int i = 0; i < names.Length; i++) + { + names[i] = names[i].Trim(); + } + this.names = names; + statusEffects = new List(); + } + + public void Save(XElement element) + { + element.Add( + new XAttribute("name", JoinedNames), + new XAttribute("type", type.ToString())); + } + + public static RelatedItem Load(XElement element) + { + string nameString = ToolBox.GetAttributeString(element, "name", ""); + if (nameString == "") return null; + + string[] names = nameString.Split(','); + + RelatedItem ri = new RelatedItem(names); + + try + { + ri.type = (RelationType)Enum.Parse(typeof(RelationType), ToolBox.GetAttributeString(element, "type", "None")); + } + + catch + { + ri.type = RelationType.None; + } + + foreach (XElement subElement in element.Elements()) + { + if (subElement.Name.ToString().ToLower() != "statuseffect") continue; + + ri.statusEffects.Add(StatusEffect.Load(subElement)); + } + + return ri; + } + } +} diff --git a/Subsurface/Lidgren.Network.XML b/Subsurface/Lidgren.Network.XML new file mode 100644 index 000000000..b84af9b9b --- /dev/null +++ b/Subsurface/Lidgren.Network.XML @@ -0,0 +1,2462 @@ + + + + Lidgren.Network + + + + + Interface for an encryption algorithm + + + + + NetPeer + + + + + Constructor + + + + + Encrypt an outgoing message in place + + + + + Decrypt an incoming message in place + + + + + Base for a non-threadsafe encryption class + + + + + NetBlockEncryptionBase constructor + + + + + Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted + + + + + Decrypt an incoming message encrypted with corresponding Encrypt + + message to decrypt + true if successful; false if failed + + + + Encrypt a block of bytes + + + + + Decrypt a block of bytes + + + + + Block size in bytes for this cipher + + + + + Example class; not very good encryption + + + + + NetXorEncryption constructor + + + + + NetXorEncryption constructor + + + + + Encrypt an outgoing message + + + + + Decrypt an incoming message + + + + + Methods to encrypt and decrypt data using the XTEA algorithm + + + + + 16 byte key + + + + + 16 byte key + + + + + String to hash for key + + + + + Encrypts a block of bytes + + + + + Decrypts a block of bytes + + + + + Gets the block size for this cipher + + + + + Lidgren Network Library + + + + + Big integer class based on BouncyCastle (http://www.bouncycastle.org) big integer code + + + + + Fixed size vector of booleans + + + + + NetBitVector constructor + + + + + Returns true if all bits/booleans are set to zero/false + + + + + Returns the number of bits/booleans set to one/true + + + + + + Shift all bits one step down, cycling the first bit to the top + + + + + Gets the first (lowest) index set to true + + + + + Gets the bit/bool at the specified index + + + + + Sets or clears the bit/bool at the specified index + + + + + Sets all bits/booleans to zero/false + + + + + Returns a string that represents this object + + + + + Gets the number of bits/booleans stored in this vector + + + + + Gets the bit/bool at the specified index + + + + + Helper class for NetBuffer to write/read bits + + + + + Read 1-8 bits from a buffer into a byte + + + + + Read several bytes from a buffer + + + + + Write 0-8 bits of data to buffer + + + + + Write several whole bytes + + + + + Reads an unsigned 16 bit integer + + + + + Reads the specified number of bits into an UInt32 + + + + + Writes an unsigned 16 bit integer + + + + + Writes the specified number of bits into a byte array + + + + + Writes the specified number of bits into a byte array + + + + + Write Base128 encoded variable sized unsigned integer + + number of bytes written + + + + Reads a UInt32 written using WriteUnsignedVarInt(); will increment offset! + + + + + Base class for NetIncomingMessage and NetOutgoingMessage + + + + + Number of bytes to overallocate for each message to avoid resizing + + + + + Gets the internal data buffer + + + + + Reads a 1-bit Boolean without advancing the read pointer + + + + + Reads a Byte without advancing the read pointer + + + + + Reads an SByte without advancing the read pointer + + + + + Reads the specified number of bits into a Byte without advancing the read pointer + + + + + Reads the specified number of bytes without advancing the read pointer + + + + + Reads the specified number of bytes without advancing the read pointer + + + + + Reads an Int16 without advancing the read pointer + + + + + Reads a UInt16 without advancing the read pointer + + + + + Reads an Int32 without advancing the read pointer + + + + + Reads the specified number of bits into an Int32 without advancing the read pointer + + + + + Reads a UInt32 without advancing the read pointer + + + + + Reads the specified number of bits into a UInt32 without advancing the read pointer + + + + + Reads a UInt64 without advancing the read pointer + + + + + Reads an Int64 without advancing the read pointer + + + + + Reads the specified number of bits into an UInt64 without advancing the read pointer + + + + + Reads the specified number of bits into an Int64 without advancing the read pointer + + + + + Reads a 32-bit Single without advancing the read pointer + + + + + Reads a 32-bit Single without advancing the read pointer + + + + + Reads a 64-bit Double without advancing the read pointer + + + + + Reads a string without advancing the read pointer + + + + + Reads a boolean value (stored as a single bit) written using Write(bool) + + + + + Reads a byte + + + + + Reads a byte and returns true or false for success + + + + + Reads a signed byte + + + + + Reads 1 to 8 bits into a byte + + + + + Reads the specified number of bytes + + + + + Reads the specified number of bytes and returns true for success + + + + + Reads the specified number of bytes into a preallocated array + + The destination array + The offset where to start writing in the destination array + The number of bytes to read + + + + Reads the specified number of bits into a preallocated array + + The destination array + The offset where to start writing in the destination array + The number of bits to read + + + + Reads a 16 bit signed integer written using Write(Int16) + + + + + Reads a 16 bit unsigned integer written using Write(UInt16) + + + + + Reads a 32 bit signed integer written using Write(Int32) + + + + + Reads a 32 bit signed integer written using Write(Int32) + + + + + Reads a signed integer stored in 1 to 32 bits, written using Write(Int32, Int32) + + + + + Reads an 32 bit unsigned integer written using Write(UInt32) + + + + + Reads an 32 bit unsigned integer written using Write(UInt32) and returns true for success + + + + + Reads an unsigned integer stored in 1 to 32 bits, written using Write(UInt32, Int32) + + + + + Reads a 64 bit unsigned integer written using Write(UInt64) + + + + + Reads a 64 bit signed integer written using Write(Int64) + + + + + Reads an unsigned integer stored in 1 to 64 bits, written using Write(UInt64, Int32) + + + + + Reads a signed integer stored in 1 to 64 bits, written using Write(Int64, Int32) + + + + + Reads a 32 bit floating point value written using Write(Single) + + + + + Reads a 32 bit floating point value written using Write(Single) + + + + + Reads a 32 bit floating point value written using Write(Single) + + + + + Reads a 64 bit floating point value written using Write(Double) + + + + + Reads a variable sized UInt32 written using WriteVariableUInt32() + + + + + Reads a variable sized UInt32 written using WriteVariableUInt32() and returns true for success + + + + + Reads a variable sized Int32 written using WriteVariableInt32() + + + + + Reads a variable sized Int64 written using WriteVariableInt64() + + + + + Reads a variable sized UInt32 written using WriteVariableInt64() + + + + + Reads a 32 bit floating point value written using WriteSignedSingle() + + The number of bits used when writing the value + A floating point value larger or equal to -1 and smaller or equal to 1 + + + + Reads a 32 bit floating point value written using WriteUnitSingle() + + The number of bits used when writing the value + A floating point value larger or equal to 0 and smaller or equal to 1 + + + + Reads a 32 bit floating point value written using WriteRangedSingle() + + The minimum value used when writing the value + The maximum value used when writing the value + The number of bits used when writing the value + A floating point value larger or equal to MIN and smaller or equal to MAX + + + + Reads a 32 bit integer value written using WriteRangedInteger() + + The minimum value used when writing the value + The maximum value used when writing the value + A signed integer value larger or equal to MIN and smaller or equal to MAX + + + + Reads a string written using Write(string) + + + + + Reads a string written using Write(string) and returns true for success + + + + + Reads a value, in local time comparable to NetTime.Now, written using WriteTime() for the connection supplied + + + + + Reads a stored IPv4 endpoint description + + + + + Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes. + + + + + Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes. + + + + + Pads data with the specified number of bits. + + + + + Reads all public and private declared instance fields of the object in alphabetical order using reflection + + + + + Reads all fields with the specified binding of the object in alphabetical order using reflection + + + + + Ensures the buffer can hold this number of bits + + + + + Ensures the buffer can hold this number of bits + + + + + Writes a boolean value using 1 bit + + + + + Write a byte + + + + + Writes a signed byte + + + + + Writes 1 to 8 bits of a byte + + + + + Writes all bytes in an array + + + + + Writes the specified number of bytes from an array + + + + + Writes an unsigned 16 bit integer + + + + + + Writes a 16 bit unsigned integer at a given offset in the buffer + + + + + Writes an unsigned integer using 1 to 16 bits + + + + + Writes a signed 16 bit integer + + + + + Writes a 16 bit signed integer at a given offset in the buffer + + + + + Writes a 32 bit signed integer + + + + + Writes a 32 bit signed integer at a given offset in the buffer + + + + + Writes a 32 bit unsigned integer + + + + + Writes a 32 bit unsigned integer at a given offset in the buffer + + + + + Writes a 32 bit signed integer + + + + + Writes a signed integer using 1 to 32 bits + + + + + Writes a 64 bit unsigned integer + + + + + Writes a 64 bit unsigned integer at a given offset in the buffer + + + + + Writes an unsigned integer using 1 to 64 bits + + + + + Writes a 64 bit signed integer + + + + + Writes a signed integer using 1 to 64 bits + + + + + Writes a 32 bit floating point value + + + + + Writes a 64 bit floating point value + + + + + Write Base128 encoded variable sized unsigned integer of up to 32 bits + + number of bytes written + + + + Write Base128 encoded variable sized signed integer of up to 32 bits + + number of bytes written + + + + Write Base128 encoded variable sized signed integer of up to 64 bits + + number of bytes written + + + + Write Base128 encoded variable sized unsigned integer of up to 64 bits + + number of bytes written + + + + Compress (lossy) a float in the range -1..1 using numberOfBits bits + + + + + Compress (lossy) a float in the range 0..1 using numberOfBits bits + + + + + Compress a float within a specified range using a certain number of bits + + + + + Writes an integer with the least amount of bits need for the specified range + Returns number of bits written + + + + + Write a string + + + + + Writes an endpoint description + + + + + Writes the current local time to a message; readable (and convertable to local time) by the remote host using ReadTime() + + + + + Writes a local timestamp to a message; readable (and convertable to local time) by the remote host using ReadTime() + + + + + Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes. + + + + + Pads data with the specified number of bits. + + + + + Append all the bits of message to this message + + + + + Writes all public and private declared instance fields of the object in alphabetical order using reflection + + + + + Writes all fields with specified binding in alphabetical order using reflection + + + + + Gets or sets the internal data buffer + + + + + Gets or sets the length of the used portion of the buffer in bytes + + + + + Gets or sets the length of the used portion of the buffer in bits + + + + + Gets or sets the read position in the buffer, in bits (not bytes) + + + + + Gets the position in the buffer in bytes; note that the bits of the first returned byte may already have been read - check the Position property to make sure. + + + + + Utility struct for writing Singles + + + + + Value as a 32 bit float + + + + + Value as an unsigned 32 bit integer + + + + + Specialized version of NetPeer used for a "client" connection. It does not accept any incoming connections and maintains a ServerConnection property + + + + + Represents a local peer capable of holding zero, one or more connections to remote peers + + + + + Send NetIntroduction to hostExternal and clientExternal; introducing client to host + + + + + Called when host/client receives a NatIntroduction message from a master server + + + + + Called when receiving a NatPunchMessage from a remote endpoint + + + + + NetPeer constructor + + + + + Binds to socket and spawns the networking thread + + + + + Get the connection, if any, for a certain remote endpoint + + + + + Read a pending message from any connection, blocking up to maxMillis if needed + + + + + Read a pending message from any connection, if any + + + + + Read a pending message from any connection, if any + + + + + Create a connection to a remote endpoint + + + + + Create a connection to a remote endpoint + + + + + Create a connection to a remote endpoint + + + + + Create a connection to a remote endpoint + + + + + Send raw bytes; only used for debugging + + + + + In DEBUG, throws an exception, in RELEASE logs an error message + + + + + + Disconnects all active connections and closes the socket + + + + + Emit a discovery signal to all hosts on your subnet + + + + + Emit a discovery signal to a single known host + + + + + Emit a discovery signal to a single known host + + + + + Send a discovery response message + + + + + Call this to register a callback for when a new message arrives + + + + + Call this to unregister a callback, but remember to do it in the same synchronization context! + + + + + If NetPeerConfiguration.AutoFlushSendQueue() is false; you need to call this to send all messages queued using SendMessage() + + + + + Creates a new message for sending + + + + + Creates a new message for sending and writes the provided string to it + + + + + Creates a new message for sending + + initial capacity in bytes + + + + Recycles a NetIncomingMessage instance for reuse; taking pressure off the garbage collector + + + + + Recycles a list of NetIncomingMessage instances for reuse; taking pressure off the garbage collector + + + + + Creates an incoming message with the required capacity for releasing to the application + + + + + Send a message to a specific connection + + The message to send + The recipient connection + How to deliver the message + + + + Send a message to a specific connection + + The message to send + The recipient connection + How to deliver the message + Sequence channel within the delivery method + + + + Send a message to a list of connections + + The message to send + The list of recipients to send to + How to deliver the message + Sequence channel within the delivery method + + + + Send a message to an unconnected host + + + + + Send a message to an unconnected host + + + + + Send a message to an unconnected host + + + + + Send a message to this exact same netpeer (loopback) + + + + + Gets the NetPeerStatus of the NetPeer + + + + + Signalling event which can be waited on to determine when a message is queued for reading. + Note that there is no guarantee that after the event is signaled the blocked thread will + find the message in the queue. Other user created threads could be preempted and dequeue + the message before the waiting thread wakes up. + + + + + Gets a unique identifier for this NetPeer based on Mac address and ip/port. Note! Not available until Start() has been called! + + + + + Gets the port number this NetPeer is listening and sending on, if Start() has been called + + + + + Returns an UPnP object if enabled in the NetPeerConfiguration + + + + + Gets or sets the application defined object containing data about the peer + + + + + Gets a copy of the list of connections + + + + + Gets the number of active connections + + + + + Statistics on this NetPeer since it was initialized + + + + + Gets the configuration used to instanciate this NetPeer + + + + + Gets the socket, if Start() has been called + + + + + NetClient constructor + + + + + + Connect to a remote server + + The remote endpoint to connect to + The hail message to pass + server connection, or null if already connected + + + + Disconnect from server + + reason for disconnect + + + + Sends message to server + + + + + Sends message to server + + + + + Returns a string that represents this object + + + + + Gets the connection to the server, if any + + + + + Gets the connection status of the server connection (or NetConnectionStatus.Disconnected if no connection) + + + + + Represents a connection to a remote peer + + + + + Change the internal endpoint to this new one. Used when, during handshake, a switch in port is detected (due to NAT) + + + + + Send a message to this remote connection + + The message to send + How to deliver the message + Sequence channel within the delivery method + + + + Zero windowSize indicates that the channel is not yet instantiated (used) + Negative freeWindowSlots means this amount of messages are currently queued but delayed due to closed window + + + + + Returns a string that represents this object + + + + + Approves this connection; sending a connection response to the remote host + + + + + Approves this connection; sending a connection response to the remote host + + The local hail message that will be set as RemoteHailMessage on the remote host + + + + Denies this connection; disconnecting it + + + + + Denies this connection; disconnecting it + + The stated reason for the disconnect, readable as a string in the StatusChanged message on the remote host + + + + Disconnect from the remote peer + + the message to send with the disconnect message + + + + Gets local time value comparable to NetTime.Now from a remote value + + + + + Gets the remote time value for a local time value produced by NetTime.Now + + + + + Gets or sets the application defined object containing data about the connection + + + + + Gets the peer which holds this connection + + + + + Gets the current status of the connection (synced to the last status message read) + + + + + Gets various statistics for this connection + + + + + Gets the remote endpoint for the connection + + + + + Gets the unique identifier of the remote NetPeer for this connection + + + + + Gets the local hail message that was sent as part of the handshake + + + + + The message that the remote part specified via Connect() or Approve() - can be null. + + + + + Gets the current average roundtrip time in seconds + + + + + Time offset between this peer and the remote peer + + + + + Gets the current MTU in bytes. If PeerConfiguration.AutoExpandMTU is false, this will be PeerConfiguration.MaximumTransmissionUnit. + + + + + Statistics for a NetConnection instance + + + + + Returns a string that represents this object + + + + + Gets the number of sent packets for this connection + + + + + Gets the number of received packets for this connection + + + + + Gets the number of sent bytes for this connection + + + + + Gets the number of received bytes for this connection + + + + + Gets the number of resent reliable messages for this connection + + + + + Status for a NetConnection instance + + + + + No connection, or attempt, in place + + + + + Connect has been sent; waiting for ConnectResponse + + + + + Connect was received, but ConnectResponse hasn't been sent yet + + + + + Connect was received and ApprovalMessage released to the application; awaiting Approve() or Deny() + + + + + Connect was received and ConnectResponse has been sent; waiting for ConnectionEstablished + + + + + Connected + + + + + In the process of disconnecting + + + + + Disconnected + + + + + All the constants used when compiling the library + + + + + Number of channels which needs a sequence number to work + + + + + Number of reliable channels + + + + + How the library deals with resends and handling of late messages + + + + + Indicates an error + + + + + Unreliable, unordered delivery + + + + + Unreliable delivery, but automatically dropping late messages + + + + + Reliable delivery, but unordered + + + + + Reliable delivery, except for late messages which are dropped + + + + + Reliable, ordered delivery + + + + + Exception thrown in the Lidgren Network Library + + + + + NetException constructor + + + + + NetException constructor + + + + + NetException constructor + + + + + Throws an exception, in DEBUG only, if first parameter is false + + + + + Throws an exception, in DEBUG only, if first parameter is false + + + + + Incoming message either sent from a remote peer or generated within the library + + + + + Decrypt a message + + The encryption algorithm used to encrypt the message + true on success + + + + Reads a value, in local time comparable to NetTime.Now, written using WriteTime() + Must have a connected sender + + + + + Returns a string that represents this object + + + + + Gets the type of this incoming message + + + + + Gets the delivery method this message was sent with (if user data) + + + + + Gets the sequence channel this message was sent with (if user data) + + + + + endpoint of sender, if any + + + + + NetConnection of sender, if any + + + + + What local time the message was received from the network + + + + + The type of a NetIncomingMessage + + + + + Error; this value should never appear + + + + + Status for a connection changed + + + + + Data sent using SendUnconnectedMessage + + + + + Connection approval is needed + + + + + Application data + + + + + Receipt of delivery + + + + + Discovery request for a response + + + + + Discovery response to a request + + + + + Verbose debug message + + + + + Debug message + + + + + Warning message + + + + + Error message + + + + + NAT introduction was successful + + + + + A roundtrip was measured and NetConnection.AverageRoundtripTime was updated + + + + + Outgoing message used to send data to remote peer(s) + + + + + Encrypt this message using the provided algorithm; no more writing can be done before sending it or the message will be corrupt! + + + + + Returns a string that represents this object + + + + + Partly immutable after NetPeer has been initialized + + + + + Default MTU value in bytes + + + + + NetPeerConfiguration constructor + + + + + Enables receiving of the specified type of message + + + + + Disables receiving of the specified type of message + + + + + Enables or disables receiving of the specified type of message + + + + + Gets if receiving of the specified type of message is enabled + + + + + Creates a memberwise shallow clone of this configuration + + + + + Gets the identifier of this application; the library can only connect to matching app identifier peers + + + + + Gets or sets the behaviour of unreliable sends above MTU + + + + + Gets or sets the name of the library network thread. Cannot be changed once NetPeer is initialized. + + + + + Gets or sets the maximum amount of connections this peer can hold. Cannot be changed once NetPeer is initialized. + + + + + Gets or sets the maximum amount of bytes to send in a single packet, excluding ip, udp and lidgren headers. Cannot be changed once NetPeer is initialized. + + + + + Gets or sets the default capacity in bytes when NetPeer.CreateMessage() is called without argument + + + + + Gets or sets the time between latency calculating pings + + + + + Gets or sets if the library should recycling messages to avoid excessive garbage collection. Cannot be changed once NetPeer is initialized. + + + + + Gets or sets the maximum number of incoming/outgoing messages to keep in the recycle cache. + + + + + Gets or sets the number of seconds timeout will be postponed on a successful ping/pong + + + + + Enables UPnP support; enabling port forwarding and getting external ip + + + + + Enables or disables automatic flushing of the send queue. If disabled, you must manully call NetPeer.FlushSendQueue() to flush sent messages to network. + + + + + If true, will not send acks for unreliable unordered messages. This will save bandwidth, but disable flow control and duplicate detection for this type of messages. + + + + + Gets or sets the local ip address to bind to. Defaults to IPAddress.Any. Cannot be changed once NetPeer is initialized. + + + + + Gets or sets the local broadcast address to use when broadcasting + + + + + Gets or sets the local port to bind to. Defaults to 0. Cannot be changed once NetPeer is initialized. + + + + + Gets or sets the size in bytes of the receiving buffer. Defaults to 131071 bytes. Cannot be changed once NetPeer is initialized. + + + + + Gets or sets the size in bytes of the sending buffer. Defaults to 131071 bytes. Cannot be changed once NetPeer is initialized. + + + + + Gets or sets if the NetPeer should accept incoming connections. This is automatically set to true in NetServer and false in NetClient. + + + + + Gets or sets the number of seconds between handshake attempts + + + + + Gets or sets the maximum number of handshake attempts before failing to connect + + + + + Gets or sets if the NetPeer should send large messages to try to expand the maximum transmission unit size + + + + + Gets or sets how often to send large messages to expand MTU if AutoExpandMTU is enabled + + + + + Gets or sets the number of failed expand mtu attempts to perform before setting final MTU + + + + + Gets or sets the simulated amount of sent packets lost from 0.0f to 1.0f + + + + + Gets or sets the minimum simulated amount of one way latency for sent packets in seconds + + + + + Gets or sets the simulated added random amount of one way latency for sent packets in seconds + + + + + Gets the average simulated one way latency in seconds + + + + + Gets or sets the simulated amount of duplicated packets from 0.0f to 1.0f + + + + + Behaviour of unreliable sends above MTU + + + + + Sending an unreliable message will ignore MTU and send everything in a single packet; this is the new default + + + + + Old behaviour; use normal fragmentation for unreliable messages - if a fragment is dropped, memory for received fragments are never reclaimed! + + + + + Alternate behaviour; just drops unreliable messages above MTU + + + + + Statistics for a NetPeer instance + + + + + Returns a string that represents this object + + + + + Gets the number of sent packets since the NetPeer was initialized + + + + + Gets the number of received packets since the NetPeer was initialized + + + + + Gets the number of sent messages since the NetPeer was initialized + + + + + Gets the number of received messages since the NetPeer was initialized + + + + + Gets the number of sent bytes since the NetPeer was initialized + + + + + Gets the number of received bytes since the NetPeer was initialized + + + + + Gets the number of bytes allocated (and possibly garbage collected) for message storage + + + + + Gets the number of bytes in the recycled pool + + + + + Status for a NetPeer instance + + + + + NetPeer is not running; socket is not bound + + + + + NetPeer is in the process of starting up + + + + + NetPeer is bound to socket and listening for packets + + + + + Shutdown has been requested and will be executed shortly + + + + + Thread safe (blocking) expanding queue with TryDequeue() and EnqueueFirst() + + + + + NetQueue constructor + + + + + Adds an item last/tail of the queue + + + + + Adds an item last/tail of the queue + + + + + Places an item first, at the head of the queue + + + + + Gets an item from the head of the queue, or returns default(T) if empty + + + + + Gets all items from the head of the queue, or returns number of items popped + + + + + Returns default(T) if queue is empty + + + + + Determines whether an item is in the queue + + + + + Copies the queue items to a new array + + + + + Removes all objects from the queue + + + + + Gets the number of items in the queue + + + + + Gets the current capacity for the queue + + + + + NetRandom base class + + + + + Get global instance of NetRandom (uses MWCRandom) + + + + + Constructor with randomized seed + + + + + Constructor with provided 32 bit seed + + + + + (Re)initialize this instance with provided 32 bit seed + + + + + Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively + + + + + Generates a random value that is greater or equal than 0 and less than Int32.MaxValue + + + + + Generates a random value greater or equal than 0 and less or equal than Int32.MaxValue (inclusively) + + + + + Returns random value larger or equal to 0.0 and less than 1.0 + + + + + Returns random value is greater or equal than 0.0 and less than 1.0 + + + + + Returns random value is greater or equal than 0.0f and less than 1.0f + + + + + Returns a random value is greater or equal than 0 and less than maxValue + + + + + Returns a random value is greater or equal than minValue and less than maxValue + + + + + Generates a random value between UInt64.MinValue to UInt64.MaxValue + + + + + Returns true or false, randomly + + + + + Fills all bytes from offset to offset + length in buffer with random values + + + + + Fill the specified buffer with random values + + + + + Multiply With Carry random + + + + + Get global instance of MWCRandom + + + + + Constructor with randomized seed + + + + + (Re)initialize this instance with provided 32 bit seed + + + + + (Re)initialize this instance with provided 64 bit seed + + + + + Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively + + + + + Xor Shift based random + + + + + Get global instance of XorShiftRandom + + + + + Constructor with randomized seed + + + + + Constructor with provided 64 bit seed + + + + + (Re)initialize this instance with provided 32 bit seed + + + + + (Re)initialize this instance with provided 64 bit seed + + + + + Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively + + + + + Mersenne Twister based random + + + + + Get global instance of MersenneTwisterRandom + + + + + Constructor with randomized seed + + + + + Constructor with provided 32 bit seed + + + + + (Re)initialize this instance with provided 32 bit seed + + + + + Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively + + + + + RNGCryptoServiceProvider based random; very slow but cryptographically safe + + + + + Global instance of CryptoRandom + + + + + Seed in CryptoRandom does not create deterministic sequences + + + + + Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively + + + + + Fill the specified buffer with random values + + + + + Fills all bytes from offset to offset + length in buffer with random values + + + + + Class for generating random seeds + + + + + Generates a 32 bit random seed + + + + + Generates a 64 bit random seed + + + + + Sender part of Selective repeat ARQ for a particular NetChannel + + + + + Result of a SendMessage call + + + + + Message failed to enqueue because there is no connection + + + + + Message was immediately sent + + + + + Message was queued for delivery + + + + + Message was dropped immediately since too many message were queued + + + + + Specialized version of NetPeer used for "server" peers + + + + + NetServer constructor + + + + + Send a message to all connections + + The message to send + How to deliver the message + + + + Send a message to all connections except one + + The message to send + How to deliver the message + Don't send to this particular connection + Which sequence channel to use for the message + + + + Returns a string that represents this object + + + + + Helper methods for implementing SRP authentication + + + + + Compute multiplier (k) + + + + + Create 16 bytes of random salt + + + + + Create 32 bytes of random ephemeral value + + + + + Computer private key (x) + + + + + Creates a verifier that the server can later use to authenticate users later on (v) + + + + + Compute client public ephemeral value (A) + + + + + Compute server ephemeral value (B) + + + + + Compute intermediate value (u) + + + + + Computes the server session value + + + + + Computes the client session value + + + + + Create XTEA symmetrical encryption object from sessionValue + + + + + Time service + + + + + Given seconds it will output a human friendly readable string (milliseconds if less than 60 seconds) + + + + + Get number of seconds since the application started + + + + + Sender part of Selective repeat ARQ for a particular NetChannel + + + + + Status of the UPnP capabilities + + + + + Still discovering UPnP capabilities + + + + + UPnP is not available + + + + + UPnP is available and ready to use + + + + + UPnP support class + + + + + NetUPnP constructor + + + + + Add a forwarding rule to the router using UPnP + + + + + Delete a forwarding rule from the router using UPnP + + + + + Retrieve the extern ip using UPnP + + + + + Status of the UPnP capabilities of this NetPeer + + + + + Utility methods + + + + + Get IPv4 endpoint from notation (xxx.xxx.xxx.xxx) or hostname and port number (asynchronous version) + + + + + Get IPv4 endpoint from notation (xxx.xxx.xxx.xxx) or hostname and port number + + + + + Get IPv4 address from notation (xxx.xxx.xxx.xxx) or hostname (asynchronous version) + + + + + Get IPv4 address from notation (xxx.xxx.xxx.xxx) or hostname + + + + + Create a hex string from an Int64 value + + + + + Create a hex string from an array of bytes + + + + + Create a hex string from an array of bytes + + + + + Returns true if the endpoint supplied is on the same subnet as this host + + + + + Returns true if the IPAddress supplied is on the same subnet as this host + + + + + Returns how many bits are necessary to hold a certain number + + + + + Returns how many bytes are required to hold a certain number of bits + + + + + Convert a hexadecimal string to a byte array + + + + + Converts a number of bytes to a shorter, more readable string representation + + + + + Gets the window size used internally in the library for a certain delivery method + + + + + Creates a comma delimited string from a lite of items + + + + + If available, returns the bytes of the physical (MAC) address for the first usable network interface + + + + + Gets my local IPv4 address (not necessarily external) and subnet mask + + + + + Resolve endpoint callback + + + + + Resolve address callback + + + + diff --git a/Subsurface/Lidgren.Network.dll b/Subsurface/Lidgren.Network.dll new file mode 100644 index 000000000..52f7f22d7 Binary files /dev/null and b/Subsurface/Lidgren.Network.dll differ diff --git a/Subsurface/Lidgren.Network.pdb b/Subsurface/Lidgren.Network.pdb new file mode 100644 index 000000000..34c246dbe Binary files /dev/null and b/Subsurface/Lidgren.Network.pdb differ diff --git a/Subsurface/Map/Entity.cs b/Subsurface/Map/Entity.cs new file mode 100644 index 000000000..756448cc5 --- /dev/null +++ b/Subsurface/Map/Entity.cs @@ -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 dictionary = new Dictionary(); + + 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) { } + + /// + /// Find an entity based on the ID + /// + public static Entity FindEntityByID(int ID) + { + Entity matchingEntity; + dictionary.TryGetValue(ID, out matchingEntity); + + return matchingEntity; + } + + public static void RemoveAll() + { + List list = new List(dictionary.Values); + foreach (Entity e in list) + { + e.Remove(); + } + } + + public virtual void Remove() + { + dictionary.Remove(this.ID); + } + } +} diff --git a/Subsurface/Map/Explosion.cs b/Subsurface/Map/Explosion.cs new file mode 100644 index 000000000..bc9960482 --- /dev/null +++ b/Subsurface/Map/Explosion.cs @@ -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; i0.0f) + { + List structureList = new List(); + + 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); + } + } + } + } + } +} diff --git a/Subsurface/Map/Gap.cs b/Subsurface/Map/Gap.cs new file mode 100644 index 000000000..c19975db3 --- /dev/null +++ b/Subsurface/Map/Gap.cs @@ -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(); + + //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(); + + 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 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 i = 0; + //while (element.Attribute("linkedto" + i) != null) + //{ + // g.linkedToID.Add(int.Parse(element.Attribute("linkedto" + i).Value)); + // i += 1; + //} + } + } +} diff --git a/Subsurface/Map/Hull.cs b/Subsurface/Map/Hull.cs new file mode 100644 index 000000000..c16135c64 --- /dev/null +++ b/Subsurface/Map/Hull.cs @@ -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 hullList = new List(); + + 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 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() + .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 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 list = new List(); + 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(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(PrimitiveType.TriangleStrip, shadowVertices, 0, shadowVertexCount * 2 - 2); + + } + + public void Remove() + { + list.Remove(this); + } + + + } + +} diff --git a/Subsurface/Map/Lights/LightManager.cs b/Subsurface/Map/Lights/LightManager.cs new file mode 100644 index 000000000..f8f7f306f --- /dev/null +++ b/Subsurface/Map/Lights/LightManager.cs @@ -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); + } + } + } + +} diff --git a/Subsurface/Map/Map.cs b/Subsurface/Map/Map.cs new file mode 100644 index 000000000..f2c3deb42 --- /dev/null +++ b/Subsurface/Map/Map.cs @@ -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 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(); + } + + } +} diff --git a/Subsurface/Map/MapEntity.cs b/Subsurface/Map/MapEntity.cs new file mode 100644 index 000000000..f05179937 --- /dev/null +++ b/Subsurface/Map/MapEntity.cs @@ -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 mapEntityList = new List(); + + //which entities have been selected for editing + protected static List selectedList = new List(); + + protected static GUIComponent editingHUD; + + protected static Vector2 selectionPos = Vector2.Zero; + protected static Vector2 selectionSize = Vector2.Zero; + + protected static Vector2 startMovingPos = Vector2.Zero; + + protected List linkedToID; + + //observable collection because some entities may need to be notified when the collection is modified + public ObservableCollection 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(); + } + } + + /// + /// Call Update() on every object in Entity.list + /// + 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) { } + + /// + /// Update the selection logic in editmap-screen + /// + 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 newSelection = new List();// 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"); + } + + + } + + } + + /// + /// Draw the "selection rectangle" and outlines of entities that are being dragged (if any) + /// + 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); + } + } + + /// + /// Call DrawEditing() if only one entity is selected + /// + 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 FindMapEntities(Vector2 pos) + { + List foundEntities = new List(); + 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; + } + + /// + /// Find entities whose rect intersects with the "selection rect" + /// + public static List FindSelectedEntities(Vector2 pos, Vector2 size) + { + List foundEntities = new List(); + + 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; + } + + /// + /// 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) + /// + 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); + } + + } +} diff --git a/Subsurface/Map/MapEntityPrefab.cs b/Subsurface/Map/MapEntityPrefab.cs new file mode 100644 index 000000000..5112cca2c --- /dev/null +++ b/Subsurface/Map/MapEntityPrefab.cs @@ -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 list = new List(); + + 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); + } + + } +} diff --git a/Subsurface/Map/Structure.cs b/Subsurface/Map/Structure.cs new file mode 100644 index 000000000..b6ff4a620 --- /dev/null +++ b/Subsurface/Map/Structure.cs @@ -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 wallList = new List(); + + ConvexHull convexHull; + + StructurePrefab prefab; + + //farseer physics bodies, separated by gaps + List 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(); + //gaps = new List(); + + 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 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); + } + } + + } +} diff --git a/Subsurface/Map/StructurePrefab.cs b/Subsurface/Map/StructurePrefab.cs new file mode 100644 index 000000000..4da137408 --- /dev/null +++ b/Subsurface/Map/StructurePrefab.cs @@ -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 list = new List(); + + //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; + } + } +} diff --git a/Subsurface/Map/WaterRenderer.cs b/Subsurface/Map/WaterRenderer.cs new file mode 100644 index 000000000..c094f9aab --- /dev/null +++ b/Subsurface/Map/WaterRenderer.cs @@ -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("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; + } + } + } + + + } +} diff --git a/Subsurface/Map/WayPoint.cs b/Subsurface/Map/WayPoint.cs new file mode 100644 index 000000000..76078fe76 --- /dev/null +++ b/Subsurface/Map/WayPoint.cs @@ -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(); + + 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 wayPoints = new List(); + + 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 i = 0; + while (element.Attribute("linkedto" + i) != null) + { + w.linkedToID.Add(int.Parse(element.Attribute("linkedto" + i).Value)); + i += 1; + } + } + + } +} diff --git a/Subsurface/NVorbis.dll b/Subsurface/NVorbis.dll new file mode 100644 index 000000000..486be3646 Binary files /dev/null and b/Subsurface/NVorbis.dll differ diff --git a/Subsurface/Networking/GameClient.cs b/Subsurface/Networking/GameClient.cs new file mode 100644 index 000000000..e3c6d7e38 --- /dev/null +++ b/Subsurface/Networking/GameClient.cs @@ -0,0 +1,384 @@ +using System; +using System.Diagnostics; +using Lidgren.Network; +using Microsoft.Xna.Framework; + +namespace Subsurface.Networking +{ + + + class GameClient : NetworkMember + { + NetClient Client; + + private Character myCharacter; + private CharacterInfo characterInfo; + + string name; + + // Create timer that tells client, when to send update + // System.Timers.Timer update; + + public Character Character + { + get { return myCharacter; } + set { myCharacter = value; } + } + + public CharacterInfo CharacterInfo + { + get { return characterInfo; } + } + + public string Name + { + get { return name; } + set + { + if (string.IsNullOrEmpty(name)) return; + name = value; + } + } + + public GameClient(string name) + { + this.name = name; + + this.characterInfo = new CharacterInfo("Content/Characters/Human/human.xml", name); + } + + public bool ConnectToServer(string hostIP) + { + myCharacter = Character.Controlled; + + // Create new instance of configs. Parameter is "application Id". It has to be same on client and server. + NetPeerConfiguration Config = new NetPeerConfiguration("subsurface"); + + //Config.SimulatedLoss = 0.2f; + //Config.SimulatedMinimumLatency = 0.25f; + + // Create new client, with previously created configs + Client = new NetClient(Config); + + NetOutgoingMessage outmsg = Client.CreateMessage(); + Client.Start(); + + outmsg.Write((byte)PacketTypes.Login); + outmsg.Write(Game1.version.ToString()); + outmsg.Write(name); + + // Connect client, to ip previously requested from user + try + { + Client.Connect(hostIP, 14242, outmsg); + } + catch (ArgumentNullException e) + { + DebugConsole.ThrowError("Couldn't connect to "+hostIP+". Error message: "+e.Message); + return false; + } + + // Create timespan of 30ms + updateInterval = new TimeSpan(0, 0, 0, 0, 200); + + // Set timer to tick every 50ms + //update = new System.Timers.Timer(50); + + // When time has elapsed ( 50ms in this case ), call "update_Elapsed" funtion + //update.Elapsed += new System.Timers.ElapsedEventHandler(Update); + + // Funtion that waits for connection approval info from server + WaitForStartingInfo(); + + if (Client.ConnectionStatus!=NetConnectionStatus.Connected) + { + DebugConsole.ThrowError("Couldn't connect to server"); + return false; + } + else + { + return true; + } + + // Start the timer + //update.Start(); + + + } + + // Before main looping starts, we loop here and wait for approval message + private void WaitForStartingInfo() + { + // When this is set to true, we are approved and ready to go + bool CanStart = false; + + DateTime timeOut = DateTime.Now + new TimeSpan(0,0,5); + + // Loop untill we are approved + while (!CanStart) + { + if (DateTime.Now>timeOut) return; + + NetIncomingMessage inc; + // If new messages arrived + if ((inc = Client.ReadMessage()) == null) continue; + + // Switch based on the message types + switch (inc.MessageType) + { + // All manually sent messages are type of "Data" + case NetIncomingMessageType.Data: + if (inc.ReadByte() == (byte)PacketTypes.LoggedIn) + { + //add the names of other connected clients to the lobby screen + int existingClients = inc.ReadInt32(); + for (int i = 1; i <= existingClients; i++) + { + Game1.netLobbyScreen.AddPlayer(inc.ReadString()); + } + + //add the name of own client to the lobby screen + Game1.netLobbyScreen.AddPlayer(name); + + CanStart = true; + } + else if (inc.ReadByte() == (byte)PacketTypes.KickedOut) + { + string msg = inc.ReadString(); + DebugConsole.ThrowError(msg); + + Game1.mainMenuScreen.Select(); + } + break; + case NetIncomingMessageType.StatusChanged: + Debug.WriteLine((NetConnectionStatus)inc.ReadByte()); + + break; + default: + // Should not happen and if happens, don't care + Console.WriteLine(inc.ReadString() + " Strange message"); + break; + } + + } + } + + public void Update() + { + if (updateTimer > DateTime.Now) return; + + if (myCharacter!=null) + { + if (myCharacter.IsDead) + { + Character.Controlled = null; + Game1.gameScreen.Cam.TargetPos = Vector2.Zero; + Game1.gameScreen.Cam.Zoom = 1.0f; + } + else + { + if (gameStarted) new NetworkEvent(myCharacter.ID, true); + } + } + + foreach (NetworkEvent networkEvent in NetworkEvent.events) + { + NetOutgoingMessage message = Client.CreateMessage(); + message.Write((byte)PacketTypes.NetworkEvent); + + + if (networkEvent.FillData(message)) + { + Client.SendMessage(message, + (networkEvent.IsImportant) ? NetDeliveryMethod.ReliableUnordered : NetDeliveryMethod.Unreliable); + } + } + + NetworkEvent.events.Clear(); + + + CheckServerMessages(); + + // Update current time + updateTimer = DateTime.Now + updateInterval; + + + } + + /// + /// Check for new incoming messages from server + /// + private void CheckServerMessages() + { + // Create new incoming message holder + NetIncomingMessage inc; + + while ((inc = Client.ReadMessage()) != null) + { + if (inc.MessageType != NetIncomingMessageType.Data) continue; + + switch (inc.ReadByte()) + { + case (byte)PacketTypes.StartGame: + if (gameStarted) continue; + + int seed = inc.ReadInt32(); + Game1.random = new Random(seed); + + string mapFile = inc.ReadString(); + Map.Load(mapFile); + + double durationMinutes = inc.ReadDouble(); + + TimeSpan duration = new TimeSpan(0,(int)durationMinutes,0); + + //int gameModeIndex = inc.ReadInt32(); + Game1.gameSession = new GameSession("", false, duration); + Game1.gameSession.StartShift(1); + + myCharacter = ReadCharacterData(inc); + Character.Controlled = myCharacter; + + int count = inc.ReadInt32(); + for (int n = 0; n < count; n++) + { + ReadCharacterData(inc); + } + + gameStarted = true; + + Game1.gameScreen.Select(); + + AddChatMessage("Press TAB to chat", ChatMessageType.Server); + + break; + case (byte)PacketTypes.EndGame: + string endMessage = inc.ReadString(); + EndGame(endMessage); + break; + case (byte)PacketTypes.PlayerJoined: + + Client otherClient = new Client(); + otherClient.name = inc.ReadString(); + + Game1.netLobbyScreen.AddPlayer(otherClient.name); + + //string newPlayerName = inc.ReadString(); + //int newPlayerID = inc.ReadInt32(); + + //CharacterInfo ch = new CharacterInfo("Content/Characters/Human/human.xml", newPlayerName); + //ch.ID = newPlayerID; + + //Character.newCharacterQueue.Enqueue(ch); + + AddChatMessage(otherClient.name + " has joined the server", ChatMessageType.Server); + + break; + case (byte)PacketTypes.PlayerLeft: + string leavingName = inc.ReadString(); + + AddChatMessage(inc.ReadString(), ChatMessageType.Server); + Game1.netLobbyScreen.RemovePlayer(leavingName); + break; + + case (byte)PacketTypes.KickedOut: + string msg= inc.ReadString(); + + DebugConsole.ThrowError(msg); + + Game1.mainMenuScreen.Select(); + + break; + case (byte)PacketTypes.Chatmessage: + ChatMessageType messageType = (ChatMessageType)inc.ReadByte(); + AddChatMessage(inc.ReadString(), messageType); + break; + case (byte)PacketTypes.NetworkEvent: + //read the data from the message and update client state accordingly + if (!gameStarted) break; + NetworkEvent.ReadData(inc); + break; + case (byte)PacketTypes.UpdateNetLobby: + if (gameStarted) continue; + Game1.netLobbyScreen.ReadData(inc); + break; + case (byte)PacketTypes.Traitor: + string targetName = inc.ReadString(); + + Game1.gameSession.NewChatMessage("You are an agent of Ordo Europae", messageColor[(int)ChatMessageType.Server]); + Game1.gameSession.NewChatMessage("Your secret task is to assassinate " + targetName + "!", messageColor[(int)ChatMessageType.Server]); + break; + } + + + } + } + + public void EndGame(string endMessage) + { + Map.Clear(); + + Game1.netLobbyScreen.Select(); + + if (Game1.gameSession!=null) Game1.gameSession.EndShift(null, null); + + DebugConsole.ThrowError(endMessage); + + myCharacter = null; + + gameStarted = false; + } + + public void Disconnect() + { + NetOutgoingMessage msg = Client.CreateMessage(); + msg.Write((byte)PacketTypes.PlayerLeft); + + Client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); + } + + public void SendCharacterData() + { + if (characterInfo == null) return; + + NetOutgoingMessage msg = Client.CreateMessage(); + msg.Write((byte)PacketTypes.CharacterInfo); + msg.Write(characterInfo.name); + msg.Write(characterInfo.gender == Gender.Male); + + Client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); + + } + + private Character ReadCharacterData(NetIncomingMessage inc) + { + string newName = inc.ReadString(); + int ID = inc.ReadInt32(); + bool isFemale = inc.ReadBoolean(); + int inventoryID = inc.ReadInt32(); + Vector2 position = new Vector2(inc.ReadFloat(), inc.ReadFloat()); + + CharacterInfo ch = new CharacterInfo("Content/Characters/Human/human.xml", newName, isFemale ? Gender.Female : Gender.Male); + Character character = new Character(ch, position); + character.ID = ID; + character.Inventory.ID = inventoryID; + + return character; + } + + public void SendChatMessage(string message) + { + //AddChatMessage(message); + + ChatMessageType type = (gameStarted && myCharacter != null && myCharacter.IsDead) ? ChatMessageType.Dead : ChatMessageType.Default; + + NetOutgoingMessage msg = Client.CreateMessage(); + msg.Write((byte)PacketTypes.Chatmessage); + msg.Write((byte)type); + msg.Write(message); + + Client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); + } + + } +} diff --git a/Subsurface/Networking/GameServer.cs b/Subsurface/Networking/GameServer.cs new file mode 100644 index 000000000..7bd5ded11 --- /dev/null +++ b/Subsurface/Networking/GameServer.cs @@ -0,0 +1,509 @@ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Lidgren.Network; +using Microsoft.Xna.Framework; + +namespace Subsurface.Networking +{ + class GameServer : NetworkMember + { + + // Server object + NetServer Server; + // Configuration object + NetPeerConfiguration Config; + + public List connectedClients = new List(); + + //NetIncomingMessage inc; + + const int sparseUpdateInterval = 150; + int sparseUpdateTimer; + + Client myClient; + + public GameServer() + { + Config = new NetPeerConfiguration("subsurface"); + + Config.Port = 14242; + + //Config.SimulatedLoss = 0.2f; + //Config.SimulatedMinimumLatency = 0.25f; + + Config.MaximumConnections = 10; + + Config.EnableMessageType(NetIncomingMessageType.ConnectionApproval); + + try + { + Server = new NetServer(Config); + Server.Start(); + } + + catch (Exception e) + { + DebugConsole.ThrowError("Couldn't start the server", e); + } + + + updateInterval = new TimeSpan(0, 0, 0, 0, 30); + + DebugConsole.NewMessage("Server started", Color.Green); + + } + + public void Update() + { + // Server.ReadMessage() Returns new messages, that have not yet been read. + // If "inc" is null -> ReadMessage returned null -> Its null, so dont do this :) + + NetIncomingMessage inc = Server.ReadMessage(); + if (inc != null) ReadMessage(inc); + + // if 30ms has passed + if ((updateTimer) < DateTime.Now) + { + if (Server.ConnectionsCount > 0) + { + if (sparseUpdateTimer <= 0) SparseUpdate(); + + SendNetworkEvents(); + } + + sparseUpdateTimer -= 1; + updateTimer = DateTime.Now + updateInterval; + } + } + + private void ReadMessage(NetIncomingMessage inc) + { + NetOutgoingMessage outmsg; + + switch (inc.MessageType) + { + case NetIncomingMessageType.ConnectionApproval: + if (inc.ReadByte() != (byte)PacketTypes.Login) break; + + DebugConsole.NewMessage("New player has joined the server", Color.White); + + //Character ch = new Character("Content/Characters/Human/human.xml"); + + Client newClient = new Client(); + newClient.version = inc.ReadString(); + newClient.name = inc.ReadString(); + newClient.Connection = inc.SenderConnection; + + connectedClients.Add(newClient); + + inc.SenderConnection.Approve(); + break; + case NetIncomingMessageType.StatusChanged: + Debug.WriteLine(inc.SenderConnection + " status changed. " + (NetConnectionStatus)inc.SenderConnection.Status); + if (inc.SenderConnection.Status == NetConnectionStatus.Connected) + { + + + Client sender = connectedClients.Find(x => x.Connection == inc.SenderConnection); + + if (sender == null) break; + + if (sender.version != Game1.version.ToString()) + { + DisconnectClient(sender, sender.name+" was unable to connect to the server (nonmatching game version)", + "Subsurface version " + Game1.version + " required to connect to the server (Your version: " + sender.version + ")"); + + } + else + { + + Game1.netLobbyScreen.AddPlayer(sender.name); + + // Notify the client that they have logged in + outmsg = Server.CreateMessage(); + + outmsg.Write((byte)PacketTypes.LoggedIn); + + //notify the client about other clients already logged in + outmsg.Write((myClient == null) ? connectedClients.Count - 1 : connectedClients.Count); + foreach (Client c in connectedClients) + { + if (c.Connection == inc.SenderConnection) continue; + outmsg.Write(c.name); + } + + if (myClient != null) outmsg.Write(myClient.name); + + Server.SendMessage(outmsg, inc.SenderConnection, NetDeliveryMethod.ReliableUnordered, 0); + + + //notify other clients about the new client + outmsg = Server.CreateMessage(); + outmsg.Write((byte)PacketTypes.PlayerJoined); + + outmsg.Write(sender.name); + + //send the message to everyone except the client who just logged in + SendMessage(outmsg, NetDeliveryMethod.ReliableUnordered, inc.SenderConnection); + + AddChatMessage(sender.name + " has joined the server", ChatMessageType.Server); + + UpdateNetLobby(null); + } + } + else if (inc.SenderConnection.Status == NetConnectionStatus.Disconnected) + { + DisconnectClient(inc.SenderConnection); + } + + + + break; + case NetIncomingMessageType.Data: + + switch (inc.ReadByte()) + { + case (byte)PacketTypes.NetworkEvent: + if (!gameStarted) break; + if (!NetworkEvent.ReadData(inc)) break; + + outmsg = Server.CreateMessage(); + outmsg.Write(inc); + + List recipients = new List(); + + foreach (Client client in connectedClients) + { + if (client.Connection == inc.SenderConnection) continue; + if (!client.inGame) continue; + + recipients.Add(client.Connection); + } + + if (recipients.Count == 0) break; + Server.SendMessage(outmsg, recipients, inc.DeliveryMethod, 0); + + break; + case (byte)PacketTypes.Chatmessage: + ChatMessageType messageType = (ChatMessageType)inc.ReadByte(); + string message = inc.ReadString(); + + SendChatMessage(message, messageType); + + break; + case (byte)PacketTypes.PlayerLeft: + DisconnectClient(inc.SenderConnection); + break; + case (byte)PacketTypes.CharacterInfo: + ReadCharacterData(inc); + break; + } + break; + case NetIncomingMessageType.WarningMessage: + Debug.WriteLine(inc.ReadString()); + break; + } + } + + private void SparseUpdate() + { + foreach (Character c in Character.characterList) + { + bool isClient = false; + foreach (Client client in connectedClients) + { + if (client.character != c) continue; + isClient = true; + break; + } + + if (!isClient) + { + c.largeUpdateTimer = 0; + new NetworkEvent(c.ID, false); + } + } + + sparseUpdateTimer = sparseUpdateInterval; + } + + private void SendMessage(NetOutgoingMessage msg, NetDeliveryMethod deliveryMethod, NetConnection excludedConnection) + { + List recipients = new List(); + + foreach (Client client in connectedClients) + { + if (client.Connection != excludedConnection) recipients.Add(client.Connection); + } + + if (recipients.Count == 0) return; + + Server.SendMessage(msg, recipients, deliveryMethod, 0); + + } + + private void SendNetworkEvents() + { + if (NetworkEvent.events.Count == 0) return; + + //System.Diagnostics.Debug.WriteLine("*************************"); + foreach (NetworkEvent networkEvent in NetworkEvent.events) + { + //System.Diagnostics.Debug.WriteLine("networkevent "+networkEvent.ID); + + NetOutgoingMessage message = Server.CreateMessage(); + message.Write((byte)PacketTypes.NetworkEvent); + //if (!networkEvent.IsClient) continue; + + networkEvent.FillData(message); + + Server.SendMessage(message, Server.Connections, + (networkEvent.IsImportant) ? NetDeliveryMethod.Unreliable : NetDeliveryMethod.ReliableUnordered, 0); + } + NetworkEvent.events.Clear(); + } + + + public bool StartGame(GUIButton button, object obj) + { + int seed = DateTime.Now.Millisecond; + Game1.random = new Random(seed); + + Map.Load(Game1.netLobbyScreen.SelectedMap); + + Game1.gameSession = new GameSession("", false, Game1.netLobbyScreen.GameDuration, Game1.netLobbyScreen.SelectedMode); + Game1.gameSession.StartShift(1); + //EventManager.SelectEvent(Game1.netLobbyScreen.SelectedEvent); + + foreach (Client client in connectedClients) + { + client.inGame = true; + + WayPoint spawnPoint = WayPoint.GetRandom(WayPoint.SpawnType.Human); + + if (client.characterInfo==null) + { + client.characterInfo = new CharacterInfo("Content/Characters/Human/human.xml", client.name); + } + + client.character = new Character(client.characterInfo, (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition, true); + } + + if (myClient != null) + { + WayPoint spawnPoint = WayPoint.GetRandom(WayPoint.SpawnType.Human); + CharacterInfo ch = new CharacterInfo("Content/Characters/Human/human.xml", myClient.name); + myClient.character = new Character(ch, (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition); + } + + foreach (Client client in connectedClients) + { + NetOutgoingMessage msg = Server.CreateMessage(); + msg.Write((byte)PacketTypes.StartGame); + + msg.Write(seed); + + msg.Write(Game1.netLobbyScreen.SelectedMap); + msg.Write(Game1.netLobbyScreen.GameDuration.TotalMinutes); + + WriteCharacterData(msg, client.name, client.character); + + msg.Write((myClient == null) ? connectedClients.Count - 1 : connectedClients.Count); + foreach (Client otherClient in connectedClients) + { + if (otherClient == client) continue; + WriteCharacterData(msg, otherClient.name, otherClient.character); + } + + if (myClient!=null) + { + WriteCharacterData(msg, myClient.name, myClient.character); + } + + Server.SendMessage(msg, client.Connection, NetDeliveryMethod.ReliableUnordered, 0); + } + + gameStarted = true; + + Game1.gameScreen.Cam.TargetPos = Vector2.Zero; + + Game1.gameScreen.Select(); + + return true; + } + + public void EndGame(string endMessage) + { + Map.Clear(); + + gameStarted = false; + + if (connectedClients.Count>0) + { + NetOutgoingMessage msg = Server.CreateMessage(); + msg.Write((byte)PacketTypes.EndGame); + msg.Write(endMessage); + + if (Server.ConnectionsCount > 0) + { + Server.SendMessage(msg, Server.Connections, NetDeliveryMethod.ReliableOrdered, 0); + } + + foreach (Client client in connectedClients) + { + client.character = null; + client.inGame = false; + } + } + + Game1.netLobbyScreen.Select(); + + DebugConsole.ThrowError(endMessage); + } + + private void DisconnectClient(NetConnection senderConnection) + { + Client client = connectedClients.Find(x => x.Connection == senderConnection); + if (client != null) DisconnectClient(client); + } + + private void DisconnectClient(Client client, string msg = "", string targetmsg = "") + { + if (client == null) return; + + if (gameStarted && client.character != null) client.character.Kill(true); + + if (msg == "") msg = client.name + " has left the server"; + if (targetmsg == "") targetmsg = "You have left the server"; + + NetOutgoingMessage outmsg = Server.CreateMessage(); + outmsg.Write((byte)PacketTypes.KickedOut); + outmsg.Write(targetmsg); + Server.SendMessage(outmsg, client.Connection, NetDeliveryMethod.ReliableUnordered, 0); + + connectedClients.Remove(client); + + outmsg = Server.CreateMessage(); + outmsg.Write((byte)PacketTypes.PlayerLeft); + outmsg.Write(client.name); + outmsg.Write(msg); + + Game1.netLobbyScreen.RemovePlayer(client.name); + + if (Server.Connections.Count > 0) + { + Server.SendMessage(outmsg, Server.Connections, NetDeliveryMethod.ReliableUnordered, 0); + } + + AddChatMessage(msg, ChatMessageType.Server); + } + + public void KickPlayer(string playerName) + { + playerName = playerName.ToLower(); + Client client = null; + foreach (Client c in connectedClients) + { + if (c.name.ToLower() != playerName) continue; + client = c; + break; + } + + if (client == null) return; + + DisconnectClient(client, client.name + " has been kicked from the server", "You have been kicked from the server"); + } + + public void NewTraitor(Client traitor, Client target) + { + NetOutgoingMessage msg = Server.CreateMessage(); + msg.Write((byte)PacketTypes.Traitor); + msg.Write(target.name); + if (Server.Connections.Count > 0) + { + Server.SendMessage(msg, traitor.Connection, NetDeliveryMethod.ReliableUnordered, 0); + } + } + + public bool UpdateNetLobby(object obj) + { + NetOutgoingMessage msg = Server.CreateMessage(); + msg.Write((byte)PacketTypes.UpdateNetLobby); + Game1.netLobbyScreen.WriteData(msg); + + if (Server.Connections.Count > 0) + { + Server.SendMessage(msg, Server.Connections, NetDeliveryMethod.ReliableUnordered, 0); + } + + return true; + } + + + + public void SendChatMessage(string message, ChatMessageType type = ChatMessageType.Server) + { + AddChatMessage(message, type); + + NetOutgoingMessage msg = Server.CreateMessage(); + msg.Write((byte)PacketTypes.Chatmessage); + msg.Write((byte)type); + msg.Write(message); + + if (Server.Connections.Count == 0) return; + + if (type==ChatMessageType.Dead) + { + List recipients = new List(); + foreach (Client c in connectedClients) + { + if (c.character != null && c.character.IsDead) recipients.Add(c.Connection); + } + if (recipients.Count>0) + { + Server.SendMessage(msg, recipients, NetDeliveryMethod.ReliableUnordered, 0); + } + } + else + { + Server.SendMessage(msg, Server.Connections, NetDeliveryMethod.ReliableUnordered, 0); + } + + } + + private void ReadCharacterData(NetIncomingMessage message) + { + string name = message.ReadString(); + Gender gender = message.ReadBoolean() ? Gender.Male : Gender.Female; + + foreach (Client c in connectedClients) + { + if (c.Connection != message.SenderConnection) continue; + c.characterInfo = new CharacterInfo("Content/Characters/Human/human.xml", name, gender); + } + } + + private void WriteCharacterData(NetOutgoingMessage message, string name, Character character) + { + message.Write(name); + message.Write(character.ID); + message.Write(character.info.gender==Gender.Female); + message.Write(character.Inventory.ID); + message.Write(character.SimPosition.X); + message.Write(character.SimPosition.Y); + } + } + + class Client + { + public string name; + + public Character character; + public CharacterInfo characterInfo; + public NetConnection Connection { get; set; } + public string version; + public bool inGame; + } +} diff --git a/Subsurface/Networking/NetworkEvent.cs b/Subsurface/Networking/NetworkEvent.cs new file mode 100644 index 000000000..4ff49954e --- /dev/null +++ b/Subsurface/Networking/NetworkEvent.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using Lidgren.Network; + +namespace Subsurface.Networking +{ + enum NetworkEventType + { + UpdateEntity = 0, + KillCharacter = 1, + UpdateComponent = 2, + DropItem = 3, + InventoryUpdate = 4, + PickItem = 5 + } + + class NetworkEvent + { + public static List events = new List(); + + private static bool[] isImportant = { false, true, true, true, true, true }; + + private int id; + + private NetworkEventType eventType; + + private bool isClientEvent; + + private object data; + + //private NetOutgoingMessage message; + + public int ID + { + get { return id; } + } + + public bool IsClient + { + get { return isClientEvent; } + } + + public bool IsImportant + { + get { return isImportant[(int)eventType]; } + } + + public NetworkEvent(int id, bool isClient) + : this(NetworkEventType.UpdateEntity, id, isClient) + { + } + + public NetworkEvent(NetworkEventType type, int id, bool isClient, object data = null) + { + if (isClient) + { + if (Game1.server != null) return; + } + else + { + if (Game1.server == null) return; + } + + this.eventType = type; + + foreach (NetworkEvent e in events) + { + if (!isImportant[(int)type] && e.id == id && e.eventType == type) return; + } + + this.id = id; + this.isClientEvent = isClient; + + this.data = data; + + events.Add(this); + } + + public bool FillData(NetOutgoingMessage message) + { + message.Write((byte)eventType); + + Entity e = Entity.FindEntityByID(id); + if (e == null) return false; + + message.Write(id); + + e.FillNetworkData(eventType, message, data); + + return true; + } + + public static bool ReadData(NetIncomingMessage message) + { + NetworkEventType eventType = (NetworkEventType)message.ReadByte(); + + int id; + try + { + id = message.ReadInt32(); + } + catch + { + DebugConsole.ThrowError("Received invalid network message"); + return false; + } + + Entity e = Entity.FindEntityByID(id); + if (e == null) + { + //DebugConsole.ThrowError("Couldn't find an entity matching the ID ''" + id + "''"); + return false; + } + + + //System.Diagnostics.Debug.WriteLine("new message: " + eventType +" - "+e); + + e.ReadNetworkData(eventType, message); + + return true; + + } + } +} diff --git a/Subsurface/Networking/NetworkMember.cs b/Subsurface/Networking/NetworkMember.cs new file mode 100644 index 000000000..be8c68799 --- /dev/null +++ b/Subsurface/Networking/NetworkMember.cs @@ -0,0 +1,51 @@ +using System; +using Microsoft.Xna.Framework; + +namespace Subsurface.Networking +{ + enum PacketTypes + { + Login, + LoggedIn, + LogOut, + + PlayerJoined, + PlayerLeft, + KickedOut, + + StartGame, + EndGame, + + CharacterInfo, + + Chatmessage, + UpdateNetLobby, + + NetworkEvent, + + Traitor + } + + class NetworkMember + { + protected static Color[] messageColor = { Color.Black, Color.DarkRed, Color.DarkBlue, Color.DarkGreen }; + + protected TimeSpan updateInterval; + protected DateTime updateTimer; + + protected bool gameStarted; + + public void AddChatMessage(string message, ChatMessageType messageType) + { + Game1.netLobbyScreen.NewChatMessage(message, messageColor[(int)messageType]); + if (Game1.gameSession != null) Game1.gameSession.NewChatMessage(message, messageColor[(int)messageType]); + + GUI.PlayMessageSound(); + } + } + + enum ChatMessageType + { + Default, Admin, Dead, Server + } +} diff --git a/Subsurface/OpenAL32.dll b/Subsurface/OpenAL32.dll new file mode 100644 index 000000000..b52b4f756 Binary files /dev/null and b/Subsurface/OpenAL32.dll differ diff --git a/Subsurface/Particles/Particle.cs b/Subsurface/Particles/Particle.cs new file mode 100644 index 000000000..d19a441eb --- /dev/null +++ b/Subsurface/Particles/Particle.cs @@ -0,0 +1,159 @@ +using FarseerPhysics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface.Particles +{ + class Particle + { + private ParticlePrefab prefab; + + private Vector2 position; + private Vector2 prevPosition; + + private Vector2 velocity; + + private float rotation; + private float prevRotation; + + private float angularVelocity; + + private Vector2 size; + private Vector2 sizeChange; + + private Color color; + private float alpha; + + private float lifeTime; + + private Vector2 velocityChange; + + private Vector2 drawPosition; + + private float checkCollisionTimer; + + public bool InWater + { + get { return prefab.inWater; } + } + + public Vector2 Size + { + get { return size; } + set { size = value; } + } + + public Vector2 VelocityChange + { + get { return velocityChange; } + set { velocityChange = value; } + } + + public void Init(Vector2 position, float rotation, Vector2 speed, ParticlePrefab prefab) + { + this.prefab = prefab; + + this.position = position; + prevPosition = position; + + drawPosition = ConvertUnits.ToDisplayUnits(position); + + velocity = speed; + + this.rotation = rotation + ToolBox.RandomFloat(prefab.startRotationMin, prefab.startRotationMax); + prevRotation = rotation; + + float rand = (float)Game1.localRandom.NextDouble(); + angularVelocity = prefab.angularVelocityMin + (prefab.angularVelocityMax - prefab.angularVelocityMin) * rand; + + lifeTime = prefab.lifeTime; + + rand = (float)Game1.localRandom.NextDouble(); + size = prefab.startSizeMin + (prefab.startSizeMax - prefab.startSizeMin)*rand; + + rand = (float)Game1.localRandom.NextDouble(); + sizeChange = prefab.sizeChangeMin + (prefab.sizeChangeMax - prefab.sizeChangeMin) * rand; + + + color = prefab.startColor; + alpha = prefab.startAlpha; + + velocityChange = prefab.velocityChange; + + } + + public bool Update(float deltaTime) + { + //over 3 times faster than position += velocity * deltatime + position.X += velocity.X * deltaTime; + position.Y += velocity.Y * deltaTime; + + if (prefab.rotateToDirection) + { + rotation = ToolBox.VectorToAngle(velocity); + } + else + { + rotation += angularVelocity * deltaTime; + } + + velocity.X += velocityChange.X * deltaTime; + velocity.Y += velocityChange.Y * deltaTime; + + size.X += sizeChange.X * deltaTime; + size.Y += sizeChange.Y * deltaTime; + + alpha += prefab.colorChange.W * deltaTime; + + color = new Color( + color.R / 255.0f + prefab.colorChange.X * deltaTime, + color.G / 255.0f + prefab.colorChange.Y * deltaTime, + color.B / 255.0f + prefab.colorChange.Z * deltaTime); + + if (prefab.deleteOnHit) + { + if (checkCollisionTimer > 0.0f) + { + checkCollisionTimer -= deltaTime; + } + else + { + if (Map.InsideWall(new Vector2(drawPosition.X, -drawPosition.Y))) + { + return false; + } + checkCollisionTimer = 0.05f; + } + } + + lifeTime -= deltaTime; + + if (lifeTime <= 0.0f || alpha <= 0.0f || size.X <= 0.0f || size.Y <= 0.0f) return false; + + return true; + } + + public void Draw(SpriteBatch spriteBatch) + { + drawPosition = Physics.Interpolate(prevPosition, position); + drawPosition.Y = -drawPosition.Y; + float drawRotation = Physics.Interpolate(prevRotation, rotation); + + drawPosition = ConvertUnits.ToDisplayUnits(drawPosition); + + + spriteBatch.Draw( + prefab.sprite.Texture, + drawPosition, + null, + color*alpha, + drawRotation, + prefab.sprite.origin, + size, + SpriteEffects.None, prefab.sprite.Depth); + + prevPosition = position; + prevRotation = rotation; + } + } +} diff --git a/Subsurface/Particles/ParticleManager.cs b/Subsurface/Particles/ParticleManager.cs new file mode 100644 index 000000000..24f9f990e --- /dev/null +++ b/Subsurface/Particles/ParticleManager.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using FarseerPhysics; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface.Particles +{ + class ParticleManager + { + public static int particleCount; + + private const int MaxParticles = 1500; + private Particle[] particles; + + private Dictionary prefabs; + + Camera cam; + + public ParticleManager(string configFile, Camera cam) + { + this.cam = cam; + + particles = new Particle[MaxParticles]; + + XDocument doc = ToolBox.TryLoadXml(configFile); + if (doc == null) return; + + prefabs = new Dictionary(); + + foreach (XElement element in doc.Root.Elements()) + { + if (prefabs.ContainsKey(element.Name.ToString())) + { + DebugConsole.ThrowError("Error in " + configFile + "! Each particle prefab must have a unique name."); + continue; + } + prefabs.Add(element.Name.ToString(), new ParticlePrefab(element)); + } + } + + public Particle CreateParticle(Vector2 position, float angle, float speed, string prefabName) + { + return CreateParticle(position, angle, new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * speed, prefabName); + } + + public Particle CreateParticle(Vector2 position, float rotation, Vector2 speed, string prefabName) + { + ParticlePrefab prefab; + prefabs.TryGetValue(prefabName, out prefab); + + if (prefab==null) + { + DebugConsole.ThrowError("Particle prefab "+prefabName+" not found!"); + return null; + } + + return CreateParticle(position, rotation, speed, prefab); + } + + public Particle CreateParticle(Vector2 position, float rotation, Vector2 speed, ParticlePrefab prefab) + { + if (!Map.RectContains(cam.WorldView, ConvertUnits.ToDisplayUnits(position))) return null; + if (particleCount >= MaxParticles) return null; + + if (particles[particleCount] == null) particles[particleCount] = new Particle(); + + particles[particleCount].Init(position, rotation, speed, prefab); + + particleCount++; + + return particles[particleCount-1]; + + } + + private void RemoveParticle(int index) + { + particleCount--; + + Particle swap = particles[index]; + particles[index] = particles[particleCount]; + particles[particleCount] = swap; + } + + + + public void Update(float deltaTime) + { + for (int i = 0; i < particleCount; i++) + { + if (!particles[i].Update(deltaTime)) RemoveParticle(i); + } + } + + public void Draw(SpriteBatch spriteBatch, bool inWater) + { + for (int i = 0; i < particleCount; i++) + { + if (particles[i].InWater != inWater) continue; + + particles[i].Draw(spriteBatch); + } + } + + + } +} diff --git a/Subsurface/Particles/ParticlePrefab.cs b/Subsurface/Particles/ParticlePrefab.cs new file mode 100644 index 000000000..433663ba6 --- /dev/null +++ b/Subsurface/Particles/ParticlePrefab.cs @@ -0,0 +1,70 @@ +using System.Xml.Linq; +using Microsoft.Xna.Framework; + +namespace Subsurface.Particles +{ + class ParticlePrefab + { + public readonly string name; + + public readonly Sprite sprite; + + public readonly float angularVelocityMin, angularVelocityMax; + + public readonly float startRotationMin, startRotationMax; + + public readonly Vector2 startSizeMin, startSizeMax; + public readonly Vector2 sizeChangeMin, sizeChangeMax; + + public readonly Color startColor; + public readonly float startAlpha; + + public readonly Vector4 colorChange; + + public readonly float lifeTime; + + public readonly bool deleteOnHit; + + public readonly Vector2 velocityChange; + + public readonly bool inWater; + + public readonly bool rotateToDirection; + + public ParticlePrefab(XElement element) + { + name = element.Name.ToString(); + + string spritePath = ToolBox.GetAttributeString(element, "sprite", ""); + sprite = new Sprite(spritePath, new Vector2(0.5f,0.5f)); + + angularVelocityMin = ToolBox.GetAttributeFloat(element, "angularvelocitymin", 0.0f); + angularVelocityMax = ToolBox.GetAttributeFloat(element, "angularvelocitymax", 0.0f); + + startSizeMin = ToolBox.GetAttributeVector2(element, "startsizemin", Vector2.One); + startSizeMax = ToolBox.GetAttributeVector2(element, "startsizemax", Vector2.One); + + sizeChangeMin = ToolBox.GetAttributeVector2(element, "sizechangemin", Vector2.Zero); + sizeChangeMax = ToolBox.GetAttributeVector2(element, "sizechangemax", Vector2.Zero); + + startRotationMin = ToolBox.GetAttributeFloat(element, "startrotationmin", 0.0f); + startRotationMax = ToolBox.GetAttributeFloat(element, "startrotationmax", 0.0f); + + startColor = new Color(ToolBox.GetAttributeVector4(element, "startcolor", Vector4.One)); + startAlpha = ToolBox.GetAttributeFloat(element, "startalpha", 1.0f); + + deleteOnHit = ToolBox.GetAttributeBool(element, "deleteonhit", false); + + colorChange = ToolBox.GetAttributeVector4(element, "colorchange", Vector4.Zero); + + lifeTime = ToolBox.GetAttributeFloat(element, "lifetime", 5.0f); + + velocityChange = ToolBox.GetAttributeVector2(element, "velocitychange", Vector2.Zero); + + rotateToDirection = ToolBox.GetAttributeBool(element, "rotatetodirection", false); + + inWater = ToolBox.GetAttributeBool(element, "inwater", false); + + } + } +} diff --git a/Subsurface/Physics/Physics.cs b/Subsurface/Physics/Physics.cs new file mode 100644 index 000000000..b2d220450 --- /dev/null +++ b/Subsurface/Physics/Physics.cs @@ -0,0 +1,52 @@ +using System; +using FarseerPhysics.Dynamics; +using Microsoft.Xna.Framework; + +namespace Subsurface +{ + static class Physics + { + private static double alpha; + + public const Category CollisionNone = Category.None; + public const Category CollisionAll = Category.All; + public const Category CollisionWall = Category.Cat1; + public const Category CollisionCharacter = Category.Cat2; + public const Category CollisionPlatform = Category.Cat3; + public const Category CollisionStairs = Category.Cat4; + public const Category CollisionMisc = Category.Cat5; + public const Category CollisionProjectile = Category.Cat6; + + public static double accumulator; + public static double step = 1.0f/60.0f; + + public static bool updated; + + + public const float DisplayToSimRation = 100.0f; + + public static double Alpha + { + get { return alpha; } + set { alpha = Math.Min(Math.Max(value, 0.0), 1.0); } + } + + public static double Interpolate(double previous, double current) + { + return current * alpha + previous * (1.0 - alpha); + } + + public static float Interpolate(float previous, float current) + { + return current * (float)alpha + previous * (1.0f - (float)alpha); + } + + public static Vector2 Interpolate(Vector2 previous, Vector2 current) + { + return new Vector2( + Interpolate(previous.X, current.X), + Interpolate(previous.Y, current.Y)); + } + } + +} diff --git a/Subsurface/Physics/PhysicsBody.cs b/Subsurface/Physics/PhysicsBody.cs new file mode 100644 index 000000000..45de3635c --- /dev/null +++ b/Subsurface/Physics/PhysicsBody.cs @@ -0,0 +1,352 @@ +using System; +using System.Xml.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Factories; +using Lidgren.Network; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Subsurface.Networking; +using System.Collections.Generic; + +namespace Subsurface +{ + class PhysicsBody + { + + public enum Shape + { + Circle, Rectangle, Capsule + }; + + public static List list = new List(); + + //the farseer physics body of the item + private Body body; + protected Vector2 prevPosition; + protected float prevRotation; + + protected Vector2 targetPosition; + protected Vector2 targetVelocity; + protected float targetRotation; + protected float targetAngularVelocity; + + private Vector2 drawPosition; + private float drawRotation; + + + public readonly Shape bodyShape; + public readonly float height, width, radius; + + private float density; + + //the direction the item is facing (for example, a gun has to be + //flipped horizontally if the character holding it turns around) + float dir; + + public Vector2 TargetPosition + { + get { return targetPosition; } + set { targetPosition = value; } + } + + public Vector2 TargetVelocity + { + get { return targetVelocity; } + set { targetVelocity = value; } + } + + public float TargetRotation + { + get { return targetRotation; } + set { targetRotation = value; } + } + + public float TargetAngularVelocity + { + get { return targetAngularVelocity; } + set { targetAngularVelocity = value; } + } + + public Vector2 DrawPosition + { + get { return drawPosition; } + } + + public float DrawRotation + { + get { return drawRotation; } + } + + public float Dir + { + get { return dir; } + set { dir = value; } + } + + public bool Enabled + { + get { return body.Enabled; } + set { body.Enabled = value; } + } + + public Vector2 Position + { + get { return body.Position; } + } + + public float Rotation + { + get { return body.Rotation; } + } + + public Vector2 LinearVelocity + { + get { return body.LinearVelocity; } + set { body.LinearVelocity = value; } + } + + public float AngularVelocity + { + get { return body.AngularVelocity; } + set { body.AngularVelocity = value; } + } + + public float Mass + { + get { return body.Mass; } + } + + public float Density + { + get { return density; } + } + + public Body FarseerBody + { + get { return body; } + } + + public object UserData + { + get { return body.UserData; } + set { body.UserData = value; } + } + + public float Friction + { + set { body.Friction = value; } + } + + public BodyType BodyType + { + set { body.BodyType = value; } + } + + public Category CollisionCategories + { + set { body.CollisionCategories = value; } + } + + public Category CollidesWith + { + set { body.CollidesWith = value; } + } + + public PhysicsBody(XElement element) + : this(element, Vector2.Zero) + { + } + + public PhysicsBody(Body body) + { + this.body = body; + + density = 10.0f; + + dir = 1.0f; + + list.Add(this); + } + + public PhysicsBody(XElement element, Vector2 position) + { + float radius = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "radius", 0.0f)); + float height = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "height", 0.0f)); + float width = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "width", 0.0f)); + + density = ToolBox.GetAttributeFloat(element, "density", 10.0f); + + if (width != 0.0f && height != 0.0f) + { + body = BodyFactory.CreateRectangle(Game1.world, width, height, density); + bodyShape = Shape.Rectangle; + } + else if (radius != 0.0f && height != 0.0f) + { + body = BodyFactory.CreateCapsule(Game1.world, height, radius, density); + bodyShape = Shape.Capsule; + } + else if (radius != 0.0f) + { + body = BodyFactory.CreateCircle(Game1.world, radius, density); + bodyShape = Shape.Circle; ; + } + else + { + DebugConsole.ThrowError("Invalid body dimensions in " + element); + } + + this.width = width; + this.height = height; + this.radius = radius; + + dir = 1.0f; + + //items only collide with the map + body.CollisionCategories = Physics.CollisionMisc; + body.CollidesWith = Physics.CollisionWall; + + body.Friction = ToolBox.GetAttributeFloat(element, "friction", 0.3f); + + body.BodyType = BodyType.Dynamic; + //body.AngularDamping = Limb.LimbAngularDamping; + + body.UserData = this; + + SetTransform(position, 0.0f); + + //prevPosition = ConvertUnits.ToDisplayUnits(position); + + list.Add(this); + } + + public void ResetDynamics() + { + body.ResetDynamics(); + } + + public void ApplyLinearImpulse(Vector2 impulse) + { + body.ApplyLinearImpulse(impulse); + } + + public void ApplyLinearImpulse(Vector2 impulse, Vector2 point) + { + body.ApplyLinearImpulse(impulse, point); + } + + public void ApplyForce(Vector2 force) + { + body.ApplyForce(force); + } + + public void ApplyForce(Vector2 force, Vector2 point) + { + body.ApplyForce(force, point); + } + + public void ApplyTorque(float torque) + { + body.ApplyTorque(torque); + } + + public void SetTransform(Vector2 position, float rotation) + { + body.SetTransform(position, rotation); + SetPrevTransform(position, rotation); + } + + public void SetPrevTransform(Vector2 position, float rotation) + { + prevPosition = position; + prevRotation = rotation; + } + + public void SetToTargetPosition() + { + if (targetPosition != Vector2.Zero) + { + body.SetTransform(targetPosition, targetRotation); + body.LinearVelocity = targetVelocity; + body.AngularVelocity = targetAngularVelocity; + targetPosition = Vector2.Zero; + } + } + + public void Draw(SpriteBatch spriteBatch, Sprite sprite, Color color) + { + if (!body.Enabled) return; + + SpriteEffects spriteEffect = (dir == 1.0f) ? SpriteEffects.None : SpriteEffects.FlipHorizontally; + + drawPosition = Physics.Interpolate(prevPosition, body.Position); + drawPosition = ConvertUnits.ToDisplayUnits(drawPosition); + + drawRotation = Physics.Interpolate(prevRotation, body.Rotation); + + sprite.Draw(spriteBatch, new Vector2(drawPosition.X, -drawPosition.Y), color, -drawRotation, 1.0f, spriteEffect); + + //prevPosition = body.Position; + //prevRotation = body.Rotation; + } + + /// + /// rotate the body towards the target rotation in the "shortest direction" + /// + public void SmoothRotate(float targetRotation, float force = 10.0f) + { + float nextAngle = body.Rotation + body.AngularVelocity * (float)Physics.step; + + float angle = ToolBox.GetShortestAngle(nextAngle, targetRotation); + + float torque = body.Mass * angle * 60.0f * (force/100.0f); + + body.ApplyTorque(torque); + + //float nextAngle = bodyAngle + body->GetAngularVelocity() / 60.0; + //float totalRotation = desiredAngle - nextAngle; + //while (totalRotation < -180 * DEGTORAD) totalRotation += 360 * DEGTORAD; + //while (totalRotation > 180 * DEGTORAD) totalRotation -= 360 * DEGTORAD; + //float desiredAngularVelocity = totalRotation * 60; + //float torque = body->GetInertia() * desiredAngularVelocity / (1 / 60.0); + //body->ApplyTorque(torque); + + + + + //body.ApplyTorque((Math.Sign(angle) + Math.Max(Math.Min(angle * force, force / 2.0f), -force / 2.0f)) * body.Mass); + //body.ApplyTorque(-body.AngularVelocity * 0.5f * body.Mass); + } + + + public void Remove() + { + list.Remove(this); + Game1.world.RemoveBody(body); + + } + + public void FillNetworkData(NetworkEventType type, NetOutgoingMessage message) + { + message.Write(body.Position.X); + message.Write(body.Position.Y); + message.Write(body.LinearVelocity.X); + message.Write(body.LinearVelocity.Y); + + message.Write(body.Rotation); + message.Write(body.AngularVelocity); + } + + public void ReadNetworkData(NetworkEventType type, NetIncomingMessage message) + { + targetPosition.X = message.ReadFloat(); + targetPosition.Y = message.ReadFloat(); + targetVelocity.X = message.ReadFloat(); + targetVelocity.Y = message.ReadFloat(); + + targetRotation = message.ReadFloat(); + targetAngularVelocity = message.ReadFloat(); + } + } +} diff --git a/Subsurface/PlayerInput.cs b/Subsurface/PlayerInput.cs new file mode 100644 index 000000000..642be272c --- /dev/null +++ b/Subsurface/PlayerInput.cs @@ -0,0 +1,159 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; + +namespace Subsurface +{ + class Key + { + private bool state, stateQueue; + private bool canBeHeld; + + public Key(bool canBeHeld) + { + this.canBeHeld = canBeHeld; + } + + public bool State + { + get + { + return state; + } + set + { + //if (value == false) return; + state = value; + //if (value) stateQueue = value; + } + } + + public void SetState(bool value) + { + state = value; + if (value) stateQueue = value; + } + + public bool Dequeue + { + get + { + bool value = stateQueue; + stateQueue = false; + return value; + } + //set + //{ + // stateQueue = value; + //} + } + + public void Reset() + { + if (!canBeHeld) state = false; + //stateQueue = false; + } + } + + static class PlayerInput + { + static MouseState mouseState, oldMouseState; + static KeyboardState keyboardState, oldKeyboardState; + + static double timeSinceClick; + + const double doubleClickDelay = 0.4; + + static bool doubleClicked; + + public static Keys selectKey = Keys.E; + + public static Vector2 MousePosition + { + get { return new Vector2(mouseState.Position.X, mouseState.Position.Y); } + } + + public static MouseState GetMouseState + { + get { return mouseState; } + } + public static MouseState GetOldMouseState + { + get { return oldMouseState; } + } + + public static Vector2 MouseSpeed + { + get + { + Point speed = mouseState.Position - oldMouseState.Position; + return new Vector2(speed.X, speed.Y); + } + } + + public static KeyboardState GetKeyboardState + { + get { return keyboardState; } + } + + public static KeyboardState GetOldKeyboardState + { + get { return oldKeyboardState; } + } + + public static int ScrollWheelSpeed + { + get { return mouseState.ScrollWheelValue - oldMouseState.ScrollWheelValue; } + + } + + public static bool LeftButtonDown() + { + return mouseState.LeftButton == ButtonState.Pressed; + } + + public static bool LeftButtonClicked() + { + return (oldMouseState.LeftButton == ButtonState.Pressed + && mouseState.LeftButton == ButtonState.Released); + } + + public static bool RightButtonClicked() + { + return (oldMouseState.RightButton == ButtonState.Pressed + && mouseState.RightButton == ButtonState.Released); + } + + public static bool DoubleClicked() + { + return doubleClicked; + } + + public static bool KeyHit(Keys button) + { + return (oldKeyboardState.IsKeyDown(button) && keyboardState.IsKeyUp(button)); + } + + public static bool KeyDown(Keys button) + { + return (keyboardState.IsKeyDown(button)); + } + + public static void Update(double deltaTime) + { + timeSinceClick += deltaTime; + + oldMouseState = mouseState; + mouseState = Mouse.GetState(); + + oldKeyboardState = keyboardState; + keyboardState = Keyboard.GetState(); + + doubleClicked = false; + if (LeftButtonClicked()) + { + if (timeSinceClick < doubleClickDelay) doubleClicked = true; + timeSinceClick = 0.0; + } + } + } +} diff --git a/Subsurface/Program.cs b/Subsurface/Program.cs new file mode 100644 index 000000000..4fefd14f5 --- /dev/null +++ b/Subsurface/Program.cs @@ -0,0 +1,26 @@ +#region Using Statements + +using System; + +#endregion + +namespace Subsurface +{ +#if WINDOWS || LINUX + /// + /// The main class. + /// + public static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + using (var game = new Game1()) + game.Run(); + } + } +#endif +} diff --git a/Subsurface/Properties.cs b/Subsurface/Properties.cs new file mode 100644 index 000000000..670a2d7bc --- /dev/null +++ b/Subsurface/Properties.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace Subsurface +{ + [AttributeUsage(AttributeTargets.Property)] + public class Editable : System.Attribute + { + } + + [AttributeUsage(AttributeTargets.Property)] + public class InGameEditable : Editable + { + } + + + [AttributeUsage(AttributeTargets.Property)] + public class HasDefaultValue : System.Attribute + { + public object defaultValue; + public bool isSaveable; + + public HasDefaultValue(object defaultValue, bool isSaveable) + { + this.defaultValue = defaultValue; + this.isSaveable = isSaveable; + } + } + + //[AttributeUsage(AttributeTargets.Property)] + //public class Saveable : Initable + //{ + // public Saveable(object defaultValue) + // :base(defaultValue) + // { + // } + //} + + class ObjectProperty + { + readonly PropertyDescriptor property; + readonly object obj; + + public string Name + { + get { return property.Name; } + } + + public AttributeCollection Attributes + { + get { return property.Attributes; } + } + + public ObjectProperty(PropertyDescriptor property, object obj) + { + this.property = property; + this.obj = obj; + } + + public bool TrySetValue(string value) + { + if (value == null) return false; + + if (property.PropertyType == typeof(string)) + { + property.SetValue(obj, value); + } + else if (property.PropertyType == typeof(float)) + { + float floatVal = 0.0f; + + if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatVal)) + { + property.SetValue(obj, floatVal); + } + } + else if (property.PropertyType == typeof(bool)) + { + property.SetValue(obj, (value.ToLower()=="true")); + } + else if (property.PropertyType == typeof(int)) + { + int intVal = 0; + if (int.TryParse(value, out intVal)) + { + property.SetValue(obj, intVal); + } + } + else + { + DebugConsole.ThrowError("Failed to set the value of the property ''" + Name + "'' of ''" + obj.ToString() + "'' to " + value.ToString()); + DebugConsole.ThrowError("(Type not implemented)"); + + return false; + } + + return true; + } + + public bool TrySetValue(object value) + { + if (value == null) return false; + + if (property.PropertyType!= value.GetType()) + { + DebugConsole.ThrowError("Failed to set the value of the property ''"+Name+"'' of ''"+obj.ToString()+"'' to "+value.ToString()); + + DebugConsole.ThrowError("(Non-matching type, should be "+property.PropertyType+" instead of " +value.GetType()+")"); + return false; + } + + if (obj == null || property == null) return false; + try + { + property.SetValue(obj, value); + } + catch + { + return false; + } + return true; + } + + public object GetValue() + { + if (obj == null || property == null) return false; + + try + { + return property.GetValue(obj); + } + catch + { + return false; + } + } + + public static List GetProperties(IPropertyObject obj) + { + List editableProperties = new List(); + + foreach (var property in obj.ObjectProperties.Values) + { + if (property.Attributes.OfType().Count() > 0) editableProperties.Add(property); + } + + return editableProperties; + } + + public static Dictionary GetProperties(object obj) + { + var properties = TypeDescriptor.GetProperties(obj.GetType()).Cast(); + + Dictionary dictionary = new Dictionary(); + + foreach (var property in properties) + { + dictionary.Add(property.Name.ToLower(), new ObjectProperty(property, obj)); + } + + return dictionary; + } + + public static Dictionary InitProperties(object obj, XElement element) + { + var properties = TypeDescriptor.GetProperties(obj.GetType()).Cast(); + + Dictionary dictionary = new Dictionary(); + + foreach (var property in properties) + { + ObjectProperty objProperty = new ObjectProperty(property, obj); + dictionary.Add(property.Name.ToLower(), objProperty); + + //set the value of the property to the default value if there is one + foreach (var ini in property.Attributes.OfType()) + { + objProperty.TrySetValue(ini.defaultValue); + break; + } + } + + //go through all the attributes in the xml element + //and set the value of the matching property if it is initializable + foreach (XAttribute attribute in element.Attributes()) + { + ObjectProperty property = null; + if (!dictionary.TryGetValue(attribute.Name.ToString().ToLower(), out property)) continue; + if (property.Attributes.OfType().Count() == 0) continue; + property.TrySetValue(attribute.Value); + } + + return dictionary; + } + + public static void SaveProperties(IPropertyObject obj, XElement element) + { + var saveProperties = ObjectProperty.GetProperties(obj); + foreach (var property in saveProperties) + { + object value = property.GetValue(); + if (value == null) continue; + + //only save if the value has been changed from the default value and the attribute is saveable + bool dontSave = true; + foreach (var attribute in property.Attributes.OfType()) + { + if (attribute.isSaveable && !attribute.defaultValue.Equals(value)) + { + dontSave = false; + break; + } + } + + if (dontSave) continue; + + string stringValue; + if (value.GetType() == typeof(float)) + { + //do this to make sure the decimal point isn't converted to a comma or anything like that + stringValue = ((float)value).ToString("G", CultureInfo.InvariantCulture); + } + else + { + stringValue = value.ToString(); + } + + element.Add(new XAttribute(property.Name.ToLower(), stringValue)); + } + } + } +} diff --git a/Subsurface/Properties/AssemblyInfo.cs b/Subsurface/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..88b6474e0 --- /dev/null +++ b/Subsurface/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Subsurface")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyCopyright("Copyright © Undertow Games 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2d7fbcf7-31a2-4e7f-a7d2-211c9096b9d5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.1.0.0")] +[assembly: AssemblyFileVersion("0.1.0.0")] diff --git a/Subsurface/Screens/EditCharacterScreen.cs b/Subsurface/Screens/EditCharacterScreen.cs new file mode 100644 index 000000000..6c715279b --- /dev/null +++ b/Subsurface/Screens/EditCharacterScreen.cs @@ -0,0 +1,334 @@ +using System; +using System.Linq; +using FarseerPhysics; +using FarseerPhysics.Dynamics.Joints; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System.Collections.Generic; + +namespace Subsurface +{ + class EditCharacterScreen : Screen + { + Camera cam; + + GUIComponent GUIpanel; + GUIButton physicsButton; + + GUIListBox limbList, jointList; + + GUIFrame limbPanel; + + Character editingCharacter; + Limb editingLimb; + //RevoluteJoint editingJoint; + + + + List textures; + List texturePaths; + + private bool physicsEnabled; + + public Camera Cam + { + get { return cam; } + } + + public EditCharacterScreen() + { + + + } + + public override void Select() + { + base.Select(); + + Ragdoll.DebugDraw = true; + + cam = new Camera(); + + GUIpanel = new GUIFrame(new Rectangle(0, 0, 300, Game1.GraphicsHeight), Color.DarkGray * 0.8f); + GUIpanel.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); + + physicsButton = new GUIButton(new Rectangle(0, 50, 200, 25), "Physics", Color.White, GUIpanel); + physicsButton.OnClicked += TogglePhysics; + + new GUITextBlock(new Rectangle(0, 80, 0, 25), "Limbs:", Color.Transparent, Color.Black, Alignment.Left, GUIpanel); + limbList = new GUIListBox(new Rectangle(0, 110, 0, 250), Color.White * 0.7f, GUIpanel); + limbList.OnSelected = SelectLimb; + + new GUITextBlock(new Rectangle(0, 360, 0, 25), "Joints:", Color.Transparent, Color.Black, Alignment.Left, GUIpanel); + jointList = new GUIListBox(new Rectangle(0, 390, 0, 250), Color.White * 0.7f, GUIpanel); + + + while (Character.characterList.Count > 1) + { + Character.characterList.First().Remove(); + } + + if (Character.characterList.Count == 1) + { + if (editingCharacter != Character.characterList[0]) UpdateLimbLists(Character.characterList[0]); + editingCharacter = Character.characterList[0]; + + Vector2 camPos = editingCharacter.animController.limbs[0].body.Position; + camPos = ConvertUnits.ToDisplayUnits(camPos); + camPos.Y = -camPos.Y; + cam.TargetPos = camPos; + + if (physicsEnabled) + { + editingCharacter.Control(cam); + } + else + { + cam.TargetPos = Vector2.Zero; + } + } + + textures = new List(); + texturePaths = new List(); + foreach (Limb limb in editingCharacter.animController.limbs) + { + if (texturePaths.Contains(limb.sprite.FilePath)) continue; + textures.Add(limb.sprite.Texture); + texturePaths.Add(limb.sprite.FilePath); + } + } + + /// + /// Allows the game to run logic such as updating the world, + /// checking for collisions, gathering input, and playing audio. + /// + /// Provides a snapshot of timing values. + public override void Update(double deltaTime) + { + Physics.accumulator += deltaTime; + //cam.Zoom += Math.Sign(PlayerInput.GetMouseState.ScrollWheelValue - PlayerInput.GetOldMouseState.ScrollWheelValue)*0.1f; + + cam.MoveCamera((float)deltaTime); + + if (physicsEnabled) + { + Physics.accumulator = Math.Min(Physics.accumulator, Physics.step * 4); + while (Physics.accumulator >= Physics.step) + { + Character.UpdateAnimAll((float)Physics.step * 1000.0f); + + Ragdoll.UpdateAll((float)Physics.step); + + Game1.world.Step((float)Physics.step); + + Physics.accumulator -= Physics.step; + } + + Physics.Alpha = Physics.accumulator / Physics.step; + } + + + + } + + /// + /// This is called when the game should draw itself. + /// + /// Provides a snapshot of timing values. + public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) + { + //cam.UpdateTransform(); + + graphics.Clear(Color.CornflowerBlue); + + spriteBatch.Begin(SpriteSortMode.BackToFront, + BlendState.AlphaBlend, + null, null, null, null, + cam.Transform); + + Map.Draw(spriteBatch, true); + + spriteBatch.End(); + + spriteBatch.Begin(SpriteSortMode.BackToFront, + BlendState.AlphaBlend, + null, null, null, null, + cam.Transform); + + //if (EntityPrefab.Selected != null) EntityPrefab.Selected.UpdatePlacing(spriteBatch, cam); + + //Entity.DrawSelecting(spriteBatch, cam); + + if (editingCharacter!=null) + editingCharacter.Draw(spriteBatch); + + spriteBatch.End(); + + //-------------------- HUD ----------------------------- + + spriteBatch.Begin(); + + GUIpanel.Draw(spriteBatch); + + EditLimb(spriteBatch); + + + int x = 0, y = 0; + for (int i = 0; i < textures.Count; i++ ) + { + x = Game1.GraphicsWidth - textures[i].Width; + spriteBatch.Draw(textures[i], new Vector2(x, y), Color.White); + + foreach (Limb limb in editingCharacter.animController.limbs) + { + if (limb.sprite.FilePath != texturePaths[i]) continue; + Rectangle rect = limb.sprite.SourceRect; + rect.X += x; + rect.Y += y; + + GUI.DrawRectangle(spriteBatch, rect, Color.Red); + + Vector2 limbBodyPos = new Vector2( + rect.X + limb.sprite.Origin.X, + rect.Y + limb.sprite.Origin.Y); + + DrawJoints(spriteBatch, limb, limbBodyPos); + + if (limb.BodyShapeTexture == null) continue; + + spriteBatch.Draw(limb.BodyShapeTexture, limbBodyPos, + null, Color.White, 0.0f, + new Vector2(limb.BodyShapeTexture.Width, limb.BodyShapeTexture.Height) / 2, + 1.0f, SpriteEffects.None, 0.0f); + + GUI.DrawLine(spriteBatch, limbBodyPos + Vector2.UnitY * 5.0f, limbBodyPos - Vector2.UnitY * 5.0f, Color.White); + GUI.DrawLine(spriteBatch, limbBodyPos + Vector2.UnitX * 5.0f, limbBodyPos - Vector2.UnitX * 5.0f, Color.White); + + if (Vector2.Distance(PlayerInput.MousePosition, limbBodyPos)<5.0f && PlayerInput.LeftButtonDown()) + { + limb.sprite.Origin -= PlayerInput.MouseSpeed; + } + } + + y += textures[i].Height; + } + + + GUI.Draw((float)deltaTime, spriteBatch); + + //EntityPrefab.DrawList(spriteBatch, new Vector2(20,50)); + + //Entity.Edit(spriteBatch, cam); + + spriteBatch.End(); + } + + private void UpdateLimbLists(Character character) + { + limbList.ClearChildren(); + foreach (Limb limb in character.animController.limbs) + { + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(0,0,0,25), + limb.type.ToString(), + Color.Transparent, + Color.Black, + Alignment.Left, + limbList); + textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); + textBlock.UserData = limb; + } + + jointList.ClearChildren(); + foreach (RevoluteJoint joint in character.animController.limbJoints) + { + Limb limb1 = (Limb)(joint.BodyA.UserData); + Limb limb2 = (Limb)(joint.BodyB.UserData); + + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(0, 0, 0, 25), + limb1.type.ToString() + " - " + limb2.type.ToString(), + Color.Transparent, + Color.Black, + Alignment.Left, + jointList); + textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); + textBlock.UserData = joint; + } + } + + private void DrawJoints(SpriteBatch spriteBatch, Limb limb, Vector2 limbBodyPos) + { + foreach (var joint in editingCharacter.animController.limbJoints) + { + Vector2 jointPos = Vector2.Zero; + + if (joint.BodyA == limb.body.FarseerBody) + { + jointPos = limbBodyPos + ConvertUnits.ToDisplayUnits(joint.LocalAnchorA); + + } + else if (joint.BodyB == limb.body.FarseerBody) + { + jointPos = limbBodyPos + ConvertUnits.ToDisplayUnits(joint.LocalAnchorB); + } + else + { + continue; + } + + GUI.DrawRectangle(spriteBatch, jointPos, new Vector2(5.0f, 5.0f), Color.Red, true); + if (Vector2.Distance(PlayerInput.MousePosition, jointPos) < 6.0f) + { + GUI.DrawRectangle(spriteBatch, jointPos - new Vector2(3.0f, 3.0f), new Vector2(11.0f, 11.0f), Color.Red, false); + if (PlayerInput.LeftButtonDown()) + { + if (joint.BodyA == limb.body.FarseerBody) + { + joint.LocalAnchorA += ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed); + } + else + { + joint.LocalAnchorB += ConvertUnits.ToSimUnits(PlayerInput.MouseSpeed); + } + } + } + } + } + + private bool SelectLimb(object selection) + { + try + { + editingLimb = (Limb)selection; + limbPanel = new GUIFrame(new Rectangle(300, 0, 500, 100), Color.Gray*0.8f); + limbPanel.Padding = new Vector4(10.0f,10.0f,10.0f,10.0f); + new GUITextBlock(new Rectangle(0, 0, 200, 25), editingLimb.type.ToString(), Color.Transparent, Color.Black, Alignment.Left, limbPanel); + + //spriteOrigin = new GUITextBlock(new Rectangle(0, 25, 200, 25), "Sprite origin: ", Color.White, Color.Black, Alignment.Left, limbPanel); + + } + catch + { + return false; + } + return true; + } + + private void EditLimb(SpriteBatch spriteBatch) + { + if (editingLimb == null) return; + + limbPanel.Draw(spriteBatch); + } + + private bool TogglePhysics(GUIButton button, object selection) + { + physicsEnabled = !physicsEnabled; + + physicsButton.Text = (physicsEnabled) ? "Disable physics" : "Enable physics"; + + return false; + } + } +} diff --git a/Subsurface/Screens/EditMapScreen.cs b/Subsurface/Screens/EditMapScreen.cs new file mode 100644 index 000000000..1d8cef236 --- /dev/null +++ b/Subsurface/Screens/EditMapScreen.cs @@ -0,0 +1,291 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using System; + +namespace Subsurface +{ + class EditMapScreen : Screen + { + Camera cam; + + GUIComponent GUIpanel; + + GUIComponent[] GUItabs; + int selectedTab; + + //a character used for picking up and manipulating items + Character dummyCharacter; + + bool characterMode; + + public Camera Cam + { + get { return cam; } + } + + public EditMapScreen() + { + cam = new Camera(); + cam.Translate(new Vector2(-10.0f, 50.0f)); + + selectedTab = -1; + + GUIpanel = new GUIFrame(new Rectangle(0,0, 120, Game1.GraphicsHeight), Color.DarkGray*0.8f); + GUIpanel.Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); + //GUIListBox constructionList = new GUIListBox(new Rectangle(0, 0, 0, 300), Color.White * 0.7f, GUIpanel); + //constructionList.OnSelected = MapEntityPrefab.SelectPrefab; + //constructionList.CheckSelected = MapEntityPrefab.GetSelected; + + GUIButton button = new GUIButton(new Rectangle(0, 50, 100, 20), "Items", GUI.style, Alignment.Left, GUIpanel); + button.UserData = 0; + button.OnClicked = SelectTab; + + button = new GUIButton(new Rectangle(0, 80, 100, 20), "Structures", GUI.style, Alignment.Left, GUIpanel); + button.UserData = 1; + button.OnClicked = SelectTab; + + button = new GUIButton(new Rectangle(0, 140, 100, 20), "Character mode", GUI.style, Alignment.Left, GUIpanel); + button.OnClicked = ToggleCharacterMode; + + GUItabs = new GUIComponent[2]; + int width = 400, height = 400; + GUItabs[0] = new GUIFrame(new Rectangle(Game1.GraphicsWidth/2-width/2, Game1.GraphicsHeight/2-height/2, width, height), Color.DarkGray*0.8f); + GUItabs[0].Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); + GUIListBox itemList = new GUIListBox(new Rectangle(0, 0, 0, 0), Color.White * 0.7f, GUItabs[0]); + itemList.OnSelected = SelectPrefab; + itemList.CheckSelected = MapEntityPrefab.GetSelected; + + GUItabs[1] = new GUIFrame(new Rectangle(Game1.GraphicsWidth / 2 - width / 2, Game1.GraphicsHeight / 2 - height / 2, width, height), Color.DarkGray * 0.8f); + GUItabs[1].Padding = new Vector4(10.0f, 10.0f, 10.0f, 10.0f); + GUIListBox structureList = new GUIListBox(new Rectangle(0, 0, 0, 300), Color.White * 0.7f, GUItabs[1]); + structureList.OnSelected = SelectPrefab; + structureList.CheckSelected = MapEntityPrefab.GetSelected; + + foreach (MapEntityPrefab ep in MapEntityPrefab.list) + { + GUIListBox parent = ((ep as ItemPrefab) == null) ? structureList : itemList; + Color color = ((parent.CountChildren % 2) == 0) ? Color.White : Color.LightGray; + + GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 50), Color.Transparent, parent); + frame.UserData = ep; + frame.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f); + frame.Color = color; + frame.HoverColor = Color.Gold * 0.2f; + frame.SelectedColor = Color.Gold * 0.5f; + + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(40, 0, 0, 25), + ep.Name, + Color.Transparent, Color.Black, + Alignment.Left, + Alignment.Left, + frame); + textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f); + + if (ep.sprite != null) + { + GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), ep.sprite, Alignment.Left, frame); + img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f); + } + } + } + + public override void Select() + { + base.Select(); + + GUIComponent.MouseOn = null; + characterMode = false; + //CreateDummyCharacter(); + } + + public override void Deselect() + { + base.Deselect(); + + GUIComponent.MouseOn = null; + + if (dummyCharacter != null) + { + dummyCharacter.Remove(); + dummyCharacter = null; + Game1.world.ProcessChanges(); + } + } + + private void CreateDummyCharacter() + { + if (dummyCharacter != null) dummyCharacter.Remove(); + + dummyCharacter = new Character("Content/Characters/Human/human.xml", Vector2.Zero); + Character.Controlled = dummyCharacter; + Game1.world.ProcessChanges(); + } + + private bool SelectTab(GUIButton button, object obj) + { + selectedTab = (int)obj; + return true; + } + + private bool ToggleCharacterMode(GUIButton button, object obj) + { + characterMode = !characterMode; + button.Color = (characterMode) ? Color.Gold : Color.White; + + if (characterMode) + { + CreateDummyCharacter(); + } + else if (dummyCharacter != null) + { + dummyCharacter.Remove(); + dummyCharacter = null; + } + + foreach (MapEntity me in MapEntity.mapEntityList) + { + me.IsHighlighted = false; + me.IsSelected = false; + } + + return true; + } + + private bool SelectPrefab(object obj) + { + MapEntityPrefab.SelectPrefab(obj); + selectedTab = -1; + GUIComponent.MouseOn = null; + return true; + } + + + /// + /// Allows the game to run logic such as updating the world, + /// checking for collisions, gathering input, and playing audio. + /// + /// Provides a snapshot of timing values. + public override void Update(double deltaTime) + { + //Vector2 mousePosition = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y); + //mousePosition = cam.ScreenToWorld(mousePosition); + + //if (!Character.characterList.Contains(dummyCharacter)) + //{ + // CreateDummyCharacter(); + //} + + cam.MoveCamera((float)deltaTime); + cam.Zoom = MathHelper.Clamp(cam.Zoom + PlayerInput.ScrollWheelSpeed/1000.0f,0.1f, 2.0f); + + if (characterMode) + { + foreach (MapEntity me in MapEntity.mapEntityList) + { + me.IsHighlighted = false; + } + + if (dummyCharacter.SelectedConstruction==null) + { + Vector2 mouseSimPos = FarseerPhysics.ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition)); + foreach (Limb limb in dummyCharacter.animController.limbs) + { + limb.body.SetTransform(mouseSimPos, 0.0f); + } + } + + dummyCharacter.ControlLocalPlayer(cam, false); + dummyCharacter.Control(cam); + } + else + { + + MapEntity.UpdateSelecting(cam); + } + + + GUIpanel.Update((float)deltaTime); + if (selectedTab > -1) + { + GUItabs[selectedTab].Update((float)deltaTime); + if (PlayerInput.RightButtonClicked()) selectedTab = -1; + } + } + + /// + /// This is called when the game should draw itself. + /// + /// Provides a snapshot of timing values. + public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) + { + //cam.UpdateTransform(); + + spriteBatch.Begin(SpriteSortMode.BackToFront, + BlendState.AlphaBlend, + null, null, null, null, + cam.Transform); + + graphics.Clear(new Color(0.051f, 0.149f, 0.271f, 1.0f)); + + Map.Draw(spriteBatch, true); + + if (!characterMode) + { + if (MapEntityPrefab.Selected != null) MapEntityPrefab.Selected.UpdatePlacing(spriteBatch, cam); + + MapEntity.DrawSelecting(spriteBatch, cam); + } + + spriteBatch.End(); + + //-------------------- HUD ----------------------------- + + spriteBatch.Begin(); + + GUIpanel.Draw(spriteBatch); + + if (selectedTab > -1) GUItabs[selectedTab].Draw(spriteBatch); + + GUI.Draw((float)deltaTime, spriteBatch); + + //EntityPrefab.DrawList(spriteBatch, new Vector2(20,50)); + + + + if (characterMode) + { + if (dummyCharacter != null && dummyCharacter.SelectedConstruction != null) + { + if (Character.Controlled.SelectedConstruction == dummyCharacter.ClosestItem) + { + dummyCharacter.SelectedConstruction.DrawHUD(spriteBatch, dummyCharacter); + } + else + { + dummyCharacter.SelectedConstruction = null; + } + } + + if (PlayerInput.GetMouseState.LeftButton != ButtonState.Pressed) + { + //if (Inventory.draggingItem!=null) + //{ + // Inventory.draggingItem.see + //} + Inventory.draggingItem = null; + } + } + else + { + MapEntity.Edit(spriteBatch, cam); + } + + + + spriteBatch.End(); + + } + } +} diff --git a/Subsurface/Screens/GameScreen.cs b/Subsurface/Screens/GameScreen.cs new file mode 100644 index 000000000..e412715ae --- /dev/null +++ b/Subsurface/Screens/GameScreen.cs @@ -0,0 +1,255 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using Subsurface.Lights; +using FarseerPhysics; + +namespace Subsurface +{ + class GameScreen : Screen + { + Camera cam; + + readonly RenderTarget2D renderTarget; + readonly RenderTarget2D renderTargetWater; + readonly RenderTarget2D renderTargetAir; + + readonly Sprite background, backgroundTop; + + public Camera Cam + { + get { return cam; } + } + + public GameScreen(GraphicsDevice graphics) + { + cam = new Camera(); + cam.Translate(new Vector2(-10.0f, 50.0f)); + + renderTarget = new RenderTarget2D(graphics, Game1.GraphicsWidth, Game1.GraphicsHeight); + renderTargetWater = new RenderTarget2D(graphics, Game1.GraphicsWidth, Game1.GraphicsHeight); + renderTargetAir = new RenderTarget2D(graphics, Game1.GraphicsWidth, Game1.GraphicsHeight); + + background = new Sprite("Content/Map/background.png", Vector2.Zero); + backgroundTop = new Sprite("Content/Map/background2.png", Vector2.Zero); + } + + public override void Select() + { + base.Select(); + + if (Game1.gameSession == null) Game1.gameSession = new GameSession("",false, TimeSpan.Zero); + + foreach (MapEntity entity in MapEntity.mapEntityList) + entity.IsHighlighted = false; + } + + /// + /// Allows the game to run logic such as updating the world, + /// checking for collisions, gathering input, and playing audio. + /// + /// Provides a snapshot of timing values. + public override void Update(double deltaTime) + { + //the accumulator code is based on this article: + //http://gafferongames.com/game-physics/fix-your-timestep/ + Physics.accumulator += deltaTime; + + AmbientSoundManager.Update(); + + Game1.gameSession.Update((float)deltaTime); + //EventManager.Update(gameTime); + + Character.UpdateAll(cam, (float)deltaTime); + + Game1.particleManager.Update((float)deltaTime); + + StatusEffect.UpdateAll((float)deltaTime); + //Physics.updated = false; + + cam.MoveCamera((float)deltaTime); + + Physics.accumulator = Math.Min(Physics.accumulator, Physics.step * 4); + while (Physics.accumulator >= Physics.step) + { + foreach (PhysicsBody pb in PhysicsBody.list) + { + pb.SetPrevTransform(pb.Position, pb.Rotation); + } + + MapEntity.UpdateAll(cam, (float)Physics.step); + + Character.UpdateAnimAll((float)Physics.step); + + Ragdoll.UpdateAll((float)Physics.step); + + Game1.world.Step((float)Physics.step); + + Physics.accumulator -= Physics.step; + } + + + Physics.Alpha = Physics.accumulator / Physics.step; + + } + + /// + /// This is called when the game should draw itself. + /// + public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) + { + //if (!Physics.updated) return; + + DrawMap(graphics, spriteBatch); + + //---------------------------------------------------------------------------------------- + //2. draw the HUD on top of everything + //---------------------------------------------------------------------------------------- + + spriteBatch.Begin(); + if (Game1.gameSession != null) Game1.gameSession.Draw(spriteBatch); + + //EventManager.DrawInfo(spriteBatch); + + if (Character.Controlled != null && Character.Controlled.SelectedConstruction != null) + { + if (Character.Controlled.SelectedConstruction == Character.Controlled.ClosestItem) + { + Character.Controlled.SelectedConstruction.DrawHUD(spriteBatch, Character.Controlled); + } + else + { + Character.Controlled.SelectedConstruction = null; + } + } + + GUI.Draw((float)deltaTime, spriteBatch); + + if (PlayerInput.GetMouseState.LeftButton != ButtonState.Pressed) Inventory.draggingItem = null; + + spriteBatch.End(); + } + + public void DrawMap(GraphicsDevice graphics, SpriteBatch spriteBatch) + { + //---------------------------------------------------------------------------------------- + //1. draw the characters and the parts of the map that are behind them + //---------------------------------------------------------------------------------------- + + //cam.UpdateTransform(); + + //---------------------------------------------------------------------------------------- + //draw the map and characters to a rendertarget + graphics.SetRenderTarget(renderTarget); + graphics.Clear(new Color(11, 18, 26, 255)); + + + spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.LinearWrap); + + int x = (int)(cam.Position.X / 20.0f); + int y = (int)((-cam.Position.Y+5000) / 20.0f); + if (y<1024) + { + if (y>-1024) + { + background.SourceRect = new Rectangle(x, Math.Max(y,0), 1024, 1024); + background.DrawTiled(spriteBatch, (y<0) ? new Vector2(0.0f, -y) : Vector2.Zero, new Vector2(Game1.GraphicsWidth, 1024 - y), + Vector2.Zero, Color.White); + } + + if (y<0) + { + backgroundTop.SourceRect = new Rectangle(x, y, 1024, 1024); + backgroundTop.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(Game1.GraphicsWidth, Math.Min(1024 - y, Game1.GraphicsHeight)), + Vector2.Zero, Color.White); + } + } + + spriteBatch.End(); + + + spriteBatch.Begin(SpriteSortMode.BackToFront, + BlendState.AlphaBlend, + null, null, null, null, + cam.Transform); + + Map.DrawBack(spriteBatch); + + foreach (Character c in Character.characterList) c.Draw(spriteBatch); + + spriteBatch.End(); + + //---------------------------------------------------------------------------------------- + //draw the rendertarget and particles that are only supposed to be drawn in water into renderTargetWater + + graphics.SetRenderTarget(renderTargetWater); + + spriteBatch.Begin(SpriteSortMode.Immediate, + BlendState.AlphaBlend); + spriteBatch.Draw(renderTarget, new Rectangle(0, 0, Game1.GraphicsWidth, Game1.GraphicsHeight), Color.White); + spriteBatch.End(); + + BlendState blend = new BlendState(); + blend.AlphaSourceBlend = Blend.One; + blend.AlphaDestinationBlend = Blend.InverseSourceAlpha; + + spriteBatch.Begin(SpriteSortMode.Immediate, + BlendState.AlphaBlend, + null, DepthStencilState.DepthRead, null, null, + cam.Transform); + Game1.particleManager.Draw(spriteBatch, true); + + spriteBatch.End(); + + //---------------------------------------------------------------------------------------- + //draw the rendertarget and particles that are only supposed to be drawn in air into renderTargetAir + + graphics.SetRenderTarget(renderTargetAir); + spriteBatch.Begin(SpriteSortMode.Immediate, + BlendState.AlphaBlend); + spriteBatch.Draw(renderTarget, new Rectangle(0, 0, Game1.GraphicsWidth, Game1.GraphicsHeight), Color.White); + spriteBatch.End(); + + spriteBatch.Begin(SpriteSortMode.Immediate, + BlendState.AlphaBlend, + null, DepthStencilState.DepthRead, null, null, + cam.Transform); + + Game1.particleManager.Draw(spriteBatch, false); + spriteBatch.End(); + + graphics.SetRenderTarget(null); + + //---------------------------------------------------------------------------------------- + //2. pass the renderTarget to the water shader to do the water effect + //---------------------------------------------------------------------------------------- + + Hull.renderer.RenderBack(graphics, renderTargetWater, Cam.ShaderTransform); + + Array.Clear(Hull.renderer.vertices, 0, Hull.renderer.vertices.Length); + Hull.renderer.positionInBuffer = 0; + foreach (Hull hull in Hull.hullList) + { + hull.Render(graphics, cam); + } + + Hull.renderer.Render(graphics, cam, renderTargetAir, Cam.ShaderTransform); + + //---------------------------------------------------------------------------------------- + //3. draw the sections of the map that are on top of the water + //---------------------------------------------------------------------------------------- + + spriteBatch.Begin(SpriteSortMode.BackToFront, + BlendState.AlphaBlend, + null, null, null, null, + cam.Transform); + + Map.DrawFront(spriteBatch); + + spriteBatch.End(); + + LightManager.DrawFow(graphics,cam); + } + } +} diff --git a/Subsurface/Screens/LobbyScreen.cs b/Subsurface/Screens/LobbyScreen.cs new file mode 100644 index 000000000..263fcdedf --- /dev/null +++ b/Subsurface/Screens/LobbyScreen.cs @@ -0,0 +1,227 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Subsurface +{ + class LobbyScreen : Screen + { + GUIFrame leftPanel; + GUIFrame[] rightPanel; + + GUIFrame shiftPanel; + + int selectedRightPanel; + + GUIListBox characterList; + GUIListBox hireList; + + public LobbyScreen() + { + Rectangle panelRect = new Rectangle( + (int)GUI.style.largePadding.X, + (int)GUI.style.largePadding.Y, + (int)(Game1.GraphicsWidth * 0.3f) - (int)(GUI.style.largePadding.X * 1.5f), + Game1.GraphicsHeight - (int)(GUI.style.largePadding.Y * 2)); + + leftPanel = new GUIFrame(panelRect, GUI.style.backGroundColor); + leftPanel.Padding = GUI.style.smallPadding; + + new GUITextBlock(new Rectangle(0, 0, 200, 25), + "asdfdasfasdf", Color.Transparent, Color.White, Alignment.Left, leftPanel); + + GUITextBlock moneyText = new GUITextBlock(new Rectangle(0, 30, 200, 25), + "", Color.Transparent, Color.White, Alignment.Left, leftPanel); + moneyText.TextGetter = GetMoney; + + GUIButton button = new GUIButton(new Rectangle(0, 60, 100, 30), "Crew", GUI.style, Alignment.CenterX, leftPanel); + button.UserData = 0; + button.OnClicked = SelectRightPanel; + + button = new GUIButton(new Rectangle(0, 100, 100, 30), "Hire", GUI.style, Alignment.CenterX, leftPanel); + button.UserData = 1; + button.OnClicked = SelectRightPanel; + + //-------------------------------------- + + panelRect = new Rectangle( + panelRect.X + panelRect.Width + (int)(GUI.style.largePadding.X), + panelRect.Y, + Game1.GraphicsWidth - panelRect.Width - (int)(GUI.style.largePadding.X * 3.0f), + (int)(Game1.GraphicsHeight * 0.3f) - (int)(GUI.style.largePadding.Y * 1.5f)); + + shiftPanel = new GUIFrame(panelRect, GUI.style.backGroundColor); + shiftPanel.Padding = GUI.style.smallPadding; + + GUITextBlock dayText = new GUITextBlock(new Rectangle(0, 0, 200, 25), + "", Color.Transparent, Color.White, Alignment.Left, shiftPanel); + dayText.TextGetter = GetDay; + + GUIProgressBar progressBar = new GUIProgressBar(new Rectangle(0, 30, 200, 20), Color.Green, 0.0f, shiftPanel); + progressBar.ProgressGetter = GetWeekProgress; + + + button = new GUIButton(new Rectangle(0,0,100,30), "Start", GUI.style, + (Alignment.Right | Alignment.Bottom), shiftPanel); + button.OnClicked = StartShift; + + //--------------------------------------------------------------- + //--------------------------------------------------------------- + + rightPanel = new GUIFrame[2]; + + panelRect = new Rectangle( + panelRect.X, + panelRect.Y + panelRect.Height + (int)(GUI.style.largePadding.Y), + panelRect.Width, + (int)(Game1.GraphicsHeight * 0.7f) - (int)(GUI.style.largePadding.Y * 1.5f)); + + rightPanel[0] = new GUIFrame(panelRect, GUI.style.backGroundColor); + rightPanel[0].Padding = GUI.style.smallPadding; + + new GUITextBlock(new Rectangle(0, 0, 200, 25), "Crew:", Color.Transparent, Color.White, Alignment.Left, rightPanel[0]); + + characterList = new GUIListBox(new Rectangle(0, 30, 300, 400), Color.White, rightPanel[0]); + + //--------------------------------------- + + rightPanel[1] = new GUIFrame(panelRect, GUI.style.backGroundColor); + rightPanel[1].Padding = GUI.style.smallPadding; + + hireList = new GUIListBox(new Rectangle(0, 30, 300, 400), Color.White, Alignment.Left, rightPanel[1]); + + hireList.OnSelected = HireCharacter; + } + + public override void Select() + { + base.Select(); + + Map.Clear(); + + UpdateCharacterLists(); + + } + + private void UpdateCharacterLists() + { + characterList.ClearChildren(); + foreach (CharacterInfo c in Game1.gameSession.crewManager.characterInfos) + { + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(0, 0, 0, 25), + c.name, GUI.style, + Alignment.Left, + Alignment.Left, + characterList); + textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); + } + + hireList.ClearChildren(); + foreach (CharacterInfo c in Game1.gameSession.hireManager.availableCharacters) + { + GUIFrame frame = new GUIFrame( + new Rectangle(0, 0, 0, 25), + Color.White, + Alignment.Left, + GUI.style, + hireList); + frame.UserData = c; + frame.Padding = new Vector4(10.0f, 0.0f, 10.0f, 0.0f); + + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(0, 0, 0, 25), + c.name, + Color.Transparent, Color.Black, + Alignment.Left, + frame); + + textBlock = new GUITextBlock( + new Rectangle(0, 0, 0, 25), + c.salary.ToString(), + Color.Transparent, Color.Black, + Alignment.Right, + frame); + } + } + + public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) + { + + if (characterList.CountChildren != Game1.gameSession.crewManager.characterInfos.Count + || hireList.CountChildren != Game1.gameSession.hireManager.availableCharacters.Count) + { + UpdateCharacterLists(); + } + + graphics.Clear(Color.CornflowerBlue); + + Game1.gameScreen.DrawMap(graphics, spriteBatch); + + spriteBatch.Begin(); + + + //ConstructionPrefab.list[5].sprite.Draw(spriteBatch, Vector2.Zero, new Vector2(10.0f, 1.0f), Color.White); + + + leftPanel.Draw(spriteBatch); + shiftPanel.Draw(spriteBatch); + + rightPanel[selectedRightPanel].Draw(spriteBatch); + + GUI.Draw((float)deltaTime, spriteBatch); + + spriteBatch.End(); + } + + public bool SelectRightPanel(GUIButton button, object selection) + { + try { selectedRightPanel = (int)selection; } + catch { return false; } + return true; + } + + private string GetMoney() + { + return "Money: " + ((Game1.gameSession == null) ? "" : Game1.gameSession.crewManager.Money.ToString()); + } + + private string GetDay() + { + + return "Day #" + ((Game1.gameSession == null) ? "" : Game1.gameSession.Day.ToString()); + } + + private float GetWeekProgress() + { + if (Game1.gameSession == null) return 0.0f; + + return (float)((Game1.gameSession.Day - 1) % 7) / 7.0f; + } + + private bool HireCharacter(object selection) + { + CharacterInfo characterInfo; + try { characterInfo = (CharacterInfo)selection; } + catch { return false; } + + if (characterInfo == null) return false; + + Game1.gameSession.TryHireCharacter(characterInfo); + + return false; + } + + private bool StartShift(GUIButton button, object selection) + { + Map.Load("", Game1.gameSession.SaveFile); + + Game1.gameSession.StartShift(); + + //EventManager.StartShift(); + + Game1.gameScreen.Select(); + + return true; + } + } +} diff --git a/Subsurface/Screens/MainMenu.cs b/Subsurface/Screens/MainMenu.cs new file mode 100644 index 000000000..9fa95d42a --- /dev/null +++ b/Subsurface/Screens/MainMenu.cs @@ -0,0 +1,177 @@ +using System; +using System.IO; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Subsurface.Networking; + +namespace Subsurface +{ + class MainMenuScreen : Screen + { + enum Tabs { Main = 0, NewGame = 1, JoinServer = 2} + + GUIFrame[] menuTabs; + GUIListBox mapList; + + GUITextBox nameBox; + GUITextBox ipBox; + + Game1 game; + + int selectedTab; + + public MainMenuScreen(Game1 game) + { + menuTabs = new GUIFrame[3]; + + Rectangle panelRect = new Rectangle( + Game1.GraphicsWidth / 2 - 250, + Game1.GraphicsHeight/ 2 - 250, + 500, 500); + + menuTabs[(int)Tabs.Main] = new GUIFrame(panelRect, GUI.style.backGroundColor); + menuTabs[(int)Tabs.Main].Padding = GUI.style.smallPadding; + + GUIButton button = new GUIButton(new Rectangle(0, 0, 0, 30), "New Game", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); + button.OnClicked = NewGameClicked; + //button.Enabled = false; + + button = new GUIButton(new Rectangle(0, 60, 0, 30), "Join Server", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); + button.OnClicked = JoinServerClicked; + + button = new GUIButton(new Rectangle(0, 120, 0, 30), "Host Server", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); + button.OnClicked = HostServerClicked; + //button.Enabled = false; + + button = new GUIButton(new Rectangle(0, 180, 0, 30), "Quit", GUI.style, Alignment.CenterX, menuTabs[(int)Tabs.Main]); + button.OnClicked = QuitClicked; + + //---------------------------------------------------------------------- + + menuTabs[(int)Tabs.NewGame] = new GUIFrame(panelRect, GUI.style.backGroundColor); + menuTabs[(int)Tabs.NewGame].Padding = GUI.style.smallPadding; + + new GUITextBlock(new Rectangle(0, 0, 0, 30), "New Game", Color.Transparent, Color.Black, Alignment.CenterX, menuTabs[(int)Tabs.NewGame]); + + new GUITextBlock(new Rectangle(0, 30, 0, 30), "Selected map:", Color.Transparent, Color.Black, Alignment.Left, menuTabs[(int)Tabs.NewGame]); + mapList = new GUIListBox(new Rectangle(0, 60, 200, 400), Color.White, menuTabs[1]); + + string[] mapFilePaths = Map.GetMapFilePaths(); + if (mapFilePaths!=null) + { + foreach (string s in mapFilePaths) + { + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(0, 0, 0, 25), + Path.GetFileNameWithoutExtension(s), + GUI.style, + Alignment.Left, + Alignment.Left, + mapList); + textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); + textBlock.UserData = s; + } + if (mapFilePaths.Length > 0) mapList.Select(mapFilePaths[0]); + } + + button = new GUIButton(new Rectangle(0, 0, 100, 30), "Start", GUI.style, Alignment.Right | Alignment.Bottom, menuTabs[(int)Tabs.NewGame]); + button.OnClicked = StartGame; + + + //---------------------------------------------------------------------- + + menuTabs[(int)Tabs.JoinServer] = new GUIFrame(panelRect, GUI.style.backGroundColor); + menuTabs[(int)Tabs.JoinServer].Padding = GUI.style.smallPadding; + + new GUITextBlock(new Rectangle(0, 0, 0, 30), "Join Server", Color.Transparent, Color.Black, Alignment.CenterX, menuTabs[(int)Tabs.JoinServer]); + + new GUITextBlock(new Rectangle(0, 30, 0, 30), "Name:", Color.Transparent, Color.Black, Alignment.CenterX, menuTabs[(int)Tabs.JoinServer]); + nameBox = new GUITextBox(new Rectangle(0, 60, 200, 30), Color.White, Color.Black, Alignment.CenterX, Alignment.CenterX, menuTabs[(int)Tabs.JoinServer]); + + new GUITextBlock(new Rectangle(0, 100, 0, 30), "Server IP:", Color.Transparent, Color.Black, Alignment.CenterX, menuTabs[(int)Tabs.JoinServer]); + ipBox = new GUITextBox(new Rectangle(0, 130, 200, 30), Color.White, Color.Black, Alignment.CenterX, Alignment.CenterX, menuTabs[(int)Tabs.JoinServer]); + + GUIButton joinButton = new GUIButton(new Rectangle(0, 0, 200, 30), "Join", Color.White, Alignment.Bottom | Alignment.CenterX, menuTabs[(int)Tabs.JoinServer]); + joinButton.OnClicked = JoinServer; + + + this.game = game; + + } + + private bool NewGameClicked(GUIButton button, object obj) + { + selectedTab = (int)Tabs.NewGame; + return true; + } + + private bool JoinServerClicked(GUIButton button, object obj) + { + selectedTab = (int)Tabs.JoinServer; + return true; + } + + private bool HostServerClicked(GUIButton button, object obj) + { + Game1.netLobbyScreen.isServer = true; + Game1.netLobbyScreen.Select(); + return true; + } + + private bool QuitClicked(GUIButton button, object obj) + { + game.Exit(); + return true; + } + + public override void Update(double deltaTime) + { + menuTabs[selectedTab].Update((float)deltaTime); + } + + public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) + { + graphics.Clear(Color.CornflowerBlue); + + Game1.gameScreen.DrawMap(graphics, spriteBatch); + + spriteBatch.Begin(); + + menuTabs[selectedTab].Draw(spriteBatch); + + GUI.Draw((float)deltaTime, spriteBatch); + + spriteBatch.End(); + } + + private bool StartGame(GUIButton button, object obj) + { + if (mapList.SelectedData == null) return false; + + Game1.gameSession = new GameSession(mapList.SelectedData.ToString(), true, TimeSpan.Zero); + + Game1.lobbyScreen.Select(); + + return true; + } + + private bool JoinServer(GUIButton button, object obj) + { + if (string.IsNullOrEmpty(nameBox.Text)) return false; + if (string.IsNullOrEmpty(ipBox.Text)) return false; + + Game1.client = new GameClient(nameBox.Text); + if (Game1.client.ConnectToServer(ipBox.Text)) + { + Game1.netLobbyScreen.Select(); + return true; + } + else + { + Game1.client = null; + return false; + } + } + + } +} diff --git a/Subsurface/Screens/NetLobbyScreen.cs b/Subsurface/Screens/NetLobbyScreen.cs new file mode 100644 index 000000000..b03132a1a --- /dev/null +++ b/Subsurface/Screens/NetLobbyScreen.cs @@ -0,0 +1,462 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Lidgren.Network; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Subsurface.Networking; +using FarseerPhysics; +using FarseerPhysics.Factories; +using FarseerPhysics.Dynamics; + +namespace Subsurface +{ + class NetLobbyScreen : Screen + { + GUIFrame menu; + GUIFrame infoFrame; + GUIListBox playerList; + + GUIListBox mapList, modeList, chatBox; + GUITextBox textBox; + + GUIScrollBar durationBar; + + GUIFrame playerFrame; + + float camAngle; + + public bool isServer; + + public string SelectedMap + { + get { return mapList.SelectedData.ToString(); } + } + + + public GameMode SelectedMode + { + get { return modeList.SelectedData as GameMode; } + } + + public TimeSpan GameDuration + { + get + { + int minutes = (int)(durationBar.BarScroll* 60.0f); + return new TimeSpan(0, minutes, 0); + } + } + + public string DurationText() + { + return "Game duration: "+GameDuration+" min"; + } + + public NetLobbyScreen() + { + Rectangle panelRect = new Rectangle( + (int)GUI.style.largePadding.X, + (int)GUI.style.largePadding.Y, + (int)(Game1.GraphicsWidth - GUI.style.largePadding.X * 2.0f), + (int)(Game1.GraphicsHeight - GUI.style.largePadding.Y * 2.0f)); + + menu = new GUIFrame(panelRect, Color.Transparent); + //menu.Padding = GUI.style.smallPadding; + + //server info panel ------------------------------------------------------------ + + infoFrame = new GUIFrame(new Rectangle(0, 0, (int)(panelRect.Width * 0.6f), (int)(panelRect.Height * 0.6f)), GUI.style.backGroundColor, menu); + infoFrame.Padding = GUI.style.smallPadding; + + //chatbox ---------------------------------------------------------------------- + GUIFrame chatFrame = new GUIFrame( + new Rectangle(0, (int)(panelRect.Height * 0.6f + GUI.style.smallPadding.W), + (int)(panelRect.Width * 0.6f), + (int)(panelRect.Height * 0.4f - GUI.style.smallPadding.W)), + GUI.style.backGroundColor, menu); + chatFrame.Padding = GUI.style.smallPadding; + + chatBox = new GUIListBox(new Rectangle(0,0,0,chatFrame.Rect.Height-80), Color.White, chatFrame); + textBox = new GUITextBox(new Rectangle(0, 0, 0, 25), Color.White, Color.Black, Alignment.Bottom, Alignment.Left, chatFrame); + textBox.OnEnter = EnterChatMessage; + + //player info panel ------------------------------------------------------------ + + playerFrame = new GUIFrame( + new Rectangle((int)(panelRect.Width * 0.6f + GUI.style.smallPadding.Z), 0, + (int)(panelRect.Width * 0.4f - GUI.style.smallPadding.Z), (int)(panelRect.Height * 0.6f)), + GUI.style.backGroundColor, menu); + playerFrame.Padding = GUI.style.smallPadding; + + //player list ------------------------------------------------------------------ + + GUIFrame playerListFrame = new GUIFrame( + new Rectangle((int)(panelRect.Width * 0.6f + GUI.style.smallPadding.Z), (int)(panelRect.Height * 0.6f + GUI.style.smallPadding.W), + (int)(panelRect.Width * 0.4f - GUI.style.smallPadding.Z), (int)(panelRect.Height * 0.4f - GUI.style.smallPadding.W)), + GUI.style.backGroundColor, menu); + playerListFrame.Padding = GUI.style.smallPadding; + + playerList = new GUIListBox(new Rectangle(0,0,0,0), Color.White, playerListFrame); + } + + public override void Deselect() + { + textBox.Deselect(); + } + + public override void Select() + { + infoFrame.ClearChildren(); + + if (isServer && Game1.server == null) Game1.server = new GameServer(); + + textBox.Select(); + + int oldMapIndex = 0; + if (mapList != null && mapList.SelectedData != null) oldMapIndex = mapList.SelectedIndex; + + new GUITextBlock(new Rectangle(0, 30, 0, 30), "Selected map:", Color.Transparent, Color.Black, Alignment.Left, infoFrame); + mapList = new GUIListBox(new Rectangle(0, 60, 200, 200), Color.White, infoFrame); + mapList.OnSelected = SelectMap; + mapList.Enabled = (Game1.server!=null); + + string[] mapFilePaths = Map.GetMapFilePaths(); + if (mapFilePaths != null) + { + foreach (string s in mapFilePaths) + { + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(0, 0, 0, 25), + Path.GetFileNameWithoutExtension(s), + GUI.style, + Alignment.Left, + Alignment.Left, + mapList); + textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); + textBlock.UserData = s; + } + } + else + { + DebugConsole.ThrowError("No saved maps found!"); + return; + } + + new GUITextBlock(new Rectangle(220, 30, 0, 30), "Selected game mode: ", Color.Transparent, Color.Black, Alignment.Left, infoFrame); + modeList = new GUIListBox(new Rectangle(220, 60, 200, 200), Color.White, infoFrame); + modeList.Enabled = (Game1.server != null); + //modeList.OnSelected = new GUIListBox.OnSelectedHandler(SelectEvent); + + foreach (GameMode mode in GameMode.list) + { + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(0, 0, 0, 25), + mode.Name, + GUI.style, + Alignment.Left, + Alignment.Left, + modeList); + textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); + textBlock.UserData = mode; + } + + GUITextBlock durationText = new GUITextBlock(new Rectangle((int)(modeList.Rect.X + modeList.Rect.Width + GUI.style.smallPadding.X), modeList.Rect.Y, 100, 20), + "Game duration: ", Color.Transparent, Color.Black, Alignment.Left, infoFrame); + durationText.TextGetter = DurationText; + + durationBar = new GUIScrollBar(new Rectangle((int)(modeList.Rect.X + modeList.Rect.Width + GUI.style.smallPadding.X), modeList.Rect.Y + 30, 100, 20), + Color.Gold, 0.1f, Alignment.Left, infoFrame); + durationBar.BarSize = 0.1f; + durationBar.Enabled = (Game1.server != null); + + if (isServer && Game1.server!=null) + { + GUIButton startButton = new GUIButton(new Rectangle(0,0,200,30), "Start", Color.White, Alignment.Right | Alignment.Bottom, infoFrame); + startButton.OnClicked = Game1.server.StartGame; + + //mapList.OnSelected = new GUIListBox.OnSelectedHandler(Game1.server.UpdateNetLobby); + modeList.OnSelected = Game1.server.UpdateNetLobby; + durationBar.OnMoved = Game1.server.UpdateNetLobby; + + if (mapFilePaths.Length > 0) mapList.Select(mapFilePaths[oldMapIndex]); + if (GameMode.list.Count > 0) modeList.Select(GameMode.list[0]); + } + else + { + int x = playerFrame.Rect.Width / 2; + GUITextBox playerName = new GUITextBox(new Rectangle(x, 0, 0, 20), Color.White, Color.Black, + Alignment.Left | Alignment.Top, Alignment.Left, playerFrame); + playerName.Text = Game1.client.CharacterInfo.name; + playerName.OnEnter += ChangeCharacterName; + + new GUITextBlock(new Rectangle(x,40,200, 30), "Gender: ", Color.Transparent, Color.Black, + Alignment.Left | Alignment.Top, Alignment.Left, playerFrame); + + GUIButton maleButton = new GUIButton(new Rectangle(x+70,50,70,20), "Male", GUI.style, + Alignment.Left | Alignment.Top, playerFrame); + maleButton.UserData = Gender.Male; + maleButton.OnClicked += SwitchGender; + + GUIButton femaleButton = new GUIButton(new Rectangle(x+150, 50, 70, 20), "Female", GUI.style, + Alignment.Left | Alignment.Top, playerFrame); + femaleButton.UserData = Gender.Female; + femaleButton.OnClicked += SwitchGender; + + new GUITextBlock(new Rectangle(0, 200, 200,30), "Job preferences:", Color.Transparent, Color.Black, Alignment.Left, playerFrame); + + GUIListBox jobList = new GUIListBox(new Rectangle(0,230,200,250), Color.White, playerFrame); + + foreach (Job job in Job.jobList) + { + GUITextBlock jobText = new GUITextBlock(new Rectangle(0,0,0,20), job.Name, Color.Transparent, Color.Black, Alignment.Left, jobList); + GUIButton upButton = new GUIButton(new Rectangle(jobText.Rect.Width - 40, 0, 20, 20), "u", Color.White, jobText); + upButton.UserData = -1; + upButton.OnClicked += ChangeJobPreference; + + GUIButton downButton = new GUIButton(new Rectangle(jobText.Rect.Width - 20, 0, 20, 20), "d", Color.White, jobText); + downButton.UserData = 1; + downButton.OnClicked += ChangeJobPreference; + } + + UpdateJobPreferences(jobList); + + } + + base.Select(); + } + + private bool SelectMap(object obj) + { + if (Game1.server != null) Game1.server.UpdateNetLobby(obj); + + if ((string)obj == Map.FilePath) return true; + Map.Load((string)obj); + + return true; + } + + + public void AddPlayer(string name) + { + GUITextBlock textBlock = new GUITextBlock( + new Rectangle(0, 0, 0, 25), + name, + GUI.style, + Alignment.Left, + Alignment.Left, + playerList); + textBlock.Padding = new Vector4(10.0f, 0.0f, 0.0f, 0.0f); + textBlock.UserData = name; + } + + public void RemovePlayer(string name) + { + playerList.RemoveChild(playerList.GetChild(name)); + } + + public override void Update(double deltaTime) + { + base.Update(deltaTime); + + Game1.gameScreen.Cam.MoveCamera((float)deltaTime); + + Vector2 pos = new Vector2( + Map.Borders.X + Map.Borders.Width / 2, + Map.Borders.Y - Map.Borders.Height / 2); + + camAngle += (float)deltaTime / 10.0f; + Vector2 offset = (new Vector2( + (float)Math.Cos(camAngle) * (Map.Borders.Width / 2.0f), + (float)Math.Sin(camAngle) * (Map.Borders.Height / 2.0f))); + + pos += offset * 0.8f; + + Game1.gameScreen.Cam.TargetPos = pos; + + menu.Update((float)deltaTime); + + if (Game1.client != null && Game1.client.Character != null) + { + + } + + durationBar.BarScroll = Math.Max(durationBar.BarScroll, 1.0f / 60.0f); + } + + public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch) + { + graphics.Clear(Color.CornflowerBlue); + + Game1.gameScreen.DrawMap(graphics, spriteBatch); + + spriteBatch.Begin(); + + menu.Draw(spriteBatch); + + GUI.Draw((float)deltaTime, spriteBatch); + + spriteBatch.End(); + + + if (Game1.client != null) + { + if (Game1.client.Character != null) + { + Vector2 position = new Vector2(playerFrame.Rect.X + playerFrame.Rect.Width * 0.25f, playerFrame.Rect.Y + 100.0f); + Matrix transform = Matrix.CreateTranslation(new Vector3(ConvertUnits.ToDisplayUnits(-Game1.client.Character.SimPosition)+position, 0.0f)); + spriteBatch.Begin(SpriteSortMode.BackToFront, null,null,null,null,null,transform); + Game1.client.Character.Draw(spriteBatch); + spriteBatch.End(); + } + else + { + CreatePreviewCharacter(); + } + } + + } + + public void NewChatMessage(string message, Color color) + { + GUITextBlock msg = new GUITextBlock(new Rectangle(0, 0, 0, 20), + message, + ((chatBox.CountChildren % 2) == 0) ? Color.Transparent : Color.Black*0.1f, color, + Alignment.Left, null, true); + + msg.Padding = new Vector4(GUI.style.smallPadding.X, 0, 0, 0); + chatBox.AddChild(msg); + } + + + public bool StartGame(object obj) + { + Game1.server.StartGame(null, obj); + return true; + } + + public bool EnterChatMessage(GUITextBox textBox, string message) + { + if (String.IsNullOrEmpty(message)) return false; + + if (isServer) + { + Game1.server.SendChatMessage("Server: " + message); + } + else + { + Game1.client.SendChatMessage(Game1.client.Name + ": " + message); + } + + return true; + } + + private void CreatePreviewCharacter() + { + if (Game1.client.Character != null) Game1.client.Character.Remove(); + + Vector2 pos = new Vector2(1000.0f, 1000.0f); + + Character character = new Character(Game1.client.CharacterInfo, pos); + + Game1.client.Character = character; + + character.animController.isStanding = true; + + //TODO: only create if hasn't been created yet + //{ + Body platform = BodyFactory.CreateRectangle(Game1.world, 3.0f, 1.0f, 5.0f); + platform.SetTransform(new Vector2(pos.X, pos.Y + 1.5f), 0.0f); + platform.IsStatic = true; + + pos = ConvertUnits.ToDisplayUnits(pos); + new Hull(new Rectangle((int)pos.X - 100, (int)-pos.Y + 100, 200, 200)); + //} + + Physics.Alpha = 1.0f; + + for (int i = 0; i < 500; i++) + { + character.animController.Update((float)Physics.step); + character.animController.UpdateAnim((float)Physics.step); + Game1.world.Step((float)Physics.step); + } + } + + private bool SwitchGender(GUIButton button, object obj) + { + try + { + Gender gender = (Gender)obj; + Game1.client.CharacterInfo.gender = gender; + Game1.client.SendCharacterData(); + CreatePreviewCharacter(); + } + catch + { + return false; + } + return true; + } + + private bool ChangeCharacterName(GUITextBox textBox, string newName) + { + if (string.IsNullOrEmpty(newName)) return false; + + Game1.client.CharacterInfo.name = newName; + Game1.client.Name = newName; + Game1.client.SendCharacterData(); + + textBox.Text = newName; + + return true; + } + + private bool ChangeJobPreference(GUIButton button, object obj) + { + GUIComponent jobText = button.Parent; + GUIListBox jobList = jobText.Parent as GUIListBox; + + int index = jobList.children.IndexOf(jobText); + int newIndex = index + (int)obj; + if (newIndex<0 || newIndex>jobList.children.Count-1) return false; + + GUIComponent temp = jobList.children[newIndex]; + jobList.children[newIndex] = jobText; + jobList.children[index] = temp; + + UpdateJobPreferences(jobList); + + return true; + } + + private void UpdateJobPreferences(GUIListBox listBox) + { + listBox.Deselect(); + for (int i = 0; i0) + { + damageSounds = new DamageSound[xDamageSounds.Count()]; + int i = 0; + foreach (XElement element in xDamageSounds) + { + Sound sound = Sound.Load(ToolBox.GetAttributeString(element, "file", "")); + if (sound == null) continue; + + DamageSoundType damageSoundType = DamageSoundType.None; + + try + { + damageSoundType = (DamageSoundType)Enum.Parse(typeof(DamageSoundType), + ToolBox.GetAttributeString(element, "damagesoundtype", "None")); + } + catch + { + damageSoundType = DamageSoundType.None; + } + + + damageSounds[i] = new DamageSound( + sound, ToolBox.GetAttributeVector2(element, "damagerange", new Vector2(0.0f,100.0f)), damageSoundType); + i++; + } + } + + //Sound.StartStream("Content/Sounds/Music/Simplex.ogg", 0.3f); + + } + + + public static void Update() + { + float ambienceVolume = 0.5f; + float lowpassHFGain = 1.0f; + if (Character.Controlled != null) + { + AnimController animController = Character.Controlled.animController; + if (animController.HeadInWater) + { + ambienceVolume = 0.5f; + ambienceVolume += animController.limbs[0].LinearVelocity.Length(); + + lowpassHFGain = 0.2f; + } + } + + SoundManager.LowPassHFGain = lowpassHFGain; + waterAmbienceIndex = waterAmbience.Loop(waterAmbienceIndex, ambienceVolume); + } + + public static void PlayDamageSound(DamageSoundType damageType, float damage, Body body) + { + Vector2 bodyPosition = ConvertUnits.ToDisplayUnits(body.Position); + bodyPosition.Y = -bodyPosition.Y; + + PlayDamageSound(damageType, damage, bodyPosition); + } + + public static void PlayDamageSound(DamageSoundType damageType, float damage, Vector2 position) + { + damage = MathHelper.Clamp(damage, 0.0f, 100.0f); + var sounds = damageSounds.Where(x => damage >= x.damageRange.X && damage <= x.damageRange.Y && x.damageType == damageType).ToList(); + if (sounds.Count() == 0) return; + + int selectedSound = Game1.localRandom.Next(sounds.Count()); + + int i = 0; + foreach (var s in sounds) + { + if (i == selectedSound) + { + Debug.WriteLine(s.sound.Play(1.0f, 2000.0f, position)); + Debug.WriteLine("playing: " + s.sound); + return; + } + i++; + } + } + + } +} diff --git a/Subsurface/Sounds/OggSound.cs b/Subsurface/Sounds/OggSound.cs new file mode 100644 index 000000000..460f380c4 --- /dev/null +++ b/Subsurface/Sounds/OggSound.cs @@ -0,0 +1,115 @@ +using System; +using NVorbis; +using OpenTK.Audio.OpenAL; + +namespace Subsurface.Sounds +{ + class OggSound : IDisposable + { + //internal VorbisReader Reader { get; private set; } + + //const int DefaultBufferSize = 44100; + + //private VorbisReader reader; + //private SoundEffect effect; + //SoundEffectInstance instance; + + public const int DefaultBufferCount = 3; + + private short[] castBuffer; + + private int sampleRate; + private ALFormat format; + + private string file; + + int alBufferId; + + public int AlBufferId + { + get { return alBufferId; } + } + + //public bool IsLooped { get; set; } + + public static OggSound Load(string oggFile, int bufferCount = DefaultBufferCount) + { + OggSound sound = new OggSound(); + sound.file = oggFile; + + using (VorbisReader reader = new VorbisReader(oggFile)) + { + int bufferSize = (int)reader.TotalSamples; + + float[] buffer = new float[bufferSize]; + sound.castBuffer = new short[bufferSize]; + + int readSamples = reader.ReadSamples(buffer, 0, bufferSize); + CastBuffer(buffer, sound.castBuffer, readSamples); + + sound.alBufferId = AL.GenBuffer(); + + sound.format = reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16; + sound.sampleRate = reader.SampleRate; + + //alSourceId = AL.GenSource(); + AL.BufferData(sound.alBufferId, reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, sound.castBuffer, + readSamples * sizeof(short), reader.SampleRate); + } + + //AL.Source(alSourceId, ALSourcei.Buffer, alBufferId); + + //if (ALHelper.XRam.IsInitialized) + //{ + // ALHelper.XRam.SetBufferMode(bufferCount, ref alBufferId, XRamExtension.XRamStorage.Hardware); + // ALHelper.Check(); + //} + + //Volume = 1; + + //if (ALHelper.Efx.IsInitialized) + //{ + // alFilterId = ALHelper.Efx.GenFilter(); + // ALHelper.Efx.Filter(alFilterId, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass); + // ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGain, 1); + // LowPassHFGain = 1; + //} + + return sound; + + } + + public void SetBufferData(int alBufferId) + { + AL.BufferData(alBufferId, format, castBuffer, + castBuffer.Length * sizeof(short), sampleRate); + } + + static void CastBuffer(float[] inBuffer, short[] outBuffer, int length) + { + for (int i = 0; i < length; i++) + { + int temp = (int)(32767f * inBuffer[i]); + if (temp > short.MaxValue) temp = short.MaxValue; + else if (temp < short.MinValue) temp = short.MinValue; + outBuffer[i] = (short)temp; + } + } + + public void Dispose() + { + //var state = AL.GetSourceState(alSourceId); + //if (state == ALSourceState.Playing || state == ALSourceState.Paused) + // Stop(); + System.Diagnostics.Debug.WriteLine(alBufferId); + //AL.DeleteSource(alSourceId); + AL.DeleteBuffer(alBufferId); + + //if (ALHelper.Efx.IsInitialized) + // ALHelper.Efx.DeleteFilter(alFilterId); + + ALHelper.Check(); + } + + } +} diff --git a/Subsurface/Sounds/OggStream.cs b/Subsurface/Sounds/OggStream.cs new file mode 100644 index 000000000..8e120144f --- /dev/null +++ b/Subsurface/Sounds/OggStream.cs @@ -0,0 +1,573 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using NVorbis; +using OpenTK.Audio.OpenAL; + +namespace Subsurface.Sounds +{ + internal static class ALHelper + { + public static readonly XRamExtension XRam = new XRamExtension(); + public static readonly EffectsExtension Efx = new EffectsExtension(); + + static ALHelper() + { + try + { + Debug.WriteLine("OpenAL Soft [" + (AL.Get(ALGetString.Version).Contains("SOFT") ? "X" : " ") + "], "); + Debug.WriteLine("X-RAM [" + (XRam.IsInitialized ? "X" : " ") + "], "); + Debug.WriteLine("Effect Extensions [" + (Efx.IsInitialized ? "X" : " ") + "]"); + } + catch (Exception e) + { + DebugConsole.ThrowError("OpenAL error!", e); + } + + } + + [Conditional("TRACE")] + public static void TraceMemoryUsage(Action logHandler) + { + var usedHeap = (double)GC.GetTotalMemory(true); + + string[] sizes = { "B", "KB", "MB", "GB" }; + int order = 0; + while (usedHeap >= 1024 && order + 1 < sizes.Length) + { + order++; + usedHeap = usedHeap / 1024; + } + + //logHandler(String.Format("Total memory : {0:0.###} {1} ", usedHeap, sizes[order]), 0, 6); + } + + public static void Check() + { + ALError error; + if ((error = AL.GetError()) != ALError.NoError) + { + DebugConsole.ThrowError(AL.GetErrorString(error)); + } + } + } + + public class OggStream : IDisposable + { + public const int DefaultBufferCount = 3; + + internal readonly object stopMutex = new object(); + internal readonly object prepareMutex = new object(); + + internal readonly int alSourceId; + internal readonly int[] alBufferIds; + + readonly int alFilterId; + readonly Stream underlyingStream; + + internal VorbisReader Reader { get; private set; } + internal bool Ready { get; private set; } + internal bool Preparing { get; private set; } + + public int BufferCount { get; private set; } + +#if TRACE + public int logX, logY; + public Action LogHandler; +#endif + + public OggStream(string filename, int bufferCount = DefaultBufferCount) : this(File.OpenRead(filename), bufferCount) { } + public OggStream(Stream stream, int bufferCount = DefaultBufferCount) + { + BufferCount = bufferCount; + + alBufferIds = AL.GenBuffers(bufferCount); + alSourceId = AL.GenSource(); + + if (ALHelper.XRam.IsInitialized) + { + ALHelper.XRam.SetBufferMode(BufferCount, ref alBufferIds[0], XRamExtension.XRamStorage.Hardware); + ALHelper.Check(); + } + + if (ALHelper.Efx.IsInitialized) + { + alFilterId = ALHelper.Efx.GenFilter(); + ALHelper.Efx.Filter(alFilterId, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass); + ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGain, 1); + LowPassHFGain = 1; + } + + underlyingStream = stream; + + Volume = 1; + IsLooped = true; + + } + + public void Prepare() + { + if (Preparing) return; + + var state = AL.GetSourceState(alSourceId); + + lock (stopMutex) + { + switch (state) + { + case ALSourceState.Playing: + case ALSourceState.Paused: + return; + + case ALSourceState.Stopped: + lock (prepareMutex) + { + Close(); + Empty(); + } + break; + } + + if (!Ready) + { + lock (prepareMutex) + { + Preparing = true; +//#if TRACE +// logX = 7; +// LogHandler("(*", logX, logY); +// logX += 2; +//#endif + Open(precache: true); +//#if TRACE +// LogHandler(")", logX++, logY); +//#endif + } + } + } + } + + public void Play() + { + var state = AL.GetSourceState(alSourceId); + + switch (state) + { + case ALSourceState.Playing: return; + case ALSourceState.Paused: + Resume(); + return; + } + + Prepare(); + +//#if TRACE +// LogHandler("{", logX++, logY); +//#endif + AL.SourcePlay(alSourceId); + try + { + ALHelper.Check(); + } + catch (Exception e) + { + throw new Exception ("AIHElper", e.InnerException); + } + + Preparing = false; + + OggStreamer.Instance.AddStream(this); + } + + public void Pause() + { + if (AL.GetSourceState(alSourceId) != ALSourceState.Playing) + return; + + OggStreamer.Instance.RemoveStream(this); +//#if TRACE +// LogHandler("]", logX++, logY); +//#endif + AL.SourcePause(alSourceId); + ALHelper.Check(); + } + + public void Resume() + { + if (AL.GetSourceState(alSourceId) != ALSourceState.Paused) + return; + + OggStreamer.Instance.AddStream(this); +#if TRACE + LogHandler("[", logX++, logY); +#endif + AL.SourcePlay(alSourceId); + ALHelper.Check(); + } + + public void Stop() + { + var state = AL.GetSourceState(alSourceId); + if (state == ALSourceState.Playing || state == ALSourceState.Paused) + { +//#if TRACE +// LogHandler("}", logX++, logY); +//#endif + StopPlayback(); + } + + lock (stopMutex) + { + OggStreamer.Instance.RemoveStream(this); + } + } + + float lowPassHfGain; + public float LowPassHFGain + { + get { return lowPassHfGain; } + set + { + if (ALHelper.Efx.IsInitialized) + { + ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGainHF, lowPassHfGain = value); + ALHelper.Efx.BindFilterToSource(alSourceId, alFilterId); + ALHelper.Check(); + } + } + } + + float volume; + public float Volume + { + get { return volume; } + set + { + AL.Source(alSourceId, ALSourcef.Gain, volume = value); + ALHelper.Check(); + } + } + + public bool IsLooped { get; set; } + + + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + var state = AL.GetSourceState(alSourceId); + if (state == ALSourceState.Playing || state == ALSourceState.Paused) + StopPlayback(); + + lock (prepareMutex) + { + OggStreamer.Instance.RemoveStream(this); + + if (state != ALSourceState.Initial) + Empty(); + + Close(); + + underlyingStream.Dispose(); + } + + AL.DeleteSource(alSourceId); + AL.DeleteBuffers(alBufferIds); + + if (ALHelper.Efx.IsInitialized) + ALHelper.Efx.DeleteFilter(alFilterId); + + ALHelper.Check(); + #if TRACE + ALHelper.TraceMemoryUsage(LogHandler); + #endif + } + } + + void StopPlayback() + { + AL.SourceStop(alSourceId); + ALHelper.Check(); + } + + void Empty() + { + int queued; + AL.GetSource(alSourceId, ALGetSourcei.BuffersQueued, out queued); + if (queued > 0) + { + try + { + AL.SourceUnqueueBuffers(alSourceId, queued); + ALHelper.Check(); + } + catch (InvalidOperationException) + { + // This is a bug in the OpenAL implementation + // Salvage what we can + int processed; + AL.GetSource(alSourceId, ALGetSourcei.BuffersProcessed, out processed); + var salvaged = new int[processed]; + if (processed > 0) + { + AL.SourceUnqueueBuffers(alSourceId, processed, salvaged); + ALHelper.Check(); + } + + // Try turning it off again? + AL.SourceStop(alSourceId); + ALHelper.Check(); + + Empty(); + } + } +//#if TRACE +// logX = 7; +// LogHandler(new string(Enumerable.Repeat(' ', Console.BufferWidth - 6).ToArray()), logX, logY); +//#endif + } + + internal void Open(bool precache = false) + { + underlyingStream.Seek(0, SeekOrigin.Begin); + Reader = new VorbisReader(underlyingStream, false); + + if (precache) + { + // Fill first buffer synchronously + OggStreamer.Instance.FillBuffer(this, alBufferIds[0]); + AL.SourceQueueBuffer(alSourceId, alBufferIds[0]); + ALHelper.Check(); + + // Schedule the others asynchronously + OggStreamer.Instance.AddStream(this); + } + + Ready = true; + } + + internal void Close() + { + if (Reader != null) + { + Reader.Dispose(); + Reader = null; + } + Ready = false; + } + } + + public class OggStreamer : IDisposable + { + const float DefaultUpdateRate = 10; + const int DefaultBufferSize = 44100; + + static readonly object singletonMutex = new object(); + + readonly object iterationMutex = new object(); + readonly object readMutex = new object(); + + readonly float[] readSampleBuffer; + readonly short[] castBuffer; + + readonly HashSet streams = new HashSet(); + readonly List threadLocalStreams = new List(); + + readonly Thread underlyingThread; + volatile bool cancelled; + + public float UpdateRate { get; private set; } + public int BufferSize { get; private set; } + + static OggStreamer instance; + public static OggStreamer Instance + { + get + { + lock (singletonMutex) + { + if (instance == null) + throw new InvalidOperationException("No instance running"); + return instance; + } + } + private set { lock (singletonMutex) instance = value; } + } + + public OggStreamer(int bufferSize = DefaultBufferSize, float updateRate = DefaultUpdateRate) + { + lock (singletonMutex) + { + if (instance != null) + throw new InvalidOperationException("Already running"); + + Instance = this; + underlyingThread = new Thread(EnsureBuffersFilled) { Priority = ThreadPriority.Lowest }; + underlyingThread.Start(); + } + + UpdateRate = updateRate; + BufferSize = bufferSize; + + readSampleBuffer = new float[bufferSize]; + castBuffer = new short[bufferSize]; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + lock (singletonMutex) + { + Debug.Assert(Instance == this, "Two instances running, somehow...?"); + + cancelled = true; + lock (iterationMutex) + streams.Clear(); + + Instance = null; + } + } + } + + internal bool AddStream(OggStream stream) + { + lock (iterationMutex) + return streams.Add(stream); + } + internal bool RemoveStream(OggStream stream) + { + lock (iterationMutex) + return streams.Remove(stream); + } + + public bool FillBuffer(OggStream stream, int bufferId) + { + int readSamples; + lock (readMutex) + { + readSamples = stream.Reader.ReadSamples(readSampleBuffer, 0, BufferSize); + CastBuffer(readSampleBuffer, castBuffer, readSamples); + } + AL.BufferData(bufferId, stream.Reader.Channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, castBuffer, + readSamples * sizeof (short), stream.Reader.SampleRate); + ALHelper.Check(); +//#if TRACE +// stream.LogHandler(readSamples == BufferSize ? "." : "|", stream.logX++, stream.logY); +// //ALHelper.TraceMemoryUsage(stream.LogHandler); +//#endif + + return readSamples != BufferSize; + } + static void CastBuffer(float[] inBuffer, short[] outBuffer, int length) + { + for (int i = 0; i < length; i++) + { + var temp = (int)(32767f * inBuffer[i]); + if (temp > short.MaxValue) temp = short.MaxValue; + else if (temp < short.MinValue) temp = short.MinValue; + outBuffer[i] = (short)temp; + } + } + + void EnsureBuffersFilled() + { + while (!cancelled) + { + Thread.Sleep((int) (1000 / UpdateRate)); + if (cancelled) break; + + threadLocalStreams.Clear(); + lock (iterationMutex) threadLocalStreams.AddRange(streams); + + foreach (var stream in threadLocalStreams) + { + lock (stream.prepareMutex) + { + lock (iterationMutex) + if (!streams.Contains(stream)) + continue; + + bool finished = false; + + int queued; + AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersQueued, out queued); + //ALHelper.Check(); + int processed; + AL.GetSource(stream.alSourceId, ALGetSourcei.BuffersProcessed, out processed); + //ALHelper.Check(); + + if (processed == 0 && queued == stream.BufferCount) continue; + + int[] tempBuffers; + if (processed > 0) + tempBuffers = AL.SourceUnqueueBuffers(stream.alSourceId, processed); + else + tempBuffers = stream.alBufferIds.Skip(queued).ToArray(); + + for (int i = 0; i < tempBuffers.Length; i++) + { + finished |= FillBuffer(stream, tempBuffers[i]); + + if (finished) + { + if (stream.IsLooped) + { + stream.Close(); + stream.Open(); + } + else + { + streams.Remove(stream); + i = tempBuffers.Length; + } + } + } + + AL.SourceQueueBuffers(stream.alSourceId, tempBuffers.Length, tempBuffers); + //ALHelper.Check(); + + if (finished && !stream.IsLooped) + continue; + } + + lock (stream.stopMutex) + { + if (stream.Preparing) continue; + + lock (iterationMutex) + if (!streams.Contains(stream)) + continue; + + var state = AL.GetSourceState(stream.alSourceId); + if (state == ALSourceState.Stopped) + { +//#if TRACE +// stream.LogHandler("!", stream.logX++, stream.logY); +//#endif + AL.SourcePlay(stream.alSourceId); + ALHelper.Check(); + } + } + } + } + } + } + +} diff --git a/Subsurface/Sounds/Sound.cs b/Subsurface/Sounds/Sound.cs new file mode 100644 index 000000000..fe5571fc9 --- /dev/null +++ b/Subsurface/Sounds/Sound.cs @@ -0,0 +1,214 @@ + +using System.Collections.Generic; +using System.IO; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using Microsoft.Xna.Framework; +using Subsurface.Sounds; + +namespace Subsurface +{ + public class Sound + { + + private static List loadedSounds = new List(); + + private OggSound oggSound; + + string filePath; + + public static Vector3 CameraPos; + + //public float Volume + //{ + // set { SoundManager.Volume(sourceIndex, value); } + //} + + public string FilePath + { + get { return filePath; } + } + + public int AlBufferId + { + get { return oggSound.AlBufferId; } + } + + public static void Init() + { + SoundManager.Init(); + } + + public static Sound Load(string file) + { + if (!File.Exists(file)) + { + DebugConsole.ThrowError("File ''" + file + "'' not found!"); + return null; + } + + Sound s = new Sound(); + + s.filePath = file; + + foreach (Sound loadedSound in loadedSounds) + { + if (loadedSound.filePath == file) s.oggSound = loadedSound.oggSound; + } + + if (s.oggSound == null) + { + s.oggSound = OggSound.Load(file); + + } + + loadedSounds.Add(s); + + return s; + } + + public int Play(float volume = 1.0f) + { + return SoundManager.Play(this, volume); + } + + public int Play(float volume, float range, Vector2 position) + { + //position = new Vector2(position.X - CameraPos.X, position.Y - CameraPos.Y); + + //volume = (range == 0.0f) ? 0.0f : MathHelper.Clamp(volume * (range - position.Length())/range, 0.0f, 1.0f); + + int newIndex = SoundManager.Play(this, volume); + + if (newIndex == -1) return -1; + + return UpdatePosition(newIndex, position, range, volume); + } + + public int Play(float volume, float range, Body body) + { + Vector2 bodyPosition = ConvertUnits.ToDisplayUnits(body.Position); + //bodyPosition.Y = -bodyPosition.Y; + + return Play(volume, range, bodyPosition); + } + + public static int UpdatePosition(int sourceIndex, Vector2 position, float range, float baseVolume = 1.0f) + { + position = new Vector2(position.X - CameraPos.X, position.Y - CameraPos.Y); + float volume = (range == 0.0f) ? 0.0f : MathHelper.Clamp(baseVolume * (range - position.Length())/range, 0.0f, 1.0f); + + if (volume <= 0.0f) + { + if (sourceIndex > 0) + { + SoundManager.Stop(sourceIndex); + sourceIndex = -1; + } + + return sourceIndex; + } + + SoundManager.UpdateSoundPosition(sourceIndex, position, volume); + + return sourceIndex; + } + + public int UpdatePosition(int sourceIndex, Body body, float range, float baseVolume = 1.0f) + { + Vector2 bodyPosition = ConvertUnits.ToDisplayUnits(body.Position); + bodyPosition.Y = -bodyPosition.Y; + return UpdatePosition(sourceIndex, bodyPosition, range, baseVolume); + } + + public int Loop(int sourceIndex, float volume) + { + if (volume == 0.0f) + { + if (sourceIndex > 0) + { + SoundManager.Stop(sourceIndex); + sourceIndex = -1; + } + + return sourceIndex; + } + + int newIndex = SoundManager.Loop(this, sourceIndex, volume); + + return newIndex; + } + + public int Loop(int sourceIndex, float volume, Vector2 position, float range) + { + if (volume <= 0.0f) + { + if (sourceIndex > 0) + { + SoundManager.Stop(sourceIndex); + sourceIndex = -1; + } + + return sourceIndex; + } + + int newIndex = SoundManager.Loop(this, sourceIndex, volume); + + return UpdatePosition(newIndex, position, range, volume); + + } + + //public int Loop(float volume = 1.0f) + //{ + // return SoundManager.Loop(this, volume); + //} + + //public void Pause() + //{ + // SoundManager.Pause(this); + //} + + //public void Resume() + //{ + // SoundManager.Resume(this); + //} + + //public void Stop() + //{ + // SoundManager.Stop(this); + //} + + public void Remove() + { + loadedSounds.Remove(this); + + System.Diagnostics.Debug.WriteLine(this.AlBufferId); + + foreach (Sound s in loadedSounds) + { + if (s.oggSound == oggSound) return; + } + + SoundManager.ClearAlSource(this.AlBufferId); + oggSound.Dispose(); + } + + + public static void StartStream(string file, float volume = 1.0f) + { + SoundManager.StartStream(file, volume); + } + + public static void Stoptream() + { + SoundManager.StopStream(); + } + + public static void Dispose() + { + SoundManager.Dispose(); + } + + } + +} \ No newline at end of file diff --git a/Subsurface/Sounds/SoundManager.cs b/Subsurface/Sounds/SoundManager.cs new file mode 100644 index 000000000..e34535605 --- /dev/null +++ b/Subsurface/Sounds/SoundManager.cs @@ -0,0 +1,299 @@ +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Xna.Framework; +using OpenTK.Audio; +using OpenTK.Audio.OpenAL; + +namespace Subsurface.Sounds +{ + static class SoundManager + { + public const int DefaultSourceCount = 16; + + private static List alSources = new List(); + private static int[] alBuffers = new int[DefaultSourceCount]; + private static int lowpassFilterId; + + + static AudioContext AC; + + public static OggStreamer oggStreamer; + public static OggStream oggStream; + + public static void Init() + { + AC = new AudioContext(); + + for (int i = 0 ; i < DefaultSourceCount; i++) + { + alSources.Add(AL.GenSource()); + } + + if (ALHelper.Efx.IsInitialized) + { + lowpassFilterId = ALHelper.Efx.GenFilter(); + //alFilters.Add(alFilterId); + ALHelper.Efx.Filter(lowpassFilterId, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass); + + //LowPassHFGain = 1; + } + + + } + + + //public SoundManager(int bufferCount = DefaultSourceCount) + //{ + // Stopwatch sw = new Stopwatch(); + // sw.Start(); + + // alSourceId = AL.GenSource(); + // //AL.Source(alSourceId, ALSourcei.Buffer, alBufferId); + + // Volume = 1; + + // if (ALHelper.Efx.IsInitialized) + // { + // alFilterId = ALHelper.Efx.GenFilter(); + // ALHelper.Efx.Filter(alFilterId, EfxFilteri.FilterType, (int)EfxFilterType.Lowpass); + // ALHelper.Efx.Filter(alFilterId, EfxFilterf.LowpassGain, 1); + // LowPassHFGain = 1; + // } + + // sw.Stop(); + // System.Diagnostics.Debug.WriteLine("oggsource: "+sw.ElapsedMilliseconds); + + //} + + // public static int Play(Sound sound, float volume = 1.0f) + // { + // return Play(sound, volume, new Vector2(0.0f, 0.0f)); + //} + + public static int Play(Sound sound, float volume = 1.0f) + { + //for (int i = 2; i < DefaultSourceCount; i++) + //{ + // AL.SourceStop(alSources[i]); + // AL.Source(alSources[i], ALSourceb.Looping, false); + // System.Diagnostics.Debug.WriteLine(i + ": " + AL.GetSourceState(alSources[i])); + // System.Diagnostics.Debug.WriteLine(AL.GetSourceType(alSources[i])); + //} + + for (int i = 1; i < DefaultSourceCount; i++) + { + //find a source that's free to use (not playing or paused) + if (AL.GetSourceState(alSources[i]) == ALSourceState.Playing + || AL.GetSourceState(alSources[i]) == ALSourceState.Paused) continue; + + //if (position!=Vector2.Zero) + // position /= 1000.0f; + + alBuffers[i]=sound.AlBufferId; + AL.Source(alSources[i], ALSourceb.Looping, false); + AL.Source(alSources[i], ALSource3f.Position, 0.0f, 0.0f, 0.0f); + AL.Source(alSources[i], ALSourcei.Buffer, sound.AlBufferId); + AL.Source(alSources[i], ALSourcef.Gain, volume); + //AL.Source(alSources[i], ALSource3f.Position, position.X, position.Y, 0.0f); + AL.SourcePlay(alSources[i]); + + //sound.sourceIndex = i; + + return i; + } + + return -1; + } + + public static int Loop(Sound sound, int sourceIndex, float volume) + { + if (sourceIndex<1) + { + sourceIndex = Play(sound, volume); + if (sourceIndex>0) + { + AL.Source(alSources[sourceIndex], ALSourceb.Looping, true); + AL.Source(alSources[sourceIndex], ALSourcef.Gain, volume); + } + return sourceIndex; + } + else + { + AL.Source(alSources[sourceIndex], ALSourceb.Looping, true); + AL.Source(alSources[sourceIndex], ALSourcef.Gain, volume); + return sourceIndex; + } + } + + //public static int Loop(int sourceIndex, float volume = 1.0f) + //{ + + // if (sourceIndex > 0 && alSources[sourceIndex]>0) + // { + // ALSourceState state = AL.GetSourceState(alSources[sourceIndex]); + // ALHelper.Check(); + // if (state == ALSourceState.Playing) return sourceIndex; + // } + + // int newSourceIndex = Play(sound, volume); + // AL.Source(alSources[sourceIndex], ALSourceb.Looping, true); + + // return sourceIndex; + //} + + public static void Pause(int sourceIndex) + { + if (AL.GetSourceState(alSources[sourceIndex]) != ALSourceState.Playing) + return; + + AL.SourcePause(alSources[sourceIndex]); + ALHelper.Check(); + } + + public static void Resume(int sourceIndex) + { + if (AL.GetSourceState(alSources[sourceIndex]) != ALSourceState.Paused) + return; + + Debug.WriteLine("sourceplay"); + AL.SourcePlay(alSources[sourceIndex]); + ALHelper.Check(); + } + + public static void Stop(int sourceIndex) + { + if (sourceIndex < 1) return; + + var state = AL.GetSourceState(alSources[sourceIndex]); + if (state == ALSourceState.Playing || state == ALSourceState.Paused) + { + AL.SourceStop(alSources[sourceIndex]); + AL.Source(alSources[sourceIndex], ALSourceb.Looping, false); + } + } + + public static bool IsPlaying(int sourceIndex) + { + if (sourceIndex < 1) return false; + var state = AL.GetSourceState(alSources[sourceIndex]); + return (state == ALSourceState.Playing); + } + + public static void Volume(int sourceIndex, float volume) + { + AL.Source(alSources[sourceIndex], ALSourcef.Gain, volume); + ALHelper.Check(); + } + + //int alFilterId; + + static float lowPassHfGain; + public static float LowPassHFGain + { + get { return lowPassHfGain; } + set + { + if (ALHelper.Efx.IsInitialized) + { + for (int i = 0; i < DefaultSourceCount; i++) + { + //find a source that's free to use (not playing or paused) + if (AL.GetSourceState(alSources[i]) != ALSourceState.Playing + && AL.GetSourceState(alSources[i])!= ALSourceState.Paused) continue; + + ALHelper.Efx.Filter(lowpassFilterId, EfxFilterf.LowpassGainHF, lowPassHfGain = value); + ALHelper.Efx.BindFilterToSource(alSources[i], lowpassFilterId); + ALHelper.Check(); + } + + } + } + } + + //float volume; + //public float Volume + //{ + // get { return volume; } + // set + // { + // AL.Source(alSourceId, ALSourcef.Gain, volume = value); + // ALHelper.Check(); + // } + //} + + public static void UpdateSoundPosition(int sourceIndex, Vector2 position, float baseVolume = 1.0f) + { + if (sourceIndex < 1) return; + + //Resume(sourceIndex); + + position/= 1000.0f; + + //System.Diagnostics.Debug.WriteLine("updatesoundpos: "+offset); + AL.Source(alSources[sourceIndex], ALSourcef.Gain, baseVolume); + AL.Source(alSources[sourceIndex], ALSource3f.Position, position.X, position.Y, 0.0f); + } + + public static OggStream StartStream(string file, float volume = 1.0f) + { + if (oggStreamer == null) + oggStreamer = new OggStreamer(); + + oggStream = new OggStream(file); + + oggStreamer.AddStream(oggStream); + + oggStream.Volume = volume; + + oggStream.Play(); + + return oggStream; + } + + public static void StopStream() + { + if (oggStream!=null) oggStream.Stop(); + } + + public static void ClearAlSource(int bufferId) + { + for (int i = 1; i < DefaultSourceCount; i++) + { + if (alBuffers[i] == bufferId) + { + AL.Source(alSources[i], ALSourcei.Buffer, 0); + } + } + + + + } + + public static void Dispose() + { + if (ALHelper.Efx.IsInitialized) + ALHelper.Efx.DeleteFilter(lowpassFilterId); + + for (int i = 0; i < DefaultSourceCount; i++) + { + var state = AL.GetSourceState(alSources[i]); + if (state == ALSourceState.Playing || state == ALSourceState.Paused) + Stop(i); + + AL.DeleteSource(alSources[i]); + + ALHelper.Check(); + } + + if (oggStream!=null) + { + oggStream.Stop(); + oggStream.Dispose(); + } + + if (oggStreamer != null) + oggStreamer.Dispose(); + } + + } +} diff --git a/Subsurface/Sprite.cs b/Subsurface/Sprite.cs new file mode 100644 index 000000000..4ef6bdf49 --- /dev/null +++ b/Subsurface/Sprite.cs @@ -0,0 +1,285 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System.Xml.Linq; + +namespace Subsurface +{ + class Sprite + { + static List list = new List(); + //the file from which the texture is loaded + //if two sprites use the same file, they share the same texture + string file; + + Texture2D texture; + + //the area in the texture that is supposed to be drawn + Rectangle sourceRect; + + //the offset used when drawing the sprite + Vector2 offset; + + public Vector2 origin; + + //the size of the drawn sprite, if larger than the source, + //the sprite is tiled to fill the target size + public Vector2 size; + + public float rotation; + + public SpriteEffects effects; + + float depth; + + public Rectangle SourceRect + { + get { return sourceRect; } + set { sourceRect = value; } + } + + public float Depth + { + get { return depth; } + set { depth = Math.Min(Math.Max(value, 0.0f), 1.0f); } + } + + public Vector2 Origin + { + get { return origin; } + set { origin = value; } + } + + public Texture2D Texture + { + get { return texture; } + } + + public string FilePath + { + get { return file; } + } + + public Sprite(XElement element, string path ="", string file="") + { + if (file=="") + { + file = ToolBox.GetAttributeString(element, "texture", ""); + } + + if (file=="") + { + DebugConsole.ThrowError("Sprite "+element+" doesn't have a texture specified!"); + return; + } + + if (!string.IsNullOrEmpty(path)) + { + if (!path.EndsWith("\\")) path += "\\"; + } + + file = path + file; + + texture = LoadTexture(file); + + if (texture == null) return; + + Vector4 sourceVector = ToolBox.GetAttributeVector4(element, "sourcerect", Vector4.Zero); + + if (sourceVector.Z == 0.0f) sourceVector.Z = texture.Width; + if (sourceVector.W == 0.0f) sourceVector.W = texture.Height; + + sourceRect = new Rectangle( + (int)sourceVector.X, (int)sourceVector.Y, + (int)sourceVector.Z, (int)sourceVector.W); + + origin = ToolBox.GetAttributeVector2(element, "origin", new Vector2(0.5f, 0.5f)); + origin.X = origin.X * sourceRect.Width; + origin.Y = origin.Y * sourceRect.Height; + + size = new Vector2(sourceRect.Width, sourceRect.Height); + + Depth = ToolBox.GetAttributeFloat(element, "depth", 0.0f); + } + + public Sprite(string newFile, Vector2 newOrigin) + { + file = newFile; + texture = LoadTexture(file); + + if (texture == null) return; + + sourceRect = new Rectangle(0, 0, texture.Width, texture.Height); + + size = new Vector2(sourceRect.Width, sourceRect.Height); + + origin = new Vector2((float)texture.Width * newOrigin.X, (float)texture.Height * newOrigin.Y); + + effects = SpriteEffects.None; + + list.Add(this); + } + + public Sprite(Texture2D texture, Rectangle? sourceRectangle, Vector2? newOffset, float newRotation = 0.0f) + { + this.texture = texture; + + sourceRect = (sourceRectangle == null) ? new Rectangle(1, 1, texture.Width, texture.Height) : (Rectangle)sourceRectangle; + + offset = (newOffset == null) ? Vector2.Zero : (Vector2)newOffset; + + size = new Vector2(sourceRect.Width, sourceRect.Height); + + origin = Vector2.Zero; + + effects = SpriteEffects.None; + + rotation = newRotation; + + list.Add(this); + } + + public Sprite(string newFile, Rectangle? sourceRectangle, Vector2? newOffset, float newRotation = 0.0f) + { + file = newFile; + texture = LoadTexture(file); + + sourceRect = (sourceRectangle == null) ? new Rectangle(1,1,texture.Width,texture.Height) : (Rectangle)sourceRectangle; + + offset = (newOffset == null) ? Vector2.Zero : (Vector2)newOffset; + + size = new Vector2(sourceRect.Width, sourceRect.Height); + + origin = Vector2.Zero; + + rotation = newRotation; + + list.Add(this); + } + + public static Texture2D LoadTexture(string file) + { + foreach (Sprite s in list) + { + if (s.file == file) return s.texture; + } + + if (File.Exists(file)) + { + return Game1.textureLoader.FromFile(file); + } + else + { + DebugConsole.ThrowError("Sprite ''"+file+"'' not found!"); + } + + return null; + } + + public void Draw(SpriteBatch spriteBatch, Vector2 pos, float rotate=0.0f, float scale=1.0f, SpriteEffects spriteEffect = SpriteEffects.None) + { + spriteBatch.Draw(texture, pos + offset, sourceRect, Color.White, rotation + rotate, origin, scale, spriteEffect, depth); + } + + public void Draw(SpriteBatch spriteBatch, Vector2 pos, Color color, float rotate = 0.0f, float scale = 1.0f, SpriteEffects spriteEffect = SpriteEffects.None) + { + spriteBatch.Draw(texture, pos + offset, sourceRect, color, rotation + rotate, origin, scale, spriteEffect, depth); + + } + + public void DrawTiled(SpriteBatch spriteBatch, Vector2 pos, Vector2 targetSize, Color color) + { + DrawTiled(spriteBatch, pos, targetSize, Vector2.Zero, color); + } + + public void DrawTiled(SpriteBatch spriteBatch, Vector2 pos, Vector2 targetSize, Vector2 startOffset, Color color) + { + pos.X = (int)pos.X; + pos.Y = (int)pos.Y; + + //how many times the texture needs to be drawn on the x-axis + int xTiles = (int)Math.Ceiling((targetSize.X+startOffset.X) / sourceRect.Width); + //how many times the texture needs to be drawn on the y-axis + int yTiles = (int)Math.Ceiling((targetSize.Y+startOffset.Y) / sourceRect.Height); + + Vector2 position = pos-startOffset; + Rectangle drawRect = sourceRect; + + position.X = pos.X; + + for (int x = 0 ; x + + + Debug + x86 + 8.0.30703 + 2.0 + {008C0F83-E914-4966-9135-EA885059EDD8} + WinExe + Properties + Subsurface + Subsurface + 512 + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 0.1.0.%2a + false + true + + + x86 + true + full + false + bin\Windows\Debug\ + DEBUG;TRACE;WINDOWS + prompt + 4 + + + x86 + pdbonly + true + bin\Windows\Release\ + TRACE;WINDOWS + prompt + 4 + + + Icon.ico + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .\FarseerPhysics MonoGame.dll + + + False + .\Lidgren.Network.dll + + + $(MSBuildProgramFiles32)\MonoGame\v3.0\Assemblies\Windows\MonoGame.Framework.dll + + + .\NVorbis.dll + + + False + .\OpenTK.dll + + + + + + + + + + PreserveNewest + + + Designer + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + Designer + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + Designer + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + Designer + + + + PreserveNewest + + + + + + + + False + Microsoft .NET Framework 4 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 4.5 + true + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + + {1e6bf44d-6e31-40cc-8321-3d5958c983e7} + Subsurface_content + + + + + \ No newline at end of file diff --git a/Subsurface/Subsurface.csproj.user b/Subsurface/Subsurface.csproj.user new file mode 100644 index 000000000..6e4c912e4 --- /dev/null +++ b/Subsurface/Subsurface.csproj.user @@ -0,0 +1,14 @@ + + + + publish\ + + + + + + en-US + false + ShowAllFiles + + \ No newline at end of file diff --git a/Subsurface/TextureLoader.cs b/Subsurface/TextureLoader.cs new file mode 100644 index 000000000..b0a64201e --- /dev/null +++ b/Subsurface/TextureLoader.cs @@ -0,0 +1,115 @@ +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using Microsoft.Xna.Framework.Graphics; +using Color = Microsoft.Xna.Framework.Color; + +namespace Subsurface +{ + /// + /// Based on http://jakepoz.com/jake_poznanski__background_load_xna.html + /// + public class TextureLoader + { + static TextureLoader() + { + BlendColorBlendState = new BlendState + { + ColorDestinationBlend = Blend.Zero, + ColorWriteChannels = ColorWriteChannels.Red | ColorWriteChannels.Green | ColorWriteChannels.Blue, + AlphaDestinationBlend = Blend.Zero, + AlphaSourceBlend = Blend.SourceAlpha, + ColorSourceBlend = Blend.SourceAlpha + }; + + BlendAlphaBlendState = new BlendState + { + ColorWriteChannels = ColorWriteChannels.Alpha, + AlphaDestinationBlend = Blend.Zero, + ColorDestinationBlend = Blend.Zero, + AlphaSourceBlend = Blend.One, + ColorSourceBlend = Blend.One + }; + } + + public TextureLoader(GraphicsDevice graphicsDevice, bool needsBmp = false) + { + _graphicsDevice = graphicsDevice; + _needsBmp = needsBmp; + _spriteBatch = new SpriteBatch(_graphicsDevice); + } + + public Texture2D FromFile(string path, bool preMultiplyAlpha = true) + { + using (Stream fileStream = File.OpenRead(path)) + return FromStream(fileStream, preMultiplyAlpha); + } + + public Texture2D FromStream(Stream stream, bool preMultiplyAlpha = true) + { + Texture2D texture; + + if (_needsBmp) + { + // Load image using GDI because Texture2D.FromStream doesn't support BMP + using (Image image = Image.FromStream(stream)) + { + // Now create a MemoryStream which will be passed to Texture2D after converting to PNG internally + using (MemoryStream ms = new MemoryStream()) + { + image.Save(ms, ImageFormat.Png); + ms.Seek(0, SeekOrigin.Begin); + texture = Texture2D.FromStream(_graphicsDevice, ms); + } + } + } + else + { + texture = Texture2D.FromStream(_graphicsDevice, stream); + } + + if (preMultiplyAlpha) + { + // Setup a render target to hold our final texture which will have premulitplied alpha values + using (RenderTarget2D renderTarget = new RenderTarget2D(_graphicsDevice, texture.Width, texture.Height)) + { + Viewport viewportBackup = _graphicsDevice.Viewport; + _graphicsDevice.SetRenderTarget(renderTarget); + _graphicsDevice.Clear(Color.Black); + + // Multiply each color by the source alpha, and write in just the color values into the final texture + _spriteBatch.Begin(SpriteSortMode.Immediate, BlendColorBlendState); + _spriteBatch.Draw(texture, texture.Bounds, Color.White); + _spriteBatch.End(); + + // Now copy over the alpha values from the source texture to the final one, without multiplying them + _spriteBatch.Begin(SpriteSortMode.Immediate, BlendAlphaBlendState); + _spriteBatch.Draw(texture, texture.Bounds, Color.White); + _spriteBatch.End(); + + // Release the GPU back to drawing to the screen + _graphicsDevice.SetRenderTarget(null); + _graphicsDevice.Viewport = viewportBackup; + + // Store data from render target because the RenderTarget2D is volatile + Color[] data = new Color[texture.Width * texture.Height]; + renderTarget.GetData(data); + + // Unset texture from graphic device and set modified data back to it + _graphicsDevice.Textures[0] = null; + texture.SetData(data); + } + + } + + return texture; + } + + private static readonly BlendState BlendColorBlendState; + private static readonly BlendState BlendAlphaBlendState; + + private readonly GraphicsDevice _graphicsDevice; + private readonly SpriteBatch _spriteBatch; + private readonly bool _needsBmp; + } +} \ No newline at end of file diff --git a/Subsurface/ToolBox.cs b/Subsurface/ToolBox.cs new file mode 100644 index 000000000..36a833b14 --- /dev/null +++ b/Subsurface/ToolBox.cs @@ -0,0 +1,496 @@ +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using Microsoft.Xna.Framework; +using System.IO.Compression; + +namespace Subsurface +{ + static class ToolBox + { + public static Vector2 SmoothStep(Vector2 v1, Vector2 v2, float amount) + { + return new Vector2( + MathHelper.SmoothStep(v1.X, v2.X, amount), + MathHelper.SmoothStep(v1.Y, v2.Y, amount)); + } + + public static float Round(float value, float div) + { + return (float)Math.Floor(value / div) * div; + } + + public static float RandomFloat(float minimum, float maximum) + { + return (float)Game1.localRandom.NextDouble() * (maximum - minimum) + minimum; + } + + public static int RandomInt(int minimum, int maximum) + { + return Game1.localRandom.Next(maximum - minimum) + minimum; + } + + public static float VectorToAngle(Vector2 vector) + { + return (float)Math.Atan2(vector.Y, vector.X); + } + + public static float CurveAngle(float from, float to, float step) + { + // Ensure that 0 <= angle < 2pi for both "from" and "to" + while (from < 0) + from += MathHelper.TwoPi; + while (from >= MathHelper.TwoPi) + from -= MathHelper.TwoPi; + + while (to < 0) + to += MathHelper.TwoPi; + while (to >= MathHelper.TwoPi) + to -= MathHelper.TwoPi; + + if (Math.Abs(from - to) < MathHelper.Pi) + { + // The simple case - a straight lerp will do. + return MathHelper.Lerp(from, to, step); + } + + // If we get here we have the more complex case. + // First, increment the lesser value to be greater. + if (from < to) + from += MathHelper.TwoPi; + else + to += MathHelper.TwoPi; + + float retVal = MathHelper.Lerp(from, to, step); + + // Now ensure the return value is between 0 and 2pi + if (retVal >= MathHelper.TwoPi) + retVal -= MathHelper.TwoPi; + return retVal; + } + + public static float WrapAngleTwoPi(float angle) + { + // Ensure that 0 <= angle < 2pi for both "from" and "to" + while (angle < 0) + angle += MathHelper.TwoPi; + while (angle >= MathHelper.TwoPi) + angle -= MathHelper.TwoPi; + + return angle; + } + + public static float WrapAnglePi(float angle) + { + // Ensure that -pi <= angle < pi for both "from" and "to" + while (angle < -MathHelper.Pi) + angle += MathHelper.TwoPi; + while (angle >= MathHelper.Pi) + angle -= MathHelper.TwoPi; + + return angle; + } + + public static float GetShortestAngle(float from, float to) + { + // Ensure that 0 <= angle < 2pi for both "from" and "to" + from = WrapAngleTwoPi(from); + to = WrapAngleTwoPi(to); + + if (Math.Abs(from - to) < MathHelper.Pi) + { + return to - from; + } + + // If we get here we have the more complex case. + // First, increment the lesser value to be greater. + if (from < to) + from += MathHelper.TwoPi; + else + to += MathHelper.TwoPi; + + return to - from; + } + + /// + /// solves the angle opposite to side a (parameters: lengths of each side) + /// + public static float SolveTriangleSSS(float a, float b, float c) + { + float A = (float)Math.Acos((b*b + c*c - a*a) / (2*b*c)); + + if (float.IsNaN(A)) A = 1.0f; + + return A; + } + + + public static void CompressStringToFile(string fileName, string value) + { + // A. + // Write string to temporary file. + string temp = Path.GetTempFileName(); + File.WriteAllText(temp, value); + + // B. + // Read file into byte array buffer. + byte[] b; + using (FileStream f = new FileStream(temp, FileMode.Open)) + { + b = new byte[f.Length]; + f.Read(b, 0, (int)f.Length); + } + + // C. + // Use GZipStream to write compressed bytes to target file. + using (FileStream f2 = new FileStream(fileName, FileMode.Create)) + using (GZipStream gz = new GZipStream(f2, CompressionMode.Compress, false)) + { + gz.Write(b, 0, b.Length); + } + } + + public static Stream DecompressFiletoStream(string fileName) + { + if (!File.Exists(fileName)) + { + DebugConsole.ThrowError("File ''"+fileName+" doesn't exist!"); + return null; + } + + using (FileStream originalFileStream = new FileStream(fileName, FileMode.Open)) + { + MemoryStream decompressedFileStream = new MemoryStream(); + + using (GZipStream decompressionStream = new GZipStream(originalFileStream, CompressionMode.Decompress)) + { + decompressionStream.CopyTo(decompressedFileStream); + return decompressedFileStream; + } + } + } + + public static XDocument TryLoadXml(string filePath) + { + XDocument doc; + try + { + doc = XDocument.Load(filePath); + } + catch (Exception e) + { + DebugConsole.ThrowError("Couldn't load xml document ''"+filePath+"''!", e); + return null; + } + + if (doc.Root == null) return null; + + return doc; + } + + public static object GetAttributeObject(XElement element, string name) + { + if (element.Attribute(name) == null) return null; + return GetAttributeObject(element.Attribute(name)); + } + + public static object GetAttributeObject(XAttribute attribute) + { + if (attribute == null) return null; + string value = attribute.Value.ToString(); + + float floatVal; + int intVal; + if (value.ToString().Contains(".") && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out floatVal)) + { + return floatVal; + } + else if (int.TryParse(value, out intVal)) + { + return intVal; + } + else + { + string lowerTrimmedVal = value.ToLower().Trim(); + if (lowerTrimmedVal == "true") + { + return true; + } + else if (lowerTrimmedVal == "false") + { + return false; + } + else + { + return value; + } + } + } + + + public static string GetAttributeString(XElement element, string name, string defaultValue) + { + if (element.Attribute(name) == null) return defaultValue; + return GetAttributeString(element.Attribute(name), defaultValue); + } + + public static string GetAttributeString(XAttribute attribute, string defaultValue) + { + string value = attribute.Value; + if (String.IsNullOrEmpty(value)) return defaultValue; + return value; + } + + public static float GetAttributeFloat(XElement element, string name, float defaultValue) + { + if (element.Attribute(name) == null) return defaultValue; + + float val = defaultValue; + + try + { + if (!float.TryParse(element.Attribute(name).Value, NumberStyles.Float, CultureInfo.InvariantCulture, out val)) + { + return defaultValue; + } + } + catch (Exception e) + { + DebugConsole.ThrowError("Error in "+element+"!", e); + } + + return val; + } + + public static float GetAttributeFloat(XAttribute attribute, float defaultValue) + { + if (attribute == null) return defaultValue; + + float val = defaultValue; + + try + { + val = float.Parse(attribute.Value, CultureInfo.InvariantCulture); + } + catch (Exception e) + { + DebugConsole.ThrowError("Error in " + attribute + "! ", e); + } + + return val; + } + + public static int GetAttributeInt(XElement element, string name, int defaultValue) + { + if (element.Attribute(name) == null) return defaultValue; + + int val = defaultValue; + + try + { + val = int.Parse(element.Attribute(name).Value); + } + catch (Exception e) + { + DebugConsole.ThrowError("Error in " + element + "! ", e); + } + + return val; + } + + public static bool GetAttributeBool(XElement element, string name, bool defaultValue) + { + if (element.Attribute(name) == null) return defaultValue; + + string val = element.Attribute(name).Value.ToLower().Trim(); + if (val == "true") + { + return true; + } + else if (val == "false") + { + return false; + } + else + { + DebugConsole.ThrowError("Error in " + element + "! ''" + val + "'' is not a valid boolean value"); + return false; + } + } + public static Vector2 GetAttributeVector2(XElement element, string name, Vector2 defaultValue) + { + if (element.Attribute(name) == null) return defaultValue; + + string val = element.Attribute(name).Value; + + return ParseToVector2(val); + } + + public static Vector4 GetAttributeVector4(XElement element, string name, Vector4 defaultValue) + { + if (element.Attribute(name) == null) return defaultValue; + + string val = element.Attribute(name).Value; + + return ParseToVector4(val); + } + + public static Vector2 ParseToVector2(string stringVector2) + { + string[] components = stringVector2.Split(','); + + Vector2 vector = Vector2.Zero; + + if (components.Length!=2) + { + DebugConsole.ThrowError("Failed to parse the string "+stringVector2+" to Vector2"); + return vector; + } + + float.TryParse(components[0], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.X); + float.TryParse(components[1], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.Y); + + return vector; + } + + public static string Vector2ToString(Vector2 vector) + { + return vector.X.ToString("G", CultureInfo.InvariantCulture) + "," + vector.Y.ToString("G", CultureInfo.InvariantCulture); + } + + + + public static Vector4 ParseToVector4(string stringVector4) + { + string[] components = stringVector4.Split(','); + + Vector4 vector = Vector4.Zero; + + if (components.Length < 3) + { + DebugConsole.ThrowError("Failed to parse the string " + stringVector4 + " to Vector4"); + return vector; + } + + float.TryParse(components[0], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.X); + float.TryParse(components[1], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.Y); + float.TryParse(components[2], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.Z); + if (components.Length>3) + float.TryParse(components[3], NumberStyles.Float, CultureInfo.InvariantCulture, out vector.W); + + return vector; + } + + public static string Vector4ToString(Vector4 vector) + { + return vector.X.ToString("G", CultureInfo.InvariantCulture) + "," + + vector.Y.ToString("G", CultureInfo.InvariantCulture) + "," + + vector.Z.ToString("G", CultureInfo.InvariantCulture) + "," + + vector.W.ToString("G", CultureInfo.InvariantCulture); + } + + public static float[] ParseArrayToFloat(string[] stringArray) + { + if (stringArray == null || stringArray.Length == 0) return null; + + float[] floatArray = new float[stringArray.Length]; + for (int i = 0; i + + + + {1E6BF44D-6E31-40CC-8321-3D5958C983E7} + {6D335F3A-9D43-41b4-9D22-F6F17C4BE596};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Windows + x86 + Library + Properties + ContentBuilder + IgnoreMe + v4.0 + Client + v4.0 + bin\$(Configuration) + Windows + HiDef + c5d15975-8253-40b2-a958-76a62addbeca + Library + x86 + false + + + Windows + + + Windows8 + + + Android + + + iOS + + + OSX + + + Linux + + + PSM + + + + + + + Subsurface_contentContent + Content + {8C1D2051-F0F3-457B-AAAE-4E155FC7C75C} + + + + + + \ No newline at end of file diff --git a/Subsurface_content/Subsurface_content/Subsurface_content.csproj.PSM.cachefile b/Subsurface_content/Subsurface_content/Subsurface_content.csproj.PSM.cachefile new file mode 100644 index 000000000..6d7e38db0 --- /dev/null +++ b/Subsurface_content/Subsurface_content/Subsurface_content.csproj.PSM.cachefile @@ -0,0 +1,2 @@ +Content\SpriteFont1.xnb +Content\SpriteFont1.spritefont diff --git a/Subsurface_content/Subsurface_content/Subsurface_content.csproj.user b/Subsurface_content/Subsurface_content/Subsurface_content.csproj.user new file mode 100644 index 000000000..a4a6cdff4 --- /dev/null +++ b/Subsurface_content/Subsurface_content/Subsurface_content.csproj.user @@ -0,0 +1,6 @@ + + + + ProjectFiles + + \ No newline at end of file diff --git a/Subsurface_content/Subsurface_content/bin/PSM/Content/SpriteFont1.spritefont b/Subsurface_content/Subsurface_content/bin/PSM/Content/SpriteFont1.spritefont new file mode 100644 index 000000000..0d1c4909c --- /dev/null +++ b/Subsurface_content/Subsurface_content/bin/PSM/Content/SpriteFont1.spritefont @@ -0,0 +1,65 @@ + + + + + + + Verdana + + + 10 + + + 0 + + + true + + + + + + + + + + + + ~ + + + + À + ə + + + + diff --git a/Subsurface_content/Subsurface_content/bin/PSM/Content/SpriteFont1.xnb b/Subsurface_content/Subsurface_content/bin/PSM/Content/SpriteFont1.xnb new file mode 100644 index 000000000..df8537731 Binary files /dev/null and b/Subsurface_content/Subsurface_content/bin/PSM/Content/SpriteFont1.xnb differ diff --git a/Subsurface_content/Subsurface_content/bin/PSM/IgnoreMe.dll b/Subsurface_content/Subsurface_content/bin/PSM/IgnoreMe.dll new file mode 100644 index 000000000..7c03bd4ff Binary files /dev/null and b/Subsurface_content/Subsurface_content/bin/PSM/IgnoreMe.dll differ diff --git a/Subsurface_content/Subsurface_content/bin/Windows/Content/SpriteFont1.xnb b/Subsurface_content/Subsurface_content/bin/Windows/Content/SpriteFont1.xnb new file mode 100644 index 000000000..51c4abd74 Binary files /dev/null and b/Subsurface_content/Subsurface_content/bin/Windows/Content/SpriteFont1.xnb differ diff --git a/Subsurface_content/Subsurface_content/bin/Windows/IgnoreMe.dll b/Subsurface_content/Subsurface_content/bin/Windows/IgnoreMe.dll new file mode 100644 index 000000000..56f61cc36 Binary files /dev/null and b/Subsurface_content/Subsurface_content/bin/Windows/IgnoreMe.dll differ diff --git a/Subsurface_content/Subsurface_content/bin/Windows8/Content/SpriteFont1.xnb b/Subsurface_content/Subsurface_content/bin/Windows8/Content/SpriteFont1.xnb new file mode 100644 index 000000000..51c4abd74 Binary files /dev/null and b/Subsurface_content/Subsurface_content/bin/Windows8/Content/SpriteFont1.xnb differ diff --git a/Subsurface_content/Subsurface_content/bin/Windows8/IgnoreMe.dll b/Subsurface_content/Subsurface_content/bin/Windows8/IgnoreMe.dll new file mode 100644 index 000000000..9a1c8db46 Binary files /dev/null and b/Subsurface_content/Subsurface_content/bin/Windows8/IgnoreMe.dll differ diff --git a/Subsurface_content/Subsurface_content/obj/PSM/ContentPipeline-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}.xml b/Subsurface_content/Subsurface_content/obj/PSM/ContentPipeline-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}.xml new file mode 100644 index 000000000..504f63588 --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/PSM/ContentPipeline-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}.xml @@ -0,0 +1,60 @@ + + + + + SpriteFont1.spritefont + SpriteFont1 + FontDescriptionImporter + FontDescriptionProcessor + None + C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb + + + true + + {8C1D2051-F0F3-457B-AAAE-4E155FC7C75C} + Windows + HiDef + PSM + false + C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_contentContent\ + C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_content\ + C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_content\obj\PSM\ + C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_content\bin\PSM\Content\ + + + + C:\Program Files (x86)\MSBuild\MonoGame\v3.0\MonoGameContentProcessors.dll + 2015-02-12T06:10:24+02:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.XImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.VideoImporters.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.TextureImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.FBXImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.EffectImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.AudioImporters.dll + 2011-09-01T16:22:30+03:00 + + + C:\Windows\Microsoft.Net\assembly\GAC_32\Microsoft.Xna.Framework.Content.Pipeline\v4.0_4.0.0.0__842cf8be1de50553\Microsoft.Xna.Framework.Content.Pipeline.dll + 2014-09-15T20:04:13.234089+03:00 + + + + \ No newline at end of file diff --git a/Subsurface_content/Subsurface_content/obj/PSM/DesignTimeResolveAssemblyReferences.cache b/Subsurface_content/Subsurface_content/obj/PSM/DesignTimeResolveAssemblyReferences.cache new file mode 100644 index 000000000..f72fc26e0 Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/PSM/DesignTimeResolveAssemblyReferences.cache differ diff --git a/Subsurface_content/Subsurface_content/obj/PSM/DesignTimeResolveAssemblyReferencesInput.cache b/Subsurface_content/Subsurface_content/obj/PSM/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 000000000..38eefe919 Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/PSM/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/Subsurface_content/Subsurface_content/obj/PSM/IgnoreMe.dll b/Subsurface_content/Subsurface_content/obj/PSM/IgnoreMe.dll new file mode 100644 index 000000000..7c03bd4ff Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/PSM/IgnoreMe.dll differ diff --git a/Subsurface_content/Subsurface_content/obj/PSM/Microsoft.Xna.Framework.RuntimeProfile.txt b/Subsurface_content/Subsurface_content/obj/PSM/Microsoft.Xna.Framework.RuntimeProfile.txt new file mode 100644 index 000000000..e6776c3a8 --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/PSM/Microsoft.Xna.Framework.RuntimeProfile.txt @@ -0,0 +1 @@ +Windows.v4.0.HiDef diff --git a/Subsurface_content/Subsurface_content/obj/PSM/Sbmr_content.csproj.FileListAbsolute.txt b/Subsurface_content/Subsurface_content/obj/PSM/Sbmr_content.csproj.FileListAbsolute.txt new file mode 100644 index 000000000..9dfe7d99d --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/PSM/Sbmr_content.csproj.FileListAbsolute.txt @@ -0,0 +1,80 @@ +D:\Omat tiedostot\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +D:\Omat tiedostot\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +D:\Omat tiedostot\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +D:\Omat tiedostot\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +D:\Omat tiedostot\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Downloads\SBMR\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Downloads\SBMR\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Downloads\SBMR\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Downloads\SBMR\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Downloads\SBMR\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +E:\CBGAM\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +E:\CBGAM\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +E:\CBGAM\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +E:\CBGAM\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +E:\CBGAM\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Documents\CBGAM\SBMR_1809\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Documents\CBGAM\SBMR_1809\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Documents\CBGAM\SBMR_1809\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Documents\CBGAM\SBMR_1809\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Documents\CBGAM\SBMR_1809\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Documents\CBGAM\SBMR_1809_2\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Documents\CBGAM\SBMR_1809_2\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Documents\CBGAM\SBMR_1809_2\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Documents\CBGAM\SBMR_1809_2\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Documents\CBGAM\SBMR_1809_2\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +E:\CBGAM\SBMR_2909\SBMR_2909\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +E:\CBGAM\SBMR_2909\SBMR_2909\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +E:\CBGAM\SBMR_2909\SBMR_2909\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +E:\CBGAM\SBMR_2909\SBMR_2909\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +E:\CBGAM\SBMR_2909\SBMR_2909\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +E:\CBGAM\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +E:\CBGAM\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +E:\CBGAM\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +E:\CBGAM\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +E:\CBGAM\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +E:\CBGAM\SBMR_2710\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +E:\CBGAM\SBMR_2710\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +E:\CBGAM\SBMR_2710\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +E:\CBGAM\SBMR_2710\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +E:\CBGAM\SBMR_2710\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_2111\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_2111\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_2111\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_2111\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_2111\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_2811\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_2811\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_2811\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_2811\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_2811\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1312\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_1312\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1312\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1312\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_1312\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_content\obj\PSM\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll diff --git a/Subsurface_content/Subsurface_content/obj/PSM/Sbmr_content.csprojResolveAssemblyReference.cache b/Subsurface_content/Subsurface_content/obj/PSM/Sbmr_content.csprojResolveAssemblyReference.cache new file mode 100644 index 000000000..391c28355 Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/PSM/Sbmr_content.csprojResolveAssemblyReference.cache differ diff --git a/Subsurface_content/Subsurface_content/obj/PSM/Subsurface_content.csproj.FileListAbsolute.txt b/Subsurface_content/Subsurface_content/obj/PSM/Subsurface_content.csproj.FileListAbsolute.txt new file mode 100644 index 000000000..1fd3989bf --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/PSM/Subsurface_content.csproj.FileListAbsolute.txt @@ -0,0 +1,198 @@ +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1512\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1512\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1512\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_1512\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1512\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_1712\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_1712\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1712\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1712\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_1712\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1712\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\SBMR_1712\Subsurface_content\Subsurface_content\bin\PSM\Content\step.xnb +C:\Users\Joonas\Desktop\SBMR_1712\Subsurface_content\Subsurface_content\bin\PSM\Content\waterambience.xnb +C:\Users\Joonas\Desktop\SBMR_1712\Subsurface_content\Subsurface_content\bin\PSM\Content\waterstream.xnb +C:\Users\Joonas\Desktop\SBMR_1912\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1912\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_1912\Subsurface_content\Subsurface_content\bin\PSM\Content\step.xnb +C:\Users\Joonas\Desktop\SBMR_1912\Subsurface_content\Subsurface_content\bin\PSM\Content\waterambience.xnb +C:\Users\Joonas\Desktop\SBMR_1912\Subsurface_content\Subsurface_content\bin\PSM\Content\waterstream.xnb +C:\Users\Joonas\Desktop\SBMR_1912\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\SBMR_1912\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_1912\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1912\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_2012\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_2012\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\SBMR_2012\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_2012\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_2012\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_2012\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2712\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_2712\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_2712\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2712\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2712\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_2712\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2812\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_2812\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_2812\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2812\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2812\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_2812\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0101\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_0101\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_0101\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0101\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_0101\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0101\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0601\Subsurface_0101\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_0601\Subsurface_0101\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_0601\Subsurface_0101\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0601\Subsurface_0101\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0601\Subsurface_0101\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_0601\Subsurface_0101\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0801\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_0801\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_0801\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0801\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0801\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_0801\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1001\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_1001\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_1001\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1001\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1001\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_1001\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1501\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_1501\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_1501\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1501\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1501\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_1501\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2401\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_2401\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_2401\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2401\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2401\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_2401\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2801\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_2801\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_2801\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2801\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2801\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_2801\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1102\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_1102\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_1102\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1102\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1102\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_1102\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1402\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_1402\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_1402\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1402\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_1402\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1402\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2102\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_2102\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_2102\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2102\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2102\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_2102\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2702\Subsurface_2702 – Kopio\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_2702\Subsurface_2702 – Kopio\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_2702\Subsurface_2702 – Kopio\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_2702\Subsurface_2702 – Kopio\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2702\Subsurface_2702 – Kopio\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_2702\Subsurface_2702 – Kopio\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0303\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_0303\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_0303\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0303\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0303\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_0303\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0603\Subsurface_0303\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_0603\Subsurface_0303\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_0603\Subsurface_0303\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0603\Subsurface_0303\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0603\Subsurface_0303\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_0603\Subsurface_0303\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0803\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_0803\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_0803\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0803\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0803\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_0803\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1030\Subsurface_1030\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_1030\Subsurface_1030\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_1030\Subsurface_1030\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1030\Subsurface_1030\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1030\Subsurface_1030\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_1030\Subsurface_1030\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1303\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_1303\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_1303\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1303\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_1303\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1303 – Kopio – Kopio\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_1303 – Kopio – Kopio\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_1303 – Kopio – Kopio\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1303 – Kopio – Kopio\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1303 – Kopio – Kopio\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_1303 – Kopio – Kopio\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1303 – Kopio – Kopio\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll.CodeAnalysisLog.xml +C:\Users\Joonas\Desktop\Subsurface_1303 – Kopio – Kopio\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll.lastcodeanalysissucceeded +C:\Users\Joonas\Desktop\Subsurface_1303\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0504\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_0504\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_0504\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0504\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0504\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_0504\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_10_04\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_10_04\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_10_04\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_10_04\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_10_04\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_10_04\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_12_04\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_12_04\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_12_04\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_12_04\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_12_04\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_12_04\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface – Kopio\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface – Kopio\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface – Kopio\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface – Kopio\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface – Kopio\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface – Kopio\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_3004\Subsurface – Kopio\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_3004\Subsurface – Kopio\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_3004\Subsurface – Kopio\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_3004\Subsurface – Kopio\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_3004\Subsurface – Kopio\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_3004\Subsurface – Kopio\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0205\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_0205\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_0205\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0205\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0205\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_0205\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0805\Subsurface_0205\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_0805\Subsurface_0205\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_0805\Subsurface_0205\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0805\Subsurface_0205\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_0805\Subsurface_0205\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_0805\Subsurface_0205\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_content\bin\PSM\Content\SpriteFont1.spritefont +C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_content\bin\PSM\IgnoreMe.dll +C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_content\obj\PSM\Subsurface_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_content\obj\PSM\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_content\obj\PSM\IgnoreMe.dll diff --git a/Subsurface_content/Subsurface_content/obj/PSM/Subsurface_content.csprojResolveAssemblyReference.cache b/Subsurface_content/Subsurface_content/obj/PSM/Subsurface_content.csprojResolveAssemblyReference.cache new file mode 100644 index 000000000..391c28355 Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/PSM/Subsurface_content.csprojResolveAssemblyReference.cache differ diff --git a/Subsurface_content/Subsurface_content/obj/PSM/cachefile-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}-targetpath.txt b/Subsurface_content/Subsurface_content/obj/PSM/cachefile-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}-targetpath.txt new file mode 100644 index 000000000..6d7e38db0 --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/PSM/cachefile-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}-targetpath.txt @@ -0,0 +1,2 @@ +Content\SpriteFont1.xnb +Content\SpriteFont1.spritefont diff --git a/Subsurface_content/Subsurface_content/obj/Windows/ContentPipeline-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}.xml b/Subsurface_content/Subsurface_content/obj/Windows/ContentPipeline-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}.xml new file mode 100644 index 000000000..f51a9df04 --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/Windows/ContentPipeline-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}.xml @@ -0,0 +1,60 @@ + + + + + SpriteFont1.spritefont + SpriteFont1 + FontDescriptionImporter + FontDescriptionProcessor + None + C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\bin\Windows\Content\SpriteFont1.xnb + + + true + + {8C1D2051-F0F3-457B-AAAE-4E155FC7C75C} + Windows + HiDef + Windows + false + C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_contentContent\ + C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\ + C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\Windows\ + C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\bin\Windows\Content\ + + + + C:\Program Files (x86)\MSBuild\MonoGame\v3.0\MonoGameContentProcessors.dll + 2014-04-06T00:56:18+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.XImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.VideoImporters.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.TextureImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.FBXImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.EffectImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.AudioImporters.dll + 2011-09-01T16:22:30+03:00 + + + C:\Windows\Microsoft.Net\assembly\GAC_32\Microsoft.Xna.Framework.Content.Pipeline\v4.0_4.0.0.0__842cf8be1de50553\Microsoft.Xna.Framework.Content.Pipeline.dll + 2014-09-15T20:04:13.234089+03:00 + + + + \ No newline at end of file diff --git a/Subsurface_content/Subsurface_content/obj/Windows/DesignTimeResolveAssemblyReferencesInput.cache b/Subsurface_content/Subsurface_content/obj/Windows/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 000000000..8237fb523 Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/Windows/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/Subsurface_content/Subsurface_content/obj/Windows/IgnoreMe.dll b/Subsurface_content/Subsurface_content/obj/Windows/IgnoreMe.dll new file mode 100644 index 000000000..56f61cc36 Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/Windows/IgnoreMe.dll differ diff --git a/Subsurface_content/Subsurface_content/obj/Windows/Microsoft.Xna.Framework.RuntimeProfile.txt b/Subsurface_content/Subsurface_content/obj/Windows/Microsoft.Xna.Framework.RuntimeProfile.txt new file mode 100644 index 000000000..e6776c3a8 --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/Windows/Microsoft.Xna.Framework.RuntimeProfile.txt @@ -0,0 +1 @@ +Windows.v4.0.HiDef diff --git a/Subsurface_content/Subsurface_content/obj/Windows/Sbmr_content.csproj.FileListAbsolute.txt b/Subsurface_content/Subsurface_content/obj/Windows/Sbmr_content.csproj.FileListAbsolute.txt new file mode 100644 index 000000000..1bab82cac --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/Windows/Sbmr_content.csproj.FileListAbsolute.txt @@ -0,0 +1,10 @@ +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\Windows\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_content\bin\Windows\IgnoreMe.dll +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\Windows\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\Windows\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_content\obj\Windows\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\bin\Windows\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\bin\Windows\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\Windows\Sbmr_content.csprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\Windows\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\Windows\IgnoreMe.dll diff --git a/Subsurface_content/Subsurface_content/obj/Windows/Sbmr_content.csprojResolveAssemblyReference.cache b/Subsurface_content/Subsurface_content/obj/Windows/Sbmr_content.csprojResolveAssemblyReference.cache new file mode 100644 index 000000000..391c28355 Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/Windows/Sbmr_content.csprojResolveAssemblyReference.cache differ diff --git a/Subsurface_content/Subsurface_content/obj/Windows/cachefile-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}-targetpath.txt b/Subsurface_content/Subsurface_content/obj/Windows/cachefile-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}-targetpath.txt new file mode 100644 index 000000000..b1ac40206 --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/Windows/cachefile-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}-targetpath.txt @@ -0,0 +1 @@ +Content\SpriteFont1.xnb diff --git a/Subsurface_content/Subsurface_content/obj/Windows8/ContentPipeline-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}.xml b/Subsurface_content/Subsurface_content/obj/Windows8/ContentPipeline-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}.xml new file mode 100644 index 000000000..b066c356a --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/Windows8/ContentPipeline-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}.xml @@ -0,0 +1,60 @@ + + + + + SpriteFont1.spritefont + SpriteFont1 + FontDescriptionImporter + FontDescriptionProcessor + None + C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\bin\Windows8\Content\SpriteFont1.xnb + + + true + + {8C1D2051-F0F3-457B-AAAE-4E155FC7C75C} + Windows + HiDef + Windows8 + false + C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_contentContent\ + C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\ + C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\Windows8\ + C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\bin\Windows8\Content\ + + + + C:\Program Files (x86)\MSBuild\MonoGame\v3.0\MonoGameContentProcessors.dll + 2014-04-06T00:56:18+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.XImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.VideoImporters.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.TextureImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.FBXImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.EffectImporter.dll + 2011-09-01T16:22:30+03:00 + + + C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.Content.Pipeline.AudioImporters.dll + 2011-09-01T16:22:30+03:00 + + + C:\Windows\Microsoft.Net\assembly\GAC_32\Microsoft.Xna.Framework.Content.Pipeline\v4.0_4.0.0.0__842cf8be1de50553\Microsoft.Xna.Framework.Content.Pipeline.dll + 2014-09-15T20:04:13.234089+03:00 + + + + \ No newline at end of file diff --git a/Subsurface_content/Subsurface_content/obj/Windows8/DesignTimeResolveAssemblyReferencesInput.cache b/Subsurface_content/Subsurface_content/obj/Windows8/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 000000000..5b0e26ae0 Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/Windows8/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/Subsurface_content/Subsurface_content/obj/Windows8/IgnoreMe.dll b/Subsurface_content/Subsurface_content/obj/Windows8/IgnoreMe.dll new file mode 100644 index 000000000..9a1c8db46 Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/Windows8/IgnoreMe.dll differ diff --git a/Subsurface_content/Subsurface_content/obj/Windows8/Microsoft.Xna.Framework.RuntimeProfile.txt b/Subsurface_content/Subsurface_content/obj/Windows8/Microsoft.Xna.Framework.RuntimeProfile.txt new file mode 100644 index 000000000..e6776c3a8 --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/Windows8/Microsoft.Xna.Framework.RuntimeProfile.txt @@ -0,0 +1 @@ +Windows.v4.0.HiDef diff --git a/Subsurface_content/Subsurface_content/obj/Windows8/Sbmr_content.csproj.FileListAbsolute.txt b/Subsurface_content/Subsurface_content/obj/Windows8/Sbmr_content.csproj.FileListAbsolute.txt new file mode 100644 index 000000000..81f024ed4 --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/Windows8/Sbmr_content.csproj.FileListAbsolute.txt @@ -0,0 +1,5 @@ +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\bin\Windows8\Content\SpriteFont1.xnb +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\bin\Windows8\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\Windows8\Microsoft.Xna.Framework.RuntimeProfile.txt +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\Windows8\IgnoreMe.dll +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_content\obj\Windows8\Sbmr_content.csprojResolveAssemblyReference.cache diff --git a/Subsurface_content/Subsurface_content/obj/Windows8/Sbmr_content.csprojResolveAssemblyReference.cache b/Subsurface_content/Subsurface_content/obj/Windows8/Sbmr_content.csprojResolveAssemblyReference.cache new file mode 100644 index 000000000..391c28355 Binary files /dev/null and b/Subsurface_content/Subsurface_content/obj/Windows8/Sbmr_content.csprojResolveAssemblyReference.cache differ diff --git a/Subsurface_content/Subsurface_content/obj/Windows8/cachefile-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}-targetpath.txt b/Subsurface_content/Subsurface_content/obj/Windows8/cachefile-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}-targetpath.txt new file mode 100644 index 000000000..b1ac40206 --- /dev/null +++ b/Subsurface_content/Subsurface_content/obj/Windows8/cachefile-{8C1D2051-F0F3-457B-AAAE-4E155FC7C75C}-targetpath.txt @@ -0,0 +1 @@ +Content\SpriteFont1.xnb diff --git a/Subsurface_content/Subsurface_contentContent/SpriteFont1.spritefont b/Subsurface_content/Subsurface_contentContent/SpriteFont1.spritefont new file mode 100644 index 000000000..0d1c4909c --- /dev/null +++ b/Subsurface_content/Subsurface_contentContent/SpriteFont1.spritefont @@ -0,0 +1,65 @@ + + + + + + + Verdana + + + 10 + + + 0 + + + true + + + + + + + + + + + + ~ + + + + À + ə + + + + diff --git a/Subsurface_content/Subsurface_contentContent/Subsurface_contentContent.contentproj b/Subsurface_content/Subsurface_contentContent/Subsurface_contentContent.contentproj new file mode 100644 index 000000000..5cdb2521b --- /dev/null +++ b/Subsurface_content/Subsurface_contentContent/Subsurface_contentContent.contentproj @@ -0,0 +1,68 @@ + + + + {8C1D2051-F0F3-457B-AAAE-4E155FC7C75C} + {96E2B04D-8817-42c6-938A-82C39BA4D311};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Windows + x86 + Library + Properties + Sbmr_contentContentContent + v4.0 + v4.0 + Windows + bin\$(MonoGamePlatform)\$(Configuration) + Content + x86 + + + Windows + + + Windows8 + + + Android + + + iOS + + + OSX + + + Linux + + + PSM + + + + + + + + + + $(MSBuildExtensionsPath)\MonoGame\v3.0\MonoGameContentProcessors.dll + + + + + SpriteFont1 + FontDescriptionImporter + FontDescriptionProcessor + Designer + Always + + + + + \ No newline at end of file diff --git a/Subsurface_content/Subsurface_contentContent/obj/PSM/DesignTimeResolveAssemblyReferencesInput.cache b/Subsurface_content/Subsurface_contentContent/obj/PSM/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 000000000..870350997 Binary files /dev/null and b/Subsurface_content/Subsurface_contentContent/obj/PSM/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/Subsurface_content/Subsurface_contentContent/obj/PSM/Sbmr_contentContent.contentproj.FileListAbsolute.txt b/Subsurface_content/Subsurface_contentContent/obj/PSM/Sbmr_contentContent.contentproj.FileListAbsolute.txt new file mode 100644 index 000000000..e56544335 --- /dev/null +++ b/Subsurface_content/Subsurface_contentContent/obj/PSM/Sbmr_contentContent.contentproj.FileListAbsolute.txt @@ -0,0 +1,16 @@ +D:\Omat tiedostot\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Downloads\SBMR\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +E:\CBGAM\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Documents\CBGAM\SBMR_1809\SBMR_1509\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Documents\CBGAM\SBMR_1809_2\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +E:\CBGAM\SBMR_2909\SBMR_2909\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +E:\CBGAM\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +E:\CBGAM\SBMR_2710\SBMR\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_2111\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_2811\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1312\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_contentContent\obj\PSM\Sbmr_contentContent.contentprojResolveAssemblyReference.cache diff --git a/Subsurface_content/Subsurface_contentContent/obj/PSM/Sbmr_contentContent.contentprojResolveAssemblyReference.cache b/Subsurface_content/Subsurface_contentContent/obj/PSM/Sbmr_contentContent.contentprojResolveAssemblyReference.cache new file mode 100644 index 000000000..fd0ad5bdd Binary files /dev/null and b/Subsurface_content/Subsurface_contentContent/obj/PSM/Sbmr_contentContent.contentprojResolveAssemblyReference.cache differ diff --git a/Subsurface_content/Subsurface_contentContent/obj/PSM/Subsurface_contentContent.contentproj.FileListAbsolute.txt b/Subsurface_content/Subsurface_contentContent/obj/PSM/Subsurface_contentContent.contentproj.FileListAbsolute.txt new file mode 100644 index 000000000..1c578d089 --- /dev/null +++ b/Subsurface_content/Subsurface_contentContent/obj/PSM/Subsurface_contentContent.contentproj.FileListAbsolute.txt @@ -0,0 +1,32 @@ +C:\Users\Joonas\Desktop\SBMR_1512\Sbmr_content\Sbmr_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1512\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1712\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_1912\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_2012\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2712\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2812\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0101\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0601\Subsurface_0101\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0801\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1001\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1501\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2401\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2801\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1102\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1402\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2102\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_2702\Subsurface_2702 – Kopio\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0303\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0603\Subsurface_0303\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0803\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1030\Subsurface_1030\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1303 – Kopio – Kopio\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1303\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0504\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_10_04\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_12_04\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface – Kopio\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_3004\Subsurface – Kopio\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0205\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_0805\Subsurface_0205\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\Subsurface_1005\Subsurface_content\Subsurface_contentContent\obj\PSM\Subsurface_contentContent.contentprojResolveAssemblyReference.cache diff --git a/Subsurface_content/Subsurface_contentContent/obj/PSM/Subsurface_contentContent.contentprojResolveAssemblyReference.cache b/Subsurface_content/Subsurface_contentContent/obj/PSM/Subsurface_contentContent.contentprojResolveAssemblyReference.cache new file mode 100644 index 000000000..503df1147 Binary files /dev/null and b/Subsurface_content/Subsurface_contentContent/obj/PSM/Subsurface_contentContent.contentprojResolveAssemblyReference.cache differ diff --git a/Subsurface_content/Subsurface_contentContent/obj/Windows/DesignTimeResolveAssemblyReferencesInput.cache b/Subsurface_content/Subsurface_contentContent/obj/Windows/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 000000000..1a0f0b334 Binary files /dev/null and b/Subsurface_content/Subsurface_contentContent/obj/Windows/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/Subsurface_content/Subsurface_contentContent/obj/Windows/Sbmr_contentContent.contentproj.FileListAbsolute.txt b/Subsurface_content/Subsurface_contentContent/obj/Windows/Sbmr_contentContent.contentproj.FileListAbsolute.txt new file mode 100644 index 000000000..9e8784414 --- /dev/null +++ b/Subsurface_content/Subsurface_contentContent/obj/Windows/Sbmr_contentContent.contentproj.FileListAbsolute.txt @@ -0,0 +1,2 @@ +C:\Users\Joonas\Desktop\SMBR_1311\Sbmr_Solution\Sbmr_content\Sbmr_contentContent\obj\Windows\Sbmr_contentContent.contentprojResolveAssemblyReference.cache +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_contentContent\obj\Windows\Sbmr_contentContent.contentprojResolveAssemblyReference.cache diff --git a/Subsurface_content/Subsurface_contentContent/obj/Windows/Sbmr_contentContent.contentprojResolveAssemblyReference.cache b/Subsurface_content/Subsurface_contentContent/obj/Windows/Sbmr_contentContent.contentprojResolveAssemblyReference.cache new file mode 100644 index 000000000..fd0ad5bdd Binary files /dev/null and b/Subsurface_content/Subsurface_contentContent/obj/Windows/Sbmr_contentContent.contentprojResolveAssemblyReference.cache differ diff --git a/Subsurface_content/Subsurface_contentContent/obj/Windows8/DesignTimeResolveAssemblyReferencesInput.cache b/Subsurface_content/Subsurface_contentContent/obj/Windows8/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 000000000..e124943b5 Binary files /dev/null and b/Subsurface_content/Subsurface_contentContent/obj/Windows8/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/Subsurface_content/Subsurface_contentContent/obj/Windows8/Sbmr_contentContent.contentproj.FileListAbsolute.txt b/Subsurface_content/Subsurface_contentContent/obj/Windows8/Sbmr_contentContent.contentproj.FileListAbsolute.txt new file mode 100644 index 000000000..73e6e3752 --- /dev/null +++ b/Subsurface_content/Subsurface_contentContent/obj/Windows8/Sbmr_contentContent.contentproj.FileListAbsolute.txt @@ -0,0 +1 @@ +C:\Users\Joonas\Desktop\SBMR_3011\Sbmr_content\Sbmr_contentContent\obj\Windows8\Sbmr_contentContent.contentprojResolveAssemblyReference.cache diff --git a/Subsurface_content/Subsurface_contentContent/obj/Windows8/Sbmr_contentContent.contentprojResolveAssemblyReference.cache b/Subsurface_content/Subsurface_contentContent/obj/Windows8/Sbmr_contentContent.contentprojResolveAssemblyReference.cache new file mode 100644 index 000000000..fd0ad5bdd Binary files /dev/null and b/Subsurface_content/Subsurface_contentContent/obj/Windows8/Sbmr_contentContent.contentprojResolveAssemblyReference.cache differ