using Arch.Core; using HSchool.Ai; using HSchool.Content; namespace HSchool.Simulation; /// /// Drains Warmth from the gap between clothing insulation and the temperature of the /// place the person is in. Off campus the generic need restore already snaps it to max. /// public static class WarmthDecay { private static readonly QueryDescription People = new QueryDescription().WithAll(); public static void Apply(School school, double gameMinutes) { if (gameMinutes <= 0) { return; } var catalog = school.Catalog; if (catalog is null || !catalog.Needs.TryGetValue("Warmth", out var warmth) || warmth.Abstract) { return; } var preset = Preset(school, catalog); var hours = gameMinutes / 60d; var world = school.World; world.Query( in People, (ref PersonNeeds needs, ref PersonTraits traits, ref PersonInsulation insulation, ref Presence presence) => { if (!presence.IsOnCampus || !needs.Values.ContainsKey(warmth.DefName)) { return; } var place = PlaceClimate.TemperatureC(school, presence.NodeId); var felt = place + insulation.Value * (preset?.InsulationPerC ?? 1f); var center = (preset?.ComfortC ?? 21f) + TraitOffset(catalog, traits); var halfWidth = preset?.ComfortHalfWidthC ?? 3f; var mismatch = Math.Abs(felt - center) - halfWidth; if (mismatch <= 0) { return; } var next = needs.Values[warmth.DefName] - (float)(mismatch * warmth.DecayPerHour * hours); needs.Values[warmth.DefName] = Math.Clamp(next, warmth.Min, warmth.Max); }); } private static ClimatePresetDef? Preset(School school, DefCatalog catalog) { if (school.ClimatePresetId is { } id && catalog.ClimatePresets.TryGetValue(id, out var preset) && !preset.Abstract) { return preset; } return null; } private static float TraitOffset(DefCatalog catalog, PersonTraits traits) { var offset = 0f; foreach (var id in traits.Ids) { if (catalog.Traits.TryGetValue(id, out var trait)) { offset += trait.ComfortTemperatureOffset; } } return offset; } }