diff --git a/src/CS2/Generators/GameEventsGenerator.cs b/src/CS2/Generators/GameEventsGenerator.cs
index 75c11c4..2f34131 100644
--- a/src/CS2/Generators/GameEventsGenerator.cs
+++ b/src/CS2/Generators/GameEventsGenerator.cs
@@ -29,6 +29,22 @@ public class GameEvents : BaseGenerator
{ "ehandle", ("nint", "Ptr", true, null) }
};
+ ///
+ /// Force a specific C# type for a given event field, overriding the type declared in the .gameevents file.
+ ///
+ private static readonly Dictionary<(string Event, string Field), (string CsType, string Accessor, bool CanSet, string? CastKind)> FieldOverrides = new()
+ {
+ { ("player_hurt", "health"), ("int", "Int", true, null) },
+ { ("player_hurt", "armor"), ("int", "Int", true, null) },
+ { ("player_hurt", "dmg_health"), ("int", "Int", true, null) },
+ { ("player_hurt", "dmg_armor"), ("int", "Int", true, null) },
+ { ("player_hurt", "hitgroup"), (CS2SchemaType.HitGroup_t, "Int", true, CS2SchemaType.HitGroup_t) },
+
+ { ("player_death", "dmg_health"), ("int", "Int", true, null) },
+ { ("player_death", "dmg_armor"), ("int", "Int", true, null) },
+ { ("player_death", "hitgroup"), (CS2SchemaType.HitGroup_t, "Int", true, CS2SchemaType.HitGroup_t) },
+ };
+
private static readonly HashSet SkipTypes = new() { "none", "local" };
private static readonly Regex EventNameRegex = new(@"^\s*""([^""]+)""");
@@ -362,7 +378,7 @@ private void GenerateInterface(GameEventDef ev, string outputDir)
});
var filePath = Path.Combine(outputDir, $"{typeName}.cs");
- File.WriteAllText(filePath, writer.GetCode() + "\n");
+ File.WriteAllText(filePath, writer.GetCode("\r\n") + "\r\n");
}
private void GenerateClass(GameEventDef ev, string outputDir)
@@ -393,7 +409,7 @@ private void GenerateClass(GameEventDef ev, string outputDir)
});
var filePath = Path.Combine(outputDir, $"{typeName}Impl.cs");
- File.WriteAllText(filePath, writer.GetCode() + "\n");
+ File.WriteAllText(filePath, writer.GetCode("\r\n") + "\r\n");
}
private void RenderHeaderComment(CodeWriter writer, GameEventDef ev)
@@ -421,11 +437,12 @@ private void RenderInterfaceFields(CodeWriter writer, GameEventDef ev)
continue;
}
- if (!TypeMap.TryGetValue(ftype, out var typeInfo))
+ if (!TypeMap.TryGetValue(ftype, out var defaultInfo))
continue;
- var (csType, _, canSet, _) = typeInfo;
+ var (csType, _, canSet, _) = defaultInfo;
var propName = GetUniquePropName(ToPropertyName(fname), usedPropNames);
+ var hasOverride = FieldOverrides.TryGetValue((ev.Name, fname), out var ov);
writer.AddLine();
writer.AddLine("/// ");
@@ -435,8 +452,32 @@ private void RenderInterfaceFields(CodeWriter writer, GameEventDef ev)
writer.AddLine("///
");
}
writer.AddLine($"/// type: {ftype}");
+ if (hasOverride)
+ {
+ writer.AddLine("///
");
+ writer.AddLine($"/// See .");
+ }
writer.AddLine("/// ");
+ if (hasOverride)
+ writer.AddLine($"[Obsolete(\"The declared type may not match the actual value. Use Actual{propName} instead.\")]");
writer.AddLine($"{csType} {propName} {{ get; {(canSet ? "set; " : "")}}}");
+
+ if (hasOverride)
+ {
+ var (ovCsType, _, ovCanSet, _) = ov;
+ var actualName = GetUniquePropName($"Actual{propName}", usedPropNames);
+
+ writer.AddLine();
+ writer.AddLine("/// ");
+ if (!string.IsNullOrEmpty(fdef.Comment))
+ {
+ writer.AddLine($"/// {fdef.Comment}");
+ writer.AddLine("///
");
+ }
+ writer.AddLine($"/// type: {ovCsType}");
+ writer.AddLine("/// ");
+ writer.AddLine($"{ovCsType} {actualName} {{ get; {(ovCanSet ? "set; " : "")}}}");
+ }
}
}
@@ -500,13 +541,13 @@ private void RenderClassFields(CodeWriter writer, GameEventDef ev)
continue;
}
- if (!TypeMap.TryGetValue(ftype, out var typeInfo))
+ if (!TypeMap.TryGetValue(ftype, out var defaultInfo))
continue;
- var (csType, accessor, canSet, castKind) = typeInfo;
+ var (csType, accessor, canSet, castKind) = defaultInfo;
var propName = GetUniquePropName(ToPropertyName(fname), usedPropNames);
-
var (getter, setter) = BuildAccessors(fname, accessor, castKind);
+ var hasOverride = FieldOverrides.TryGetValue((ev.Name, fname), out var ov);
writer.AddLine();
if (!string.IsNullOrEmpty(fdef.Comment))
@@ -516,6 +557,22 @@ private void RenderClassFields(CodeWriter writer, GameEventDef ev)
writer.AddLine($"{{ get => {getter}; set => {setter}; }}");
else
writer.AddLine($"{{ get => {getter}; }}");
+
+ if (hasOverride)
+ {
+ var (ovCsType, ovAccessor, ovCanSet, ovCastKind) = ov;
+ var actualName = GetUniquePropName($"Actual{propName}", usedPropNames);
+ var (ovGetter, ovSetter) = BuildAccessors(fname, ovAccessor, ovCastKind);
+
+ writer.AddLine();
+ if (!string.IsNullOrEmpty(fdef.Comment))
+ writer.AddLine($"// {fdef.Comment}");
+ writer.AddLine($"public {ovCsType} {actualName}");
+ if (ovCanSet && ovSetter != null)
+ writer.AddLine($"{{ get => {ovGetter}; set => {ovSetter}; }}");
+ else
+ writer.AddLine($"{{ get => {ovGetter}; }}");
+ }
}
}
@@ -564,6 +621,7 @@ private void RenderClassPlayerField(CodeWriter writer, string fname, EventField
"Bool" => ($"Accessor.GetBool(\"{fname}\")", $"Accessor.SetBool(\"{fname}\", value)"),
"Int" when castKind == "byte" => ($"(byte)Accessor.GetInt32(\"{fname}\")", $"Accessor.SetInt32(\"{fname}\", value)"),
"Int" when castKind == "short" => ($"(short)Accessor.GetInt32(\"{fname}\")", $"Accessor.SetInt32(\"{fname}\", value)"),
+ "Int" when castKind != null => ($"({castKind})Accessor.GetInt32(\"{fname}\")", $"Accessor.SetInt32(\"{fname}\", (int)value)"),
"Int" => ($"Accessor.GetInt32(\"{fname}\")", $"Accessor.SetInt32(\"{fname}\", value)"),
"Uint64" => ($"Accessor.GetUInt64(\"{fname}\")", $"Accessor.SetUInt64(\"{fname}\", value)"),
"Float" => ($"Accessor.GetFloat(\"{fname}\")", $"Accessor.SetFloat(\"{fname}\", value)"),
@@ -674,6 +732,10 @@ public EventField(string name, string typeName, string comment)
}
}
+internal static class CS2SchemaType {
+ public const string HitGroup_t = "HitGroup_t";
+}
+
internal class GameEventDef
{
public string Name { get; }