Author Topic: Custom tokens in journal entries problem  (Read 1257 times)

Legacy_Grani

  • Hero Member
  • *****
  • Posts: 1040
  • Karma: +0/-0
Custom tokens in journal entries problem
« on: May 06, 2016, 01:52:35 am »


               

Oh powerful deities of NWScript, I summon thee!


 


Here's the thing. I have the random quest system in place and it comes with journal entries, too. As such, these entries need to be dynamic, so custom tokens need to be put in place.


 


I have created a function SetQuestTokens(object oPC), which gets quest data from a PC-specific database and sets tokens used in journal entries accordingly. My thinking is as follows: if I use the SetQuestTokens function just before adding a quest entry to the PC's journal, it will correctly display PC's quest data and will support multiple players with quests with varying data, because a moment before adding each PC's journal entry, tokens will be set for that specific PC.


 


And while it does work flawlessly for a single player, I've noticed display bugs in the journal when more people play - stuff like reward being different than what it should, missing quest objective NPC's name, etc. I have no idea what the reason for these bugs are, since I thought journal entries are baked and won't change once they're in the PC's journal even if new tokens are set.


 


Here's my SetQuestTokens function (it's supposed to work in Polish and English, hence two tokens for each piece of data):



void SetQuestTokens(object oPC)
    {
        string sDB = CharacterDB(oPC); //PC-specific database
        string sRegion = GetCampaignString(sDB, "QUEST_REGION");
        int nTavern = GetCampaignInt(sDB, "QUEST_TAVERN");
        string sExtra = GetCampaignString(sDB, "QUEST_EXTRA");
        string sExtra2 = GetCampaignString(sDB, "QUEST_EXTRA2");
        string sNpcName = GetCampaignString(sDB, "QUEST_NPC_NAME");
        string sNpcLastName = GetCampaignString(sDB, "QUEST_NPC_LASTNAME");
        SetCustomToken(10001, GetCampaignString(sDB, "QUEST_CLI_NAME"));
        SetCustomToken(10002, GetCampaignString(sDB, "QUEST_CLI_LASTNAME"));
        SetCustomToken(10003, sNpcName);
        SetCustomToken(10004, sNpcLastName);
        SetCustomToken(10015, GetCampaignString(sDB, "QUEST_NP2_NAME"));
        SetCustomToken(10016, GetCampaignString(sDB, "QUEST_NP2_LASTNAME"));
        SetCustomToken(10020, IntToString(GetCampaignInt(sDB, "QUEST_INTELCOST")));
        if (GetCampaignString(sDB, "QUEST_TEMPLATE") == "ransom" && (GetCampaignInt(sDB, "QUEST_BACKGROUND") == 1 || GetCampaignInt(sDB, "QUEST_BACKGROUND") == 2))
            {
                SetCustomToken(10018, "kompletny");
                SetCustomToken(10019, "a complete");
            }
        if (GetCampaignString(sDB, "QUEST_TEMPLATE") == "ransom" && (GetCampaignInt(sDB, "QUEST_BACKGROUND") == 3 || GetCampaignInt(sDB, "QUEST_BACKGROUND") == 4))
            {
                SetCustomToken(10018, "niekompletny");
                SetCustomToken(10019, "an incomplete");
            }
        if (sRegion == "greencoast")
            {
                SetCustomToken(10005, "Zielone Wybrzeze");
                SetCustomToken(10006, "Green Coast");
            }
        SetCustomToken(10007, IntToString(GetCampaignInt(sDB, "QUEST_REWARD")));
        if (nTavern == 1)
            {
                SetCustomToken(10008, "Alverton");
                SetCustomToken(10009, "Alverton");
            }
        SetCustomToken(10010, IntToString(11*GetCampaignInt(sDB, "QUEST_REWARD")/10));
        if (sExtra == "sister")
            {
                SetCustomToken(10011, "Moja ukochana siostra");
                SetCustomToken(10012, "My beloved sister");
            }
        if (sExtra == "brother")
            {
                SetCustomToken(10011, "Moj brat");
                SetCustomToken(10012, "My brother");
            }
        if (sExtra == "kid")
            {
                SetCustomToken(10011, "Moje biedne dziecko");
                SetCustomToken(10012, "My poor child");
            }
        if (sExtra == "all")
            {
                SetCustomToken(10011, "Moja rodzina");
                SetCustomToken(10012, "My whole family");
            }
        if (sExtra == "murderer")
            {
                SetCustomToken(10011, "winnym wielokrotnego morderstwa");
                SetCustomToken(10012, "who is guilty of numerous murders");
            }
        if (sExtra == "bandit")
            {
                SetCustomToken(10011, "przewodzi zorganizowanej gildii bandytow");
                SetCustomToken(10012, "who is the leader of an organized bandit guild");
            }
        if (sExtra == "fugitive")
            {
                SetCustomToken(10011, "niedawno uciekl z pilnie strzezonego garnizonu");
                SetCustomToken(10012, "who has recently escaped from a guarded garrison");
            }
        if (sExtra == "male")
            {
                SetCustomToken(10011, "mojego drogiego przyjaciela");
                SetCustomToken(10012, "him");
            }
        if (sExtra == "female")
            {
                SetCustomToken(10011, "moja droga przyjaciolke");
                SetCustomToken(10012, "her");
            }
        if (sExtra == "cards")
            {
                SetCustomToken(10011, "karty");
                SetCustomToken(10012, "cards");
            }
        if (sExtra == "dice")
            {
                SetCustomToken(10011, "kosci");
                SetCustomToken(10012, "dice");
            }
        if (sExtra2 == "cankill")
            {
                SetCustomToken(10013, "Moge zabic swoj cel.");
                SetCustomToken(10014, "I can also kill my target.");
            }
        if (sExtra2 == "mustkill")
            {
                SetCustomToken(10013, "Po wykonaniu tego zadania musze zabic moj cel.");
                SetCustomToken(10014, "I have to kill my target once I complete this task.");
            }
        if (sExtra2 == "cantkill")
            {
                SetCustomToken(10013, "Nie moge zabic swojego celu.");
                SetCustomToken(10014, "I must not kill my target.");
            }
        if (sExtra2 == "cantnotice")
            {
                SetCustomToken(10013, "Moj cel nie moze zginac, ani nawet zauwazyc, ze zostal okradziony.");
                SetCustomToken(10014, "My target must not die or even notice they've been robbed.");
            }
        if (sExtra2 == "1000" || sExtra2 == "700" || sExtra2 == "1500" || sExtra2 == "2000")
            {
                SetCustomToken(10017, sExtra2);
            }
        if (sExtra == "stealring")
            {
                SetCustomToken(10011, "pierscien");
                SetCustomToken(10012, "ring");
            }
        if (sExtra == "stealamulet")
            {
                SetCustomToken(10011, "amulet");
                SetCustomToken(10012, "amulet");
            }
        if (sExtra == "stealsphere")
            {
                SetCustomToken(10011, "przedmiot przypominajacy kule energii");
                SetCustomToken(10012, "item resembling a sphere of energy");
            }
        if (sExtra == "stealhand")
            {
                SetCustomToken(10011, "przedmiot przypominajacy martwa dlon");
                SetCustomToken(10012, "item resembling a dead hand");
            }
        if (sExtra == "stealdisc")
            {
                SetCustomToken(10011, "przedmiot przypominajacy kamienny dysk");
                SetCustomToken(10012, "item resembling a stone disc");
            }
        if (sExtra == "lostbrother")
            {
                SetCustomToken(10011, "moim drogim bracie");
                SetCustomToken(10012, "my dear brother");
            }
        if (sExtra == "lostsister")
            {
                SetCustomToken(10011, "mojej drogiej siostrze");
                SetCustomToken(10012, "my dear sister");
            }
        if (sExtra == "lostmale")
            {
                SetCustomToken(10011, "moj dobry przyjaciel");
                SetCustomToken(10012, "he");
            }
        if (sExtra == "lostfemale")
            {
                SetCustomToken(10011, "moja dobra przyjaciolka");
                SetCustomToken(10012, "she");
            }
        if (sExtra == "husband")
            {
                SetCustomToken(10011, "moj ukochany maz");
                SetCustomToken(10012, "my beloved husband");
            }
        if (sExtra == "wife")
            {
                SetCustomToken(10011, "moja ukochana zona");
                SetCustomToken(10012, "my beloved wife");
            }
        if (sExtra == "escortmale")
            {
                SetCustomToken(10011, "Moj przyjaciel");
                SetCustomToken(10012, "he");
            }
        if (sExtra == "escortfemale")
            {
                SetCustomToken(10011, "Moja przyjaciolka");
                SetCustomToken(10012, "she");
            }
        if (sExtra == "kidnbrother")
            {
                SetCustomToken(10011, "moj brat");
                SetCustomToken(10012, "my brother");
            }
        if (sExtra == "kidnsister")
            {
                SetCustomToken(10011, "moja siostra");
                SetCustomToken(10012, "my sister");
            }
        if (sExtra == "kidnhusband")
            {
                SetCustomToken(10011, "mojego drogiego meza");
                SetCustomToken(10012, "my dear husband");
            }
        if (sExtra == "kidnwife")
            {
                SetCustomToken(10011, "moja droga zone");
                SetCustomToken(10012, "my dear wife");
            }
        if (sExtra == "rescuewife")
            {
                SetCustomToken(10011, "Moja zona zostala porwana");
                SetCustomToken(10012, "My wife has been kidnapped");
            }
        if (sExtra == "rescuehusband")
            {
                SetCustomToken(10011, "Moj maz zostal porwany");
                SetCustomToken(10012, "My husband has been kidnapped");
            }
        if (sExtra == "rescuebrother")
            {
                SetCustomToken(10011, "Moj brat zostal porwany");
                SetCustomToken(10012, "My brother has been kidnapped");
            }
        if (sExtra == "rescuesister")
            {
                SetCustomToken(10011, "Moja siostra zostala porwana");
                SetCustomToken(10012, "My sister has been kidnapped");
            }
        if (sExtra == "rescuemfriend")
            {
                SetCustomToken(10011, "Moj przyjaciel zostal porwany");
                SetCustomToken(10012, "My friend has been kidnapped");
            }
        if (sExtra == "rescueffriend")
            {
                SetCustomToken(10011, "Moja przyjaciolka zostala porwana");
                SetCustomToken(10012, "My friend has been kidnapped");
            }
        if (sExtra2 == "forc")
            {
                SetCustomToken(10013, "orkow");
                SetCustomToken(10014, "orcs");
            }
        if (sExtra2 == "fhobgoblin")
            {
                SetCustomToken(10013, "orkliny");
                SetCustomToken(10014, "bugbears");
            }
        if (sExtra2 == "freptilian")
            {
                SetCustomToken(10013, "jaszczuroludzi");
                SetCustomToken(10014, "reptilians");
            }
        if (sExtra2 == "fdrow")
            {
                SetCustomToken(10013, "drowy");
                SetCustomToken(10014, "drows");
            }
        if (sExtra2 == "fduergar")
            {
                SetCustomToken(10013, "duergarow");
                SetCustomToken(10014, "duergars");
            }
        if (sExtra == "drow")
            {
                SetCustomToken(10011, "drowy");
                SetCustomToken(10012, "drows");
            }
        if (sExtra == "duergar")
            {
                SetCustomToken(10011, "duergary");
                SetCustomToken(10012, "duergars");
            }
        if (sExtra == "human")
            {
                SetCustomToken(10011, "ludzie");
                SetCustomToken(10012, "humans");
            }
        if (sExtra == "elf")
            {
                SetCustomToken(10011, "elfy");
                SetCustomToken(10012, "elves");
            }
        if (sExtra == "dwarf")
            {
                SetCustomToken(10011, "krasnoludy");
                SetCustomToken(10012, "dwarves");
            }
        if (sExtra2 == "drow")
            {
                SetCustomToken(10013, "drowy");
                SetCustomToken(10014, "drows");
            }
        if (sExtra2 == "duergar")
            {
                SetCustomToken(10013, "duergary");
                SetCustomToken(10014, "duergars");
            }
        if (sExtra2 == "human")
            {
                SetCustomToken(10013, "ludzie");
                SetCustomToken(10014, "humans");
            }
        if (sExtra2 == "elf")
            {
                SetCustomToken(10013, "elfy");
                SetCustomToken(10014, "elves");
            }
        if (sExtra2 == "dwarf")
            {
                SetCustomToken(10013, "krasnoludy");
                SetCustomToken(10014, "dwarves");
            }
        if (sExtra2 == "orc")
            {
                SetCustomToken(10013, "orkowie");
                SetCustomToken(10014, "orcs");
            }
        if (sExtra2 == "hobgoblin")
            {
                SetCustomToken(10013, "orkliny");
                SetCustomToken(10014, "bugbears");
            }
        if (sExtra2 == "reptilian")
            {
                SetCustomToken(10013, "jaszczuroludzie");
                SetCustomToken(10014, "reptilians");
            }
    }

This function is called on several occassions: when the quest is first taken by the PC, when the quest entry is updated to reflect on the PC's success or failure and in OnClientEnter event if the PC's database has quest data.


 


Here's the example taken from the OnClientEnter script:



//Add random quest journal entry the PC should have
    string sCharDB = CharacterDB(oPC);
    int nQuestEntry = GetCampaignInt(sCharDB, "QUEST_JOURNAL");
    if (nQuestEntry != 0)
        {
        SetQuestTokens(oPC);
        AddJournalQuestEntry("random_quest", nQuestEntry, oPC, FALSE, FALSE, TRUE);
        }
    else RemoveAllItems(oPC, "quest_proof");

If you need me to send more code, just give me the word.


 


Big thanks in advance.



               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Custom tokens in journal entries problem
« Reply #1 on: May 06, 2016, 01:55:50 pm »


               

Custom Tokens are not static or instanced to players.


When you create a Journal entry, it will look like this.


 



Please take this cup of tea to <CUSTOM1002>



 


If player 1 looks at the journal entry in their journal - and the token has not been set.


They will either get a blank value for CUSTOM1002 or they will get Invalid str#


 


If you set the value to Bob via SetCustomToken(1002,"Bob"); (or whatever the function is)


Then player 1 when they re-open their journal, will see 


'Please take this cup of tea to Bob'


 


The value will have changed.


 


If player 2 logs in, and gets the same quest, but his NPC is 'Mary' instead of Bob.


SetCustomToken(1002,"Mary");


 


'Please take this cup of tea to Mary'


 


will appear not only for Player 2, but also for Player 1 the next time he opens his Journal.


 


As such - actions undertook by player 2 can and will contaminate the journal entries of your other players if the same token numbers are being used.


 


It looks like your code is getting the string values from a player specific database, however your problem is that you are trying to save those values into the Custom Tokens, which are server wide, not limited/restricted to a single PC.


               
               

               
            

Legacy_meaglyn

  • Hero Member
  • *****
  • Posts: 1451
  • Karma: +0/-0
Custom tokens in journal entries problem
« Reply #2 on: May 06, 2016, 03:18:31 pm »


               

I don't believe that's the case.  Journal entries are supposed to get "baked" with the token value.  I have not tested that but according to the tutorials in the lexicon that's what it does.  So by that documentation Grani's approach could work.  What you are describing is more generally true to tokens used in conversation and such where the baking does not happen.



               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Custom tokens in journal entries problem
« Reply #3 on: May 06, 2016, 03:43:29 pm »


               

Hmm, Maybe they do get baked in.


I've never experimented with them in Journals tbh - I assumed they might follow the same direction as Dialog.


 


Would that suggest that Journal data is stored in the Player Turds?


I knew that it stored the 'what journal entries you have'.


 


I just didn't know it stored the textual value of said journal entries as well.


 


It is likely one of two possibilities.


 


1. The server just stores what journal entries you have access to.


    * Your Custom tokens are not baked in, changing the token elsewhere will update the value on your journal page.


 


2. The server saves the journal entries you have access to and the text of those journal entries.


    * Your custom tokens get baked in, changing the custom token elsewhere will not affect the value already in your journal.


 


However, i am unsure if the 2nd option is the case - as it goes against the 'feel' of how the nwn data structures/architecture works.


 


The whole point of having the module.jrl file was to contain the journal text data and identifiers for said journal entries - in a single place.


When you give a player a journal entry, it is an basically that identifier + the int indicating stage of the quest.


I had believed that it simply marks your character as having access to that journal entry, at which point it gets the text data from the JRL file when you open your journal.


If the JRL file references a CUSTOM1002, I thought it would, at journal opening time, display what the current value of CUSTOM1002 is.


 


I am aware that the nwn symbols references a player journal class - Once again - I thought it was just a management class that held the identifiers and stage information that the player had access to.


I didn't think it held any large text data, as that would be like copying the journal entry from the jrl into every players Journal class, which could a large amount of data, depending on the text in the journal.


 


Imagine 10,000 characters (or whatever the limit is) of text per journal entry, duplicated for 64 players x 100 Journal entries.


 


 


[Edit]


Having read 


http://www.nwnlexico...e=Custom_Tokens


 


I can see that it 'should' work as meaglyn says - I guess it was a hidden gem. I would have used player journals for so much more had I known I could actually make them dynamic.



               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Custom tokens in journal entries problem
« Reply #4 on: May 06, 2016, 03:50:48 pm »


               

The only other solution / debug / triage solution I can suggest is to add loads of reporting.


Eg: 


WriteTimeStampedLogEntry


 


Record the scenario/situation, the custom token number and the returned value from the db.


If it is getting wrong or blank values - it suggests that it is a database issue or possibly returning blank data.



               
               

               
            

Legacy_Tchos

  • Sr. Member
  • ****
  • Posts: 454
  • Karma: +0/-0
Custom tokens in journal entries problem
« Reply #5 on: May 06, 2016, 04:00:43 pm »


               

Custom tokens certainly do get baked into journals in NWN2, but they need to be refreshed on reloads.  It would be a surprise if it were different in NWN1.


 


As this tutorial says, custom tokens are not persistent, and require scripting to refresh them when needed.  I use the token duplication method it describes for my own work, which may work for you in your journal issue.


 


http://palmergames.c...stomTokens.html



               
               

               
            

Legacy_Grani

  • Hero Member
  • *****
  • Posts: 1040
  • Karma: +0/-0
Custom tokens in journal entries problem
« Reply #6 on: May 06, 2016, 10:45:23 pm »


               

The thing is I do refresh the tokens before using them when adding journal entries. That's what my SetQuestToken function is for. As you can see in the code sample in my original post, this function is called before adding the journal entry.


 


Baaleos, if it was the problem of writing blank data or (I think) a db issue, then I would not get in my journal data of other players. For example, in one case I received a quest for which the reward for over a thousand gold pieces, while my friend got another one with a reward of 900 gold pieces. But when I looked at my journal earlier, it reported a reward of 900 gold instead of 1000.


 


What's weird is that at another time when I played with just one other person (the earlier case was when more players were present), our journals reported correct data for each of us - even though we had two different quests.



               
               

               
            

Legacy_Proleric

  • Hero Member
  • *****
  • Posts: 1750
  • Karma: +0/-0
Custom tokens in journal entries problem
« Reply #7 on: May 07, 2016, 07:48:21 am »


               Have you tried deleting the journal entry before creating it each time, rather than using the overwrite option?
               
               

               
            

Legacy_Grani

  • Hero Member
  • *****
  • Posts: 1040
  • Karma: +0/-0
Custom tokens in journal entries problem
« Reply #8 on: May 07, 2016, 09:07:58 am »


               

Well, I haven't tried to do it specifically, but this problem occured even in case of journal entries created anew (when a quest is first received), so I think this is not the solution.


 


Anyway, I've just noticed that my dedicated server application reports version 1.68.8109 instead of 1.69.8109. Nwserver.exe, however, is a file from the 3rd of July 2008, which I think indicates it's the last version.


 


Given how unusual this bug is (in that everything seems fine in the code, yet the bug exists, and is somewhat unpredictable), could it be that the reason is a not fully updated server application?


 


Edit: I've just downloaded the dedicated server application from the Vault and it reports 1.68, too. On the other hand, server launched from my "home" installation of NWN reports 1.69. Geez, what's going on here...



               
               

               
            

Legacy_Lightfoot8

  • Hero Member
  • *****
  • Posts: 4797
  • Karma: +0/-0
Custom tokens in journal entries problem
« Reply #9 on: May 10, 2016, 12:51:23 am »


               Custom tokens do get baked into the journal. The problem you are having is when they get baked into the journal.  Custom token and setting the quest state are handled by the server. Unfortunately the baking of the journal enters is handled by the client, at the time when the player opens the journal on the client.  


 I know of no workaround for this, unless someone has created a hook for when someone opens the journal client side.
               
               

               
            

Legacy_Grani

  • Hero Member
  • *****
  • Posts: 1040
  • Karma: +0/-0
Custom tokens in journal entries problem
« Reply #10 on: May 18, 2016, 02:32:06 pm »


               


Custom tokens do get baked into the journal. The problem you are having is when they get baked into the journal. Custom token and setting the quest state are handled by the server. Unfortunately the baking of the journal enters is handled by the client, at the time when the player opens the journal on the client.


I know of no workaround for this, unless someone has created a hook for when someone opens the journal client side.




 


I've just realized today that this is probably the problem and wanted to share this theory here, only to realize you wrote about it 8 days ago. '<img'> Sorry, I haven't noticed.


 


Anyway, I think I'll have to do it the hard and rough way.


 


1. Assign an entering player a number (unique among all the players currently logged in) and save it as a local variable on the PC


2. Copy and paste the random quest category of journal entries to have 20 of them


3. Make each copy use different tokens


4. Fill the tokens for the specific player with PC's quest data and give him the appropriate copy of the journal entry


 


My module supports up to 20 players at most, so this should work - basically I'd make tokens pseudo-local so that they're not shared among logged-in characters. It's quite some work, but I think I won't come up with anything better, hm? '<img'>