private void ServerRemote_ReloadWeapon(ReloadWeaponRequest args) { var character = ServerRemoteContext.Character; // force re-select current item PlayerCharacter.SharedForceRefreshCurrentItem(character); var itemWeapon = args.Item; if (itemWeapon is null) { throw new Exception("Item not found."); } if (!(itemWeapon.ProtoItem is IProtoItemWeapon itemProto)) { throw new Exception("Not a weapon: " + itemWeapon); } if (itemProto.AmmoCapacity == 0) { throw new Exception("The weapon is not reloadable: " + itemWeapon); } if (IsServer && !Server.Core.IsInPrivateScope(character, itemWeapon)) { throw new Exception( $"{character} cannot access {itemWeapon} because it's container is not in the private scope"); } if (itemWeapon.IsDestroyed || itemWeapon.Count < 1) { throw new Exception($"{itemWeapon} is destroyed"); } var privateState = itemWeapon.GetPrivateState <WeaponPrivateState>(); var weaponState = PlayerCharacter.GetPrivateState(character).WeaponState; if (weaponState is null || weaponState.ItemWeapon != itemWeapon) { throw new Exception( $"Only current active weapon could be reloaded: want to reload {itemWeapon}, but current active weapon is {weaponState?.ItemWeapon}"); } var selectedProtoItemAmmo = args.ProtoItemAmmo; var ammoCurrent = privateState.AmmoCount; var ammoMax = itemProto.AmmoCapacity; if (weaponState.WeaponReloadingState is null && ammoCurrent == ammoMax && privateState.CurrentProtoItemAmmo == selectedProtoItemAmmo) { Logger.Warning("Weapon is already full, no need to reload " + itemWeapon, character); return; } if (weaponState.WeaponReloadingState != null && weaponState.WeaponReloadingState.ProtoItemAmmo == selectedProtoItemAmmo) { Logger.Info("Weapon is already reloading this ammo, no need to reload " + itemWeapon, character); return; } // create reloading state on the Server-side var weaponReloadingState = new WeaponReloadingState( character, itemWeapon, itemProto, selectedProtoItemAmmo); weaponState.WeaponReloadingState = weaponReloadingState; Logger.Info( $"Weapon reloading started for {itemWeapon} reload duration: {weaponReloadingState.SecondsToReloadRemains:F2}s", character); if (weaponReloadingState.SecondsToReloadRemains == 0) { // instant-reloading weapon SharedProcessWeaponReload(character, weaponState, out _); } else if (IsServer) { ServerNotifyAboutReloading(character, weaponState, isFinished: false); } }
public static void ClientTryReloadOrSwitchAmmoType( bool isSwitchAmmoType, bool sendToServer = true, bool?showNotificationIfNoAmmo = null) { var character = Api.Client.Characters.CurrentPlayerCharacter; var currentWeaponState = PlayerCharacter.GetPrivateState(character).WeaponState; var itemWeapon = currentWeaponState.ItemWeapon; if (itemWeapon is null) { // no active weapon to reload return; } var protoWeapon = (IProtoItemWeapon)itemWeapon.ProtoItem; if (protoWeapon.AmmoCapacity == 0) { // the item is non-reloadable return; } var itemPrivateState = itemWeapon.GetPrivateState <WeaponPrivateState>(); var ammoCountNeed = isSwitchAmmoType ? protoWeapon.AmmoCapacity : (ushort)Math.Max(0, protoWeapon.AmmoCapacity - itemPrivateState.AmmoCount); if (ammoCountNeed == 0) { Logger.Info("No need to reload the weapon " + itemWeapon, character); return; } var compatibleAmmoGroups = SharedGetCompatibleAmmoGroups(character, protoWeapon); if (compatibleAmmoGroups.Count == 0 && !isSwitchAmmoType) { if (showNotificationIfNoAmmo.HasValue && showNotificationIfNoAmmo.Value || currentWeaponState.SharedGetInputIsFiring()) { protoWeapon.SoundPresetWeapon.PlaySound(WeaponSound.Empty, character, volume: SoundConstants.VolumeWeapon); NotificationSystem.ClientShowNotification( NotificationNoAmmo_Title, NotificationNoAmmo_Message, NotificationColor.Bad, protoWeapon.Icon, playSound: false); } if (currentWeaponState.SharedGetInputIsFiring()) { // stop firing the weapon currentWeaponState.ProtoWeapon.ClientItemUseFinish(itemWeapon); } return; } IProtoItemAmmo selectedProtoItemAmmo = null; var currentReloadingState = currentWeaponState.WeaponReloadingState; if (currentReloadingState is null) { // don't have reloading state - find ammo item matching current weapon ammo type var currentProtoItemAmmo = itemPrivateState.CurrentProtoItemAmmo; if (currentProtoItemAmmo is null) { // no ammo selected in weapon selectedProtoItemAmmo = SharedFindNextAmmoGroup(protoWeapon.CompatibleAmmoProtos, compatibleAmmoGroups, currentProtoItemAmmo: null)?.Key; } else // if weapon already has ammo { if (isSwitchAmmoType) { selectedProtoItemAmmo = SharedFindNextAmmoGroup(protoWeapon.CompatibleAmmoProtos, compatibleAmmoGroups, currentProtoItemAmmo)?.Key; if (selectedProtoItemAmmo == currentProtoItemAmmo && itemPrivateState.AmmoCount >= protoWeapon.AmmoCapacity) { // this ammo type is already loaded and it's fully reloaded Logger.Info("No need to reload the weapon " + itemWeapon, character); return; } } else // simple reload requested { // try to find ammo of the same type as already loaded into the weapon var isFound = false; foreach (var ammoGroup in compatibleAmmoGroups) { if (ammoGroup.Key == currentProtoItemAmmo) { isFound = true; selectedProtoItemAmmo = currentProtoItemAmmo; break; } } if (!isFound) { // no group selected - select first isSwitchAmmoType = true; sendToServer = true; selectedProtoItemAmmo = SharedFindNextAmmoGroup(protoWeapon.CompatibleAmmoProtos, compatibleAmmoGroups, currentProtoItemAmmo: null)?.Key; } } } } else { if (!isSwitchAmmoType) { // already reloading return; } // already reloading - try select another ammo type (alternate between them) var currentReloadingProtoItemAmmo = currentReloadingState.ProtoItemAmmo; selectedProtoItemAmmo = SharedFindNextAmmoGroup(protoWeapon.CompatibleAmmoProtos, compatibleAmmoGroups, currentReloadingProtoItemAmmo)?.Key; if (selectedProtoItemAmmo == currentReloadingProtoItemAmmo) { // already reloading this ammo type return; } } if (currentReloadingState != null && currentReloadingState.ProtoItemAmmo == selectedProtoItemAmmo) { // already reloading with these ammo items return; } if (currentReloadingState is null && selectedProtoItemAmmo is null && itemPrivateState.CurrentProtoItemAmmo is null) { // already unloaded return; } // create reloading state on the Client-side var weaponReloadingState = new WeaponReloadingState( character, itemWeapon, protoWeapon, selectedProtoItemAmmo); currentWeaponState.WeaponReloadingState = weaponReloadingState; protoWeapon.SoundPresetWeapon.PlaySound(WeaponSound.Reload, character, SoundConstants.VolumeWeapon); Logger.Info( $"Weapon reloading started for {itemWeapon} reload duration: {weaponReloadingState.SecondsToReloadRemains:F2}s", character); if (weaponReloadingState.SecondsToReloadRemains <= 0) { // instant-reload weapon - perform local reloading SharedProcessWeaponReload(character, currentWeaponState, out _); } if (sendToServer || isSwitchAmmoType) { // perform reload on server var arg = new ReloadWeaponRequest(itemWeapon, selectedProtoItemAmmo); Instance.CallServer(_ => _.ServerRemote_ReloadWeapon(arg)); } }