[TUTORIAL] - KotOR Modding Tutorial Series (2024)

02 - [TUTORIAL] - Creating a Merchant - TSL

Spoiler

Merchants

In order to add a merchant, you will need to first add a character to whatever module you want and create a .dlg (Dialog) file which is used by the .utc (Character) you will then need to add any relevant lines of dialog for your character to speak.

[TUTORIAL] - KotOR Modding Tutorial Series (1)

Make sure to name the dialog file in the Conversation section of the utc file.

From there you will need to link it to a script using a line of dialog.

The following is an example of the script you will need to use to launch the merchants inventory window.

void main(){ object oStore = GetObjectByTag("merchant_file"); // this is the .utm file listing your merchants inventory if(!GetIsObjectValid(oStore)) oStore = CreateObject(OBJECT_TYPE_STORE, "merchant_file", GetLocation(OBJECT_SELF)); if(GetIsObjectValid(oStore)) DelayCommand(0.5, OpenStore(oStore, GetPCSpeaker()));}

this script then needs to be compiled using the KotOR Scripting Tool. ( Make sure to set it to KotOR 2 Mode )

It will open the .utm (Merchant) file which contains a list of the items which are available for purchase.

Another example is to use global variables as conditions to determine what stock your merchant has available.

The next example is ascript I am using for the Yavin Orbital Station, it determines which merchant script to load dependant on how many Jedi Masters the Exile has already encountered during their playthrough. ( Between 0 & 4 )

void main(){ if((GetGlobalNumber("000_Jedi_Found") == 0)) { object oStore = GetObjectByTag("thor0"); if (!GetIsObjectValid(oStore)) oStore = CreateObject(OBJECT_TYPE_STORE, "thor0", GetLocation(OBJECT_SELF)); if (GetIsObjectValid(oStore)) DelayCommand(0.5, OpenStore(oStore, GetPCSpeaker())); } else if((GetGlobalNumber("000_Jedi_Found") == 1)) { object oStore = GetObjectByTag("thor1"); if (!GetIsObjectValid(oStore)) oStore = CreateObject(OBJECT_TYPE_STORE, "thor1", GetLocation(OBJECT_SELF)); if (GetIsObjectValid(oStore)) DelayCommand(0.5, OpenStore(oStore, GetPCSpeaker())); } else if((GetGlobalNumber("000_Jedi_Found") == 2)) { object oStore = GetObjectByTag("thor2"); if (!GetIsObjectValid(oStore)) oStore = CreateObject(OBJECT_TYPE_STORE, "thor2", GetLocation(OBJECT_SELF)); if (GetIsObjectValid(oStore)) DelayCommand(0.5, OpenStore(oStore, GetPCSpeaker())); } else if((GetGlobalNumber("000_Jedi_Found") == 3)) { object oStore = GetObjectByTag("thor3"); if (!GetIsObjectValid(oStore)) oStore = CreateObject(OBJECT_TYPE_STORE, "thor3", GetLocation(OBJECT_SELF)); if (GetIsObjectValid(oStore)) DelayCommand(0.5, OpenStore(oStore, GetPCSpeaker())); } else if((GetGlobalNumber("000_Jedi_Found") == 4)) { object oStore = GetObjectByTag("thor4"); if (!GetIsObjectValid(oStore)) oStore = CreateObject(OBJECT_TYPE_STORE, "thor4", GetLocation(OBJECT_SELF)); if (GetIsObjectValid(oStore)) DelayCommand(0.5, OpenStore(oStore, GetPCSpeaker())); }}

This script is used in the same way as the previous script which has only one merchant filebut it will require 5 merchant scripts.

[TUTORIAL] - KotOR Modding Tutorial Series (4)

Using the source script from the games files “a_playpazaak” you can also start a game of pazaak using the same process.

The following script will open a door on the map when you interact with a placeable, it could also be used in a dialog if you wish.

void main(){ object oDoor = GetObjectByTag("yav50_airlock"); ActionOpenDoor(oDoor);}

In this example the placeable “yav_panel50x.utp” which is an invisible placeable loads the above script “yav_panel05” in the OnUsed section which can be found using the KGFF editor on a “.utp” (Placeable) file, the script then opens the door called “yav50_airlock”.

Another useful property to note is the Static property of the doors, which can be used to determine whether or not the door can be selected by the player.

03 - [ADDITIONAL]- Using ERFEdit

Spoiler

ERFEdit is useful for extracting the contents of other module files, this can be handy to look up an example from another level.

In order to make use of the following three examples, you will want to use ERFEdit to extract the contents of the module, you will also need DLGEdit & K-GFF Editor, extract the files and examine them to hopefully save others time from searching through the Characters, Placeables and Alien Voice Overs.

You can also just spawn in each module and look around, each character or placeable will have their number references as their name.

Characters 0-700 approx

Placeables 0-400 approx

Dialog 0-42

With all of these testingrooms I have still had the main three mods I am planning to provide compatibility for ( TSLRCM, M478EP & Jedi Temple ( Coruscant ) Mods ) installed to the game, so if you do not some of their appearances may be missing.

04 - [ADDITIONAL] - Picking an NPC to place in your level - TSL

Spoiler

I was dreading finding a specific NPC to place around any of the levels I am working on so I drafted up these test levels with every NPC from TSL in them.

Using these modules you should have no trouble picking an NPC and identifying what number entry they are in the appearance.2da file.

Testing Rooms.7z

Screenshots

Spoiler

099dia

199dia

299dia

399dia

499dia599dia699dia

I hope this will be useful for others, I haven't made full use of it yet, but I can now roam around and pick out which NPC's I would like to place and not have to worry about finding their entry number, as well as having the ability to view nearly all of them at once.

05- [ADDITIONAL] - Picking Placeables to fill your level with - TSL

Spoiler

I decided to do the same thing for placeables, so that I could not only look through them all at once but hopefully find it easier to populate thelevels I am working on with placeables and NPC's.

Testing Placeable Rooms.7z

Screenshots

Spoiler

699plc

799plc

899plc

999plc

06 - [ADDITIONAL] - Picking Alien Voice Over Dialog for your level - TSL

Spoiler

In the same style as the placeables and characters, but contains one character per alien in the "alienvo.2da" file and a dialog entry for each type of voice over they can use, excluding null entries.

Alien VO Dialog Testing Room.7z

The Rakata & I think a few others don't have voice files even though the 2da file states that they do, I will go through these at some point.

Screenshots

Spoiler

000voi

07- [ADDITIONAL] - Placement & Rotation

Spoiler

The bearing of a placeable is it's rotation, characters have an X orientation and a Y orientation which also pertains to their rotation.

The below co-ordinates are what I am using for making a placeable rotate to face any of the cardinal directions.

I have seen that sometimes people over rotate from these numbers until they get to the right position, for example to face East can also be 4.5 approximately, but I have found the below values to be the best, if someone knows better please let me know.

I generally use KotOR Toolset to locate an item in an existing module and record its co-ordinates then Iedit its co-ordinatesusing K-GFF Editor, from there I tend to test the level over and over again until I get the placement just right.

North
-3.1415901184082

West
-1.5707950592041

South
0

East
1.5707950592041

I realise they are using Radians or Pi, so this can be positive or negative accordingly, Pi itself is*3.14159265359 ( so that might be a more accurate value to use ) though I haven't used that just yet, but it will be such a minute change in rotation that it would be practically impossible to notice anyway.

But I have noticed some things in the game over rotate to get to the correct position, so I thought this might be worth noting for other users, I used other objects in game to determine North & South and went from there.

I also found the below example which is probably a lot more detailed, quoted from Lucas Forums by e-varmint

Spoiler

The "bearing" measurements for doors
and placeables in the .git file use radians instead of degrees.

Degrees Radians
15= 0.261799388
30= 0.523598776
45= 0.785398163
60= 1.047197551
75= 1.308996939
90= 1.570796327
105= 1.832595715
120= 2.094395102
135= 2.35619449
150= 2.617993878
165= 2.879793266
180= 3.141592654
195= 3.403392041
210= 3.665191429
225= 3.926990817
240= 4.188790205
255= 4.450589593
270= 4.71238898
285= 4.974188368
300= 5.235987756
315= 5.497787144
330= 5.759586532
345= 6.021385919


by e-varmint

08- [TUTORIAL] - Waypoint Markers

Spoiler

Below is the documentation left within the blueprint waypoint files within the game, when I have actually placed some myself and am more familiar with the process I will try to write a step by step for it as well.

"This is the default waypoint you may place to set a patrol path for a creature or NPC.
1. Create the creature and either use its current Tag or fill in a new one.
2. Place or make sure the WalkWayPoints() is within the body of the On Spawn script for the creature.
3. Place a series of waypoints along the route you wish the creature to walk.
4. Select all of the newly created waypoints and right click. Choose the Create Set option. ( The set will be defined by their numbers )
5. The waypoint set will have a set name of "WP_" + NPC Tag. Thus if an NPC with the Tag "Guard" will have a waypoint set called "WP_Guard". Note that Tags are case sensitive."

Bullet Point #1 Create your creature. ( .utc file which will be spawned using the .git file for the module )
Bullet Point #2 refers to what will be needed in the script. ( Example Scripts Below )
Bullet Point #3 Place your waypoints. ( placing the .utw files along a specified path using the .git file of the module )
Bullet Point #4 will be referring to the software they would have been using during development.
Bullet Point #5 refers to naming the waypoint files. ( "WP_Guard_01.utc" etc )

I have three script examples so far for NPC's walking, but I haven't used any of them yet so cannot confirm they work until I start populating the areas I am working with.

Continue Walking Waypoints - "walk_continue.nss"

Spoiler

#include "k_inc_generic"
#include "k_inc_debug"
#include "k_inc_treas_k2"

void main() {
// Force them to continue walking waypoints. onEndDialog
GN_WalkWayPoints();
}

Walk Around Randomly - "walk_random.nss"

Spoiler

void main() {
// onHeartbeat
AssignCommand(OBJECT_SELF,ActionRandomWalk())
}

Walk Waypoints On Spawn - "walk_spawn.nss"

Spoiler

#include "k_inc_generic"
#include "k_inc_debug"
#include "k_inc_treas_k2"

void main()
{
GN_SetDayNightPresence(AMBIENT_PRESENCE_ALWAYS_PRESENT);
GN_SetListeningPatterns();
GN_WalkWayPoints();
}

09 - [TUTORIAL] - Scripting Basics

Spoiler

I will not cover much scripting, but when I am finished with the project all the source will be made available.

You will need KotOR Scripting Tool & possibly DeNCS if you plan to use it ( it can be helpful to get examples from other scripts already in the game to see how they handle certain events and to get an idea of the common operators, functions and the syntax )

The script is saved as a .nss file and then compiled into a .ncs file, I use KotOR Scripting Tool but I think KotOR Tools Text Editor can also compile scripts.

Remotely Opening A Door

Spoiler

void main()
{
object oDoor = GetObjectByTag("yav50_airlock");
ActionOpenDoor(oDoor);
}

The above script is called by an invisible placeable in front of the computer screens on the Yavin Orbital Station and the script itself is referenced in the OnUsed section of the associated .utp file, it is also getting the relevant door by its tag which is declared in the relevant doors .utd file.

That's a very basic script, you can also scroll through the KotOR Scripting Tool to see all the available commands on the right hand side, I will be posting basic scripts in this section and hopefully they might be useful for others.

Upon typing the command there is also a description shown in the bottom window of the program.

If you have trouble compiling a script, make sure that "nwscript.nss" is located in the games override folder, also check that the tool is set to KotOR 1 or KotOR 2 accordingly, another possible problem is your relevant registry entry which can either be added back in manually, or just run the "swupdate.exe" and it will update the registry entry ( I had this problem as I was using two seperate installs of KotOR 2 for testing )

10 - [EXAMPLE SCRIPT] - Spawning NPC's on Enter

Spoiler

Below is an example of an on Enter script spawning four NPC's via their utc file packaged with the .module but not placed in the levels git file.

In order to do this you will also need to add a waypoint to the git file to point to where to spawn your NPC.

This code can be used to spawn NPC's dependant on a Global Variable, it is also checking if the leader of the group already exists ( I should check if they all exist, but this works for the moment )

Spoiler

void main() {
if(GetGlobalNumber("SLE_FIREBLOODS") == 1)
{
if (!GetIsObjectValid(GetObjectByTag("rodian2", 0))) {
CreateObject(1, "rodian2", GetLocation(GetObjectByTag("wp_rodian2", 0)), 0);
CreateObject(1, "rodian1", GetLocation(GetObjectByTag("wp_rodian1", 0)), 0);
CreateObject(1, "rodian3", GetLocation(GetObjectByTag("wp_rodian3", 0)), 0);
CreateObject(1, "rodian4", GetLocation(GetObjectByTag("wp_rodian4", 0)), 0);
}
}
}

Hopefully this short example helps.

11 - [EXAMPLE SCRIPT] - Making NPC's Hostile!

Spoiler

The below code changes the above spawned NPC's to being hostile and changes the Global Number for the Variable storing the state of that questline / encounter.

Spoiler

void main() {
object oRodian2 = GetObjectByTag("rodian2", 0);
object oRodian1 = GetObjectByTag("rodian1", 0);
object oRodian3 = GetObjectByTag("rodian3", 0);
object oRodian4 = GetObjectByTag("rodian4", 0);
ChangeToStandardFaction(oRodian2, 1);
AssignCommand(oRodian2, ActionAttack(GetFirstPC(), 0));
ChangeToStandardFaction(oRodian1, 1);
AssignCommand(oRodian1, ActionAttack(GetFirstPC(), 0));
ChangeToStandardFaction(oRodian3, 1);
AssignCommand(oRodian3, ActionAttack(GetFirstPC(), 0));
ChangeToStandardFaction(oRodian4, 1);
AssignCommand(oRodian4, ActionAttack(GetFirstPC(), 0));
SetAreaUnescapable(1);
SetGlobalNumber("SLE_FIREBLOODS", 2);
}

12 - [EXAMPLE SCRIPT] - Trigger talking to an NPC!

Spoiler

The below code requires a trigger to be placed in the level, on entering the trigger this script will fire.

It starts a conversation with the lead Rodian from the above examples.

Spoiler

void main() {
if(GetGlobalNumber("SLE_FIREBLOODS") == 1)
{
object oEntering = GetEnteringObject();
if ((!GetIsPartyLeader(oEntering))) {
return;
}
object oRodian2 = GetObjectByTag("rodian2", 0);
AssignCommand(oRodian2, ActionStartConversation(oEntering, "fireblood", 0, 0, 0, "", "", "", "", "", "", 0, 0xFFFFFFFF, 0xFFFFFFFF, 0));
DestroyObject(OBJECT_SELF, 0.0, 0, 0.0, 0);
}
}

13 - [EXAMPLE SCRIPT] - Make an NPC walk randomly

Spoiler

This script is assigned to the heartbeat function of the utc file for the NPC.

Spoiler

void main() {
// onHeartbeat
AssignCommand(OBJECT_SELF,ActionRandomWalk());
}

14 - [EXAMPLE SCRIPT] - Place a dead NPC

Spoiler

This script needs to be set in the spawn function for the NPC in the relevant .utc file.

Spoiler

void main() {
effect efDeath = EffectDeath(0, 0, 1);
ApplyEffectToObject(2, efDeath, OBJECT_SELF, 0.0);
}

15 - [EXAMPLE SCRIPTS] - Starting Conditionals

Spoiler

This script is an example of a starting conditional that can be used to check what module the player is in, this could be used for companion dialogs to check you are in a specific area in order to engage in a certain dialog option.

Spoiler

int StartingConditional()
{
if ( ( GetModuleName() == "end_m01aa") ||
( GetModuleName() == "end_m01ab") ||
( GetModuleName() == "tar_m11ab")) {
return TRUE;
}
return FALSE;
}

The example I have provided checks if it is any of the three named modules, if so it returns TRUE to the DLG, if not it returns FALSE.

It can be modified to checkas many modules as required, I included three as an example to either cut down or expand upon.

c_has_feat.nss

This checks if the player has a specific feat using the number of the row in the feat.2da file.

Spoiler

//Thor110
int StartingConditional()
{
int nParam1 = GetScriptParameter (1);
if(GetHasFeat(nParam1,GetFirstPC()) == TRUE )
{
return TRUE;
}
return FALSE;
}

c_modname_diff.nss

This is essentially c_modname_comp.nss, but it returns False instead of True if you are inside the level in question and I fixed a typo in the header.

Spoiler

/*
Returns TRUE if the passed in string parameter is equal to
the current module name.

Created by: Anthony Davis-OEI
Modified (hardly) by: Thor110
*/
int StartingConditional()
{
string modName = GetScriptStringParameter();
string currentMod = GetModuleName();
if(modName == currentMod)
return FALSE;
return TRUE;
}

That's it for now, but I will add any more conditional scripts I write here when I write them.

16 - [TUTORIAL] - Companion Vendor

Spoiler

I recently came up with an idea for a "Droid Companion Vendor" which will sell droids to the player to replace any lost or missed companions throughout the game.

Also given that it demonstrates control of the party / companions, I thought I would work this into a tutorial.

When I have completed the level / vendor itself, I will come back here to write a tutorial, provide the files and give examples on how this could be used to create a Total Conversion

There are a total of 50-55 Droid Types, the plan is to have either all or a select set of the best droids as options.

Maybe even spawning the droid next to the Vendor so that you can see them before buying them, or housing them in a store house with the droids you can purchase on display in the area.

From here you will be able to purchase a new companion for any slot in the game.

for anybody interested here is the code for the conditional script I wrote to check if a specificplayers companion slot is free.

Spoiler

#include "k_inc_debug"

int StartingConditional()
{
int nComp = GetScriptParameter( 1 );
if ((IsAvailableCreature(nComp)) == FALSE)
return TRUE;
else
return FALSE;
}

Here is a slightly different version that checks all companion slots at once.

Spoiler

#include "k_inc_debug"

int StartingConditional()
{
if ((GetNPCSelectability(9)) == -1)
return TRUE;
else if ((GetNPCSelectability(8)) == -1)
return TRUE;
else if ((GetNPCSelectability(7)) == -1)
return TRUE;
else if ((GetNPCSelectability(6)) == -1)
return TRUE;
else if ((GetNPCSelectability(5)) == -1)
return TRUE;
else if ((GetNPCSelectability(4)) == -1)
return TRUE;
else if ((GetNPCSelectability(3)) == -1)
return TRUE;
else if ((GetNPCSelectability(2)) == -1)
return TRUE;
else if ((GetNPCSelectability(1)) == -1)
return TRUE;
else if ((GetNPCSelectability(0)) == -1)
return TRUE;
else
return FALSE;
}

here is the code I use to preview the droid, spawning them at the location of the vendor.

Spoiler

void main() {
string nDroid = GetScriptStringParameter();
CreateObject(1, nDroid, GetLocation(GetObjectByTag("blackmarket", 0)), 0);
}

this code is then used to destroy them, if you don't purchase them.

Spoiler

void main() {
string nDroid = GetScriptStringParameter();
DelayCommand(0.1, DestroyObject(GetObjectByTag(nDroid), 0.0, 1, 0.0));
}

and here is the code I used to make them join the party.

Spoiler

void main() {
string nDroid = GetScriptStringParameter();
if ((IsAvailableCreature(9)) == FALSE) {
AddAvailableNPCByTemplate(9, nDroid);
ShowPartySelectionGUI("k_pend_reset", 9, 0xFFFFFFFF);
}
else if ((IsAvailableCreature(8)) == FALSE) {
AddAvailableNPCByTemplate(8, nDroid);
DelayCommand(0.1, ShowPartySelectionGUI("k_pend_reset", 8, 0xFFFFFFFF));
}
else if ((IsAvailableCreature(7)) == FALSE) {
AddAvailableNPCByTemplate(7, nDroid);
DelayCommand(0.1, ShowPartySelectionGUI("k_pend_reset", 7, 0xFFFFFFFF));
}
else if ((IsAvailableCreature(6)) == FALSE) {
AddAvailableNPCByTemplate(6, nDroid);
DelayCommand(0.1, ShowPartySelectionGUI("k_pend_reset", 6, 0xFFFFFFFF));
}
else if ((IsAvailableCreature(5)) == FALSE) {
AddAvailableNPCByTemplate(5, nDroid);
DelayCommand(0.1, ShowPartySelectionGUI("k_pend_reset", 5, 0xFFFFFFFF));
}
else if ((IsAvailableCreature(4)) == FALSE) {
AddAvailableNPCByTemplate(4, nDroid);
DelayCommand(0.1, ShowPartySelectionGUI("k_pend_reset", 4, 0xFFFFFFFF));
}
else if ((IsAvailableCreature(3)) == FALSE) {
AddAvailableNPCByTemplate(3, nDroid);
DelayCommand(0.1, ShowPartySelectionGUI("k_pend_reset", 3, 0xFFFFFFFF));
}
else if ((IsAvailableCreature(2)) == FALSE) {
AddAvailableNPCByTemplate(2, nDroid);
DelayCommand(0.1, ShowPartySelectionGUI("k_pend_reset", 2, 0xFFFFFFFF));
}
else if ((IsAvailableCreature(1)) == FALSE) {
AddAvailableNPCByTemplate(1, nDroid);
DelayCommand(0.1, ShowPartySelectionGUI("k_pend_reset", 1, 0xFFFFFFFF));
}
else if ((IsAvailableCreature(0)) == FALSE) {
AddAvailableNPCByTemplate(0, nDroid);
DelayCommand(0.1, ShowPartySelectionGUI("k_pend_reset", 0, 0xFFFFFFFF));
}
}

This checks if the companionis available, it then adds the NPC to the party and shows the selection screenmaking sure that the correct slot is selected.

For now this is a very roughly written idea for a tutorial, but I have created the level and when it is finished I will upload it here as a tutorial for people to make use of.

I plan to continue to expand upon this idea and fill the level with scripts and examples provided here for people to pull apart and make use of.

Fow now there is a bug that means if you buy the same droid twice, they remain there because it tries to destroy the NPC in your Party, instead of the one being previewed, this is a side-effect of the fact I wanted to try and have all 55 available droids in the game as options, but it seems without using a global variable the best way to do this will be to individually select each droid for the list. ( there might still be a better way, I have yet to try again )

000dia.mod

Feel free to learn from this example, the idea itself I plan to use and release as either a stand-alone mod oras a part of the Expanded Galaxy Project.

17- [TUTORIAL] - Upgradeable Items

Spoiler

Below is a list of the UpgradeLevel numbers that can be used in .uti files that I found posted by another user on DeadlyStream, I thought I would link through to it and repost it here.

In order to make an item upgradeable the below entry needs editing in the associated .uti file to be in line with the numbers below by item type.

[TUTORIAL] - KotOR Modding Tutorial Series (22)

Robes (Knight & Master) 1
Armored Robes (Zal Shey) 2
Light Armors 2
Medium Armors 3
Heavy Armors 4

Regular (Non-Powered) 1
Vibro Weapons (Powered) 2
Lightsabers (All Types) 3

Blasters (Scope Only) 1
Blasters (Scope,Chamber) 2
Blasters (All Upgrades) 3

Below is a link to the source from which I found this information.

Source :https://deadlystream.com/topic/1488-question-on-making-an-item-upgradeable-in-game/

18- [ADDITIONAL] -Door Gallery

Spoiler

A recent request by another modder to make a similar room to my other testing rooms that displayed the available doors in the game.

Doors are all numbered appropriately according to their 2da entry in genericdoors.2da to make them easier to find. They also autoclose after being opened if you are far enough away ( same script as jekk jekk tarr tunnels )

Door Gallery.7z

19- [ADDITIONAL] -Main Menu Details

Spoiler

In-case anybody ever want's to modify the main menu for a Total Conversion or general mod, you can use the following details to help you do so and the following line of code in a script to change the relevant variable.

SetGlobalNumber("GBL_MAIN_SITH_LORD",4);

Keep in mind for total conversions and big mods that this variable exists by default in globalcat.2da as do some other variables that you might need to keep.

The most recently loaded save determines which menu is shown on the main menu while in-game and I think it might be the auto-save when starting the game up,the number variable "GBL_MAIN_SITH_LORD" sets which is shown.

1 = mainmenu01.mdl = Darth Sion
2 = mainmenu03.mdl = Darth Nihilus
3 = mainmenu04.mdl = Kreia
4 = mainmenu05.mdl = Player

The player can also be changed to other appearances in the main menu using disguises or when the player character is set to something else such as when playing as Bao-Dur's Remote on Malachor V.

At some point in future I might work on a script that can change the players appearance based on another variable using either disguises or changing the active character under a fade-in / fade-out so that the number of main menus can be almost infinitely expanded or at least to the limit of number values.

This could then be spoofed so that any appearance could be shown on the menu, for example it could show the last NPC you spoke to.

However I am not sure how efficient this would be under a save/load fade-in/fade-out cover. But it's an interesting thought or idea none-the-less.

20 - [ADDITIONAL] - UICustomization

Spoiler

Where I have completely recoloured the GUI from green to blue for my port of the first game as well as helped out with a few other GUI changes I felt like I would write a short piece on how to quickly recolour the GUI files.

First off you will need to collect all the original files needed or recolour those included in my release.

For the TGA files, they can be quickly recoloured in GIMP using Color -> Colorisation and then picking a colour.

The following five files differ between the Steam & 4CD versions of the game, so that is worth paying attention to.

custpnl_p.gui
equip_p.gui
mainmenu8x6_p.gui
optgraphics_p.gui
saveload_p.gui

For the Steam version of the game there are also a bunch of extra TGA files.

This is the latest version of the K1 GUI for the Steam version of the game.

K1 GUI.7z

Lot's of details can be changed in the .gui files themselves, but for the most part it is the border, hilight and text colours you want to pay attention to.

This can also be made easily viewable using the KotOR GUI Editor :Visual KotOR GUI Editor (KGE) - Modding Tools - Deadly Stream

I have had trouble changing some elements of the .gui, as best as I can tell the problem is caused by a mixture if incorrectly applied masks and colour values and that some of the buttons text seems to have hardcoded colours ( though I could be wrong and have just not found where they are stored yet )

It seems as if in some instances the .tga files are supposed to act as masks while the colour values stored in the .gui files are supposed to colour those masks, however in some instances the .tga files themselves have been recoloured making it difficult to determine why the colour applied in the .gui file is wrong.

21- [ADDITIONAL] - Recolouring Textures

Spoiler

This sort of thing can pretty much be done at the click of a single button in GIMP.

Simply open the .tga file, follow the instructions here, then click File - > Overwrite original file.

Colors -> Colorize -> Colorizer -> Color

[TUTORIAL] - KotOR Modding Tutorial Series (24)

You can also use "Rotate Colors" after selecting an area of the image you are modifying.

Colors -> Map -> Rotate Colors

[TUTORIAL] - KotOR Modding Tutorial Series (25)

These two methods can allow you to quickly recolour an entire image or to manually select part of an image to recolour using the selection tools available.

22 - [ADDITIONAL] - Recolouring the .GUI Files and Textures

Spoiler

To continue from recolouring textures to recolouring the .gui files I have prepared a basic mask to demonstrate how you can quickly and easily recolour part of an image using a mask.

First open the file and make the "Pasted Layer" visible with the eye icon button and then use the Fuzzy Select Tool to select the mask.

[TUTORIAL] - KotOR Modding Tutorial Series (26)

Next hide the "Pasted Layer" and switch back to the main layer as the active layer by selecting it.

[TUTORIAL] - KotOR Modding Tutorial Series (27)

From here go to Colors -> Colorize and select your profile or set one up.

[TUTORIAL] - KotOR Modding Tutorial Series (28)

Now the image will change colour according to your selection.

[TUTORIAL] - KotOR Modding Tutorial Series (29)

This technique can be used in a lot of different ways for a lot of different tasks.

In order to create the mask in the first place I had to slowly select the area I chose to recolour by using the Fuzzy Select Tool with a tolerance that was adequate.

The following two files contain the mask and controller image shown in the above images.

cntrl_ps3_fre.xcf

cntrl_xb360_eng.xcf

There are five in total that were added to the game as of the Aspyr patch on Steam.

To recolour the rest of the GUI in the game you will need to single out the images used as well as the .gui files that also contain various colour values.

The KotOR GUI Editor is a fantastic tool and visual aid for doing this, if you set up a folder with all of the game's gui textures in a folder as well as the .gui files, it makes it quick and easy to view and recolour many of it's pieces.

At some point in the future I will try to have a complete .gui recolour pack prepared for users that will make it easier to recolour the entire .gui quickly or you can find a version of the K1 recolour in the following tutorial from this series.#20 - [ADDITIONAL] - UICustomization

I advise using that as a base to pick out the original files and recolour those instead of recolouring the K1 recolours to preserve their original looks as best as possible.

The following is a list of files that the user interface uses, though I might have missed a few because I pulled this list from an old set of files.

Spoiler

Aspyr Patch

cntrl_ps3_eng
cntrl_ps3_fre
cntrl_ps3_ger
cntrl_ps3_ita
cntrl_ps3_spa
cntrl_xb360_eng
cntrl_xb360_fre
cntrl_xb360_ger
cntrl_xb360_ita
cntrl_xb360_spa
cus_gpad_bg
cus_gpad_fper
cus_gpad_gen
cus_gpad_hand
cus_gpad_help
cus_gpad_map
cus_gpad_save
cus_gpad_solo
cus_gpad_solox
cus_gpad_ste
cus_gpad_ste3

Regular Game

blackdot
friendlyarrow
lbl_force_neg
lbl_force_pos
lbl_icn_msg
lbl_mibox00
lbl_micombat
lbl_micomeff02
lbl_miscroll_f
lbl_miscroll_f2
lbl_miscroll_open_f
mm_barrow
mm_barrow_p
uibit_abi_arrow
uibit_abi_back
uibit_arrowneg_p
uibit_arrowneg_x
uibit_arrowpos_p
uibit_arrowpos_x
uibit_arrow_l
uibit_arrow_r
uibit_brdr_16bct
uibit_brdr_16bet
uibit_brdr_16gc
uibit_brdr_16ge
uibit_brdr_16oc
uibit_brdr_16oe
uibit_brdr_16wc
uibit_brdr_16wct
uibit_brdr_16we
uibit_brdr_16wet
uibit_brdr_c3d_p
uibit_brdr_c3d_x
uibit_brdr_chr1
uibit_brdr_chr1p
uibit_brdr_chr2
uibit_brdr_chr2p
uibit_brdr_chr3p
uibit_brdr_chr4p
uibit_brdr_map_p
uibit_brdr_map_x
uibit_brdr_port
uibit_brdr_portp
uibit_checkbxoff
uibit_checkbxon
uibit_eqp_cnfg
uibit_eqp_cnfg_p
uibit_eqp_itm1
uibit_eqp_itm2
uibit_eqp_itm3
uibit_eqp_shld
uibit_eqp_shld_p
uibit_fb_arrow
uibit_fb_arrow_x
uibit_fill_16b
uibit_fill_16g
uibit_fill_16gd
uibit_fill_16o
uibit_fill_2wt
uibit_no_npc
uibit_no_npc_p
uibit_slider
uibit_slider2
uibit_slider2_p
uibit_slider_p
uibit_stf_abi3
uibit_uparrowg
uibit_uparrowo

23- [ADDITIONAL] - Different Camera Modes & Dynamically Spawned Grid Of Placeables

Spoiler

Came across this recently, there is a Top Down Camera Mode.

As defined in nwscript.nss

int CAMERA_MODE_CHASE_CAMERA = 0;
int CAMERA_MODE_TOP_DOWN = 1;
int CAMERA_MODE_STIFF_CHASE_CAMERA = 2;

Top Down

[TUTORIAL] - KotOR Modding Tutorial Series (30)

Stiff Chase Camera

[TUTORIAL] - KotOR Modding Tutorial Series (31)

Chase Camera

[TUTORIAL] - KotOR Modding Tutorial Series (32)

A script to change this value, this script is meant to be used in a dialog.

Spoiler

void main() {
int cValue = GetScriptParameter( 1 );
SetCameraMode(GetFirstPC(),cValue);
}

I was thinking about making a Chess mini-game.

[TUTORIAL] - KotOR Modding Tutorial Series (33)

Having finished the script for generating the grid, I have made sure that the size can be altered with a single value, the value must be an even number otherwise the pattern doesn't work, I might look at fixing that later on for the fun of it.

Below is the script I wrote to generate the grid, I have ensured to comment it fairly well to make it easier to understand.

I am fairly certain it could be a lot more efficiently written, but this is what I ended up with when writing it from scratch.

Spoiler

void main() {
float gridX = 0.0f;//Grid X co-ordinate
float gridY = 0.0f;//Grid Y co-ordinate
float gridS = 2.0f;//Grid Size ( Size or spacing of the object used as the tile is stored here)
string gridO = "flpnl000";//Grid Object String = Template Res Ref
location oLocation;//Object Location Variable
int tCount=0;//tCount keeps track of the current tile being spawned
int gridSize=8;//Length & Width have to be the same and multiples of 2
int tLength=gridSize;//Equal to gridSize ( this variable get's modified so that the while statement in the for loop repeats
int i;
for(i = 1; i <= gridSize; i++)//count from 1 to grid size
{
while(tCount != tLength)
{
oLocation = Location(Vector(gridX, gridY, -2.6f), 0.0f);//Set up the location co-ordinates.
CreateObject(64,gridO,oLocation,FALSE);//Create the object at the specified co-ordinates
gridX+=gridS;//adding the grid spacing to the X co-ordinate
tCount++;//increase the tCount counter by 1
}
tLength+=gridSize;//Adding gridSize to tLength each count so that the while statement repeats on the next count
gridX=0.0f;//resetting the X co-ordinate to 0.0f
gridY+=gridS;//adding the grid spacing to the Y co-ordinate
}
tLength=gridSize-1;//Reset tLength and remove one to account for it being used for the nTh value when setting the pattern and to match against tCount IE : 0-7 instead of 0-8
tCount=0;//Reset tCount
int test=0;//Used to alternate the pattern each row of the grid
for(i = 1; i <= gridSize; i++)//count from 1 to grid size
{
while(tCount <= tLength)
{
if(test==0) {
test=1;
} else {
AssignCommand(GetObjectByTag("FloorPanel00", tCount), ActionPlayAnimation(201, 1.0, 0.0));
test=0;
}
tCount++;
}
tLength+=gridSize;//Adding tLength to gridSize each count so that the while statement repeats on the next count
if(test==0) {
test=1;
} else {
test=0;
}
}
}

[TUTORIAL] - KotOR Modding Tutorial Series (34)

Above is a 32x32 grid.

In-case you are interested in checking out the level, I have included the .mod file here :000top.mod( Version 17 )

All source scripts are inside the archive, nothing really works yet but I have added a terminal and some test functions for using while I work out the specific interactions I want to use and the logic I need.

[TUTORIAL] - KotOR Modding Tutorial Series (35)

24 - [ADVANCED] - Hard Coded GUI Elements

Spoiler

For a while now it has bothered me that some of the GUI elements are hardcoded inside the executable and so I decided to take it upon myself to track them down and change them.

LegacyPC/CD Values incoming once I have found the time to test them properly.

Aspyr Executable Hexadecimal Addresses and Original Values

Dialog Text ( RGB )

0.10 @ 005862FC = CD CC CC 3D
0.69 @ 0058575C = 33 33 33 3F
0.55 @ 0059DBB0 = CD CC 0C 3F

Button Border ( RGB )

0.05 @ 0058C530 = CD CC 4C 3D ( effects use item text and item icon highlight as well )
0.34 @ 0058AA98 = 33 33 B3 3E ( effects use item text and item icon highlight as well )
0.27 @ 005A03AC = 71 3D 8A 3E ( effects use item text, use item button border and item icon highlight as well )

Status Effects( R?B)

Can't find green for the status effects

0.94 @ 0059AF70 = 33 33 73 3F
0.70 - 0.80 @ ?
0.30 @ 00585760 = 9A 99 99 3E ( effects use item button border in inventory and unused item button borders as well )

Text Highlight( ??B )

Can't find red or green for the text highlight

0.80 @ ?
0.80 @ ?
0.69 @ 0058A978 = 9A 99 19 3F

Text Highlight Pulsing( Byte )

Found this by accident.

63 @ 005861B7 = 3F

The higher the value, the slower the pulsing.
The lower the value, the faster the pulsing.

Note : thisbyte is already at it's highest value.

Reference Table

I wrote up this table as reference while I was hexediting the executable, changing the values to 0.0 or 1.0 causes problems elsewhere in the game.

It's likely that if the value was originally 0.0 or 1.0 for any of the RGB values used that that the values wouldn't have taken up four bytes and would have been compiled differently, though I am not certain.

But I knowthat if I changed the red value for the text to 0.0, the game didn't load in and stayed stuck on a black screen and if I changed the blue value of the text to 1.0, models didn't load in properlystarting with the main menu character.

It's also possible that glColor3f or glColor4f just doesn't accept 0.0 or 1.0 but I am not certain.

0.0 = 00 00 00 00
0.1 = CD CC CC 3D
0.2 = CD CC 4C 3E
0.3 = 9A 99 99 3E
0.4 = CD CC CC 3E
0.5 = 00 00 00 3F
0.6 = 9A 99 19 3F
0.7 = 33 33 33 3F
0.8 = CD CC 4C 3F
0.9 = 66 66 66 3F
1.0 = 00 00 80 3F

It is now possible to fully recolour the entire GUI of the game.

Because I plan to get my project on the original Xbox one day,I also tracked down these valuesinside the .xbe and luckily they all sat neatly alongside each other.

Xbox GUI Memory Addresses and Original Values

Light Green Text ( RGB )

0.10 @ 003F5B50 = CD CC CC 3D
0.69 @ 003F5B54 = 33 33 33 3F
0.55 @ 003F5B58 = CD CC 0C 3F

Dark Green Border ( RGB )

0.05 @ 003F5B38 = CD CC 4C 3D
0.34 @ 003F5B3C = 33 33 B3 3E
0.27 @ 003F5B40 = 71 3D 8A 3E

Status Effects ( RGB )

0.94 @ 003F5B5C = 33 33 73 3F
0.66 @ 003F5B60 = C3 F5 28 3F
0.30 @ 003F5B64 = 9A 99 99 3E

Text Highlight ( RGB )

0.69 @ 003F5B44 = 33 33 33 3F
0.69 @ 003F5B48 = 33 33 33 3F
0.60 @ 003F5B4C = 9A 99 19 3F

Text Highlight Pulsing ( Unknown )

A 3F somewhere amongst 9517 3F's

This colour effects items that you haven't viewed yet as well as the currently selected waypoint on the map.

[TUTORIAL] - KotOR Modding Tutorial Series (36)

While searching for these values, I also found another value that seems to change the border of one of the inventory button icons, not entirely sure why.

[TUTORIAL] - KotOR Modding Tutorial Series (37)

0.05 @ 003F68D8 =CD CC 4C 3D

How strange.

25 - One Line Dialogs

Spoiler

One line dialogs are a great way to give your background NPCs a bit of life.

Starting Conditional Script

Spoiler

//Thor110
//TSL One Line Dialog Random Starting Conditional Script
int StartingConditional()
{
int i = GetScriptParameter(1);
return Random(i) == 0;
}

This script can be used to have as many one line dialogs as you want that make use of the Random function to determine which line is spoken.

one_liner.dlg

This dialog file showcases how to make use of it, ensure to compile it and pass it the correct line number.

[TUTORIAL] - KotOR Modding Tutorial Series (38)

Like this.

99 - [REQUESTS] - Submit a tutorial idea!

Spoiler

If you have ideas for a tutorial feel free to message me and let me know.

For a while now I have been considering creating a playground of sorts across a few maps and filling it with a combination of all possible interactions in the game, from animations, to dialog and the various alien responses so that people could play through them, seeing everything in action and use it to better pick out the pieces of code, voiceover, items, placeables, doors or characters that they might need to use in their modification of the game.

[TUTORIAL] - KotOR Modding Tutorial Series (39)

For now I have set up the Shuttle as an example Starship with access to the galaxy map, but there is a lot more work to be done before I release whatever this is.

Very much like the testing roomsI have created that feature all the doors and characters in the game.

I will continue to provide examples that I think might be useful to people, these are all for TSL so far.

I hope these tutorials and any others I write will help people continue to make mods for KotOR 1 & 2.

These tutorials are getting shorter and less detailed, I may or may not go back and re-write them at some point but if anyone struggles with anything feel free to message me and let me know what I haven't covered very well.

Feel free to join the Discord for my project if you need help with anything related to KotOR modding and I will do my best to assist.

Discord :https://discord.gg/S3YyfTjMV8

Base Game Scripts & How To Use Them

Spoiler

I am considering working on an index for the base game scripts and how they all function so that people can more easily use them in dialog files without having to constantly look up how they work, what parameters need to be passed along to the script and other such details.

For now here is one such example that I discussed with another user recently.

a_hitman.nss/.ncs - is used to turn an NPC hostile, if they are already hostile it will turn them insane.

Spoiler

//:: FileName a_hostile
//:: Created By: Kevin Saunders
//:: Created On: 07/20/04
//::
//:: Object with Tag = ScriptStringParameter becomes hostile.
// Modified: Tony Evans 9/9/04 (Added nInst parameter)
// Modified: Tony Evans 9/15/04 (Added OBJECT_SELF default and nSane param)

void main()
{

string sCritter = GetScriptStringParameter();
int nInst = GetScriptParameter(1);
int nSane = GetScriptParameter(2);

object oTarg;

if (sCritter == "") oTarg = OBJECT_SELF;
else oTarg = GetObjectByTag(sCritter, nInst);

if (!nSane) ChangeToStandardFaction(oTarg,STANDARD_FACTION_HOSTILE_1);
else ChangeToStandardFaction(oTarg,STANDARD_FACTION_INSANE);
}

[TUTORIAL] - KotOR Modding Tutorial Series (40)

The above image shows how you would use this in a dialog file, example is from the Entertainment Promenade on Nar Shaddaa.

KotOR Modding - Crash Course

This 2 hour stream covers the basics of KotOR modding and some of the programs involved, I also regularly stream development of my mod project on Twitch/YouTube.

Not all of this covers KotOR modding, there is about 10-20 minutes during the first hour where I get distracted and the second hour of it is mostly me playing games.

Spoiler

By the basics, I mean the very basics. An overview of the programs and filetypes associated with KotOR as well as the structure of level files.

These are the testing rooms but for K1 the room is lacking a texture but I find this just makes it easier to look through the characters.

Spoiler

K1 Testing Rooms.7z

If anything in any of my short tutorials isn't clear or you don't understand for some reasonthen let me know and I will do what I can to help.

ADVANCED TUTORIALS

Utilising the following batch scripts and suggestions might take a while to get to grips with for some users but I would always advise doing so, it's very handy to have the ability to quickly generate a file list or compare things in various ways utilising the power of batch scripting.

Comparing Level Contents

Let's say you have two levels and you need to compare the differences of the contents.

Extract everything from the original level to a folder called "level-a" and the modified folder to "level-b"

Run the following batch script inside the "level-a" folder.

file-list.bat

Then delete or move the .bat file and move "file-list.txt" that it just created outside of the folder.

You can also use this script to check that all the files exist.

check-if-file-exists-from-file-list.bat

You will have to move the file-list.txt and this script inside the folder to run the script.

Now run this script at the same location as "file-list.txt" to run a file check against the contents of both folders.

compare-files-from-file-list.bat

Hopefully this might help someone, where I recently had to transfer all of my changes from manual installs to a TSLPatcher changes.ini file, I found this process helped speed the process up a little bit.

From here, you should know which files differ and you can then use ChangeEdit.exe that comes with TSLPatcher in order to generate the differences between GFF files.

Compiling Scripts

If you have made it this far into the tutorial, you should already be familiar with compiling scripts, however for large projects you might need to recompile a lot of scripts at once, so here is a batch script that I used to do it.

compile.bat

Simply edit the script so that it points to the location of nwnnsscomp.exe for you and then you will be able to put the script into any folder that you need to compile a lot of scripts in and use it to do it all at once.

If the scripts don't work for you, you might need to edit them and read them a bit in order to understand them and then to make sure that you have all of the right files in the right places.

Thor110

Edited by Thor110
One Line Dialogs

[TUTORIAL] - KotOR Modding Tutorial Series (2024)
Top Articles
Quest Diagnostics Inside Walmart Supercenter #1032
Flu Test Near Me - Get a Flu Test in Lynn Haven, FL Today! | Solv
Srtc Tifton Ga
Cranes For Sale in United States| IronPlanet
Craigslist Home Health Care Jobs
Moon Stone Pokemon Heart Gold
Faridpur Govt. Girls' High School, Faridpur Test Examination—2023; English : Paper II
Craigslist Vans
Sarah F. Tebbens | people.wright.edu
Craigslist Parsippany Nj Rooms For Rent
Lost Ark Thar Rapport Unlock
Displays settings on Mac
Snowflake Activity Congruent Triangles Answers
Declan Mining Co Coupon
Aquatic Pets And Reptiles Photos
Audrey Boustani Age
Wisconsin Women's Volleyball Team Leaked Pictures
The Witcher 3 Wild Hunt: Map of important locations M19
Stihl Km 131 R Parts Diagram
Games Like Mythic Manor
Sky X App » downloaden & Vorteile entdecken | Sky X
Download Center | Habasit
Craigslist Missoula Atv
Hyvee Workday
Conan Exiles Sorcery Guide – How To Learn, Cast & Unlock Spells
Tips and Walkthrough: Candy Crush Level 9795
Construction Management Jumpstart 3Rd Edition Pdf Free Download
Vernon Dursley To Harry Potter Nyt Crossword
Skycurve Replacement Mat
480-467-2273
Kimoriiii Fansly
Dal Tadka Recipe - Punjabi Dhaba Style
Access a Shared Resource | Computing for Arts + Sciences
Encore Atlanta Cheer Competition
Grove City Craigslist Pets
Craigs List Tallahassee
Missing 2023 Showtimes Near Mjr Southgate
Moonrise Time Tonight Near Me
Autotrader Bmw X5
Culver's Hartland Flavor Of The Day
RFK Jr., in Glendale, says he's under investigation for 'collecting a whale specimen'
Google Jobs Denver
Ippa 番号
Baywatch 2017 123Movies
Raising Canes Franchise Cost
Newsweek Wordle
Dinar Detectives Cracking the Code of the Iraqi Dinar Market
Inducement Small Bribe
Fool's Paradise Showtimes Near Roxy Stadium 14
Learn4Good Job Posting
Workday Latech Edu
Mail2World Sign Up
Latest Posts
Article information

Author: Tish Haag

Last Updated:

Views: 6464

Rating: 4.7 / 5 (67 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Tish Haag

Birthday: 1999-11-18

Address: 30256 Tara Expressway, Kutchburgh, VT 92892-0078

Phone: +4215847628708

Job: Internal Consulting Engineer

Hobby: Roller skating, Roller skating, Kayaking, Flying, Graffiti, Ghost hunting, scrapbook

Introduction: My name is Tish Haag, I am a excited, delightful, curious, beautiful, agreeable, enchanting, fancy person who loves writing and wants to share my knowledge and understanding with you.