mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-22 15:02:54 +08:00
434 lines
12 KiB
C#
434 lines
12 KiB
C#
using CodeWalker.GameFiles;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Xml;
|
|
using SharpDX;
|
|
|
|
namespace CodeWalker.World
|
|
{
|
|
public class Trains
|
|
{
|
|
public volatile bool Inited = false;
|
|
public GameFileCache GameFileCache;
|
|
|
|
public List<TrainTrack> TrainTracks { get; set; } = new List<TrainTrack>();
|
|
|
|
|
|
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
|
|
{
|
|
GameFileCache = gameFileCache;
|
|
|
|
var rpfman = gameFileCache.RpfMan;
|
|
|
|
string trainsfilename = "common.rpf\\data\\levels\\gta5\\trains.xml";
|
|
XmlDocument trainsxml = rpfman.GetFileXml(trainsfilename);
|
|
XmlElement trainsdata = trainsxml.DocumentElement;
|
|
//TODO: parse train_configs
|
|
|
|
|
|
string tracksfilename = "common.rpf\\data\\levels\\gta5\\traintracks.xml";
|
|
XmlDocument tracksxml = rpfman.GetFileXml(tracksfilename);
|
|
XmlElement tracksdata = tracksxml.DocumentElement;
|
|
XmlNodeList tracks = tracksdata.SelectNodes("train_track");
|
|
|
|
TrainTracks.Clear();
|
|
for (int i = 0; i < tracks.Count; i++)
|
|
{
|
|
var trackxml = tracks[i];
|
|
TrainTrack tt = new TrainTrack();
|
|
tt.Load(gameFileCache, trackxml);
|
|
TrainTracks.Add(tt);
|
|
}
|
|
|
|
|
|
Inited = true;
|
|
}
|
|
|
|
}
|
|
|
|
public class TrainTrack : BasePathData
|
|
{
|
|
public string filename { get; set; }
|
|
public string trainConfigName { get; set; }
|
|
public bool isPingPongTrack { get; set; }
|
|
public bool stopsAtStations { get; set; }
|
|
public bool MPstopsAtStations { get; set; }
|
|
public float speed { get; set; }
|
|
public float brakingDist { get; set; }
|
|
|
|
|
|
public List<TrainTrackNode> Nodes { get; set; }
|
|
public int NodeCount { get; set; }
|
|
|
|
|
|
public int StationCount
|
|
{
|
|
get
|
|
{
|
|
int sc = 0;
|
|
foreach (var node in Nodes)
|
|
{
|
|
if ((node.NodeType == 1) || (node.NodeType == 2) || (node.NodeType == 5))
|
|
{
|
|
sc++;
|
|
}
|
|
}
|
|
return sc;
|
|
}
|
|
}
|
|
|
|
|
|
public EditorVertex[] LinkedVerts { get; set; }
|
|
public Vector4[] NodePositions { get; set; }
|
|
|
|
public EditorVertex[] GetPathVertices()
|
|
{
|
|
return LinkedVerts;
|
|
}
|
|
public EditorVertex[] GetTriangleVertices()
|
|
{
|
|
return null;
|
|
}
|
|
public Vector4[] GetNodePositions()
|
|
{
|
|
return NodePositions;
|
|
}
|
|
|
|
public PathBVH BVH { get; set; }
|
|
|
|
public string NodesString { get; set; }
|
|
public RpfFileEntry RpfFileEntry { get; set; }
|
|
public string Name { get; set; }
|
|
public string FilePath { get; set; }
|
|
public bool HasChanged { get; set; }
|
|
public bool Loaded { get; set; }
|
|
|
|
|
|
|
|
|
|
public void Load(GameFileCache gameFileCache, XmlNode node)
|
|
{
|
|
//load from game file cache
|
|
|
|
filename = Xml.GetStringAttribute(node, "filename");
|
|
trainConfigName = Xml.GetStringAttribute(node, "trainConfigName");
|
|
isPingPongTrack = Xml.GetBoolAttribute(node, "isPingPongTrack");
|
|
stopsAtStations = Xml.GetBoolAttribute(node, "stopsAtStations");
|
|
MPstopsAtStations = Xml.GetBoolAttribute(node, "MPstopsAtStations");
|
|
speed = Xml.GetFloatAttribute(node, "speed");
|
|
brakingDist = Xml.GetFloatAttribute(node, "brakingDist");
|
|
|
|
RpfFileEntry = gameFileCache.RpfMan.GetEntry(filename) as RpfFileEntry;
|
|
NodesString = gameFileCache.RpfMan.GetFileUTF8Text(filename);
|
|
SetNameFromFilename();
|
|
FilePath = Name;
|
|
|
|
Load(NodesString);
|
|
|
|
BuildVertices();
|
|
|
|
BuildBVH();
|
|
|
|
Loaded = true;
|
|
}
|
|
|
|
public void Load(byte[] data)
|
|
{
|
|
filename = string.Empty;
|
|
trainConfigName = string.Empty;
|
|
RpfFileEntry = new RpfBinaryFileEntry();
|
|
|
|
|
|
string str = Encoding.UTF8.GetString(data);
|
|
Load(str);
|
|
|
|
BuildVertices();
|
|
|
|
BuildBVH();
|
|
|
|
Loaded = true;
|
|
}
|
|
|
|
public byte[] Save()
|
|
{
|
|
NodeCount = Nodes.Count;
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.AppendLine(Nodes.Count.ToString());
|
|
foreach (var node in Nodes)
|
|
{
|
|
var nstr = FloatUtil.GetVector3String(node.Position).Replace(",","") + " " + node.NodeType.ToString();
|
|
sb.AppendLine(nstr);
|
|
}
|
|
string str = sb.ToString();
|
|
return Encoding.UTF8.GetBytes(str);
|
|
}
|
|
|
|
|
|
public void SetNameFromFilename()
|
|
{
|
|
string[] fparts = filename.Replace('\\', '/').Split('/');
|
|
if ((fparts == null) || (fparts.Length == 0))
|
|
{
|
|
Name = filename;
|
|
}
|
|
else
|
|
{
|
|
Name = fparts[fparts.Length - 1];
|
|
}
|
|
}
|
|
|
|
|
|
public void Load(string trackstr)
|
|
{
|
|
//load nodes from a text string...
|
|
NodesString = trackstr;
|
|
|
|
if (!string.IsNullOrEmpty(trackstr))
|
|
{
|
|
string[] trackstrs = trackstr.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
|
if (trackstrs.Length > 1)
|
|
{
|
|
int nodecount;
|
|
int.TryParse(trackstrs[0], out nodecount);
|
|
NodeCount = nodecount;
|
|
List<TrainTrackNode> nodes = new List<TrainTrackNode>();
|
|
for (int i = 1; i < trackstrs.Length; i++)
|
|
{
|
|
var nodestr = trackstrs[i].Trim();
|
|
var nodevals = nodestr.Split(' ');
|
|
if (nodevals.Length == 4)
|
|
{
|
|
TrainTrackNode ttnode = new TrainTrackNode();
|
|
var x = FloatUtil.Parse(nodevals[0]);
|
|
var y = FloatUtil.Parse(nodevals[1]);
|
|
var z = FloatUtil.Parse(nodevals[2]);
|
|
int nodetype;
|
|
int.TryParse(nodevals[3], out nodetype);
|
|
ttnode.Position = new Vector3(x, y, z);
|
|
ttnode.NodeType = nodetype;
|
|
ttnode.Track = this;
|
|
ttnode.Index = nodes.Count;
|
|
ttnode.Links[0] = (nodes.Count > 0) ? nodes[nodes.Count - 1] : null;
|
|
if (ttnode.Links[0] != null)
|
|
{
|
|
ttnode.Links[0].Links[1] = ttnode;
|
|
}
|
|
nodes.Add(ttnode);
|
|
}
|
|
else
|
|
{ }
|
|
}
|
|
Nodes = nodes;
|
|
}
|
|
else
|
|
{ }
|
|
}
|
|
else
|
|
{ }
|
|
|
|
if (Nodes == null)
|
|
{
|
|
Nodes = new List<TrainTrackNode>();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
public void BuildVertices()
|
|
{
|
|
if ((Nodes != null) && (Nodes.Count > 0))
|
|
{
|
|
var nc = Nodes.Count;
|
|
var lc = nc - 1;
|
|
var lvc = lc * 2;
|
|
var np = new Vector4[nc];
|
|
var lv = new EditorVertex[lvc];
|
|
for (int i = 0; i < nc; i++)
|
|
{
|
|
np[i] = new Vector4(Nodes[i].Position, 1.0f);
|
|
if (i > 0)
|
|
{
|
|
var l = i - 1;
|
|
var li = l * 2;
|
|
var ni = li + 1;
|
|
lv[li].Position = Nodes[l].Position;
|
|
lv[ni].Position = Nodes[i].Position;
|
|
lv[li].Colour = (uint)Nodes[l].GetColour();
|
|
lv[ni].Colour = (uint)Nodes[i].GetColour();
|
|
}
|
|
}
|
|
NodePositions = np;
|
|
LinkedVerts = lv;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void UpdateBvhForNode(TrainTrackNode node)
|
|
{
|
|
//this needs to be called when a node's position changes...
|
|
//need to recalc the BVH for mouse intersection optimisation purposes.
|
|
|
|
//if (BVH == null) return;
|
|
//BVH.UpdateForNode(node);
|
|
|
|
BuildBVH();
|
|
|
|
//also updates the NodePositions for the visible vertex
|
|
if (Nodes != null)
|
|
{
|
|
for (int i = 0; i < Nodes.Count; i++)
|
|
{
|
|
if (Nodes[i] == node)
|
|
{
|
|
NodePositions[i] = new Vector4(node.Position, 1.0f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public void BuildBVH()
|
|
{
|
|
BVH = new PathBVH(Nodes, 10, 10);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public TrainTrackNode AddNode(TrainTrackNode afternode = null)
|
|
{
|
|
int cnt = Nodes?.Count ?? 0;
|
|
TrainTrackNode tn = new TrainTrackNode();
|
|
tn.Track = this;
|
|
tn.Index = (afternode != null) ? afternode.Index + 1 : cnt;
|
|
|
|
if (Nodes == null)
|
|
{
|
|
Nodes = new List<TrainTrackNode>();
|
|
}
|
|
|
|
if (afternode != null)
|
|
{
|
|
TrainTrackNode aln = afternode.Links[1];
|
|
if (aln != null) aln.Links[0] = tn;
|
|
afternode.Links[1] = tn;
|
|
tn.Links[0] = afternode;
|
|
tn.Links[1] = aln;
|
|
|
|
int idx = tn.Index;
|
|
Nodes.Insert(idx, tn);
|
|
|
|
for (int i = 0; i < Nodes.Count; i++)
|
|
{
|
|
Nodes[i].Index = i;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (cnt > 0)
|
|
{
|
|
TrainTrackNode ln = Nodes[cnt - 1];
|
|
tn.Links[0] = ln;
|
|
ln.Links[1] = tn;
|
|
}
|
|
|
|
Nodes.Add(tn);
|
|
}
|
|
|
|
|
|
NodeCount = Nodes.Count;
|
|
|
|
return tn;
|
|
}
|
|
|
|
public bool RemoveNode(TrainTrackNode node)
|
|
{
|
|
bool r = false;
|
|
|
|
r = Nodes.Remove(node);
|
|
|
|
NodeCount = Nodes.Count;
|
|
|
|
if (r)
|
|
{
|
|
var l0 = node.Links[0];
|
|
var l1 = node.Links[1];
|
|
|
|
if (l0 != null)
|
|
{
|
|
l0.Links[1] = l1;
|
|
}
|
|
if (l1 != null)
|
|
{
|
|
l1.Links[0] = l0;
|
|
}
|
|
|
|
for (int i = 0; i < Nodes.Count; i++)
|
|
{
|
|
Nodes[i].Index = i;
|
|
}
|
|
|
|
BuildVertices();
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
{
|
|
return Name + ": " + filename + " (" + NodeCount.ToString() + " nodes)";
|
|
}
|
|
}
|
|
|
|
public class TrainTrackNode : BasePathNode
|
|
{
|
|
public Vector3 Position { get; set; }
|
|
public int NodeType { get; set; }
|
|
|
|
public TrainTrack Track { get; set; }
|
|
public int Index { get; set; }
|
|
public TrainTrackNode[] Links { get; set; } = new TrainTrackNode[2];
|
|
|
|
public int GetColour()
|
|
{
|
|
switch (NodeType)
|
|
{
|
|
case 0: return new Color4(1.0f, 0.0f, 0.0f, 1.0f).ToRgba();
|
|
case 1: return new Color4(1.0f, 1.0f, 0.0f, 1.0f).ToRgba();
|
|
case 2: return new Color4(0.0f, 1.0f, 0.0f, 1.0f).ToRgba();
|
|
case 3: return new Color4(0.0f, 1.0f, 1.0f, 1.0f).ToRgba();
|
|
case 4: return new Color4(0.0f, 0.0f, 1.0f, 1.0f).ToRgba();
|
|
case 5: return new Color4(1.0f, 0.0f, 1.0f, 1.0f).ToRgba();
|
|
default: return new Color4(1.0f, 1.0f, 1.0f, 1.0f).ToRgba();
|
|
}
|
|
}
|
|
|
|
|
|
public void SetPosition(Vector3 pos)
|
|
{
|
|
Position = pos;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return Index.ToString() + ": " + NodeType.ToString();// + ": " + FloatUtil.GetVector3String(Position);
|
|
}
|
|
}
|
|
|
|
}
|