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."); } }); }
public static void ApplyCooldown(NWCreature creature, CooldownCategory cooldown, IPerkHandler handler, int spellTier, float armorPenalty) { if (armorPenalty <= 0.0f) { armorPenalty = 1.0f; } // If player has a a cooldown recovery bonus on their equipment, apply that change now. if (creature.IsPlayer) { var effectiveStats = PlayerStatService.GetPlayerItemEffectiveStats(creature.Object); if (effectiveStats.CooldownRecovery > 0) { armorPenalty -= (effectiveStats.CooldownRecovery * 0.01f); } } // There's a cap of 50% cooldown reduction from equipment. if (armorPenalty < 0.5f) { armorPenalty = 0.5f; } Console.WriteLine("armorPenalty final = " + armorPenalty); float finalCooldown = handler.CooldownTime(creature, (float)cooldown.BaseCooldownTime, spellTier) * armorPenalty; int cooldownSeconds = (int)finalCooldown; int cooldownMillis = (int)((finalCooldown - cooldownSeconds) * 100); DateTime unlockDate = DateTime.UtcNow.AddSeconds(cooldownSeconds).AddMilliseconds(cooldownMillis); if (creature.IsPlayer) { PCCooldown pcCooldown = DataService.Single <PCCooldown>(x => x.PlayerID == creature.GlobalID && x.CooldownCategoryID == cooldown.ID); pcCooldown.DateUnlocked = unlockDate; DataService.SubmitDataChange(pcCooldown, DatabaseActionType.Update); } else { string unlockDateString = unlockDate.ToString("yyyy-MM-dd hh:mm:ss"); creature.SetLocalString("ABILITY_COOLDOWN_ID_" + (int)handler.PerkType, unlockDateString); } }
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 the current script names as local variables on the creature and then disables all events. /// </summary> /// <param name="creature">The creatures whose events we're disabling.</param> private static void DisableCreatureEvents(NWCreature creature) { creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_HEARTBEAT", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_NOTICE", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_NOTICE)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_SPELLCASTAT", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_MELEE_ATTACKED", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_DAMAGED", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DAMAGED)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_DISTURBED", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DISTURBED)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_END_COMBATROUND", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_DIALOGUE", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_RESTED", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_RESTED)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_DEATH", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DEATH)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_USER_DEFINED_EVENT", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_BLOCKED_BY_DOOR", GetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR)); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_HEARTBEAT, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_NOTICE, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_SPELLCASTAT, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_MELEE_ATTACKED, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DAMAGED, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DISTURBED, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_END_COMBATROUND, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DIALOGUE, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_RESTED, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_DEATH, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_USER_DEFINED_EVENT, string.Empty); SetEventScript(creature, EVENT_SCRIPT_CREATURE_ON_BLOCKED_BY_DOOR, string.Empty); }
/// <summary> /// Sets the current script names as local variables on the creature and then disables all events. /// </summary> /// <param name="creature">The creatures whose events we're disabling.</param> private static void DisableCreatureEvents(NWCreature creature) { creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_HEARTBEAT", GetEventScript(creature, EventScript.Creature_OnHeartbeat)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_NOTICE", GetEventScript(creature, EventScript.Creature_OnNotice)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_SPELLCASTAT", GetEventScript(creature, EventScript.Creature_OnSpellCastAt)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_MELEE_ATTACKED", GetEventScript(creature, EventScript.Creature_OnMeleeAttacked)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_DAMAGED", GetEventScript(creature, EventScript.Creature_OnDamaged)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_DISTURBED", GetEventScript(creature, EventScript.Creature_OnDisturbed)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_END_COMBATROUND", GetEventScript(creature, EventScript.Creature_OnEndCombatRound)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_DIALOGUE", GetEventScript(creature, EventScript.Creature_OnDialogue)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_RESTED", GetEventScript(creature, EventScript.Creature_OnRested)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_DEATH", GetEventScript(creature, EventScript.Creature_OnDeath)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_USER_DEFINED_EVENT", GetEventScript(creature, EventScript.Creature_OnUserDefined)); creature.SetLocalString("SPAWN_CREATURE_EVENT_ON_BLOCKED_BY_DOOR", GetEventScript(creature, EventScript.Creature_OnBlockedByDoor)); SetEventScript(creature, EventScript.Creature_OnHeartbeat, string.Empty); SetEventScript(creature, EventScript.Creature_OnNotice, string.Empty); SetEventScript(creature, EventScript.Creature_OnSpellCastAt, string.Empty); SetEventScript(creature, EventScript.Creature_OnMeleeAttacked, string.Empty); SetEventScript(creature, EventScript.Creature_OnDamaged, string.Empty); SetEventScript(creature, EventScript.Creature_OnDisturbed, string.Empty); SetEventScript(creature, EventScript.Creature_OnEndCombatRound, string.Empty); SetEventScript(creature, EventScript.Creature_OnDialogue, string.Empty); SetEventScript(creature, EventScript.Creature_OnRested, string.Empty); SetEventScript(creature, EventScript.Creature_OnDeath, string.Empty); SetEventScript(creature, EventScript.Creature_OnUserDefined, string.Empty); SetEventScript(creature, EventScript.Creature_OnBlockedByDoor, string.Empty); }
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); }