Tchos Posted July 7, 2013 Author Share Posted July 7, 2013 I worked some on the last planned quest conversation, which was fun. Gnome dialogue is fun to write. The quest itself was already in place, and functioning, but there was no conversation to start it and end it. Hooking up all of the possible nodes and assigning actions and conditions to them, on the other hand, is not fun. This time, though, I tried an organisational strategy of moving a chunk of dialogue out of the deeply-nested nodes, and into a new branch off the main node, with a kind of "header" describing what its purpose is. I then jump to the first actual dialogue in the branch as a link destination, and jump back into the main nodes as necessary. This one's a complicated one, since it needs the option of refusal as well as a "try again later" state if you either can't afford it or don't want to do it immediately, and it also plays a part in a much later quest. It also includes lines spoken by multiple speakers. Then I had to write a script to equip the equipment, as well as an unequip tag-based script to make sure you don't try to unequip it when you shouldn't. And also to unequip weapons and cloaks, which don't work well with the swimming animation. This is done with the consent of the player, of course, and in a safe environment, not just arbitrarily stripping gear. This didn't work for anyone except the main PC on the first try, and I traced it to the loop that cycles through the party with GetFirstFactionMember(oPC), which in fact needs to be GetFirstFactionMember(oPC, FALSE). The "FALSE" is for the boolean "PC Only". I don't know why the default TRUE doesn't work here, since all of the other checks I've used like GetIsPC() work on all party members, so why the other party members aren't "PCs" to this function is a mystery to me. Anyway, now it works, and that quest is finally hooked up to the dialogue. Still need to script the unequipping of the gear and the dialogue for the last part of the quest. Elsewhere, sometimes I'm having trouble with return dialogue. When using the gc_talkto() condition, sometimes it just skips right past it, even though I'm using it in its correct but non-intuitive way to check that the NPC has not been talked to. Since I have its counterpart action on the same node, perhaps that's the problem. What is the order of execution between conditions and actions? Surely it should check to see if a condition is true before it executes any actions on that node, so there shouldn't be any problem setting the condition to true on the same node that checks for it. At any rate, that doesn't seem to be the problem anyway, because I moved the action to set the variable to a subsequent node, and it still skipped past the initial first-time-speaker node. Link to comment Share on other sites More sharing options...
Tchos Posted July 9, 2013 Author Share Posted July 9, 2013 Dealing with the gnome dialogue and attendant quest feels like clearing a roadblock. I spent the day testing it and fixing problems that I found with the dialogue and the quest conditions, as well as the unequipping script. I had to make a special exception for one of the recruitable companions, who wears a special armour, because I learned that the "never show armour" property includes helms, despite helms having their own property (never draw helmet), which I thought would override it if it's set to allow drawing the helmet. It doesn't. I experimented briefly with using a VFX for the helm before settling on the unequip script solution. I also made the assistant a little more reactive to the quest states. There's still the second interaction for this NPC to deal with, but since it doesn't involve a new quest, it shouldn't take as long or need as complex a dialogue tree. I removed the gc_talkto() and ga_talkto() scripts from the conversations and returned to the way that always works -- just manually checking and setting a local variable. It should work with the general scripts, but maybe later I'll just make my own. As shown in my custom placeables thread, I also made a new placeable of a rack of pamphlets, for both decoration and function. I'm also really glad I made a custom item type for sheets of paper as mentioned a month or two ago, because a lot of my quest items are papers, and I didn't like using the "book" item type for a simple sheet of paper. Wrong weight and sound effect, for one thing. And using the "scroll" type is problematic since wizards can scribe spells onto them, which you don't want them to do to your quest items! Something that's somewhat exploded lately has been my use of custom tokens. I started using them early on for the taverns, but I've started using them extensively to provide running counts in the journal entries for quests, as well as in the perception scripts to allow NPCs to call out barks that incorporate information about the PC they're seeing (be it gender, race, class, or name if they know the PC), and most recently to allow the NPC to mention the current number of members of the player's party in a conversation. Like the other important data in my module, I keep a list of which custom tokens I'm using, and what they're for. Link to comment Share on other sites More sharing options...
Tchos Posted July 11, 2013 Author Share Posted July 11, 2013 Today I tackled another, simpler item from my to-do list, which was to add the captain of your rented ship to the cabin after a certain event, and give him an appropriate conversation for it, referring to certain quest stages and giving certain options. I know, very cryptic, but it's more of a progress report than a spoiler-fest. Just saying the to-do list is getting shorter. There's actually already another conversation with the captain in place while on the overwater map, but I didn't need to place an NPC for that. I also added a necessary limitation to the Hearthstone of Divine Recall. It will not work underwater, or across large bodies of water. I did this to avoid breaking quests that use area transitions to update the journal stages. Logically, as well, you shouldn't teleport back to town while you're at sea, and leave your boat out in the water. Though I suppose you might, if it were a matter of life and death. I wrote another simple utility script, again for the docks. This one simply reports the tag and location vector of whatever waypoint is nearest to the player's current location, because I didn't see any console command for making waypoints visible in-game. Yes, it really is easier to write these scripts than to get that area to actually open in the toolset. I also had it fire a VFX at the location of the waypoint so I can see exactly where it is. Since the town area is covered liberally with waypoints, chances are there's a waypoint close enough to where I want to spawn something, or send an NPC to walk to, or for something else to happen at. For more precision placement of things, of course, I'll still be using the vectors. I had trouble getting an inventory item to fire a conversation on activation. I tried a few ways, and one way almost worked, except that it only showed the first line of the conversation as a bark rather than actually running the conversation. I found a few forum posts about how to do it, but none of the suggestions worked. It didn't strictly need to be a conversation, though, so I just went about it in a different way. Still, it should have worked. Well, I ended up trying the code from the IP Speaker include file, starting with CreateIPSpeaker(). The trouble with this is that the scripts that fire throughout that process require some other object to be the "speaker". It's not like the way I've used ipoints before, which was to substitute for a speaker -- in other words, speaking directly to the ipoint as if it were a particular NPC, so that the conversation wouldn't be broken if the NPC dies or is destroyed. It seems that the IP Speaker scripts are solely for the purpose of making sure a conversation between the PC and some object fires, and then it goes away. But since I was having such trouble getting the conversation to fire in the first place, I kept at it. But nothing was happening. I'd activate the item, see the debug text saying the script was firing, and no conversation would start. I dug around in the scripts for a long time before determining that it had a special section dealing with the PC. It didn't take a PC object parameter. Instead, it got the PC inside the script where it starts trying to speak, and normally, that's GetFactionLeader(GetFirstPC()), except that it has an if statement looking for a global string CAMPAIGN_STRING_CUSTOM_IPSPEAKER, and if that exists, then it doesn't use that function, and instead looks for another script to execute to determine the PC. Why? Okay, so it's a campaign string. I traced it to something I could do, and found in my module load script a place where it specifies the name of the script. Naturally, I didn't have such a script, and so the IP speaker was just failing. So I went into the SoZ campaign folder and found the script and put it into mine. After that, it got a little further before stalling, telling me that oSpeaker was invalid. Since it's an inventory item that starts the conversation, the player could be anywhere when activating it, and so the "speaker" had to be something that would always be present -- like the inventory item itself. But that doesn't work! Why, I don't know. Because items just can't talk? Because the scripts look for the nearest object with that tag, and inventory items aren't near? Who knows. And the PC doesn't seem to have a tag, and since the scripts are all set up to get the object by its tag, it had to have a tag. So I decided to spawn a second ipoint manually, to act as the speaker for the IP Speaker. I scripted the activation of the item to create a blank generic ipoint. The conversation still wasn't firing! I enabled the debug text, and it kept telling me the speaker was invalid. Many tests later I tried spawning a wine crate instead of an ipoint, and surprise surprise, it worked! The conversation finally fired, between me and the wine crate. Interestingly, the wine crate blueprint is set to static, but I guess when you spawn something by script, it isn't static, despite what the blueprint's set to. So I compared the wine crate and the ipoint blueprints to see what the difference was, and aside from the static setting on the wine crates I didn't see anything different. I went to put the ipoint's resref back into the script, but this time I copied it from the field instead of typing it like I had the first time, and I saw that there's a space at the end of the resref. So it wasn't spawning because I didn't include the space in the string. I put the space into the string, and also set the ipoint's first name to the item's name so it wouldn't say "Ipoint" in the dialogue box, and it finally all worked. Then I tried actually doing the quest, and found that somehow, some way, firing this conversation breaks the quest. The updates don't happen, and I don't know if it's because the AoEs aren't getting created at all, or what. There's nothing in the conversation that affects those things at all. The conversation does nothing but destroy the ipoint that's acting as a speaker at the end of it. I can start the quest, get a couple of updates just find, but as soon as I fire the conversation, no more updates. It's a mystery. So I figured out how to get the IP Speaker working, but I can't use it here. The whole purpose of using a conversation was to make a rather long bit of text be broken up into sections, rather than as a long document in the item's description. But having the quest work at all was more of a priority, so I just abandoned the conversation and put it as a description. It wasn't a total loss, though, because I needed to figure out the IP Speaker system to use for the module's epilogue slide show. Link to comment Share on other sites More sharing options...
Tchos Posted July 20, 2013 Author Share Posted July 20, 2013 Today I finished the gnome dialogue. That was the second to last major step preventing the main quest from being completed. I just need to rough out the extra boss fight, and then I can send the module out to a few people who asked to beta test it, and polish up the remaining bits while they do. The beta will not include the epilogue scene, a few side quests, or all of the companions. Three companions are included, but their dialogues are not finished -- just functional. It turns out there was more I needed to do before I could release the beta test. I had left the sahuagin in an unfinished state, and I needed to create their proper classes, looks, powers, properties, and inventories. The looks are unfortunately limited to colour changes, scaling, some armour pieces, and weapons. Armour displays only as a colour change on the creature, which looks bad, so I have them set to not show their armour. There are no head pieces, which would help a good deal in differentiating them, but there are some good shoulder pieces in the form of turtle shells that some of them can wear. Source material claims that sahuagin priestesses undergo a ritual that leaves them yellow in colour, so this is how you can tell which ones they are. There is no specifically female model, though, which is unfortunate considering the prominence females have in their society, as their clerics are implied to all be female. Kuo-toas, on the other hand, seem to have a mixed population of clerics, despite the source material (Drow of the Underdark) only outlining a female cleric (called a Whip). Anyway, I also created specialised loot tables for these creatures, with variations by class, with a couple dozen new items appropriate to their society and what they value as described in the manuals. Also wrote a user-defined event script to apply certain behaviours to the sahuagin in response to player actions, which are described in the source material. I finished the epilogue script after all, and wrote the epilogue cinematic (one of the only places where I use cinematics), though it still needs slide images to show during the text. It does have a shortcoming in that it assumes that your character would opt to be recognised as a hero, when some characters may prefer to slip out of town before the fanfare begins. Now for a test to see if it's ready to send out. Link to comment Share on other sites More sharing options...
Tchos Posted July 21, 2013 Author Share Posted July 21, 2013 In my full run-through playtest, I amassed a huge list of bugs to fix. The first was somewhat amusing, as I was surprised to find a sahuagin high priestess waiting for me at the office of the harbourmaster, because I had placed one in there just to check her tinting, and must have had the area open when I did a full save to save the blueprints. I also found in that instant that the plot-essential harbourmaster NPC was level 1 and not set to plot. Other issues involved things like one companion not gaining XP, despite his initialisation dialogue actions being exactly the same as the others, while a different companion was not dismissed from the party like the others are when you choose to edit your roster. More things like conversations not showing the correct node depending on quest stages, and one spot where I missed a loophole that could cause a quest not to complete. I think I've taken care of those now. The creatures that I created before I started using the SoZ loot system are now a bit embarrassing because they all drop the exact same things. So I spent a few hours making appropriate random loot tables for them, with a lot of custom loot, and also giving them the new random perception and death barks. I somehow either forgot to assign a deathscript to a certain NPC, or the script was somehow lost, which activates an object that the player should be able to interact with. During an important conversation, party members are pre-emptively attacking creatures that should be non-hostile. I retooled the script to use the ginc_group functions instead, so we'll see how that works. Needs another run-through. Link to comment Share on other sites More sharing options...
Tchos Posted July 22, 2013 Author Share Posted July 22, 2013 I could be wrong, but I think the function notes may be wrong for GetLocalInt(). It claims that if the object doesn't exist, then it returns 0. But I had a door with an int of 1 on it, and I was depending on it returning 0 if the door were destroyed. But the condition on the conversation that checks that was behaving as if the door still existed and had a 1 on it. I reorganised the conversation to work differently so that it doesn't matter, but that was annoying. Using the group functions fixed the other problem with the companions attacking the not-yet-enemies. I had to read through how the scripts were working to determine that group assignments persist outside of the current script. I've fixed almost everything else, except that my barbarian companion still isn't gaining any XP, while the others are. And they're all given the same amount of XP when they're first added to the roster, although I tried giving him a slightly higher number since it wasn't working. Still didn't work. Need to do another runthrough to make sure my other fixes work. Link to comment Share on other sites More sharing options...
Tchos Posted July 23, 2013 Author Share Posted July 23, 2013 While testing again, I reached the cave, only to find the game constantly crashing when I approach a certain spot. That did not bode well. I determined that there was a trigger around that spot, so I tried removing it, but it didn't help. Next, I determined that it seemed to happen as the sahuagin were becoming visible, so I moved them further away. This time I was able to go into the cave further before it crashed, so I disabled their spawns. Once I did that, the cave no longer crashed.So something's wrong with the sahuagin. Possibly something to do with the new script I placed on them, or possibly an error with the loot tables.First I'll try disabling the special script, then I'll try disabling the loot. If it's the loot, I'll have to do a lot of checking. It also might be only certain sahuagin crashing it, so I'll try spawning them one at a time. Link to comment Share on other sites More sharing options...
Tchos Posted July 23, 2013 Author Share Posted July 23, 2013 After setting up a conversation so the I could spawn each of the sahuagin types at will, I found it was crashing on the high priestess. I traced the trouble to the visual effect I had placed on her. It displays in the toolset, but after removing it, she no longer crashed the game. I'll put the custom script back on them and see how that works. Luckily, I didn't get as far as needing to adjust the loot, though now that they're not crashing, I see I do need to lower the percentages of their drops. I had to add another 2da to the hak, this time being vfx_persistent.2da, because I needed a blank AoE larger than the default 5-unit custom AoE. There's some very interesting information in that 2da file, and I can see many new possibilities in using it, but for now I just created a blank 10-unity AoE with no specified scripts that I can manage through my scripts, even though I should be able to use any of those and override their scripts. The function doesn't include changing their SEFs, but I could probably use a different function for doing that after I create the AoE. Simpler just to have a blank one to start with, though. I could also use a 15- and 20-unit one for later. This one, at least, didn't give me any problems. Link to comment Share on other sites More sharing options...
Tchos Posted July 27, 2013 Author Share Posted July 27, 2013 Lowering the loot drop percentages on the sahuagin helped a lot. I must remember what is obvious in retrospect, that the larger the loot table is, the lower I need to make the percentage chance of each item dropping, since each item gets its own roll. I searched in vain for a function that would allow me to dynamically adjust a creature's CR via script. I suppose that if I want to do that, I'll have to create duplicate blueprints with the alternate CR, and use a script that selects and destroys the existing creatures, replacing them with their alternate CR counterparts. I hope this doesn't induce a Capgras delusion in my players, though of course in this case, it would not be a delusion. I was making some edits to the cave to make it usable for the beta despite having a whole section missing, by putting in a way to skip past the unfinished areas, and I since the sahuagin were working, I fixed up the quest involving them. I ended up having 3 deathscripts on them, chained through hooks I put in my custom scripts, so that they would get their barks, the quest updates, and the default death behaviour as well. I couldn't help it. I wrote another massive script to facilitate a particular archetype of questing -- that being the collection quest, or the gathering quest. I had already written similar scripts to handle kill quests and exploration quests, but the idea is to be able to put an essentially all-in-one script for the archetype that can handle a wide variation of subtypes. My first one was the kill quest, and I used a somewhat rougher method in that first attempt, which requires more variables than it should. I should recode that one later to take advantage of these more compact and self-sufficient routines. Importantly, this tracker allows an arbitrary number of different kinds of placeables to manipulate, each of which can have a different number of them that need to be used, and different results and feedback when you do, and of course you can don't have to do them in any particular order (if you want a specific order, you'd have to make a separate quest stage for that). It's not limited strictly to gathering, but also works for destroying placeables, using items on placeables, or just activating them in some way. It tracks and displays the quest progress through a combination of the journal, local variables, and custom tokens, and the main goal was to automate as much as possible so that a new quest or a new sub-goal could be added simply, and it requires just a single script to be placed in the placeable's On Used slot, and the addition of some variables on the placeable. I was very happy to see it working. I tested it by starting with a single set of placeables to manipulate, and when that worked, I added a second set, and then a third, just to make sure I could complete the manipulations in any order no matter how many sets I added. Link to comment Share on other sites More sharing options...
Tchos Posted July 27, 2013 Author Share Posted July 27, 2013 I needed a "timer" for a quest, so that an NPC could ask you to return the next day for a quest reward (because she needed a day to make the reward). Such a thing could also be used in situations such as in The Witcher, in which an NPC might get angry with you and not want to talk to you, but would calm down after a few hours had passed and be willing to talk again. I looked first in the built-in functions and the ginc_time include, and then in all includes through EC's Super Include, but couldn't find what I needed. I did a search online, and found a few proposed solutions, but they seemed too complicated for what I wanted, and I certainly didn't want to deal with delayed commands or heartbeats or temporary object creation. The game is keeping track of the passage of time already, as I can see by it telling me the exact time, plus the day, month, and year when I start the module. I just wanted an absolute game time function that I could store and compare, so nothing has to be running in the background or making automatic checks or anything -- just set it once, and check it when you need to, comparing the time now with the time then to get the time passed. But all I found were strings and wrap-around hour things that looked more suited to other tasks than what I had in mind. Finally I found a function that set it up the way I wanted it in the CSL, creating a single integer made from the year, month, day, and hour. int CSLTimeStamp() { int iYear = GetCalendarYear(); int iMonth = GetCalendarMonth(); int iDay = GetCalendarDay(); int iHour = GetTimeHour(); return (iYear)*12*28*24 + (iMonth-1)*28*24 + (iDay-1)*24 + iHour; } So I made a ga_ script to start the timer (setting a local int on the NPC containing the current time stamp), and a gc_ script to report how many hours had passed (subtracting the stored time stamp from the current time stamp), and I used the same comparison function as is used in the gc_journal_entry and gc_local_int so I could show certain dialogue options only if 24 hours had passed since the timer had started. It seems like such a basic thing that you'd want for all kinds of things, so I can't believe this wasn't built right in. Maybe there was a function of that sort in the default assets, but I sure couldn't find it, and it probably wouldn't have been needed in CSL if there were one. Link to comment Share on other sites More sharing options...
Recommended Posts