WS: Language Guide
Basics
Comments
During the development of new scripts it is possible to add comments to make it easier to follow your code.
Single line comments can be added like so:
// This is a single line comment
Multi-line comments can be used for bigger blocks of text as well:
/*
This text is multiple lines long.
...
*/
Together they can be used to make your code more easily readable, for example:
/*
Example mod by Bob
Version 1.0
Created on 2024.05.01.
*/
exec function scripts_tutorial_test()
{
// Show the hello text so we can verify easily that the mod works.
GetWitcherPlayer().DisplayHudMessage('Hello');
}
Types
WitcherScript has a list of basic types that can be used inside the scripts to store data:
int
- Standard 32bit integer (value range from -2,147,483,648 to 2,147,483,647)String
- Standard string, use quotation marks to pass values, eg.: "This is a string"name
- Name type variable, essentially a string used for item names, tags, etc. Use apostrophes to pass values, eg.: 'this is an item name'float
- Standard float type, eg.: 1.0fVector
- 4 variable vector (Quaternion) ordered in XYZW order, eg.: Vector(100, 100, 100, 1)EulerAngles
- 3 variable rotation ordered in Pitch, Yaw, Roll order, eg.: EulerAngles(0, 180, 0)bool
- standard true/false boolean, also accepts 0 and 1 as inputs, however unlike Unreal Script does not accept Yes/No valuesMatrix
- Indicates matrix variable type.array< X >
- Indicates an array with X being the type of array, eg. array< int > being an array of integers
Global objects
The game exposes a list of global object that you can use from anywhere. They are always available and expose a lot of very useful functions.
theGame=CR4Game
theServer=CServerInterface
thePlayer=CR4Player
theCamera=CCamera
theUI=CGuiWitcher
theSound=CScriptSoundSystem
theDebug=CDebugAttributesManager
theTimer=CTimerScriptKeyword
theInput=CInputManager
For example if you want to do something with the player. You can simply write:
thePlayer.DisplayHudMessage('Hello')
Instead of using GetWitcherPlayer()
and the game will know that you are referring to the player. These can be changed inside redscripts.ini
but it is not recommended to change them since your mod will not be compatible with others who have the default settings.
Variables
It is possible to define variables to store your data and you can use them to pass around your data.
Local variables
Inside a given function to process the data etc. it is possible to define variables that can be used throughout the function. They do have to be defined at the top of your function however.
exec function acquire(skillName : name)
{
var i : int;
var skills : String;
// Do something cool with the data
}
Class variables
Classes can contain member variables as well. They are tied to the given class and can be used to store the data.
class Test
{
var SomeText : String;
}
Function parameters
It is possible to pass parameters to functions which you can use to parameterise them. For example the code snippet bellow can help you change the weather in-game. Try running it in the debug console as RequestWeatherChangeTo('WT_Rain_Storm');
and observer that it has started raining
exec function changeweather(weatherName : name)
{
RequestWeatherChangeTo( weatherName , 1, false );
}
Functions
WitcherScript has different kinds of functions that you can set up. Check the list below.
Exec function
Executable functions are exposed to the game’s debug console and can be used to start functions or utility functions when developing your mods.
exec function stoprain()
{
RequestWeatherChangeTo('WT_Clear', 1.0, false);
}
An example from the Mariska’s Wonderland demo mod, which was used to give the player the key ( item_sqmod1_canyon_hideout_key
) for the back room in the hidden dungeon during debugging:
exec function addKey( key_name : name )
{
thePlayer.inv.AddAnItem(key_name, 1);
}
Latent function
These are like Coroutines etc. in other engines basically they allow for passing time and slower execution. Bellow you can see the sleep call in the function which allows to wait X amount of seconds.
latent storyscene function ShaveGeralt( player: CStoryScenePlayer )
{
var acs : array< CComponent >;
acs = thePlayer.GetComponentsByClassName( 'CHeadManagerComponent' );
( ( CHeadManagerComponent ) acs[0] ).Shave();
Sleep(1.0f);
}
Timer function
These functions are real time based timer which can be attached to entities.
states.ws has some utility functions for this:
// Add named real time based timer to entity; returns unique timer id
import final function AddTimer( timerName : name, period : float, optional repeats : bool /* false */, optional scatter : bool /* false */, optional group : ETickGroup /* Main */, optional saveable : bool /* false */, optional overrideExisting : bool /* true */ ) : int;
// Add named gameplay time based timer to entity; returns unique timer id
import final function AddGameTimeTimer( timerName : name, period : GameTime, optional repeats : bool /* false */, optional scatter : bool /* false */, optional group : ETickGroup /* Main */, optional saveable : bool /* false */, optional overrideExisting : bool /* true */ ) : int;
// Removes all timers with matching name from entity (in given group or all groups if none is specified)
import final function RemoveTimer( timerName : name, optional group : ETickGroup );
// Removes all timers with matching id from entity (in given group or all groups if none is specified)
import final function RemoveTimerById( id : int, optional group : ETickGroup );
// Remove all timers from entity
import final function RemoveTimers();
Setting up a function is really easy like so:
timer function Loop(dt : float, id : int)
{
LoopFunction(dt);
}
The dt parameter defines the “delta time” the amount of time that has passed.
Storyscene function
Special functions that can be used in scenes.
storyscene function EnableFastTravelPin( player: CStoryScenePlayer ,pinTag : name, enable : bool )
{
var manager : CCommonMapManager = theGame.GetCommonMapManager();
manager.SetEntityMapPinDisabled( pinTag, !enable );
}
Quest function
Special functions that can be used in Quest nodes.
quest function LaunchCredits()
{
theGame.GetGuiManager().RequestCreditsMenu(CreditsIndex_Wither3);
}
Reward function
These functions can be attached to a given reward inside the editor.
reward function TutorialLevelUp()
{
var witcher : W3PlayerWitcher;
witcher = GetWitcherPlayer();
witcher.AddPoints(EExperiencePoint, 50, false);
if(witcher.GetLevel() == FactsQuerySum('tutorial_starting_level'))
{
witcher.AddPoints(EExperiencePoint, witcher.GetMissingExpForNextLevel(), true);
}
}
Cleanup function
They cannot return anything and have no parameters. They are used for cleaning up after something has finished.
cleanup function ThrowProjectileCleanup()
{
if( thrownEntity )
{
thrownEntity.StopAiming( false );
if ( !parent.wasBombReleased )
{
rider.RaiseEvent( 'actionShootEnd' );
parent.wasBombReleased = true;
thrownEntity.Destroy();
thrownEntity = NULL;
}
}
}
Entry function
An entry function is a state entry function for which you can see an example below. These are rarely used in the game.
entry function DrawEvent()
{
parent.ownerPlayer.SetBehaviorVariable( 'failSafeDraw', 1.0 );
virtual_parent.RaiseOwnerGraphEvents( 'Crossbow_Draw', true );
parent.performedDraw = true;
}
Summary
Possible function flags
enum EFunctionFlags
{
FF_NativeFunction //!< Function is native ( implemented in C++ )
FF_StaticFunction //!< Function is static
FF_OperatorFunction //!< Function is data operator
FF_ExportedFunction //!< Function is native function that was exported to script
FF_FinalFunction //!< Function is final and cannot be overridden in child classes
FF_EventFunction //!< Function is special event function
FF_LatentFunction //!< Function takes time to execute
FF_EntryFunction //!< Function is a state entry function
FF_ExecFunction //!< Function can be called from console
FF_UndefinedBody //!< Function has no body (just a declaration)
FF_TimerFunction //!< Function is a timer
FF_SceneFunction //!< Function can be used in Scenes
FF_QuestFunction //!< Function can be used in Quests
FF_CleanupFunction //!< Function is a cleanup
FF_PrivateFunction //!< Function is private
FF_ProtectedFunction //!< Function is protected
FF_PublicFunction //!< Function is public
FF_RewardFunction //!< Function can be attached to reward
FF_AccessModifiers = FF_PrivateFunction | FF_ProtectedFunction | FF_PublicFunction,
};
Generic flags
enum EScriptStubFlags
{
SSF_Import
SSF_Editable
SSF_Const
SSF_Timer
SSF_Abstract
SSF_Entry
SSF_Auto
SSF_Inlined
SSF_Out
SSF_Optional
SSF_Final
SSF_Private
SSF_Protected
SSF_Public
SSF_Event
SSF_Latent
SSF_Exec
SSF_Unused
SSF_Scene
SSF_Saved
SSF_Quest
SSF_Cleanup
SSF_Reward
SSF_StateMachine
};
Classes
A class in object-oriented programming (OOP) is a blueprint for creating objects. It encapsulates data for the object and methods to manipulate that data. Classes allow for the organization of code into reusable and modular components.
Regular class
Normal classes allow you to hold values and methods inside a common container which you can reuse.
class InterpCurve
{
var something: float;
function doSomething( inVal : float, outVal : float ) : int
{
return 10;
}
}
Native class
These are classes defined in C++ and need to be imported to WitcherScript
import class C2dArray extends CResource
{
// Get value by column, row
import final function GetValueAt( column : int, row : int ) : string;
// Get value using column header and row
import final function GetValue( header : string, row : int ) : string;
// Get name value by column, row
import final function GetValueAtAsName( column : int, row : int ) : name;
// Get name value using column header and row
import final function GetValueAsName( header : string, row : int ) : name;
// Get number of rows
import final function GetNumRows() : int;
// Get index of row with given value in given colum
import final function GetRowIndexAt( column : int, value : string ) : int;
// Get index of row with given value in given colum
import final function GetRowIndex( header : string, value : string ) : int;
}
Statemachine
These classes define a default state and then you can define states that it can transition to, think about it like a text like graph with transitions. Below you can see the statemachine for the Witches cage which has two state of being turned on and off.
statemachine class W3WitchesCage extends CEntity
{
default autoState = 'TurnedOff';
}
state TurnedOff in W3WitchesCage
{
event OnEnterState( prevStateName : name )
{
super.OnEnterState( prevStateName );
parent.ApplyAppearance("roots_off");
}
}
state TurnedOn in W3WitchesCage
{
event OnEnterState( prevStateName : name )
{
super.OnEnterState( prevStateName );
parent.ApplyAppearance("roots_on");
}
}
You can check states.ws for reference on how to use these functions.
import class CScriptableState extends IScriptable
{
// Is this state the active one in the state machine
import function IsActive() : bool;
// Get the name of this state
import function GetStateName() : name;
// Called when we are entering this state
event OnEnterState( prevStateName : name ) {}
// Called when we are leaving this state
event OnLeaveState( nextStateName : name ) {}
// Called to check if this state can be entered
import function CanEnterState( prevStateName : name ) : bool;
// Called to check if this tate can be leaved
import function CanLeaveState( nextStateName : name ) : bool;
// ---- State change callbacks ---
// Invoked before state begins
import function BeginState( prevStateName : name );
// Invoked before state ends
import function EndState( nextStateName : name );
// Invoked on return to state (after other state was popped from the stack)
import function ContinuedState();
// Invoked when other state gets pushed on the stack (thus current state gets paused)
import function PausedState();
}
Summary
enum EClassFlags
{
CF_Abstract //!< Class is abstract, no instance of it can be created
CF_Native //!< Class is defined in C++
CF_Scripted //!< Class has definition in script
CF_Exported //!< Class definition has been exported to C++ code
CF_State //!< Class is a state class
CF_NoDefaultObjectSerialization //!< Don't compare properties to default object on serialize
CF_AlwaysTransient //!< NEVER save or load objects of this class to ANY storage
CF_EditorOnly //!< Class and all derived classes should be used in editor only
CF_UndefinedFunctions //!< This class has one or more undefined functions
CF_StateMachine //!< Class is allowed to have states (set by scripts checked by script compiler only)
};
______________________________________
The Witcher 3: Wild Hunt Complete Edition © 2024 CD PROJEKT S.A. Published by CD PROJEKT S.A. Developed by CD PROJEKT RED. CD PROJEKT®, The Witcher®, REDengine® are registered trademarks of CD PROJEKT Capital Group. All rights reserved. The Witcher game is set in the universe created by Andrzej Sapkowski in his series of books. All rights reserved.