public static IComponentSkeleton CreateCharacterSkeleton(
            IClientSceneObject sceneObject,
            IProtoCharacterSkeleton protoCharacterSkeleton,
            double worldScale,
            sbyte spriteQualityOffset = 0)
        {
            if (protoCharacterSkeleton.SkeletonResourceFront is null)
            {
                return(null);
            }

            var skeletonResources = new List <SkeletonResource>();

            skeletonResources.Add(protoCharacterSkeleton.SkeletonResourceFront);
            if (protoCharacterSkeleton.SkeletonResourceBack is not null)
            {
                skeletonResources.Add(protoCharacterSkeleton.SkeletonResourceBack);
            }

            var skeleton = Api.Client.Rendering.CreateSkeletonRenderer(
                sceneObject,
                skeletonResources: skeletonResources,
                defaultSkeleton: protoCharacterSkeleton.SkeletonResourceFront,
                defaultLoopedAnimationName: "Idle",
                positionOffset: (0, 0),
                worldScale: worldScale,
                speedMultiplier: protoCharacterSkeleton.SpeedMultiplier,
                spriteQualityOffset: spriteQualityOffset);

            protoCharacterSkeleton.OnSkeletonCreated(skeleton);
            return(skeleton);
        }
示例#2
0
        public override void ClientSetupSkeleton(
            IDynamicWorldObject vehicle,
            IProtoCharacterSkeleton protoSkeleton,
            IComponentSkeleton skeletonRenderer,
            List <IClientComponent> skeletonComponents)
        {
            var publicState = GetPublicState(vehicle);

            if (!vehicle.IsInitialized ||
                publicState.PilotCharacter is null ||
                !publicState.PilotCharacter.IsCurrentClientCharacter)
            {
                return;
            }

            var componentNightVision = vehicle.ClientSceneObject
                                       .AddComponent <ClientComponentNightVisionEffectMechNemesis>();

            publicState.ClientSubscribe(
                _ => _.IsLightsEnabled,
                _ => RefreshNightVision(),
                subscriptionOwner: GetClientState(vehicle));

            RefreshNightVision();

            void RefreshNightVision()
            {
                componentNightVision.IsEnabled = publicState.IsLightsEnabled;
            }
        }
        private static bool IsOrientedUp(IProtoCharacterSkeleton protoSkeleton, double angleDeg)
        {
            // we consider upper half-circle as starting from "extra angle" to 180-"extra angle" degrees, not 0 to 180
            var extraAngle = protoSkeleton.OrientationDownExtraAngle;

            return(angleDeg > extraAngle &&
                   angleDeg < 180.0 - extraAngle);
        }
 public void SharedGetSkeletonProto(
     ICharacter character,
     out IProtoCharacterSkeleton skeleton,
     out double scale)
 {
     scale = 1;
     this.SharedGetSkeletonProto(character, out var protoSkeleton, ref scale);
     skeleton = protoSkeleton ?? throw new NullReferenceException("No proto skeleton provided for " + character);
 }
        public static void GetCurrentAnimationSetting(
            IProtoCharacterSkeleton protoSkeleton,
            CharacterMoveModes moveModes,
            double angle,
            ViewOrientation lastViewOrientation,
            out string starterAnimationName,
            out string currentAnimationName,
            out DrawMode currentDrawMode,
            out float aimCoef,
            out ViewOrientation viewOrientation,
            out bool isIdle)
        {
            GetAimingOrientation(protoSkeleton, angle, lastViewOrientation, out viewOrientation, out aimCoef);

            currentDrawMode = viewOrientation.IsLeft ? DrawMode.Default : DrawMode.FlipHorizontally;

            isIdle = moveModes == CharacterMoveModes.None;
            if (isIdle)
            {
                starterAnimationName = null;
                currentAnimationName = "Idle";
                return;
            }

            if ((moveModes & (CharacterMoveModes.Left | CharacterMoveModes.Right))
                != CharacterMoveModes.None)
            {
                if (viewOrientation.IsLeft && (moveModes & CharacterMoveModes.Left) != 0 ||
                    !viewOrientation.IsLeft && (moveModes & CharacterMoveModes.Right) != 0)
                {
                    starterAnimationName = "RunSideStart";
                    currentAnimationName = "RunSide";
                }
                else
                {
                    starterAnimationName = "RunSideBackwardStart";
                    currentAnimationName = "RunSideBackward";
                }
            }
            else
            {
                // going up or down
                var isMoveUp = (moveModes & CharacterMoveModes.Up) != 0;
                starterAnimationName = isMoveUp ? "RunUpStart" : "RunDownStart";
                currentAnimationName = isMoveUp ? "RunUp" : "RunDown";
            }
        }
        public static void ClientRebuildAppearance(
            ICharacter character,
            BaseCharacterClientState clientState,
            ICharacterPublicState publicState,
            IItem selectedItem)
        {
            if (!character.IsNpc)
            {
                var vehicle = character.SharedGetCurrentVehicle();
                if (vehicle is not null &&
                    !vehicle.IsInitialized)
                {
                    // Character has a vehicle which is not yet initialized.
                    // Don't build a skeleton for it now.
                    // When vehicle will be initialized,
                    // it will automatically re-initialize the character and invoke this method.
                    return;
                }
            }

            IProtoCharacterSkeleton newProtoSkeleton = null;
            double skeletonScaleMultiplier           = 0;

            if (publicState is PlayerCharacterPublicState playerCharacterPublicState &&
                playerCharacterPublicState.CurrentVehicle is not null &&
                playerCharacterPublicState.CurrentVehicle.IsInitialized)
            {
                var protoVehicle = (IProtoVehicle)playerCharacterPublicState.CurrentVehicle.ProtoWorldObject;
                protoVehicle.SharedGetSkeletonProto(null,
                                                    out var protoSkeletonVehicle,
                                                    out var scaleResult);
                if (protoSkeletonVehicle is not null)
                {
                    newProtoSkeleton        = (ProtoCharacterSkeleton)protoSkeletonVehicle;
                    skeletonScaleMultiplier = scaleResult;
                }
            }

            if (newProtoSkeleton is null)
            {
                character.ProtoCharacter.SharedGetSkeletonProto(
                    character,
                    out newProtoSkeleton,
                    out skeletonScaleMultiplier);
            }

            var isSkeletonChanged = newProtoSkeleton != clientState.CurrentProtoSkeleton;

            clientState.CurrentProtoSkeleton = (ProtoCharacterSkeleton)newProtoSkeleton;
            clientState.CurrentProtoSkeletonScaleMultiplier = skeletonScaleMultiplier;

            var skeletonRenderer   = clientState.SkeletonRenderer;
            var skeletonComponents = clientState.SkeletonComponents;

            if (skeletonComponents.Count > 0)
            {
                foreach (var comp in skeletonComponents)
                {
                    try
                    {
                        comp.Destroy();
                    }
                    catch (Exception ex)
                    {
                        Api.Logger.Exception(ex);
                    }
                }

                skeletonComponents.Clear();
            }

            var isNewSkeleton = false;

            // create shadow renderer
            clientState.RendererShadow?.Destroy();
            clientState.RendererShadow = ((ProtoCharacterSkeleton)newProtoSkeleton)
                                         .ClientCreateShadowRenderer(character,
                                                                     skeletonScaleMultiplier);

            if (skeletonRenderer is null ||
                isSkeletonChanged)
            {
                skeletonRenderer?.Destroy();
                if (newProtoSkeleton is null ||
                    newProtoSkeleton.SkeletonResourceFront is null)
                {
                    return;
                }

                var scale = clientState.CurrentProtoSkeleton.WorldScale
                            * clientState.CurrentProtoSkeletonScaleMultiplier;

                // create new skeleton renderer
                skeletonRenderer = CreateCharacterSkeleton(character.ClientSceneObject, newProtoSkeleton, scale);

                clientState.SkeletonRenderer = skeletonRenderer;
                isNewSkeleton = true;
                //Api.Logger.Write("Skeleton created for " + character);
            }

            if (clientState.LastSelectedItem != selectedItem)
            {
                clientState.LastSelectedItem = selectedItem;
                if (!isNewSkeleton)
                {
                    // cleanup skeleton
                    skeletonRenderer.ResetSkeleton();
                }
            }

            var containerEquipment = (publicState as ICharacterPublicStateWithEquipment)
                                     ?.ContainerEquipment;

            if (containerEquipment is not null)
            {
                SetupSkeletonEquipmentForCharacter(
                    character,
                    containerEquipment,
                    skeletonRenderer,
                    clientState.CurrentProtoSkeleton,
                    skeletonComponents);
            }

            if (!character.IsNpc)
            {
                var vehicle = character.SharedGetCurrentVehicle();
                if (vehicle is not null &&
                    vehicle.IsInitialized)
                {
                    var protoVehicle = (IProtoVehicle)vehicle.ProtoGameObject;
                    protoVehicle.ClientSetupSkeleton(vehicle,
                                                     newProtoSkeleton,
                                                     skeletonRenderer,
                                                     skeletonComponents);
                }
            }

            if (selectedItem is not null)
            {
                var activeProtoItem = selectedItem.ProtoGameObject as IProtoItemWithCharacterAppearance;
                activeProtoItem?.ClientSetupSkeleton(selectedItem,
                                                     character,
                                                     clientState.CurrentProtoSkeleton,
                                                     skeletonRenderer,
                                                     skeletonComponents);
            }

            if (character.IsCurrentClientCharacter &&
                character.ProtoCharacter is PlayerCharacter)
            {
                TryAddArtificialLightArea(character, skeletonComponents);
            }

            // ensure all the added skeleton components are instantly updated
            // to make them ready for rendering (fixes light flickering issue)
            foreach (var c in skeletonComponents)
            {
                if (!(c is ClientComponent component) ||
                    !component.IsEnabled)
                {
                    continue;
                }

                component.Update(0);

                if (component.IsLateUpdateEnabled)
                {
                    component.LateUpdate(0);
                }
            }
        }
        public static void GetAimingOrientation(
            IProtoCharacterSkeleton protoSkeleton,
            double angleRad,
            ViewOrientation lastViewOrientation,
            out ViewOrientation viewOrientation,
            out float aimCoef)
        {
            //const float thresholdUpToDownFlipDeg = 0;

            var angleDeg = angleRad * RadToDeg;

            viewOrientation = new ViewOrientation(
                isUp: IsOrientedUp(protoSkeleton, angleDeg),
                isLeft: IsLeftHalfOfCircle(angleDeg));

            if (viewOrientation.IsLeft == lastViewOrientation.IsLeft &&
                viewOrientation.IsUp != lastViewOrientation.IsUp)
            {
                // switched up/down
                if (lastViewOrientation.IsUp)
                {
                    // COMMENTED OUT - we don't need tolerance for keeping the up orientation
                    //// if view orientation was up, but now down
                    //if (lastViewOrientation.IsLeft)
                    //{
                    //	// if view orientation was up-left, but now down-left
                    //	if (angleDeg < 90 + toleranceUpVerticalFlipDeg)
                    //	{
                    //		// keep up-left orientation
                    //		viewOrientation.IsUp = true;
                    //	}
                    //}
                    //else // if view orientation was up-right, but now down-right
                    //{
                    //	if (angleDeg > 90 - toleranceUpVerticalFlipDeg)
                    //	{
                    //		// keep up-right orientation
                    //		viewOrientation.IsUp = true;
                    //	}
                    //}
                }
                else
                {
                    // if view orientation was down, but now up
                    if (lastViewOrientation.IsLeft)
                    {
                        // if view orientation was down-left, but now up-left
                        if (angleDeg > 180 - protoSkeleton.OrientationThresholdDownToUpFlipDeg)
                        {
                            // keep down-left orientation
                            viewOrientation.IsUp = false;
                        }
                    }
                    else // if view orientation was down-right, but now up-right
                    {
                        if (angleDeg < protoSkeleton.OrientationThresholdDownToUpFlipDeg)
                        {
                            // keep down-right orientation
                            viewOrientation.IsUp = false;
                        }
                    }
                }
            }
            else if (viewOrientation.IsLeft != lastViewOrientation.IsLeft &&
                     viewOrientation.IsUp == lastViewOrientation.IsUp)
            {
                // switched left/right
                if (lastViewOrientation.IsUp)
                {
                    if (lastViewOrientation.IsLeft)
                    {
                        // if view orientation was left-up, but now right-up
                        if (angleDeg > 90 - protoSkeleton.OrientationThresholdUpHorizontalFlipDeg)
                        {
                            // keep up-left orientation
                            viewOrientation.IsLeft = true;
                        }
                    }
                    else
                    {
                        // if view orientation was right-up, but now left-up
                        if (angleDeg < 90 + protoSkeleton.OrientationThresholdUpHorizontalFlipDeg)
                        {
                            // keep up-right orientation
                            viewOrientation.IsLeft = false;
                        }
                    }
                }
                else
                {
                    if (lastViewOrientation.IsLeft)
                    {
                        // if view orientation was left-down, but now right-down
                        if (angleDeg < 270 + protoSkeleton.OrientationThresholdDownHorizontalFlipDeg &&
                            angleDeg > 270)
                        {
                            // keep down-left orientation
                            viewOrientation.IsLeft = true;
                        }
                    }
                    else
                    {
                        // if view orientation was right-down, but now left-down
                        if (angleDeg > 270 - protoSkeleton.OrientationThresholdDownHorizontalFlipDeg &&
                            angleDeg < 270)
                        {
                            // keep down-right orientation
                            viewOrientation.IsLeft = false;
                        }
                    }
                }
            }

            // let's calculate aim coef
            var aimAngle = angleDeg;

            if (viewOrientation.IsUp)
            {
                // offset angle
                aimAngle += 45;
                // calculated angle between 0 and 270 degrees
                aimAngle %= 360;
                // calculate coef (from 0 to 2)
                aimCoef = (float)(aimAngle / 180);

                if (viewOrientation.IsLeft)
                {
                    // if left orientation, aimCoef values will be from 1 to 2
                    // remap them to values from 1 to 0 respectively
                    aimCoef = 1.5f - aimCoef;
                }
            }
            else // if oriented down
            {
                if (aimAngle < 90)
                {
                    // it means we're in first quarter, extend aimAngle
                    // on 360 degrees to keep coordinates continuum (from 180*3/4 to 180*(2+(1/4)))
                    aimAngle += 360;
                }

                // offset angle
                aimAngle -= 45;
                // calculated angle between 0 and 270 degrees
                aimAngle = 360 - aimAngle;
                // calculate coef (from 0 to 2)
                aimCoef = (float)(aimAngle / 180);

                if (viewOrientation.IsLeft)
                {
                    // if left orientation, aimCoef values will be from 1 to 2
                    // remap them to values from 1 to 0 respectively
                    aimCoef = 1.5f - aimCoef;
                }

                // invert coefficient
                aimCoef = 1f - aimCoef;
            }

            //Api.Logger.WriteDev(
            //    $"AngleDeg: {angleDeg:F2}. Aiming coef: {aimCoef:F2}. Current view data: isUp={viewOrientation.IsUp} isLeft={viewOrientation.IsLeft}");
        }