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> /// Sets the max FP for a creature to a specific amount. /// </summary> /// <param name="creature">The creature whose max FP we're setting.</param> /// <param name="amount">The amount of max FP to assign to the creature.</param> public static void SetMaxFP(NWCreature creature, int amount) { if (amount < 0) { amount = 0; } if (creature.IsPlayer) { var player = DataService.Player.GetByID(creature.GlobalID); player.MaxFP = amount; if (player.CurrentFP > player.MaxFP) { player.CurrentFP = player.MaxFP; } DataService.SubmitDataChange(player, DatabaseActionType.Update); } else { if (creature.GetLocalInt("CURRENT_FP") > amount) { creature.SetLocalInt("CURRENT_FP", amount); } creature.SetLocalInt("MAX_FP", amount); } }
/// <summary> /// Processes all feats which are linked to perks. /// </summary> private static void OnModuleUseFeat() { // Activator is the creature who used the feat. // Target is who the activator selected to use this feat on. NWCreature activator = _.OBJECT_SELF; NWCreature target = NWNXObject.StringToObject(NWNXEvents.GetEventData("TARGET_OBJECT_ID")); var featID = (Feat)Convert.ToInt32(NWNXEvents.GetEventData("FEAT_ID")); // Ensure this perk feat can be activated. if (!CanUsePerkFeat(activator, target, featID)) { return; } // Retrieve information necessary for activation of perk feat. var perkFeat = DataService.PerkFeat.GetByFeatID((int)featID); Data.Entity.Perk perk = DataService.Perk.GetByID(perkFeat.PerkID); int creaturePerkLevel = PerkService.GetCreaturePerkLevel(activator, perk.ID); var handler = PerkService.GetPerkHandler(perkFeat.PerkID); SendAOEMessage(activator, activator.Name + " readies " + perk.Name + "."); // Force Abilities (aka Spells) if (perk.ExecutionTypeID == PerkExecutionType.ForceAbility) { target.SetLocalInt(LAST_ATTACK + activator.GlobalID, ATTACK_FORCE); ActivateAbility(activator, target, perk, handler, creaturePerkLevel, PerkExecutionType.ForceAbility, perkFeat.PerkLevelUnlocked); } // Combat Abilities else if (perk.ExecutionTypeID == PerkExecutionType.CombatAbility) { target.SetLocalInt(LAST_ATTACK + activator.GlobalID, ATTACK_PHYSICAL); ActivateAbility(activator, target, perk, handler, creaturePerkLevel, PerkExecutionType.CombatAbility, perkFeat.PerkLevelUnlocked); } // Queued Weapon Skills else if (perk.ExecutionTypeID == PerkExecutionType.QueuedWeaponSkill) { target.SetLocalInt(LAST_ATTACK + activator.GlobalID, ATTACK_PHYSICAL); HandleQueueWeaponSkill(activator, perk, handler, featID); } // Stances else if (perk.ExecutionTypeID == PerkExecutionType.Stance) { target.SetLocalInt(LAST_ATTACK + activator.GlobalID, ATTACK_COMBATABILITY); ActivateAbility(activator, target, perk, handler, creaturePerkLevel, PerkExecutionType.Stance, perkFeat.PerkLevelUnlocked); } // Concentration Abilities else if (perk.ExecutionTypeID == PerkExecutionType.ConcentrationAbility) { target.SetLocalInt(LAST_ATTACK + activator.GlobalID, ATTACK_FORCE); ActivateAbility(activator, target, perk, handler, creaturePerkLevel, PerkExecutionType.ConcentrationAbility, perkFeat.PerkLevelUnlocked); } }
/// <summary> /// Sets the current FP amount of a creature to a specific value. /// This value must be between 0 and the creature's maximum FP. /// </summary> /// <param name="creature">The creature whose FP we're setting.</param> /// <param name="amount">The amount of FP to set it to.</param> public static void SetCurrentFP(NWCreature creature, int amount) { if (amount < 0) { amount = 0; } if (creature.IsPlayer) { var player = DataService.Get <Player>(creature.GlobalID); if (amount > player.MaxFP) { amount = player.MaxFP; } player.CurrentFP = amount; DataService.SubmitDataChange(player, DatabaseActionType.Update); } else { int maxFP = creature.GetLocalInt("MAX_FP"); if (amount > maxFP) { amount = maxFP; } creature.SetLocalInt("CURRENT_FP", amount); } }
public void ApplyEffects(NWCreature user, NWItem item, NWObject target, Location targetLocation, CustomData customData) { Location effectLocation; NWPlayer player = (user.Object); if (!string.IsNullOrWhiteSpace(target.GetLocalString("RESOURCE_RESREF"))) { ScanResource(user, target); DurabilityService.RunItemDecay(player, item, RandomService.RandomFloat(0.05f, 0.1f)); effectLocation = target.Location; } else { user.FloatingText("You cannot scan that object with this type of scanner."); return; } _.ApplyEffectAtLocation(DurationType.Instant, _.EffectVisualEffect(VisualEffect.Vfx_Fnf_Summon_Monster_3), effectLocation); if (user.IsPlayer && GetLocalBool(user, target.GlobalID.ToString()) == false) { int scanningBonus = item.ScanningBonus; SkillService.GiveSkillXP(player, SkillType.Harvesting, 150); user.SetLocalInt(target.GlobalID.ToString(), 1 + scanningBonus); } }
public void ApplyEffects(NWCreature user, NWItem item, NWObject target, Location targetLocation, CustomData customData) { NWArea area = user.Area; NWPlayer player = new NWPlayer(user); string structureID = area.GetLocalString("PC_BASE_STRUCTURE_ID"); Guid structureGuid = new Guid(structureID); PCBaseStructure pcbs = DataService.PCBaseStructure.GetByID(structureGuid); BaseStructure structure = DataService.BaseStructure.GetByID(pcbs.BaseStructureID); int repair = SkillService.GetPCSkillRank(player, SkillType.Piloting); int maxRepair = (int)structure.Durability - (int)pcbs.Durability; if (maxRepair < repair) { repair = maxRepair; } // TODO - add perks to make repairing faster/better/shinier/etc. // Maybe a perk to allow repairing in space, with ground repairs only otherwise? NWCreature ship = area.GetLocalObject("CREATURE"); if (ship.IsValid) { ship.SetLocalInt("HP", ship.GetLocalInt("HP") + repair); ship.FloatingText("Hull repaired: " + ship.GetLocalInt("HP") + "/" + ship.MaxHP); } pcbs.Durability += repair; DataService.SubmitDataChange(pcbs, DatabaseActionType.Update); player.SendMessage("Ship repaired for " + repair + " points. (Hull points: " + pcbs.Durability + "/" + structure.Durability + ")"); }
private static void CheckForSpellInterruption(NWCreature activator, string spellUUID, Vector position) { if (activator.GetLocalInt(spellUUID) == (int)SpellStatusType.Completed) { return; } Vector currentPosition = activator.Position; if (currentPosition.X != position.X || currentPosition.Y != position.Y || currentPosition.Z != position.Z) { var effect = activator.Effects.SingleOrDefault(x => _.GetEffectTag(x) == "ACTIVATION_VFX"); if (effect != null) { _.RemoveEffect(activator, effect); } NWNXPlayer.StopGuiTimingBar(activator, "", -1); activator.IsBusy = false; activator.SetLocalInt(spellUUID, (int)SpellStatusType.Interrupted); activator.SendMessage("Your ability has been interrupted."); return; } _.DelayCommand(0.5f, () => { CheckForSpellInterruption(activator, spellUUID, position); }); }
public void ApplyEffects(NWCreature user, NWItem item, NWObject target, Location targetLocation, CustomData customData) { Location effectLocation; NWPlayer player = (user.Object); // Targeted a location or self. Locate nearest resource. if (!target.IsValid || Equals(user, target)) { ScanArea(user, targetLocation); _durability.RunItemDecay(player, item, _random.RandomFloat(0.02f, 0.08f)); effectLocation = targetLocation; } else if (!string.IsNullOrWhiteSpace(target.GetLocalString("RESOURCE_RESREF"))) { ScanResource(user, target); _durability.RunItemDecay(player, item, _random.RandomFloat(0.05f, 0.1f)); effectLocation = target.Location; } else { user.FloatingText("You cannot scan that object with this type of scanner."); return; } _.ApplyEffectAtLocation(DURATION_TYPE_INSTANT, _.EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3), effectLocation); if (user.IsPlayer && user.GetLocalInt(target.GlobalID.ToString()) == FALSE) { int scanningBonus = item.ScanningBonus; _skill.GiveSkillXP(player, SkillType.Harvesting, 150); user.SetLocalInt(target.GlobalID.ToString(), 1 + scanningBonus); } }
private void ApplyEffect(NWCreature creature, NWObject target, int spellTier) { Effect effectMindShield; // Handle effects for differing spellTier values switch (spellTier) { case 1: effectMindShield = _.EffectImmunity(ImmunityType.Dazed); creature.AssignCommand(() => { _.ApplyEffectToObject(DurationType.Temporary, effectMindShield, target, 6.1f); }); break; case 2: effectMindShield = _.EffectImmunity(ImmunityType.Dazed); effectMindShield = _.EffectLinkEffects(effectMindShield, _.EffectImmunity(ImmunityType.Confused)); effectMindShield = _.EffectLinkEffects(effectMindShield, _.EffectImmunity(ImmunityType.Dominate)); // Force Pursuade is DOMINATION effect creature.AssignCommand(() => { _.ApplyEffectToObject(DurationType.Temporary, effectMindShield, target, 6.1f); }); break; case 3: effectMindShield = _.EffectImmunity(ImmunityType.Dazed); effectMindShield = _.EffectLinkEffects(effectMindShield, _.EffectImmunity(ImmunityType.Confused)); effectMindShield = _.EffectLinkEffects(effectMindShield, _.EffectImmunity(ImmunityType.Dominate)); // Force Pursuade is DOMINATION effect if (target.GetLocalInt("FORCE_DRAIN_IMMUNITY") == 1) { creature.SetLocalInt("FORCE_DRAIN_IMMUNITY", 0); } creature.DelayAssignCommand(() => { creature.DeleteLocalInt("FORCE_DRAIN_IMMUNITY"); }, 6.1f); creature.AssignCommand(() => { _.ApplyEffectToObject(DurationType.Temporary, effectMindShield, target, 6.1f); }); break; default: throw new ArgumentOutOfRangeException(nameof(spellTier)); } // Play VFX _.ApplyEffectToObject(DurationType.Instant, _.EffectVisualEffect(VisualEffect.Dur_Mind_Affecting_Positive), target); if (creature.IsPlayer) { SkillService.RegisterPCToAllCombatTargetsForSkill(creature.Object, SkillType.ForceControl, null); } }
public void OnImpact(NWCreature creature, NWObject target, int perkLevel, int spellTier) { float minimum = creature.Facing - 20; float maximum = creature.Facing + 20; if (target.Facing >= minimum && target.Facing <= maximum) { // Mark the player as committing a sneak attack. // This is later picked up in the OnApplyDamage event. creature.SetLocalInt("SNEAK_ATTACK_ACTIVE", 1); } else { creature.SetLocalInt("SNEAK_ATTACK_ACTIVE", 2); } }
private void ApplyEffect(NWCreature creature, NWObject target, int spellTier) { Effect effectMindShield; // Handle effects for differing spellTier values switch (spellTier) { case 1: effectMindShield = _.EffectImmunity(IMMUNITY_TYPE_DAZED); creature.AssignCommand(() => { _.ApplyEffectToObject(_.DURATION_TYPE_TEMPORARY, effectMindShield, target, 6.1f); }); break; case 2: effectMindShield = _.EffectImmunity(IMMUNITY_TYPE_DAZED); effectMindShield = _.EffectLinkEffects(effectMindShield, _.EffectImmunity(IMMUNITY_TYPE_CONFUSED)); effectMindShield = _.EffectLinkEffects(effectMindShield, _.EffectImmunity(IMMUNITY_TYPE_DOMINATE)); // Force Pursuade is DOMINATION effect creature.AssignCommand(() => { _.ApplyEffectToObject(_.DURATION_TYPE_TEMPORARY, effectMindShield, target, 6.1f); }); break; case 3: effectMindShield = _.EffectImmunity(IMMUNITY_TYPE_DAZED); effectMindShield = _.EffectLinkEffects(effectMindShield, _.EffectImmunity(IMMUNITY_TYPE_CONFUSED)); effectMindShield = _.EffectLinkEffects(effectMindShield, _.EffectImmunity(IMMUNITY_TYPE_DOMINATE)); // Force Pursuade is DOMINATION effect if (target.GetLocalInt("FORCE_DRAIN_IMMUNITY") == 1) { creature.SetLocalInt("FORCE_DRAIN_IMMUNITY", 0); } creature.DelayAssignCommand(() => { creature.DeleteLocalInt("FORCE_DRAIN_IMMUNITY"); }, 6.1f); creature.AssignCommand(() => { _.ApplyEffectToObject(_.DURATION_TYPE_TEMPORARY, effectMindShield, target, 6.1f); }); break; default: throw new ArgumentOutOfRangeException(nameof(spellTier)); } // Play VFX _.ApplyEffectToObject(_.DURATION_TYPE_INSTANT, _.EffectVisualEffect(_.VFX_DUR_MIND_AFFECTING_POSITIVE), target); }
public void OnImpact(NWCreature creature, NWObject target, int perkLevel, int spellTier) { // Mark the player as performing a recovery blast. // This is later picked up in the OnApplyDamage event to reduce all damage to 0. creature.SetLocalInt("RECOVERY_BLAST_ACTIVE", 1); var members = creature.PartyMembers.Where(x => _.GetDistanceBetween(x, target) <= 10.0f); int luck = PerkService.GetCreaturePerkLevel(creature, PerkType.Lucky); foreach (var member in members) { HealTarget(member, perkLevel, luck); } }
/// <summary> /// Looks at the creature's feats and if any of them are Perks, stores the highest /// level as a local variable on the creature. This variable is later used when the /// creature actually uses the feat. /// Also registers all of the available PerkFeats (highest tier) on the creature's Data. /// This data is also used in the AI to make decisions quicker. /// </summary> /// <param name="self">The creature whose perks we're registering.</param> private static void RegisterCreaturePerks(NWCreature self) { var perkFeatCache = new Dictionary <int, AIPerkDetails>(); var featIDs = new List <int>(); // Add all feats the creature has to the list. int featCount = NWNXCreature.GetFeatCount(self); for (int x = 0; x <= featCount - 1; x++) { var featID = NWNXCreature.GetFeatByIndex(self, x); featIDs.Add((int)featID); } bool hasPerkFeat = false; // Retrieve perk feat information for only those feats registered as a perk. var perkFeats = DataService.PerkFeat.GetAllByIDs(featIDs); // Mark the highest perk level on the creature. foreach (var perkFeat in perkFeats) { int level = self.GetLocalInt("PERK_LEVEL_" + perkFeat.PerkID); if (level >= perkFeat.PerkLevelUnlocked) { continue; } var perk = DataService.Perk.GetByID(perkFeat.PerkID); self.SetLocalInt("PERK_LEVEL_" + perkFeat.PerkID, perkFeat.PerkLevelUnlocked); perkFeatCache[perkFeat.PerkID] = new AIPerkDetails(perkFeat.FeatID, perk.ExecutionTypeID); hasPerkFeat = true; } // If a builder sets a perk feat but forgets to set the FP, do it automatically. if (hasPerkFeat && self.GetLocalInt("MAX_FP") <= 0) { int fp = 50; fp += (self.IntelligenceModifier + self.WisdomModifier + self.CharismaModifier) * 5; SetMaxFP(self, fp); SetCurrentFP(self, fp); } if (hasPerkFeat) { // Store a new dictionary containing PerkID and FeatID onto the creature's data. // This is later used in the AI processing for decision making. self.Data["PERK_FEATS"] = perkFeatCache; } }
public static void StartConcentrationEffect(NWCreature creature, int perkID, int spellTier) { if (creature.IsPlayer) { var player = DataService.Player.GetByID(creature.GlobalID); player.ActiveConcentrationPerkID = perkID; player.ActiveConcentrationTier = spellTier; DataService.SubmitDataChange(player, DatabaseActionType.Update); } else { creature.SetLocalInt("ACTIVE_CONCENTRATION_PERK_ID", perkID); } ConcentratingCreatures.Add(creature); }
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 static string ProcessEventAndBuildDetails(int eventID) { string details = string.Empty; NWObject target; int amount; switch (eventID) { case 1: // Spawn Creature string areaName = NWNXEvents.OnDMSpawnObject_GetArea().Name; NWCreature creature = NWNXEvents.OnDMSpawnObject_GetObject().Object; int objectTypeID = NWNXEvents.OnDMSpawnObject_GetObjectType(); float x = NWNXEvents.OnDMSpawnObject_GetPositionX(); float y = NWNXEvents.OnDMSpawnObject_GetPositionY(); float z = NWNXEvents.OnDMSpawnObject_GetPositionZ(); creature.SetLocalInt("DM_SPAWNED", _.TRUE); details = areaName + "," + creature.Name + "," + objectTypeID + "," + x + "," + y + "," + z; break; case 22: // Give XP amount = NWNXEvents.OnDMGiveXP_GetAmount(); target = NWNXEvents.OnDMGiveXP_GetTarget(); details = amount + "," + target.Name; break; case 23: // Give Level amount = NWNXEvents.OnDMGiveLevels_GetAmount(); target = NWNXEvents.OnDMGiveLevels_GetTarget(); details = amount + "," + target.Name; break; case 24: // Give Gold amount = NWNXEvents.OnDMGiveGold_GetAmount(); target = NWNXEvents.OnDMGiveGold_GetTarget(); details = amount + "," + target.Name; break; } return(details); }
public static void StartConcentrationEffect(NWCreature creature, int perkID, int spellTier) { if (creature.IsPlayer) { var player = DataService.Player.GetByID(creature.GlobalID); player.ActiveConcentrationPerkID = perkID; player.ActiveConcentrationTier = spellTier; DataService.SubmitDataChange(player, DatabaseActionType.Update); } else { creature.SetLocalInt("ACTIVE_CONCENTRATION_PERK_ID", perkID); } // If swapping from one concentration to another, remove any existing entries. if (ConcentratingCreatures.Contains(creature)) { ConcentratingCreatures.Remove(creature); } ConcentratingCreatures.Add(creature); }
private static void OnCreatureSpawn() { NWCreature self = OBJECT_SELF; // Don't modify AI behaviour for DM-spawned creatures. if (GetLocalBool(self, "DM_SPAWNED") == true) { return; } string script = GetBehaviourScript(OBJECT_SELF); if (string.IsNullOrWhiteSpace(script)) { return; } IAIBehaviour ai = GetAIBehaviour(script); if (ai.IgnoreNWNEvents) { self.SetLocalInt("IGNORE_NWN_EVENTS", 1); } if (ai.IgnoreOnBlocked) { self.SetLocalInt("IGNORE_NWN_ON_BLOCKED_EVENT", 1); } if (ai.IgnoreOnCombatRoundEnd) { self.SetLocalInt("IGNORE_NWN_ON_COMBAT_ROUND_END_EVENT", 1); } if (ai.IgnoreOnConversation) { self.SetLocalInt("IGNORE_NWN_ON_CONVERSATION_EVENT", 1); } if (ai.IgnoreOnDamaged) { self.SetLocalInt("IGNORE_NWN_ON_DAMAGED_EVENT", 1); } if (ai.IgnoreOnDeath) { self.SetLocalInt("IGNORE_NWN_ON_DEATH_EVENT", 1); } if (ai.IgnoreOnDisturbed) { self.SetLocalInt("IGNORE_NWN_ON_DISTURBED_EVENT", 1); } if (ai.IgnoreOnHeartbeat) { self.SetLocalInt("IGNORE_NWN_ON_HEARTBEAT_EVENT", 1); } if (ai.IgnoreOnPerception) { self.SetLocalInt("IGNORE_NWN_ON_PERCEPTION_EVENT", 1); } if (ai.IgnoreOnPhysicalAttacked) { self.SetLocalInt("IGNORE_NWN_ON_PHYSICAL_ATTACKED_EVENT", 1); } if (ai.IgnoreOnRested) { self.SetLocalInt("IGNORE_NWN_ON_RESTED_EVENT", 1); } if (ai.IgnoreOnSpawn) { self.SetLocalInt("IGNORE_NWN_ON_SPAWN_EVENT", 1); } if (ai.IgnoreOnSpellCastAt) { self.SetLocalInt("IGNORE_NWN_ON_SPELL_CAST_AT_EVENT", 1); } if (ai.IgnoreOnUserDefined) { self.SetLocalInt("IGNORE_NWN_ON_USER_DEFINED_EVENT", 1); } _areaAICreatures[self.Area].Add(self); ai.OnSpawn(self); }
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); }
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); }
public void OnImpact(NWPlayer player, NWObject target, int perkLevel) { int massLevel = _perk.GetPCPerkLevel(player, PerkType.MassTranquilizer); int tranqLevel = _perk.GetPCPerkLevel(player, PerkType.Tranquilizer); int luck = _perk.GetPCPerkLevel(player, PerkType.Lucky); float duration; float range = 5 * massLevel; switch (tranqLevel) { case 0: duration = 6; break; case 1: duration = 12; break; case 2: duration = 24; break; case 3: duration = 36; break; case 4: duration = 48; break; case 5: duration = 60; break; case 6: duration = 72; break; case 7: duration = 84; break; case 8: duration = 96; break; case 9: duration = 108; break; case 10: duration = 120; break; default: return; } if (_random.D100(1) <= luck) { duration *= 2; player.SendMessage("Lucky shot!"); } int current = 1; NWCreature nearest = _.GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, target, current); while (nearest.IsValid) { if (_.GetDistanceBetween(nearest, target) > range) { break; } if (RemoveExistingEffect(target, duration)) { continue; } nearest.SetLocalInt("TRANQUILIZER_EFFECT_FIRST_RUN", 1); Effect effect = _.EffectDazed(); effect = _.EffectLinkEffects(effect, _.EffectVisualEffect(VFX_DUR_IOUNSTONE_BLUE)); effect = _.TagEffect(effect, "TRANQUILIZER_EFFECT"); _.ApplyEffectToObject(DURATION_TYPE_TEMPORARY, effect, target, duration); current++; nearest = _.GetNearestCreature(CREATURE_TYPE_IS_ALIVE, TRUE, target, current); } }
public bool Run(params object[] args) { using (new Profiler(nameof(FinishAbilityUse))) { // These arguments are sent from the AbilityService's ActivateAbility method. NWCreature activator = (NWCreature)args[0]; string spellUUID = Convert.ToString(args[1]); int perkID = (int)args[2]; NWObject target = (NWObject)args[3]; int pcPerkLevel = (int)args[4]; int spellTier = (int)args[5]; float armorPenalty = (float)args[6]; // Get the relevant perk information from the database. Data.Entity.Perk dbPerk = DataService.Single <Data.Entity.Perk>(x => x.ID == perkID); // The execution type determines how the perk behaves and the rules surrounding it. PerkExecutionType executionType = dbPerk.ExecutionTypeID; // Get the class which handles this perk's behaviour. IPerkHandler perk = PerkService.GetPerkHandler(perkID); // Pull back cooldown information. int?cooldownID = perk.CooldownCategoryID(activator, dbPerk.CooldownCategoryID, spellTier); CooldownCategory cooldown = cooldownID == null ? null : DataService.SingleOrDefault <CooldownCategory>(x => x.ID == cooldownID); // If the activator interrupted the spell or died, we can bail out early. if (activator.GetLocalInt(spellUUID) == (int)SpellStatusType.Interrupted || // Moved during casting activator.CurrentHP < 0 || activator.IsDead) // Or is dead/dying { activator.DeleteLocalInt(spellUUID); return(false); } // Remove the temporary UUID which is tracking this spell cast. activator.DeleteLocalInt(spellUUID); // Force Abilities, Combat Abilities, Stances, and Concentration Abilities if (executionType == PerkExecutionType.ForceAbility || executionType == PerkExecutionType.CombatAbility || executionType == PerkExecutionType.Stance || executionType == PerkExecutionType.ConcentrationAbility) { // Run the impact script. perk.OnImpact(activator, target, pcPerkLevel, spellTier); // If an animation is specified for this perk, play it now. if (dbPerk.CastAnimationID != null && dbPerk.CastAnimationID > 0) { activator.AssignCommand(() => { _.ActionPlayAnimation((int)dbPerk.CastAnimationID, 1f, 1f); }); } // If the target is an NPC, assign enmity towards this creature for that NPC. if (target.IsNPC) { AbilityService.ApplyEnmity(activator, target.Object, dbPerk); } } // Adjust creature's current FP, if necessary. // Adjust FP only if spell cost > 0 PerkFeat perkFeat = DataService.Single <PerkFeat>(x => x.PerkID == perkID && x.PerkLevelUnlocked == spellTier); int fpCost = perk.FPCost(activator, perkFeat.BaseFPCost, spellTier); if (fpCost > 0) { int currentFP = AbilityService.GetCurrentFP(activator); int maxFP = AbilityService.GetMaxFP(activator); currentFP -= fpCost; AbilityService.SetCurrentFP(activator, currentFP); activator.SendMessage(ColorTokenService.Custom("FP: " + currentFP + " / " + maxFP, 32, 223, 219)); } // Notify activator of concentration ability change and also update it in the DB. if (executionType == PerkExecutionType.ConcentrationAbility) { AbilityService.StartConcentrationEffect(activator, perkID, spellTier); activator.SendMessage("Concentration ability activated: " + dbPerk.Name); // The Skill Increase effect icon and name has been overwritten. Apply the effect to the player now. // This doesn't do anything - it simply gives a visual cue that the player has an active concentration effect. _.ApplyEffectToObject(_.DURATION_TYPE_PERMANENT, _.EffectSkillIncrease(_.SKILL_USE_MAGIC_DEVICE, 1), activator); } // Handle applying cooldowns, if necessary. if (cooldown != null) { AbilityService.ApplyCooldown(activator, cooldown, perk, spellTier, armorPenalty); } // Mark the creature as no longer busy. activator.IsBusy = false; // Mark the spell cast as complete. activator.SetLocalInt(spellUUID, (int)SpellStatusType.Completed); return(true); } }
public void ApplyCustomEffect(NWCreature oCaster, NWCreature oTarget, int customEffectID, int ticks, int effectLevel) { // Can't apply the effect if the existing one is stronger. int existingEffectLevel = GetActiveEffectLevel(oTarget, customEffectID); if (existingEffectLevel > effectLevel) { oCaster.SendMessage("A more powerful effect already exists on your target."); return; } Data.Entities.CustomEffect effectEntity = _db.CustomEffects.Single(x => x.CustomEffectID == customEffectID); // PC custom effects are tracked in the database. if (oTarget.IsPlayer) { PCCustomEffect entity = _db.PCCustomEffects.SingleOrDefault(x => x.PlayerID == oTarget.GlobalID && x.CustomEffectID == customEffectID); if (entity == null) { entity = new PCCustomEffect { PlayerID = oTarget.GlobalID, CustomEffectID = customEffectID }; _db.PCCustomEffects.Add(entity); } entity.Ticks = ticks; _db.SaveChanges(); oTarget.SendMessage(effectEntity.StartMessage); } // NPCs custom effects are tracked in server memory. else { // Look for existing effect. foreach (var entry in _state.NPCEffects) { CasterSpellVO casterSpellModel = entry.Key; if (casterSpellModel.Caster.Equals(oCaster) && casterSpellModel.CustomEffectID == customEffectID && casterSpellModel.Target.Equals(oTarget)) { _state.NPCEffects[entry.Key] = ticks; return; } } // Didn't find an existing effect. Create a new one. CasterSpellVO spellModel = new CasterSpellVO { Caster = oCaster, CustomEffectID = customEffectID, EffectName = effectEntity.Name, Target = oTarget }; _state.NPCEffects[spellModel] = ticks; } ICustomEffect handler = App.ResolveByInterface <ICustomEffect>("CustomEffect." + effectEntity.ScriptHandler); handler?.Apply(oCaster, oTarget); oTarget.SetLocalInt("CUSTOM_EFFECT_ACTIVE_" + customEffectID, effectLevel); }
private static void ActivateAbility( NWCreature activator, NWObject target, Data.Entity.Perk entity, IPerkHandler perkHandler, int pcPerkLevel, PerkExecutionType executionType, int spellTier) { string uuid = Guid.NewGuid().ToString(); float baseActivationTime = perkHandler.CastingTime(activator, (float)entity.BaseCastingTime, spellTier); float activationTime = baseActivationTime; var vfxID = VisualEffect.Invalid; var animationID = Animation.Invalid; if (baseActivationTime > 0f && activationTime < 1.0f) { activationTime = 1.0f; } // Force ability armor penalties float armorPenalty = 0.0f; if (executionType == PerkExecutionType.ForceAbility || executionType == PerkExecutionType.ConcentrationAbility) { string penaltyMessage = string.Empty; foreach (var item in activator.EquippedItems) { if (item.CustomItemType == CustomItemType.HeavyArmor) { armorPenalty = 2; penaltyMessage = "Heavy armor slows your force cooldown by 100%."; break; } else if (item.CustomItemType == CustomItemType.LightArmor) { armorPenalty = 1.25f; penaltyMessage = "Light armor slows your force cooldown by 25%."; } } // If there's an armor penalty, send a message to the player. if (armorPenalty > 0.0f) { activator.SendMessage(penaltyMessage); } } // If player is in stealth mode, force them out of stealth mode. if (_.GetActionMode(activator.Object, ActionMode.Stealth)) { _.SetActionMode(activator.Object, ActionMode.Stealth, false); } // Make the player face their target. _.ClearAllActions(); BiowarePosition.TurnToFaceObject(target, activator); // Force and Concentration Abilities will display a visual effect during the casting process. if (executionType == PerkExecutionType.ForceAbility || executionType == PerkExecutionType.ConcentrationAbility) { vfxID = VisualEffect.Vfx_Dur_Iounstone_Yellow; animationID = Animation.LoopingConjure1; } if (executionType == PerkExecutionType.ConcentrationAbility) { activator.SetLocalObject("CONCENTRATION_TARGET", target); } // If a VFX ID has been specified, play that effect instead of the default one. if (vfxID != VisualEffect.Invalid) { var vfx = _.EffectVisualEffect(vfxID); vfx = _.TagEffect(vfx, "ACTIVATION_VFX"); _.ApplyEffectToObject(DurationType.Temporary, vfx, activator.Object, activationTime + 0.2f); } // If an animation has been specified, make the player play that animation now. // bypassing if perk is throw saber due to couldn't get the animation to work via db table edit if (animationID != Animation.Invalid && entity.ID != (int)PerkType.ThrowSaber) { activator.AssignCommand(() => _.ActionPlayAnimation(animationID, 1.0f, activationTime - 0.1f)); } // Mark player as busy. Busy players can't take other actions (crafting, harvesting, etc.) activator.IsBusy = true; // Non-players can't be interrupted via movement. if (!activator.IsPlayer) { // Begin the check for spell interruption. If the activator moves, the spell will be canceled. CheckForSpellInterruption(activator, uuid, activator.Position); } activator.SetLocalInt(uuid, (int)SpellStatusType.Started); // If there's a casting delay, display a timing bar on-screen. if (activationTime > 0) { NWNXPlayer.StartGuiTimingBar(activator, (int)activationTime, string.Empty); } // Run the FinishAbilityUse event at the end of the activation time. int perkID = entity.ID; var @event = new OnFinishAbilityUse(activator, uuid, perkID, target, pcPerkLevel, spellTier, armorPenalty); activator.DelayEvent(activationTime + 0.2f, @event); }
public bool Run(params object[] args) { NWPlayer player = (_.GetLastDisturbed()); NWPlaceable bay = (Object.OBJECT_SELF); int disturbType = _.GetInventoryDisturbType(); NWItem item = (_.GetInventoryDisturbItem()); bool stronidiumOnly = bay.GetLocalInt("CONTROL_TOWER_FUEL_TYPE") == TRUE; string allowedResref = stronidiumOnly ? "stronidium" : "fuel_cell"; string structureID = bay.GetLocalString("PC_BASE_STRUCTURE_ID"); // Check for either fuel cells or stronidium when adding an item to the container. if (disturbType == INVENTORY_DISTURB_TYPE_ADDED) { if (item.Resref != allowedResref) { ItemService.ReturnItem(player, item); player.SendMessage("Only " + (stronidiumOnly ? "Stronidium" : "Fuel Cells") + " may be placed inside this fuel bay."); return(false); } } // If the item removed wasn't fuel cells or stronidium, exit early. We don't need to do anything else. else if (disturbType == INVENTORY_DISTURB_TYPE_REMOVED) { if (item.Resref != allowedResref) { return(false); } } var structure = DataService.Single <PCBaseStructure>(x => x.ID == new Guid(structureID)); var pcBase = DataService.Get <PCBase>(structure.PCBaseID); // Calculate how much fuel exists in the bay's inventory. int fuelCount = 0; foreach (var fuel in bay.InventoryItems) { fuelCount += fuel.StackSize; } // If there are extra units of fuel, destroy them. We will set the stack size of the first fuel later on. NWItem firstFuel = (_.GetFirstItemInInventory(bay.Object)); NWItem nextFuel = (_.GetNextItemInInventory(bay.Object)); while (nextFuel.IsValid) { nextFuel.Destroy(); nextFuel = (_.GetNextItemInInventory(bay.Object)); } int maxFuel; // Handle Stronidium fuel process if (stronidiumOnly) { maxFuel = BaseService.CalculateMaxReinforcedFuel(pcBase.ID); // For starships only: Add the ship's cargo bonus to the max stronidium amount. if (bay.Area.GetLocalInt("BUILDING_TYPE") == (int)Enumeration.BuildingType.Starship) { maxFuel += 25 * SpaceService.GetCargoBonus(SpaceService.GetCargoBay(player.Area, null), (int)CustomItemPropertyType.StarshipStronidiumBonus); } // Did the player put too much fuel inside? Return the excess to their inventory. if (fuelCount > maxFuel) { int returnAmount = fuelCount - maxFuel; _.CreateItemOnObject("stronidium", player.Object, returnAmount); fuelCount = maxFuel; } firstFuel.StackSize = fuelCount; pcBase.ReinforcedFuel = fuelCount; if (bay.Area.GetLocalInt("BUILDING_TYPE") == (int)Enumeration.BuildingType.Starship) { // This is a starship. Update the creature object, if any, with the new fuel count. NWCreature ship = bay.Area.GetLocalObject("CREATURE"); if (ship.IsValid) { ship.SetLocalInt("STRONIDIUM", fuelCount); } } } // Handle Fuel Cell process else { maxFuel = BaseService.CalculateMaxFuel(pcBase.ID); // For starships only: Add the ship's cargo bonus to the max fuel amount. if (bay.Area.GetLocalInt("BUILDING_TYPE") == (int)Enumeration.BuildingType.Starship) { maxFuel += 25 * SpaceService.GetCargoBonus(SpaceService.GetCargoBay(player.Area, null), (int)CustomItemPropertyType.StarshipFuelBonus); } // Did the player put too much fuel inside? Return the excess to their inventory. if (fuelCount > maxFuel) { int returnAmount = fuelCount - maxFuel; _.CreateItemOnObject("fuel_cell", player.Object, returnAmount); fuelCount = maxFuel; } firstFuel.StackSize = fuelCount; pcBase.Fuel = fuelCount; } // Submit a DB data change for the fuel or stronidium amount adjustment. DataService.SubmitDataChange(pcBase, DatabaseActionType.Update); var tower = BaseService.GetBaseControlTower(structure.PCBaseID); var towerStructure = DataService.Single <BaseStructure>(x => x.ID == tower.BaseStructureID); if (towerStructure.BaseStructureTypeID == (int)BaseStructureType.Starship) { // This is a spaceship, so don't give the feedback message. return(true); } int fuelRating = towerStructure.FuelRating; // Stronidium - Every unit lasts for 6 seconds during reinforcement mode. if (stronidiumOnly) { int seconds = 6 * fuelCount; TimeSpan timeSpan = TimeSpan.FromSeconds(seconds); player.SendMessage(ColorTokenService.Gray("Reinforcement mode will last for " + TimeService.GetTimeLongIntervals(timeSpan.Days, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, false) + " (" + fuelCount + " / " + maxFuel + " units")); } // Regular fuel cells - Every unit lasts for 45, 15, or 5 minutes depending on the size of the tower. else { int minutes; switch (fuelRating) { case 1: // Small minutes = 45; break; case 2: // Medium minutes = 15; break; case 3: // Large minutes = 5; break; default: throw new Exception("Invalid fuel rating value: " + fuelRating); } TimeSpan timeSpan = TimeSpan.FromMinutes(minutes * fuelCount); player.SendMessage(ColorTokenService.Gray("Fuel will last for " + TimeService.GetTimeLongIntervals(timeSpan.Days, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, false) + " (" + fuelCount + " / " + maxFuel + " units)")); } return(true); }
public void OnItemDisturbed() { NWPlaceable bay = OBJECT_SELF; // Filer to only events created by a fuel bay, and ignore this event if it was triggered by clearing // the inventory when the bay is being destroyed. if (bay.Resref != "fuel_bay" || bay.GetLocalBool("SETUP") == true) { return; } NWItem item = StringToObject(NWNXEvents.GetEventData("ITEM")); NWPlayer player = bay.GetLocalObject("BAY_ACCESSOR"); var disturbType = NWNXEvents.GetCurrentEvent(); bool stronidiumOnly = GetLocalBool(bay, "CONTROL_TOWER_FUEL_TYPE"); string allowedResref = stronidiumOnly ? "stronidium" : "fuel_cell"; string structureID = bay.GetLocalString("PC_BASE_STRUCTURE_ID"); // Check for either fuel cells or stronidium when adding an item to the container. if (disturbType == EventType.ItemInventoryAddItemAfter) { if (item.Resref != allowedResref) { ItemService.ReturnItem(player, item); player.SendMessage("Only " + (stronidiumOnly ? "Stronidium" : "Fuel Cells") + " may be placed inside this fuel bay."); return; } } // If the item removed wasn't fuel cells or stronidium, exit early. We don't need to do anything else. else if (disturbType == EventType.ItemInventoryRemoveItemAfter) { if (item.Resref != allowedResref) { return; } } var structure = DataService.PCBaseStructure.GetByID(new Guid(structureID)); var pcBase = DataService.PCBase.GetByID(structure.PCBaseID); // Calculate how much fuel exists in the bay's inventory. int fuelCount = 0; foreach (var fuel in bay.InventoryItems) { fuelCount += fuel.StackSize; } // If there are extra units of fuel, destroy them. We will set the stack size of the first fuel later on. NWItem firstFuel = GetFirstItemInInventory(bay.Object); NWItem nextFuel = GetNextItemInInventory(bay.Object); while (nextFuel.IsValid) { nextFuel.Destroy(); nextFuel = GetNextItemInInventory(bay.Object); } int maxFuel; // Handle Stronidium fuel process if (stronidiumOnly) { maxFuel = BaseService.CalculateMaxReinforcedFuel(pcBase.ID); // For starships only: Add the ship's cargo bonus to the max stronidium amount. if (bay.Area.GetLocalInt("BUILDING_TYPE") == (int)Enumeration.BuildingType.Starship) { maxFuel += 25 * SpaceService.GetCargoBonus(SpaceService.GetCargoBay(player.Area, player), ItemPropertyType.StarshipStronidiumBonus); } // Did the player put too much fuel inside? Return the excess to their inventory. if (fuelCount > maxFuel) { int returnAmount = fuelCount - maxFuel; CreateItemOnObject("stronidium", player.Object, returnAmount); fuelCount = maxFuel; } firstFuel.StackSize = fuelCount; pcBase.ReinforcedFuel = fuelCount; if (bay.Area.GetLocalInt("BUILDING_TYPE") == (int)Enumeration.BuildingType.Starship) { // This is a starship. Update the creature object, if any, with the new fuel count. NWCreature ship = bay.Area.GetLocalObject("CREATURE"); if (ship.IsValid) { ship.SetLocalInt("STRONIDIUM", fuelCount); } } } // Handle Fuel Cell process else { maxFuel = BaseService.CalculateMaxFuel(pcBase.ID); // For starships only: Add the ship's cargo bonus to the max fuel amount. if (bay.Area.GetLocalInt("BUILDING_TYPE") == (int)Enumeration.BuildingType.Starship) { maxFuel += 25 * SpaceService.GetCargoBonus(SpaceService.GetCargoBay(player.Area, player), ItemPropertyType.StarshipFuelBonus); } // Did the player put too much fuel inside? Return the excess to their inventory. if (fuelCount > maxFuel) { int returnAmount = fuelCount - maxFuel; CreateItemOnObject("fuel_cell", player.Object, returnAmount); fuelCount = maxFuel; } firstFuel.StackSize = fuelCount; pcBase.Fuel = fuelCount; } // Submit a DB data change for the fuel or stronidium amount adjustment. DataService.SubmitDataChange(pcBase, DatabaseActionType.Update); var tower = BaseService.GetBaseControlTower(structure.PCBaseID); if (tower == null) { Console.WriteLine("Could not locate tower in Fuel Bay. PCBaseID = " + structure.PCBaseID); return; } var towerStructure = DataService.BaseStructure.GetByID(tower.BaseStructureID); if (towerStructure.BaseStructureTypeID == (int)BaseStructureType.Starship) { // This is a spaceship, so don't give the feedback message. return; } int fuelRating = towerStructure.FuelRating; // Stronidium - Every unit lasts for 6 seconds during reinforcement mode. if (stronidiumOnly) { int seconds = 6 * fuelCount; TimeSpan timeSpan = TimeSpan.FromSeconds(seconds); player.SendMessage(ColorTokenService.Gray("Reinforcement mode will last for " + TimeService.GetTimeLongIntervals(timeSpan.Days, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, false) + " (" + fuelCount + " / " + maxFuel + " units")); } // Regular fuel cells - Every unit lasts for 45, 15, or 5 minutes depending on the size of the tower. else { int minutes; switch (fuelRating) { case 1: // Small minutes = 45; break; case 2: // Medium minutes = 15; break; case 3: // Large minutes = 5; break; default: throw new Exception("Invalid fuel rating value: " + fuelRating); } TimeSpan timeSpan = TimeSpan.FromMinutes(minutes * fuelCount); player.SendMessage(ColorTokenService.Gray("Fuel will last for " + TimeService.GetTimeLongIntervals(timeSpan.Days, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, false) + " (" + fuelCount + " / " + maxFuel + " units)")); } return; }
private static void OnModuleUseFeat() { NWPlayer pc = Object.OBJECT_SELF; NWCreature target = NWNXEvents.OnFeatUsed_GetTarget().Object; int featID = NWNXEvents.OnFeatUsed_GetFeatID(); var perkFeat = DataService.SingleOrDefault <PerkFeat>(x => x.FeatID == featID); if (perkFeat == null) { return; } Data.Entity.Perk perk = DataService.GetAll <Data.Entity.Perk>().SingleOrDefault(x => x.ID == perkFeat.PerkID); if (perk == null) { return; } // Check to see if we are a spaceship. Spaceships can't use abilities... if (pc.GetLocalInt("IS_SHIP") > 0 || pc.GetLocalInt("IS_GUNNER") > 0) { pc.SendMessage("You cannot use that ability while piloting a ship."); return; } var perkAction = PerkService.GetPerkHandler(perkFeat.PerkID); Player playerEntity = DataService.Get <Player>(pc.GlobalID); int pcPerkLevel = PerkService.GetPCPerkLevel(pc, perk.ID); // If player is disabling an existing stance, remove that effect. if (perk.ExecutionTypeID == (int)PerkExecutionType.Stance) { PCCustomEffect stanceEffect = DataService.SingleOrDefault <PCCustomEffect>(x => x.StancePerkID == perk.ID && x.PlayerID == pc.GlobalID); if (stanceEffect != null) { if (CustomEffectService.RemoveStance(pc)) { return; } } } if (pcPerkLevel <= 0) { pc.SendMessage("You do not meet the prerequisites to use this ability."); return; } if (perkAction.IsHostile() && target.IsPlayer) { if (!PVPSanctuaryService.IsPVPAttackAllowed(pc, target.Object)) { return; } } if (pc.Area.Resref != target.Area.Resref || _.LineOfSightObject(pc.Object, target.Object) == 0) { pc.SendMessage("You cannot see your target."); return; } if (!perkAction.CanCastSpell(pc, target)) { pc.SendMessage(perkAction.CannotCastSpellMessage(pc, target) ?? "That ability cannot be used at this time."); return; } int fpCost = perkAction.FPCost(pc, perkAction.FPCost(pc, perk.BaseFPCost, featID), featID); if (playerEntity.CurrentFP < fpCost) { pc.SendMessage("You do not have enough FP. (Required: " + fpCost + ". You have: " + playerEntity.CurrentFP + ")"); return; } if (pc.IsBusy || pc.CurrentHP <= 0) { pc.SendMessage("You are too busy to activate that ability."); return; } // Check cooldown int? cooldownCategoryID = perkAction.CooldownCategoryID(pc, perk.CooldownCategoryID, featID); PCCooldown pcCooldown = DataService.GetAll <PCCooldown>().SingleOrDefault(x => x.PlayerID == pc.GlobalID && x.CooldownCategoryID == cooldownCategoryID); if (pcCooldown == null) { pcCooldown = new PCCooldown { CooldownCategoryID = Convert.ToInt32(cooldownCategoryID), DateUnlocked = DateTime.UtcNow.AddSeconds(-1), PlayerID = pc.GlobalID }; DataService.SubmitDataChange(pcCooldown, DatabaseActionType.Insert); } DateTime unlockDateTime = pcCooldown.DateUnlocked; DateTime now = DateTime.UtcNow; if (unlockDateTime > now) { string timeToWait = TimeService.GetTimeToWaitLongIntervals(now, unlockDateTime, false); pc.SendMessage("That ability can be used in " + timeToWait + "."); return; } // Force Abilities (aka Spells) if (perk.ExecutionTypeID == (int)PerkExecutionType.ForceAbility) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_FORCE); ActivateAbility(pc, target, perk, perkAction, pcPerkLevel, PerkExecutionType.ForceAbility, featID); } // Combat Abilities else if (perk.ExecutionTypeID == (int)PerkExecutionType.CombatAbility) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_PHYSICAL); ActivateAbility(pc, target, perk, perkAction, pcPerkLevel, PerkExecutionType.CombatAbility, featID); } // Queued Weapon Skills else if (perk.ExecutionTypeID == (int)PerkExecutionType.QueuedWeaponSkill) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_PHYSICAL); HandleQueueWeaponSkill(pc, perk, perkAction, featID); } // Stances else if (perk.ExecutionTypeID == (int)PerkExecutionType.Stance) { target.SetLocalInt(LAST_ATTACK + pc.GlobalID, ATTACK_COMBATABILITY); ActivateAbility(pc, target, perk, perkAction, pcPerkLevel, PerkExecutionType.Stance, featID); } }