My Second Mod


If you've read My First Mod and hungry for something a bit more advanced then you may want to try making this modification to Gabriel Knight 3. It isn't particularly interesting, but it does show the basic idea of how mods could work. This mod makes Gabriel yawn whenever he opens or closes the wardrobe in his hotel room.

This tutorial will involve some Sheep scripting, so it might be a good idea to extract "sheep engine.doc" from the game and keep it handy.

Getting Started

The basic idea of this mod is to replace "r25_all.shp" with a custom version. This file contains code for various functions that get run at particular times. Since we don't want to rewrite all that handy code the first thing to do is create a new sheep file with "call-throughs" to the original script.

First, extract r25_all.shp from the game, either using the in-game console or with the Barn Browser tool. Move the file into the Data directory of the GK3 install path and rename it to something like "r25_all_original.shp".

Copy and paste the following code into a new text file and save the file to the Data directory (the same directory where you put r25_all_original.shp in the above step) as "r25_all.shp".

code
{
    PlaceEgo$()
    {
        CallSheep("r25_all_original", "PlaceEgo$");
    }
    
    Window$()
    {
        CallSheep("r25_all_original", "Window$");
    }
    
    DoYawnScene$()
    {
        CallSheep("r25_all_original", "DoYawnScene$");
    }
    
    LockTurnOn$()
    {
        CallSheep("r25_all_original", "LockTurnOn$");
    }
    
    LockTurnOff$()
    {
        CallSheep("r25_all_original", "LockTurnOff$");
    }
    
    OpenCloset$()
    {
        CallSheep("r25_all_original", "OpenCloset$");
    }
    
    NightStand$()
    {
        CallSheep("r25_all_original", "NightStand$");
    }
    
    GoInSide$()
    {
        CallSheep("r25_all_original", "GoInSide$");
    }
    
    GoOutSide$()
    {
        CallSheep("r25_all_original", "GoOutSide$");
    }
    
    Window_Open$()
    {
        CallSheep("r25_all_original", "Window_Open$");
    }
    
    Window_Open_Grace$()
    {
        CallSheep("r25_all_original", "Window_Open_Grace$");
    }
    
    Hanger$()
    {
        CallSheep("r25_all_original", "Hanger$");
    }
    
    TakeTape$()
    {
        CallSheep("r25_all_original", "TakeTape$");
    }
    
    Open$()
    {
        CallSheep("r25_all_original", "Open$");
    }
    
    Close$()
    {
        CallSheep("r25_all_original", "Close$");
    }
    
    Lock$()
    {
        CallSheep("r25_all_original", "Lock$");
    }
    
    UnLock$()
    {
        CallSheep("r25_all_original", "UnLock$");
    }
    
    GabeStartSidney$()
    {
        CallSheep("r25_all_original", "GabeStartSidney$");
    }
    
    GraceStartSidney$()
    {
        CallSheep("r25_all_original", "GraceStartSidney$");
    }
    
    ExitSidney$()
    {
        CallSheep("r25_all_original", "ExitSidney$");
    }
    
    FingerPrintKit$()
    {
        CallSheep("r25_all_original", "FingerPrintKit$");
    }
    
    GraceTimeBlockStart$()
    {
        CallSheep("r25_all_original", "GraceTimeBlockStart$");
    }
}
            

Now you've got a new file full of Sheep (the name of the scripting language they used for GK3). PlaceEgo$, Window$, OpenCloset$- those are all functions, and right now all the code in every function looks something like this:

CallSheep("r25_all_original", "OpenCloset$");
        

"CallSheep" is a special function that tells the GK3 engine to execute another Sheep function inside an external Sheep script. The first parameter ("r25_all_original") is the filename of the Sheep script (the engine automatically appends ".shp"). The second parameter is the name of the function to execute. So you can see that right now every function just executes the function of the same name in the original Sheep script that we extracted earlier. So PlaceEgo$ executes PlaceEgo$ inside r25_all_original.shp, etc.

As it is now our new Sheep script is kind of pointless. If you were to run GK3 with the new script everything would behave exactly as it did originally.

Editing the Sheep

The function we're interested in is OpenCloset$. That's the function that gets called whenever the player opens or closes the wardrobe, so that's what we want to add the special yawn code to. Change the code in OpenCloset$ to look like this:

wait CallSheep("r25_all_original", "OpenCloset$");
wait StartAnimation("GabYawn");
wait StartIdleFidget("Gabriel");
        

The function has gone from a single line to three lines. the first line should look familiar. The second line tells the GK3 engine to run the animation called "GabYawn." This is what actually causes Gabe to yawn. The third line tells the engine to start Gabriel's "idle" animation. Without that line Gabe would stand there after yawning like a statue- perfectly still.

Notice that each line as "wait" in front of it. For a more thorough description of what "wait" does take a look at 2.3 of sheep engine.doc. All "wait" really does is wait until one function finishes executing before moving on to the next. So here, since each function has "wait" in front of it, the engine first runs the original version of OpenCloset$, waits for it to finish, runs the GabYawn animation, waits for it to finish, and then starts the Idle animation. Without the "wait" keywords in front it just wouldn't work.

Testing the mod

Now you're ready to run GK3 and make sure everything works. Hopefully you can have Gabe open the wardrobe and see him yawn.

One challenging part of modding GK3 is it's pretty silent about errors in sheep scripts. If you have a syntax error in the script GK3 just refuses to run it, so in this particular case you probably wouldn't see Gabe or be able to execute any actions.