static IEnumerable <CodeInstruction> RefreshPreviews(IEnumerable <CodeInstruction> instructions, ILGenerator generator) { float MirrorBuildingRotation(float yaw, BlueprintBuilding building) { if (buildingsAxis.ContainsKey(building.modelIndex) && buildingsAxis[building.modelIndex] == MajorAxis.XAXIS) { return(MirrorRotation(yaw + 90f) - 90f); } return(MirrorRotation(yaw)); } CodeMatcher matcher = new CodeMatcher(instructions, generator) .MatchForward(true, new CodeMatch(OpCodes.Ldelem, typeof(Vector4)), new CodeMatch(OpCodes.Stloc_S), new CodeMatch(OpCodes.Ldloca_S)); matcher.Advance(1); object longAxisVar = matcher.Operand; while (matcher.Opcode != OpCodes.Stfld) { matcher.RemoveInstruction(); } matcher.RemoveInstructions(2); object latAxisVar = matcher.Operand; while (matcher.Opcode != OpCodes.Stfld) { matcher.RemoveInstruction(); } matcher.RemoveInstruction() .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, longAxisVar)) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, latAxisVar)) .InsertAndAdvance(Transpilers.EmitDelegate <RefAction>(MirrorArea)); matcher.MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(BlueprintUtils), nameof(BlueprintUtils.TransitionWidthAndHeight)))) .Advance(-3) .RemoveInstruction().RemoveInstruction().RemoveInstruction() .SetInstruction(Transpilers.EmitDelegate <Func <float, BlueprintBuilding, Vector2> >((yaw, building) => { float x = building.localOffset_x; float y = building.localOffset_y; if ((mirrorLat && !mirrorLong || !mirrorLat && mirrorLong) && buildingsOffsets.ContainsKey(building.modelIndex)) { Vector2 offset = buildingsOffsets[building.modelIndex]; float rotatedYaw = MirrorBuildingRotation(building.yaw, building); offset = offset.Rotate(rotatedYaw + (mirrorLong ? 180 : 0)); x += offset.x; y += offset.y; } return(BlueprintUtils.TransitionWidthAndHeight(yaw, x, y)); })); matcher.MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BlueprintBuilding), nameof(BlueprintBuilding.yaw))) ).Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 30)) .InsertAndAdvance(Transpilers.EmitDelegate <Func <float, BlueprintBuilding, float> >(MirrorBuildingRotation)).Advance(2); matcher.MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BlueprintBuilding), nameof(BlueprintBuilding.yaw2))) ).Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 30)) .InsertAndAdvance(Transpilers.EmitDelegate <Func <float, BlueprintBuilding, float> >(MirrorBuildingRotation)); matcher.MatchForward(true, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(BuildPreview), nameof(BuildPreview.desc))), new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PrefabDesc), nameof(PrefabDesc.isInserter)))) .Advance(2); object previewVar2 = matcher.Operand; matcher.Advance(1) .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, previewVar2)) .InsertAndAdvance(Transpilers.EmitDelegate <Action <BuildPreview> >(preview => { if (preview.input != null && preview.input.desc.slotPoses.Length > preview.inputFromSlot && !preview.input.desc.isBelt) { Quaternion invRot = Quaternion.Inverse(preview.input.lrot); Vector3 slotPosition = preview.lpos - preview.input.lpos; slotPosition = invRot * slotPosition; Quaternion slotRotation = invRot * preview.lrot; for (int i = 0; i < preview.input.desc.slotPoses.Length; i++) { Pose slotPose = preview.input.desc.slotPoses[i]; if (!((slotPose.position - slotPosition).sqrMagnitude < 0.1f)) { continue; } if (!slotPose.rotation.Approximately(slotRotation)) { continue; } if (preview.inputFromSlot == i) { break; } preview.inputFromSlot = i; break; } } if (preview.output != null && preview.output.desc.slotPoses.Length > preview.outputToSlot && !preview.output.desc.isBelt) { Quaternion invRot = Quaternion.Inverse(preview.output.lrot); Vector3 slotPosition = preview.lpos2 - preview.output.lpos; slotPosition = invRot * slotPosition; Quaternion slotRotation = invRot * (preview.lrot2 * Quaternion.Euler(0f, -180f, 0f)); for (int i = 0; i < preview.output.desc.slotPoses.Length; i++) { Pose slotPose = preview.output.desc.slotPoses[i]; if (!((slotPose.position - slotPosition).sqrMagnitude < 0.1f)) { continue; } if (!slotPose.rotation.Approximately(slotRotation)) { continue; } if (preview.outputToSlot == i) { break; } preview.outputToSlot = i; break; } } })); return(matcher.InstructionEnumeration()); }
static void PatchClearFromConstructionObstacles(CodeMatcher codeCursor, ILGenerator generator) { codeCursor.Start(); codeCursor.MatchForward(false, new CodeMatch(OpCodes.Ldloc_1), new CodeMatch(OpCodes.Ldloc_3), new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(List <GameObject>), nameof(List <GameObject> .Count))), new CodeMatch(OpCodes.Ldc_I4_0), new CodeMatch(OpCodes.Ceq), new CodeMatch(OpCodes.And), new CodeMatch(OpCodes.Stloc_1) ); if (codeCursor.IsValid) { // Remove boolean result assignent as we are "moving" it to after obstacle highlighting block var labels = codeCursor.Instruction.ExtractLabels(); codeCursor.RemoveInstructions(7); codeCursor.AddLabels(labels); codeCursor.MatchForward(true, new CodeMatch(OpCodes.Ldloc_3), new CodeMatch(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(List <GameObject>), nameof(List <GameObject> .Count))), new CodeMatch(OpCodes.Ldc_I4_0), new CodeMatch(OpCodes.Ble) ); if (codeCursor.IsValid) { var countCondBranchOperand = (Label)codeCursor.Operand; codeCursor.MatchForward(false, new CodeMatch(instruction => instruction.labels.Contains(countCondBranchOperand))); if (codeCursor.IsValid) { labels = codeCursor.Instruction.ExtractLabels(); // Make it to allow construction by clearing contruction obstacles for boolean result assignment if destroying obstacles is enabled var disabledDestroyObstaclesLabel = generator.DefineLabel(); codeCursor.InsertAndAdvance( new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(Config), nameof(Config.Instance))).WithLabels(labels), new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(Config), nameof(Config.destroyLargerObstaclesOnConstruction))), new CodeInstruction(OpCodes.Brfalse_S, disabledDestroyObstaclesLabel) ); codeCursor.InsertAndAdvance( new CodeInstruction(OpCodes.Ldloc_3), new CodeInstruction(OpCodes.Call, AccessTools.Method($"{typeof(UpdateAllowedPatch)}:{nameof(ClearConstructionObstacles)}", new Type[] { typeof(List <GameObject>) })) ); // Assign boolean result here (was before highlighting block) codeCursor.InsertAndAdvance( new CodeInstruction(OpCodes.Ldloc_1).WithLabels(disabledDestroyObstaclesLabel), new CodeInstruction(OpCodes.Ldloc_3), new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(List <GameObject>), nameof(List <GameObject> .Count))), new CodeInstruction(OpCodes.Ldc_I4_0), new CodeInstruction(OpCodes.Ceq), new CodeInstruction(OpCodes.And), new CodeInstruction(OpCodes.Stloc_1) ); } } } }