public void RestoreSaveData(object dataIn)
        {
            if (!enemy)
            {
                return;
            }

            EnemyData_v1 data = (EnemyData_v1)dataIn;

            if (data.loadID != LoadID)
            {
                return;
            }

            DaggerfallEntityBehaviour entityBehaviour = enemy.GetComponent <DaggerfallEntityBehaviour>();
            EnemySenses senses = enemy.GetComponent <EnemySenses>();
            EnemyMotor  motor  = enemy.GetComponent <EnemyMotor>();
            EnemyEntity entity = entityBehaviour.Entity as EnemyEntity;

            // Restore enemy career or class if different
            if (entity == null || entity.EntityType != data.entityType || entity.CareerIndex != data.careerIndex)
            {
                SetupDemoEnemy setupEnemy = enemy.GetComponent <SetupDemoEnemy>();
                setupEnemy.ApplyEnemySettings(data.entityType, data.careerIndex, data.mobileGender, data.isHostile, alliedToPlayer: data.alliedToPlayer);
                setupEnemy.AlignToGround();

                if (entity == null)
                {
                    entity = entityBehaviour.Entity as EnemyEntity;
                }
            }

            // Quiesce entity during state restore
            entity.Quiesce = true;

            // Restore enemy data
            entityBehaviour.gameObject.name = data.gameObjectName;
            enemy.transform.rotation        = data.currentRotation;
            entity.QuestFoeSpellQueueIndex  = data.questFoeSpellQueueIndex;
            entity.QuestFoeItemQueueIndex   = data.questFoeItemQueueIndex;
            entity.WabbajackActive          = data.wabbajackActive;
            entity.Items.DeserializeItems(data.items);
            entity.ItemEquipTable.DeserializeEquipTable(data.equipTable, entity.Items);
            entity.MaxHealth = data.startingHealth;
            entity.SetHealth(data.currentHealth, true);
            entity.SetFatigue(data.currentFatigue, true);
            entity.SetMagicka(data.currentMagicka, true);
            motor.IsHostile             = data.isHostile;
            senses.HasEncounteredPlayer = data.hasEncounteredPlayer;

            // Restore enemy position and migrate to floating y support for exteriors
            // Interiors seem to be working fine at this stage with any additional support
            // Dungeons are not involved with floating y and don't need any changes
            WorldContext enemyContext = GetEnemyWorldContext(enemy);

            if (enemyContext == WorldContext.Exterior)
            {
                RestoreExteriorPositionHandler(enemy, data, enemyContext);
            }
            else
            {
                // Everything else
                enemy.transform.position = data.currentPosition;
            }

            // Disable dead enemies
            if (data.isDead)
            {
                entityBehaviour.gameObject.SetActive(false);
            }

            // Restore quest resource link
            enemy.QuestSpawn = data.questSpawn;
            if (enemy.QuestSpawn)
            {
                // Add QuestResourceBehaviour to GameObject
                QuestResourceBehaviour questResourceBehaviour = entityBehaviour.gameObject.AddComponent <QuestResourceBehaviour>();
                questResourceBehaviour.RestoreSaveData(data.questResource);

                // Destroy QuestResourceBehaviour if no actual quest properties are restored from save
                if (questResourceBehaviour.QuestUID == 0 || questResourceBehaviour.TargetSymbol == null)
                {
                    enemy.QuestSpawn = false;
                    Destroy(questResourceBehaviour);
                }
            }

            // Restore instanced effect bundles
            GetComponent <EntityEffectManager>().RestoreInstancedBundleSaveData(data.instancedEffectBundles);

            // Resume entity
            entity.Quiesce = false;
        }
예제 #2
0
        public override void Resume(EntityEffectManager.EffectSaveData_v1 effectData, EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
        {
            base.Resume(effectData, manager, caster);

            // Capture rest and travel events for disease progression on Resume
            if (IsIncumbent)
            {
                DaggerfallRestWindow.OnSleepTick       += ProgressDiseaseAfterSleepOrTravel;
                DaggerfallTravelPopUp.OnPostFastTravel += ProgressDiseaseAfterSleepOrTravel;
            }
        }
예제 #3
0
 public override void Resume(EntityEffectManager.EffectSaveData_v1 effectData, EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
 {
     base.Resume(effectData, manager, caster);
     isIncumbent = effectData.isIncumbent;
 }
예제 #4
0
 void Awake()
 {
     mobile                       = GetComponentInChildren <DaggerfallMobileUnit>();
     entityBehaviour              = GetComponent <DaggerfallEntityBehaviour>();
     entityBehaviour.OnSetEntity += EntityBehaviour_OnSetEntity;
 }
 public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
 {
     base.Start(manager, caster);
     StartConcealment();
 }
예제 #6
0
        public override PayloadCallbackResults?EnchantmentPayloadCallback(EnchantmentPayloadFlags context, EnchantmentParam?param = null, DaggerfallEntityBehaviour sourceEntity = null, DaggerfallEntityBehaviour targetEntity = null, DaggerfallUnityItem sourceItem = null, int sourceDamage = 0)
        {
            base.EnchantmentPayloadCallback(context, param, sourceEntity, targetEntity, sourceItem, sourceDamage);

            // Validate
            if ((context != EnchantmentPayloadFlags.Equipped &&
                 context != EnchantmentPayloadFlags.MagicRound &&
                 context != EnchantmentPayloadFlags.RerollEffect) ||
                param == null || sourceEntity == null || sourceItem == null)
            {
                return(null);
            }

            // Get caster effect manager
            EntityEffectManager casterManager = sourceEntity.GetComponent <EntityEffectManager>();

            if (!casterManager)
            {
                return(null);
            }

            if (context == EnchantmentPayloadFlags.Equipped)
            {
                // Cast when held enchantment invokes a spell bundle that is permanent until item is removed
                InstantiateSpellBundle(param.Value, sourceEntity, sourceItem, casterManager);
            }
            else if (context == EnchantmentPayloadFlags.MagicRound)
            {
                // Apply CastWhenHeld durability loss
                ApplyDurabilityLoss(sourceItem, sourceEntity);
            }
            else if (context == EnchantmentPayloadFlags.RerollEffect)
            {
                // Recast spell bundle - previous instance has already been removed by EntityEffectManager prior to callback
                InstantiateSpellBundle(param.Value, sourceEntity, sourceItem, casterManager, true);
            }

            return(null);
        }
예제 #7
0
 public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
 {
     base.Start(manager, caster);
     PlayerAggro();
 }
예제 #8
0
 public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
 {
     base.Start(manager, caster);
     CacheReferences();
 }
예제 #9
0
 public override void Resume(EntityEffectManager.EffectSaveData_v1 effectData, EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
 {
     base.Resume(effectData, manager, caster);
     CacheReferences();
 }
        protected override void IncrementDailyDiseaseEffects()
        {
            if (infectionCyclesPassed <= 1) // Simulated Absorption/Incubation period before effects start to kick in, in this case 2 cycles, or approx. 4 in-game minutes, or 4 magic rounds.
            {
                return;
            }

            DaggerfallEntityBehaviour host = GetPeeredEntityBehaviour(manager);

            if (infectionCyclesPassed == 2)
            {
                ChangeStatMod(DFCareer.Stats.Intelligence, UnityEngine.Random.Range(20, 30 + 1));
                ChangeStatMod(DFCareer.Stats.Endurance, -UnityEngine.Random.Range(2, 5 + 1));
                host.Entity.IncreaseMagicka(UnityEngine.Random.Range(20, 35 + 1));
                host.Entity.DecreaseHealth(UnityEngine.Random.Range(2, 5 + 1));
                DaggerfallUI.AddHUDText("This horrible burning sensation must be the feeling of pure magical energy!", 4f);
                return;
            }

            if (infectionCyclesPassed >= 3 && infectionCyclesPassed < 17)
            {
                ChangeStatMod(DFCareer.Stats.Endurance, -1);
                host.Entity.IncreaseMagicka(UnityEngine.Random.Range(15, 30 + 1));
                host.Entity.DecreaseHealth(UnityEngine.Random.Range(1, 3 + 1));
                DaggerfallUI.AddHUDText("You feel another surge of burning", 3f);
                return;
            }

            if (infectionCyclesPassed == 17)
            {
                ChangeStatMod(DFCareer.Stats.Intelligence, -10);
                ChangeStatMod(DFCareer.Stats.Endurance, -1);
                host.Entity.IncreaseMagicka(UnityEngine.Random.Range(15, 25 + 1));
                host.Entity.DecreaseHealth(UnityEngine.Random.Range(1, 3 + 1));
                DaggerfallUI.AddHUDText("You feel the burning becoming less intense, the stuff must be wearing off...", 4f);
                return;
            }

            if (infectionCyclesPassed >= 18 && infectionCyclesPassed < 25)
            {
                ChangeStatMod(DFCareer.Stats.Endurance, -1);
                host.Entity.IncreaseMagicka(UnityEngine.Random.Range(10, 20 + 1));
                host.Entity.DecreaseHealth(UnityEngine.Random.Range(1, 2 + 1));
                DaggerfallUI.AddHUDText("Another spike of burning...", 3f);
                return;
            }

            if (infectionCyclesPassed >= 25)
            {
                if (GetAttributeMod(DFCareer.Stats.Intelligence) > 0)
                {
                    SetStatMod(DFCareer.Stats.Intelligence, 0);
                }

                ChangeStatMod(DFCareer.Stats.Endurance, -1);
                host.Entity.IncreaseMagicka(UnityEngine.Random.Range(7, 15 + 1));
                host.Entity.DecreaseHealth(UnityEngine.Random.Range(1, 2 + 1));
                DaggerfallUI.AddHUDText("You no longer feel the burning, nor the magical energy, but you don't feel very healthy after that experience", 4f);
                return;
            }
        }
예제 #11
0
        public override PayloadCallbackResults?EnchantmentPayloadCallback(EnchantmentPayloadFlags context, EnchantmentParam?param = null, DaggerfallEntityBehaviour sourceEntity = null, DaggerfallEntityBehaviour targetEntity = null, DaggerfallUnityItem sourceItem = null, int sourceDamage = 0)
        {
            base.EnchantmentPayloadCallback(context, param, sourceEntity, targetEntity, sourceItem);

            // Validate
            if (context != EnchantmentPayloadFlags.Used || sourceEntity == null || param == null)
            {
                return(null);
            }

            // Get caster effect manager
            EntityEffectManager effectManager = sourceEntity.GetComponent <EntityEffectManager>();

            if (!effectManager)
            {
                return(null);
            }

            // Do not activate enchantment if broken
            // But still return durability loss so "item has broken" message displays
            // If AllowMagicRepairs enabled then item will not disappear
            if (sourceItem != null && sourceItem.currentCondition <= 0)
            {
                return new PayloadCallbackResults()
                       {
                           durabilityLoss = durabilityLossOnUse
                       }
            }
            ;

            // Cast when used enchantment prepares a new ready spell
            if (!string.IsNullOrEmpty(param.Value.CustomParam))
            {
                // TODO: Ready a custom spell bundle
            }
            else
            {
                // Ready a classic spell bundle
                SpellRecord.SpellRecordData spell;

                EffectBundleSettings bundleSettings;
                EntityEffectBundle   bundle;
                if (GameManager.Instance.EntityEffectBroker.GetClassicSpellRecord(param.Value.ClassicParam, out spell))
                {
                    if (GameManager.Instance.EntityEffectBroker.ClassicSpellRecordDataToEffectBundleSettings(spell, BundleTypes.Spell, out bundleSettings))
                    {
                        // Self-cast spells are all assigned directly to self, "click to cast" spells are loaded to ready spell
                        // TODO: Support multiple ready spells so all loaded spells are launched on click
                        bundle            = new EntityEffectBundle(bundleSettings, sourceEntity);
                        bundle.CastByItem = sourceItem;
                        if (bundle.Settings.TargetType == TargetTypes.CasterOnly)
                        {
                            effectManager.AssignBundle(bundle, AssignBundleFlags.BypassSavingThrows | AssignBundleFlags.BypassChance);
                        }
                        else
                        {
                            effectManager.SetReadySpell(bundle, true);
                        }
                    }
                }
            }

            return(new PayloadCallbackResults()
            {
                durabilityLoss = durabilityLossOnUse
            });
        }
예제 #12
0
 void Awake()
 {
     entityBehaviour = GetComponent <DaggerfallEntityBehaviour>();
 }
예제 #13
0
        private void WeaponDamage(FPSWeapon weapon, out bool hitEnemy)
        {
            hitEnemy = false;

            if (!mainCamera || !weapon)
            {
                return;
            }

            // Fire ray along player facing using weapon range
            // Origin point of ray is set back slightly to fix issue where strikes against enemy capsules touching player capsule do not connect
            RaycastHit hit;
            Ray        ray = new Ray(mainCamera.transform.position + -mainCamera.transform.forward * 0.1f, mainCamera.transform.forward);

            if (Physics.SphereCast(ray, SphereCastRadius, out hit, weapon.Reach - SphereCastRadius))
            {
                // Check if hit has an DaggerfallAction component
                DaggerfallAction action = hit.transform.gameObject.GetComponent <DaggerfallAction>();
                if (action)
                {
                    action.Receive(player, DaggerfallAction.TriggerTypes.Attack);
                }

                // Check if hit has an DaggerfallActionDoor component
                DaggerfallActionDoor actionDoor = hit.transform.gameObject.GetComponent <DaggerfallActionDoor>();
                if (actionDoor)
                {
                    actionDoor.AttemptBash();
                    return;
                }

                // Check if hit an entity and remove health
                DaggerfallEntityBehaviour entityBehaviour  = hit.transform.GetComponent <DaggerfallEntityBehaviour>();
                DaggerfallMobileUnit      entityMobileUnit = hit.transform.GetComponentInChildren <DaggerfallMobileUnit>();
                if (entityBehaviour)
                {
                    if (entityBehaviour.EntityType == EntityTypes.EnemyMonster || entityBehaviour.EntityType == EntityTypes.EnemyClass)
                    {
                        EnemyEntity enemyEntity = entityBehaviour.Entity as EnemyEntity;

                        // Calculate damage
                        int damage;
                        if (usingRightHand)
                        {
                            damage = FormulaHelper.CalculateAttackDamage(playerEntity, enemyEntity, (int)(EquipSlots.RightHand), entityMobileUnit.Summary.AnimStateRecord);
                        }
                        else
                        {
                            damage = FormulaHelper.CalculateAttackDamage(playerEntity, enemyEntity, (int)(EquipSlots.LeftHand), entityMobileUnit.Summary.AnimStateRecord);
                        }

                        EnemyMotor  enemyMotor  = hit.transform.GetComponent <EnemyMotor>();
                        EnemySounds enemySounds = hit.transform.GetComponent <EnemySounds>();

                        // Play arrow sound and add arrow to target's inventory
                        if (weapon.WeaponType == WeaponTypes.Bow)
                        {
                            enemySounds.PlayArrowSound();
                            DaggerfallUnityItem arrow = ItemBuilder.CreateItem(ItemGroups.Weapons, (int)Weapons.Arrow);
                            enemyEntity.Items.AddItem(arrow);
                        }

                        // Play hit sound and trigger blood splash at hit point
                        if (damage > 0)
                        {
                            if (usingRightHand)
                            {
                                enemySounds.PlayHitSound(currentRightHandWeapon);
                            }
                            else
                            {
                                enemySounds.PlayHitSound(currentLeftHandWeapon);
                            }

                            EnemyBlood blood = hit.transform.GetComponent <EnemyBlood>();
                            if (blood)
                            {
                                blood.ShowBloodSplash(enemyEntity.MobileEnemy.BloodIndex, hit.point);
                            }

                            // Knock back enemy based on damage and enemy weight
                            if (enemyMotor)
                            {
                                if (enemyMotor.KnockBackSpeed <= (5 / (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10)) &&
                                    entityBehaviour.EntityType == EntityTypes.EnemyClass ||
                                    enemyEntity.MobileEnemy.Weight > 0)
                                {
                                    float enemyWeight    = enemyEntity.GetWeightInClassicUnits();
                                    float tenTimesDamage = damage * 10;
                                    float twoTimesDamage = damage * 2;

                                    float knockBackAmount = ((tenTimesDamage - enemyWeight) * 256) / (enemyWeight + tenTimesDamage) * twoTimesDamage;
                                    float knockBackSpeed  = (tenTimesDamage / enemyWeight) * (twoTimesDamage - (knockBackAmount / 256));
                                    knockBackSpeed /= (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10);

                                    if (knockBackSpeed < (15 / (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10)))
                                    {
                                        knockBackSpeed = (15 / (PlayerSpeedChanger.classicToUnitySpeedUnitRatio / 10));
                                    }
                                    enemyMotor.KnockBackSpeed     = knockBackSpeed;
                                    enemyMotor.KnockBackDirection = mainCamera.transform.forward;
                                }
                            }
                        }
                        else
                        {
                            if ((weapon.WeaponType != WeaponTypes.Bow && !enemyEntity.MobileEnemy.ParrySounds) || weapon.WeaponType == WeaponTypes.Melee)
                            {
                                weapon.PlaySwingSound();
                            }
                            else if (enemyEntity.MobileEnemy.ParrySounds)
                            {
                                enemySounds.PlayParrySound();
                            }
                        }

                        // Remove health
                        enemyEntity.DecreaseHealth(damage);
                        hitEnemy = true;

                        // Make foe attack their aggressor
                        // Currently this is just player, but should be expanded later
                        // for a wider variety of behaviours
                        if (enemyMotor)
                        {
                            // Make enemies in an area aggressive if player attacked a non-hostile one.
                            if (!enemyMotor.IsHostile)
                            {
                                GameManager.Instance.MakeEnemiesHostile();
                            }
                            enemyMotor.MakeEnemyHostileToPlayer(gameObject);
                        }
                    }
                }

                // Check if hit a mobile NPC
                MobilePersonNPC mobileNpc = hit.transform.GetComponent <MobilePersonNPC>();
                if (mobileNpc)
                {
                    EnemyBlood blood = hit.transform.GetComponent <EnemyBlood>();
                    if (blood)
                    {
                        blood.ShowBloodSplash(0, hit.point);
                    }
                    mobileNpc.Motor.gameObject.SetActive(false);
                    playerEntity.TallyCrimeGuildRequirements(false, 5);
                    // TODO: LOS check from each townsperson. If seen, register crime and start spawning guards as below.
                    playerEntity.CrimeCommitted = PlayerEntity.Crimes.Murder;
                    playerEntity.SpawnCityGuards(true);
                }
            }
        }
예제 #14
0
 public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
 {
     base.Start(manager, caster);
     StartWaitingForDoor();
 }
예제 #15
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;
                }
            }
        }
예제 #16
0
        void DoCollision(Collision collision, Collider other)
        {
            // Missile collision should only happen once
            if (impactDetected)
            {
                return;
            }

            // Set my collider to trigger and rigidbody to kinematic immediately after impact
            // This helps prevent mobiles from walking over low missiles or the missile bouncing off in some other direction
            // Seems to eliminate the combined worst-case scenario where mobile will "ride" a missile bounce, throwing them high into the air
            // Now the worst that seems to happen is mobile will "bump" over low missiles occasionally
            // TODO: Review later and find a better way to eliminate issue other than this quick workaround
            if (myCollider)
            {
                myCollider.isTrigger = true;
            }
            if (myRigidbody)
            {
                myRigidbody.isKinematic = true;
            }

            // Play spell impact animation, this replaces spell missile animation
            if (elementType != ElementTypes.None && targetType != TargetTypes.ByTouch)
            {
                UseSpellBillboardAnims(1, true);
                myBillboard.FramesPerSecond = ImpactBillboardFramesPerSecond;
                impactDetected = true;
            }

            // Get entity based on collision type
            DaggerfallEntityBehaviour entityBehaviour = null;

            if (collision != null && other == null)
            {
                entityBehaviour = collision.gameObject.transform.GetComponent <DaggerfallEntityBehaviour>();
            }
            else if (collision == null && other != null)
            {
                entityBehaviour = other.gameObject.transform.GetComponent <DaggerfallEntityBehaviour>();
            }
            else
            {
                return;
            }

            // If entity was hit then add to target list
            if (entityBehaviour)
            {
                targetEntities.Add(entityBehaviour);
                //Debug.LogFormat("Missile hit target {0} by range", entityBehaviour.name);
            }

            if (isArrow)
            {
                if (other != null)
                {
                    AssignBowDamageToTarget(other);
                }

                // Destroy 3d arrow
                Destroy(goModel.gameObject);
                impactDetected = true;
            }

            // If missile is area at range
            if (targetType == TargetTypes.AreaAtRange)
            {
                DoAreaOfEffect(transform.position);
            }
        }
예제 #17
0
        void IncrementPoisonEffects()
        {
            // Count down to poison start
            if (currentState == PoisonStates.Waiting)
            {
                if (--minutesToStart > 0)
                {
                    return;
                }
                else
                {
                    currentState = PoisonStates.Active;
                }
            }

            // Tick poison effect
            DaggerfallEntityBehaviour host = GetPeeredEntityBehaviour(manager);

            switch (PoisonType)
            {
            case Poisons.Nux_Vomica:
                host.Entity.DecreaseHealth(Random.Range(2, 12));
                break;

            case Poisons.Arsenic:
                host.Entity.DecreaseHealth(2);
                ChangeStatMod(DFCareer.Stats.Endurance, -1);
                break;

            case Poisons.Moonseed:
                host.Entity.DecreaseHealth(Random.Range(1, 10));
                break;

            case Poisons.Drothweed:
                ChangeStatMod(DFCareer.Stats.Strength, -Random.Range(5, 10));
                ChangeStatMod(DFCareer.Stats.Agility, -Random.Range(1, 5));
                ChangeStatMod(DFCareer.Stats.Speed, -Random.Range(1, 5));
                break;

            case Poisons.Somnalius:
                host.Entity.DecreaseFatigue(Random.Range(10, 100), true);
                break;

            case Poisons.Pyrrhic_Acid:
                host.Entity.DecreaseHealth(Random.Range(1, 30));
                break;

            case Poisons.Magebane:
                ChangeStatMod(DFCareer.Stats.Willpower, -Random.Range(1, 5));
                host.Entity.DecreaseMagicka(Random.Range(5, 15));
                break;

            case Poisons.Thyrwort:
                ChangeStatMod(DFCareer.Stats.Willpower, -Random.Range(5, 20));
                ChangeStatMod(DFCareer.Stats.Personality, -Random.Range(10, 20));
                break;

            case Poisons.Indulcet:
                host.Entity.DecreaseFatigue(Random.Range(10, 100), true);
                ChangeStatMod(DFCareer.Stats.Luck, Random.Range(4, 10));
                break;

            case Poisons.Sursum:
                ChangeStatMod(DFCareer.Stats.Intelligence, -Random.Range(10, 30));
                ChangeStatMod(DFCareer.Stats.Strength, Random.Range(5, 20));
                break;

            case Poisons.Quaesto_Vil:
                ChangeStatMod(DFCareer.Stats.Willpower, -Random.Range(1, 4));
                host.Entity.IncreaseFatigue(Random.Range(5, 10), true);
                break;

            case Poisons.Aegrotat:
                ChangeStatMod(DFCareer.Stats.Endurance, -Random.Range(1, 5));
                host.Entity.IncreaseMagicka(Random.Range(5, 10));
                break;
            }

            if (host.Entity == GameManager.Instance.PlayerEntity)
            {
                DaggerfallUI.AddHUDText(TextManager.Instance.GetLocalizedText("youFeelSomewhatBad"));
            }

            if (--minutesRemaining <= 0)
            {
                currentState = PoisonStates.Complete;
            }
        }
예제 #18
0
        public override PayloadCallbackResults?EnchantmentPayloadCallback(EnchantmentPayloadFlags context, EnchantmentParam?param = null, DaggerfallEntityBehaviour sourceEntity = null, DaggerfallEntityBehaviour targetEntity = null, DaggerfallUnityItem sourceItem = null, int sourceDamage = 0)
        {
            base.EnchantmentPayloadCallback(context, param, sourceEntity, targetEntity, sourceItem);

            // Requires param
            if (param == null)
            {
                return(null);
            }

            // Check target is an enemy type
            EnemyEntity enemyEntity = null;

            if (targetEntity != null && (targetEntity.EntityType == EntityTypes.EnemyMonster || targetEntity.EntityType == EntityTypes.EnemyClass))
            {
                enemyEntity = targetEntity.Entity as EnemyEntity;
            }
            else
            {
                return(null);
            }

            // Check enemy matches param type
            Params type = (Params)param.Value.ClassicParam;

            if (type == Params.Undead && enemyEntity.MobileEnemy.Affinity == MobileAffinity.Undead ||
                type == Params.Daedra && enemyEntity.MobileEnemy.Affinity == MobileAffinity.Daedra ||
                type == Params.Humanoid && enemyEntity.MobileEnemy.Affinity == MobileAffinity.Human ||
                type == Params.Animals && enemyEntity.MobileEnemy.Affinity == MobileAffinity.Animal)
            {
                // Modulating damage to a higher value
                // Currently unknown what values classic uses to increase damage
                return(new PayloadCallbackResults()
                {
                    strikesModulateDamage = increaseDamageAmount
                });
            }

            return(null);
        }
예제 #19
0
        public override PayloadCallbackResults?EnchantmentPayloadCallback(EnchantmentPayloadFlags context, EnchantmentParam?param = null, DaggerfallEntityBehaviour sourceEntity = null, DaggerfallEntityBehaviour targetEntity = null, DaggerfallUnityItem sourceItem = null, int sourceDamage = 0)
        {
            base.EnchantmentPayloadCallback(context, param, sourceEntity, targetEntity, sourceItem, sourceDamage);

            // Validate
            if (sourceItem == null)
            {
                return(null);
            }

            // Used payload
            if (context == EnchantmentPayloadFlags.Used)
            {
                if (sourceItem.TrappedSoulType != MobileTypes.None)
                {
                    sourceItem.TrappedSoulType = MobileTypes.None;
                    DaggerfallUI.MessageBox(soulReleasedID);
                }
                else
                {
                    DaggerfallUI.MessageBox(noSoulToReleaseID);
                }
            }

            return(null);
        }
예제 #20
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);

                Vector3 adjust;
                // Offset up so it comes from same place LOS check is done from
                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>());
            }
        }
        public override PayloadCallbackResults?EnchantmentPayloadCallback(EnchantmentPayloadFlags context, EnchantmentParam?param = null, DaggerfallEntityBehaviour sourceEntity = null, DaggerfallEntityBehaviour targetEntity = null, DaggerfallUnityItem sourceItem = null, int sourceDamage = 0)
        {
            base.EnchantmentPayloadCallback(context, param, sourceEntity, targetEntity, sourceItem, sourceDamage);

            // Must be Used payload
            if (context != EnchantmentPayloadFlags.Used)
            {
                return(null);
            }

            // Must have nearby non-allied enemies
            List <PlayerGPS.NearbyObject> nearby = GameManager.Instance.PlayerGPS.GetNearbyObjects(PlayerGPS.NearbyObjectFlags.Enemy, enemyRange)
                                                   .Where(x => ((EnemyEntity)x.gameObject.GetComponent <DaggerfallEntityBehaviour>().Entity).Team != MobileTeams.PlayerAlly).ToList();
            MobileTypes nearestType;

            if (nearby.Count == 0)
            {
                ShowSummonFailMessage();
                return(null);
            }
            else
            {
                // Use nearest enemy for cloning
                PlayerGPS.NearbyObject nearest = nearby[0];
                foreach (PlayerGPS.NearbyObject nearbyObject in nearby.Skip(1))
                {
                    if (nearbyObject.distance < nearest.distance)
                    {
                        nearest = nearbyObject;
                    }
                }
                EnemyEntity enemy = (EnemyEntity)nearest.gameObject.GetComponent <DaggerfallEntityBehaviour>().Entity;
                if (enemy.Team == MobileTeams.PlayerAlly)
                {
                    ShowSummonFailMessage();
                    return(null);
                }
                nearestType = (MobileTypes)enemy.MobileEnemy.ID;
            }

            // Spawn clone
            GameObjectHelper.CreateFoeSpawner(foeType: nearestType, spawnCount: 1, alliedToPlayer: true);

            // Durability loss for this effect
            return(new PayloadCallbackResults()
            {
                durabilityLoss = 100,
            });
        }
예제 #22
0
        private void Update()
        {
            // Exit if no caster
            if (!caster)
            {
                return;
            }

            // Execute based on target type
            if (!missileReleased)
            {
                switch (targetType)
                {
                case TargetTypes.ByTouch:
                    DoTouch();
                    break;

                case TargetTypes.SingleTargetAtRange:
                case TargetTypes.AreaAtRange:
                    DoMissile();
                    break;

                case TargetTypes.AreaAroundCaster:
                    DoAreaOfEffect(caster.transform.position, true);
                    break;

                default:
                    return;
                }
            }

            // Handle missile lifespan pre and post-impact
            if (!impactDetected)
            {
                // Transform missile along direction vector
                transform.position += (direction * MovementSpeed) * Time.deltaTime;

                // Update lifespan and self-destruct if expired (e.g. spell fired straight up and will never hit anything)
                lifespan += Time.deltaTime;
                if (lifespan > LifespanInSeconds)
                {
                    Destroy(gameObject);
                }
            }
            else
            {
                // Notify listeners work is done and automatically assign impact
                if (!impactAssigned)
                {
                    PlayImpactSound();
                    RaiseOnCompleteEvent();
                    AssignPayloadToTargets();
                    impactAssigned = true;

                    // Handle arrow
                    if (isArrow)
                    {
                        // Disable 3d arrow
                        goModel.gameObject.SetActive(false);

                        if (caster != GameManager.Instance.PlayerEntityBehaviour)
                        {
                            DaggerfallEntityBehaviour entityBehaviour = null;
                            if (arrowHit.transform)
                            {
                                entityBehaviour = arrowHit.transform.GetComponent <DaggerfallEntityBehaviour>();
                            }
                            if (entityBehaviour == caster.Target)
                            {
                                EnemyAttack attack = caster.GetComponent <EnemyAttack>();
                                if (attack)
                                {
                                    attack.BowDamage(goModel.transform.forward);
                                }
                            }
                        }
                        else
                        {
                            GameManager.Instance.WeaponManager.WeaponDamage(arrowHit, goModel.transform.forward, true);
                        }
                    }
                }

                // Track post impact lifespan and allow impact clip to finish playing
                postImpactLifespan += Time.deltaTime;
                if (postImpactLifespan > PostImpactLifespanInSeconds)
                {
                    myLight.enabled = false;
                    if (ImpactSound != SoundClips.None && !audioSource.IsPlaying())
                    {
                        Destroy(gameObject);
                    }
                }
            }

            // Update light
            UpdateLight();
        }
        public override PayloadCallbackResults?EnchantmentPayloadCallback(EnchantmentPayloadFlags context, EnchantmentParam?param = null, DaggerfallEntityBehaviour sourceEntity = null, DaggerfallEntityBehaviour targetEntity = null, DaggerfallUnityItem sourceItem = null, int sourceDamage = 0)
        {
            base.EnchantmentPayloadCallback(context, param, sourceEntity, targetEntity, sourceItem, sourceDamage);

            // Validate
            if (context != EnchantmentPayloadFlags.Strikes || targetEntity == null || sourceItem == null)
            {
                return(null);
            }

            // Entity must save vs magic
            if (FormulaHelper.SavingThrow(DFCareer.Elements.Magic, DFCareer.EffectFlags.Magic, targetEntity.Entity, 0) != 0)
            {
                // Kill target instantly - durability loss is equal to target health removed
                int healthRemoved = targetEntity.Entity.CurrentHealth;
                return(new PayloadCallbackResults()
                {
                    strikesModulateDamage = healthRemoved,
                    durabilityLoss = healthRemoved,
                });
            }

            return(null);
        }
예제 #24
0
 public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
 {
     base.Start(manager, caster);
     DaggerfallUI.Instance.DaggerfallHUD.HUDCompass.RegisterDetector(this);
 }
 public override void Resume(EntityEffectManager.EffectSaveData_v1 effectData, EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
 {
     base.Resume(effectData, manager, caster);
     StartConcealment();
 }
예제 #26
0
        public override PayloadCallbackResults?EnchantmentPayloadCallback(EnchantmentPayloadFlags context, EnchantmentParam?param = null, DaggerfallEntityBehaviour sourceEntity = null, DaggerfallEntityBehaviour targetEntity = null, DaggerfallUnityItem sourceItem = null, int sourceDamage = 0)
        {
            base.EnchantmentPayloadCallback(context, param, sourceEntity, targetEntity, sourceItem);

            // Validate
            if (param == null || sourceEntity == null || targetEntity == null)
            {
                return(null);
            }

            // Must be correct vampiric effect type
            Params type = (Params)param.Value.ClassicParam;

            if (type != Params.WhenStrikes)
            {
                return(null);
            }

            // Heal source entity by base damage caused to target
            // Was not able to fully confirm this how effect works, but seems close from observation alone.
            // TODO: This will likely need more research and refinement.
            sourceEntity.Entity.CurrentHealth += sourceDamage;
            //UnityEngine.Debug.LogFormat("Entity {0} drained {1} health by striking {2}", sourceEntity.Entity.Name, sourceDamage, targetEntity.Entity.Name);

            return(null);
        }
예제 #27
0
 public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
 {
     base.Start(manager, caster);
     AttachHost();
 }
예제 #28
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);
            }
        }
 public SkillTallyOverride(DaggerfallEntityBehaviour entityBehaviour)
     : base(entityBehaviour)
 {
 }
 public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null)
 {
     base.Start(manager, caster);
     StartWaterBreathing();
 }