public static ItemVO PickRandomItemFromLootTable(int lootTableID) { if (lootTableID <= 0) { return(null); } var lootTableItems = DataService.LootTableItem.GetAllByLootTableID(lootTableID).ToList(); if (lootTableItems.Count <= 0) { return(null); } int[] weights = new int[lootTableItems.Count]; for (int x = 0; x < lootTableItems.Count; x++) { weights[x] = lootTableItems.ElementAt(x).Weight; } int randomIndex = RandomService.GetRandomWeightedIndex(weights); LootTableItem itemEntity = lootTableItems.ElementAt(randomIndex); int quantity = RandomService.Random(itemEntity.MaxQuantity) + 1; ItemVO result = new ItemVO { Quantity = quantity, Resref = itemEntity.Resref, SpawnRule = itemEntity.SpawnRule }; return(result); }
/// <summary> /// Cycle out the available guild tasks if the previous set has been available for 24 hours. /// </summary> private static void OnModuleLoad() { var config = DataService.Get <ServerConfiguration>(1); var now = DateTime.UtcNow; // 24 hours haven't passed since the last cycle. Bail out now. if (now < config.LastGuildTaskUpdate.AddHours(24)) { return; } // Start by marking the existing tasks as not currently offered. foreach (var task in DataService.Where <GuildTask>(x => x.IsCurrentlyOffered)) { task.IsCurrentlyOffered = false; DataService.SubmitDataChange(task, DatabaseActionType.Update); } int maxRank = RankProgression.Keys.Max(); // Active available tasks are grouped by GuildID and RequiredRank. // 10 of each are randomly selected and marked as currently offered. // This makes them appear in the dialog menu for players. // If there are 10 or less available tasks, all of them will be enabled and no randomization will occur. foreach (var guild in DataService.GetAll <Guild>()) { for (int rank = 0; rank <= maxRank; rank++) { var rank1 = rank; // VS recommends copying the variable. Unsure why. var potentialTasks = DataService.Where <GuildTask>(x => x.GuildID == guild.ID && x.RequiredRank == rank1); IEnumerable <GuildTask> tasks; // Need at least 11 tasks to randomize. We have ten or less. Simply enable all of these. if (potentialTasks.Count <= 10) { tasks = potentialTasks; } // Pick 10 tasks randomly out of the potential list. else { tasks = potentialTasks.OrderBy(o => RandomService.Random()).Take(10); } // We've got our set of tasks. Mark them as currently offered and submit the data change. foreach (var task in tasks) { task.IsCurrentlyOffered = true; DataService.SubmitDataChange(task, DatabaseActionType.Update); } } } // Update the server config and mark the timestamp. config.LastGuildTaskUpdate = now; DataService.SubmitDataChange(config, DatabaseActionType.Update); }
public static Location GetRandomSpawnPoint(NWArea area) { var walkmeshes = AreaService.GetAreaWalkmeshes(area); int count = walkmeshes.Count; var index = count <= 0 ? 0 : RandomService.Random(count); var spawnPoint = walkmeshes[index]; return(Location(area.Object, Vector((float)spawnPoint.LocationX, (float)spawnPoint.LocationY, (float)spawnPoint.LocationZ), RandomService.RandomFloat(0, 360))); }
public static Location GetRandomSpawnPoint(NWArea area) { Area dbArea = DataService.Area.GetByResref(area.Resref); var walkmeshes = DataService.AreaWalkmesh.GetAllByAreaID(dbArea.ID).ToList(); int count = walkmeshes.Count; var index = count <= 0 ? 0 : RandomService.Random(count); var spawnPoint = walkmeshes[index]; return(Location(area.Object, Vector((float)spawnPoint.LocationX, (float)spawnPoint.LocationY, (float)spawnPoint.LocationZ), RandomService.RandomFloat(0, 360))); }
private static void RunLootAttempt(NWCreature target, int lootTableID, int chance, int attempts) { if (chance <= 0) { chance = 75; } else if (chance > 100) { chance = 100; } if (attempts <= 0) { attempts = 1; } for (int a = 1; a <= attempts; a++) { if (RandomService.Random(100) + 1 <= chance) { ItemVO model = PickRandomItemFromLootTable(lootTableID); if (model == null) { continue; } int spawnQuantity = model.Quantity > 1 ? RandomService.Random(1, model.Quantity) : 1; for (int x = 1; x <= spawnQuantity; x++) { var item = CreateItemOnObject(model.Resref, target); if (!string.IsNullOrWhiteSpace(model.SpawnRule)) { var rule = SpawnService.GetSpawnRule(model.SpawnRule); rule.Run(item); } } } } }
private static void SaveChestInventory(NWPlayer oPC, NWPlaceable oChest, bool resetTimeLock) { int chestID = oChest.GetLocalInt(SearchSiteIDVariableName); PCSearchSite entity = DataService.SingleOrDefault <PCSearchSite>(x => x.PlayerID == oPC.GlobalID && x.SearchSiteID == chestID); int lockHours = RandomService.Random(2, 5); DateTime lockTime = DateTime.UtcNow.AddHours(lockHours); if (entity != null) { if (resetTimeLock) { lockTime = entity.UnlockDateTime; } DataService.SubmitDataChange(entity, DatabaseActionType.Delete); } entity = new PCSearchSite { PlayerID = oPC.GlobalID, SearchSiteID = chestID, UnlockDateTime = lockTime }; foreach (NWItem item in oChest.InventoryItems) { if (item.GetLocalInt("QUEST_ID") <= 0) { PCSearchSiteItem itemEntity = new PCSearchSiteItem { SearchItem = SerializationService.Serialize(item), SearchSiteID = entity.SearchSiteID }; DataService.SubmitDataChange(itemEntity, DatabaseActionType.Insert); } } }
private static ItemVO PickResultItem(int lootTableID) { var lootTableItems = DataService.Where <LootTableItem>(x => x.LootTableID == lootTableID).ToList(); int[] weights = new int[lootTableItems.Count]; for (int x = 0; x < lootTableItems.Count; x++) { weights[x] = lootTableItems.ElementAt(x).Weight; } int randomIndex = RandomService.GetRandomWeightedIndex(weights); LootTableItem itemEntity = lootTableItems.ElementAt(randomIndex); int quantity = RandomService.Random(itemEntity.MaxQuantity) + 1; ItemVO result = new ItemVO { Quantity = quantity, Resref = itemEntity.Resref }; return(result); }
private static void RunSearchCycle(NWPlayer oPC, NWPlaceable oChest, int iDC) { int lootTable = oChest.GetLocalInt(SearchSiteLootTableVariableName); int skill = _.GetSkillRank(_.SKILL_SEARCH, oPC.Object); if (skill > 10) { skill = 10; } else if (skill < 0) { skill = 0; } int roll = RandomService.Random(20) + 1; int combinedRoll = roll + skill; if (roll + skill >= iDC) { oPC.FloatingText(ColorTokenService.SkillCheck("Search: *success*: (" + roll + " + " + skill + " = " + combinedRoll + " vs. DC: " + iDC + ")")); ItemVO spawnItem = PickResultItem(lootTable); if (!string.IsNullOrWhiteSpace(spawnItem.Resref) && spawnItem.Quantity > 0) { NWItem foundItem = (_.CreateItemOnObject(spawnItem.Resref, oChest.Object, spawnItem.Quantity, "")); float maxDurability = DurabilityService.GetMaxDurability(foundItem); if (maxDurability > -1) { DurabilityService.SetDurability(foundItem, RandomService.RandomFloat() * maxDurability + 1); } } } else { oPC.FloatingText(ColorTokenService.SkillCheck("Search: *failure*: (" + roll + " + " + skill + " = " + combinedRoll + " vs. DC: " + iDC + ")")); } }
private static void InitializeAreaSpawns(NWArea area) { var areaSpawn = new AreaSpawn(); // Check for manually placed spawns NWObject obj = GetFirstObjectInArea(area.Object); while (obj.IsValid) { bool isSpawn = obj.ObjectType == OBJECT_TYPE_WAYPOINT && obj.GetLocalInt("IS_SPAWN") == TRUE; if (isSpawn) { int spawnType = obj.GetLocalInt("SPAWN_TYPE"); int objectType = spawnType == 0 || spawnType == OBJECT_TYPE_CREATURE ? OBJECT_TYPE_CREATURE : spawnType; int spawnTableID = obj.GetLocalInt("SPAWN_TABLE_ID"); int npcGroupID = obj.GetLocalInt("SPAWN_NPC_GROUP_ID"); string behaviourScript = obj.GetLocalString("SPAWN_BEHAVIOUR_SCRIPT"); if (string.IsNullOrWhiteSpace(behaviourScript)) { behaviourScript = obj.GetLocalString("SPAWN_BEHAVIOUR"); } string spawnResref = obj.GetLocalString("SPAWN_RESREF"); float respawnTime = obj.GetLocalFloat("SPAWN_RESPAWN_SECONDS"); string spawnRule = obj.GetLocalString("SPAWN_RULE"); int deathVFXID = obj.GetLocalInt("SPAWN_DEATH_VFX"); AIFlags aiFlags = (AIFlags)obj.GetLocalInt("SPAWN_AI_FLAGS"); bool useResref = true; // No resref specified but a table was, look in the database for a random record. if (string.IsNullOrWhiteSpace(spawnResref) && spawnTableID > 0) { // Pick a random record. var spawnObjects = DataService.SpawnObject.GetAllBySpawnTableID(spawnTableID).ToList(); int count = spawnObjects.Count; int index = count <= 0 ? 0 : RandomService.Random(count); var dbSpawn = spawnObjects[index]; if (dbSpawn != null) { spawnResref = dbSpawn.Resref; useResref = false; if (dbSpawn.NPCGroupID != null && dbSpawn.NPCGroupID > 0) { npcGroupID = Convert.ToInt32(dbSpawn.NPCGroupID); } if (!string.IsNullOrWhiteSpace(dbSpawn.BehaviourScript)) { behaviourScript = dbSpawn.BehaviourScript; } if (!string.IsNullOrWhiteSpace(dbSpawn.SpawnRule)) { spawnRule = dbSpawn.SpawnRule; } if (deathVFXID <= 0) { deathVFXID = dbSpawn.DeathVFXID; } if (aiFlags == AIFlags.None) { aiFlags = dbSpawn.AIFlags; } } } // If we found a resref, spawn the object and add it to the cache. if (!string.IsNullOrWhiteSpace(spawnResref)) { // Delay the creation so that the iteration through the area doesn't get thrown off by new entries. Location location = obj.Location; bool isInstance = area.IsInstance; ObjectSpawn newSpawn; if (useResref) { newSpawn = new ObjectSpawn(location, true, spawnResref, respawnTime); } else { newSpawn = new ObjectSpawn(location, true, spawnTableID, respawnTime); } if (npcGroupID > 0) { newSpawn.NPCGroupID = npcGroupID; } if (deathVFXID > 0) { newSpawn.DeathVFXID = deathVFXID; } if (!string.IsNullOrWhiteSpace(behaviourScript)) { newSpawn.BehaviourScript = behaviourScript; } if (!string.IsNullOrWhiteSpace(spawnRule)) { newSpawn.SpawnRule = spawnRule; } if (aiFlags == AIFlags.None) { newSpawn.AIFlags = aiFlags; } // Instance spawns are one-shot. if (isInstance) { newSpawn.Respawns = false; } if (objectType == OBJECT_TYPE_CREATURE) { areaSpawn.Creatures.Add(newSpawn); } else if (objectType == OBJECT_TYPE_PLACEABLE) { areaSpawn.Placeables.Add(newSpawn); } } } obj = GetNextObjectInArea(area.Object); } AreaSpawns.Add(area, areaSpawn); DelayCommand(1.0f, () => { SpawnResources(area, areaSpawn); }); }
public static string TranslateSnippetForListener(NWObject speaker, NWObject listener, SkillType language, string snippet) { Dictionary <SkillType, Type> map = new Dictionary <SkillType, Type> { { SkillType.Bothese, typeof(TranslatorBothese) }, { SkillType.Catharese, typeof(TranslatorCatharese) }, { SkillType.Cheunh, typeof(TranslatorCheunh) }, { SkillType.Dosh, typeof(TranslatorDosh) }, { SkillType.Droidspeak, typeof(TranslatorDroidspeak) }, { SkillType.Huttese, typeof(TranslatorHuttese) }, { SkillType.Mandoa, typeof(TranslatorMandoa) }, { SkillType.Shyriiwook, typeof(TranslatorShyriiwook) }, { SkillType.Twileki, typeof(TranslatorTwileki) }, { SkillType.Zabraki, typeof(TranslatorZabraki) }, { SkillType.Mirialan, typeof(TranslatorMirialan) }, { SkillType.MonCalamarian, typeof(TranslatorMonCalamarian) }, { SkillType.Ugnaught, typeof(TranslatorUgnaught) } }; Type type = typeof(TranslatorGeneric); map.TryGetValue(language, out type); ITranslator translator = (ITranslator)Activator.CreateInstance(type); if (speaker.IsPC && !speaker.IsDM) { // Get the rank and max rank for the speaker, and garble their English text based on it. NWPlayer speakerAsPlayer = speaker.Object; int speakerSkillRank = SkillService.GetPCSkillRank(speakerAsPlayer, language); int speakerSkillMaxRank = SkillService.GetSkill(language).MaxRank; if (speakerSkillRank != speakerSkillMaxRank) { int garbledChance = 100 - (int)(((float)speakerSkillRank / (float)speakerSkillMaxRank) * 100); string[] split = snippet.Split(' '); for (int i = 0; i < split.Length; ++i) { if (RandomService.Random(100) <= garbledChance) { split[i] = new string(split[i].ToCharArray().OrderBy(s => (RandomService.Random(2) % 2) == 0).ToArray()); } } snippet = split.Aggregate((a, b) => a + " " + b); } } if (!listener.IsPC || listener.IsDM) { // Short circuit for a DM or NPC - they will always understand the text. return(snippet); } // Let's grab the max rank for the listener skill, and then we roll for a successful translate based on that. NWPlayer listenerAsPlayer = listener.Object; int rank = SkillService.GetPCSkillRank(listenerAsPlayer, language); int maxRank = SkillService.GetSkill(language).MaxRank; // Check for the Comprehend Speech concentration ability. Player dbPlayer = DataService.Player.GetByID(listenerAsPlayer.GlobalID); bool grantSenseXP = false; if (dbPlayer.ActiveConcentrationPerkID == (int)PerkType.ComprehendSpeech) { int bonus = 5 * dbPlayer.ActiveConcentrationTier; rank += bonus; grantSenseXP = true; } // Ensure we don't go over the maximum. if (rank > maxRank) { rank = maxRank; } if (rank == maxRank || speaker == listener) { // Guaranteed success - return original. return(snippet); } string textAsForeignLanguage = translator.Translate(snippet); if (rank != 0) { int englishChance = (int)(((float)rank / (float)maxRank) * 100); string[] originalSplit = snippet.Split(' '); string[] foreignSplit = textAsForeignLanguage.Split(' '); StringBuilder endResult = new StringBuilder(); // WARNING: We're making the assumption that originalSplit.Length == foreignSplit.Length. // If this assumption changes, the below logic needs to change too. for (int i = 0; i < originalSplit.Length; ++i) { if (RandomService.Random(100) <= englishChance) { endResult.Append(originalSplit[i]); } else { endResult.Append(foreignSplit[i]); } endResult.Append(" "); } textAsForeignLanguage = endResult.ToString(); } long now = DateTime.Now.Ticks; int lastSkillUpLow = listenerAsPlayer.GetLocalInt("LAST_LANGUAGE_SKILL_INCREASE_LOW"); int lastSkillUpHigh = listenerAsPlayer.GetLocalInt("LAST_LANGUAGE_SKILL_INCREASE_HIGH"); long lastSkillUp = lastSkillUpHigh; lastSkillUp = (lastSkillUp << 32) | (uint)lastSkillUpLow; long differenceInSeconds = (now - lastSkillUp) / 10000000; if (differenceInSeconds / 60 >= 2) { int amount = Math.Max(10, Math.Min(150, snippet.Length) / 3); // Reward exp towards the language - we scale this with character count, maxing at 50 exp for 150 characters. SkillService.GiveSkillXP(listenerAsPlayer, language, amount); // Grant Sense XP if player is concentrating Comprehend Speech. if (grantSenseXP) { SkillService.GiveSkillXP(listenerAsPlayer, SkillType.ForceSense, amount * 10); } listenerAsPlayer.SetLocalInt("LAST_LANGUAGE_SKILL_INCREASE_LOW", (int)(now & 0xFFFFFFFF)); listenerAsPlayer.SetLocalInt("LAST_LANGUAGE_SKILL_INCREASE_HIGH", (int)((now >> 32) & 0xFFFFFFFF)); } return(textAsForeignLanguage); }
public static Tuple <ItemProperty, int> GetRandomComponentBonusIP(ResourceQuality quality) { string[] normalIP = { "compbon_arm1", // Armorsmith 1 "compbon_cspd1", // Cooldown Recovery 1 "compbon_charges1", // Charges 1 //"compbon_cooking1", // Cooking 1 "compbon_dmg1", // Damage 1 "compbon_eng1", // Engineering 1 "compbon_enmdown1", // Enmity Down 1 "compbon_harv1", // Harvesting 1 "compbon_wpn1", // Weaponsmith 1 "compbon_hp2", // Hit Points 2 "compbon_fp2", // Force Points 2 "compbon_enmup1", // Enmity Up 1 "compbon_med1", // Meditate 1 "compbon_faid1", // Medicine 1 "compbon_fab1", // Fabrication 1 "compbon_scanup1", // Scanning 1 "compbon_rest1", // Rest 1 "compbon_ab1" // Attack Bonus 1 }; string[] highIP = { "compbon_arm2", // Armorsmith 2 "compbon_cspd2", // Cooldown Recovery 2 "compbon_charges2", // Charges 2 //"compbon_cooking2", // Cooking 2 "compbon_dmg2", // Damage 2 "compbon_eng2", // Engineering 2 "compbon_enmdown2", // Enmity Down 2 "compbon_harv2", // Harvesting 2 "compbon_wpn2", // Weaponsmith 2 "compbon_hp4", // Hit Points 4 "compbon_fp4", // Force Points 4 "compbon_enmup2", // Enmity Up 2 "compbon_luck1", // Luck 1 "compbon_med2", // Meditate 2 "compbon_faid2", // Medicine 2 "compbon_hpregen1", // HP Regen 1 "compbon_fpregen1", // FP Regen 1 "compbon_snkatk1", // Sneak Attack 1 "compbon_fab2", // Fabrication 2 "compbon_scanup2", // Scanning 2 "compbon_rest2", // Rest 2 "compbon_str1", // Strength 1 "compbon_dex1", // Dexterity 1 "compbon_con1", // Constitution 1 "compbon_wis1", // Wisdom 1 "compbon_int1", // Intelligence 1 "compbon_cha1", // Charisma 1 "compbon_ab2", // Attack Bonus 2 "compbon_scavup1" // Scavenging 1 }; string[] veryHighIP = { "compbon_arm3", // Armorsmith 3 "compbon_cspd3", // Cooldown Recovery 3 //"compbon_cooking3", // Cooking 3 "compbon_charges3", // Charges 3 "compbon_dmg3", // Damage 3 "compbon_eng3", // Engineering 3 "compbon_enmdown3", // Enmity Down 3 "compbon_harv3", // Harvesting 3 "compbon_wpn3", // Weaponsmith 3 "compbon_hp6", // Hit Points 6 "compbon_fp6", // Force Points 6 "compbon_enmup3", // Enmity Up 3 "compbon_luck2", // Luck 2 "compbon_med3", // Meditate 3 "compbon_faid3", // Medicine 3 "compbon_hpregen2", // HP Regen 2 "compbon_fpregen2", // FP Regen 2 "compbon_snkatk2", // Sneak Attack 2 "compbon_fab3", // Fabrication 3 "compbon_scanup3", // Scanning 3 "compbon_rest3", // Rest 3 "compbon_str2", // Strength 2 "compbon_dex2", // Dexterity 2 "compbon_con2", // Constitution 2 "compbon_wis2", // Wisdom 2 "compbon_int2", // Intelligence 2 "compbon_cha2", // Charisma 2 "compbon_ab3", // Attack Bonus 3 "compbon_scavup2" // Scavenging 2 }; string[] setToUse; switch (quality) { case ResourceQuality.Low: case ResourceQuality.Normal: setToUse = normalIP; break; case ResourceQuality.High: setToUse = highIP; break; case ResourceQuality.VeryHigh: setToUse = veryHighIP; break; default: throw new ArgumentOutOfRangeException(nameof(quality), quality, null); } int index = RandomService.Random(0, setToUse.Length - 1); string itemTag = setToUse[index]; return(new Tuple <ItemProperty, int>(ItemService.GetCustomItemPropertyByItemTag(itemTag), Colorize(itemTag))); }
public static Tuple <ItemProperty, int> GetRandomComponentBonusIP(ResourceQuality quality) { string[] commonIP = { "compbon_ac1", "compbon_arm1", "compbon_cspd1", "compbon_charges1", "compbon_charges2", "compbon_cooking1", "compbon_dmg1", "compbon_dur1", "compbon_dur2", "compbon_eng1", "compbon_enmdown1", "compbon_epup1", "compbon_epdown1", "compbon_edup1", "compbon_eddown1", "compbon_dpdown1", "compbon_dpup1", "compbon_dddown1", "compbon_ddup1", "compbon_lpdown1", "compbon_lpup1", "compbon_lddown1", "compbon_ldup1", "compbon_mpdown1", "compbon_mpup1", "compbon_mddown1", "compbon_mdup1", "compbon_harv1", "compbon_wpn1", "compbon_hp2", "compbon_fp2", "compbon_enmup1", "compbon_med1", "compbon_faid1", "compbon_fab1", "compbon_scanup1", "compbon_rest1", "compbon_ab1" }; string[] uncommonIP = { "compbon_ac2", "compbon_arm2", "compbon_cspd2", "compbon_charges3", "compbon_cooking2", "compbon_dmg2", "compbon_dur3", "compbon_eng2", "compbon_enmdown2", "compbon_epup2", "compbon_epdown2", "compbon_edup2", "compbon_eddown2", "compbon_dpdown2", "compbon_dpup2", "compbon_dddown2", "compbon_ddup2", "compbon_lpdown2", "compbon_lpup2", "compbon_lddown2", "compbon_ldup2", "compbon_mpdown2", "compbon_mpup2", "compbon_mddown2", "compbon_mdup2", "compbon_harv2", "compbon_wpn2", "compbon_hp4", "compbon_fp4", "compbon_enmup2", "compbon_luck1", "compbon_med2", "compbon_faid2", "compbon_hpregen1", "compbon_fpregen1", "compbon_snkatk1", "compbon_fab2", "compbon_scanup2", "compbon_rest2", "compbon_str1", "compbon_dex1", "compbon_con1", "compbon_wis1", "compbon_int1", "compbon_cha1", "compbon_ab2", "compbon_scavup1" }; string[] rareIP = { "compbon_ac3", "compbon_arm3", "compbon_cspd3", "compbon_cooking3", "compbon_dmg3", "compbon_eng3", "compbon_enmdown3", "compbon_epup3", "compbon_epdown3", "compbon_edup3", "compbon_eddown3", "compbon_dpdown3", "compbon_dpup3", "compbon_dddown3", "compbon_ddup3", "compbon_lpdown3", "compbon_lpup3", "compbon_lddown3", "compbon_ldup3", "compbon_mpdown3", "compbon_mpup3", "compbon_mddown3", "compbon_mdup3", "compbon_harv3", "compbon_wpn3", "compbon_hp6", "compbon_fp6", "compbon_enmup3", "compbon_luck2", "compbon_med3", "compbon_faid3", "compbon_hpregen2", "compbon_fpregen2", "compbon_snkatk2", "compbon_fab3", "compbon_scanup3", "compbon_rest3", "compbon_str2", "compbon_dex2", "compbon_con2", "compbon_wis2", "compbon_int2", "compbon_cha2", "compbon_ab3", "compbon_scavup2" }; string[] ultraRareIP = { "compbon_luck3", "compbon_hpregen3", "compbon_fpregen3", "compbon_snkatk3", "compbon_str3", "compbon_dex3", "compbon_con3", "compbon_wis3", "compbon_int3", "compbon_cha3", "compbon_fpup1", "compbon_faup1", "compbon_fdup1", "compbon_scavup3" }; // Order: Common, Uncommon, Rare, Ultra Rare int[] chance; switch (quality) { case ResourceQuality.Low: chance = new[] { 100, 0, 0, 0 }; break; case ResourceQuality.Normal: chance = new[] { 80, 20, 0, 0 }; break; case ResourceQuality.High: chance = new[] { 20, 40, 40, 0 }; break; case ResourceQuality.VeryHigh: chance = new[] { 0, 0, 70, 30 }; break; default: throw new ArgumentOutOfRangeException(nameof(quality)); } string[] setToUse = null; int index = RandomService.GetRandomWeightedIndex(chance); switch (index) { case 0: setToUse = commonIP; break; case 1: setToUse = uncommonIP; break; case 2: setToUse = rareIP; break; case 3: setToUse = ultraRareIP; break; } if (setToUse == null) { throw new NullReferenceException(nameof(setToUse)); } index = RandomService.Random(0, setToUse.Length - 1); string itemTag = setToUse[index]; return(new Tuple <ItemProperty, int>(ItemService.GetCustomItemPropertyByItemTag(itemTag), index)); }
private static bool ApplySkillDecay(NWPlayer oPC, PCSkill levelingSkill, int xp) { int totalSkillRanks = GetPCTotalSkillCount(oPC); if (totalSkillRanks < SkillCap) { return(true); } // Find out if we have enough XP to remove. If we don't, make no changes and return false signifying no XP could be removed. var pcSkills = DataService.Where <PCSkill>(x => x.PlayerID == oPC.GlobalID && x.SkillID != levelingSkill.SkillID); var totalXPs = pcSkills.Select(s => { var reqXP = DataService.Where <SkillXPRequirement>(x => x.SkillID == s.SkillID && (x.Rank < s.Rank || x.Rank == 0 && s.XP > 0)); var totalXP = reqXP.Sum(x => x.XP); return(new { s.SkillID, TotalSkillXP = totalXP }); }).ToList(); int aggregateXP = 0; foreach (var p in totalXPs) { aggregateXP += p.TotalSkillXP; } if (aggregateXP < xp) { return(false); } // We have enough XP to remove. Reduce XP, picking random skills each time we reduce. var skillsPossibleToDecay = GetAllPCSkills(oPC) .Where(x => { var skill = DataService.Get <Skill>(x.SkillID); return(!x.IsLocked && skill.ContributesToSkillCap && x.SkillID != levelingSkill.SkillID && (x.XP > 0 || x.Rank > 0)); }).ToList(); // There's an edge case where players can be at the cap, but we're unable to find a skill to decay. // In this scenario we can't go any further. Return false which will cause the GiveSkillXP method to // bail out with no changes to XP or decayed skills. if (skillsPossibleToDecay.Count <= 0) { return(false); } while (xp > 0) { int skillIndex = RandomService.Random(skillsPossibleToDecay.Count); PCSkill decaySkill = skillsPossibleToDecay[skillIndex]; int totalDecaySkillXP = totalXPs.Find(x => x.SkillID == decaySkill.SkillID).TotalSkillXP; int oldRank = decaySkill.Rank; if (totalDecaySkillXP >= xp) { totalDecaySkillXP = totalDecaySkillXP - xp; xp = 0; } else if (totalDecaySkillXP < xp) { totalDecaySkillXP = 0; xp = xp - totalDecaySkillXP; } // If skill drops to 0 total XP, remove it from the possible list of skills if (totalDecaySkillXP <= 0) { skillsPossibleToDecay.Remove(decaySkill); decaySkill.XP = 0; decaySkill.Rank = 0; } // Otherwise calculate what rank and XP value the skill should now be. else { // Get the XP amounts required per level, in ascending order, so we can see how many levels we're now meant to have. List <SkillXPRequirement> reqs = DataService.Where <SkillXPRequirement>(x => x.SkillID == decaySkill.SkillID && x.Rank <= decaySkill.Rank).OrderBy(o => o.Rank).ToList(); // The first entry in the database is for rank 0, and if passed, will raise us to 1. So start our count at 0. int newDecaySkillRank = 0; foreach (SkillXPRequirement req in reqs) { if (totalDecaySkillXP >= req.XP) { totalDecaySkillXP = totalDecaySkillXP - req.XP; newDecaySkillRank++; } else if (totalDecaySkillXP < req.XP) { break; } } decaySkill.Rank = newDecaySkillRank; decaySkill.XP = totalDecaySkillXP; } PCSkill dbDecaySkill = new PCSkill { SkillID = decaySkill.SkillID, IsLocked = decaySkill.IsLocked, ID = decaySkill.ID, PlayerID = decaySkill.PlayerID, Rank = decaySkill.Rank, XP = decaySkill.XP }; DataService.SubmitDataChange(dbDecaySkill, DatabaseActionType.Update); MessageHub.Instance.Publish(new SkillDecayedMessage(oPC, decaySkill.SkillID, oldRank, decaySkill.Rank)); } PlayerStatService.ApplyStatChanges(oPC, null); return(true); }
private static void HandleBattlemagePerk() { DamageEventData data = NWNXDamage.GetDamageEventData(); if (data.Base <= 0) { return; } NWObject target = Object.OBJECT_SELF; if (!data.Damager.IsPlayer || !target.IsNPC) { return; } if (_.GetHasFeat((int)CustomFeatType.Battlemage, data.Damager.Object) == FALSE) { return; } NWPlayer player = data.Damager.Object; NWItem weapon = _.GetLastWeaponUsed(player.Object); if (weapon.CustomItemType != CustomItemType.Baton) { return; } if (player.Chest.CustomItemType != CustomItemType.ForceArmor) { return; } int perkRank = PerkService.GetPCPerkLevel(player, PerkType.Battlemage); int restoreAmount = 0; bool metRoll = RandomService.Random(100) + 1 <= 50; switch (perkRank) { case 1 when metRoll: restoreAmount = 1; break; case 2: restoreAmount = 1; break; case 3: restoreAmount = 1; if (metRoll) { restoreAmount++; } break; case 4: restoreAmount = 2; break; case 5: restoreAmount = 2; if (metRoll) { restoreAmount++; } break; case 6: restoreAmount = 3; break; } if (restoreAmount > 0) { AbilityService.RestoreFP(player, restoreAmount); } }
public static void OnChestOpen(NWPlaceable oChest) { NWPlayer oPC = (_.GetLastOpenedBy()); if (!oPC.IsPlayer) { return; } if (_.GetActionMode(oPC.Object, _.ACTION_MODE_STEALTH) == _.TRUE) { _.SetActionMode(oPC.Object, _.ACTION_MODE_STEALTH, _.FALSE); } string resref = oChest.Resref; int chestID = oChest.GetLocalInt(SearchSiteIDVariableName); int skillRank = _.GetSkillRank(_.SKILL_SEARCH, oPC.Object); int numberOfSearches = (skillRank / ExtraSearchPerNumberLevels) + 1; PCSearchSite searchEntity = DataService.SingleOrDefault <PCSearchSite>(x => x.PlayerID == oPC.GlobalID && x.SearchSiteID == chestID); DateTime timeLock = DateTime.UtcNow; if (numberOfSearches <= 0) { numberOfSearches = 1; } if (searchEntity != null) { timeLock = searchEntity.UnlockDateTime; } if (resref == SearchSiteCopyResref) { oChest.IsUseable = false; } QuestService.SpawnQuestItems(oChest, oPC); if (timeLock < DateTime.UtcNow || searchEntity == null) { int dc = oChest.GetLocalInt(SearchSiteDCVariableName); for (int search = 1; search <= numberOfSearches; search++) { RunSearchCycle(oPC, oChest, dc); dc += RandomService.Random(3) + 1; } SaveChestInventory(oPC, oChest, false); } else { var searchItems = DataService.Where <PCSearchSiteItem>(x => x.PlayerID == oPC.GlobalID && x.SearchSiteID == searchEntity.SearchSiteID).ToList(); foreach (PCSearchSiteItem item in searchItems) { NWItem oItem = SerializationService.DeserializeItem(item.SearchItem, oChest); // Prevent item duplication in containers if (oItem.HasInventory) { foreach (NWItem containerItem in oItem.InventoryItems) { containerItem.Destroy(); } } } } }