Advertisement
Krythic

Voidwalker LootGenerator 8/31/2021

Aug 31st, 2021
1,596
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 20.22 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using VoidwalkerEngine.Framework.Collections;
  5. using VoidwalkerEngine.Framework.DataTypes;
  6. using VoidwalkerEngine.Framework.Game.Systems.GameItems;
  7. using VoidwalkerEngine.Framework.Game.Systems.GameItems.Data;
  8. using VoidwalkerEngine.Framework.Game.Systems.GameItems.Enchanting;
  9. using VoidwalkerEngine.Framework.Game.Systems.Loot;
  10. using VoidwalkerEngine.Framework.Logic;
  11. using VoidwalkerEngine.Framework.Maths;
  12. using VoidwalkerEngine.Framework.Utilities;
  13.  
  14. namespace VoidwalkerEngine.Framework.Algorithms
  15. {
  16.     public partial class LootGenerator
  17.     {
  18.         private VoidwalkerRandom _random;
  19.         private List<UniqueEquipmentTemplate> _uniqueTemplates { get; set; }
  20.         private List<Enchantment> _enchantments { get; set; }
  21.         private GameDatabase _database;
  22.  
  23.         private Dictionary<Range, List<Affix>> _affixesByLevelBracket;
  24.  
  25.         static LootGenerator()
  26.         {
  27.             InitializeRareNameSuffixes();
  28.         }
  29.  
  30.         public LootGenerator(GameDatabase database)
  31.         {
  32.             this._database = database;
  33.             /**
  34.              * Filter Affixes by level bracket
  35.              */
  36.             _affixesByLevelBracket = new Dictionary<Range, List<Affix>>();
  37.             foreach (Affix affix in _database.AffixTemplates)
  38.             {
  39.                 if (_affixesByLevelBracket.ContainsKey(affix.LevelBracket))
  40.                 {
  41.                     _affixesByLevelBracket[affix.LevelBracket].Add(affix);
  42.                 }
  43.                 else
  44.                 {
  45.                     _affixesByLevelBracket.Add(affix.LevelBracket, new List<Affix>());
  46.                     _affixesByLevelBracket[affix.LevelBracket].Add(affix);
  47.                 }
  48.             }
  49.             /**
  50.              * Init
  51.              */
  52.             _enchantments = new List<Enchantment>();
  53.             _random = new VoidwalkerRandom();
  54.             if (_database.Enchantments != null)
  55.             {
  56.                 foreach (Enchantment enchantment in _database.Enchantments)
  57.                 {
  58.                     if (enchantment.IsSpawnable)
  59.                     {
  60.                         this._enchantments.Add(enchantment);
  61.                     }
  62.                 }
  63.                 this._uniqueTemplates = _database.UniqueGameItemTemplates;
  64.             }
  65.         }
  66.  
  67.         #region Process LootTables
  68.         public List<GameItemTemplate> Process(int level, List<LootTableDrop> items)
  69.         {
  70.             //if (items == null || items.Count == 0)
  71.             //{
  72.             //    return null;
  73.             //}
  74.             //List<GameItem> results = new List<GameItem>();
  75.  
  76.             //foreach (LootTableDrop drop in items)
  77.             //{
  78.             //    int quantity = _random.NextRange(drop.Quantity);
  79.             //    if (quantity > 0)
  80.             //    {
  81.             //        if (drop.Item.ItemType.IsGeneratable())
  82.             //        {
  83.             //            /**
  84.             //             * Roll Enchantments for each item in quantity.
  85.             //             * This allows for multiple items to have different
  86.             //             * enchantments, and qualities.
  87.             //             */
  88.             //            for (int i = 0; i < quantity; i++)
  89.             //            {
  90.             //                GameItem item = Generate(drop.Item, drop.LootArgs, level);
  91.             //                results.Add(item);
  92.             //            }
  93.             //        }
  94.             //        else
  95.             //        {
  96.             //            List<GameItem> existingStacks = new List<GameItem>();
  97.             //            foreach (GameItem item in results)
  98.             //            {
  99.             //                if (item.IsStackableWith(drop.Item))
  100.             //                {
  101.             //                    existingStacks.Add(item);
  102.             //                }
  103.             //            }
  104.             //            if (existingStacks.Count == 0)
  105.             //            {
  106.             //                GameItem copy = new GameItem();
  107.             //                copy.Copy(drop.Item);
  108.             //                existingStacks.Add(copy);
  109.             //            }
  110.             //            int currentStackIndex = 0;
  111.             //            GameItem stackable = existingStacks[currentStackIndex];
  112.             //            for (int i = 1; i < quantity; i++)
  113.             //            {
  114.             //                int totalQuantity = stackable.Quantity + 1;
  115.             //                if (totalQuantity <= stackable.StackSize)
  116.             //                {
  117.             //                    stackable.Quantity++;
  118.             //                }
  119.             //                else
  120.             //                {
  121.             //                    currentStackIndex++;
  122.             //                    GameItem copy = new GameItem();
  123.             //                    copy.Copy(drop.Item);
  124.             //                    existingStacks.Add(copy);
  125.             //                }
  126.             //            }
  127.             //            results.RemoveAll(p => p.Identifier.Equals(drop.Item.Identifier));
  128.             //            results.AddRange(existingStacks);
  129.             //        }
  130.             //    }
  131.             //}
  132.             //return results;
  133.             return null;
  134.         }
  135.         #endregion
  136.  
  137.         public void RollAffixCounts(QualityType quality, bool isEthereal, out int prefixes, out int suffixes)
  138.         {
  139.             switch (quality)
  140.             {
  141.                 case QualityType.Uncommon:
  142.                     if (isEthereal)
  143.                     {
  144.                         prefixes = 1;
  145.                         suffixes = 1;
  146.                         return;
  147.                     }
  148.                     if (_random.NextProbability(25))
  149.                     {
  150.                         prefixes = 1;
  151.                         suffixes = 0;
  152.                         return;
  153.                     }
  154.                     if (_random.NextProbability(50))
  155.                     {
  156.                         prefixes = 0;
  157.                         suffixes = 1;
  158.                         return;
  159.                     }
  160.                     // Default to both Prefix and Suffix
  161.                     prefixes = 1;
  162.                     suffixes = 1;
  163.                     return;
  164.  
  165.                 case QualityType.Rare:
  166.                     prefixes = 0;
  167.                     suffixes = 0;
  168.                     if (isEthereal)
  169.                     {
  170.                         prefixes = 3;
  171.                         suffixes = 3;
  172.                         return;
  173.                     }
  174.                     int probability = 100;
  175.                     for (int i = 0; i < 6; i++)
  176.                     {
  177.                         switch (i)
  178.                         {
  179.                             case 0:
  180.                             case 1:
  181.                             case 2:
  182.                                 probability = 100;
  183.                                 break;
  184.                             case 3:
  185.                             case 4:
  186.                                 probability = 75;
  187.                                 break;
  188.                             case 5:
  189.                                 probability = 40;
  190.                                 break;
  191.                             default:
  192.                                 break;
  193.                         }
  194.                         if (_random.NextProbability(probability))
  195.                         {
  196.  
  197.                             int selection = _random.NextRange(0, 1);
  198.                             switch (selection)
  199.                             {
  200.                                 case 0: // Prefix
  201.                                     if (prefixes < 3)
  202.                                     {
  203.                                         prefixes++;
  204.                                     }
  205.                                     else
  206.                                     {
  207.                                         suffixes++;
  208.                                     }
  209.                                     break;
  210.                                 case 1: // Suffix
  211.                                     if (suffixes < 3)
  212.                                     {
  213.                                         suffixes++;
  214.                                     }
  215.                                     else
  216.                                     {
  217.                                         prefixes++;
  218.                                     }
  219.                                     break;
  220.                             }
  221.                         }
  222.                     }
  223.                     return;
  224.                 case QualityType.Epic:
  225.                     if (isEthereal)
  226.                     {
  227.                         prefixes = 3;
  228.                         suffixes = 3;
  229.                         return;
  230.                     }
  231.                     prefixes = 3;
  232.                     suffixes = 3;
  233.                     return;
  234.                 case QualityType.Unique:
  235.                     prefixes = 0;
  236.                     suffixes = 0;
  237.                     return;
  238.                 default:
  239.                     throw new Exception(); // Common
  240.             }
  241.         }
  242.  
  243.         /// <summary>
  244.         ///  Summary
  245.         /// </summary>
  246.         /// <param name="template"></param>
  247.         /// <param name="lootParams"></param>
  248.         /// <param name="level"></param>
  249.         /// <returns></returns>
  250.         public GameItem Generate(GameItemTemplate template, LootDropArgs lootParams, int level)
  251.         {
  252.             GameItem result = new GameItem();
  253.             #region Copy template and roll base properties
  254.             result.ItemType = template.ItemType;
  255.             result.Identifier = template.Identifier;
  256.             result.Template = template.Identifier;
  257.             result.BaseName = template.BaseName;
  258.             result.Icon = template.Icon;
  259.             result.FlavorText = template.FlavorText;
  260.             result.BaseSellPrice = template.BaseSellPrice;
  261.             if (result.ItemType.IsWeapon())
  262.             {
  263.                 result.BaseWeaponDamage = new Range(
  264.                 _random.NextRange(template.BaseMinimumWeaponDamage),
  265.                 _random.NextRange(template.BaseMaximumWeaponDamage));
  266.             }
  267.             if (result.ItemType.IsArmor())
  268.             {
  269.                 result.BaseArmorRating = _random.NextRange(template.BaseMaximumWeaponDamage);
  270.             }
  271.             result.BaseDurability = _random.NextRange(template.BaseDurability);
  272.             result.BaseBlockChance = _random.NextRange(template.BaseBlockChance);
  273.             #endregion
  274.             result.Quality = GenerateItemQuality(lootParams);
  275.             bool isEthereal = GenerateEthereal();
  276.             GameItemType itemConstraint = template.ItemType;
  277.             List<Affix> affixes = new List<Affix>();
  278.  
  279.             RollAffixCounts(result.Quality, isEthereal, out int prefixes, out int suffixes);
  280.             switch (result.Quality)
  281.             {
  282.                 case QualityType.Uncommon:
  283.                     affixes = GenerateAffixes(itemConstraint, level, prefixes, suffixes);
  284.                     result.Header = GenerateUncommonName(affixes, template.BaseName);
  285.                     break;
  286.                 case QualityType.Rare:
  287.                     affixes = GenerateAffixes(itemConstraint, level, prefixes, suffixes);
  288.                     result.Header = GenerateRareName(itemConstraint);
  289.                     break;
  290.                 case QualityType.Epic:
  291.                     affixes = GenerateAffixes(itemConstraint, level, prefixes, suffixes);
  292.                     result.Header = GenerateEpicName();
  293.                     break;
  294.                 case QualityType.Unique:
  295.                     //if (this._legendaries != null)
  296.                     //{
  297.                     //    List<LegendaryItemTemplate> candidates = new List<LegendaryItemTemplate>();
  298.                     //    foreach (LegendaryItemTemplate candidate in this._legendaries)
  299.                     //    {
  300.                     //        if (candidate.Constraints.Contains(itemConstraint))
  301.                     //        {
  302.                     //            if (candidate.LevelBracket.Contains(level))
  303.                     //            {
  304.                     //                candidates.Add(candidate);
  305.                     //            }
  306.                     //        }
  307.                     //    }
  308.                     //    if (candidates.Count > 0)
  309.                     //    {
  310.                     //        LegendaryItemTemplate legendary = _random.Choose(candidates);
  311.                     //        result.LegendaryTemplate = legendary;
  312.                     //        result.Enchantments = GenerateEnchantments(itemConstraint, level, enchantmentCount);
  313.                     //        result.Header = legendary.Header;
  314.                     //        break;
  315.                     //    }
  316.                     //}
  317.                     /**
  318.                      * The player rolled a legendary, however the routine failed. This may mean
  319.                      * that no legendary exists for their level bracket, or the item type has no
  320.                      * corresponding legendary. We will default to giving them a guaranteed epic.
  321.                      */
  322.                     result.Quality = QualityType.Epic;
  323.                     goto case QualityType.Epic;
  324.             }
  325.             /**
  326.              * Finalize, Convert, and Apply all affixes
  327.              */
  328.             result.Properties = new List<GameItemProperty>();
  329.             foreach (Affix affix in affixes)
  330.             {
  331.                 GameItemProperty property = new GameItemProperty();
  332.                 if (isEthereal)
  333.                 {
  334.                     /**
  335.                      * Ethereal Items always gain maximum affix values
  336.                      */
  337.                     property.DynamicValue = affix.DynamicRange.Maximum;
  338.                     property.ProcChance = affix.ProcChance.Maximum;
  339.                     property.Property = affix.Property;
  340.                     property.StaticRange = affix.StaticRange;
  341.  
  342.                 }
  343.                 else
  344.                 {
  345.                     property.DynamicValue = _random.NextRange(affix.DynamicRange);
  346.                     property.ProcChance = _random.NextRange(affix.ProcChance);
  347.                     property.Property = affix.Property;
  348.                     property.StaticRange = affix.StaticRange;
  349.                 }
  350.                 /**
  351.                  * Check for duplicate properties, and if they exist, combine them.
  352.                  */
  353.                 foreach (GameItemProperty existingProperty in result.Properties)
  354.                 {
  355.                     if (existingProperty.Property == property.Property)
  356.                     {
  357.                         // This may introduce edge cases.
  358.                         existingProperty.DynamicValue += property.DynamicValue;
  359.                         existingProperty.ProcChance += property.ProcChance;
  360.                         existingProperty.StaticRange = new Range(
  361.                             existingProperty.StaticRange.Minimum + property.StaticRange.Minimum,
  362.                             existingProperty.StaticRange.Maximum + property.StaticRange.Maximum);
  363.                     }
  364.                 }
  365.                 result.Properties.Add(property);
  366.             }
  367.             /**
  368.              * Append Ethereal Property
  369.              */
  370.             if (isEthereal)
  371.             {
  372.                 result.Properties.Add(
  373.                     new GameItemProperty()
  374.                     {
  375.                         Property = GameItemPropertyType.Ethereal,
  376.                         DynamicValue = 0,
  377.                         ProcChance = 0,
  378.                         StaticRange = Range.Empty
  379.                     });
  380.             }
  381.             //result.Repair();
  382.             return result;
  383.         }
  384.  
  385.         private string GenerateUncommonName(List<Affix> affixes, string baseName)
  386.         {
  387.             switch (affixes.Count)
  388.             {
  389.                 case 1:
  390.                     Affix affix = affixes[0];
  391.                     switch (affix.AffixType)
  392.                     {
  393.                         case AffixType.Prefix:
  394.                             return affixes[0].Name + " " + baseName;
  395.                         case AffixType.Suffix:
  396.                             return baseName + " " + affixes[0].Name;
  397.                         default:
  398.                             throw new Exception();
  399.                     }
  400.                 case 2:
  401.                     return affixes[0].Name + " " + baseName + " " + affixes[1].Name;
  402.                 default:
  403.                     throw new Exception("Count: " + affixes.Count);
  404.             }
  405.         }
  406.  
  407.         public List<Affix> GenerateAffixes(GameItemType itemConstraint, int level, int prefixCount, int suffixCount)
  408.         {
  409.             List<Affix> results = new List<Affix>();
  410.             if(prefixCount > 0)
  411.             {
  412.                 results.AddRange(GenerateAffixes(itemConstraint, level, AffixType.Prefix, prefixCount));
  413.             }
  414.             if(suffixCount > 0)
  415.             {
  416.                 results.AddRange(GenerateAffixes(itemConstraint, level, AffixType.Suffix, suffixCount));
  417.             }
  418.             return results;
  419.         }
  420.  
  421.         public List<Affix> GenerateAffixes(GameItemType itemConstraint, int level, AffixType affixType, int count)
  422.         {
  423.             List<Affix> results = new List<Affix>();
  424.             List<Affix> candidates = new List<Affix>();
  425.             // Loop through all Keys, and collect lists that collide with level.
  426.             foreach (KeyValuePair<Range, List<Affix>> pair in _affixesByLevelBracket)
  427.             {
  428.                 if (pair.Key.Contains(level))
  429.                 {
  430.                     // Second collision is itemConstraint
  431.                     foreach (Affix affix in pair.Value)
  432.                     {
  433.                         if (affix.Constraints.Contains(itemConstraint) &&
  434.                             affix.AffixType == affixType)
  435.                         {
  436.                             candidates.Add(affix);
  437.                         }
  438.                     }
  439.                 }
  440.             }
  441.             bool isSearching = true;
  442.             int maximumIterations = 32; // Infinite Loop Error Guard
  443.             int iterations = 0;
  444.             while (isSearching)
  445.             {
  446.                 Affix candidate = _random.Choose(candidates);
  447.                 if (candidate != null)
  448.                 {
  449.                     if (_random.NextProbability(candidate.SpawnChance) &&
  450.                         !results.Exists(x => x.Property == candidate.Property) &&
  451.                         candidate.AffixType == affixType)
  452.                     {
  453.                         results.Add(candidate);
  454.                     }
  455.                 }
  456.                 iterations++;
  457.                 if (results.Count == count || iterations >= maximumIterations)
  458.                 {
  459.                     isSearching = false;
  460.                 }
  461.             }
  462.             return results;
  463.         }
  464.  
  465.         /// <summary>
  466.         /// sdfsdf
  467.         /// </summary>
  468.         /// <param name="dropArgs"></param>
  469.         /// <returns></returns>
  470.         public QualityType GenerateItemQuality(LootDropArgs dropArgs)
  471.         {
  472.             /**
  473.              * This needs to handle probabilities as doubles instead.
  474.              */
  475.             if (_random.NextProbability(dropArgs.LegendaryChance))
  476.             {
  477.                 return QualityType.Unique;
  478.             }
  479.             if (_random.NextProbability(dropArgs.EpicChance))
  480.             {
  481.                 return QualityType.Epic;
  482.             }
  483.             if (_random.NextProbability(dropArgs.RareChance))
  484.             {
  485.                 return QualityType.Rare;
  486.             }
  487.             if (_random.NextProbability(dropArgs.UncommonChance))
  488.             {
  489.                 return QualityType.Uncommon;
  490.             }
  491.             return QualityType.Common;
  492.         }
  493.  
  494.         /// <summary>
  495.         /// Attempts to generate the flag for an item being Ethereal
  496.         /// The current odds of rolling an Ethereal item is 1 in 1024 (‭‭0.0009765625‬%)
  497.         /// </summary>
  498.         public bool GenerateEthereal()
  499.         {
  500.             return _random.NextProbability(new Probability(1, 1024));
  501.         }
  502.  
  503.  
  504.     }
  505. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement