Mod to Bind Console Command to a Key?

+
Mod to Bind Console Command to a Key?

I am using a Companion mod to spawn a white wolf as a pet for Geralt. I was wondering if there is a way to bind that, or any other console command, to a hotkey? I could not find any existing mods that does this already.

Thanks!
 
Go to documents/witcher 3. Open input.settings with notepad. Add below one of the sections (like [EXPLORATION] - so key will work while exporing):

Code:
IK_NumPad0=(Action=KBinderCallFunction)

Create a custom script in mod folder/content/scripts, paste it what is below and add that function there.

Code:
class KeyBinder
{
    function Init()
    {
        theInput.RegisterListener(this, 'OnCallFunction', 'KBinderCallFunction');
    }
    event OnCallFunction( action : SInputAction )
    {
        if(IsPressed(action))
        // Function you want to call. Like:
            example();
    }
    function example()
    {
        GetCDProjektRed().SendMessage("gib redkit pls");
    }
}
// copy your exec function here (for convenience), then delete "exec" from it

Remember to delete the example function - it's just there to show you how it should look, you can leave lines starting with //.

Find playerWitcher.ws in game folder/content/content0/scripts. Copy it to mod folder/content/scripts/game/player. Open it and after first lines:

Code:
statemachine class W3PlayerWitcher extends CR4Player
{

add:
Code:
var keyBinder : KeyBinder;

Find "event OnSpawned" there.
At the end of it add:

Code:
keyBinder = new KeyBinder in this;
keyBinder.Init();

Now pressing NumPad0 will call that function. You can change the key in input.settings.

Edit: In case you don't know, exec function is console command. So just open the mod scripts and find that by typing console command in console.
 
Murzinio,
Hello! Prompt please as now will fulfill function, I probyval to write GetCDProjektRed().SendMessage produces an error. I attach a screenshot. Game version: Witcher 3 Goty 1.31 Steam edition. Thanks for the help! I apologize for my english, I use an interpreter.

Code:
Error [mod]1.ws(15): Could not find function 'GetCDProjektRed'

Warning [content0]engine\environment.ws(30): Global native function 'EnableDebugOverlayFilter' was not exported from C++ code.
Warning [content0]engine\environment.ws(32): Global native function 'EnableDebugPostProcess' was not exported from C++ code.
Warning [content0]engine\showflags.ws(11): Global native function 'DebugSetEShowFlag' was not exported from C++ code.
 
Wolfysge that was just a joke. That function does not exist. You have to delete that line and between the brackets above and below it write what you want to do.
 
Hello!

Sorry to necro this post, but I'm *very* interested in getting this working!

@Murzinio I followed the steps you laid out to the best of my ability, and yet it still doesn't seem to be working. I have a sneaking suspicion I know the reason, but I don't know how to fix it.

Besides altering the existing config files as instructed, I created a "KBinderCallFunction.ws" script in a newly created Witcher 3/mods/HarmTheHermit/content/scripts folder. That script contains:

Code:
class KeyBinder
{
    function Init()
    {
        theInput.RegisterListener(this, 'OnCallFunction', 'KBinderCallFunction');
    }
    event OnCallFunction( action : SInputAction )
    {
        if(IsPressed(action))
        // Function you want to call. Like:
            example();
    }
    function example()
    {
        spawn( cavetroll, 1, 1, false);
    }
}
// copy your exec function here (for convenience), then delete "exec" from it

where I am attempting to spawn a cave troll when I press the Numpad0 key. I tested the spawn command manually in the debug console, and it appears to be working fine.

My *guess* is that the spawn command must be written somewhat differently in the script? If you (or someone who knows) would be so kind as to provide the proper syntax, or otherwise help troubleshoot the issue...I would be MUCH appreciated!

Thanks!
 
@generikb I would suggest you replace
spawn( cavetroll, 1, 1, false);
with
spawn('cavetroll', 1, 1, false);
As this seems to be the proper syntax recommended for input in the console itself as well. However, I followed the instructions too and tried to bind a "buffme" function to Numpad0 and it doesn't work. So perhaps the whole setup is incorrect? However, it seemed to work for Wolfysge, as they did receive an error message, which means the game tried to execute the joke command and failed, while I get absolutely nothing.
 
Single quotes make up a name literal, i.e. the game will treat it as a value of type name. Without the single quotes the game will look for a variable called cavetroll, if you don't have one the script shouldn't even compile. If you can run the game it means the script is not being compiled in the first place, you can fix it by removing the .redscript files in the game folder which should force the game to recompile. If you get a compilation error then using the single quotes should fix it assuming you got the value right and everything else is set up correctly.

Edit: I just noticed your mod name is not prefixed with "mod", iirc it needs to be or otherwise the game will ignore it. So it should be modHarmTheHermit.
 
Thank you @Murzinio and Lernos! I'll try those changes this weekend to see if that fixes it.

Side question: If I wanted a script that handled 5 different spawn commands (Numpad 1-5), would that be 5 different scripts as above, or could I somehow handle all that in one script? If you could give a script example I would really really appreciate it!
Post automatically merged:

Well, I made those changes but now I get the following error:

Code:
Error [modharmthehermit]kbindercallfunction.ws(15): Cannot call exec function 'spawn' from scripts instead of the console.

which sounds like "you can't spawn stuff using a script" to me? Any ideas on a workaround?

Again, many many thanks for your help in getting this working!
 
Last edited:
I also noticed something wasn't right, so before I went back to read the replies here, I put the "mod" prefix at the folder's name and now I too get an error that says the same: cannot call exec function 'buffme' from scripts instead of the console. That and some global native functions not exported from C++ code.
Post automatically merged:

@generikb I've tried googling it, and apparently someone on this forum said that you really can't execute console commands from scripts. They suggested copying the "body" of the function you're looking for. Most common cheats are located in The Witcher 3\content\content0\scripts\game\temp.ws. For example, the spawn function is this beast:
Code:
exec function spawn(nam : name, optional quantity : int, optional distance : float, optional isHostile : bool, optional level : int )
{
    var ent : CEntity;
    var horse : CEntity;
    var pos, cameraDir, player, posFin, normal, posTemp : Vector;
    var rot : EulerAngles;
    var i, sign : int;
    var s,r,x,y : float;
    var template : CEntityTemplate;
    var horseTemplate : CEntityTemplate;
    var horseTag : array<name>;
    var resourcePath    : string;
    var l_aiTree        : CAIHorseDoNothingAction;
    var templateCSV : C2dArray;
    quantity = Max(quantity, 1);
    
    rot = thePlayer.GetWorldRotation();   
    if(nam != 'boat')
    {
        rot.Yaw += 180;       
    }
    
    
    cameraDir = theCamera.GetCameraDirection();
    
    if( distance == 0 ) distance = 3;
    cameraDir.X *= distance;   
    cameraDir.Y *= distance;
    
    
    player = thePlayer.GetWorldPosition();
    
    
    pos = cameraDir + player;   
    pos.Z = player.Z;
    
    
    posFin.Z = pos.Z;           
    s = quantity / 0.2;           
    r = SqrtF(s/Pi());
    
    
    template = (CEntityTemplate)LoadResource(nam);
    
    if ( nam == 'rider' )
        horseTemplate = (CEntityTemplate)LoadResource('horse');
        
    if(!template)
    {
        resourcePath = "characters\npc_entities\monsters";
        resourcePath = resourcePath + NameToString(nam);
        resourcePath = resourcePath + ".w2ent";
        template = (CEntityTemplate)LoadResource( resourcePath, true );
    }
    
    if( nam == 'def' )
    {
        templateCSV = LoadCSV("gameplay\globals\temp_spawner.csv");
        
        resourcePath = templateCSV.GetValueAt(0,0);
        template = (CEntityTemplate)LoadResource( resourcePath, true );
    }

    for(i=0; i<quantity; i+=1)
    {       
        x = RandF() * r;           
        y = RandF() * (r - x);       
        
        if(RandRange(2))                   
            sign = 1;
        else
            sign = -1;
            
        posFin.X = pos.X + sign * x;   
        
        if(RandRange(2))                   
            sign = 1;
        else
            sign = -1;
            
        posFin.Y = pos.Y + sign * y;   
        
        if(nam == 'boat')
        {
            posFin.Z = 0.0f;
        }
        else
        {
            if(theGame.GetWorld().StaticTrace( posFin + Vector(0,0,5), posFin - Vector(0,0,5), posTemp, normal ))
            {
                posFin = posTemp;
            }
        }
        
        if( nam == 'boat' )
        {
            ent = theGame.CreateEntity(template, posFin, rot, true, false, false, PM_Persist );
        }
        else
        {
            ent = theGame.CreateEntity(template, posFin, rot);
        }
        
        if ( horseTemplate )
        {
            horseTag.PushBack('enemy_horse');
            horse = theGame.CreateEntity(horseTemplate, posFin, rot,true,false,false,PM_DontPersist,horseTag);
            
            
            
            
            l_aiTree = new CAIHorseDoNothingAction in ent;
            l_aiTree.OnCreated();
            ((CActor)ent).ForceAIBehavior( l_aiTree, BTAP_AboveEmergency2, 'AI_Rider_Load_Forced' );
            
            ((CActor)ent).SignalGameplayEventParamInt( 'RidingManagerMountHorse', MT_instant | MT_fromScript );
        }
            
        if( isHostile )
        {
            ((CActor)ent).SetTemporaryAttitudeGroup( 'hostile_to_player', AGP_Default );
        }
            
        if ( level != 0 )
        {
            ((CNewNPC)ent).SetLevel( level );
        }
    }
}
So, instead of using "spawn('cavetroll', 1, 1, false);" you are apparently supposed to put this between the brackets. However, the problem is, this is a command that requires arguments. So basically it says "spawn 'whatever the player put in the console'". If you want to bind it to a key to spawn a specific thing, you have to somehow rewrite it to make it spawn a cave troll only, and I have no idea how. Same with my problem, buffme looks like this:
Code:
exec function buffme( type : EEffectType, optional duration : float, optional src : name )
{
    var params : SCustomEffectParams;

    if(duration > 0)
    {
        params.effectType = type;
        params.sourceName = src;
        params.duration = duration;
        thePlayer.AddEffectCustom(params);
    }
    else
    {
        thePlayer.AddEffectDefault(type, NULL, src);
    }
}
Yet I still have no idea how to make it give me only the specific buff effect. I tried several things, but the compiler would always give me some kind of error.
 
Last edited:
UPD: @generikb I figured out how to do what I wanted. Basically I wanted the hotkey to add echidna and archgriffin decoctions' effects 40 minutes each. This is how I did it:
Code:
class KeyBinder
{
    function Init()
    {
        theInput.RegisterListener(this, 'OnCallFunction', 'KBinderCallFunction');
    }
    event OnCallFunction( action : SInputAction )
    {
        if(IsPressed(action))
        // Function you want to call. Like:
            example();
            example2();
    }
    function example()
    {
        var params : SCustomEffectParams;
        params.effectType = EET_Mutagen21;
        params.duration = 2400;
        thePlayer.AddEffectCustom(params);
    }
    function example2()
    {
        var params : SCustomEffectParams;
        params.effectType = EET_Mutagen04;
        params.duration = 2400;
        thePlayer.AddEffectCustom(params);
    }
}
You can compare it to the original code for the buffme command and see what changed. Right now I don't know how to do it for the spawn command, so perhaps @Murzinio could help with that.
 
Hey @Lernos1 I finally managed to get it to work!

Code:
class KeyBinder
{
    function Init()
    {
        theInput.RegisterListener(this, 'OnCallFunction', 'KBinderCallFunction');
    }
    event OnCallFunction( action : SInputAction )
    {
        if(IsPressed(action))
        // Function you want to call. Like:
            example();
    }
    function example()
    {
        spawnhermit('cavetroll', 1, 1, false);
    }
}
// copy your exec function here (for convenience), then delete "exec" from it

function spawnhermit(nam : name, optional quantity : int, optional distance : float, optional isHostile : bool, optional level : int )
{
    var ent : CEntity;
    var horse : CEntity;
    var pos, cameraDir, player, posFin, normal, posTemp : Vector;
    var rot : EulerAngles;
    var i, sign : int;
    var s,r,x,y : float;
    var template : CEntityTemplate;
    var horseTemplate : CEntityTemplate;
    var horseTag : array<name>;
    var resourcePath    : string;
    var l_aiTree        : CAIHorseDoNothingAction;
    var templateCSV : C2dArray;
    quantity = Max(quantity, 1);
    
    rot = thePlayer.GetWorldRotation();   
    if(nam != 'boat')
    {
        rot.Yaw += 180;       
    }
    
    
    cameraDir = theCamera.GetCameraDirection();
    
    if( distance == 0 ) distance = 3;
    cameraDir.X *= distance;   
    cameraDir.Y *= distance;
    
    
    player = thePlayer.GetWorldPosition();
    
    
    pos = cameraDir + player;   
    pos.Z = player.Z;
    
    
    posFin.Z = pos.Z;           
    s = quantity / 0.2;           
    r = SqrtF(s/Pi());
    
    
    template = (CEntityTemplate)LoadResource(nam);
    
    if ( nam == 'rider' )
        horseTemplate = (CEntityTemplate)LoadResource('horse');
        
    if(!template)
    {
        resourcePath = "characters\npc_entities\monsters";
        resourcePath = resourcePath + NameToString(nam);
        resourcePath = resourcePath + ".w2ent";
        template = (CEntityTemplate)LoadResource( resourcePath, true );
    }
    
    if( nam == 'def' )
    {
        templateCSV = LoadCSV("gameplay\globals\temp_spawner.csv");
        
        resourcePath = templateCSV.GetValueAt(0,0);
        template = (CEntityTemplate)LoadResource( resourcePath, true );
    }

    for(i=0; i<quantity; i+=1)
    {       
        x = RandF() * r;           
        y = RandF() * (r - x);       
        
        if(RandRange(2))                   
            sign = 1;
        else
            sign = -1;
            
        posFin.X = pos.X + sign * x;   
        
        if(RandRange(2))                   
            sign = 1;
        else
            sign = -1;
            
        posFin.Y = pos.Y + sign * y;   
        
        if(nam == 'boat')
        {
            posFin.Z = 0.0f;
        }
        else
        {
            if(theGame.GetWorld().StaticTrace( posFin + Vector(0,0,5), posFin - Vector(0,0,5), posTemp, normal ))
            {
                posFin = posTemp;
            }
        }
        
        if( nam == 'boat' )
        {
            ent = theGame.CreateEntity(template, posFin, rot, true, false, false, PM_Persist );
        }
        else
        {
            ent = theGame.CreateEntity(template, posFin, rot);
        }
        
        if ( horseTemplate )
        {
            horseTag.PushBack('enemy_horse');
            horse = theGame.CreateEntity(horseTemplate, posFin, rot,true,false,false,PM_DontPersist,horseTag);
            
            
            
            
            l_aiTree = new CAIHorseDoNothingAction in ent;
            l_aiTree.OnCreated();
            ((CActor)ent).ForceAIBehavior( l_aiTree, BTAP_AboveEmergency2, 'AI_Rider_Load_Forced' );
            
            ((CActor)ent).SignalGameplayEventParamInt( 'RidingManagerMountHorse', MT_instant | MT_fromScript );
        }
            
        if( isHostile )
        {
            ((CActor)ent).SetTemporaryAttitudeGroup( 'hostile_to_player', AGP_Default );
        }
            
        if ( level != 0 )
        {
            ((CNewNPC)ent).SetLevel( level );
        }
    }
}

I did basically what you and the forums said...found the original spawn function (I believe it was in temp.ws), copy/pasted it to the end of the script mentioned here, and removed "exec".

The result works but I'm 100% sure it's terrible spaghetti code. I'm a Twitch streamer, not a programmer LOL.

The next step I have to figure out today is how to make it where I can do this with 5 different spawn commands depending on which Numpad 1-5 I press. This script is using an IF statement, so I'm assuming I can do it....but I don't know enough about scripting/coding and will have to do more research.

Hopefully though my example helps get yours to work!
 
@generikb Ah, I see. Yes, this is definitely the intended way to use Murzinio's script, at least I believe so now. Thank you for your input! As for binding to different keys, perhaps you could add lines like
Code:
IK_NumPad1=(Action=KBinderCallFunction2)
to input.settings and make more scripts with different spawn commands that start with
Code:
class KeyBinder
{
    function Init()
    {
        theInput.RegisterListener(this, 'OnCallFunction', 'KBinderCallFunction2');
    }
? I don't know if it will actually work since I have no idea if "CallFunction" is part of the code that you can't change, but it's worth trying, I suppose.
 
Yeah I think that might be the quickest way to get it to work, even if it’s not the most efficient/correct. I *might* have to give the spawn command in each script a different name as well but that shouldn’t be too difficult.

Thanks for the help @Lernos1 really appreciate it!
 
Top Bottom