To add more detail here:
LINQ is a performance issue because it tends to create a lot of short-lived objects that need to be garbage collected. In a tight loop where the loop needs to run as quickly as possible on the CPU, the work done collecting that garbage can create more work than the time spent on the loop itself. Unfortunately for games, the game loop itself is a tight loop!
I use ReSharper's Heap Allocation Viewer plugin when I'm writing C# code to keep track of stuff like that and make sure I'm only using LINQ in code that doesn't run every frame, e.g. initialization or in response to loading a new resource at run-time for debugging.
That code you're looking at is decompiled, meaning that an algorithm has turned something that isn't human readable into what you're seeing. It's not necessarily what the original code looked like.
To use another example, consider
C#:public override float GetScoreOfKingdomToGetClan(Kingdom kingdom, Clan clan) { float num1 = Math.Min(2f, Math.Max(0.33f, (float) (1.0 + 0.0199999995529652 * (double) FactionManager.GetRelationBetweenClans(kingdom.RulingClan, clan)))); float num2 = (float) (1.0 + (kingdom.Culture == clan.Culture ? 1.0 : 0.0)); int num3 = clan.CommanderHeroes != null ? clan.CommanderHeroes.Count<Hero>() : 0; float num4 = (float) (((double) clan.TotalStrength + 150.0 * (double) num3) * 10.0); float powerRatioToEnemies = FactionHelper.GetPowerRatioToEnemies(kingdom); float reliabilityConstant = HeroHelper.CalculateReliabilityConstant(clan.Leader, 1f); float num5 = 1f / Math.Max(0.4f, Math.Min(2.5f, (float) Math.Sqrt((double) powerRatioToEnemies))); float num6 = num4 * num5; return (clan.CalculateSettlementValue(kingdom) * 0.1f + num6) * num1 * num2 * reliabilityConstant; }
It's possible that this code is fairly close to what was in the game's original code.
It's also possible that it actually looked something more like:
C#:public override float GetScoreOfKingdomToGetClan(Kingdom kingdom, Clan clan) { var numClanHeroes = clan.CommanderHeroes.Count; var existingHeroesInKingdom = GetNumberOfHeroesInKingdom(kingdom); if ((numClanHeroes == 0) || (existingHeroesInKingdom == 0)) { return -1E+08f; } // Start with a basis of 0. var valueProposition = 0.0f; // Add value for the settlements the clan will bring to the kingdom, and the adjusted clan strength. // Adjusted clan strength is the clan strength ~ how badly the kingdom needs allies. var clanStrength = GetClanStrength(clan, numClanHeroes); var powerRatioToEnemies = FactionHelper.GetPowerRatioToEnemies(kingdom); var howBadlyDoesThisKingdomNeedSupport = 1f / Clamp(powerRatioToEnemies > 1 ? (float)Math.Sqrt(powerRatioToEnemies) : powerRatioToEnemies, 0.1f, 2.5f); var adjustedClanStrength = clanStrength * howBadlyDoesThisKingdomNeedSupport; valueProposition += clan.CalculateSettlementValue(kingdom) * 0.1f + adjustedClanStrength; // Increase the value for happier relations. var relationMult = Clamp(1.0f + 0.02f * FactionManager.GetRelationBetweenClans(kingdom.RulingClan, clan), 0.33f, 2f); valueProposition *= relationMult; // Increase the value for the same culture. var sameCultureMult = (float)(1.0 + (kingdom.Culture == clan.Culture ? 1.0 : 0.0)); valueProposition *= sameCultureMult; // Increase the value if the lord is known to be honorable. var reliabilityConstant = HeroHelper.CalculateReliabilityConstant(clan.Leader); valueProposition *= reliabilityConstant; return valueProposition; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float Clamp(float value, float min, float max) { return Math.Min(max, Math.Max(min, value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetNumberOfHeroesInKingdom(Kingdom kingdom) { var numHeroes = 0; foreach (var clan in kingdom.Clans) { if (clan.IsMinorFaction && clan != Clan.PlayerClan) { continue; } numHeroes += clan.CommanderHeroes.Count; } return numHeroes; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float GetClanStrength(Clan clan, int numClanHeroes) { return (clan.TotalStrength + 150.0f * numClanHeroes) * 10.0f; }
The decompiled code could have lost variable names, or added new intermediate values. There may be function calls missing because they were inlined. There won't be any comments. The spacing and separation won't look like a human did it.
This function CalculateStrikeMagnitudeForSwing that you're looking at is actually used by a class called DefaultStrikeMagnitudeModel. While the function isn't immediately modifiable, the class can be entirely replaced. Once you do so, everything else in the game will use your class instead. That is incredibly powerful. It's as easy as creating a new class that inherits from DefaultStrikeMagnitudeModel and overriding whatever methods you want. You could replace the swing calculation, or the thrust calculation, or anything.
It is common in game development to write your code in one language (e.g. C++, Java, or C#) and then support reading from a data file at run-time (XML, JSON, other custom text formats) so that you can quickly tweak or iterate on specific numbers, including multipliers.
C# has a syntax much like all other typed languages. It's comparable to Java, C, C++, TypeScript, and others. If you've only used a dynamic language like Python or Lua I can understand why it might be surprising to you.
The architecture of the game engine seems fine to me. I've only made two mods so far but in both cases I was pleasantly surprised by the organization of the code and thought given to decoupling components from each other. It's been very easy to reach in and make changes by swapping out a model or an XML file and the game's care given to the mod-focused architecture is what makes that possible, e.g. the loop that finds game models by searching backwards through the list of all the models grabbed from modules during init, or the use of reflection for the data-binding between the UI schema in XML and the view models in the game.
I think what's happening here is that for many people this is their first experience with a large, complex codebase. With a dedicated sandbox for game scripting, that complexity is hidden from you. Unfortunately, the only way to hide that complexity is by making assumptions about what modders will want or need for you. One way to avoid that is to "dog-food" your system, meaning to use the system you expose for making mods to make your game. That's what TaleWorlds has done here. The core game is a module loader. The native game is a module. You'll get truly incredible mods from this design.
It'll get easier to learn as you see more mods demonstrate how to replace or modify different parts of the code.
^^^