public static string TestLootGen(int numItems, int tier, bool logstats, string displaytable) { string displayHeader = $"\n LootFactory Simulator - Items\n ---------------------\n"; Console.WriteLine($"Creating {numItems} items, that are in tier {tier}"); var ls = new LootStats(logstats); // Create a dummy treasure profile for passing in tier value TreasureDeath profile = new TreasureDeath { Tier = tier, LootQualityMod = 0 }; // Loop depending on how many items you are creating for (int i = 0; i < numItems; i++) { var testItem = LootGenerationFactory.CreateRandomLootObjects(profile, true); ls = LootStats(testItem, ls, logstats); } Console.WriteLine(displayHeader); Console.WriteLine(DisplayStats(ls, displaytable)); displayHeader += $" A total of {ls.TotalItems} items were generated in Tier {tier}. \n"; if (logstats) { string myfilename = string.Format("LootSim-{0:hh-mm-ss-tt_MM-dd-yyyy}.csv", DateTime.Now); File.WriteAllText(myfilename, displayHeader + DisplayStats(ls, displaytable)); } return(displayHeader); }
private static bool AssignArmorLevel_New(WorldObject wo, TreasureDeath profile, TreasureRoll roll) { // retail was only divied up into a few different mutation scripts here // anything with ArmorLevel ran these mutation scripts // anything that covered extremities (head / hand / foot wear) started with a slightly higher base AL, // but otherwise used the same mutation as anything that covered non-extremities // shields also had their own mutation script // only exceptions found: covenant armor, olthoi armor, metal cap if (!roll.HasArmorLevel(wo)) { return(false); } var scriptName = GetMutationScript_ArmorLevel(wo, roll); if (scriptName == null) { log.Error($"AssignArmorLevel_New({wo.Name}, {profile.TreasureType}, {roll.ItemType}) - unknown item type"); return(false); } //Console.WriteLine($"Mutating {wo.Name} with {scriptName}"); var mutationFilter = MutationCache.GetMutation(scriptName); return(mutationFilter.TryMutate(wo, profile.Tier)); }
private static void MutateCaster_SpellDID(WorldObject wo, TreasureDeath profile) { var firstSpell = CasterSlotSpells.Roll(wo); var spellLevels = SpellLevelProgression.GetSpellLevels(firstSpell); if (spellLevels == null) { log.Error($"MutateCaster_SpellDID: couldn't find {firstSpell}"); return; } if (spellLevels.Count != 8) { log.Error($"MutateCaster_SpellDID: found {spellLevels.Count} spell levels for {firstSpell}, expected 8"); return; } int minSpellLevel = GetLowSpellTier(profile.Tier); int maxSpellLevel = GetHighSpellTier(profile.Tier); var spellLevel = ThreadSafeRandom.Next(minSpellLevel, maxSpellLevel); wo.SpellDID = (uint)spellLevels[spellLevel - 1]; var spell = new Server.Entity.Spell(wo.SpellDID.Value); wo.ItemManaCost = (int)spell.BaseMana * 5; wo.ItemUseable = Usable.SourceWieldedTargetRemoteNeverWalk; }
private static void MutateCaster_SpellDID(WorldObject wo, TreasureDeath profile) { var firstSpell = CasterSlotSpells.Roll(wo); var spellLevels = SpellLevelProgression.GetSpellLevels(firstSpell); if (spellLevels == null) { log.Error($"MutateCaster_SpellDID: couldn't find {firstSpell}"); return; } if (spellLevels.Count != 8) { log.Error($"MutateCaster_SpellDID: found {spellLevels.Count} spell levels for {firstSpell}, expected 8"); return; } var spellLevel = SpellLevelChance.Roll(profile.Tier); wo.SpellDID = (uint)spellLevels[spellLevel - 1]; var spell = new Server.Entity.Spell(wo.SpellDID.Value); var castableMod = CasterSlotSpells.IsOrb(wo) ? 5.0f : 2.5f; wo.ItemManaCost = (int)(spell.BaseMana * castableMod); wo.ItemUseable = Usable.SourceWieldedTargetRemoteNeverWalk; }
private static WorldObject CreateJewelry(TreasureDeath profile, bool isMagical, bool mutate = true) { // 31% chance ring, 31% chance bracelet, 30% chance necklace 8% chance Trinket int jewelrySlot = ThreadSafeRandom.Next(1, 100); int jewelType; // Made this easier to read (switch -> if statement) if (jewelrySlot <= 31) { jewelType = LootTables.ringItems[ThreadSafeRandom.Next(0, LootTables.ringItems.Length - 1)]; } else if (jewelrySlot <= 62) { jewelType = LootTables.braceletItems[ThreadSafeRandom.Next(0, LootTables.braceletItems.Length - 1)]; } else if (jewelrySlot <= 92) { jewelType = LootTables.necklaceItems[ThreadSafeRandom.Next(0, LootTables.necklaceItems.Length - 1)]; } else { jewelType = LootTables.trinketItems[ThreadSafeRandom.Next(0, LootTables.trinketItems.Length - 1)]; } WorldObject wo = WorldObjectFactory.CreateNewWorldObject((uint)jewelType); if (wo != null && mutate) { MutateJewelry(wo, profile, isMagical); } return(wo); }
private static WorldObject TryRollAetheria(TreasureDeath profile) { var aetheria_drop_rate = (float)PropertyManager.GetDouble("aetheria_drop_rate").Item; if (aetheria_drop_rate <= 0.0f) { return(null); } var dropRateMod = 1.0f / aetheria_drop_rate; // 2% base chance in here, which turns out to be less per corpse w/ MundaneItemChance > 0, // when the outer MundaneItemChance roll is factored in // loot quality mod? var rng = ThreadSafeRandom.Next(0.0f, 1.0f * dropRateMod); if (rng < 0.02f) { return(CreateAetheria_New(profile)); } else { return(null); } }
/// <summary> /// Returns a quality level between 0-12 /// </summary> public static int Roll(TreasureDeath treasureDeath, bool isArmorModVsType = false) { // roll for the initial chance for any quality modification -- based on tier if (!RollTierChance(treasureDeath, isArmorModVsType)) { return(0); } // if the initial roll succeeds, roll for the actual quality level -- also based on tier var chances = GetQualityChancesForTier(treasureDeath.Tier); var rng = ThreadSafeRandom.Next(treasureDeath.LootQualityMod, 1.0f); for (var i = 0; i < chances.Count; i++) { var curChance = chances[i]; if (rng < curChance) { return(i + 1); } } log.Error($"QualityTables.Roll({treasureDeath.Tier}, {treasureDeath.LootQualityMod}, {isArmorModVsType}) - this shouldn't happen"); return(0); }
private static bool AssignMagic_Gem_New(WorldObject wo, TreasureDeath profile, TreasureRoll roll) { // TODO: move to standard AssignMagic() pipeline var spell = SpellSelectionTable.Roll(1); var spellLevel = SpellLevelChance.Roll(profile.Tier); var spellLevels = SpellLevelProgression.GetSpellLevels(spell); if (spellLevels == null || spellLevels.Count != 8) { log.Error($"AssignMagic_Gem_New({wo.Name}, {profile.TreasureType}, {roll.ItemType}) - unknown spell {spell}"); return(false); } var finalSpellId = spellLevels[spellLevel - 1]; wo.SpellDID = (uint)finalSpellId; var _spell = new Server.Entity.Spell(finalSpellId); // retail spellcraft was capped at 370 wo.ItemSpellcraft = Math.Min((int)_spell.Power, 370); var castableMana = (int)_spell.BaseMana * 5; wo.ItemMaxMana = RollItemMaxMana_New(wo, roll, castableMana); wo.ItemCurMana = wo.ItemMaxMana; // verified wo.ItemManaCost = castableMana; return(true); }
/// <summary> /// Creates and optionally mutates a new MissileWeapon /// </summary> public static WorldObject CreateMissileWeapon(TreasureDeath profile, bool isMagical, bool mutate = true) { int weaponWeenie; int wieldDifficulty = RollWieldDifficulty(profile.Tier, TreasureWeaponType.MissileWeapon); // Changing based on wield, not tier. Refactored, less code, best results. HarliQ 11/18/19 if (wieldDifficulty < 315) { weaponWeenie = GetNonElementalMissileWeapon(); } else { weaponWeenie = GetElementalMissileWeapon(); } WorldObject wo = WorldObjectFactory.CreateNewWorldObject((uint)weaponWeenie); if (wo != null && mutate) { MutateMissileWeapon(wo, profile, isMagical, wieldDifficulty); } return(wo); }
/// <summary> /// Performs a weighted RNG roll /// linearly interpolates between discrete values /// </summary> /// <returns>A quality level between 0.0f - 1.0f, higher values = better</returns> public static float RollInterval(TreasureDeath treasureDeath) { // roll for the initial chance for any quality modification -- based on tier if (!RollTierChance(treasureDeath)) { return(0.0f); } // if the initial roll succeeds, roll for the actual quality level -- also based on tier var chances = GetQualityChancesForTier(treasureDeath.Tier); var rng = ThreadSafeRandom.Next(treasureDeath.LootQualityMod, 1.0f); for (var i = 0; i < chances.Count; i++) { var curChance = chances[i]; if (rng < curChance) { var prevChance = i > 0 ? chances[i - 1] : 0; var dx = curChance - prevChance; var dy = 1.0f / chances.Count; var interval = (rng - prevChance) / dx; return((float)(dy * (interval + i))); } } log.Error($"QualityTables.RollInterval({treasureDeath.Tier}, {treasureDeath.LootQualityMod}) - this shouldn't happen"); return(0.0f); }
private static List <SpellId> RollItemSpells(WorldObject wo, TreasureDeath profile, TreasureRoll roll) { List <SpellId> spells = null; //if (roll.IsArmor || roll.IsArmorClothing(wo)) if (roll.HasArmorLevel(wo)) { spells = ArmorSpells.Roll(profile); } else if (roll.IsMeleeWeapon) { spells = MeleeSpells.Roll(profile); } else if (roll.IsMissileWeapon) { spells = MissileSpells.Roll(profile); } else if (roll.IsCaster) { spells = WandSpells.Roll(wo, profile); } else { log.Error($"RollItemSpells({wo.Name}) - item is not clothing / armor / weapon"); return(null); } return(RollSpellLevels(wo, profile, spells)); }
public void CreateSQLINSERTStatement(TreasureDeath input, StreamWriter writer) { writer.WriteLine("INSERT INTO `treasure_death` (`treasure_Type`, `tier`, `loot_Quality_Mod`, `unknown_Chances`, `item_Chance`, `item_Min_Amount`, `item_Max_Amount`, `item_Treasure_Type_Selection_Chances`, `magic_Item_Chance`, `magic_Item_Min_Amount`, `magic_Item_Max_Amount`, `magic_Item_Treasure_Type_Selection_Chances`, `mundane_Item_Chance`, `mundane_Item_Min_Amount`, `mundane_Item_Max_Amount`, `mundane_Item_Type_Selection_Chances`)"); var output = "VALUES (" + $"{input.TreasureType}, " + $"{input.Tier}, " + $"{input.LootQualityMod}, " + $"{input.UnknownChances}, " + $"{input.ItemChance}, " + $"{input.ItemMinAmount}, " + $"{input.ItemMaxAmount}, " + $"{input.ItemTreasureTypeSelectionChances}, " + $"{input.MagicItemChance}, " + $"{input.MagicItemMinAmount}, " + $"{input.MagicItemMaxAmount}, " + $"{input.MagicItemTreasureTypeSelectionChances}, " + $"{input.MundaneItemChance}, " + $"{input.MundaneItemMinAmount}, " + $"{input.MundaneItemMaxAmount}, " + $"{input.MundaneItemTypeSelectionChances}" + ");"; output = FixNullFields(output); writer.WriteLine(output); }
private static void AssignMagic(WorldObject wo, TreasureDeath profile, TreasureRoll roll, bool isArmor = false) { int numSpells = 0; if (roll == null) { // previous method if (!AssignMagic_Spells(wo, profile, isArmor, out numSpells)) { return; } } else { // new method if (!AssignMagic_New(wo, profile, roll, out numSpells)) { return; } } wo.UiEffects = UiEffects.Magical; var maxBaseMana = GetMaxBaseMana(wo); wo.ManaRate = CalculateManaRate(maxBaseMana); if (roll == null) { wo.ItemMaxMana = RollItemMaxMana(numSpells, profile.Tier); wo.ItemCurMana = wo.ItemMaxMana; wo.ItemSpellcraft = RollSpellcraft(wo); wo.ItemDifficulty = RollItemDifficulty(wo); } else { var maxSpellMana = maxBaseMana; if (wo.SpellDID != null) { var spell = new Server.Entity.Spell(wo.SpellDID.Value); var castableMana = (int)spell.BaseMana * 5; if (castableMana > maxSpellMana) { maxSpellMana = castableMana; } } wo.ItemMaxMana = RollItemMaxMana_New(wo, roll, maxSpellMana); wo.ItemCurMana = wo.ItemMaxMana; wo.ItemSpellcraft = RollSpellcraft(wo, roll); AddActivationRequirements(wo, roll); } }
/// <summary> /// Rolls for the initial chance of getting a quality bonus for an item /// </summary> /// <param name="treasureDeath">The chances are based on treasureDeath.Tier, and can be increased with treasureDeath.LootQualityMod</param> private static bool RollTierChance(TreasureDeath treasureDeath) { var tierChance = QualityChancePerTier[treasureDeath.Tier - 1]; // use for initial roll? logic seems backwards here... var rng = ThreadSafeRandom.NextInterval(treasureDeath.LootQualityMod); return(rng < tierChance); }
private static int GetSpellDistribution(TreasureDeath profile, out int numMinors, out int numMajors, out int numEpics, out int numLegendaries) { int numNonCantrips = 0; numMinors = 0; numMajors = 0; numEpics = 0; numLegendaries = 0; int nonCantripChance = ThreadSafeRandom.Next(1, 100000); numMinors = GetNumMinorCantrips(profile); // All tiers have a chance for at least one minor cantrip numMajors = GetNumMajorCantrips(profile); numEpics = GetNumEpicCantrips(profile); numLegendaries = GetNumLegendaryCantrips(profile); // Fixing the absurd amount of spells on items - HQ 6/21/2020 // From Mags Data all tiers have about the same chance for a given number of spells on items. This is the ratio for magical items. // 1 Spell(s) - 46.410 % // 2 Spell(s) - 27.040 % // 3 Spell(s) - 17.850 % // 4 Spell(s) - 6.875 % // 5 Spell(s) - 1.525 % // 6 Spell(s) - 0.235 % // 7 Spell(s) - 0.065 % if (nonCantripChance <= 46410) { numNonCantrips = 1; } else if (nonCantripChance <= 73450) { numNonCantrips = 2; } else if (nonCantripChance <= 91300) { numNonCantrips = 3; } else if (nonCantripChance <= 98175) { numNonCantrips = 4; } else if (nonCantripChance <= 99700) { numNonCantrips = 5; } else if (nonCantripChance <= 99935) { numNonCantrips = 6; } else { numNonCantrips = 7; } return(numNonCantrips + numMinors + numMajors + numEpics + numLegendaries); }
/// <summary> /// Default is formed from: input.TreasureType.ToString("00000") /// </summary> public string GetDefaultFileName(TreasureDeath input) { string fileName = input.TreasureType.ToString("00000"); fileName = IllegalInFileName.Replace(fileName, "_"); fileName += ".sql"; return(fileName); }
/// <summary> /// Creates a Melee weapon object. /// </summary> /// <param name="profile"></param><param name="isMagical"></param> /// <returns>Returns Melee Weapon WO</returns> public static WorldObject CreateMeleeWeapon(TreasureDeath profile, bool isMagical, int weaponType = -1, bool mutate = true) { int weaponWeenie = 0; int subtype = 0; int eleType = ThreadSafeRandom.Next(0, 4); if (weaponType == -1) { weaponType = ThreadSafeRandom.Next(0, 3); } // Weapon Types // 0 = Heavy // 1 = Light // 2 = Finesse // default = Two Handed switch (weaponType) { case 0: // Heavy Weapons subtype = ThreadSafeRandom.Next(0, LootTables.HeavyWeaponsMatrix.Length - 1); weaponWeenie = LootTables.HeavyWeaponsMatrix[subtype][eleType]; break; case 1: // Light Weapons subtype = ThreadSafeRandom.Next(0, LootTables.LightWeaponsMatrix.Length - 1); weaponWeenie = LootTables.LightWeaponsMatrix[subtype][eleType]; break; case 2: // Finesse Weapons; subtype = ThreadSafeRandom.Next(0, LootTables.FinesseWeaponsMatrix.Length - 1); weaponWeenie = LootTables.FinesseWeaponsMatrix[subtype][eleType]; break; default: // Two handed subtype = ThreadSafeRandom.Next(0, LootTables.TwoHandedWeaponsMatrix.Length - 1); weaponWeenie = LootTables.TwoHandedWeaponsMatrix[subtype][eleType]; break; } var wo = WorldObjectFactory.CreateNewWorldObject((uint)weaponWeenie); if (wo != null && mutate) { if (!MutateMeleeWeapon(wo, profile, isMagical)) { log.Warn($"[LOOT] Missing needed melee weapon properties on loot item {wo.WeenieClassId} - {wo.Name} for mutations"); return(null); } } return(wo); }
/// <summary> /// Rolls for the initial chance of getting a quality bonus for an item /// </summary> /// <param name="treasureDeath">The chances are based on treasureDeath.Tier, and can be increased with treasureDeath.LootQualityMod</param> /// <param name="isArmorModVsType">ArmorModVsType has a separate chance table</param> private static bool RollTierChance(TreasureDeath treasureDeath, bool isArmorModVsType = false) { var tierChance = isArmorModVsType ? QualityChancePerTier_ArmorModVsType[treasureDeath.Tier - 1] : QualityChancePerTier[treasureDeath.Tier - 1]; // use for initial roll? logic seems backwards here... var rng = ThreadSafeRandom.Next(0.0f, 1.0f) - treasureDeath.LootQualityMod; return(rng < tierChance); }
private static void MutateSocietyArmor(WorldObject wo, TreasureDeath profile, bool isMagical) { int materialType = GetMaterialType(wo, profile.Tier); if (materialType > 0) { wo.MaterialType = (MaterialType)materialType; } if (wo.GemCode != null) { wo.GemCount = GemCountChance.Roll(wo.GemCode.Value, profile.Tier); } else { wo.GemCount = ThreadSafeRandom.Next(1, 6); } wo.GemType = RollGemType(profile.Tier); int workmanship = GetWorkmanship(profile.Tier); wo.ItemWorkmanship = workmanship; double materialMod = LootTables.getMaterialValueModifier(wo); double gemMaterialMod = LootTables.getGemMaterialValueModifier(wo); var value = GetValue(profile.Tier, workmanship, gemMaterialMod, materialMod); wo.Value = value; // wo.WieldSkillType = (int)Skill.Axe; // Set by examples from PCAP data if (isMagical) { // looks like society armor always had impen on it AssignMagic(wo, profile, true); } else { wo.ItemManaCost = null; wo.ItemMaxMana = null; wo.ItemCurMana = null; wo.ItemSpellcraft = null; wo.ItemDifficulty = null; } wo = AssignArmorLevel(wo, profile.Tier, LootTables.ArmorType.SocietyArmor); wo.LongDesc = GetLongDesc(wo); // try mutate burden, if MutateFilter exists if (wo.HasMutateFilter(MutateFilter.EncumbranceVal)) { MutateBurden(wo, profile, false); } }
private static void MutateJewelry(WorldObject wo, TreasureDeath profile, bool isMagical) { wo.AppraisalLongDescDecoration = AppraisalLongDescDecorations.PrependWorkmanship; wo.LongDesc = wo.Name; int materialType = GetMaterialType(wo, profile.Tier); if (materialType > 0) { wo.MaterialType = (MaterialType)materialType; } int gemCount = ThreadSafeRandom.Next(1, 5); int gemType = ThreadSafeRandom.Next(10, 50); wo.GemCount = gemCount; wo.GemType = (MaterialType)gemType; int workmanship = GetWorkmanship(profile.Tier); double materialMod = LootTables.getMaterialValueModifier(wo); double gemMaterialMod = LootTables.getGemMaterialValueModifier(wo); var value = GetValue(profile.Tier, workmanship, gemMaterialMod, materialMod); wo.Value = value; wo.ItemWorkmanship = workmanship; wo.ItemSkillLevelLimit = null; if (profile.Tier > 6) { wo.WieldRequirements = WieldRequirement.Level; wo.WieldSkillType = (int)Skill.Axe; // Set by examples from PCAP data var wield = profile.Tier switch { 7 => 150, // In this instance, used for indicating player level, rather than skill level _ => 180, // In this instance, used for indicating player level, rather than skill level }; wo.WieldDifficulty = wield; } if (isMagical) { wo = AssignMagic(wo, profile); } else { wo.ItemManaCost = null; wo.ItemMaxMana = null; wo.ItemCurMana = null; wo.ItemSpellcraft = null; wo.ItemDifficulty = null; wo.ManaRate = null; } RandomizeColor(wo); }
private static bool AssignMagic_New(WorldObject wo, TreasureDeath profile, TreasureRoll roll, out int numSpells) { var spells = RollSpells(wo, profile, roll); foreach (var spell in spells) { wo.Biota.GetOrAddKnownSpell((int)spell, wo.BiotaDatabaseLock, out _); } numSpells = spells.Count; return(true); }
/// <summary> /// Creates a Melee weapon object. /// </summary> /// <param name="profile"></param><param name="isMagical"></param> /// <returns>Returns Melee Weapon WO</returns> public static WorldObject CreateMeleeWeapon(TreasureDeath profile, bool isMagical, int weaponType = -1, bool mutate = true) { int weaponWeenie = 0; int subtype = 0; int eleType = ThreadSafeRandom.Next(0, 4); if (weaponType == -1) { weaponType = ThreadSafeRandom.Next(0, 3); } // Weapon Types // 0 = Heavy // 1 = Light // 2 = Finesse // default = Two Handed switch (weaponType) { case 0: // Heavy Weapons subtype = ThreadSafeRandom.Next(0, 22); weaponWeenie = LootTables.HeavyWeaponsMatrix[subtype][eleType]; break; case 1: // Light Weapons subtype = ThreadSafeRandom.Next(0, 19); weaponWeenie = LootTables.LightWeaponsMatrix[subtype][eleType]; break; case 2: // Finesse Weapons; subtype = ThreadSafeRandom.Next(0, 22); weaponWeenie = LootTables.FinesseWeaponsMatrix[subtype][eleType]; break; default: // Two handed subtype = ThreadSafeRandom.Next(0, 11); weaponWeenie = LootTables.TwoHandedWeaponsMatrix[subtype][eleType]; break; } var wo = WorldObjectFactory.CreateNewWorldObject((uint)weaponWeenie); if (wo != null && mutate) { MutateMeleeWeapon(wo, profile, isMagical, weaponType, subtype); } return(wo); }
private static WorldObject CreateAetheria_New(TreasureDeath profile, bool mutate = true) { var wcid = AetheriaWcids.Roll(profile.Tier); var wo = WorldObjectFactory.CreateNewWorldObject((uint)wcid); if (mutate) { MutateAetheria_New(wo, profile); } return(wo); }
public static WeenieClassName Roll(TreasureDeath treasureDeath, TreasureWeaponType weaponType) { switch (weaponType) { /*case TreasureWeaponType.Sword: * return RollSwordWcid(treasureDeath); * * case TreasureWeaponType.Mace: * return RollMaceWcid(treasureDeath); * * case TreasureWeaponType.Axe: * return RollAxeWcid(treasureDeath); * * case TreasureWeaponType.Spear: * return RollSpearWcid(treasureDeath); * * case TreasureWeaponType.Unarmed: * return RollUnarmedWcid(treasureDeath); * * case TreasureWeaponType.Staff: * return RollStaffWcid(treasureDeath); * * case TreasureWeaponType.Dagger: * return RollDaggerWcid(treasureDeath);*/ case TreasureWeaponType.Sword: case TreasureWeaponType.Mace: case TreasureWeaponType.Axe: case TreasureWeaponType.Spear: case TreasureWeaponType.Unarmed: case TreasureWeaponType.Staff: case TreasureWeaponType.Dagger: return(RollMeleeWeapon()); case TreasureWeaponType.Bow: return(RollBowWcid(treasureDeath)); case TreasureWeaponType.Crossbow: return(RollCrossbowWcid(treasureDeath)); case TreasureWeaponType.Atlatl: return(RollAtlatlWcid(treasureDeath)); case TreasureWeaponType.Caster: return(RollCaster(treasureDeath)); case TreasureWeaponType.TwoHandedWeapon: return(RollTwoHandedWeaponWcid()); } return(WeenieClassName.undef); }
/// <summary> /// Creates and optionally mutates a new MeleeWeapon /// </summary> public static WorldObject CreateMeleeWeapon(TreasureDeath profile, bool isMagical, MeleeWeaponSkill weaponSkill = MeleeWeaponSkill.Undef, bool mutate = true) { var wcid = 0; var weaponType = 0; var eleType = ThreadSafeRandom.Next(0, 4); if (weaponSkill == MeleeWeaponSkill.Undef) { weaponSkill = (MeleeWeaponSkill)ThreadSafeRandom.Next(1, 4); } switch (weaponSkill) { case MeleeWeaponSkill.HeavyWeapons: weaponType = ThreadSafeRandom.Next(0, LootTables.HeavyWeaponsMatrix.Length - 1); wcid = LootTables.HeavyWeaponsMatrix[weaponType][eleType]; break; case MeleeWeaponSkill.LightWeapons: weaponType = ThreadSafeRandom.Next(0, LootTables.LightWeaponsMatrix.Length - 1); wcid = LootTables.LightWeaponsMatrix[weaponType][eleType]; break; case MeleeWeaponSkill.FinesseWeapons: weaponType = ThreadSafeRandom.Next(0, LootTables.FinesseWeaponsMatrix.Length - 1); wcid = LootTables.FinesseWeaponsMatrix[weaponType][eleType]; break; case MeleeWeaponSkill.TwoHandedCombat: weaponType = ThreadSafeRandom.Next(0, LootTables.TwoHandedWeaponsMatrix.Length - 1); wcid = LootTables.TwoHandedWeaponsMatrix[weaponType][eleType]; break; } var wo = WorldObjectFactory.CreateNewWorldObject((uint)wcid); if (wo != null && mutate) { if (!MutateMeleeWeapon(wo, profile, isMagical)) { log.Warn($"[LOOT] {wo.WeenieClassId} - {wo.Name} is not a MeleeWeapon"); return(null); } } return(wo); }
private static int GetNumEpicCantrips(TreasureDeath profile) { int numEpics = 0; if (profile.Tier < 7) { return(0); } var dropRate = PropertyManager.GetDouble("epic_cantrip_drop_rate").Item; if (dropRate <= 0) { return(0); } var dropRateMod = 1.0 / dropRate; double lootQualityMod = 1.0f; if (PropertyManager.GetBool("loot_quality_mod").Item&& profile.LootQualityMod > 0 && profile.LootQualityMod < 1) { lootQualityMod = 1.0f - profile.LootQualityMod; } // 25% base chance for no epics for tier 7 if (ThreadSafeRandom.Next(1, 4) > 1) { // 1% chance for 1 Epic, 0.1% chance for 2 Epics, // 0.01% chance for 3 Epics, 0.001% chance for 4 Epics if (ThreadSafeRandom.Next(1, (int)(100 * dropRateMod * lootQualityMod)) == 1) { numEpics = 1; } if (ThreadSafeRandom.Next(1, (int)(1000 * dropRateMod * lootQualityMod)) == 1) { numEpics = 2; } if (ThreadSafeRandom.Next(1, (int)(10000 * dropRateMod * lootQualityMod)) == 1) { numEpics = 3; } if (ThreadSafeRandom.Next(1, (int)(100000 * dropRateMod * lootQualityMod)) == 1) { numEpics = 4; } } return(numEpics); }
public static WeenieClassName Roll(TreasureDeath profile) { // roll for pet level var petLevel = PetDeviceChance.Roll(profile); // even chance of rolling each pet device type var rng = ThreadSafeRandom.Next(0, petDevices.Count - 1); var table = petDevices[rng]; var petLevelIdx = petLevelIndexes[petLevel]; return(table[petLevelIdx]); }
private static WorldObject TryRollMundaneAddon(TreasureDeath profile) { // coalesced mana only dropped in tiers 1-4 if (profile.Tier <= 4) { return(TryRollCoalescedMana(profile)); } // aetheria dropped in tiers 5+ else { return(TryRollAetheria(profile)); } }
private static List <SpellId> RollCantrips(WorldObject wo, TreasureDeath profile, TreasureRoll roll) { // no cantrips on dinnerware? if (roll.ItemType == TreasureItemType_Orig.ArtObject) { return(null); } var numCantrips = CantripChance.RollNumCantrips(profile); if (numCantrips == 0) { return(null); } var numAttempts = numCantrips * 3; var cantrips = new HashSet <SpellId>(); for (var i = 0; i < numAttempts && cantrips.Count < numCantrips; i++) { var cantrip = RollCantrip(wo, profile, roll); cantrip = AdjustForWeaponMastery(wo, cantrip); if (cantrip != SpellId.Undef) { cantrips.Add(cantrip); } } var finalCantrips = new List <SpellId>(); foreach (var cantrip in cantrips) { var cantripLevel = CantripChance.RollCantripLevel(profile); var cantripLevels = SpellLevelProgression.GetSpellLevels(cantrip); if (cantripLevels.Count != 4) { log.Error($"RollCantrips({wo.Name}, {profile.TreasureType}, {roll.ItemType}) - {cantrip} has {cantripLevels.Count} cantrip levels, expected 4"); continue; } finalCantrips.Add(cantripLevels[cantripLevel - 1]); } return(finalCantrips); }
private static SpellId RollCantrip(WorldObject wo, TreasureDeath profile, TreasureRoll roll) { if (roll.HasArmorLevel(wo) || roll.IsClothing) { // armor / clothing cantrip // this table also applies to crowns (treasureitemtype.jewelry w/ al) return(ArmorCantrips.Roll()); } if (roll.IsMeleeWeapon) { // melee cantrip var meleeCantrip = MeleeCantrips.Roll(); // adjust for weapon skill if (meleeCantrip == SpellId.CANTRIPLIGHTWEAPONSAPTITUDE1) { meleeCantrip = AdjustForWeaponMastery(wo); } return(meleeCantrip); } else if (roll.IsMissileWeapon) { // missile cantrip return(MissileCantrips.Roll()); } else if (roll.IsCaster) { // caster cantrip var casterCantrip = WandCantrips.Roll(); if (casterCantrip == SpellId.CANTRIPWARMAGICAPTITUDE1) { casterCantrip = AdjustForDamageType(wo, casterCantrip); } return(casterCantrip); } else if (roll.IsJewelry) { // jewelry cantrip return(JewelryCantrips.Roll()); } else { log.Error($"RollCantrip({wo.Name}, {profile.TreasureType}, {roll.ItemType}) - unknown item type"); return(SpellId.Undef); } }