Esempio n. 1
0
        private void FiringUpdate()
        {
            // If SpellTarget is out of range - switch to abort state
            if (Spell.RangeBehaviour == Spell.TargetRangeBehaviour.AbortWhenOutOfRange &&
                !TargetUtility.IsInRange(Source, CastTarget, _minRange, _maxRange))
            {
                Abort();
                return;
            }

            var canStopFiring = true;

            for (var i = _children.Count - 1; i >= 0; i--)
            {
                var subProcessor = _children[i];
                subProcessor.Update();

                // Remove inactive SubSpells
                if (!subProcessor.IsActive)
                {
                    _children.RemoveAt(i);
                }
                else if (subProcessor.SubSpell.SpellShouldWaitUntilEnd)
                {
                    canStopFiring = false;
                }
            }

            if (canStopFiring)
            {
                _state = SpellState.Finilizing;
                Event?.Invoke(this, SpellEvent.FinishedFire, null);
            }
        }
Esempio n. 2
0
        // Proxy targeting for special range handling
        private Vector3?GetTargetLocation()
        {
            // If target is invalid, we can't update proxy
            if (!_originalTarget.IsValid || !_originalTarget.HasPosition)
            {
                return(null);
            }

            // If source is invalid, we can't update proxy
            if (!Source.IsValid || !Source.HasPosition)
            {
                return(null);
            }

            var desired  = _originalTarget.Position;
            var source   = Source.Position;
            var dir      = new Vector3(desired.x - source.x, 0, desired.z - source.z);
            var distance = dir.magnitude;

            dir.Normalize();

            switch (Spell.RangeBehaviour)
            {
            case Spell.TargetRangeBehaviour.RetargetClampToRange:
                desired = source + dir * Mathf.Clamp(distance, _minRange, _maxRange);
                break;

            case Spell.TargetRangeBehaviour.RetargetSetMaxRange:
                desired = source + dir * _maxRange;
                break;
            }

            // Stick to floor
            return(TargetUtility.AboveGround(desired));
        }
Esempio n. 3
0
        public SpellHandler(Spell spell, Target source, Target castTarget, int stacks)
        {
            Assert.IsNotNull(spell, "spell != null");
            Assert.IsTrue(source.IsValid, "source.IsValid");
            Assert.IsTrue(castTarget.IsValid, "castTarget.IsValid");
            Assert.IsTrue(castTarget.Type == spell.TargetType, "castTarget.Type == spell.TargetType");

            Spell           = spell;
            Source          = source;
            _originalTarget = castTarget;
            _minRange       = spell.MinRange.GetValue(Stacks);
            _maxRange       = spell.MaxRange.GetValue(Stacks);

#if DEBUG
            // Draw debug info
            if (spell.TargetType != TargetType.None)
            {
                Debugger.Default.DrawCircle(source.OffsettedPosition, Vector3.up, _minRange, Color.yellow, 1f);
                Debugger.Default.DrawCircle(source.OffsettedPosition, Vector3.up, _maxRange, Color.yellow, 1f);
            }
            TargetUtility.DebugDrawSourceAndTarget(source, castTarget);
#endif

            // Create target proxy if we need to retarget
            if (spell.RangeBehaviour == Spell.TargetRangeBehaviour.RetargetClampToRange ||
                spell.RangeBehaviour == Spell.TargetRangeBehaviour.RetargetSetMaxRange)
            {
                if (castTarget.Type == TargetType.Location || castTarget.Type == TargetType.LocationProvider)
                {
                    CastTarget = new Target(_locationTargetProxy);
                }
                else
                {
                    Debug.LogWarning($"Can't create proxy target for target of type {castTarget.Type}");
                }
            }
            else
            {
                CastTarget = castTarget;
            }

            Stacks = stacks;
            _state = SpellState.Started;
        }
Esempio n. 4
0
        public ISpellHandler Cast(Spell spell, CharacterState caster, Target target, int stacks)
        {
            if (!target.IsValid)
            {
                return(null);
            }

            if (caster == null || !caster.IsAlive)
            {
                return(null);
            }

            Assert.IsNotNull(spell);

            if (target.Type != spell.TargetType)
            {
                Debug.LogWarning($"Can't cast {spell.name}. Invalid target, required type: {spell.TargetType}, got {target.Type}", this);
                return(null);
            }

            var source   = new Target(caster);
            var minRange = spell.MinRange.GetValue(stacks);
            var maxRange = spell.MaxRange.GetValue(stacks);



            if (spell.CheckRangeOnCast && !TargetUtility.IsInRange(source, target, minRange, maxRange))
            {
                Debug.LogWarning($"Can't cast {spell.name}. Out of range", this);
                return(null);
            }

            if (target.Type == TargetType.Character &&
                !TargetUtility.IsValidTeam(caster, target.Character, spell.AffectsTeam))
            {
                Debug.LogWarning($"Can't cast {spell.name}. Invalid team", this);
                return(null);
            }

            var state = new SpellHandler(spell, source, target, stacks);

            _activeSpellStates.Add(state);
            return(state);
        }
Esempio n. 5
0
        public SubSpellHandler(
            SpellHandler spellHandler,
            SubSpell subSpell,
            Target source,
            Target target)
        {
            Assert.IsTrue(source.IsValid);
            Assert.IsTrue(target.IsValid);
            Assert.IsNotNull(subSpell);
            Assert.IsNotNull(spellHandler);

            SubSpell      = subSpell;
            _spellHandler = spellHandler;
            Source        = source;
            Target        = target;
            _state        = SubSpellState.Started;

            #if DEBUG
            TargetUtility.DebugDrawSourceAndTarget(source, target);
            #endif
        }
Esempio n. 6
0
        private void QueryTargets(List <Target> queried, Query query, Target defaultOrigin)
        {
            queried.Clear();
            var origin = ResolveTarget(query.Origin, defaultOrigin);

            if (!origin.IsValid)
            {
                return;
            }

            switch (query.NewTargetsQueryType)
            {
            case Query.QueryType.None:
                return;

            case Query.QueryType.OriginAsTarget:
                queried.Add(origin);
                return;

            case Query.QueryType.FillAoE:
                FillTargetsInAoe(queried, query.Area, origin);
                return;

            case Query.QueryType.RandomLocationInAoe:
                var randomLoc = RandomLocationInAoe(query.Area, origin);
                queried.Add(new Target(randomLoc, origin.Forward));
                return;

            default:
                break;
            }

            // Find all characters inside AoE and put them inside buffer
            CharactersInAoe(QueriedCharactersBuffer, query.Area, origin);

            // Filter characters
            for (var i = QueriedCharactersBuffer.Count - 1; i >= 0; i--)
            {
                var c = QueriedCharactersBuffer[i];

                // Team filtering
                if (!TargetUtility.IsValidTeam(SpellHandler.Source.Character, c, query.AffectsTeam))
                {
                    QueriedCharactersBuffer.RemoveAt(i);
                    continue;
                }

                // Filter affected
                if (query.ExcludeAlreadyAffected &&
                    _spellHandler.AffectedCharacters != null &&
                    _spellHandler.AffectedCharacters.Contains(c))
                {
                    QueriedCharactersBuffer.RemoveAt(i);
                }
            }

            // If we are empty after filtering
            if (QueriedCharactersBuffer.Count == 0)
            {
                return;
            }

            if (query.NewTargetsQueryType == Query.QueryType.RandomTargetInAoe)
            {
                queried.Add(new Target(RandomUtils.Choice(QueriedCharactersBuffer)));
                return;
            }

            if (query.NewTargetsQueryType == Query.QueryType.AllTargetsInAoe)
            {
                for (var i = 0; i < QueriedCharactersBuffer.Count; i++)
                {
                    queried.Add(new Target(QueriedCharactersBuffer[i]));
                }
                return;
            }

            if (query.NewTargetsQueryType == Query.QueryType.ClosestToOriginInAoe)
            {
                var minDistance = 1e8f;
                var minIndex    = 0;
                var originPos   = origin.Position;
                for (var i = 1; i < QueriedCharactersBuffer.Count; i++)
                {
                    var c        = QueriedCharactersBuffer[i];
                    var distance = Vector3.Distance(originPos, c.transform.position);
                    if (distance < minDistance)
                    {
                        minIndex    = i;
                        minDistance = distance;
                    }
                }

                queried.Add(new Target(QueriedCharactersBuffer[minIndex]));
                return;
            }
        }
Esempio n. 7
0
        private void FireEvent(SubSpellEvent eventType, Target defaultOrigin)
        {
            // Send non-targeted event to effect
            SubSpell.EffectHandler?.OnEvent(new SubSpellEventArgs(this, eventType));

            for (var eventIndex = 0; eventIndex < SubSpell.FireSubSpellEvents.Length; eventIndex++)
            {
                // Filter SubSpell events
                var e = SubSpell.FireSubSpellEvents[eventIndex];
                if (e.Type != eventType)
                {
                    continue;
                }

                QueryTargets(Queried, e.Query, defaultOrigin);

                // Targeted event
                SubSpell.EffectHandler?.OnEvent(new SubSpellEventArgs(this, eventType, e.Query, Queried));

                // New source of SubSpell
                var newSsSource = ResolveTarget(e.SubSpellSource, ResolveTarget(e.Query.Origin, defaultOrigin));

                foreach (var target in Queried)
                {
                    #if DEBUG
                    TargetUtility.DebugDraw(target, Color.yellow);
                    #endif
                    if (target.Type == TargetType.Character)
                    {
                        if (e.Query.ExcludeAlreadyAffected &&
                            _spellHandler.AffectedCharacters.Contains(target.Character))
                        {
                            continue;
                        }

                        if (e.TrackAffectedCharacters)
                        {
                            _spellHandler.AffectedCharacters.Add(target.Character);
                        }

                        if (e.ApplyBuffToTarget != null)
                        {
                            target.Character.ApplyBuff(
                                e.ApplyBuffToTarget,
                                SpellHandler.Source.Character,
                                Stacks,
                                this);
                        }
                    }

                    // SubSpell firing
                    if (e.FireSubSpell != null)
                    {
                        // Before adding SubSpell we first need to figure out what
                        // new sources and targets will be.
                        // Because some SubSpells overrides targeting
                        var newTarget = ResolveTarget(e.SubSpellTarget, target);

                        // Fire child sub spell
                        // Queue it to the spell processor
                        _spellHandler.CastSubSpell(e.FireSubSpell, newSsSource, newTarget);
                    }
                }
            }
        }
Esempio n. 8
0
        private void Update()
        {
            if (!IsActive)
            {
                return;
            }

            // Lifetime check
            if (_timer > 0)
            {
                _timer -= Time.deltaTime;
                if (_timer <= 0)
                {
                    HandleEvent(ProjectileEvents.TimeExpired, new Target(transform.position));
                }
            }

            // If target become invalid (i.e. character died) - retarget to its last location
            if (!_target.IsValid)
            {
                _target = new Target(_lastValidTargetPosition);
            }

            // Sample new target since the target can move (i.e. character)
            _lastValidTargetPosition = _target.Position;
            var currentPosition = transform.position;

            // If we have reached destination than no additional movement required
            if (TargetUtility.XZDistance(currentPosition, _lastValidTargetPosition) < DestinationThreshold)
            {
                return;
            }

            // Calculate direction and distance
            var xzDir = _lastValidTargetPosition - currentPosition;

            xzDir.y = 0;
            var xzDistance = xzDir.magnitude;

            // XZ direction only
            xzDir.Normalize();

            // Calculate desired position
            var nextPosition = currentPosition + _speed * Time.deltaTime * xzDir;

            _distanceTraveled += _speed * Time.deltaTime * xzDir.magnitude;

            // Update height by calculating relative progress using traveled and remaining distance
            var progress = Mathf.Clamp01(_distanceTraveled / (_distanceTraveled + xzDistance));

            // And sampling height from Height curve
            float baseY;

            if (_projectile.HoverGround)
            {
                baseY = TargetUtility.AboveGround(nextPosition).y;
            }
            else
            {
                baseY = Mathf.Lerp(_spawnPoint.y, _lastValidTargetPosition.y, progress);
            }

            nextPosition.y = baseY + _projectile.HeightProfile.Evaluate(progress);

            // If projectile have reached the destination
            var targetDir = nextPosition - _lastValidTargetPosition;

            // Update position and rotation
            transform.position = nextPosition;
            transform.rotation = Quaternion.LookRotation(targetDir.normalized);

            if (TargetUtility.XZDistance(nextPosition, _lastValidTargetPosition) < DestinationThreshold)
            {
                var eventTarget = _handler.Target;

                // If eventTarget (destination) is not valid
                // Raise event with repositioned target
                if (!eventTarget.IsValid)
                {
                    eventTarget = _target;
                }

                HandleEvent(ProjectileEvents.ReachedDestination, eventTarget);
            }

            // Distance check
            if (_maxDistance > 0 && _distanceTraveled > _maxDistance)
            {
                HandleEvent(ProjectileEvents.ReachedMaxDistance, new Target(transform.position));
            }
        }
Esempio n. 9
0
        public void Initialize(SubSpellHandler handler, ProjectileData projectileData)
        {
            _projectile = projectileData;
            _handler    = handler;

            // Setup RigidBody to handle collisions
            var body = GetComponentInChildren <Rigidbody>();

            if (body == null)
            {
                body = gameObject.AddComponent <Rigidbody>();
            }
            body.isKinematic = false;
            body.useGravity  = false;

            // Bake source point
            if (handler.Source.Type == TargetType.Character)
            {
                _spawnPoint = handler.Source.Character.GetNodeTransform(_projectile.SpawnNode).position;
            }
            else
            {
                _spawnPoint = handler.Source.Transform.position;
            }

            if (_projectile.Type == ProjectileType.Targeted && handler.Target.IsValid)
            {
                TargetUtility.DebugDraw(handler.Target, Color.blue);

                // ReTargeting to specific transform of character
                if (handler.Target.Type == TargetType.Character)
                {
                    _target = new Target(handler.Target.Character.GetNodeTransform(_projectile.TargetNode));
                }
                else
                {
                    _target = handler.Target;
                }
            }

            // Get initial direction
            if (_projectile.Type == ProjectileType.Directional)
            {
                var direction = handler.Source.Forward;

                // If target is specified, use it
                if (handler.Target.HasPosition)
                {
                    direction   = handler.Target.Position - _spawnPoint;
                    direction.y = 0;
                    direction.Normalize();
                }

                NavMesh.Raycast(_spawnPoint, _spawnPoint + direction * 100f, out var hit, NavMesh.AllAreas);
                _target = new Target(hit.position);
            }

            // Resolve stacked properties
            _timer       = projectileData.TimeToLive.GetValue(handler.Stacks);
            _speed       = projectileData.Speed.GetValue(handler.Stacks);
            _maxDistance = projectileData.MaxDistance.GetValue(handler.Stacks);

            // Initial positioning
            transform.position = _spawnPoint;
            //transform.rotation = Quaternion.LookRotation(_direction);

            IsActive = true;
        }
Esempio n. 10
0
        private void OnTriggerEnter(Collider other)
        {
            if (!IsActive)
            {
                return;
            }

            Target eventTarget;

            switch (_projectile.CollisionTarget)
            {
            case ProjectileData.EventTarget.CollisionPosition:
                eventTarget = new Target(transform.position);
                break;

            case ProjectileData.EventTarget.OtherColliderTransform:
                eventTarget = new Target(other.transform);
                break;

            case ProjectileData.EventTarget.OtherColliderPosition:
                eventTarget = new Target(other.transform.position);
                break;

            case ProjectileData.EventTarget.OtherColliderCharacter:
                var otherChar = other.GetComponent <CharacterState>();
                if (otherChar != null)
                {
                    eventTarget = new Target(otherChar);
                }
                else
                {
                    eventTarget = new Target(other.transform);
                }
                break;

            case ProjectileData.EventTarget.SelfPosition:
                eventTarget = new Target(transform.position);
                break;

            default:
            case ProjectileData.EventTarget.SelfTransform:
                eventTarget = new Target(transform);
                break;
            }

            var character = other.GetComponent <CharacterState>();

            if (character == null)
            {
                // Collision with non-character object.
                HandleEvent(ProjectileEvents.CollisionWithWorld, eventTarget);
            }
            else if (character.Equals(_handler.Target.Character))
            {
                if (TargetUtility.IsValidTeam(_handler.SpellHandler.Source.Character, character, _projectile.Affects))
                {
                    // Collision with target character object
                    HandleEvent(ProjectileEvents.CollisionWithTarget, eventTarget);
                }
            }
            else
            {
                if (TargetUtility.IsValidTeam(_handler.SpellHandler.Source.Character, character, _projectile.Affects))
                {
                    // Collision with non-target character object
                    HandleEvent(ProjectileEvents.CollisionWithOtherTargets, eventTarget);
                }
            }
        }