Ejemplo n.º 1
0
        private void LadderAnim(IMyEntity ent, LadderAnimation anim)
        {
            if(lastLadderAnim == anim)
                return;

            lastLadderAnim = anim;
            var skinned = ent as MySkinnedEntity;

            if(skinned.UseNewAnimationSystem)
            {
                // TODO how does this even...
                /*
                var character = ent as IMyCharacter;
                character.TriggerCharacterAnimationEvent("SMBody_WalkRun".ToLower(), true);
                character.TriggerCharacterAnimationEvent("Sprint".ToLower(), true);
                 */
            }
            else
            {
                skinned.AddCommand(new MyAnimationCommand()
                {
                    AnimationSubtypeName = ladderAnimations[(int)anim],
                    FrameOption = MyFrameOption.Loop,
                    PlaybackCommand = MyPlaybackCommand.Play,
                    TimeScale = (anim == LadderAnimation.MOUNTING ? 1.5f : 1f),
                    KeepContinuingAnimations = false,
                    BlendOption = MyBlendOption.Immediate,
                    BlendTime = 0.1f,
                }, true);
            }
        }
Ejemplo n.º 2
0
        private void PlayerUpdate()
        {
            var playerControlled = MyAPIGateway.Session.ControlledObject;

            if(playerControlled != null)
            {
                if(playerControlled.Entity is IMyCharacter)
                {
                    SetCharacterReference(playerControlled.Entity);
                }
                else if(playerControlled.Entity is IMyCockpit) // in a seat, certainly not gonna climb ladders
                {
                    SetCharacterReference(null);
                }
                // other cases depend on having the character controlled for a bit to get the reference
                else if(character != null && character.Closed)
                {
                    SetCharacterReference(null);
                }
            }
            else
            {
                character = null;
            }

            if(character != null)
            {
                var cb = MyCubeBuilder.Static;

                // Dynamically enable/disable UseModelIntersection on ladder blocks that you hold to have the useful effect
                // of being able the block when another entity is blocking the grid space but not the blocks's physical shape.
                // This will still have the side effect issue if you aim at a ladder block with the same ladder block.
                if(cb.IsActivated && cb.CubeBuilderState != null && cb.CubeBuilderState.CurrentBlockDefinition != null && LadderLogic.ladderIds.Contains(cb.CubeBuilderState.CurrentBlockDefinition.Id.SubtypeName))
                {
                    if(prevCubeBuilderDefinition == null || prevCubeBuilderDefinition.Id != cb.CubeBuilderState.CurrentBlockDefinition.Id)
                    {
                        if(prevCubeBuilderDefinition != null)
                            prevCubeBuilderDefinition.UseModelIntersection = false;

                        prevCubeBuilderDefinition = cb.CubeBuilderState.CurrentBlockDefinition;
                        cb.CubeBuilderState.CurrentBlockDefinition.UseModelIntersection = true;
                    }
                }
                else if(prevCubeBuilderDefinition != null)
                {
                    prevCubeBuilderDefinition.UseModelIntersection = false;
                    prevCubeBuilderDefinition = null;
                }

                var charCtrl = character as IMyControllableEntity;
                bool controllingCharacter = (playerControlled != null && playerControlled.Entity is IMyCharacter);
                IMyTerminalBlock ladder = null;
                MyCubeBlock ladderInternal = null;

                MatrixD ladderMatrix = character.WorldMatrix; // temporarily using it for character matrix, then used for ladder matrix
                var charPos = ladderMatrix.Translation + ladderMatrix.Up * 0.05;
                var charPos2 = ladderMatrix.Translation + ladderMatrix.Up * RAY_HEIGHT;
                var charRay = new RayD(charPos, ladderMatrix.Up);

                if(dismounting <= 1) // relative top dismount sequence
                {
                    if(usingLadder == null)
                    {
                        dismounting = 2;
                        return;
                    }

                    ladder = usingLadder;
                    ladderInternal = ladder as MyCubeBlock;
                    dismounting *= ALIGN_MUL;

                    if(settings.clientPrediction)
                    {
                        ladderMatrix = ladder.WorldMatrix;
                        var charOnLadder = ladderMatrix.Translation + ladderMatrix.Forward * (ladderInternal.BlockDefinition.ModelOffset.Z + EXTRA_OFFSET_Z);

                        if(ladder.CubeGrid.GridSizeEnum == MyCubeSize.Large)
                            charOnLadder += ladderMatrix.Backward;

                        var topDir = Vector3D.Dot(character.WorldMatrix.Up, ladderMatrix.Up);
                        var matrix = character.WorldMatrix;
                        var halfY = ((ladderInternal.BlockDefinition.Size.Y * ladder.CubeGrid.GridSize) / 2);
                        matrix.Translation = charOnLadder + (topDir > 0 ? ladderMatrix.Up : ladderMatrix.Down) * (halfY + 0.1f) + ladderMatrix.Backward * 0.75;

                        character.SetWorldMatrix(MatrixD.SlerpScale(character.WorldMatrix, matrix, MathHelper.Clamp(dismounting, 0.0f, 1.0f)));

                        character.Physics.LinearVelocity = ladder.CubeGrid.Physics.GetVelocityAtPoint(character.WorldMatrix.Translation); // sync velocity with the ladder
                    }

                    SetLadderStatus("Dismounting ladder...", MyFontEnum.White);

                    if(dismounting > 1f)
                        ExitLadder(false);

                    return;
                }

                // UNDONE DEBUG
                //{
                //    var c = Color.Blue.ToVector4();
                //    MySimpleObjectDraw.DrawLine(charPos, charPos2, "WeaponLaserIgnoreDepth", ref c, 0.5f);
                //}

                // find a ladder
                foreach(var l in ladders.Values)
                {
                    if(l.Closed || l.MarkedForClose || !l.IsFunctional)
                        continue;

                    if(Vector3D.DistanceSquared(l.WorldMatrix.Translation, charPos) <= UPDATE_RADIUS)
                    {
                        ladderInternal = l as MyCubeBlock;
                        ladderMatrix = l.WorldMatrix;

                        // update ladder oriented box to find character in it accurately
                        Quaternion.CreateFromRotationMatrix(ref ladderMatrix, out ladderBox.Orientation);

                        if(l is MyAdvancedDoor && !(l as MyAdvancedDoor).FullyOpen)
                        {
                            ladderBox.HalfExtent = (ladderInternal.BlockDefinition.Size * l.CubeGrid.GridSize) / 2;

                            var offset = ladderInternal.BlockDefinition.ModelOffset;
                            ladderBox.Center = ladderMatrix.Translation + ladderMatrix.Up * 1.125f + ladderMatrix.Forward * (offset.Z + EXTRA_OFFSET_Z);

                            ladderBox.HalfExtent.Y = 0.25 + 0.06; // 6mm offset to avoid some inaccuracies
                            ladderBox.HalfExtent.Z = 0.5;

                            if(l.CubeGrid.GridSizeEnum == MyCubeSize.Large)
                                ladderBox.Center += ladderMatrix.Backward;
                        }
                        else
                        {
                            ladderBox.HalfExtent = (ladderInternal.BlockDefinition.Size * l.CubeGrid.GridSize) / 2;
                            ladderBox.HalfExtent.Y += 0.06; // 6mm offset to avoid some inaccuracies
                            ladderBox.HalfExtent.Z = 0.5;

                            var offset = ladderInternal.BlockDefinition.ModelOffset;
                            ladderBox.Center = ladderMatrix.Translation + ladderMatrix.Forward * (offset.Z + EXTRA_OFFSET_Z);

                            if(l.CubeGrid.GridSizeEnum == MyCubeSize.Large)
                                ladderBox.Center += ladderMatrix.Backward;
                        }

                        if(!ladderBox.Contains(ref charPos) && !ladderBox.Contains(ref charPos2))
                        {
                            var intersect = ladderBox.Intersects(ref charRay);

                            if(!intersect.HasValue || intersect.Value < 0 || intersect.Value > RAY_HEIGHT)
                                continue;
                        }

                        // UNDONE DEBUG
                        //{
                        //    {
                        //        var c = Color.Red.ToVector4();
                        //        MySimpleObjectDraw.DrawLine(ladderBox.Center + ladderMatrix.Down * ladderBox.HalfExtent.Y, ladderBox.Center + ladderMatrix.Up * ladderBox.HalfExtent.Y, "WeaponLaserIgnoreDepth", ref c, 0.01f);
                        //    }
                        //
                        //    if(debugBox == null)
                        //    {
                        //        debugBox = new MyEntity();
                        //        debugBox.Init(null, @"Models\Debug\Error.mwm", null, null, null);
                        //        debugBox.PositionComp.LocalMatrix = Matrix.Identity;
                        //        debugBox.Flags = EntityFlags.Visible | EntityFlags.NeedsDraw | EntityFlags.NeedsDrawFromParent | EntityFlags.InvalidateOnMove;
                        //        debugBox.OnAddedToScene(null);
                        //        debugBox.Render.Transparency = 0.5f;
                        //        debugBox.Render.RemoveRenderObjects();
                        //        debugBox.Render.AddRenderObjects();
                        //    }
                        //    var matrix = MatrixD.CreateWorld(ladderBox.Center, ladderMatrix.Forward, ladderMatrix.Up);
                        //    var scale = ladderBox.HalfExtent * 2;
                        //    MatrixD.Rescale(ref matrix, ref scale);
                        //    debugBox.PositionComp.SetWorldMatrix(matrix);
                        //}

                        ladder = l;
                        ladderInternal = l as MyCubeBlock;
                        break;
                    }
                }

                if(ladder != null)
                {
                    var offset = ladderInternal.BlockDefinition.ModelOffset;
                    var charOnLadder = ladderMatrix.Translation + ladderMatrix.Forward * (offset.Z + EXTRA_OFFSET_Z);

                    if(ladder.CubeGrid.GridSizeEnum == MyCubeSize.Large)
                        charOnLadder += ladderMatrix.Backward;

                    if(++skipRetryGravity >= GRAVITY_UPDATERATE)
                    {
                        skipRetryGravity = 0;
                        alignedToGravity = true;
                        var gravity = MyParticlesManager.CalculateGravityInPoint(character.WorldMatrix.Translation);
                        var gravityLength = gravity.Normalize();

                        if(gravityLength > 0)
                        {
                            float gravDot = Vector3.Dot(gravity, ladderMatrix.Down);

                            if(!(gravDot >= 0.9f || gravDot <= -0.9f))
                            {
                                alignedToGravity = false;
                            }
                        }
                    }

                    //bool readInput = InputHandler.IsInputReadable(); // HACK use before GetPressedOr() once ActiveGameplayScreen's NRE is resolved

                    if(!alignedToGravity)
                    {
                        bool pressed = InputHandler.GetPressedOr(settings.useLadder1, settings.useLadder2, false, false) && InputHandler.IsInputReadable(); // needs to support hold-press, so don't set JustPressed to true!

                        if(pressed)
                            SetLadderStatus("Gravity not parallel to ladder!", MyFontEnum.Red, 1000);

                        if(usingLadder != null)
                            ExitLadder(false);

                        return;
                    }

                    if(usingLadder == null) // first ladder interaction
                    {
                        //var controlUse = MyAPIGateway.Input.GetGameControl(MyControlsSpace.USE);
                        //
                        //if(!controlUse.IsPressed())
                        //{
                        //    string assigned = (controlUse.GetKeyboardControl() != MyKeys.None ? MyAPIGateway.Input.GetKeyName(controlUse.GetKeyboardControl()) : (controlUse.GetMouseControl() != MyMouseButtonsEnum.None ? MyAPIGateway.Input.GetName(controlUse.GetMouseControl()) : "(NONE)")) + (controlUse.GetSecondKeyboardControl() != MyKeys.None ? " or " + MyAPIGateway.Input.GetKeyName(controlUse.GetSecondKeyboardControl()) : null);
                        //    SetLadderStatus("Press "+assigned+" to use the ladder.", MyFontEnum.White);
                        //    return;
                        //}

                        if(grabOnLoad)
                        {
                            grabOnLoad = false;
                        }
                        else
                        {
                            aimingAtLadder = true;

                            if(settings.useLadder1 == null && settings.useLadder2 == null)
                            {
                                SetLadderStatus("Ladder interaction is unassigned, edit the settings.cfg file!\nFor now you can use the USE key.", MyFontEnum.Red);

                                if(!MyAPIGateway.Input.IsGameControlPressed(MyControlsSpace.USE))
                                    return;
                            }
                            else
                            {
                                bool pressed = InputHandler.GetPressedOr(settings.useLadder1, settings.useLadder2, false, false) && InputHandler.IsInputReadable(); // needs to support hold-press, so don't set JustPressed to true!

                                if(!pressed)
                                {
#if !STABLE // STABLE CONDITION
                                    if(!highlightedLadders.Contains(ladder.EntityId))
                                    {
                                        if(highlightedLadders.Count > 0)
                                        {
                                            foreach(var id in highlightedLadders)
                                            {
                                                MyVisualScriptLogicProvider.SetHighlight(LADDER_NAME_PREFIX + id, false);
                                            }

                                            highlightedLadders.Clear();
                                        }

                                        var envDef = MyDefinitionManager.Static.EnvironmentDefinition;
                                        var color = envDef.ContourHighlightColor;
                                        var thick = (int)envDef.ContourHighlightThickness;

                                        highlightedLadders.Add(ladder.EntityId);
                                        MyVisualScriptLogicProvider.SetHighlight(ladder.Name, true, thick, LADDER_HIGHLIGHT_PULSE, color);

                                        var ladderGrid = ladder.CubeGrid;

                                        for(int i = 1; i <= 10; i++)
                                        {
                                            var slim = ladderGrid.GetCubeBlock(ladderGrid.WorldToGridInteger(ladderMatrix.Translation + ladderMatrix.Up * i * ladderGrid.GridSize));

                                            if(slim?.FatBlock?.GameLogic?.GetAs<LadderLogic>() == null)
                                                break;

                                            var id = slim.FatBlock.EntityId;
                                            highlightedLadders.Add(id);
                                            MyVisualScriptLogicProvider.SetHighlight(LADDER_NAME_PREFIX + id, true, thick, LADDER_HIGHLIGHT_PULSE, color);
                                        }

                                        for(int i = 1; i <= 10; i++)
                                        {
                                            var slim = ladderGrid.GetCubeBlock(ladderGrid.WorldToGridInteger(ladderMatrix.Translation + ladderMatrix.Down * i * ladderGrid.GridSize));

                                            if(slim?.FatBlock?.GameLogic?.GetAs<LadderLogic>() == null)
                                                break;

                                            var id = slim.FatBlock.EntityId;
                                            highlightedLadders.Add(id);
                                            MyVisualScriptLogicProvider.SetHighlight(LADDER_NAME_PREFIX + id, true, thick, LADDER_HIGHLIGHT_PULSE, color);
                                        }
                                    }
#endif

                                    SetLadderStatus("Press " + InputHandler.GetFriendlyStringOr(settings.useLadder1, settings.useLadder2) + " to use the ladder.", MyFontEnum.White);
                                    return;
                                }
                            }
                        }

                        skipRefreshAnim = 60;
                        mounting = (controllingCharacter ? ALIGN_STEP : 2);
                        usingLadder = ladder;
                        SendLadderData(LadderAction.MOUNT, entId: ladder.EntityId);
                        LadderAnim(character, LadderAnimation.MOUNTING);
                    }

                    if(usingLadder != ladder)
                    {
                        usingLadder = ladder;
                        SendLadderData(LadderAction.CHANGE_LADDER, entId: ladder.EntityId);
                    }

                    if(charCtrl.Entity.Physics == null)
                    {
                        ExitLadder(false);
                        return;
                    }

                    if(settings.clientPrediction)
                        character.Physics.LinearVelocity = ladder.CubeGrid.Physics.GetVelocityAtPoint(character.WorldMatrix.Translation); // sync velocity with the ladder

                    if(skipRefreshAnim > 0 && --skipRefreshAnim == 0) // force refresh animation after mounting due to an issue
                    {
                        var anim = lastLadderAnim;
                        lastLadderAnim = LadderAnimation.NONE;
                        LadderAnim(character, anim);
                    }

                    if(mounting <= 1) // mounting on ladder sequence
                    {
                        mounting *= ALIGN_MUL;

                        if(settings.clientPrediction)
                        {
                            float align = Vector3.Dot(ladderMatrix.Up, character.WorldMatrix.Up);

                            var matrix = MatrixD.CreateFromDir(ladderMatrix.Backward, (align > 0 ? ladderMatrix.Up : ladderMatrix.Down));
                            var halfY = ((ladderInternal.BlockDefinition.Size.Y * ladder.CubeGrid.GridSize) / 2);
                            var diff = Vector3D.Dot(character.WorldMatrix.Translation, ladderMatrix.Up) - Vector3D.Dot(charOnLadder, ladderMatrix.Up);
                            matrix.Translation = charOnLadder + ladderMatrix.Up * MathHelper.Clamp(diff, -halfY, halfY);

                            character.SetWorldMatrix(MatrixD.SlerpScale(character.WorldMatrix, matrix, MathHelper.Clamp(mounting, 0.0f, 1.0f)));
                        }

                        if(mounting >= 0.75f && charCtrl.EnabledThrusts) // delayed turning off thrusts because gravity aligns you faster and can make you fail to attach to the ladder
                            charCtrl.SwitchThrusts();

                        SetLadderStatus("Mounting ladder...", MyFontEnum.White);
                        return;
                    }

                    // TODO jetpack assited climb/descend ? / gravity assisted descend ?

                    if(charCtrl.EnabledThrusts)
                    {
                        if(!learned[4])
                            learned[4] = true;

                        ExitLadder(false); // leave ladder if jetpack is turned on
                        return;
                    }

                    // HACK use once input reading NRE is fixed
                    //if(!controllingCharacter) // disable ladder control if not controlling character
                    //    readInput = false;

                    bool movingSideways = false;
                    var analogInput = MyAPIGateway.Input.GetPositionDelta();

                    // HACK use in-line once NRE is fixed
                    if(!controllingCharacter)
                        analogInput = Vector3.Zero;
                    else if(analogInput.LengthSquared() > 0 && !InputHandler.IsInputReadable())
                        analogInput = Vector3.Zero;

                    if(analogInput.Y < 0) // crouch
                    {
                        if(!learned[4])
                            learned[4] = true;

                        ExitLadder(false);
                        return;
                    }

                    float move = MathHelper.Clamp((float)Math.Round(-analogInput.Z, 1), -1, 1); // forward/backward
                    float side = MathHelper.Clamp((float)Math.Round(analogInput.X, 1), -1, 1); // left/right

                    //float move = readInput ? MathHelper.Clamp(MyAPIGateway.Input.GetGameControlAnalogState(MyControlsSpace.FORWARD) - MyAPIGateway.Input.GetGameControlAnalogState(MyControlsSpace.BACKWARD), -1, 1) : 0;
                    //float side = readInput ? MathHelper.Clamp(MyAPIGateway.Input.GetGameControlAnalogState(MyControlsSpace.STRAFE_RIGHT) - MyAPIGateway.Input.GetGameControlAnalogState(MyControlsSpace.STRAFE_LEFT), -1, 1) : 0;
                    var alignVertical = ladderMatrix.Up.Dot(character.WorldMatrix.Up);

                    if(!loadedAllLearned)
                    {
                        bool allLearned = (learned[0] && learned[1] && learned[2] && learned[3] && learned[4]);

                        for(int i = 0; i < learned.Length; i++)
                        {
                            if(learnNotify[i] == null)
                                learnNotify[i] = MyAPIGateway.Utilities.CreateNotification("");

                            learnNotify[i].Text = (learned[i] ? LEARN_CHECK : LEARN_UNCHECK) + learnText[i];
                            learnNotify[i].Font = (learned[i] ? MyFontEnum.White : MyFontEnum.DarkBlue);
                            learnNotify[i].AliveTime = (allLearned ? 1000 : 100);
                            learnNotify[i].Show();
                        }

                        if(allLearned)
                        {
                            SaveLearn();
                            loadedAllLearned = true;
                        }
                    }

                    var view = MyAPIGateway.Session.ControlledObject.GetHeadMatrix(false, true);
                    float lookVertical = Vector3.Dot(character.WorldMatrix.Up, view.Forward);

                    if(settings.relativeControls) // climbing relative to camera
                    {
                        float verticalModifier = MathHelper.Clamp((lookVertical + 0.65f) / 0.5f, -0.5f, 1.0f);

                        if(verticalModifier < 0)
                            verticalModifier *= 2;

                        move = (float)Math.Round(move * verticalModifier, 1);
                    }

                    if(analogInput.Y > 0) // jump
                    {
                        if(characterMovementState == MyCharacterMovementEnum.Jump) // this is still fine for avoiding jump as the character still is able to jump without needing feet on the ground
                        {
                            ExitLadder(false); // only release if on the floor as the character will jump regardless
                            return;
                        }

                        if(settings.clientPrediction)
                            character.Physics.LinearVelocity += view.Forward * (characterDefinition == null ? VEL_JUMP : 200 * characterDefinition.JumpForce) * TICKRATE;

                        SendLadderData(LadderAction.JUMP_OFF, vec: view.Forward);
                        LadderAnim(character, LadderAnimation.JUMP_OFF);

                        if(!learned[3])
                            learned[3] = true;

                        ExitLadder(false);
                        return;
                    }

                    bool sprint = (characterDefinition != null && characterDefinition.Jetpack != null && MyAPIGateway.Input.IsGameControlPressed(MyControlsSpace.SPRINT));

                    if(Math.Abs(side) > 0.0001f)
                    {
                        if(settings.relativeControls) // side dismounting relative to camera
                        {
                            var alignForward = ladderMatrix.Backward.Dot(character.WorldMatrix.Forward);

                            if(alignForward < 0)
                                side = -side;
                        }

                        float speed = (characterDefinition == null ? (sprint ? VEL_SIDE : VEL_CLIMB) : CHAR_SPEED_MUL * (sprint ? characterDefinition.MaxSprintSpeed : characterDefinition.MaxRunStrafingSpeed));

                        if(settings.clientPrediction)
                            character.Physics.LinearVelocity += side * (alignVertical > 0 ? ladderMatrix.Left : ladderMatrix.Right) * speed * TICKRATE;

                        LadderAnim(character, (side > 0 ? LadderAnimation.DISMOUNT_LEFT : LadderAnimation.DISMOUNT_RIGHT));
                        movingSideways = true;

                        if(!learned[2])
                            learned[2] = true;
                    }
                    else
                    {
                        // aligning player to ladder

                        if(settings.clientPrediction)
                        {
                            Vector3 dir = charOnLadder - charPos;
                            Vector3 vel = dir - (ladderMatrix.Up * Vector3D.Dot(dir, ladderMatrix.Up)); // projecting up/down direction to ignore it
                            float len = vel.Normalize();

                            if(len >= ALIGN_ACCURACY)
                            {
                                float speed = (characterDefinition == null ? (sprint ? VEL_SIDE : VEL_CLIMB) : CHAR_SPEED_MUL * (sprint ? characterDefinition.MaxRunStrafingSpeed : characterDefinition.MaxWalkStrafingSpeed));
                                len = MathHelper.Clamp(len, 0.1f, 1);
                                character.Physics.LinearVelocity += vel * len * speed * TICKRATE;
                            }
                        }
                    }

                    // TODO find a way to control view
                    {
                        //var ctrl2 = (Sandbox.Game.Entities.IMyControllableEntity)character;
                        //
                        //ctrl2.HeadLocalYAngle = 0;
                        //ctrl2.HeadLocalXAngle = 0;
                        //
                        // or MoveAndRotate() ?
                        // or... angularvelocity ?
                        // or I dunno!
                    }

                    if(Math.Abs(move) > 0.0001f)
                    {
                        if(!learned[0])
                            learned[0] = true;

                        var halfY = ((ladderInternal.BlockDefinition.Size.Y * ladder.CubeGrid.GridSize) / 2);
                        var edge = charOnLadder + ((alignVertical > 0 ? ladderMatrix.Up : ladderMatrix.Down) * halfY);

                        // climb over at the end when climbing up
                        if(move > 0 && Vector3D.DistanceSquared(charPos, edge) <= 0.0625) // 0.25 squared
                        {
                            var nextBlockWorldPos = ladderMatrix.Translation + ladderMatrix.Forward * offset.Z + ((alignVertical > 0 ? ladderMatrix.Up : ladderMatrix.Down) * (halfY + 0.1f));
                            var nextBlockPos = ladder.CubeGrid.WorldToGridInteger(nextBlockWorldPos);
                            var slim = ladder.CubeGrid.GetCubeBlock(nextBlockPos);

                            // if the next block is not a ladder, dismount
                            if(slim == null || !(slim.FatBlock is IMyTerminalBlock) || !LadderLogic.ladderIds.Contains(slim.FatBlock.BlockDefinition.SubtypeId))
                            {
                                dismounting = ALIGN_STEP;
                                SendLadderData(LadderAction.DISMOUNT);
                                return;
                            }
                        }

                        // on the floor and moving backwards makes you dismount
                        if(move < 0)
                        {
                            var feetStart = character.WorldMatrix.Translation + character.WorldMatrix.Up * 0.2; // a bit higher because the floor might clip through the character
                            var feetTarget = feetStart + character.WorldMatrix.Down * 0.3;
                            IHitInfo hit;

                            if(MyAPIGateway.Physics.CastRay(feetStart, feetTarget, out hit, COLLISSIONLAYER_NOCHARACTER))
                            {
                                // need to check the block under the ladder if it's anything but a ladder because "standing" stance occurs when character rests on its chest-sphere collision mesh too
                                var prevBlockWorldPos = ladderMatrix.Translation + ladderMatrix.Forward * offset.Z + (alignVertical > 0 ? ladderMatrix.Down : ladderMatrix.Up) * (halfY + 0.1f);
                                var prevBlockPos = ladder.CubeGrid.WorldToGridInteger(prevBlockWorldPos);
                                var slim = ladder.CubeGrid.GetCubeBlock(prevBlockPos);

                                // if it's not a ladder, check the distance and confirm your feet are close to its edge
                                if(slim == null || !(slim.FatBlock is IMyTerminalBlock) || !LadderLogic.ladderIds.Contains(slim.FatBlock.BlockDefinition.SubtypeId))
                                {
                                    // get the block's edge and the character feet position only along the ladder's up/down axis
                                    var blockPosProjectedUp = ladderMatrix.Up * Vector3D.Dot(prevBlockWorldPos, ladderMatrix.Up);
                                    var charPosProjectedUp = ladderMatrix.Up * Vector3D.Dot(character.WorldMatrix.Translation, ladderMatrix.Up);

                                    if(Vector3D.DistanceSquared(blockPosProjectedUp, charPosProjectedUp) <= 0.04) // 0.2 squared
                                    {
                                        ExitLadder(false); // to recap: if you're moving char-relative down and in "standing" stance and the block below is not a ladder and you're closer than 0.1m to its edge, then let go of the ladder.
                                        return;
                                    }
                                }
                            }
                        }

                        // climbing on the ladder

                        if(!learned[1] && sprint)
                            learned[1] = true;

                        float speed = (characterDefinition == null ? (sprint ? VEL_SPRINT : VEL_CLIMB) : CHAR_SPEED_MUL * (sprint ? characterDefinition.MaxSprintSpeed : characterDefinition.MaxRunSpeed));

                        if(settings.clientPrediction)
                            character.Physics.LinearVelocity += (alignVertical > 0 ? ladderMatrix.Up : ladderMatrix.Down) * move * speed * TICKRATE;

                        if(!movingSideways)
                            LadderAnim(character, (move > 0 ? LadderAnimation.UP : LadderAnimation.DOWN));
                    }
                    else if(!movingSideways)
                    {
                        LadderAnim(character, LadderAnimation.IDLE);
                    }

                    SendLadderData(LadderAction.CLIMB,
                                   climb: (alignVertical > 0 ? move : -move),
                                   side: (alignVertical > 0 ? side : -side),
                                   sprint: sprint);

                    return;
                }
            }

            ExitLadder();
        }
Ejemplo n.º 3
0
        private void ExitLadder(bool setFreeFallAnimation = true)
        {
            if(grabOnLoad)
                grabOnLoad = false;

            if(usingLadder == null)
                return;

            SendLadderData(LadderAction.LET_GO);

            usingLadder = null;
            alignedToGravity = false;
            skipRetryGravity = GRAVITY_UPDATERATE;

            if(MyAPIGateway.Session.ControlledObject is IMyCharacter && setFreeFallAnimation && character != null && lastLadderAnim != LadderAnimation.NONE)
            {
                LadderAnim(character, LadderAnimation.JUMP_OFF);
            }

            lastLadderAnim = LadderAnimation.NONE;
        }