Jump to content

Home

Journal Editing


Miltiades

Recommended Posts

Global.jrl... my nemesis. Everything I've done so far involving modding (Not that much, to be honest) has worked, after struggling a lot. Except for the Global.jrl file.

 

The problem: I want to make a new Quest Entry. The name of the quest appears in-game, but not the text that explains the quest.

 

I followed the tutorial written by tk102 on how to make a new journal entry with K-GFF (added string under text field). But it didn't work.

 

Can it be the PlotIndex value or maybe the Struct ID that's wrong?

 

Can someone help me please?

Link to comment
Share on other sites

Global.jrl... my nemesis.

The problem: I want to make a new Quest Entry. The name of the quest appears in-game, but not the text that explains the quest.

I followed the tutorial written by tk102 on how to make a new journal entry with K-GFF (added string under text field). But it didn't work.

 

Make sure you've added the localized substring under the correct language id. If it does not correspond to the language setting in your game's dialog.tlk file nothing will be displayed. Substrings added with language id 0 are only visible if your game is set to English language.

 

Also make sure you, when activating the journal entry in-game, set the state to a valid entry ID.

 

Can it be the PlotIndex value or maybe the Struct ID that's wrong?

 

The struct ID should be the same as the list index of the struct in the Categories List field containing it, for JRL files. K-GFF doesn't seem to display the list index of structs in a list for some reason, so you'll have to check with the (Bioware) GFF Editor to verify that they are correctly set (it's the number in the leftmost column in the treeview there).

Link to comment
Share on other sites

Make sure you've added the localized substring under the correct language id. If it does not correspond to the language setting in your game's dialog.tlk file nothing will be displayed. Substrings added with language id 0 are only visible if your game is set to English language.

 

In the substring I can choose only which language it is, there's no language ID field to fill in a number. My game's English, although it's the international version. I hope that doesn't cause any problems. I don't think so, because filling in the title of the quest doesn't give any problems, that works, and it's done the same way as writing the text.

 

The struct ID should be the same as the list index of the struct in the Categories List field containing it, for JRL files. K-GFF doesn't seem to display the list index of structs in a list for some reason, so you'll have to check with the (Bioware) GFF Editor to verify that they are correctly set (it's the number in the leftmost column in the treeview there).

 

So if I want to add a new Quest entry, and I haven't added one before (or i haven't installed any other mods), then the struct ID would be 117?

Link to comment
Share on other sites

My game's English, although it's the international version. I hope that doesn't cause any problems.

 

English (language id 0) should work for both the US-English and EU/UK-English version, at least it works fine for me. :)

 

So if I want to add a new Quest entry, and I haven't added one before (or i haven't installed any other mods), then the struct ID would be 117?

 

That should be correct for TSL. The structs in the "EntryList" of each journal category works the same way, where the Struct ID should be the same as the list index (starting at 0 and counting up).

Link to comment
Share on other sites

This is really weird. When I type in just 'test' in the field, then in works. But when I have a text of a few lines, then it doesn't.

 

Edit: In fact, when I hit enter after one line, so that the next sentences begins after it, then it works. It doesn't work when I just keep wrighting without using enter to begin a new paragraph. Really weird.

Link to comment
Share on other sites

Question: Is there a limit on how much you can write for a journal entry? Could you write a text of 10 sentences for example? Because when I wrote about 5 sentences and it didn't work. But when I write one sentences (a short one), it works. Two short sentences also works. but if it's a bit longer, then it fails to work.

Link to comment
Share on other sites

Question: Is there a limit on how much you can write for a journal entry? Could you write a text of 10 sentences for example? Because when I wrote about 5 sentences and it didn't work. But when I write one sentences (a short one), it works. Two short sentences also works. but if it's a bit longer, then it fails to work.

 

The suggested max length for ExoStrings in the GFF specification is 1024 characters, though this is not enforced since I've seen strings longer than that used without any problems.

 

How do you enter the text in the Text field? Do you type it in directly in the GFF editor or do you copy&paste it from another source? If it's the latter it's possible you get some invisible formating/whitespace characters along which the game does not like.

Link to comment
Share on other sites

Well, I don't use GFF, I use K-GFF and use a substringto type in my text. I didn't use copy&paste, typed in in directly. And I haven't used more than 1024 characters.

 

Edit: Apparently, it works when I use Bioware's GFF Editor. Hmm... Well, thank you for your help, this was a huge obstacle for me to overcome.

 

Anyway, got another question. I want a new quest entry when I got an item a few times. For example, a enemy attacks you, and when you kill him, you take an item. Then you get a quest entry. To get the next quest entry, you need 3 more of that item. When you got four at last, you get that quest entry.

 

I believe I need to do this with a script. How do I do that and won't this give problems to the first quest entry, which I made using GFF Editor, knowing it's the same quest?

Link to comment
Share on other sites

Anyway, got another question. I want a new quest entry when I got an item a few times. For example, a enemy attacks you, and when you kill him, you take an item. Then you get a quest entry. To get the next quest entry, you need 3 more of that item. When you got four at last, you get that quest entry.

I believe I need to do this with a script. How do I do that and won't this give problems to the first quest entry, which I made using GFF Editor, knowing it's the same quest?

 

You'd just need one Quest entry for this, i.e. one new struct field in the Categories list in the global.jrl file. This entry would hold the name/title of the whole quest in the Name field. Then you add the different quest stages to the EntryList field of this quest. Each struct added to this list would be a step on your quest. To use what you said above, the global.jrl addition might look something like:

 

¤ Categories
  ¤ 117
     ¤ Name = "Kill and Loot"
     ¤ PlanetID = 1
     ¤ PlotIndex = 1
     ¤ Priority = 0
     ¤ Tag = "MyQuestKillAndLoot"
     ¤ EntryList
        ¤ 0
           ¤ End = 0
           ¤ ID = 10
           ¤ XP_Percentage = 0.0
           ¤ Text = "I have killed an enemy and taken an item from his corpse!"
        ¤ 1
           ¤ End = 0
           ¤ ID = 20
           ¤ XP_Percentage = 0.0
           ¤ Text = "I have found three more of the item I looted before!"
        ¤ 2
           ¤ End = 1
           ¤ ID = 99
           ¤ XP_Percentage = 0.0
           ¤ Text = "I have done something stupid and failed the quest. Oh no!"            
        ¤ 3
           ¤ End = 1
           ¤ ID = 100
           ¤ XP_Percentage = 1.0
           ¤ Text = "I have found all the noteworthy items! Yay!"

 

Then, when you need to trigger each step, you fire a script that runs the script function:

// Sets the first stage, when the first item is taken
AddJournalQuestEntry("MyQuestKillAndLoot", 10);

// Sets the second stage, where 3 items are taken
AddJournalQuestEntry("MyQuestKillAndLoot", 20);

// Quest has failed for some reason, moves it to the "Completed" List
AddJournalQuestEntry("MyQuestKillAndLoot", 99);

// Quest is finished, move it to the "Completed" list.
AddJournalQuestEntry("MyQuestKillAndLoot", 100);

 

This sets the quest stage, determining which text is displayed in the journal screen. The two last stages are set as "End" as well, meaning the quest will be moved over to the "Finished" List when those stages are set. Note that you can only set the stage to a higher value than is currently set, unless you set the third optional parameter to TRUE in the above function calls. (This is to prevent accidentally falling back to earlier quest stages if there are multiple solutions or trigger points for a quest.)

 

For containers you could run a script off their OnDisturbed event script to detect if the player has taken the item. For looting it off creatures you'd probably need to either run some heartbeat script that continually checks if the player has taken the item, or modify the module to add a script in the Mod_OnAcquirItemevent slot set in module.ifo.

 

To get the currently active state value (i.e. "ID" value in the GFF) you can usr the GetJournalEntry() function, where you specify the tag of the quest entry as parameter.

 

You can also set quest/journal entries directly from DLG files without using scripts.

Link to comment
Share on other sites

But if these items are all on different planets, how does the game know I have all items?

 

The way I'd do it would be to update the journal entry the first time you pick up one of the items. The journal works between modules, so it'll keep track of the progress. You could make states like this:

10 = First item picked up

20 = Second item picked up

30 = Third item picked up

40 = Fourth item picked up

50 = All items picked up

 

Then you could do like this when the player acquires one of the items:

AddJournalQuestEntry("MyQuest", GetJournalEntry("MyQuest") + 10);

 

Another way would be to use a global variable to count the number of items already picked up.

Link to comment
Share on other sites

To what do I attach this script then?

 

For looting it off creatures you'd probably need to either run some heartbeat script that continually checks if the player has taken the item, or modify the module to add a script in the Mod_OnAcquirItemevent slot set in module.ifo.

 

What's the easiest way and how do I do it?

Link to comment
Share on other sites

Attach it to the OnInventoryDisturbed event in the .ut*. You will want to make sure that you set a Local Boolean so that the container does not continually update the quest. A simple script such as:

 

#include "k_inc_utility"

void main() {

int nQuest = GetJournalEntry("MyQuest");
  if (UT_GetTalkedToBooleanFlag(OBJECT_SELF)) { //We'll use the TalkedTo Flag just for ease of use.
       return; //Do nothing if the flag is TRUE
  }

  UT_SetTalkedToBooleanFlag(OBJECT_SELF, TRUE);
  AddJournalQuestEntry("MyQuest", nQuest + 10);

}

 

Should do the trick.

Link to comment
Share on other sites

Well you don't have to include k_inc_utility. I often just do it out of habit (Same with k_inc_generic) as it has some useful functions in it. To gain access to the functions defined within an include script you just need to use:

#include "k_inc_foobar"

Replace the "k_inc_foobar" with the name of your include file. You can use as many as you want in one script.

 

I used an asterisk in ".ut*" as a wildcard. It can stand for anything. If you are using placeables for the quest then it would be a UTP; if you used creatures it would be a UTC and so on.

 

Copy and paste the script then compile it under whatever name you like. Plonk the script in the override folder before proceeding to put the script's ResRef (The name without the extension) into the correct field.

 

If you are using a UTC: Use the OnDisturbed field

If you are using a UTP: Use the OnInvDisturbed field

Link to comment
Share on other sites

I used an asterisk in ".ut*" as a wildcard. It can stand for anything. If you are using placeables for the quest then it would be a UTP; if you used creatures it would be a UTC and so on.

(...snip...(

If you are using a UTC: Use the OnDisturbed field

If you are using a UTP: Use the OnInvDisturbed field

 

Are you sure about this? I've never been able to get the OnDisturbed event to fire on dead creatures. It works on container placeables and such, but doesn't seem to trigger when looting things off dead bodies.

 

Which was why I suggested using an invisible placeable with a heartbeat continually checking if the player has taken the item, preferably spawned by the OnDeath event of the creature, and destroyed when the item is taken.

 

But if I've just made some mistake and the OnDisturbed event does work on corpses then that is of course the preferable solution.

Link to comment
Share on other sites

So if I want get a quest entry when I get an item of a creature/NPC, then I use the OnDisturbed, which I can find in the .UTC file? And I put the name of my script in the OnDisturbed field of the creature/NPC? Can I replace the name of the script that's already in there without it causing problems? Can I name my script whatever I want? And in the script itself, where do I put the name of the item? Or is that not necessary?

 

And if that doesn't work, how do I do the heartbeat thing? Is it also in the .UTC file, because I see OnHeartbeat in the list too?

Link to comment
Share on other sites

I put the name of my script in the OnDisturbed field of the creature/NPC? Can I replace the name of the script that's already in there without it causing problems? Can I name my script whatever I want? And in the script itself, where do I put the name of the item? Or is that not necessary?

 

And if that doesn't work, how do I do the heartbeat thing? Is it also in the .UTC file, because I see OnHeartbeat in the list too?

 

If the OnDisturb event actually does work on dead creatures, a script like this might work for it:

void main() {
   if ((GetInventoryDisturbType() == INVENTORY_DISTURB_TYPE_REMOVED)
       && IsObjectPartyMember(GetLastDisturbed())) 
   {
       string sTag = GetTag(GetInventoryDisturbItem());
       if (!GetLocalBoolean(OBJECT_SELF, 140) && (sTag == "[color=Red]MySpecialItemTag[/color]")) {
           AddJournalQuestEntry("[color=Yellow]MyQuest[/color]", GetJournalEntry("[color=Yellow]MyQuest[/color]") + 10);
           SetLocalBoolean(OBJECT_SELF, 140, TRUE);
       }
   }	
}

 

Change the part in red color above to the tag of your item to check for, and change the part in Yellow to the tag of your Journal Quest entry. Put it into a script, name it something valid (max 16 alphanumerical characters or underscore only), set the name (without the .ncs suffix) in the OnInvDisturbed event slot in the creature UTC. The default script in that slot is empty so it would not cause any problems to just replace it.

 

However, as said, I've never gotten those events to actually fire when looting creatures, just on placeable containers, so don't be surprised if it doesn't work. (And please let me know f it does, in fact, work. ;))

 

Another approach may be to use an extra object with a heartbeat script that continually checks if the player has taken the item from the corpse. To set that up, make a copy of the plc_invisible.utp placeable, make sure the Static flag is not set for it. In this example I'll call the file "questchecker.utp". Put the name of a script in the OnHeartbeat event slot for that placeable, whose content might look like:

void main() {
   object oNPC = GetObjectByTag("[color=Pink]TagOfNPCHere[/color]");

   if (GetIsDead(oNPC) 
       && !GetIsObjectValid(GetItemPossessedBy(oNPC, "[color=Red]TagOfItemHere[/color]"))
       && GetIsObjectValid(GetItemPossessedBy(GetFirstPC(), "[color=Red]TagOfItemHere[/color]")))
   {
       AddJournalQuestEntry("[color=Yellow]MyQuest[/color]", GetJournalEntry("[color=Yellow]MyQuest[/color]") + 10);
       RemoveHeartbeat(OBJECT_SELF);
       DestroyObject(OBJECT_SELF, 0.25, TRUE);
       return;
   }
   else {
       int bOdd = GetLocalBoolean(OBJECT_SELF, 140);
       SetLocalBoolean(OBJECT_SELF, 140, !bOdd);
       if (!bOdd)
           DelayCommand(1.5, ForceHeartbeat(OBJECT_SELF));
   }
}

 

Change the red part to the tag of your item, the pink part to the tag of the NPC carrying the item, and the yellow part to the tag of your Journal/quest entry.

 

Then you give the creature carrying the item a special OnDeath script that might look like:

void main() {
   if (!GetIsObjectValid( GetObjectByTag("[color=Pink]questchecker[/color]")) ) {
       CreateObject(OBJECT_TYPE_PLACEABLE, "[color=Pink]questchecker[/color]", GetLocation(OBJECT_SELF));  
   }

   ExecuteScript("k_ai_master", OBJECT_SELF, 1007);
}

...which would spawn the checker placeable when the creature dies. Change the part in pink to the tag of your invisible placeable which has your heartbeat script assigned.

 

 

 

You could, of course, also do the check in the Heartbeat script of the creature, but personally I prefer to keep it on a separate listener placeable since you have more control over when the check is performed then, so the creature won't do checks OnHeartbeat needlessly. The placeable's heartbeat would only run while the checks would need to be made, while the creature's would run for the duration of its (and its corpses) existance. :)

Link to comment
Share on other sites

The Ondisturb doesn't work (it was an NPC, no creature, so the field where I put my script was OnDisturbed and not OnInvDisturbed).

 

I'll try the Heartbeat script now.

 

Edit: Hmm, the Heartbeat doesn't work either. There's a lot to do for this one, and much chance of doing something wrong, but I don't think I did something wrong. This works both for creatures and NPCs I hope?

Link to comment
Share on other sites

Good to know. It clears up a few things.

 

Because the other one didn't work either for me, I would wait and see. I know of myself I can make really stupid mistakes. When I tried the Heartbeat one, I suddenly thought about the OnDisturbed: "Oh, ****, I forgot to compile". Really stupid, although it didn't work when I had compiled either.

 

Edit: Ha! Unbelievable how many stupid mistakes I've made. I could write a book of it. Although I've fixed my mistakes, the heartbeat thing still doesn't work. Does it matter that the item is a datapad? Because from the moment you get a datapad, it always opens it, which could prevent the journal to update or something?

 

Edit2: Another question that just raised my mind. The datapad that I made I also put in the inventory of the NPC. But is this still necessary with the Heartbeat method?

Link to comment
Share on other sites

The Ondisturb doesn't work (it was an NPC, no creature, so the field where I put my script was OnDisturbed and not OnInvDisturbed).

 

I'll try the Heartbeat script now.

 

Edit: Hmm, the Heartbeat doesn't work either. There's a lot to do for this one, and much chance of doing something wrong, but I don't think I did something wrong. This works both for creatures and NPCs I hope?

 

I can't recall all of the fields in the UTC files however there is a OnDeath field. When a creature dies that eventually becomes lootable it generates a bodybag placeable which is basically a temporary placeable that vanishes as soon as all items are removed.

 

What you could do with the OnDeath field is have the script run through a series of Actions. First it will give you the item using the GiveItem script function then it can update and give you the quest and update your quest. This way you don't have to worry about trying to mess with the modules ifo file.

Link to comment
Share on other sites

When a creature dies that eventually becomes lootable it generates a bodybag placeable which is basically a temporary placeable that vanishes as soon as all items are removed.

 

Doh... :fist: I knew there was something I had forgotten about. I've been playing too much Oblivion lately, mixing up how the games work. :) (The earlier script wouldn't work properly since the item is no longer in the inventory of the NPC when they are dead.)

 

Using the below simplified heartbeat script on the invisible placeable does work, at least it did when I just tested it a moment ago. :) The journal updated when I picked up my test-datapad from the corpse of an NPC. You'd need to make sure your items each have a unique tag though for it to work if you use this way.

 

void main() {
   if (GetIsObjectValid( GetItemPossessedBy(GetFirstPC(), "[color=Red]st_testitem[/color]") ))
   {
       AddJournalQuestEntry("[color=Yellow]ST_TestQuest[/color]", [color=Green]1[/color]);
       RemoveHeartbeat(OBJECT_SELF);
       DestroyObject(OBJECT_SELF, 0.25, TRUE);
   }
   else {
       int bOdd = GetLocalBoolean(OBJECT_SELF, 140);
       SetLocalBoolean(OBJECT_SELF, 140, !bOdd);
       if (!bOdd)
           DelayCommand(1.5, ForceHeartbeat(OBJECT_SELF));
   }   
}

 

Change the yellow text to the tag of the quest, the green to the state you wish to set the quest journal to, and the red text to the tag of the item.

 

Otherwise same setup as before, i.e:

  1. NPC has the quest item in their inventory, set to droppable
  2. NPC has a custom script in the OnDeath event, as posted earlier, which spawns the checker placeable when they die.
  3. The checker placeable's Heartbeat script continually checks if the player has picked up the quest item.
  4. When the player has the item the placeable's script updates the journal and then destroys itself.

Link to comment
Share on other sites

You'd need to make sure your items each have a unique tag though for it to work if you use this way.

 

Even if it's the same item?

 

Anyway, I changed it quickly, but it didn't work. I already spawned all the other NPCs on the other planets, with the same item, but without the script to get a journal entry. Could this be a problem?

 

And to be sure, the new script you gave replaces the second script (first of the Heartbeat method) of your previous post, or am I wrong? And about the new script, how will it know of which NPC it'll have to give a journal entry? In the previous script you could fill in what the tag of the NPC was.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...