protected void SharedApplyInput(
            ICharacter character,
            PlayerCharacterPrivateState privateState,
            PlayerCharacterPublicState publicState)
        {
            var characterIsOffline = !character.ServerIsOnline;

            if (characterIsOffline)
            {
                privateState.Input = default;
            }

            var vehicle = publicState.CurrentVehicle;

            if (!(vehicle is null))
            {
                if (!vehicle.IsInitialized)
                {
                    return;
                }

                var protoVehicle = (IProtoVehicle)vehicle.ProtoGameObject;
                protoVehicle.SharedApplyInput(vehicle,
                                              character,
                                              privateState,
                                              publicState);
                return;
            }

            // please note - input is a structure so actually we're implicitly copying it here
            var input = privateState.Input;
            // please note - applied input is a class and we're getting it by reference here
            var appliedInput   = publicState.AppliedInput;
            var hasRunningFlag = (input.MoveModes & CharacterMoveModes.ModifierRun) != 0;
            var isRunning      = hasRunningFlag;

            if (isRunning)
            {
                var stats          = publicState.CurrentStatsExtended;
                var wasRunning     = (appliedInput.MoveModes & CharacterMoveModes.ModifierRun) != 0;
                var staminaCurrent = stats.StaminaCurrent;

                if (!wasRunning)
                {
                    // can start running only when the current energy is at least on 10%
                    isRunning = staminaCurrent >= 0.1 * stats.StaminaMax;
                }
                else // if was running
                {
                    // can continue to run while has energy
                    isRunning = staminaCurrent > 0;
                }

                //Logger.WriteDev($"Was running: {wasRunning}; now running: {isRunning}; stamina: {staminaCurrent}");
            }

            double moveSpeed;

            if (characterIsOffline ||
                (privateState.CurrentActionState?.IsBlocksMovement ?? false))
            {
                // offline or current action blocks movement
                moveSpeed       = 0;
                isRunning       = false;
                input.MoveModes = CharacterMoveModes.None;
            }
            else
            {
                var characterFinalStateCache = privateState.FinalStatsCache;
                moveSpeed  = characterFinalStateCache[StatName.MoveSpeed];
                moveSpeed *= ProtoTile.SharedGetTileMoveSpeedMultiplier(character.Tile);

                if (isRunning)
                {
                    var moveSpeedMultiplier = characterFinalStateCache[StatName.MoveSpeedRunMultiplier];
                    if (moveSpeedMultiplier > 0)
                    {
                        moveSpeed = moveSpeed * moveSpeedMultiplier;
                    }
                    else
                    {
                        isRunning = false;
                    }
                }
            }

            if (!isRunning)
            {
                // cannot run - remove running flag
                input.MoveModes &= ~CharacterMoveModes.ModifierRun;
            }

            if (appliedInput.MoveModes == input.MoveModes &&
                appliedInput.RotationAngleRad == input.RotationAngleRad &&
                publicState.AppliedInput.MoveSpeed == moveSpeed)
            {
                // input is not changed
                return;
            }

            // apply new input
            appliedInput.Set(input, moveSpeed);

            var moveModes = input.MoveModes;

            double directionX = 0, directionY = 0;

            if ((moveModes & CharacterMoveModes.Up) != 0)
            {
                directionY = 1;
            }

            if ((moveModes & CharacterMoveModes.Down) != 0)
            {
                directionY = -1;
            }

            if ((moveModes & CharacterMoveModes.Left) != 0)
            {
                directionX = -1;
            }

            if ((moveModes & CharacterMoveModes.Right) != 0)
            {
                directionX = 1;
            }

            if (directionX == 0 &&
                directionY == 0)
            {
                moveSpeed = 0;
            }

            Vector2D directionVector  = (directionX, directionY);
            var      moveAcceleration = directionVector.Normalized * this.PhysicsBodyAccelerationCoef * moveSpeed;

            if (IsServer)
            {
                Server.World.SetDynamicObjectPhysicsMovement(character,
                                                             moveAcceleration,
                                                             targetVelocity: moveSpeed);
                character.PhysicsBody.Friction = this.PhysicsBodyFriction;
            }
            else // if client
            {
                if (ClientCurrentCharacterLagPredictionManager.IsLagPredictionEnabled)
                {
                    Client.World.SetDynamicObjectPhysicsMovement(character,
                                                                 moveAcceleration,
                                                                 targetVelocity: moveSpeed);
                    character.PhysicsBody.Friction = this.PhysicsBodyFriction;
                }
            }
        }
        protected void SharedApplyInput(
            ICharacter character,
            PlayerCharacterPrivateState privateState,
            PlayerCharacterPublicState publicState)
        {
            var characterIsOffline = !character.IsOnline;

            if (characterIsOffline)
            {
                privateState.Input = default;
            }

            // please note - input is a structure so actually we're implicitly copying it here
            var input = privateState.Input;
            // please note - applied input is a class and we're getting it by reference here
            var appliedInput   = publicState.AppliedInput;
            var hasRunningFlag = (input.MoveModes & CharacterMoveModes.ModifierRun) != 0;
            var isRunning      = hasRunningFlag;

            if (isRunning)
            {
                var stats          = publicState.CurrentStatsExtended;
                var wasRunning     = (appliedInput.MoveModes & CharacterMoveModes.ModifierRun) != 0;
                var staminaCurrent = stats.StaminaCurrent;

                if (!wasRunning)
                {
                    // can start running only when the current energy is at least on 10%
                    isRunning = staminaCurrent >= 0.1 * stats.StaminaMax;
                }
                else // if was running
                {
                    // can continue to run while has energy
                    isRunning = staminaCurrent > 0;
                }

                //Logger.WriteDev($"Was running: {wasRunning}; now running: {isRunning}; stamina: {staminaCurrent}");
            }

            double moveSpeed;

            if (characterIsOffline)
            {
                moveSpeed = 0;
                isRunning = false;
            }
            else
            {
                var characterFinalStateCache = privateState.FinalStatsCache;
                moveSpeed  = characterFinalStateCache[StatName.MoveSpeed];
                moveSpeed *= ProtoTile.SharedGetTileMoveSpeedMultiplier(character.Tile);

                if (isRunning)
                {
                    var moveSpeedMultiplier = characterFinalStateCache[StatName.MoveSpeedRunMultiplier];
                    if (moveSpeedMultiplier > 0)
                    {
                        moveSpeed = moveSpeed * moveSpeedMultiplier;
                    }
                    else
                    {
                        isRunning = false;
                    }
                }
            }

            if (!isRunning)
            {
                // cannot run - remove running flag
                input.MoveModes &= ~CharacterMoveModes.ModifierRun;
            }

            if (appliedInput.MoveModes == input.MoveModes &&
                appliedInput.RotationAngleRad == input.RotationAngleRad &&
                publicState.AppliedInput.MoveSpeed == moveSpeed)
            {
                // input is not changed
                return;
            }

            // apply new input
            appliedInput.Set(input, moveSpeed);

            var moveModes = input.MoveModes;

            double directionX = 0, directionY = 0;

            if ((moveModes & CharacterMoveModes.Up) != 0)
            {
                directionY = 1;
            }

            if ((moveModes & CharacterMoveModes.Down) != 0)
            {
                directionY = -1;
            }

            if ((moveModes & CharacterMoveModes.Left) != 0)
            {
                directionX = -1;
            }

            if ((moveModes & CharacterMoveModes.Right) != 0)
            {
                directionX = 1;
            }

            Vector2D directionVector = (directionX, directionY);
            var      moveVelocity    = directionVector.Normalized * moveSpeed;

            if (IsServer)
            {
                Server.Characters.SetVelocity(character, moveVelocity);
            }
            else // if client
            {
                if (ClientCurrentCharacterLagPredictionManager.IsLagPredictionEnabled)
                {
                    Client.Characters.SetVelocity(character, moveVelocity);
                }
            }
        }
Esempio n. 3
0
 protected BaseActionState(ICharacter character)
 {
     this.Character             = character;
     this.CharacterPrivateState = PlayerCharacter.GetPrivateState(character);
     this.CharacterPublicState  = PlayerCharacter.GetPublicState(character);
 }
Esempio n. 4
0
        /// <summary>
        /// Shared (current character on client, server) update of
        /// player character's stamina (every frame, called directly from PlayerCharacter).
        /// </summary>
        public static void SharedUpdate(
            ICharacter character,
            PlayerCharacterPublicState publicState,
            PlayerCharacterPrivateState privateState,
            double deltaTime)
        {
            if (!character.ServerIsOnline ||
                privateState.IsDespawned ||
                publicState.IsDead)
            {
                return;
            }

            var stats = publicState.CurrentStatsExtended;

            // restore stamina slower when no food or water
            var staminaRestoreMultiplier = stats.FoodCurrent <= 0 || stats.WaterCurrent <= 0
                                               ? 0.33
                                               : 1;

            var finalStatsCache         = privateState.FinalStatsCache;
            var statStaminaRegeneration = finalStatsCache[StatName.StaminaRegenerationPerSecond];

            // update character stats
            if (publicState.AppliedInput.MoveModes != CharacterMoveModes.None &&
                publicState.CurrentVehicle is null)
            {
                if ((publicState.AppliedInput.MoveModes & CharacterMoveModes.ModifierRun) != 0)
                {
                    // character is running - consume stamina
                    var staminaConsumption = finalStatsCache[StatName.RunningStaminaConsumptionPerSecond]
                                             * deltaTime;
                    if (staminaConsumption > 0)
                    {
                        stats.SharedSetStaminaCurrent((float)(stats.StaminaCurrent - staminaConsumption),
                                                      notifyClient: false);
                    }

                    if (Api.IsServer)
                    {
                        // TODO: check if character really moved and not running into a wall
                        privateState.Skills.ServerAddSkillExperience <SkillAthletics>(
                            SkillAthletics.ExperienceAddWhenRunningPerSecond * deltaTime);
                    }
                }
                else // if (!privateState.Input.MoveModes.HasFlag(CharacterMoveModes.ModifierRun))
                {
                    // character is moving but not running (and client is not requested running) - restore stamina at special rate
                    var staminaRestore = StaminaRegenerationWhenMovingMultiplier
                                         * statStaminaRegeneration
                                         * staminaRestoreMultiplier
                                         * deltaTime;
                    stats.SharedSetStaminaCurrent((float)(stats.StaminaCurrent + staminaRestore), notifyClient: false);
                }
            }
            else
            {
                // character is staying or in a vehicle - restore stamina
                var staminaRestore = statStaminaRegeneration * staminaRestoreMultiplier * deltaTime;
                stats.SharedSetStaminaCurrent((float)(stats.StaminaCurrent + staminaRestore), notifyClient: false);
            }
        }