Leaderboard
Popular Content
Showing content with the highest reputation on 03/20/2020 in Posts
-
5 points
-
4 pointsHello DeadlyStreamers! To think all it took was a pandemic and an overly long SWTOR content update to get me to announce the 2018 Mod of the Year winners. And of course, keeping in tradition with my minimum effort work ethic, here they are: Best Texture Enhancement: Best Content Restoration / Addition Most Helpful Community Member @DarthParametric and of course Mod of the Year Each winner will receive their new Mod of the Year 2018 medals. Also due to the nature of the nature of the 2018 Mod of the Year winner, I'll be awarding two versions of the medal - one black and one gold! So what's the deal with Mod of the Year 2019? I don't know yet. Due to all this COVID-19 business, sounds like I'll have more time for DeadlyStream business. I think it'd simply be a matter of choosing the categories and then starting up nominations. We'll see.
-
2 points
-
2 pointsI'm assuming these are all the debug functions? PrintString, AurPostString and the like? I was playing with those only yesterday and noticed that despite removing if (!ShipBuild()) checks, they still didn't appear to function. I assume they gutted them from the retail release altogether? While they are unnecessary for the code to run, I would argue they should remain in any decompiled scripts since, in the absence of commented source for the module scripts, the debug functions are the only thing remaining that can indicate developer intent. Additionally, the user can manually change them to SendMessageToPC for their own debugging purposes if needed.
-
1 pointUnreachable code, by definition, should have no effect on script execution. Analysis of unreachable code is simply to facilitate analysis of reachable code.For example: 0xNNNNNNNN CPTOPSP -4 4 0xNNNNNNNN CONSTI N 0xNNNNNNNN EQUALII 0xNNNNNNNN JNZ 0xNNNNNNNN Is analyzed to see if it is a switch case label. Every case label I've seen fits this pattern, and for all but the first case label the above code is the entirety of a basic block. However, the first case label has instructions prior to CPTOPSP. It's necessary to see if such code is unreachable, and therefore part of a separate basic block, or actually a functional part of the first case's basic block. And, that's assuming it's actually a switch! It could just be an if (false) {} statement, but we have to analyze the flow to find out! I won't rule out optimizations, but I doubt they exist. They would be meaningless since NWScript isn't meant for high-performance. We aren't using NWScript to create databases, calculate protein folds, or process bank transactions. Slow code won't be noticeable, but such optimizations are error-prone and require a lot of development effort that clearly would have been better spent elsewhere.
-
1 pointI don't remember seeing this exacty case. Then again, it's been a while since I touched that code and I'm not known to have a good memory. :P Also, my disassembler just follows the starting segment and IIRC I don't even check for unreachable blocks without an edge leading into them. That's maybe something that could use improvement. I take GitHub pull requests! ;) However, I can say that I have found a few bugs in BioWare's script compiler (I explained two in my replies above, I think), so I wouldn't particularily rule that out. And while I can't name any specifics right now, I also never got the feeling that their compiler was all that great in optimizing, fusing instructions, or removing outright unnecessary instructions. I don't think they focussed on that at all, rather keeping it simple and working without any surprises (bugs nonwithstanding).
-
1 pointMy apologies. As with all things STEM, terminology must be accurate. I am learning compiler design in order to create this compiler/decompiler, so I am still unfamiliar with communicating this topic effectively. "Dead code" has two meanings, one more accurate than the other: Instructions that compute a value that is never used Unreachable code The red blocks do not represent "instructions that compute a value that is never used". They may fit the first definition, but I am not looking for that in these diagrams. The red blocks are "unreachable code"; they never get called, which is why they aren't preceded by another green block. Only the function entry point (the very first block in a function/subroutine) should be without a preceding block. The image above has 10 functions/subroutines, each represented by a box filled with one or more colored blocks. (Accuracy alert!!! Functions and subroutines are similar, but not identical. The difference only matters to academics and compiler designers, but this entire post is about pedantry...) Thus, there should only be 10 blocks that aren't preceded by another green block: the function entry points. If a block is in red, that's because no other block explicitly jumps to it or implicitly proceeds to it. These blocks are therefore useless, and could be removed if they serve no other purpose. x86/x86-64 uses the NOP op code to allow for instruction padding to improve memory alignment, and resulting access times. To my knowledge, such a technique has no effect in the NWScript runtime. If I am correct, then the code could be stripped. My concern is that perhaps there IS a reason for these blocks, or that the existing script compilers are riddled with bugs... Finally, these blocks are not the result of debug code. One of the switch statements in the diagram has a debug function under a case label in its source. switch(nPlanet) { case PLANET_PERAGUS: { AurPostString("ERROR: We should not be able to travel back to peragus.",0,10,5.0); } break; //Other case statements removed } The compiled case label: case 8: 0x00000ffb CPTOPSP -4 4 0x00001003 CONSTI 8 //PLANET_PERAGUS defined as 8 in nwscript.nss 0x00001009 EQUALII 0x0000100b JNZ 0x000010dd The target block in the compiled file: case 8 block 0x000010dd 0x000010dd CONSTF 0.000000 0x000010e3 CONSTI 10 0x000010e9 CONSTI 0 0x000010ef CONSTS ERROR: We should not be able to travel back to peragus. 0x0000112a ACTION 582 4 //Engine functions are zero-indexed, AurPostString() is #582, and it takes 4 parameters 0x0000112f JMP 0x0000158e Case label 8 jumps to the Case 8 block, and calls the debug function. No unreachable code here!
-
1 pointIt may be that those weren't intended to print anything in the game itself but a console window, which would be present in a working/debug version of the game. If that's the case, it's not so much that they don't function as that they have no effect. In any case, anything that can be preserved, should be!
-
1 pointHello! Somehow the modification swapped place for the conditional script k_pman_comp66 which is moved to the Script to Fire slot in the man26_bigcomp.dlg file. The original game file does not have this problem.
-
1 pointBefore [to even decide] downloading/using that mod I'd recommend you to see its prefix/tag... though I'd recommend you don't unless you're a Simpsons' fan, hahah. While at it I'd like to recommend you to use the latest version of KOTOR 1 Community Patch - if, you haven't used it before. The current version fixes lots of [previously-unfixed] bugs and significantly improve the gameplay's experience. There is also this mod that fix the missing lamps inside the Temple Main Floor, if - you want to see the Temple get its rock-sh#t together, lol.
-
1 pointIt's been quite interesting these last few weeks, eh? I hope you are all doing well. There's been progress in analyzing the NCS byte code. Graphviz has been invaluable in producing the maps necessary to visualize the program flow. At the moment, one script has been reverse engineered. By no means is a decompilier close to being ready, but it is a significant step in analyzing the program flow. At the moment, several NCS code patterns have been identified that map back to NWScript: Dead code (e.g. perfectly valid code that never gets called, and theoretically could be removed) Assignment (e.g. i = 5) Named variable declaration (e.g. int i, float f, string s, object o) initialized from constant (e.g. int i = 5, float f = 0.0, string s = "Jello, World!") initialized from engine routine (object o = GetFirstPC()) initialized from subroutine (bool b = IsItTrue()) initialized from named variable (int i = integer_i ) Selection If (GetLocalBoolean(55)) If (!GetLocalBoolean(55)) If (IsItTrue()) If (!IsItTrue() Switch (i) {case 0: break; default: break;} Attached is a control flow diagram of the script k_sup_galaxymap.ncs. Red blocks are dead code. There are three switch statements in the code. Can you find them? Expect more updates in the next few weeks!
-
1 pointDeleting geometry is actually ok. It's only adding new geometry that destroys the weights. This was something I was going to come back and add after re-reading what Malkior said. Namely that you could select the appropriate block of polys around the neck region all weighted 1 to torsoUpr_g, then duplicate the mesh, on the original delete the selected polys, on the duplicate invert and delete everything else. Now on the split out neck piece you can add new geometry with impunity, as long as you don't reposition/alter the bottom ring of verts that mate to the rest of the body. Reskinning it manually once modelling is complete is trivial, since all the verts will be weighted 100% to one bone.
-
1 pointSo its been a few months since posted an update on this. Just letting people know I am still working on this. Back in January I started to rewrite this from scratch, currently there is still functionality missing from the older versions that need to be re-implemented. However that said, I've completed editors for all the .ut* file types, but some other things like a dialog editor still need to be made. I've also done some changes to the UI. I'm going to postpone pushing out updates until I have editors/viewers for all the file types which hopefully will be a few months tops.
-
1 pointHey JC it appears that "ready" stances are fixed But idle stances aren't I have no other mods that mess with mdls or mdx or nothing except for tslrcm, and yet I think it must be from my end otherwise you would have noticed this right away.
-
1 pointHello! I'd not have any problems testing a modified script but I checked the Selkath and it has RACIAL_TYPE_HUMAN and SUBRACE_NONE and that's why it won't be caught in the second condition check of yours. I suppose I could just tweak this line: } else if (GetRacialType(oTarget) == RACIAL_TYPE_HUMAN && GetSubRace(oTarget) != SUBRACE_NONE) { to: } else if ((GetRacialType(oTarget) == RACIAL_TYPE_HUMAN && GetSubRace(oTarget) != SUBRACE_NONE) || (GetTag(oTarget) == "man28_inssel")) { This wouldn't break anything. I'll test it and then let you know. Cheers!
-
1 pointMy time is limited but I will see what I can do. However, I do have a request for the admin folks... For those modders that aren't on this site (like Quanon for example), I'd like for a new screen name to be created - such as "JumpStationZArchive" - so no one here gets undue credit for uploading the mods. Credit should always remain with the original modder and having my name (or anyone else's) attached to it lessens those modder's work. AFAIK, administrators can still change the ownership of the mods from one person to another but moderators may not be in the same boat. If one or more administrators can both create the pseudo-member and reassign the uploads to that account, I'd be willing to start work on this. As I don't have time to go through every mod, I will not be listing compatibility for any of these mods nor providing representative pictures. I will assume that each and every mod that I upload is incompatible with either K1R or TSL. Instead of a representative picture, I'll post something like this for each of the JumpStationZ archived mods. As I go through the mods, I'll also look over the documentation for each for spelling errors and also change the TXT files to either RTF or DOCX for modernization purposes. Sound good, admin types? @ChAiNz.2da: Does the above logo meet with your satisfaction? Addendum: I have been informed through back channels that @jc2 is still quite willing to do this. I commend him for his efforts if he does wish to continue and he can use the above logo if he likes. (The font used above is "Verdana" and matches what @ChAiNz.2da used in his original logo on his site. IIRC, Verdana is a standard font included with Windows.)
-
1 pointThe two heads are both shading issues. It's a common problem with a number of head models, usually down to the fact that there are UV seams in those areas and thus the mesh is split along those seams, causing hard shaded edges. Attempts to fix the issue are often mixed. A lot depends on the specifics of the particular head model. I'm not really clear on what you are referring to with Zaalbar. His texture is garbage, but aside from that it looks about on par. Edit: Here's a smoothed out Trask: Although he has a bigger problem, namely that his eyelid meshes clip pretty badly. That's probably more effort to fix. Edit 2: I tried a kind of simplistic fix. Turned off the render flags for the original eyelash trimeshes and weighted the upper edges of the eyesockets to the lash bones without creating any actual new eyelash geometry. It doesn't look as bad as I was expecting, but try it out and see what you think. Extract the attached into your Override folder. Trask_Head_Smoothing_and_Eyelids_Fix.zip
-
1 pointHello! I resurrect my own old thread to report some news. First of all, I am now able to choose new record times to beat for the three tiers in Tatooine and Manaan. Said that, there is still something that I can't figure out. Namely how the original times are calculated by the game. Using Manaan as example we have the following record times: Queedle: 0:23:82 Casandra: 0:23:25 Hukta: 0:22:50 Queedle as Sector Champion: 0:22:48 But the formula we find in the include script shows this: int QUEEDLE_TIME = 3012; int CASSANDRA_TIME = 2702; int JAX_TIME = 2548; int CHAMP_TIME = 2348; void SetTokenRaceTime(int nToken, int nRacerTime) { // calculate the time components int nMinutes = nRacerTime/6000; int nSeconds = (nRacerTime - (nMinutes * 6000)) / 100; int nFractions = nRacerTime - ((nMinutes * 6000) + (nSeconds * 100)); //building the time string string sTime = IntToString(nMinutes) + ":"; if (nSeconds < 10) { sTime = sTime + "0"; } sTime = sTime + IntToString(nSeconds) + ":"; if(nFractions < 10) { sTime = sTime + "0"; } sTime = sTime + IntToString(nFractions); SetCustomToken(nToken,sTime); } And according to this, the times should be: Queedle: 0:30:12 Casandra: 0:27:02 Hukta: 0:25:48 Queedle as Sector Champion: 0:23:48 which would be just what I wished it was from the very beginning. Records that would show a real gap between one Tier and the next one rather than a series of almost identical results. Somehow the game changes those times somewhere although I wouldn't know how and when exactly. Still, in my own SW: KotOR Upgrade, the times indicated above will be restored thus making the Swoop Racing a much more sensible kind of challenge. Cheers!
-
0 pointsFirst, 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!
-
0 pointsI figured it out now, I used this video to help me: Then after I mapped the animations I go into the animation editor and can play each one.
-
0 pointsHere you go: https://www.darthparametric.com/files/kotor/k1/[K1]_Player_Head_TSL_PMHC06_For_K1.7z I completely forgot until making this post that @ndix UR had already fixed this model previously for TSL, so I ended up reinventing the wheel a bit there (I didn't look at the hair mesh verts though, so I might need to go back and check those). I also hex edited the TPCs, adding the blending punchthrough TXI semantic, which seems to be necessary in K1 to get a clean hair alpha. The vanilla version of the textures was inducing a lot of haloing.