Script Idea - packing a crate
#1
Posté 14 janvier 2014 - 03:17
So far I have the PC talk to the NPC.
- NPC conversation spawns a sack 'placeable' on a random pallet "waypoint" when player says something like "Ready to get to work."
- PC uses the sack 'placeable' and the sack 'placeable' disappears.
- A sack 'item' now appears in the PC's inventory.
- PC puts sack 'item' in crate 'placeable container'
That is what happens so far, but what must happen next is:
- Crate 'placeable container' counts sack 'items' in it's own inventory.
- When (5) sack 'items' are in the inventory of the crate placeable container the quest is completed.
Miscellaneous Things:
- Sack 'item' weight needs to be about 50 lbs. and I can't seem to find how to adjust the weight.
#2
Posté 14 janvier 2014 - 03:54
Or, you can add a property to it that increases its weight in the toolset, under Item Properties. (Edit: No, you can only reduce the weight there.)
For the crate, make a script to put in its "On Inventory Disturbed" slot (or On Closed), so it'll count up the necessary item when the player closes it.
I think it may need to be in On Closed, not Disturbed, because when I did a similar thing, I put it in the On Closed slot. Mine looks for separate, uniquely-tagged items, though, so it's not quite what you're wanting.
Modifié par Tchos, 14 janvier 2014 - 04:22 .
#3
Posté 14 janvier 2014 - 06:32
I'm thinking the On Closed script might be the best route now that you mentioned it.
Here's what I have so far:
//Put this script OnClose
void main()
{
object oPC = GetLastClosedBy();
if (!GetIsPC(oPC)) return;
if (GetItemPossessedBy(oPC, "warehouse_goods1")!= OBJECT_INVALID)
{
oTarget = GetObjectByTag("warehouse_goods1");
//What should I do to get the 'check for item' to happen on the placeable crate container and not the PC? What is the command for quantity?
DestroyObject(oTarget, 0.0);
AddJournalQuestEntry("The Quest", 1, oPC, TRUE, FALSE);
}
}
#4
Posté 14 janvier 2014 - 08:57
// Put this script OnClose
// - relies on "warehouse_goods" (items in crate) all having the same Tag.
void main()
{
int iTotal = 0;
object oGoods = GetFirstItemInInventory();
while (GetIsObjectValid(oGoods))
{
// count how many Goods are in the crate
if (GetTag(oGoods) == "warehouse_goods")
iTotal++;
if (iTotal == 5)
break;
oGoods = GetNextItemInInventory();
}
// if the quota is met
if (iTotal == 5)
{
object oPC = GetLastClosedBy();
if (!GetIsPC(oPC))
return;
AddJournalQuestEntry("the_warehouse_goods_quest", 1, oPC);
iTotal = 0;
oGoods = GetFirstItemInInventory();
while (GetIsObjectValid(oGoods)
&& iTotal <= 5)
{
// don't destroy more than 5 Goods per onClose
if (GetTag(oGoods) == "warehouse_goods")
DestroyObject(oGoods);
iTotal++;
oGoods = GetNextItemInInventory();
}
}
}sorta like that?
Modifié par kevL, 14 janvier 2014 - 09:00 .
#5
Posté 14 janvier 2014 - 09:07
...
#6
Posté 14 janvier 2014 - 10:37
Just for future reference, since kevL took care of the script, but if you put a script in an object's script slot, like On Closed, then you can refer to that object with OBJECT_SELF. So just replace oPC with OBJECT_SELF.koundog1 wrote...
//What should I do to get the 'check for item' to happen on the placeable crate container and not the PC?
#7
Posté 15 janvier 2014 - 12:14
I am learning so much about scripting and it is really really appreciated.
EDIT:
Since you guys are so knowledgeable and you've been so helpful I might add to this question.
I've created a conversation, for the quest giver (NPC_2), which has (5) possible NPC intro stems from the conversation root.
- Each stem should give you the quest of loading the crates with (5) items; previously mentioned.
- Each completion will allow you to talk to NPC_1 and be paid 1 or 2 gold depending on whether you successfully rolled on the appraise skill after talking to him earlier.
- Each completion will allow the PC to proceed to the next stem in the conversation.
- By the fourth stem the PC will have done enough of loading crates. He will have also learned some valuable information from the other workers and finally, he will have earned the respect of everyone in the warehouse thus allowing him to proceed should he desire.
- The story can go on and he can meet the contact that NPC_2 will tell him about or NPC_1 will offer other side jobs.
- By the fifth conversation stem the PC can work as many times in the warehouse as his little heart desires for the pitance of 1 or 2 gp...
no hope for a raise... no hope of ever going on a fantastic adventure... oh gee sorry we aren't talking about my personal problems here are we? lol!
- Bottom line is that the quest should be repeatable, but that the first four conversations will have all of the interesting particulars. For that reason I'm wondering if going the quest journal route is the best option. I'm trying to become more acquainted with variables or coins or whatever they are called. But before I delve into it I thought maybe you guys might have some more useful suggestions.
Modifié par koundog1, 15 janvier 2014 - 04:41 .
#8
Posté 15 janvier 2014 - 08:37
#9
Posté 15 janvier 2014 - 10:13
I'd tend to go with whatever's easiest, in my mind, to use. ie, the syntax for GetLocalInt/SetLocalInt, gc_local_int/ga_local_int are etched up there (here). But Tchos has been a big proponent of using the journal and i'd, err, have to look it up but it strikes me as a really good method if&as applicable! For example, when using locals, where do ya put them? on the Crate, on the Area, on the Module, use a global??? On the other hand, you always know where the journal entry is.
But sometimes all ya needs a local... SetLocalInt(OBJECT_SELF, "Done", TRUE); ( not in your case here tho )
Personally I'd have to look more into the two scripts, ga_journal & gc_journal, before using the method. That is, i notice that gc_journal simply uses local_ints on the PC, with a varname that starts with "NW_JOURNAL_ENTRY", +sQuestTag appended. But does ga_journal actually write a local? not that i see.... a test is in order there before i'd stamp it Reliable. Ofc a fella can write their own gc/ga scripts but, everything has a butt except 1==TRUE and 0==FALSE
although NwN2 has even pushed my boundaries on that,
/shrugzorz
ps. coins? I think you mean tokens, related to locals but not quite.
#10
Posté 15 janvier 2014 - 05:49
I can't know for certain, but without further information one way or the other, my conjecture is that the hard-coded function works the same way as gc_journal does it, and is the same way the journal was handled in NWN1, but with the hard-coded function AddJournalQuestEntry() you just can't see what it's doing. The main evidence is that you can use one method to set the journal entry, and it still works if you use the other method to check it. In other words, yes, the journal's current status is handled with local variables on the PC. But I'd welcome more testing to confirm that.kevL wrote...
Personally I'd have to look more into the two scripts, ga_journal & gc_journal, before using the method. That is, i notice that gc_journal simply uses local_ints on the PC, with a varname that starts with "NW_JOURNAL_ENTRY", +sQuestTag appended. But does ga_journal actually write a local? not that i see.... a test is in order there before i'd stamp it Reliable.
The important thing is to ensure the ga_ function is set to update the journal on all PCs, or else you can have inconsistent results if you start a conversation with a party member other than the main PC. That's one of the reasons that I wrote replacement ga_ and gc_ scripts for my campaign which always set the journal entries on the First PC.
The thing that sets the journal apart from any other local ints is that it gives you a built-in, easily accessible spreadsheet that allows extensive commenting and description, so you know exactly what the different integers (the quest stages) mean.
#11
Posté 15 janvier 2014 - 06:08
{0 or 10: PC is not on the quest, or got the quest from X and has not found any [quest item]s yet}
----{10: PC got quest from X}
---------{Set quest to 20}
----{Some other things}
---------{Set quest to 20}
{15: PC found some [quest item]s, but had never talked to X and thus was not yet on the quest}
----{Set quest to 20}
{20: PC was sent by Y to find her [quest item]s, but doesn't have them all yet, and may not have any of them}
{25: PC found all items before talking to Y}
----{SET QUEST TO 40}
{30: PC was sent by Y to find her [quest item]s, and has them all.}
----{SET QUEST TO 40}
{>30: Y's part in the quest is finished, but she has not yet [left].}
I obscured some of the details to avoid spoilers. I also left out the actual dialogue and most side branches, to show just the skeleton of the conversation based purely on the journal. There are other ways of checking things, such as actually checking your inventory to see if you have items, but I set this up in such a way that finding the items updates the journal.
There are other conversation structures, such as the fall-through method, and I used that for my earlier conversations, but I like this method better.
#12
Posté 15 janvier 2014 - 07:21
Tchos wrote...
The thing that sets the journal apart from any other local ints is that it gives you a built-in, easily accessible spreadsheet that allows extensive commenting and description, so you know exactly what the different integers (the quest stages) mean.
Furthermore, it's avaible for the player to look at right in the journal gui. I've actually started prefacing my journal entries with the state number, just so I know exactly where the player is in the quest when they report a bug.
ga_journal is also easily used from the console to update journal entries, while ga_localint needs a more specific pointer to the object with the local int.
#13
Posté 15 janvier 2014 - 08:38
My guess, too, is it's hardcoded. The only other reference i find to "NW_JOURNAL_ENTRY" is in ginc_journal:
int GetJournalQuestEntry(string sPlotID, object oCreature)
{
int iQuestEntry = GetLocalInt(oCreature, "NW_JOURNAL_ENTRY" + sPlotID);
return iQuestEntry;
}( for those times when PC isn't in dialog as GetPCSpeaker )
btw, just noting the 2 dialog scripts are
- ga_journal
- gc_journal_entry
and, in case koundog's wondering: 0==FALSE, anything other than 0 is TRUE...
(except in NwN2 where half a function is hardcoded and the other half isnt
Modifié par kevL, 15 janvier 2014 - 08:39 .
#14
Posté 02 juillet 2014 - 03:08
// Put this script OnClose // - relies on "warehouse_goods" (items in crate) all having the same Tag. void main() { int iTotal = 0; object oGoods = GetFirstItemInInventory(); while (GetIsObjectValid(oGoods)) { // count how many Goods are in the crate if (GetTag(oGoods) == "warehouse_goods") iTotal++; if (iTotal == 5) break; oGoods = GetNextItemInInventory(); } // if the quota is met if (iTotal == 5) { object oPC = GetLastClosedBy(); if (!GetIsPC(oPC)) return; AddJournalQuestEntry("the_warehouse_goods_quest", 1, oPC); iTotal = 0; oGoods = GetFirstItemInInventory(); while (GetIsObjectValid(oGoods) && iTotal <= 5) { // don't destroy more than 5 Goods per onClose if (GetTag(oGoods) == "warehouse_goods") DestroyObject(oGoods); iTotal++; oGoods = GetNextItemInInventory(); } } }
sorta like that?
I'm happy with this script. Thanks KevL!
It logs how many objects I put in a container and then the quest updates.
The issue I'm running into now is that I need the script to update more journal entries, but I do not know how to make it distinguish which entry it should update.
Right now it only updates
AddJournalQuestEntry("job_laborer1", 21, oPC);
I need it to update several other journal entries.
AddJournalQuestEntry("job_laborer1", 21, oPC);
AddJournalQuestEntry("job_laborer1", 61, oPC);
AddJournalQuestEntry("job_laborer1", 101, oPC);
AddJournalQuestEntry("job_laborer1", 141, oPC);
AddJournalQuestEntry("job_laborer1", 181, oPC);
It also needs to update these quests if the player successfully roled on the appraise skill and is paid 2 gp after completion.
AddJournalQuestEntry("job_laborer2", 21, oPC);
AddJournalQuestEntry("job_laborer2", 61, oPC);
AddJournalQuestEntry("job_laborer2", 101, oPC);
AddJournalQuestEntry("job_laborer2", 141, oPC);
AddJournalQuestEntry("job_laborer2", 181, oPC);
So as the player finishes each quest they can ask to do it again. To the player it looks like they are grinding until they get to entries 141 and 181.
By that time the NPCs will explain they earned everyone's respect and they know of an opportunity that might be more profitable.
Quest 181 can continue to loop and the player can grind out 1 to 2 gp to their litle hearts desire. But with the appropriate journal entry finished new dialogue options appear and they can move on if they want.
So if journal entry 21 is completed then the script should check and then if yes then update to entry 61.
if journal entry 61 is completed then update to 101
if 101 is then update to 141
if 141 then 181
If 181 then reset 181?
I hope this makes sense.
#15
Posté 02 juillet 2014 - 04:44
[edit] k, this ought work better
// Put this script OnClose
// - relies on "warehouse_goods" (items in crate) all having the same Tag.
// - assumes PC can have only one of the two related quests.
void main()
{
object oPC = GetLastClosedBy();
if (!GetIsPC(oPC))
return;
int bLabor = 0;
int iState = GetJournalEntry("job_laborer1", oPC);
if (!iState)
{
bLabor = 1;
iState = GetJournalEntry("job_laborer2", oPC);
}
if (!iState)
return;
switch (iState)
{
case 1:
case 41:
case 81:
case 121:
case 161:
SendMessageToPC(oPC, "Talk to the foreman for instructions.");
return;
case 21:
case 61:
case 101:
case 141:
case 181:
SendMessageToPC(oPC, "Talk to the clerk to get paid.");
return;
case 31:
case 71:
case 111:
case 151:
case 191:
SendMessageToPC(oPC, "Talk to the clerk for instructions.");
return;
}
int iTotal = 0;
object oGoods = GetFirstItemInInventory();
while (GetIsObjectValid(oGoods))
{
// count how many Goods are in the crate
if (GetTag(oGoods) == "warehouse_goods")
iTotal++;
if (iTotal == 5)
break;
oGoods = GetNextItemInInventory();
}
// if the quota is met
if (iTotal == 5)
{
switch (iState)
{
case 11:
iState = 21;
break;
case 51:
iState = 61;
break;
case 91:
iState = 101;
break;
case 131:
iState = 141;
SendMessageToPC(oPC, "You have become a respected member of the community"
+ " and may continue loading goods to your heart's desire.");
break;
case 171:
iState = 181;
SendMessageToPC(oPC, "You have completed this quest"
+ " but may continue loading goods to your heart's desire.");
break;
}
if (bLabor)
AddJournalQuestEntry("job_laborer2", iState, oPC);
else
AddJournalQuestEntry("job_laborer1", iState, oPC);
// destroy goods
iTotal = 0;
oGoods = GetFirstItemInInventory();
while (GetIsObjectValid(oGoods)
&& iTotal < 5)
{
// don't destroy more than 5 Goods per onClose
if (GetTag(oGoods) == "warehouse_goods")
DestroyObject(oGoods);
iTotal++;
oGoods = GetNextItemInInventory();
}
}
}--Modifié par kevL, 02 juillet 2014 - 04:37 .
#16
Posté 02 juillet 2014 - 04:55
I have a similar quest in a module I'm working on, except it involves carrying several heavy chests and dropping them onto a palette. The OnUnaquire script for the item checks to see if there is no valid owner (which means you dropped it on the ground), and if so destroys the dropped item and spawns a usable chest placeable that has no inventory. Clicking on the chest placeable picks it up (ie. destroys the placeable and spawns the item in your inventory).
I placed several ipoints with identical tags on the destination palette, which the OnUnaquire script checks for in a certain radius. Dropping a chest within range of one of the ipoints makes the spawned chest placeable non-usable and destroys that ipoint. It also positions the chest according to the location and bearing of the ipoint, so they look nice and neat on the palette and won't overlap each other. A conversation with someone in the same area as the palette checks how many of the ipoints remain, and when none do it makes a conversation node available that ends the quest.
I then had someone else give another quest to move the same chests to another palette in another area (you end up spending lots of quality time with those heavy chests). A script makes all the chest placeables usable again so they can be picked up, and spawns new destination ipoints elsewhere.
#17
Posté 02 juillet 2014 - 07:10
I need it to update several other journal entries.
AddJournalQuestEntry("job_laborer1", 21, oPC);
AddJournalQuestEntry("job_laborer1", 61, oPC);
AddJournalQuestEntry("job_laborer1", 101, oPC);
AddJournalQuestEntry("job_laborer1", 141, oPC);
AddJournalQuestEntry("job_laborer1", 181, oPC);
I have several quests in my campaign working the same way, and in your case I'd have something like
void UpdateQuest(string sJournal, int iQty)
{
object oPC = GetFirstPC();
if (sJournal == "") sJournal = "job_laborer1"; // refer to main quest by default
int nEntry = 21 + (iQty - 1) * 40;
AddJournalQuestEntry(sJournal, nEntry, oPC);
}
However this means your entries need to be sorted, so the "switch (iState)" solution is the way to go if you don't want to renumber.
But believe me, for your main quest/journal, you'd want to avoid all the "if" and "switch" lines, especially if you want it to be bilingual (here's how I do it for example:
10 text in English
11 text in French, male character
12 text in French, female character
20 same quest, second progress, English
21 same quest, second progress, French, male character
22 etc...
#18
Posté 02 juillet 2014 - 01:36
Hey KevL,
It's a strange pickle I've put myself in. Essentially, the player talks to a clerk who gives him the quest to talk to a foreman for work. If the player uses his appraise skill he can negotiate for 2 gp per job rather than 1 gp.
If he successfully negotiates 2 gp then the conversation gives the player the "job_laborer2" quest. If not then the conversation will give the "job_laborer1" quest and the player can only hope to earn 1 gp per job.
So the clerk gives the quest and it is posted at entry 1
The PC talks to the foreman.
Foreman explains the job and PC's journal updates to entry 11
The player puts the sacks in a crate and the journal updates to entry 21
The player talks again to the clerk, gets paid by the clerk, and the entry updates to 31
The player runs the quest again
goes to clerk journal updates this time to entry 41
goes to foreman journal updates this time to entry 51
places sacks in crate updates to entry 61
talks again to clerk,paid, and it updates to 71
player talks to clerk - 81
talk to foreman - 91
fills the crate - 101
reports to clerk for payment - 111
Pc to clerk -121
PC tp foreman - 131
to crate - 141
to clerk and is paid - 151
This last one should loop because by this time the 151 entry is completed then some new dialogue options appear and the player can pretty much move on or keep breaking his back doing physical labor.
Pc goes to clerk - 161
Foreman is overjoyed to see the character again, gives work - 171
loads crates the same as always - 181
gets paid with no raise in sight - 191
repeat and rinse to 161, 171 etc etc
The other kicker is that the crate needs to be able to update on both "job_laborer1" and "job_laborer2" iStates.
The only thing I could think of was to get conversations and journal entries to distinguish between the two pay rates. It seems like a messy way to do it, but I guess that's how Im doing it. The conversations are doing their part with conditionals, but I think the script on the crate wants to only update the journal back to ("job_laborer", 21, oPC)
@DannJ
That's a smart idea about the ipoints.
@4760
It's unfortunate I do not speak French and I'm going to revisit your idea because I'm not sure yet that it is what I am looking for. Still your answer is appreciated. ![]()
#19
Posté 02 juillet 2014 - 02:52
The way I see it, you've got two options:
1) put all journal entries in the same "job_laborer" journal, and it's a local variable (attached to the PC or to the clerk, doesn't matter) that will decide which half of the journal to use. Say that between 0 and 300 it's 1 gp, and 2 gp otherwise:
void UpdateQuest(int iEntry)
{
object oPC = GetFirstPC();
int bHighPay = GetLocalInt(oPC, "bHighPay");
if (bHighPay) iEntry+= 300;
AddJournalQuestEntry(sJournal, iEntry, oPC);
}
In this case, your journal entries 11, 21, 31 etc... will have to be 311, 321, 331 etc... for the 2 gp variante.
Note that in this case, you provide the exact entry number.
2) keep two separate journals, with the same structure. To update the quest, you'd have something like:
void UpdateQuest(int bHighPay)
{
object oPC = GetFirstPC();
// other possibility: bHighPay is a local variable, in which case not needed as a parameter
// int bHighPay = GetLocalInt(oPC, "bHighPay");
sJournal = "job_laborer1";
if (bHighPay) sJournal = "job_laborer2"
int iJournal = GetJournalEntry(sJournal, oPC);
iJournal += 10;
AddJournalQuestEntry(sJournal, iJournal, oPC);
}
Here, the journal entries have the same numbers.
Note that in this case, the entry is automatically updated (add 10 to the last entry), all you have to provide is the value to decide if it's job_laborer1 or job_laborer2 that will be used. But even this is not really necessary if you work with a variable (remove the // before int bHighPay, and of course delete "int bHighPay" in the function parameters).
#20
Posté 02 juillet 2014 - 03:52
No doubt I need to make this more simple.
But I'm afraid to combine the two journal strings because that means I'd need to go back and fix all the conversations that look for it. Complicated huh?
One bit of good news is that I think I could kill the journal entry that says the player has talked to the foreman.
It isn't necessary for triggering any of the events. The player can just load the crate and get an update telling them to talk to the clerk and get paid. Unless I could use it to tell the crate script which journal it should update. oh my stars this is making my head hurt.
#21
Posté 02 juillet 2014 - 04:39
ok, updated the script above.
It's pretty simple and I don't think you have to change much ... either in script or dialog
ps. Fine tuning depends on how the conversation is setup.
#22
Posté 02 juillet 2014 - 04:42
Try this out, but i warn you I don't quite understand what you're getting at w/ the two journal threads.
// Put this script OnClose // - relies on "warehouse_goods" (items in crate) all having the same Tag. void main() { int iTotal = 0; object oGoods = GetFirstItemInInventory(); while (GetIsObjectValid(oGoods)) { // count how many Goods are in the crate if (GetTag(oGoods) == "warehouse_goods") iTotal++; if (iTotal == 5) break; oGoods = GetNextItemInInventory(); } // if the quota is met if (iTotal == 5) { object oPC = GetLastClosedBy(); if (!GetIsPC(oPC)) return; int iState = GetJournalEntry("job_laborer1", oPC); switch (iState) { case 0: iState = 21; break; case 21: iState = 61; break; case 61: iState = 141; break; case 141: iState = 181; SendMessageToPC(oPC, "You have become a respected member of the community" + " and may continue loading goods to your heart's desire."); break; default: SendMessageToPC(oPC, "You have completed this quest" + " but may continue loading goods to your heart's desire."); break; } AddJournalQuestEntry("job_laborer1", iState, oPC); if (Random(20) < GetSkillRank(SKILL_APPRAISE, oPC)) { AddJournalQuestEntry("job_laborer2", iState, oPC); GiveGoldToCreature(oPC, Random(2) + 1); } iTotal = 0; oGoods = GetFirstItemInInventory(); while (GetIsObjectValid(oGoods) && iTotal <= 5) { // don't destroy more than 5 Goods per onClose if (GetTag(oGoods) == "warehouse_goods") DestroyObject(oGoods); iTotal++; oGoods = GetNextItemInInventory(); } } }--
Above, considers the two journal threads as running in parallel; 'laborer1' always updates on 5 items, while 'laborer2' updates in parallel only when an Appraise DC is successful.
The good news is that the script would only need to go on the crate so the crate itself won't be paying out 1 gp or 2 gp to the player. So if the player puts the goods in the crate then only the journal needs to update appropriately. The rest is worked out in conversations.
Conversations will look for which journal entry is updated in the conditions under
Script: gc_journal_entry
sQuestTag: job_laborer1 / job_laborer2
sCheck (String): blah blah
All the crate must do is empty like it's been doing when it gets full and then updating the journal so the player can get paid each time. Im just not sure of how it will distinguish which journal string it's on and then which entry its already done and which entry it should update to next.
#23
Posté 02 juillet 2014 - 04:57
yep. I updated the script a minute ago (removed payment &tc)
#24
Posté 02 juillet 2014 - 05:24
Thanks kevL,
You've really been a big help at this.
I did plug the script into the crate to see if it works, but it isn't responding.
The one you came up with back in January does but as you can see it doesn't fully answer the many needs this needy little quest has.
Any idea why? I noticed some lines missing at the beginning, but wasn't sure if that was to get another part to work or ..
#25
Posté 02 juillet 2014 - 05:43
Does PC have either journal "job_laborer1" or "job_laborer2" ? if not, the close event does nada.
i can put debug in to show where its failing ... if you want
The 'missing lines' aren't missing; they've been moved ( and the safety checks put up at the top instead )
oh, and make sure you have the latest version of the script ......





Retour en haut






