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; }
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; } }
public override void Resume(EntityEffectManager.EffectSaveData_v1 effectData, EntityEffectManager manager, DaggerfallEntityBehaviour caster = null) { base.Resume(effectData, manager, caster); isIncumbent = effectData.isIncumbent; }
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(); }
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); }
public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null) { base.Start(manager, caster); PlayerAggro(); }
public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null) { base.Start(manager, caster); CacheReferences(); }
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; } }
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 }); }
void Awake() { entityBehaviour = GetComponent <DaggerfallEntityBehaviour>(); }
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); } } }
public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null) { base.Start(manager, caster); StartWaitingForDoor(); }
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; } } }
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); } }
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; } }
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); }
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); }
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, }); }
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); }
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(); }
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); }
public override void Start(EntityEffectManager manager, DaggerfallEntityBehaviour caster = null) { base.Start(manager, caster); AttachHost(); }
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(); }