Esempio n. 1
0
        private void Start()
        {
            // Setup light and shadows
            myLight                   = GetComponent <Light>();
            myLight.enabled           = EnableLight;
            forceDisableSpellLighting = !DaggerfallUnity.Settings.EnableSpellLighting;
            if (forceDisableSpellLighting)
            {
                myLight.enabled = false;
            }
            if (!DaggerfallUnity.Settings.EnableSpellShadows)
            {
                myLight.shadows = LightShadows.None;
            }
            initialRange     = myLight.range;
            initialIntensity = myLight.intensity;

            // Setup collider
            myCollider        = GetComponent <SphereCollider>();
            myCollider.radius = ColliderRadius;

            // Setup rigidbody
            myRigidbody            = GetComponent <Rigidbody>();
            myRigidbody.useGravity = false;

            // Use payload when available
            if (payload != null)
            {
                // Set payload missile properties
                caster      = payload.CasterEntityBehaviour;
                targetType  = payload.Settings.TargetType;
                elementType = payload.Settings.ElementType;

                // Set spell billboard anims automatically from payload for mobile missiles
                if (targetType == TargetTypes.SingleTargetAtRange ||
                    targetType == TargetTypes.AreaAtRange)
                {
                    UseSpellBillboardAnims();
                }
            }

            // Setup senses
            if (caster != GameManager.Instance.PlayerEntityBehaviour)
            {
                enemySenses = caster.GetComponent <EnemySenses>();
            }

            // Setup arrow
            if (isArrow)
            {
                // Create and orient 3d arrow
                goModel = GameObjectHelper.CreateDaggerfallMeshGameObject(99800, transform);
                MeshCollider arrowCollider = GetComponent <MeshCollider>();
                arrowCollider.sharedMesh = goModel.GetComponent <MeshFilter>().sharedMesh;

                // Offset up so it comes from same place LOS check is done from
                Vector3 adjust;
                if (caster != GameManager.Instance.PlayerEntityBehaviour)
                {
                    CharacterController controller = caster.transform.GetComponent <CharacterController>();
                    adjust    = caster.transform.forward * 0.6f;
                    adjust.y += controller.height / 3;
                }
                else
                {
                    // Offset forward to avoid collision with player
                    adjust = GameManager.Instance.MainCamera.transform.forward * 0.6f;
                    // Adjust slightly downward to match bow animation
                    adjust.y -= 0.11f;
                    // Adjust to the right or left to match bow animation
                    if (!GameManager.Instance.WeaponManager.ScreenWeapon.FlipHorizontal)
                    {
                        adjust += GameManager.Instance.MainCamera.transform.right * 0.15f;
                    }
                    else
                    {
                        adjust -= GameManager.Instance.MainCamera.transform.right * 0.15f;
                    }
                }

                goModel.transform.localPosition = adjust;
                goModel.transform.rotation      = Quaternion.LookRotation(GetAimDirection());
            }

            // Ignore missile collision with caster (this is a different check to AOE targets)
            if (caster)
            {
                Physics.IgnoreCollision(caster.GetComponent <Collider>(), this.GetComponent <Collider>());
            }
        }
Esempio n. 2
0
        void GetTargets()
        {
            DaggerfallEntityBehaviour highestPriorityTarget       = null;
            DaggerfallEntityBehaviour secondHighestPriorityTarget = null;
            float   highestPriority         = -1;
            float   secondHighestPriority   = -1;
            bool    sawSelectedTarget       = false;
            Vector3 directionToTargetHolder = directionToTarget;
            float   distanceToTargetHolder  = distanceToTarget;

            DaggerfallEntityBehaviour[] entityBehaviours = FindObjectsOfType <DaggerfallEntityBehaviour>();
            for (int i = 0; i < entityBehaviours.Length; i++)
            {
                DaggerfallEntityBehaviour targetBehaviour = entityBehaviours[i];
                EnemyEntity targetEntity = null;
                if (targetBehaviour != player)
                {
                    targetEntity = targetBehaviour.Entity as EnemyEntity;
                }

                // Can't target self
                if (targetBehaviour == entityBehaviour)
                {
                    continue;
                }

                // Evaluate potential targets
                if (targetBehaviour.EntityType == EntityTypes.EnemyMonster || targetBehaviour.EntityType == EntityTypes.EnemyClass ||
                    targetBehaviour.EntityType == EntityTypes.Player)
                {
                    // NoTarget mode
                    if ((GameManager.Instance.PlayerEntity.NoTargetMode || !motor.IsHostile || enemyEntity.MobileEnemy.Team == MobileTeams.PlayerAlly) && targetBehaviour == player)
                    {
                        continue;
                    }

                    // Can't target ally
                    if (targetBehaviour == player && enemyEntity.Team == MobileTeams.PlayerAlly)
                    {
                        continue;
                    }
                    else if (DaggerfallUnity.Settings.EnemyInfighting)
                    {
                        if (targetEntity != null && targetEntity.Team == enemyEntity.Team)
                        {
                            continue;
                        }
                    }
                    else
                    {
                        if (targetBehaviour != player && enemyEntity.MobileEnemy.Team != MobileTeams.PlayerAlly)
                        {
                            continue;
                        }
                    }

                    // For now, quest AI only targets player
                    if (questBehaviour && targetBehaviour != player)
                    {
                        continue;
                    }

                    EnemySenses targetSenses = null;
                    if (targetBehaviour.EntityType == EntityTypes.EnemyMonster || targetBehaviour.EntityType == EntityTypes.EnemyClass)
                    {
                        targetSenses = targetBehaviour.GetComponent <EnemySenses>();
                    }

                    // For now, quest AI can't be targeted
                    if (targetSenses && targetSenses.QuestBehaviour && !targetSenses.QuestBehaviour.IsAttackableByAI)
                    {
                        continue;
                    }

                    Vector3 toTarget = targetBehaviour.transform.position - transform.position;
                    directionToTarget = toTarget.normalized;
                    distanceToTarget  = toTarget.magnitude;

                    bool see = CanSeeTarget(targetBehaviour);

                    // Is potential target neither visible nor in area around player? If so, reject as target.
                    if (targetSenses && !targetSenses.WouldBeSpawnedInClassic && !see)
                    {
                        continue;
                    }

                    float priority = 0;

                    // Add 5 priority if this potential target isn't already targeting someone
                    if (targetSenses && targetSenses.Target == null)
                    {
                        priority += 5;
                    }

                    if (see)
                    {
                        priority += 10;
                    }

                    // Add distance priority
                    float distancePriority = 30 - distanceToTarget;
                    if (distancePriority < 0)
                    {
                        distancePriority = 0;
                    }

                    priority += distancePriority;
                    if (priority > highestPriority)
                    {
                        secondHighestPriority       = highestPriority;
                        highestPriority             = priority;
                        secondHighestPriorityTarget = highestPriorityTarget;
                        highestPriorityTarget       = targetBehaviour;
                        sawSecondaryTarget          = sawSelectedTarget;
                        sawSelectedTarget           = see;
                        directionToTargetHolder     = directionToTarget;
                        distanceToTargetHolder      = distanceToTarget;
                    }
                    else if (priority > secondHighestPriority)
                    {
                        sawSecondaryTarget          = see;
                        secondHighestPriority       = priority;
                        secondHighestPriorityTarget = targetBehaviour;
                    }
                }
            }

            // Restore direction and distance values
            directionToTarget = directionToTargetHolder;
            distanceToTarget  = distanceToTargetHolder;

            targetInSight = sawSelectedTarget;
            target        = highestPriorityTarget;
            if (DaggerfallUnity.Settings.EnhancedCombatAI && secondHighestPriorityTarget)
            {
                secondaryTarget = secondHighestPriorityTarget;
                if (sawSecondaryTarget)
                {
                    secondaryTargetPos = secondaryTarget.transform.position;
                }
            }
        }
Esempio n. 3
0
        void FixedUpdate()
        {
            if (GameManager.Instance.DisableAI)
            {
                return;
            }

            targetPosPredictTimer += Time.deltaTime;
            if (targetPosPredictTimer >= predictionInterval)
            {
                targetPosPredictTimer = 0f;
                targetPosPredict      = true;
            }
            else
            {
                targetPosPredict = false;
            }

            // Update whether enemy would be spawned or not in classic.
            // Only check if within the maximum possible distance (Just under 1094 classic units)
            if (GameManager.ClassicUpdate)
            {
                if (distanceToPlayer < 1094 * MeshReader.GlobalScale)
                {
                    float upperXZ;
                    float upperY       = 0;
                    float lowerY       = 0;
                    bool  playerInside = GameManager.Instance.PlayerGPS.GetComponent <PlayerEnterExit>().IsPlayerInside;

                    if (!playerInside)
                    {
                        upperXZ = classicSpawnDespawnExterior;
                    }
                    else
                    {
                        if (!wouldBeSpawnedInClassic)
                        {
                            upperXZ = classicSpawnXZDist;
                            upperY  = classicSpawnYDistUpper;
                            lowerY  = classicSpawnYDistLower;
                        }
                        else
                        {
                            upperXZ = classicDespawnXZDist;
                            upperY  = classicDespawnYDist;
                        }
                    }

                    float YDiffToPlayer      = transform.position.y - player.transform.position.y;
                    float YDiffToPlayerAbs   = Mathf.Abs(YDiffToPlayer);
                    float distanceToPlayerXZ = Mathf.Sqrt(distanceToPlayer * distanceToPlayer - YDiffToPlayerAbs * YDiffToPlayerAbs);

                    wouldBeSpawnedInClassic = true;

                    if (distanceToPlayerXZ > upperXZ)
                    {
                        wouldBeSpawnedInClassic = false;
                    }

                    if (playerInside)
                    {
                        if (lowerY == 0)
                        {
                            if (YDiffToPlayerAbs > upperY)
                            {
                                wouldBeSpawnedInClassic = false;
                            }
                        }
                        else if (YDiffToPlayer < lowerY || YDiffToPlayer > upperY)
                        {
                            wouldBeSpawnedInClassic = false;
                        }
                    }
                }
                else
                {
                    wouldBeSpawnedInClassic = false;
                }
            }

            if (GameManager.ClassicUpdate)
            {
                classicTargetUpdateTimer += Time.deltaTime / systemTimerUpdatesDivisor;

                if (target != null && target.Entity.CurrentHealth <= 0)
                {
                    target = null;
                }

                // Non-hostile mode
                if (GameManager.Instance.PlayerEntity.NoTargetMode || !motor.IsHostile)
                {
                    if (target == player)
                    {
                        target = null;
                    }
                    if (secondaryTarget == player)
                    {
                        secondaryTarget = null;
                    }
                }

                // Reset these values if no target
                if (target == null)
                {
                    lastKnownTargetPos   = ResetPlayerPos;
                    predictedTargetPos   = ResetPlayerPos;
                    directionToTarget    = ResetPlayerPos;
                    lastDistanceToTarget = 0;
                    targetRateOfApproach = 0;
                    distanceToTarget     = 0;
                    targetSenses         = null;

                    // If we have a valid secondary target that we acquired when we got the primary, switch to it.
                    // There will only be a secondary target if using enhanced combat AI.
                    if (secondaryTarget != null && secondaryTarget.Entity.CurrentHealth > 0)
                    {
                        target = secondaryTarget;

                        // If the secondary target was actually seen, use the last place we saw it to begin pursuit.
                        if (sawSecondaryTarget)
                        {
                            lastKnownTargetPos = secondaryTargetPos;
                        }
                        awareOfTargetForLastPrediction = false;
                    }
                }

                // Compare change in target position to give AI some ability to read opponent's movements
                if (target != null && target == targetOnLastUpdate)
                {
                    if (DaggerfallUnity.Settings.EnhancedCombatAI)
                    {
                        targetRateOfApproach = (lastDistanceToTarget - distanceToTarget);
                    }
                }
                else
                {
                    lastDistanceToTarget = 0;
                    targetRateOfApproach = 0;
                }

                if (target != null)
                {
                    lastDistanceToTarget = distanceToTarget;
                    targetOnLastUpdate   = target;
                }
            }

            if (player != null)
            {
                // Get distance to player
                Vector3 toPlayer = player.transform.position - transform.position;
                distanceToPlayer = toPlayer.magnitude;

                // If out of classic spawn range, still check for direct LOS to player so that enemies who see player will
                // try to attack.
                if (!wouldBeSpawnedInClassic)
                {
                    distanceToTarget  = distanceToPlayer;
                    directionToTarget = toPlayer.normalized;
                    playerInSight     = CanSeeTarget(player);
                }

                if (classicTargetUpdateTimer > 5)
                {
                    classicTargetUpdateTimer = 0f;

                    // Is enemy in area around player or can see player?
                    if (wouldBeSpawnedInClassic || playerInSight)
                    {
                        GetTargets();

                        if (target != null && target != player)
                        {
                            targetSenses = target.GetComponent <EnemySenses>();
                        }
                        else
                        {
                            targetSenses = null;
                        }
                    }

                    // Make targeted character also target this character if it doesn't have a target yet.
                    if (target != null && targetSenses && targetSenses.Target == null)
                    {
                        targetSenses.Target = entityBehaviour;
                    }
                }

                if (target == null)
                {
                    targetInSight  = false;
                    detectedTarget = false;
                    return;
                }

                if (!wouldBeSpawnedInClassic && target == player)
                {
                    distanceToTarget  = distanceToPlayer;
                    directionToTarget = toPlayer.normalized;
                    targetInSight     = playerInSight;
                }
                else
                {
                    Vector3 toTarget = target.transform.position - transform.position;
                    distanceToTarget  = toTarget.magnitude;
                    directionToTarget = toTarget.normalized;
                    targetInSight     = CanSeeTarget(target);
                }

                // Classic stealth mechanics would be interfered with by hearing, so only enable
                // hearing if the enemy has detected the target. If target is visible we can omit hearing.
                if (detectedTarget && !targetInSight)
                {
                    targetInEarshot = CanHearTarget();
                }
                else
                {
                    targetInEarshot = false;
                }

                // Note: In classic an enemy can continue to track the player as long as their
                // giveUpTimer is > 0. Since the timer is reset to 200 on every detection this
                // would make chameleon and shade essentially useless, since the enemy is sure
                // to detect the player during one of the many AI updates. Here, the enemy has to
                // successfully see through the illusion spell each classic update to continue
                // to know where the player is.
                if (GameManager.ClassicUpdate)
                {
                    blockedByIllusionEffect = BlockedByIllusionEffect();
                    if (lastHadLOSTimer > 0)
                    {
                        lastHadLOSTimer--;
                    }
                }

                if (!blockedByIllusionEffect && (targetInSight || targetInEarshot))
                {
                    detectedTarget     = true;
                    lastKnownTargetPos = target.transform.position;
                    lastHadLOSTimer    = 200f;
                }
                else if (!blockedByIllusionEffect && StealthCheck())
                {
                    detectedTarget = true;

                    // Only get the target's location from the stealth check if we haven't had
                    // actual LOS for a while. This gives better pursuit behavior since enemies
                    // will go to the last spot they saw the player instead of walking into walls.
                    if (lastHadLOSTimer <= 0)
                    {
                        lastKnownTargetPos = target.transform.position;
                    }
                }
                else
                {
                    detectedTarget = false;
                }

                if (oldLastKnownTargetPos == ResetPlayerPos)
                {
                    oldLastKnownTargetPos = lastKnownTargetPos;
                }

                if (predictedTargetPos == ResetPlayerPos || !DaggerfallUnity.Settings.EnhancedCombatAI)
                {
                    predictedTargetPos = lastKnownTargetPos;
                }

                // Predict target's next position
                if (targetPosPredict && lastKnownTargetPos != ResetPlayerPos)
                {
                    // Be sure to only take difference of movement if we've seen the target for two consecutive prediction updates
                    if (!blockedByIllusionEffect && targetInSight)
                    {
                        if (awareOfTargetForLastPrediction)
                        {
                            lastPositionDiff = lastKnownTargetPos - oldLastKnownTargetPos;
                        }

                        // Store current last known target position for next prediction update
                        oldLastKnownTargetPos = lastKnownTargetPos;

                        awareOfTargetForLastPrediction = true;
                    }
                    else
                    {
                        awareOfTargetForLastPrediction = false;
                    }

                    if (DaggerfallUnity.Settings.EnhancedCombatAI)
                    {
                        float moveSpeed = (enemyEntity.Stats.LiveSpeed + PlayerSpeedChanger.dfWalkBase) * MeshReader.GlobalScale;
                        predictedTargetPos = PredictNextTargetPos(moveSpeed);
                    }
                }

                if (detectedTarget && !hasEncounteredPlayer && target == player)
                {
                    hasEncounteredPlayer = true;

                    // Check appropriate language skill to see if player can pacify enemy
                    if (!questBehaviour && entityBehaviour && motor &&
                        (entityBehaviour.EntityType == EntityTypes.EnemyMonster || entityBehaviour.EntityType == EntityTypes.EnemyClass))
                    {
                        DFCareer.Skills languageSkill = enemyEntity.GetLanguageSkill();
                        if (languageSkill != DFCareer.Skills.None)
                        {
                            PlayerEntity player = GameManager.Instance.PlayerEntity;
                            if (FormulaHelper.CalculateEnemyPacification(player, languageSkill))
                            {
                                motor.IsHostile = false;
                                DaggerfallUI.AddHUDText(HardStrings.languagePacified.Replace("%e", enemyEntity.Name).Replace("%s", languageSkill.ToString()), 5);
                                player.TallySkill(languageSkill, 3);    // BCHG: increased skill uses from 1 in classic on success to make raising language skills easier
                            }
                            else if (languageSkill != DFCareer.Skills.Etiquette && languageSkill != DFCareer.Skills.Streetwise)
                            {
                                player.TallySkill(languageSkill, 1);
                            }
                        }
                    }
                }
            }

            // If target is player and in sight then raise enemy alert on player
            // This can only be lowered again by killing an enemy or escaping for some amount of time
            // Any enemies actively targeting player will continue to raise alert state
            if (Target == GameManager.Instance.PlayerEntityBehaviour && TargetInSight)
            {
                GameManager.Instance.PlayerEntity.SetEnemyAlert(true);
            }
        }
Esempio n. 4
0
        void CompleteDeath()
        {
            if (!entityBehaviour)
            {
                return;
            }

            // If enemy associated with quest system, make sure quest system is done with it first
            QuestResourceBehaviour questResourceBehaviour = GetComponent <QuestResourceBehaviour>();

            if (questResourceBehaviour)
            {
                if (!questResourceBehaviour.IsFoeDead)
                {
                    return;
                }
            }

            // Disable enemy gameobject
            // Do not destroy as we must still save enemy state when dead
            gameObject.SetActive(false);

            // Show death message
            string deathMessage = TextManager.Instance.GetLocalizedText("thingJustDied");

            deathMessage = deathMessage.Replace("%s", TextManager.Instance.GetLocalizedEnemyName(mobile.Summary.Enemy.ID));
            DaggerfallUI.Instance.PopupMessage(deathMessage);

            // Generate lootable corpse marker
            DaggerfallLoot loot = GameObjectHelper.CreateLootableCorpseMarker(
                GameManager.Instance.PlayerObject,
                entityBehaviour.gameObject,
                enemyEntity,
                mobile.Summary.Enemy.CorpseTexture,
                DaggerfallUnity.NextUID);

            // This is still required so enemy equipment is not marked as equipped
            // This item collection is transferred to loot container below
            for (int i = (int)Items.EquipSlots.Head; i <= (int)Items.EquipSlots.Feet; i++)
            {
                Items.DaggerfallUnityItem item = enemyEntity.ItemEquipTable.GetItem((Items.EquipSlots)i);
                if (item != null)
                {
                    enemyEntity.ItemEquipTable.UnequipItem((Items.EquipSlots)i);
                }
            }

            entityBehaviour.CorpseLootContainer = loot;

            // Transfer any items owned by entity to loot container
            // Many quests will stash a reward in enemy inventory for player to find
            // This will be in addition to normal random loot table generation
            loot.Items.TransferAll(entityBehaviour.Entity.Items);

            // Play body collapse sound
            if (DaggerfallUI.Instance.DaggerfallAudioSource)
            {
                DaggerfallUI.Instance.DaggerfallAudioSource.PlayClipAtPoint(SoundClips.BodyFall, loot.transform.position, 1f);
            }

            // Lower enemy alert state on player now that enemy is dead
            // If this is final enemy targeting player then alert state will remain clear
            // Other enemies still targeting player will continue to raise alert state every update
            EnemySenses senses = entityBehaviour.GetComponent <EnemySenses>();

            if (senses && senses.Target == GameManager.Instance.PlayerEntityBehaviour)
            {
                GameManager.Instance.PlayerEntity.SetEnemyAlert(false);
            }

            // Raise static event
            if (OnEnemyDeath != null)
            {
                OnEnemyDeath(this, null);
            }
        }
 void Start()
 {
     senses = GetComponent<EnemySenses>();
     controller = GetComponent<CharacterController>();
     mobile = GetComponentInChildren<DaggerfallMobileUnit>();
     isHostile = (mobile.Summary.Enemy.Reactions == MobileReactions.Hostile);
 }
 void Start()
 {
     motor = GetComponent<EnemyMotor>();
     senses = GetComponent<EnemySenses>();
     sounds = GetComponent<EnemySounds>();
     mobile = GetComponentInChildren<DaggerfallMobileUnit>();
     entityBehaviour = GetComponent<DaggerfallEntityBehaviour>();
 }