예제 #1
0
        private static void ServerCheckFiredShotsMismatch(WeaponState state, ICharacter character)
        {
            var ammoConsumptionPerShot = state.ProtoWeapon.AmmoConsumptionPerShot;

            if (ammoConsumptionPerShot == 0)
            {
                // weapon doesn't use any ammo - no problem with possible desync
                return;
            }

            if (!WeaponAmmoSystem.IsResetsShotsDoneNumberOnReload(state.ProtoWeapon))
            {
                // this weapon can keep firing after the reload on the server side
                return;
            }

            var requestedShotsCount = state.ServerLastClientReportedShotsDoneCount;

            if (!requestedShotsCount.HasValue)
            {
                return;
            }

            var extraShotsDone = (int)(state.ShotsDone - (long)requestedShotsCount.Value);

            state.ServerLastClientReportedShotsDoneCount = null;

            if (extraShotsDone == 0)
            {
                return;
            }

            if (extraShotsDone < 0)
            {
                // should never happen as server should fire as much as client requested, always
                return;
            }

            var itemWeapon = state.ItemWeapon;

            if (itemWeapon == null)
            {
                return;
            }

            Logger.Important($"Shots count mismatch: requested={requestedShotsCount} actualShotsDone={state.ShotsDone}",
                             character);
            Instance.CallClient(character,
                                _ => _.ClientRemote_FixAmmoCount(itemWeapon, extraShotsDone));
        }
예제 #2
0
        public static void SharedUpdateCurrentWeapon(
            ICharacter character,
            WeaponState state,
            double deltaTime)
        {
            var protoWeapon = state.ActiveProtoWeapon;

            if (protoWeapon == null)
            {
                return;
            }

            if (state.CooldownSecondsRemains > 0)
            {
                // decrease cooldown
                state.CooldownSecondsRemains -= deltaTime;
            }

            if (!state.IsFiring)
            {
                WeaponAmmoSystem.SharedUpdateReloading(state, character, ref deltaTime);
            }

            if (deltaTime <= 0)
            {
                // the weapon reloading process is consumed the whole delta time
                return;
            }

            if (state.SharedGetInputIsFiring() &&
                !character.IsOnline)
            {
                state.SetInputIsFiring(false);
            }

            if (state.SharedGetInputIsFiring() &&
                StatusEffectDazed.SharedIsCharacterDazed(character,
                                                         StatusEffectDazed.NotificationCannotAttackWhileDazed))
            {
                state.SetInputIsFiring(false);
            }

            // check ammo (if applicable to this weapon prototype)
            var canFire = protoWeapon.SharedCanFire(character, state);

            if (state.CooldownSecondsRemains > 0)
            {
                // firing cooldown is not completed
                if (!state.SharedGetInputIsFiring() &&
                    state.IsEventWeaponStartSent)
                {
                    // not firing anymore
                    SharedCallOnWeaponInputStop(state, character);
                }

                return;
            }

            var wasFiring = state.IsFiring;

            if (!state.IsFiring)
            {
                state.IsFiring = state.SharedGetInputIsFiring();
            }
            else // if IsFiring
            {
                if (!SharedShouldFireMore(state))
                {
                    state.IsFiring = state.SharedGetInputIsFiring();
                }
            }

            if (!canFire)
            {
                // cannot fire (no ammo, etc)
                state.IsFiring = false;
            }

            if (!state.IsFiring)
            {
                if (wasFiring)
                {
                    // just stopped firing
                    SharedCallOnWeaponFinished(state, character);
                }

                // the character is not firing
                // reset delay for the next shot (it will be set when firing starts next time)
                state.DamageApplyDelaySecondsRemains = 0;
                return;
            }

            // let's process what happens when we're in the firing mode
            if (!state.IsEventWeaponStartSent)
            {
                // started firing
                SharedCallOnWeaponStart(state, character);
            }

            if (state.DamageApplyDelaySecondsRemains <= 0)
            {
                // initialize delay to next shot
                state.DamageApplyDelaySecondsRemains = protoWeapon.DamageApplyDelay;
                SharedCallOnWeaponShot(character);
            }

            // decrease the remaining time to the damage application
            state.DamageApplyDelaySecondsRemains -= deltaTime;

            if (state.DamageApplyDelaySecondsRemains > 0)
            {
                // firing delay not completed
                return;
            }

            // firing delay completed
            state.ShotsDone++;
            //Logger.Dev("Weapon fired, shots done: " + state.ShotsDone);
            SharedFireWeapon(character, state.ActiveItemWeapon, protoWeapon, state);
            state.CooldownSecondsRemains += protoWeapon.FireInterval - protoWeapon.DamageApplyDelay;

            if (!protoWeapon.IsLoopedAttackAnimation)
            {
                // we don't want to stuck this animation in the last frame
                // that's fix for the issue:
                // "Fix extended animation "stuck" issue for mobs (like limbs stuck in the end position and movement animation appears broken)"
                state.IsEventWeaponStartSent = false;
            }
        }
예제 #3
0
        public static void SharedUpdateCurrentWeapon(
            ICharacter character,
            WeaponState state,
            double deltaTime)
        {
            var protoWeapon = state.ProtoWeapon;

            if (protoWeapon == null)
            {
                return;
            }

            if (deltaTime > 0.4)
            {
                // too large delta time probably due to a frame skip
                deltaTime = 0.4;
            }

            if (state.CooldownSecondsRemains > 0)
            {
                state.CooldownSecondsRemains -= deltaTime;
                if (state.CooldownSecondsRemains < -0.2)
                {
                    // clamp the remaining cooldown in case of a frame skip
                    state.CooldownSecondsRemains = -0.2;
                }
            }

            if (state.ReadySecondsRemains > 0)
            {
                state.ReadySecondsRemains -= deltaTime;
            }

            if (state.FirePatternCooldownSecondsRemains > 0)
            {
                state.FirePatternCooldownSecondsRemains -= deltaTime;

                if (state.FirePatternCooldownSecondsRemains <= 0)
                {
                    state.FirePatternCurrentShotNumber = 0;
                }
            }

            // TODO: restore this condition when we redo UI countdown animation for ViewModelHotbarItemWeaponOverlayControl.ReloadDurationSeconds
            //if (state.CooldownSecondsRemains <= 0)
            //{
            WeaponAmmoSystem.SharedUpdateReloading(state, character, deltaTime);
            //}

            if (Api.IsServer &&
                !character.ServerIsOnline &&
                state.SharedGetInputIsFiring())
            {
                state.SharedSetInputIsFiring(false);
            }

            // check ammo (if applicable to this weapon prototype)
            var canFire = (Api.IsClient || character.ServerIsOnline) &&
                          state.WeaponReloadingState is null &&
                          protoWeapon.SharedCanFire(character, state);

            if (state.CooldownSecondsRemains > 0)
            {
                // firing cooldown is not completed
                if (!state.SharedGetInputIsFiring() &&
                    state.IsEventWeaponStartSent)
                {
                    // not firing anymore
                    SharedCallOnWeaponInputStop(state, character);
                }

                return;
            }

            var wasFiring = state.IsFiring;

            if (!state.IsFiring)
            {
                state.IsFiring = state.SharedGetInputIsFiring();
            }
            else // if IsFiring
            {
                if (!SharedShouldFireMore(state))
                {
                    state.IsFiring = state.SharedGetInputIsFiring();
                }
            }

            if (!canFire)
            {
                // cannot fire (no ammo, etc)
                state.IsFiring = false;
            }

            if (!state.IsFiring)
            {
                if (wasFiring)
                {
                    // just stopped firing
                    SharedCallOnWeaponFinished(state, character);
                }

                // the character is not firing
                // reset delay for the next shot (it will be set when firing starts next time)
                state.DamageApplyDelaySecondsRemains = 0;
                return;
            }

            if (state.WeaponCache is null)
            {
                SharedRebuildWeaponCache(character, state);
            }

            // let's process what happens when we're in the firing mode
            if (!state.IsEventWeaponStartSent)
            {
                // started firing
                SharedCallOnWeaponStart(state, character);
            }

            if (state.DamageApplyDelaySecondsRemains <= 0)
            {
                // initialize delay to next shot
                state.DamageApplyDelaySecondsRemains =
                    Shared.RoundDurationByServerFrameDuration(protoWeapon.DamageApplyDelay);

                SharedCallOnWeaponShot(character, protoWeapon);
            }

            // decrease the remaining time to the damage application
            state.DamageApplyDelaySecondsRemains -= deltaTime;

            if (state.DamageApplyDelaySecondsRemains > 0)
            {
                // firing delay not completed
                return;
            }

            // firing delay completed
            state.ShotsDone++;
            //Logger.Dev("Weapon fired, shots done: " + state.ShotsDone);
            SharedFireWeapon(character, state.ItemWeapon, protoWeapon, state);
            var cooldownDuration = Shared.RoundDurationByServerFrameDuration(protoWeapon.FireInterval)
                                   - Shared.RoundDurationByServerFrameDuration(protoWeapon.DamageApplyDelay);

            //Logger.Dev($"Cooldown adding: {cooldownDuration} for {protoWeapon}");

            state.CooldownSecondsRemains += cooldownDuration;

            if (!protoWeapon.IsLoopedAttackAnimation)
            {
                // we don't want to stuck this animation in the last frame
                // that's fix for the issue:
                // "Fix extended animation "stuck" issue for mobs (like limbs stuck in the end position and movement animation appears broken)"
                state.IsEventWeaponStartSent = false;
            }
        }