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); }
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}"); }