public bool Run(params object[] args) { NWCreature self = Object.OBJECT_SELF; string creatureScript = self.GetLocalString("BEHAVIOUR"); if (string.IsNullOrWhiteSpace(creatureScript)) { creatureScript = self.GetLocalString("BEHAVIOR"); } if (string.IsNullOrWhiteSpace(creatureScript)) { creatureScript = self.GetLocalString("SCRIPT"); } if (string.IsNullOrWhiteSpace(creatureScript)) { return(false); } if (!App.IsKeyRegistered <IBehaviour>("AI." + creatureScript)) { return(false); } App.ResolveByInterface <IBehaviour>("AI." + creatureScript, behaviour => { behaviour.OnPhysicalAttacked(); }); return(true); }
private static void HandleQueueWeaponSkill(NWCreature activator, Data.Entity.Perk entity, IPerkHandler ability, Feat spellFeatID) { var perkFeat = DataService.PerkFeat.GetByFeatID((int)spellFeatID); int? cooldownCategoryID = ability.CooldownCategoryID(activator, entity.CooldownCategoryID, perkFeat.PerkLevelUnlocked); var cooldownCategory = DataService.CooldownCategory.GetByID(Convert.ToInt32(cooldownCategoryID)); string queueUUID = Guid.NewGuid().ToString(); activator.SetLocalInt("ACTIVE_WEAPON_SKILL", entity.ID); activator.SetLocalString("ACTIVE_WEAPON_SKILL_UUID", queueUUID); activator.SetLocalInt("ACTIVE_WEAPON_SKILL_FEAT_ID", (int)spellFeatID); activator.SendMessage("Weapon skill '" + entity.Name + "' queued for next attack."); SendAOEMessage(activator, activator.Name + " readies weapon skill '" + entity.Name + "'."); ApplyCooldown(activator, cooldownCategory, ability, perkFeat.PerkLevelUnlocked, 0.0f); // Player must attack within 30 seconds after queueing or else it wears off. _.DelayCommand(30f, () => { if (activator.GetLocalString("ACTIVE_WEAPON_SKILL_UUID") == queueUUID) { activator.DeleteLocalInt("ACTIVE_WEAPON_SKILL"); activator.DeleteLocalString("ACTIVE_WEAPON_SKILL_UUID"); activator.DeleteLocalInt("ACTIVE_WEAPON_SKILL_FEAT_ID"); activator.SendMessage("Your weapon skill '" + entity.Name + "' is no longer queued."); SendAOEMessage(activator, activator.Name + " no longer has weapon skill '" + entity.Name + "' readied."); } }); }
/// <summary> /// Retrieves the DateTime in which the specified cooldownCategoryID will be available for usel /// </summary> /// <param name="activator">The creature whose cooldown we're checking.</param> /// <param name="cooldownCategoryID">The cooldown category we're checking for.</param> /// <returns></returns> private static DateTime GetAbilityCooldownUnlocked(NWCreature activator, int cooldownCategoryID) { // Players: Retrieve info from cache/DB, if it doesn't exist create a new record and insert it. Return unlock date. if (activator.IsPlayer) { PCCooldown pcCooldown = DataService.PCCooldown.GetByPlayerAndCooldownCategoryIDOrDefault(activator.GlobalID, cooldownCategoryID); if (pcCooldown == null) { pcCooldown = new PCCooldown { CooldownCategoryID = Convert.ToInt32(cooldownCategoryID), DateUnlocked = DateTime.UtcNow.AddSeconds(-1), PlayerID = activator.GlobalID }; DataService.SubmitDataChange(pcCooldown, DatabaseActionType.Insert); } return(pcCooldown.DateUnlocked); } // Creatures: Retrieve info from local variable, convert to DateTime if possible. Return parsed unlock date. else { string unlockDate = activator.GetLocalString("ABILITY_COOLDOWN_ID_" + cooldownCategoryID); if (string.IsNullOrWhiteSpace(unlockDate)) { return(DateTime.UtcNow.AddSeconds(-1)); } else { return(DateTime.ParseExact(unlockDate, "yyyy-MM-dd hh:mm:ss", CultureInfo.InvariantCulture)); } } }
public bool Run(params object[] args) { NWCreature self = Object.OBJECT_SELF; _skill.OnCreatureDeath(self); _loot.OnCreatureDeath(self); _quest.OnCreatureDeath(self); _creatureCorpse.OnCreatureDeath(); if (_cache.CustomObjectData.ContainsKey(self.GlobalID)) { _cache.CustomObjectData.Remove(self.GlobalID); } string creatureScript = self.GetLocalString("BEHAVIOUR"); if (string.IsNullOrWhiteSpace(creatureScript)) { creatureScript = self.GetLocalString("BEHAVIOR"); } if (string.IsNullOrWhiteSpace(creatureScript)) { creatureScript = self.GetLocalString("SCRIPT"); } if (string.IsNullOrWhiteSpace(creatureScript)) { return(false); } if (!App.IsKeyRegistered <IBehaviour>("AI." + creatureScript)) { return(false); } App.ResolveByInterface <IBehaviour>("AI." + creatureScript, behaviour => { behaviour.OnDeath(); }); return(true); }
private static string GetBehaviourScript(NWCreature self) { string creatureScript = self.GetLocalString("BEHAVIOUR"); if (string.IsNullOrWhiteSpace(creatureScript)) { creatureScript = self.GetLocalString("BEHAVIOR"); } if (string.IsNullOrWhiteSpace(creatureScript)) { creatureScript = self.GetLocalString("SCRIPT"); } // Fall back to standard behaviour if a script can't be found. if (string.IsNullOrWhiteSpace(creatureScript) || !_aiBehaviours.ContainsKey(creatureScript)) { return("StandardBehaviour"); } return(creatureScript); }
public virtual void OnConversation(NWCreature self) { string convo = self.GetLocalString("CONVERSATION"); if (!string.IsNullOrWhiteSpace(convo)) { NWPlayer player = (GetLastSpeaker()); DialogService.StartConversation(player, self, convo); } else if (!string.IsNullOrWhiteSpace(NWNXObject.GetDialogResref(self))) { BeginConversation(NWNXObject.GetDialogResref(self)); } }
public static void AddTemporaryForceDefense(NWCreature target, ForceAbilityType forceAbility, int amount = 5, int length = 5) { if (amount <= 0) { amount = 1; } string variable = "TEMP_FORCE_DEFENSE_" + (int)forceAbility; int tempDefense = target.GetLocalInt(variable) + amount; string tempDateExpires = target.GetLocalString(variable); DateTime expirationDate = DateTime.UtcNow; if (!string.IsNullOrWhiteSpace(tempDateExpires)) { expirationDate = DateTime.Parse(tempDateExpires); } expirationDate = expirationDate.AddSeconds(length); target.SetLocalString(variable, expirationDate.ToString(CultureInfo.InvariantCulture)); target.SetLocalInt(variable, tempDefense); }
private void HandleEvadeOrDeflectBlasterFire() { DamageData data = _nwnxDamage.GetDamageEventData(); if (data.Total <= 0) { return; } NWCreature damager = data.Damager.Object; NWCreature target = Object.OBJECT_SELF; NWItem damagerWeapon = _.GetLastWeaponUsed(damager); NWItem targetWeapon = target.RightHand; int perkLevel; // Attacker isn't using a pistol or rifle. Return. if (damagerWeapon.CustomItemType != CustomItemType.BlasterPistol && damagerWeapon.CustomItemType != CustomItemType.BlasterRifle) { return; } int modifier; string action; // Check target's equipped weapon, armor and perk. if (target.Chest.CustomItemType == CustomItemType.LightArmor && (targetWeapon.CustomItemType == CustomItemType.MartialArtWeapon || !target.RightHand.IsValid && !target.LeftHand.IsValid)) { // Martial Arts (weapon or unarmed) uses the Evade Blaster Fire perk which is primarily DEX based. perkLevel = _perk.GetPCPerkLevel(target.Object, PerkType.EvadeBlasterFire); modifier = target.DexterityModifier; action = "evade"; } else if (target.Chest.CustomItemType == CustomItemType.ForceArmor && (targetWeapon.CustomItemType == CustomItemType.Lightsaber || targetWeapon.CustomItemType == CustomItemType.Saberstaff || targetWeapon.GetLocalInt("LIGHTSABER") == TRUE)) { // Lightsabers (lightsaber or saberstaff) uses the Deflect Blaster Fire perk which is primarily CHA based. perkLevel = _perk.GetPCPerkLevel(target.Object, PerkType.DeflectBlasterFire); modifier = target.CharismaModifier; action = "deflect"; } else { return; } // Don't have the perk. Return. if (perkLevel <= 0) { return; } // Check attacker's DEX against the primary stat of the perk. int delta = modifier - damager.DexterityModifier; if (delta <= 0) { return; } // Has the delay between block/evade attempts past? DateTime cooldown = DateTime.UtcNow; string lastAttemptVar = target.GetLocalString("EVADE_OR_DEFLECT_BLASTER_FIRE_COOLDOWN"); if (!string.IsNullOrWhiteSpace(lastAttemptVar)) { cooldown = DateTime.Parse(lastAttemptVar); } // Cooldown hasn't expired yet. Not ready to attempt a deflect. if (cooldown >= DateTime.UtcNow) { return; } // Ready to attempt a deflect. Adjust chance based on the delta of attacker DEX versus primary stat of defender. int chanceToDeflect = 5 * delta; if (chanceToDeflect > 80) { chanceToDeflect = 80; } int delay; // Seconds delay between deflect attempts. switch (perkLevel) { case 1: delay = 18; break; case 2: delay = 12; break; case 3: delay = 6; break; default: throw new Exception("HandleEvadeOrDeflectBlasterFire -> Perk Level " + perkLevel + " unsupported."); } cooldown = DateTime.UtcNow.AddSeconds(delay); target.SetLocalString("EVADE_OR_DEFLECT_BLASTER_FIRE_COOLDOWN", cooldown.ToString(CultureInfo.InvariantCulture)); int roll = _random.D100(1); if (roll <= chanceToDeflect) { target.SendMessage(_color.Gray("You " + action + " a blaster shot.")); data.AdjustAllByPercent(-1); _nwnxDamage.SetDamageEventData(data); } else { target.SendMessage(_color.Gray("You fail to " + action + " a blaster shot. (" + roll + " vs " + chanceToDeflect + ")")); } }
/// <summary> /// Sets event scripts base on local variables and deletes the local variables when done. /// </summary> /// <param name="creature">The creatures whose events we're enabling</param> private static void EnableCreatureEvents(NWCreature creature) { // NOTE: Don't disable the spawn-in event because it's necessary for AI to work. SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_HEARTBEAT")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_NOTICE, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_NOTICE")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_SPELLCASTAT")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_MELEE_ATTACKED")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_DAMAGED")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_DISTURBED")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_END_COMBATROUND")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_DIALOGUE")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_RESTED, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_RESTED")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DEATH, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_DEATH")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_USER_DEFINED_EVENT")); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_BLOCKED_BY_DOOR")); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_HEARTBEAT"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_NOTICE"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_SPELLCASTAT"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_MELEE_ATTACK"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_DAMAGED"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_DISTURBED"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_END_COMBATROUND"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_DIALOGUE"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_RESTED"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_DEATH"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_USER_DEFINED_EVENT"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_BLOCKED_BY_DOOR"); }
public bool Run(params object[] args) { NWCreature self = Object.OBJECT_SELF; string creatureScript = self.GetLocalString("BEHAVIOUR"); if (string.IsNullOrWhiteSpace(creatureScript)) { creatureScript = self.GetLocalString("BEHAVIOR"); } if (string.IsNullOrWhiteSpace(creatureScript)) { creatureScript = self.GetLocalString("SCRIPT"); } if (string.IsNullOrWhiteSpace(creatureScript)) { return(false); } if (!App.IsKeyRegistered <IBehaviour>("AI." + creatureScript)) { return(false); } App.ResolveByInterface <IBehaviour>("AI." + creatureScript, behaviour => { if (behaviour.IgnoreNWNEvents) { self.SetLocalInt("IGNORE_NWN_EVENTS", 1); } if (behaviour.IgnoreOnBlocked) { self.SetLocalInt("IGNORE_NWN_ON_BLOCKED_EVENT", 1); } if (behaviour.IgnoreOnCombatRoundEnd) { self.SetLocalInt("IGNORE_NWN_ON_COMBAT_ROUND_END_EVENT", 1); } if (behaviour.IgnoreOnConversation) { self.SetLocalInt("IGNORE_NWN_ON_CONVERSATION_EVENT", 1); } if (behaviour.IgnoreOnDamaged) { self.SetLocalInt("IGNORE_NWN_ON_DAMAGED_EVENT", 1); } if (behaviour.IgnoreOnDeath) { self.SetLocalInt("IGNORE_NWN_ON_DEATH_EVENT", 1); } if (behaviour.IgnoreOnDisturbed) { self.SetLocalInt("IGNORE_NWN_ON_DISTURBED_EVENT", 1); } if (behaviour.IgnoreOnHeartbeat) { self.SetLocalInt("IGNORE_NWN_ON_HEARTBEAT_EVENT", 1); } if (behaviour.IgnoreOnPerception) { self.SetLocalInt("IGNORE_NWN_ON_PERCEPTION_EVENT", 1); } if (behaviour.IgnoreOnPhysicalAttacked) { self.SetLocalInt("IGNORE_NWN_ON_PHYSICAL_ATTACKED_EVENT", 1); } if (behaviour.IgnoreOnRested) { self.SetLocalInt("IGNORE_NWN_ON_RESTED_EVENT", 1); } if (behaviour.IgnoreOnSpawn) { self.SetLocalInt("IGNORE_NWN_ON_SPAWN_EVENT", 1); } if (behaviour.IgnoreOnSpellCastAt) { self.SetLocalInt("IGNORE_NWN_ON_SPELL_CAST_AT_EVENT", 1); } if (behaviour.IgnoreOnUserDefined) { self.SetLocalInt("IGNORE_NWN_ON_USER_DEFINED_EVENT", 1); } if (behaviour.Behaviour != null) { var result = behaviour.Behaviour .End() .Build(); _behaviour.RegisterBehaviour(result, self); } behaviour.OnSpawn(); }); return(true); }
/// <summary> /// Sets event scripts base on local variables and deletes the local variables when done. /// </summary> /// <param name="creature">The creatures whose events we're enabling</param> private static void EnableCreatureEvents(NWCreature creature) { // NOTE: Don't disable the spawn-in event because it's necessary for AI to work. SetEventScript(creature, EventScript.Creature_OnHeartbeat, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_HEARTBEAT")); SetEventScript(creature, EventScript.Creature_OnNotice, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_NOTICE")); SetEventScript(creature, EventScript.Creature_OnSpellCastAt, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_SPELLCASTAT")); SetEventScript(creature, EventScript.Creature_OnMeleeAttacked, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_MELEE_ATTACKED")); SetEventScript(creature, EventScript.Creature_OnDamaged, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_DAMAGED")); SetEventScript(creature, EventScript.Creature_OnDisturbed, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_DISTURBED")); SetEventScript(creature, EventScript.Creature_OnEndCombatRound, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_END_COMBATROUND")); SetEventScript(creature, EventScript.Creature_OnDialogue, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_DIALOGUE")); SetEventScript(creature, EventScript.Creature_OnRested, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_RESTED")); SetEventScript(creature, EventScript.Creature_OnDeath, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_DEATH")); SetEventScript(creature, EventScript.Creature_OnUserDefined, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_USER_DEFINED_EVENT")); SetEventScript(creature, EventScript.Creature_OnBlockedByDoor, creature.GetLocalString("SPAWN_CREATURE_EVENT_ON_BLOCKED_BY_DOOR")); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_HEARTBEAT"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_NOTICE"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_SPELLCASTAT"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_MELEE_ATTACK"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_DAMAGED"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_DISTURBED"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_END_COMBATROUND"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_DIALOGUE"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_RESTED"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_DEATH"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_USER_DEFINED_EVENT"); creature.DeleteLocalString("SPAWN_CREATURE_EVENT_ON_BLOCKED_BY_DOOR"); }
private bool UseFeat(int featID, string featName, NWCreature caster, NWCreature target) { // Note - this code is loosely based on code from AbilityService. However, the perk interface // is written assuming players will always be using perks. To allow NPCs to use them requires some hackery. int perkLevel = (int)caster.ChallengeRating / 5; if (perkLevel < 1) { perkLevel = 1; } if (caster.Area.Resref != target.Area.Resref || _.LineOfSightObject(caster.Object, target.Object) == 0) { return(false); } // Give NPCs a bit longer range than most PCs. if (_.GetDistanceBetween(caster, target) > 20.0f) { return(false); } // Note - NPCs are assumed to have infinite FPs. if (_.GetIsDead(caster) == 1) { return(false); } // Cooldown of 1 round. string timeout = caster.GetLocalString("TIMEOUT_" + featName); DateTime unlockTime = DateTime.UtcNow; if (!string.IsNullOrWhiteSpace(timeout)) { unlockTime = DateTime.Parse(timeout); } DateTime now = DateTime.UtcNow; if (unlockTime > now) { return(false); } else { unlockTime = now.AddSeconds(6); caster.SetLocalString("TIMEOUT_" + featName, unlockTime.ToString()); } // Do the actual force attack. Code taken from perks. if (featID == (int)CustomFeatType.ForceLightning) { int length; int dotAmount; int basePotency; const float Tier1Modifier = 1.0f; const float Tier2Modifier = 1.6f; const float Tier3Modifier = 2.2f; const float Tier4Modifier = 0; switch (perkLevel) { case 1: basePotency = 15; length = 0; dotAmount = 0; break; case 2: basePotency = 20; length = 6; dotAmount = 4; break; case 3: basePotency = 25; length = 6; dotAmount = 6; break; case 4: basePotency = 40; length = 12; dotAmount = 6; break; case 5: basePotency = 50; length = 12; dotAmount = 6; break; case 6: basePotency = 60; length = 12; dotAmount = 6; break; case 7: basePotency = 70; length = 12; dotAmount = 6; break; case 8: basePotency = 80; length = 12; dotAmount = 8; break; case 9: basePotency = 90; length = 12; dotAmount = 8; break; default: basePotency = 100; length = 12; dotAmount = 10; break; } var calc = CombatService.CalculateForceDamage( caster, target.Object, ForceAbilityType.Electrical, basePotency, Tier1Modifier, Tier2Modifier, Tier3Modifier, Tier4Modifier); caster.AssignCommand(() => { _.SetFacingPoint(target.Location.Position); _.ActionPlayAnimation(ANIMATION_LOOPING_CONJURE1, 1.0f, 1.0f); }); caster.SetLocalInt("CASTING", 1); _.DelayCommand(1.0f, () => { caster.AssignCommand(() => { Effect damage = _.EffectDamage(calc.Damage, DAMAGE_TYPE_ELECTRICAL); _.ApplyEffectToObject(DURATION_TYPE_INSTANT, damage, target); }); if (length > 0.0f && dotAmount > 0) { CustomEffectService.ApplyCustomEffect(caster, target.Object, CustomEffectType.ForceShock, length, perkLevel, dotAmount.ToString()); } caster.AssignCommand(() => { _.ApplyEffectToObject(DURATION_TYPE_TEMPORARY, _.EffectVisualEffect(VFX_BEAM_LIGHTNING), target, 1.0f); caster.DeleteLocalInt("CASTING"); }); CombatService.AddTemporaryForceDefense(target.Object, ForceAbilityType.Electrical); }); } else if (featID == (int)CustomFeatType.DrainLife) { float recoveryPercent; int basePotency; const float Tier1Modifier = 1; const float Tier2Modifier = 2; const float Tier3Modifier = 0; const float Tier4Modifier = 0; switch (perkLevel) { case 1: basePotency = 10; recoveryPercent = 0.2f; break; case 2: basePotency = 15; recoveryPercent = 0.2f; break; case 3: basePotency = 20; recoveryPercent = 0.4f; break; case 4: basePotency = 25; recoveryPercent = 0.4f; break; default: basePotency = 30; recoveryPercent = 0.5f; break; } var calc = CombatService.CalculateForceDamage( caster, target.Object, ForceAbilityType.Dark, basePotency, Tier1Modifier, Tier2Modifier, Tier3Modifier, Tier4Modifier); caster.AssignCommand(() => { _.SetFacingPoint(target.Location.Position); _.ActionPlayAnimation(ANIMATION_LOOPING_CONJURE1, 1.0f, 1.0f); }); caster.SetLocalInt("CASTING", 1); _.DelayCommand(1.0f, () => { _.AssignCommand(caster, () => { int heal = (int)(calc.Damage * recoveryPercent); if (heal > target.CurrentHP) { heal = target.CurrentHP; } _.ApplyEffectToObject(DURATION_TYPE_INSTANT, _.EffectDamage(calc.Damage), target); _.ApplyEffectToObject(DURATION_TYPE_INSTANT, _.EffectHeal(heal), caster); _.ApplyEffectToObject(DURATION_TYPE_TEMPORARY, _.EffectVisualEffect(VFX_BEAM_MIND), target, 1.0f); caster.DeleteLocalInt("CASTING"); }); }); CombatService.AddTemporaryForceDefense(target.Object, ForceAbilityType.Dark); } return(true); }
private static int CalculateForceAccuracy( NWCreature caster, NWCreature target, ForceAbilityType abilityType) { EffectiveItemStats casterItemStats = caster.IsPlayer ? PlayerStatService.GetPlayerItemEffectiveStats(caster.Object) : null; float casterPrimary; float casterSecondary; float casterItemAccuracy = casterItemStats?.ForceAccuracy ?? 0; EffectiveItemStats targetItemStats = target.IsPlayer ? PlayerStatService.GetPlayerItemEffectiveStats(target.Object) : null; float targetPrimary; float targetSecondary; float targetItemDefense = targetItemStats?.ForceDefense ?? 0; switch (abilityType) { case ForceAbilityType.Electrical: casterPrimary = caster.Intelligence; casterSecondary = caster.Wisdom; targetPrimary = target.Intelligence; targetSecondary = target.Wisdom; targetItemDefense = targetItemDefense + targetItemStats?.ElectricalDefense ?? 0; break; case ForceAbilityType.Dark: casterPrimary = caster.Intelligence; casterSecondary = caster.Wisdom; targetPrimary = target.Wisdom; targetSecondary = target.Intelligence; targetItemDefense = targetItemDefense + targetItemStats?.DarkDefense ?? 0; break; case ForceAbilityType.Mind: casterPrimary = caster.Wisdom; casterSecondary = caster.Intelligence; targetPrimary = target.Wisdom; targetSecondary = target.Intelligence; targetItemDefense = targetItemDefense + targetItemStats?.MindDefense ?? 0; break; case ForceAbilityType.Light: casterPrimary = caster.Wisdom; casterSecondary = caster.Intelligence; targetPrimary = target.Intelligence; targetSecondary = target.Wisdom; targetItemDefense = targetItemDefense + targetItemStats?.ElectricalDefense ?? 0; break; default: throw new ArgumentOutOfRangeException(nameof(abilityType), abilityType, null); } // Calculate accuracy based on the caster's primary and secondary stats. Add modifiers for overall item accuracy. float baseAccuracy = caster.Charisma * 0.25f + casterPrimary * 0.75f + casterSecondary * 0.5f + casterItemAccuracy * 0.15f; // Calculate defense based on target's primary and secondary stats. Add modifiers for specific defense types. float baseDefense = target.Charisma * 0.25f + targetPrimary * 0.75f + targetSecondary * 0.5f + targetItemDefense * 0.15f; // Temp defense increases whenever a hostile force ability is used. This is primarily a deterrant towards spamming the same ability over and over. string expiration = target.GetLocalString("TEMP_FORCE_DEFENSE_" + (int)abilityType); if (DateTime.TryParse(expiration, out var unused)) { int tempDefense = target.GetLocalInt("TEMP_FORCE_DEFENSE_" + (int)abilityType); baseDefense += tempDefense; } float delta = baseAccuracy - baseDefense; float finalAccuracy = delta < 0 ? 75 + (float)Math.Floor(delta / 2.0f) : 75 + delta; // Accuracy cannot go above 95% or below 0% if (finalAccuracy > 95) { finalAccuracy = 95; } else if (finalAccuracy < 0) { finalAccuracy = 0; } return((int)finalAccuracy); }