Sign in to follow this  
Salk

The "CUSTOM" token

Recommended Posts

Hello again!

I was wondering if someone would know how to retrieve the value of custom tokens. The game doesn't unfortunately have a GetCustomToken() function but earlier I found something that gave me hope, although it was short-lived.

There is this tutorial from I don't know when and by I don't know who which says:

Custom Tokens:


The Custom Tokens are slightly different from the Game Tokens. For one thing, to use them them you have to use the format <CUSTOM#>, where "#" is a number between 0 to 2147483647.

In practice, however, the game uses 0-9 and the WhereAmI band mod uses 61 and 62, so I'd avoid those in TSL. Also, in K1, the K1R mod uses 1998-2030(or around there) for the Pazaak Tourney, so I'd really avoid those...:)

To set a Custom Token:


void main()
{
    // Syntax for the function: SetCustomToken(int iTokenToSet, string sMessage)
    SetCustomToken(2000, "Hi, I'm token 2000!");
}
Though there is no GetCustomToken function, you can still check the value in a script:

 


void main()
{
    string sToken = "<CUSTOM2000>";
    if(sToken == "Hi, I'm token 2000")
    {
        SendMessageToPC(GetFirstPC(), "Yep, this is it.");
    }
    else
    {
        SendMessageToPC(GetFirstPC(), "No, it's not it.");
    }
}

Unfortunately it seems that string sToken returns "<CUSTOM2000>" rather than the content of the token. The NWN Lexicon here under "Token Duplication" speaks of another method using two other functions that are not present in KotOR (SetLocalString() and GetLocalString()).

The game uses <CUSTOM17> and <CUSTOM 26> to store the time for the latest swoop race and I need to retrieve those values.

Any idea?

Thanks a lot!

Share this post


Link to post
Share on other sites

Add new lines to the TLK that are just the <CUSTOMxx> identifiers. Then use GetStringByStrRef(nStrRef) to pull the value as a string. You can use GetStringLeft/GetStringRight and StringToInt if you need to break it up into its mins/secs/milliseconds components.

Share this post


Link to post
Share on other sites

I think you use the following to set up a token:

Spoiler

//  c_parts_spikes
/*
    Checks to see if the player has a specified number of repair parts or spikes,
        optionally modified by skill

    iNum    The number of parts or spikes
    iSpikes 0 = repair parts, 1 = spikes
    iSkillMod  0 = skill not factored in, 1 = skill is factored in

    Use c_token_setup to setup tokens beforehand
*/
//  Created By: Tony Evans 8/9/04

#include "k_inc_debug"
#include "k_inc_utility"

int StartingConditional()
{
    int iNum = GetScriptParameter(1);
    int iSpikes = GetScriptParameter(2);
    int iSkillMod = GetScriptParameter(3);
    int iType;
    int iCost;

    if (iSpikes) iType = SKILL_COMPUTER_USE;

    else iType = SKILL_REPAIR;

    if (iSkillMod) iCost = UT_DeterminesItemCost(iNum, iType);

    else (iCost = iNum);

    AurPostString("Spikes = " + IntToString(UT_ReturnSpikePartAmount(iType)) + " Cost = " +  IntToString(iCost), 5, 5, 5.0);

    if(UT_ReturnSpikePartAmount(iType) >= iCost)
    {
        return TRUE;
    }
    return FALSE;
}

 

 

Then, you use the following to retrieve a token:

Spoiler

//  c_parts_spikes
/*
    Checks to see if the player has a specified number of repair parts or spikes,
        optionally modified by skill

    iNum    The number of parts or spikes
    iSpikes 0 = repair parts, 1 = spikes
    iSkillMod  0 = skill not factored in, 1 = skill is factored in

    Use c_token_setup to setup tokens beforehand
*/
//  Created By: Tony Evans 8/9/04

#include "k_inc_debug"
#include "k_inc_utility"

int StartingConditional()
{
    int iNum = GetScriptParameter(1);
    int iSpikes = GetScriptParameter(2);
    int iSkillMod = GetScriptParameter(3);
    int iType;
    int iCost;

    if (iSpikes) iType = SKILL_COMPUTER_USE;

    else iType = SKILL_REPAIR;

    if (iSkillMod) iCost = UT_DeterminesItemCost(iNum, iType);

    else (iCost = iNum);

    AurPostString("Spikes = " + IntToString(UT_ReturnSpikePartAmount(iType)) + " Cost = " +  IntToString(iCost), 5, 5, 5.0);

    if(UT_ReturnSpikePartAmount(iType) >= iCost)
    {
        return TRUE;
    }
    return FALSE;
}

 

 

tl;dr GetScriptParameter(int)

Share this post


Link to post
Share on other sites
1 hour ago, DarthParametric said:

Add new lines to the TLK that are just the <CUSTOMxx> identifiers. Then use GetStringByStrRef(nStrRef) to pull the value as a string. You can use GetStringLeft/GetStringRight and StringToInt if you need to break it up into its mins/secs/milliseconds components.

That's exactly what I did but GetStringByStrRef() returns <CUSTOMxx>, not its value.

My STR 49415 is <CUSTOM17> (this should be the token used to store the time for the player's latest race on Manaan).

 

and this is my code:

int Result (int nResult) {
  SetGlobalBoolean("MAN_JUST_RACED", FALSE);
  return nResult;
}

int StartingConditional()
{
  if (GetGlobalBoolean("MAN_JUST_RACED") == TRUE) {
    
    string sLastTimeManaan = GetStringByStrRef(49415);
    string sLastTimeManaanMinutes = GetSubString(sLastTimeManaan, 0, 1);
    string sLastTimeManaanSeconds = GetSubString(sLastTimeManaan, 2, 2);
    string sLastTimeManaanFractions = GetSubString(sLastTimeManaan, 5, 2);
    //string sOpponentTimeManaan;
 
    if (GetGlobalNumber("CRI_SWP_RESULT") == 1) {
      //sOpponentTimeManaan = "0:30:12";
      if (StringToInt(sLastTimeManaanMinutes) > 0) {
        return Result(1);
      }
      else if (StringToInt(sLastTimeManaanSeconds) > 30) {
        return Result(1);
      }
      else if (StringToInt(sLastTimeManaanSeconds) > 12) {
        return Result(1);
      }
      else {
        return 0;
      }
    }
    else if (GetGlobalNumber("CRI_SWP_RESULT") == 2) {
      //sOpponentTimeManaan = "0:26:16";
      if (StringToInt(sLastTimeManaanMinutes) > 0) {
        return Result(1);
      }
      else if (StringToInt(sLastTimeManaanSeconds) > 26) {
        return Result(1);
      }
      else if (StringToInt(sLastTimeManaanSeconds) > 16) {
        return Result(1);
      }
      else {
        return 0;
      }
    }
    else if (GetGlobalNumber("CRI_SWP_RESULT") == 3) {
      //sOpponentTimeManaan = "0:23:63";
      if (StringToInt(sLastTimeManaanMinutes) > 0) {
        return Result(1);
      }
      else if (StringToInt(sLastTimeManaanSeconds) > 23) {
        return Result(1);
      }
      else if (StringToInt(sLastTimeManaanSeconds) > 63) {
        return Result(1);
      }
      else {
        return 0;
      }
    }
    else {
      //sOpponentTimeManaan = "0:22:98";
      if (StringToInt(sLastTimeManaanMinutes) > 0) {
        return Result(1);
      }
      else if (StringToInt(sLastTimeManaanSeconds) > 22) {
        return Result(1);
      }
      else if (StringToInt(sLastTimeManaanSeconds) > 98) {
        return Result(1);
      }
      else {
        return 0;
      }
    }
  }
  else {         
    return 0;
  }
}

 

Share this post


Link to post
Share on other sites

Weird... The BarkString DOES return the token's value instead...

Either I need more coffee (which I don't drink) like our friend above or the April's fool is on me today.

void main {
  string sLastTimeManaan = GetStringByStrRef(49415);
  BarkString(OBJECT_SELF, 49415);
  SendMessageToPC(GetFirstPC(), sLastTimeManaan);
}

The BarkString() correctly retrieves the value of the token while the message feedback reports <CUSTOM17>.

More ideas?

Cheers!

Edited by Salk

Share this post


Link to post
Share on other sites

Depends on how exactly the BarkString function works. Since, results-wise, it performs exactly the same as calling a DLG with a single node, it's probably just passing the StrRef to the engine's conversation system in the same manner as when it parses a DLG.

Share this post


Link to post
Share on other sites

I suppose there is no solution then.

It's really quite frustrating that Bioware didn't bother writing a simple GetCustomToken function to retrieve the value and it's even more frustrating to find a "tutorial" whose author didn't even bother to test the veracity of their own code.

If someone else has any other idea however, I'd be overjoyed.

Thanks!

Share this post


Link to post
Share on other sites

Got it to work. There is no GetCustomToken because you don't need it:

//SetToken.nss
void main()
{
    SetCustomToken(2000, "Hi, I'm token 2000!");
}

The DLG file:

Line 1 - "I am ugly."			//Execute SetToken.ncs here, just as an example
Line 2 - "Yes, you are very ugly."
Line 3 - "I knew it. <CUSTOM2000>"

The printed text:

NPC - "I am ugly."			//SetToken.ncs fires and sets 2000
PC - "Yes, you are very ugly."
NPC - "I knew it. Hi, I'm token 2000!"

 

Share this post


Link to post
Share on other sites

Did you try modifying k_inc_man.nss?

int QUEEDLE_TIME = 3012;
int CASSANDRA_TIME = 2702;
int JAX_TIME = 2548;
int CHAMP_TIME = 2348;

 

Share this post


Link to post
Share on other sites

I modified the necessary files so that the game shows the desired times in dialogues creating this following new file, called on heartbeat in the area:

void SetTokenRaceTime(int nToken, int nRacerTime)
{
    // calculate the time components
    int nMinutes = nRacerTime/6000;
    int nSeconds = (nRacerTime - (nMinutes * 6000)) / 100;
    int nFractions =  nRacerTime - ((nMinutes * 6000) + (nSeconds * 100));

    //building the time string
    string sTime = IntToString(nMinutes) + ":";
    if (nSeconds < 10)
    {
        sTime = sTime + "0";
    }
    sTime = sTime + IntToString(nSeconds) + ":";
    if(nFractions < 10)
    {
        sTime = sTime + "0";
    }
    sTime = sTime + IntToString(nFractions);
    SetCustomToken(nToken,sTime);

}

void main()
{
    int QUEEDLE_TIME = 3012;
    int CASSANDRA_TIME = 2616;
    int JAX_TIME = 2363;
    int CHAMP_TIME = 2298;

    SetTokenRaceTime(18, QUEEDLE_TIME);
    SetTokenRaceTime(19, CASSANDRA_TIME);
    SetTokenRaceTime(20, JAX_TIME);
    SetTokenRaceTime(21, CHAMP_TIME);
}

This code changes the custom tokens (I have a similar one for Tatooine) in the dialogues so that the times displayed in dialogues are the ones I want. But at the end of each race the game uses a global variable to check whether the player has won or lost and that variable is set using the original racing times. 

That's why I tried to create a new WON/LOST conditional script that no longer depends on checking that variable but rather compares the Player's time in the last race (which is stored in CUSTOM17 for Manaan) and the opponent's record time.

The problem is that there is no way, it seems, to retrieve that CUSTOM17 value from script.

And that goes against what that person that wrote the tutorial said.

The reason I cannot try and change things directly in the original script is, of course, that it doesn't decompile.

Cheers!

Share this post


Link to post
Share on other sites

Thanks to AmanoJyaku's input, there's now a hacky workaround for decompiling most of the problematic Tatooine and Manaan scripts. Some still won't decompile, but these should be all of the relevant ones that fail specifically due to one of the globals in the Tatooine/Manaan includes.

Tatooine_Swoop_Registration_(tat_m17ae)_Scripts_Using_Include.7z Manaan_Ahto_East_(manm26ab)_Scripts_Using_Include.7z

  • Thanks 1

Share this post


Link to post
Share on other sites

Well, I am truly delighted.

Thanks, AmanoJyaku! And DP too, of course.

I cannot even begin to count how many headaches having these means one year ago would have spared. :cheers:

Share this post


Link to post
Share on other sites

I might not be remembering this clearly since it's been a while since I took a look at custom tokens, but as I recall they are limited to the dialogue that's currently active. If you want to keep track of a value for longer than that, you  need to use local or global variables.

And if I'm remembering correctly, that would also mean it should be safe to use whatever number you want. The game uses some consistently, like 30s for computer spikes and 40s for repair parts, and it would be safe to avoid the ones used by the combat system, but apart from that it probably doesn't matter.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this