Merge pull request #53 from alexguirre/scenario-type-groups

Support for ScenarioTypeGroups in scenario files
This commit is contained in:
dexyfex 2019-10-30 22:40:04 +11:00 committed by GitHub
commit 2bba3d6e89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 210 additions and 23 deletions

View File

@ -4714,7 +4714,7 @@ namespace CodeWalker.GameFiles
public byte TypeId { get { return _Data.iType; } set { _Data.iType = value; } }
public ScenarioType Type { get; set; }
public ScenarioTypeRef? Type { get; set; }
public byte ModelSetId { get { return _Data.ModelSetId; } set { _Data.ModelSetId = value; } }
public AmbientModelSet ModelSet { get; set; }
@ -5298,7 +5298,7 @@ namespace CodeWalker.GameFiles
public Vector3 Position { get { return _Data.Position; } set { _Data.Position = value; } }
public MetaHash Unk1 { get { return _Data.Unk_2602393771; } set { _Data.Unk_2602393771 = value; } }
public MetaHash TypeHash { get { return _Data.ScenarioType; } set { _Data.ScenarioType = value; } }
public ScenarioType Type { get; set; }
public ScenarioTypeRef? Type { get; set; }
public bool NotFirst { get { return _Data.Unk_407126079_NotFirst == 1; } set { _Data.Unk_407126079_NotFirst = (byte)(value ? 1 : 0); } }
public bool NotLast { get { return _Data.Unk_1308720135_NotLast == 1; } set { _Data.Unk_1308720135_NotLast = (byte)(value ? 1 : 0); } }

View File

@ -208,13 +208,23 @@ namespace CodeWalker.World
if (tpind < typhashes.Length)
{
var hash = typhashes[tpind];
scp.Type = types.GetScenarioType(hash);
if (scp.Type != null)
var st = types.GetScenarioType(hash);
if (st != null)
{
isveh = scp.Type.IsVehicle;
scp.Type = new ScenarioTypeRef(st);
}
else
{ }
{
var stg = types.GetScenarioTypeGroup(hash);
if (stg != null)
{
scp.Type = new ScenarioTypeRef(stg);
}
else
{ }
}
isveh = scp.Type.Value.IsVehicle;
}
else
{ }
@ -282,13 +292,23 @@ namespace CodeWalker.World
if ((hash != 0) && (hash != 493038497))
{
bool isveh = false;
spn.Type = types.GetScenarioType(hash);
if (spn.Type != null)
var st = types.GetScenarioType(hash);
if (st != null)
{
isveh = spn.Type.IsVehicle;
spn.Type = new ScenarioTypeRef(st);
}
else
{ }
{
var stg = types.GetScenarioTypeGroup(hash);
if (stg != null)
{
spn.Type = new ScenarioTypeRef(stg);
}
else
{ }
}
isveh = spn.Type?.IsVehicle ?? false;
if (isveh)
{ }
else
@ -1067,10 +1087,10 @@ namespace CodeWalker.World
int interiorid = 0;
int groupid = 0;
int imapid = 0;
if ((mp.Type != null) && (!typeNames.TryGetValue(mp.Type.NameHash, out typeid)))
if ((mp.Type != null) && (!typeNames.TryGetValue(mp.Type.Value.NameHash, out typeid)))
{
typeid = typeNames.Count;
typeNames[mp.Type.NameHash] = typeid;
typeNames[mp.Type.Value.NameHash] = typeid;
}
if (mp.ModelSet != null)
{
@ -1159,10 +1179,10 @@ namespace CodeWalker.World
int interiorid = 0;
int groupid = 0;
int imapid = 0;
if ((mp.Type != null) && (!typeNames.TryGetValue(mp.Type.NameHash, out typeid)))
if ((mp.Type != null) && (!typeNames.TryGetValue(mp.Type.Value.NameHash, out typeid)))
{
typeid = typeNames.Count;
typeNames[mp.Type.NameHash] = typeid;
typeNames[mp.Type.Value.NameHash] = typeid;
}
if (mp.ModelSet != null)
{
@ -1502,6 +1522,7 @@ namespace CodeWalker.World
private object SyncRoot = new object(); //keep this thread-safe.. technically shouldn't be necessary, but best to be safe
private Dictionary<uint, ScenarioType> Types { get; set; }
private Dictionary<uint, ScenarioTypeGroup> TypeGroups { get; set; }
private Dictionary<uint, AmbientModelSet> PropSets { get; set; }
private Dictionary<uint, AmbientModelSet> PedModelSets { get; set; }
private Dictionary<uint, AmbientModelSet> VehicleModelSets { get; set; }
@ -1514,6 +1535,7 @@ namespace CodeWalker.World
lock (SyncRoot)
{
Types = LoadTypes(gfc, "common:\\data\\ai\\scenarios.meta");
TypeGroups = LoadTypeGroups(gfc, "common:\\data\\ai\\scenarios.meta");
PropSets = LoadModelSets(gfc, "common:\\data\\ai\\propsets.meta");
PedModelSets = LoadModelSets(gfc, "common:\\data\\ai\\ambientpedmodelsets.meta");
VehicleModelSets = LoadModelSets(gfc, "common:\\data\\ai\\vehiclemodelsets.meta");
@ -1594,6 +1616,40 @@ namespace CodeWalker.World
return types;
}
private Dictionary<uint, ScenarioTypeGroup> LoadTypeGroups(GameFileCache gfc, string filename)
{
Dictionary<uint, ScenarioTypeGroup> types = new Dictionary<uint, ScenarioTypeGroup>();
var xml = LoadXml(gfc, filename);
if ((xml == null) || (xml.DocumentElement == null))
{
return types;
}
var typesxml = xml.DocumentElement;
var items = typesxml.SelectNodes("ScenarioTypeGroups/Item");
foreach (XmlNode item in items)
{
ScenarioTypeGroup group = new ScenarioTypeGroup();
group.Load(item);
if (!string.IsNullOrEmpty(group.NameLower))
{
JenkIndex.Ensure(group.NameLower);
uint hash = JenkHash.GenHash(group.NameLower);
types[hash] = group;
}
else
{ }
}
JenkIndex.Ensure("none");
return types;
}
private Dictionary<uint, AmbientModelSet> LoadModelSets(GameFileCache gfc, string filename)
{
Dictionary<uint, AmbientModelSet> sets = new Dictionary<uint, AmbientModelSet>();
@ -1666,6 +1722,16 @@ namespace CodeWalker.World
return st;
}
}
public ScenarioTypeGroup GetScenarioTypeGroup(uint hash)
{
lock (SyncRoot)
{
if (TypeGroups == null) return null;
ScenarioTypeGroup tg;
TypeGroups.TryGetValue(hash, out tg);
return tg;
}
}
public AmbientModelSet GetPropSet(uint hash)
{
lock (SyncRoot)
@ -1733,6 +1799,14 @@ namespace CodeWalker.World
return Types.Values.ToArray();
}
}
public ScenarioTypeGroup[] GetScenarioTypeGroups()
{
lock (SyncRoot)
{
if (TypeGroups == null) return null;
return TypeGroups.Values.ToArray();
}
}
public AmbientModelSet[] GetPropSets()
{
lock (SyncRoot)
@ -1768,6 +1842,69 @@ namespace CodeWalker.World
}
/// <summary>
/// Represents a scenario type that may either be a <see cref="ScenarioType"/> or a <see cref="ScenarioTypeGroup"/>.
/// Used with CScenarioChainingNode and CScenarioPoint.
/// </summary>
[TypeConverter(typeof(ExpandableObjectConverter))] public struct ScenarioTypeRef
{
public string Name => IsGroup ? Group.Name : Type.Name;
public string NameLower => IsGroup ? Group.NameLower : Type.NameLower;
public MetaHash NameHash => IsGroup ? Group.NameHash : Type.NameHash;
public bool IsVehicle => IsGroup ? false : Type.IsVehicle; // groups don't support vehicle infos, so always false
public string VehicleModelSet => IsGroup ? null : Type.VehicleModelSet;
public MetaHash VehicleModelSetHash => IsGroup ? 0 : Type.VehicleModelSetHash;
public bool IsGroup { get; }
public ScenarioType Type { get; }
public ScenarioTypeGroup Group { get; }
public ScenarioTypeRef(ScenarioTypeRef typeRef)
{
IsGroup = typeRef.IsGroup;
Type = typeRef.Type;
Group = typeRef.Group;
}
public ScenarioTypeRef(ScenarioType type)
{
IsGroup = false;
Type = type;
Group = null;
}
public ScenarioTypeRef(ScenarioTypeGroup group)
{
IsGroup = true;
Type = null;
Group = group;
}
public override string ToString()
{
return Name;
}
public override bool Equals(object obj)
{
return obj is ScenarioTypeRef other && other == this;
}
public override int GetHashCode()
{
return NameHash.GetHashCode();
}
public static bool operator ==(ScenarioTypeRef a, ScenarioTypeRef b)
{
return a.NameHash == b.NameHash;
}
public static bool operator !=(ScenarioTypeRef a, ScenarioTypeRef b)
{
return a.NameHash != b.NameHash;
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class ScenarioType
{
@ -1813,6 +1950,29 @@ namespace CodeWalker.World
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class ScenarioTypeGroup
{
public string OuterXml { get; set; }
public string Name { get; set; }
public string NameLower { get; set; }
public MetaHash NameHash { get; set; }
public void Load(XmlNode node)
{
OuterXml = node.OuterXml;
Name = Xml.GetChildInnerText(node, "Name");
NameLower = Name.ToLowerInvariant();
NameHash = JenkHash.GenHash(NameLower);
}
public override string ToString()
{
return Name;
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class AmbientModelSet
{

View File

@ -133,6 +133,9 @@ namespace CodeWalker.Project.Panels
var stypes = types.GetScenarioTypes();
if (stypes == null) return;
var stgroups = types.GetScenarioTypeGroups();
if (stgroups == null) return;
var pmsets = types.GetPedModelSets();
if (pmsets == null) return;
@ -147,9 +150,17 @@ namespace CodeWalker.Project.Panels
ScenarioChainNodeTypeComboBox.Items.Add("");
foreach (var stype in stypes)
{
ScenarioPointTypeComboBox.Items.Add(stype);
ScenarioClusterPointTypeComboBox.Items.Add(stype);
ScenarioChainNodeTypeComboBox.Items.Add(stype);
ScenarioTypeRef? typeRef = new ScenarioTypeRef(stype);
ScenarioPointTypeComboBox.Items.Add(typeRef);
ScenarioClusterPointTypeComboBox.Items.Add(typeRef);
ScenarioChainNodeTypeComboBox.Items.Add(typeRef);
}
foreach (var stgroup in stgroups)
{
ScenarioTypeRef? typeRef = new ScenarioTypeRef(stgroup);
ScenarioPointTypeComboBox.Items.Add(typeRef);
ScenarioClusterPointTypeComboBox.Items.Add(typeRef);
ScenarioChainNodeTypeComboBox.Items.Add(typeRef);
}
ScenarioPointModelSetComboBox.Items.Clear();
@ -827,7 +838,7 @@ namespace CodeWalker.Project.Panels
if (populatingui) return;
if (CurrentScenarioNode == null) return;
if (CurrentScenarioNode.MyPoint == null) return;
ScenarioType stype = ScenarioPointTypeComboBox.SelectedItem as ScenarioType;
ScenarioTypeRef? stype = ScenarioPointTypeComboBox.SelectedItem as ScenarioTypeRef?;
lock (ProjectForm.ProjectSyncRoot)
{
if (CurrentScenarioNode.MyPoint.Type != stype)
@ -1711,7 +1722,7 @@ namespace CodeWalker.Project.Panels
if (populatingui) return;
if (CurrentScenarioNode == null) return;
if (CurrentScenarioNode.ChainingNode == null) return;
ScenarioType stype = ScenarioChainNodeTypeComboBox.SelectedItem as ScenarioType;
ScenarioTypeRef? stype = ScenarioChainNodeTypeComboBox.SelectedItem as ScenarioTypeRef?;
lock (ProjectForm.ProjectSyncRoot)
{
if (CurrentScenarioNode.ChainingNode.Type != stype)
@ -2133,7 +2144,7 @@ namespace CodeWalker.Project.Panels
if (populatingui) return;
if (CurrentScenarioNode == null) return;
if (CurrentScenarioNode.ClusterMyPoint == null) return;
ScenarioType stype = ScenarioClusterPointTypeComboBox.SelectedItem as ScenarioType;
ScenarioTypeRef? stype = ScenarioClusterPointTypeComboBox.SelectedItem as ScenarioTypeRef?;
lock (ProjectForm.ProjectSyncRoot)
{
if (CurrentScenarioNode.ClusterMyPoint.Type != stype)

View File

@ -4266,7 +4266,7 @@ namespace CodeWalker.Project
var action = Unk_3609807418.Move;
var navMode = Unk_3971773454.Direct;
var navSpeed = Unk_941086046.Unk_00_3279574318;
var stype = defaulttype;
var stype = new ScenarioTypeRef(defaulttype);
var modelset = defaultmodelset;
var flags = defaultflags;
var ok = true;
@ -4298,7 +4298,23 @@ namespace CodeWalker.Project
if (vals.Length > 6)
{
var sthash = JenkHash.GenHash(vals[6].Trim().ToLowerInvariant());
stype = stypes?.GetScenarioType(sthash) ?? defaulttype;
var st = stypes?.GetScenarioType(sthash);
if (st != null)
{
stype = new ScenarioTypeRef(st);
}
else
{
var stg = stypes?.GetScenarioTypeGroup(sthash);
if (stg != null)
{
stype = new ScenarioTypeRef(stg);
}
else
{
stype = new ScenarioTypeRef(defaulttype);
}
}
}
if (vals.Length > 7)
{
@ -4326,7 +4342,7 @@ namespace CodeWalker.Project
thisnode.ChainingNode.ScenarioNode = thisnode;
thisnode.ChainingNode.Chain = chain;
thisnode.ChainingNode.Type = stype;
thisnode.ChainingNode.TypeHash = stype?.NameHash ?? 0;
thisnode.ChainingNode.TypeHash = stype.NameHash;
thisnode.ChainingNode.NotLast = (i < (lines.Length - 1));
thisnode.ChainingNode.NotFirst = (lastnode != null);