v0.1
This commit is contained in:
211
Subsurface/Source/Camera.cs
Normal file
211
Subsurface/Source/Camera.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
public class Camera
|
||||
{
|
||||
const float DefaultZoom = 1.0f;
|
||||
const float ZoomSmoothness = 8.0f;
|
||||
const float MoveSmoothness = 8.0f;
|
||||
|
||||
private float zoom;
|
||||
|
||||
private float offsetAmount;
|
||||
|
||||
private Matrix transform, shaderTransform, viewMatrix;
|
||||
private Vector2 position;
|
||||
private float rotation;
|
||||
|
||||
public float Shake;
|
||||
private Vector2 shakePosition;
|
||||
private Vector2 shakeTargetPosition;
|
||||
|
||||
|
||||
|
||||
//the area of the world inside the camera view
|
||||
private Rectangle worldView;
|
||||
|
||||
private Point resolution;
|
||||
|
||||
private Vector2 targetPos;
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
get { return zoom; }
|
||||
set
|
||||
{
|
||||
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.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.A)) moveCam.X -= moveSpeed;
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.D)) moveCam.X += moveSpeed;
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.S)) moveCam.Y -= moveSpeed;
|
||||
if (Keyboard.GetState().IsKeyDown(Keys.W)) moveCam.Y += moveSpeed;
|
||||
|
||||
Zoom = MathHelper.Clamp(Zoom + PlayerInput.ScrollWheelSpeed / 1000.0f, 0.1f, 2.0f);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
shakeTargetPosition = Rand.Vector(Shake);
|
||||
shakePosition = Vector2.Lerp(shakePosition, shakeTargetPosition, 0.5f);
|
||||
Shake = MathHelper.Lerp(Shake, 0.0f, 0.03f);
|
||||
|
||||
Translate((moveCam+shakePosition)*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)
|
||||
{
|
||||
coords.Y = -coords.Y;
|
||||
//Vector2 screenCoords = Vector2.Transform(coords, transform);
|
||||
return Vector2.Transform(coords, transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Subsurface/Source/Characters/AI/AIController.cs
Normal file
54
Subsurface/Source/Characters/AI/AIController.cs
Normal file
@@ -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) { }
|
||||
|
||||
}
|
||||
}
|
||||
47
Subsurface/Source/Characters/AI/AITarget.cs
Normal file
47
Subsurface/Source/Characters/AI/AITarget.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class AITarget
|
||||
{
|
||||
public static List<AITarget> List = new List<AITarget>();
|
||||
|
||||
public Entity Entity;
|
||||
|
||||
protected float soundRange;
|
||||
protected float sightRange;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
540
Subsurface/Source/Characters/AI/EnemyAIController.cs
Normal file
540
Subsurface/Source/Characters/AI/EnemyAIController.cs
Normal file
@@ -0,0 +1,540 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Xml.Linq;
|
||||
using FarseerPhysics;
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using FarseerPhysics.Dynamics;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
|
||||
class EnemyAIController : AIController
|
||||
{
|
||||
private const float UpdateTargetsInterval = 5.0f;
|
||||
|
||||
private const float RaycastInterval = 1.0f;
|
||||
|
||||
//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 float raycastTimer;
|
||||
|
||||
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 IDamageable targetEntity;
|
||||
|
||||
//the limb selected for the current attack
|
||||
private Limb attackingLimb;
|
||||
|
||||
private AITarget selectedTarget;
|
||||
private AITargetMemory selectedTargetMemory;
|
||||
private float targetValue;
|
||||
|
||||
private Dictionary<AITarget, AITargetMemory> 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<AITarget, AITargetMemory>();
|
||||
|
||||
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)
|
||||
{
|
||||
UpdateCoolDown(attackPosition, deltaTime);
|
||||
return;
|
||||
}
|
||||
|
||||
if (raycastTimer > 0.0)
|
||||
{
|
||||
raycastTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
GetTargetEntity();
|
||||
|
||||
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 == AttackType.None) continue;
|
||||
if (Vector2.Distance(limb.SimPosition, attackPosition) > limb.attack.Range) continue;
|
||||
|
||||
attackingLimb = limb;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateLimbAttack(deltaTime, attackingLimb, attackPosition);
|
||||
|
||||
}
|
||||
|
||||
private void UpdateCoolDown(Vector2 attackPosition, float deltaTime)
|
||||
{
|
||||
coolDownTimer -= deltaTime;
|
||||
attackingLimb = null;
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine("cooldown");
|
||||
|
||||
if (selectedTarget.Entity is Hull ||
|
||||
Vector2.Distance(attackPosition, Character.AnimController.limbs[0].SimPosition) < ConvertUnits.ToSimUnits(500.0f))
|
||||
{
|
||||
steeringManager.SteeringSeek(attackPosition, -0.8f);
|
||||
steeringManager.SteeringAvoid(deltaTime, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
steeringManager.SteeringSeek(attackPosition, -0.5f);
|
||||
steeringManager.SteeringAvoid(deltaTime, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private void GetTargetEntity()
|
||||
{
|
||||
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;
|
||||
Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd);
|
||||
|
||||
if (Submarine.LastPickedFraction == 1.0f || closestBody == null)
|
||||
{
|
||||
wallAttackPos = Vector2.Zero;
|
||||
return;
|
||||
}
|
||||
|
||||
Structure wall = closestBody.UserData as Structure;
|
||||
if (wall == null)
|
||||
{
|
||||
wallAttackPos = Submarine.LastPickedPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
int sectionIndex = wall.FindSectionIndex(ConvertUnits.ToDisplayUnits(Submarine.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 = closestBody.UserData as IDamageable;
|
||||
}
|
||||
|
||||
private void UpdateLimbAttack(float deltaTime, Limb limb, Vector2 attackPosition)
|
||||
{
|
||||
IDamageable damageTarget = null;
|
||||
|
||||
switch (limb.attack.Type)
|
||||
{
|
||||
case AttackType.PinchCW:
|
||||
case AttackType.PinchCCW:
|
||||
|
||||
float dir = (limb.attack.Type == AttackType.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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//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 && Rand.Range(1,3, false)==1)
|
||||
{
|
||||
selectedTarget = null;
|
||||
character.AnimController.TargetMovement = -character.AnimController.TargetMovement;
|
||||
state = AiState.None;
|
||||
return;
|
||||
}
|
||||
distanceAccumulator = 0.0f;
|
||||
|
||||
selectedTarget = null;
|
||||
selectedTargetMemory = null;
|
||||
targetValue = 0.0f;
|
||||
|
||||
UpdateTargetMemories();
|
||||
|
||||
foreach (AITarget target in AITarget.List)
|
||||
{
|
||||
float valueModifier = 0.0f;
|
||||
float dist = 0.0f;
|
||||
|
||||
IDamageable targetDamageable = target.Entity as IDamageable;
|
||||
if (targetDamageable!=null && targetDamageable.Health <= 0.0f) continue;
|
||||
|
||||
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 = valueModifier * targetMemory.Priority / dist;
|
||||
//dist -= targetMemory.Priority;
|
||||
|
||||
if (Math.Abs(valueModifier) > Math.Abs(targetValue) && (dist < target.SightRange * sight || dist < target.SoundRange * hearing))
|
||||
{
|
||||
Vector2 rayStart = character.AnimController.limbs[0].SimPosition;
|
||||
Vector2 rayEnd = target.Position;
|
||||
|
||||
Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd);
|
||||
Structure closestStructure = (closestBody == null) ? null : closestBody.UserData as Structure;
|
||||
|
||||
//if (targetCharacter != null)
|
||||
//{
|
||||
// //if target is a character that isn't visible, ignore
|
||||
// if (closestStructure != null) continue;
|
||||
|
||||
// //prefer targets with low health
|
||||
// valueModifier = valueModifier / targetCharacter.Health;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
if (targetDamageable != null)
|
||||
{
|
||||
valueModifier = valueModifier / targetDamageable.Health;
|
||||
}
|
||||
else if (closestStructure!=null)
|
||||
{
|
||||
valueModifier = valueModifier / (closestStructure as IDamageable).Health;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueModifier = valueModifier / 1000.0f;
|
||||
}
|
||||
|
||||
//}
|
||||
|
||||
|
||||
|
||||
//float newTargetValue = valueModifier/dist;
|
||||
if (selectedTarget == null || Math.Abs(valueModifier) > Math.Abs(targetValue))
|
||||
{
|
||||
selectedTarget = target;
|
||||
selectedTargetMemory = targetMemory;
|
||||
|
||||
targetValue = valueModifier;
|
||||
Debug.WriteLine(selectedTarget.Entity+": "+targetValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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<AITarget> toBeRemoved = new List<AITarget>();
|
||||
foreach(KeyValuePair<AITarget, AITargetMemory> 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 as Entity).ID);
|
||||
}
|
||||
|
||||
public override void ReadNetworkData(NetIncomingMessage message)
|
||||
{
|
||||
AiState newState = AiState.None;
|
||||
Vector2 newWallAttackPos;
|
||||
float wanderAngle;
|
||||
float updateTargetsTimer, raycastTimer, coolDownTimer;
|
||||
|
||||
int targetID;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
newState = (AiState)(message.ReadByte());
|
||||
newWallAttackPos = new Vector2(message.ReadFloat(), message.ReadFloat());
|
||||
|
||||
wanderAngle = MathUtils.WrapAngleTwoPi(message.ReadFloat());
|
||||
updateTargetsTimer = MathHelper.Clamp(message.ReadFloat(), 0.0f, UpdateTargetsInterval);
|
||||
raycastTimer = MathHelper.Clamp(message.ReadFloat(), 0.0f, RaycastInterval);
|
||||
coolDownTimer = MathHelper.Clamp(message.ReadFloat(), 0.0f, attackCoolDown);
|
||||
|
||||
targetID = message.ReadInt32();
|
||||
}
|
||||
|
||||
catch { return; }
|
||||
|
||||
wallAttackPos = newWallAttackPos;
|
||||
|
||||
steeringManager.WanderAngle = wanderAngle;
|
||||
this.updateTargetsTimer = updateTargetsTimer;
|
||||
this.raycastTimer = raycastTimer;
|
||||
this.coolDownTimer = coolDownTimer;
|
||||
|
||||
if (targetID > -1)
|
||||
targetEntity = Entity.FindEntityByID(targetID) as IDamageable;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
27
Subsurface/Source/Characters/AI/ISteerable.cs
Normal file
27
Subsurface/Source/Characters/AI/ISteerable.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
interface ISteerable
|
||||
{
|
||||
|
||||
Vector2 Steering
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
Vector2 Velocity
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vector2 Position
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
240
Subsurface/Source/Characters/AI/PathFinder.cs
Normal file
240
Subsurface/Source/Characters/AI/PathFinder.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class PathNode
|
||||
{
|
||||
private WayPoint wayPoint;
|
||||
|
||||
private int wayPointID;
|
||||
|
||||
public int state;
|
||||
|
||||
public PathNode Parent;
|
||||
|
||||
private Vector2 position;
|
||||
|
||||
public float F,G,H;
|
||||
|
||||
public List<PathNode> connections;
|
||||
public float[] distances;
|
||||
|
||||
public WayPoint Waypoint
|
||||
{
|
||||
get { return wayPoint; }
|
||||
}
|
||||
|
||||
public Vector2 Position
|
||||
{
|
||||
get {return position;}
|
||||
}
|
||||
|
||||
public PathNode(WayPoint wayPoint)
|
||||
{
|
||||
this.wayPoint = wayPoint;
|
||||
this.position = wayPoint.SimPosition;
|
||||
wayPointID = wayPoint.ID;
|
||||
|
||||
connections = new List<PathNode>();
|
||||
}
|
||||
|
||||
public static List<PathNode> GenerateNodes(List<WayPoint> wayPoints)
|
||||
{
|
||||
var nodes = new Dictionary<int, PathNode>();
|
||||
foreach (WayPoint wayPoint in wayPoints)
|
||||
{
|
||||
nodes.Add(wayPoint.ID, new PathNode(wayPoint));
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<int,PathNode> node in nodes)
|
||||
{
|
||||
foreach (MapEntity linked in node.Value.wayPoint.linkedTo)
|
||||
{
|
||||
PathNode connectedNode = null;
|
||||
nodes.TryGetValue(linked.ID, out connectedNode);
|
||||
if (connectedNode == null) continue;
|
||||
|
||||
node.Value.connections.Add(connectedNode);
|
||||
}
|
||||
}
|
||||
|
||||
var nodeList = nodes.Values.ToList();
|
||||
foreach (PathNode node in nodeList)
|
||||
{
|
||||
node.distances = new float[node.connections.Count];
|
||||
for (int i = 0; i< node.distances.Length; i++)
|
||||
{
|
||||
node.distances[i] = Vector2.Distance(node.position, node.connections[i].position);
|
||||
}
|
||||
}
|
||||
return nodeList;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PathFinder
|
||||
{
|
||||
List<PathNode> nodes;
|
||||
|
||||
private bool insideSubmarine;
|
||||
|
||||
public PathFinder(List<WayPoint> wayPoints, bool insideSubmarine = false)
|
||||
{
|
||||
nodes = PathNode.GenerateNodes(wayPoints.FindAll(w => w.MoveWithLevel != insideSubmarine));
|
||||
|
||||
this.insideSubmarine = insideSubmarine;
|
||||
}
|
||||
|
||||
public SteeringPath FindPath(Vector2 start, Vector2 end)
|
||||
{
|
||||
float closestDist = 0.0f;
|
||||
PathNode startNode = null;
|
||||
foreach (PathNode node in nodes)
|
||||
{
|
||||
float dist = Vector2.Distance(start,node.Position);
|
||||
if (dist<closestDist || startNode==null)
|
||||
{
|
||||
closestDist = dist;
|
||||
startNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
closestDist = 0.0f;
|
||||
PathNode endNode = null;
|
||||
foreach (PathNode node in nodes)
|
||||
{
|
||||
float dist = Vector2.Distance(end, node.Position);
|
||||
if (dist < closestDist || endNode == null)
|
||||
{
|
||||
closestDist = dist;
|
||||
endNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
if (startNode == null || endNode == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Pathfinding error, couldn't find pathnodes");
|
||||
return new SteeringPath();
|
||||
}
|
||||
|
||||
return FindPath(startNode,endNode);
|
||||
}
|
||||
|
||||
public SteeringPath FindPath(WayPoint start, WayPoint end)
|
||||
{
|
||||
PathNode startNode=null, endNode=null;
|
||||
foreach (PathNode node in nodes)
|
||||
{
|
||||
if (node.Waypoint == start)
|
||||
{
|
||||
startNode = node;
|
||||
if (endNode != null) break;
|
||||
}
|
||||
if (node.Waypoint == end)
|
||||
{
|
||||
endNode = node;
|
||||
if (startNode != null) break;
|
||||
}
|
||||
|
||||
if (startNode==null || endNode==null)
|
||||
{
|
||||
DebugConsole.ThrowError("Pathfinding error, couldn't find matching pathnodes to waypoints");
|
||||
return new SteeringPath();;
|
||||
}
|
||||
}
|
||||
|
||||
return FindPath(startNode, endNode);
|
||||
}
|
||||
|
||||
private SteeringPath FindPath(PathNode start, PathNode end)
|
||||
{
|
||||
foreach (PathNode node in nodes)
|
||||
{
|
||||
node.state = 0;
|
||||
node.F = 0.0f;
|
||||
node.G = 0.0f;
|
||||
node.H = 0.0f;
|
||||
}
|
||||
|
||||
start.state = 1;
|
||||
while (true)
|
||||
{
|
||||
|
||||
PathNode currNode = null;
|
||||
float dist = 10000.0f;
|
||||
foreach (PathNode node in nodes)
|
||||
{
|
||||
if (node.state != 1) continue;
|
||||
if (node.F < dist)
|
||||
{
|
||||
dist = node.F;
|
||||
currNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
if (currNode == null || currNode == end) break;
|
||||
|
||||
currNode.state = 2;
|
||||
|
||||
for (int i = 0; i < currNode.connections.Count; i++)
|
||||
{
|
||||
PathNode nextNode = currNode.connections[i];
|
||||
|
||||
//a node that hasn't been searched yet
|
||||
if (nextNode.state==0)
|
||||
{
|
||||
nextNode.H = Vector2.Distance(nextNode.Position,end.Position);
|
||||
nextNode.G = currNode.G + currNode.distances[i];
|
||||
nextNode.F = nextNode.G + nextNode.H;
|
||||
nextNode.Parent = currNode;
|
||||
nextNode.state = 1;
|
||||
}
|
||||
//node that has been searched
|
||||
else if (nextNode.state==1)
|
||||
{
|
||||
float tempG = currNode.G + currNode.distances[i];
|
||||
//only use if this new route is better than the
|
||||
//route the node was a part of
|
||||
if (tempG < nextNode.G)
|
||||
{
|
||||
nextNode.G = tempG;
|
||||
nextNode.F = nextNode.G + nextNode.H;
|
||||
nextNode.Parent = currNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (end.state==0)
|
||||
{
|
||||
//path not found
|
||||
return new SteeringPath();
|
||||
}
|
||||
|
||||
SteeringPath path = new SteeringPath();
|
||||
List<WayPoint> finalPath = new List<WayPoint>();
|
||||
|
||||
PathNode pathNode = end;
|
||||
while (pathNode != start && pathNode != null)
|
||||
{
|
||||
finalPath.Add(pathNode.Waypoint);
|
||||
|
||||
pathNode = pathNode.Parent;
|
||||
}
|
||||
|
||||
finalPath.Reverse();
|
||||
|
||||
foreach (WayPoint wayPoint in finalPath)
|
||||
{
|
||||
path.AddNode(wayPoint);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
151
Subsurface/Source/Characters/AI/SteeringManager.cs
Normal file
151
Subsurface/Source/Characters/AI/SteeringManager.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using FarseerPhysics.Dynamics;
|
||||
|
||||
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 = Rand.Range(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 += Rand.Range(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;
|
||||
Body closestBody = Submarine.CheckVisibility(host.Position, ahead);
|
||||
if (closestBody == null)
|
||||
{
|
||||
avoidSteering = Vector2.Zero;
|
||||
return Vector2.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
Structure closestStructure = closestBody.UserData as Structure;
|
||||
if (closestStructure!=null)
|
||||
{
|
||||
Vector2 obstaclePosition = Submarine.LastPickedPosition;
|
||||
if (closestStructure.IsHorizontal)
|
||||
{
|
||||
obstaclePosition.Y = closestStructure.SimPosition.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
obstaclePosition.X = closestStructure.SimPosition.X;
|
||||
}
|
||||
|
||||
avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - obstaclePosition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
rayCastTimer -= deltaTime;
|
||||
}
|
||||
|
||||
return avoidSteering * speed;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
41
Subsurface/Source/Characters/AI/SteeringPath.cs
Normal file
41
Subsurface/Source/Characters/AI/SteeringPath.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class SteeringPath
|
||||
{
|
||||
private Queue<WayPoint> nodes;
|
||||
|
||||
WayPoint currentNode;
|
||||
|
||||
public SteeringPath()
|
||||
{
|
||||
nodes = new Queue<WayPoint>();
|
||||
}
|
||||
|
||||
public void AddNode(WayPoint node)
|
||||
{
|
||||
if (node == null) return;
|
||||
nodes.Enqueue(node);
|
||||
}
|
||||
|
||||
public WayPoint CurrentNode
|
||||
{
|
||||
get { return currentNode; }
|
||||
}
|
||||
|
||||
public WayPoint GetNode(Vector2 pos, float minDistance = 0.1f)
|
||||
{
|
||||
if (nodes.Count == 0) return null;
|
||||
if (currentNode == null || Vector2.Distance(pos, currentNode.SimPosition) < minDistance) currentNode = nodes.Dequeue();
|
||||
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
public void ClearPath()
|
||||
{
|
||||
nodes.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Subsurface/Source/Characters/AnimController.cs
Normal file
58
Subsurface/Source/Characters/AnimController.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Xml.Linq;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class AnimController : Ragdoll
|
||||
{
|
||||
public bool IsStanding;
|
||||
|
||||
public enum Animation { None, Climbing, UsingConstruction, Struggle };
|
||||
public Animation Anim;
|
||||
|
||||
public Direction TargetDir;
|
||||
|
||||
protected Character character;
|
||||
|
||||
protected float walkSpeed, swimSpeed;
|
||||
|
||||
//how large impacts the character can take before being stunned
|
||||
//protected float impactTolerance;
|
||||
|
||||
protected float stunTimer;
|
||||
|
||||
protected float walkPos;
|
||||
|
||||
protected readonly Vector2 stepSize;
|
||||
protected readonly float legTorque;
|
||||
protected readonly Vector2 stepOffset;
|
||||
|
||||
public float StunTimer
|
||||
{
|
||||
get { return stunTimer; }
|
||||
set { stunTimer = value; }
|
||||
}
|
||||
|
||||
public AnimController(Character character, XElement element)
|
||||
: base(character, element)
|
||||
{
|
||||
this.character = character;
|
||||
|
||||
stepSize = ToolBox.GetAttributeVector2(element, "stepsize", Vector2.One);
|
||||
stepSize = ConvertUnits.ToSimUnits(stepSize);
|
||||
|
||||
stepOffset = ToolBox.GetAttributeVector2(element, "stepoffset", Vector2.One);
|
||||
stepOffset = ConvertUnits.ToSimUnits(stepOffset);
|
||||
|
||||
//impactTolerance = ToolBox.GetAttributeFloat(element, "impacttolerance", 10.0f);
|
||||
|
||||
legTorque = ToolBox.GetAttributeFloat(element, "legtorque", 0.0f);
|
||||
}
|
||||
|
||||
public virtual void UpdateAnim(float deltaTime) { }
|
||||
|
||||
public virtual void HoldItem(float deltaTime, Camera cam, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, float holdAngle) { }
|
||||
|
||||
}
|
||||
}
|
||||
116
Subsurface/Source/Characters/Attack.cs
Normal file
116
Subsurface/Source/Characters/Attack.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
|
||||
public enum DamageType { None, Blunt, Slash }
|
||||
|
||||
public enum AttackType
|
||||
{
|
||||
None, PinchCW, PinchCCW
|
||||
}
|
||||
|
||||
struct AttackResult
|
||||
{
|
||||
public readonly float Damage;
|
||||
public readonly float Bleeding;
|
||||
|
||||
public readonly bool HitArmor;
|
||||
|
||||
public AttackResult(float damage, float bleeding, bool hitArmor=false)
|
||||
{
|
||||
this.Damage = damage;
|
||||
this.Bleeding = bleeding;
|
||||
|
||||
this.HitArmor = hitArmor;
|
||||
}
|
||||
}
|
||||
|
||||
class Attack
|
||||
{
|
||||
|
||||
public readonly AttackType 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 = (AttackType)Enum.Parse(typeof(AttackType), element.Attribute("type").Value, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Type = AttackType.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, "structuredamage", 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 AttackResult DoDamage(IDamageable target, Vector2 position, float deltaTime, bool playSound = true)
|
||||
{
|
||||
float damageAmount = 0.0f;
|
||||
//DamageSoundType damageSoundType = DamageSoundType.None;
|
||||
|
||||
if (target as Character == null)
|
||||
{
|
||||
damageAmount = StructureDamage;
|
||||
//damageSoundType = (damageType == DamageType.Blunt) ? DamageSoundType.StructureBlunt: DamageSoundType.StructureSlash;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
damageAmount = Damage;
|
||||
//damageSoundType = (damageType == DamageType.Blunt) ? DamageSoundType.LimbBlunt : DamageSoundType.LimbSlash;
|
||||
}
|
||||
//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)
|
||||
{
|
||||
return target.AddDamage(position, DamageType, damageAmount, bleedingAmount, Stun, playSound);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new AttackResult(0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1183
Subsurface/Source/Characters/Character.cs
Normal file
1183
Subsurface/Source/Characters/Character.cs
Normal file
File diff suppressed because it is too large
Load Diff
279
Subsurface/Source/Characters/CharacterInfo.cs
Normal file
279
Subsurface/Source/Characters/CharacterInfo.cs
Normal file
@@ -0,0 +1,279 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
public enum Gender { None, Male, Female };
|
||||
|
||||
class CharacterInfo
|
||||
{
|
||||
public string Name;
|
||||
|
||||
public Character Character;
|
||||
|
||||
public readonly string File;
|
||||
|
||||
public Job Job;
|
||||
|
||||
private List<int> pickedItems;
|
||||
|
||||
private Vector2[] headSpriteRange;
|
||||
|
||||
private Gender gender;
|
||||
|
||||
public int Salary;
|
||||
|
||||
public int HeadSpriteId;
|
||||
private Sprite headSprite;
|
||||
|
||||
public bool StartItemsGiven;
|
||||
|
||||
public List<int> PickedItemIDs
|
||||
{
|
||||
get { return pickedItems; }
|
||||
}
|
||||
|
||||
public Sprite HeadSprite
|
||||
{
|
||||
get
|
||||
{
|
||||
if (headSprite == null) LoadHeadSprite();
|
||||
return headSprite;
|
||||
}
|
||||
}
|
||||
|
||||
public Gender Gender
|
||||
{
|
||||
get { return gender; }
|
||||
set
|
||||
{
|
||||
if (gender == value) return;
|
||||
gender = value;
|
||||
|
||||
int genderIndex = (this.gender == Gender.Female) ? 1 : 0;
|
||||
if (headSpriteRange[genderIndex] != Vector2.Zero)
|
||||
{
|
||||
HeadSpriteId = Rand.Range((int)headSpriteRange[genderIndex].X, (int)headSpriteRange[genderIndex].Y + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
HeadSpriteId = 0;
|
||||
}
|
||||
|
||||
LoadHeadSprite();
|
||||
}
|
||||
}
|
||||
|
||||
public CharacterInfo(string file, string name = "", Gender gender = Gender.None, JobPrefab jobPrefab = null)
|
||||
{
|
||||
this.File = file;
|
||||
|
||||
headSpriteRange = new Vector2[2];
|
||||
|
||||
pickedItems = new List<int>();
|
||||
|
||||
//ID = -1;
|
||||
|
||||
XDocument doc = ToolBox.TryLoadXml(file);
|
||||
if (doc == null) return;
|
||||
|
||||
if (ToolBox.GetAttributeBool(doc.Root, "genders", false))
|
||||
{
|
||||
if (gender == Gender.None)
|
||||
{
|
||||
float femaleRatio = ToolBox.GetAttributeFloat(doc.Root, "femaleratio", 0.5f);
|
||||
this.gender = (Rand.Range(0.0f, 1.0f, false) < femaleRatio) ? Gender.Female : Gender.Male;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.gender = gender;
|
||||
}
|
||||
}
|
||||
|
||||
headSpriteRange[0] = ToolBox.GetAttributeVector2(doc.Root, "headid", Vector2.Zero);
|
||||
headSpriteRange[1] = headSpriteRange[0];
|
||||
if (headSpriteRange[0] == Vector2.Zero)
|
||||
{
|
||||
headSpriteRange[0] = ToolBox.GetAttributeVector2(doc.Root, "maleheadid", Vector2.Zero);
|
||||
headSpriteRange[1] = ToolBox.GetAttributeVector2(doc.Root, "femaleheadid", Vector2.Zero);
|
||||
}
|
||||
|
||||
int genderIndex = (this.gender == Gender.Female) ? 1 : 0;
|
||||
if (headSpriteRange[genderIndex] != Vector2.Zero)
|
||||
{
|
||||
HeadSpriteId = Rand.Range((int)headSpriteRange[genderIndex].X, (int)headSpriteRange[genderIndex].Y + 1);
|
||||
}
|
||||
|
||||
this.Job = (jobPrefab == null) ? Job.Random() : new Job(jobPrefab);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Salary = CalculateSalary();
|
||||
}
|
||||
|
||||
private void LoadHeadSprite()
|
||||
{
|
||||
XDocument doc = ToolBox.TryLoadXml(File);
|
||||
if (doc == null) return;
|
||||
|
||||
XElement ragdollElement = doc.Root.Element("ragdoll");
|
||||
foreach (XElement limbElement in ragdollElement.Elements())
|
||||
{
|
||||
if (ToolBox.GetAttributeString(limbElement, "type", "").ToLower() != "head") continue;
|
||||
|
||||
XElement spriteElement = limbElement.Element("sprite");
|
||||
|
||||
string spritePath = spriteElement.Attribute("texture").Value;
|
||||
|
||||
spritePath = spritePath.Replace("[GENDER]", (this.gender == Gender.Female) ? "f" : "");
|
||||
spritePath = spritePath.Replace("[HEADID]", HeadSpriteId.ToString());
|
||||
|
||||
headSprite = new Sprite(spriteElement, "", spritePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public GUIFrame CreateInfoFrame(Rectangle rect)
|
||||
{
|
||||
GUIFrame frame = new GUIFrame(rect, Color.Transparent);
|
||||
frame.Padding = new Vector4(10.0f,10.0f,10.0f,10.0f);
|
||||
|
||||
return CreateInfoFrame(frame);
|
||||
}
|
||||
|
||||
public GUIFrame CreateInfoFrame(GUIFrame frame)
|
||||
{
|
||||
GUIImage image = new GUIImage(new Rectangle(0,0,30,30), HeadSprite, Alignment.TopLeft, frame);
|
||||
|
||||
int x = 0, y = 0;
|
||||
new GUITextBlock(new Rectangle(x+80, y, 200, 20), Name, GUI.style, frame);
|
||||
y += 20;
|
||||
new GUITextBlock(new Rectangle(x+80, y, 200, 20), Job.Name, GUI.style, frame);
|
||||
y += 30;
|
||||
|
||||
var skills = Job.Skills;
|
||||
skills.Sort((s1, s2) => -s1.Level.CompareTo(s2.Level));
|
||||
|
||||
new GUITextBlock(new Rectangle(x, y, 200, 20), "Skills:", GUI.style, frame);
|
||||
y += 20;
|
||||
foreach (Skill skill in skills)
|
||||
{
|
||||
Color textColor = Color.White * (0.5f + skill.Level/200.0f);
|
||||
new GUITextBlock(new Rectangle(x+20, y, 200, 20), skill.Name, Color.Transparent, textColor, Alignment.Left, GUI.style, frame);
|
||||
new GUITextBlock(new Rectangle(x + 20, y, 200, 20), skill.Level.ToString(), Color.Transparent, textColor, Alignment.Right, GUI.style, frame);
|
||||
y += 20;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
public void UpdateCharacterItems()
|
||||
{
|
||||
pickedItems.Clear();
|
||||
foreach (Item item in Character.Inventory.items)
|
||||
{
|
||||
if (item == null) continue;
|
||||
pickedItems.Add(item.ID);
|
||||
}
|
||||
}
|
||||
|
||||
public CharacterInfo(XElement element)
|
||||
{
|
||||
Name = ToolBox.GetAttributeString(element, "name", "unnamed");
|
||||
|
||||
string genderStr = ToolBox.GetAttributeString(element, "gender", "male").ToLower();
|
||||
gender = (genderStr == "m") ? Gender.Male : Gender.Female;
|
||||
|
||||
File = ToolBox.GetAttributeString(element, "file", "");
|
||||
Salary = ToolBox.GetAttributeInt(element, "salary", 1000);
|
||||
HeadSpriteId = ToolBox.GetAttributeInt(element, "headspriteid", 1);
|
||||
StartItemsGiven = ToolBox.GetAttributeBool(element, "startitemsgiven", false);
|
||||
|
||||
pickedItems = new List<int>();
|
||||
|
||||
string pickedItemString = ToolBox.GetAttributeString(element, "items", "");
|
||||
if (!string.IsNullOrEmpty(pickedItemString))
|
||||
{
|
||||
string[] itemIds = pickedItemString.Split(',');
|
||||
foreach (string s in itemIds)
|
||||
{
|
||||
pickedItems.Add(int.Parse(s));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLower() != "job") continue;
|
||||
|
||||
Job = new Job(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private int CalculateSalary()
|
||||
{
|
||||
if (Name == null || Job == null) return 0;
|
||||
|
||||
int salary = Math.Abs(Name.GetHashCode()) % 100;
|
||||
|
||||
foreach (Skill skill in Job.Skills)
|
||||
{
|
||||
salary += skill.Level * 10;
|
||||
}
|
||||
|
||||
return salary;
|
||||
}
|
||||
|
||||
public virtual XElement Save(XElement parentElement)
|
||||
{
|
||||
XElement charElement = new XElement("character");
|
||||
|
||||
charElement.Add(
|
||||
new XAttribute("name", Name),
|
||||
new XAttribute("file", File),
|
||||
new XAttribute("gender", gender == Gender.Male ? "m" : "f"),
|
||||
new XAttribute("salary", Salary),
|
||||
new XAttribute("headspriteid", HeadSpriteId),
|
||||
new XAttribute("startitemsgiven", StartItemsGiven));
|
||||
|
||||
if (Character != null && Character.Inventory != null)
|
||||
{
|
||||
UpdateCharacterItems();
|
||||
}
|
||||
|
||||
if (pickedItems.Count > 0)
|
||||
{
|
||||
charElement.Add(new XAttribute("items", string.Join(",", pickedItems)));
|
||||
}
|
||||
|
||||
Job.Save(charElement);
|
||||
|
||||
parentElement.Add(charElement);
|
||||
return charElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Subsurface/Source/Characters/DelayedEffect.cs
Normal file
53
Subsurface/Source/Characters/DelayedEffect.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class DelayedEffect : StatusEffect
|
||||
{
|
||||
public static List<DelayedEffect> List = new List<DelayedEffect>();
|
||||
|
||||
private float delay;
|
||||
|
||||
private float timer;
|
||||
|
||||
private Entity entity;
|
||||
|
||||
private List<IPropertyObject> targets;
|
||||
|
||||
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, Entity entity, List<IPropertyObject> targets)
|
||||
{
|
||||
if (this.type != type) return;
|
||||
|
||||
timer = delay;
|
||||
this.entity = entity;
|
||||
|
||||
this.targets = targets;
|
||||
|
||||
List.Add(this);
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
timer -= deltaTime;
|
||||
|
||||
if (timer > 0.0f) return;
|
||||
|
||||
base.Apply(1.0f, entity, targets);
|
||||
List.Remove(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
381
Subsurface/Source/Characters/FishAnimController.cs
Normal file
381
Subsurface/Source/Characters/FishAnimController.cs
Normal file
@@ -0,0 +1,381 @@
|
||||
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 rotateTowardsMovement;
|
||||
|
||||
private bool flip;
|
||||
|
||||
private float flipTimer;
|
||||
|
||||
private float? footRotation;
|
||||
|
||||
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);
|
||||
|
||||
float footRot = ToolBox.GetAttributeFloat(element,"footrotation", float.NaN);
|
||||
if (!float.IsNaN(footRot))
|
||||
{
|
||||
footRotation = MathHelper.ToRadians(footRot);
|
||||
}
|
||||
|
||||
rotateTowardsMovement = ToolBox.GetAttributeBool(element, "rotatetowardsmovement", true);
|
||||
}
|
||||
|
||||
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)*0.5f)
|
||||
{
|
||||
TargetDir = Direction.Right;
|
||||
}
|
||||
else if (movement.X < -0.1f && movement.X < -Math.Abs(movement.Y)*0.5f)
|
||||
{
|
||||
TargetDir = Direction.Left;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Limb head = GetLimb(LimbType.Head);
|
||||
float rotation = MathUtils.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;
|
||||
flipTimer += deltaTime;
|
||||
|
||||
if (TargetDir != dir)
|
||||
{
|
||||
if (flipTimer>1.0f)
|
||||
{
|
||||
Flip();
|
||||
if (flip) Mirror();
|
||||
flipTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateSineAnim(float deltaTime)
|
||||
{
|
||||
movement = MathUtils.SmoothStep(movement, TargetMovement*swimSpeed, 1.0f);
|
||||
if (movement == Vector2.Zero) return;
|
||||
|
||||
if (!inWater) movement.Y = Math.Min(0.0f, movement.Y);
|
||||
|
||||
float movementAngle = MathUtils.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 = MathUtils.GetShortestAngle(tail.body.Rotation, movementAngle + waveRotation);
|
||||
|
||||
tail.body.ApplyTorque(angle * tail.Mass);
|
||||
|
||||
//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 = (rotateTowardsMovement) ?
|
||||
head.body.Rotation+ MathUtils.GetShortestAngle(head.body.Rotation, movementAngle) :
|
||||
HeadAngle*Dir;
|
||||
|
||||
|
||||
head.body.SmoothRotate(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 = MathUtils.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, 10.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
colliderLimb = head;
|
||||
colliderHeight = HeadPosition;
|
||||
|
||||
if (onGround) colliderLimb.body.SmoothRotate(HeadAngle*Dir, 100.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 (!onGround) return;
|
||||
|
||||
if (closestFraction == 1) //raycast didn't hit anything
|
||||
floorY = (currentHull == null) ? -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) < colliderHeight * 1.2f)
|
||||
{
|
||||
colliderLimb.Move(new Vector2(colliderPos.X + movement.X * 0.2f, floorY + colliderHeight), 5.0f);
|
||||
}
|
||||
|
||||
float walkCycleSpeed = head.LinearVelocity.X * 0.05f;
|
||||
|
||||
walkPos -= walkCycleSpeed;
|
||||
|
||||
Vector2 transformedStepSize = new Vector2(
|
||||
(float)Math.Cos(walkPos) * stepSize.X * 3.0f,
|
||||
(float)Math.Sin(walkPos) * stepSize.Y * 2.0f);
|
||||
|
||||
foreach (Limb limb in limbs)
|
||||
{
|
||||
switch (limb.type)
|
||||
{
|
||||
case LimbType.LeftFoot:
|
||||
case LimbType.RightFoot:
|
||||
Vector2 footPos = new Vector2(limb.SimPosition.X, colliderPos.Y - colliderHeight);
|
||||
|
||||
if (limb.RefJointIndex>-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);
|
||||
}
|
||||
|
||||
if (footRotation!=null) limb.body.SmoothRotate((float)footRotation*Dir, 50.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);
|
||||
|
||||
if (head != null) head.body.ApplyTorque(head.Mass * Dir * 0.1f);
|
||||
if (tail != null) 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
826
Subsurface/Source/Characters/HumanoidAnimController.cs
Normal file
826
Subsurface/Source/Characters/HumanoidAnimController.cs
Normal file
@@ -0,0 +1,826 @@
|
||||
using System;
|
||||
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;
|
||||
|
||||
if (inWater) stairs = null;
|
||||
|
||||
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:
|
||||
if (inWater) return -1;
|
||||
Structure structure = fixture.Body.UserData as Structure;
|
||||
if (stairs == null && structure!=null)
|
||||
{
|
||||
if (LowestLimb.SimPosition.Y < structure.SimPosition.Y)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
stairs = structure;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Physics.CollisionPlatform:
|
||||
Structure platform = fixture.Body.UserData as Structure;
|
||||
if (IgnorePlatforms || LowestLimb.Position.Y < platform.Rect.Y) return -1;
|
||||
break;
|
||||
case Physics.CollisionWall:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
onGround = true;
|
||||
if (fraction < closestFraction)
|
||||
{
|
||||
closestFraction = fraction;
|
||||
|
||||
Structure structure = fixture.Body.UserData as Structure;
|
||||
if (structure != null) closestStructure = structure;
|
||||
}
|
||||
onFloorTimer = 0.05f;
|
||||
return closestFraction;
|
||||
}
|
||||
, rayStart, rayEnd);
|
||||
|
||||
if (closestStructure != null && closestStructure.StairDirection != Direction.None)
|
||||
{
|
||||
stairs = closestStructure;
|
||||
}
|
||||
else
|
||||
{
|
||||
stairs = null;
|
||||
}
|
||||
|
||||
//the ragdoll "stays on ground" for 50 millisecs after separation
|
||||
if (onFloorTimer <= 0.0f)
|
||||
{
|
||||
onGround = false;
|
||||
if (GetLimb(LimbType.Torso).inWater) inWater = true;
|
||||
//TODO: joku järkevämpi systeemi
|
||||
//if (!inWater && lastTimeOnFloor + 200 < gameTime.TotalGameTime.Milliseconds)
|
||||
// stunTimer = Math.Max(stunTimer, (float)gameTime.TotalGameTime.TotalMilliseconds + 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
onFloorTimer -= deltaTime;
|
||||
}
|
||||
|
||||
if (closestFraction == 1) //raycast didn't hit anything
|
||||
{
|
||||
floorY = (currentHull == null) ? -1000.0f : ConvertUnits.ToSimUnits(currentHull.Rect.Y - currentHull.Rect.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
floorY = rayStart.Y + (rayEnd.Y - rayStart.Y) * closestFraction;
|
||||
}
|
||||
|
||||
|
||||
IgnorePlatforms = (TargetMovement.Y < 0.0f);
|
||||
|
||||
//stun (= disable the animations) if the ragdoll receives a large enough impact
|
||||
if (strongestImpact > 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)
|
||||
{
|
||||
TargetMovement = new Vector2(MathHelper.Clamp(TargetMovement.X, -2.0f, 2.0f), TargetMovement.Y) ;
|
||||
|
||||
if ((TargetMovement.X>0.0f && stairs.StairDirection == Direction.Right) ||
|
||||
TargetMovement.X < 0.0f && stairs.StairDirection == Direction.Left)
|
||||
{
|
||||
TargetMovement *= 1.35f;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
TargetMovement /= 1.2f;
|
||||
}
|
||||
|
||||
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 = MathUtils.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 =
|
||||
MathUtils.SmoothStep(torso.SimPosition,
|
||||
new Vector2(footMid + movement.X * 0.35f, colliderPos.Y + TorsoPosition), getUpSpeed);
|
||||
|
||||
head.pullJoint.Enabled = true;
|
||||
head.pullJoint.WorldAnchorB =
|
||||
MathUtils.SmoothStep(head.SimPosition,
|
||||
new Vector2(footMid + movement.X * 0.4f, colliderPos.Y + HeadPosition), getUpSpeed);
|
||||
}
|
||||
|
||||
|
||||
//moving horizontally
|
||||
if (TargetMovement.X != 0.0f)
|
||||
{
|
||||
//progress the walking animation
|
||||
walkPos -= (walkCycleSpeed / runningModifier)*0.8f;
|
||||
|
||||
MoveLimb(leftFoot,
|
||||
colliderPos + new Vector2(
|
||||
stepSize.X,
|
||||
(stepSize.Y > 0.0f) ? stepSize.Y : -0.15f),
|
||||
15.0f, true);
|
||||
|
||||
MoveLimb(rightFoot,
|
||||
colliderPos + new Vector2(
|
||||
-stepSize.X,
|
||||
(-stepSize.Y > 0.0f) ? -stepSize.Y : -0.15f),
|
||||
15.0f, true);
|
||||
|
||||
if (Math.Sign(stepSize.X) == Math.Sign(Dir))
|
||||
{
|
||||
leftFoot.body.SmoothRotate(leftLeg.body.Rotation + MathHelper.PiOver2 * Dir * 1.6f, 20.0f * runningModifier);
|
||||
}
|
||||
else if (Math.Sign(-stepSize.X) == Math.Sign(Dir))
|
||||
{
|
||||
rightFoot.body.SmoothRotate(rightLeg.body.Rotation + MathHelper.PiOver2 * Dir * 1.6f, 20 * runningModifier);
|
||||
}
|
||||
|
||||
if (walkPosY > 0.0f)
|
||||
{
|
||||
GetLimb(LimbType.LeftThigh).body.ApplyTorque(-walkPosY * Dir * Math.Abs(movement.X) * -5.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetLimb(LimbType.RightThigh).body.ApplyTorque(walkPosY * Dir * Math.Abs(movement.X) * -5.0f);
|
||||
}
|
||||
|
||||
//calculate the positions of hands
|
||||
handPos = torso.SimPosition;
|
||||
handPos.X = -walkPosX * 0.1f * runningModifier;
|
||||
|
||||
float lowerY = -0.6f + runningModifier/3.5f;
|
||||
|
||||
handPos.Y = lowerY + (float)(Math.Abs(Math.Sin(walkPos - Math.PI * 1.5f) * 0.1)) / runningModifier;
|
||||
|
||||
Vector2 posAdditon = new Vector2(movement.X*0.07f, 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, 0.05f, true);
|
||||
//rightHand.body.ApplyLinearImpulse((handPos - rightHand.Position));
|
||||
rightHand.body.SmoothRotate(0.0f, 5.0f);
|
||||
|
||||
var rightArm = GetLimb(LimbType.RightArm);
|
||||
rightArm.body.SmoothRotate(0.0f, 20.0f);
|
||||
}
|
||||
|
||||
if (!leftHand.Disabled)
|
||||
{
|
||||
//MoveLimb(leftHand, handPos, 0.05f, true);
|
||||
//leftHand.body.ApplyLinearImpulse((handPos - leftHand.Position));
|
||||
leftHand.body.SmoothRotate(0.0f, 5.0f);
|
||||
|
||||
var leftArm = GetLimb(LimbType.LeftArm);
|
||||
leftArm.body.SmoothRotate(0.0f, 20.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.Rect.Y - currentHull.Surface > 50.0f) && !head.inWater)
|
||||
{
|
||||
surfaceLimiter = (ConvertUnits.ToDisplayUnits(head.SimPosition.Y)-surfaceY);
|
||||
surfaceLimiter = Math.Max(1.0f, surfaceLimiter);
|
||||
if (surfaceLimiter > 20.0f) return;
|
||||
}
|
||||
|
||||
Limb torso = GetLimb(LimbType.Torso);
|
||||
Limb leftHand = GetLimb(LimbType.LeftHand);
|
||||
Limb rightHand = GetLimb(LimbType.RightHand);
|
||||
|
||||
Limb leftFoot = GetLimb(LimbType.LeftFoot);
|
||||
Limb rightFoot = GetLimb(LimbType.RightFoot);
|
||||
Limb leftLeg = GetLimb(LimbType.LeftLeg);
|
||||
Limb rightLeg = GetLimb(LimbType.RightLeg);
|
||||
|
||||
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 = MathUtils.SmoothStep(movement, TargetMovement, 0.3f);
|
||||
|
||||
//dont try to move upwards if head is already out of water
|
||||
if (surfaceLimiter > 1.0f && TargetMovement.Y > 0.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);
|
||||
|
||||
//float legCorrection = MathUtils.GetShortestAngle(leftLeg.Rotation, torso.body.Rotation);
|
||||
|
||||
//leftLeg.body.ApplyTorque(legCorrection);
|
||||
|
||||
//legCorrection = MathUtils.GetShortestAngle(rightLeg.Rotation, torso.body.Rotation);
|
||||
|
||||
//rightLeg.body.ApplyTorque(legCorrection);
|
||||
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 && TargetMovement.Y>0)
|
||||
{
|
||||
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 = MathUtils.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.5f + movement.Y * 0.1f - ladderSimPos.Y);
|
||||
|
||||
MoveLimb(leftHand,
|
||||
new Vector2(handPos.X,
|
||||
MathUtils.Round(handPos.Y - stepHeight, stepHeight * 2.0f) + stepHeight + ladderSimPos.Y),
|
||||
5.2f);
|
||||
|
||||
MoveLimb(rightHand,
|
||||
new Vector2(handPos.X,
|
||||
MathUtils.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.05f,
|
||||
head.SimPosition.Y - stepHeight * 2.7f - ladderSimPos.Y);
|
||||
|
||||
//if (movement.Y < 0) footPos.Y += 0.05f;
|
||||
|
||||
MoveLimb(leftFoot,
|
||||
new Vector2(footPos.X,
|
||||
MathUtils.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight + ladderSimPos.Y),
|
||||
15.5f, true);
|
||||
|
||||
MoveLimb(rightFoot,
|
||||
new Vector2(footPos.X,
|
||||
MathUtils.Round(footPos.Y, stepHeight * 2.0f) + ladderSimPos.Y),
|
||||
15.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 * -8.0f);
|
||||
rightLeg.body.ApplyTorque(Dir * -8.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.4f) * movementFactor;
|
||||
torso.body.ApplyForce(climbForce * 40.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*1.5f) ||
|
||||
(TargetMovement.Y > 0.0f && handPos.Y > 0.3f))
|
||||
{
|
||||
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 = MathUtils.VectorToAngle(new Vector2(diff.X, diff.Y * Dir)) - torso.body.Rotation * Dir;
|
||||
holdAngle = MathHelper.Clamp(MathUtils.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 = limbJoints[2].WorldAnchorA;
|
||||
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(MathUtils.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 = 37.0f;
|
||||
|
||||
//arm length
|
||||
float b = 28.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 = MathUtils.VectorToAngle((transformedHoldPos + transformedHandlePos[i]) - shoulderPos)+MathHelper.PiOver2;
|
||||
|
||||
float armAngle = MathUtils.SolveTriangleSSS(a, b, c);
|
||||
float handAngle = MathUtils.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),
|
||||
MathUtils.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,
|
||||
MathUtils.WrapAnglePi(l.body.Rotation * (l.DoesFlip ? -1.0f : 1.0f)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
129
Subsurface/Source/Characters/Jobs/Job.cs
Normal file
129
Subsurface/Source/Characters/Jobs/Job.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class Skill
|
||||
{
|
||||
string name;
|
||||
int level;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public int Level
|
||||
{
|
||||
get { return level; }
|
||||
}
|
||||
|
||||
public Skill(string name, int level)
|
||||
{
|
||||
this.name = name;
|
||||
this.level = level;
|
||||
}
|
||||
}
|
||||
|
||||
class Job
|
||||
{
|
||||
|
||||
private JobPrefab prefab;
|
||||
|
||||
private Dictionary<string, Skill> skills;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return prefab.Name; }
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get { return prefab.Description; }
|
||||
}
|
||||
|
||||
public JobPrefab Prefab
|
||||
{
|
||||
get { return prefab; }
|
||||
}
|
||||
|
||||
public List<string> SpawnItemNames
|
||||
{
|
||||
get { return prefab.ItemNames; }
|
||||
}
|
||||
|
||||
public List<Skill> Skills
|
||||
{
|
||||
get { return skills.Values.ToList(); }
|
||||
}
|
||||
|
||||
//public List<float> SkillLevels
|
||||
//{
|
||||
// get { return skills.Values.ToList(); }
|
||||
//}
|
||||
|
||||
public Job(JobPrefab jobPrefab)
|
||||
{
|
||||
prefab = jobPrefab;
|
||||
|
||||
skills = new Dictionary<string, Skill>();
|
||||
foreach (KeyValuePair<string, Vector2> skill in prefab.Skills)
|
||||
{
|
||||
skills.Add(
|
||||
skill.Key,
|
||||
new Skill( skill.Key, (int)Rand.Range(skill.Value.X, skill.Value.Y, false)));
|
||||
}
|
||||
}
|
||||
|
||||
public Job(XElement element)
|
||||
{
|
||||
string name = ToolBox.GetAttributeString(element, "name", "").ToLower();
|
||||
prefab = JobPrefab.List.Find(jp => jp.Name.ToLower() == name);
|
||||
|
||||
skills = new Dictionary<string, Skill>();
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLower() != "skill") continue;
|
||||
string skillName = ToolBox.GetAttributeString(subElement, "name", "");
|
||||
if (string.IsNullOrEmpty(name)) continue;
|
||||
skills.Add(
|
||||
skillName,
|
||||
new Skill(skillName, ToolBox.GetAttributeInt(subElement, "level", 0)));
|
||||
}
|
||||
}
|
||||
|
||||
public static Job Random()
|
||||
{
|
||||
JobPrefab prefab = JobPrefab.List[Rand.Int(JobPrefab.List.Count-1, false)];
|
||||
|
||||
return new Job(prefab);
|
||||
}
|
||||
|
||||
public int GetSkillLevel(string skillName)
|
||||
{
|
||||
Skill skill = null;
|
||||
skills.TryGetValue(skillName, out skill);
|
||||
|
||||
return (skill==null) ? 0 : skill.Level;
|
||||
}
|
||||
|
||||
public virtual XElement Save(XElement parentElement)
|
||||
{
|
||||
XElement jobElement = new XElement("job");
|
||||
|
||||
jobElement.Add(new XAttribute("name", Name));
|
||||
|
||||
foreach (KeyValuePair<string, Skill> skill in skills)
|
||||
{
|
||||
jobElement.Add(new XElement("skill", new XAttribute("name", skill.Value.Name), new XAttribute("level", skill.Value.Level)));
|
||||
}
|
||||
|
||||
parentElement.Add(jobElement);
|
||||
return jobElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
129
Subsurface/Source/Characters/Jobs/JobPrefab.cs
Normal file
129
Subsurface/Source/Characters/Jobs/JobPrefab.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class JobPrefab
|
||||
{
|
||||
public static List<JobPrefab> List;
|
||||
|
||||
string name;
|
||||
string description;
|
||||
|
||||
//how many crew members can have the job (only one captain etc)
|
||||
private int maxNumber;
|
||||
|
||||
//how many crew members are REQUIRED to have a job
|
||||
//(i.e. if one captain is required, one captain is chosen even if all the players have set captain to lowest preference)
|
||||
private int minNumber;
|
||||
|
||||
//if set to true, a client that has chosen this as their preferred job will get it no matter what
|
||||
public bool AllowAlways
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
//names of the items the character spawns with
|
||||
public List<string> ItemNames;
|
||||
|
||||
public Dictionary<string, Vector2> Skills;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get { return description; }
|
||||
}
|
||||
|
||||
public int MaxNumber
|
||||
{
|
||||
get { return maxNumber; }
|
||||
}
|
||||
|
||||
public int MinNumber
|
||||
{
|
||||
get { return minNumber; }
|
||||
}
|
||||
|
||||
public JobPrefab(XElement element)
|
||||
{
|
||||
name = element.Name.ToString();
|
||||
|
||||
description = ToolBox.GetAttributeString(element, "description", "");
|
||||
|
||||
minNumber = ToolBox.GetAttributeInt(element, "minnumber", 0);
|
||||
maxNumber = ToolBox.GetAttributeInt(element, "maxnumber", 10);
|
||||
|
||||
AllowAlways = ToolBox.GetAttributeBool(element, "allowalways", false);
|
||||
|
||||
ItemNames = new List<string>();
|
||||
|
||||
Skills = new Dictionary<string, Vector2>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLower())
|
||||
{
|
||||
case "item":
|
||||
string itemName = ToolBox.GetAttributeString(subElement, "name", "");
|
||||
if (!string.IsNullOrEmpty(itemName)) ItemNames.Add(itemName);
|
||||
break;
|
||||
case "skills":
|
||||
LoadSkills(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static JobPrefab Random()
|
||||
{
|
||||
return List[Rand.Int(List.Count)];
|
||||
}
|
||||
|
||||
private void LoadSkills(XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
string skillName = ToolBox.GetAttributeString(subElement, "name", "");
|
||||
|
||||
if (string.IsNullOrEmpty(skillName) || Skills.ContainsKey(skillName)) continue;
|
||||
|
||||
var levelString = ToolBox.GetAttributeString(subElement, "level", "");
|
||||
if (levelString.Contains(","))
|
||||
{
|
||||
Skills.Add(skillName, ToolBox.ParseToVector2(levelString, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
float skillLevel = float.Parse(levelString, CultureInfo.InvariantCulture);
|
||||
Skills.Add(skillName, new Vector2(skillLevel, skillLevel));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void LoadAll(List<string> filePaths)
|
||||
{
|
||||
List = new List<JobPrefab>();
|
||||
|
||||
foreach (string filePath in filePaths)
|
||||
{
|
||||
XDocument doc = ToolBox.TryLoadXml(filePath);
|
||||
if (doc == null) return;
|
||||
|
||||
foreach (XElement element in doc.Root.Elements())
|
||||
{
|
||||
JobPrefab job = new JobPrefab(element);
|
||||
List.Add(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
458
Subsurface/Source/Characters/Limb.cs
Normal file
458
Subsurface/Source/Characters/Limb.cs
Normal file
@@ -0,0 +1,458 @@
|
||||
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 readonly float maxHealth;
|
||||
//private float damage;
|
||||
//private float bleeding;
|
||||
|
||||
public readonly float impactTolerance;
|
||||
|
||||
private readonly Vector2 armorSector;
|
||||
private readonly float armorValue;
|
||||
|
||||
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 Position
|
||||
{
|
||||
get { return ConvertUnits.ToDisplayUnits(body.Position); }
|
||||
}
|
||||
|
||||
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 MaxHealth
|
||||
//{
|
||||
// get { return maxHealth; }
|
||||
//}
|
||||
|
||||
//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", 8.0f);
|
||||
|
||||
body.UserData = this;
|
||||
|
||||
refJointIndex = -1;
|
||||
|
||||
if (element.Attribute("type") != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
type = (LimbType)Enum.Parse(typeof(LimbType), element.Attribute("type").Value, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
type = LimbType.None;
|
||||
DebugConsole.ThrowError("Error in "+element+"! ''"+element.Attribute("type").Value+"'' is not a valid limb type");
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
armorSector = ToolBox.GetAttributeVector2(element, "armorsector", Vector2.Zero);
|
||||
armorSector.X = MathHelper.ToRadians(armorSector.X);
|
||||
armorSector.Y = MathHelper.ToRadians(armorSector.Y);
|
||||
|
||||
armorValue = Math.Max(ToolBox.GetAttributeFloat(element, "armor", 1.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" : "");
|
||||
spritePath = spritePath.Replace("[HEADID]", character.Info.HeadSpriteId.ToString());
|
||||
}
|
||||
|
||||
|
||||
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 AttackResult AddDamage(Vector2 position, DamageType damageType, float amount, float bleedingAmount, bool playSound)
|
||||
{
|
||||
DamageSoundType damageSoundType = (damageType == DamageType.Blunt) ? DamageSoundType.LimbBlunt : DamageSoundType.LimbSlash;
|
||||
|
||||
bool hitArmor = false;
|
||||
if (armorSector != Vector2.Zero)
|
||||
{
|
||||
float rot = body.Rotation;
|
||||
if (Dir == -1) rot -= MathHelper.Pi;
|
||||
|
||||
Vector2 armorLimits = new Vector2(rot-armorSector.X*Dir, rot-armorSector.Y*Dir);
|
||||
|
||||
float mid = (armorLimits.X + armorLimits.Y) / 2.0f;
|
||||
|
||||
float angleDiff = MathUtils.GetShortestAngle(MathUtils.VectorToAngle(position - SimPosition), mid);
|
||||
|
||||
if (Math.Abs(angleDiff) < (armorSector.Y - armorSector.X) / 2.0f)
|
||||
{
|
||||
hitArmor = true;
|
||||
damageSoundType = DamageSoundType.LimbArmor;
|
||||
amount /= armorValue;
|
||||
bleedingAmount /= armorValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (playSound)
|
||||
{
|
||||
AmbientSoundManager.PlayDamageSound(damageSoundType, amount, position);
|
||||
}
|
||||
|
||||
//Bleeding += bleedingAmount;
|
||||
//Damage += amount;
|
||||
|
||||
float bloodAmount = hitArmor ? 0 : (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 = SimPosition - position;
|
||||
if (particleVel != Vector2.Zero) particleVel = Vector2.Normalize(particleVel);
|
||||
|
||||
Game1.ParticleManager.CreateParticle("blood",
|
||||
SimPosition,
|
||||
particleVel * Rand.Range(1.0f, 3.0f));
|
||||
}
|
||||
|
||||
for (int i = 0; i < bloodAmount / 2; i++)
|
||||
{
|
||||
Game1.ParticleManager.CreateParticle("waterblood", SimPosition, Vector2.Zero);
|
||||
}
|
||||
|
||||
return new AttackResult(amount, bleedingAmount, hitArmor);
|
||||
}
|
||||
|
||||
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 (MathUtils.RandomFloat(0.0f, 1000.0f) < Bleeding)
|
||||
//{
|
||||
// Game1.particleManager.CreateParticle(
|
||||
// !inWater ? "blood" : "waterblood",
|
||||
// SimPosition, Vector2.Zero);
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
Color color = Color.White;// 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 (!Game1.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
673
Subsurface/Source/Characters/Ragdoll.cs
Normal file
673
Subsurface/Source/Characters/Ragdoll.cs
Normal file
@@ -0,0 +1,673 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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<Ragdoll> list = new List<Ragdoll>();
|
||||
|
||||
protected Hull currentHull;
|
||||
|
||||
public Limb[] limbs;
|
||||
private Dictionary<LimbType, Limb> 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 float Mass
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Vector2 TargetMovement
|
||||
{
|
||||
get
|
||||
{
|
||||
return (correctionMovement == Vector2.Zero) ? targetMovement : correctionMovement;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (float.IsNaN(value.X) || float.IsNaN(value.Y))
|
||||
{
|
||||
targetMovement = Vector2.Zero;
|
||||
return;
|
||||
}
|
||||
targetMovement.X = MathHelper.Clamp(value.X, -3.0f, 3.0f);
|
||||
targetMovement.Y = MathHelper.Clamp(value.Y, -3.0f, 3.0f);
|
||||
}
|
||||
}
|
||||
|
||||
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<LimbType, Limb>();
|
||||
|
||||
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;
|
||||
Mass += limb.Mass;
|
||||
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.Position.Y < structure.Rect.Y) return false;
|
||||
}
|
||||
else if (structure.StairDirection!=Direction.None)
|
||||
{
|
||||
if (inWater || !(targetMovement.Y>Math.Abs(targetMovement.X/2.0f)) && lowestLimb.Position.Y < structure.Rect.Y - structure.Rect.Height + 50.0f)
|
||||
{
|
||||
stairs = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetMovement.Y >= 0.0f && lowestLimb.SimPosition.Y > ConvertUnits.ToSimUnits(structure.Rect.Y - Submarine.GridSize.Y * 8.0f))
|
||||
{
|
||||
stairs = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
Limb limb = f1.Body.UserData as Limb;
|
||||
if (limb != null && (limb.type == LimbType.LeftFoot || limb.type == LimbType.RightFoot))
|
||||
{
|
||||
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;
|
||||
|
||||
Vector2 avgVelocity = Vector2.Zero;
|
||||
foreach (Limb limb in limbs)
|
||||
{
|
||||
avgVelocity += limb.LinearVelocity;
|
||||
}
|
||||
|
||||
avgVelocity = avgVelocity / limbs.Count();
|
||||
|
||||
float impact = Vector2.Dot((f1.Body.LinearVelocity + avgVelocity) / 2.0f, -normal);
|
||||
|
||||
if (Game1.Server != null) impact = impact / 2.0f;
|
||||
|
||||
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), impact * 100.0f, l.body.FarseerBody);
|
||||
|
||||
if (impact > l.impactTolerance)
|
||||
{
|
||||
character.Health -= (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);
|
||||
}
|
||||
|
||||
if (!Game1.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);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true);
|
||||
|
||||
pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorB);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pullFromCenter">if false, force is applied to the position of pullJoint</param>
|
||||
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 (currentHull.Volume>currentHull.FullVolume*0.95f || ConvertUnits.ToSimUnits(currentHull.Surface)-floorY> HeadPosition*0.95f)
|
||||
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 && Submarine.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
|
||||
Subsurface.Particles.Particle splash = Game1.ParticleManager.CreateParticle("watersplash",
|
||||
new Vector2(limb.SimPosition.X, ConvertUnits.ToSimUnits(limbHull.Surface)),
|
||||
new Vector2(0.0f, Math.Abs(-limb.LinearVelocity.Y * 0.1f)),
|
||||
0.0f);
|
||||
|
||||
if (splash != null) splash.yLimits = ConvertUnits.ToSimUnits(
|
||||
new Vector2(
|
||||
limbHull.Rect.Y,
|
||||
limbHull.Rect.Y - limbHull.Rect.Height));
|
||||
|
||||
Game1.ParticleManager.CreateParticle("bubbles",
|
||||
new Vector2(limb.SimPosition.X, ConvertUnits.ToSimUnits(limbHull.Surface)),
|
||||
limb.LinearVelocity*0.001f,
|
||||
0.0f);
|
||||
|
||||
|
||||
|
||||
//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)/1000.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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
248
Subsurface/Source/Characters/StatusEffect.cs
Normal file
248
Subsurface/Source/Characters/StatusEffect.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class StatusEffect
|
||||
{
|
||||
[Flags]
|
||||
public enum TargetType
|
||||
{
|
||||
This = 1, Parent = 2, Character = 4, Contained = 8, Nearby = 16, UseTarget=32
|
||||
}
|
||||
|
||||
private TargetType targetTypes;
|
||||
private string[] targetNames;
|
||||
|
||||
public string[] propertyNames;
|
||||
private object[] propertyEffects;
|
||||
|
||||
private bool disableDeltaTime;
|
||||
|
||||
private string[] onContainingNames;
|
||||
|
||||
public readonly ActionType type;
|
||||
|
||||
private Explosion explosion;
|
||||
|
||||
private Sound sound;
|
||||
|
||||
public TargetType Targets
|
||||
{
|
||||
get { return targetTypes; }
|
||||
}
|
||||
|
||||
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<XAttribute> attributes = element.Attributes();
|
||||
List<XAttribute> propertyAttributes = new List<XAttribute>();
|
||||
|
||||
disableDeltaTime = ToolBox.GetAttributeBool(element, "disabledeltatime", false);
|
||||
|
||||
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)
|
||||
{
|
||||
targetTypes |= (TargetType)Enum.Parse(typeof(TargetType), 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)
|
||||
//{
|
||||
// if (this.type == type) Apply(deltaTime, character, item);
|
||||
//}
|
||||
|
||||
public virtual void Apply(ActionType type, float deltaTime, Entity entity, IPropertyObject target)
|
||||
{
|
||||
if (targetNames != null && !targetNames.Contains(target.Name)) return;
|
||||
|
||||
List<IPropertyObject> targets = new List<IPropertyObject>();
|
||||
targets.Add(target);
|
||||
|
||||
if (this.type == type) Apply(deltaTime, entity, targets);
|
||||
}
|
||||
|
||||
public virtual void Apply(ActionType type, float deltaTime, Entity entity, List<IPropertyObject> targets)
|
||||
{
|
||||
if (this.type == type) Apply(deltaTime, entity, targets);
|
||||
}
|
||||
|
||||
protected virtual void Apply(float deltaTime, Entity entity, List<IPropertyObject> targets)
|
||||
{
|
||||
if (explosion != null) explosion.Explode(entity.SimPosition);
|
||||
|
||||
if (sound != null) sound.Play(1.0f, 1000.0f, ConvertUnits.ToDisplayUnits(entity.SimPosition));
|
||||
|
||||
for (int i = 0; i < propertyNames.Count(); i++)
|
||||
{
|
||||
ObjectProperty property;
|
||||
foreach (IPropertyObject target in targets)
|
||||
{
|
||||
if (targetNames!=null && !targetNames.Contains(target.Name)) continue;
|
||||
if (!target.ObjectProperties.TryGetValue(propertyNames[i], out property)) continue;
|
||||
|
||||
ApplyToProperty(property, propertyEffects[i], deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//protected virtual void Apply(float deltaTime, Character character, Item item)
|
||||
//{
|
||||
// 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)
|
||||
{
|
||||
if (disableDeltaTime) deltaTime = 1.0f;
|
||||
|
||||
Type type = value.GetType();
|
||||
if (type == typeof(float))
|
||||
{
|
||||
property.TrySetValue((float)property.GetValue() + (float)value * deltaTime);
|
||||
}
|
||||
else 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
216
Subsurface/Source/ContentPackage.cs
Normal file
216
Subsurface/Source/ContentPackage.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
public enum ContentType
|
||||
{
|
||||
None, Jobs, Item, Character, Structure, Executable
|
||||
}
|
||||
|
||||
public class ContentPackage
|
||||
{
|
||||
public static List<ContentPackage> list = new List<ContentPackage>();
|
||||
|
||||
|
||||
string name;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public string Path
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private Md5Hash md5Hash;
|
||||
|
||||
public Md5Hash MD5hash
|
||||
{
|
||||
get
|
||||
{
|
||||
if (md5Hash == null) CalculateHash();
|
||||
return md5Hash;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ContentFile> files;
|
||||
|
||||
private ContentPackage()
|
||||
{
|
||||
files = new List<ContentFile>();
|
||||
}
|
||||
|
||||
public ContentPackage(string filePath)
|
||||
: this()
|
||||
{
|
||||
XDocument doc = ToolBox.TryLoadXml(filePath);
|
||||
|
||||
if (doc==null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't load content package ''"+filePath+"''!");
|
||||
return;
|
||||
}
|
||||
|
||||
Path = filePath;
|
||||
|
||||
name = ToolBox.GetAttributeString(doc.Root, "name", "");
|
||||
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
{
|
||||
ContentType type = (ContentType)Enum.Parse(typeof(ContentType), subElement.Name.ToString(), true);
|
||||
files.Add(new ContentFile(ToolBox.GetAttributeString(subElement, "file", ""), type));
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public static ContentPackage CreatePackage(string name)
|
||||
{
|
||||
ContentPackage newPackage = new ContentPackage();
|
||||
newPackage.name = name;
|
||||
list.Add(newPackage);
|
||||
|
||||
return newPackage;
|
||||
}
|
||||
|
||||
public ContentFile AddFile(string path, ContentType type)
|
||||
{
|
||||
if (files.Find(file => file.path == path && file.type == type) != null) return null;
|
||||
|
||||
ContentFile cf = new ContentFile(path, type);
|
||||
files.Add(cf);
|
||||
|
||||
return cf;
|
||||
}
|
||||
|
||||
public void RemoveFile(ContentFile file)
|
||||
{
|
||||
files.Remove(file);
|
||||
}
|
||||
|
||||
public void Save(string filePath)
|
||||
{
|
||||
XDocument doc = new XDocument();
|
||||
doc.Add(new XElement("contentpackage",
|
||||
new XAttribute("name", name),
|
||||
new XAttribute("path", Path)));
|
||||
|
||||
//doc.Root.Add(
|
||||
// new XElement("jobs", new XAttribute("file", JobFile)),
|
||||
// new XElement("structures", new XAttribute("file", StructureFile)));
|
||||
|
||||
foreach (ContentFile file in files)
|
||||
{
|
||||
doc.Root.Add(new XElement(file.type.ToString(), new XAttribute("file", file.path)));
|
||||
}
|
||||
|
||||
//foreach (string itemFile in itemFiles)
|
||||
//{
|
||||
// doc.Root.Add(new XElement("item", new XAttribute("file", itemFile)));
|
||||
//}
|
||||
doc.Save(filePath+"//"+name+".xml");
|
||||
}
|
||||
|
||||
private void CalculateHash()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
List<byte[]> hashes = new List<byte[]>();
|
||||
|
||||
//foreach (ContentFile file in files)
|
||||
//{
|
||||
// if (file.path.EndsWith(".xml", true, System.Globalization.CultureInfo.InvariantCulture))
|
||||
// {
|
||||
// XDocument doc = ToolBox.TryLoadXml(file.path);
|
||||
// sb.Append(doc.ToString());
|
||||
// }
|
||||
//}
|
||||
var md5 = MD5.Create();
|
||||
foreach (ContentFile file in files)
|
||||
{
|
||||
using (var stream = File.OpenRead(file.path))
|
||||
{
|
||||
hashes.Add(md5.ComputeHash(stream));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//string str = sb.ToString();
|
||||
byte[] bytes = new byte[hashes.Count()*16];
|
||||
for (int i = 0; i < hashes.Count; i++ )
|
||||
{
|
||||
hashes[i].CopyTo(bytes, i*16);
|
||||
}
|
||||
//System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
|
||||
|
||||
md5Hash = new Md5Hash(bytes);
|
||||
}
|
||||
|
||||
public List<string> GetFilesOfType(ContentType type)
|
||||
{
|
||||
List<ContentFile> contentFiles = files.FindAll(f => f.type == type);
|
||||
|
||||
List<string> filePaths = new List<string>();
|
||||
foreach (ContentFile contentFile in contentFiles)
|
||||
{
|
||||
filePaths.Add(contentFile.path);
|
||||
}
|
||||
return filePaths;
|
||||
}
|
||||
public static void LoadAll(string folder)
|
||||
{
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string[] files = Directory.GetFiles(folder, "*.xml");
|
||||
|
||||
foreach (string filePath in files)
|
||||
{
|
||||
ContentPackage package = new ContentPackage(filePath);
|
||||
list.Add(package);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ContentFile
|
||||
{
|
||||
public string path;
|
||||
public ContentType type;
|
||||
|
||||
public ContentFile(string path, ContentType type)
|
||||
{
|
||||
Directory.GetCurrentDirectory();
|
||||
//Path.get
|
||||
this.path = path;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
45
Subsurface/Source/CoroutineManager.cs
Normal file
45
Subsurface/Source/CoroutineManager.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
enum Status
|
||||
{
|
||||
Running, Success, Failure
|
||||
}
|
||||
|
||||
// Keeps track of all running coroutines, and runs them till the end.
|
||||
static class CoroutineManager
|
||||
{
|
||||
static List<IEnumerator<Status>> Coroutines = new List<IEnumerator<Status>>();
|
||||
|
||||
// Starting a coroutine just means adding an enumerator to the list.
|
||||
// You might also want to be able to stop coroutines or delete them,
|
||||
// which might mean putting them into a dictionary
|
||||
public static void StartCoroutine(IEnumerable<Status> func)
|
||||
{
|
||||
Coroutines.Add(func.GetEnumerator());
|
||||
}
|
||||
|
||||
// Updating just means stepping through all the coroutines
|
||||
public static void Update()
|
||||
{
|
||||
for (int i = Coroutines.Count-1; i>=0; i--)
|
||||
{
|
||||
Coroutines[i].MoveNext();
|
||||
|
||||
switch (Coroutines[i].Current)
|
||||
{
|
||||
case Status.Success:
|
||||
Coroutines.RemoveAt(i);
|
||||
break;
|
||||
case Status.Failure:
|
||||
DebugConsole.ThrowError("Coroutine ''" + Coroutines[i]+ "'' has failed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
299
Subsurface/Source/DebugConsole.cs
Normal file
299
Subsurface/Source/DebugConsole.cs
Normal file
@@ -0,0 +1,299 @@
|
||||
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<ColoredText> messages = new List<ColoredText>();
|
||||
|
||||
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 (Game1.Client!=null)
|
||||
{
|
||||
ThrowError("Console commands are disabled in multiplayer mode");
|
||||
return;
|
||||
}
|
||||
|
||||
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(SpawnType.Human);
|
||||
Character.Controlled = new Character(Character.HumanConfigFile, (spawnPoint == null) ? Vector2.Zero : spawnPoint.SimPosition);
|
||||
if (Game1.GameSession != null)
|
||||
{
|
||||
SinglePlayerMode mode = Game1.GameSession.gameMode as SinglePlayerMode;
|
||||
if (mode == null) break;
|
||||
mode.crewManager.AddCharacter(Character.Controlled);
|
||||
mode.crewManager.SelectCharacter(Character.Controlled);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WayPoint spawnPoint = WayPoint.GetRandom(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.NetworkMember = 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.NetworkMember = 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 "freecamera":
|
||||
case "freecam":
|
||||
Character.Controlled = null;
|
||||
Game1.GameScreen.Cam.TargetPos = Vector2.Zero;
|
||||
break;
|
||||
case "editwater":
|
||||
case "water":
|
||||
if (Game1.Client== null)
|
||||
{
|
||||
Hull.EditWater = !Hull.EditWater;
|
||||
}
|
||||
break;
|
||||
case "generatelevel":
|
||||
Game1.Level = new Level("asdf", 50.0f, 500,500, 50);
|
||||
Game1.Level.Generate(100.0f);
|
||||
break;
|
||||
case "fixitems":
|
||||
foreach (Item it in Item.itemList)
|
||||
{
|
||||
it.Condition = 100.0f;
|
||||
}
|
||||
break;
|
||||
case "shake":
|
||||
Game1.GameScreen.Cam.Shake = 10.0f;
|
||||
break;
|
||||
case "losenabled":
|
||||
case "los":
|
||||
case "drawlos":
|
||||
Game1.LightManager.LosEnabled = !Game1.LightManager.LosEnabled;
|
||||
break;
|
||||
case "lighting":
|
||||
case "lightingenabled":
|
||||
case "light":
|
||||
case "lights":
|
||||
Game1.LightManager.LightingEnabled = !Game1.LightManager.LightingEnabled;
|
||||
break;
|
||||
case "oxygen":
|
||||
case "air":
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
hull.OxygenPercentage = 100.0f;
|
||||
}
|
||||
break;
|
||||
case "lobbyscreen":
|
||||
case "lobby":
|
||||
Game1.LobbyScreen.Select();
|
||||
break;
|
||||
case "savemap":
|
||||
if (commands.Length < 2) break;
|
||||
Submarine.SaveCurrent("Content/SavedMaps/" + string.Join(" ", commands.Skip(1)));
|
||||
NewMessage("map saved", Color.Green);
|
||||
break;
|
||||
case "loadmap":
|
||||
if (commands.Length < 2) break;
|
||||
Submarine.Load("Content/SavedMaps/" + string.Join(" ", commands.Skip(1)));
|
||||
break;
|
||||
//case "savegame":
|
||||
// SaveUtil.SaveGame();
|
||||
// break;
|
||||
//case "loadgame":
|
||||
// SaveUtil.LoadGame(SaveUtil.SaveFolder + "save");
|
||||
// break;
|
||||
case "messagebox":
|
||||
if (commands.Length < 3) break;
|
||||
new GUIMessageBox(commands[1], commands[2]);
|
||||
break;
|
||||
case "debugdraw":
|
||||
//Hull.DebugDraw = !Hull.DebugDraw;
|
||||
//Ragdoll.DebugDraw = !Ragdoll.DebugDraw;
|
||||
Game1.DebugDraw = !Game1.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 + "}";
|
||||
System.Diagnostics.Debug.WriteLine(error);
|
||||
NewMessage(error, Color.Red);
|
||||
isOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
208
Subsurface/Source/EventInput/EventInput.cs
Normal file
208
Subsurface/Source/EventInput/EventInput.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Event raised when a character has been entered.
|
||||
/// </summary>
|
||||
public static event CharEnteredHandler CharEntered;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a key has been pressed down. May fire multiple times due to keyboard repeat.
|
||||
/// </summary>
|
||||
public static event KeyEventHandler KeyDown;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when a key has been released.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the TextInput with the given GameWindow.
|
||||
/// </summary>
|
||||
/// <param name="window">The XNA window to which text input should be linked.</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
85
Subsurface/Source/EventInput/KeyboardDispatcher.cs
Normal file
85
Subsurface/Source/EventInput/KeyboardDispatcher.cs
Normal file
@@ -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() : "";
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Subsurface/Source/Events/MonsterEvent.cs
Normal file
61
Subsurface/Source/Events/MonsterEvent.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
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 = Math.Max(ToolBox.GetAttributeInt(element, "maxamount", 1),minAmount);
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
WayPoint randomWayPoint = WayPoint.GetRandom(SpawnType.Enemy);
|
||||
|
||||
int amount = Rand.Range(minAmount, maxAmount, false);
|
||||
|
||||
monsters = new Character[amount];
|
||||
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
Vector2 position = (randomWayPoint == null) ? Vector2.Zero : randomWayPoint.SimPosition;
|
||||
position.X += Rand.Range(-0.5f, 0.5f);
|
||||
position.Y += Rand.Range(-0.5f, 0.5f);
|
||||
monsters[i] = new Character(characterFile, position);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Subsurface/Source/Events/PropertyTask.cs
Normal file
30
Subsurface/Source/Events/PropertyTask.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace Subsurface
|
||||
{
|
||||
class PropertyTask : Task
|
||||
{
|
||||
Item item;
|
||||
|
||||
|
||||
public delegate bool IsFinishedHandler();
|
||||
private IsFinishedHandler IsFinishedChecker;
|
||||
|
||||
public PropertyTask(Item item, IsFinishedHandler isFinished, float priority, string name)
|
||||
: base(priority, name)
|
||||
{
|
||||
if (taskManager == null) return;
|
||||
|
||||
this.item = item;
|
||||
IsFinishedChecker = isFinished;
|
||||
|
||||
taskManager.TaskStarted(this);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (IsFinishedChecker())
|
||||
{
|
||||
Finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Subsurface/Source/Events/Quests/MonsterQuest.cs
Normal file
53
Subsurface/Source/Events/Quests/MonsterQuest.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
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 MonsterQuest : Quest
|
||||
{
|
||||
//string monsterName;
|
||||
|
||||
string monsterFile;
|
||||
|
||||
Character monster;
|
||||
|
||||
public override Vector2 RadarPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return monster.Position;
|
||||
}
|
||||
}
|
||||
|
||||
public MonsterQuest(XElement element)
|
||||
: base(element)
|
||||
{
|
||||
monsterFile = ToolBox.GetAttributeString(element, "monsterfile", "");
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
{
|
||||
Vector2 position = level.PositionsOfInterest[Rand.Int(level.PositionsOfInterest.Count)];
|
||||
|
||||
monster = new Character(monsterFile, ConvertUnits.ToSimUnits(position+level.Position));
|
||||
}
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (!monster.IsDead)
|
||||
{
|
||||
new GUIMessageBox("Quest failed", failureMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
GiveReward();
|
||||
|
||||
completed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
171
Subsurface/Source/Events/Quests/Quest.cs
Normal file
171
Subsurface/Source/Events/Quests/Quest.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class Quest
|
||||
{
|
||||
private static List<Quest> list = new List<Quest>();
|
||||
|
||||
private static string configFile = "Content/Quests.xml";
|
||||
|
||||
private string name;
|
||||
|
||||
private string description;
|
||||
|
||||
protected bool completed;
|
||||
|
||||
protected string successMessage;
|
||||
protected string failureMessage;
|
||||
|
||||
protected string radarLabel;
|
||||
|
||||
private int reward;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get { return description; }
|
||||
}
|
||||
|
||||
public int Reward
|
||||
{
|
||||
get { return reward; }
|
||||
}
|
||||
|
||||
public bool Completed
|
||||
{
|
||||
get { return completed; }
|
||||
}
|
||||
|
||||
public virtual string RadarLabel
|
||||
{
|
||||
get { return radarLabel; }
|
||||
}
|
||||
|
||||
public virtual Vector2 RadarPosition
|
||||
{
|
||||
get { return Vector2.Zero; }
|
||||
}
|
||||
|
||||
public Quest(XElement element)
|
||||
{
|
||||
name = ToolBox.GetAttributeString(element, "name", "");
|
||||
|
||||
description = ToolBox.GetAttributeString(element, "description", "");
|
||||
|
||||
reward = ToolBox.GetAttributeInt(element, "reward", 1);
|
||||
|
||||
successMessage = ToolBox.GetAttributeString(element, "successmessage", "");
|
||||
failureMessage = ToolBox.GetAttributeString(element, "failuremessage", "");
|
||||
|
||||
radarLabel = ToolBox.GetAttributeString(element, "radarlabel", "");
|
||||
}
|
||||
|
||||
public static Quest LoadRandom(Location[] locations, Random rand)
|
||||
{
|
||||
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);
|
||||
|
||||
probabilitySum += eventProbability[i];
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
float randomNumber = (float)rand.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 quest 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 });
|
||||
|
||||
Quest quest = (Quest)instance;
|
||||
|
||||
for (int n = 0; n<2; n++)
|
||||
{
|
||||
quest.description = quest.description.Replace("[location"+(n+1)+"]", locations[n].Name);
|
||||
|
||||
quest.successMessage = quest.successMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
|
||||
quest.failureMessage = quest.failureMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return quest;
|
||||
}
|
||||
|
||||
randomNumber -= eventProbability[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual void Start(Level level)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End the quest and give a reward if it was completed successfully
|
||||
/// </summary>
|
||||
/// <returns>whether the quest was completed or not</returns>
|
||||
public virtual void End()
|
||||
{
|
||||
completed = true;
|
||||
|
||||
GiveReward();
|
||||
}
|
||||
|
||||
public void GiveReward()
|
||||
{
|
||||
var mode = Game1.GameSession.gameMode as SinglePlayerMode;
|
||||
mode.Money += reward;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(successMessage))
|
||||
{
|
||||
new GUIMessageBox("Quest completed", successMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Subsurface/Source/Events/Quests/SalvageQuest.cs
Normal file
59
Subsurface/Source/Events/Quests/SalvageQuest.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class SalvageQuest : Quest
|
||||
{
|
||||
ItemPrefab itemPrefab;
|
||||
|
||||
Item item;
|
||||
|
||||
public override Vector2 RadarPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return item.Position;
|
||||
}
|
||||
}
|
||||
|
||||
public SalvageQuest(XElement element)
|
||||
: base(element)
|
||||
{
|
||||
string itemName = ToolBox.GetAttributeString(element, "itemname", "");
|
||||
|
||||
itemPrefab = ItemPrefab.list.Find(ip => ip.Name == itemName) as ItemPrefab;
|
||||
|
||||
if (itemPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in salvagequest: couldn't find an item prefab with the name "+itemName);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
{
|
||||
Vector2 position = level.PositionsOfInterest[Rand.Int(level.PositionsOfInterest.Count)];
|
||||
|
||||
item = new Item(itemPrefab, position + level.Position);
|
||||
//item.MoveWithLevel = true;
|
||||
}
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (item.CurrentHull == null)
|
||||
{
|
||||
new GUIMessageBox("Quest failed", failureMessage);
|
||||
return;
|
||||
}
|
||||
item.Remove();
|
||||
|
||||
GiveReward();
|
||||
|
||||
completed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Subsurface/Source/Events/RepairTask.cs
Normal file
22
Subsurface/Source/Events/RepairTask.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace Subsurface
|
||||
{
|
||||
class RepairTask : Task
|
||||
{
|
||||
Item item;
|
||||
|
||||
public RepairTask(Item item, float priority, string name)
|
||||
: base(priority, name)
|
||||
{
|
||||
if (taskManager == null) return;
|
||||
|
||||
this.item = item;
|
||||
|
||||
taskManager.TaskStarted(this);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (item.Condition > 50.0f) Finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
202
Subsurface/Source/Events/ScriptedEvent.cs
Normal file
202
Subsurface/Source/Events/ScriptedEvent.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
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<int> previousEvents = new List<int>();
|
||||
|
||||
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 string MusicType
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
MusicType = ToolBox.GetAttributeString(element, "musictype", "default");
|
||||
|
||||
|
||||
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(Random rand)
|
||||
{
|
||||
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)rand.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 an event class of the type ''" + type + "''.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + configFile + "! Could not find 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 = Rand.Range(startTimeMin, startTimeMax, false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
32
Subsurface/Source/Events/ScriptedTask.cs
Normal file
32
Subsurface/Source/Events/ScriptedTask.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Subsurface
|
||||
{
|
||||
class ScriptedTask : Task
|
||||
{
|
||||
private ScriptedEvent scriptedEvent;
|
||||
|
||||
private bool prevStarted;
|
||||
|
||||
public ScriptedTask(ScriptedEvent scriptedEvent)
|
||||
: base(scriptedEvent.Difficulty, scriptedEvent.Name)
|
||||
{
|
||||
if (taskManager == null) return;
|
||||
|
||||
this.musicType = scriptedEvent.MusicType;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Subsurface/Source/Events/Task.cs
Normal file
60
Subsurface/Source/Events/Task.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class Task
|
||||
{
|
||||
|
||||
protected string name;
|
||||
|
||||
private float priority;
|
||||
|
||||
protected string musicType;
|
||||
|
||||
protected TaskManager taskManager;
|
||||
|
||||
protected bool isFinished;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public float Priority
|
||||
{
|
||||
get { return priority; }
|
||||
}
|
||||
|
||||
public string MusicType
|
||||
{
|
||||
get { return musicType; }
|
||||
}
|
||||
|
||||
public bool IsFinished
|
||||
{
|
||||
get { return isFinished; }
|
||||
}
|
||||
|
||||
public Task(float priority, string name)
|
||||
{
|
||||
if (Game1.GameSession==null || Game1.GameSession.taskManager == null) return;
|
||||
|
||||
taskManager = Game1.GameSession.taskManager;
|
||||
musicType = "repair";
|
||||
this.priority = priority;
|
||||
this.name = name;
|
||||
|
||||
taskManager.AddTask(this);
|
||||
}
|
||||
|
||||
public virtual void Update(float deltaTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void Finished()
|
||||
{
|
||||
taskManager.TaskFinished(this);
|
||||
isFinished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
163
Subsurface/Source/Events/TaskManager.cs
Normal file
163
Subsurface/Source/Events/TaskManager.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class TaskManager
|
||||
{
|
||||
const float CriticalPriority = 50.0f;
|
||||
|
||||
private List<Task> tasks;
|
||||
|
||||
private GUIListBox taskListBox;
|
||||
|
||||
public List<Task> Tasks
|
||||
{
|
||||
get { return tasks; }
|
||||
}
|
||||
|
||||
public bool CriticalTasks
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (Task task in tasks)
|
||||
{
|
||||
if (task.Priority >= CriticalPriority) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public TaskManager(GameSession session)
|
||||
{
|
||||
tasks = new List<Task>();
|
||||
|
||||
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(Level level)
|
||||
{
|
||||
CreateScriptedEvents(level);
|
||||
|
||||
taskListBox.ClearChildren();
|
||||
}
|
||||
|
||||
|
||||
public void EndShift()
|
||||
{
|
||||
taskListBox.ClearChildren();
|
||||
tasks.Clear();
|
||||
}
|
||||
|
||||
private void CreateScriptedEvents(Level level)
|
||||
{
|
||||
Random rand = new Random(level.Seed.GetHashCode());
|
||||
|
||||
float totalDifficulty = level.Difficulty;
|
||||
|
||||
int tries = 0;
|
||||
do
|
||||
{
|
||||
ScriptedEvent scriptedEvent = ScriptedEvent.LoadRandom(rand);
|
||||
if (scriptedEvent==null || scriptedEvent.Difficulty > totalDifficulty)
|
||||
{
|
||||
tries++;
|
||||
continue;
|
||||
}
|
||||
|
||||
AddTask(new ScriptedTask(scriptedEvent));
|
||||
totalDifficulty -= scriptedEvent.Difficulty;
|
||||
tries = 0;
|
||||
} while (tries < 5);
|
||||
|
||||
}
|
||||
|
||||
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, null, 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, null, frame);
|
||||
GUITextBlock textBlock = new GUITextBlock(new Rectangle(5, 5, 0, 20), task.Name, Color.Transparent, Color.Black, Alignment.Right, null, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Subsurface/Source/FrameCounter.cs
Normal file
46
Subsurface/Source/FrameCounter.cs
Normal file
@@ -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<double> sampleBuffer = new Queue<double>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
58
Subsurface/Source/GUI/ComponentStyle.cs
Normal file
58
Subsurface/Source/GUI/ComponentStyle.cs
Normal file
@@ -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
|
||||
{
|
||||
class GUIComponentStyle
|
||||
{
|
||||
public readonly Vector4 Padding;
|
||||
|
||||
public readonly Color Color;
|
||||
|
||||
public readonly Color textColor;
|
||||
|
||||
public readonly Color HoverColor;
|
||||
public readonly Color SelectedColor;
|
||||
|
||||
public readonly Color OutlineColor;
|
||||
|
||||
public readonly List<Sprite> Sprites;
|
||||
|
||||
|
||||
public GUIComponentStyle(XElement element)
|
||||
{
|
||||
Sprites = new List<Sprite>();
|
||||
|
||||
Padding = ToolBox.GetAttributeVector4(element, "padding", Vector4.Zero);
|
||||
|
||||
Vector4 colorVector = ToolBox.GetAttributeVector4(element, "color", new Vector4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
Color = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W);
|
||||
|
||||
colorVector = ToolBox.GetAttributeVector4(element, "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(element, "hovercolor", new Vector4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
HoverColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W);
|
||||
|
||||
colorVector = ToolBox.GetAttributeVector4(element, "selectedcolor", new Vector4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
SelectedColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W);
|
||||
|
||||
colorVector = ToolBox.GetAttributeVector4(element, "outlinecolor", new Vector4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
OutlineColor = new Color(colorVector.X, colorVector.Y, colorVector.Z, colorVector.W);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLower())
|
||||
{
|
||||
case "sprite":
|
||||
Sprites.Add(new Sprite(subElement));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
398
Subsurface/Source/GUI/GUI.cs
Normal file
398
Subsurface/Source/GUI/GUI.cs
Normal file
@@ -0,0 +1,398 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
[Flags]
|
||||
public enum Alignment
|
||||
{
|
||||
CenterX = 1, Left = 2, Right = 4, CenterY = 8, Top = 16, Bottom = 32 ,
|
||||
TopRight = (Top | Right), TopLeft = (Top | Left), TopCenter = (CenterX | Top),
|
||||
Center = (CenterX | CenterY),
|
||||
BottomRight = (Bottom | Right), BottomLeft = (Bottom | Left), BottomCenter = (CenterX | Bottom)
|
||||
}
|
||||
|
||||
class GUI
|
||||
{
|
||||
public static GUIStyle style;
|
||||
|
||||
static Texture2D t;
|
||||
public static SpriteFont Font, SmallFont;
|
||||
|
||||
private static GraphicsDevice graphicsDevice;
|
||||
|
||||
private static List<GUIMessage> messages = new List<GUIMessage>();
|
||||
|
||||
private static Sound[] sounds;
|
||||
|
||||
private static bool pauseMenuOpen;
|
||||
private static GUIFrame pauseMenu;
|
||||
|
||||
public static bool PauseMenuOpen
|
||||
{
|
||||
get { return pauseMenuOpen; }
|
||||
}
|
||||
|
||||
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<Color>(
|
||||
new Color[] { Color.White });// fill the texture with white
|
||||
|
||||
style = new GUIStyle("Content/UI/style.xml");
|
||||
}
|
||||
|
||||
public static void TogglePauseMenu()
|
||||
{
|
||||
if (Screen.Selected == Game1.MainMenuScreen) return;
|
||||
|
||||
TogglePauseMenu(null, null);
|
||||
|
||||
if (pauseMenuOpen)
|
||||
{
|
||||
pauseMenu = new GUIFrame(new Rectangle(0,0,200,300), null, Alignment.Center, style);
|
||||
|
||||
int y = 0;
|
||||
var button = new GUIButton(new Rectangle(0, y, 0, 30), "Resume", Alignment.CenterX, GUI.style, pauseMenu);
|
||||
button.OnClicked = TogglePauseMenu;
|
||||
|
||||
y += 60;
|
||||
|
||||
if (Screen.Selected == Game1.GameScreen && Game1.GameSession !=null)
|
||||
{
|
||||
SinglePlayerMode spMode = Game1.GameSession.gameMode as SinglePlayerMode;
|
||||
if (spMode!=null)
|
||||
{
|
||||
button = new GUIButton(new Rectangle(0, y, 0, 30), "Load previous", Alignment.CenterX, GUI.style, pauseMenu);
|
||||
button.OnClicked += TogglePauseMenu;
|
||||
button.OnClicked += Game1.GameSession.LoadPrevious;
|
||||
|
||||
y += 60;
|
||||
}
|
||||
}
|
||||
|
||||
if (Screen.Selected == Game1.LobbyScreen)
|
||||
{
|
||||
SinglePlayerMode spMode = Game1.GameSession.gameMode as SinglePlayerMode;
|
||||
if (spMode != null)
|
||||
{
|
||||
button = new GUIButton(new Rectangle(0, y, 0, 30), "Save & quit", Alignment.CenterX, GUI.style, pauseMenu);
|
||||
button.OnClicked += QuitClicked;
|
||||
button.OnClicked += TogglePauseMenu;
|
||||
button.UserData = "save";
|
||||
|
||||
y += 60;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
button = new GUIButton(new Rectangle(0, y, 0, 30), "Quit", Alignment.CenterX, GUI.style, pauseMenu);
|
||||
button.OnClicked += QuitClicked;
|
||||
button.OnClicked += TogglePauseMenu;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static bool TogglePauseMenu(GUIButton button, object obj)
|
||||
{
|
||||
pauseMenuOpen = !pauseMenuOpen;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool QuitClicked(GUIButton button, object obj)
|
||||
{
|
||||
if (button.UserData as string == "save")
|
||||
{
|
||||
SaveUtil.SaveGame(Game1.GameSession.SaveFile);
|
||||
}
|
||||
|
||||
|
||||
Game1.MainMenuScreen.Select();
|
||||
Game1.MainMenuScreen.SelectTab(null, (int)MainMenuScreen.Tabs.Main);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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<textureHeight-radius; y++)
|
||||
{
|
||||
data[y * textureWidth] = Color.White;
|
||||
data[y * textureWidth + (textureWidth-1)] = Color.White;
|
||||
}
|
||||
|
||||
texture.SetData(data);
|
||||
return texture;
|
||||
}
|
||||
|
||||
public static Texture2D CreateRectangle(int width, int height)
|
||||
{
|
||||
Texture2D texture = new Texture2D(graphicsDevice, width, height);
|
||||
Color[] data = new Color[width * height];
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
data[i] = Color.Transparent;
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
data[y * width] = Color.White;
|
||||
data[y * width + (width-1)] = Color.White;
|
||||
}
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
data[x] = Color.White;
|
||||
data[(height - 1) * width + x] = Color.White;
|
||||
}
|
||||
|
||||
texture.SetData(data);
|
||||
return texture;
|
||||
}
|
||||
|
||||
public static bool DrawButton(SpriteBatch sb, Rectangle rect, string text, bool isHoldable = false)
|
||||
{
|
||||
Color color = new Color(200, 200, 200);
|
||||
|
||||
bool clicked = false;
|
||||
|
||||
if (rect.Contains(PlayerInput.GetMouseState.Position))
|
||||
{
|
||||
clicked = (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed);
|
||||
|
||||
color = clicked ? new Color(100, 100, 100) : new Color(250, 250, 250);
|
||||
|
||||
if (!isHoldable)
|
||||
clicked = PlayerInput.LeftButtonClicked();
|
||||
}
|
||||
|
||||
DrawRectangle(sb, rect, color, true);
|
||||
sb.DrawString(Font, text, new Vector2(rect.X + 10, rect.Y + 10), Color.White);
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
||||
public static void Draw(float deltaTime, SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
spriteBatch.DrawString(Font,
|
||||
"FPS: " + (int)Game1.FrameCounter.AverageFramesPerSecond,
|
||||
new Vector2(10, 10), Color.White);
|
||||
|
||||
if (Game1.DebugDraw)
|
||||
{
|
||||
spriteBatch.DrawString(Font,
|
||||
"Physics: " + Game1.World.UpdateTime
|
||||
+ " - bodies: " + Game1.World.BodyList.Count
|
||||
+ "Camera pos: " + Game1.GameScreen.Cam.Position,
|
||||
new Vector2(10, 30), Color.White);
|
||||
}
|
||||
|
||||
|
||||
if (Character.Controlled != null && cam!=null) Character.Controlled.DrawHud(spriteBatch, cam);
|
||||
if (Game1.NetworkMember != null) Game1.NetworkMember.Draw(spriteBatch);
|
||||
|
||||
DrawMessages(spriteBatch, (float)deltaTime);
|
||||
|
||||
if (GUIMessageBox.messageBoxes.Count>0)
|
||||
{
|
||||
var messageBox = GUIMessageBox.messageBoxes.Peek();
|
||||
if (messageBox != null) messageBox.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
if (pauseMenuOpen)
|
||||
{
|
||||
pauseMenu.Update(1.0f);
|
||||
pauseMenu.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
DebugConsole.Draw(spriteBatch);
|
||||
|
||||
|
||||
if (GUIComponent.MouseOn != null && !string.IsNullOrWhiteSpace(GUIComponent.MouseOn.ToolTip)) GUIComponent.MouseOn.DrawToolTip(spriteBatch);
|
||||
}
|
||||
|
||||
public static void Update(float deltaTime)
|
||||
{
|
||||
if (GUIMessageBox.messageBoxes.Count > 0)
|
||||
{
|
||||
var messageBox = GUIMessageBox.messageBoxes.Peek();
|
||||
if (messageBox != null)
|
||||
{
|
||||
GUIComponent.MouseOn = messageBox;
|
||||
messageBox.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddMessage(string message, Color color, float lifeTime = 3.0f, bool playSound = true)
|
||||
{
|
||||
if (messages.Count>0 && messages[messages.Count-1].Text == message)
|
||||
{
|
||||
messages[messages.Count - 1].LifeTime = lifeTime;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 currPos = new Vector2(Game1.GraphicsWidth / 2.0f, Game1.GraphicsHeight * 0.7f);
|
||||
currPos.Y += messages.Count * 30;
|
||||
|
||||
messages.Add(new GUIMessage(message, color, currPos, lifeTime));
|
||||
if (playSound) PlayMessageSound();
|
||||
}
|
||||
|
||||
public static void PlayMessageSound()
|
||||
{
|
||||
sounds[0].Play();
|
||||
}
|
||||
|
||||
private static void DrawMessages(SpriteBatch spriteBatch, float deltaTime)
|
||||
{
|
||||
if (messages.Count == 0) return;
|
||||
|
||||
Vector2 currPos = new Vector2(Game1.GraphicsWidth / 2.0f, Game1.GraphicsHeight * 0.7f);
|
||||
|
||||
int i = 1;
|
||||
foreach (GUIMessage msg in messages)
|
||||
{
|
||||
float alpha = 1.0f;
|
||||
|
||||
if (msg.LifeTime < 1.0f)
|
||||
{
|
||||
alpha -= 1.0f - msg.LifeTime;
|
||||
}
|
||||
|
||||
msg.Pos = MathUtils.SmoothStep(msg.Pos, currPos, deltaTime*20.0f);
|
||||
|
||||
spriteBatch.DrawString(Font, msg.Text,
|
||||
new Vector2((int)msg.Pos.X, (int)msg.Pos.Y),
|
||||
msg.Color * alpha, 0.0f,
|
||||
new Vector2((int)(0.5f * msg.Size.X), (int)(0.5f * msg.Size.Y)), 1.0f, SpriteEffects.None, 0.0f);
|
||||
|
||||
currPos.Y += 30.0f;
|
||||
|
||||
messages[0].LifeTime -= deltaTime/i;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (messages[0].LifeTime <= 0.0f) messages.Remove(messages[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
103
Subsurface/Source/GUI/GUIButton.cs
Normal file
103
Subsurface/Source/GUI/GUIButton.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class GUIButton : GUIComponent
|
||||
{
|
||||
protected GUITextBlock textBlock;
|
||||
protected GUIFrame frame;
|
||||
|
||||
public delegate bool OnClickedHandler(GUIButton button, object obj);
|
||||
public OnClickedHandler OnClicked;
|
||||
|
||||
public delegate bool OnPressedHandler();
|
||||
public OnPressedHandler OnPressed;
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return textBlock.Text; }
|
||||
set { textBlock.Text = value; }
|
||||
}
|
||||
|
||||
public GUIButton(Rectangle rect, string text, GUIStyle style, GUIComponent parent = null)
|
||||
: this(rect, text, null, Alignment.Left, style, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public GUIButton(Rectangle rect, string text, Alignment alignment, GUIStyle style, GUIComponent parent = null)
|
||||
: this(rect, text, null, alignment, style, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public GUIButton(Rectangle rect, string text, Color? color, GUIStyle style, GUIComponent parent = null)
|
||||
: this(rect, text, color, (Alignment.Left | Alignment.Top), style, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public GUIButton(Rectangle rect, string text, Color? color, Alignment alignment, GUIStyle style, GUIComponent parent = null)
|
||||
:base (style)
|
||||
{
|
||||
this.rect = rect;
|
||||
if (color!=null) this.color = (Color)color;
|
||||
this.alignment = alignment;
|
||||
|
||||
Enabled = true;
|
||||
|
||||
if (parent != null)
|
||||
parent.AddChild(this);
|
||||
|
||||
frame = new GUIFrame(new Rectangle(0,0,0,0), style, this);
|
||||
if (style!=null) style.Apply(frame, this);
|
||||
|
||||
textBlock = new GUITextBlock(new Rectangle(0, 0, 0, 0), text,
|
||||
Color.Transparent, (this.style==null) ? Color.Black : this.style.textColor,
|
||||
Alignment.Center, style, this);
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (rect.Contains(PlayerInput.GetMouseState.Position) && Enabled && (MouseOn == null || MouseOn == this || IsParentOf(MouseOn)))
|
||||
{
|
||||
state = ComponentState.Hover;
|
||||
if (PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed)
|
||||
{
|
||||
if (OnPressed != null)
|
||||
{
|
||||
if (OnPressed()) state = ComponentState.Selected;
|
||||
}
|
||||
}
|
||||
else if (PlayerInput.LeftButtonClicked())
|
||||
{
|
||||
if (OnClicked != null)
|
||||
{
|
||||
if (OnClicked(this, UserData)) state = ComponentState.Selected;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state = ComponentState.None;
|
||||
}
|
||||
|
||||
frame.State = state;
|
||||
|
||||
//Color currColor = color;
|
||||
//if (state == ComponentState.Hover) currColor = hoverColor;
|
||||
//if (state == ComponentState.Selected) currColor = selectedColor;
|
||||
|
||||
//GUI.DrawRectangle(spriteBatch, rect, currColor * alpha, true);
|
||||
|
||||
////spriteBatch.DrawString(HUD.font, text, new Vector2(rect.X+rect.Width/2, rect.Y+rect.Height/2), Color.Black, 0.0f, new Vector2(0.5f,0.5f), 1.0f, SpriteEffects.None, 0.0f);
|
||||
|
||||
//GUI.DrawRectangle(spriteBatch, rect, Color.Black * alpha, false);
|
||||
|
||||
DrawChildren(spriteBatch);
|
||||
|
||||
if (!Enabled) GUI.DrawRectangle(spriteBatch, rect, Color.Gray*0.5f, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
347
Subsurface/Source/GUI/GUIComponent.cs
Normal file
347
Subsurface/Source/GUI/GUIComponent.cs
Normal file
@@ -0,0 +1,347 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using EventInput;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
abstract class GUIComponent
|
||||
{
|
||||
public static GUIComponent MouseOn;
|
||||
|
||||
protected static KeyboardDispatcher keyboardDispatcher;
|
||||
|
||||
public enum ComponentState { None, Hover, Selected};
|
||||
|
||||
protected Alignment alignment;
|
||||
|
||||
protected GUIComponentStyle style;
|
||||
|
||||
protected object userData;
|
||||
|
||||
protected Rectangle rect;
|
||||
|
||||
public bool CanBeFocused;
|
||||
|
||||
protected Vector4 padding;
|
||||
|
||||
protected Color color;
|
||||
protected Color hoverColor;
|
||||
protected Color selectedColor;
|
||||
|
||||
protected GUIComponent parent;
|
||||
public List<GUIComponent> children;
|
||||
|
||||
protected ComponentState state;
|
||||
|
||||
public virtual SpriteFont Font
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string ToolTip
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private GUITextBlock toolTipBlock;
|
||||
|
||||
//protected float alpha;
|
||||
|
||||
public GUIComponent Parent
|
||||
{
|
||||
get { return parent; }
|
||||
}
|
||||
|
||||
public Vector2 Center
|
||||
{
|
||||
get { return new Vector2(rect.Center.X, rect.Center.Y); }
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected List<Sprite> sprites;
|
||||
//public Alignment SpriteAlignment { get; set; }
|
||||
//public bool RepeatSpriteX, RepeatSpriteY;
|
||||
|
||||
public Color OutlineColor { get; set; }
|
||||
|
||||
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(GUIStyle style)
|
||||
{
|
||||
//alpha = 1.0f;
|
||||
|
||||
OutlineColor = Color.Transparent;
|
||||
|
||||
Font = GUI.Font;
|
||||
|
||||
sprites = new List<Sprite>();
|
||||
children = new List<GUIComponent>();
|
||||
|
||||
CanBeFocused = true;
|
||||
|
||||
if (style!=null) style.Apply(this);
|
||||
}
|
||||
|
||||
public static void Init(GameWindow window)
|
||||
{
|
||||
keyboardDispatcher = new KeyboardDispatcher(window);
|
||||
}
|
||||
|
||||
public T GetChild<T>()
|
||||
{
|
||||
foreach (GUIComponent child in children)
|
||||
{
|
||||
if (child is T) return (T)(object)child;
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public GUIComponent GetChild(object obj)
|
||||
{
|
||||
foreach (GUIComponent child in children)
|
||||
{
|
||||
if (child.UserData == obj) return child;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsParentOf(GUIComponent component)
|
||||
{
|
||||
foreach (GUIComponent child in children)
|
||||
{
|
||||
if (child == component) return true;
|
||||
if (child.IsParentOf(component)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
Color currColor = color;
|
||||
if (state == ComponentState.Selected) currColor = selectedColor;
|
||||
if (state == ComponentState.Hover) currColor = hoverColor;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, rect, currColor * (currColor.A / 255.0f), true);
|
||||
|
||||
if (sprites != null)
|
||||
{
|
||||
foreach (Sprite sprite in sprites)
|
||||
{
|
||||
Vector2 startPos = new Vector2(rect.X, rect.Y);
|
||||
Vector2 size = new Vector2(sprite.SourceRect.Width, sprite.SourceRect.Height);
|
||||
|
||||
if (sprite.size.X == 0.0f) size.X = rect.Width;
|
||||
if (sprite.size.Y == 0.0f) size.Y = rect.Height;
|
||||
|
||||
sprite.DrawTiled(spriteBatch, startPos, size, currColor * (currColor.A / 255.0f));
|
||||
}
|
||||
}
|
||||
|
||||
//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 void DrawToolTip(SpriteBatch spriteBatch)
|
||||
{
|
||||
int width = 200;
|
||||
if (toolTipBlock==null || (string)toolTipBlock.userData != ToolTip)
|
||||
{
|
||||
string wrappedText = ToolBox.WrapText(ToolTip, width, GUI.SmallFont);
|
||||
toolTipBlock = new GUITextBlock(new Rectangle(0,0,width, wrappedText.Split('\n').Length*15), ToolTip, GUI.style, null, true);
|
||||
toolTipBlock.userData = ToolTip;
|
||||
}
|
||||
|
||||
toolTipBlock.rect = new Rectangle((int)PlayerInput.MousePosition.X, (int)PlayerInput.MousePosition.Y, toolTipBlock.rect.Width, toolTipBlock.rect.Height);
|
||||
toolTipBlock.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
public virtual void Update(float deltaTime)
|
||||
{
|
||||
if (CanBeFocused)
|
||||
{
|
||||
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 = null)
|
||||
{
|
||||
Rectangle parentRect = (parent==null) ? new Rectangle(0,0,Game1.GraphicsWidth, Game1.GraphicsHeight) : parent.rect;
|
||||
|
||||
Vector4 padding = (parent == null) ? Vector4.Zero : parent.padding;
|
||||
|
||||
if (rect.Width == 0) rect.Width = parentRect.Width - rect.X
|
||||
- (int)padding.X - (int)padding.Z;
|
||||
|
||||
if (rect.Height == 0) rect.Height = parentRect.Height - rect.Y
|
||||
- (int)padding.Y - (int)padding.W;
|
||||
|
||||
if (alignment.HasFlag(Alignment.CenterX))
|
||||
{
|
||||
rect.X += parentRect.X + (int)parentRect.Width/2 - (int)rect.Width/2;
|
||||
}
|
||||
else if (alignment.HasFlag(Alignment.Right))
|
||||
{
|
||||
rect.X += parentRect.X + (int)parentRect.Width - (int)padding.Z - (int)rect.Width;
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.X += parentRect.X + (int)padding.X;
|
||||
}
|
||||
|
||||
if (alignment.HasFlag(Alignment.CenterY))
|
||||
{
|
||||
rect.Y += parentRect.Y + (int)parentRect.Height / 2 - (int)rect.Height / 2;
|
||||
}
|
||||
else if (alignment.HasFlag(Alignment.Bottom))
|
||||
{
|
||||
rect.Y += parentRect.Y + (int)parentRect.Height - (int)padding.W - (int)rect.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.Y += parentRect.Y + (int)padding.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ApplyStyle(GUIComponentStyle style)
|
||||
{
|
||||
color = style.Color;
|
||||
hoverColor = style.HoverColor;
|
||||
selectedColor = style.SelectedColor;
|
||||
|
||||
padding = style.Padding;
|
||||
sprites = style.Sprites;
|
||||
|
||||
OutlineColor = style.OutlineColor;
|
||||
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public virtual void DrawChildren(SpriteBatch spriteBatch)
|
||||
{
|
||||
for (int i = 0; i < children.Count; i++ )
|
||||
{
|
||||
children[i].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 virtual void ClearChildren()
|
||||
{
|
||||
children.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
61
Subsurface/Source/GUI/GUIFrame.cs
Normal file
61
Subsurface/Source/GUI/GUIFrame.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class GUIFrame : GUIComponent
|
||||
{
|
||||
public GUIFrame(Rectangle rect, GUIStyle style = null, GUIComponent parent = null)
|
||||
: this(rect, null, (Alignment.Left | Alignment.Top), style, parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public GUIFrame(Rectangle rect, Color color, GUIStyle style = null, GUIComponent parent = null)
|
||||
: this(rect, color, (Alignment.Left | Alignment.Top), style, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public GUIFrame(Rectangle rect, Color? color, Alignment alignment, GUIStyle style = null, GUIComponent parent = null)
|
||||
: base(style)
|
||||
{
|
||||
this.rect = rect;
|
||||
|
||||
this.alignment = alignment;
|
||||
|
||||
if (color!=null) this.color = (Color)color;
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
parent.AddChild(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateDimensions();
|
||||
}
|
||||
|
||||
//if (style != null) ApplyStyle(style);
|
||||
}
|
||||
|
||||
public override void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
|
||||
{
|
||||
|
||||
|
||||
Color currColor = color;
|
||||
if (state == ComponentState.Selected) currColor = selectedColor;
|
||||
if (state == ComponentState.Hover) currColor = hoverColor;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, rect, currColor * (currColor.A/255.0f), true);
|
||||
base.Draw(spriteBatch);
|
||||
|
||||
if (OutlineColor != Color.Transparent)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, rect, OutlineColor * (OutlineColor.A/255.0f), false);
|
||||
}
|
||||
|
||||
|
||||
DrawChildren(spriteBatch);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
85
Subsurface/Source/GUI/GUIImage.cs
Normal file
85
Subsurface/Source/GUI/GUIImage.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
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)
|
||||
: base(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 * (currColor.A / 255.0f), 0.0f, Vector2.Zero,
|
||||
Scale, SpriteEffects.None, 0.0f);
|
||||
|
||||
DrawChildren(spriteBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
302
Subsurface/Source/GUI/GUIListBox.cs
Normal file
302
Subsurface/Source/GUI/GUIListBox.cs
Normal file
@@ -0,0 +1,302 @@
|
||||
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 GUIFrame frame;
|
||||
|
||||
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 float BarScroll
|
||||
{
|
||||
get { return scrollBar.BarScroll; }
|
||||
set { scrollBar.BarScroll = value; }
|
||||
}
|
||||
|
||||
public float BarSize
|
||||
{
|
||||
get { return scrollBar.BarSize; }
|
||||
}
|
||||
|
||||
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, GUIComponent parent = null)
|
||||
: this(rect, style, Alignment.TopLeft, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public GUIListBox(Rectangle rect, GUIStyle style, Alignment alignment, GUIComponent parent = null)
|
||||
: this(rect, null, style, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public GUIListBox(Rectangle rect, Color? color, GUIStyle style = null, GUIComponent parent = null)
|
||||
: this(rect, color, (Alignment.Left | Alignment.Top), style, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public GUIListBox(Rectangle rect, Color? color, Alignment alignment, GUIStyle style = null, GUIComponent parent = null)
|
||||
: base(style)
|
||||
{
|
||||
this.rect = rect;
|
||||
this.alignment = alignment;
|
||||
|
||||
if (color!=null) this.color = (Color)color;
|
||||
|
||||
if (parent != null)
|
||||
parent.AddChild(this);
|
||||
|
||||
scrollBarHidden = true;
|
||||
|
||||
scrollBar = new GUIScrollBar(
|
||||
new Rectangle(this.rect.X + this.rect.Width-20, this.rect.Y, 20, this.rect.Height), color, 1.0f, style);
|
||||
|
||||
frame = new GUIFrame(Rectangle.Empty, style, this);
|
||||
if (style != null) style.Apply(frame, this);
|
||||
|
||||
UpdateScrollBarSize();
|
||||
|
||||
enabled = true;
|
||||
|
||||
scrollBarEnabled = true;
|
||||
|
||||
scrollBar.BarScroll = 0.0f;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
//children[0] is the GUIFrame, ignore it
|
||||
childIndex += 1;
|
||||
|
||||
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)
|
||||
{
|
||||
if (child == frame) continue;
|
||||
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 (oldSize == 1.0f && scrollBar.BarScroll == 0.0f) scrollBar.BarScroll = 1.0f;
|
||||
|
||||
//if (scrollBar.BarSize < 1.0f && oldScroll == 1.0f)
|
||||
//{
|
||||
// scrollBar.BarScroll = 1.0f;
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
public override void ClearChildren()
|
||||
{
|
||||
base.ClearChildren();
|
||||
selected = null;
|
||||
}
|
||||
|
||||
public override void RemoveChild(GUIComponent child)
|
||||
{
|
||||
base.RemoveChild(child);
|
||||
|
||||
if (selected == child) selected = null;
|
||||
|
||||
UpdateScrollBarSize();
|
||||
}
|
||||
|
||||
private void ShowScrollBar()
|
||||
{
|
||||
if (scrollBarHidden) Rect = new Rectangle(rect.X, rect.Y, rect.Width - scrollBar.Rect.Width, rect.Height);
|
||||
scrollBarHidden = false;
|
||||
|
||||
}
|
||||
|
||||
private void HideScrollBar()
|
||||
{
|
||||
if (!scrollBarHidden) Rect = new Rectangle(rect.X, rect.Y, rect.Width + scrollBar.Rect.Width, rect.Height);
|
||||
scrollBarHidden = true;
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
base.Draw(spriteBatch);
|
||||
|
||||
frame.Draw(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];
|
||||
if (child == frame) continue;
|
||||
|
||||
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 (enabled && (MouseOn == this || (MouseOn != null && this.IsParentOf(MouseOn))) && child.Rect.Contains(PlayerInput.GetMouseState.Position))
|
||||
{
|
||||
child.State = ComponentState.Hover;
|
||||
if (PlayerInput.LeftButtonClicked())
|
||||
{
|
||||
Debug.WriteLine("clicked");
|
||||
selected = child;
|
||||
if (OnSelected != null)
|
||||
{
|
||||
if (!OnSelected(child.UserData)) selected = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child.State = ComponentState.None;
|
||||
}
|
||||
|
||||
child.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
//GUI.DrawRectangle(spriteBatch, rect, Color.Black, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Subsurface/Source/GUI/GUIMessage.cs
Normal file
56
Subsurface/Source/GUI/GUIMessage.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
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 position, float lifeTime)
|
||||
{
|
||||
coloredText = new ColoredText(text, color);
|
||||
pos = position;
|
||||
this.lifeTime = lifeTime;
|
||||
|
||||
size = GUI.Font.MeasureString(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
Subsurface/Source/GUI/GUIMessageBox.cs
Normal file
65
Subsurface/Source/GUI/GUIMessageBox.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class GUIMessageBox : GUIFrame
|
||||
{
|
||||
public static Queue<GUIMessageBox> messageBoxes = new Queue<GUIMessageBox>();
|
||||
|
||||
const int DefaultWidth=400, DefaultHeight=200;
|
||||
|
||||
//public delegate bool OnClickedHandler(GUIButton button, object obj);
|
||||
//public OnClickedHandler OnClicked;
|
||||
|
||||
//GUIFrame frame;
|
||||
public GUIButton[] Buttons;
|
||||
|
||||
public GUIMessageBox(string header, string text)
|
||||
: this(header, text, new string[] {"OK"})
|
||||
{
|
||||
this.Buttons[0].OnClicked = Close;
|
||||
}
|
||||
|
||||
public GUIMessageBox(string header, string text, int width, int height)
|
||||
: this(header, text, new string[] { "OK" }, width, height)
|
||||
{
|
||||
this.Buttons[0].OnClicked = Close;
|
||||
}
|
||||
|
||||
public GUIMessageBox(string header, string text, string[] buttons, int width=DefaultWidth, int height=DefaultHeight, Alignment textAlignment = Alignment.TopLeft)
|
||||
: base(new Rectangle(0,0, width, height),
|
||||
null, Alignment.Center, GUI.style, null)
|
||||
{
|
||||
//Padding = GUI.style.smallPadding;
|
||||
|
||||
//if (buttons == null || buttons.Length == 0)
|
||||
//{
|
||||
// DebugConsole.ThrowError("Creating a message box with no buttons isn't allowed");
|
||||
// return;
|
||||
//}
|
||||
|
||||
new GUITextBlock(new Rectangle(0, 0, 0, 30), header, Color.Transparent, Color.White, textAlignment, GUI.style, this, true);
|
||||
new GUITextBlock(new Rectangle(0, 30, 0, height - 70), text, Color.Transparent, Color.White, textAlignment, GUI.style, this, true);
|
||||
|
||||
int x = 0;
|
||||
this.Buttons = new GUIButton[buttons.Length];
|
||||
for (int i = 0; i < buttons.Length; i++)
|
||||
{
|
||||
this.Buttons[i] = new GUIButton(new Rectangle(x, 0, 150, 30), buttons[i], Alignment.Left | Alignment.Bottom, GUI.style, this);
|
||||
|
||||
x += this.Buttons[i].Rect.Width + 20;
|
||||
}
|
||||
|
||||
messageBoxes.Enqueue(this);
|
||||
}
|
||||
|
||||
public bool Close(GUIButton button, object obj)
|
||||
{
|
||||
if (parent != null) parent.RemoveChild(this);
|
||||
|
||||
messageBoxes.Dequeue();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Subsurface/Source/GUI/GUIProgressBar.cs
Normal file
78
Subsurface/Source/GUI/GUIProgressBar.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
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)
|
||||
: base(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, null, 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 * (color.A / 255.0f), true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
178
Subsurface/Source/GUI/GUIScrollBar.cs
Normal file
178
Subsurface/Source/GUI/GUIScrollBar.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
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, null, barSize, style, parent)
|
||||
{
|
||||
}
|
||||
|
||||
public GUIScrollBar(Rectangle rect, Color? color, float barSize, GUIStyle style = null, GUIComponent parent = null)
|
||||
: this(rect, color, barSize, Alignment.TopLeft, style, parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public GUIScrollBar(Rectangle rect, Color? color, float barSize, Alignment alignment, GUIStyle style = null, GUIComponent parent = null)
|
||||
: base(style)
|
||||
{
|
||||
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, style, this);
|
||||
//AddChild(frame);
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine(frame.rect);
|
||||
|
||||
bar = new GUIButton(new Rectangle(0, 0, 0, 0), "", color, style, this);
|
||||
|
||||
bar.OnPressed = SelectBar;
|
||||
//AddChild(bar);
|
||||
|
||||
enabled = true;
|
||||
|
||||
UpdateRect();
|
||||
}
|
||||
|
||||
private void UpdateRect()
|
||||
{
|
||||
|
||||
bar.Rect = new Rectangle(
|
||||
bar.Rect.X,
|
||||
bar.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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
67
Subsurface/Source/GUI/GUIStyle.cs
Normal file
67
Subsurface/Source/GUI/GUIStyle.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class GUIStyle
|
||||
{
|
||||
private Dictionary<string, GUIComponentStyle> componentStyles;
|
||||
|
||||
public GUIStyle(string file)
|
||||
{
|
||||
|
||||
componentStyles = new Dictionary<string, GUIComponentStyle>();
|
||||
|
||||
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);
|
||||
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
{
|
||||
GUIComponentStyle componentStyle = new GUIComponentStyle(subElement);
|
||||
componentStyles.Add(subElement.Name.ToString().ToLower(), componentStyle);
|
||||
}
|
||||
}
|
||||
|
||||
public void Apply(GUIComponent targetComponent, GUIComponent parent = null)
|
||||
{
|
||||
GUIComponentStyle componentStyle = null;
|
||||
string name = (parent==null) ? targetComponent.GetType().Name.ToLower() : parent.GetType().Name.ToLower();
|
||||
componentStyles.TryGetValue(name, out componentStyle);
|
||||
|
||||
if (componentStyle==null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't find a GUI style for "+targetComponent.GetType().Name);
|
||||
return;
|
||||
}
|
||||
|
||||
targetComponent.ApplyStyle(componentStyle);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
228
Subsurface/Source/GUI/GUITextBlock.cs
Normal file
228
Subsurface/Source/GUI/GUITextBlock.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
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;
|
||||
|
||||
public bool Wrap;
|
||||
|
||||
|
||||
|
||||
public override Vector4 Padding
|
||||
{
|
||||
get { return padding; }
|
||||
set
|
||||
{
|
||||
padding = value;
|
||||
SetTextPos();
|
||||
}
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return text; }
|
||||
set
|
||||
{
|
||||
text = value;
|
||||
SetTextPos();
|
||||
}
|
||||
}
|
||||
|
||||
public bool LimitText
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Vector2 TextPos
|
||||
{
|
||||
get { return textPos; }
|
||||
}
|
||||
|
||||
public Vector2 Origin
|
||||
{
|
||||
get { return origin; }
|
||||
}
|
||||
|
||||
public Color TextColor
|
||||
{
|
||||
get { return textColor; }
|
||||
set { textColor = value; }
|
||||
}
|
||||
|
||||
public Vector2 CaretPos
|
||||
{
|
||||
get { return caretPos; }
|
||||
}
|
||||
|
||||
public GUITextBlock(Rectangle rect, string text, GUIStyle style, GUIComponent parent = null, bool wrap = false)
|
||||
: this(rect, text, style, Alignment.TopLeft, Alignment.TopLeft, parent, wrap)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public GUITextBlock(Rectangle rect, string text, GUIStyle style, Alignment alignment = Alignment.TopLeft, Alignment textAlignment = Alignment.TopLeft, GUIComponent parent = null, bool wrap = false)
|
||||
: this (rect, text, null, null, alignment, textAlignment, style, parent, wrap)
|
||||
{
|
||||
}
|
||||
|
||||
public GUITextBlock(Rectangle rect, string text, Color? color, Color? textColor, Alignment textAlignment = Alignment.Left, GUIStyle style = null, GUIComponent parent = null, bool wrap = false)
|
||||
: this(rect, text,color, textColor, Alignment.TopLeft, textAlignment, style, parent, wrap)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void UpdateDimensions(GUIComponent parent)
|
||||
{
|
||||
base.UpdateDimensions(parent);
|
||||
|
||||
SetTextPos();
|
||||
}
|
||||
|
||||
public override void ApplyStyle(GUIComponentStyle style)
|
||||
{
|
||||
base.ApplyStyle(style);
|
||||
|
||||
textColor = style.textColor;
|
||||
}
|
||||
|
||||
|
||||
public GUITextBlock(Rectangle rect, string text, Color? color, Color? textColor, Alignment alignment, Alignment textAlignment = Alignment.Left, GUIStyle style = null, GUIComponent parent = null, bool wrap = false)
|
||||
:base (style)
|
||||
{
|
||||
this.rect = rect;
|
||||
|
||||
if (color!=null) this.color = (Color)color;
|
||||
if (textColor!=null) this.textColor = (Color)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(text);
|
||||
|
||||
if (Wrap && rect.Width>0)
|
||||
{
|
||||
//text = text.Replace("\n"," ");
|
||||
text = ToolBox.WrapText(text, rect.Width, Font);
|
||||
|
||||
Vector2 newSize = MeasureText(text);
|
||||
|
||||
//Rectangle newRect = rect;
|
||||
|
||||
//newRect.Width += (int)(newSize.X-size.X);
|
||||
//newRect.Height += (int)(newSize.Y - size.Y);
|
||||
|
||||
//Rect = newRect;
|
||||
size = newSize;
|
||||
}
|
||||
|
||||
if (LimitText && text.Length>1 && size.Y > rect.Height)
|
||||
{
|
||||
string[] lines = text.Split('\n');
|
||||
text = string.Join("\n", lines, 0, lines.Length-1);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (text.Contains("\n"))
|
||||
{
|
||||
string[] lines = text.Split('\n');
|
||||
Vector2 lastLineSize = MeasureText(lines[lines.Length-1]);
|
||||
caretPos = new Vector2(rect.X + lastLineSize.X, rect.Y + size.Y - lastLineSize.Y) + textPos - origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
caretPos = new Vector2(rect.X + size.X, rect.Y) + textPos - origin;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Vector2 MeasureText(string text)
|
||||
{
|
||||
Vector2 size = Vector2.Zero;
|
||||
while (size == Vector2.Zero)
|
||||
{
|
||||
try { size = 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*(currColor.A/255.0f), true);
|
||||
|
||||
base.Draw(spriteBatch);
|
||||
|
||||
if (TextGetter != null) text = TextGetter();
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
spriteBatch.DrawString(Font,
|
||||
text,
|
||||
new Vector2(rect.X, rect.Y) + textPos,
|
||||
textColor * (textColor.A / 255.0f),
|
||||
0.0f, origin, 1.0f,
|
||||
SpriteEffects.None, 0.0f);
|
||||
}
|
||||
|
||||
DrawChildren(spriteBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
245
Subsurface/Source/GUI/GUITextBox.cs
Normal file
245
Subsurface/Source/GUI/GUITextBox.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
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 GUITextBlock.TextGetterHandler TextGetter
|
||||
{
|
||||
get { return textBlock.TextGetter; }
|
||||
set { textBlock.TextGetter = value; }
|
||||
}
|
||||
|
||||
public bool Wrap
|
||||
{
|
||||
get { return textBlock.Wrap; }
|
||||
set { textBlock.Wrap = value; }
|
||||
}
|
||||
|
||||
public bool LimitText
|
||||
{
|
||||
get { return textBlock.LimitText; }
|
||||
set { textBlock.LimitText = value; }
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public override SpriteFont Font
|
||||
{
|
||||
set
|
||||
{
|
||||
base.Font = value;
|
||||
if (textBlock == null) return;
|
||||
textBlock.Font = value;
|
||||
}
|
||||
}
|
||||
|
||||
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 (Font.Characters.Contains(c)) filtered += c;
|
||||
}
|
||||
|
||||
textBlock.Text = filtered;
|
||||
|
||||
if (!Wrap && Font.MeasureString(textBlock.Text).X > rect.Width)
|
||||
{
|
||||
//ensure that text cannot be larger than the box
|
||||
Text = textBlock.Text.Substring(0, textBlock.Text.Length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GUITextBox(Rectangle rect, GUIStyle style = null, GUIComponent parent = null)
|
||||
: this(rect, null, null, Alignment.Left, Alignment.Left, style, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public GUITextBox(Rectangle rect, Alignment alignment = Alignment.Left, GUIStyle style = null, GUIComponent parent = null)
|
||||
: this(rect, null, null, alignment, Alignment.Left, style, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public GUITextBox(Rectangle rect, Color? color, Color? textColor, Alignment alignment, Alignment textAlignment = Alignment.Left, GUIStyle style = null, GUIComponent parent = null)
|
||||
: base(style)
|
||||
{
|
||||
Enabled = true;
|
||||
|
||||
this.rect = rect;
|
||||
|
||||
if (color != null) this.color = (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, style, this);
|
||||
textBlock.Padding = new Vector4(10.0f, 0.0f, 10.0f, 0.0f);
|
||||
if (style != null) style.Apply(textBlock, this);
|
||||
|
||||
previousMouse = PlayerInput.GetMouseState;
|
||||
//SetTextPos();
|
||||
}
|
||||
|
||||
public void Select()
|
||||
{
|
||||
Selected = true;
|
||||
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)
|
||||
{
|
||||
if (!Enabled) return;
|
||||
|
||||
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 + Font.MeasureString("I").Y - 3),
|
||||
textBlock.TextColor * (textBlock.TextColor.A / 255.0f));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Subsurface/Source/GUI/GUITickBox.cs
Normal file
78
Subsurface/Source/GUI/GUITickBox.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
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;
|
||||
|
||||
private bool selected;
|
||||
|
||||
public bool Selected
|
||||
{
|
||||
get { return selected; }
|
||||
set
|
||||
{
|
||||
if (value == selected) return;
|
||||
selected = value;
|
||||
state = (selected) ? ComponentState.Selected : ComponentState.None;
|
||||
}
|
||||
}
|
||||
|
||||
public GUITickBox(Rectangle rect, string label, Alignment alignment, GUIComponent parent)
|
||||
: base(null)
|
||||
{
|
||||
if (parent != null)
|
||||
parent.AddChild(this);
|
||||
|
||||
box = new GUIFrame(rect, Color.DarkGray, null, this);
|
||||
box.HoverColor = Color.Gray;
|
||||
box.SelectedColor = Color.DarkGray;
|
||||
|
||||
text = new GUITextBlock(new Rectangle(rect.X + 40, rect.Y, 200, 30), label, Color.Transparent, Color.White, Alignment.TopLeft, null, 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 + 2, box.Rect.Y + 2, box.Rect.Width - 4, box.Rect.Height - 4), Color.Green * 0.8f, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Subsurface/Source/GUI/TitleScreen.cs
Normal file
101
Subsurface/Source/GUI/TitleScreen.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class TitleScreen
|
||||
{
|
||||
private Texture2D backgroundTexture,monsterTexture,titleTexture;
|
||||
|
||||
readonly RenderTarget2D renderTarget;
|
||||
|
||||
float state;
|
||||
|
||||
public Vector2 Position;
|
||||
|
||||
public TitleScreen(GraphicsDevice graphics)
|
||||
{
|
||||
backgroundTexture = Game1.TextureLoader.FromFile("Content/UI/titleBackground.png");
|
||||
monsterTexture = Game1.TextureLoader.FromFile("Content/UI/titleMonster.png");
|
||||
titleTexture = Game1.TextureLoader.FromFile("Content/UI/titleText.png");
|
||||
|
||||
renderTarget = new RenderTarget2D(graphics, Game1.GraphicsWidth, Game1.GraphicsHeight);
|
||||
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float loadState, float deltaTime)
|
||||
{
|
||||
//if (stopwatch == null)
|
||||
//{
|
||||
// stopwatch = new Stopwatch();
|
||||
// stopwatch.Start();
|
||||
//}
|
||||
|
||||
graphics.SetRenderTarget(renderTarget);
|
||||
//Debug.WriteLine(stopwatch.Elapsed.TotalMilliseconds);
|
||||
|
||||
float scale = Game1.GraphicsHeight/2048.0f;
|
||||
|
||||
state += deltaTime;
|
||||
|
||||
Vector2 center = new Vector2(Game1.GraphicsWidth*0.3f, Game1.GraphicsHeight/2.0f) + Position*scale;
|
||||
|
||||
Vector2 titlePos = center + new Vector2(-0.0f + (float)Math.Sqrt(state) * 220.0f, 0.0f) * scale;
|
||||
titlePos.X = Math.Min(titlePos.X, (float)Game1.GraphicsWidth / 2.0f);
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
|
||||
graphics.Clear(Color.Black);
|
||||
|
||||
spriteBatch.Draw(backgroundTexture, center, null, Color.White * Math.Min(state / 5.0f, 1.0f), 0.0f,
|
||||
new Vector2(backgroundTexture.Width / 2.0f, backgroundTexture.Height / 2.0f),
|
||||
scale, SpriteEffects.None, 0.2f);
|
||||
|
||||
spriteBatch.Draw(monsterTexture,
|
||||
center + new Vector2(state * 100.0f - 1200.0f, state * 30.0f - 100.0f) * scale, null,
|
||||
Color.White, 0.0f, Vector2.Zero, scale, SpriteEffects.None, 0.1f);
|
||||
|
||||
spriteBatch.Draw(titleTexture,
|
||||
titlePos, null,
|
||||
Color.White * Math.Min((state - 1.0f) / 5.0f, 1.0f), 0.0f, new Vector2(titleTexture.Width / 2.0f, titleTexture.Height / 2.0f), scale, SpriteEffects.None, 0.0f);
|
||||
|
||||
spriteBatch.End();
|
||||
|
||||
graphics.SetRenderTarget(null);
|
||||
|
||||
Matrix transform = Matrix.CreateTranslation(
|
||||
new Vector3(Game1.GraphicsWidth / 2.0f,
|
||||
Game1.GraphicsHeight / 2.0f, 0));
|
||||
|
||||
Hull.renderer.RenderBack(graphics, renderTarget, transform);
|
||||
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
|
||||
|
||||
spriteBatch.Draw(titleTexture,
|
||||
titlePos, null,
|
||||
Color.White * Math.Min((state - 3.0f) / 5.0f, 1.0f), 0.0f, new Vector2(titleTexture.Width / 2.0f, titleTexture.Height / 2.0f), scale, SpriteEffects.None, 0.0f);
|
||||
|
||||
string loadText = "";
|
||||
if (loadState == 100.0f)
|
||||
{
|
||||
loadText = "Press any key to continue";
|
||||
|
||||
}
|
||||
else if (loadState > 0.0f)
|
||||
{
|
||||
loadText = "Loading... " + (int)loadState + " %";
|
||||
}
|
||||
|
||||
spriteBatch.DrawString(GUI.Font, loadText, new Vector2(Game1.GraphicsWidth/2.0f - 50.0f, Game1.GraphicsHeight*0.8f), Color.White);
|
||||
|
||||
spriteBatch.End();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
320
Subsurface/Source/Game1.cs
Normal file
320
Subsurface/Source/Game1.cs
Normal file
@@ -0,0 +1,320 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Subsurface.Networking;
|
||||
using Subsurface.Particles;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class Game1 : Game
|
||||
{
|
||||
public static GraphicsDeviceManager Graphics;
|
||||
static int graphicsWidth, graphicsHeight;
|
||||
static SpriteBatch spriteBatch;
|
||||
|
||||
public static bool DebugDraw;
|
||||
|
||||
public static GraphicsDevice CurrGraphicsDevice;
|
||||
|
||||
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 Lights.LightManager LightManager;
|
||||
|
||||
public static ContentPackage SelectedPackage
|
||||
{
|
||||
get { return Config.SelectedContentPackage; }
|
||||
}
|
||||
|
||||
public static Level Level;
|
||||
|
||||
public static GameSession GameSession;
|
||||
|
||||
public static NetworkMember NetworkMember;
|
||||
|
||||
public static ParticleManager ParticleManager;
|
||||
|
||||
public static TextureLoader TextureLoader;
|
||||
|
||||
public static World World;
|
||||
|
||||
public static TitleScreen TitleScreen;
|
||||
private bool titleScreenOpen;
|
||||
|
||||
public static GameSettings Config;
|
||||
|
||||
//public static Random localRandom;
|
||||
//public static Random random;
|
||||
|
||||
//private Stopwatch renderTimer;
|
||||
//public static int renderTimeElapsed;
|
||||
|
||||
public Camera Cam
|
||||
{
|
||||
get { return GameScreen.Cam; }
|
||||
}
|
||||
|
||||
public static int GraphicsWidth
|
||||
{
|
||||
get { return graphicsWidth; }
|
||||
}
|
||||
|
||||
public static int GraphicsHeight
|
||||
{
|
||||
get { return graphicsHeight; }
|
||||
}
|
||||
|
||||
public static GameServer Server
|
||||
{
|
||||
get { return NetworkMember as GameServer; }
|
||||
}
|
||||
|
||||
public static GameClient Client
|
||||
{
|
||||
get { return NetworkMember as GameClient; }
|
||||
}
|
||||
|
||||
public Game1()
|
||||
{
|
||||
Graphics = new GraphicsDeviceManager(this);
|
||||
|
||||
Config = new GameSettings("config.xml");
|
||||
|
||||
graphicsWidth = Config.GraphicsWidth;
|
||||
graphicsHeight = Config.GraphicsHeight;
|
||||
|
||||
Graphics.IsFullScreen = Config.FullScreenEnabled;
|
||||
Graphics.PreferredBackBufferWidth = graphicsWidth;
|
||||
Graphics.PreferredBackBufferHeight = graphicsHeight;
|
||||
Content.RootDirectory = "Content";
|
||||
|
||||
//graphics.SynchronizeWithVerticalRetrace = false;
|
||||
//graphics.ApplyChanges();
|
||||
|
||||
FrameCounter = new FrameCounter();
|
||||
|
||||
//renderTimer = new Stopwatch();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
CurrGraphicsDevice = GraphicsDevice;
|
||||
|
||||
//Event.Init("Content/randomevents.xml");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// LoadContent will be called once per game and is the place to load
|
||||
/// all of your content.
|
||||
/// </summary>
|
||||
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);
|
||||
|
||||
titleScreenOpen = true;
|
||||
TitleScreen = new TitleScreen(GraphicsDevice);
|
||||
|
||||
CoroutineManager.StartCoroutine(Load());
|
||||
}
|
||||
|
||||
private float loadState = 0.0f;
|
||||
private IEnumerable<Status> Load()
|
||||
{
|
||||
GUI.Font = ToolBox.TryLoadFont("SpriteFont1", Content);
|
||||
GUI.SmallFont = ToolBox.TryLoadFont("SmallFont", Content);
|
||||
|
||||
sw = new Stopwatch();
|
||||
|
||||
LightManager = new Lights.LightManager(GraphicsDevice);
|
||||
|
||||
Hull.renderer = new WaterRenderer(GraphicsDevice);
|
||||
loadState = 1.0f;
|
||||
yield return Status.Running;
|
||||
|
||||
GUI.LoadContent(GraphicsDevice);
|
||||
loadState = 2.0f;
|
||||
yield return Status.Running;
|
||||
|
||||
MapEntityPrefab.Init();
|
||||
loadState = 10.0f;
|
||||
yield return Status.Running;
|
||||
|
||||
JobPrefab.LoadAll(SelectedPackage.GetFilesOfType(ContentType.Jobs));
|
||||
loadState = 15.0f;
|
||||
yield return Status.Running;
|
||||
|
||||
StructurePrefab.LoadAll(SelectedPackage.GetFilesOfType(ContentType.Structure));
|
||||
loadState = 25.0f;
|
||||
yield return Status.Running;
|
||||
|
||||
ItemPrefab.LoadAll(SelectedPackage.GetFilesOfType(ContentType.Item));
|
||||
loadState = 40.0f;
|
||||
yield return Status.Running;
|
||||
|
||||
Debug.WriteLine("sounds");
|
||||
CoroutineManager.StartCoroutine(AmbientSoundManager.Init());
|
||||
loadState = 70.0f;
|
||||
yield return Status.Running;
|
||||
|
||||
GameMode.Init();
|
||||
|
||||
Submarine.Preload("Content/SavedMaps");
|
||||
loadState = 80.0f;
|
||||
yield return Status.Running;
|
||||
|
||||
GameScreen = new GameScreen(Graphics.GraphicsDevice);
|
||||
loadState = 90.0f;
|
||||
yield return Status.Running;
|
||||
|
||||
MainMenuScreen = new MainMenuScreen(this);
|
||||
LobbyScreen = new LobbyScreen();
|
||||
NetLobbyScreen = new NetLobbyScreen();
|
||||
EditMapScreen = new EditMapScreen();
|
||||
EditCharacterScreen = new EditCharacterScreen();
|
||||
yield return Status.Running;
|
||||
|
||||
ParticleManager = new ParticleManager("Content/Particles/prefabs.xml", Cam);
|
||||
yield return Status.Running;
|
||||
|
||||
GUIComponent.Init(Window);
|
||||
DebugConsole.Init(Window);
|
||||
yield return Status.Running;
|
||||
|
||||
LocationType.Init("Content/Map/locationTypes.xml");
|
||||
MainMenuScreen.Select();
|
||||
yield return Status.Running;
|
||||
|
||||
loadState = 100.0f;
|
||||
yield return Status.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UnloadContent will be called once per game and is the place to unload
|
||||
/// all content.
|
||||
/// </summary>
|
||||
protected override void UnloadContent()
|
||||
{
|
||||
Sound.Dispose();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Allows the game to run logic such as updating the world,
|
||||
/// checking for collisions, gathering input, and playing audio.
|
||||
/// </summary>
|
||||
/// <param name="gameTime">Provides a snapshot of timing values.</param>
|
||||
protected override void Update(GameTime gameTime)
|
||||
{
|
||||
base.Update(gameTime);
|
||||
|
||||
double deltaTime = gameTime.ElapsedGameTime.TotalSeconds;
|
||||
PlayerInput.Update(deltaTime);
|
||||
|
||||
if (loadState >= 100.0f && !titleScreenOpen)
|
||||
{
|
||||
if (PlayerInput.KeyHit(Keys.Escape)) GUI.TogglePauseMenu();
|
||||
|
||||
DebugConsole.Update(this, (float)deltaTime);
|
||||
|
||||
if ((!DebugConsole.IsOpen && !GUI.PauseMenuOpen) || NetworkMember != null) Screen.Selected.Update(deltaTime);
|
||||
|
||||
GUI.Update((float)deltaTime);
|
||||
|
||||
if (NetworkMember != null)
|
||||
{
|
||||
NetworkMember.Update();
|
||||
}
|
||||
else
|
||||
{
|
||||
NetworkEvent.events.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
CoroutineManager.Update();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This is called when the game should draw itself.
|
||||
/// </summary>
|
||||
protected override void Draw(GameTime gameTime)
|
||||
{
|
||||
//renderTimer.Restart();
|
||||
|
||||
double deltaTime = gameTime.ElapsedGameTime.TotalSeconds;
|
||||
|
||||
FrameCounter.Update(deltaTime);
|
||||
|
||||
if (titleScreenOpen)
|
||||
{
|
||||
TitleScreen.Draw(spriteBatch, GraphicsDevice, loadState, (float)deltaTime);
|
||||
if (loadState>=100.0f && (PlayerInput.GetKeyboardState.GetPressedKeys().Length>0 || PlayerInput.LeftButtonClicked()))
|
||||
{
|
||||
titleScreenOpen = false;
|
||||
}
|
||||
}
|
||||
else if (loadState >= 100.0f)
|
||||
{
|
||||
Screen.Selected.Draw(deltaTime, GraphicsDevice, spriteBatch);
|
||||
}
|
||||
|
||||
//renderTimeElapsed = (int)renderTimer.Elapsed.Ticks;
|
||||
//renderTimer.Stop();
|
||||
if (sw.Elapsed.TotalSeconds < Physics.step)
|
||||
{
|
||||
System.Threading.Thread.Sleep((int)((Physics.step - sw.Elapsed.TotalSeconds)*1000.0));
|
||||
}
|
||||
sw.Restart();
|
||||
}
|
||||
|
||||
Stopwatch sw;
|
||||
|
||||
protected override void OnExiting(object sender, EventArgs args)
|
||||
{
|
||||
if (NetworkMember != null) NetworkMember.Disconnect();
|
||||
|
||||
base.OnExiting(sender, args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
53
Subsurface/Source/GameSession/CargoManager.cs
Normal file
53
Subsurface/Source/GameSession/CargoManager.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class CargoManager
|
||||
{
|
||||
private List<MapEntityPrefab> purchasedItems;
|
||||
|
||||
public CargoManager()
|
||||
{
|
||||
purchasedItems = new List<MapEntityPrefab>();
|
||||
}
|
||||
|
||||
public void AddItem(MapEntityPrefab item)
|
||||
{
|
||||
purchasedItems.Add(item);
|
||||
}
|
||||
|
||||
public void CreateItems()
|
||||
{
|
||||
WayPoint wp = WayPoint.GetRandom(SpawnType.Cargo);
|
||||
|
||||
if (wp==null)
|
||||
{
|
||||
DebugConsole.ThrowError("The submarine must have a waypoint marked as Cargo for bought items to be placed correctly!");
|
||||
return;
|
||||
}
|
||||
|
||||
Hull cargoRoom = Hull.FindHull(wp.Position);
|
||||
|
||||
if (wp == null)
|
||||
{
|
||||
DebugConsole.ThrowError("A waypoint marked as Cargo must be placed inside a room!");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (MapEntityPrefab prefab in purchasedItems)
|
||||
{
|
||||
Vector2 position = new Vector2(
|
||||
Rand.Range(cargoRoom.Rect.X + 20, cargoRoom.Rect.Right - 20),
|
||||
Rand.Range(cargoRoom.Rect.Y - cargoRoom.Rect.Height + 20.0f, cargoRoom.Rect.Y));
|
||||
|
||||
new Item(prefab as ItemPrefab, wp.Position);
|
||||
}
|
||||
|
||||
purchasedItems.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
180
Subsurface/Source/GameSession/CrewManager.cs
Normal file
180
Subsurface/Source/GameSession/CrewManager.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
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 CrewManager
|
||||
{
|
||||
public List<Character> characters;
|
||||
public List<CharacterInfo> 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()
|
||||
{
|
||||
characters = new List<Character>();
|
||||
characterInfos = new List<CharacterInfo>();
|
||||
|
||||
guiFrame = new GUIFrame(new Rectangle(0, 50, 150, 450), Color.Transparent);
|
||||
|
||||
listBox = new GUIListBox(new Rectangle(0, 0, 150, 0), Color.Transparent, null, guiFrame);
|
||||
listBox.ScrollBarEnabled = false;
|
||||
listBox.OnSelected = SelectCharacter;
|
||||
|
||||
money = 10000;
|
||||
}
|
||||
|
||||
public CrewManager(XElement element)
|
||||
: this()
|
||||
{
|
||||
money = ToolBox.GetAttributeInt(element, "money", 0);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLower()!="character") continue;
|
||||
|
||||
characterInfos.Add(new CharacterInfo(subElement));
|
||||
}
|
||||
}
|
||||
|
||||
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, null, 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.White,
|
||||
Alignment.Left, Alignment.Left,
|
||||
null, frame);
|
||||
textBlock.Padding = new Vector4(5.0f, 0.0f, 5.0f, 0.0f);
|
||||
|
||||
new GUIImage(new Rectangle(-10, -10, 0, 0), character.AnimController.limbs[0].sprite, Alignment.Left, frame);
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
guiFrame.Update(deltaTime);
|
||||
}
|
||||
|
||||
public void KillCharacter(Character killedCharacter)
|
||||
{
|
||||
GUIComponent characterBlock = listBox.GetChild(killedCharacter) as GUIComponent;
|
||||
if (characterBlock != null) characterBlock.Color = Color.DarkRed * 0.5f;
|
||||
|
||||
//if (characters.Find(c => !c.IsDead)==null)
|
||||
//{
|
||||
// Game1.GameSession.EndShift(null, null);
|
||||
//}
|
||||
}
|
||||
|
||||
public void StartShift()
|
||||
{
|
||||
listBox.ClearChildren();
|
||||
characters.Clear();
|
||||
|
||||
WayPoint[] waypoints = WayPoint.SelectCrewSpawnPoints(characterInfos);
|
||||
|
||||
for (int i = 0; i < waypoints.Length; i++)
|
||||
{
|
||||
//WayPoint randomWayPoint = WayPoint.GetRandom(SpawnType.Human);
|
||||
//Vector2 position = (randomWayPoint == null) ? Vector2.Zero : randomWayPoint.SimPosition;
|
||||
|
||||
Character character = new Character(characterInfos[i], waypoints[i]);
|
||||
Character.Controlled = character;
|
||||
|
||||
if (!character.Info.StartItemsGiven)
|
||||
{
|
||||
character.GiveJobItems(waypoints[i]);
|
||||
character.Info.StartItemsGiven = true;
|
||||
}
|
||||
|
||||
AddCharacter(character);
|
||||
}
|
||||
|
||||
if (characters.Count > 0) SelectCharacter(characters[0]);
|
||||
}
|
||||
|
||||
public void EndShift()
|
||||
{
|
||||
foreach (Character c in characters)
|
||||
{
|
||||
if (!c.IsDead)
|
||||
{
|
||||
c.Info.UpdateCharacterItems();
|
||||
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);
|
||||
}
|
||||
|
||||
public void Save(XElement parentElement)
|
||||
{
|
||||
XElement element = new XElement("crew");
|
||||
|
||||
element.Add(new XAttribute("money", money));
|
||||
|
||||
foreach (CharacterInfo ci in characterInfos)
|
||||
{
|
||||
ci.Save(element);
|
||||
}
|
||||
|
||||
parentElement.Add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
154
Subsurface/Source/GameSession/GameMode.cs
Normal file
154
Subsurface/Source/GameSession/GameMode.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class GameModePreset
|
||||
{
|
||||
public static List<GameModePreset> list = new List<GameModePreset>();
|
||||
|
||||
public ConstructorInfo Constructor;
|
||||
public string Name;
|
||||
public bool IsSinglePlayer;
|
||||
|
||||
public GameModePreset(string name, Type type, bool isSinglePlayer = false)
|
||||
{
|
||||
this.Name = name;
|
||||
//Constructor = constructor;
|
||||
|
||||
|
||||
Constructor = type.GetConstructor(new Type[] { typeof(GameModePreset)});
|
||||
|
||||
IsSinglePlayer = isSinglePlayer;
|
||||
|
||||
list.Add(this);
|
||||
}
|
||||
|
||||
public GameMode Instantiate()
|
||||
{
|
||||
object[] lobject = new object[] { this };
|
||||
return(GameMode)Constructor.Invoke(lobject);
|
||||
}
|
||||
}
|
||||
|
||||
class GameMode
|
||||
{
|
||||
public static List<GameModePreset> presetList = new List<GameModePreset>();
|
||||
|
||||
TimeSpan duration;
|
||||
protected DateTime startTime;
|
||||
protected DateTime endTime;
|
||||
|
||||
//public readonly bool IsSinglePlayer;
|
||||
|
||||
private GUIProgressBar timerBar;
|
||||
|
||||
protected bool isRunning;
|
||||
|
||||
//protected string name;
|
||||
|
||||
protected GameModePreset preset;
|
||||
|
||||
private string endMessage;
|
||||
|
||||
public virtual Quest Quest
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public DateTime StartTime
|
||||
{
|
||||
get { return startTime; }
|
||||
}
|
||||
|
||||
public DateTime EndTime
|
||||
{
|
||||
get { return endTime; }
|
||||
}
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
get { return isRunning; }
|
||||
}
|
||||
|
||||
public bool IsSinglePlayer
|
||||
{
|
||||
get { return preset.IsSinglePlayer; }
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return preset.Name; }
|
||||
}
|
||||
|
||||
public string EndMessage
|
||||
{
|
||||
get { return endMessage; }
|
||||
}
|
||||
|
||||
public GameMode(GameModePreset preset)
|
||||
{
|
||||
this.preset = preset;
|
||||
|
||||
|
||||
//list.Add(this);
|
||||
}
|
||||
|
||||
public virtual void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (timerBar != null) timerBar.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
public virtual void Start(TimeSpan duration)
|
||||
{
|
||||
startTime = DateTime.Now;
|
||||
if (duration!=TimeSpan.Zero)
|
||||
{
|
||||
endTime = startTime + duration;
|
||||
this.duration = duration;
|
||||
|
||||
timerBar = new GUIProgressBar(new Rectangle(Game1.GraphicsWidth - 120, 20, 100, 25), Color.Gold, 0.0f, null);
|
||||
}
|
||||
|
||||
endMessage = "The round has ended!";
|
||||
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
public virtual void Update(float deltaTime)
|
||||
{
|
||||
if (!isRunning) return;
|
||||
|
||||
if (duration != TimeSpan.Zero)
|
||||
{
|
||||
double elapsedTime = (DateTime.Now - startTime).TotalSeconds;
|
||||
timerBar.BarSize = (float)(elapsedTime / duration.TotalSeconds);
|
||||
}
|
||||
//if (DateTime.Now >= endTime)
|
||||
//{
|
||||
// End(endMessage);
|
||||
//}
|
||||
}
|
||||
|
||||
public virtual void End(string endMessage = "")
|
||||
{
|
||||
isRunning = false;
|
||||
|
||||
if (endMessage != "" || this.endMessage == null) this.endMessage = endMessage;
|
||||
|
||||
Game1.GameSession.EndShift(endMessage);
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
new GameModePreset("Single Player", typeof(SinglePlayerMode), true);
|
||||
|
||||
new GameModePreset("SandBox", typeof(GameMode), false);
|
||||
new GameModePreset("Traitor", typeof(TraitorMode), false);
|
||||
new GameModePreset("Quest", typeof(QuestMode), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
203
Subsurface/Source/GameSession/GameSession.cs
Normal file
203
Subsurface/Source/GameSession/GameSession.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class GameSession
|
||||
{
|
||||
public readonly TaskManager taskManager;
|
||||
|
||||
|
||||
//protected DateTime startTime;
|
||||
//protected DateTime endTime;
|
||||
|
||||
public readonly GameMode gameMode;
|
||||
|
||||
private GUIFrame guiRoot;
|
||||
|
||||
//private GUIListBox chatBox;
|
||||
//private GUITextBox textBox;
|
||||
|
||||
private string saveFile;
|
||||
|
||||
private Submarine submarine;
|
||||
|
||||
|
||||
public Quest Quest
|
||||
{
|
||||
get
|
||||
{
|
||||
if (gameMode != null) return gameMode.Quest;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Level level;
|
||||
|
||||
public Level Level
|
||||
{
|
||||
get { return level; }
|
||||
}
|
||||
|
||||
public Map Map
|
||||
{
|
||||
get
|
||||
{
|
||||
SinglePlayerMode mode = (gameMode as SinglePlayerMode);
|
||||
return (mode == null) ? null : mode.map;
|
||||
}
|
||||
}
|
||||
|
||||
public Submarine Submarine
|
||||
{
|
||||
get { return submarine; }
|
||||
}
|
||||
|
||||
public string SaveFile
|
||||
{
|
||||
get { return saveFile; }
|
||||
}
|
||||
|
||||
public GameSession(Submarine submarine, string saveFile, GameModePreset gameModePreset)
|
||||
:this(submarine, saveFile, gameModePreset.Instantiate())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public GameSession(Submarine selectedSub, string saveFile, GameMode gameMode = null)
|
||||
{
|
||||
taskManager = new TaskManager(this);
|
||||
|
||||
this.saveFile = saveFile;
|
||||
|
||||
guiRoot = new GUIFrame(new Rectangle(0,0,Game1.GraphicsWidth,Game1.GraphicsWidth), Color.Transparent);
|
||||
|
||||
this.gameMode = gameMode;
|
||||
this.submarine = selectedSub;
|
||||
|
||||
}
|
||||
|
||||
public GameSession(Submarine selectedSub, string saveFile, string filePath)
|
||||
: this(selectedSub, saveFile)
|
||||
{
|
||||
XDocument doc = ToolBox.TryLoadXml(filePath);
|
||||
if (doc == null) return;
|
||||
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLower() != "gamemode") continue;
|
||||
|
||||
gameMode = new SinglePlayerMode(subElement);
|
||||
}
|
||||
}
|
||||
|
||||
public void StartShift(TimeSpan duration, string levelSeed)
|
||||
{
|
||||
Level level = Level.CreateRandom(levelSeed);
|
||||
|
||||
StartShift(duration, level);
|
||||
}
|
||||
|
||||
public void StartShift(TimeSpan duration, Level level, bool reloadSub = true)
|
||||
{
|
||||
Game1.LightManager.LosEnabled = (Game1.Server==null);
|
||||
|
||||
this.level = level;
|
||||
|
||||
if (reloadSub || Submarine.Loaded != submarine) submarine.Load();
|
||||
|
||||
if (level != null)
|
||||
{
|
||||
level.Generate(submarine == null ? 100.0f : Math.Max(Submarine.Borders.Width, Submarine.Borders.Height));
|
||||
submarine.SetPosition(level.StartPosition - new Vector2(0.0f, 2000.0f));
|
||||
}
|
||||
|
||||
if (Quest!=null) Quest.Start(Level.Loaded);
|
||||
|
||||
if (gameMode!=null) gameMode.Start(duration);
|
||||
|
||||
taskManager.StartShift(level);
|
||||
}
|
||||
|
||||
public void EndShift(string endMessage)
|
||||
{
|
||||
if (Quest != null) Quest.End();
|
||||
|
||||
if (Game1.Server!=null)
|
||||
{
|
||||
Game1.Server.EndGame(endMessage);
|
||||
|
||||
}
|
||||
else if (Game1.Client==null)
|
||||
{
|
||||
Game1.LobbyScreen.Select();
|
||||
}
|
||||
|
||||
taskManager.EndShift();
|
||||
//gameMode.End();
|
||||
|
||||
//return true;
|
||||
}
|
||||
|
||||
|
||||
public void KillCharacter(Character character)
|
||||
{
|
||||
SinglePlayerMode singlePlayerMode = gameMode as SinglePlayerMode;
|
||||
if (singlePlayerMode == null) return;
|
||||
singlePlayerMode.crewManager.KillCharacter(character);
|
||||
}
|
||||
|
||||
public bool LoadPrevious(GUIButton button, object obj)
|
||||
{
|
||||
SaveUtil.LoadGame(saveFile);
|
||||
|
||||
Game1.LobbyScreen.Select();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
taskManager.Update(deltaTime);
|
||||
|
||||
guiRoot.Update(deltaTime);
|
||||
|
||||
if (gameMode != null) gameMode.Update(deltaTime);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
guiRoot.Draw(spriteBatch);
|
||||
taskManager.Draw(spriteBatch);
|
||||
|
||||
if (gameMode != null) gameMode.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
public void Save(string filePath)
|
||||
{
|
||||
XDocument doc = new XDocument(
|
||||
new XElement((XName)"Gamesession"));
|
||||
|
||||
var now = DateTime.Now;
|
||||
doc.Root.Add(new XAttribute("savetime", now.Hour + ":" + now.Minute + ", " + now.ToShortDateString()));
|
||||
|
||||
((SinglePlayerMode)gameMode).Save(doc.Root);
|
||||
|
||||
try
|
||||
{
|
||||
doc.Save(filePath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
DebugConsole.ThrowError("Saving gamesession to ''" + filePath + "'' failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Subsurface/Source/GameSession/HireManager.cs
Normal file
25
Subsurface/Source/GameSession/HireManager.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class HireManager
|
||||
{
|
||||
public List<CharacterInfo> availableCharacters;
|
||||
|
||||
const int MaxAvailableCharacters = 10;
|
||||
|
||||
public HireManager()
|
||||
{
|
||||
availableCharacters = new List<CharacterInfo>();
|
||||
}
|
||||
|
||||
public void GenerateCharacters(string file, int amount)
|
||||
{
|
||||
|
||||
for (int i = 0 ; i<amount ; i++)
|
||||
{
|
||||
availableCharacters.Add(new CharacterInfo(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Subsurface/Source/GameSession/QuestMode.cs
Normal file
51
Subsurface/Source/GameSession/QuestMode.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class QuestMode : GameMode
|
||||
{
|
||||
Quest quest;
|
||||
|
||||
public override Quest Quest
|
||||
{
|
||||
get
|
||||
{
|
||||
return quest;
|
||||
}
|
||||
}
|
||||
|
||||
public QuestMode(GameModePreset preset)
|
||||
: base(preset)
|
||||
{
|
||||
Location[] locations = new Location[2];
|
||||
|
||||
Random rand = new Random(Game1.NetLobbyScreen.LevelSeed.GetHashCode());
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
locations[i] = Location.CreateRandom(new Vector2((float)rand.NextDouble() * 10000.0f, (float)rand.NextDouble() * 10000.0f));
|
||||
}
|
||||
quest = Quest.LoadRandom(locations, rand);
|
||||
}
|
||||
|
||||
public override void Start(TimeSpan duration)
|
||||
{
|
||||
base.Start(duration);
|
||||
|
||||
new GUIMessageBox(quest.Name, quest.Description, 400, 400);
|
||||
|
||||
quest.Start(Level.Loaded);
|
||||
}
|
||||
|
||||
public override void End(string endMessage = "")
|
||||
{
|
||||
quest.End();
|
||||
|
||||
base.End(endMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
253
Subsurface/Source/GameSession/SinglePlayerMode.cs
Normal file
253
Subsurface/Source/GameSession/SinglePlayerMode.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
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
|
||||
{
|
||||
class SinglePlayerMode : GameMode
|
||||
{
|
||||
//private const int StartCharacterAmount = 3;
|
||||
|
||||
public readonly CrewManager crewManager;
|
||||
//public readonly HireManager hireManager;
|
||||
|
||||
private GUIButton endShiftButton;
|
||||
|
||||
public readonly CargoManager CargoManager;
|
||||
|
||||
public Map map;
|
||||
|
||||
private bool crewDead;
|
||||
private float endTimer;
|
||||
|
||||
private bool savedOnStart;
|
||||
|
||||
public override Quest Quest
|
||||
{
|
||||
get
|
||||
{
|
||||
return map.SelectedConnection.Quest;
|
||||
}
|
||||
}
|
||||
|
||||
public int Money
|
||||
{
|
||||
get { return crewManager.Money; }
|
||||
set { crewManager.Money = value; }
|
||||
}
|
||||
|
||||
public SinglePlayerMode(GameModePreset preset)
|
||||
: base(preset)
|
||||
{
|
||||
crewManager = new CrewManager();
|
||||
|
||||
CargoManager = new CargoManager();
|
||||
|
||||
endShiftButton = new GUIButton(new Rectangle(Game1.GraphicsWidth - 220, 20, 200, 25), "End shift", Alignment.TopLeft, GUI.style);
|
||||
endShiftButton.OnClicked = EndShift;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
JobPrefab jobPrefab = null;
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
jobPrefab = JobPrefab.List.Find(jp => jp.Name == "Captain");
|
||||
break;
|
||||
case 1:
|
||||
jobPrefab = JobPrefab.List.Find(jp => jp.Name == "Engineer");
|
||||
break;
|
||||
case 2:
|
||||
jobPrefab = JobPrefab.List.Find(jp => jp.Name == "Mechanic");
|
||||
break;
|
||||
}
|
||||
|
||||
CharacterInfo characterInfo =
|
||||
new CharacterInfo(Character.HumanConfigFile, "", Gender.None, jobPrefab);
|
||||
crewManager.characterInfos.Add(characterInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public SinglePlayerMode(XElement element)
|
||||
: this(GameModePreset.list.Find(gm => gm.Name == "Single Player"))
|
||||
{
|
||||
string mapSeed = ToolBox.GetAttributeString(element, "mapseed", "a");
|
||||
|
||||
GenerateMap(mapSeed);
|
||||
|
||||
map.SetLocation(ToolBox.GetAttributeInt(element, "currentlocation", 0));
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLower() != "crew") continue;
|
||||
|
||||
crewManager = new CrewManager(subElement);
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateMap(string seed)
|
||||
{
|
||||
map = new Map(seed, 500);
|
||||
}
|
||||
|
||||
public override void Start(TimeSpan duration)
|
||||
{
|
||||
CargoManager.CreateItems();
|
||||
|
||||
if (!savedOnStart)
|
||||
{
|
||||
SaveUtil.SaveGame(Game1.GameSession.SaveFile);
|
||||
savedOnStart = true;
|
||||
}
|
||||
|
||||
endTimer = 5.0f;
|
||||
|
||||
crewManager.StartShift();
|
||||
}
|
||||
|
||||
public bool TryHireCharacter(HireManager hireManager, 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 override void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
base.Draw(spriteBatch);
|
||||
|
||||
crewManager.Draw(spriteBatch);
|
||||
|
||||
if (Level.Loaded.AtEndPosition)
|
||||
{
|
||||
endShiftButton.Text = "Enter " + map.SelectedLocation.Name;
|
||||
endShiftButton.Draw(spriteBatch);
|
||||
}
|
||||
else if (Level.Loaded.AtStartPosition)
|
||||
{
|
||||
endShiftButton.Text = "Enter " + map.CurrentLocation.Name;
|
||||
endShiftButton.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
//chatBox.Draw(spriteBatch);
|
||||
//textBox.Draw(spriteBatch);
|
||||
|
||||
//timerBar.Draw(spriteBatch);
|
||||
|
||||
//if (Game1.Client == null) endShiftButton.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
crewManager.Update(deltaTime);
|
||||
|
||||
endShiftButton.Update(deltaTime);
|
||||
|
||||
if (!crewDead)
|
||||
{
|
||||
if (crewManager.characters.Find(c => !c.IsDead) == null)
|
||||
{
|
||||
crewDead = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
endTimer -= deltaTime;
|
||||
|
||||
if (endTimer <= 0.0f) End("");
|
||||
}
|
||||
}
|
||||
|
||||
public override void End(string endMessage = "")
|
||||
{
|
||||
|
||||
isRunning = false;
|
||||
|
||||
//if (endMessage != "" || this.endMessage == null) this.endMessage = endMessage;
|
||||
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
List<Character> casualties = crewManager.characters.FindAll(c => c.IsDead);
|
||||
|
||||
if (casualties.Count == crewManager.characters.Count)
|
||||
{
|
||||
sb.Append("Your entire crew has died!");
|
||||
|
||||
var msgBox = new GUIMessageBox("", sb.ToString(), new string[] { "Load game", "Quit" });
|
||||
msgBox.Buttons[0].OnClicked += Game1.GameSession.LoadPrevious;
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
msgBox.Buttons[1].OnClicked = Game1.LobbyScreen.QuitToMainMenu;
|
||||
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (casualties.Any())
|
||||
{
|
||||
sb.Append("Casualties: \n");
|
||||
foreach (Character c in casualties)
|
||||
{
|
||||
sb.Append(" - " + c.Info.Name + "\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append("No casualties!");
|
||||
}
|
||||
|
||||
if (Level.Loaded.AtEndPosition)
|
||||
{
|
||||
map.MoveToNextLocation();
|
||||
}
|
||||
|
||||
SaveUtil.SaveGame(Game1.GameSession.SaveFile);
|
||||
}
|
||||
|
||||
crewManager.EndShift();
|
||||
for (int i = Character.CharacterList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
Character.CharacterList[i].Remove();
|
||||
}
|
||||
|
||||
Game1.GameSession.EndShift("");
|
||||
|
||||
}
|
||||
|
||||
private bool EndShift(GUIButton button, object obj)
|
||||
{
|
||||
End("");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Save(XElement element)
|
||||
{
|
||||
//element.Add(new XAttribute("day", day));
|
||||
XElement modeElement = new XElement("gamemode");
|
||||
|
||||
modeElement.Add(new XAttribute("currentlocation", map.CurrentLocationIndex));
|
||||
modeElement.Add(new XAttribute("mapseed", map.Seed));
|
||||
|
||||
crewManager.Save(modeElement);
|
||||
|
||||
element.Add(modeElement);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
74
Subsurface/Source/GameSession/TraitorMode.cs
Normal file
74
Subsurface/Source/GameSession/TraitorMode.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Subsurface.Networking;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class TraitorMode : GameMode
|
||||
{
|
||||
Client traitor;
|
||||
Client target;
|
||||
|
||||
public TraitorMode(GameModePreset preset)
|
||||
: base(preset)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void Start(TimeSpan duration)
|
||||
{
|
||||
base.Start(duration);
|
||||
|
||||
traitor = null;
|
||||
target = null;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (Game1.Server == null) return;
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (!isRunning) return;
|
||||
|
||||
if (DateTime.Now >= 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 = Rand.Int(clientCount, false);
|
||||
traitor = Game1.Server.connectedClients[traitorIndex];
|
||||
|
||||
int targetIndex = 0;
|
||||
while (targetIndex == traitorIndex)
|
||||
{
|
||||
targetIndex = Rand.Int(clientCount, false);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
102
Subsurface/Source/GameSettings.cs
Normal file
102
Subsurface/Source/GameSettings.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
public class GameSettings
|
||||
{
|
||||
public int GraphicsWidth
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public int GraphicsHeight
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool FullScreenEnabled
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ContentPackage SelectedContentPackage
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public GameSettings(string filePath)
|
||||
{
|
||||
Load(filePath);
|
||||
}
|
||||
|
||||
public void Load(string filePath)
|
||||
{
|
||||
XDocument doc = ToolBox.TryLoadXml(filePath);
|
||||
try
|
||||
{
|
||||
XElement graphicsMode = doc.Root.Element("graphicsmode");
|
||||
GraphicsWidth = int.Parse(graphicsMode.Attribute("width").Value);
|
||||
GraphicsHeight = int.Parse(graphicsMode.Attribute("height").Value);
|
||||
|
||||
FullScreenEnabled = graphicsMode.Attribute("fullscreen").Value == "true";
|
||||
}
|
||||
catch
|
||||
{
|
||||
GraphicsWidth = 1024;
|
||||
GraphicsHeight = 768;
|
||||
}
|
||||
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLower())
|
||||
{
|
||||
case "contentpackage":
|
||||
string path = ToolBox.GetAttributeString(subElement, "path", "");
|
||||
SelectedContentPackage = ContentPackage.list.Find(cp => cp.Path == path);
|
||||
|
||||
if (SelectedContentPackage == null) SelectedContentPackage = new ContentPackage(path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Save(string filePath)
|
||||
{
|
||||
XDocument doc = new XDocument();
|
||||
|
||||
if (doc.Root == null)
|
||||
{
|
||||
doc.Add(new XElement("config"));
|
||||
}
|
||||
|
||||
XElement gMode = doc.Root.Element("graphicsmode");
|
||||
if (gMode == null)
|
||||
{
|
||||
gMode = new XElement("graphicsmode");
|
||||
doc.Root.Add(gMode);
|
||||
}
|
||||
|
||||
gMode.ReplaceAttributes(
|
||||
new XAttribute("width", GraphicsWidth),
|
||||
new XAttribute("height", GraphicsHeight),
|
||||
new XAttribute("fullscreen", FullScreenEnabled ? "true" : "false"));
|
||||
|
||||
if (SelectedContentPackage != null)
|
||||
{
|
||||
doc.Root.Add(new XElement("contentpackage",
|
||||
new XAttribute("path", SelectedContentPackage.Path)));
|
||||
}
|
||||
|
||||
|
||||
doc.Save(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
Subsurface/Source/IPropertyObject.cs
Normal file
17
Subsurface/Source/IPropertyObject.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
interface IPropertyObject
|
||||
{
|
||||
string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
Dictionary<string, ObjectProperty> ObjectProperties
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
279
Subsurface/Source/Items/CharacterInventory.cs
Normal file
279
Subsurface/Source/Items/CharacterInventory.cs
Normal file
@@ -0,0 +1,279 @@
|
||||
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 static Texture2D icons;
|
||||
|
||||
private Character character;
|
||||
|
||||
private static LimbSlot[] limbSlots = new LimbSlot[] {
|
||||
LimbSlot.Head, LimbSlot.Torso, LimbSlot.Legs, LimbSlot.LeftHand, LimbSlot.RightHand,
|
||||
LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any, LimbSlot.Any };
|
||||
|
||||
private Vector2[] slotPositions;
|
||||
|
||||
public CharacterInventory(int capacity, Character character)
|
||||
: base(capacity)
|
||||
{
|
||||
this.character = character;
|
||||
|
||||
if (icons == null) icons = Game1.TextureLoader.FromFile("Content/UI/inventoryIcons.png");
|
||||
|
||||
slotPositions = new Vector2[limbSlots.Length];
|
||||
|
||||
|
||||
int rectWidth = 40, rectHeight = 40;
|
||||
int spacing = 10;
|
||||
for (int i = 0; i < slotPositions.Length; i++)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
//head, torso, legs
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
slotPositions[i] = new Vector2(
|
||||
spacing,
|
||||
Game1.GraphicsHeight - (spacing + rectHeight) * (3 - i));
|
||||
break;
|
||||
//lefthand, righthand
|
||||
case 3:
|
||||
case 4:
|
||||
slotPositions[i] = new Vector2(
|
||||
spacing * 2 + rectWidth + (spacing + rectWidth) * (i - 3),
|
||||
Game1.GraphicsHeight - (spacing + rectHeight));
|
||||
break;
|
||||
default:
|
||||
slotPositions[i] = new Vector2(
|
||||
spacing * (4 + (i - 5)) + rectWidth * (3 + (i - 5)),
|
||||
Game1.GraphicsHeight - (spacing + rectHeight));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DropItem(Item item)
|
||||
{
|
||||
item.Drop(character);
|
||||
item.body.SetTransform(character.SimPosition, 0.0f);
|
||||
}
|
||||
|
||||
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<items.Length; i++)
|
||||
{
|
||||
if (items[i] == item && limbSlots[i] == limbSlot) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If there is room, puts the item in the inventory and returns true, otherwise returns false
|
||||
/// </summary>
|
||||
public override bool TryPutItem(Item item, LimbSlot allowedSlots, bool createNetworkEvent = true)
|
||||
{
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
//item is already in the inventory!
|
||||
if (items[i] == item) return true;
|
||||
}
|
||||
|
||||
if (allowedSlots.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 (allowedSlots.HasFlag(limbSlots[i]) && items[i]!=null) return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
if (allowedSlots.HasFlag(limbSlots[i]) && items[i] == null)
|
||||
{
|
||||
PutItem(item, i, createNetworkEvent);
|
||||
item.Equip(character);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (allowedSlots.HasFlag(LimbSlot.BothHands)) TryPutItem(item, 3, createNetworkEvent);
|
||||
|
||||
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;
|
||||
Rectangle slotRect = new Rectangle(0, 0, rectWidth, rectHeight);
|
||||
Rectangle draggingItemSlot = slotRect;
|
||||
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
slotRect.X = (int)slotPositions[i].X;
|
||||
slotRect.Y = (int)slotPositions[i].Y;
|
||||
|
||||
if (i==1) //head
|
||||
{
|
||||
spriteBatch.Draw(icons, new Vector2(slotRect.Center.X, slotRect.Center.Y),
|
||||
new Rectangle(0,0,56,128), Color.White*0.7f, 0.0f,
|
||||
new Vector2(28.0f, 64.0f), Vector2.One,
|
||||
SpriteEffects.None, 0.1f);
|
||||
}
|
||||
else if (i==3 || i==4)
|
||||
{
|
||||
spriteBatch.Draw(icons, new Vector2(slotRect.Center.X, slotRect.Center.Y),
|
||||
new Rectangle(92, 41*(4-i), 36, 40), Color.White * 0.7f, 0.0f,
|
||||
new Vector2(18.0f, 20.0f), Vector2.One,
|
||||
SpriteEffects.None, 0.1f);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
slotRect.X = (int)slotPositions[i].X;
|
||||
slotRect.Y = (int)slotPositions[i].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.body.SetTransform(character.SimPosition, 0.0f);
|
||||
DropItem(draggingItem);
|
||||
//draggingItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
273
Subsurface/Source/Items/Components/Container.cs
Normal file
273
Subsurface/Source/Items/Components/Container.cs
Normal file
@@ -0,0 +1,273 @@
|
||||
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<RelatedItem> containableItems;
|
||||
public ItemInventory inventory;
|
||||
|
||||
//how many items can be contained
|
||||
[HasDefaultValue(5, false)]
|
||||
public int Capacity
|
||||
{
|
||||
get { return capacity; }
|
||||
set { capacity = Math.Max(value, 1); }
|
||||
}
|
||||
private int capacity;
|
||||
|
||||
[HasDefaultValue(true, false)]
|
||||
public bool HideItems
|
||||
{
|
||||
get { return hideItems; }
|
||||
set { hideItems = value; }
|
||||
}
|
||||
private bool hideItems;
|
||||
|
||||
[HasDefaultValue(false, false)]
|
||||
public bool DrawInventory
|
||||
{
|
||||
get { return drawInventory; }
|
||||
set { drawInventory = value; }
|
||||
}
|
||||
private bool drawInventory;
|
||||
|
||||
//the position of the first item in the container
|
||||
[HasDefaultValue("0.0,0.0", false)]
|
||||
public string ItemPos
|
||||
{
|
||||
get { return ToolBox.Vector2ToString(itemPos); }
|
||||
set { itemPos = ToolBox.ParseToVector2(value); }
|
||||
}
|
||||
private Vector2 itemPos;
|
||||
|
||||
//item[i].Pos = itemPos + itemInterval*i
|
||||
[HasDefaultValue("0.0,0.0", false)]
|
||||
public string ItemInterval
|
||||
{
|
||||
get { return ToolBox.Vector2ToString(itemInterval); }
|
||||
set { itemInterval = ToolBox.ParseToVector2(value); }
|
||||
}
|
||||
private Vector2 itemInterval;
|
||||
|
||||
[HasDefaultValue(0.0f, false)]
|
||||
public float ItemRotation
|
||||
{
|
||||
get { return itemRotation; }
|
||||
set { itemRotation = value; }
|
||||
}
|
||||
private float itemRotation;
|
||||
|
||||
|
||||
[HasDefaultValue("0.5,0.9", false)]
|
||||
public string HudPos
|
||||
{
|
||||
get { return ToolBox.Vector2ToString(hudPos); }
|
||||
set
|
||||
{
|
||||
hudPos = ToolBox.ParseToVector2(value);
|
||||
//inventory.CenterPos = hudPos;
|
||||
}
|
||||
}
|
||||
private Vector2 hudPos;
|
||||
|
||||
[HasDefaultValue(5, false)]
|
||||
public int SlotsPerRow
|
||||
{
|
||||
get { return slotsPerRow; }
|
||||
set { slotsPerRow = value; }
|
||||
}
|
||||
private int slotsPerRow;
|
||||
|
||||
public ItemContainer(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
inventory = new ItemInventory(this, capacity, hudPos, slotsPerRow);
|
||||
containableItems = new List<RelatedItem>();
|
||||
|
||||
//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)
|
||||
{
|
||||
if (containableItems.Count == 0) return true;
|
||||
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 || contained.Condition<=0.0f) continue;
|
||||
|
||||
if (contained.body!=null) contained.body.Enabled = false;
|
||||
|
||||
RelatedItem ri = containableItems.Find(x => x.MatchesItem(contained));
|
||||
if (ri == null) continue;
|
||||
|
||||
foreach (StatusEffect effect in ri.statusEffects)
|
||||
{
|
||||
if (effect.Targets.HasFlag(StatusEffect.TargetType.This)) effect.Apply(ActionType.OnContaining, deltaTime, item, item.AllPropertyObjects);
|
||||
if (effect.Targets.HasFlag(StatusEffect.TargetType.Contained)) effect.Apply(ActionType.OnContaining, deltaTime, item, contained.AllPropertyObjects);
|
||||
}
|
||||
|
||||
contained.ApplyStatusEffects(ActionType.OnContained, deltaTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch, bool editing)
|
||||
{
|
||||
base.Draw(spriteBatch);
|
||||
|
||||
if (hideItems || (item.body!=null && !item.body.Enabled)) return;
|
||||
|
||||
Vector2 transformedItemPos = itemPos;
|
||||
Vector2 transformedItemInterval = itemInterval;
|
||||
float currentRotation = itemRotation;
|
||||
//float transformedItemRotation = itemRotation;
|
||||
if (item.body == null)
|
||||
{
|
||||
transformedItemPos = new Vector2(item.Rect.X, item.Rect.Y);
|
||||
transformedItemPos = transformedItemPos + itemPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
//item.body.Enabled = true;
|
||||
|
||||
Matrix transform = Matrix.CreateRotationZ(item.body.Rotation);
|
||||
|
||||
if (item.body.Dir==-1.0f)
|
||||
{
|
||||
transformedItemPos.X = -transformedItemPos.X;
|
||||
transformedItemInterval.X = -transformedItemInterval.X;
|
||||
}
|
||||
transformedItemPos = Vector2.Transform(transformedItemPos, transform);
|
||||
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
|
||||
|
||||
transformedItemPos += ConvertUnits.ToDisplayUnits(item.body.Position);
|
||||
|
||||
currentRotation += item.body.Rotation;
|
||||
}
|
||||
|
||||
foreach (Item containedItem in inventory.items)
|
||||
{
|
||||
if (containedItem == null) continue;
|
||||
|
||||
containedItem.sprite.Draw(
|
||||
spriteBatch,
|
||||
new Vector2(transformedItemPos.X, -transformedItemPos.Y),
|
||||
-currentRotation,
|
||||
1.0f,
|
||||
(item.body != null && item.body.Dir == -1) ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
|
||||
|
||||
transformedItemPos += transformedItemInterval;
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
if (!drawInventory && false) return;
|
||||
|
||||
inventory.Draw(spriteBatch);
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return (picker != null);
|
||||
}
|
||||
|
||||
|
||||
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!=null && !item.body.Enabled)) item.body.Enabled = false;
|
||||
|
||||
item.container = this.item;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
if (itemIds == null) return;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
325
Subsurface/Source/Items/Components/Door.cs
Normal file
325
Subsurface/Source/Items/Components/Door.cs
Normal file
@@ -0,0 +1,325 @@
|
||||
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;
|
||||
|
||||
private float stuck;
|
||||
public float Stuck
|
||||
{
|
||||
get { return stuck; }
|
||||
set
|
||||
{
|
||||
if (isOpen) return;
|
||||
stuck = MathHelper.Clamp(value, 0.0f, 100.0f);
|
||||
if (stuck == 0.0f) isStuck = false;
|
||||
if (stuck == 100.0f) isStuck = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool isStuck;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private Rectangle doorRect;
|
||||
|
||||
public float OpenState
|
||||
{
|
||||
get { return openState; }
|
||||
set
|
||||
{
|
||||
|
||||
float prevValue = openState;
|
||||
openState = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
if (openState == prevValue) return;
|
||||
|
||||
UpdateConvexHulls();
|
||||
}
|
||||
}
|
||||
|
||||
PhysicsBody body;
|
||||
|
||||
Sprite doorSprite;
|
||||
|
||||
public Door(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
//Vector2 position = new Vector2(newRect.X, newRect.Y);
|
||||
|
||||
// isOpen = false;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLower() != "sprite") continue;
|
||||
doorSprite = new Sprite(subElement, Path.GetDirectoryName(item.Prefab.ConfigFile));
|
||||
break;
|
||||
}
|
||||
|
||||
doorRect = new Rectangle(
|
||||
item.Rect.Center.X - (int)(doorSprite.size.X / 2),
|
||||
item.Rect.Y,
|
||||
(int)doorSprite.size.X,
|
||||
(int)doorSprite.size.Y);
|
||||
|
||||
|
||||
body = new PhysicsBody(BodyFactory.CreateRectangle(Game1.World,
|
||||
ConvertUnits.ToSimUnits(Math.Max(doorRect.Width, 1)),
|
||||
ConvertUnits.ToSimUnits(Math.Max(doorRect.Height, 1)),
|
||||
1.5f));
|
||||
|
||||
body.CollisionCategories = Physics.CollisionWall;
|
||||
body.UserData = item;
|
||||
body.BodyType = BodyType.Static;
|
||||
body.SetTransform(
|
||||
ConvertUnits.ToSimUnits(new Vector2(doorRect.Center.X, doorRect.Y - doorRect.Height / 2)),
|
||||
0.0f);
|
||||
body.Friction = 0.5f;
|
||||
|
||||
|
||||
//string spritePath = Path.GetDirectoryName(item.Prefab.ConfigFile) + "\\"+ ToolBox.GetAttributeString(element, "sprite", "");
|
||||
|
||||
Vector2[] corners = GetConvexHullCorners(doorRect);
|
||||
|
||||
convexHull = new ConvexHull(corners, Color.Black);
|
||||
if (window!=Rectangle.Empty) convexHull2 = new ConvexHull(corners, Color.Black);
|
||||
|
||||
UpdateConvexHulls();
|
||||
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
private void UpdateConvexHulls()
|
||||
{
|
||||
Rectangle rect = doorRect;
|
||||
|
||||
rect.Height = (int)(rect.Height * (1.0f - openState));
|
||||
if (window.Height == 0 || window.Width == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//Rectangle rect = item.Rect;
|
||||
//rect.Height = (int)(rect.Height * (1.0f - openState));
|
||||
|
||||
rect.Height = -window.Y;
|
||||
|
||||
rect.Y += (int)(doorRect.Height * openState);
|
||||
rect.Height = Math.Max(rect.Height - (rect.Y - doorRect.Y), 0);
|
||||
rect.Y = Math.Min(doorRect.Y, rect.Y);
|
||||
|
||||
|
||||
if (convexHull2 != null)
|
||||
{
|
||||
Rectangle rect2 = doorRect;
|
||||
rect2.Y = rect2.Y + window.Y - window.Height;
|
||||
|
||||
rect2.Y += (int)(doorRect.Height * openState);
|
||||
rect2.Y = Math.Min(doorRect.Y, rect2.Y);
|
||||
rect2.Height = rect2.Y - (doorRect.Y - (int)(doorRect.Height * (1.0f - openState)));
|
||||
//convexHull2.SetVertices(GetConvexHullCorners(rect2));
|
||||
|
||||
if (rect2.Height == 0)
|
||||
{
|
||||
convexHull2.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
convexHull2.Enabled = true;
|
||||
convexHull2.SetVertices(GetConvexHullCorners(rect2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rect.Height == 0)
|
||||
{
|
||||
convexHull.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
convexHull.Enabled = true;
|
||||
convexHull.SetVertices(GetConvexHullCorners(rect));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
//LinkedGap.Move(amount);
|
||||
|
||||
body.SetTransform(body.Position + ConvertUnits.ToSimUnits(amount), 0.0f);
|
||||
|
||||
convexHull.Move(amount);
|
||||
if (convexHull2 != null) convexHull2.Move(amount);
|
||||
}
|
||||
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
isOpen = !isOpen;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (!isStuck)
|
||||
{
|
||||
OpenState += deltaTime * ((isOpen) ? 3.0f : -3.0f);
|
||||
LinkedGap.Open = openState;
|
||||
}
|
||||
|
||||
|
||||
item.SendSignal((isOpen) ? "1" : "0", "state_out");
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
body.Enabled = false;
|
||||
convexHull.Enabled = false;
|
||||
linkedGap.Open = 1.0f;
|
||||
if (convexHull2 != null) convexHull2.Enabled = false;
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch, bool editing)
|
||||
{
|
||||
Color color = (item.IsSelected) ? Color.Green : Color.White;
|
||||
color = color * (item.Condition / 100.0f);
|
||||
color.A = 255;
|
||||
|
||||
//prefab.sprite.Draw(spriteBatch, new Vector2(rect.X, -rect.Y), new Vector2(rect.Width, rect.Height), color);
|
||||
|
||||
if (openState == 1.0f)
|
||||
{
|
||||
body.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteBatch.Draw(doorSprite.Texture, new Vector2(item.Rect.Center.X, -item.Rect.Y),
|
||||
new Rectangle(doorSprite.SourceRect.X, (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);
|
||||
|
||||
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);
|
||||
|
||||
if (linkedGap!=null) linkedGap.Remove();
|
||||
|
||||
doorSprite.Remove();
|
||||
|
||||
convexHull.Remove();
|
||||
if (convexHull2 != null) convexHull2.Remove();
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(string signal, Connection connection, Item sender, float power=0.0f)
|
||||
{
|
||||
if (connection.Name=="toggle")
|
||||
{
|
||||
isOpen = !isOpen;
|
||||
}
|
||||
else if (connection.Name == "set_state")
|
||||
{
|
||||
isOpen = (signal!="0");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
234
Subsurface/Source/Items/Components/Holdable/Holdable.cs
Normal file
234
Subsurface/Source/Items/Components/Holdable/Holdable.cs
Normal file
@@ -0,0 +1,234 @@
|
||||
using System.Xml.Linq;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class Holdable : Pickable
|
||||
{
|
||||
//the position(s) in the item that the character grabs
|
||||
protected Vector2[] handlePos;
|
||||
|
||||
private List<RelatedItem> prevRequiredItems;
|
||||
|
||||
string prevMsg;
|
||||
|
||||
//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)]
|
||||
public 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]);
|
||||
}
|
||||
|
||||
canBePicked = true;
|
||||
|
||||
if (attachable)
|
||||
{
|
||||
prevRequiredItems = new List<RelatedItem>(requiredItems);
|
||||
prevMsg = Msg;
|
||||
|
||||
requiredItems.Clear();
|
||||
Msg = "";
|
||||
}
|
||||
|
||||
|
||||
//holdAngle = ToolBox.GetAttributeFloat(element, "holdangle", 0.0f);
|
||||
//holdAngle = MathHelper.ToRadians(holdAngle);
|
||||
}
|
||||
|
||||
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 base.Pick(picker);
|
||||
}
|
||||
|
||||
if (!base.Pick(picker))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
requiredItems.Clear();
|
||||
Msg = "";
|
||||
}
|
||||
|
||||
attached = false;
|
||||
if (body!=null) item.body = body;
|
||||
//item.body.Enabled = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (!attachable || item.body==null) return true;
|
||||
|
||||
item.Drop();
|
||||
item.body.Enabled = false;
|
||||
item.body = null;
|
||||
|
||||
requiredItems = new List<RelatedItem>(prevRequiredItems);
|
||||
Msg = prevMsg;
|
||||
|
||||
attached = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
Update(deltaTime, cam);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
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;
|
||||
|
||||
//item.sprite.Depth = picker.AnimController.GetLimb(LimbType.RightHand).sprite.Depth + 0.01f;
|
||||
|
||||
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()
|
||||
{
|
||||
//prevRequiredItems = new List<RelatedItem>(requiredItems);
|
||||
|
||||
if (attached)
|
||||
{
|
||||
Use(1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
requiredItems.Clear();
|
||||
Msg = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
Subsurface/Source/Items/Components/Holdable/Pickable.cs
Normal file
91
Subsurface/Source/Items/Components/Holdable/Pickable.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
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;
|
||||
|
||||
if (picker.Inventory.TryPutItem(item, allowedSlots))
|
||||
{
|
||||
if (!picker.HasSelectedItem(item) && item.body!=null) item.body.Enabled = false;
|
||||
this.picker = picker;
|
||||
|
||||
for (int i = item.linkedTo.Count - 1; i >= 0; i--)
|
||||
item.linkedTo[i].RemoveLinked(item);
|
||||
item.linkedTo.Clear();
|
||||
|
||||
ApplyStatusEffects(ActionType.OnPicked, 1.0f, picker);
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
125
Subsurface/Source/Items/Components/Holdable/RangedWeapon.cs
Normal file
125
Subsurface/Source/Items/Components/Holdable/RangedWeapon.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
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;
|
||||
|
||||
[HasDefaultValue("0.0,0.0", false)]
|
||||
public string BarrelPos
|
||||
{
|
||||
get { return ToolBox.Vector2ToString(ConvertUnits.ToDisplayUnits(barrelPos)); }
|
||||
set { barrelPos = ConvertUnits.ToSimUnits(ToolBox.ParseToVector2(value)); }
|
||||
}
|
||||
|
||||
public Vector2 TransformedBarrelPos
|
||||
{
|
||||
get
|
||||
{
|
||||
Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation);
|
||||
Vector2 flippedPos = barrelPos;
|
||||
if (item.body.Dir < 0.0f) flippedPos.X = -flippedPos.X;
|
||||
return (Vector2.Transform(flippedPos, 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(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null) return false;
|
||||
if (!character.SecondaryKeyDown.State || reload > 0.0f) return false;
|
||||
isActive = true;
|
||||
reload = 1.0f;
|
||||
|
||||
bool failed = DoesUseFail(character);
|
||||
|
||||
List<Body> limbBodies = new List<Body>();
|
||||
foreach (Limb l in character.AnimController.limbs)
|
||||
{
|
||||
limbBodies.Add(l.body.FarseerBody);
|
||||
}
|
||||
|
||||
Item[] containedItems = item.ContainedItems;
|
||||
if (containedItems == null || !containedItems.Any()) 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<Projectile>();
|
||||
if (projectileComponent == null) continue;
|
||||
|
||||
projectile.body.ResetDynamics();
|
||||
projectile.SetTransform(TransformedBarrelPos,
|
||||
(item.body.Dir == 1.0f) ? item.body.Rotation : item.body.Rotation - MathHelper.Pi);
|
||||
|
||||
projectile.Use(deltaTime);
|
||||
|
||||
|
||||
if (failed)
|
||||
{
|
||||
Vector2 modifiedVelocity = projectile.body.LinearVelocity;
|
||||
modifiedVelocity.X *= Rand.Range(0.0f, 0.5f);
|
||||
modifiedVelocity.Y *= Rand.Range(0.0f, 0.5f);
|
||||
|
||||
projectile.body.LinearVelocity = modifiedVelocity;
|
||||
projectile.body.ApplyTorque(projectile.body.Mass * Rand.Range(-10.0f, 10.0f));
|
||||
|
||||
//recoil
|
||||
//item.body.ApplyLinearImpulse(
|
||||
// new Vector2((float)Math.Cos(projectile.body.Rotation), (float)Math.Sin(projectile.body.Rotation)) * item.body.Mass * -10.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
projectileComponent.ignoredBodies = limbBodies;
|
||||
|
||||
//recoil
|
||||
//item.body.ApplyLinearImpulse(
|
||||
// new Vector2((float)Math.Cos(projectile.body.Rotation), (float)Math.Sin(projectile.body.Rotation)) * -item.body.Mass);
|
||||
}
|
||||
|
||||
item.RemoveContained(projectile);
|
||||
|
||||
|
||||
|
||||
Rope rope = item.GetComponent<Rope>();
|
||||
if (rope != null) rope.Attach(projectile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
198
Subsurface/Source/Items/Components/Holdable/RepairTool.cs
Normal file
198
Subsurface/Source/Items/Components/Holdable/RepairTool.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Subsurface.Particles;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class RepairTool : ItemComponent
|
||||
{
|
||||
List<string> fixableEntities;
|
||||
|
||||
float range;
|
||||
|
||||
Vector2 pickedPosition;
|
||||
|
||||
Vector2 barrelPos;
|
||||
|
||||
float structureFixAmount, limbFixAmount;
|
||||
|
||||
[HasDefaultValue(0.0f, false)]
|
||||
public float Range
|
||||
{
|
||||
get { return ConvertUnits.ToDisplayUnits(range); }
|
||||
set { range = ConvertUnits.ToSimUnits(value); }
|
||||
}
|
||||
|
||||
[HasDefaultValue(0.0f, false)]
|
||||
public float StructureFixAmount
|
||||
{
|
||||
get { return structureFixAmount; }
|
||||
set { structureFixAmount = value; }
|
||||
}
|
||||
|
||||
[HasDefaultValue(0.0f, false)]
|
||||
public float LimbFixAmount
|
||||
{
|
||||
get { return limbFixAmount; }
|
||||
set { limbFixAmount = value; }
|
||||
}
|
||||
|
||||
[HasDefaultValue("0.0,0.0", false)]
|
||||
public string BarrelPos
|
||||
{
|
||||
get { return ToolBox.Vector2ToString(ConvertUnits.ToDisplayUnits(barrelPos)); }
|
||||
set { barrelPos = ConvertUnits.ToSimUnits(ToolBox.ParseToVector2(value)); }
|
||||
}
|
||||
|
||||
public Vector2 TransformedBarrelPos
|
||||
{
|
||||
get
|
||||
{
|
||||
Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation);
|
||||
Vector2 flippedPos = barrelPos;
|
||||
if (item.body.Dir < 0.0f) flippedPos.X = -flippedPos.X;
|
||||
return (Vector2.Transform(flippedPos, bodyTransform) + item.body.Position);
|
||||
}
|
||||
}
|
||||
|
||||
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<string>();
|
||||
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(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null) return false;
|
||||
if (!character.SecondaryKeyDown.State) return false;
|
||||
|
||||
if (DoesUseFail(character)) return false;
|
||||
|
||||
isActive = true;
|
||||
|
||||
Vector2 targetPosition = item.body.Position;
|
||||
//targetPosition = targetPosition.X, -targetPosition.Y);
|
||||
|
||||
targetPosition += new Vector2(
|
||||
(float)Math.Cos(item.body.Rotation),
|
||||
(float)Math.Sin(item.body.Rotation)) * range * item.body.Dir;
|
||||
|
||||
List<Body> ignoredBodies = new List<Body>();
|
||||
foreach (Limb limb in character.AnimController.limbs)
|
||||
{
|
||||
ignoredBodies.Add(limb.body.FarseerBody);
|
||||
}
|
||||
|
||||
Body targetBody = Submarine.PickBody(TransformedBarrelPos, targetPosition, ignoredBodies);
|
||||
pickedPosition = Submarine.LastPickedPosition;
|
||||
|
||||
if (targetBody==null || targetBody.UserData==null) return true;
|
||||
|
||||
//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 true;
|
||||
|
||||
int sectionIndex = targetStructure.FindSectionIndex(ConvertUnits.ToDisplayUnits(pickedPosition));
|
||||
if (sectionIndex < 0) return true;
|
||||
|
||||
targetStructure.HighLightSection(sectionIndex);
|
||||
|
||||
targetStructure.AddDamage(sectionIndex, -structureFixAmount);
|
||||
|
||||
}
|
||||
else if ((targetLimb = (targetBody.UserData as Limb)) != null)
|
||||
{
|
||||
if (character.SecondaryKeyDown.State)
|
||||
{
|
||||
targetLimb.character.Health += limbFixAmount;
|
||||
//isActive = true;
|
||||
}
|
||||
}
|
||||
else if ((targetItem = (targetBody.UserData as Item)) != null)
|
||||
{
|
||||
//targetItem.Condition -= structureFixAmount;
|
||||
targetItem.IsHighlighted = true;
|
||||
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
{
|
||||
//if (Array.IndexOf(effect.TargetNames, targetItem.Name) == -1) continue;
|
||||
effect.Apply(ActionType.OnUse, deltaTime, item, targetItem.AllPropertyObjects);
|
||||
//targetItem.ApplyStatusEffect(effect, ActionType.OnUse, deltaTime);
|
||||
}
|
||||
//ApplyStatusEffects(ActionType.OnUse, 1.0f, null, targ);
|
||||
}
|
||||
|
||||
//if (character.SecondaryKeyDown.State)
|
||||
//{
|
||||
// IPropertyObject propertyObject = targetBody.UserData as IPropertyObject;
|
||||
// if (propertyObject!=null) ApplyStatusEffects(ActionType.OnUse, 1.0f, item.SimPosition, propertyObject);
|
||||
// //isActive = true;
|
||||
//}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
//isActive = true;
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch, bool editing)
|
||||
{
|
||||
if (!isActive) return;
|
||||
|
||||
Vector2 particleSpeed = new Vector2(
|
||||
(float)Math.Cos(item.body.Rotation),
|
||||
(float)Math.Sin(item.body.Rotation)) *item.body.Dir * 5.0f;
|
||||
|
||||
Game1.ParticleManager.CreateParticle("weld", TransformedBarrelPos, particleSpeed);
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
99
Subsurface/Source/Items/Components/Holdable/Throwable.cs
Normal file
99
Subsurface/Source/Items/Components/Holdable/Throwable.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class Throwable : Holdable
|
||||
{
|
||||
float throwForce;
|
||||
|
||||
float throwPos;
|
||||
|
||||
bool throwing;
|
||||
|
||||
[HasDefaultValue(1.0f, false)]
|
||||
public float ThrowForce
|
||||
{
|
||||
get { return throwForce; }
|
||||
set { throwForce = value; }
|
||||
}
|
||||
|
||||
public Throwable(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
//throwForce = ToolBox.GetAttributeFloat(element, "throwforce", 1.0f);
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null) return false;
|
||||
if (!character.SecondaryKeyDown.State || throwing) return false;
|
||||
|
||||
throwing = true;
|
||||
|
||||
isActive = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
if (throwing) return;
|
||||
throwPos = 0.25f;
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
{
|
||||
base.Drop(dropper);
|
||||
|
||||
throwing = false;
|
||||
throwPos = 0.0f;
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
Update(deltaTime, cam);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (!item.body.Enabled) return;
|
||||
if (!picker.HasSelectedItem(item)) isActive = false;
|
||||
|
||||
if (!picker.SecondaryKeyDown.State && !throwing) throwPos = 0.0f;
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
|
||||
|
||||
if (item.body.Dir != picker.AnimController.Dir) Flip(item);
|
||||
|
||||
AnimController ac = picker.AnimController;
|
||||
|
||||
ac.HoldItem(deltaTime, cam, item, handlePos, new Vector2(throwPos, 0.0f), aimPos, holdAngle);
|
||||
|
||||
if (!throwing) return;
|
||||
|
||||
throwPos += deltaTime*5.0f;
|
||||
|
||||
Vector2 throwVector = ConvertUnits.ToSimUnits(picker.CursorPosition) - item.body.Position;
|
||||
throwVector = Vector2.Normalize(throwVector);
|
||||
|
||||
if (handlePos[0]!=Vector2.Zero)
|
||||
{
|
||||
Limb leftHand = ac.GetLimb(LimbType.LeftHand);
|
||||
leftHand.body.ApplyForce(throwVector*10.0f);
|
||||
}
|
||||
|
||||
if (handlePos[1] != Vector2.Zero)
|
||||
{
|
||||
Limb rightHand = ac.GetLimb(LimbType.RightHand);
|
||||
rightHand.body.ApplyForce(throwVector * 10.0f);
|
||||
}
|
||||
|
||||
if (throwPos>1.0f)
|
||||
{
|
||||
item.Drop();
|
||||
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
622
Subsurface/Source/Items/Components/ItemComponent.cs
Normal file
622
Subsurface/Source/Items/Components/ItemComponent.cs
Normal file
@@ -0,0 +1,622 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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 System.IO;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class ItemSound
|
||||
{
|
||||
public readonly Sound Sound;
|
||||
public readonly ActionType Type;
|
||||
|
||||
public string VolumeProperty;
|
||||
|
||||
public float VolumeMultiplier;
|
||||
|
||||
public readonly float Range;
|
||||
|
||||
public ItemSound(Sound sound, ActionType type, float range)
|
||||
{
|
||||
this.Sound = sound;
|
||||
this.Type = type;
|
||||
this.Range = range;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The base class for components holding the different functionalities of the item
|
||||
/// </summary>
|
||||
class ItemComponent : IPropertyObject
|
||||
{
|
||||
protected Item item;
|
||||
|
||||
protected string name;
|
||||
|
||||
protected bool isActive;
|
||||
|
||||
protected bool characterUsable;
|
||||
|
||||
protected bool canBePicked;
|
||||
protected bool canBeSelected;
|
||||
|
||||
public List<StatusEffect> statusEffects;
|
||||
|
||||
protected bool updated;
|
||||
|
||||
public List<RelatedItem> requiredItems;
|
||||
|
||||
public List<Skill> requiredSkills;
|
||||
|
||||
private List<ItemSound> sounds;
|
||||
|
||||
private GUIFrame guiFrame;
|
||||
|
||||
public readonly Dictionary<string, ObjectProperty> properties;
|
||||
public Dictionary<string, ObjectProperty> 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; }
|
||||
}
|
||||
|
||||
[HasDefaultValue(false, false)]
|
||||
public bool DeleteOnUse
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Item Item
|
||||
{
|
||||
get { return item; }
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
protected GUIFrame GuiFrame
|
||||
{
|
||||
get
|
||||
{
|
||||
if (guiFrame==null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error: the component "+name+" in "+item.Name+" doesn't have a guiFrame");
|
||||
guiFrame = new GUIFrame(new Rectangle(0, 0, 100, 100), Color.Black);
|
||||
}
|
||||
return guiFrame;
|
||||
}
|
||||
}
|
||||
|
||||
[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<RelatedItem>();
|
||||
|
||||
requiredSkills = new List<Skill>();
|
||||
|
||||
sounds = new List<ItemSound>();
|
||||
|
||||
statusEffects = new List<StatusEffect>();
|
||||
|
||||
//var initableProperties = ObjectProperty.GetProperties<Initable>(this);
|
||||
//foreach (ObjectProperty initableProperty in initableProperties)
|
||||
//{
|
||||
// object value = ToolBox.GetAttributeObject(element, initableProperty.Name.ToLower());
|
||||
// if (value==null)
|
||||
// {
|
||||
// foreach (var ini in initableProperty.Attributes.OfType<Initable>())
|
||||
// {
|
||||
// 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":
|
||||
case "requireditems":
|
||||
RelatedItem ri = RelatedItem.Load(subElement);
|
||||
if (ri != null) requiredItems.Add(ri);
|
||||
break;
|
||||
|
||||
case "requiredskill":
|
||||
case "requiredskills":
|
||||
string skillName = ToolBox.GetAttributeString(subElement, "name", "");
|
||||
requiredSkills.Add(new Skill(skillName, ToolBox.GetAttributeInt(subElement, "level", 0)));
|
||||
break;
|
||||
case "statuseffect":
|
||||
statusEffects.Add(StatusEffect.Load(subElement));
|
||||
break;
|
||||
case "guiframe":
|
||||
Vector4 rect = ToolBox.GetAttributeVector4(subElement, "rect", Vector4.One);
|
||||
rect.X *= Game1.GraphicsWidth;
|
||||
rect.Y *= Game1.GraphicsHeight;
|
||||
rect.Z *= Game1.GraphicsWidth;
|
||||
rect.W *= Game1.GraphicsHeight;
|
||||
|
||||
Vector4 color = ToolBox.GetAttributeVector4(subElement, "color", Vector4.One);
|
||||
|
||||
Alignment alignment = Alignment.Center;
|
||||
try
|
||||
{
|
||||
alignment = (Alignment)Enum.Parse(typeof(Alignment),
|
||||
ToolBox.GetAttributeString(subElement, "alignment", "Center"), true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + "! ''" + element.Attribute("type").Value + "'' is not a valid alignment");
|
||||
}
|
||||
|
||||
guiFrame = new GUIFrame(
|
||||
new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Z, (int)rect.W),
|
||||
new Color(color.X, color.Y, color.Z, color.W), alignment, GUI.style);
|
||||
//guiFrame.Alpha = color.W;
|
||||
|
||||
break;
|
||||
case "sound":
|
||||
string filePath = ToolBox.GetAttributeString(subElement, "file", "");
|
||||
if (filePath=="") continue;
|
||||
if (!filePath.Contains("\\")) filePath = Path.GetDirectoryName(item.Prefab.ConfigFile)+"\\"+filePath;
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
Sound sound = Sound.Load(filePath);
|
||||
|
||||
float range = ToolBox.GetAttributeFloat(subElement, "range", 800.0f);
|
||||
ItemSound itemSound = new ItemSound(sound, type, range);
|
||||
itemSound.VolumeProperty = ToolBox.GetAttributeString(subElement, "volume", "");
|
||||
itemSound.VolumeMultiplier = ToolBox.GetAttributeFloat(subElement, "volumemultiplier", 1.0f);
|
||||
sounds.Add(itemSound);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private ItemSound loopingSound;
|
||||
private int loopingSoundIndex;
|
||||
public void PlaySound(ActionType type, Vector2 position, bool loop=false)
|
||||
{
|
||||
ItemSound itemSound = null;
|
||||
if (!loop || !Sounds.SoundManager.IsPlaying(loopingSoundIndex))
|
||||
{
|
||||
List<ItemSound> matchingSounds = sounds.FindAll(x => x.Type == type);
|
||||
if (matchingSounds.Count == 0) return;
|
||||
|
||||
int index = Rand.Int(matchingSounds.Count);
|
||||
itemSound = matchingSounds[index];
|
||||
|
||||
|
||||
|
||||
if (loop) loopingSound = itemSound;
|
||||
}
|
||||
|
||||
|
||||
if (loop)
|
||||
{
|
||||
loopingSoundIndex = loopingSound.Sound.Loop(loopingSoundIndex, GetSoundVolume(loopingSound), position, loopingSound.Range);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
itemSound.Sound.Play(GetSoundVolume(itemSound), itemSound.Range, position);
|
||||
}
|
||||
}
|
||||
|
||||
private float GetSoundVolume(ItemSound sound)
|
||||
{
|
||||
if (sound.VolumeProperty == "") return 1.0f;
|
||||
|
||||
ObjectProperty op = null;
|
||||
if (properties.TryGetValue(sound.VolumeProperty.ToLower(), out op))
|
||||
{
|
||||
float newVolume = 0.0f;
|
||||
float.TryParse(op.GetValue().ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out newVolume);
|
||||
|
||||
newVolume *= sound.VolumeMultiplier;
|
||||
|
||||
return MathHelper.Clamp(newVolume, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public virtual void Move(Vector2 amount) { }
|
||||
|
||||
/// <summary>a character has picked the item</summary>
|
||||
public virtual bool Pick(Character picker)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool Select(Character character)
|
||||
{
|
||||
return CanBeSelected;
|
||||
}
|
||||
|
||||
/// <summary>a character has dropped the item</summary>
|
||||
public virtual void Drop(Character dropper) { }
|
||||
|
||||
public virtual void Draw(SpriteBatch spriteBatch, bool editing = false) { }
|
||||
|
||||
public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { }
|
||||
|
||||
/// <summary>
|
||||
/// a construction has activated the item (such as a turret shooting a projectile)
|
||||
/// call the Activate-methods of the components</summary>
|
||||
/// <param name="c"> The construction which activated the item</param>
|
||||
/// <param name="modifier"> A vector that can be used to pass additional information to the components</param>
|
||||
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(float deltaTime, Character character = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//called when the item is equipped and right mouse button is pressed
|
||||
public virtual void SecondaryUse(float deltaTime, 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, float power = 0.0f)
|
||||
{
|
||||
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "activate":
|
||||
case "use":
|
||||
item.Use(1.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool Combine(Item item)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual void Remove()
|
||||
{
|
||||
if (loopingSound!=null)
|
||||
{
|
||||
Sounds.SoundManager.Stop(loopingSoundIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasRequiredSkills(Character character)
|
||||
{
|
||||
Skill temp;
|
||||
return HasRequiredSkills(character, out temp);
|
||||
}
|
||||
|
||||
public bool HasRequiredSkills(Character character, out Skill insufficientSkill)
|
||||
{
|
||||
foreach (Skill skill in requiredSkills)
|
||||
{
|
||||
int characterLevel = character.GetSkillLevel(skill.Name);
|
||||
if (characterLevel < skill.Level)
|
||||
{
|
||||
insufficientSkill = skill;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
insufficientSkill = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool DoesUseFail(Character character)
|
||||
{
|
||||
foreach (Skill skill in requiredSkills)
|
||||
{
|
||||
int characterLevel = character.GetSkillLevel(skill.Name);
|
||||
if (characterLevel > skill.Level) continue;
|
||||
|
||||
if (Rand.Int(characterLevel) - skill.Level < 0)
|
||||
{
|
||||
item.ApplyStatusEffects(ActionType.OnFailure, 1.0f, character);
|
||||
//Item.ApplyStatusEffects();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasRequiredContainedItems(bool addMessage)
|
||||
{
|
||||
List<RelatedItem> requiredContained = requiredItems.FindAll(ri=> ri.Type == RelatedItem.RelationType.Contained);
|
||||
|
||||
if (!requiredContained.Any()) return true;
|
||||
|
||||
Item[] containedItems = item.ContainedItems;
|
||||
if (containedItems == null || !containedItems.Any()) return false;
|
||||
|
||||
foreach (RelatedItem ri in requiredContained)
|
||||
{
|
||||
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 HasRequiredItems(Character character, bool addMessage)
|
||||
{
|
||||
if (!requiredItems.Any()) return true;
|
||||
|
||||
foreach (RelatedItem ri in requiredItems)
|
||||
{
|
||||
if (!ri.Type.HasFlag(RelatedItem.RelationType.Equipped) && !ri.Type.HasFlag(RelatedItem.RelationType.Picked)) continue;
|
||||
|
||||
bool hasItem = false;
|
||||
if (ri.Type.HasFlag(RelatedItem.RelationType.Equipped))
|
||||
{
|
||||
if (character.SelectedItems.FirstOrDefault(it => it != null && it.Condition > 0.0f && ri.MatchesItem(it)) != null) hasItem = true;
|
||||
}
|
||||
if (!hasItem && ri.Type.HasFlag(RelatedItem.RelationType.Picked))
|
||||
{
|
||||
if (character.Inventory.items.FirstOrDefault(x => x!=null && x.Condition>0.0f && ri.MatchesItem(x))!=null) hasItem = true;
|
||||
}
|
||||
if (!hasItem)
|
||||
{
|
||||
if (addMessage && !string.IsNullOrEmpty(ri.Msg)) GUI.AddMessage(ri.Msg, Color.Red);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null)
|
||||
{
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
{
|
||||
if (effect.type != type) continue;
|
||||
item.ApplyStatusEffect(effect, type, deltaTime, character);
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, IPropertyObject target)
|
||||
{
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
{
|
||||
if (effect.type != type) continue;
|
||||
effect.Apply(type, deltaTime, item, target);
|
||||
}
|
||||
}
|
||||
|
||||
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<Saveable>(this);
|
||||
//foreach (var property in saveProperties)
|
||||
//{
|
||||
// object value = property.GetValue();
|
||||
// if (value == null) continue;
|
||||
|
||||
// bool dontSave = false;
|
||||
// foreach (var ini in property.Attributes.OfType<Initable>())
|
||||
// {
|
||||
// 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;
|
||||
|
||||
|
||||
|
||||
foreach (XAttribute attribute in componentElement.Attributes())
|
||||
{
|
||||
ObjectProperty property = null;
|
||||
if (!properties.TryGetValue(attribute.Name.ToString().ToLower(), out property)) continue;
|
||||
|
||||
property.TrySetValue(attribute.Value);
|
||||
}
|
||||
|
||||
List<RelatedItem> prevRequiredItems = new List<RelatedItem>(requiredItems);
|
||||
requiredItems.Clear();
|
||||
|
||||
foreach (XElement subElement in componentElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLower())
|
||||
{
|
||||
case "requireditem":
|
||||
RelatedItem newRequiredItem = RelatedItem.Load(subElement);
|
||||
|
||||
if (newRequiredItem == null) continue;
|
||||
|
||||
var prevRequiredItem = prevRequiredItems.Find(ri => ri.JoinedNames == newRequiredItem.JoinedNames);
|
||||
if (prevRequiredItem!=null)
|
||||
{
|
||||
newRequiredItem.statusEffects = prevRequiredItem.statusEffects;
|
||||
newRequiredItem.Msg = prevRequiredItem.Msg;
|
||||
}
|
||||
|
||||
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!=typeof(ItemComponent) && !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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
103
Subsurface/Source/Items/Components/Label.cs
Normal file
103
Subsurface/Source/Items/Components/Label.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
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 Label : ItemComponent
|
||||
{
|
||||
GUITextBox textBox;
|
||||
|
||||
private string text;
|
||||
|
||||
[HasDefaultValue("", true)]
|
||||
public string Text
|
||||
{
|
||||
get { return text; }
|
||||
set
|
||||
{
|
||||
text = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Label(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
if (textBox == null)
|
||||
{
|
||||
textBox = new GUITextBox(Rectangle.Empty, GUI.style, GuiFrame);
|
||||
textBox.Wrap = true;
|
||||
textBox.OnTextChanged = TextChanged;
|
||||
textBox.LimitText = true;
|
||||
|
||||
GUIButton button = new GUIButton(new Rectangle(0,0,100,15), "OK", null, Alignment.BottomRight, GUI.style, GuiFrame);
|
||||
button.OnClicked = Close;
|
||||
}
|
||||
|
||||
textBox.Text = text;
|
||||
|
||||
textBox.Select();
|
||||
|
||||
return base.Select(character);
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
//isActive = true;
|
||||
GuiFrame.Update((float)Physics.step);
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
//int width = 300, height = 300;
|
||||
//int x = Game1.GraphicsWidth / 2 - width / 2;
|
||||
//int y = Game1.GraphicsHeight / 2 - height / 2 - 50;
|
||||
|
||||
//GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black, true);
|
||||
if (!textBox.Selected) character.SelectedConstruction = null;
|
||||
}
|
||||
|
||||
private bool TextChanged(GUITextBox textBox, string text)
|
||||
{
|
||||
this.text = text;
|
||||
item.NewComponentEvent(this, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Close(GUIButton button, object obj)
|
||||
{
|
||||
textBox.Deselect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message)
|
||||
{
|
||||
message.Write(Text);
|
||||
}
|
||||
|
||||
public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetIncomingMessage message)
|
||||
{
|
||||
string newText = "";
|
||||
try
|
||||
{
|
||||
newText = message.ReadString();
|
||||
}
|
||||
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Text = newText;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Subsurface/Source/Items/Components/Ladder.cs
Normal file
23
Subsurface/Source/Items/Components/Ladder.cs
Normal file
@@ -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 Select(Character character = null)
|
||||
{
|
||||
if (character == null) return false;
|
||||
|
||||
character.AnimController.Anim = AnimController.Animation.Climbing;
|
||||
//picker.SelectedConstruction = item;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
210
Subsurface/Source/Items/Components/Machines/Controller.cs
Normal file
210
Subsurface/Source/Items/Components/Machines/Controller.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
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<LimbPos> limbPositions;
|
||||
|
||||
Direction dir;
|
||||
|
||||
//the x-position where the user walks to when using the controller
|
||||
float userPos;
|
||||
|
||||
Camera cam;
|
||||
|
||||
Character character;
|
||||
|
||||
[HasDefaultValue(1.0f,false)]
|
||||
public float UserPos
|
||||
{
|
||||
set { userPos = value; }
|
||||
}
|
||||
|
||||
public Controller(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
limbPositions = new List<LimbPos>();
|
||||
|
||||
dir = (Direction)Enum.Parse(typeof(Direction), ToolBox.GetAttributeString(element, "direction", "None"), true);
|
||||
|
||||
foreach (XElement el in element.Elements())
|
||||
{
|
||||
if (el.Name != "limbposition") continue;
|
||||
|
||||
LimbPos lp = new LimbPos();
|
||||
try
|
||||
{
|
||||
lp.limbType = (LimbType)Enum.Parse(typeof(LimbType), el.Attribute("limb").Value, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in " + element + ": " + e.Message, e);
|
||||
}
|
||||
|
||||
lp.position = ToolBox.GetAttributeVector2(el, "position", Vector2.Zero);
|
||||
|
||||
limbPositions.Add(lp);
|
||||
}
|
||||
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
this.cam = cam;
|
||||
|
||||
if (character == null || character.SelectedConstruction != item)
|
||||
{
|
||||
if (character != null)
|
||||
{
|
||||
character.SelectedConstruction = null;
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
character = null;
|
||||
}
|
||||
isActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, character);
|
||||
|
||||
if (userPos != 0.0f && character.AnimController.Anim != AnimController.Animation.UsingConstruction)
|
||||
{
|
||||
Limb torso = character.AnimController.GetLimb(LimbType.Torso);
|
||||
float torsoX = ConvertUnits.ToDisplayUnits(torso.SimPosition.X);
|
||||
|
||||
if (Math.Abs(torsoX - item.Rect.X + userPos) > 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);
|
||||
//}
|
||||
|
||||
item.SendSignal(ToolBox.Vector2ToString(character.CursorPosition), "position_out");
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character activator = null)
|
||||
{
|
||||
//character = activator;
|
||||
//foreach (MapEntity e in item.linkedTo)
|
||||
//{
|
||||
// Item linkedItem = e as Item;
|
||||
// if (linkedItem == null) continue;
|
||||
// linkedItem.Use(deltaTime, activator);
|
||||
//}
|
||||
|
||||
item.SendSignal("1", "trigger_out");
|
||||
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, activator);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null) return;
|
||||
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
if (c.Name != "position_out") continue;
|
||||
|
||||
foreach (Connection c2 in c.Recipients)
|
||||
{
|
||||
if (c2 == null || c2.Item==null || !c2.Item.Prefab.FocusOnSelected) continue;
|
||||
|
||||
Vector2 centerPos = c2.Item.Position;
|
||||
|
||||
if (character == Character.Controlled && cam != null)
|
||||
{
|
||||
Lights.LightManager.ViewPos = centerPos;
|
||||
cam.TargetPos = c2.Item.Position;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//foreach (MapEntity e in item.linkedTo)
|
||||
//{
|
||||
// Item linkedItem = e as Item;
|
||||
// if (linkedItem == null) continue;
|
||||
// linkedItem.SecondaryUse(deltaTime, character);
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
item.SendSignal("1", "signal_out");
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Select(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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
122
Subsurface/Source/Items/Components/Machines/Engine.cs
Normal file
122
Subsurface/Source/Items/Components/Machines/Engine.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
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 Engine : Powered
|
||||
{
|
||||
|
||||
float force;
|
||||
|
||||
float targetForce;
|
||||
|
||||
float maxForce;
|
||||
|
||||
float powerPerForce;
|
||||
|
||||
[Editable, HasDefaultValue(1.0f, true)]
|
||||
public float PowerPerForce
|
||||
{
|
||||
get { return powerPerForce; }
|
||||
set
|
||||
{
|
||||
powerPerForce = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, HasDefaultValue(2000.0f, true)]
|
||||
public float MaxForce
|
||||
{
|
||||
get { return maxForce; }
|
||||
set
|
||||
{
|
||||
maxForce = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
public float Force
|
||||
{
|
||||
get { return force;}
|
||||
set { force = MathHelper.Clamp(value, -100.0f, 100.0f); }
|
||||
}
|
||||
|
||||
public Engine(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
public float CurrentVolume
|
||||
{
|
||||
get { return Math.Abs((force / 100.0f) * (voltage / minVoltage)); }
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
currPowerConsumption = Math.Abs(targetForce) * powerPerForce;
|
||||
|
||||
Force = MathHelper.Lerp(force, (voltage < minVoltage) ? 0.0f : targetForce, 0.1f);
|
||||
if (Force != 0.0f)
|
||||
{
|
||||
Vector2 currForce = new Vector2((force / 100.0f) * maxForce * (voltage / minVoltage), 0.0f);
|
||||
|
||||
Submarine.Loaded.ApplyForce(currForce);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
Game1.ParticleManager.CreateParticle("bubbles", item.SimPosition,
|
||||
-currForce/500.0f + new Vector2(Rand.Range(-1.0f, 1.0f), Rand.Range(-0.5f, 0.5f)));
|
||||
}
|
||||
}
|
||||
|
||||
voltage = 0.0f;
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
//isActive = true;
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
//int width = 300, height = 300;
|
||||
//int x = Game1.GraphicsWidth / 2 - width / 2;
|
||||
//int y = Game1.GraphicsHeight / 2 - height / 2 - 50;
|
||||
|
||||
//GUI.DrawRectangle(spriteBatch, new Rectangle(x, y, width, height), Color.Black, true);
|
||||
|
||||
spriteBatch.DrawString(GUI.Font, "Force: " + (int)(targetForce) + " %", new Vector2(GuiFrame.Rect.X + 30, GuiFrame.Rect.Y + 30), Color.White);
|
||||
|
||||
if (GUI.DrawButton(spriteBatch, new Rectangle(GuiFrame.Rect.X + 280, GuiFrame.Rect.Y + 30, 40, 40), "+", true)) targetForce += 1.0f;
|
||||
if (GUI.DrawButton(spriteBatch, new Rectangle(GuiFrame.Rect.X + 280, GuiFrame.Rect.Y + 80, 40, 40), "-", true)) targetForce -= 1.0f;
|
||||
|
||||
item.NewComponentEvent(this, true);
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
force = MathHelper.Lerp(force, 0.0f, 0.1f);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(string signal, Connection connection, Item sender, float power=0.0f)
|
||||
{
|
||||
base.ReceiveSignal(signal, connection, sender, power);
|
||||
|
||||
if (connection.Name == "set_force")
|
||||
{
|
||||
float tempForce;
|
||||
if (float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out tempForce))
|
||||
{
|
||||
targetForce = tempForce;
|
||||
targetForce = MathHelper.Clamp(targetForce, -100.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
213
Subsurface/Source/Items/Components/Machines/Fabricator.cs
Normal file
213
Subsurface/Source/Items/Components/Machines/Fabricator.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
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 FabricableItem
|
||||
{
|
||||
//public static List<FabricableItem> list = new List<FabricableItem>();
|
||||
|
||||
//public readonly string[] FabricatorTags;
|
||||
|
||||
public readonly ItemPrefab TargetItem;
|
||||
|
||||
public readonly List<ItemPrefab> RequiredItems;
|
||||
|
||||
public readonly float RequiredTime;
|
||||
|
||||
|
||||
//ListOrSomething requiredLevels
|
||||
|
||||
public FabricableItem(XElement element)
|
||||
{
|
||||
string name = ToolBox.GetAttributeString(element, "name", "").ToLower();
|
||||
|
||||
TargetItem = ItemPrefab.list.Find(ip => ip.Name.ToLower() == name) as ItemPrefab;
|
||||
if (TargetItem == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Fabricable Item! Item ''" + element.Name + "'' not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
RequiredItems = new List<ItemPrefab>();
|
||||
|
||||
string[] requiredItemNames = ToolBox.GetAttributeString(element, "requireditems", "").Split(',');
|
||||
foreach (string requiredItemName in requiredItemNames)
|
||||
{
|
||||
ItemPrefab requiredItem = ItemPrefab.list.Find(ip => ip.Name.ToLower() == requiredItemName.Trim().ToLower()) as ItemPrefab;
|
||||
if (requiredItem == null) continue;
|
||||
RequiredItems.Add(requiredItem);
|
||||
}
|
||||
|
||||
RequiredTime = ToolBox.GetAttributeFloat(element, "requiredtime", 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
class Fabricator : ItemComponent
|
||||
{
|
||||
List<FabricableItem> fabricableItems;
|
||||
|
||||
GUIListBox itemList;
|
||||
|
||||
GUIFrame selectedItemFrame;
|
||||
|
||||
FabricableItem fabricatedItem;
|
||||
float timeUntilReady;
|
||||
|
||||
public Fabricator(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
fabricableItems = new List<FabricableItem>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString() != "fabricableitem") continue;
|
||||
|
||||
FabricableItem fabricableItem = new FabricableItem(subElement);
|
||||
if (fabricableItem.TargetItem != null) fabricableItems.Add(fabricableItem);
|
||||
|
||||
}
|
||||
|
||||
int width = 400, height = 300;
|
||||
itemList = new GUIListBox(new Rectangle(Game1.GraphicsWidth / 2 - width / 2, Game1.GraphicsHeight / 2 - height / 2, width, height), Color.White * 0.7f);
|
||||
itemList.OnSelected = SelectItem;
|
||||
//structureList.CheckSelected = MapEntityPrefab.GetSelected;
|
||||
|
||||
foreach (FabricableItem fi in fabricableItems)
|
||||
{
|
||||
Color color = ((itemList.CountChildren % 2) == 0) ? Color.White : Color.LightGray;
|
||||
|
||||
//GUIFrame frame = new GUIFrame(new Rectangle(0, 0, 0, 50), Color.Transparent, itemList);
|
||||
//frame.UserData = fi;
|
||||
//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(0, 0, 0, 25), fi.TargetItem.Name,
|
||||
color, Color.Black,
|
||||
Alignment.Left, Alignment.Left, null, itemList);
|
||||
textBlock.UserData = fi;
|
||||
textBlock.Padding = new Vector4(5.0f, 5.0f, 5.0f, 5.0f);
|
||||
|
||||
//if (fi.TargetItem.sprite != null)
|
||||
//{
|
||||
// GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), fi.TargetItem.sprite, Alignment.Left, frame);
|
||||
// img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private bool SelectItem(object obj)
|
||||
{
|
||||
FabricableItem targetItem = obj as FabricableItem;
|
||||
if (targetItem == null) return false;
|
||||
|
||||
int width = 200, height = 150;
|
||||
selectedItemFrame = new GUIFrame(new Rectangle(Game1.GraphicsWidth / 2 - width / 2, itemList.Rect.Bottom+20, width, height), Color.Black*0.8f);
|
||||
//selectedItemFrame.Padding = GUI.style.smallPadding;
|
||||
|
||||
if (targetItem.TargetItem.sprite != null)
|
||||
{
|
||||
GUIImage img = new GUIImage(new Rectangle(0, 0, 40, 40), targetItem.TargetItem.sprite, Alignment.CenterX, selectedItemFrame);
|
||||
img.Scale = Math.Min(Math.Min(40.0f / img.SourceRect.Width, 40.0f / img.SourceRect.Height), 1.0f);
|
||||
|
||||
string text = targetItem.TargetItem.Name + "\n";
|
||||
text += "Required items:\n";
|
||||
foreach (ItemPrefab ip in targetItem.RequiredItems)
|
||||
{
|
||||
text += " - " + ip.Name + "\n";
|
||||
}
|
||||
text += "Required time: "+targetItem.RequiredTime+" s";
|
||||
|
||||
GUITextBlock textBlock = new GUITextBlock(
|
||||
new Rectangle(0, 0, 0, 25),
|
||||
text,
|
||||
Color.Transparent, Color.White,
|
||||
Alignment.CenterX | Alignment.CenterY,
|
||||
Alignment.Left, null,
|
||||
selectedItemFrame);
|
||||
|
||||
GUIButton button = new GUIButton(new Rectangle(0,0,100,20), "Create", Color.White, Alignment.CenterX | Alignment.Bottom, GUI.style, selectedItemFrame);
|
||||
button.OnClicked = StartFabricating;
|
||||
button.UserData = targetItem;
|
||||
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return (picker != null);
|
||||
}
|
||||
|
||||
private bool StartFabricating(GUIButton button, object obj)
|
||||
{
|
||||
GUIComponent listElement = itemList.GetChild(obj);
|
||||
|
||||
listElement.Color = Color.Green;
|
||||
itemList.Enabled = false;
|
||||
|
||||
fabricatedItem = obj as FabricableItem;
|
||||
isActive = true;
|
||||
|
||||
timeUntilReady = fabricatedItem.RequiredTime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
timeUntilReady -= deltaTime;
|
||||
|
||||
if (timeUntilReady > 0.0f) return;
|
||||
|
||||
ItemContainer container = item.GetComponent<ItemContainer>();
|
||||
foreach (ItemPrefab ip in fabricatedItem.RequiredItems)
|
||||
{
|
||||
var requiredItem = Array.Find(container.inventory.items, it => it != null && it.Prefab == ip);
|
||||
container.inventory.RemoveItem(requiredItem);
|
||||
}
|
||||
|
||||
new Item(fabricatedItem.TargetItem, item.Position);
|
||||
|
||||
isActive = false;
|
||||
fabricatedItem = null;
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
FabricableItem targetItem = itemList.SelectedData as FabricableItem;
|
||||
if (targetItem != null)
|
||||
{
|
||||
selectedItemFrame.GetChild<GUIButton>().Enabled = true;
|
||||
|
||||
ItemContainer container = item.GetComponent<ItemContainer>();
|
||||
foreach (ItemPrefab ip in targetItem.RequiredItems)
|
||||
{
|
||||
if (Array.Find(container.inventory.items, it => it != null && it.Prefab == ip) != null) continue;
|
||||
selectedItemFrame.GetChild<GUIButton>().Enabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
itemList.Update(0.016f);
|
||||
itemList.Draw(spriteBatch);
|
||||
|
||||
if (selectedItemFrame != null)
|
||||
{
|
||||
selectedItemFrame.Update(0.016f);
|
||||
selectedItemFrame.Draw(spriteBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Subsurface/Source/Items/Components/Machines/MiniMap.cs
Normal file
84
Subsurface/Source/Items/Components/Machines/MiniMap.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
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 = GuiFrame.Rect.Width, height = GuiFrame.Rect.Height;
|
||||
int x = GuiFrame.Rect.X;
|
||||
int y = GuiFrame.Rect.Y;
|
||||
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
//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)Submarine.Borders.Width, (float)miniMap.Height / (float)Submarine.Borders.Height);
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
Rectangle hullRect = new Rectangle(
|
||||
miniMap.X + (int)((hull.Rect.X - Submarine.Borders.X) * size),
|
||||
miniMap.Y - (int)((hull.Rect.Y - Submarine.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 - Submarine.Borders.X) * size),
|
||||
miniMap.Y - (int)((c.Position.Y - Submarine.Borders.Y) * size),
|
||||
5, 5);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, characterRect, Color.White, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class OxygenGenerator : Powered
|
||||
{
|
||||
PropertyTask powerUpTask;
|
||||
|
||||
float powerDownTimer;
|
||||
|
||||
bool running;
|
||||
|
||||
List<Vent> ventList;
|
||||
|
||||
public bool IsRunning()
|
||||
{
|
||||
return (running && item.Condition>0.0f);
|
||||
}
|
||||
|
||||
public OxygenGenerator(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
isActive = true;
|
||||
|
||||
ventList = new List<Vent>();
|
||||
|
||||
item.linkedTo.CollectionChanged += 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)
|
||||
{
|
||||
powerDownTimer += deltaTime;
|
||||
running = false;
|
||||
if ((powerUpTask==null || powerUpTask.IsFinished) && powerDownTimer>5.0f)
|
||||
{
|
||||
powerUpTask = new PropertyTask(item, IsRunning, 50.0f, "Turn on the oxygen generator");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
powerDownTimer = 0.0f;
|
||||
}
|
||||
|
||||
running = true;
|
||||
|
||||
float deltaOxygen = Math.Min(voltage, 1.0f) * 50000.0f;
|
||||
item.CurrentHull.Oxygen += deltaOxygen * deltaTime;
|
||||
|
||||
UpdateVents(deltaOxygen);
|
||||
|
||||
|
||||
voltage = 0.0f;
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
powerDownTimer += deltaTime;
|
||||
}
|
||||
|
||||
private void GetVents()
|
||||
{
|
||||
foreach (MapEntity entity in item.linkedTo)
|
||||
{
|
||||
Item linkedItem = entity as Item;
|
||||
if (linkedItem == null) continue;
|
||||
|
||||
Vent vent = linkedItem.GetComponent<Vent>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
181
Subsurface/Source/Items/Components/Machines/Pump.cs
Normal file
181
Subsurface/Source/Items/Components/Machines/Pump.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class Pump : Powered
|
||||
{
|
||||
float flowPercentage;
|
||||
float maxFlow;
|
||||
|
||||
float? targetLevel;
|
||||
|
||||
Hull hull1, hull2;
|
||||
|
||||
[HasDefaultValue(0.0f, true)]
|
||||
private float FlowPercentage
|
||||
{
|
||||
get { return flowPercentage; }
|
||||
set
|
||||
{
|
||||
if (float.IsNaN(flowPercentage)) return;
|
||||
flowPercentage = MathHelper.Clamp(value,-100.0f,100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(100.0f, false)]
|
||||
public float MaxFlow
|
||||
{
|
||||
get { return maxFlow; }
|
||||
set { maxFlow = value; }
|
||||
}
|
||||
|
||||
public Pump(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
item.linkedTo.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{ GetHulls(); };
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (targetLevel != null)
|
||||
{
|
||||
float hullPercentage = 0.0f;
|
||||
if (hull1 != null) hullPercentage = (hull1.Volume / hull1.FullVolume) * 100.0f;
|
||||
flowPercentage = ((float)targetLevel - hullPercentage);
|
||||
}
|
||||
|
||||
|
||||
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
|
||||
|
||||
if (voltage < minVoltage) return;
|
||||
|
||||
if (hull2 == null && hull1 == null) return;
|
||||
|
||||
float powerFactor = (currPowerConsumption==0.0f) ? 1.0f : voltage;
|
||||
//flowPercentage = maxFlow * powerFactor;
|
||||
|
||||
float deltaVolume = 0.0f;
|
||||
|
||||
deltaVolume = (flowPercentage/100.0f) * maxFlow * powerFactor;
|
||||
|
||||
|
||||
hull1.Volume += deltaVolume;
|
||||
if (hull1.Volume > hull1.FullVolume) hull1.Pressure += 0.5f;
|
||||
|
||||
if (hull2 != null)
|
||||
{
|
||||
hull2.Volume -= deltaVolume;
|
||||
if (hull2.Volume > hull1.FullVolume) hull2.Pressure += 0.5f;
|
||||
}
|
||||
|
||||
voltage = 0.0f;
|
||||
}
|
||||
|
||||
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 DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
int width = GuiFrame.Rect.Width, height = GuiFrame.Rect.Height;
|
||||
int x = GuiFrame.Rect.X;
|
||||
int y = GuiFrame.Rect.Y;
|
||||
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
if (GUI.DrawButton(spriteBatch, new Rectangle(x + 20, y + 20, 100, 40), ((isActive) ? "TURN OFF" : "TURN ON")))
|
||||
{
|
||||
targetLevel = null;
|
||||
isActive = !isActive;
|
||||
if (!isActive) currPowerConsumption = 0.0f;
|
||||
}
|
||||
|
||||
spriteBatch.DrawString(GUI.Font, "Flow percentage: " + (int)flowPercentage + " %", new Vector2(x + 20, y + 80), Color.White);
|
||||
|
||||
if (GUI.DrawButton(spriteBatch, new Rectangle(x + 200, y + 70, 40, 40), "+", true)) FlowPercentage += 1.0f;
|
||||
if (GUI.DrawButton(spriteBatch, new Rectangle(x + 250, y + 70, 40, 40), "-", true)) FlowPercentage -= 1.0f;
|
||||
|
||||
|
||||
item.NewComponentEvent(this, true);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(string signal, Connection connection, Item sender, float power=0.0f)
|
||||
{
|
||||
base.ReceiveSignal(signal, connection, sender, power);
|
||||
|
||||
if (connection.Name == "toggle")
|
||||
{
|
||||
isActive = !isActive;
|
||||
}
|
||||
else if (connection.Name == "set_active")
|
||||
{
|
||||
isActive = (signal != "0");
|
||||
}
|
||||
else if (connection.Name == "set_speed")
|
||||
{
|
||||
float tempSpeed;
|
||||
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out tempSpeed))
|
||||
{
|
||||
flowPercentage = MathHelper.Clamp(tempSpeed, -100.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
else if (connection.Name == "set_targetlevel")
|
||||
{
|
||||
float tempTarget;
|
||||
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out tempTarget))
|
||||
{
|
||||
targetLevel = MathHelper.Clamp(tempTarget, 0.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isActive) currPowerConsumption = 0.0f;
|
||||
}
|
||||
|
||||
public override void FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message)
|
||||
{
|
||||
message.Write(flowPercentage);
|
||||
message.Write(isActive);
|
||||
}
|
||||
|
||||
public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetIncomingMessage message)
|
||||
{
|
||||
float newFlow = 0.0f;
|
||||
bool newActive;
|
||||
|
||||
try
|
||||
{
|
||||
newFlow = message.ReadFloat();
|
||||
newActive = message.ReadBoolean();
|
||||
}
|
||||
|
||||
catch { return; }
|
||||
|
||||
FlowPercentage = newFlow;
|
||||
isActive = newActive;
|
||||
}
|
||||
}
|
||||
}
|
||||
221
Subsurface/Source/Items/Components/Machines/Radar.cs
Normal file
221
Subsurface/Source/Items/Components/Machines/Radar.cs
Normal file
@@ -0,0 +1,221 @@
|
||||
using FarseerPhysics;
|
||||
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 Radar : Powered
|
||||
{
|
||||
private float range;
|
||||
|
||||
private float pingState;
|
||||
|
||||
private Sprite pingCircle, screenOverlay;
|
||||
|
||||
[HasDefaultValue(0.0f, false)]
|
||||
public float Range
|
||||
{
|
||||
get { return ConvertUnits.ToDisplayUnits(range); }
|
||||
set { range = ConvertUnits.ToSimUnits(value); }
|
||||
}
|
||||
|
||||
public Radar(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLower())
|
||||
{
|
||||
case "pingcircle":
|
||||
pingCircle = new Sprite(subElement);
|
||||
break;
|
||||
case "screenoverlay":
|
||||
screenOverlay = new Sprite(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//renderTarget = new RenderTarget2D(Game1.CurrGraphicsDevice, GuiFrame.Rect.Width, GuiFrame.Rect.Height);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
if (voltage>=minVoltage)
|
||||
{
|
||||
pingState = (pingState + deltaTime * 0.5f);
|
||||
if (pingState>1.0f)
|
||||
{
|
||||
item.Use(deltaTime, null);
|
||||
pingState = 0.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pingState = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
return (pingState > 1.0f);
|
||||
}
|
||||
|
||||
public override void DrawHUD(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
int width = GuiFrame.Rect.Width, height = GuiFrame.Rect.Height;
|
||||
int x = GuiFrame.Rect.X;
|
||||
int y = GuiFrame.Rect.Y;
|
||||
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
if (voltage < minVoltage) return;
|
||||
|
||||
if (GUI.DrawButton(spriteBatch, new Rectangle(x+20, y+20, 200, 30), "Activate Radar")) isActive = !isActive;
|
||||
|
||||
int radius = GuiFrame.Rect.Height / 2 - 10;
|
||||
DrawRadar(spriteBatch, new Rectangle((int)GuiFrame.Center.X - radius, (int)GuiFrame.Center.Y - radius, radius * 2, radius * 2));
|
||||
|
||||
//voltage = 0.0f;
|
||||
}
|
||||
|
||||
private void DrawRadar(SpriteBatch spriteBatch, Rectangle rect)
|
||||
{
|
||||
|
||||
Vector2 center = new Vector2(rect.Center.X, rect.Center.Y);
|
||||
//lineEnd += new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Math.Min(width, height) / 2.0f;
|
||||
//GUI.DrawLine(spriteBatch, GuiFrame.Center, lineEnd, Color.Green);
|
||||
|
||||
if (!isActive) return;
|
||||
|
||||
if (pingCircle!=null)
|
||||
{
|
||||
pingCircle.Draw(spriteBatch, center, Color.White * (1.0f-pingState), 0.0f, (rect.Width/pingCircle.size.X)*pingState);
|
||||
}
|
||||
|
||||
float radius = pingCircle.size.X / 2.0f;
|
||||
|
||||
float simScale = 1.5f;
|
||||
float displayScale = 0.015f;
|
||||
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
List<Vector2[]> edges = Level.Loaded.GetCellEdges(-Level.Loaded.Position, 7);
|
||||
Vector2 offset = Vector2.Zero;
|
||||
|
||||
for (int i = 0; i < edges.Count; i++)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch,
|
||||
center + (edges[i][0] - offset) * displayScale,
|
||||
center + (edges[i][1] - offset) * displayScale, Color.White);
|
||||
}
|
||||
|
||||
for (int i = 0; i < Submarine.Loaded.HullVertices.Count; i++)
|
||||
{
|
||||
Vector2 start = Submarine.Loaded.HullVertices[i] * simScale;
|
||||
start.Y = -start.Y;
|
||||
Vector2 end = Submarine.Loaded.HullVertices[(i + 1) % Submarine.Loaded.HullVertices.Count] * simScale;
|
||||
end.Y = -end.Y;
|
||||
|
||||
GUI.DrawLine(spriteBatch, center + start, center + end, Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.AnimController.CurrentHull != null) continue;
|
||||
|
||||
Vector2 pos = c.Position * displayScale;
|
||||
if (c.SimPosition != Vector2.Zero && pos.Length() < radius)
|
||||
{
|
||||
int width = (int)MathHelper.Clamp(c.Mass / 20, 1, 10);
|
||||
|
||||
pos.Y = -pos.Y;
|
||||
pos += center;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X - width / 2, (int)pos.Y - width / 2, width, width), Color.White, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (screenOverlay!=null)
|
||||
{
|
||||
screenOverlay.Draw(spriteBatch, center, 0.0f, rect.Width/screenOverlay.size.X);
|
||||
}
|
||||
|
||||
//if (Level.Loaded != null)
|
||||
//{
|
||||
|
||||
// for (int i = 0; i < 2; i++)
|
||||
// {
|
||||
// Vector2 targetPos = (i == 0) ? Level.Loaded.StartPosition : Level.Loaded.EndPosition;
|
||||
// targetPos += Level.Loaded.Position;
|
||||
|
||||
// float dist = targetPos.Length();
|
||||
|
||||
// targetPos.Y = -targetPos.Y;
|
||||
// Vector2 markerPos = Vector2.Normalize(targetPos) * (rect.Width * 0.55f);
|
||||
// markerPos += center;
|
||||
|
||||
// GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X, (int)markerPos.Y, 5, 5), Color.LightGreen);
|
||||
|
||||
// string label;
|
||||
// if (Game1.GameSession.Map!=null)
|
||||
// {
|
||||
// label = (i == 0) ? Game1.GameSession.Map.CurrentLocation.Name : Game1.GameSession.Map.SelectedLocation.Name;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// label = (i == 0) ? "Start" : "End";
|
||||
// }
|
||||
|
||||
// spriteBatch.DrawString(GUI.SmallFont, label, new Vector2(markerPos.X + 10, markerPos.Y), Color.LightGreen);
|
||||
// spriteBatch.DrawString(GUI.SmallFont, (int)(dist / 80.0f) + " m", new Vector2(markerPos.X + 10, markerPos.Y + 15), Color.LightGreen);
|
||||
// }
|
||||
|
||||
DrawMarker(spriteBatch,
|
||||
(Game1.GameSession.Map == null) ? "Start" : Game1.GameSession.Map.CurrentLocation.Name,
|
||||
(Level.Loaded.StartPosition + Level.Loaded.Position), displayScale, center, (rect.Width * 0.55f));
|
||||
|
||||
DrawMarker(spriteBatch,
|
||||
(Game1.GameSession.Map == null) ? "End" : Game1.GameSession.Map.SelectedLocation.Name,
|
||||
(Level.Loaded.EndPosition + Level.Loaded.Position), displayScale, center, (rect.Width * 0.55f));
|
||||
|
||||
if (Game1.GameSession.Quest != null)
|
||||
{
|
||||
var quest = Game1.GameSession.Quest;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(quest.RadarLabel))
|
||||
{
|
||||
DrawMarker(spriteBatch,
|
||||
quest.RadarLabel,
|
||||
quest.RadarPosition, displayScale, center, (rect.Width * 0.55f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMarker(SpriteBatch spriteBatch, string label, Vector2 position, float scale, Vector2 center, float radius)
|
||||
{
|
||||
//position += Level.Loaded.Position;
|
||||
|
||||
float dist = position.Length();
|
||||
|
||||
position *= scale;
|
||||
position.Y = -position.Y;
|
||||
|
||||
Vector2 markerPos = (dist*scale>radius) ? Vector2.Normalize(position) * radius : position;
|
||||
markerPos += center;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)markerPos.X, (int)markerPos.Y, 5, 5), Color.LightGreen);
|
||||
|
||||
spriteBatch.DrawString(GUI.SmallFont, label, new Vector2(markerPos.X + 10, markerPos.Y), Color.LightGreen);
|
||||
spriteBatch.DrawString(GUI.SmallFont, (int)(dist / 80.0f) + " m", new Vector2(markerPos.X + 10, markerPos.Y + 15), Color.LightGreen);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
393
Subsurface/Source/Items/Components/Machines/Reactor.cs
Normal file
393
Subsurface/Source/Items/Components/Machines/Reactor.cs
Normal file
@@ -0,0 +1,393 @@
|
||||
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;
|
||||
|
||||
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 float AvailableFuel { 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;
|
||||
|
||||
shutDownTemp = 500.0f;
|
||||
|
||||
powerPerTemp = 1.0f;
|
||||
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
//ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
fissionRate = Math.Min(fissionRate, AvailableFuel);
|
||||
|
||||
float heat = 100 * fissionRate * (AvailableFuel/2000.0f);
|
||||
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(item, IsRunning, 50.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;
|
||||
|
||||
List<Connection> connections = item.Connections;
|
||||
if (connections!=null && connections.Count>0)
|
||||
{
|
||||
foreach (Connection connection in connections)
|
||||
{
|
||||
foreach (Connection recipient in connection.Recipients)
|
||||
{
|
||||
Item it = recipient.Item as Item;
|
||||
if (it == null) continue;
|
||||
|
||||
PowerTransfer pt = it.GetComponent<PowerTransfer>();
|
||||
if (pt != null) load += pt.PowerLoad;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//foreach (MapEntity e in item.linkedTo)
|
||||
//{
|
||||
// Item it = e as Item;
|
||||
// if (it == null) continue;
|
||||
|
||||
// PowerTransfer pt = it.GetComponent<PowerTransfer>();
|
||||
// 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*powerPerTemp;
|
||||
|
||||
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;
|
||||
AvailableFuel = 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(item, 60.0f, "Reactor meltdown!");
|
||||
item.Condition = 0.0f;
|
||||
//fissionRate = 0.0f;
|
||||
//coolingRate = 0.0f;
|
||||
|
||||
//PlaySound(ActionType.OnFailure, item.Position);
|
||||
//item.ApplyStatusEffects(ActionType.OnFailure, 1.0f, null);
|
||||
|
||||
//new Explosion(item.SimPosition, 6.0f, 500.0f, 600.0f, 10.0f, 2.0f).Explode();
|
||||
|
||||
if (item.ContainedItems!=null)
|
||||
{
|
||||
foreach (Item containedItem in item.ContainedItems)
|
||||
{
|
||||
if (containedItem == null) continue;
|
||||
containedItem.Condition = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return (picker != null);
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch, bool editing)
|
||||
{
|
||||
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 = GuiFrame.Rect.Width, height = GuiFrame.Rect.Height;
|
||||
int x = GuiFrame.Rect.X;
|
||||
int y = GuiFrame.Rect.Y;
|
||||
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
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, "Automatic Temperature Control: " + ((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, "Shutdown 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<T>(IList<T> graph, T newValue)
|
||||
{
|
||||
for (int i = graph.Count - 1; i > 0; i--)
|
||||
{
|
||||
graph[i] = graph[i - 1];
|
||||
}
|
||||
graph[0] = newValue;
|
||||
}
|
||||
|
||||
static void DrawGraph(IList<float> 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 + width, y + height - (graph[1] + (graph[0] - graph[1]) * xOffset) * yScale);
|
||||
|
||||
float currX = x + width - ((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,
|
||||
y + height - (graph[graph.Count - 1] + (graph[graph.Count - 2] - graph[graph.Count - 1]) * xOffset) * yScale);
|
||||
|
||||
GUI.DrawLine(spriteBatch, prevPoint, lastPoint, Color.White);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
bool newAutoTemp;
|
||||
float newTemperature, newShutDownTemp;
|
||||
float newCoolingRate, newFissionRate;
|
||||
|
||||
try
|
||||
{
|
||||
newAutoTemp = message.ReadBoolean();
|
||||
newTemperature = message.ReadFloat();
|
||||
newShutDownTemp = message.ReadFloat();
|
||||
|
||||
newCoolingRate = message.ReadFloat();
|
||||
newFissionRate = message.ReadFloat();
|
||||
}
|
||||
|
||||
catch { return; }
|
||||
|
||||
autoTemp = newAutoTemp;
|
||||
Temperature = newTemperature;
|
||||
shutDownTemp = newShutDownTemp;
|
||||
|
||||
CoolingRate = newCoolingRate;
|
||||
FissionRate = newFissionRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
180
Subsurface/Source/Items/Components/Machines/Steering.cs
Normal file
180
Subsurface/Source/Items/Components/Machines/Steering.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using FarseerPhysics;
|
||||
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 Steering : ItemComponent
|
||||
{
|
||||
private Vector2 currVelocity;
|
||||
private Vector2 targetVelocity;
|
||||
|
||||
private bool autoPilot;
|
||||
|
||||
private SteeringPath steeringPath;
|
||||
|
||||
private static PathFinder pathFinder;
|
||||
|
||||
bool AutoPilot
|
||||
{
|
||||
get { return autoPilot; }
|
||||
set
|
||||
{
|
||||
if (value == autoPilot) return;
|
||||
|
||||
autoPilot = value;
|
||||
|
||||
if (autoPilot)
|
||||
{
|
||||
if (pathFinder==null) pathFinder = new PathFinder(WayPoint.WayPointList, false);
|
||||
steeringPath = pathFinder.FindPath(
|
||||
ConvertUnits.ToSimUnits(Level.Loaded.Position),
|
||||
ConvertUnits.ToSimUnits(Level.Loaded.EndPosition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 TargetVelocity
|
||||
{
|
||||
get { return targetVelocity;}
|
||||
set
|
||||
{
|
||||
if (float.IsNaN(value.X) || float.IsNaN(value.Y))
|
||||
{
|
||||
return;
|
||||
}
|
||||
targetVelocity.X = MathHelper.Clamp(value.X, -100.0f, 100.0f);
|
||||
targetVelocity.Y = MathHelper.Clamp(value.Y, -100.0f, 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public Steering(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
if (autoPilot)
|
||||
{
|
||||
//if (steeringPath==null)
|
||||
//{
|
||||
// PathFinder pathFinder = new PathFinder(WayPoint.WayPointList, false);
|
||||
// steeringPath = pathFinder.FindPath(
|
||||
// ConvertUnits.ToSimUnits(Level.Loaded.StartPosition),
|
||||
// ConvertUnits.ToSimUnits(Level.Loaded.EndPosition));
|
||||
//}
|
||||
|
||||
steeringPath.GetNode(Vector2.Zero, 20.0f);
|
||||
|
||||
if (steeringPath.CurrentNode!=null)
|
||||
{
|
||||
float prediction = 10.0f;
|
||||
|
||||
Vector2 futurePosition = Submarine.Loaded.Speed * prediction;
|
||||
|
||||
Vector2 targetSpeed = (steeringPath.CurrentNode.Position - futurePosition);
|
||||
|
||||
//float dist = targetSpeed.Length();
|
||||
targetSpeed = Vector2.Normalize(targetSpeed);
|
||||
|
||||
TargetVelocity = targetSpeed*100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
item.SendSignal(targetVelocity.X.ToString(CultureInfo.InvariantCulture), "velocity_x_out");
|
||||
item.SendSignal((-targetVelocity.Y).ToString(CultureInfo.InvariantCulture), "velocity_y_out");
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
int width = GuiFrame.Rect.Width, height = GuiFrame.Rect.Height;
|
||||
int x = GuiFrame.Rect.X;
|
||||
int y = GuiFrame.Rect.Y;
|
||||
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
Rectangle velRect = new Rectangle(x + 20, y + 20, width - 40, height - 40);
|
||||
GUI.DrawRectangle(spriteBatch, velRect, Color.White, false);
|
||||
|
||||
if (GUI.DrawButton(spriteBatch, new Rectangle(x + width - 150, y + height - 30, 150, 30), "Autopilot"))
|
||||
{
|
||||
AutoPilot = !AutoPilot;
|
||||
|
||||
item.NewComponentEvent(this, true);
|
||||
}
|
||||
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(velRect.Center.X,velRect.Center.Y),
|
||||
new Vector2(velRect.Center.X + currVelocity.X, velRect.Center.Y - currVelocity.Y),
|
||||
Color.Gray);
|
||||
|
||||
Vector2 targetVelPos = new Vector2(velRect.Center.X + targetVelocity.X, velRect.Center.Y - targetVelocity.Y);
|
||||
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(velRect.Center.X, velRect.Center.Y),
|
||||
targetVelPos,
|
||||
Color.LightGray);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)targetVelPos.X - 5, (int)targetVelPos.Y - 5, 10, 10), Color.White);
|
||||
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, new Vector2(velRect.Center.X, velRect.Center.Y)) < 200.0f)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)targetVelPos.X -10, (int)targetVelPos.Y - 10, 20, 20), Color.Red);
|
||||
|
||||
if (PlayerInput.LeftButtonDown())
|
||||
{
|
||||
targetVelocity = PlayerInput.MousePosition - new Vector2(velRect.Center.X, velRect.Center.Y);
|
||||
targetVelocity.Y = -targetVelocity.Y;
|
||||
|
||||
item.NewComponentEvent(this, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(string signal, Connection connection, Item sender, float power=0.0f)
|
||||
{
|
||||
if (connection.Name == "velocity_in")
|
||||
{
|
||||
currVelocity = ToolBox.ParseToVector2(signal, false);
|
||||
}
|
||||
}
|
||||
|
||||
public override void FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message)
|
||||
{
|
||||
message.Write(targetVelocity.X);
|
||||
message.Write(targetVelocity.Y);
|
||||
|
||||
message.Write(autoPilot);
|
||||
}
|
||||
|
||||
public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetIncomingMessage message)
|
||||
{
|
||||
Vector2 newTargetVelocity = Vector2.Zero;
|
||||
bool newAutoPilot = false;
|
||||
|
||||
try
|
||||
{
|
||||
newTargetVelocity = new Vector2(message.ReadFloat(), message.ReadFloat());
|
||||
newAutoPilot = message.ReadBoolean();
|
||||
}
|
||||
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TargetVelocity = newTargetVelocity;
|
||||
AutoPilot = newAutoPilot;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Subsurface/Source/Items/Components/Machines/Vent.cs
Normal file
32
Subsurface/Source/Items/Components/Machines/Vent.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
233
Subsurface/Source/Items/Components/Power/PowerContainer.cs
Normal file
233
Subsurface/Source/Items/Components/Power/PowerContainer.cs
Normal file
@@ -0,0 +1,233 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class PowerContainer : Powered
|
||||
{
|
||||
//[power/min]
|
||||
float capacity;
|
||||
|
||||
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;
|
||||
|
||||
[Editable, HasDefaultValue(10.0f, true)]
|
||||
public float MaxOutPut
|
||||
{
|
||||
set { maxOutput = value; }
|
||||
get { return maxOutput; }
|
||||
}
|
||||
|
||||
[HasDefaultValue(0.0f, true)]
|
||||
public float Charge
|
||||
{
|
||||
get { return charge; }
|
||||
set
|
||||
{
|
||||
if (float.IsNaN(value)) return;
|
||||
charge = MathHelper.Clamp(value, 0.0f, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(10.0f, false), Editable]
|
||||
public float Capacity
|
||||
{
|
||||
get { return capacity; }
|
||||
set { capacity = Math.Max(value, 1.0f); }
|
||||
}
|
||||
|
||||
[HasDefaultValue(10.0f, false), Editable]
|
||||
public float RechargeSpeed
|
||||
{
|
||||
get { return rechargeSpeed; }
|
||||
set
|
||||
{
|
||||
if (float.IsNaN(value)) return;
|
||||
rechargeSpeed = MathHelper.Clamp(value, 0.0f, maxRechargeSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
[HasDefaultValue(10.0f, false), Editable]
|
||||
public float MaxRechargeSpeed
|
||||
{
|
||||
get { return maxRechargeSpeed; }
|
||||
set { maxRechargeSpeed = Math.Max(value, 1.0f); }
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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<PowerTransfer>();
|
||||
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<PowerTransfer>();
|
||||
// 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, bool editing)
|
||||
{
|
||||
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))
|
||||
{
|
||||
item.NewComponentEvent(this, 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);
|
||||
item.NewComponentEvent(this, true);
|
||||
}
|
||||
}
|
||||
|
||||
public override void FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message)
|
||||
{
|
||||
message.Write(rechargeSpeed);
|
||||
message.Write(charge);
|
||||
}
|
||||
|
||||
public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetIncomingMessage message)
|
||||
{
|
||||
float newRechargeSpeed = 0.0f;
|
||||
float newCharge = 0.0f;
|
||||
|
||||
try
|
||||
{
|
||||
newRechargeSpeed = message.ReadFloat();
|
||||
newCharge = message.ReadFloat();
|
||||
}
|
||||
catch { }
|
||||
|
||||
RechargeSpeed = newRechargeSpeed;
|
||||
Charge = newCharge;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
145
Subsurface/Source/Items/Components/Power/PowerTransfer.cs
Normal file
145
Subsurface/Source/Items/Components/Power/PowerTransfer.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
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<Powered> connectedList = new List<Powered>();
|
||||
|
||||
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("", "power", fullPower / Math.Max(fullLoad, 1.0f));
|
||||
if (-pt.currPowerConsumption > Math.Max(pt.powerLoad * 2.0f, 200.0f)) pt.item.Condition -= deltaTime*10.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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<Connection> 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<Powered>();
|
||||
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.CurrPowerConsumption > 0.0f)
|
||||
{
|
||||
fullLoad += powered.CurrPowerConsumption;
|
||||
}
|
||||
else if (powered.CurrPowerConsumption < 0.0f)
|
||||
//negative power consumption = the construction is a
|
||||
//generator/battery or another junction box
|
||||
{
|
||||
fullPower -= powered.CurrPowerConsumption;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
int width = GuiFrame.Rect.Width, height = GuiFrame.Rect.Height;
|
||||
int x = GuiFrame.Rect.X;
|
||||
int y = GuiFrame.Rect.Y;
|
||||
|
||||
GuiFrame.Draw(spriteBatch);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(string signal, Connection connection, Item sender, float power)
|
||||
{
|
||||
base.ReceiveSignal(signal, connection, sender, power);
|
||||
|
||||
if (connection.Name == "signal")
|
||||
{
|
||||
connection.SendSignal(signal, sender, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
99
Subsurface/Source/Items/Components/Power/Powered.cs
Normal file
99
Subsurface/Source/Items/Components/Power/Powered.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
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;
|
||||
|
||||
//current voltage of the item (load / power)
|
||||
protected float voltage;
|
||||
|
||||
//the minimum voltage required for the item to work
|
||||
protected float minVoltage;
|
||||
|
||||
//the maximum amount of power the item can draw from connected items
|
||||
protected float powerConsumption;
|
||||
|
||||
private bool powerOnSoundPlayed;
|
||||
|
||||
private static Sound powerOnSound;
|
||||
|
||||
[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, float power)
|
||||
{
|
||||
if (currPowerConsumption == 0.0f) voltage = 0.0f;
|
||||
if (connection.Name == "power_in" || connection.Name == "power") voltage = power;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (currPowerConsumption == 0.0f) return;
|
||||
if (voltage > minVoltage)
|
||||
{
|
||||
if (!powerOnSoundPlayed)
|
||||
{
|
||||
powerOnSound.Play(1.0f, 600.0f, item.Position);
|
||||
powerOnSoundPlayed = true;
|
||||
}
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime);
|
||||
}
|
||||
else if (voltage < 0.1f)
|
||||
{
|
||||
powerOnSoundPlayed = false;
|
||||
}
|
||||
}
|
||||
|
||||
public Powered(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
if (powerOnSound==null)
|
||||
{
|
||||
powerOnSound = Sound.Load("Content/Items/Electricity/powerOn.ogg");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
221
Subsurface/Source/Items/Components/Projectile.cs
Normal file
221
Subsurface/Source/Items/Components/Projectile.cs
Normal file
@@ -0,0 +1,221 @@
|
||||
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<Body> 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<Body>();
|
||||
|
||||
//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(float deltaTime, 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();
|
||||
|
||||
AttackResult attackResult = new AttackResult(0.0f, 0.0f);
|
||||
if (attack!=null)
|
||||
{
|
||||
Limb limb;
|
||||
Structure structure;
|
||||
if ((limb = (f2.Body.UserData as Limb)) != null)
|
||||
{
|
||||
attackResult = attack.DoDamage(limb.character, item.SimPosition, 1.0f);
|
||||
}
|
||||
else if ((structure = (f2.Body.UserData as Structure)) != null)
|
||||
{
|
||||
attackResult = attack.DoDamage(structure, item.SimPosition, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
|
||||
|
||||
item.body.FarseerBody.IsBullet = false;
|
||||
item.body.CollisionCategories = Physics.CollisionMisc;
|
||||
item.body.CollidesWith = Physics.CollisionWall;
|
||||
|
||||
ignoredBodies.Clear();
|
||||
|
||||
if (attackResult.HitArmor)
|
||||
{
|
||||
item.body.LinearVelocity *= 0.5f;
|
||||
}
|
||||
else 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 = 30.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
335
Subsurface/Source/Items/Components/Rope.cs
Normal file
335
Subsurface/Source/Items/Components/Rope.cs
Normal file
@@ -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<RangedWeapon>();
|
||||
//if (weapon != null) barrelPos = weapon.barrelPos;
|
||||
|
||||
return barrelPos;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 TransformedBarrelPos
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector2 barrelPos = Vector2.Zero;
|
||||
|
||||
RangedWeapon weapon = item.GetComponent<RangedWeapon>();
|
||||
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);
|
||||
|
||||
List<Body>ropeList = PathManager.EvenlyDistributeShapesAlongPath(Game1.World, ropePath, shape, BodyType.Dynamic, (int)(length/sectionLength));
|
||||
|
||||
ropeBodies = new PhysicsBody[ropeList.Count()];
|
||||
for (int i = 0; i<ropeBodies.Length; i++)
|
||||
{
|
||||
ropeList[i].Mass = 0.01f;
|
||||
ropeList[i].Enabled = false;
|
||||
//only collide with the map
|
||||
ropeList[i].CollisionCategories = Physics.CollisionMisc;
|
||||
ropeList[i].CollidesWith = Physics.CollisionWall;
|
||||
|
||||
ropeBodies[i] = new PhysicsBody(ropeList[i]);
|
||||
}
|
||||
|
||||
List<RevoluteJoint> 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(float deltaTime, 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.5f<sectionLength)
|
||||
{
|
||||
NextSection(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
gunJoint.Length = Math.Max(gunJoint.Length-0.01f,0.01f);
|
||||
gunJoint.Frequency = 30;
|
||||
gunJoint.DampingRatio = 0.05f;
|
||||
//gunJoint.MotorEnabled = true;
|
||||
//gunJoint.MotorSpeed = -150.0f;
|
||||
//ropeBodies[i + 1].ApplyForce(dist / length * pullForce);
|
||||
//ropeJoints[0].LocalAnchorA = new Vector2(ropeJoints[0].LocalAnchorA.X-0.05f,ropeJoints[0].LocalAnchorA.Y);
|
||||
//ropeBodies[i].SmoothRotate(item.body.Rotation);
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Vector2 dist = ropeBodies[i].Position - ropeBodies[i + 1].Position;
|
||||
//float length = dist.Length();
|
||||
|
||||
//ropeBodies[i + 1].ApplyForce(dist / length * pullForce * 0.1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void NextSection(int i)
|
||||
{
|
||||
gunJoint.Length = sectionLength;
|
||||
ropeBodies[i].UserData = true;
|
||||
ropeBodies[i].Enabled = false;
|
||||
|
||||
//if (ropeJoints[0] != null) Game1.world.RemoveJoint(ropeJoints[0]);
|
||||
//ropeJoints[0] = JointFactory.CreateRevoluteJoint(Game1.world,
|
||||
// item.body.FarseerBody, ropeBodies[i + 1].FarseerBody,
|
||||
// BarrelPos, new Vector2(-sectionLength / 2, 0.0f));
|
||||
|
||||
AttachGunJoint(ropeBodies[i + 1].FarseerBody);
|
||||
|
||||
if (i == ropeBodies.Length - 2)
|
||||
{
|
||||
item.Combine(projectile);
|
||||
ropeBodies[ropeBodies.Length - 1].Enabled = false;
|
||||
isActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (reload>0.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, bool editing)
|
||||
{
|
||||
base.Draw(spriteBatch);
|
||||
|
||||
if (!isActive) return;
|
||||
|
||||
RevoluteJoint firstJoint = null;
|
||||
|
||||
for (int i = 0; i<ropeBodies.Length-1; i++)
|
||||
{
|
||||
if (!ropeBodies[i].Enabled) continue;
|
||||
|
||||
if (firstJoint==null) firstJoint = ropeJoints[i];
|
||||
|
||||
DrawSection(spriteBatch, ropeJoints[i].WorldAnchorA, ropeJoints[i+1].WorldAnchorA, i);
|
||||
}
|
||||
|
||||
if (gunJoint == null || firstJoint==null) return;
|
||||
|
||||
DrawSection(spriteBatch, gunJoint.WorldAnchorA, firstJoint.WorldAnchorA, 0);
|
||||
|
||||
}
|
||||
|
||||
private void DrawSection(SpriteBatch spriteBatch, Vector2 start, Vector2 end, int i)
|
||||
{
|
||||
start.Y = -start.Y;
|
||||
end.Y = -end.Y;
|
||||
|
||||
spriteBatch.Draw(sprite.Texture,
|
||||
ConvertUnits.ToDisplayUnits(start), null, Color.White,
|
||||
MathUtils.VectorToAngle(end - start),
|
||||
new Vector2(0.0f, sprite.size.Y / 2.0f),
|
||||
new Vector2((ConvertUnits.ToDisplayUnits(Vector2.Distance(start, end))) / sprite.Texture.Width, 1.0f),
|
||||
SpriteEffects.None,
|
||||
sprite.Depth + i*0.00001f);
|
||||
}
|
||||
|
||||
public void Attach(Item projectile)
|
||||
{
|
||||
reload = 0.5f;
|
||||
isActive = true;
|
||||
|
||||
this.projectile = projectile;
|
||||
//Projectile projectileComponent = projectile.GetComponent<Projectile>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
76
Subsurface/Source/Items/Components/Signal/AndComponent.cs
Normal file
76
Subsurface/Source/Items/Components/Signal/AndComponent.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
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<timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] > timeFrame) sendOutput = false;
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
if (sendOutput)
|
||||
{
|
||||
item.SendSignal(output, "signal_out");
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(string signal, Connection connection, Item sender, float power=0.0f)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
470
Subsurface/Source/Items/Components/Signal/Connection.cs
Normal file
470
Subsurface/Source/Items/Components/Signal/Connection.cs
Normal file
@@ -0,0 +1,470 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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;
|
||||
|
||||
private List<StatusEffect> effects;
|
||||
|
||||
int[] wireId;
|
||||
|
||||
public List<Connection> Recipients
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Connection> recipients = new List<Connection>();
|
||||
for (int i = 0; i<MaxLinked; i++)
|
||||
{
|
||||
if (Wires[i] == null) continue;
|
||||
Connection recipient = Wires[i].OtherConnection(this);
|
||||
if (recipient != null) recipients.Add(recipient);
|
||||
}
|
||||
return recipients;
|
||||
}
|
||||
}
|
||||
|
||||
public Item Item
|
||||
{
|
||||
get { return item; }
|
||||
}
|
||||
|
||||
public Connection(XElement element, Item item)
|
||||
{
|
||||
|
||||
if (connector == null)
|
||||
{
|
||||
connector = new Sprite("Content/Items/connector.png", new Vector2(0.5f, 0.5f));
|
||||
wireCorner = new Sprite("Content/Items/wireCorner.png", new Vector2(0.5f, 0.1f));
|
||||
wireVertical = new Sprite("Content/Items/wireVertical.png", new Vector2(0.5f, 0.5f));
|
||||
wireHorizontal = new Sprite("Content/Items/wireHorizontal.png", new Vector2(0.5f, 0.5f));
|
||||
}
|
||||
|
||||
this.item = item;
|
||||
|
||||
//recipient = new Connection[MaxLinked];
|
||||
Wires = new Wire[MaxLinked];
|
||||
|
||||
IsOutput = (element.Name.ToString() == "output");
|
||||
Name = ToolBox.GetAttributeString(element, "name", (IsOutput) ? "output" : "input");
|
||||
|
||||
effects = new List<StatusEffect>();
|
||||
|
||||
wireId = new int[MaxLinked];
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLower())
|
||||
{
|
||||
case "link":
|
||||
int index = -1;
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (wireId[i]<1) index = i;
|
||||
}
|
||||
if (index == -1) break;
|
||||
|
||||
wireId[index] = ToolBox.GetAttributeInt(subElement, "w", -1);
|
||||
|
||||
break;
|
||||
|
||||
case "statuseffect":
|
||||
effects.Add(StatusEffect.Load(subElement));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int FindEmptyIndex()
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (Wires[i]==null) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//public int FindLinkIndex(Item item)
|
||||
//{
|
||||
// for (int i = 0; i < MaxLinked; i++)
|
||||
// {
|
||||
// if (item == null && recipient[i] == null) return i;
|
||||
// if (recipient[i]!=null && recipient[i].item == item) return i;
|
||||
// }
|
||||
// return -1;
|
||||
//}
|
||||
|
||||
public int FindWireIndex(Item wireItem)
|
||||
{
|
||||
for (int i = 0; i < MaxLinked; i++)
|
||||
{
|
||||
if (Wires[i] == null && wireItem == null) return i;
|
||||
if (Wires[i] != null && Wires[i].Item == wireItem) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void AddLink(int index, Wire wire)
|
||||
{
|
||||
//linked[index] = connectedItem;
|
||||
//recipient[index] = otherConnection;
|
||||
Wires[index] = wire;
|
||||
}
|
||||
|
||||
//public bool AddLink(Item connectedItem, Connection otherConnection)
|
||||
//{
|
||||
// if (linked.Contains(connectedItem)) return false;
|
||||
|
||||
// for (int i = 0; i<MaxLinked; i++)
|
||||
// {
|
||||
// if (linked[i]!=null) continue;
|
||||
|
||||
// linked[i] = connectedItem;
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// return false;
|
||||
//}
|
||||
|
||||
public void SendSignal(string signal, Item sender, float power)
|
||||
{
|
||||
for (int i = 0; i<MaxLinked; i++)
|
||||
{
|
||||
if (Wires[i]==null) continue;
|
||||
|
||||
Connection recipient = Wires[i].OtherConnection(this);
|
||||
if (recipient == null) continue;
|
||||
if (recipient.item == this.item || recipient.item == sender) continue;
|
||||
|
||||
foreach (ItemComponent ic in recipient.item.components)
|
||||
{
|
||||
ic.ReceiveSignal(signal, recipient, sender, power);
|
||||
}
|
||||
|
||||
foreach (StatusEffect effect in recipient.effects)
|
||||
{
|
||||
|
||||
//effect.Apply(ActionType.OnUse, 1.0f, recipient.item, recipient.item);
|
||||
recipient.item.ApplyStatusEffect(effect, ActionType.OnUse, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearConnections()
|
||||
{
|
||||
for (int i = 0; i<MaxLinked; i++)
|
||||
{
|
||||
if (Wires[i] == null) continue;
|
||||
|
||||
Wires[i].RemoveConnection(this);
|
||||
Wires[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void DrawConnections(SpriteBatch spriteBatch, ConnectionPanel panel, Character character)
|
||||
{
|
||||
|
||||
int width = 400, height = 200;
|
||||
int x = Game1.GraphicsWidth/2 - width/2, y = Game1.GraphicsHeight - height;
|
||||
|
||||
Rectangle panelRect = new Rectangle(x, y, width, height);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, panelRect, Color.Black, true);
|
||||
|
||||
bool mouseInRect = panelRect.Contains(PlayerInput.MousePosition);
|
||||
|
||||
Wire equippedWire = null;
|
||||
//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<Wire>();
|
||||
if (wireComponent != null) equippedWire = wireComponent;
|
||||
}
|
||||
|
||||
Vector2 rightPos = new Vector2(x + width - 110, y + 20);
|
||||
Vector2 leftPos = new Vector2(x + 110, y + 20);
|
||||
|
||||
float wireInterval = 10.0f;
|
||||
|
||||
float rightWireX = x+width / 2 + wireInterval;
|
||||
float leftWireX = x + width / 2 - wireInterval;
|
||||
foreach (Connection c in panel.connections)
|
||||
{
|
||||
//if dragging a wire, let the Inventory know so that the wire can be
|
||||
//dropped or dragged from the panel to the players inventory
|
||||
if (draggingConnected != null)
|
||||
{
|
||||
int linkIndex = c.FindWireIndex(draggingConnected);
|
||||
if (linkIndex>-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, equippedWire != null);
|
||||
|
||||
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, equippedWire != null);
|
||||
|
||||
leftPos.Y += 30;
|
||||
leftWireX -= wireInterval;
|
||||
}
|
||||
}
|
||||
|
||||
//if the character using the panel has a wire item equipped
|
||||
//and the wire hasn't been connected yet, draw it on the panel
|
||||
if (equippedWire!=null)
|
||||
{
|
||||
if (panel.connections.Find(c => c.Wires.Contains(equippedWire)) == null)
|
||||
{
|
||||
DrawWire(spriteBatch, equippedWire.Item, equippedWire.Item,
|
||||
new Vector2(x + width / 2, y + height - 100),
|
||||
new Vector2(x + width / 2, y + height), mouseInRect, false);
|
||||
|
||||
if (draggingConnected == equippedWire.Item) Inventory.draggingItem = equippedWire.Item;
|
||||
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
//for (int i = 0; i < character.SelectedItems.Length; i++ )
|
||||
//{
|
||||
// Item selectedItem = character.SelectedItems[i];
|
||||
|
||||
// if (selectedItem == null) continue;
|
||||
|
||||
// Wire wireComponent = selectedItem.GetComponent<Wire>();
|
||||
|
||||
|
||||
//}
|
||||
|
||||
//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, bool wireEquipped)
|
||||
{
|
||||
|
||||
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<MaxLinked; i++)
|
||||
{
|
||||
if (Wires[i]==null) continue;
|
||||
|
||||
Connection recipient = Wires[i].OtherConnection(this);
|
||||
|
||||
DrawWire(spriteBatch, Wires[i].Item, (recipient == null) ? Wires[i].Item : recipient.item, position, wirePosition, mouseIn, wireEquipped);
|
||||
wirePosition.X += (IsOutput) ? -20 : 20;
|
||||
}
|
||||
|
||||
//dragging a wire and released the mouse -> 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<Wire>();
|
||||
|
||||
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].Item.SetTransform(item.SimPosition, 0.0f);
|
||||
Wires[index].Item.Drop();
|
||||
Wires[index].Item.body.Enabled = true;
|
||||
Wires[index] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static void DrawWire(SpriteBatch spriteBatch, Item wireItem, Item item, Vector2 end, Vector2 start, bool mouseIn, bool wireEquipped)
|
||||
{
|
||||
if (draggingConnected == wireItem)
|
||||
{
|
||||
if (!mouseIn) return;
|
||||
end = PlayerInput.MousePosition;
|
||||
}
|
||||
|
||||
bool mouseOn = false;
|
||||
|
||||
int textX = (int)start.X;
|
||||
float connLength = 10.0f;
|
||||
|
||||
float alpha = wireEquipped ? 0.5f : 1.0f;
|
||||
|
||||
//Color color = (wireEquipped) ? wireItem.Color * 0.5f : wireItem.Color;
|
||||
|
||||
if (Math.Abs(end.X-start.X)<connLength*6.0f)
|
||||
{
|
||||
wireVertical.DrawTiled(spriteBatch,
|
||||
new Vector2(end.X - wireVertical.size.X / 2, end.Y + connLength),
|
||||
new Vector2(wireVertical.size.X, (float)Math.Abs(end.Y - start.Y)), wireItem.Color * alpha);
|
||||
textX = (int)end.X;
|
||||
connector.Draw(spriteBatch, end, Color.White*alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 pos = new Vector2(start.X, end.Y + wireCorner.size.Y+1) - wireVertical.size / 2;
|
||||
Vector2 size = new Vector2(wireVertical.size.X, (float)Math.Abs((end.Y + wireCorner.size.Y) - start.Y));
|
||||
wireVertical.DrawTiled(spriteBatch, pos, size, wireItem.Color * alpha);
|
||||
|
||||
Rectangle rect = new Rectangle((int)pos.X, (int)pos.Y, (int)size.X, (int)size.Y);
|
||||
if (!wireEquipped && rect.Contains(PlayerInput.MousePosition)) mouseOn = true;
|
||||
|
||||
float dir = (end.X > start.X) ? -1.0f : 1.0f;
|
||||
|
||||
wireCorner.Draw(spriteBatch,
|
||||
new Vector2(start.X, end.Y-1), wireItem.Color * alpha, 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;
|
||||
|
||||
pos = new Vector2(Math.Min(wireStartX,wireEndX), end.Y - wireVertical.size.Y / 2);
|
||||
size = new Vector2(Math.Abs(wireStartX - wireEndX), wireHorizontal.size.Y);
|
||||
|
||||
wireHorizontal.DrawTiled(spriteBatch, pos, size, wireItem.Color * alpha);
|
||||
rect = new Rectangle((int)pos.X, (int)pos.Y, (int)size.X, (int)size.Y);
|
||||
if (!wireEquipped && rect.Contains(PlayerInput.MousePosition)) mouseOn = true;
|
||||
|
||||
connector.Draw(spriteBatch, end, Color.White*alpha, -MathHelper.PiOver2 * dir);
|
||||
}
|
||||
|
||||
if (draggingConnected == null && !wireEquipped)
|
||||
{
|
||||
if (mouseOn || Vector2.Distance(end, PlayerInput.MousePosition)<20.0f)
|
||||
{
|
||||
item.IsHighlighted = true;
|
||||
//start dragging the wire
|
||||
if (PlayerInput.LeftButtonDown()) draggingConnected = wireItem;
|
||||
}
|
||||
}
|
||||
|
||||
spriteBatch.DrawString(GUI.Font, item.Name,
|
||||
new Vector2(textX, start.Y-30),
|
||||
(mouseOn && !wireEquipped) ? Color.Gold : 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));
|
||||
|
||||
Array.Sort(Wires, delegate(Wire wire1, Wire wire2)
|
||||
{
|
||||
if (wire1 == null) return 1;
|
||||
if (wire2 == null) return -1;
|
||||
return wire1.Item.ID.CompareTo(wire2.Item.ID);
|
||||
});
|
||||
|
||||
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].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<Wire>();
|
||||
|
||||
if (Wires[i]!=null)
|
||||
{
|
||||
Wires[i].Item.body.Enabled = false;
|
||||
Wires[i].Connect(this, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
wireId = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
143
Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs
Normal file
143
Subsurface/Source/Items/Components/Signal/ConnectionPanel.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class ConnectionPanel : ItemComponent
|
||||
{
|
||||
public List<Connection> connections;
|
||||
|
||||
Character user;
|
||||
|
||||
public ConnectionPanel(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
connections = new List<Connection>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
if (character != Character.Controlled || character != user) return;
|
||||
Connection.DrawConnections(spriteBatch, this, character);
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
XElement componentElement = base.Save(parentElement);
|
||||
|
||||
foreach (Connection c in connections)
|
||||
{
|
||||
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 Select(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 Remove()
|
||||
{
|
||||
base.Remove();
|
||||
}
|
||||
|
||||
public override void FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message)
|
||||
{
|
||||
foreach (Connection c in connections)
|
||||
{
|
||||
Wire[] wires = Array.FindAll(c.Wires, w => w != null);
|
||||
message.Write((byte)wires.Length);
|
||||
for (int i = 0 ; i < c.Wires.Length; i++)
|
||||
{
|
||||
if (c.Wires[i] == null) continue;
|
||||
message.Write(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();
|
||||
try
|
||||
{
|
||||
byte wireCount = message.ReadByte();
|
||||
|
||||
for (int i = 0; i < wireCount; i++)
|
||||
{
|
||||
int wireId = message.ReadInt32();
|
||||
|
||||
Item wireItem = MapEntity.FindEntityByID(wireId) as Item;
|
||||
if (wireItem == null) continue;
|
||||
|
||||
Wire wireComponent = wireItem.GetComponent<Wire>();
|
||||
if (wireComponent == null) continue;
|
||||
|
||||
c.Wires[i] = wireComponent;
|
||||
wireComponent.Connect(c, false);
|
||||
}
|
||||
}
|
||||
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
Subsurface/Source/Items/Components/Signal/LightComponent.cs
Normal file
149
Subsurface/Source/Items/Components/Signal/LightComponent.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Subsurface.Lights;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class LightComponent : Powered
|
||||
{
|
||||
static Sound[] sparkSounds;
|
||||
|
||||
private Color lightColor;
|
||||
|
||||
//private Sprite sprite;
|
||||
|
||||
LightSource light;
|
||||
|
||||
float range;
|
||||
|
||||
float lightBrightness;
|
||||
|
||||
[Editable, HasDefaultValue(100.0f, true)]
|
||||
public float Range
|
||||
{
|
||||
get { return range; }
|
||||
set
|
||||
{
|
||||
range = MathHelper.Clamp(value, 0.0f, 2048.0f);
|
||||
}
|
||||
}
|
||||
|
||||
[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 override void Move(Vector2 amount)
|
||||
{
|
||||
light.Position += amount;
|
||||
}
|
||||
|
||||
public LightComponent(Item item, XElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
if (sparkSounds==null)
|
||||
{
|
||||
sparkSounds = new Sound[4];
|
||||
string dir = Path.GetDirectoryName(item.Prefab.ConfigFile)+"\\";
|
||||
for (int i = 0; i<4; i++)
|
||||
{
|
||||
sparkSounds[i] = Sound.Load(dir+"zap"+(i+1)+".ogg");
|
||||
}
|
||||
}
|
||||
|
||||
//foreach (XElement subElement in element.Elements())
|
||||
//{
|
||||
// if (subElement.Name.ToString().ToLower() != "sprite") continue;
|
||||
// sprite = new Sprite(subElement);
|
||||
// break;
|
||||
//}
|
||||
|
||||
light = new LightSource(item.Position, 100.0f, Color.White);
|
||||
|
||||
isActive = true;
|
||||
|
||||
//lightColor = new Color(ToolBox.GetAttributeVector4(element, "color", Vector4.One));
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
if (item.body != null)
|
||||
{
|
||||
light.Position = ConvertUnits.ToDisplayUnits(item.body.Position);
|
||||
}
|
||||
|
||||
Pickable pickable = item.GetComponent<Pickable>();
|
||||
if (item.container!= null || (pickable!=null && pickable.Picker!=null))
|
||||
{
|
||||
light.Color = Color.Transparent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (powerConsumption == 0.0f)
|
||||
{
|
||||
voltage = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
currPowerConsumption = powerConsumption;
|
||||
}
|
||||
|
||||
if (Rand.Range(0.0f, 1.0f) < 0.05f && voltage < Rand.Range(0.0f, minVoltage))
|
||||
{
|
||||
if (voltage > 0.1f) sparkSounds[Rand.Int(sparkSounds.Length)].Play(1.0f, 400.0f, item.Position);
|
||||
lightBrightness = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
lightBrightness = MathHelper.Lerp(lightBrightness, Math.Min(voltage, 1.0f), 0.1f);
|
||||
}
|
||||
|
||||
light.Color = lightColor * lightBrightness;
|
||||
|
||||
light.Range = range * (float)Math.Sqrt(lightBrightness);
|
||||
|
||||
voltage = 0.0f;
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch, bool editing)
|
||||
{
|
||||
if (!isActive)
|
||||
{
|
||||
light.Color = Color.Transparent;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Remove()
|
||||
{
|
||||
base.Remove();
|
||||
|
||||
light.Remove();
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(string signal, Connection connection, Item sender, float power=0.0f)
|
||||
{
|
||||
base.ReceiveSignal(signal, connection, sender, power);
|
||||
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "toggle":
|
||||
isActive = !isActive;
|
||||
break;
|
||||
case "set_state":
|
||||
isActive = (signal != "0");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Subsurface/Source/Items/Components/Signal/NotComponent.cs
Normal file
19
Subsurface/Source/Items/Components/Signal/NotComponent.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
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, float power=0.0f)
|
||||
{
|
||||
if (connection.Name != "signal_in") return;
|
||||
|
||||
item.SendSignal(signal=="0" ? "1" : "0", "signal_out");
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Subsurface/Source/Items/Components/Signal/OrComponent.cs
Normal file
27
Subsurface/Source/Items/Components/Signal/OrComponent.cs
Normal file
@@ -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<timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] > timeFrame) sendOutput = false;
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
if (sendOutput)
|
||||
{
|
||||
item.SendSignal(output, "signal_out");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Subsurface/Source/Items/Components/Signal/OxygenDetector.cs
Normal file
36
Subsurface/Source/Items/Components/Signal/OxygenDetector.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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, float power = 0.0f)
|
||||
{
|
||||
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");
|
||||
return;
|
||||
}
|
||||
|
||||
item.SendSignal(success ? output : "0", "signal_out");
|
||||
|
||||
break;
|
||||
case "set_output":
|
||||
output = signal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class SignalCheckComponent : ItemComponent
|
||||
{
|
||||
private string output;
|
||||
|
||||
private string targetSignal;
|
||||
|
||||
[InGameEditable, HasDefaultValue("1", true)]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
set { output = value; }
|
||||
}
|
||||
|
||||
[InGameEditable, HasDefaultValue("", true)]
|
||||
public string TargetSignal
|
||||
{
|
||||
get { return targetSignal; }
|
||||
set { targetSignal = value; }
|
||||
}
|
||||
|
||||
public SignalCheckComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(string signal, Connection connection, Item sender, float power=0.0f)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_in":
|
||||
item.SendSignal((signal == targetSignal) ? output : "0", "signal_out");
|
||||
|
||||
break;
|
||||
case "set_output":
|
||||
output = signal;
|
||||
break;
|
||||
case "set_targetsignal":
|
||||
targetSignal = signal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
417
Subsurface/Source/Items/Components/Signal/Wire.cs
Normal file
417
Subsurface/Source/Items/Components/Signal/Wire.cs
Normal file
@@ -0,0 +1,417 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class Wire : ItemComponent
|
||||
{
|
||||
const float nodeDistance = 32.0f;
|
||||
const float heightFromFloor = 128.0f;
|
||||
|
||||
static Sprite wireSprite;
|
||||
|
||||
public List<Vector2> Nodes;
|
||||
|
||||
Connection[] connections;
|
||||
|
||||
private Vector2 newNodePos;
|
||||
|
||||
private static int? selectedNodeIndex;
|
||||
|
||||
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<Vector2>();
|
||||
|
||||
connections = new Connection[2];
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
{
|
||||
amount = FarseerPhysics.ConvertUnits.ToDisplayUnits(amount);
|
||||
//for (int i = 0; i<Nodes.Count; i++)
|
||||
//{
|
||||
// Nodes[i] += amount;
|
||||
//}
|
||||
}
|
||||
|
||||
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, bool loading = false)
|
||||
{
|
||||
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)
|
||||
{
|
||||
//List<Vector2> prevNodes = new List<Vector2>(Nodes);
|
||||
|
||||
|
||||
foreach (ItemComponent ic in item.components)
|
||||
{
|
||||
if (ic == this) continue;
|
||||
ic.Drop(null);
|
||||
}
|
||||
if (item.container != null) item.container.RemoveContained(this.item);
|
||||
|
||||
|
||||
item.body.Enabled = false;
|
||||
|
||||
isActive = false;
|
||||
|
||||
//Nodes = prevNodes;
|
||||
CleanNodes();
|
||||
}
|
||||
|
||||
if (!loading) Item.NewComponentEvent(this, true);
|
||||
}
|
||||
|
||||
public override void Equip(Character character)
|
||||
{
|
||||
ClearConnections();
|
||||
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
public override void Unequip(Character character)
|
||||
{
|
||||
ClearConnections();
|
||||
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
{
|
||||
ClearConnections();
|
||||
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (Nodes.Count == 0) return;
|
||||
|
||||
item.FindHull();
|
||||
|
||||
Vector2 position = item.Position;
|
||||
position.X = MathUtils.Round(item.Position.X, nodeDistance);
|
||||
if (item.CurrentHull == null)
|
||||
{
|
||||
position.Y = MathUtils.Round(item.Position.Y, nodeDistance);
|
||||
}
|
||||
else
|
||||
{
|
||||
position.Y -= item.CurrentHull.Rect.Y - item.CurrentHull.Rect.Height;
|
||||
position.Y = Math.Max(MathUtils.Round(position.Y, nodeDistance), heightFromFloor);
|
||||
position.Y += item.CurrentHull.Rect.Y - item.CurrentHull.Rect.Height;
|
||||
}
|
||||
|
||||
newNodePos = RoundNode(item.Position, item.CurrentHull);
|
||||
|
||||
//if (Vector2.Distance(position, nodes[nodes.Count - 1]) > nodeDistance*10)
|
||||
//{
|
||||
// nodes.Add(position);
|
||||
|
||||
// item.NewComponentEvent(this, true);
|
||||
//}
|
||||
//else if (Math.Abs(position.Y - nodes[nodes.Count - 1].Y) > nodeDistance)
|
||||
//{
|
||||
// nodes.Add(new Vector2(nodes[nodes.Count - 1].X,
|
||||
// position.Y));
|
||||
|
||||
// item.NewComponentEvent(this, true);
|
||||
//}
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == Character.Controlled && character.SelectedConstruction != null) return false;
|
||||
|
||||
if (newNodePos!= Vector2.Zero && Nodes.Count>0 && Vector2.Distance(newNodePos, Nodes[Nodes.Count - 1]) > nodeDistance)
|
||||
{
|
||||
Nodes.Add(newNodePos);
|
||||
newNodePos = Vector2.Zero;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
if (Nodes.Count > 1)
|
||||
{
|
||||
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 Vector2 RoundNode(Vector2 position, Hull hull)
|
||||
{
|
||||
position.X = MathUtils.Round(position.X, nodeDistance);
|
||||
if (hull == null)
|
||||
{
|
||||
position.Y = MathUtils.Round(position.Y, nodeDistance);
|
||||
}
|
||||
else
|
||||
{
|
||||
position.Y -= hull.Rect.Y - hull.Rect.Height;
|
||||
position.Y = Math.Max(MathUtils.Round(position.Y, nodeDistance), heightFromFloor);
|
||||
position.Y += hull.Rect.Y -hull.Rect.Height;
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
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, bool editing)
|
||||
{
|
||||
if (Nodes.Count == 0) return;
|
||||
|
||||
//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; i < Nodes.Count; i++)
|
||||
{
|
||||
DrawSection(spriteBatch, Nodes[i], Nodes[i - 1], i, item.Color);
|
||||
}
|
||||
|
||||
if (isActive && Vector2.Distance(newNodePos, Nodes[Nodes.Count - 1]) > nodeDistance)
|
||||
{
|
||||
DrawSection(spriteBatch, Nodes[Nodes.Count - 1], newNodePos, Nodes.Count, item.Color * 0.5f);
|
||||
//nodes.Add(newNodePos);
|
||||
}
|
||||
|
||||
if (!editing) return;
|
||||
|
||||
for (int i = 1; i < Nodes.Count; i++)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)Nodes[i].X - 3, (int)-Nodes[i].Y -3, 6, 6), Color.Red, true, 0.0f);
|
||||
|
||||
if (Vector2.Distance(Game1.EditMapScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), Nodes[i]) < 20.0f)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)Nodes[i].X - 10, (int)-Nodes[i].Y - 10, 20, 20), Color.Red, false, 0.0f);
|
||||
|
||||
if (selectedNodeIndex==null && selectedNodeIndex>0 && selectedNodeIndex<Nodes.Count-1)
|
||||
{
|
||||
if ( PlayerInput.LeftButtonDown())
|
||||
{
|
||||
MapEntity.SelectEntity(item);
|
||||
selectedNodeIndex = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
Nodes.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (PlayerInput.LeftButtonDown())
|
||||
{
|
||||
|
||||
if (selectedNodeIndex!=null && item.IsSelected)
|
||||
{
|
||||
MapEntity.DisableSelect = true;
|
||||
Nodes[(int)selectedNodeIndex] = Game1.EditMapScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
|
||||
Vector2 pos = Nodes[(int)selectedNodeIndex];
|
||||
|
||||
|
||||
Nodes[(int)selectedNodeIndex] = RoundNode(Nodes[(int)selectedNodeIndex], Hull.FindHull(Nodes[(int)selectedNodeIndex]));
|
||||
MapEntity.SelectEntity(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (selectedNodeIndex != null) MapEntity.SelectEntity(item); ;
|
||||
selectedNodeIndex = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void DrawSection(SpriteBatch spriteBatch, Vector2 start, Vector2 end, int i, Color color)
|
||||
{
|
||||
start.Y = -start.Y;
|
||||
end.Y = -end.Y;
|
||||
|
||||
spriteBatch.Draw(wireSprite.Texture,
|
||||
start, null, color,
|
||||
MathUtils.VectorToAngle(end - start),
|
||||
new Vector2(0.0f, wireSprite.size.Y / 2.0f),
|
||||
new Vector2((Vector2.Distance(start, end)) / wireSprite.Texture.Width, 0.3f),
|
||||
SpriteEffects.None,
|
||||
wireSprite.Depth + 0.1f + i * 0.00001f);
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
XElement componentElement = base.Save(parentElement);
|
||||
|
||||
if (Nodes == null || Nodes.Count == 0) return componentElement;
|
||||
|
||||
string[] nodeCoords = new string[Nodes.Count() * 2];
|
||||
for (int i = 0; i < Nodes.Count(); i++)
|
||||
{
|
||||
nodeCoords[i * 2] = Nodes[i].X.ToString(CultureInfo.InvariantCulture);
|
||||
nodeCoords[i * 2 + 1] = Nodes[i].Y.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
componentElement.Add(new XAttribute("nodes", string.Join(";", nodeCoords)));
|
||||
|
||||
return componentElement;
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement)
|
||||
{
|
||||
base.Load(componentElement);
|
||||
|
||||
string nodeString = ToolBox.GetAttributeString(componentElement, "nodes", "");
|
||||
if (nodeString == "") return;
|
||||
|
||||
string[] nodeCoords = nodeString.Split(';');
|
||||
for (int i = 0; i < nodeCoords.Length / 2; i++)
|
||||
{
|
||||
float x = 0.0f, y = 0.0f;
|
||||
|
||||
try
|
||||
{
|
||||
x = float.Parse(nodeCoords[i * 2], CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch { x = 0.0f; }
|
||||
|
||||
try
|
||||
{
|
||||
y = float.Parse(nodeCoords[i * 2 + 1], CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch { y = 0.0f; }
|
||||
|
||||
Nodes.Add(new Vector2(x, y));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void FillNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetOutgoingMessage message)
|
||||
{
|
||||
message.Write(Nodes.Count);
|
||||
for (int i = 0; i < Nodes.Count; i++)
|
||||
{
|
||||
message.Write(Nodes[i].X);
|
||||
message.Write(Nodes[i].Y);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReadNetworkData(Networking.NetworkEventType type, Lidgren.Network.NetIncomingMessage message)
|
||||
{
|
||||
Nodes.Clear();
|
||||
int nodeCount = message.ReadInt32();
|
||||
for (int i = 0; i < nodeCount; i++)
|
||||
{
|
||||
Nodes.Add(new Vector2(message.ReadFloat(), message.ReadFloat()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
212
Subsurface/Source/Items/Components/Turret.cs
Normal file
212
Subsurface/Source/Items/Components/Turret.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using FarseerPhysics;
|
||||
|
||||
namespace Subsurface.Items.Components
|
||||
{
|
||||
class Turret : Powered
|
||||
{
|
||||
Sprite barrelSprite;
|
||||
|
||||
Vector2 barrelPos;
|
||||
|
||||
float rotation, targetRotation;
|
||||
|
||||
float reload, reloadTime;
|
||||
|
||||
float minRotation, 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", true), Editable]
|
||||
public string RotationLimits
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector2 limits = new Vector2(minRotation, maxRotation);
|
||||
limits.X = MathHelper.ToDegrees(limits.X);
|
||||
limits.Y = MathHelper.ToDegrees(limits.Y);
|
||||
|
||||
return ToolBox.Vector2ToString(limits);
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch, bool editing)
|
||||
{
|
||||
barrelSprite.Draw(spriteBatch, new Vector2(item.Rect.X, -item.Rect.Y) + barrelPos, rotation + MathHelper.PiOver2, 1.0f);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
this.cam = cam;
|
||||
|
||||
if (reload>0.0f) reload -= deltaTime;
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
if (targetRotation < minRotation || targetRotation > maxRotation)
|
||||
{
|
||||
float diff = MathUtils.WrapAngleTwoPi(targetRotation - (minRotation + maxRotation) / 2.0f);
|
||||
targetRotation = (diff > Math.PI) ? minRotation : maxRotation;
|
||||
}
|
||||
|
||||
rotation = MathUtils.CurveAngle(rotation, targetRotation, 0.05f);
|
||||
|
||||
|
||||
}
|
||||
|
||||
//public override void SecondaryUse(float deltaTime, Character character = null)
|
||||
//{
|
||||
// if (character == null) return;
|
||||
|
||||
// Vector2 centerPos = new Vector2(item.Rect.X + barrelPos.X, item.Rect.Y - barrelPos.Y);
|
||||
|
||||
// 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(float deltaTime, Character character = null)
|
||||
{
|
||||
if (reload > 0.0f) return false;
|
||||
|
||||
Projectile projectileComponent = null;
|
||||
|
||||
//currPowerConsumption = powerConsumption;
|
||||
|
||||
float availablePower = 0.0f;
|
||||
//List<PowerContainer> batteries = new List<PowerContainer>();
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
foreach (Connection c2 in c.Recipients)
|
||||
{
|
||||
if (c2 == null || c2.Item == null) continue;
|
||||
|
||||
PowerContainer batteryComponent = c2.Item.GetComponent<PowerContainer>();
|
||||
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<ItemContainer>();
|
||||
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<Projectile>()) != 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(ConvertUnits.ToSimUnits(new Vector2(item.Rect.X + barrelPos.X, item.Rect.Y - barrelPos.Y)), -rotation);
|
||||
|
||||
//if (useSounds.Count() > 0) useSounds[Game1.localRandom.Next(useSounds.Count())].Play(1.0f, 800.0f, item.body.FarseerBody);
|
||||
|
||||
projectileComponent.Use(deltaTime);
|
||||
item.RemoveContained(projectile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(string signal, Connection connection, Item sender, float power)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "position_in":
|
||||
Vector2 receivedPos = ToolBox.ParseToVector2(signal, false);
|
||||
|
||||
Vector2 centerPos = new Vector2(item.Rect.X + barrelPos.X, item.Rect.Y - barrelPos.Y);
|
||||
|
||||
Vector2 offset = receivedPos - centerPos;
|
||||
offset.Y = -offset.Y;
|
||||
|
||||
targetRotation = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(offset));
|
||||
|
||||
isActive = true;
|
||||
|
||||
break;
|
||||
case "trigger_in":
|
||||
item.Use((float)Physics.step, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user