Jump to content

Home

Need Help with some scripting


quantumpencil

Recommended Posts

Hello Everyone. New Guy Here, working on a rebalance/difficulty mod.

 

I'm working on a new mod for KotoR1, and I'm hoping to add some general difficulty rebalancing as well as some rewrites for which abilities enemies actually use.

 

So I have three semi-separate questions:

 

Does anyone happen to have the source code for Talchia's mod? I've garnered from remnants that it overwrote the on_perception case in the k_ai_master script, but access to the original source code would help me greatly in making my own version.

 

Does anyone happen to know what script controls the actions of specific bosses, like Malak? AI master looks like it kind of handles thinks at a higher level (It's mostly handling events/flags to tell if something has been seen, if it's hostile, if it's moved in range and such) and the include inc_generic has a method GN_DetermineCombatRound which looks like it is doing what I'm interested in (Determining combat actions) through references to other scripts I can't seem to find anywhere.

 

And finally, does anyone know what scripts control the behavior of containers that are locked? I looked for scripts linked to their .utps but came up empty handed. I'm wanting to add something where bashing a container to open it can result in the items inside being destroyed and replaced with a new "crushed goods" item similar to kotor 2.

 

Thanks a ton!

 

EDIT:

 

I found the what I was looking for in in re: the second question k_inc_generic.nss. The actual AI's are all here inside their respective function bodies (GN_RunMalakAIRoutine) ands so forth, with a bunch of helper methods coming from the includes. So just need talchia's mod and any info on bashed container handling location

 

 

~QP

Link to comment
Share on other sites

And finally, does anyone know what scripts control the behavior of containers that are locked? I looked for scripts linked to their .utps but came up empty handed. I'm wanting to add something where bashing a container to open it can result in the items inside being destroyed and replaced with a new "crushed goods" item similar to kotor 2.

You should be able to attach a script to the placeable's OnDamaged event in the UTP for this. But editing the game's existing placeables is tricky... you really could only do it for ones with unique file names, unless you want to go edit every module file in the game.

Link to comment
Share on other sites

You should be able to attach a script to the placeable's OnDamaged event in the UTP for this. But editing the game's existing placeables is tricky... you really could only do it for ones with unique file names, unless you want to go edit every module file in the game.

 

With a bit of guile, there's ways around that limitation. Basically you extract, then rename a "standard named" container for the area you know you need. Name it something new. Then you basically script in a function to destroy the original container that *should* be in the level, replacing it with your new (uniquely named) one.

 

If you can't find any clean source to work with, you can also do this with a "custom injection" hack. Basically, you find ANY script that you know for a fact *will* run sometime between entering the area, but before you deal with the container. Extract that script, rename it slightly. Write new source with the name it had originally.

 

Then you write/compile your script like:

void main (void) {
  object oCont = GetObjectByTag("container_tag", 1); 
  location oLoc = GetLocation(oCont);

  DestroyObject(oCont);
  // Next line re-assigns our container variable to a NEW container.
  oCont = CreateObject(OBJECT_TYPE_PLACEABLE, "my_container_template", oLoc);

  /* Do anything else we need to the new container here.  Put stuff in it, blah blah blah /*

  // Then we let the game get on with its usual business...
  ExecuteScript("new_name_of_original_script", OBJECT_SELF);
}

 

Meh. That's off the top of my head, and admittedly a bit hackish, but it works, and it lets you "modify" (technically, append to the beginning of) almost any script in the game whether you have source code available for it or not. It can be handy at times.

 

-Kitt

Link to comment
Share on other sites

You should be able to attach a script to the placeable's OnDamaged event in the UTP for this. But editing the game's existing placeables is tricky... you really could only do it for ones with unique file names, unless you want to go edit every module file in the game.

 

Ah, I see. So when I look in the modules and see thinks like corpse1.utp, that's basically letting the engine know that there will be objects of type/class corpse1 in that module, but different instances have their properties set in a script somewhere?

 

I'm not opposed to editing every module in the game. I'm looking at the modules and I only see compiled scripts in the kotor tree. Are these the ones I'd need to edit? Does the source live anywhere/Is this actual handled somewhere else?

 

If I need to edit the compiled scripts -- is there a an opcode list the community has reverse engineered anywhere? I have some assembly background so it would be possible, just a pain in the ass.

Link to comment
Share on other sites

With a bit of guile, there's ways around that limitation. Basically you extract, then rename a "standard named" container for the area you know you need. Name it something new. Then you basically script in a function to destroy the original container that *should* be in the level, replacing it with your new (uniquely named) one.

 

If you can't find any clean source to work with, you can also do this with a "custom injection" hack. Basically, you find ANY script that you know for a fact *will* run sometime between entering the area, but before you deal with the container. Extract that script, rename it slightly. Write new source with the name it had originally.

 

Then you write/compile your script like:

void main (void) {
  object oCont = GetObjectByTag("container_tag", 1); 
  location oLoc = GetLocation(oCont);

  DestroyObject(oCont);
  // Next line re-assigns our container variable to a NEW container.
  oCont = CreateObject(OBJECT_TYPE_PLACEABLE, "my_container_template", oLoc);

  /* Do anything else we need to the new container here.  Put stuff in it, blah blah blah /*

  // Then we let the game get on with its usual business...
  ExecuteScript("new_name_of_original_script", OBJECT_SELF);
}

 

Meh. That's off the top of my head, and admittedly a bit hackish, but it works, and it lets you "modify" (technically, append to the beginning of) almost any script in the game whether you have source code available for it or not. It can be handy at times.

 

-Kitt

 

Very Clever! So the game must have scripts that run whenever you first enter a new area, To do things like see which chests you've opened and such. This could be a good place for such an injection. I can't read the decompiled code yet, but it looks like the kor35_enter.ncs files are there for all modules so it's probably those?

 

Is it safe to use this technique there? Do these scripts already set the containers values (In which case I guess I need to run the old script and then change everything? When Executescript() runs, will the engine return control to the previous script on the stack, allowing those manipulations to be done afterwards?) That way you could do a loop over all the containers, manually set them to have the contents you expect based on your modified generic .utp w/ custom on damaged script.

Link to comment
Share on other sites

Very Clever! So the game must have scripts that run whenever you first enter a new area, To do things like see which chests you've opened and such. This could be a good place for such an injection. I can't read the decompiled code yet, but it looks like the kor35_enter.ncs files are there for all modules so it's probably those?

 

Yep. Good call. See, you're learning already. ;)

 

Most objects (areas, creature, etc...) have various "events". To get an idea of what I'm talking about, load up a character.utc in KotOR tool (p_bastila.utc or whatever) and click on the "scripts" tab. What's being defined there is which script runs when that specific creature (or whatever) triggers that specific event. Events are internal to the engine, but you can "hook into" and detect them easily in scripts.

 

Since you're looking for OnEnter events (presumably for the m35aa module), you can look at m35aa.are with a GFF editor. Use KT to browse to: K1 -> Rims -> Modules -> korr_m35aa.rim -> Static Area Info -> m35aa.are and double click (if you have a GFF editor paired with KT's install).

 

Anyways, once you're looking at that, you can scroll down and find the OnEnter field, which in this case has been set to: kor35_enter, exactly as you suspected. ;)

 

Is it safe to use this technique there? Do these scripts already set the containers values (In which case I guess I need to run the old script and then change everything? When Executescript() runs, will the engine return control to the previous script on the stack, allowing those manipulations to be done afterwards?) That way you could do a loop over all the containers, manually set them to have the contents you expect based on your modified generic .utp w/ custom on damaged script.

 

You're right on track. These scripts are an excellent choice for manipulating things in a given area, since you know it's going to trigger when you walk in.

 

Now, lots and lots of things don't even get messed with by the scripts. Like a lot of containers, especially when their name appears in the module files (K1 -> Rims -> Modules -> korr_m35aa_s.rim -> Blueprint, Placeables) just have a "preset inventory" and get dropped in somewhere by way of level editing tools instead of scripting them in.

 

In those cases, the method I mentioned above works fine, but if what you're trying to manipulate does get acted on or created in the script somewhere (you'll know if none of the changes you make "stick" on testing, but everything else should be correct), then yes -you then want to run the original OnEnter script first, and then do whatever functions you need for your custom content, which for changes like this, may not be bad to just adopt anyways, since there's no real reason you need it to happen before the normal OnEnter behavior, and if you do it after, you avoid that potential problem entirely.

 

From the http://nwn.wikia.com/wiki/Category:Commands (which can be an excellent place to learn, even if not everything there is quite the same or even available in some cases for KotOR scripting):

ExecuteScript

 

The ExecuteScript() NWScript command interrupts the execution of the current script in order to run another one. After the second script finishes, the original script resumes execution. This is an alternative to include files for sharing code among several scripts. An advantage to executing a script instead of calling a function in an included script is that the common code can be changed and compiled once, instead of compiled into each script that uses the common code. (This is the principle that makes spellhooking possible, for example.)

 

This command allows an easy transfer of execution to another object if so desired (rather than going through AssignCommand()). In fact, this is the only way to transfer execution to another object in the middle of a script. (Something assigned via AssignCommand() is not executed until the current script finishes.)

 

Definition

 

void ExecuteScript (string sScript, object oTarget)

  string sScript 
     The name of a compiled script to run.
  object oTarget 
     The object that will run the script (will be OBJECT_SELF within the script).
If either parameter is invalid, nothing happens.

 

As you can see, you should be able to use this little trick (which I did not invent, just to be fair.) to append pretty much any manipulations and effects you may wish either before, after, or both from where the "real" script does its stuff.

 

The big key is making sure you name things smart, and keep consistent, then test small changes at a time until you get comfortable with the sorts of things you're manipulating and how they work.

 

For example, in this case I would probably drop a copy of "kor_35_enter.ncs" into the \override folder, but rename it as "kk_kor_35_old_enter.ncs" (I put a KK on all my modded content so people can locate the files easily -it just stands for Kitty Kitty ;) ) Then you name YOUR script "kk_kor_35_enter.ncs" and when you run your ExecuteScript command, point it to "kk_kor_35_old_enter" (never use file extensions when looking for something by template reference)

 

I have a tendency to be a bit verbose, and sometimes over-explain things, so I'll shut up for now. If anything was unclear, or you need any further assistance, please ask. ^-^

 

-Kitt

Link to comment
Share on other sites

Since you're looking for OnEnter events (presumably for the m35aa module), you can look at m35aa.are with a GFF editor. Use KT to browse to: K1 -> Rims -> Modules -> korr_m35aa.rim -> Static Area Info -> m35aa.are and double click (if you have a GFF editor paired with KT's install).

 

You're right on track. These scripts are an excellent choice for manipulating things in a given area, since you know it's going to trigger when you walk in.

 

For example, in this case I would probably drop a copy of "kor_35_enter.ncs" into the \override folder, but rename it as "kk_kor_35_old_enter.ncs" (I put a KK on all my modded content so people can locate the files easily -it just stands for Kitty Kitty ;) ) Then you name YOUR script "kk_kor_35_enter.ncs" and when you run your ExecuteScript command, point it to "kk_kor_35_old_enter" (never use file extensions when looking for something by template reference)

 

Only two warnings I can provide here:

 

1. This trick does work great, but if another mod does this on the same level, you can get into trouble very fast unless you account for the other mod somehow (like a separate small patch).

 

2. Might want to watch how long your scripts are named... KotOR just ignores it after the first 16 characters. KotOR would consider your scripts as "kk_kor_35_old_en.ncs" and "kk_kor_35_enter.ncs" respectively (second one works, first one won't ever trigger)...

Link to comment
Share on other sites

Ah, I see. So when I look in the modules and see thinks like corpse1.utp, that's basically letting the engine know that there will be objects of type/class corpse1 in that module, but different instances have their properties set in a script somewhere?

 

I'm not opposed to editing every module in the game. I'm looking at the modules and I only see compiled scripts in the kotor tree. Are these the ones I'd need to edit? Does the source live anywhere/Is this actual handled somewhere else?

 

If I need to edit the compiled scripts -- is there a an opcode list the community has reverse engineered anywhere? I have some assembly background so it would be possible, just a pain in the ass.

 

The game will spawn any instances of corpse1.utp the first time you enter the level, or will use that template file when you spawn one via scripting. From that point on, the settings for each instance or kept with that instance in memory and then saved to the .git file when you leave the area or save your game.

 

The source code is not used by the game at all and is strictly there for the programmer's convenience.

 

You can grab DeNCS at starwarsknights.com (in the modding tools section on the left) and it will decompile .ncs files most of the time. When it fails, it won't tell you why, it'll just keep on failing on that script. At that point, you could try double-clicking that script in KotOR Tool to see the byte-code spat out and try to decipher the right-hand column, from bottom to top. Sometimes it's easy, sometimes not so much...

Link to comment
Share on other sites

Thank you so much for that long and detailed explanation. I'm happy to say that I've managed to get the intended functionality working! Though my method is probably not the most efficient.

 

For containers that have unique resrefs, I just copied the container & put in the override bin with a custom script to destroy items inside if it takes damage & replace them with a trash item.

 

For containers like footlocker01.utp, I made code which copies the contents of all those into a new container into the on enter script of each module, as per your suggestion.

 

It works, and hopefully it'll make security have some function now (Especially when paired with more valuable items in chests).

 

If anyone has access to Talchia's original hardcore mod, I'd like to see the source code. Otherwise I'm off brainstorming about ways to make the other skills useful =p

Link to comment
Share on other sites

Thank you so much for that long and detailed explanation. I'm happy to say that I've managed to get the intended functionality working! Though my method is probably not the most efficient.

 

For containers that have unique resrefs, I just copied the container & put in the override bin with a custom script to destroy items inside if it takes damage & replace them with a trash item.

 

For containers like footlocker01.utp, I made code which copies the contents of all those into a new container into the on enter script of each module, as per your suggestion.

 

It works, and hopefully it'll make security have some function now (Especially when paired with more valuable items in chests).

 

Glad you got it working. :)

 

Also, that's probably how I'd have gone about it. Not sure it's the 'best' way, but it works well, and let's face it.. when you're dealing with after-market, unsupported mods, elegant is rarely available. ;)

 

Incidentally, I'm not sure where your mind is going with this idea, but if you wanted to have the items destroyed some, but not all of the time, the script engine has some really simple built in random functions you could incorporate.

 

Personally, I like varied, random-appearing results, but you may well want them to always get smashed. Just thought I'd mention it just in case. ;)

 

Only two warnings I can provide here:

 

1. This trick does work great, but if another mod does this on the same level, you can get into trouble very fast unless you account for the other mod somehow (like a separate small patch).

 

2. Might want to watch how long your scripts are named... KotOR just ignores it after the first 16 characters. KotOR would consider your scripts as "kk_kor_35_old_en.ncs" and "kk_kor_35_enter.ncs" respectively (second one works, first one won't ever trigger)...

 

Good tips FS. I was a bit tired and typing from the hip, and forgot to mention. ;)

 

I wasn't actually aware the 16 characters was a hard-limit (like say, running from ExecuteScript), but since some of the fields in the various dialogue editors and whatnot only let you go that far, I've always kept to less than that as a rule anyways.

 

You can grab DeNCS at starwarsknights.com (in the modding tools section on the left) and it will decompile .ncs files most of the time. When it fails, it won't tell you why, it'll just keep on failing on that script. At that point, you could try double-clicking that script in KotOR Tool to see the byte-code spat out and try to decipher the right-hand column, from bottom to top. Sometimes it's easy, sometimes not so much...

 

Correct me where I go astray here, but it sounds like you're getting some translation in DeNCS decompiles? No matter what I've used to decompile scripts, I've never seen anything but solid byte code.

 

Now, that usually works well enough, but I'n no Stoffe. Combing through hundreds of lines of that crap makes my eyes bleed, so if anyone has successfully done some work in getting reasonably accurate translations working.. man that'd be nice. xD

 

-Kitt

Link to comment
Share on other sites

Glad you got it working. :)

 

Also, that's probably how I'd have gone about it. Not sure it's the 'best' way, but it works well, and let's face it.. when you're dealing with after-market, unsupported mods, elegant is rarely available. ;)

 

Incidentally, I'm not sure where your mind is going with this idea, but if you wanted to have the items destroyed some, but not all of the time, the script engine has some really simple built in random functions you could incorporate.

 

Personally, I like varied, random-appearing results, but you may well want them to always get smashed. Just thought I'd mention it just in case. ;)

 

Yep, that's my vision as well. Essentially the script generates a random integer between 1-100, and then if that's greater than 50 it destroys the item and increments a counter of how many items were destroyed. At the end of the loop, it'll add that many stacks of "trash" to the container.

 

I really just want to try and make all the skills useful. This was one of the more obvious fixes (Since it's done in KotoR II) but some of the others are tough. Only other thing I've figured out so far is having grenade/explosive weapon damage scale with demolitions skill.

 

I'd like to have awareness work like it does in KotoR 2, where certain dialog options are opened up by an awareness check. Only issue is whenever I add a script into a dlg file in kotor tool that returns a boolean in the "make node available" slot... the dialogue behavior is very strange. Sometimes it works and sometimes it seems to bug out that NPC and their dialogue tree will just stop.

Link to comment
Share on other sites

I'd like to have awareness work like it does in KotoR 2, where certain dialog options are opened up by an awareness check. Only issue is whenever I add a script into a dlg file in kotor tool that returns a boolean in the "make node available" slot... the dialogue behavior is very strange. Sometimes it works and sometimes it seems to bug out that NPC and their dialogue tree will just stop.

 

*Usually* this means you've got a situation where the only available choices all return negative. Like say you have a spot in the tree where the only option is a "continue" reply, but you've checked a value for the availability there, and it returns false.

 

They can be a bit tricky, but with perseverence, you'll get it worked out.

 

Personally, I'd use tk102's DLG editor, but your opinion may vary. ;)

 

-Kitt

Link to comment
Share on other sites

Good tips FS. I was a bit tired and typing from the hip, and forgot to mention. ;)

 

I wasn't actually aware the 16 characters was a hard-limit (like say, running from ExecuteScript), but since some of the fields in the various dialogue editors and whatnot only let you go that far, I've always kept to less than that as a rule anyways.

 

Correct me where I go astray here, but it sounds like you're getting some translation in DeNCS decompiles? No matter what I've used to decompile scripts, I've never seen anything but solid byte code.

 

Now, that usually works well enough, but I'n no Stoffe. Combing through hundreds of lines of that crap makes my eyes bleed, so if anyone has successfully done some work in getting reasonably accurate translations working.. man that'd be nice. xD

 

-Kitt

 

I know how that goes... Once carried a custom function in my head for three days because I had no computer. :p

 

I've always obeyed the limit, as it would seem stupid to have an exception like that in regards to a character limit sometimes not applying.

 

I've often gotten good results with DeNCS. Only thing to look out for is that if you see "Partial byte-code mismatch", then right-click the gibberish and select "View source" or something.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...