public static void RefreshCharacterFinalStatsCache( IReadOnlyStatsDictionary protoEffects, ICharacterPublicState publicState, BaseCharacterPrivateState privateState, bool isFirstTime = false) { var containerEquipment = (publicState as ICharacterPublicStateWithEquipment)?.ContainerEquipment; privateState.ContainerEquipmentLastStateHash = containerEquipment?.StateHash; FinalStatsCache finalStatsCache; using (var tempStatsCache = TempStatsCache.GetFromPool(isMultipliersSummed: false)) { // merge character prototype effects tempStatsCache.Merge(protoEffects); if (privateState is PlayerCharacterPrivateState playerCharacterPrivateState) { // merge skill effects var skills = playerCharacterPrivateState.Skills; skills.SharedFillEffectsCache(tempStatsCache); } foreach (var statusEffect in privateState.StatusEffects) { var protoStatusEffect = (IProtoStatusEffect)statusEffect.ProtoLogicObject; tempStatsCache.Merge(protoStatusEffect.ProtoEffects); } if (containerEquipment != null) { // merge equipment effects foreach (var item in containerEquipment.Items) { if (item.ProtoGameObject is IProtoItemEquipment protoEquipment) { tempStatsCache.Merge(protoEquipment.ProtoEffects); } } } // calculate the final stats cache finalStatsCache = tempStatsCache.CalculateFinalStatsCache(); } privateState.FinalStatsCache = finalStatsCache; // need to recalculate the weapon cache as it depends on the final cache privateState.WeaponState.WeaponCache = null; ApplyFinalStatsCache(finalStatsCache, publicState.CurrentStats, isFirstTime); }
public ViewModelCharacterBossHealthbarControl(ICharacter character) { this.character = character; this.publicState = character.GetPublicState <ICharacterPublicState>(); this.clientState = character.GetClientState <BaseCharacterClientState>(); this.ViewModelCharacterHealthBarControl = new ViewModelCharacterHealthBarControl { CharacterCurrentStats = this.publicState.CurrentStats }; ClientUpdateHelper.UpdateCallback += this.Update; }
public void Setup(IComponentSkeleton skeleton, ICharacter characterPilot) { this.skeleton = skeleton; this.characterPublicState = characterPilot.GetPublicState <ICharacterPublicState>(); this.soundEmitter = Api.Client.Audio.CreateSoundEmitter(this.SceneObject, SoundResource, is3D: !characterPilot.IsCurrentClientCharacter, isLooped: true, isPlaying: true, volume: 0); this.lastAngleRad = this.GetCurrentAngleRad(); }
private void ServerRebuildFinalCacheIfNeeded( ICharacter character, BaseCharacterPrivateState privateState, ICharacterPublicState publicState) { if (!privateState.FinalStatsCache.IsDirty) { return; } // rebuild stats cache SharedCharacterStatsHelper.RefreshCharacterFinalStatsCache(this.ProtoCharacterDefaultEffects, publicState, privateState); }
public static double SharedGetCurrentRangeMax( ICharacterPublicState characterPublicState) { if (!(characterPublicState.SelectedItemWeaponProto is IProtoItemWeaponRanged protoWeaponRanged)) { return(0); } var damageDescription = WeaponSystem.GetCurrentDamageDescription(characterPublicState.SelectedItem, protoWeaponRanged, out _); return(damageDescription is null ? 0 : damageDescription.RangeMax *protoWeaponRanged.RangeMultiplier); }
public static void ClientUpdateAnimation( ICharacter character, BaseCharacterClientState clientState, ICharacterPublicState publicState) { var skeletonRenderer = clientState.SkeletonRenderer; if (skeletonRenderer is null) { return; } var activeWeaponProto = publicState.SelectedItemWeaponProto; var protoSkeleton = clientState.CurrentProtoSkeleton; var rendererShadow = clientState.RendererShadow; if (publicState is PlayerCharacterPublicState { CurrentPublicActionState : CharacterLaunchpadEscapeAction.PublicState })
public ViewModelCharacterOverlayControl(ICharacter character) { this.character = character; this.publicState = character.GetPublicState <ICharacterPublicState>(); if (!character.IsNpc) { this.ViewModelCharacterNameControl = new ViewModelCharacterNameControl(character); } this.ViewModelCharacterUnstuckInfoControl = new ViewModelCharacterUnstuckInfoControl(character); this.ViewModelCharacterHealthBarControl = new ViewModelCharacterHealthBarControl(); this.ViewModelCharacterHealthBarControl.CharacterCurrentStats = this.publicState.CurrentStats; this.ViewModelCharacterPublicStatusEffects = new ViewModelCharacterPublicStatusEffects(this.publicState.CurrentPublicStatusEffects); ClientUpdateHelper.UpdateCallback += this.Update; this.Update(); }
public ViewModelCharacterOverlayControl(ICharacter character, Action callbackVisualStateChanged) { this.character = character; this.callbackVisualStateChanged = callbackVisualStateChanged; this.publicState = character.GetPublicState <ICharacterPublicState>(); if (!character.IsNpc) { this.ViewModelCharacterNameControl = new ViewModelCharacterNameControl(character); } this.ViewModelCharacterUnstuckInfoControl = new ViewModelCharacterUnstuckInfoControl(character); this.ViewModelCharacterHealthBarControl = new ViewModelCharacterHealthBarControl(); this.ViewModelCharacterHealthBarControl.CharacterCurrentStats = this.publicState.CurrentStats; this.ViewModelCharacterHealthBarControl.MobPublicState = this.publicState as CharacterMobPublicState; //MOD this.ViewModelCharacterPublicStatusEffects = new ViewModelCharacterPublicStatusEffects( this.publicState.CurrentPublicStatusEffects); ClientUpdateHelper.UpdateCallback += this.Update; this.visualStateName = this.GetDesiredVisualStateName(); }
public static void ClientUpdateAnimation( ICharacter character, BaseCharacterClientState clientState, ICharacterPublicState publicState) { var skeletonRenderer = clientState.SkeletonRenderer; if (skeletonRenderer is null) { return; } var activeWeaponProto = publicState.SelectedItemWeaponProto; var protoSkeleton = clientState.CurrentProtoSkeleton; var rendererShadow = clientState.RendererShadow; var wasDead = clientState.IsDead; clientState.IsDead = publicState.IsDead; if (publicState.IsDead) { if (skeletonRenderer.GetCurrentAnimationName(AnimationTrackIndexes.Primary) == "Death") { // already in death animation return; } // character was not dead on client and now become dead skeletonRenderer.ResetSkeleton(); skeletonRenderer.SelectCurrentSkeleton( protoSkeleton.SkeletonResourceFront, "Idle", isLooped: false); skeletonRenderer.SetAnimation(AnimationTrackIndexes.Primary, "Death", isLooped: false); if (!wasDead.HasValue) { // character entered scope and it's dead skeletonRenderer.SetAnimationTime(0, 10000); // hide skeleton completely HideBody(); return; } // character just died (was in another animation state) if (!character.IsNpc) { // play death sound (please note - NPC death sound is played by network event only) protoSkeleton.PlaySound(CharacterSound.Death, character); } clientState.SoundEmitterLoopCharacter.Stop(); clientState.SoundEmitterLoopMovemement.Stop(); // hide skeleton after timeout ClientTimersSystem.AddAction( CorpseTimeoutSeconds, HideBody); void HideBody() { if (!publicState.IsDead) { // the character has been respawned return; } skeletonRenderer.IsEnabled = false; rendererShadow.IsEnabled = false; } return; } skeletonRenderer.IsEnabled = true; rendererShadow.IsEnabled = true; var appliedInput = publicState.AppliedInput; var rotationAngleRad = GetCurrentRotationAngleRadInterpolated(character, clientState, appliedInput); var moveModes = appliedInput.MoveModes; if (publicState is PlayerCharacterPublicState playerCharacterPublicState && playerCharacterPublicState.CurrentVehicle is { } vehicle) { var protoVehicle = (IProtoVehicle)vehicle.ProtoGameObject; protoVehicle.SharedGetSkeletonProto(vehicle, out var vehicleSkeleton, out _); if (vehicleSkeleton is null) { // this vehicle doesn't provide any skeleton so player is simply displayed as standing idle inside it moveModes = CharacterMoveModes.None; } } protoSkeleton.GetCurrentAnimationSetting( character, moveModes, rotationAngleRad, clientState.LastViewOrientation, out var newAnimationStarterName, out var newAnimationName, out var currentDrawMode, out var aimCoef, out var viewOrientation, out var isIdle); clientState.LastViewOrientation = viewOrientation; // TODO: consider adding new field - HasBackwardAnimations if (!protoSkeleton.HasMoveStartAnimations) { newAnimationStarterName = null; if (newAnimationName == "RunSideBackward") { newAnimationName = "RunSide"; } } var currentSkeleton = viewOrientation.IsUp && protoSkeleton.SkeletonResourceBack is not null ? protoSkeleton.SkeletonResourceBack : protoSkeleton.SkeletonResourceFront; if (skeletonRenderer.CurrentSkeleton != currentSkeleton) { // switch skeleton completely skeletonRenderer.SelectCurrentSkeleton( currentSkeleton, // please note: no starter animation in that case! animationName: newAnimationName, isLooped: true); } else { var activeAnimationName = skeletonRenderer.GetLatestAddedAnimationName( trackIndex: AnimationTrackIndexes.Primary); if (newAnimationName != activeAnimationName && newAnimationStarterName != activeAnimationName) { //Api.Logger.WriteDev( // newAnimationStarterName is not null // ? $"Changing move animation: {activeAnimationName}->{newAnimationStarterName}->{newAnimationName}" // : $"Changing move animation: {activeAnimationName}->{newAnimationName}"); skeletonRenderer.RemoveAnimationTrackNextEntries( trackIndex: AnimationTrackIndexes.Primary); var currentAnimationName = skeletonRenderer.GetCurrentAnimationName( trackIndex: AnimationTrackIndexes.Primary, getLatestAddedAnimation: false); var hasStarterAnimation = newAnimationStarterName is not null; if (newAnimationName != "Idle" && hasStarterAnimation) { // add starter animation skeletonRenderer.SetAnimationLoopMode(AnimationTrackIndexes.Primary, isLooped: false); if (newAnimationStarterName != currentAnimationName) { if (currentAnimationName == "Idle") { //Api.Logger.Dev("Adding starter animation: " // + newAnimationStarterName // + " (current animation is " // + currentAnimationName // + ")"); skeletonRenderer.SetAnimation( trackIndex: AnimationTrackIndexes.Primary, animationName: newAnimationStarterName, isLooped: false); } else { //Api.Logger.Dev("No starter animation will be added. Current animation: " // + currentAnimationName // + " starter name: " // + newAnimationStarterName); //skeletonRenderer.AddAnimation( // trackIndex: AnimationTrackIndexes.Primary, // animationName: newAnimationStarterName, // isLooped: false); } } else { //Api.Logger.Dev("No need to add starter animation: " // + newAnimationStarterName // + " (current animation is " // + currentAnimationName // + ")"); } // add looped animation skeletonRenderer.AddAnimation( trackIndex: AnimationTrackIndexes.Primary, animationName: newAnimationName, isLooped: true); } else { if (currentAnimationName is not null && currentAnimationName.EndsWith("Start")) { //Api.Logger.Dev("Adding new animation + Abort animation after START animation: " // + newAnimationName // + " (current animation is " // + currentAnimationName // + ")"); // going into idle when playing a start animation - allow to finish it! // remove queued entries skeletonRenderer.RemoveAnimationTrackNextEntries( trackIndex: AnimationTrackIndexes.Primary); // add abort animation skeletonRenderer.AddAnimation( trackIndex: AnimationTrackIndexes.Primary, animationName: currentAnimationName + "Abort", isLooped: false); // add looped animation skeletonRenderer.AddAnimation( trackIndex: AnimationTrackIndexes.Primary, animationName: newAnimationName, isLooped: true); } else if (currentAnimationName is not null && currentAnimationName.EndsWith("Abort")) { //Api.Logger.Dev("Adding new animation after Abort animation: " // + newAnimationName // + " (current animation is " // + currentAnimationName // + ")"); // remove queued entries skeletonRenderer.RemoveAnimationTrackNextEntries( trackIndex: AnimationTrackIndexes.Primary); // add end animation skeletonRenderer.AddAnimation( trackIndex: AnimationTrackIndexes.Primary, animationName: currentAnimationName + "Abort", isLooped: false); // add looped animation skeletonRenderer.AddAnimation( trackIndex: AnimationTrackIndexes.Primary, animationName: newAnimationName, isLooped: true); }
public static void RefreshCharacterFinalStatsCache( IReadOnlyStatsDictionary protoEffects, ICharacterPublicState publicState, BaseCharacterPrivateState privateState, bool isFirstTime = false) { var containerEquipment = (publicState as ICharacterPublicStateWithEquipment)?.ContainerEquipment; privateState.ContainerEquipmentLastStateHash = containerEquipment?.StateHash; FinalStatsCache finalStatsCache; using (var tempStatsCache = TempStatsCache.GetFromPool()) { // merge character prototype effects tempStatsCache.Merge(protoEffects); if (privateState is PlayerCharacterPrivateState playerCharacterPrivateState) { // merge origin effects if (playerCharacterPrivateState.Origin is not null) { tempStatsCache.Merge(playerCharacterPrivateState.Origin.Effects); } // merge skill effects var skills = playerCharacterPrivateState.Skills; skills.SharedFillEffectsCache(tempStatsCache); // merge perks from tech nodes foreach (var techNode in playerCharacterPrivateState.Technologies.Nodes) { foreach (var nodeEffect in techNode.NodeEffects) { if (nodeEffect is TechNodeEffectPerkUnlock perkUnlock) { tempStatsCache.Merge(perkUnlock.Perk.ProtoEffects); } } } } foreach (var statusEffect in privateState.StatusEffects) { if (statusEffect.IsDestroyed) { // the status effect might be already destroyed but still remain in the list continue; } var protoStatusEffect = (IProtoStatusEffect)statusEffect.ProtoLogicObject; tempStatsCache.Merge(protoStatusEffect.ProtoEffects); } if (containerEquipment is not null) { // merge equipment effects foreach (var item in containerEquipment.Items) { if (item.ProtoGameObject is IProtoItemEquipment protoEquipment && protoEquipment.SharedCanApplyEffects(item, containerEquipment)) { tempStatsCache.Merge(protoEquipment.ProtoEffects); } } } // calculate the final stats cache finalStatsCache = tempStatsCache.CalculateFinalStatsCache(); } privateState.FinalStatsCache = finalStatsCache; // need to recalculate the weapon cache as it depends on the final cache privateState.WeaponState.WeaponCache = null; ApplyFinalStatsCache(finalStatsCache, publicState.CurrentStats, isFirstTime); }
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 ClientUpdateAnimation( ICharacter character, BaseCharacterClientState clientState, ICharacterPublicState publicState) { var skeletonRenderer = clientState.SkeletonRenderer; if (skeletonRenderer == null) { return; } var activeWeaponProto = publicState.CurrentItemWeaponProto; var protoSkeleton = clientState.CurrentProtoSkeleton; var rendererShadow = clientState.RendererShadow; var wasDead = clientState.IsDead; clientState.IsDead = publicState.IsDead; if (publicState.IsDead) { if (skeletonRenderer.GetCurrentAnimationName(AnimationTrackIndexes.Primary) == "Death") { // already in death animation return; } // character was not dead on client and now become dead skeletonRenderer.ResetSkeleton(); skeletonRenderer.SelectCurrentSkeleton( protoSkeleton.SkeletonResourceFront, "Idle", isLooped: false); skeletonRenderer.SetAnimation(AnimationTrackIndexes.Primary, "Death", isLooped: false); if (!wasDead.HasValue) { // character entered scope and it's dead skeletonRenderer.SetAnimationTime(0, 10000); // hide skeleton completely HideBody(); return; } // character just died // play death sound protoSkeleton.PlaySound(CharacterSound.Death, character); clientState.SoundEmitterLoopCharacter.Stop(); clientState.SoundEmitterLoopMovemement.Stop(); // hide skeleton after timeout ClientTimersSystem.AddAction( CorpseTimeoutSeconds, HideBody); void HideBody() { if (!publicState.IsDead) { // the character has been respawned return; } skeletonRenderer.IsEnabled = false; rendererShadow.IsEnabled = false; } return; } skeletonRenderer.IsEnabled = true; rendererShadow.IsEnabled = true; var appliedInput = publicState.AppliedInput; var rotationAngleRad = GetCurrentRotationAngleRadInterpolated(character, clientState, appliedInput); GetCurrentAnimationSetting( protoSkeleton, appliedInput.MoveModes, rotationAngleRad, clientState.LastViewOrientation, out var newAnimationStarterName, out var newAnimationName, out var currentDrawMode, out var aimCoef, out var viewOrientation, out var isIdle); clientState.LastViewOrientation = viewOrientation; // TODO: consider adding new field - HasBackwardAnimations if (!protoSkeleton.HasMoveStartAnimations) { newAnimationStarterName = null; if (newAnimationName == "RunSideBackward") { newAnimationName = "RunSide"; } } var currentSkeleton = viewOrientation.IsUp && protoSkeleton.SkeletonResourceBack != null ? protoSkeleton.SkeletonResourceBack : protoSkeleton.SkeletonResourceFront; if (skeletonRenderer.CurrentSkeleton != currentSkeleton) { // switch skeleton completely skeletonRenderer.SelectCurrentSkeleton( currentSkeleton, // please note: no starter animation in that case! animationName: newAnimationName, isLooped: true); } else { var activeAnimationName = skeletonRenderer.GetLatestAddedAnimationName(trackIndex: AnimationTrackIndexes.Primary); if (newAnimationName != activeAnimationName && newAnimationStarterName != activeAnimationName) { //Api.Logger.WriteDev( // newAnimationStarterName != null // ? $"Changing move animation: {activeAnimationName}->{newAnimationStarterName}->{newAnimationName}" // : $"Changing move animation: {activeAnimationName}->{newAnimationName}"); var hasStarterAnimation = newAnimationStarterName != null; if (hasStarterAnimation) { // add starter animation skeletonRenderer.SetAnimation( trackIndex: AnimationTrackIndexes.Primary, animationName: newAnimationStarterName, isLooped: false); // add looped animation skeletonRenderer.AddAnimation( trackIndex: AnimationTrackIndexes.Primary, animationName: newAnimationName, isLooped: true); } else if (newAnimationName == "Idle" && skeletonRenderer.GetCurrentAnimationName(trackIndex: AnimationTrackIndexes.Primary) .EndsWith("Start")) { // going into idle when playing a start animation - allow to finish it! // remove queued entries skeletonRenderer.RemoveAnimationTrackNextEntries(trackIndex: AnimationTrackIndexes.Primary); // add looped idle animation skeletonRenderer.AddAnimation( trackIndex: AnimationTrackIndexes.Primary, animationName: newAnimationName, isLooped: true); } else { // set looped animation skeletonRenderer.SetAnimation( trackIndex: AnimationTrackIndexes.Primary, animationName: newAnimationName, isLooped: true); } } } if (appliedInput.MoveModes != CharacterMoveModes.None) { // moving mode var animationSpeedMultiplier = appliedInput.MoveSpeed / (protoSkeleton.DefaultMoveSpeed * clientState.CurrentProtoSkeletonScaleMultiplier); skeletonRenderer.SetAnimationSpeed( trackIndex: AnimationTrackIndexes.Primary, speedMultiliper: (float)animationSpeedMultiplier); // moving - remove animation for static firing skeletonRenderer.RemoveAnimationTrack(AnimationTrackIndexes.ItemFiringStatic); } else { skeletonRenderer.SetAnimationSpeed( trackIndex: AnimationTrackIndexes.Primary, speedMultiliper: 1.0f); } skeletonRenderer.DrawMode = currentDrawMode; if (activeWeaponProto != null) { clientState.HasWeaponAnimationAssigned = true; clientState.LastAimCoef = aimCoef; var aimingAnimationName = activeWeaponProto.CharacterAnimationAimingName; if (aimingAnimationName != null) { //Api.Logger.WriteDev( // $"Setting aiming animation: {aimingAnimationName} timePercents: {aimCoef:F2}"); skeletonRenderer.SetAnimationFrame( trackIndex: AnimationTrackIndexes.ItemAiming, animationName: aimingAnimationName, timePositionPercents: aimCoef); } else { skeletonRenderer.RemoveAnimationTrack(AnimationTrackIndexes.ItemAiming); } WeaponSystemClientDisplay.RefreshCurrentAttackAnimation(character); } else { clientState.HasWeaponAnimationAssigned = false; } SetLoopSounds(character, clientState, publicState.AppliedInput, protoSkeleton, isIdle); }
public static void ClientRebuildAppearance( ICharacter character, BaseCharacterClientState clientState, ICharacterPublicState publicState, IItem selectedItem) { character.ProtoCharacter.SharedGetSkeletonProto( character, out var newProtoSkeleton, out var 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; if (skeletonRenderer == null || isSkeletonChanged) { skeletonRenderer?.Destroy(); if (newProtoSkeleton == null || newProtoSkeleton.SkeletonResourceFront == null) { return; } var sceneObject = Api.Client.Scene.GetSceneObject(character); var scale = clientState.CurrentProtoSkeleton.WorldScale * clientState.CurrentProtoSkeletonScaleMultiplier; // create new skeleton renderer skeletonRenderer = CreateCharacterSkeleton(sceneObject, 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 != null) { SetupSkeletonEquipmentForCharacter( character, containerEquipment, skeletonRenderer, clientState.CurrentProtoSkeleton, skeletonComponents); } if (selectedItem != null) { var activeProtoItem = selectedItem.ProtoGameObject as IProtoItemWithCharacterAppearance; activeProtoItem?.ClientSetupSkeleton(selectedItem, character, skeletonRenderer, skeletonComponents); } if (character.IsCurrentClientCharacter && character.ProtoCharacter is PlayerCharacter && !skeletonComponents.Any(c => c is ClientComponentLightInSkeleton)) { // this is current client character and it doesn't have light in hand // create a faint light source (see called method comments) var lightSource = PlayerCharacter.ClientCreateDefaultLightSource(character); if (lightSource != null) { skeletonComponents.Add(lightSource); } } }