First commit
205
Subsurface/Camera.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Subsurface/Characters/AI/AIController.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
class AIController : ISteerable
|
||||
{
|
||||
|
||||
public enum AiState { None, Attack, GoTo, Escape }
|
||||
public enum SteeringState { Wander, Seek, Escape }
|
||||
|
||||
public Character character;
|
||||
|
||||
protected AiState state;
|
||||
|
||||
protected SteeringManager steeringManager;
|
||||
|
||||
public Vector2 Steering
|
||||
{
|
||||
get { return character.animController.TargetMovement; }
|
||||
set { character.animController.TargetMovement = value; }
|
||||
}
|
||||
|
||||
public Vector2 Position
|
||||
{
|
||||
get { return character.animController.limbs[0].SimPosition; }
|
||||
}
|
||||
|
||||
public Vector2 Velocity
|
||||
{
|
||||
get { return character.animController.limbs[0].LinearVelocity; }
|
||||
}
|
||||
|
||||
public AiState State
|
||||
{
|
||||
get { return state; }
|
||||
}
|
||||
|
||||
public AIController (Character c)
|
||||
{
|
||||
character = c;
|
||||
|
||||
steeringManager = new SteeringManager(this);
|
||||
}
|
||||
|
||||
public virtual void Update(float deltaTime) { }
|
||||
|
||||
//protected Structure lastStructurePicked;
|
||||
|
||||
public virtual void FillNetworkData(NetOutgoingMessage message) { }
|
||||
public virtual void ReadNetworkData(NetIncomingMessage message) { }
|
||||
|
||||
}
|
||||
}
|
||||
48
Subsurface/Characters/AI/AITarget.cs
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
506
Subsurface/Characters/AI/EnemyAIController.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
27
Subsurface/Characters/AI/ISteerable.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Subsurface
|
||||
{
|
||||
interface ISteerable
|
||||
{
|
||||
|
||||
Vector2 Steering
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
Vector2 Velocity
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vector2 Position
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
146
Subsurface/Characters/AI/SteeringManager.cs
Normal 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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Subsurface/Characters/AI/SteeringPath.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Subsurface/Characters/AnimController.cs
Normal 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) { }
|
||||
|
||||
}
|
||||
}
|
||||
963
Subsurface/Characters/Character.cs
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
76
Subsurface/Characters/CharacterInfo.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Subsurface/Characters/DelayedEffect.cs
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
353
Subsurface/Characters/FishAnimController.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
787
Subsurface/Characters/HumanoidAnimController.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
6
Subsurface/Characters/Job.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Subsurface.Characters
|
||||
{
|
||||
class Job
|
||||
{
|
||||
}
|
||||
}
|
||||
58
Subsurface/Characters/Jobs/Job.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
362
Subsurface/Characters/Limb.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
94
Subsurface/Characters/LimbAttack.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
643
Subsurface/Characters/Ragdoll.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
211
Subsurface/Characters/StatusEffect.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
BIN
Subsurface/Content/Characters/Crawler/crawler.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
77
Subsurface/Content/Characters/Crawler/crawler.xml
Normal 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>
|
||||
|
||||
171
Subsurface/Content/Characters/Human/ffirstnames.txt
Normal 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
|
||||
BIN
Subsurface/Content/Characters/Human/fhead.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
206
Subsurface/Content/Characters/Human/firstnames.txt
Normal 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
|
||||
BIN
Subsurface/Content/Characters/Human/flegs.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
Subsurface/Content/Characters/Human/ftorso.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
Subsurface/Content/Characters/Human/head.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Subsurface/Content/Characters/Human/head2.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
88
Subsurface/Content/Characters/Human/human.xml
Normal 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>
|
||||
|
||||
157
Subsurface/Content/Characters/Human/lastnames.txt
Normal 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
|
||||
BIN
Subsurface/Content/Characters/Human/legs.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
Subsurface/Content/Characters/Human/torso.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
18
Subsurface/Content/Characters/Jobs.xml
Normal 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>
|
||||
BIN
Subsurface/Content/Characters/Scorpion/scorpion.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
48
Subsurface/Content/Characters/Scorpion/scorpion.xml
Normal 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>
|
||||
|
||||
BIN
Subsurface/Content/Characters/Scorpion/scorpionattack1.ogg
Normal file
BIN
Subsurface/Content/Characters/Scorpion/scorpionidle1.ogg
Normal file
BIN
Subsurface/Content/Characters/Scorpion/scorpionidle2.ogg
Normal file
BIN
Subsurface/Content/Characters/TigerThresher/tigerthresher.png
Normal file
|
After Width: | Height: | Size: 343 KiB |
@@ -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>
|
||||
BIN
Subsurface/Content/Characters/panzershrimp.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
39
Subsurface/Content/Characters/shrimp.xml
Normal 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>
|
||||
|
||||
BIN
Subsurface/Content/HUD/caret.png
Normal file
|
After Width: | Height: | Size: 123 B |
11
Subsurface/Content/HUD/style.xml
Normal 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"/>
|
||||
BIN
Subsurface/Content/HUD/textboxTexture.png
Normal file
|
After Width: | Height: | Size: 685 B |
BIN
Subsurface/Content/Items/Button/button.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
18
Subsurface/Content/Items/Button/item.xml
Normal 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>
|
||||
BIN
Subsurface/Content/Items/Diving/DivingMask.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Subsurface/Content/Items/Diving/DivingSuit.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
50
Subsurface/Content/Items/Diving/divinggear.xml
Normal 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>
|
||||
BIN
Subsurface/Content/Items/Door/door.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
Subsurface/Content/Items/Door/doorframe.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
41
Subsurface/Content/Items/Door/doors.xml
Normal 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>
|
||||
BIN
Subsurface/Content/Items/Door/windowedDoor.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
11
Subsurface/Content/Items/Ladder/item.xml
Normal 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>
|
||||
BIN
Subsurface/Content/Items/Ladder/ladder.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
Subsurface/Content/Items/Lockers/locker.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
14
Subsurface/Content/Items/Lockers/lockers.xml
Normal 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>
|
||||
|
||||
11
Subsurface/Content/Items/MiniMap/item.xml
Normal 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>
|
||||
BIN
Subsurface/Content/Items/MiniMap/reactor.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
Subsurface/Content/Items/OxygenGenerator/battery.png
Normal file
|
After Width: | Height: | Size: 520 B |
23
Subsurface/Content/Items/OxygenGenerator/item.xml
Normal 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>
|
||||
13
Subsurface/Content/Items/OxygenTank/item.xml
Normal 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>
|
||||
BIN
Subsurface/Content/Items/OxygenTank/oxygentank.png
Normal file
|
After Width: | Height: | Size: 464 B |
BIN
Subsurface/Content/Items/Pump/pump.png
Normal file
|
After Width: | Height: | Size: 882 B |
16
Subsurface/Content/Items/Pump/pump.xml
Normal 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>
|
||||
BIN
Subsurface/Content/Items/Signal/and.png
Normal file
|
After Width: | Height: | Size: 177 B |
BIN
Subsurface/Content/Items/Signal/light.png
Normal file
|
After Width: | Height: | Size: 237 B |
BIN
Subsurface/Content/Items/Signal/lightsprite.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Subsurface/Content/Items/Signal/not.png
Normal file
|
After Width: | Height: | Size: 203 B |
BIN
Subsurface/Content/Items/Signal/or.png
Normal file
|
After Width: | Height: | Size: 187 B |
180
Subsurface/Content/Items/Signal/signalitems.xml
Normal 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>
|
||||
|
||||
BIN
Subsurface/Content/Items/Signal/wire.png
Normal file
|
After Width: | Height: | Size: 934 B |
BIN
Subsurface/Content/Items/Tools/screwdriver.png
Normal file
|
After Width: | Height: | Size: 331 B |
51
Subsurface/Content/Items/Tools/tools.xml
Normal 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>
|
||||
BIN
Subsurface/Content/Items/Tools/weldingtool.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Subsurface/Content/Items/Tools/wrench.png
Normal file
|
After Width: | Height: | Size: 255 B |
10
Subsurface/Content/Items/Vent/item.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<Item
|
||||
name="Vent"
|
||||
linkable="true">
|
||||
|
||||
|
||||
<Sprite texture ="vent.png" depth="0.8"/>
|
||||
|
||||
<Vent/>
|
||||
|
||||
</Item>
|
||||
BIN
Subsurface/Content/Items/Vent/vent.png
Normal file
|
After Width: | Height: | Size: 241 B |
BIN
Subsurface/Content/Items/Weapons/harpoon.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Subsurface/Content/Items/Weapons/harpoon1.ogg
Normal file
BIN
Subsurface/Content/Items/Weapons/harpoon2.ogg
Normal file
BIN
Subsurface/Content/Items/Weapons/harpoongun.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Subsurface/Content/Items/Weapons/rope.png
Normal file
|
After Width: | Height: | Size: 152 B |
BIN
Subsurface/Content/Items/Weapons/stunGrenade.ogg
Normal file
BIN
Subsurface/Content/Items/Weapons/stungrenade.png
Normal file
|
After Width: | Height: | Size: 661 B |
61
Subsurface/Content/Items/Weapons/weapons.xml
Normal 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>
|
||||
|
||||
|
||||
BIN
Subsurface/Content/Items/battery.png
Normal file
|
After Width: | Height: | Size: 520 B |
BIN
Subsurface/Content/Items/circuitboard.png
Normal file
|
After Width: | Height: | Size: 293 B |
BIN
Subsurface/Content/Items/connector.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Subsurface/Content/Items/fuelrod.png
Normal file
|
After Width: | Height: | Size: 577 B |
BIN
Subsurface/Content/Items/heatabsorber.png
Normal file
|
After Width: | Height: | Size: 922 B |
BIN
Subsurface/Content/Items/idcard.png
Normal file
|
After Width: | Height: | Size: 188 B |
16
Subsurface/Content/Items/idcard.xml
Normal 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>
|
||||
|
||||
|
||||
|
||||
BIN
Subsurface/Content/Items/junctionbox.png
Normal file
|
After Width: | Height: | Size: 498 B |
36
Subsurface/Content/Items/poweritems.xml
Normal 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>
|
||||
BIN
Subsurface/Content/Items/railgun.ogg
Normal file
61
Subsurface/Content/Items/railgun.xml
Normal 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>
|
||||
|
||||
BIN
Subsurface/Content/Items/railgunbarrel.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
Subsurface/Content/Items/railgunbase.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |