Scale World Map Entities
Huge thanks to Iberian Wolf for doing majority of the research where info on how to scale the entities is stored and order_without_power for helping me to solve ranged siege engines scaling in the code.
My goal was to scale down World Map entities for the map to feel larger.
Towns/castles/villages
Towns/castles/villages can be easily scaled in the Editor using the Inspector - Transform tab:
or 'y' for scale controls, or holding 'b' when the entity is selected.
People (characters)
0.3 scale is hard coded for the people in the PartyVisual.AddCharacterToPartyIcon. I used such Harmony Transpiler to change it to 0.15 (half the size):
[HarmonyPatch(typeof(PartyVisual), "AddCharacterToPartyIcon")]
public static class ScalePatch
{
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var newInstructions = new List<CodeInstruction>(instructions);
for (int i = 0; i < newInstructions.Count; i++)
{
if (newInstructions[i].opcode == OpCodes.Callvirt && newInstructions[i].operand is MethodInfo method && method.Name == "Scale")
{
if (newInstructions[i - 1].opcode == OpCodes.Ldc_R4 && (float)newInstructions[i - 1].operand == 0.3f)
{
newInstructions[i - 1].operand = 0.15f;
break;
}
}
}
return newInstructions;
}
}
Result applying only this patch:
Mounts
0.3 hardcoded into PartyVisual.AddCharacterToPartyIcon. Transpiler to change it to 0.15:
[HarmonyPatch(typeof(PartyVisual), "AddCharacterToPartyIcon")]
public static class MountScalePatch
{
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var newInstructions = new List<CodeInstruction>(instructions);
for (int i = 0; i < newInstructions.Count; i++)
{
if (newInstructions[i].opcode == OpCodes.Mul && newInstructions[i - 1].opcode == OpCodes.Ldc_R4)
{
if ((float)newInstructions[i - 1].operand == 0.3f)
{
newInstructions[i - 1].operand = 0.15f;
break;
}
}
}
return newInstructions;
}
}
Result applying only this patch:
Caravan animals
In PartyVisual.AddMountToPartyIcon changing 0.3 -> 0.15
[HarmonyPatch(typeof(PartyVisual), "AddMountToPartyIcon")]
public static class ScaleMountPatch
{
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var newInstructions = new List<CodeInstruction>(instructions);
for (int i = 0; i < newInstructions.Count; i++)
{
if (newInstructions[i].opcode == OpCodes.Mul && newInstructions[i - 1].opcode == OpCodes.Ldc_R4)
{
if ((float)newInstructions[i - 1].operand == 0.3f)
{
newInstructions[i - 1].operand = 0.15f;
break; // Break out of the loop once we've made our change
}
}
}
return newInstructions;
}
}
Ranged Siege Engines
I have tried to scale siege engines in the Blender and reimport the meshes. That way siege engines are scaled, but their animations are not.
How it looks:
Swan [ADOD/IAF]:
This is an animation issue, although you scaled the object and skeleton, the animation is still keyframed to a set radius.
This patch scales down only ranged siege engines (ballistas, mangonels, trebuchets) with their animations intact:
[HarmonyPatch(typeof(PartyVisual))]
[HarmonyPatch("AddSiegeMachine")]
public class PartyVisual_AddSiegeMachine_Patch
{
public static bool Prefix(SiegeEngineType type, MatrixFrame globalFrame, BattleSideEnum side, int wallLevel, int slotIndex, PartyVisual __instance)
{
// do not apply this patch for not-ranged siege engines to avoid the crash NullReferenceException in PartyVisual.TickSettlementVisual
if (!type.IsRanged) return true;
string siegeEngineMapPrefabName = Campaign.Current.Models.SiegeEventModel.GetSiegeEngineMapPrefabName(type, wallLevel, side);
// Use reflection to get the PropertyInfo object for the 'MapScene' property
PropertyInfo mapScenePropInfo = __instance.GetType().GetProperty("MapScene", BindingFlags.NonPublic | BindingFlags.Instance);
if (mapScenePropInfo == null) return true;
Scene mapScene = (Scene)mapScenePropInfo.GetValue(__instance);
GameEntity gameEntity = GameEntity.Instantiate(mapScene, siegeEngineMapPrefabName, true);
if (gameEntity != null)
{
__instance.StrategicEntity.AddChild(gameEntity, false);
MatrixFrame matrixFrame;
gameEntity.GetFrame(out matrixFrame);
// scaling here
matrixFrame.Scale(matrixFrame.GetScale() * 0.5f);
gameEntity.SetFrame(ref matrixFrame);
GameEntity gameEntity2 = gameEntity;
MatrixFrame matrixFrame2 = globalFrame.TransformToParent(matrixFrame);
gameEntity2.SetGlobalFrame(matrixFrame2);
List<GameEntity> list = new List<GameEntity>();
gameEntity.GetChildrenRecursive(ref list);
GameEntity gameEntity3 = null;
if (list.Any((GameEntity entity) => entity.HasTag("siege_machine_mapicon_skeleton")))
{
GameEntity gameEntity4 = list.Find((GameEntity entity) => entity.HasTag("siege_machine_mapicon_skeleton"));
if (gameEntity4.Skeleton != null)
{
gameEntity3 = gameEntity4;
// scaling the skeleton
gameEntity3.SetFrame(ref matrixFrame);
string siegeEngineMapFireAnimationName = Campaign.Current.Models.SiegeEventModel.GetSiegeEngineMapFireAnimationName(type, side);
gameEntity3.Skeleton.SetAnimationAtChannel(siegeEngineMapFireAnimationName, 0, 1f, 0f, 1f);
}
}
// Access the private readonly field '_siegeRangedMachineEntities'
var fieldInfo = AccessTools.Field(__instance.GetType(), "_siegeRangedMachineEntities");
var siegeRangedMachineEntities = (List<ValueTuple<GameEntity, BattleSideEnum, int, MatrixFrame, GameEntity>>)fieldInfo.GetValue(__instance);
if (type.IsRanged)
{
siegeRangedMachineEntities.Add(ValueTuple.Create<GameEntity, BattleSideEnum, int, MatrixFrame, GameEntity>(gameEntity, side, slotIndex, globalFrame, gameEntity3));
return false;
}
siegeRangedMachineEntities.Add(ValueTuple.Create<GameEntity, BattleSideEnum, int, MatrixFrame, GameEntity>(gameEntity, side, slotIndex, globalFrame, gameEntity3));
}
return false;
}
}
Sadly this patch crashes when applied to the not-ranged siege engines.
Not-ranged Siege Engines and Projectiles
To scale down not-ranged siege engines I scaled them down in the Blender and reimported them into the Editor.
I think it's possible to scale them down the same way as ranged siege engines, but I spent too much time till this moment on this problem and don't want to look for a better method. If somebody will fix my previous patch to not crash for not-ranged siege engines - please let me know. I will update this guide.
Also, I think Transpiler would be better in this case, but again - maybe in the future.
This is how imported and scaled-down entities look in the Resource Browser: