DarthTyren 103 Posted March 16, 2020 First, let me go ahead and say that not only is this possible, it's actually extremely simple. It's also extremely tedious to set up as the global default, unless you're building from the ground up anyway. Now, a little background info: When I was younger and more naive (read: four years ago), I had the idea to start working on a massively ambitious project (read: total conversion mod) all on my own just to see how far I could push the envelope when it comes to modding. I was no novice when it came to ingenuity in KotOR modding, having already cut corners to introduce the community to a mod that had been wished for many times since the release of TSL (insert shameless PartySwap plug here). It quickly hit me that there was something else about the KotOR games that bugged the crap out of me, and that was that they were fully voiced - with exception to the most important character in both games. I quickly decided that would be the next hurdle I tackle. It didn't take too long to figure it out. That's right - I've been semi-selfishly sitting on this information for four years. Why? I don't really know, maybe because I wanted to present it to the community in a large project that I was happy to be a part of and blow everyone's minds. But it has become clear to me that others have been looking for a way to make it work, and I'd be remiss to not share my experiences with this particular endeavor. With all that said, here's a bit of a disclaimer: All instructions in this tutorial are given under the assumption of modding TSL, NOT KotOR. The reason for this is simple - TSL scripting, especially when it comes to dialog, is a lot more malleable. I'm not saying this is the only way to pull it off, it's just the easiest to illustrate. The first question is "How do we enable the PC to be a valid speaker?" The answer: We don't. Simply put, any time you type 'PLAYER' into the Speaker tag line in a DLG node, the dialog will crash. This alone has turned others in the past off of pursuing this endeavor. "So, we can't use the PLAYER tag to make the PC talk, and we can't assign a new tag to the PC, so how do we make this work?" This is where one's fundamental understanding of how dialogs work comes in handy. There's always one performer in a dialog that doesn't need a stated speaker tag, and who is that? The owner. So necessary workaround #1: Make the PC the owner of the dialog. This presents a new problem, however. The game is coded to always assume whatever other valid object is in the command chain in game scripts that fire dialog is supposed to be the owner, whether it's the object starting a conversation with the PC or vice versa. You know what the solution for this is? Remove those objects from the command chain. So necessary workaround #2: Make the PC start the dialog with their self. That's right, the PC is going to be starting up conversations with him/herself that you will need to invite all other participants into through the use of the Speaker/Listener tags - and those tag lines are going to be used a lot, unless you literally want the PC to be talking to him/herself. The way to set this up in a script is quite simple: AssignCommand(GetFirstPC(), ActionStartConversation(GetFirstPC(), "name of dialog")); Note: I'm including a Modders Resource (jb_func.nss) that functions as an #include script, which already has this function set up as a custom function, so all you need to type up is PCStartDialog("name of dialog"); jb_func.nss Any dialog fired by this function will have the PC speak all the entry nodes with blank speaker tag lines. Yes, you are still required to put VO on Entry nodes, not Reply nodes, which means having the PC speak a line of dialog that the player chooses from a replies list requires creating an entry after said reply with the VO information attached. Of course, this particular solution only covers game-triggered dialogs so far. What about click-started dialogs, the ones triggered by the player? This is where we get into potentially tedious territory. See, player-triggered dialogs are fired by the game gathering the name of said dialog from the objects Dialog string entry. This data call is hard-coded - we have no access to it through scripting. The obvious solution would be to set up a custom script for that object's OnDialogue event, but when one such object becomes a hundred or a thousand, that's a lot of OnDialogue scripts. This is where I came up with a different solution, which proved to be quite ingenious. Simply put, convert the player-triggered dialog to a game-triggered dialog. So necessary workaround #3: Make the dialog re-trigger itself. Note: Regarding the steps below, If you want to skip most of the work, I'm including here the script that I use to re-trigger the dialog, as well as a new dialog template to start from. Sadly, you'll have to do the globalcat.2da editing yourself. a_load_dialog.nss a_load_dialog.ncs newdialog.dlg How do we set this up? First, you need to create a global in the globalcat.2da. It can be a boolean or a number, doesn't really matter. For the sake of this tutorial, I'll be using a boolean and naming it "dialog_pc_owned". Next, create a blank entry at the very top of your DLG. Set its first conditional to "c_glob_bool_set", set the String Param to be "dialog_pc_owned", and check that "Not" box. Right now, this node does nothing but always fires. What we want to do next is make something happen. Specifically, we want to set the global, so in the first script field, insert "a_glob_bool_set", set the first parameter to 1, and the String Param to "dialog_pc_owned". This means the next time the dialog is fired, it will ignore this top node because the condition isn't being met. Obviously, there's still something missing. We need to trigger the dialog to fire again under ownership of the PC. So leave the DLG as is for a moment, and create a new script - I'm naming mine "a_load_dialog.nss". Now, in order to avoid compiling a ton of different versions of this script, we'll be making use of the String Param in the TSL dialog system. So if you're using my include file, the script would look like this: #include "jb_func" void main() { PCStartDialog(GetScriptStringParameter()); } Compile that bad boy (make sure the include script is in the override folder in your game directory), then go back to that top node in the DLG file. Insert "a_load_dialog" into the second script field, then in the String Param field, insert the name of your DLG file. So now your dialog will re-trigger itself with ownership belonging to the PC. We're done, right? WRONG! This is where I reckon I'll lose most of you, because it's a bit difficult to put into words, so please bear with me. When you trigger a dialog like this, you're setting a variable that wasn't set previously so that you can call it to re-trigger itself. The downfall is that variable will not automatically reset so that the top node triggers every time. In order for this to work, that top node needs to trigger every time, which requires resetting the global after the dialog re-triggers itself. How you set this up in the Dialog file is completely up to you, but my preferred method is to create a second blank entry underneath the first one, set the first script field to "a_glob_bool_set", first parameter to 0, and String Param to "dialog_pc_owned", then let that node be the master root for the rest of the actual dialog tree. That's all there is to it, obviously the rest is on you to write the dialog and acquire the VO in the typical way. I hope this helps, folks. Until next time... DarthTyren has spoken! 4 1 1 1 Quote Share this post Link to post Share on other sites