The Witcher 3 Notes

General information about the game The Witcher 3
back to summary

Merging

This article isn't about how to install mods as mods tend to have different installation processes. Instead this article will focus on what is often considered the most complex part: script merging.

Why do you need to merge

The game has exposed scripts, a good portion of the source code used to make the game and its logic, is in a folder located in /content/content0. This means that it is possible for someone to copy the script and its folder path and change the code resulting in parts of the games logic/code being changed Let's say you would want to change the content0/gameplay/playerwitcher.ws file, you would then create a mod in the mods folder with the same path /gameplay/playerwitcher.ws

This method has an issue, if two mods change the same file then only one of the mods will get its changes load by the game, the other one will be completely ignored. The solution is to merge the scripts of all the mods that edit the file into a single new mod that will be loaded before everyone else. This will ensure everyone's changes will be included and loaded.

Why is it so complicated

Short answer: because code is complicated.
The long answer is that the scriptmerger is already doing an insane job. Of course it is not perfect, and new better solutions could be developped (i am personally working on one but it may take months to come), but it offers an intuitive and easy to understand interface that anyone can use to compare different versions of a code file. And even with a better tool you would still have to do some manual merging at some point.

You can learn a few things that will make your merging life much easier. You can't learn how to read code in a few minutes or hours but you can learn a few things you should look for to avoid compiling errors like these:

hold click to see larger image

As you can see the errors are always under this format : Error [origin of the error]path/to/the/file/with/error.ws(line number): And the error. From them you can learn a lot, by looking at the origin of the error between the [] you can learn which mod causes the errors. Here in the example we see it comes from the merges and from the file playerwitcher.ws. So without looking at the error itself we already know which file we should maybe re-merge

You may also have noticed a warning below the error Warnings generally of less concern than errors and the game can function with them

Basic merging guide

Merging scripts through kdiff (Script Merger's merging program) comes down to picking the right line of code from one mod or another. You are offered three script versions, the vanilla scripts, mod Bs scripts and mod Cs scripts. It is up to you to pick either A if you want vanilla, B or C. Picking one doesn't stop you from picking another. You can pick both A & B, or B & C etc... You should know that you should rarely pick A as it is vanilla, and you're merging mods for a reason because you don't want vanilla.

The three views at the top are A, B and C as said earlier. And the view at the bottom is the result. What matters most here is how the result looks and you should pick accordingly.

For each conflict, you should use the A, B, C buttons to pick the version of the code you want. Then you use the arrow buttons to move from one conflict to another. In the image you can see both B and C were picked. And there is no other conflict in the current file. You can now close the window safely

Important stuff when merging

This part is about stuff you should watch for in the code. You're not supposed to understand exactly what the code does, but the compiler expects the code to be formatted correctly or else you will get errors like shown above in the article. There are important things that are common causes for errors among non software developers, here is a list of them

For every { comes a }.

hide Here is an valid example:
          if (thePlayer.GetLevel() > 10)
          {
            thePlayer.TeleportWithRotation(Vector(10, 10, 10));
          }
        
And here is an invalid one
          for (i = 0; i < npc_list.Size(); i += 1)
          {
            npc.SetLevel((i + 2) / 5);
          
        
show

A line that starts with else cannot follow another line that starts with an else too. And should also follow a previous if statement. It should not follow it closely but still, you should not see an else without an if

hide Here is a valid example:
          if (thePlayer.GetLevel() > 10)
          {
            thePlayer.TeleportWithRotation(Vector(10, 10, 10));
          }
          else
          {
            thePlayer.TeleportWithRotation(Vector(50, 50, 50));
          }

          if (thePlayer.GetLevel() > 10)
            thePlayer.TeleportWithRotation(Vector(10, 10, 10));
          else 
            thePlayer.TeleportWithRotation(Vector(50, 50, 50));
        
and here is an invalid example
          if (thePlayer.GetLevel() > 10)
          {
            thePlayer.TeleportWithRotation(Vector(10, 10, 10));
          }
          else
          {
            thePlayer.TeleportWithRotation(Vector(50, 50, 50));
          }
          else
          {
            thePlayer.TeleportWithRotation(Vector(100, 100, 100));
          }

          if (thePlayer.GetLevel() > 10)
            thePlayer.TeleportWithRotation(Vector(10, 10, 10));
          else 
            thePlayer.TeleportWithRotation(Vector(50, 50, 50));
          else 
            thePlayer.TeleportWithRotation(Vector(100, 100, 100));
        
show

For every /* comes a */.

hide Here is a valid example:
          if (thePlayer.GetLevel() > 10)
          {
            thePlayer.TeleportWithRotation(Vector(10, 10, 10));
          }
          /*
          els
          {
            thePlayer.TeleportWithRotation(Vector(50, 50, 50));
          }
          */
        
and here is an invalid one
          if (thePlayer.GetLevel() > 10)
          {
            thePlayer.TeleportWithRotation(Vector(10, 10, 10));
          }
          /*
          else
          {
            thePlayer.TeleportWithRotation(Vector(50, 50, 50));
          }
        
show