First commit

This commit is contained in:
Regalis
2015-05-25 01:04:03 +03:00
commit fadb89ae9e
320 changed files with 32186 additions and 0 deletions

205
Subsurface/Camera.cs Normal file
View File

@@ -0,0 +1,205 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace Subsurface
{
public class Camera
{
float zoom;
const float DefaultZoom = 1.0f;
const float ZoomSmoothness = 8.0f;
const float MoveSmoothness = 8.0f;
float offsetAmount;
Matrix transform;
Matrix shaderTransform;
Matrix viewMatrix;
private Vector2 position;
float rotation;
//the area of the world inside the camera view
//used by the sprite drawing functions to determine whether
//a sprite should be drawn
Rectangle worldView;
Point resolution;
private Vector2 targetPos;
public float Zoom
{
get { return zoom; }
set
{
//prevZoom = zoom;
zoom = value;
if (zoom < 0.1f) zoom = 0.1f;
//if (prevZoom == zoom) return;
Vector2 center = WorldViewCenter;
float newWidth = resolution.X / zoom;
float newHeight = resolution.Y / zoom;
worldView = new Rectangle(
(int)(center.X - newWidth/2.0f),
(int)(center.Y - newHeight/2.0f),
(int)newWidth,
(int)newHeight);
UpdateTransform();
}
}
public float Rotation
{
get { return rotation; }
set { rotation = value; }
}
public float OffsetAmount
{
get { return offsetAmount; }
set { offsetAmount = value; }
}
public Point Resolution
{
get { return resolution; }
}
public Rectangle WorldView
{
get { return worldView; }
}
public Vector2 WorldViewCenter
{
get
{
return new Vector2(
worldView.X + worldView.Width / 2.0f,
worldView.Y - worldView.Height / 2.0f);
}
}
public Matrix Transform
{
get { return transform; }
}
public Matrix ShaderTransform
{
get { return shaderTransform; }
}
public Camera()
{
zoom = 1.0f;
rotation = 0.0f;
position = Vector2.Zero;
worldView = new Rectangle(0,0,
Game1.GraphicsWidth,
Game1.GraphicsHeight);
resolution = new Point(Game1.GraphicsWidth, Game1.GraphicsHeight);
viewMatrix =
//Matrix.CreateRotationZ(Rotation) *
Matrix.CreateTranslation(new Vector3(Game1.GraphicsWidth / 2.0f, Game1.GraphicsHeight / 2.0f, 0));
}
public Vector2 TargetPos
{
get { return targetPos; }
set { targetPos = value; }
}
// Auxiliary function to move the camera
public void Translate(Vector2 amount)
{
position += amount;
Sound.CameraPos = new Vector3(WorldViewCenter.X, WorldViewCenter.Y, 0.0f);
UpdateTransform();
}
private void UpdateTransform()
{
Vector2 interpolatedPosition = position;//Physics.Interpolate(prevPosition,position);
float interpolatedZoom = zoom;// Physics.Interpolate(prevZoom, zoom);
worldView.X = (int)(interpolatedPosition.X - worldView.Width / 2.0);
worldView.Y = (int)(interpolatedPosition.Y + worldView.Height / 2.0);
transform = Matrix.CreateTranslation(
new Vector3(-interpolatedPosition.X, interpolatedPosition.Y, 0)) *
Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) *
viewMatrix;
shaderTransform = Matrix.CreateTranslation(
new Vector3(
-interpolatedPosition.X - resolution.X / interpolatedZoom / 2.0f,
-interpolatedPosition.Y - resolution.Y / interpolatedZoom / 2.0f, 0)) *
Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) *
viewMatrix;
}
public void MoveCamera(float deltaTime)
{
float moveSpeed = 20.0f/zoom;
Vector2 moveCam = Vector2.Zero;
if (targetPos == Vector2.Zero)
{
if (Keyboard.GetState().IsKeyDown(Keys.Left)) moveCam.X -= moveSpeed;
if (Keyboard.GetState().IsKeyDown(Keys.Right)) moveCam.X += moveSpeed;
if (Keyboard.GetState().IsKeyDown(Keys.Down)) moveCam.Y -= moveSpeed;
if (Keyboard.GetState().IsKeyDown(Keys.Up)) moveCam.Y += moveSpeed;
}
else
{
Vector2 mousePos = new Vector2(PlayerInput.GetMouseState.X, PlayerInput.GetMouseState.Y);
Vector2 offset = mousePos - new Vector2(resolution.X / 2.0f, resolution.Y / 2.0f);
offset.X = offset.X / (resolution.X * 0.4f);
offset.Y = -offset.Y / (resolution.Y * 0.3f);
if (offset.Length() > 1.0f) offset.Normalize();
offset = offset * offsetAmount;
float newZoom = Math.Min(DefaultZoom - Math.Min(offset.Length() / resolution.Y, 1.0f),1.0f);
Zoom += (newZoom - zoom) / ZoomSmoothness;
moveCam = (targetPos + offset - position) / MoveSmoothness;
}
Translate(moveCam*deltaTime*60.0f);
}
public Vector2 Position
{
get { return position; }
}
public Vector2 ScreenToWorld(Vector2 coords)
{
Vector2 worldCoords = Vector2.Transform(coords, Matrix.Invert(transform));
return new Vector2(worldCoords.X, -worldCoords.Y);
}
public Vector2 WorldToScreen(Vector2 coords)
{
Vector2 screenCoords = Vector2.Transform(coords, transform);
return new Vector2(screenCoords.X, screenCoords.Y);
}
}
}

View 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) { }
}
}

View File

@@ -0,0 +1,48 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace Subsurface
{
class AITarget
{
public static List<AITarget> list = new List<AITarget>();
protected float soundRange;
protected float sightRange;
public Entity entity;
public float SoundRange
{
get
{
return soundRange;
}
set { soundRange = value; }
}
public float SightRange
{
get { return sightRange; }
set { sightRange = value; }
}
public Vector2 Position
{
get { return entity.SimPosition; }
}
public AITarget(Entity e)
{
entity = e;
list.Add(this);
}
public void Remove()
{
list.Remove(this);
}
}
}

View File

@@ -0,0 +1,506 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml.Linq;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
namespace Subsurface
{
class EnemyAIController : AIController
{ //the preference to attack a specific type of target (-1.0 - 1.0)
//0.0 = doesn't attack targets of the type
//positive values = attacks targets of this type
//negative values = escapes targets of this type
private float attackRooms;
private float attackHumans;
private float attackWeaker;
private float attackStronger;
private float updateTargetsTimer;
private const float UpdateTargetsInterval = 5.0f;
private float raycastTimer;
private const float RaycastInterval = 1.0f;
private Vector2 prevPosition;
private float distanceAccumulator;
//a timer for attacks such as biting that last for a specific amount of time
//the duration is determined by the attackDuration of the attacking limb
private float attackTimer;
//a "cooldown time" after an attack during which the character doesn't try to attack again
private float attackCoolDown;
private float coolDownTimer;
//a point in a wall which the character is currently targeting
private Vector2 wallAttackPos;
//the entity (a wall) which the character is targeting
private MapEntity targetEntity;
//the limb selected for the current attack
private Limb attackingLimb;
private AITarget selectedTarget;
private AITargetMemory selectedTargetMemory;
private float targetValue;
private Dictionary<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)
{
coolDownTimer -= deltaTime;
//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);
}
return;
}
if (raycastTimer > 0.0)
{
raycastTimer -= deltaTime;
}
else
{
targetEntity = null;
//check if there's a wall between the target and the character
Vector2 rayStart = character.animController.limbs[0].SimPosition;
Vector2 rayEnd = selectedTarget.Position;
Structure closestStructure = Map.CheckVisibility(rayStart, rayEnd);
if (Map.LastPickedFraction == 1.0f || closestStructure == null)
{
wallAttackPos = Vector2.Zero;
}
else
{
Structure wall = closestStructure as Structure;
if (wall==null)
{
wallAttackPos = Map.LastPickedPosition;
}
else
{
int sectionIndex = wall.FindSectionIndex(ConvertUnits.ToDisplayUnits(Map.LastPickedPosition));
float sectionDamage = wall.SectionDamage(sectionIndex);
for (int i = sectionIndex-2; i<=sectionIndex+2; i++)
{
if (wall.SectionHasHole(i))
{
sectionIndex = i;
break;
}
if (wall.SectionDamage(i) > sectionDamage) sectionIndex = i;
}
wallAttackPos = wall.SectionPosition(sectionIndex);
wallAttackPos = ConvertUnits.ToSimUnits(wallAttackPos);
}
targetEntity = closestStructure;
}
raycastTimer = RaycastInterval;
}
steeringManager.SteeringSeek(attackPosition);
//check if any of the limbs is close enough to attack the target
if (attackingLimb == null)
{
foreach (Limb limb in character.animController.limbs)
{
if (limb.attack==null || limb.attack.type == Attack.Type.None) continue;
if (Vector2.Distance(limb.SimPosition, attackPosition) > limb.attack.range) continue;
attackingLimb = limb;
break;
}
return;
}
UpdateLimbAttack(deltaTime, attackingLimb, attackPosition);
}
private void UpdateLimbAttack(float deltaTime, Limb limb, Vector2 attackPosition)
{
//DamageType damageType = DamageType.None;
// float damage = 0.0f;
//bool hasAttacked = false;
IDamageable damageTarget = null;
switch (limb.attack.type)
{
case Attack.Type.PinchCW:
case Attack.Type.PinchCCW:
float dir = (limb.attack.type == Attack.Type.PinchCW) ? 1.0f : -1.0f;
float dist = Vector2.Distance(limb.SimPosition, attackPosition);
if (wallAttackPos != Vector2.Zero && targetEntity != null)
{
damageTarget = targetEntity as IDamageable;
}
else
{
damageTarget = selectedTarget.entity as IDamageable;
}
attackTimer += deltaTime*0.05f;
if (damageTarget == null)
{
attackTimer = limb.attack.duration;
break;
}
if (dist < limb.attack.range * 0.5f)
{
attackTimer += deltaTime;
limb.body.ApplyTorque(limb.Mass * 50.0f * character.animController.Dir * dir);
limb.attack.DoDamage(damageTarget, limb.SimPosition, deltaTime, (limb.soundTimer <= 0.0f));
limb.soundTimer = Limb.SoundInterval;
}
else
{
//limb.body.ApplyTorque(limb.Mass * -20.0f * character.animController.Dir * dir);
}
limb.body.ApplyLinearImpulse(limb.Mass * 10.0f *
Vector2.Normalize(attackPosition - limb.SimPosition));
steeringManager.SteeringSeek(attackPosition + (limb.SimPosition-Position), 5.0f);
break;
default:
attackTimer = limb.attack.duration;
break;
}
if (attackTimer >= limb.attack.duration)
{
attackTimer = 0.0f;
if (Vector2.Distance(limb.SimPosition, attackPosition)<5.0) coolDownTimer = attackCoolDown;
System.Diagnostics.Debug.WriteLine("cooldown: " + coolDownTimer);
}
}
//private float GetAttackDamage(Limb limb, float deltaTime)
//{
// return (limb.attack.duration == 0.0f) ? limb.attack.damage : limb.attack.damage * deltaTime;
//}
//goes through all the AItargets, evaluates how preferable it is to attack the target,
//whether the character can see/hear the target and chooses the most preferable target within
//sight/hearing range
public void UpdateTargets(Character character)
{
if (distanceAccumulator<5.0f && Game1.random.Next(1,3)==1)
{
selectedTarget = null;
character.animController.TargetMovement = -character.animController.TargetMovement;
state = AiState.None;
return;
}
distanceAccumulator = 0.0f;
selectedTarget = null;
selectedTargetMemory = null;
this.targetValue = 0.0f;
UpdateTargetMemories();
foreach (AITarget target in AITarget.list)
{
float valueModifier = 0.0f;
float dist = 0.0f;
Character targetCharacter = target.entity as Character;
//ignore the aitarget if it is the character itself
if (targetCharacter == character) continue;
if (targetCharacter!=null)
{
if (attackHumans == 0.0f || targetCharacter.speciesName != "human") continue;
valueModifier = attackHumans;
}
else if (target.entity!=null && attackRooms!=0.0f)
{
//skip the target if it's the room the character is inside of
if (character.animController.CurrentHull != null && character.animController.CurrentHull == target.entity as Hull) continue;
valueModifier = attackRooms;
}
dist = Vector2.Distance(
character.animController.limbs[0].SimPosition,
target.Position);
dist = ConvertUnits.ToDisplayUnits(dist);
AITargetMemory targetMemory = FindTargetMemory(target);
valueModifier *= targetMemory.Priority;
//dist -= targetMemory.Priority;
if (valueModifier != 0.0f && (dist < target.SightRange * sight || dist < target.SoundRange * hearing))
{
//if the target is a character, check if it is visible
if (targetCharacter != null)
{
Vector2 rayStart = character.animController.limbs[0].SimPosition;
Vector2 rayEnd = target.Position;
Structure closestStructure = Map.CheckVisibility(rayStart, rayEnd);
//System.Diagnostics.Debug.WriteLine("closestfraction: " + closestFraction);
//if not visible, ignore this AItarget
if (closestStructure != null) continue;
}
float newTargetValue = valueModifier/dist;
if (selectedTarget == null || Math.Abs(newTargetValue) > Math.Abs(this.targetValue))
{
selectedTarget = target;
selectedTargetMemory = targetMemory;
targetValue = newTargetValue;
Debug.WriteLine(selectedTarget);
}
}
}
//selectedTarget = bestTarget;
//selectedTargetMemory = targetMemory;
//this.targetValue = bestTargetValue;
}
//find the targetMemory that corresponds to some AItarget or create if there isn't one yet
private AITargetMemory FindTargetMemory(AITarget target)
{
AITargetMemory memory = null;
if (targetMemories.TryGetValue(target, out memory))
{
return memory;
}
memory = new AITargetMemory(100.0f);
targetMemories.Add(target, memory);
return memory;
}
//go through all the targetmemories and delete ones that don't
//have a corresponding AItarget or whose priority is 0.0f
private void UpdateTargetMemories()
{
List<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.ID);
}
public override void ReadNetworkData(NetIncomingMessage message)
{
state = (AiState)(message.ReadByte());
wallAttackPos.X = message.ReadFloat();
wallAttackPos.Y = message.ReadFloat();
float wanderAngle = message.ReadFloat();
float updateTargetsTimer = message.ReadFloat();
float raycastTimer = message.ReadFloat();
float coolDownTimer = message.ReadFloat();
int targetID = message.ReadInt32();
steeringManager.WanderAngle = wanderAngle;
this.updateTargetsTimer = updateTargetsTimer;
this.raycastTimer = raycastTimer;
this.coolDownTimer = coolDownTimer;
if (targetID>-1)
targetEntity = Entity.FindEntityByID(targetID) as MapEntity;
}
}
//the "memory" of the character
//keeps track of how preferable it is to attack a specific target
//(if the character can't inflict much damage the target, the priority decreases
//and if the target attacks the character, the priority increases)
class AITargetMemory
{
//private AITarget target;
private float priority;
//public AITarget Target
//{
// get { return target; }
//}
public float Priority
{
get { return priority; }
set { priority = MathHelper.Clamp(value, 1.0f, 100.0f); }
}
public AITargetMemory(float priority)
{
this.priority = priority;
}
}
}

View File

@@ -0,0 +1,27 @@
using Microsoft.Xna.Framework;
namespace Subsurface
{
interface ISteerable
{
Vector2 Steering
{
get;
set;
}
Vector2 Velocity
{
get;
}
Vector2 Position
{
get;
}
}
}

View File

@@ -0,0 +1,146 @@
using System;
using Microsoft.Xna.Framework;
namespace Subsurface
{
class SteeringManager
{
private const float CircleDistance = 2.5f;
private const float CircleRadius = 0.3f;
private const float RayCastInterval = 0.5f;
private ISteerable host;
private Vector2 steering;
//the steering amount when avoiding obstacles
//(needs a separate variable because it's only updated when a raycast is done to detect any nearby obstacles)
private Vector2 avoidSteering;
private float rayCastTimer;
private float wanderAngle;
public float WanderAngle
{
get { return wanderAngle; }
set { wanderAngle = value; }
}
public SteeringManager(ISteerable host)
{
this.host = host;
wanderAngle = ToolBox.RandomFloat(0.0f, MathHelper.TwoPi);
}
public void SteeringSeek(Vector2 target, float speed = 1.0f)
{
steering += DoSteeringSeek(target, speed);
}
public void SteeringWander(float speed = 1.0f)
{
steering += DoSteeringWander(speed);
}
public void SteeringAvoid(float deltaTime, float speed)
{
steering += DoSteeringAvoid(deltaTime, speed);
}
public void Update(float speed = 1.0f)
{
float steeringSpeed = steering.Length();
if (steeringSpeed>speed)
{
steering = Vector2.Normalize(steering) * Math.Abs(speed);
}
host.Steering = steering;
}
private Vector2 DoSteeringSeek(Vector2 target, float speed = 1.0f)
{
Vector2 targetVel = target - host.Position;
targetVel = Vector2.Normalize(targetVel) * speed;
Vector2 newSteering = targetVel - host.Steering;
if (newSteering==Vector2.Zero) return Vector2.Zero;
float steeringSpeed = (newSteering + host.Steering).Length();
if (steeringSpeed > Math.Abs(speed))
{
newSteering = Vector2.Normalize(newSteering)*Math.Abs(speed);
}
return newSteering;
}
private Vector2 DoSteeringWander(float speed = 1.0f)
{
Vector2 circleCenter = (host.Velocity == Vector2.Zero) ? new Vector2(speed, 0.0f) : host.Velocity;
circleCenter = Vector2.Normalize(circleCenter) * CircleDistance;
Vector2 displacement = new Vector2(
(float)Math.Cos(wanderAngle),
(float)Math.Sin(wanderAngle));
displacement = displacement * CircleRadius;
float angleChange = 1.5f;
wanderAngle += ToolBox.RandomFloat(0.0f, 1.0f) * angleChange - angleChange * 0.5f;
Vector2 newSteering = circleCenter + displacement;
float steeringSpeed = (newSteering + host.Steering).Length();
if (steeringSpeed > speed)
{
newSteering = Vector2.Normalize(newSteering) * speed;
}
return newSteering;
}
private Vector2 DoSteeringAvoid(float deltaTime, float speed = 1.0f)
{
if (steering == Vector2.Zero || host.Steering == Vector2.Zero) return Vector2.Zero;
float MaxDistance = 2.0f;
Vector2 ahead = host.Position + Vector2.Normalize(host.Steering)*MaxDistance;
if (rayCastTimer <= 0.0f)
{
rayCastTimer = RayCastInterval;
Structure closestStructure = Map.CheckVisibility(host.Position, ahead);
if (closestStructure == null)
{
avoidSteering = Vector2.Zero;
return Vector2.Zero;
}
else
{
Vector2 obstaclePosition = Map.LastPickedPosition;
if (closestStructure.IsHorizontal)
{
obstaclePosition.Y = closestStructure.SimPosition.Y;
}
else
{
obstaclePosition.X = closestStructure.SimPosition.X;
}
avoidSteering = Vector2.Normalize(Map.LastPickedPosition - obstaclePosition);
}
}
else
{
rayCastTimer -= deltaTime;
}
return avoidSteering * speed;
}
}
}

View File

@@ -0,0 +1,43 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
namespace Subsurface
{
class SteeringPath
{
private Queue<Vector2> nodes;
const float minDistance = 0.1f;
Vector2 currentNode;
public SteeringPath()
{
nodes = new Queue<Vector2>();
}
public void AddNode(Vector2 node)
{
if (node == Vector2.Zero) return;
nodes.Enqueue(node);
}
public Vector2 CurrentNode
{
get { return currentNode; }
}
public Vector2 GetNode(Vector2 pos)
{
if (nodes.Count == 0) return Vector2.Zero;
if (currentNode==null || currentNode==Vector2.Zero || Vector2.Distance(pos, currentNode)<minDistance) currentNode = nodes.Dequeue();
return currentNode;
}
public void ClearPath()
{
nodes.Clear();
}
}
}

View File

@@ -0,0 +1,58 @@
using System.Xml.Linq;
using FarseerPhysics;
using Microsoft.Xna.Framework;
namespace Subsurface
{
class AnimController : Ragdoll
{
protected Character character;
public bool isStanding;
public enum Animation { None, Climbing, UsingConstruction, Struggle };
public Animation anim;
protected float walkSpeed, swimSpeed;
public Direction targetDir;
//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) { }
}
}

View File

@@ -0,0 +1,963 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics.Joints;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Subsurface.Networking;
using Subsurface.Particles;
namespace Subsurface
{
class Character : Entity, IDamageable
{
public static List<Character> characterList = new List<Character>();
public static Queue<CharacterInfo> newCharacterQueue = new Queue<CharacterInfo>();
public static bool disableControls;
//the character that the player is currently controlling
private static Character controlled;
public static Character Controlled
{
get { return controlled; }
set { controlled = value; }
}
public readonly bool IsNetworkPlayer;
private Inventory inventory;
public double lastNetworkUpdate;
public byte largeUpdateTimer;
public readonly Dictionary<string, ObjectProperty> properties;
protected Key selectKeyHit;
protected Key actionKeyHit;
protected Key actionKeyDown;
protected Key secondaryKeyHit;
protected Key secondaryKeyDown;
private Item selectedConstruction;
private Item[] selectedItems;
public AnimController animController;
private AIController aiController;
private Vector2 cursorPosition;
protected bool needsAir;
protected float oxygen;
protected float drowningTime;
protected Item closestItem;
protected bool isDead;
bool isHumanoid;
//the name of the species (e.q. human)
public readonly string speciesName;
public CharacterInfo info;
protected float soundTimer;
protected float soundInterval;
private float blood;
private Sound[] sounds;
//which AIstate each sound is for
private AIController.AiState[] soundStates;
public Inventory Inventory
{
get { return inventory; }
}
public Vector2 CursorPosition
{
get { return cursorPosition; }
}
public float SoundRange
{
get { return aiTarget.SoundRange; }
}
public float SightRange
{
get { return aiTarget.SightRange; }
}
private float pressureProtection;
public float PressureProtection
{
get { return pressureProtection; }
set
{
//if (!value && pressureProtection) Debug.WriteLine("pp set: " + value);
pressureProtection = MathHelper.Clamp(value, 0.0f, 100.0f);
}
}
public float Oxygen
{
get { return oxygen; }
set
{
oxygen = MathHelper.Clamp(value, 0.0f, 100.0f);
if (oxygen == 0.0f) Kill();
}
}
public float Blood
{
get { return blood; }
set
{
blood = MathHelper.Clamp(value, 0.0f, 100.0f);
if (blood == 0.0f) Kill();
}
}
public Item[] SelectedItems
{
get { return selectedItems; }
}
public bool HasSelectedItem(Item item)
{
return selectedItems.Contains(item);
}
public bool TrySelectItem(Item item)
{
bool rightHand = ((CharacterInventory)inventory).IsInLimbSlot(item, LimbSlot.RightHand);
bool leftHand = ((CharacterInventory)inventory).IsInLimbSlot(item, LimbSlot.LeftHand);
bool selected = false;
if (rightHand && SelectedItems[0] == null)
{
selectedItems[0] = item;
selected = true;
}
if (leftHand && SelectedItems[1] == null)
{
selectedItems[1] = item;
selected = true;
}
return selected;
}
public bool TrySelectItem(Item item, int index)
{
if (selectedItems[index] != null) return false;
selectedItems[index] = item;
return true;
}
public void DeselectItem(Item item)
{
for (int i = 0; i < selectedItems.Length; i++)
{
if (selectedItems[i] == item) selectedItems[i] = null;
}
}
public Item SelectedConstruction
{
get { return selectedConstruction; }
set { selectedConstruction = value; }
}
public Item ClosestItem
{
get { return closestItem; }
}
public Key SelectKeyHit
{
get { return selectKeyHit; }
}
public Key ActionKeyHit
{
get { return actionKeyHit; }
}
public Key ActionKeyDown
{
get { return actionKeyDown; }
}
public Key SecondaryKeyHit
{
get { return secondaryKeyHit; }
}
public Key SecondaryKeyDown
{
get { return secondaryKeyDown; }
}
public bool IsDead
{
get { return isDead; }
}
public override Vector2 SimPosition
{
get { return animController.limbs[0].SimPosition; }
}
public Vector2 Position
{
get { return ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition); }
}
public Character(string file) : this(file, Vector2.Zero, null)
{
}
public Character(string file, Vector2 position)
: this(file, position, null)
{
}
public Character(CharacterInfo characterInfo, Vector2 position, bool isNetworkPlayer = false)
: this(characterInfo.file, position, characterInfo, isNetworkPlayer)
{
}
public Character(string file, Vector2 position, CharacterInfo characterInfo = null, bool isNetworkPlayer = false)
{
selectKeyHit = new Key(false);
actionKeyDown = new Key(true);
actionKeyHit = new Key(false);
secondaryKeyHit = new Key(false);
secondaryKeyDown = new Key(true);
selectedItems = new Item[2];
IsNetworkPlayer = isNetworkPlayer;
oxygen = 100.0f;
blood = 100.0f;
aiTarget = new AITarget(this);
properties = ObjectProperty.GetProperties(this);
XDocument doc = ToolBox.TryLoadXml(file);
if (doc == null) return;
speciesName = ToolBox.GetAttributeString(doc.Root, "name", "Unknown");
isHumanoid = ToolBox.GetAttributeBool(doc.Root, "humanoid", false);
info = characterInfo ?? new CharacterInfo(file);
if (isHumanoid)
{
animController = new HumanoidAnimController(this, doc.Root.Element("ragdoll"));
animController.targetDir = Direction.Right;
inventory = new CharacterInventory(10, this);
}
else
{
animController = new FishAnimController(this, doc.Root.Element("ragdoll"));
PressureProtection = 100.0f;
//FishAnimController fishAnim = (FishAnimController)animController;
aiController = new EnemyAIController(this, file);
}
foreach (Limb limb in animController.limbs)
{
limb.body.SetTransform(position+limb.SimPosition, 0.0f);
//limb.prevPosition = ConvertUnits.ToDisplayUnits(position);
}
needsAir = ToolBox.GetAttributeBool(doc.Root, "needsair", false);
drowningTime = ToolBox.GetAttributeFloat(doc.Root, "drowningtime", 10.0f);
soundInterval = ToolBox.GetAttributeFloat(doc.Root, "soundinterval", 10.0f);
var xSounds = doc.Root.Elements("sound");
if (xSounds.Count() > 0)
{
sounds = new Sound[xSounds.Count()];
soundStates = new AIController.AiState[xSounds.Count()];
int i = 0;
foreach (XElement xSound in xSounds)
{
sounds[i] = Sound.Load(xSound.Attribute("file").Value);
if (xSound.Attribute("state") == null)
{
soundStates[i] = AIController.AiState.None;
}
else
{
soundStates[i] = (AIController.AiState)Enum.Parse(
typeof(AIController.AiState), xSound.Attribute("state").Value, true);
}
i++;
}
}
animController.FindHull();
if (this.info.ID >= 0)
{
ID = this.info.ID;
}
characterList.Add(this);
}
/// <summary>
/// Control the characte
/// </summary>
public void Control(Camera cam, bool forcePick=false)
{
if (isDead) return;
//find the closest item if selectkey has been hit, or if the character is being
//controlled by the player (in order to highlight it)
//closestItem = null;
if (controlled==this)
{
Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition));
closestItem = FindClosestItem(mouseSimPos);
if (closestItem != null)
{
closestItem.IsHighlighted = true;
if (selectKeyHit.State && closestItem.Pick(this, forcePick))
{
new NetworkEvent(NetworkEventType.PickItem, ID, true, closestItem.ID);
}
}
}
for (int i = 0; i < selectedItems.Length; i++ )
{
if (selectedItems[i] == null) continue;
if (i == 1 && selectedItems[0] == selectedItems[1]) continue;
if (actionKeyDown.State) selectedItems[i].Use(this);
if (secondaryKeyDown.State && selectedItems[i] != null) selectedItems[i].SecondaryUse(this);
}
if (selectedConstruction != null)
{
if (actionKeyDown.State) selectedConstruction.Use(this);
if (secondaryKeyDown.State) selectedConstruction.SecondaryUse(this);
}
if (IsNetworkPlayer)
{
selectKeyHit.Reset();
actionKeyHit.Reset();
actionKeyDown.Reset();
secondaryKeyHit.Reset();
secondaryKeyDown.Reset();
}
}
private Item FindClosestItem(Vector2 mouseSimPos)
{
Limb torso = animController.GetLimb(LimbType.Torso);
Vector2 pos = (torso.body.TargetPosition != Vector2.Zero) ? torso.body.TargetPosition : torso.SimPosition;
return Item.FindPickable(pos, selectedConstruction == null ? mouseSimPos : selectedConstruction.SimPosition, null, selectedItems);
}
/// <summary>
/// Control the character according to player input
/// </summary>
public void ControlLocalPlayer(Camera cam, bool moveCam = true)
{
if (isDead) return;
Limb head = animController.GetLimb(LimbType.Head);
Lights.LightManager.viewPos = ConvertUnits.ToDisplayUnits(head.SimPosition);
Vector2 targetMovement = Vector2.Zero;
if (!disableControls)
{
if (PlayerInput.KeyDown(Keys.W)) targetMovement.Y += 1.0f;
if (PlayerInput.KeyDown(Keys.S)) targetMovement.Y -= 1.0f;
if (PlayerInput.KeyDown(Keys.A)) targetMovement.X -= 1.0f;
if (PlayerInput.KeyDown(Keys.D)) targetMovement.X += 1.0f;
//the vertical component is only used for falling through platforms and climbing ladders when not in water,
//so the movement can't be normalized or the character would walk slower when pressing down/up
if (animController.InWater)
{
float length = targetMovement.Length();
if (length > 0.0f) targetMovement = targetMovement / length;
}
if (Keyboard.GetState().IsKeyDown(Keys.LeftShift) && Math.Sign(targetMovement.X) == Math.Sign(animController.Dir))
targetMovement *= 3.0f;
selectKeyHit.SetState(PlayerInput.KeyHit(Keys.E));
actionKeyHit.SetState(PlayerInput.LeftButtonClicked());
actionKeyDown.SetState(PlayerInput.GetMouseState.LeftButton == ButtonState.Pressed);
secondaryKeyHit.SetState(PlayerInput.RightButtonClicked());
secondaryKeyDown.SetState(PlayerInput.GetMouseState.RightButton == ButtonState.Pressed);
}
else
{
selectKeyHit.SetState(false);
actionKeyHit.SetState(false);
actionKeyDown.SetState(false);
secondaryKeyHit.SetState(false);
secondaryKeyDown.SetState(false);
}
animController.TargetMovement = targetMovement;
animController.isStanding = true;
if (moveCam)
{
cam.TargetPos = ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition);
cam.OffsetAmount = 250.0f;
}
cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition);
Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);
if (animController.onGround &&
!animController.InWater &&
animController.anim != AnimController.Animation.UsingConstruction)
{
if (mouseSimPos.X < head.SimPosition.X-1.0f)
{
animController.targetDir = Direction.Left;
}
else if (mouseSimPos.X > head.SimPosition.X + 1.0f)
{
animController.targetDir = Direction.Right;
}
}
disableControls = false;
}
public static void UpdateAnimAll(float deltaTime)
{
foreach (Character c in characterList)
{
if (c.isDead) continue;
c.animController.UpdateAnim(deltaTime);
}
}
public static void UpdateAll(Camera cam, float deltaTime)
{
if (newCharacterQueue.Count>0)
{
new Character(newCharacterQueue.Dequeue(), Vector2.Zero);
}
foreach (Character c in characterList)
{
c.Update(cam, deltaTime);
}
}
public void Update(Camera cam, float deltaTime)
{
if (isDead) return;
Debug.WriteLine("pp: " + PressureProtection);
if (PressureProtection==0.0f &&
(animController.CurrentHull == null || animController.CurrentHull.LethalPressure >= 100.0f))
{
Implode();
return;
}
if (controlled == this) ControlLocalPlayer(cam);
Control(cam);
UpdateSightRange();
aiTarget.SoundRange = 0.0f;
if (needsAir)
{
if (animController.HeadInWater)
{
Oxygen -= deltaTime*100.0f / drowningTime;
}
else if (animController.CurrentHull != null)
{
float hullOxygen = animController.CurrentHull.OxygenPercentage;
hullOxygen -= 30.0f;
Oxygen += deltaTime * 100.0f * (hullOxygen / 500.0f);
animController.CurrentHull.Oxygen -= Hull.OxygenConsumptionSpeed * deltaTime;
}
PressureProtection -= deltaTime*100.0f;
}
if (soundTimer > 0)
{
soundTimer -= deltaTime;
}
else
{
PlaySound((aiController == null) ? AIController.AiState.None : aiController.State);
soundTimer = soundInterval;
}
foreach (Limb limb in animController.limbs)
{
Blood = blood - limb.Bleeding * deltaTime * 0.1f;
}
if (aiController != null) aiController.Update(deltaTime);
}
private void UpdateSightRange()
{
aiTarget.SightRange = 0.0f;
//distance is approximated based on the mass of the character
//(which corresponds to size because all the characters have the same limb density)
foreach (Limb limb in animController.limbs)
{
aiTarget.SightRange += limb.Mass * 1000.0f;
}
//the faster the character is moving, the easier it is to see it
Limb torso = animController.GetLimb(LimbType.Torso);
if (torso !=null)
{
aiTarget.SightRange += torso.LinearVelocity.Length() * 500.0f;
}
}
public void Draw(SpriteBatch spriteBatch)
{
animController.Draw(spriteBatch);
if (IsNetworkPlayer)
{
Vector2 pos = new Vector2(Position.X, -Position.Y - 50.0f) - GUI.font.MeasureString(info.name) * 0.5f;
spriteBatch.DrawString(GUI.font, info.name, pos - new Vector2(1.0f, 1.0f), Color.Black);
spriteBatch.DrawString(GUI.font, info.name, pos, Color.White);
}
//spriteBatch.DrawString(GUI.font, ID.ToString(), ConvertUnits.ToDisplayUnits(animController.limbs[0].Position), Color.White);
//GUI.DrawLine(spriteBatch, ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition.X, animController.limbs[0].SimPosition.Y),
// ConvertUnits.ToDisplayUnits(animController.limbs[0].SimPosition.X, animController.limbs[0].SimPosition.Y) +
// ConvertUnits.ToDisplayUnits(animController.targetMovement.X, animController.targetMovement.Y), Color.Green);
}
public void PlaySound(AIController.AiState state)
{
if (sounds == null || sounds.Count()==0) return;
var matchingSoundStates = soundStates.Where(x => x == state).ToList();
int selectedSound = Game1.localRandom.Next(matchingSoundStates.Count());
int n = 0;
for (int i = 0; i < sounds.Count(); i++)
{
if (soundStates[i] != state) continue;
if (n == selectedSound)
{
sounds[i].Play(1.0f, 2000.0f,
animController.limbs[0].body.FarseerBody);
Debug.WriteLine("playing: " + sounds[i]);
return;
}
n++;
}
}
public void AddDamage(Vector2 position, float amount, float bleedingAmount, float stun)
{
int bloodAmount = 0;
animController.StunTimer = Math.Max(animController.StunTimer, stun);
Limb closestLimb = null;
float closestDistance = 0.0f;
foreach (Limb limb in animController.limbs)
{
float distance = Vector2.Distance(position, limb.SimPosition);
if (closestLimb == null || distance < closestDistance)
{
closestLimb = limb;
closestDistance = distance;
}
}
Vector2 pull = position - closestLimb.SimPosition;
if (pull != Vector2.Zero) pull = Vector2.Normalize(pull);
closestLimb.body.ApplyForce(pull*Math.Min(amount*100.0f, 100.0f));
closestLimb.Bleeding += bleedingAmount;
closestLimb.Damage += amount;
bloodAmount = (int)Math.Min((int)(amount*2.0f),20);
//if (closestLimb.Damage>=100.0f)
//{
// bloodAmount *= 2;
// foreach (var joint in animController.limbJoints)
// {
// if (!(joint.BodyA == closestLimb.body.FarseerBody) && !(joint.BodyB == closestLimb.body.FarseerBody)) continue;
// joint.Enabled = false;
// break;
// }
//}
for (int i = 0; i < bloodAmount; i++)
{
Vector2 particleVel = closestLimb.SimPosition-position;
if (particleVel != Vector2.Zero) particleVel = Vector2.Normalize(particleVel);
Game1.particleManager.CreateParticle(
closestLimb.SimPosition,
ToolBox.RandomFloat(0.0f, 3.1f),
particleVel * ToolBox.RandomFloat(1.0f,3.0f),
"blood");
}
for (int i = 0; i < bloodAmount / 2; i++)
{
Game1.particleManager.CreateParticle(closestLimb.SimPosition,
0.0f,
Vector2.Zero, "waterblood");
}
}
public void Stun()
{
for (int i = 0; i < selectedItems.Length; i++ )
{
if (selectedItems[i] == null) continue;
selectedItems[i].Drop();
selectedItems[i] = null;
}
selectedConstruction = null;
}
private void Implode()
{
Limb torso= animController.GetLimb(LimbType.Torso);
if (torso == null) torso = animController.GetLimb(LimbType.Head);
Vector2 centerOfMass = Vector2.Zero;
float totalMass = 0.0f;
foreach (Limb limb in animController.limbs)
{
centerOfMass += limb.Mass * limb.SimPosition;
totalMass += limb.Mass;
}
centerOfMass /= totalMass;
foreach (Limb limb in animController.limbs)
{
Vector2 diff = centerOfMass - limb.SimPosition;
if (diff == Vector2.Zero) continue;
limb.body.ApplyLinearImpulse(diff * 10.0f);
limb.Damage = 100.0f;
}
AmbientSoundManager.PlayDamageSound(DamageSoundType.Implode, 50.0f, torso.body.FarseerBody);
for (int i = 0; i < 10; i++)
{
Particle p = Game1.particleManager.CreateParticle(
torso.SimPosition + new Vector2(ToolBox.RandomFloat(-0.5f, 0.5f), ToolBox.RandomFloat(-0.5f, 0.5f)),
0.0f,
Vector2.Zero, "waterblood");
if (p!=null) p.Size *= 2.0f;
Game1.particleManager.CreateParticle(
torso.SimPosition,
0.0f,
new Vector2(ToolBox.RandomFloat(-0.5f, 0.5f), ToolBox.RandomFloat(-1.0f,0.5f)),
"bubbles");
}
foreach (var joint in animController.limbJoints)
{
joint.LimitEnabled = false;
}
Kill(true);
}
public void Kill(bool networkMessage = false)
{
if (isDead) return;
//if the game is run by a client, characters are only killed when the server says so
if (Game1.client != null)
{
if (networkMessage)
{
new NetworkEvent(NetworkEventType.KillCharacter, ID, true);
}
else
{
return;
}
}
if (Game1.server != null)
{
new NetworkEvent(NetworkEventType.KillCharacter, ID, false);
}
if (Game1.gameSession.crewManager!=null)
{
Game1.gameSession.crewManager.KillCharacter(this);
}
isDead = true;
animController.movement = Vector2.Zero;
animController.TargetMovement = Vector2.Zero;
for (int i = 0; i < selectedItems.Length; i++ )
{
if (selectedItems[i] != null) selectedItems[i].Drop(this);
}
aiTarget.Remove();
aiTarget = null;
foreach (Limb limb in animController.limbs)
{
if (limb.pullJoint == null) continue;
limb.pullJoint.Enabled = false;
}
foreach (RevoluteJoint joint in animController.limbJoints)
{
joint.MotorEnabled = false;
joint.MaxMotorTorque = 0.0f;
}
}
public override void FillNetworkData(NetworkEventType type, NetOutgoingMessage message, object data)
{
if (type == NetworkEventType.PickItem)
{
message.Write((int)data);
Debug.WriteLine("pickitem");
return;
}
else if (type == NetworkEventType.KillCharacter)
{
return;
}
//if (type == Networking.NetworkEventType.KeyHit)
//{
// message.Write(selectKeyHit.Dequeue);
message.Write(actionKeyDown.Dequeue);
message.Write(secondaryKeyDown.Dequeue);
//}
message.Write(NetTime.Now);
// Write byte = move direction
message.Write(animController.TargetMovement.X);
message.Write(animController.TargetMovement.Y);
message.Write(animController.targetDir==Direction.Right);
message.Write(cursorPosition.X);
message.Write(cursorPosition.Y);
message.Write(largeUpdateTimer <= 0);
if (largeUpdateTimer<=0)
{
foreach (Limb limb in animController.limbs)
{
message.Write(limb.body.Position.X);
message.Write(limb.body.Position.Y);
message.Write(limb.body.LinearVelocity.X);
message.Write(limb.body.LinearVelocity.Y);
message.Write(limb.body.Rotation);
message.Write(limb.body.AngularVelocity);
}
message.Write(animController.StunTimer);
largeUpdateTimer = 5;
}
else
{
Limb torso = animController.GetLimb(LimbType.Torso);
message.Write(torso.body.Position.X);
message.Write(torso.body.Position.Y);
largeUpdateTimer = (byte)Math.Max(0, largeUpdateTimer-1);
}
if (aiController != null) aiController.FillNetworkData(message);
}
public override void ReadNetworkData(NetworkEventType type, NetIncomingMessage message)
{
if (type == NetworkEventType.PickItem)
{
int itemId = message.ReadInt32();
Item item = FindEntityByID(itemId) as Item;
if (item != null)
{
Debug.WriteLine("pickitem "+itemId );
item.Pick(this);
}
else
{
}
//DebugConsole.ThrowError("pickitem");
return;
}
else if (type == NetworkEventType.KillCharacter)
{
Kill(true);
if (Game1.client != null && controlled == this)
{
Game1.client.AddChatMessage("YOU HAVE DIED. Your chat messages will only be visible to other dead players.", ChatMessageType.Dead);
}
return;
}
//if (type == Networking.NetworkEventType.KeyHit)
//{
// selectKeyHit.State = message.ReadBoolean();
//}
actionKeyDown.State = message.ReadBoolean();
secondaryKeyDown.State = message.ReadBoolean();
double sendingTime = message.ReadDouble();
Vector2 targetMovement = Vector2.Zero;
targetMovement.X = message.ReadFloat();
targetMovement.Y = message.ReadFloat();
animController.isStanding = true;
bool targetDir = message.ReadBoolean();
Vector2 cursorPos = Vector2.Zero;
cursorPos.X = message.ReadFloat();
cursorPos.Y = message.ReadFloat();
if (sendingTime > lastNetworkUpdate)
{
cursorPosition = cursorPos;
animController.TargetMovement= targetMovement;
animController.targetDir = (targetDir) ? Direction.Right : Direction.Left;
if (message.ReadBoolean())
{
foreach (Limb limb in animController.limbs)
{
Vector2 pos = Vector2.Zero;
pos.X = message.ReadFloat();
pos.Y = message.ReadFloat();
Vector2 vel = Vector2.Zero;
vel.X = message.ReadFloat();
vel.Y = message.ReadFloat();
float rotation = message.ReadFloat();
float angularVel = message.ReadFloat();
if (limb.body == null) continue;
if (vel != Vector2.Zero && vel.Length() > 100.0f) { }
if (pos != Vector2.Zero && pos.Length() > 100.0f) { }
limb.body.TargetVelocity = vel;
limb.body.TargetPosition = pos;// +vel * (float)(deltaTime / 60.0);
limb.body.TargetRotation = rotation;// +angularVel * (float)(deltaTime / 60.0);
limb.body.TargetAngularVelocity = angularVel;
}
animController.StunTimer = message.ReadFloat();
largeUpdateTimer = 1;
}
else
{
Vector2 pos = Vector2.Zero;
pos.X = message.ReadFloat();
pos.Y = message.ReadFloat();
Limb torso = animController.GetLimb(LimbType.Torso);
torso.body.TargetPosition = pos;
largeUpdateTimer = 0;
}
if (aiController != null) aiController.ReadNetworkData(message);
lastNetworkUpdate = sendingTime;
}
}
public override void Remove()
{
base.Remove();
characterList.Remove(this);
if (controlled == this) controlled = null;
if (Game1.client!=null && Game1.client.Character == this) Game1.client.Character = null;
if (aiTarget != null)
aiTarget.Remove();
if (animController!=null)
animController.Remove();
}
}
}

View File

@@ -0,0 +1,76 @@
using System.Xml.Linq;
namespace Subsurface
{
public enum Gender { None, Male, Female };
class CharacterInfo
{
//the name of the character (e.q. Urist McEngineer)
public string name;
public readonly string file;
public int ID;
public Gender gender;
public int salary;
public string GenderString()
{
return gender.ToString();
}
public CharacterInfo(string file, string name = "", Gender gender = Gender.None)
{
this.file = file;
ID = -1;
XDocument doc = ToolBox.TryLoadXml(file);
if (doc == null) return;
salary = 500;
if (ToolBox.GetAttributeBool(doc.Root, "genders", false))
{
if (gender==Gender.None)
{
float femaleRatio = ToolBox.GetAttributeFloat(doc.Root, "femaleratio", 0.5f);
this.gender = (Game1.random.NextDouble() < femaleRatio) ? Gender.Female : Gender.Male;
}
else
{
this.gender = gender;
}
}
if (!string.IsNullOrEmpty(name))
{
this.name = name;
return;
}
if (doc.Root.Element("name") != null)
{
string firstNamePath = (ToolBox.GetAttributeString(doc.Root.Element("name"), "firstname", ""));
if (firstNamePath != "")
{
firstNamePath = firstNamePath.Replace("[GENDER]", (this.gender == Gender.Female) ? "f" : "");
this.name = ToolBox.GetRandomLine(firstNamePath);
}
string lastNamePath = (ToolBox.GetAttributeString(doc.Root.Element("name"), "lastname", ""));
if (lastNamePath != "")
{
lastNamePath = lastNamePath.Replace("[GENDER]", (this.gender == Gender.Female) ? "f" : "");
if (this.name != "") this.name += " ";
this.name += ToolBox.GetRandomLine(lastNamePath);
}
}
}
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Subsurface
{
class DelayedEffect : StatusEffect
{
public static List<DelayedEffect> list = new List<DelayedEffect>();
float delay;
float timer;
private Item item;
private Character character;
private Limb limb;
public float Timer
{
get { return timer; }
}
public DelayedEffect(XElement element)
: base(element)
{
delay = ToolBox.GetAttributeFloat(element, "delay", 1.0f);
}
public override void Apply(ActionType type, float deltaTime, Item item, Character character = null, Limb limb = null)
{
if (this.type != type) return;
this.item = item;
this.character = character;
this.limb = limb;
this.timer = delay;
list.Add(this);
}
public void Update(float deltaTime)
{
timer -= deltaTime;
if (timer > 0.0f) return;
base.Apply(1.0f, character, item, limb);
list.Remove(this);
}
}
}

View File

@@ -0,0 +1,353 @@
using System;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
namespace Subsurface
{
class FishAnimController : AnimController
{
//amplitude and wave length of the "sine wave" swimming animation
//if amplitude = 0, sine wave animation isn't used
private float waveAmplitude;
private float waveLength;
private bool flip;
public FishAnimController(Character character, XElement element)
: base(character, element)
{
waveAmplitude = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "waveamplitude", 0.0f));
waveLength = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "wavelength", 0.0f));
flip = ToolBox.GetAttributeBool(element, "flip", false);
walkSpeed = ToolBox.GetAttributeFloat(element, "walkspeed", 1.0f);
swimSpeed = ToolBox.GetAttributeFloat(element, "swimspeed", 1.0f);
}
public override void UpdateAnim(float deltaTime)
{
ResetPullJoints();
if (strongestImpact > 0.0f)
{
stunTimer = MathHelper.Clamp(strongestImpact * 0.5f, stunTimer, 5.0f);
strongestImpact = 0.0f;
}
if (stunTimer>0.0f)
{
UpdateStruggling();
stunTimer -= deltaTime;
return;
}
else
{
if (inWater)
{
UpdateSineAnim(deltaTime);
}
else
{
UpdateWalkAnim(deltaTime);
}
}
if (flip)
{
//targetDir = (movement.X > 0.0f) ? Direction.Right : Direction.Left;
if (movement.X > 0.1f && movement.X > Math.Abs(movement.Y))
{
targetDir = Direction.Right;
}
else if (movement.X < -0.1f && movement.X < -Math.Abs(movement.Y))
{
targetDir = Direction.Left;
}
}
else
{
Limb head = GetLimb(LimbType.Head);
float rotation = ToolBox.WrapAngleTwoPi(head.Rotation);
rotation = MathHelper.ToDegrees(rotation);
if (rotation < 0.0f) rotation += 360;
if (rotation > 20 && rotation < 160)
{
targetDir = Direction.Left;
}
else if (rotation > 200 && rotation < 340)
{
targetDir = Direction.Right;
}
}
//if (stunTimer > gameTime.TotalGameTime.TotalMilliseconds) return;
if (targetDir != dir)
{
Flip();
if (flip) Mirror();
}
}
void UpdateSineAnim(float deltaTime)
{
movement = ToolBox.SmoothStep(movement, TargetMovement*swimSpeed, 1.0f);
if (movement == Vector2.Zero) return;
if (!inWater) movement.Y = Math.Min(0.0f, movement.Y);
float movementAngle = ToolBox.VectorToAngle(movement)+MathHelper.PiOver2;
Limb tail = GetLimb(LimbType.Tail);
if (tail != null && waveAmplitude>0.0f)
{
walkPos -= movement.Length();
float waveRotation = (float)Math.Sin(walkPos / waveLength)*waveAmplitude;
float angle = ToolBox.GetShortestAngle(tail.body.Rotation, movementAngle + waveRotation);
//limbs[tailIndex].body.ApplyTorque((Math.Sign(angle) + Math.Max(Math.Min(angle * 10.0f, 10.0f), -10.0f)) * limbs[tailIndex].body.Mass);
//limbs[tailIndex].body.ApplyTorque(-limbs[tailIndex].body.AngularVelocity * 0.5f * limbs[tailIndex].body.Mass);
}
Vector2 steerForce = Vector2.Zero;
Limb head = GetLimb(LimbType.Head);
if (head != null)
{
float angle = ToolBox.GetShortestAngle(head.body.Rotation, movementAngle);
head.body.SmoothRotate(head.body.Rotation+angle, 25.0f);
//rotate head towards the angle of movement
//float torque = (Math.Sign(angle)*10.0f + MathHelper.Clamp(angle * 10.0f, -10.0f, 10.0f));
//angular drag
//torque -= head.body.AngularVelocity * 0.5f;
//head.body.ApplyTorque(torque * head.body.Mass);
//the movement vector if going to the direction of the head
//Vector2 headMovement = new Vector2(
// (float)Math.Cos(head.body.Rotation - MathHelper.PiOver2),
// (float)Math.Sin(head.body.Rotation - MathHelper.PiOver2));
//headMovement *= movement.Length();
//the movement angle is between direction of the head and the direction
//where the character is actually trying to go
//current * (float)alpha + previous * (1.0f - (float)alpha);
steerForce = (movement * 50.0f - head.LinearVelocity * 30.0f);
// force += (headMovement - movement) * Math.Min(head.LinearVelocity.Length()/movement.Length(), 1.0f);
if (!inWater) steerForce.Y = 0.0f;
}
for (int i = 0; i < limbs.Count(); i++)
{
if (steerForce!=Vector2.Zero)
limbs[i].body.ApplyForce(steerForce * limbs[i].SteerForce * limbs[i].Mass);
if (limbs[i].type != LimbType.Torso) continue;
float dist = (limbs[0].SimPosition - limbs[i].SimPosition).Length();
Vector2 limbPos = limbs[0].SimPosition - Vector2.Normalize(movement) * dist;
limbs[i].body.ApplyForce(((limbPos - limbs[i].SimPosition) * 3.0f - limbs[i].LinearVelocity * 3.0f) * limbs[i].Mass);
}
if (!inWater)
{
UpdateWalkAnim(deltaTime);
}
else
{
floorY = limbs[0].SimPosition.Y;
}
}
void UpdateWalkAnim(float deltaTime)
{
movement = ToolBox.SmoothStep(movement, TargetMovement * walkSpeed, 0.2f);
if (movement == Vector2.Zero) return;
Limb colliderLimb;
float colliderHeight;
Limb torso = GetLimb(LimbType.Torso);
Limb head = GetLimb(LimbType.Head);
if (torso!=null)
{
colliderLimb = torso;
colliderHeight = TorsoPosition;
colliderLimb.body.SmoothRotate(TorsoAngle*Dir, 5.0f);
}
else
{
colliderLimb = head;
colliderHeight = HeadPosition;
colliderLimb.body.SmoothRotate(HeadAngle*Dir, 10.0f);
}
Vector2 colliderPos = colliderLimb.SimPosition;
Vector2 rayStart = colliderPos;
Vector2 rayEnd = rayStart - new Vector2(0.0f, colliderHeight);
if (stairs != null) rayEnd.Y -= 0.5f;
//do a raytrace straight down from the torso to figure
//out whether the ragdoll is standing on ground
float closestFraction = 1;
//Structure closestStructure = null;
Game1.world.RayCast((fixture, point, normal, fraction) =>
{
//other limbs and bodies with no collision detection are ignored
if (fixture == null ||
fixture.CollisionCategories == Physics.CollisionCharacter ||
fixture.CollisionCategories == Physics.CollisionNone ||
fixture.CollisionCategories == Physics.CollisionMisc) return -1;
Structure structure = fixture.Body.UserData as Structure;
if (structure != null)
{
if (structure.StairDirection != Direction.None && (stairs == null)) return -1;
if (structure.IsPlatform && (IgnorePlatforms || stairs != null)) return -1;
}
onGround = true;
onFloorTimer = 0.05f;
if (fraction < closestFraction) closestFraction = fraction;
return 1;
}
, rayStart, rayEnd);
//the ragdoll "stays on ground" for 50 millisecs after separation
if (onFloorTimer <= 0.0f)
{
onGround = false;
}
else
{
onFloorTimer -= deltaTime;
}
if (closestFraction == 1) //raycast didn't hit anything
floorY = (currentHull == null || onGround) ? -1000.0f : ConvertUnits.ToSimUnits(currentHull.Rect.Y - currentHull.Rect.Height);
else
floorY = rayStart.Y + (rayEnd.Y - rayStart.Y) * closestFraction;
if (Math.Abs(colliderPos.Y - floorY)<colliderHeight*1.2f && onGround)
{
colliderLimb.Move(new Vector2(colliderPos.X + movement.X * 0.2f, floorY + colliderHeight), 5.0f);
}
float walkCycleSpeed = head.LinearVelocity.X * 0.08f;
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);
}
break;
case LimbType.LeftLeg:
case LimbType.RightLeg:
if (legTorque!=0.0f) limb.body.ApplyTorque(limb.Mass*legTorque*Dir);
break;
}
}
}
void UpdateStruggling()
{
Limb head = GetLimb(LimbType.Head);
Limb tail = GetLimb(LimbType.Tail);
head.body.ApplyTorque(head.Mass * Dir * 0.1f);
tail.body.ApplyTorque(tail.Mass * -Dir * 0.1f);
}
public override void Flip()
{
base.Flip();
foreach (Limb l in limbs)
{
if (!l.DoesFlip) continue;
l.body.SetTransform(l.SimPosition,
-l.body.Rotation);
}
}
private void Mirror()
{
float leftX = limbs[0].SimPosition.X, rightX = limbs[0].SimPosition.X;
for (int i = 1; i < limbs.Count(); i++ )
{
if (limbs[i].SimPosition.X < leftX)
{
leftX = limbs[i].SimPosition.X;
}
else if (limbs[i].SimPosition.X > rightX)
{
rightX = limbs[i].SimPosition.X;
}
}
float midX = (leftX + rightX) / 2.0f;
foreach (Limb l in limbs)
{
Vector2 newPos = new Vector2(midX - (l.SimPosition.X - midX), l.SimPosition.Y);
l.body.SetTransform(newPos, l.body.Rotation);
}
}
}
}

View File

@@ -0,0 +1,787 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using Microsoft.Xna.Framework;
namespace Subsurface
{
class HumanoidAnimController : AnimController
{
public HumanoidAnimController(Character character, XElement element)
: base(character, element)
{
}
public override void UpdateAnim(float deltaTime)
{
Vector2 colliderPos = GetLimb(LimbType.Torso).SimPosition;
Vector2 rayStart = colliderPos; // at the bottom of the player sprite
Vector2 rayEnd = rayStart - new Vector2(0.0f, TorsoPosition);
if (stairs != null) rayEnd.Y -= 0.5f;
if (anim != Animation.UsingConstruction) ResetPullJoints();
//do a raytrace straight down from the torso to figure
//out whether the ragdoll is standing on ground
float closestFraction = 1;
Structure closestStructure = null;
Game1.world.RayCast((fixture, point, normal, fraction) =>
{
switch (fixture.CollisionCategories)
{
case Physics.CollisionStairs:
Structure structure = fixture.Body.UserData as Structure;
if (stairs == null)
{
if (LowestLimb.SimPosition.Y<structure.SimPosition.Y)
{
return -1;
}
else
{
stairs = structure;
}
}
break;
case Physics.CollisionPlatform:
if (IgnorePlatforms || stairs != null) 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;
//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)
{
if (TargetMovement!=Vector2.Zero && TargetMovement.Length()>3.0f)
{
TargetMovement /= TargetMovement.Length() / 3.0f;
}
walkCycleSpeed *= 1.5f;
}
Vector2 colliderPos = new Vector2(torso.SimPosition.X, floorY);
float walkPosX = (float)Math.Cos(walkPos);
float walkPosY = (float)Math.Sin(walkPos);
float runningModifier = (float)Math.Max(Math.Abs(movement.X) / 1.5f, 1.0);
Vector2 stepSize = new Vector2(
this.stepSize.X * walkPosX * runningModifier,
this.stepSize.Y * walkPosY * runningModifier * runningModifier);
float footMid = (leftFoot.SimPosition.X + rightFoot.SimPosition.X) / 2.0f;
movement = ToolBox.SmoothStep(movement, TargetMovement, 0.5f);
movement.Y = 0.0f;
//place the anchors of the head and the torso to make the ragdoll stand
if (onGround && LowestLimb != null && (LowestLimb.SimPosition.Y-floorY < 0.5f || stairs != null) && head !=null)
{
getUpSpeed = Math.Max(getUpSpeed * (head.SimPosition.Y - colliderPos.Y), 0.25f);
if (stairs != null)
{
if (LowestLimb.SimPosition.Y < stairs.SimPosition.Y) IgnorePlatforms = true;
torso.pullJoint.Enabled = true;
torso.pullJoint.WorldAnchorB = new Vector2(
MathHelper.SmoothStep(torso.SimPosition.X, footMid + movement.X * 0.35f, getUpSpeed * 0.8f),
MathHelper.SmoothStep(torso.SimPosition.Y, colliderPos.Y + TorsoPosition - Math.Abs(walkPosX * 0.05f), getUpSpeed * 3.0f));
head.pullJoint.Enabled = true;
head.pullJoint.WorldAnchorB = new Vector2(
MathHelper.SmoothStep(head.SimPosition.X, footMid + movement.X * 0.4f, getUpSpeed * 0.8f),
MathHelper.SmoothStep(head.SimPosition.Y, colliderPos.Y + HeadPosition - Math.Abs(walkPosX * 0.05f), getUpSpeed * 3.0f));
}
else
{
torso.pullJoint.Enabled = true;
torso.pullJoint.WorldAnchorB =
ToolBox.SmoothStep(torso.SimPosition,
new Vector2(footMid + movement.X * 0.35f, colliderPos.Y + TorsoPosition - Math.Abs(walkPosX * 0.05f)), getUpSpeed);
head.pullJoint.Enabled = true;
head.pullJoint.WorldAnchorB =
ToolBox.SmoothStep(head.SimPosition,
new Vector2(footMid + movement.X * 0.4f, colliderPos.Y + HeadPosition - Math.Abs(walkPosX * 0.05f)), getUpSpeed);
}
//moving horizontally
if (TargetMovement.X != 0.0f)
{
//progress the walking animation
walkPos -= (walkCycleSpeed / runningModifier);
MoveLimb(leftFoot,
colliderPos + new Vector2(
stepSize.X,
(stepSize.Y > 0.0f) ? stepSize.Y : -0.15f),
10.0f, true);
MoveLimb(rightFoot,
colliderPos + new Vector2(
-stepSize.X,
(-stepSize.Y > 0.0f) ? -stepSize.Y : -0.15f),
10.0f, true);
if (Math.Sign(stepSize.X) == Math.Sign(Dir))
{
leftFoot.body.SmoothRotate(leftLeg.body.Rotation + MathHelper.PiOver2 * Dir * 1.3f, 20.0f * runningModifier);
}
else if (Math.Sign(-stepSize.X) == Math.Sign(Dir))
{
rightFoot.body.SmoothRotate(rightLeg.body.Rotation + MathHelper.PiOver2 * Dir * 1.3f, 20 * runningModifier);
}
if (walkPosY > 0.0f)
{
GetLimb(LimbType.LeftThigh).body.ApplyTorque(-walkPosY * Dir * Math.Abs(movement.X) * -2.0f);
}
else
{
GetLimb(LimbType.RightThigh).body.ApplyTorque(walkPosY * Dir * Math.Abs(movement.X) * -2.0f);
}
//calculate the positions of hands
handPos = torso.SimPosition;
handPos.X = -walkPosX * 0.25f;
float lowerY = -0.4f / runningModifier;
handPos.Y = lowerY + (float)(Math.Abs(Math.Sin(walkPos - Math.PI * 1.5f) * 0.1)) * runningModifier;
Vector2 posAdditon = new Vector2(movement.X*0.05f, 0.0f);
if (stairs!=null)
{
if ((stairs.StairDirection == Direction.Right && movement.X < 0.0f) ||
(stairs.StairDirection == Direction.Left && movement.X > 0.0f))
{
posAdditon.Y -= 0.1f;
}
else
{
posAdditon.Y += 0.1f;
}
}
if (!rightHand.Disabled)
{
rightHand.body.ApplyTorque(walkPosY * runningModifier * Dir);
MoveLimb(rightHand, torso.SimPosition + posAdditon +
new Vector2(
-handPos.X,
(Math.Sign(walkPosX) == Math.Sign(Dir)) ? handPos.Y : lowerY),
15.0f, true);
}
if (!leftHand.Disabled)
{
leftHand.body.ApplyTorque(-walkPosY * runningModifier * Dir);
MoveLimb(leftHand, torso.SimPosition + posAdditon +
new Vector2(
handPos.X,
(Math.Sign(walkPosX) == Math.Sign(-Dir)) ? handPos.Y : lowerY),
15.0f, true);
}
}
else
{
//add torque to the head to do a subtle "breathing" effect
//head.body.ApplyTorque((float)Math.Sin(gameTime.TotalGameTime.TotalMilliseconds / 300) * 0.2f);
//standing still -> "attach" the feet to the ground
float movementFactor = (movement.X / 4.0f) * movement.X * Math.Sign(movement.X);
Vector2 footPos = new Vector2(
colliderPos.X + movementFactor - Dir * 0.05f,
colliderPos.Y - 0.2f - Math.Abs(movementFactor));
MoveLimb(leftFoot, footPos, 2.5f);
MoveLimb(rightFoot, footPos, 2.5f);
leftFoot.body.SmoothRotate(Dir * MathHelper.PiOver2, 5.0f);
rightFoot.body.SmoothRotate(Dir * MathHelper.PiOver2, 5.0f);
handPos = torso.SimPosition;
handPos.X += movement.X;
handPos.Y -= 0.4f;
if (!rightHand.Disabled)
{
MoveLimb(rightHand, handPos, 1.5f, true);
}
if (!leftHand.Disabled)
{
MoveLimb(leftHand, handPos, 1.5f, true);
}
}
}
for (int i = 0; i < 2; i++)
{
Limb leg = (i == 0) ? rightFoot : leftFoot;
if (leg.SimPosition.Y < torso.SimPosition.Y) continue;
leg.body.ApplyTorque(-Dir * leg.Mass * 20.0f);
}
}
void UpdateSwimming()
{
IgnorePlatforms = true;
Vector2 footPos, handPos;
float surfaceLimiter = 1.0f;
Limb head = GetLimb(LimbType.Head);
if (currentHull != null && currentHull.Volume < currentHull.FullVolume)
{
surfaceLimiter = (ConvertUnits.ToDisplayUnits(head.SimPosition.Y)-surfaceY);
surfaceLimiter = Math.Max(1.0f, surfaceLimiter);
if (surfaceLimiter > 20.0f) return;
}
Limb leftFoot = GetLimb(LimbType.LeftFoot);
Limb rightFoot = GetLimb(LimbType.RightFoot);
Limb torso = GetLimb(LimbType.Torso);
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
float rotation = MathHelper.WrapAngle(torso.Rotation);
rotation = MathHelper.ToDegrees(rotation);
if (rotation < 0.0f) rotation += 360;
if (!character.IsNetworkPlayer)
{
if (rotation > 20 && rotation < 170)
targetDir = Direction.Left;
else if (rotation > 190 && rotation < 340)
targetDir = Direction.Right;
}
if (TargetMovement == Vector2.Zero) return;
float targetSpeed = TargetMovement.Length();
if (targetSpeed > 0.0f) TargetMovement /= targetSpeed;
//if trying to head to the opposite direction, apply torque
//to the torso to flip the ragdoll around
if (Math.Sign(TargetMovement.X) != Dir && TargetMovement.X != 0.0f)
{
float torque = torso.Mass * 10.0f;
torque *= (rotation > 90 && rotation < 270) ? -Dir : Dir;
torso.body.ApplyTorque(torque);
}
movement = ToolBox.SmoothStep(movement, TargetMovement, 0.3f);
//dont try to move upwards if head is already out of water
if (surfaceLimiter > 1.0f)
{
if (TargetMovement.X == 0.0f)
{
head.body.ApplyForce(head.Mass * new Vector2(-Dir * 5.1f, -5.0f));
torso.body.ApplyForce(torso.Mass *new Vector2(-Dir*5.1f, -15.0f));
leftFoot.body.ApplyForce(leftFoot.Mass *new Vector2(0.0f, -80.0f));
rightFoot.body.ApplyForce(rightFoot.Mass *new Vector2(0.0f, -80.0f));
}
else
{
TargetMovement = new Vector2(
(float)Math.Sqrt(targetSpeed * targetSpeed - TargetMovement.Y * TargetMovement.Y)
* Math.Sign(TargetMovement.X),
Math.Max(TargetMovement.Y, TargetMovement.Y * 0.2f));
head.body.ApplyTorque(Dir * 0.1f);
}
movement.Y = movement.Y - (surfaceLimiter - 1.0f) * 0.01f;
}
head.body.ApplyForce((new Vector2(movement.X,
movement.Y / surfaceLimiter + 0.2f) - head.body.LinearVelocity * 0.2f) *
20.0f * head.body.Mass);
torso.body.ApplyForce((new Vector2(movement.X,
movement.Y / surfaceLimiter + 0.2f) - torso.body.LinearVelocity * 0.2f) * 10.0f * torso.body.Mass);
walkPos += movement.Length() * 0.15f;
footPos = (leftFoot.SimPosition + rightFoot.SimPosition) / 2.0f;
Vector2 transformedFootPos = new Vector2((float)Math.Sin(walkPos) * 0.3f, 0.0f);
transformedFootPos = Vector2.Transform(
transformedFootPos,
Matrix.CreateRotationZ(torso.body.Rotation));
MoveLimb(leftFoot, footPos + transformedFootPos, 2.5f);
MoveLimb(rightFoot, footPos - transformedFootPos, 2.5f);
Vector2 feetExtendForce = new Vector2(
(float)-Math.Sin(torso.body.Rotation),
(float)Math.Cos(torso.body.Rotation));
leftFoot.body.ApplyForce(feetExtendForce);
rightFoot.body.ApplyForce(feetExtendForce);
leftFoot.body.ApplyTorque(leftFoot.body.Mass * -Dir);
rightFoot.body.ApplyTorque(rightFoot.body.Mass * -Dir);
handPos = (torso.SimPosition + head.SimPosition) / 2.0f;
//if (!rightHand.Disabled) rightHand.body.ApplyTorque(leftHand.body.Mass * Dir);
//if (!leftHand.Disabled) leftHand.body.ApplyTorque(leftHand.body.Mass * Dir);
//at the surface, not moving sideways -> hands just float around
if (!headInWater && TargetMovement.X == 0.0f)
{
handPos.X = handPos.X + Dir * 0.6f;
float wobbleAmount = 0.05f;
if (!rightHand.Disabled)
{
MoveLimb(rightHand, new Vector2(
handPos.X + (float)Math.Sin(walkPos / 1.5f) * wobbleAmount,
handPos.Y + (float)Math.Sin(walkPos / 3.5f) * wobbleAmount - 0.0f), 1.5f);
}
if (!leftHand.Disabled)
{
MoveLimb(leftHand, new Vector2(
handPos.X + (float)Math.Sin(walkPos / 2.0f) * wobbleAmount,
handPos.Y + (float)Math.Sin(walkPos / 3.0f) * wobbleAmount - 0.0f), 1.5f);
}
return;
}
handPos += head.LinearVelocity * 0.1f;
float handCyclePos = walkPos / 2.0f;
float handPosX = (float)Math.Cos(handCyclePos * Dir) * 0.4f;
float handPosY = (float)Math.Sin(handCyclePos * Dir) * 0.7f;
handPosY = MathHelper.Clamp(handPosY, -0.6f, 0.6f);
Matrix rotationMatrix = Matrix.CreateRotationZ(torso.Rotation);
if (!rightHand.Disabled)
{
Vector2 rightHandPos = new Vector2(-handPosX, -handPosY);
rightHandPos.X = (Dir == 1.0f) ? Math.Max(0.2f, rightHandPos.X) : Math.Min(-0.2f, rightHandPos.X);
rightHandPos = Vector2.Transform(rightHandPos, rotationMatrix);
MoveLimb(rightHand, handPos + rightHandPos, 3.5f);
}
if (!leftHand.Disabled)
{
Vector2 leftHandPos = new Vector2(handPosX, handPosY);
leftHandPos.X = (Dir == 1.0f) ? Math.Max(0.2f, leftHandPos.X) : Math.Min(-0.2f, leftHandPos.X);
leftHandPos = Vector2.Transform(leftHandPos, rotationMatrix);
MoveLimb(leftHand, handPos + leftHandPos, 3.5f);
}
}
void UpdateClimbing()
{
if (character.SelectedConstruction == null)
{
anim = Animation.None;
return;
}
onGround = false;
IgnorePlatforms = true;
movement = ToolBox.SmoothStep(movement, TargetMovement, 0.3f);
Vector2 footPos, handPos;
Limb leftFoot = GetLimb(LimbType.LeftFoot);
Limb rightFoot = GetLimb(LimbType.RightFoot);
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
Limb waist = GetLimb(LimbType.Waist);
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb rightHand = GetLimb(LimbType.RightHand);
Vector2 ladderSimPos = ConvertUnits.ToSimUnits(
character.SelectedConstruction.Rect.X + character.SelectedConstruction.Rect.Width / 2.0f,
character.SelectedConstruction.Rect.Y);
MoveLimb(head, new Vector2(ladderSimPos.X - 0.27f * Dir, head.SimPosition.Y + 0.05f), 10.5f);
MoveLimb(torso, new Vector2(ladderSimPos.X - 0.27f * Dir, torso.SimPosition.Y), 10.5f);
MoveLimb(waist, new Vector2(ladderSimPos.X - 0.35f * Dir, waist.SimPosition.Y), 10.5f);
float stepHeight = ConvertUnits.ToSimUnits(30.0f);
handPos = new Vector2(
ladderSimPos.X,
head.SimPosition.Y + 0.3f + movement.Y * 0.1f - ladderSimPos.Y);
MoveLimb(leftHand,
new Vector2(handPos.X,
ToolBox.Round(handPos.Y - stepHeight, stepHeight * 2.0f) + stepHeight + ladderSimPos.Y),
5.2f);
MoveLimb(rightHand,
new Vector2(handPos.X,
ToolBox.Round(handPos.Y, stepHeight * 2.0f) + ladderSimPos.Y),
5.2f);
leftHand.body.ApplyTorque(Dir * 2.0f);
rightHand.body.ApplyTorque(Dir * 2.0f);
footPos = new Vector2(
handPos.X + Dir*0.1f,
head.SimPosition.Y - stepHeight * 2.7f - ladderSimPos.Y);
MoveLimb(leftFoot,
new Vector2(footPos.X,
ToolBox.Round(footPos.Y + stepHeight, stepHeight * 2.0f) - stepHeight + ladderSimPos.Y),
7.5f, true);
MoveLimb(rightFoot,
new Vector2(footPos.X,
ToolBox.Round(footPos.Y, stepHeight * 2.0f) + ladderSimPos.Y),
7.5f, true);
//apply torque to the legs to make the knees bend
Limb leftLeg = GetLimb(LimbType.LeftLeg);
Limb rightLeg = GetLimb(LimbType.RightLeg);
leftLeg.body.ApplyTorque(Dir * -3.0f);
rightLeg.body.ApplyTorque(Dir * -3.0f);
//apply forces to the head and the torso to move the character up/down
float movementFactor = (handPos.Y / stepHeight) * (float)Math.PI;
movementFactor = 0.8f + (float)Math.Abs(Math.Sin(movementFactor));
Vector2 climbForce = new Vector2(0.0f, movement.Y + 0.5f) * movementFactor;
torso.body.ApplyForce(climbForce * 65.0f * torso.Mass);
head.body.SmoothRotate(0.0f);
Rectangle trigger = character.SelectedConstruction.Prefab.triggers.First();
trigger = character.SelectedConstruction.TransformTrigger(trigger);
//stop climbing if:
// - going too fast (can't grab a ladder while falling)
// - moving sideways
// - reached the top or bottom of the ladder
if (Math.Abs(torso.LinearVelocity.Y) > 5.0f ||
TargetMovement.X != 0.0f ||
(TargetMovement.Y < 0.0f && ConvertUnits.ToSimUnits(trigger.Height) + handPos.Y < HeadPosition*2.0f) ||
(TargetMovement.Y > 0.0f && -handPos.Y < ConvertUnits.ToSimUnits(10.0f)))
{
anim = Animation.None;
character.SelectedConstruction = null;
IgnorePlatforms = false;
}
}
void UpdateStruggling()
{
Limb leftLeg = GetLimb(LimbType.LeftFoot);
Limb rightLeg = GetLimb(LimbType.RightFoot);
Limb torso = GetLimb(LimbType.Torso);
walkPos += 0.2f;
if (inWater) return;
Vector2 footPos = torso.body.Position+ new Vector2(TorsoPosition*Dir,0.0f);
MoveLimb(leftLeg, footPos, 0.7f);
MoveLimb(rightLeg, footPos, 0.7f);
}
public override void HoldItem(float deltaTime, Camera cam, Item item, Vector2[] handlePos, Vector2 holdPos, Vector2 aimPos, float holdAngle)
{
//calculate the handle positions
Matrix itemTransfrom = Matrix.CreateRotationZ(item.body.Rotation);
Vector2[] transformedHandlePos = new Vector2[2];
transformedHandlePos[0] = Vector2.Transform(handlePos[0], itemTransfrom);
transformedHandlePos[1] = Vector2.Transform(handlePos[1], itemTransfrom);
Limb head = GetLimb(LimbType.Head);
Limb torso = GetLimb(LimbType.Torso);
Limb leftHand = GetLimb(LimbType.LeftHand);
Limb leftArm = GetLimb(LimbType.LeftArm);
Limb rightHand = GetLimb(LimbType.RightHand);
Limb rightArm = GetLimb(LimbType.RightArm);
Vector2 itemPos = character.SecondaryKeyDown.State ? aimPos : holdPos;
float itemAngle;
if (character.SecondaryKeyDown.State && itemPos != Vector2.Zero)
{
Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition);
Vector2 diff = (mousePos - torso.SimPosition) * Dir;
holdAngle = ToolBox.VectorToAngle(new Vector2(diff.X, diff.Y * Dir)) - torso.body.Rotation * Dir;
holdAngle = MathHelper.Clamp(ToolBox.WrapAnglePi(holdAngle), -1.3f, 1.0f);
itemAngle = (torso.body.Rotation + holdAngle * Dir);
head.body.SmoothRotate(itemAngle);
if (TargetMovement == Vector2.Zero && inWater)
{
torso.body.SmoothRotate(0.2f * Dir);
torso.body.ApplyForce(torso.body.LinearVelocity * -0.5f);
}
}
else
{
itemAngle = (torso.body.Rotation + holdAngle * Dir);
}
Vector2 shoulderPos = head.SimPosition + (torso.SimPosition - head.SimPosition) *0.9f;
Vector2 transformedHoldPos = shoulderPos;
if (itemPos == Vector2.Zero)
{
if (character.SelectedItems[0] == item)
{
transformedHoldPos = rightHand.pullJoint.WorldAnchorA - transformedHandlePos[0];
itemAngle = (rightHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
//rightHand.Disabled = true;
}
if (character.SelectedItems[1] == item)
{
transformedHoldPos = leftHand.pullJoint.WorldAnchorA - transformedHandlePos[1];
itemAngle = (leftHand.Rotation + (holdAngle - MathHelper.PiOver2) * Dir);
//leftHand.Disabled = true;
}
}
else
{
if (character.SelectedItems[0] == item)
{
rightHand.Disabled = true;
}
if (character.SelectedItems[1] == item)
{
leftHand.Disabled = true;
}
itemPos.X = itemPos.X * Dir;
Matrix torsoTransform = Matrix.CreateRotationZ(itemAngle);
transformedHoldPos += Vector2.Transform(itemPos, torsoTransform);
}
Vector2 bodyVelocity = torso.body.LinearVelocity / 60.0f;
item.body.ResetDynamics();
item.body.SetTransform(ToolBox.SmoothStep(item.body.Position, transformedHoldPos + bodyVelocity, 0.5f), itemAngle);
//item.body.SmoothRotate(itemAngle, 50.0f);
for (int i = 0; i < 2; i++)
{
if (character.SelectedItems[i] != item) continue;
if (itemPos == Vector2.Zero) continue;
Limb hand = (i == 0) ? rightHand : leftHand;
Limb arm = (i == 0) ? rightArm : leftArm;
//hand length
float a = 24.0f;
//arm length
float b = 34.0f;
//distance from shoulder to holdpos
float c = ConvertUnits.ToDisplayUnits(Vector2.Distance(transformedHoldPos + transformedHandlePos[i], shoulderPos));
c = MathHelper.Clamp(a + b - 1, b-a, c);
float ang2 = ToolBox.VectorToAngle((transformedHoldPos + transformedHandlePos[i]) - shoulderPos)+MathHelper.PiOver2;
float armAngle = ToolBox.SolveTriangleSSS(a, b, c);
float handAngle = ToolBox.SolveTriangleSSS(b, a, c);
arm.body.SmoothRotate((ang2 - armAngle * Dir), 20.0f);
hand.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f);
}
}
public override void Flip()
{
base.Flip();
Limb torso = GetLimb(LimbType.Torso);
Vector2 difference;
Matrix torsoTransform = Matrix.CreateRotationZ(torso.Rotation);
for (int i = 0; i < character.SelectedItems.Length; i++)
{
if (character.SelectedItems[i] != null)
{
difference = character.SelectedItems[i].body.Position - torso.SimPosition;
difference = Vector2.Transform(difference, torsoTransform);
difference.Y = -difference.Y;
character.SelectedItems[i].body.SetTransform(
torso.SimPosition + Vector2.Transform(difference, -torsoTransform),
ToolBox.WrapAngleTwoPi(-character.SelectedItems[i].body.Rotation));
}
}
foreach (Limb l in limbs)
{
switch (l.type)
{
case LimbType.LeftHand:
case LimbType.LeftArm:
case LimbType.RightHand:
case LimbType.RightArm:
difference = l.body.Position - torso.SimPosition;
difference = Vector2.Transform(difference, torsoTransform);
difference.Y = -difference.Y;
l.body.SetTransform(torso.SimPosition + Vector2.Transform(difference, -torsoTransform), -l.body.Rotation);
break;
default:
if (!inWater) l.body.SetTransform(l.body.Position,
ToolBox.WrapAnglePi(l.body.Rotation * (l.DoesFlip ? -1.0f : 1.0f)));
break;
}
}
}
}
}

View File

@@ -0,0 +1,6 @@
namespace Subsurface.Characters
{
class Job
{
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Subsurface
{
class Job
{
public static List<Job> jobList;
string name;
string description;
//names of the items the character spawns with
public List<string> itemNames;
public string Name
{
get { return name; }
}
public Job(XElement element)
{
name = element.Name.ToString();
description = ToolBox.GetAttributeString(element, "description", "");
itemNames = new List<string>();
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString())
{
case "item":
string itemName = ToolBox.GetAttributeString(subElement, "name", "");
if (!string.IsNullOrEmpty(itemName)) itemNames.Add(itemName);
break;
}
}
}
public static void LoadAll(string filePath)
{
jobList = new List<Job>();
XDocument doc = ToolBox.TryLoadXml(filePath);
foreach (XElement element in doc.Root.Elements())
{
Job job = new Job(element);
jobList.Add(job);
}
}
}
}

View File

@@ -0,0 +1,362 @@
using System;
using System.Xml.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Subsurface
{
public enum LimbType
{
None, LeftHand, RightHand, LeftArm, RightArm,
LeftLeg, RightLeg, LeftFoot, RightFoot, Head, Torso, Tail, Legs, RightThigh, LeftThigh, Waist
};
class Limb
{
private const float LimbDensity = 15;
private const float LimbAngularDamping = 7;
public readonly Character character;
//the physics body of the limb
public PhysicsBody body;
private Texture2D bodyShapeTexture;
private readonly int refJointIndex;
private readonly float steerForce;
private readonly bool doesFlip;
public Sprite sprite;
public bool inWater;
public FixedMouseJoint pullJoint;
public readonly LimbType type;
public readonly bool ignoreCollisions;
private float maxHealth;
private float damage;
private float bleeding;
public readonly float impactTolerance;
Sound hitSound;
//a timer for delaying when a hitsound/attacksound can be played again
public float soundTimer;
public const float SoundInterval = 0.2f;
public readonly Attack attack;
private Direction dir;
private Item wearingItem;
private Sprite wearingItemSprite;
private Vector2 animTargetPos;
public Texture2D BodyShapeTexture
{
get { return bodyShapeTexture; }
}
public bool DoesFlip
{
get { return doesFlip; }
}
public Vector2 SimPosition
{
get { return body.Position; }
}
public float Rotation
{
get { return body.Rotation; }
}
public Vector2 AnimTargetPos
{
get { return animTargetPos; }
}
public float SteerForce
{
get { return steerForce; }
}
public float Mass
{
get { return body.Mass; }
}
public bool Disabled { get; set; }
public Sound HitSound
{
get { return hitSound; }
}
public Vector2 LinearVelocity
{
get { return body.LinearVelocity; }
}
public float Dir
{
get { return ((dir == Direction.Left) ? -1.0f : 1.0f); }
set { dir = (value==-1.0f) ? Direction.Left : Direction.Right; }
}
public int RefJointIndex
{
get { return refJointIndex; }
}
public float Damage
{
get { return damage; }
set
{
damage = Math.Max(value, 0.0f);
if (damage >=maxHealth) character.Kill();
}
}
public float Bleeding
{
get { return bleeding; }
set { bleeding = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public Item WearingItem
{
get { return wearingItem; }
set { wearingItem = value; }
}
public Sprite WearingItemSprite
{
get { return wearingItemSprite; }
set { wearingItemSprite = value; }
}
public Limb (Character character, XElement element)
{
this.character = character;
dir = Direction.Right;
doesFlip = ToolBox.GetAttributeBool(element, "flip", false);
body = new PhysicsBody(element);
if (ToolBox.GetAttributeBool(element, "ignorecollisions", false))
{
body.CollisionCategories = Category.None;
body.CollidesWith = Category.None;
ignoreCollisions = true;
}
else
{
//limbs don't collide with each other
body.CollisionCategories = Physics.CollisionCharacter;
body.CollidesWith = Physics.CollisionAll & ~Physics.CollisionCharacter & ~Physics.CollisionMisc;
}
impactTolerance = ToolBox.GetAttributeFloat(element, "impacttolerance", 20.0f);
body.UserData = this;
refJointIndex = -1;
if (element.Attribute("type") != null)
{
type = (LimbType)Enum.Parse(typeof(LimbType), element.Attribute("type").Value, true);
Vector2 jointPos = ToolBox.GetAttributeVector2(element, "pullpos", Vector2.Zero);
jointPos = ConvertUnits.ToSimUnits(jointPos);
refJointIndex = ToolBox.GetAttributeInt(element, "refjoint", -1);
pullJoint = new FixedMouseJoint(body.FarseerBody, jointPos);
pullJoint.Enabled = false;
pullJoint.MaxForce = 150.0f * body.Mass;
Game1.world.AddJoint(pullJoint);
}
else
{
type = LimbType.None;
}
steerForce = ToolBox.GetAttributeFloat(element, "steerforce", 0.0f);
maxHealth = Math.Max(ToolBox.GetAttributeFloat(element, "health", 100.0f),1.0f);
body.BodyType = BodyType.Dynamic;
body.FarseerBody.AngularDamping = LimbAngularDamping;
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString())
{
case "sprite":
string spritePath = subElement.Attribute("texture").Value;
if (character.info!=null)
spritePath = spritePath.Replace("[GENDER]", (character.info.gender == Gender.Female) ? "f" : "");
sprite = new Sprite(subElement, "", spritePath);
break;
case "attack":
attack = new Attack(subElement);
break;
case "sound":
hitSound = Sound.Load(ToolBox.GetAttributeString(subElement, "file", ""));
break;
}
}
}
public void Move(Vector2 pos, float amount, bool pullFromCenter=false)
{
Vector2 pullPos = body.Position;
if (pullJoint!=null && !pullFromCenter)
{
pullPos = pullJoint.WorldAnchorA;
}
animTargetPos = pos;
Vector2 vel = body.LinearVelocity;
Vector2 deltaPos = pos - pullPos;
deltaPos *= amount;
body.ApplyLinearImpulse((deltaPos - vel * 0.5f) * body.Mass, pullPos);
}
public void Update(float deltaTime)
{
if (LinearVelocity.X>100.0f)
{
DebugConsole.ThrowError("CHARACTER EXPLODED");
foreach (Limb limb in character.animController.limbs)
{
limb.body.ResetDynamics();
limb.body.SetTransform(body.Position, 0.0f);
}
}
if (inWater)
{
//buoyancy
Vector2 buoyancy = new Vector2(0, Mass * 9.6f);
//drag
Vector2 velDir = Vector2.Normalize(LinearVelocity);
Vector2 line = new Vector2((float)Math.Cos(body.Rotation), (float)Math.Sin(body.Rotation));
line *= ConvertUnits.ToSimUnits(sprite.size.Y);
Vector2 normal = new Vector2(-line.Y, line.X);
normal = Vector2.Normalize(-normal);
float dragDot = Vector2.Dot(normal, velDir);
Vector2 dragForce = Vector2.Zero;
if (dragDot > 0)
{
float vel = LinearVelocity.Length();
float drag = dragDot * vel * vel
* ConvertUnits.ToSimUnits(sprite.size.Y);
dragForce = drag * -velDir;
if (dragForce.Length() > 100.0f) { }
}
body.ApplyForce(dragForce + buoyancy);
body.ApplyTorque(body.AngularVelocity * body.Mass * -0.05f);
}
if (character.IsDead) return;
soundTimer -= deltaTime;
if (ToolBox.RandomFloat(0.0f, 1000.0f) < Bleeding)
{
Game1.particleManager.CreateParticle(SimPosition,
MathHelper.Pi,
ToolBox.RandomFloat(0.0f, 0.0f), !inWater ? "blood" : "waterblood");
}
}
public void Draw(SpriteBatch spriteBatch, bool debugDraw)
{
Color color = new Color(1.0f, 1.0f - damage / maxHealth, 1.0f - damage / maxHealth);
body.Dir = Dir;
body.Draw(spriteBatch, sprite, color);
if (wearingItem != null)
{
SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
wearingItemSprite.Draw(spriteBatch,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color,
-body.DrawRotation,
1.0f, spriteEffect);
}
if (!debugDraw) return;
if (pullJoint!=null)
{
Vector2 pos = ConvertUnits.ToDisplayUnits(pullJoint.WorldAnchorB);
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), Color.Red, true);
}
if (bodyShapeTexture == null)
{
switch (body.bodyShape)
{
case PhysicsBody.Shape.Rectangle:
bodyShapeTexture = GUI.CreateRectangle(
(int)ConvertUnits.ToDisplayUnits(body.width),
(int)ConvertUnits.ToDisplayUnits(body.height));
break;
case PhysicsBody.Shape.Capsule:
bodyShapeTexture = GUI.CreateCapsule(
(int)ConvertUnits.ToDisplayUnits(body.radius),
(int)ConvertUnits.ToDisplayUnits(body.height));
break;
case PhysicsBody.Shape.Circle:
bodyShapeTexture = GUI.CreateCircle((int)ConvertUnits.ToDisplayUnits(body.radius));
break;
}
}
spriteBatch.Draw(
bodyShapeTexture,
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
null,
Color.White,
-body.DrawRotation,
new Vector2(bodyShapeTexture.Width / 2, bodyShapeTexture.Height / 2), 1.0f, SpriteEffects.None, 0.0f);
}
public void Remove()
{
sprite.Remove();
body.Remove();
if (hitSound!=null) hitSound.Remove();
}
}
}

View File

@@ -0,0 +1,94 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Subsurface
{
class Attack
{
public enum DamageType { None, Blunt, Slash };
public enum Type
{
None, PinchCW, PinchCCW
};
public readonly Type type;
public readonly float range;
public readonly float duration;
public readonly DamageType damageType;
public readonly float structureDamage;
public readonly float damage;
public readonly float bleedingDamage;
public readonly float stun;
private float priority;
public Attack(XElement element)
{
try
{
type = (Type)Enum.Parse(typeof(Type), element.Attribute("type").Value, true);
}
catch
{
type = Type.None;
}
try
{
damageType = (DamageType)Enum.Parse(typeof(DamageType), ToolBox.GetAttributeString(element, "damagetype", "None"), true);
}
catch
{
damageType = DamageType.None;
}
damage = ToolBox.GetAttributeFloat(element, "damage", 0.0f);
structureDamage = ToolBox.GetAttributeFloat(element, "damage", 0.0f);
bleedingDamage = ToolBox.GetAttributeFloat(element, "bleedingdamage", 0.0f);
stun = ToolBox.GetAttributeFloat(element, "stun", 0.0f);
range = FarseerPhysics.ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "range", 0.0f));
duration = ToolBox.GetAttributeFloat(element, "duration", 0.0f);
priority = ToolBox.GetAttributeFloat(element, "priority", 1.0f);
}
public void DoDamage(IDamageable target, Vector2 position, float deltaTime, bool playSound=true)
{
float damageAmount = 0.0f;
DamageSoundType damageSoundType = DamageSoundType.None;
if (target as Structure == null)
{
damageAmount = damage;
damageSoundType = (damageType == DamageType.Blunt) ? DamageSoundType.LimbBlunt : DamageSoundType.LimbSlash;
}
else
{
damageAmount = structureDamage;
damageSoundType = (damageType == DamageType.Blunt) ? DamageSoundType.StructureBlunt: DamageSoundType.StructureSlash;
}
if (playSound) AmbientSoundManager.PlayDamageSound(damageSoundType, damageAmount, position);
if (duration > 0.0f) damageAmount *= deltaTime;
float bleedingAmount = (duration == 0.0f) ? bleedingDamage : bleedingDamage * deltaTime;
if (damageAmount>0.0f) target.AddDamage(position, damageAmount, bleedingAmount, stun);
}
}
}

View File

@@ -0,0 +1,643 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Subsurface
{
class Ragdoll
{
public static List<Ragdoll> list = new List<Ragdoll>();
public static bool DebugDraw = false;
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 Vector2 TargetMovement
{
get { return (correctionMovement == Vector2.Zero) ? targetMovement : correctionMovement; }
set { targetMovement = value; }
}
public float HeadPosition
{
get { return headPosition; }
}
public float HeadAngle
{
get { return headAngle; }
}
public float TorsoPosition
{
get { return torsoPosition; }
}
public float TorsoAngle
{
get { return torsoAngle; }
}
public float Dir
{
get { return ((dir == Direction.Left) ? -1.0f : 1.0f); }
}
public bool InWater
{
get { return inWater; }
}
public bool HeadInWater
{
get { return headInWater; }
}
public Hull CurrentHull
{
get { return currentHull;}
}
public bool IgnorePlatforms
{
get { return ignorePlatforms; }
set
{
if (ignorePlatforms == value) return;
ignorePlatforms = value;
foreach (Limb l in limbs)
{
if (l.ignoreCollisions) continue;
l.body.CollidesWith = (ignorePlatforms) ?
Physics.CollisionWall | Physics.CollisionProjectile | Physics.CollisionStairs
: Physics.CollisionAll & ~Physics.CollisionCharacter & ~Physics.CollisionMisc;
}
}
}
public float StrongestImpact
{
get { return strongestImpact; }
set { strongestImpact = Math.Max(value, strongestImpact); }
}
public Structure Stairs
{
get { return stairs; }
}
public Ragdoll(Character character, XElement element)
{
list.Add(this);
this.character = character;
dir = Direction.Right;
//int limbAmount = ;
limbs = new Limb[element.Elements("limb").Count()];
limbJoints = new RevoluteJoint[element.Elements("joint").Count()];
limbDictionary = new Dictionary<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;
if (!limbDictionary.ContainsKey(limb.type)) limbDictionary.Add(limb.type, limb);
break;
case "joint":
Byte limb1ID = Convert.ToByte(subElement.Attribute("limb1").Value);
Byte limb2ID = Convert.ToByte(subElement.Attribute("limb2").Value);
Vector2 limb1Pos = ToolBox.GetAttributeVector2(subElement, "limb1anchor", Vector2.Zero);
limb1Pos = ConvertUnits.ToSimUnits(limb1Pos);
Vector2 limb2Pos = ToolBox.GetAttributeVector2(subElement, "limb2anchor", Vector2.Zero);
limb2Pos = ConvertUnits.ToSimUnits(limb2Pos);
RevoluteJoint joint = new RevoluteJoint(limbs[limb1ID].body.FarseerBody, limbs[limb2ID].body.FarseerBody, limb1Pos, limb2Pos);
joint.CollideConnected = false;
if (subElement.Attribute("lowerlimit")!=null)
{
joint.LimitEnabled = true;
joint.LowerLimit = float.Parse(subElement.Attribute("lowerlimit").Value) * ((float)Math.PI / 180.0f);
joint.UpperLimit = float.Parse(subElement.Attribute("upperlimit").Value) * ((float)Math.PI / 180.0f);
}
joint.MotorEnabled = true;
joint.MaxMotorTorque = 0.25f;
Game1.world.AddJoint(joint);
for (int i = 0; i < limbJoints.Length; i++ )
{
if (limbJoints[i] != null) continue;
limbJoints[i] = joint;
break;
}
break;
}
}
foreach (var joint in limbJoints)
{
joint.BodyB.SetTransform(
joint.BodyA.Position+joint.LocalAnchorA-joint.LocalAnchorB,
(joint.LowerLimit+joint.UpperLimit)/2.0f);
}
float startDepth = 0.1f;
float increment = 0.0001f;
foreach (Character otherCharacter in Character.characterList)
{
if (otherCharacter==character) continue;
startDepth+=increment;
}
foreach (Limb limb in limbs)
{
limb.sprite.Depth = startDepth + limb.sprite.Depth * 0.00001f;
}
}
public bool OnLimbCollision(Fixture f1, Fixture f2, Contact contact)
{
Structure structure = f2.Body.UserData as Structure;
//always collides with bodies other than structures
if (structure == null)
{
CalculateImpact(f1, f2, contact);
return true;
}
if (structure.IsPlatform)
{
if (ignorePlatforms) return false;
//the collision is ignored if the lowest limb is under the platform
if (lowestLimb==null || lowestLimb.SimPosition.Y < f2.Body.Position.Y) return false;
}
else if (structure.StairDirection!=Direction.None)
{
if (inWater || (!(targetMovement.Y>Math.Abs(targetMovement.X/2.0f)) && lowestLimb.body.Position.Y < ConvertUnits.ToSimUnits(structure.Rect.Y - structure.Rect.Height) + 0.5f))
{
stairs = null;
return false;
}
Limb limb = f1.Body.UserData as Limb;
if (limb != null && (limb.type == LimbType.LeftFoot || limb.type == LimbType.RightFoot))
{
Debug.WriteLine(contact.Manifold.LocalNormal.Y);
if (contact.Manifold.LocalNormal.Y >= 0.0f)
{
stairs = structure;
return true;
}
else
{
stairs = null;
return false;
}
}
else
{
return false;
}
}
CalculateImpact(f1, f2, contact);
return true;
}
private void CalculateImpact(Fixture f1, Fixture f2, Contact contact)
{
Vector2 normal = contact.Manifold.LocalNormal;
float impact = Vector2.Dot(f1.Body.LinearVelocity, -normal);
Limb l = (Limb)f1.Body.UserData;
if (impact > 1.0f && l.HitSound != null && l.soundTimer<=0.0f) l.HitSound.Play(Math.Min(impact / 5.0f, 1.0f));
if (impact > l.impactTolerance)
{
l.Damage += (impact-l.impactTolerance*0.1f);
strongestImpact = Math.Max(strongestImpact, impact - l.impactTolerance);
}
}
public virtual void Draw(SpriteBatch spriteBatch)
{
foreach (Limb limb in limbs)
{
limb.Draw(spriteBatch, DebugDraw);
}
if (!DebugDraw) return;
foreach (Limb limb in limbs)
{
if (limb.pullJoint != null)
{
Vector2 pos = ConvertUnits.ToDisplayUnits(limb.pullJoint.WorldAnchorA);
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), Color.Red, true, 0.01f);
if (limb.AnimTargetPos == Vector2.Zero) continue;
Vector2 pos2 = ConvertUnits.ToDisplayUnits(limb.AnimTargetPos);
pos2.Y = -pos2.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos2.X, (int)pos2.Y, 5, 5), Color.Blue, true, 0.01f);
GUI.DrawLine(spriteBatch, pos, pos2, Color.Green);
}
}
foreach (RevoluteJoint joint in limbJoints)
{
Vector2 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorA);
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true);
pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorB);
pos.Y = -pos.Y;
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true);
}
}
public virtual void Flip()
{
dir = (dir == Direction.Left) ? Direction.Right : Direction.Left;
for (int i = 0; i < limbJoints.Count(); i++)
{
float lowerLimit = -limbJoints[i].UpperLimit;
float upperLimit = -limbJoints[i].LowerLimit;
limbJoints[i].LowerLimit = lowerLimit;
limbJoints[i].UpperLimit = upperLimit;
limbJoints[i].LocalAnchorA = new Vector2(-limbJoints[i].LocalAnchorA.X, limbJoints[i].LocalAnchorA.Y);
limbJoints[i].LocalAnchorB = new Vector2(-limbJoints[i].LocalAnchorB.X, limbJoints[i].LocalAnchorB.Y);
}
for (int i = 0; i < limbs.Count(); i++)
{
if (limbs[i] == null) continue;
Vector2 spriteOrigin = limbs[i].sprite.Origin;
spriteOrigin.X = limbs[i].sprite.SourceRect.Width - spriteOrigin.X;
limbs[i].sprite.Origin = spriteOrigin;
limbs[i].Dir = Dir;
if (limbs[i].pullJoint == null) continue;
limbs[i].pullJoint.LocalAnchorA =
new Vector2(
-limbs[i].pullJoint.LocalAnchorA.X,
limbs[i].pullJoint.LocalAnchorA.Y);
}
}
/// <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 (ConvertUnits.ToSimUnits(currentHull.Surface)-floorY> HeadPosition)
inWater = true;
}
foreach (Limb limb in limbs)
{
Vector2 limbPosition = ConvertUnits.ToDisplayUnits(limb.SimPosition);
//find the room which the limb is in
//the room where the ragdoll is in is used as the "guess", meaning that it's checked first
Hull limbHull = Hull.FindHull(limbPosition, currentHull);
bool prevInWater = limb.inWater;
limb.inWater = false;
if (limbHull==null)
{
//limb isn't in any room -> it's in the water
limb.inWater = true;
}
else if (limbHull.Volume>0.0f && Map.RectContains(limbHull.Rect, limbPosition))
{
if (limbPosition.Y < limbHull.Surface)
{
limb.inWater = true;
if (flowForce.Length() > 0.01f)
{
limb.body.ApplyForce(flowForce);
if (flowForce.Length() > 15.0f) surfaceY = limbHull.Surface;
}
surfaceY = limbHull.Surface;
if (limb.type == LimbType.Head)
{
headInWater = true;
surfaceY = limbHull.Surface;
}
}
//the limb has gone through the surface of the water
if (Math.Abs(limb.LinearVelocity.Y) > 3.0 && inWater != prevInWater)
{
//create a splash particle
Game1.particleManager.CreateParticle(
new Vector2(limb.SimPosition.X, ConvertUnits.ToSimUnits(limbHull.Surface)),
0.0f,
new Vector2(0.0f, Math.Abs(limb.LinearVelocity.Y*0.1f)),
"watersplash");
Game1.particleManager.CreateParticle(
new Vector2(limb.SimPosition.X, ConvertUnits.ToSimUnits(limbHull.Surface)),
0.0f,
limb.LinearVelocity*0.001f,
"bubbles");
//if the character dropped into water, create a wave
if (limb.LinearVelocity.Y<0.0f)
{
//1.0 when the limb is parallel to the surface of the water
// = big splash and a large impact
float parallel = (float)Math.Abs(Math.Sin(limb.Rotation));
Vector2 impulse = Vector2.Multiply(limb.LinearVelocity, -parallel * limb.Mass);
//limb.body.ApplyLinearImpulse(impulse);
int n = (int)((limbPosition.X - limbHull.Rect.X) / Hull.WaveWidth);
limbHull.WaveVel[n] = Math.Min(impulse.Y * 1.0f, 5.0f);
StrongestImpact = ((impulse.Length() * 0.5f) - limb.impactTolerance);
}
}
}
limb.Update(deltaTime);
}
}
private void UpdateNetplayerPosition()
{
Limb refLimb = GetLimb(LimbType.Torso);
if (refLimb== null) refLimb = GetLimb(LimbType.Head);
if (refLimb.body.TargetPosition == Vector2.Zero) return;
//if the limb is further away than resetdistance, all limbs are immediately snapped to their targetpositions
float resetDistance = 1.5f;
//if the limb is closer than alloweddistance, limb positions aren't updated
float allowedDistance = 0.1f;
float dist = Vector2.Distance(limbs[0].body.Position, refLimb.body.TargetPosition);
bool resetAll = (dist > resetDistance && character.largeUpdateTimer == 1);
Vector2 newMovement = (refLimb.body.TargetPosition - refLimb.body.Position);
if (newMovement == Vector2.Zero || newMovement.Length() < allowedDistance)
{
refLimb.body.TargetPosition = Vector2.Zero;
correctionMovement = Vector2.Zero;
return;
}
else
{
if (inWater)
{
foreach (Limb limb in limbs)
{
if (limb.body.TargetPosition == Vector2.Zero) continue;
limb.body.SetTransform(limb.SimPosition + newMovement * 0.1f, limb.Rotation);
}
}
else
{
correctionMovement = Vector2.Normalize(newMovement) * ((targetMovement == Vector2.Zero) ? 1.0f : targetMovement.Length());
}
}
if (resetAll)
{
System.Diagnostics.Debug.WriteLine("resetall");
foreach (Limb limb in limbs)
{
if (limb.body.TargetPosition == Vector2.Zero) continue;
limb.body.LinearVelocity = limb.body.TargetVelocity;
limb.body.AngularVelocity = limb.body.TargetAngularVelocity;
limb.body.SetTransform(limb.body.TargetPosition, limb.body.TargetRotation);
limb.body.TargetPosition = Vector2.Zero;
}
}
}
private Vector2 GetFlowForce()
{
Vector2 limbPos = ConvertUnits.ToDisplayUnits(limbs[0].SimPosition);
Vector2 force = Vector2.Zero;
foreach (MapEntity e in MapEntity.mapEntityList)
{
Gap gap = e as Gap;
if (gap == null || gap.FlowTargetHull!=currentHull ||gap.FlowForce == Vector2.Zero) continue;
Vector2 gapPos = gap.SimPosition;
float dist = Vector2.Distance(limbPos, gapPos);
force += Vector2.Normalize(gap.FlowForce)*(Math.Max(gap.FlowForce.Length() - dist, 0.0f)/500.0f);
}
if (force.Length() > 20.0f) return force;
return force;
}
public Limb GetLimb(LimbType limbType)
{
Limb limb = null;
limbDictionary.TryGetValue(limbType, out limb);
return limb;
}
public void FindLowestLimb()
{
//find the lowest limb
lowestLimb = null;
foreach (Limb limb in limbs)
{
if (lowestLimb == null)
lowestLimb = limb;
else if (limb.SimPosition.Y < lowestLimb.SimPosition.Y)
lowestLimb = limb;
}
}
public void Remove()
{
foreach (Limb l in limbs) l.Remove();
foreach (RevoluteJoint joint in limbJoints)
{
Game1.world.RemoveJoint(joint);
}
}
}
}

View File

@@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Xml.Linq;
namespace Subsurface
{
class StatusEffect
{
[Flags]
public enum Target
{
This = 1, Parent = 2, Character = 4, Contained = 8, Nearby = 16
}
private Target targets;
private string[] targetNames;
public string[] propertyNames;
private object[] propertyEffects;
private string[] onContainingNames;
public readonly ActionType type;
private Explosion explosion;
private Sound sound;
public Target Targets
{
get { return targets; }
}
public string[] TargetNames
{
get { return targetNames; }
}
public string[] OnContainingNames
{
get { return onContainingNames; }
}
public static StatusEffect Load(XElement element)
{
if (element.Attribute("delay")!=null)
{
return new DelayedEffect(element);
}
else
{
return new StatusEffect(element);
}
}
protected StatusEffect(XElement element)
{
IEnumerable<XAttribute> attributes = element.Attributes();
List<XAttribute> propertyAttributes = new List<XAttribute>();
foreach (XAttribute attribute in attributes)
{
switch (attribute.Name.ToString())
{
case "type":
try
{
type = (ActionType)Enum.Parse(typeof(ActionType), attribute.Value, true);
}
catch
{
string[] split = attribute.Value.Split('=');
type = (ActionType)Enum.Parse(typeof(ActionType), split[0], true);
string[] containingNames = split[1].Split(',');
onContainingNames = new string[containingNames.Count()];
for (int i =0; i < containingNames.Count(); i++)
{
onContainingNames[i] = containingNames[i].Trim();
}
}
break;
case "target":
string[] Flags = attribute.Value.Split(',');
foreach (string s in Flags)
{
targets |= (Target)Enum.Parse(typeof(Target), s, true);
}
break;
case "targetnames":
string[] names = attribute.Value.Split(',');
targetNames = new string[names.Count()];
for (int i=0; i < names.Count(); i++ )
{
targetNames[i] = names[i].Trim();
}
break;
case "sound":
sound = Sound.Load(attribute.Value.ToString());
break;
default:
propertyAttributes.Add(attribute);
break;
}
}
int count = propertyAttributes.Count();
propertyNames = new string[count];
propertyEffects = new object[count];
int n = 0;
foreach (XAttribute attribute in propertyAttributes)
{
propertyNames[n] = attribute.Name.ToString().ToLower();
propertyEffects[n] = ToolBox.GetAttributeObject(attribute);
n++;
}
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLower())
{
case "explosion":
explosion = new Explosion(subElement);
break;
}
}
//oxygen = ToolBox.GetAttributeFloat(element, "oxygen", 0.0f);
//deteriorateOnActive = ToolBox.GetAttributeFloat(element, "deteriorateonactive", 0.0f);
//deteriorateOnUse = ToolBox.GetAttributeFloat(element, "deteriorateonuse", 0.0f);
}
public virtual void Apply(ActionType type, float deltaTime, Item item, Character character = null, Limb limb = null)
{
if (this.type == type) Apply(deltaTime, character, item);
}
protected virtual void Apply(float deltaTime, Character character, Item item, Limb limb = null)
{
if (explosion != null) explosion.Explode(item.SimPosition);
if (sound != null) sound.Play(1.0f, 1000.0f, item.body.FarseerBody);
for (int i = 0; i < propertyNames.Count(); i++)
{
ObjectProperty property;
if (character!=null && character.properties.TryGetValue(propertyNames[i], out property))
{
ApplyToProperty(property, propertyEffects[i], deltaTime);
}
if (item == null) continue;
if (item.properties.TryGetValue(propertyNames[i], out property))
{
ApplyToProperty(property, propertyEffects[i], deltaTime);
}
foreach (ItemComponent ic in item.components)
{
if (!ic.properties.TryGetValue(propertyNames[i], out property)) continue;
ApplyToProperty(property, propertyEffects[i], deltaTime);
}
}
}
protected void ApplyToProperty(ObjectProperty property, object value, float deltaTime)
{
Type type = value.GetType();
if (type == typeof(float))
{
property.TrySetValue((float)property.GetValue() + (float)value * deltaTime);
}
if (type == typeof(int))
{
property.TrySetValue((int)property.GetValue() + (int)value * deltaTime);
}
else if (type == typeof(bool))
{
property.TrySetValue((bool)value);
}
else if (type == typeof(string))
{
property.TrySetValue((string)value);
}
}
public static void UpdateAll(float deltaTime)
{
for (int i = DelayedEffect.list.Count-1; i>= 0; i--)
{
DelayedEffect.list[i].Update(deltaTime);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8" ?>
<character name ="crawler" humanoid="false">
<ragdoll headposition="50" headangle="-70"
waveamplitude="50.0" wavelength="2500"
swimspeed="2.0" walkspeed="2.0"
stepsize ="15.0,20.0"
stepoffset="20.0,0.0"
legtorque="10"
flip="true">
<!-- head -->
<limb id = "0" radius="22" height="45" mass = "6" type="Head" flip="true" steerforce="1.0">
<sprite texture="Content/Characters/Crawler/crawler.png" sourcerect="0,0,51,121" depth="0.02" origin="0.45,0.63"/>
</limb>
<!-- middle part -->
<limb id = "1" width="36" height="40" mass = "6" flip="true">
<sprite texture="Content/Characters/Crawler/crawler.png" sourcerect="57,7,59,57" depth="0.021" origin="0.3,0.65"/>
</limb>
<!-- tail -->
<limb id = "2" width="41" height="63" mass = "6" type="Tail" flip="true">
<sprite texture="Content/Characters/Crawler/crawler.png" sourcerect="6,141,60,95" depth="0.022" origin="0.37,0.43"/>
</limb>
<limb id = "3" width="13" height="45" mass = "6" ignorecollisions="true" flip="true">
<sprite texture="Content/Characters/Crawler/crawler.png" sourcerect="65,131,36,50" depth="0.15" origin="0.4,0.5"/>
<attack type="PinchCW" range="120" duration="0.5" damage="30" stun="0.1" bleedingdamage="20" structuredamage="80" damagetype="slash"/>
</limb>
<limb id = "4" width="11" height="34" mass = "4" type="RightLeg" flip="true">
<sprite texture="Content/Characters/Crawler/crawler.png" sourcerect="82,83,11,34" depth="0.03" origin="0.5,0.5"/>
</limb>
<limb id = "5" width="5" height="40" mass = "4" type="RightFoot" flip="true" pullpos="0.0,15.0" refjoint="3">
<sprite texture="Content/Characters/Crawler/crawler.png" sourcerect="59,78,19,43" depth="0.03" origin="0.5,0.5"/>
<sound file ="Content/Sounds/stepMetal.ogg"/>
</limb>
<limb id = "6" width="13" height="35" mass = "4" type="LeftLeg" flip="true">
<sprite texture="Content/Characters/Crawler/crawler.png" sourcerect="82,83,11,34" depth="0.03" origin="0.5,0.5"/>
</limb>
<limb id = "7" width="5" height="40" mass = "4" type="LeftFoot" flip="true" pullpos="0.0,15.0" refjoint="5">
<sprite texture="Content/Characters/Crawler/crawler.png" sourcerect="59,78,19,43" depth="0.03" origin="0.5,0.5"/>
<sound file ="Content/Sounds/stepMetal.ogg"/>
</limb>
<limb id = "8" width="13" height="35" mass = "4" type="RightLeg" flip="true">
<sprite texture="Content/Characters/Crawler/crawler.png" sourcerect="82,83,11,34" depth="0.03" origin="0.5,0.5"/>
</limb>
<limb id = "9" width="5" height="40" mass = "4" type="RightFoot" flip="true" pullpos="0.0,15.0" refjoint="7">
<sprite texture="Content/Characters/Crawler/crawler.png" sourcerect="59,78,19,43" depth="0.03" origin="0.5,0.5"/>
<sound file ="Content/Sounds/stepMetal.ogg"/>
</limb>
<joint limb1="0" limb1anchor="-5,-38" limb2="1" limb2anchor="-2,25" lowerlimit="-20" upperlimit="40"/>
<joint limb1="1" limb1anchor="0,-15" limb2="2" limb2anchor="-10,31" lowerlimit="-20" upperlimit="40"/>
<joint limb1="0" limb1anchor="7,30" limb2="3" limb2anchor="-2,-17" lowerlimit="-180" upperlimit="-90"/>
<joint limb1="0" limb1anchor="13,6" limb2="4" limb2anchor="0,14" lowerlimit="-270" upperlimit="-180"/>
<joint limb1="4" limb1anchor="0,-17" limb2="5" limb2anchor="0,-17" lowerlimit="-350" upperlimit="-190"/>
<joint limb1="0" limb1anchor="13,-15" limb2="6" limb2anchor="0,14" lowerlimit="-270" upperlimit="-180"/>
<joint limb1="6" limb1anchor="0,-17" limb2="7" limb2anchor="0,-17" lowerlimit="-350" upperlimit="-190"/>
<joint limb1="0" limb1anchor="13,-35" limb2="8" limb2anchor="0,14" lowerlimit="-270" upperlimit="-180"/>
<joint limb1="8" limb1anchor="0,-17" limb2="9" limb2anchor="0,-17" lowerlimit="-350" upperlimit="-190"/>
</ragdoll>
<ai attackhumans="100" attackrooms="50.0" attackweaker="50" attackstronger="-30"
sight="0.5" hearing="1.0"
attackcooldown="5.0"/>
</character>

View File

@@ -0,0 +1,171 @@
Albertine
Alfredia
Alita
Alla
Alta
Althea
Alyce
Amee
Annette
Ara
Arcelia
Arie
Armanda
Ashly
Astrid
Avril
Barbra
Beaulah
Bell
Bertha
Bettyann
Beverley
Bridgett
Candace
Carie
Carla
Cathleen
Cecille
Christiana
Christine
Cinda
Cleopatra
Codi
Corinne
Creola
Criselda
Dahlia
Danielle
Dannette
Darleen
Deborah
Denise
Denisha
Dona
Doria
Dorthy
Eleanor
Elinor
Emerald
Erline
Ethel
Eugenia
Fanny
Fernanda
Glenda
Hortensia
Ilene
Ina
Inga
Iris
Jacquline
Janee
Janessa
Janie
Jeanene
Jenine
Jina
Jodee
Joy
Joyce
Juana
Julianne
Jutta
Karyn
Katerina
Katherina
Kathy
Kellie
Keri
Kizzie
Kristeen
Kristie
Larissa
Larita
Laurie
Laurinda
Lavinia
Leanne
Leila
Lelah
Leola
Lindy
Liza
Lola
Lora
Lorriane
Lorrie
Mafalda
Maira
Majorie
Marcy
Margeret
Mariela
Marin
Marissa
Marla
Maryann
Marylee
Masako
Maudie
Maybell
Mechelle
Melany
Melba
Michele
Mila
Miranda
Muoi
Natalia
Natashia
Nola
Noriko
Page
Paula
Peggie
Randi
Reatha
Renata
Rhonda
Roberta
Roselle
Rosina
Roslyn
Rowena
Ruthie
Sabrina
Sage
Sanda
Sara
Serena
Sharie
Shayla
Shelly
Sherise
Sherita
Sherry
Shin
Shirlee
Socorro
Stefany
Stephane
Susy
Synthia
Tania
Tanika
Tanya
Tawanda
Tera
Tessie
Thea
Tisha
Tracy
Trista
Trudie
Trudy
Valerie
Vanessa
Velma
Yahaira
Zandra
Zoe

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,206 @@
Abe
Adam
Adan
Alan
Aldo
Allan
Alonso
Alton
Alvaro
Andres
Anton
Antony
Arkadi
Arron
Arturo
Austin
Avery
Barney
Barry
Barton
Ben
Bennie
Bertram
Bill
Boyce
Boyd
Brady
Brain
Britt
Bruno
Bryant
Bryce
Burl
Burt
Carlos
Carlton
Carmen
Carson
Chance
Christoper
Chuck
Claude
Coleman
Dan
Darius
Darrin
Darwin
David
Delbert
Devin
Dewey
Dewitt
Dimitry
Dominick
Donn
Dorsey
Edgar
Edison
Eldridge
Elmer
Erich
Erik
Ernie
Everette
Ezequiel
Filiberto
Frances
Franklin
Freeman
Fritz
Garrett
Gerard
Hal
Harlan
Harris
Harry
Hector
Herman
Hobert
Irwin
Ivan
Jackie
Jaime
Jamey
Jan
Jared
Jarrod
Jarvis
Jc
Jean
Jerald
Jeremiah
Jerrell
Jerrod
Jess
Joe
John
Jonathan
Josef
Joseph
Joshua
Jude
Keith
Kendall
Keneth
Kenton
Kerry
Keven
Kim
Kip
Lamont
Lane
Lanny
Lee
Len
Lenny
Lionel
Lynn
Lynwood
Malcom
Manuel
Marc
Marcel
Marcelino
Marcellus
Marco
Mathew
Matt
Maurice
Mauricio
Mckinley
Mechislav
Merrill
Micheal
Milton
Minh
Mitchel
Mitrofan
Mohammad
Monte
Morton
Mose
Murray
Neal
Neil
Nolan
Norman
Orval
Oscar
Owen
Pete
Peter
Quincy
Ramon
Randall
Raphael
Raymon
Reed
Reggie
Reginald
Renato
Rene
Richard
Rob
Roderick
Roland
Romeo
Rosendo
Roy
Rupert
Sammie
Santiago
Saul
Sebastian
Sergei
Seth
Shelby
Sidney
Son
Sonny
Steve
Stevie
Sylvester
Tad
Terrance
Terrell
Timur
Tod
Todd
Tommie
Tory
Travis
Tyler
Tyson
Ulof
Virgil
Virgilio
Waldo
Walker
Warner
Wilford
Will
Williams
Willy
Winston
Zachary

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8" ?>
<character name ="human" humanoid="true" needsair="true" genders="true" drowningtime="30">
<name firstname="Content/Characters/Human/[GENDER]firstnames.txt" lastname="Content/Characters/Human/lastnames.txt" />
<ragdoll headposition="150" torsoposition="110"
stepsize="35.0, 10.0">
<!-- head -->
<limb id = "0" radius="13" mass = "6" type="Head" attackpriority="2" impacttolerance="3.0">
<sprite texture="Content/Characters/Human/[GENDER]head.png" sourcerect="1,1,37,38" depth="0.12" origin="0.5,0.5"/>
</limb>
<!-- body -->
<limb id = "1" width="26" height="30" mass = "20" type="Torso" attackpriority="3" impacttolerance="5.0">
<sprite texture="Content/Characters/Human/[GENDER]torso.png" sourcerect="32,0,36,39" depth="0.045" origin="0.5,0.5"/>
</limb>
<limb id = "12" width="26" height="30" mass = "20" type="Waist" impacttolerance="5.0">
<sprite texture="Content/Characters/Human/[GENDER]torso.png" sourcerect="35,30,37,31" depth="0.05" origin="0.5,0.5"/>
</limb>
<!-- left arm -->
<limb id = "2" width="12" height="30" mass="3" type="LeftArm">
<sprite texture="Content/Characters/Human/[GENDER]torso.png" sourcerect="0,0,16,34" depth="0.15" origin="0.5,0.5"/>
</limb>
<limb id = "3" width="12" height="30" mass="3" type="LeftHand" pullpos="0.0,-15.0">
<sprite texture="Content/Characters/Human/[GENDER]torso.png" sourcerect="16,0,16,49" depth="0.16" origin="0.5,0.5"/>
</limb>
<!-- right arm -->
<limb id = "4" width="12" height="30" mass="3" type="RightArm">
<sprite texture="Content/Characters/Human/[GENDER]torso.png" sourcerect="0,0,16,34" origin="0.5,0.5"/>
</limb>
<limb id = "5" width="12" height="30" mass ="3" type="RightHand" pullpos="0.0,-15.0">
<sprite texture="Content/Characters/Human/[GENDER]torso.png" sourcerect="16,0,16,49" origin="0.5,0.5"/>
</limb>
<!-- left leg -->
<limb id = "6" width="12" height="30" mass = "8" type="LeftThigh">
<sprite texture="Content/Characters/Human/[GENDER]legs.png" sourcerect="0,0,24,47" depth="0.16" origin="0.5,0.5"/>
</limb>
<limb id = "7" width="12" height="30" mass = "5" type="LeftLeg">
<sprite texture="Content/Characters/Human/[GENDER]legs.png" sourcerect="24,1,18,39" depth="0.15" origin="0.5,0.5"/>
</limb>
<limb id = "8" width="6" height="16" mass = "2" type ="LeftFoot" flip="true" pullpos="-3.0,0.0">
<sprite texture="Content/Characters/Human/[GENDER]legs.png" sourcerect="43,4,15,26" depth="0.14" origin="0.5,0.5"/>
<sound file ="Content/step.ogg"/>
</limb>
<!-- right leg -->
<limb id = "9" width="12" height="30" mass = "8" type="RightThigh">
<sprite texture="Content/Characters/Human/[GENDER]legs.png" sourcerect="1,1,24,47" depth="0.12" origin="0.5,0.5"/>
</limb>
<limb id = "10" width="12" height="30" mass = "5" type="RightLeg">
<sprite texture="Content/Characters/Human/[GENDER]legs.png" sourcerect="24,1,18,39" depth="0.11" origin="0.5,0.5"/>
</limb>
<limb id = "11" width="6" height="16" mass = "2" type ="RightFoot" flip="true" pullpos="-3.0,0.0">
<sprite texture="Content/Characters/Human/[GENDER]legs.png" sourcerect="43,4,15,26" depth="0.1" origin="0.5,0.5"/>
<sound file ="Content/step.ogg"/>
</limb>
<!-- head to body -->
<joint limb1="0" limb1anchor="0,-13" limb2="1" limb2anchor="0,10" lowerlimit="-90" upperlimit="45"/>
<joint limb1="1" limb1anchor="0,-10" limb2="12" limb2anchor="0,10" lowerlimit="-40" upperlimit="40"/>
<!-- body to left arm -->
<joint limb1="1" limb1anchor="-5,0" limb2="2" limb2anchor="0,10"/>
<joint limb1="2" limb1anchor="2,-14" limb2="3" limb2anchor="0,19" lowerlimit="0" upperlimit="170"/>
<!-- body to right arm -->
<joint limb1="1" limb1anchor="-5,0" limb2="4" limb2anchor="0,10"/>
<joint limb1="4" limb1anchor="2,-14" limb2="5" limb2anchor="0,19" lowerlimit="0" upperlimit="170"/>
<!-- body to left leg -->
<joint limb1="12" limb1anchor="0,-10" limb2="6" limb2anchor="0,14" lowerlimit="-30" upperlimit="140"/>
<joint limb1="6" limb1anchor="0,-15" limb2="7" limb2anchor="0,14" lowerlimit="-170" upperlimit="0"/>
<joint limb1="7" limb1anchor="0,-17" limb2="8" limb2anchor="5,9" lowerlimit="70" upperlimit="135"/>
<!-- body to right leg -->
<joint limb1="12" limb1anchor="0,-10" limb2="9" limb2anchor="0,14" lowerlimit="-30" upperlimit="140"/>
<joint limb1="9" limb1anchor="0,-15" limb2="10" limb2anchor="0,14" lowerlimit="-170" upperlimit="0"/>
<joint limb1="10" limb1anchor="0,-17" limb2="11" limb2anchor="5,9" lowerlimit="70" upperlimit="135"/>
</ragdoll>
</character>

View File

@@ -0,0 +1,157 @@
Aker
Angles
Atterbury
Bartolomeo
Basnett
Baumgardner
Bergman
Berlin
Berner
Blackwell
Blakley
Boman
Bonham
Books
Box
Breeki
Bushman
Callahan
Callanan
Carey
Celina Ryder
Chandler
Chaney
Chapman
Chiu
Clothier
Cobble
Colpitts
Combs
Connors
Cork
Cortese
Danko
Day
Delange
Deloney
Deras
Dews
Dimauro
Dollinger
Dorado
Dudley
Duplantier
Eastman
Feinberg
Feinman
Fesler
Fleig
Flowers
Fonte
Frye
Gadberry
Gallagher
Gallardo
Gears
Geist
Gonzales
Goodin
Gumbs
Hajek
Hallett
Hardy
Harp
Hebert
Hedrick
Hefner
Heuer
Hillard
Hines
Hocker
Holter
House
Jack
Jones
Keller
Kinney
Klein
Knowles
Krall
Lane
Lavey
Leonard
Lester
Linares
Lowe
Malone
Mansfield
Marchi
Mason
Maynard
Maynes
McLeroy
McNeal
Mead
Meals
Meisner
Mell
Mendel
Mulford
Mungo
Navarro
Newton
Nicolosi
Ordway
Ouellette
Palmieri
Parchman
Parker
Parks
Petersen
Phoenix
Pickett
Planck
Pool
Poole
Price
Radebaugh
Radice
Ram
Rhymer
Riddle
Riles
Robertson
Rolfe
Rosewood
Sacks
Santillan
Santistevan
Sawyer
Severance
Smith
Southworth
Sparks
Stalvey
Stanley
Stein
Stogner
Summitt
Sunderland
Swanger
Swanson
Sykes
Tamashiro
Tate
Thompkins
Tootle
Tseng
Tyson
Ulrich
Vaughn
Veach
Velazquez
Welden
Wheeling
Whitely
Wiley
Williams

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<Jobs>
<Captain description="The Commanding Officer with authority over the entire crew.">
<Item name="ID Card"/>
</Captain>
<Engineer>
<Item name="ID Card"/>
<Item name="Wrench"/>
<Item name="Screwdriver"/>
</Engineer>
<Mechanic>
<Item name="ID Card"/>
<Item name="Wrench"/>
<Item name="Screwdriver"/>
</Mechanic>
</Jobs>

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8" ?>
<character name ="scorpion" humanoid="false">
<sound file="Content/Characters/Scorpion/scorpionattack1.ogg" state="Attack" />
<sound file="Content/Characters/Scorpion/scorpionidle1.ogg" state="None" />
<sound file="Content/Characters/Scorpion/scorpionidle2.ogg" state="None" />
<ragdoll waveamplitude="50.0" wavelength="2500" speed="1.5">
<!-- head -->
<limb id = "0" radius="37" height="81" mass="6" type="Head" steerforce="1.0">
<sprite texture="Content/Characters/Scorpion/scorpion.png" sourcerect="11,12,74,155" depth="0.02"/>
</limb>
<!-- body -->
<limb id = "1" radius="33" height="51" mass = "40">
<sprite texture="Content/Characters/Scorpion/scorpion.png" sourcerect="94,32,66,117" depth="0.03" origin="0.5,0.5"/>
</limb>
<limb id = "2" radius="30" height="60" mass = "40" type="Torso">
<sprite texture="Content/Characters/Scorpion/scorpion.png" sourcerect="94,32,66,117" depth="0.03" origin="0.5,0.5"/>
</limb>
<limb id = "3" radius="30" height="60" mass = "40" type="Torso">
<sprite texture="Content/Characters/Scorpion/scorpion.png" sourcerect="94,32,66,117" depth="0.03" origin="0.5,0.5"/>
</limb>
<limb id = "4" radius="30" height="60" mass = "40" type="Tail">
<sprite texture="Content/Characters/Scorpion/scorpion.png" sourcerect="94,32,66,117" depth="0.03" origin="0.5,0.5"/>
</limb>
<limb id = "5" width="26" height="98" mass = "6" ignorecollisions="true">
<sprite texture="Content/Characters/Scorpion/scorpion.png" sourcerect="190,30,49,98" depth="0.15" origin="0.3,0.5"/>
<attack type="PinchCW" range="120" duration="0.5" damage="80"/>
</limb>
<!-- head to body -->
<joint limb1="0" limb1anchor="0,60" limb2="1" limb2anchor="0,-45" lowerlimit="-10" upperlimit="45"/>
<joint limb1="1" limb1anchor="0,45" limb2="2" limb2anchor="0,-45" lowerlimit="-10" upperlimit="45"/>
<joint limb1="2" limb1anchor="0,45" limb2="3" limb2anchor="0,-45" lowerlimit="-10" upperlimit="45"/>
<joint limb1="3" limb1anchor="0,45" limb2="4" limb2anchor="0,-45" lowerlimit="-10" upperlimit="45"/>
<joint limb1="0" limb1anchor="-10,-65" limb2="5" limb2anchor="10,40" lowerlimit="200" upperlimit="240"/>
</ragdoll>
<ai attackhumans="100" attackrooms="50.0" attackweaker="50" attackstronger="-30" sight="0.5" hearing="1.0"/>
</character>

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8" ?>
<character name ="tigerthresher" humanoid="false">
<sound file="Content/Characters/Scorpion/scorpionattack1.ogg" state="Attack" />
<sound file="Content/Characters/Scorpion/scorpionidle1.ogg" state="None" />
<sound file="Content/Characters/Scorpion/scorpionidle2.ogg" state="None" />
<ragdoll waveamplitude="50.0" wavelength="2500" swimspeed="4.0" walkspeed="3.5">
<!-- head -->
<limb id = "0" radius="12" height="100" type="Head" steerforce="1.0">
<sprite texture="Content/Characters/TigerThresher/tigerthresher.png" sourcerect="371,15,66,136" depth="0.02" origin ="0.5,0.5"/>
</limb>
<!-- lower yaw -->
<limb id = "1" width="16" height="103">
<sprite texture="Content/Characters/TigerThresher/tigerthresher.png" sourcerect="391,169,28,110" depth="0.025" origin="0.5,0.5"/>
<attack type="PinchCCW" range="200" duration="0.5" damage="80" bleedingdamage="50" structuredamage="100" damagetype="slash"/>
</limb>
<!-- body -->
<limb id = "2" radius="50" height="320" type="Torso" steerforce="0.1">
<sprite texture="Content/Characters/TigerThresher/tigerthresher.png" sourcerect="230,0,136,442" depth="0.015" origin="0.6,0.5"/>
</limb>
<!-- tail -->
<limb id = "3" radius="10" height="320" type="Tail">
<sprite texture="Content/Characters/TigerThresher/tigerthresher.png" sourcerect="9,0,79,344" depth="0.02" origin="0.35,0.5"/>
</limb>
<!-- front fins -->
<limb id = "4" width="50" height="200">
<sprite texture="Content/Characters/TigerThresher/tigerthresher.png" sourcerect="133,5,91,202" depth="0.01" origin="0.7,0.5"/>
</limb>
<!-- back fins -->
<limb id = "5" width="25" height="100">
<sprite texture="Content/Characters/TigerThresher/tigerthresher.png" sourcerect="387,304,38,128" depth="0.01" origin="0.7,0.5"/>
</limb>
<!-- long "fins" -->
<limb id = "6" width="10" height="270" ignorecollisions="true">
<sprite texture="Content/Characters/TigerThresher/tigerthresher.png" sourcerect="96,2,23,277" depth="0.01" origin="0.5,0.5"/>
</limb>
<!-- body to head -->
<joint limb1="0" limb1anchor="-22,-63" limb2="2" limb2anchor="-36,165" lowerlimit="-10" upperlimit="10"/>
<!-- body to lower yaw -->
<joint limb1="1" limb1anchor="0,-39" limb2="2" limb2anchor="20,194" lowerlimit="-10" upperlimit="30"/>
<!-- body to tail -->
<joint limb1="2" limb1anchor="-14,-197" limb2="3" limb2anchor="3,163" lowerlimit="-20" upperlimit="20"/>
<!-- body to front fin -->
<joint limb1="2" limb1anchor="12,93" limb2="4" limb2anchor="6,89" lowerlimit="-50" upperlimit="0"/>
<!-- body to back fin -->
<joint limb1="2" limb1anchor="5,-30" limb2="5" limb2anchor="7,57" lowerlimit="-50" upperlimit="0"/>
<!-- body to long -->
<joint limb1="2" limb1anchor="-5,-137" limb2="6" limb2anchor="-8,132" lowerlimit="-30" upperlimit="0"/>
</ragdoll>
<ai attackhumans="100.0" attackrooms="50.0" attackweaker="50" attackstronger="-30" sight="0.5" hearing="1.0"/>
</character>

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<character name ="shrimp" humanoid="false" waveamplitude="0.5" wavelength="80">
<ragdoll texture ="Content/Characters/panzershrimp.png">
<!-- head -->
<limb id = "0" radius="50" length = "300" mass = "6" type="Head">
<sprite sourcex="6" sourcey="30" sourcewidth="251" sourceheight="242" depth="0.1"/>
</limb>
<!-- body -->
<limb id = "1" radius="50" mass = "40" type="Torso">
<sprite sourcex="373" sourcey="6" sourcewidth="122" sourceheight="208" depth="0.05"/>
</limb>
<!-- left arm -->
<limb id = "2" radius="45" height="153" mass="2">
<sprite sourcex="252" sourcey="22" sourcewidth="115" sourceheight="227" depth="0.06"/>
</limb>
<limb id = "3" radius="45" height="205" mass="3">
<sprite sourcex="359" sourcey="265" sourcewidth="109" sourceheight="216" depth="0.07"/>
</limb>
<!-- right arm -->
<limb id = "4" radius="40" height="175" mass="3" type="Torso">
<sprite sourcex="237" sourcey="302" sourcewidth="92" sourceheight="175" depth="0.1"/>
</limb>
<!-- head to body -->
<joint limb1="0" limb1y="80" limb2="1" limb2x="-30" limb2y="-20" lowerlimit="0" upperlimit="40"/>
<joint limb1="1" limb1y="16" limb2="2" limb2y="-50" lowerlimit="5" upperlimit="60"/>
<joint limb1="2" limb1y="20" limb2="3" limb2y="-70" lowerlimit="10" upperlimit="60"/>
<joint limb1="3" limb1y="60" limb2="4" limb2y="-50" lowerlimit="20" upperlimit="60"/>
</ragdoll>
<ai attackhumans="100" attackrooms="50.0" attackweaker="50" attackstronger="-30" sight="0.5" hearing="1.0"/>
</character>

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<style
smallpadding="20.0, 20.0, 20.0, 20.0"
largepadding="40.0, 40.0, 40.0, 40.0"
backgroundcolor="0.2, 0.2, 0.2, 0.8"
foregroundcolor="1.0, 1.0, 1.0, 1.0"
textcolor="0.0, 0.0, 0.0, 1.0"
hovercolor="0.8, 0.8, 0.8, 1.0"
selectedcolor="1.0, 0.82, 0.05, 1.0"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,18 @@
<Item
name="Button"
type="Controller"
linkable="true"
pickdistance="150.0">
<Sprite texture ="button.png" depth="0.8"/>
<Controller userpos="0" direction ="None" canbepicked = "true">
<RequiredItem name="ID Card" type="Picked"/>
</Controller>
<ConnectionPanel canbeselected = "true">
<RequiredItem name="Screwdriver" type="Equipped"/>
<output name="signal_out"/>
</ConnectionPanel>
</Item>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8" ?>
<Items>
<Item
name="Diving Mask"
Tags="smallitem"
pickdistance="200">
<Sprite texture ="DivingMask.png" depth="0.4"/>
<Body radius="18" density="5"/>
<Wearable limbtype="Head" slots="Any,Head">
<sprite texture="DivingMask.png" limb="Head" sourcerect="1,1,37,38"/>
<StatusEffect type="OnWearing" target="Contained,Character" targetnames="Oxygen Tank" Condition="-0.1" Oxygen="20.0"/>
</Wearable>
<ItemContainer capacity="1" hideitems="true">
<Containable name="Oxygen Tank"/>
</ItemContainer>
</Item>
<Item
name="Diving Suit"
pickdistance="200">
<Sprite texture ="DivingSuit.png" sourcerect="82,15,46,113" depth="0.4"/>
<Body width="37" height="113" density="5"/>
<Wearable slots="Head,Torso">
<sprite texture="DivingSuit.png" limb="Head" sourcerect="1,94,32,33" origin="0.5,0.5" depth="0.02"/>
<sprite texture="DivingSuit.png" limb="Torso" sourcerect="40,0,38,65" origin="0.5,0.33" depth="0.01"/>
<sprite texture="DivingSuit.png" limb="RightHand" sourcerect="21,0,18,50" origin="0.45,0.4"/>
<sprite texture="DivingSuit.png" limb="LeftHand" sourcerect="21,0,18,50" origin="0.45,0.4"/>
<sprite texture="DivingSuit.png" limb="RightArm" sourcerect="0,0,18,40" origin="0.5,0.4" depth="0.005"/>
<sprite texture="DivingSuit.png" limb="LeftArm" sourcerect="0,0,18,40" origin="0.5,0.4" depth="0.005"/>
<StatusEffect type="OnWearing" target="Contained,Character" targetnames="Oxygen Tank" Condition="-0.1" Oxygen="20.0"/>
<StatusEffect type="OnWearing" target="Character" PressureProtection="100.0"/>
</Wearable>
<ItemContainer capacity="1" hideitems="true">
<Containable name="Oxygen Tank"/>
</ItemContainer>
</Item>
</Items>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -0,0 +1,41 @@
<Items>
<Item
name="Door"
linkable="true"
pickdistance="150.0">
<Sprite texture ="doorframe.png" depth="0.8" origin="0.5,0.5"/>
<Door>
<Sprite texture ="door.png" depth="0.4" origin="0.5,0.0"/>
</Door>
<ConnectionPanel canbeselected = "true">
<requireditem name="Screwdriver" type="Equipped"/>
<input name="toggle"/>
<input name="set_state"/>
<output name="state_out"/>
</ConnectionPanel>
</Item>
<Item
name="Windowed Door"
linkable="true"
pickdistance="150.0">
<Sprite texture ="doorframe.png" depth="0.8" origin="0.5,0.5"/>
<Door window="0,-18,10,89">
<Sprite texture ="windowedDoor.png" depth="0.4" origin="0.5,0.0"/>
</Door>
<ConnectionPanel canbeselected = "true">
<requireditem name="Screwdriver" type="Equipped"/>
<input name="toggle"/>
<input name="set_state"/>
<output name="state_out"/>
</ConnectionPanel>
</Item>
</Items>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -0,0 +1,11 @@
<Item
name="Ladder"
resizevertical="true">
<Sprite texture ="ladder.png" depth="0.05"/>
<Ladder canbeselected = "true"/>
<trigger x="-40" width="90"/>
</Item>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,14 @@
<Items>
<Item
name="Steel Cabinet"
linkable="true"
pickdistance ="150">
<Sprite texture ="locker.png" depth="0.85"/>
<ItemContainer capacity="20" canbeselected="true" hideitems="true">
<Containable name="smallitem"/>
</ItemContainer>
</Item>
</Items>

View File

@@ -0,0 +1,11 @@
<Item
name="MiniMap"
linkable="true">
<Sprite texture ="reactor.png" depth="0.8"/>
<trigger/>
<MiniMap MinVoltage="0.5" PowerConsumption="100" canbeselected = "true"/>
</Item>

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

View File

@@ -0,0 +1,23 @@
<Item
name="Oxygen Generator"
linkable="true">
<Sprite texture ="battery.png" depth="0.8"/>
<OxygenGenerator powerconsumption="2000.0" minvoltage="0.5" canbeselected = "true">
<StatusEffect type="OnActive" target="Contained" targetnames="Oxygen Tank" Condition="1.0"/>
</OxygenGenerator>
<trigger/>
<ItemContainer capacity="5" canbeselected = "true">
<Containable name="Oxygen Tank"/>
</ItemContainer>
<ConnectionPanel canbeselected = "true">
<requireditem name="Screwdriver" type="Equipped"/>
<input name="power_in"/>
</ConnectionPanel>
</Item>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<Item
name="Oxygen Tank"
Tags="smallitem"
pickdistance="150">
<Sprite texture ="oxygentank.png" depth="0.05"/>
<Body radius="6" height="22" density="5"/>
<Holdable holdpos="30,-15" handle1="0,5" handle2="0,-5"/>
<Pickable slots="RightHand,Any"/>
</Item>

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

View File

@@ -0,0 +1,16 @@
<Item
name="Pump"
linkable="true"
pickdistance="200">
<Sprite texture ="pump.png" depth="0.08"/>
<Pump maxflow="500" PowerConsumption="300.0" MinVoltage="0.3"/>
<ConnectionPanel canbeselected = "true">
<requireditem name="Screwdriver" type="Equipped"/>
<input name="power_in"/>
<input name="toggle"/>
<input name="set_state"/>
</ConnectionPanel>
</Item>

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

View File

@@ -0,0 +1,180 @@
<?xml version="1.0" encoding="utf-8" ?>
<Items>
<Item
name="Wire"
Tags="smallitem"
pickdistance="150"
linkable="true"
canbepicked="true">
<Sprite texture ="wire.png" depth="0.55"/>
<Body radius="15"/>
<Holdable handle1="0,0"/>
<Pickable slots="Any,RightHand,LeftHand"/>
<Wire/>
</Item>
<Item
name="And Component"
Tags="smallitem"
pickdistance="150"
linkable="true">
<Sprite texture ="and.png" depth="0.8"/>
<AndComponent canbeselected = "true"/>
<Body width="16" height="16"/>
<Holdable aimpos="35,-10" handle1="0,0" attachable="true" aimable="true"/>
<Pickable slots="Any,RightHand,LeftHand" msg="Detach [Wrench]">
<requireditem name="Wrench" type="Equipped"/>
</Pickable>
<ConnectionPanel canbeselected = "true" msg="Rewire [Screwdriver]">
<requireditem name="Screwdriver,Wire" type="Equipped"/>
<input name="signal_in1"/>
<input name="signal_in2"/>
<input name="set_output"/>
<output name="signal_out"/>
</ConnectionPanel>
</Item>
<Item
name="Or Component"
Tags="smallitem"
pickdistance="150"
linkable="true">
<Sprite texture ="or.png" depth="0.8"/>
<OrComponent canbeselected = "true"/>
<Body width="16" height="16"/>
<Holdable aimpos="35,-10" handle1="0,0" attachable="true" aimable="true"/>
<Pickable slots="Any,RightHand,LeftHand" msg="Detach [Wrench]">
<requireditem name="Wrench" type="Equipped"/>
</Pickable>
<ConnectionPanel canbeselected = "true" msg="Rewire [Screwdriver]">
<requireditem name="Screwdriver,Wire" type="Equipped"/>
<input name="signal_in1"/>
<input name="signal_in2"/>
<input name="set_output"/>
<output name="signal_out"/>
</ConnectionPanel>
</Item>
<Item
name="Not Component"
Tags="smallitem"
pickdistance="150"
linkable="true">
<Sprite texture ="not.png" depth="0.8"/>
<NotComponent canbeselected = "true"/>
<Body width="16" height="16"/>
<Holdable aimpos="35,-10" handle1="0,0" attachable="true" aimable="true"/>
<Pickable slots="Any,RightHand,LeftHand" msg="Detach [Wrench]">
<requireditem name="Wrench" type="Equipped"/>
</Pickable>
<ConnectionPanel canbeselected = "true" msg="Rewire [Screwdriver]">
<requireditem name="Screwdriver,Wire" type="Equipped"/>
<input name="signal_in"/>
<output name="signal_out"/>
</ConnectionPanel>
</Item>
<Item
name="Light Component"
Tags="smallitem"
pickdistance="150"
linkable="true">
<Sprite texture ="light.png" depth="0.8"/>
<LightComponent canbeselected = "true" color="1.0,0.0,0.0,1.0">
<sprite texture="Content/Items/Signal/lightsprite.png" origin="0.5,0.5"/>
</LightComponent>
<Body width="16" height="16"/>
<Holdable aimpos="35,-10" handle1="0,0" attachable="true" aimable="true"/>
<Pickable slots="Any,RightHand,LeftHand" msg="Detach [Wrench]">
<requireditem name="Wrench" type="Equipped"/>
</Pickable>
<ConnectionPanel canbeselected = "true" msg="Rewire [Screwdriver]">
<requireditem name="Screwdriver,Wire" type="Equipped"/>
<input name="toggle"/>
<input name="set_state"/>
</ConnectionPanel>
</Item>
<Item
name="Oxygen Detector"
Tags="smallitem"
pickdistance="150"
linkable="true">
<Sprite texture ="light.png" depth="0.8"/>
<OxygenDetector canbeselected = "true"/>
<Body width="16" height="16"/>
<Holdable aimpos="35,-10" handle1="0,0" attachable="true" aimable="true"/>
<Pickable slots="Any,RightHand,LeftHand" msg="Detach [Wrench]">
<requireditem name="Wrench" type="Equipped"/>
</Pickable>
<ConnectionPanel canbeselected = "true" msg="Rewire [Screwdriver]">
<requireditem name="Screwdriver,Wire" type="Equipped"/>
<output name="signal_out"/>
</ConnectionPanel>
</Item>
<Item
name="RegEx Find Component"
Tags="smallitem"
pickdistance="150"
linkable="true">
<Sprite texture ="light.png" depth="0.8"/>
<RegExFindComponent canbeselected = "true"/>
<Body width="16" height="16"/>
<Holdable aimpos="35,-10" handle1="0,0" attachable="true" aimable="true"/>
<Pickable slots="Any,RightHand,LeftHand" msg="Detach [Wrench]">
<requireditem name="Wrench" type="Equipped"/>
</Pickable>
<ConnectionPanel canbeselected = "true" msg="Rewire [Screwdriver]">
<requireditem name="Screwdriver,Wire" type="Equipped"/>
<input name="signal_in"/>
<output name="signal_out"/>
</ConnectionPanel>
</Item>
</Items>

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8" ?>
<Items>
<Item
name="Welding Tool"
Tags="smallitem"
pickdistance="200">
<Sprite texture ="weldingtool.png" depth="0.04"/>
<Body width="50" height="44" density="5"/>
<Holdable holdpos="-9,-20" holdangle="-30"/>
<RepairTool range="80">
<Fixable name="structure"/>
</RepairTool>
<Pickable slots="Any,RightHand"/>
</Item>
<Item
name="Screwdriver"
Tags="smallitem"
pickdistance="200">
<Sprite texture ="screwdriver.png"/>
<Body width="30" height="8" density="20"/>
<Holdable holdangle="30" handle1="0,0"/>
<Pickable slots="Any,RightHand,LeftHand"/>
</Item>
<Item
name="Wrench"
Tags="smallitem"
pickdistance="200">
<Sprite texture ="wrench.png"/>
<Body width="30" height="8" density="20"/>
<Holdable holdangle="30" handle1="0,0"/>
<Pickable slots="Any,RightHand,LeftHand"/>
</Item>
</Items>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

View File

@@ -0,0 +1,10 @@
<Item
name="Vent"
linkable="true">
<Sprite texture ="vent.png" depth="0.8"/>
<Vent/>
</Item>

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8" ?>
<Items>
<Item
name="Harpoon"
pickdistance="200">
<Sprite texture ="harpoon.png" depth="0.55"/>
<Body width="80" height="5" density="10"/>
<Pickable slots="Any"/>
<Projectile launchimpulse="20.0" damage="20.0" bleedingdamage="20.0" doesstick="true">
<Attack damage="20" bleeding="20" structuredamage="50" damagetype="Blunt"/>
</Projectile>
</Item>
<Item
name="Harpoon Gun"
pickdistance="200" >
<Sprite texture ="harpoongun.png" depth="0.04"/>
<Body width="90" height="30" density="5"/>
<Holdable holdpos="35,-10" aimpos="35,-10" handle1="-15,-6" handle2="26,7"/>
<Pickable slots="Any,BothHands"/>
<RangedWeapon barrelpos="49,10">
<Sound path="Content/Items/Weapons/harpoon1.ogg" type="OnUse"/>
<Sound path="Content/Items/Weapons/harpoon2.ogg" type="OnUse"/>
<RequiredItems name="Harpoon" type="Contained"/>
</RangedWeapon>
<Rope sprite="Content/Items/Weapons/rope.png" projectileanchorx="-40.0"/>
<ItemContainer hideitems="true">
<Containable name="Harpoon"/>
</ItemContainer>
</Item>
<Item
name="Stun Grenade"
pickdistance="200" >
<Sprite texture ="stungrenade.png" depth="0.04"/>
<Body width="11" height="24" density="15" friction="0.8f"/>
<Throwable holdpos="0,0" handle1="0,0" throwforce="5.0">
<StatusEffect type="OnUse" target="This" Condition="-100.0" delay="3.0" sound="Content/Items/Weapons/stunGrenade.ogg">
<Explosion range="5" damage="5" stun="10" force="0.1"/>
</StatusEffect>
</Throwable>
<Pickable slots="Any,RightHand,LeftHand"/>
</Item>
</Items>

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<Item
name="ID Card"
Tags="smallitem"
pickdistance="150">
<Sprite texture ="idcard.png"/>
<Body width="16" height="16" density="10"/>
<Pickable/>
</Item>

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8" ?>
<Items>
<Item
name="Junction Box"
type="JunctionBox"
linkable="true"
pickdistance="150">
<Sprite texture ="junctionbox.png" depth="0.8"/>
<PowerTransfer canbeselected = "true"/>
<ConnectionPanel canbeselected = "true">
<requireditem name="Screwdriver" type="Equipped"/>
<output name="power_out"/>
</ConnectionPanel>
</Item>
<Item
name="Battery"
linkable="true"
pickdistance="150">
<Sprite texture ="battery.png" depth="0.8"/>
<PowerContainer capacity="2000.0" maxinput="500.0" maxoutput="1000.0" canbeselected = "true"/>
<ConnectionPanel canbeselected = "true">
<requireditem name="Screwdriver" type="Equipped"/>
<input name="power_in"/>
</ConnectionPanel>
</Item>
</Items>

Binary file not shown.

View File

@@ -0,0 +1,61 @@
<Items>
<Item
name="Railgun"
focusonselected="true"
offsetonselected="500"
linkable="true">
<Sprite texture ="railgunbase.png"/>
<Turret barrelsprite="railgunbarrel.png" canbeselected = "true" linkable="true" origin="0.5, 0.85" barrelpos="117, 57"
rotationlimits="180,360"
powerconsumption="500.0">
<Sound path="Content/Items/railgun.ogg" type="OnUse"/>
</Turret>
</Item>
<Item
name="Railgun Controller"
type="Controller"
linkable="true"
pickdistance="150">
<Sprite texture ="railguncontroller.png" depth="0.8"/>
<Controller userpos="-1" direction ="Right" canbeselected = "true">
<limbposition limb="Head" position="0,-124"/>
<limbposition limb="LeftHand" position="38,-125"/>
<limbposition limb="RightHand" position="38,-125"/>
</Controller>
<trigger x="-50" y="-87" width="200" height ="153"/>
</Item>
<Item
name="Railgun Loader"
linkable="true"
pickdistance="150">
<Sprite texture ="railgunloader.png" depth =" 0.8"/>
<ItemContainer hideitems="false" drawinventory="true" capacity="5" itempos="54,-61" iteminterval="40,0" canbeselected = "true">
<Containable name="Railgun Shell"/>
</ItemContainer>
</Item>
<Item
name="Railgun Shell"
pickdistance="200">
<Sprite texture ="railgunshell.png"/>
<Body radius="14" height="63" density="15"/>
<Holdable holdpos="30,-15" handle1="0,20" handle2="0,-20"/>
<Pickable slots="RightHand"/>
<Projectile launchimpulse="80.0"/>
</Item>
</Items>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Some files were not shown because too many files have changed in this diff Show More