Skip to content

Custom loot - too ambitious?

Hey everyone

I am working on a PW and what I'd really like is a custom item drop system, that goes like this:

*Player kills a monster, or opens a chest* - so far it's relatively simple.



Now what I want the game to do is do some rolls to check what the player's going to grab.. to easier understand my weird thoughts, I've added a table

eh2q7fkiciks.png


Basically, I'd like a script to roll for a category, then roll for a type of said category and finally pick the item. Since it's 4 categories, the game should roll for categories at a 25% chance to pick from 1, 2, 3 or 4. Then, depending on types inside a category - e.g. armor - roll for 5 different types at a 20% chance and finally pick an item.

There's a bit of an anomaly, since I'd like the game - should it roll for category 3 or 4 - to have the chances be distributed differently than for categories 1 and 2. Regular should have a high chance to drop, common a bit less, rare .. well.. should be rare, expectional should be exceptionally rare and unique should almost never drop.

How do I approach this? Maybe someone has an idea.. or can already tell this ain't going to work^^

Comments

  • NeverwinterWightsNeverwinterWights Member Posts: 339
    edited September 2019
    This is kind of a crude skeleton of something that could work for you. You can alter percentages and sections to meet each categories needs.
    void main()
    {
        object oPC = GetLastKiller();//or whatever
        string sItemResRef;
        int iCategory = Random(4);//reminder Random is always one number less because it starts at 0. So random 4 would be 0 to 3
    
        if (iCategory == 0)//For example 0 will be the weapon category
        {
            int iPercent = Random(100);
    
            if (iPercent <= 49)//50% chance to drop this stuff/common stuff or whatever % you want. This could also be its own category such as martial weapon
            {
                int iNumItems = 20;//this should equal whatever amount of items you want in common stuff
                switch (Random(iNumItems))
                {
                    case 0: sItemResRef = "resref of weapon 1"; break;
                    case 1: sItemResRef = "resref of weapon 2"; break;
                    //etc.. all the way up to 19 or however many items in this section
                    case 19: sItemResRef = "resref of weapon 19"; break;
                }
            }
            if (iPercent > 49 && iPercent <= 74)//25% chance of this stuff dropping or whatever % you want. Could also be something like ranged weapon category
            {
                int iNumItems = 10;//this should equal whatever amount of items you want is better weapons section
                switch (Random(iNumItems))
                {
                    case 0: sItemResRef = "resref of weapon 1"; break;
                    case 1: sItemResRef = "resref of weapon 2"; break;
                    //etc.. all the way up to 9 or however many items in this section
                    case 9: sItemResRef = "resref of weapon 9"; break;
                }
            }
            if (iPercent > 74 && iPercent <= 84)//10% chance
            {
                int iNumItems = 5;
                switch (Random(iNumItems))
                {
                    case 0: sItemResRef = "resref of weapon 1"; break;
                    //etc..
                }
            }
            if (iPercent > 84 && iPercent <= 98)//4%
            {
                int iNumItems = 5;
                switch (Random(iNumItems))
                {
                    case 0: sItemResRef = "resref of weapon 1"; break;
                    //etc..
                }
            }
            if (iPercent > 98)//last 1%
            {
                int iNumItems = 5;
                switch (Random(iNumItems))
                {
                    case 0: sItemResRef = "resref of weapon 1"; break;
                    //etc..
                }
            }
        }
    
        if (iCategory == 1)//For example this can be Armor category
        {
            //All the same/similar stuff from category 0
        }
    
        if (iCategory == 2)//For example this could be crafting items
        {
            //Same stuff from other categories
        }
    
        if (iCategory == 3)// etc..
        {
            //etc..
        }
    
        CreateItemOnObject(sItemResRef, oPC);//Or however/wherever you want item created
    }
    

    There are a lot of different approaches you can take to do this via scripting. This is just one example I slapped together.
  • ThakeThake Member Posts: 14
    Holy moly and holly holy!

    Thank you very much - although this looks very alien to me (I can reliably use the script wizard) I am definitely going to wrap my head around this work of you and try to make it work!

    Awesome community!
  • ZwerkulesZwerkules Member Posts: 112
    From 84 to 98 is 14%, not 4%, so it will drop more often than the category before which is at 10%.
  • NeverwinterWightsNeverwinterWights Member Posts: 339
    Zwerkules wrote: »
    From 84 to 98 is 14%, not 4%, so it will drop more often than the category before which is at 10%.

    Good catch. Shoulda been 94. And I skipped the 84 to 94 section. Just kind of a rough draft example I typed up real quick.
  • KamirynKamiryn Member Posts: 74
    I would suggest to use a somewhat different approach. Instead of writing a complicated script I would use a rather simple script and put all the random stuff in a (not so simple as I have to admit :D ) 2da file:

    Script:
    const string ITEM_DROP_2DA_FILE = "myitemdrop";
    
    
    int MyStringToInt(string sString)
    {
        if (sString=="0") return 0;
        int nValue = StringToInt(sString);
        if (nValue==0) return -1;
        return nValue;
    }
    
    string GetItemResRef()
    {
        int nRow=0;
        int nCol;
        string sResRef = "";
    
        int nCounter=0; // to prevent 'too many instructions' error in case there something wrong with our 2DA file
        while ((nRow>=0) && (++nCounter<=10))
        {
            int nColCount = StringToInt(Get2DAString(ITEM_DROP_2DA_FILE, "COUNT", nRow));
            if (nColCount>0)
            {
                nCol = Random(nColCount);
                sResRef = Get2DAString(ITEM_DROP_2DA_FILE, "Col"+IntToString(nCol), nRow);
                nRow = MyStringToInt(sResRef);
                if (nRow<0) return sResRef;
            }
            else
            {
                nRow = -1;
            }
        }
        return "";
    }
    
    void main()
    {
        object oPC = GetLastKiller();
        string sItemResRef = GetItemResRef();
        CreateItemOnObject(sItemResRef, oPC);
    }
    

    2DA file 'myitemdrop':
    2DA V2.0
    
            Label       COUNT       Col0        Col1        Col2        Col3        Col4        Col5        Col6        Col7        Col8        Col9        Col10       Col11       Col12       Col13       Col14
    0       Category    4           1           2           3           4
    1       Armor       5           10          11          12          13          14
    2       Weapons     3           20          21          22
    3       Crafting    15          30          30          30          30          30          31          31          31          31          32          32          32          33          33          34
    4       Misc        15          40          40          40          40          40          41          41          41          41          42          42          42          43          43          44
    5       ****
    6       ****
    7       ****
    8       ****
    9       ****
    10      HeavyArmor  4           50          51          52          53
    11      MediumArmor 4           54          55          56          57
    12      LightArmor  5           58          59          60          61          62
    13      Body        6           62          63          64          65          66          67
    14      Shields     3           68          69          70
    ..
    ..
    20      SimpleWeap  11          80          81          ...         ...
    21      MartialWeap 17          ...         ...         ...         ...
    22      ...
    ..
    ..
    30      CraftReg    2           ...         ...
    31      CraftCom    2           ...         ...
    32      CraftRare   2           ...         ...
    33      ...
    ..
    ..
    40      MiscReg     ...         ...
    41      MiscCom     ...
    42      ...
    ..
    ..
    50      BandedMail  ...         "ResRef"    "ResRef"    "ResRef"    ...
    51      FullPlate   ...         "ResRef"    "ResRef"    "ResRef"    ...
    52      HalfPlate   ...         "ResRef"    "ResRef"    "ResRef"    ...
    53      SplintMail  ...         "ResRef"    "ResRef"    "ResRef"    ...
    54      ChainShirt  ...         "ResRef"    ...
    55      ...
    

    So how does it work: our script starts at row 0. It reads the column COUNT (= number of columns in that row) and rolls a random between 0 and that number-1. Than it looks at the value in that column and that value will be our new row. Let's say the random was 0 then our new row is 1 (category 'armor'). Again the script looks at column COUNT, rolls a random and gets a new row (something between 10 and 14). This goes on and on until the script is in a row with resrefs. Then it just returns the resref.

    For chances distributed differently look at row 3 (Crafting) and row 4 (Misc): item types with higher probability appear more often.

    Might be some work to build that 2da file but once it's done it should be easy to maintain. You could even use diffeerent 2da files for different classes, levels,...
  • NeverwinterWightsNeverwinterWights Member Posts: 339
    edited December 2019
    @Kamiryn Do .2da reads of this type still cause more noticeable server lag than a standard script? Especially since it's used in a loop and in that example on every kill?
  • SherincallSherincall Member Posts: 387
    2DA reads are very fast; faster than local variable lookups in many cases.
  • NeverwinterWightsNeverwinterWights Member Posts: 339
    Good to know. Looks like the Lexicon has been updated in agreement. Perhaps the "//avoid using this function in loops" warning in the function description in the editor should be removed? Been avoiding some .2da lookups for this reason.
  • KamirynKamiryn Member Posts: 74
    I guess the warning in the editor is from the very first NWN versions and nobody thought about removing it.

    The 2da cache size can be changed in settings.tml (in case you want to read many 2da files):
    [server.tweaks]
    	2da-engine-cache-size = 11
    	2da-user-cache-size = 10
    
Sign In or Register to comment.