/// <summary> /// Shared (current chararacter 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, float deltaTime) { if (!character.ServerIsOnline || publicState.IsDead) { return; } var stats = publicState.CurrentStatsExtended; // restore stamina slower when no food or water var staminaRestoreMultiplier = stats.FoodCurrent == 0f || stats.WaterCurrent == 0f ? 0.33f : 1f; var finalStatsCache = privateState.FinalStatsCache; var statStaminaRegeneration = finalStatsCache[StatName.StaminaRegenerationPerSecond]; // update character stats if (publicState.AppliedInput.MoveModes != CharacterMoveModes.None) { 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 - restore stamina var staminaRestore = statStaminaRegeneration * staminaRestoreMultiplier * deltaTime; stats.SharedSetStaminaCurrent((float)(stats.StaminaCurrent + staminaRestore), notifyClient: false); } }
private static void GameInit(ICharacter character) { foreach (var child in Api.Client.UI.LayoutRootChildren) { if (child is HUDLayoutControl layoutControl) { hudLayoutControl = layoutControl; } } if (hudLayoutControl != null) { hudLayoutControl.Loaded += LayoutControl_Loaded; } else { Api.Logger.Error("LPNotifications: HUDLayoutControl not found."); } stateSubscriptionStorage = new StateSubscriptionStorage(); privateState = PlayerCharacter.GetPrivateState(character); lp = privateState.Technologies.LearningPoints; privateState.Technologies.ClientSubscribe( t => t.LearningPoints, OnLPChanged, stateSubscriptionStorage); }
private void SharedApplyInput(ICharacter character, PlayerCharacterPrivateState privateState) { var input = privateState.Input; // apply input privateState.Input.SetChanged(false); 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; } var moveSpeed = IsServer ? Server.World.GetDynamicObjectMoveSpeed(character) : Client.World.GetDynamicObjectMoveSpeed(character); if (directionX == 0 && directionY == 0) { moveSpeed = 0; } Vector2D directionVector = (directionX, directionY); var moveAcceleration = this.PhysicsBodyAccelerationCoef * moveSpeed * directionVector.Normalized; if (IsServer) { Server.World.SetDynamicObjectPhysicsMovement(character, moveAcceleration, targetVelocity: moveSpeed); } else { Client.World.SetDynamicObjectPhysicsMovement(character, moveAcceleration, targetVelocity: moveSpeed); } }
/// <summary> /// Checks if current hotbar item is different /// </summary> public static void SharedRefreshSelectedHotbarItem( ICharacter character, PlayerCharacterPrivateState privateState) { if ((IsServer || character.IsCurrentClientCharacter) && privateState.ContainerHotbar.StateHash != privateState.ContainerHotbarLastStateHash) { // need to refresh the selected hotbar item SharedSelectHotbarSlotId(character, privateState.SelectedHotbarSlotId); } }
public void Setup(PlayerCharacterPrivateState privateState) { this.privateState = privateState; this.skillsDictionary = privateState.Skills.Skills; this.skillsDictionary.ClientPairSet += this.SkillsDictionaryPairSetHandler; this.skillsDictionary.ClientPairRemoved += this.SkillsDictionaryClientPairRemoved; foreach (var pair in this.skillsDictionary) { this.RegisterSkill(pair.Key, pair.Value, isInitial: true); } }
public static void Init(ICharacter character) { ClientCurrentCharacterFinalStatsHelper.character = ClientCurrentCharacterHelper.Character; privateState = PlayerCharacter.GetPrivateState(character); subscriptionStorage?.Dispose(); subscriptionStorage = new StateSubscriptionStorage(); privateState.ClientSubscribe(_ => _.FinalStatsCache, _ => Api.SafeInvoke(FinalStatsCacheChanged), subscriptionStorage); Api.SafeInvoke(FinalStatsCacheChanged); }
public ClientWorldMapBedVisualizer(WorldMapController worldMapController) : base(worldMapController) { this.stateSubscriptionOwner = new StateSubscriptionStorage(); this.playerCharacterPrivateState = ClientCurrentCharacterHelper.PrivateState; this.playerCharacterPrivateState.ClientSubscribe( _ => _.CurrentBedObjectPosition, _ => this.Refresh(), this.stateSubscriptionOwner); this.Refresh(); }
public ClientWorldMapBedVisualizer(WorldMapController worldMapController) { this.stateSubscriptionOwner = new StateSubscriptionStorage(); this.worldMapController = worldMapController; this.playerCharacterPrivateState = Api.Client.Characters.CurrentPlayerCharacter .GetPrivateState <PlayerCharacterPrivateState>(); this.playerCharacterPrivateState.ClientSubscribe( _ => _.CurrentBedObjectPosition, _ => this.Refresh(), this.stateSubscriptionOwner); this.Refresh(); }
private int GetGroundSlotCount(PlayerCharacterPrivateState playerPrivateState) { int nb = 0; for (byte i = PlayerConstants.InventorySlotsCount; i < playerPrivateState.ContainerInventory.SlotsCount; i++) { IItem itemToDrop = playerPrivateState.ContainerInventory.GetItemAtSlot(i); if (itemToDrop is not null) { nb++; } } return(nb); }
private void SharedApplyInput(ICharacter character, PlayerCharacterPrivateState privateState) { var input = privateState.Input; // apply input privateState.Input.SetChanged(false); 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 moveSpeed = IsServer ? Server.Characters.GetMoveSpeed(character) : Client.Characters.GetCurrentCharacterMoveSpeed(); var moveVelocity = directionVector.Normalized * moveSpeed; if (IsServer) { Server.Characters.SetVelocity(character, moveVelocity); } else { Client.Characters.SetVelocity(character, moveVelocity); } }
protected override void OnEnable() { this.Reset(); this.subscriptionStorage = new StateSubscriptionStorage(); this.characterPrivateState = ClientCurrentCharacterHelper.PrivateState; this.characterPrivateState.ClientSubscribe(_ => _.LastDismountedVehicleMapMark, _ => this.Refresh(), this.subscriptionStorage); this.characterPublicState = ClientCurrentCharacterHelper.PublicState; this.characterPublicState.ClientSubscribe(_ => _.CurrentVehicle, _ => this.Refresh(), this.subscriptionStorage); this.Refresh(); }
/// <summary> /// Checks if current hotbar item is different /// </summary> public static void SharedRefreshSelectedHotbarItem( ICharacter character, PlayerCharacterPrivateState privateState) { if (IsClient && !character.IsCurrentClientCharacter) { // don't refresh hotbar items for other characters on client return; } var itemsContainer = TryGetVehicleHotbarContainer(character, out _, out _, out _) ?? privateState.ContainerHotbar; if (itemsContainer.StateHash != privateState.ContainerHotbarLastStateHash) { // need to refresh the selected hotbar item SharedForceRefreshCurrentItem(character, privateState); } }
public void Setup(PlayerCharacterPrivateState privateState) { TechGroupsChanged = null; TechNodesChanged = null; this.technologies = privateState.Technologies; this.techGroups = this.technologies.Groups; this.techNodes = this.technologies.Nodes; this.technologies.ClientSubscribe( _ => _.LearningPoints, _ => LearningPointsChanged?.Invoke(), this); this.techGroups.ClientAnyModification += this.TechGroupsClientAnyModificationHandler; this.techNodes.ClientAnyModification += this.TechNodesClientAnyModificationHandler; instance = this; CurrentTechnologiesChanged?.Invoke(); TechGroupsChanged?.Invoke(); TechNodesChanged?.Invoke(); LearningPointsChanged?.Invoke(); }
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); } } }
public void Setup(PlayerCharacterPrivateState setPrivateState) { this.privateState = setPrivateState; this.Setup(); }
protected BaseActionState(ICharacter character) { this.Character = character; this.CharacterPrivateState = PlayerCharacter.GetPrivateState(character); this.CharacterPublicState = PlayerCharacter.GetPublicState(character); }
private static void SharedForceRefreshCurrentItem( ICharacter character, PlayerCharacterPrivateState privateState) { SharedSelectHotbarSlotId(character, privateState.SelectedHotbarSlotId, isByPlayer: false); }
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 not 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?.IsBlockingMovement ?? 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) { if (characterFinalStateCache.HasPerk(StatName.PerkCannotRun)) { isRunning = false; } else { 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; } } }