예제 #1
0
        public static void FpsModeThread(MainWindow w)
        {
            int processHandle = w.GetRayman2ProcessHandle();

            int bytesReadOrWritten = 0; // Required somehow

            byte[] buffer = new byte[4];

            int off_DNM_p_stDynamicsCameraMechanics = 0x4359D0; // original byte = 81, replaced by ret = C3
            int off_ForceCameraPos = 0x473420;                  // original byte = 53, replaced by ret = C3
            int off_ForceCameraTgt = 0x473480;                  // original byte = 83, replaced by ret = C3

            byte[] fixePositionPersoHackOriginal = new byte[] { 0x74, 0x16, 0x6A, 0x00, 0x6A, 0x28, 0xE8, 0x39, 0x50, 0x01, 0x00 };
            byte[] fixePositionPersoHackModified = new byte[] { 0x90, 0x90, 0x6A, 0x90, 0x6A, 0x28, 0x90, 0x90, 0x90, 0x90, 0x90 };

            buffer = new byte[] { 0xC3 };
            Memory.WriteProcessMemory(processHandle, off_DNM_p_stDynamicsCameraMechanics, buffer, buffer.Length, ref bytesReadOrWritten);
            Memory.WriteProcessMemory(processHandle, off_ForceCameraPos, buffer, buffer.Length, ref bytesReadOrWritten); Memory.WriteProcessMemory(processHandle, off_DNM_p_stDynamicsCameraMechanics, buffer, buffer.Length, ref bytesReadOrWritten);
            Memory.WriteProcessMemory(processHandle, off_ForceCameraTgt, buffer, buffer.Length, ref bytesReadOrWritten);

            int off_cameraSPO    = Memory.GetPointerPath(processHandle, Constants.off_cameraArrayPointer, 0);
            int off_cameraMatrix = Memory.GetPointerPath(processHandle, Constants.off_cameraArrayPointer, 0, 0x20);

            int off_raymanCustomBits = Memory.GetPointerPath(processHandle, Constants.off_mainChar, 4, 4) + 0x24;
            int off_raymanGravity    = Memory.GetPointerPath(processHandle, Constants.off_mainChar, 4, 8, 0) + 0x10;
            int off_raymanDsgVar16   = Memory.GetPointerPath(processHandle, Constants.off_mainChar, 4, 0xC, 0, 0xC, 8) + 0x203; // rayman dsg var 16 indicates if he can be controlled
            int off_cameraFOV        = Memory.GetPointerPath(processHandle, Constants.off_cameraArrayPointer, 0, 0x4, 0x10, 0x4) + 0x5c;

            int off_raymanDynamicsMatrix = Memory.GetPointerPath((int)processHandle, 0x500578, 0x4, 0x8, 0x0) + 0x7c;

            int off_cameraPerso = Memory.GetPointerPath(processHandle, Constants.off_cameraArrayPointer, 0, 0x4);
            int off_cameraRule  = Memory.GetPointerPath(processHandle, Constants.off_cameraArrayPointer, 0, 0x4, 0xC, 0) + 0x4; // brain.mind.intelligenceNormal

            buffer = new byte[] { 0, 0, 0, 0 };
            //Memory.WriteProcessMemory(processHandle, off_cameraRule, buffer, buffer.Length, ref bytesReadOrWritten);

            //byte[] cameraMatrix = new byte[;
            byte[] matrixBuffer = new byte[188];

            Matrix matrix;

            bool tempDisable = false;

            int off_cameraTarget = Memory.GetPointerPath(processHandle, Constants.off_cameraArrayPointer, 0, 0x4, 0x10, 0xc) + 0x68;
            int off_targetFamily = Memory.GetPointerPath(processHandle, off_cameraTarget, 0x4, 0x0, 0x14);
            int familyIndex      = Memory.ReadProcessMemoryInt32(processHandle, off_targetFamily + 0xC);

            int raymanSPO = Memory.ReadProcessMemoryInt32(processHandle, off_cameraTarget);

            int[] raymanBehaviors = Utils.GetAIModelNormalBehaviorsList(processHandle, raymanSPO);

            Dictionary <int, float> bodyPartVertOffsets = new Dictionary <int, float>();

            if (familyIndex == 0)
            {
                bodyPartVertOffsets = Utils.GetFamilyPOVertOffsets(processHandle, off_targetFamily, true, new int[] { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 18, 19, 20, 21, 22, 23, 24, 25 });
            }
            else
            {
                w.fpsModeEnabled = false;
                MessageBox.Show("You can only start fps mode while you are in game and playing as Rayman.");
            }

            bool bodyPartsHidden = false;

            var objectTypes = Utils.ReadObjectTypes(processHandle);

            string lastLevelName = "";
            string levelName     = "";

            int   dockingTimer         = 0;
            int   waitForDockedTimer   = 0;
            int   dockedTimer          = 0;
            float dockingRotation      = 0;
            float dockingOffset        = 0;
            int   dockingCooldownTimer = 0;

            int levelTimer = 0;

            int off_cameraTargetX = Memory.GetPointerPath(processHandle, Constants.off_cameraArrayPointer, 0, 4, 8, 4) + 0x10;
            int off_cameraTargetY = Memory.GetPointerPath(processHandle, Constants.off_cameraArrayPointer, 0, 4, 8, 4) + 0x14;
            int off_cameraTargetZ = Memory.GetPointerPath(processHandle, Constants.off_cameraArrayPointer, 0, 4, 8, 4) + 0x18;

            int umberTimer = 0;

            float pitch = 0;
            float yaw   = 0;

            float climbingOffsetY = 0;
            float climbingOffsetZ = 0;
            float climbingPitch   = 0;
            float climbingYaw     = 0;

            float bobbin = 0;

            Quaternion rotation             = new Quaternion();
            Quaternion interpolatedRotation = new Quaternion();

            float backStrafeOffset = 0;

            bool downHeld = false;

            var inputSim = new InputSimulator();

            float carryingFloat     = 0; // smooth object carrying animations
            float barrelFlyingFloat = 0; // smooth barrel flying animations

            while (w.fpsModeEnabled)
            {
                objectTypes = Utils.ReadObjectTypes(processHandle);

                int engineMode = Memory.ReadProcessMemoryByte(processHandle, 0x500380);                 // engineMode

                bool frozen = Memory.ReadProcessMemoryByte(processHandle, 0x500FAA) > 0 ? true : false; // unfreeze game while thread sleeps

                if (engineMode != 9 && engineMode != 8)
                {
                    levelTimer = 0;
                    continue; // ignore
                }

                var activeSuperObjects             = Utils.GetActiveSuperObjectNames(processHandle, objectTypes[2]);
                var activeSuperObjectsAIModelNames = Utils.GetActiveSuperObjectAIModelNames(processHandle, objectTypes[1]);

                if (activeSuperObjectsAIModelNames == null)
                {
                    w.fpsModeEnabled = false;
                    tempDisable      = true;
                }

                levelTimer++;

                levelName = Utils.GetCurrentLevelName(processHandle);

                if (lastLevelName != levelName && off_cameraTarget != 0)
                {
                    lastLevelName = levelName;
                    objectTypes   = Utils.ReadObjectTypes(processHandle);
                }

                Vector3 raymanSpeed = new Vector3(
                    Memory.ReadProcessMemoryFloat(processHandle, off_raymanGravity + 0x2C),
                    Memory.ReadProcessMemoryFloat(processHandle, off_raymanGravity + 0x30),
                    Memory.ReadProcessMemoryFloat(processHandle, off_raymanGravity + 0x34)
                    );

                Vector3 raymanSpeedRotated = new Vector3(raymanSpeed);
                Vector3 rotationEulers     = Matrix.QuaternionToEuler(rotation);;
                raymanSpeedRotated.X = (float)(raymanSpeed.X * Math.Cos(rotationEulers.Z) - raymanSpeed.Y * Math.Sin(rotationEulers.Z));
                raymanSpeedRotated.Y = (float)(raymanSpeed.X * Math.Sin(rotationEulers.Z) + raymanSpeed.Y * Math.Cos(rotationEulers.Z));

                uint  raymanFlags  = Memory.ReadProcessMemoryUInt32(processHandle, off_raymanGravity - 8);
                uint  raymanFlags2 = Memory.ReadProcessMemoryUInt32(processHandle, off_raymanGravity - 4);
                float gravity      = Memory.ReadProcessMemoryFloat(processHandle, off_raymanGravity);
                bool  climbing     = (raymanFlags & (1 << 6)) != 0;
                bool  onGround     = (raymanFlags & (1 << 5)) != 0;
                bool  sliding      = (raymanFlags2 & (1 << 21)) != 0;

                off_cameraTarget = Memory.GetPointerPath(processHandle, Constants.off_cameraArrayPointer, 0, 0x4, 0x10, 0xc) + 0x68;
                int off_raymanMatrix = Memory.GetPointerPath(processHandle, off_cameraTarget, 0x20);
                off_targetFamily = Memory.GetPointerPath(processHandle, off_cameraTarget, 0x4, 0x0, 0x14);

                familyIndex = Memory.ReadProcessMemoryInt32(processHandle, off_targetFamily + 0xC);
                string familyName = objectTypes[0][familyIndex];

                string[] allowedFamilies =
                {
                    "rayman",
                    "obus",
                    "new_chaise_russe",
                    "Rayman_ski",
                    "nef_pirate"
                };

                // If not one of allowed families, assume main character instead of camera target
                if (!allowedFamilies.Contains(familyName))
                {
                    off_raymanMatrix = Memory.GetPointerPath(processHandle, Constants.off_mainChar, 0x20);
                }

                uint raymanCustomBits = Memory.ReadProcessMemoryUInt32(processHandle, off_raymanCustomBits);
                byte raymanDsgVar16   = Memory.ReadProcessMemoryByte(processHandle, off_raymanDsgVar16);

                tempDisable = false;

                if (levelName.ToLower() == "mapmonde" || levelName.ToLower() == "raycap" || levelName.ToLower() == "menu" || levelName.ToLower() == "staff_10" || levelName.ToLower() == "end_10")
                {
                    tempDisable = true;
                }

                bool noPitchSmoothing = false;
                bool inBush           = false;

                // Rayman custom bit 16 = no control
                if ((raymanCustomBits & (1 << 16)) != 0)
                {
                    tempDisable = true;

                    if (activeSuperObjects.ContainsKey("CCC_Buisson"))
                    {
                        float x = Memory.ReadProcessMemoryFloat(processHandle, off_raymanMatrix + 4);

                        if (x < 16878)
                        {
                            tempDisable = false;
                            inBush      = true;
                        }
                    }
                }

                // Rayman dsg var 16 set = control
                if (raymanDsgVar16 == 0)
                {
                    tempDisable = true;
                }

                // If an object with AI model ARG_Rayseau exists, this is a rayman dummy that should be used as camera viewpoint
                if (activeSuperObjectsAIModelNames.ContainsKey("ARG_Rayseau") && levelName != "bast_20")
                {
                    int dummyRaymanObject   = activeSuperObjectsAIModelNames["ARG_Rayseau"][0];
                    int off_dummyCustomBits = Memory.GetPointerPath(processHandle, dummyRaymanObject + 4, 4) + 0x24;
                    int dummyBits           = Memory.ReadProcessMemoryInt32(processHandle, off_dummyCustomBits);

                    // bit 16 disabled
                    if ((dummyBits & (1 << 16)) == 0)
                    {
                        tempDisable      = false;
                        off_raymanMatrix = Memory.GetPointerPath(processHandle, dummyRaymanObject + 0x20);
                    }
                }

                if (familyName == "nef_pirate")
                {
                    tempDisable = false;
                }

                if (levelName.ToLower() == "vulca_10")
                {
                    if (levelTimer < 10)
                    { // disable at start
                        tempDisable = true;
                    }
                }

                if (levelName.ToLower() == "vulca_20")
                {
                    float z = Memory.ReadProcessMemoryFloat(processHandle, off_raymanMatrix + 12);
                    if (z < -42 && z > -490)
                    {
                        tempDisable = true;
                    }
                }

                matrix = Matrix.Read(processHandle, off_raymanMatrix);
                int off_raymanParentObject = Memory.GetPointerPath(processHandle, off_cameraTarget, 0x1C);
                int raymanParentObjectType = Memory.ReadProcessMemoryInt32(processHandle, off_raymanParentObject);

                Matrix parentMatrix = null;
                if (raymanParentObjectType == 2)
                {
                    int off_parentMatrix = Memory.GetPointerPath(processHandle, 0x500578, 0x1C, 0x20);
                    parentMatrix     = Matrix.Read(processHandle, off_parentMatrix);
                    matrix.m         = parentMatrix.m * matrix.m;
                    noPitchSmoothing = true;
                }

                if (levelName.ToLower() == "ile_10")
                {
                    if (activeSuperObjects.ContainsKey("MIC_Freddox"))
                    {
                        int chickenAddress             = activeSuperObjects["MIC_Freddox"];
                        int off_aiModel                = Memory.GetPointerPath(processHandle, chickenAddress + 4, 0xC, 0);
                        int off_aiModelNormalBehaviors = Memory.ReadProcessMemoryInt32(processHandle, off_aiModel);

                        int   off_scriptDistanceCheckNodeRule6 = off_aiModelNormalBehaviors + 0x8AC;
                        int   off_scriptDistanceCheckNodeMacro = off_aiModel + 0x1130;
                        float distRule  = Memory.ReadProcessMemoryFloat(processHandle, off_scriptDistanceCheckNodeRule6);
                        float distMacro = Memory.ReadProcessMemoryFloat(processHandle, off_scriptDistanceCheckNodeMacro);

                        Memory.WriteProcessMemoryFloat(processHandle, off_scriptDistanceCheckNodeRule6, 0);
                        Memory.WriteProcessMemoryFloat(processHandle, off_scriptDistanceCheckNodeMacro, 0);
                    }
                }

                if (levelName.ToLower() == "rodeo_60")
                {
                    // in menhir hills 3, the menhirs can shake the screen and mess up FPS mode. here we replace some code to prevent this from happening, but only between certain coordinates
                    if (activeSuperObjects.ContainsKey("OLP_Menhir_Tombe_1"))
                    {
                        int menhirAddress = activeSuperObjects["OLP_Menhir_Tombe_1"];
                        int off_aiModel   = Memory.GetPointerPath(processHandle, menhirAddress + 4, 0xC, 0);
                        int off_aiModelNormalBehaviors = Memory.ReadProcessMemoryInt32(processHandle, off_aiModel);

                        int off_scriptTimeCheckNodeRule3 = off_aiModelNormalBehaviors + 0x3F8;

                        Memory.WriteProcessMemoryInt32(processHandle, off_scriptTimeCheckNodeRule3, 0);
                    }
                }

                if (levelName.ToLower() == "glob_10")
                {
                    if (activeSuperObjects.ContainsKey("ZOR_Tremble_Mat01"))
                    {
                        int trembleAddress = activeSuperObjects["ZOR_Tremble_Mat01"];

                        int off_trembleCustomBits = Memory.GetPointerPath(processHandle, trembleAddress + 4, 4) + 0x24;
                        Memory.WriteProcessMemoryInt32(processHandle, off_trembleCustomBits, 1 << 16);
                    }
                }

                Toe.Matrix4 offsetMatrix = Toe.Matrix4.CreateTranslation(0, 0, 0);
                offsetMatrix.M14 = 0f;
                offsetMatrix.M24 = 0.2f + backStrafeOffset;
                offsetMatrix.M34 = 1.5f;
                if (raymanSpeedRotated.Y > 0.5f && raymanParentObjectType != 2)
                {
                    backStrafeOffset += (0.6f - backStrafeOffset) / 5.0f;
                }
                else
                {
                    backStrafeOffset += (-backStrafeOffset) / 5.0f;
                }

                if (onGround && !sliding && familyName == "rayman")
                { // Head bobbing when walking
                    float bobbinFactor = raymanSpeed.Length / 20.0f;

                    offsetMatrix.M14 += (float)Math.Sin(bobbin) * bobbinFactor * 0.4f;
                    offsetMatrix.M34 += (float)Math.Cos(bobbin * 2) * bobbinFactor * 0.2f;

                    bobbin += 0.35f * bobbinFactor;
                    if (float.IsNaN(bobbin))
                    {
                        bobbin = 0;
                    }
                }

                if (familyName == "rayman")
                {
                    int currentBehaviorOffset = Utils.GetActiveNormalBehavior(processHandle, raymanSPO);
                    int currentBehaviorIndex  = Array.IndexOf(raymanBehaviors, currentBehaviorOffset);

                    // Carrying barrel/plum/orb?
                    if (currentBehaviorIndex == 28 || currentBehaviorIndex == 29)
                    {
                        carryingFloat += (1.0f - carryingFloat) / 15.0f;
                    }
                    else
                    {
                        carryingFloat += (0.0f - carryingFloat) / 10.0f;
                    }

                    offsetMatrix.M14 += 0.9f * carryingFloat; // look a little to the left
                    offsetMatrix.M24 += 0.3f * carryingFloat; // move camera back a little
                    offsetMatrix.M34 -= 0.3f * carryingFloat; // move camera back a little

                    // Flying barrel?
                    if (currentBehaviorIndex == 19)
                    {
                        barrelFlyingFloat += (1.0f - barrelFlyingFloat) / 5.0f;
                    }
                    else
                    {
                        barrelFlyingFloat += (0.0f - barrelFlyingFloat) / 5.0f;
                    }

                    offsetMatrix.M24 -= 0.15f * barrelFlyingFloat; // move camera forward a little
                    offsetMatrix.M34 -= 0.8f * barrelFlyingFloat;  // move camera down a little

                    if (activeSuperObjectsAIModelNames.ContainsKey("MIC_Obus_Complexe"))
                    {
                        if (currentBehaviorIndex == 10)
                        {
                            offsetMatrix.M34 += 1.1f;
                            offsetMatrix.M24 += 0.3f;
                        }
                    }


                    // disable fps mode in final cutscene on crow's nest
                    if (levelName.ToLower() == "rhop_10")
                    {
                        if (currentBehaviorIndex == 10)
                        {
                            tempDisable = true;
                        }
                    }

                    // disable fps mode at start of level of marshes 2 and start of whale bay 3
                    if (levelName.ToLower() == "ski_60")
                    {
                        if (currentBehaviorIndex == 10)
                        {
                            tempDisable = true;
                        }
                    }
                }

                offsetMatrix.M24 += climbingOffsetY;
                offsetMatrix.M34 += climbingOffsetZ;

                rotation = matrix.m.ExtractRotation();
                if (interpolatedRotation == null)
                {
                    interpolatedRotation = rotation;
                }

                Toe.Matrix4 rotationMatrix = Toe.Matrix4.CreateRotationZ(0);

                // Immediately update lastRotation when not on shell
                if (familyName != "obus" && !inBush)
                {
                    interpolatedRotation = rotation;
                }

                if (familyName == "obus" || inBush)
                {
                    offsetMatrix.M34 = 2.3f;
                    offsetMatrix.M24 = 0.7f;
                    noPitchSmoothing = true;

                    // smooth rotation on shell a little with quaternion interpolation
                    matrix.m = matrix.m.ClearRotation();
                    Vector3 eulers = Matrix.QuaternionToEuler(rotation);

                    interpolatedRotation = Quaternion.Slerp(interpolatedRotation, rotation, 0.25f);
                    Matrix4 interpolatedRotationMatrix = Matrix4.CreateFromQuaternion(interpolatedRotation);

                    matrix.m = matrix.m * interpolatedRotationMatrix;
                }
                else if (familyName == "new_chaise_russe")
                {
                    offsetMatrix.M34 = 2.1f;
                    offsetMatrix.M24 = 0.3f;
                    noPitchSmoothing = true;
                }
                else if (familyName == "nef_pirate")
                {
                    offsetMatrix.M34 = 12f;
                    offsetMatrix.M24 = -4f;
                    noPitchSmoothing = true;

                    dockedTimer -= 1;

                    int  off_pirateCustomBits = Memory.GetPointerPath(processHandle, Constants.off_mainChar, 4, 4) + 0x24;
                    uint pirateCustomBits     = Memory.ReadProcessMemoryUInt32(processHandle, off_pirateCustomBits);

                    // Pirate ship custom bit 27 = docking
                    if ((pirateCustomBits & (1 << 27)) != 0 && dockingCooldownTimer <= 0)
                    {
                        if (dockedTimer <= 0)
                        {
                            dockingTimer += 1;
                            if (dockingTimer > 15)
                            {
                                waitForDockedTimer   = 60 * 2;
                                dockingTimer         = 0;
                                dockingCooldownTimer = 60 * 20;
                            }
                        }
                    }
                    else
                    {
                        dockingTimer = 0;
                    }

                    dockingCooldownTimer -= 1;

                    if (waitForDockedTimer > 0)
                    {
                        waitForDockedTimer -= 1;
                        if (waitForDockedTimer == 0)
                        {
                            dockedTimer = 60 * 8;
                        }
                    }

                    if (dockedTimer > 0)
                    {
                        dockingRotation += (float)(Math.PI - dockingRotation) / 20.0f;
                        dockingOffset   += (2 - dockingOffset) / 20.0f;
                    }
                    else
                    {
                        dockingRotation += (0 - dockingRotation) / 20.0f;
                        dockingOffset   += (-dockingOffset) / 20.0f;
                    }

                    offsetMatrix.M24 -= dockingOffset * 5;

                    rotationMatrix = Toe.Matrix4.CreateRotationZ(dockingRotation);
                }

                matrix.m = matrix.m * offsetMatrix * rotationMatrix;

                // smooth out rotation
                if (!noPitchSmoothing)
                {
                    matrix.m = matrix.m.ClearRotation();
                    Vector3 eulers = Matrix.QuaternionToEuler(rotation);

                    // Slightly look down when helicoptering
                    if (Math.Round(gravity) == 4 || Math.Round(gravity) == 25)
                    {
                        if (raymanSpeed.Z < 0)
                        {
                            eulers.X -= 0.22f;
                        }
                    }

                    // Look down when freefalling
                    if (raymanSpeed.Z < -15 && !onGround)
                    {
                        float downAngle = (float)Math.Abs(raymanSpeed.Z + 15);
                        eulers.X -= downAngle * 0.03f;
                    }

                    pitch += Matrix.AngleDifference(pitch, eulers.X) / 10f;

                    matrix.m = matrix.m * Toe.Matrix4.CreateRotationZ(eulers.Z) * Toe.Matrix4.CreateRotationZ(eulers.Y) * Toe.Matrix4.CreateRotationX(pitch);
                }
                //

                if (climbing)
                {
                    climbingOffsetY += (0.3f - climbingOffsetY) / 8.0f;
                    climbingOffsetZ += (0.65f - climbingOffsetZ) / 8.0f;

                    // smooth yaw
                    matrix.m = matrix.m.ClearRotation();
                    Vector3 eulers = Matrix.QuaternionToEuler(rotation);

                    float pitchOffset = 1f;
                    float pitchInterp = 6f;
                    float yawOffset   = 1f;
                    float yawInterp   = 6f;

                    if (activeSuperObjects.ContainsKey("OLP_Generateur_DK_1"))
                    { // Fairy glade 3 barrel climb section
                        pitchOffset = 2f;
                        pitchInterp = 12f;
                        yawOffset   = 0.3f;
                        yawInterp   = 4f;
                    }

                    if (raymanSpeed.Z > 1f)
                    {
                        climbingPitch += (pitchOffset - climbingPitch) / pitchInterp;
                    }
                    else if (raymanSpeed.Z < -1f)
                    {
                        climbingPitch += (-pitchOffset - climbingPitch) / pitchInterp;
                    }
                    else
                    {
                        climbingPitch += (-climbingPitch) / pitchInterp * 2;
                    }

                    if (raymanSpeedRotated.X > 1f)
                    {
                        climbingYaw += (-yawOffset - climbingYaw) / yawInterp;
                    }
                    else if (raymanSpeedRotated.X < -1f)
                    {
                        climbingYaw += (yawOffset - climbingYaw) / yawInterp;
                    }
                    else
                    {
                        climbingYaw += (-climbingYaw) / yawInterp * 2;
                    }

                    eulers.X += climbingPitch;
                    eulers.Z += climbingYaw;

                    pitch += Matrix.AngleDifference(pitch, eulers.X) / 10f;
                    yaw   += Matrix.AngleDifference(yaw, eulers.Z) / 10f;

                    matrix.m = matrix.m * Toe.Matrix4.CreateRotationZ(yaw) * Toe.Matrix4.CreateRotationZ(eulers.Y) * Toe.Matrix4.CreateRotationX(pitch);
                }
                else
                {
                    climbingOffsetY += (0 - climbingOffsetY) / 8.0f;
                    climbingOffsetZ += (0 - climbingOffsetZ) / 8.0f;

                    Vector3 eulers = Matrix.QuaternionToEuler(rotation);
                    yaw = eulers.Z;
                }


                if (levelName.ToLower().StartsWith("plum_10"))
                {
                    if (parentMatrix != null)
                    {
                        float platformX = parentMatrix.m.M14;
                        float platformY = parentMatrix.m.M24;
                        float platformZ = parentMatrix.m.M34;

                        Toe.Vector3 platformPos = new Toe.Vector3(parentMatrix.m.M14, parentMatrix.m.M24, parentMatrix.m.M34);
                        Toe.Vector3 checkPos    = new Toe.Vector3(151.6f, 7.13f, -149.2f);
                        Toe.Vector3 diff        = platformPos - checkPos;

                        if (diff.LengthSquared < 20 * 20)
                        {
                            tempDisable = true;
                            umberTimer  = 60 * 2;
                        }
                    }
                }

                if (levelName.ToLower() == "mine_10")
                {
                    if (activeSuperObjects.ContainsKey("MOM_OLD_THX2"))
                    {
                        int   cutsceneObj           = activeSuperObjects["MOM_OLD_THX2"];
                        int   cutsceneBehavior      = Utils.GetActiveNormalBehavior(processHandle, cutsceneObj);
                        int[] behaviorList          = Utils.GetAIModelNormalBehaviorsList(processHandle, cutsceneObj);
                        int   cutsceneBehaviorIndex = Array.IndexOf(behaviorList, cutsceneBehavior);

                        // behavior != rule 2 (attend)
                        if (cutsceneBehaviorIndex != 5)
                        {
                            // Disable fps mode in the ending cutscene of mine_10
                            tempDisable = true;
                        }
                    }
                }

                if (umberTimer-- > 0)
                {
                    tempDisable = true;
                }

                float inputX = Memory.ReadProcessMemoryFloat(processHandle, Constants.off_inputX);
                float inputY = Memory.ReadProcessMemoryFloat(processHandle, Constants.off_inputY);

                if (inputY > 20 && (familyName != "obus"))
                {
                    if (!downHeld)
                    {
                        downHeld = true;
                        inputSim.Keyboard.KeyDown(WindowsInput.Native.VirtualKeyCode.RCONTROL);
                    }
                }
                else
                {
                    if (downHeld)
                    {
                        inputSim.Keyboard.KeyUp(WindowsInput.Native.VirtualKeyCode.RCONTROL);
                        downHeld = false;
                    }
                }

                if (frozen)
                {
                    tempDisable = true;
                }

                if (tempDisable)
                { // bit 17 = take away control
                    // restore opcodes
                    buffer = new byte[] { 0x81 };
                    Memory.WriteProcessMemory(processHandle, off_DNM_p_stDynamicsCameraMechanics, buffer, buffer.Length, ref bytesReadOrWritten);
                    buffer = new byte[] { 0x53 };
                    Memory.WriteProcessMemory(processHandle, off_ForceCameraPos, buffer, buffer.Length, ref bytesReadOrWritten);
                    buffer = new byte[] { 0x83 };
                    Memory.WriteProcessMemory(processHandle, off_ForceCameraTgt, buffer, buffer.Length, ref bytesReadOrWritten);

                    // restore turn speed
                    Memory.WriteProcessMemoryFloat(processHandle, Constants.off_turnFactor, 0.00009999999747f, true);

                    // restore verts for body parts
                    if (bodyPartsHidden)
                    {
                        foreach (int offset in bodyPartVertOffsets.Keys)
                        {
                            Memory.WriteProcessMemoryFloat(processHandle, offset, bodyPartVertOffsets[offset]); // restore original value
                        }
                        bodyPartsHidden = false;
                    }
                }
                else
                {
                    // set opcodes to ret (0xC3)
                    buffer = new byte[] { 0xC3 };
                    Memory.WriteProcessMemory(processHandle, off_DNM_p_stDynamicsCameraMechanics, buffer, buffer.Length, ref bytesReadOrWritten);
                    Memory.WriteProcessMemory(processHandle, off_ForceCameraPos, buffer, buffer.Length, ref bytesReadOrWritten);
                    Memory.WriteProcessMemory(processHandle, off_ForceCameraTgt, buffer, buffer.Length, ref bytesReadOrWritten);

                    // lower turn speed
                    float sensitivityFactor = 0.5f;
                    sensitivityFactor = w.fpsModeSensitivity;
                    Memory.WriteProcessMemoryFloat(processHandle, Constants.off_turnFactor, 0.00009999999747f * sensitivityFactor, true);

                    // set verts for body parts to 0 to hide them
                    if (!bodyPartsHidden)
                    {
                        foreach (int offset in bodyPartVertOffsets.Keys)
                        {
                            Memory.WriteProcessMemoryFloat(processHandle, offset, 0); // hide
                        }
                        bodyPartsHidden = true;
                    }
                }

                if (!tempDisable && !frozen)
                {
                    // Add speed to the matrix
                    matrix.m.M14 += raymanSpeed.X / 100.0f;
                    matrix.m.M24 += raymanSpeed.Y / 100.0f;
                    matrix.m.M34 += raymanSpeed.Z / 100.0f;

                    matrix.Write(processHandle, off_cameraMatrix);

                    Memory.WriteProcessMemoryFloat(processHandle, off_cameraTargetX, matrix.m.M14);
                    Memory.WriteProcessMemoryFloat(processHandle, off_cameraTargetY, matrix.m.M24);
                    Memory.WriteProcessMemoryFloat(processHandle, off_cameraTargetZ, matrix.m.M34);

                    Memory.WriteProcessMemoryFloat(processHandle, off_cameraFOV, 1.8f);
                }
                else
                {
                    Memory.WriteProcessMemoryFloat(processHandle, off_cameraFOV, 1.2f);
                }

                Thread.Sleep(15);
            }

            buffer = new byte[] { 0x81 };
            Memory.WriteProcessMemory(processHandle, off_DNM_p_stDynamicsCameraMechanics, buffer, buffer.Length, ref bytesReadOrWritten);
            buffer = new byte[] { 0x53 };
            Memory.WriteProcessMemory(processHandle, off_ForceCameraPos, buffer, buffer.Length, ref bytesReadOrWritten);
            buffer = new byte[] { 0x83 };
            Memory.WriteProcessMemory(processHandle, off_ForceCameraTgt, buffer, buffer.Length, ref bytesReadOrWritten);

            // restore verts for body parts
            foreach (int offset in bodyPartVertOffsets.Keys)
            {
                Memory.WriteProcessMemoryFloat(processHandle, offset, bodyPartVertOffsets[offset]); // restore original value
            }

            // restore fov
            Memory.WriteProcessMemoryFloat(processHandle, off_cameraFOV, 1.2f);
        }