Objective
The objective of this short tutorial is to show how to activate shaders for a particular player, allowing to change how they see the world without affecting the rest of players.
Requirements
The latest version of NWNX (https://github.com/nwnxee/unified). The following functions: NWNX_Player_UpdateWind() and NWNX_Area_GetWindPower() will be used.
Limitations
This tutorial supposes you are not using the `SetAreaWind` function of the base game:
// Sets the detailed wind data for oArea
// The predefined values in the toolset are:
// NONE: vDirection=(1.0, 1.0, 0.0), fMagnitude=0.0, fYaw=0.0, fPitch=0.0
// LIGHT: vDirection=(1.0, 1.0, 0.0), fMagnitude=1.0, fYaw=100.0, fPitch=3.0
// HEAVY: vDirection=(1.0, 1.0, 0.0), fMagnitude=2.0, fYaw=150.0, fPitch=5.0
void SetAreaWind(object oArea, vector vDirection, float fMagnitude, float fYaw, float fPitch);
That is, the wind in your module areas is set by the toolset and not by any script. If this is not your case you'll have to change the code of this tutorial and adapt it to your needs.
The code presented here does not allow the application of different shader effects at the same time (but you can easily change it to do so).
How it works
The shaders have access to some uniforms (see shaders for more info on shaders and uniforms) that are set by the game: the area flags, the world time... and the one we will use in this tutorial areaGlobalWind which contains the global wind vector for the area.
¿Why this uniform? because this uniform can be changed in a per player basis, so we can change the value of areaGlobalWind for only one player. There are sureley other uniforms that can be used but, as we will see, this one allows an easy way for the shader to detect we want to apply a specific effect without affecting, at least noticeably, the wind effects in the area.
If you haven't changed the wind in any area of your module using the SetAreaWind() function, the wind will be configured by the toolset. From SetAreaWind() help, this means we'll have only three possible states for vDirection and fMagnitude (if you know maths you'll see the fMagnitude is not what we think it is, because the length of vDirection is not 1):
- vDirection=(1.0, 1.0, 0.0), fMagnitude=0.0: the squared modulus of the areaGlobalWind vector will be 0
- vDirection=(1.0, 1.0, 0.0), fMagnitude=1.0: the squared modulus of the areaGlobalWind vector will be 2
- vDirection=(1.0, 1.0, 0.0), fMagnitude=2.0: the squared modulus of the areaGlobalWind vector will be 8
What we are going to do is to change slightly those numbers (slightly, in order to not have any noticeable/visible effect on the wind of the area). Since the modulus of areaGlobalWind is a float, an easy way to do this is to add a fractional part to the squared modulus: the shader will check the fractional part and apply an effect depending on its value. For example if the fractional part is 0.01 we can remove the colors, if it's 0.02 we can inverse the colors to get an ethereal effect, etc. Note that a change of, let say, 0.08 in the squared modulus of the wind vector will not be noticeable for the player.
Shader code
For the sake of brevity I'm showing the code for the `fslit.shd` shader, you'll have to also modify fslit_nm.shd, fslit_sm.shd, fslit_sm_nm.shd, fswater.shd and fsgrass.shd. As an alternative you can modify only fsfbpostpr.shd but be carefull because this will affect all the textures: traps will not be red if you change the colors.
NWSCRIPT code
Add the following code to an include file:
So, if for example you want to change the darkvision spell, add the include to the NW_S0_DarkVis.nss file and call SetWindShader(oTarget, SM_WIND_SHADER_ULTRAVISION) when applying the spell effects.
Note that:
- You'll need a way to detect the end of the spell to restore the normal shader (call SetWindShader(oPlayer, SM_WIND_SHADER_NONE))
- Since the shader depends on the wind of an area, you'll need a way to re-apply the shader when the player moves to another area
Although this can be done easily but it is out of the scope of this tutorial.