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; }