PDA

View Full Version : Journal Editing


Miltiades
07-29-2006, 11:54 AM
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?

stoffe
07-29-2006, 12:04 PM
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).

Miltiades
07-29-2006, 03:36 PM
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?

stoffe
07-29-2006, 03:49 PM
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).

Miltiades
07-29-2006, 04:33 PM
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.

Miltiades
07-30-2006, 08:54 AM
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.

stoffe
07-30-2006, 09:44 AM
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.

Miltiades
07-30-2006, 09:57 AM
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?

stoffe
07-30-2006, 11:06 AM
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.

Miltiades
07-30-2006, 11:34 AM
But if these items are all on different planets, how does the game know I have all items?

stoffe
07-30-2006, 07:26 PM
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.

Miltiades
07-31-2006, 09:37 AM
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?

Pavlos
07-31-2006, 10:10 AM
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.

Miltiades
07-31-2006, 10:27 AM
Wait, I'm not familiar with all this. Where do I find this .ut* file? And how do I include "k_inc_utility"? Do I copy&paste the whole stuff?

Pavlos
07-31-2006, 12:39 PM
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

stoffe
07-31-2006, 01:22 PM
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.

Pavlos
07-31-2006, 01:25 PM
Hmm, I'll have a check next time I load up the game. You are probably right as I scarcely use the event.

Miltiades
07-31-2006, 02:52 PM
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?

stoffe
07-31-2006, 04:19 PM
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 == "MySpecialItemTag")) {
AddJournalQuestEntry("MyQuest", GetJournalEntry("MyQuest") + 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("TagOfNPCHere");

if (GetIsDead(oNPC)
&& !GetIsObjectValid(GetItemPossessedBy(oNPC, "TagOfItemHere"))
&& GetIsObjectValid(GetItemPossessedBy(GetFirstPC(), "TagOfItemHere")))
{
AddJournalQuestEntry("MyQuest", GetJournalEntry("MyQuest") + 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("questchecker")) ) {
CreateObject(OBJECT_TYPE_PLACEABLE, "questchecker", 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. :)

Miltiades
07-31-2006, 05:15 PM
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?

Pavlos
07-31-2006, 05:23 PM
The Ondisturb doesn't work

Good to know. It clears up a few things.

Miltiades
07-31-2006, 05:43 PM
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?

Darkkender
08-01-2006, 02:04 PM
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.

stoffe
08-01-2006, 04:04 PM
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(), "st_testitem") ))
{
AddJournalQuestEntry("ST_TestQuest", 1);
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:
NPC has the quest item in their inventory, set to droppable
NPC has a custom script in the OnDeath event, as posted earlier, which spawns the checker placeable when they die.
The checker placeable's Heartbeat script continually checks if the player has picked up the quest item.
When the player has the item the placeable's script updates the journal and then destroys itself.

Miltiades
08-01-2006, 05:55 PM
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.

stoffe
08-01-2006, 07:45 PM
Hmm, this is hard to explain in an understandable way. I'll do an example to illustrate how I was thinking, since I have no idea what kind of quest you are doing... (I did like this for testing in my game and it seemed to work.)

Premise: 4 quest NPCs, each carrying a quest item. Journal updates each time the player picks up a datapad from a fallen NPC. When all 4 datapads are collected the quest is completed.

Files involved (all in override:
(All items have identical tags and ResRefs for simplicity.)

questnpc1.utc (the first NPC)
questitem1.uti (item carried by questnpc1)
questchecker1.utp (placeable spawned on questnpc1's death)
questchecker_hb1.ncs (heartbeat event script of questchecker1 placeable)
death_questnpc1.ncs (OnDeath event script of questnpc1)

questnpc2.utc (the second NPC)
questitem2.uti (item carried by questnpc2)
questchecker2.utp (placeable spawned on questnpc2's death)
questchecker_hb2.ncs (heartbeat event script of questchecker2 placeable)
death_questnpc2.ncs (OnDeath event script of questnpc2)

questnpc3.utc (the third NPC)
questitem3.uti (item carried by questnpc3)
questchecker3.utp (placeable spawned on questnpc3's death)
questchecker_hb3.ncs (heartbeat event script of questchecker3 placeable)
death_questnpc3.ncs (OnDeath event script of questnpc3)

questnpc4.utc (the fourth NPC)
questitem4.uti (item carried by questnpc4)
questchecker4.utp (placeable spawned on questnpc4's death)
questchecker_hb4.ncs (heartbeat event script of questchecker4 placeable)
death_questnpc4.ncs (OnDeath event script of questnpc4)

global.jrl (one new Journal category added, with 4 quest stages)


Note: The first/second/third/fourth label for the NPCs do not mean they must be fought in that order, it's just a way of saying there are four encounters, and group together the files used for each encounter. In this case it won't matter in which order the player encounters them and retrieves the datapads.

File relations:

Each NPC UTC has their death_questnpc* script assigned to their respective OnDeath event slot (the ScriptDeath field if using a GFF editor).
Each NPC UTC has their questitem* UTI item added to their inventory, set to droppable.
Each questchecker* placeable UTP has their questchecker_hb* script assigned to their Heartbeat event slot (the OnHeartbeat field if using a GFF editor)


Important: The UTP files must not have the Static field set, otherwise their scripts won't run. Use "plc_invisible.utp" as a blueprint for making your placeable UTPs.

Sequence of events:

Player fights and kills one of the NPCs.
When the NPC dies their questchecker invisible placeable is spawned.
Player loots the questitem from the fallen NPC.
The questchecker placeable detects this, updates the journal stage to the next and deletes itself.
Repeat for the other 3 NPCs. When all 4 datapads are picked up the quest is moved over to the "Completed" section of the Journal.



New addition to global.jrl:

Categories
117 (dynamic if you use a mod installer to modify global.jrl)
Name = "Capture the Datapads"
PlanetID = -1
PlotIndex = 1
Priority = 0
Tag = "ST_Q_FindDatapads"
EntryList
0
End = 0
ID = 10
XP_Percentage = 0.0
Text = "I have killed an enemy and found a nice datapad among his possessions."
1
End = 0
ID = 20
XP_Percentage = 0.0
Text = "On the body of another fallen enemy I found another nice datapad."
2
End = 0
ID = 30
XP_Percentage = 0.0
Text = "Yet another vanquished foe was lugging around a nice datapad. I think I see a pattern here..."
3
End = 1
ID = 40
XP_Percentage = 1.0
Text = "I have looted a fourth datapad from the battered remains of an enemy. I think my collection is now complete. Yay!"



Script code to compile to death_questnpc1.ncs:

void main() {
if (!GetIsObjectValid(GetObjectByTag("questchecker1"))) {
CreateObject(OBJECT_TYPE_PLACEABLE, "questchecker1", GetLocation(OBJECT_SELF));
}

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


Script code to compile to death_questnpc2.ncs:

void main() {
if (!GetIsObjectValid(GetObjectByTag("questchecker2"))) {
CreateObject(OBJECT_TYPE_PLACEABLE, "questchecker2", GetLocation(OBJECT_SELF));
}

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


Script code to compile to death_questnpc3.ncs:

void main() {
if (!GetIsObjectValid(GetObjectByTag("questchecker3"))) {
CreateObject(OBJECT_TYPE_PLACEABLE, "questchecker3", GetLocation(OBJECT_SELF));
}

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


Script code to compile to death_questnpc4.ncs:

void main() {
if (!GetIsObjectValid(GetObjectByTag("questchecker4"))) {
CreateObject(OBJECT_TYPE_PLACEABLE, "questchecker4", GetLocation(OBJECT_SELF));
}

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


Script code to compile to questchecker_hb1.ncs:

void main() {
if (GetIsObjectValid( GetItemPossessedBy(GetFirstPC(), "questitem1") ))
{
AddJournalQuestEntry("ST_Q_FindDatapads", GetJournalEntry("ST_Q_FindDatapads") + 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));
}
}


Script code to compile to questchecker_hb2.ncs:

void main() {
if (GetIsObjectValid( GetItemPossessedBy(GetFirstPC(), "questitem2") ))
{
AddJournalQuestEntry("ST_Q_FindDatapads", GetJournalEntry("ST_Q_FindDatapads") + 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));
}
}


Script code to compile to questchecker_hb3.ncs:

void main() {
if (GetIsObjectValid( GetItemPossessedBy(GetFirstPC(), "questitem3") ))
{
AddJournalQuestEntry("ST_Q_FindDatapads", GetJournalEntry("ST_Q_FindDatapads") + 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));
}
}


Script code to compile to questchecker_hb4.ncs:

void main() {
if (GetIsObjectValid( GetItemPossessedBy(GetFirstPC(), "questitem4") ))
{
AddJournalQuestEntry("ST_Q_FindDatapads", GetJournalEntry("ST_Q_FindDatapads") + 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));
}
}


While it makes this post look like a christmas tree; I have color coded the different parts to make it a little easier to see how they fit together. :)

* * *

All this is, of course, a workaround so that you won't have to modify any of the modules in which these encounters occur. Normally, if you'd be designing new modules from scratch where the encounters occur you'd use the module's Mod_OnAcquirItem event instead to detect when the player picks something up. That would require modifying the module.ifo file and shipping the whole modified module file with your mod, which is extreme overkill (and a source for incompatibilities with other mods) unless you make significant other changes to the module or its contained area, or make your own new module.

Using dynamically spawned placeables checking for the player acquiring items instead makes it easier to insert into existing modules without having to modify them directly, and the end result would be essentially the same (though there are a few more scripts and template files involved).

Using placeables with a heartbeat script instead of starting a recursive function on the NPCs death also ensures the player can leave the area and return between killing the NPC and picking up the datapad without breaking anything if, for some reason, they decide to do so. :)

Lit Ridl
08-02-2006, 04:37 AM
That is a perfect tutorial! I'll try PM T7 to add it!

Miltiades
08-02-2006, 09:30 AM
void main() {
if (GetIsObjectValid( GetItemPossessedBy(GetFirstPC(), "questitem1") ))
{
AddJournalQuestEntry("ST_Q_FindDatapads", GetJournalEntry("ST_Q_FindDatapads") + 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));
}
}

This heartbeat script is different than the other heartbeat scripts you gave, is it not?

Anyway, thanks for the comprehensive explanation. This'll help a lot. Maybe the problems lays with the fact that I changed the +10 in the hb-script to +20 because that was the ID of the journal entry. I'll go check out right away!

Edit: Ahem... well, uh, it, uh, didn't work :p. The first journal entry of the quest I don't get by picking up an item, that only starts with the second journal entry. I don't think there's a problem there.

The .utp file has an inventory, do I have to put something in there?

stoffe
08-02-2006, 11:57 AM
This heartbeat script is different than the other heartbeat scripts you gave, is it not?


Huh? Not that I can see. The only thing that differs between the heartbeat scripts of the 4 placeables is the tag of the item they are supposed to check for.


Maybe the problems lays with the fact that I changed the +10 in the hb-script to +20 because that was the ID of the journal entry.


What you do is you retrieve the current quest stage, and then add enough to it to reach the next quest stage. Since an inactive quest has quest stage 0, 0 + 10 = 10, and the first quest stage in my example had ID 10. The next becomes 10 + 10 = 20, which is the ID of the next quest stage in my example etc...


Edit: Ahem... well, uh, it, uh, didn't work :p. The first journal entry of the quest I don't get by picking up an item, that only starts with the second journal entry. I don't think there's a problem there.


I have tested the example in the previous post on my game, and it worked fine there. Without knowing exactly how you have done things there is no way can tell what you have done differently or wrong. :)


The .utp file has an inventory, do I have to put something in there?

Uh, what UTP file? UTPs are placeable templates. Those used in my example were based on plc_invisible.utp, which has no model or collision data and is thus completely invisible unless it's made selectable. Those have no inventory as standard.

Miltiades
08-02-2006, 12:39 PM
Huh? Not that I can see. The only thing that differs between the heartbeat scripts of the 4 placeables is the tag of the item they are supposed to check for.

No, I mean it's different than the heartbeatscripts of your previous posts.


Uh, what UTP file? UTPs are placeable templates. Those used in my example were based on plc_invisible.utp, which has no model or collision data and is thus completely invisible unless it's made selectable. Those have no inventory as standard.

Yes, I used plc_invisible.utp and renamed it. But if you open the file, you can select objects to be in its inventory. I guess I don't have to do something with that.

Miltiades
08-02-2006, 01:36 PM
I took some screenshots to show you what I've done.

The placeable: http://images6.theimagehosting.com/placeable.th.gif (http://server6.theimagehosting.com/image.php?img=placeable.gif)

The heartbeat script: http://images6.theimagehosting.com/Heartbeat script copy.th.gif (http://server6.theimagehosting.com/image.php?img=Heartbeat script copy.gif)

The questchecker script: http://images6.theimagehosting.com/Questchecker script copy.th.gif (http://server6.theimagehosting.com/image.php?img=Questchecker script copy.gif)

The journal file: http://images6.theimagehosting.com/Journal file copy.th.gif (http://server6.theimagehosting.com/image.php?img=Journal file copy.gif)

stoffe
08-02-2006, 05:42 PM
I took some screenshots to show you what I've done.
(...snip...)


Well, the only thing I can see at a quick glance that looks out of the ordinary is that you haven't set the Struct Type value to match the List index for the structs in the EntryList of your quest in global.jrl. They all seem to have the value 0 in your screenshot. Perhaps that is what is causing the problem?

Miltiades
08-02-2006, 06:01 PM
I changed it, but it didn't fix the problem.

Miltiades
08-03-2006, 09:29 AM
Can I make a remark? In one of your previous posts (th large one), you showed how the journal file must look like. The PLanetID is 1, though then the quest would only be on Dxun. I thought -1 was what must be filled in. If what I say is wrong, please tell me.

Darkkender
08-03-2006, 12:10 PM
Can I make a remark? In one of your previous posts (th large one), you showed how the journal file must look like. The PLanetID is 1, though then the quest would only be on Dxun. I thought -1 was what must be filled in. If what I say is wrong, please tell me.

I'm not sure myself but when looking at Stoffe's example with that in it earlier I believe that may be a Boolean data type for PlanetID. However if it's not and it's the actual ID for a Planet then you want to set it with the ID for your first Planet when the Quest begins. If your on Dxun then it starts with a 1. Because the Global.jrl file is a Global file the values set in there are merely initializers that will get updated and changed as you proceed through the game. Everytime the game saves it likely will save changes of your global.jrl which might include a new PlanetID from the last time that your quest got updated.

stoffe
08-03-2006, 03:27 PM
In one of your previous posts (th large one), you showed how the journal file must look like. The PLanetID is 1, though then the quest would only be on Dxun. I thought -1 was what must be filled in. If what I say is wrong, please tell me.

The value in the PlanetID is a line number in the planetary.2da file to which planet your quest is supposed to be associated with.

However this has no effect at all on where the quest can be triggered. All the PlanetID field does is determine the name of the planet shown above the quest stage text on the journal screen in the game. So it has no bearing on whether your quest entries appear or not. It's just there to help the player keep track on where they need to be to do the quest. :)

For example, using the above example, with the PlanetID set to 1 the text for the first stage would be shown as:

Dxun:
I have killed an enemy and found a nice datapad among his possessions.

...on the journal screen in the game.

Setting the PlanetID field to the value -1 will disable the planet name above the quest text, which is usually what you'd want for a quest spanning multiple planets.

The missing minus character in front of 1 in the above example was a typo. :)

I changed it, but it didn't fix the problem.

Hmm... very odd. I don't know what else to suggest as it works fine in my game. :confused:

I've implemented the above example to make sure I didn't forget something important something this time: I inserted the encounters on Dxun, Dantooine, Nar Shaddaa and Korriban, and my custom journal entry shows up when the first datapad is picked up, is updated when the two others are picked up, and moved to the "Finished" section when the last pad is picked up.

Miltiades
08-03-2006, 06:07 PM
Oh, it does just that, add the name of the planet in the journal entry. Ok then.

Yes, I know it's really weird that it doesn't work. I double-checked it a couple of times, made a few changes sometimes, without any luck. I hope I don't need to start from the beginning every time I change something to this quest. The first entry is obtained through dialog, and from there I warped to another module, fought my way through and saved before encountering the NPCs.

-Could it be the fact that I work with GFF-editor and not with K-GFF?
-Or could it be that it doesn't work because the NPCs on the other planets who are supposed to be killed to get a journal entry only have been placed in their module, and at the moment don't have anything to get a journal entry from?