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 + ")"); }
/// <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 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); } }
/// <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); } }
private static void OnModuleApplyDamage() { DamageEventData data = NWNXDamage.GetDamageEventData(); NWPlayer player = data.Damager.Object; NWCreature target = NWGameObject.OBJECT_SELF; int attackType = target.GetLocalInt(AbilityService.LAST_ATTACK + player.GlobalID); LoggingService.Trace(TraceComponent.LastAttack, "Last attack from " + player.GlobalID + " on " + _.GetName(target) + " was type " + attackType); if (attackType == AbilityService.ATTACK_PHYSICAL) { // Only apply bonus damage from physical attacks. HandleWeaponStatBonuses(); HandleEvadeOrDeflectBlasterFire(); HandleApplySneakAttackDamage(); } HandleDamageImmunity(); HandleAbsorptionFieldEffect(); HandleRecoveryBlast(); HandleTranquilizerEffect(); HandleStances(); }
/// <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); } }
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); }); }
private void Bar(NWCreature self) { // barActivity local var on each creature is set 0-3. Leaving room for expansion as we may want people in the bar doing different junk on spawn. // barActivity Vars: // 0: Do nothing // 1: Smoking // 2: Drinking // 3: Sitting in a chair int barActivity = self.GetLocalInt("barActivity"); /* Random head/clothes check * * */ switch (barActivity) { case 1: ActionPlayAnimation(Animation.LoopingCustom7, 1.0F, 9999F); break; case 2: ActionPlayAnimation(Animation.LoopingCustom9, 1.0F, 9999F); break; case 3: NWObject chair = GetNearestObjectByTag("chair", self); ActionSit(chair); break; default: break; } }
private static void ProcessLoot() { NWCreature creature = _.OBJECT_SELF; // Single loot table (without an index) int singleLootTableID = creature.GetLocalInt("LOOT_TABLE_ID"); if (singleLootTableID > 0) { int chance = creature.GetLocalInt("LOOT_TABLE_CHANCE"); int attempts = creature.GetLocalInt("LOOT_TABLE_ATTEMPTS"); RunLootAttempt(creature, singleLootTableID, chance, attempts); } // Multiple loot tables (with an index) int lootTableNumber = 1; int lootTableID = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber); while (lootTableID > 0) { int chance = creature.GetLocalInt("LOOT_TABLE_CHANCE_" + lootTableNumber); int attempts = creature.GetLocalInt("LOOT_TABLE_ATTEMPTS_" + lootTableNumber); RunLootAttempt(creature, lootTableID, chance, attempts); lootTableNumber++; lootTableID = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber); } }
public void OnCreatureDeath(NWCreature creature) { // Single loot table (without an index) int singleLootTableID = creature.GetLocalInt("LOOT_TABLE_ID"); if (singleLootTableID > 0) { int chance = creature.GetLocalInt("LOOT_TABLE_CHANCE"); int attempts = creature.GetLocalInt("LOOT_TABLE_ATTEMPTS"); RunLootAttempt(creature, singleLootTableID, chance, attempts); } // Multiple loot tables (with an index) int lootTableNumber = 1; int lootTableID = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber); while (lootTableID > 0) { int chance = creature.GetLocalInt("LOOT_TABLE_CHANCE_" + lootTableNumber); int attempts = creature.GetLocalInt("LOOT_TABLE_ATTEMPTS_" + lootTableNumber); RunLootAttempt(creature, lootTableID, chance, attempts); lootTableNumber++; lootTableID = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber); } }
public virtual void OnDeath(NWCreature self) { int vfx = self.GetLocalInt("DEATH_VFX"); if (vfx > 0) { ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(vfx), self); } }
private static void ProcessPerkFeats(NWCreature self) { // Bail early if any of the following is true: // - Creature has a weapon skill queued. // - Creature does not have a PerkFeat cache. // - There are no perk feats in the cache. // - Creature has no target. if (self.GetLocalInt("ACTIVE_WEAPON_SKILL") > 0) { return; } if (!self.Data.ContainsKey("PERK_FEATS")) { return; } Dictionary <int, AIPerkDetails> cache = self.Data["PERK_FEATS"]; if (cache.Count <= 0) { return; } NWObject target = _.GetAttackTarget(self); if (!target.IsValid) { return; } // Pull back whatever concentration effect is currently active, if any. var concentration = AbilityService.GetActiveConcentrationEffect(self); // Exclude any concentration effects, if necessary, then randomize potential feats to use. var randomizedFeatIDs = concentration.Type == PerkType.Unknown ? cache.Values // No concentration exclusions : cache.Values.Where(x => x.ExecutionType != PerkExecutionType.ConcentrationAbility); // Exclude concentration abilities randomizedFeatIDs = randomizedFeatIDs.OrderBy(o => RandomService.Random()); foreach (var perkDetails in randomizedFeatIDs) { // Move to next feat if this creature cannot use this one. if (!AbilityService.CanUsePerkFeat(self, target, perkDetails.FeatID)) { continue; } self.AssignCommand(() => { _.ActionUseFeat(perkDetails.FeatID, target); }); break; } }
public virtual void OnDeath(NWCreature self) { var vfx = (VisualEffect)self.GetLocalInt("DEATH_VFX"); if (vfx != VisualEffect.Invalid) { ApplyEffectToObject(DurationType.Instant, EffectVisualEffect(vfx), self); } }
public void OnCreatureDeath(NWCreature creature) { int lootTableNumber = 1; int lootTableID = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber); while (lootTableID > 0) { int chance = creature.GetLocalInt("LOOT_TABLE_CHANCE_" + lootTableNumber); if (chance <= 0 || chance > 100) { chance = 100; } int attempts = creature.GetLocalInt("LOOT_TABLE_ATTEMPTS_" + lootTableNumber); if (attempts <= 0) { attempts = 1; } for (int a = 1; a <= attempts; a++) { if (_random.Random(100) + 1 <= chance) { ItemVO model = PickRandomItemFromLootTable(lootTableID); if (model == null) { continue; } int spawnQuantity = model.Quantity > 1 ? _random.Random(1, model.Quantity) : 1; for (int x = 1; x <= spawnQuantity; x++) { _.CreateItemOnObject(model.Resref, creature.Object); } } } lootTableNumber++; lootTableID = creature.GetLocalInt("LOOT_TABLE_ID_" + lootTableNumber); } }
public static int GetCreaturePerkLevel(NWCreature creature, int perkTypeID) { if (creature.IsPlayer) { NWPlayer player = creature.Object; return(GetPCEffectivePerkLevel(player, perkTypeID)); } else { return(creature.GetLocalInt("PERK_LEVEL_" + perkTypeID)); } }
/// <summary> /// Retrieves the maximum FP a creature has. /// </summary> /// <param name="creature">The creature whose max FP we're getting.</param> /// <returns>The max FP a creature has.</returns> public static int GetMaxFP(NWCreature creature) { if (creature.IsPlayer) { var player = DataService.Player.GetByID(creature.GlobalID); return(player.MaxFP); } else { return(creature.GetLocalInt("MAX_FP")); } }
/// <summary> /// Returns the currently active concentration perk for a given creature. /// If no concentration perk is active, PerkType.Unknown will be returned. /// </summary> /// <param name="creature"></param> /// <returns>A ConcentrationEffect containing data about the active concentration effect.</returns> public static ConcentrationEffect GetActiveConcentrationEffect(NWCreature creature) { if (creature.IsPlayer) { Player dbPlayer = DataService.Player.GetByID(creature.GlobalID); if (dbPlayer.ActiveConcentrationPerkID == null) { return(new ConcentrationEffect(PerkType.Unknown, 0)); } return(new ConcentrationEffect((PerkType)dbPlayer.ActiveConcentrationPerkID, dbPlayer.ActiveConcentrationTier)); } else { // Creatures are assumed to always use the highest perk level available. int perkID = creature.GetLocalInt("ACTIVE_CONCENTRATION_PERK_ID"); int tier = creature.GetLocalInt("PERK_LEVEL_" + perkID); PerkType type = perkID <= 0 ? PerkType.Unknown : (PerkType)perkID; return(new ConcentrationEffect(type, tier)); } }
/// <summary> /// Returns the current FP amount of a creature. /// </summary> /// <param name="creature">The creature whose FP we're getting.</param> /// <returns>The amount of FP the creature currently has.</returns> public static int GetCurrentFP(NWCreature creature) { if (creature.IsPlayer) { var player = DataService.Player.GetByID(creature.GlobalID); return(player.CurrentFP); } else { return(creature.GetLocalInt("CURRENT_FP")); } }
private static void OnModuleApplyDamage() { var data = NWNXDamage.GetDamageEventData(); if (data.Base <= 0) { return; } NWObject damager = data.Damager; if (!damager.IsPlayer) { return; } NWCreature target = NWGameObject.OBJECT_SELF; // Check that this was a normal attack, and not (say) a damage over time effect. if (target.GetLocalInt(AbilityService.LAST_ATTACK + damager.GlobalID) != AbilityService.ATTACK_PHYSICAL) { return; } NWItem weapon = (_.GetLastWeaponUsed(damager.Object)); int damageBonus = weapon.DamageBonus; NWPlayer player = (damager.Object); int itemLevel = weapon.RecommendedLevel; SkillType skill = ItemService.GetSkillTypeForItem(weapon); if (skill == SkillType.Unknown) { return; } int rank = SkillService.GetPCSkillRank(player, skill); int delta = itemLevel - rank; if (delta >= 1) { damageBonus--; } damageBonus = damageBonus - delta / 5; if (damageBonus <= 0) { damageBonus = 0; } data.Base += damageBonus; NWNXDamage.SetDamageEventData(data); }
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 ForceAttackHighestEnmity(NWCreature self) { if (self.GetLocalInt("CASTING") == 1) { return; } var enmityTable = EnmityService.GetEnmityTable(self); var target = enmityTable.Values .OrderByDescending(o => o.TotalAmount) .FirstOrDefault(x => x.TargetObject.IsValid && x.TargetObject.Area.Equals(self.Area)); if (target == null) { return; } self.AssignCommand(() => { bool bDone = false; // See which force feats we have, and pick one to use. if (_.GetHasFeat((int)CustomFeatType.ForceLightning, self) == 1) { _.ClearAllActions(); bDone = UseFeat((int)CustomFeatType.ForceLightning, "ForceLightning", self, target.TargetObject); } if (!bDone && _.GetHasFeat((int)CustomFeatType.DrainLife, self) == 1) { _.ClearAllActions(); bDone = UseFeat((int)CustomFeatType.DrainLife, "DrainLife", self, target.TargetObject); } if (!bDone) { // No abilities available right now, run away! _.ActionMoveAwayFromObject(target.TargetObject, 1); } }); }
private static void OnCreatureDeath() { NWCreature creature = Object.OBJECT_SELF; int npcGroupID = creature.GetLocalInt("NPC_GROUP"); if (npcGroupID <= 0) { return; } NWObject oKiller = _.GetLastKiller(); if (!oKiller.IsPlayer) { return; } string areaResref = creature.Area.Resref; List <KeyValuePair <NWPlayer, int> > playersToAdvance = new List <KeyValuePair <NWPlayer, int> >(); NWPlayer oPC = _.GetFirstFactionMember(oKiller); while (oPC.IsValid) { if (areaResref != oPC.Area.Resref) { oPC = _.GetNextFactionMember(oKiller); continue; } if (_.GetDistanceBetween(creature, oPC) <= 0.0f || _.GetDistanceBetween(creature, oPC) > 20.0f) { oPC = _.GetNextFactionMember(oKiller); continue; } var playerID = oPC.GlobalID; var killTargets = DataService.Where <PCQuestKillTargetProgress>(x => x.PlayerID == playerID && x.NPCGroupID == npcGroupID).ToList(); foreach (var kt in killTargets) { var questStatus = DataService.Get <PCQuestStatus>(kt.PCQuestStatusID); var quest = DataService.Get <Quest>(questStatus.QuestID); var npcGroup = DataService.Get <NPCGroup>(kt.NPCGroupID); kt.RemainingToKill--; string targetGroupName = npcGroup.Name; string updateMessage = "[" + quest.Name + "] " + targetGroupName + " remaining: " + kt.RemainingToKill; DatabaseActionType action = DatabaseActionType.Update; if (kt.RemainingToKill <= 0) { updateMessage += " " + ColorTokenService.Green(" {COMPLETE}"); playersToAdvance.Add(new KeyValuePair <NWPlayer, int>(oPC, quest.ID)); action = DatabaseActionType.Delete; } DataService.SubmitDataChange(kt, action); var pc = oPC; _.DelayCommand(1.0f, () => { pc.SendMessage(updateMessage); }); string ruleName = quest.OnKillTargetRule; if (!string.IsNullOrWhiteSpace(ruleName)) { var pcCopy = oPC; var rule = GetQuestRule(ruleName); string[] args = null; if (!string.IsNullOrWhiteSpace(quest.OnKillTargetArgs)) { args = quest.OnKillTargetArgs.Split(','); } rule.Run(pcCopy, creature, quest.ID, args); } } oPC = _.GetNextFactionMember(oKiller); } foreach (var player in playersToAdvance) { AdvanceQuestState(player.Key, null, player.Value); } }
public void ApplyEffects(NWCreature user, NWItem item, NWObject target, Location targetLocation, CustomData customData) { NWPlayer player = user.Object; ResourceQuality quality = (ResourceQuality)target.GetLocalInt("RESOURCE_QUALITY"); int tier = target.GetLocalInt("RESOURCE_TIER"); int remaining = target.GetLocalInt("RESOURCE_COUNT") - 1; string itemResref = target.GetLocalString("RESOURCE_RESREF"); int gemChance = ResourceService.CalculateChanceForComponentBonus(player, tier, quality); int roll = RandomService.Random(1, 100); int rank = SkillService.GetPCSkillRank(player, SkillType.Harvesting); if (item.RecommendedLevel < rank) { rank = item.RecommendedLevel; } int difficulty = (tier - 1) * 10 + ResourceService.GetDifficultyAdjustment(quality); int delta = difficulty - rank; int baseXP = 0; if (delta >= 6) { baseXP = 400; } else if (delta == 5) { baseXP = 350; } else if (delta == 4) { baseXP = 325; } else if (delta == 3) { baseXP = 300; } else if (delta == 2) { baseXP = 250; } else if (delta == 1) { baseXP = 225; } else if (delta == 0) { baseXP = 200; } else if (delta == -1) { baseXP = 150; } else if (delta == -2) { baseXP = 100; } else if (delta == -3) { baseXP = 50; } else if (delta == -4) { baseXP = 25; } int itemHarvestBonus = item.HarvestingBonus; int scanningBonus = user.GetLocalInt(target.GlobalID.ToString()); gemChance += itemHarvestBonus * 2 + scanningBonus * 2; baseXP = baseXP + scanningBonus * 5; // Spawn the normal resource. NWItem resource = CreateItemOnObject(itemResref, player); user.SendMessage("You harvest " + resource.Name + "."); // If player meets the chance to acquire a gem, create one and modify its properties. if (quality > ResourceQuality.Low && roll <= gemChance) { // Gemstone quality is determined by the quality of the vein. switch (quality) { case ResourceQuality.Normal: resource = CreateItemOnObject("flawed_gemstone", player); break; case ResourceQuality.High: resource = CreateItemOnObject("gemstone", player); break; case ResourceQuality.VeryHigh: resource = CreateItemOnObject("perfect_gemstone", player); break; } var ip = ResourceService.GetRandomComponentBonusIP(quality); BiowareXP2.IPSafeAddItemProperty(resource, ip.Item1, 0.0f, AddItemPropertyPolicy.IgnoreExisting, true, true); switch (ip.Item2) { case 0: resource.Name = ColorTokenService.Green(resource.Name); break; case 1: resource.Name = ColorTokenService.Blue(resource.Name); break; case 2: resource.Name = ColorTokenService.Purple(resource.Name); break; case 3: resource.Name = ColorTokenService.Orange(resource.Name); break; case 4: resource.Name = ColorTokenService.LightPurple(resource.Name); break; case 5: resource.Name = ColorTokenService.Yellow(resource.Name); break; case 6: resource.Name = ColorTokenService.Red(resource.Name); break; case 7: resource.Name = ColorTokenService.Cyan(resource.Name); break; } user.SendMessage("You harvest " + resource.Name + "."); } float decayMinimum = 0.03f; float decayMaximum = 0.07f; if (delta > 0) { decayMinimum += delta * 0.1f; decayMaximum += delta * 0.1f; } DurabilityService.RunItemDecay(player, item, RandomService.RandomFloat(decayMinimum, decayMaximum)); int xp = baseXP; SkillService.GiveSkillXP(player, SkillType.Harvesting, xp); if (remaining <= 0) { NWPlaceable prop = target.GetLocalObject("RESOURCE_PROP_OBJ"); if (prop.IsValid) { prop.Destroy(); } target.Destroy(); user.DeleteLocalInt(target.GlobalID.ToString()); } else { target.SetLocalInt("RESOURCE_COUNT", remaining); } ApplyEffectAtLocation(DurationType.Instant, EffectVisualEffect(VisualEffect.Vfx_Fnf_Summon_Monster_3), target.Location); }
/// <summary> /// Updates a player's quest status if the creature is part of an ongoing quest. /// Progresses the player to the next state if all requirements are met. /// </summary> private static void OnCreatureDeath() { NWCreature creature = _.OBJECT_SELF; int npcGroupID = creature.GetLocalInt("NPC_GROUP"); if (npcGroupID <= 0) { return; } NWObject oKiller = GetLastKiller(); if (!oKiller.IsPlayer) { return; } string areaResref = creature.Area.Resref; List <KeyValuePair <NWPlayer, int> > playersToAdvance = new List <KeyValuePair <NWPlayer, int> >(); NWPlayer oPC = GetFirstFactionMember(oKiller); while (oPC.IsValid) { if (areaResref != oPC.Area.Resref) { oPC = GetNextFactionMember(oKiller); continue; } if (GetDistanceBetween(creature, oPC) <= 0.0f || GetDistanceBetween(creature, oPC) > 40.0f) { oPC = GetNextFactionMember(oKiller); continue; } var playerID = oPC.GlobalID; var killTargets = DataService.PCQuestKillTargetProgress.GetAllByPlayerIDAndNPCGroupID(playerID, npcGroupID).ToList(); foreach (var kt in killTargets) { var questStatus = DataService.PCQuestStatus.GetByID(kt.PCQuestStatusID); var quest = GetQuestByID(questStatus.QuestID); var npcGroup = DataService.NPCGroup.GetByID(kt.NPCGroupID); kt.RemainingToKill--; string targetGroupName = npcGroup.Name; string updateMessage = "[" + quest.Name + "] " + targetGroupName + " remaining: " + kt.RemainingToKill; DatabaseActionType action = DatabaseActionType.Update; if (kt.RemainingToKill <= 0) { updateMessage += " " + ColorTokenService.Green(" {COMPLETE}"); playersToAdvance.Add(new KeyValuePair <NWPlayer, int>(oPC, quest.QuestID)); action = DatabaseActionType.Delete; } DataService.SubmitDataChange(kt, action); var pc = oPC; DelayCommand(1.0f, () => { pc.SendMessage(updateMessage); }); } oPC = GetNextFactionMember(oKiller); } foreach (var player in playersToAdvance) { var quest = GetQuestByID(player.Value); quest.Advance(player.Key, creature); } }
public void ApplyEffects(NWCreature user, NWItem item, NWObject target, Location targetLocation, CustomData customData) { NWPlayer player = user.Object; ResourceQuality quality = (ResourceQuality)target.GetLocalInt("RESOURCE_QUALITY"); int tier = target.GetLocalInt("RESOURCE_TIER"); int remaining = target.GetLocalInt("RESOURCE_COUNT") - 1; string itemResref = target.GetLocalString("RESOURCE_RESREF"); int ipBonusChance = _resource.CalculateChanceForComponentBonus(player, tier, quality); int roll = _random.Random(1, 100); int rank = _skill.GetPCSkillRank(player, SkillType.Harvesting); if (item.RecommendedLevel < rank) { rank = item.RecommendedLevel; } int difficulty = (tier - 1) * 10 + _resource.GetDifficultyAdjustment(quality); int delta = difficulty - rank; int baseXP = 0; if (delta >= 6) { baseXP = 400; } else if (delta == 5) { baseXP = 350; } else if (delta == 4) { baseXP = 325; } else if (delta == 3) { baseXP = 300; } else if (delta == 2) { baseXP = 250; } else if (delta == 1) { baseXP = 225; } else if (delta == 0) { baseXP = 200; } else if (delta == -1) { baseXP = 150; } else if (delta == -2) { baseXP = 100; } else if (delta == -3) { baseXP = 50; } else if (delta == -4) { baseXP = 25; } int itemHarvestBonus = item.HarvestingBonus; int scanningBonus = user.GetLocalInt(target.GlobalID.ToString()); ipBonusChance += itemHarvestBonus * 2 + scanningBonus * 2; baseXP = baseXP + scanningBonus * 5; NWItem resource = _.CreateItemOnObject(itemResref, player.Object); if (roll <= ipBonusChance) { var ip = _resource.GetRandomComponentBonusIP(quality); _biowareXP2.IPSafeAddItemProperty(resource, ip.Item1, 0.0f, AddItemPropertyPolicy.IgnoreExisting, true, true); switch (ip.Item2) { case 0: resource.Name = _color.Green(resource.Name); break; case 1: resource.Name = _color.Blue(resource.Name); break; case 2: resource.Name = _color.Purple(resource.Name); break; case 3: resource.Name = _color.Orange(resource.Name); break; } } float decayMinimum = 0.03f; float decayMaximum = 0.07f; if (delta > 0) { decayMinimum += delta * 0.1f; decayMaximum += delta * 0.1f; } user.SendMessage("You harvest " + resource.Name + "."); _durability.RunItemDecay(player, item, _random.RandomFloat(decayMinimum, decayMaximum)); int xp = baseXP; _skill.GiveSkillXP(player, SkillType.Harvesting, xp); if (remaining <= 0) { NWPlaceable prop = target.GetLocalObject("RESOURCE_PROP_OBJ"); if (prop.IsValid) { prop.Destroy(); } target.Destroy(); user.DeleteLocalInt(target.GlobalID.ToString()); } else { target.SetLocalInt("RESOURCE_COUNT", remaining); } _.ApplyEffectAtLocation(DURATION_TYPE_INSTANT, _.EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3), target.Location); }
private static void OnCreatureSpawn() { NWCreature self = Object.OBJECT_SELF; // Don't modify AI behaviour for DM-spawned creatures. if (self.GetLocalInt("DM_SPAWNED") == _.TRUE) { return; } string script = GetBehaviourScript(Object.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); }
private AIFlags GetAIFlags(NWCreature self) { return((AIFlags)self.GetLocalInt("AI_FLAGS")); }
public void OnCreatureDeath(NWCreature creature) { int npcGroupID = creature.GetLocalInt("NPC_GROUP"); if (npcGroupID <= 0) { return; } NWObject oKiller = NWObject.Wrap(_.GetLastKiller()); if (_.GetIsPC(oKiller.Object) == FALSE || _.GetIsDM(oKiller.Object) == TRUE) { return; } string areaResref = creature.Area.Resref; NWPlayer oPC = NWPlayer.Wrap(_.GetFirstFactionMember(oKiller.Object)); while (oPC.IsValid) { if (areaResref != oPC.Area.Resref) { continue; } if (_.GetDistanceBetween(creature.Object, oPC.Object) == 0.0f || _.GetDistanceBetween(creature.Object, oPC.Object) > 20.0f) { continue; } List <PCQuestKillTargetProgress> killTargets = _db.PCQuestKillTargetProgresses.Where(x => x.PlayerID == oPC.GlobalID && x.NPCGroupID == npcGroupID).ToList(); foreach (PCQuestKillTargetProgress kt in killTargets) { kt.RemainingToKill--; string targetGroupName = kt.NPCGroup.Name; string questName = kt.PcQuestStatus.Quest.Name; string updateMessage = "[" + questName + "] " + targetGroupName + " remaining: " + kt.RemainingToKill; if (kt.RemainingToKill <= 0) { _db.PCQuestKillTargetProgresses.Remove(kt); updateMessage += " " + _color.Green(" {COMPLETE}"); if (kt.PcQuestStatus.PCQuestKillTargetProgresses.Count - 1 <= 0) { AdvanceQuestState(oPC, kt.PcQuestStatus.QuestID); } } _db.SaveChanges(); string finalMessage = updateMessage; var pc = oPC; oPC.DelayCommand(() => { pc.SendMessage(finalMessage); }, 1.0f); } oPC = NWPlayer.Wrap(_.GetNextFactionMember(oKiller.Object)); } }
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); } }
/// <summary> /// Runs validation checks to ensure activator can use a perk feat. /// Activation will fail if any of the following are true: /// - Target is invalid /// - Activator is a ship /// - Feat is not a perk feat /// - Cooldown has not passed /// </summary> /// <param name="activator">The creature activating a perk feat.</param> /// <param name="target">The target of the perk feat.</param> /// <param name="featID">The ID number of the feat being used.</param> /// <returns>true if able to use perk feat on target, false otherwise.</returns> public static bool CanUsePerkFeat(NWCreature activator, NWObject target, Feat featID) { var perkFeat = DataService.PerkFeat.GetByFeatIDOrDefault((int)featID); // There's no matching feat in the DB for this ability. Exit early. if (perkFeat == null) { return(false); } // Retrieve the perk information. Data.Entity.Perk perk = DataService.Perk.GetByIDOrDefault(perkFeat.PerkID); // No perk could be found. Exit early. if (perk == null) { return(false); } // Check to see if we are a spaceship. Spaceships can't use abilities... if (activator.GetLocalInt("IS_SHIP") > 0 || activator.GetLocalInt("IS_GUNNER") > 0) { activator.SendMessage("You cannot use that ability while piloting a ship."); return(false); } // Retrieve the perk-specific handler logic. var handler = PerkService.GetPerkHandler(perkFeat.PerkID); // Get the creature's perk level. int creaturePerkLevel = PerkService.GetCreaturePerkLevel(activator, perk.ID); // If player is disabling an existing stance, remove that effect. if (perk.ExecutionTypeID == PerkExecutionType.Stance) { // Can't process NPC stances at the moment. Need to do some more refactoring before this is possible. // todo: handle NPC stances. if (!activator.IsPlayer) { return(false); } PCCustomEffect stanceEffect = DataService.PCCustomEffect.GetByStancePerkOrDefault(activator.GlobalID, perk.ID); if (stanceEffect != null) { if (CustomEffectService.RemoveStance(activator)) { return(false); } } } // Check for a valid perk level. if (creaturePerkLevel <= 0) { activator.SendMessage("You do not meet the prerequisites to use this ability."); return(false); } // Verify that this hostile action meets PVP sanctuary restriction rules. if (handler.IsHostile() && target.IsPlayer) { if (!PVPSanctuaryService.IsPVPAttackAllowed(activator.Object, target.Object)) { return(false); } } // Activator and target must be in the same area and within line of sight. if (activator.Area.Resref != target.Area.Resref || _.LineOfSightObject(activator.Object, target.Object) == false) { activator.SendMessage("You cannot see your target."); return(false); } // Run this perk's specific checks on whether the activator may use this perk on the target. string canCast = handler.CanCastSpell(activator, target, perkFeat.PerkLevelUnlocked); if (!string.IsNullOrWhiteSpace(canCast)) { activator.SendMessage(canCast); return(false); } // Calculate the FP cost to use this ability. Verify activator has sufficient FP. int fpCost = handler.FPCost(activator, handler.FPCost(activator, perkFeat.BaseFPCost, perkFeat.PerkLevelUnlocked), perkFeat.PerkLevelUnlocked); int currentFP = GetCurrentFP(activator); if (currentFP < fpCost) { activator.SendMessage("You do not have enough FP. (Required: " + fpCost + ". You have: " + currentFP + ")"); return(false); } // Verify activator isn't busy or dead. if (activator.IsBusy || activator.CurrentHP <= 0) { activator.SendMessage("You are too busy to activate that ability."); return(false); } // verify activator is commandable. https://github.com/zunath/SWLOR_NWN/issues/940#issue-467175951 if (!activator.IsCommandable) { activator.SendMessage("You cannot take actions currently."); return(false); } // If we're executing a concentration ability, check and see if the activator currently has this ability // active. If it's active, then we immediately remove its effect and bail out. // Any other ability (including other concentration abilities) execute as normal. if (perk.ExecutionTypeID == PerkExecutionType.ConcentrationAbility) { // Retrieve the concentration effect for this creature. var concentrationEffect = GetActiveConcentrationEffect(activator); if ((int)concentrationEffect.Type == perk.ID) { // It's active. Time to disable it. EndConcentrationEffect(activator); activator.SendMessage("Concentration ability '" + perk.Name + "' deactivated."); SendAOEMessage(activator, activator.Name + " deactivates concentration ability '" + perk.Name + "'."); return(false); } } // Retrieve the cooldown information and determine the unlock time. int? cooldownCategoryID = handler.CooldownCategoryID(activator, perk.CooldownCategoryID, perkFeat.PerkLevelUnlocked); DateTime now = DateTime.UtcNow; DateTime unlockDateTime = cooldownCategoryID == null ? now : GetAbilityCooldownUnlocked(activator, (int)cooldownCategoryID); // Check if we've passed the unlock date. Exit early if we have not. if (unlockDateTime > now) { string timeToWait = TimeService.GetTimeToWaitLongIntervals(now, unlockDateTime, false); activator.SendMessage("That ability can be used in " + timeToWait + "."); return(false); } // Passed all checks. Return true. return(true); }