A* pathfinding, autopilot, WIP radar rendering, proper location names, character skills shown in menus, got rid of PreviewCharacter, mantis

This commit is contained in:
Regalis
2015-07-14 21:09:00 +03:00
parent a2636133ca
commit 44b9a63c94
51 changed files with 1971 additions and 940 deletions
+240
View File
@@ -0,0 +1,240 @@
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
namespace Subsurface
{
class PathNode
{
private WayPoint wayPoint;
private int wayPointID;
public int state;
public PathNode Parent;
private Vector2 position;
public float F,G,H;
public List<PathNode> connections;
public float[] distances;
public WayPoint Waypoint
{
get { return wayPoint; }
}
public Vector2 Position
{
get {return position;}
}
public PathNode(WayPoint wayPoint)
{
this.wayPoint = wayPoint;
this.position = wayPoint.SimPosition;
wayPointID = wayPoint.ID;
connections = new List<PathNode>();
}
public static List<PathNode> GenerateNodes(List<WayPoint> wayPoints)
{
var nodes = new Dictionary<int, PathNode>();
foreach (WayPoint wayPoint in wayPoints)
{
nodes.Add(wayPoint.ID, new PathNode(wayPoint));
}
foreach (KeyValuePair<int,PathNode> node in nodes)
{
foreach (MapEntity linked in node.Value.wayPoint.linkedTo)
{
PathNode connectedNode = null;
nodes.TryGetValue(linked.ID, out connectedNode);
if (connectedNode == null) continue;
node.Value.connections.Add(connectedNode);
}
}
var nodeList = nodes.Values.ToList();
foreach (PathNode node in nodeList)
{
node.distances = new float[node.connections.Count];
for (int i = 0; i< node.distances.Length; i++)
{
node.distances[i] = Vector2.Distance(node.position, node.connections[i].position);
}
}
return nodeList;
}
}
class PathFinder
{
List<PathNode> nodes;
private bool insideSubmarine;
public PathFinder(List<WayPoint> wayPoints, bool insideSubmarine = false)
{
nodes = PathNode.GenerateNodes(wayPoints.FindAll(w => w.MoveWithLevel != insideSubmarine));
this.insideSubmarine = insideSubmarine;
}
public SteeringPath FindPath(Vector2 start, Vector2 end)
{
float closestDist = 0.0f;
PathNode startNode = null;
foreach (PathNode node in nodes)
{
float dist = Vector2.Distance(start,node.Position);
if (dist<closestDist || startNode==null)
{
closestDist = dist;
startNode = node;
}
}
closestDist = 0.0f;
PathNode endNode = null;
foreach (PathNode node in nodes)
{
float dist = Vector2.Distance(end, node.Position);
if (dist < closestDist || endNode == null)
{
closestDist = dist;
endNode = node;
}
}
if (startNode == null || endNode == null)
{
DebugConsole.ThrowError("Pathfinding error, couldn't find pathnodes");
return null;
}
return FindPath(startNode,endNode);
}
public SteeringPath FindPath(WayPoint start, WayPoint end)
{
PathNode startNode=null, endNode=null;
foreach (PathNode node in nodes)
{
if (node.Waypoint == start)
{
startNode = node;
if (endNode != null) break;
}
if (node.Waypoint == end)
{
endNode = node;
if (startNode != null) break;
}
if (startNode==null || endNode==null)
{
DebugConsole.ThrowError("Pathfinding error, couldn't find matching pathnodes to waypoints");
return null;
}
}
return FindPath(startNode, endNode);
}
private SteeringPath FindPath(PathNode start, PathNode end)
{
foreach (PathNode node in nodes)
{
node.state = 0;
node.F = 0.0f;
node.G = 0.0f;
node.H = 0.0f;
}
start.state = 1;
while (true)
{
PathNode currNode = null;
float dist = 10000.0f;
foreach (PathNode node in nodes)
{
if (node.state != 1) continue;
if (node.F < dist)
{
dist = node.F;
currNode = node;
}
}
if (currNode == null || currNode == end) break;
currNode.state = 2;
for (int i = 0; i < currNode.connections.Count; i++)
{
PathNode nextNode = currNode.connections[i];
//a node that hasn't been searched yet
if (nextNode.state==0)
{
nextNode.H = Vector2.Distance(nextNode.Position,end.Position);
nextNode.G = currNode.G + currNode.distances[i];
nextNode.F = nextNode.G + nextNode.H;
nextNode.Parent = currNode;
nextNode.state = 1;
}
//node that has been searched
else if (nextNode.state==1)
{
float tempG = currNode.G + currNode.distances[i];
//only use if this new route is better than the
//route the node was a part of
if (tempG < nextNode.G)
{
nextNode.G = tempG;
nextNode.F = nextNode.G + nextNode.H;
nextNode.Parent = currNode;
}
}
}
}
if (end.state==0)
{
//path not found
return new SteeringPath();
}
SteeringPath path = new SteeringPath();
List<WayPoint> finalPath = new List<WayPoint>();
PathNode pathNode = end;
while (pathNode != start && pathNode != null)
{
finalPath.Add(pathNode.Waypoint);
pathNode = pathNode.Parent;
}
finalPath.Reverse();
foreach (WayPoint wayPoint in finalPath)
{
path.AddNode(wayPoint);
}
return path;
}
}
}
+10 -12
View File
@@ -5,32 +5,30 @@ namespace Subsurface
{
class SteeringPath
{
private Queue<Vector2> nodes;
const float MinDistance = 0.1f;
Vector2 currentNode;
private Queue<WayPoint> nodes;
WayPoint currentNode;
public SteeringPath()
{
nodes = new Queue<Vector2>();
nodes = new Queue<WayPoint>();
}
public void AddNode(Vector2 node)
public void AddNode(WayPoint node)
{
if (node == Vector2.Zero) return;
if (node == null) return;
nodes.Enqueue(node);
}
public Vector2 CurrentNode
public WayPoint CurrentNode
{
get { return currentNode; }
}
public Vector2 GetNode(Vector2 pos)
public WayPoint GetNode(Vector2 pos, float minDistance = 0.1f)
{
if (nodes.Count == 0) return Vector2.Zero;
if (currentNode == Vector2.Zero || Vector2.Distance(pos, currentNode) < MinDistance) currentNode = nodes.Dequeue();
if (nodes.Count == 0) return null;
if (currentNode == null || Vector2.Distance(pos, currentNode.SimPosition) < minDistance) currentNode = nodes.Dequeue();
return currentNode;
}
+24 -4
View File
@@ -1,4 +1,5 @@
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using Lidgren.Network;
using Microsoft.Xna.Framework;
@@ -94,6 +95,11 @@ namespace Subsurface
}
}
public float Mass
{
get { return AnimController.Mass; }
}
public Inventory Inventory
{
get { return inventory; }
@@ -397,6 +403,11 @@ namespace Subsurface
}
}
public int GetSkillLevel(string skillName)
{
return Info.Job.GetSkillLevel(skillName);
}
public void Control(float deltaTime, Camera cam, bool forcePick = false)
{
if (isDead) return;
@@ -509,12 +520,23 @@ namespace Subsurface
if (moveCam)
{
cam.TargetPos = ConvertUnits.ToDisplayUnits(AnimController.limbs[0].SimPosition);
cam.OffsetAmount = 250.0f;
cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, 250.0f, 0.05f);
}
cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition);
Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);
Body body = Submarine.PickBody(AnimController.limbs[0].SimPosition, mouseSimPos);
Structure structure = null;
if (body != null) structure = body.UserData as Structure;
if (structure!=null)
{
if (!structure.CastShadow && moveCam)
{
cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, 500.0f, 0.05f);
}
}
if (AnimController.onGround &&
!AnimController.InWater &&
AnimController.Anim != AnimController.Animation.UsingConstruction)
@@ -780,14 +802,12 @@ namespace Subsurface
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;
centerOfMass /= AnimController.Mass;
health = 0.0f;
+84 -8
View File
@@ -15,7 +15,17 @@ namespace Subsurface
public Job Job;
public Gender Gender;
private Gender gender;
public Gender Gender
{
get { return gender; }
set
{
if (gender == value) return;
gender = value;
LoadHeadSprite();
}
}
public int Salary;
@@ -26,6 +36,16 @@ namespace Subsurface
// return gender.ToString();
//}
private Sprite headSprite;
public Sprite HeadSprite
{
get
{
if (headSprite == null) LoadHeadSprite();
return headSprite;
}
}
public CharacterInfo(string file, string name = "", Gender gender = Gender.None, JobPrefab jobPrefab = null)
{
this.File = file;
@@ -42,11 +62,11 @@ namespace Subsurface
if (gender == Gender.None)
{
float femaleRatio = ToolBox.GetAttributeFloat(doc.Root, "femaleratio", 0.5f);
this.Gender = (Rand.Range(0.0f, 1.0f, false) < femaleRatio) ? Gender.Female : Gender.Male;
this.gender = (Rand.Range(0.0f, 1.0f, false) < femaleRatio) ? Gender.Female : Gender.Male;
}
else
{
this.Gender = gender;
this.gender = gender;
}
}
@@ -55,7 +75,7 @@ namespace Subsurface
{
headSpriteRange = ToolBox.GetAttributeVector2(
doc.Root,
this.Gender == Gender.Female ? "femaleheadid" : "maleheadid",
this.gender == Gender.Female ? "femaleheadid" : "maleheadid",
Vector2.Zero);
}
@@ -77,26 +97,82 @@ namespace Subsurface
string firstNamePath = ToolBox.GetAttributeString(doc.Root.Element("name"), "firstname", "");
if (firstNamePath != "")
{
firstNamePath = firstNamePath.Replace("[GENDER]", (this.Gender == Gender.Female) ? "f" : "");
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" : "");
lastNamePath = lastNamePath.Replace("[GENDER]", (this.gender == Gender.Female) ? "f" : "");
if (this.Name != "") this.Name += " ";
this.Name += ToolBox.GetRandomLine(lastNamePath);
}
}
}
private void LoadHeadSprite()
{
XDocument doc = ToolBox.TryLoadXml(File);
if (doc == null) return;
XElement ragdollElement = doc.Root.Element("ragdoll");
foreach (XElement limbElement in ragdollElement.Elements())
{
if (ToolBox.GetAttributeString(limbElement, "type", "").ToLower() != "head") continue;
XElement spriteElement = limbElement.Element("sprite");
string spritePath = spriteElement.Attribute("texture").Value;
spritePath = spritePath.Replace("[GENDER]", (this.gender == Gender.Female) ? "f" : "");
spritePath = spritePath.Replace("[HEADID]", HeadSpriteId.ToString());
headSprite = new Sprite(spriteElement, "", spritePath);
break;
}
}
public GUIFrame CreateInfoFrame(Rectangle rect)
{
GUIFrame frame = new GUIFrame(rect, Color.Transparent);
frame.Padding = new Vector4(10.0f,10.0f,10.0f,10.0f);
return CreateInfoFrame(frame);
}
public GUIFrame CreateInfoFrame(GUIFrame frame)
{
GUIImage image = new GUIImage(new Rectangle(0,0,30,30), HeadSprite, Alignment.TopLeft, frame);
int x = 0, y = 0;
new GUITextBlock(new Rectangle(x+80, y, 200, 20), Name, GUI.style, frame);
y += 20;
new GUITextBlock(new Rectangle(x+80, y, 200, 20), Job.Name, GUI.style, frame);
y += 30;
var skills = Job.Skills;
skills.Sort((s1, s2) => -s1.Level.CompareTo(s2.Level));
new GUITextBlock(new Rectangle(x, y, 200, 20), "Skills:", GUI.style, frame);
y += 20;
foreach (Skill skill in skills)
{
Color textColor = Color.White * (0.5f + skill.Level/200.0f);
new GUITextBlock(new Rectangle(x+20, y, 200, 20), skill.Name, Color.Transparent, textColor, Alignment.Left, GUI.style, frame);
new GUITextBlock(new Rectangle(x + 20, y, 200, 20), skill.Level.ToString(), Color.Transparent, textColor, Alignment.Right, GUI.style, frame);
y += 20;
}
return frame;
}
public CharacterInfo(XElement element)
{
Name = ToolBox.GetAttributeString(element, "name", "unnamed");
string genderStr = ToolBox.GetAttributeString(element, "gender", "male").ToLower();
Gender = (genderStr == "male") ? Gender.Male : Gender.Female;
gender = (genderStr == "male") ? Gender.Male : Gender.Female;
File = ToolBox.GetAttributeString(element, "file", "");
Salary = ToolBox.GetAttributeInt(element, "salary", 1000);
@@ -119,7 +195,7 @@ namespace Subsurface
charElement.Add(
new XAttribute("name", Name),
new XAttribute("file", File),
new XAttribute("gender", Gender == Gender.Male ? "male" : "female"),
new XAttribute("gender", gender == Gender.Male ? "male" : "female"),
new XAttribute("salary", Salary),
new XAttribute("headspriteid", HeadSpriteId),
new XAttribute("startitemsgiven", StartItemsGiven));
+17 -3
View File
@@ -20,6 +20,8 @@ namespace Subsurface
private float flipTimer;
private float? footRotation;
public FishAnimController(Character character, XElement element)
: base(character, element)
{
@@ -31,6 +33,12 @@ namespace Subsurface
walkSpeed = ToolBox.GetAttributeFloat(element, "walkspeed", 1.0f);
swimSpeed = ToolBox.GetAttributeFloat(element, "swimspeed", 1.0f);
float footRot = ToolBox.GetAttributeFloat(element,"footrotation", float.NaN);
if (!float.IsNaN(footRot))
{
footRotation = MathHelper.ToRadians(footRot);
}
rotateTowardsMovement = ToolBox.GetAttributeBool(element, "rotatetowardsmovement", true);
}
@@ -202,19 +210,20 @@ namespace Subsurface
Limb torso = GetLimb(LimbType.Torso);
Limb head = GetLimb(LimbType.Head);
if (torso!=null)
{
colliderLimb = torso;
colliderHeight = TorsoPosition;
colliderLimb.body.SmoothRotate(TorsoAngle*Dir, 5.0f);
colliderLimb.body.SmoothRotate(TorsoAngle*Dir, 10.0f);
}
else
{
colliderLimb = head;
colliderHeight = HeadPosition;
colliderLimb.body.SmoothRotate(HeadAngle*Dir, 10.0f);
colliderLimb.body.SmoothRotate(HeadAngle*Dir, 100.0f);
}
Vector2 colliderPos = colliderLimb.SimPosition;
@@ -270,7 +279,9 @@ namespace Subsurface
colliderLimb.Move(new Vector2(colliderPos.X + movement.X * 0.2f, floorY + colliderHeight), 5.0f);
}
float walkCycleSpeed = head.LinearVelocity.X * 0.08f;
//colliderLimb.body.SetTransform(Vector2.Zero, colliderLimb.Rotation);
float walkCycleSpeed = head.LinearVelocity.X * 0.05f;
walkPos -= walkCycleSpeed;
@@ -308,6 +319,9 @@ namespace Subsurface
(-transformedStepSize.Y > 0.0f) ? -transformedStepSize.Y : 0.0f),
8.0f);
}
if (footRotation!=null) limb.body.SmoothRotate((float)footRotation*Dir, 50.0f);
break;
case LimbType.LeftLeg:
case LimbType.RightLeg:
@@ -17,6 +17,8 @@ namespace Subsurface
{
Vector2 colliderPos = GetLimb(LimbType.Torso).SimPosition;
if (inWater) stairs = null;
Vector2 rayStart = colliderPos; // at the bottom of the player sprite
Vector2 rayEnd = rayStart - new Vector2(0.0f, TorsoPosition);
if (stairs != null) rayEnd.Y -= 0.5f;
@@ -31,10 +33,11 @@ namespace Subsurface
switch (fixture.CollisionCategories)
{
case Physics.CollisionStairs:
if (inWater) return -1;
Structure structure = fixture.Body.UserData as Structure;
if (stairs == null && !inWater && structure!=null)
if (stairs == null && structure!=null)
{
if (LowestLimb.SimPosition.Y<structure.SimPosition.Y)
if (LowestLimb.SimPosition.Y < structure.SimPosition.Y)
{
return -1;
}
@@ -92,8 +95,7 @@ namespace Subsurface
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;
+46 -11
View File
@@ -7,12 +7,34 @@ using System.Xml.Linq;
namespace Subsurface
{
class Skill
{
string name;
int level;
public string Name
{
get { return name; }
}
public int Level
{
get { return level; }
}
public Skill(string name, int level)
{
this.name = name;
this.level = level;
}
}
class Job
{
private JobPrefab prefab;
private Dictionary<string, float> skills;
private Dictionary<string, Skill> skills;
public string Name
{
@@ -34,15 +56,26 @@ namespace Subsurface
get { return prefab.ItemNames; }
}
public List<Skill> Skills
{
get { return skills.Values.ToList(); }
}
//public List<float> SkillLevels
//{
// get { return skills.Values.ToList(); }
//}
public Job(JobPrefab jobPrefab)
{
prefab = jobPrefab;
skills = new Dictionary<string, float>();
skills = new Dictionary<string, Skill>();
foreach (KeyValuePair<string, Vector2> skill in prefab.Skills)
{
skills.Add(skill.Key, Rand.Range(skill.Value.X, skill.Value.Y, false));
skills.Add(
skill.Key,
new Skill( skill.Key, (int)Rand.Range(skill.Value.X, skill.Value.Y, false)));
}
}
@@ -53,10 +86,12 @@ namespace Subsurface
foreach (XElement subElement in element.Elements())
{
skills.Add(subElement.Name.ToString(), ToolBox.GetAttributeFloat(subElement, "level", 0.0f));
skills.Add(
subElement.Name.ToString(),
new Skill(subElement.Name.ToString(), ToolBox.GetAttributeInt(subElement, "level", 0)));
}
}
public static Job Random()
{
JobPrefab prefab = JobPrefab.List[Rand.Int(JobPrefab.List.Count-1, false)];
@@ -64,12 +99,12 @@ namespace Subsurface
return new Job(prefab);
}
public float GetSkill(string skillName)
public int GetSkillLevel(string skillName)
{
float skillLevel = 0.0f;
skills.TryGetValue(skillName.ToLower(), out skillLevel);
Skill skill = null;
skills.TryGetValue(skillName, out skill);
return skillLevel;
return (skill==null) ? 0 : skill.Level;
}
public virtual XElement Save(XElement parentElement)
@@ -78,9 +113,9 @@ namespace Subsurface
jobElement.Add(new XAttribute("name", Name));
foreach (KeyValuePair<string, float> skill in skills)
foreach (KeyValuePair<string, Skill> skill in skills)
{
jobElement.Add(new XElement(skill.Key, new XAttribute("level", skill.Value)));
jobElement.Add(new XElement(skill.Key, new XAttribute("level", skill.Value.Level)));
}
parentElement.Add(jobElement);
+7 -6
View File
@@ -85,17 +85,18 @@ namespace Subsurface
{
foreach (XElement subElement in element.Elements())
{
string skillName = subElement.Name.ToString();
if (Skills.ContainsKey(skillName)) continue;
string skillName = ToolBox.GetAttributeString(subElement, "name", "");
var levelAttribute = subElement.Attribute("level").ToString();
if (levelAttribute.Contains("'"))
if (string.IsNullOrEmpty(skillName) || Skills.ContainsKey(skillName)) continue;
var levelString = ToolBox.GetAttributeString(subElement, "level", "");
if (levelString.Contains(","))
{
Skills.Add(skillName, ToolBox.ParseToVector2(levelAttribute, false));
Skills.Add(skillName, ToolBox.ParseToVector2(levelString, false));
}
else
{
float skillLevel = float.Parse(levelAttribute, CultureInfo.InvariantCulture);
float skillLevel = float.Parse(levelString, CultureInfo.InvariantCulture);
Skills.Add(skillName, new Vector2(skillLevel, skillLevel));
}
+8 -1
View File
@@ -59,6 +59,12 @@ namespace Subsurface
get { return lowestLimb; }
}
public float Mass
{
get;
private set;
}
public Vector2 TargetMovement
{
get { return (correctionMovement == Vector2.Zero) ? targetMovement : correctionMovement; }
@@ -173,6 +179,7 @@ namespace Subsurface
limb.body.FarseerBody.OnCollision += OnLimbCollision;
limbs[ID] = limb;
Mass += limb.Mass;
if (!limbDictionary.ContainsKey(limb.type)) limbDictionary.Add(limb.type, limb);
break;
case "joint":
@@ -257,7 +264,7 @@ namespace Subsurface
}
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))
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;