Ejemplo n.º 1
0
        /// <summary>
        /// Calculate the max range this turret can shoot at with at least one weapon,
        /// IN UNITY UNITS.
        /// </summary>
        /// This is a method to avoid storing duplicate information, and
        /// because we may want to ignore disabled turrets, or turrets
        /// that can't shoot at a specific target etc.
        ///
        /// TODO: Code duplication can be reduced if we only implement this in
        /// the turret class and have a fake toplevel turret we call this method on,
        /// but a fake turret like that also adds complexity, hard to decide.
        private float MaxRange(
            TargetTuple target,
            bool includeDirectFire,
            bool includeIndirectFire)
        {
            float maxRange = 0;

            foreach (Turret turret in Children)
            {
                float turretMax;
                if (includeDirectFire && includeIndirectFire)
                {
                    turretMax = turret.MaxRange(target);
                }
                else if (includeDirectFire)
                {
                    turretMax = turret.MaxRangeDirectFire(target);
                }
                else if (includeIndirectFire)
                {
                    turretMax = turret.MaxRangeIndirectFire(target);
                }
                else
                {
                    turretMax = 0;
                }


                if (maxRange < turretMax)
                {
                    maxRange = turretMax;
                }
            }
            return(maxRange);
        }
Ejemplo n.º 2
0
        private void Update()
        {
            foreach (Turret turret in Children)
            {
                turret.HandleUpdate();
            }

            float distanceToTarget = 99999;

            if (_target != null && _target.Exists)
            {
                // TODO move most of the Update logic to the respective turrets
                // (likely to a new member class of them called 'TargetingStrategy')
                distanceToTarget = Vector3.Distance(
                    Unit.transform.position, _target.Position);
                if (_fireRange > distanceToTarget && _movingTowardsTarget)
                {
                    StopMoving();
                }
                else if (distanceToTarget >= _fireRange && !_movingTowardsTarget)
                {
                    // todo start chasing target again..
                }

                MaybeDropOutOfRangeTarget();

                if (_target.IsUnit && !_target.Enemy.VisionComponent.IsSpotted)
                {
                    Logger.LogTargeting(
                        "Dropping a target because it is no longer spotted.", gameObject);
                    _target = null;
                }
            }

            if (_target != null && _target.Exists)
            {
                bool targetInRange = !_movingTowardsTarget;
                bool shotFired     = false;

                foreach (Turret turret in Children)
                {
                    shotFired |= turret.MaybeShoot(distanceToTarget, isServer);
                }

                // If shooting at the ground, stop after the first shot:
                if (shotFired && _target.IsGround)
                {
                    _target         = null;
                    _explicitTarget = null;
                    foreach (Turret turret in Children)
                    {
                        turret.SetExplicitTarget(_explicitTarget);
                    }
                }
            }
            else
            {
                FindAndTargetClosestEnemy();
            }
        }
Ejemplo n.º 3
0
        private void FireWeapon(TargetTuple target, Vector3 displacement, bool isServer)
        {
            // sound
            _source.PlayOneShot(_shotSound, _shotVolume);
            // particle
            _shotEffect.Play();

            if (_muzzleFlashEffect != null)
            {
                _muzzleFlashEffect.Play();
            }

            if (isServer)
            {
                if (target.IsUnit)
                {
                    float roll = _random.NextFloat(0.0, 100.0);
                    // HIT
                    if (roll <= _data.Accuracy)
                    {
                        Debug.LogWarning("Cannon shell dispersion is not implemented yet");
                        target.Enemy.HandleHit(_data.Damage, displacement, null);
                    }
                }
                else
                {
                    // TODO: fire pos damage not implemented
                }
            }
        }
Ejemplo n.º 4
0
 public void ClearExplicitTarget()
 {
     _explicitTarget = null;
     _target         = null;
     foreach (Turret turret in Children)
     {
         turret.ClearExplicitTarget();
     }
 }
Ejemplo n.º 5
0
 /// <summary>
 /// Sets a max-priority target for this turret.
 /// </summary>
 public void SetExplicitTarget(TargetTuple target)
 {
     _explicitTarget = target;
     _target         = target; // TODO check if we can shoot it before setting this
     foreach (Turret turret in Children)
     {
         turret.SetExplicitTarget(target);
     }
 }
Ejemplo n.º 6
0
 /// <summary>
 /// Get the range at which this turret can shoot at a specific target.
 /// </summary>
 /// 
 /// TODO In the future we will need to also return -1
 /// for turrets that can't shoot the target at all.
 /// 
 /// <param name="target"></param>
 /// <returns></returns>
 public float MaxRange(TargetTuple target)
 { 
     float maxRange = _fireRange;
     foreach (Turret turret in Children)
     {
         float turretMax = turret.MaxRange(target);
         maxRange = maxRange > turretMax ? maxRange : turretMax;
     }
     return maxRange;
 }
Ejemplo n.º 7
0
        /// <summary>
        /// Calculate the max range this turret can shoot at with at least one weapon,
        /// IN UNITY UNITS.
        /// </summary>
        /// This is a method to avoid storing duplicate information, and
        /// because we may want to ignore disabled turrets, or turrets
        /// that can't shoot at a specific target etc.
        ///
        /// TODO: Code duplication can be reduced if we only implement this in
        /// the turret class and have a fake toplevel turret we call this method on,
        /// but a fake turret like that also adds complexity, hard to decide.
        private float MaxRange(TargetTuple target)
        {
            float maxRange = 0;

            foreach (Turret turret in Children)
            {
                float turretMax = turret.MaxRange(target);
                maxRange = maxRange > turretMax ? maxRange : turretMax;
            }
            return(maxRange);
        }
Ejemplo n.º 8
0
        /// <summary>
        ///     To pick which ammo type to use we need an estimation
        ///     of the expected damage. This can differ from the actual
        ///     damage dealt if we hit a different armor section,
        ///     if the explosion lands to the side etc..
        /// </summary>
        public float EstimateDamageAgainstTarget(
            TargetTuple target,
            Vector3 displacement,
            float distance)
        {
            float result = 0;

            float range = GetRangeAgainstTargetType(target.Type);

            if (range > distance)
            {
                switch (DamageType)
                {
                case DamageType.HE:
                    if (target.Type == TargetType.GROUND)
                    {
                        result = DamageValue;
                    }
                    else
                    {
                        // Distance = 0 because we assume the explosion is on the target
                        result = target.Enemy.EstimateDamage(
                            DamageType, DamageValue, displacement, 0);
                    }
                    break;

                case DamageType.HEAT:
                    result = target.Enemy.EstimateDamage(
                        DamageType, DamageValue, displacement, distance);
                    break;

                case DamageType.KE:
                    result = target.Enemy.EstimateDamage(
                        DamageType, DamageValue, displacement, distance);
                    break;

                case DamageType.SMALL_ARMS:
                    result = target.Enemy.EstimateDamage(
                        DamageType, DamageValue, displacement, distance);
                    break;

                case DamageType.HEAVY_ARMS:
                    result = target.Enemy.EstimateDamage(
                        DamageType, DamageValue, displacement, distance);
                    break;
                }
            }
            Logger.LogDamage(
                LogLevel.DUMP,
                $"Estimating {result} damage with dmg type {DamageType}," +
                $" firepower {DamageValue} at range {distance / Constants.MAP_SCALE}");

            return(result);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Get the range at which this turret can shoot at a specific target.
        /// </summary>
        ///
        /// TODO In the future we will need to also return -1
        /// for turrets that can't shoot the target at all.
        public float MaxRange(TargetTuple target)
        {
            float maxRange = _maxRanges[(int)target.Type];

            foreach (Turret turret in Children)
            {
                float turretMax = turret.MaxRange(target);
                maxRange = maxRange > turretMax ? maxRange : turretMax;
            }
            return(maxRange);
        }
Ejemplo n.º 10
0
        public void CancelOrders()
        {
            _movingTowardsTarget = false;
            _explicitTarget      = null;
            _target = null;

            foreach (Turret turret in Children)
            {
                turret.ClearExplicitTarget();
            }
        }
Ejemplo n.º 11
0
        public bool TryShoot(
            TargetTuple target,
            float deltaTime,
            Vector3 displacement,
            bool isServer)
        {
            _reloadTimeLeft -= deltaTime;
            if (_reloadTimeLeft > 0)
            {
                return(false);
            }

            _reloadTimeLeft = _data.SalvoReload;
            return(Shoot(target, isServer));
        }
Ejemplo n.º 12
0
        /// <summary>
        ///     Fire on the provided target if the weapon is not reloading etc.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="displacement">Vector from the firing unit to the target unit</param>
        /// <param name="distance">Distance from the firing unit to the target unit</param>
        /// <param name="isServer">Non-server code should only affect art.</param>
        /// <returns>True if a shot was fired, false otherwise.</returns>
        public bool TryShoot(
            TargetTuple target,
            Vector3 displacement,
            float distance,
            bool isServer)
        {
            if (_reloadTimeLeft > 0)
            {
                return(false);
            }

            // TODO implement salvo + shot reload
            _reloadTimeLeft = _salvoReload;
            FireWeapon(target, displacement, distance, isServer);
            return(true);
        }
Ejemplo n.º 13
0
        public bool TryShoot(
            TargetTuple target,
            float deltaTime,
            Vector3 displacement,
            bool isServer)
        {
            _reloadTimeLeft -= deltaTime;
            if (_reloadTimeLeft > 0)
            {
                return(false);
            }

            // TODO implement salvo + shot reload
            _reloadTimeLeft = _data.SalvoReload;
            FireWeapon(target, displacement, isServer);
            return(true);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Shoot at the current target if in range.
        /// </summary>
        /// <returns>True if a shot was produced.</returns>
        public bool MaybeShoot(TargetTuple target, float distanceToTarget, bool isServer)
        {
            bool shotFired = false;

            if (IsFacingTarget && TargetInRange(target, distanceToTarget))
            {
                Vector3 vectorToTarget = target.Position - _turret.transform.position;
                shotFired = _weapon.TryShoot(
                    target, vectorToTarget, distanceToTarget, isServer);
            }

            foreach (Turret turret in Children)
            {
                shotFired |= turret.MaybeShoot(target, distanceToTarget, isServer);
            }

            return(shotFired);
        }
Ejemplo n.º 15
0
        /// <summary>
        /// Set a max-priority target for all child turrets.
        /// </summary>
        private void SetTarget(TargetTuple target, bool autoApproach)
        {
            Logger.LogTargeting(
                LogLevel.DEBUG,
                gameObject,
                "Received target from the outside.");
            float distance = Vector3.Distance(Unit.transform.position, target.Position);

            _explicitTarget = target;
            _target         = target;

            if (distance > _fireRange && autoApproach)
            {
                _movingTowardsTarget = true;
                // TODO if the UnitDispatcher can detect that we're in range, we
                // would be able to drop the handle to it
                Unit.SetDestination(target.Position);
            }
        }
Ejemplo n.º 16
0
        private bool Shoot(TargetTuple target, bool isServer)
        {
            //  Vector3 start = new Vector3(ShotStarterPosition.position.x, ShotStarterPosition.position.y+0., ShotStarterPosition.position.z);

            GameObject shell     = Resources.Load <GameObject>("shell");
            GameObject shell_new = GameObject.Instantiate(
                shell,
                _shotStarterPosition.position,
                _shotStarterPosition.transform.rotation);

            shell_new.GetComponent <BulletBehavior>().SetUp(_shotStarterPosition, target.Position, 60);

            if (isServer)
            {
                // TODO apply damage;
            }

            return(true);
        }
Ejemplo n.º 17
0
        /// <summary>
        /// Get the range at which this turret can shoot at a specific target.
        /// </summary>
        public float MaxRange(
            TargetTuple target,
            bool includeDirectFireAmmo,
            bool includeIndirectFireAmmo)
        {
            float maxRange;

            if (includeDirectFireAmmo)
            {
                if (includeIndirectFireAmmo)
                {
                    maxRange = _maxRanges[(int)target.Type];
                }
                else
                {
                    maxRange = _maxRangesDirectFire[(int)target.Type];
                }
            }
            else
            {
                if (includeIndirectFireAmmo)
                {
                    maxRange = _maxRangesIndirectFire[(int)target.Type];
                }
                else
                {
                    maxRange = 0;
                }
            }


            foreach (Turret turret in Children)
            {
                float turretMax = turret.MaxRange(
                    target, includeDirectFireAmmo, includeIndirectFireAmmo);
                if (maxRange < turretMax)
                {
                    maxRange = turretMax;
                }
            }
            return(maxRange);
        }
Ejemplo n.º 18
0
        private Ammo PickBestAmmo(
            TargetTuple target,
            Vector3 displacement,
            float distance)
        {
            Ammo  result     = _ammo[0];
            float bestDamage = result.EstimateDamageAgainstTarget(
                target, displacement, distance);

            for (int i = 1; i < _ammo.Length; i++)
            {
                float damage = _ammo[i].EstimateDamageAgainstTarget(
                    target, displacement, distance);
                if (damage > bestDamage)
                {
                    result     = _ammo[i];
                    bestDamage = damage;
                }
            }

            return(result);
        }
Ejemplo n.º 19
0
        /// <summary>
        ///     Fire on the provided target if the weapon is not reloading etc.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="displacement">Vector from the firing unit to the target unit</param>
        /// <param name="distance">Distance from the firing unit to the target unit</param>
        /// <param name="isServer">Non-server code should only affect art.</param>
        /// <returns>True if a shot was fired, false otherwise.</returns>
        public bool TryShoot(
            TargetTuple target,
            Vector3 displacement,
            float distance,
            bool isServer)
        {
            if (_lastTarget != target)
            {
                _lastTarget          = target;
                _aimStartedTimestamp = Time.time;
                return(false);
            }

            if (PercentageAimed < 1)
            {
                return(false);
            }

            if (PercentageReloaded < 1)
            {
                return(false);
            }

            if (CanReloadSalvo)
            {
                _salvoRemaining = _salvoLength;
            }

            if (FireWeapon(target, displacement, distance, isServer))
            {
                _lastShotTimestamp = Time.time;

                _salvoRemaining--;
                return(true);
            }

            return(false);
        }
Ejemplo n.º 20
0
        private Ammo PickBestAmmo(
            TargetTuple target,
            Vector3 displacement,
            float distance)
        {
            Ammo  result     = null;
            float bestDamage = 0;
            bool  mustUseAp  = _apRange > distance && target.Type == TargetType.VEHICLE;

            for (int i = 0; i < Ammo.Length; i++)
            {
                if (Ammo[i].ShellCountRemaining == 0)
                {
                    continue;
                }

                float damage = Ammo[i].EstimateDamageAgainstTarget(
                    target, displacement, distance);

                // Tanks and autocannons dont shoot other vehicles with HE:
                if (mustUseAp)
                {
                    if (Ammo[i].DamageType != DamageType.KE && Ammo[i].DamageType != DamageType.HEAT)
                    {
                        damage = 0;
                    }
                }

                if (damage > bestDamage)
                {
                    result     = Ammo[i];
                    bestDamage = damage;
                }
            }

            return(result);
        }
Ejemplo n.º 21
0
 public void CancelOrders()
 {
     _movingTowardsTarget = false;
     _explicitTarget      = null;
     _target = null;
 }
Ejemplo n.º 22
0
        private bool FireWeapon(
            TargetTuple target,
            Vector3 displacement,
            float distance,
            bool isServer)
        {
            Ammo ammo = PickBestAmmo(target, displacement, distance);

            if (ammo == null)
            {
                return(false);
            }

            ammo.ShellCountRemaining--;

            // sound
            _audioSource.PlayOneShot(ammo.ShotSound, _shotVolume);

            if (ammo.MuzzleFlashEffect != null)
            {
                ammo.MuzzleFlashEffect.transform.LookAt(target.Position);
                ammo.MuzzleFlashEffect.Play();
            }

            GameObject shell = GameObject.Instantiate(
                _shellPrefab,
                _barrelTip.position,
                _barrelTip.transform.rotation);

            GameObject.Instantiate(ammo.ShellArtPrefab, shell.transform);

            float   roll             = _random.NextFloat(0.0, 100.0);
            bool    isHit            = roll <= ammo.Accuracy;
            Vector3 shellDestination = target.Position;

            if (!isHit)
            {
                int deviationMode = (int)roll % 4;

                float missFactor = _random.NextFloat(
                    Constants.MISS_FACTOR_MIN,
                    Constants.MISS_FACTOR_MAX);

                float weightX = _random.NextFloat(0, 1);

                switch (deviationMode)
                {
                case 0:
                    shellDestination.x += distance * missFactor * weightX;
                    shellDestination.y += distance * missFactor * (1 - weightX);
                    break;

                case 1:
                    shellDestination.x -= distance * missFactor * weightX;
                    shellDestination.y += distance * missFactor * (1 - weightX);
                    break;

                case 2:
                    shellDestination.x += distance * missFactor * weightX;
                    shellDestination.y -= distance * missFactor * (1 - weightX);
                    break;

                case 3:
                    shellDestination.x -= distance * missFactor * weightX;
                    shellDestination.y -= distance * missFactor * (1 - weightX);
                    break;
                }
            }

            ShellBehaviour shellBehaviour = shell.GetComponent <ShellBehaviour>();

            shellBehaviour.Initialize(shellDestination, ammo);

            if (isServer)
            {
                if (target.IsUnit)
                {
                    if (isHit && !ammo.IsAoe)
                    {
                        target.Enemy.HandleHit(
                            ammo.DamageType, ammo.DamageValue, displacement, distance);
                    }
                }
                else
                {
                    // HE damage is applied by the shellBehavior when it explodes
                }
            }

            return(true);
        }
Ejemplo n.º 23
0
 public float MaxRange(TargetTuple target) => MaxRange(target, true, true);
Ejemplo n.º 24
0
        private void Update()
        {
            foreach (Turret turret in Children)
            {
                turret.Rotate(_target);
            }

            float distanceToTarget = 99999;

            if (_target != null && _target.Exists)
            {
                // TODO move most of the Update logic to the respective turrets
                // (likely to a new member class of them called 'TargetingStrategy')
                distanceToTarget = Vector3.Distance(
                    Unit.transform.position, _target.Position);
                if (_fireRange > distanceToTarget && _movingTowardsTarget)
                {
                    StopChasingTarget();
                }
                else if (distanceToTarget >= _fireRange && !_movingTowardsTarget)
                {
                    StartChasingTarget();
                }

                if (_target.IsUnit && !_target.Enemy.VisionComponent.IsSpotted)
                {
                    Logger.LogTargeting(
                        LogLevel.DEBUG,
                        gameObject,
                        "Dropping a target because it is no longer spotted.");
                    if (_target == _explicitTarget)
                    {
                        _explicitTarget = null;
                    }
                    _target = null;
                }

                if (!HasTargetingOrder && _target != null)
                {
                    if (!_vision.IsInHardLineOfSightFast(_target.Position) ||
                        !_vision.IsInSoftLineOfSight(_target.Position, 0) ||
                        distanceToTarget > _fireRange)
                    {
                        _target = null;
                        Logger.LogTargeting(
                            LogLevel.DEBUG,
                            gameObject,
                            "Dropping a target because it is out of range or LoS.");
                    }
                }
            }

            if (_target != null && _target.Exists)
            {
                bool targetInRange = !_movingTowardsTarget;
                bool shotFired     = false;

                foreach (Turret turret in Children)
                {
                    shotFired |= turret.MaybeShoot(_target, distanceToTarget, isServer);
                }

                // If shooting at the ground, stop after the first shot:
                if (shotFired && _target.IsGround)
                {
                    _target         = null;
                    _explicitTarget = null;
                }
            }
            else
            {
                FindAndTargetClosestEnemy();
            }
        }
Ejemplo n.º 25
0
 private bool TargetInRange(TargetTuple target, float distanceToTarget)
 {
     return(_maxRanges[(int)target.Type] > distanceToTarget);
 }
Ejemplo n.º 26
0
        public void Rotate(TargetTuple target)
        {
            // Do not return from this method before we've called into each child's
            // update handler!
            foreach (Turret turret in Children)
            {
                turret.Rotate(target);
            }

            if (target == null || !target.Exists)
            {
                TurnTurretBackToDefaultPosition();
                return;
            }

            bool  aimed = false;
            float targetHorizontalAngle = 0f;
            float targetVerticalAngle   = 0f;

            if (target.Position != Vector3.zero)
            {
                aimed = true;

                Vector3 directionToTarget = target.Position - _turret.position;
                directionToTarget = new Vector3(directionToTarget.x, 0, directionToTarget.z);
                Quaternion rotationToTarget = Quaternion.LookRotation(
                    _turret.parent.transform.InverseTransformDirection(directionToTarget));

                // Add any necessary cannon elevation to the rotation
                rotationToTarget *= ShellBehaviour.CalculateBarrelAngle(
                    _shellVelocity, _turret.transform.position, target.Position, out _);

                targetHorizontalAngle = rotationToTarget.eulerAngles.y.unwrapDegree();

                // If this turret has no flexibility (ArcHorizontal = 0) and is fully
                // rotated by a parent turret, it can get stuck 0.0000000001 degrees
                // away from the target due to float rounding errors (parent rounds
                // one way and decides he's done, child rounds the other way).
                // So round away the last degree to avoid this case:
                targetHorizontalAngle = Util.RoundTowardZero(targetHorizontalAngle);
                if (Mathf.Abs(targetHorizontalAngle) > _arcHorizontal)
                {
                    targetHorizontalAngle = 0f;
                    aimed = false;
                }

                targetVerticalAngle = rotationToTarget.eulerAngles.x.unwrapDegree();
                targetVerticalAngle = (float)Math.Floor(targetVerticalAngle);
                if (targetVerticalAngle < -_arcUp || targetVerticalAngle > _arcDown)
                {
                    targetVerticalAngle = 0f;
                    aimed = false;
                }
            }

            float turn            = Time.deltaTime * _rotationRate;
            float horizontalAngle = _turret.localEulerAngles.y;
            float verticalAngle   = _turret.localEulerAngles.x;
            float deltaAngle;

            deltaAngle = (targetHorizontalAngle - horizontalAngle).unwrapDegree();
            if (Mathf.Abs(deltaAngle) > turn)
            {
                horizontalAngle += (deltaAngle > 0 ? 1 : -1) * turn;
                aimed            = false;
            }
            else
            {
                horizontalAngle = targetHorizontalAngle;
            }

            deltaAngle = (targetVerticalAngle - verticalAngle).unwrapDegree();
            if (Mathf.Abs(deltaAngle) > turn)
            {
                verticalAngle += (deltaAngle > 0 ? 1 : -1) * turn;
                aimed          = false;
            }
            else
            {
                verticalAngle = targetVerticalAngle;
            }

            _turret.localEulerAngles = new Vector3(verticalAngle, horizontalAngle, 0);

            IsFacingTarget = aimed;
        }
Ejemplo n.º 27
0
 public float MaxRangeDirectFire(TargetTuple target) => MaxRange(target, true, false);
Ejemplo n.º 28
0
 public float MaxRangeIndirectFire(TargetTuple target) => MaxRange(target, false, true);
Ejemplo n.º 29
0
 private float MaxRange(TargetTuple target)
 => MaxRange(target, true, true);