Scripting for Dummies - Me

I need some help. While many years ago I was a coder of sorts, I haven’t used those skills in over 30 years. I am trying to add some new feats, redesign some old ones, as well as adding and redesigning some spells. The scripting commands and syntax seem to escape my immediate understanding. If someone could help with defining the commands as well as laying out the syntax, that would be very helpful.

Here is a possible way someone can help.
Take the Fireball script and change it so that it delivers cold damage, as well as looks like a cold spell.
Please explain each line in the original as well as the modification.

I know I am asking a lot, but once I can get this down correctly, I should be able to play with the game on my own.

I appreciate anyone’s help.

Thanx.

NwScript syntax is a very basic subset of C/C++/C#

(no pointers, no arrays, no memory allocation, etc)

the Lexicon for nwn1/ee is 90% accurate for nwn2.

the Fireball script →
( see Spells.2da row 58 col ImpactScript )

I changed 3 constants and made notes in CAPS to the right of the changed lines.

// 'nw_s0_fireball'

#include "x2_inc_spellhook"
#include "x0_i0_spells"


void main()
{
	// if code within the PreSpellCastHook (i.e. UMD) reports FALSE do not run this spell
	if (!X2PreSpellCastCode())
	{
		return;
	}

	// declare major variables
	object oCaster = OBJECT_SELF;

	int nCasterLvl = GetCasterLevel(oCaster);
	int nMetaMagic = GetMetaMagicFeat();

	int nDamage;
	float fDelay;

	// Brock H. - OEI 03/03/06 - handled by the ImpactSEF column in Spells.2da
//	effect eExplode = EffectVisualEffect(VFX_FNF_FIREBALL);


//	effect eVis = EffectVisualEffect(VFX_IMP_FLAME_M);
	effect eVis = EffectVisualEffect(VFX_IMP_FROST_S); // <-- CHANGED TO COLD
	effect eDam;

	// get the spell target location as opposed to the spell target
	location lTarget = GetSpellTargetLocation();

	// limit casterlevel for the purposes of damage
	if (nCasterLvl > 10)
	{
		nCasterLvl = 10;
	}

	// apply the fireball explosion at the location captured above

	// Brock H. - OEI 03/03/06 - Handled by the ImpactSEF column in Spells.2da
//	ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eExplode, lTarget);


	// declare the spellshape, size and location. Capture the first target object in the shape
	object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);

	// cycle through the targets within the spell shape until an invalid object is captured
	while (GetIsObjectValid(oTarget))
	{
		if (spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF))
		{
			if (GetSpellId() == 341 || GetSpellId() == 58)
			{
				// fire cast spell at event for the specified target
				SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_FIREBALL)); // <-- THE ROW-ID IN SPELLS.2DA (no change)

				// get the distance between the explosion and the target to calculate delay
				fDelay = GetDistanceBetweenLocations(lTarget, GetLocation(oTarget)) / 20;

				if (!MyResistSpell(OBJECT_SELF, oTarget, fDelay))
				{
					// roll damage for each target
					nDamage = d6(nCasterLvl);

					// resolve metamagic
					if (nMetaMagic == METAMAGIC_MAXIMIZE)
					{
						nDamage = 6 * nCasterLvl;
					}
					else if (nMetaMagic == METAMAGIC_EMPOWER)
					{
						nDamage = nDamage + nDamage / 2;
					}

					// adjust the damage based on the Reflex Save, Evasion and Improved Evasion
//					nDamage = GetReflexAdjustedDamage(nDamage, oTarget, GetSpellSaveDC(), SAVING_THROW_TYPE_FIRE);
					nDamage = GetReflexAdjustedDamage(nDamage, oTarget, GetSpellSaveDC(), SAVING_THROW_TYPE_COLD); // <-- CHANGED TO COLD

					// set the damage effect
//					eDam = EffectDamage(nDamage, DAMAGE_TYPE_FIRE);
					eDam = EffectDamage(nDamage, DAMAGE_TYPE_COLD); // <-- CHANGED TO COLD

					if (nDamage > 0)
					{
						// apply effects to the current target
						DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget));

						// this visual effect is applied to the target object not the
						// location as above. This visual effect represents the flame
						// that erupts on the target not on the ground
						DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget));
					}
				}
			}
		}

		// select the next target within the spell shape
		oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
	}
}

 
The notes are in the stock script. Describing them in detail would take a lot of time. If you want to know about something specific just ask … there are quite a few persons around who know a lot about nwn2 scripting

2 Likes

@temyankee You don’t mention what OS you are on. In some ways that is important. Why? Well the version of the Lexicon that @kevL_s links to above is windows only - it’s in chm format (Compiled HTML Help) which I know for a fact that Apple Macs have a problem with. I suspect that is true for Linux too. There are 2 alternatives but they are both for NwN EE which means there are things in them that won’t apply to NwN 2 in addition to the few in the chm version of the Lexicon. But FWIW here’s links to both -

Online version of the Lexicon
NWN Lexicon Offline HTML

If you want a refresher (you did say you used to code), you could try the tutorials I wrote for NwN (1.69 onwards) -

TR’s Basics - Variables, Types and Functions (TBC)
TR’s Basics - Decisions
TR’s Basics - Boolean Algebra
TR’s Basics - Mostly Operators

I’ll let @kevL_s tell you how relavent they are to NwN 2, though.

TR

3 Likes

@temyankee

And possibly helpful for you … For beginners:

SCRIPTING:

https://neverwintervault.org/project/nwn2/other/scripting-tutorial-beginners-nwn2

XML STUFF:

https://neverwintervault.org/project/nwn2/other/gui/nwn2-xml-gui-coding-beginners

2 Likes

there’s also (my) Basic Scripting (not so basic)

it’s kinda intermediate level, and lots of reference stuff.

2 Likes

So far, what I have been referred to I have already looked at and I’m not getting what I am looking for exactly. Much of what the resources you have all referred me to is more for use in creating modules. I am looking to try and create feats and spells from scratch.

The fireball change that kevL_s did was somewhat helpful, but when I compare it to the Fireball spell through the use of the Toolset, there are a few characters in the Fireball code that are not in the Fireball modification code.

Maybe a breakdown of a spell, line by line, and definitions of what each line/command/variable is doing/calling for, could help a little more. Then, maybe an example of creating either a new feat or new spell would show me what I am trying to accomplish. Much of what I would like to do would basically involve modifying existing spells/feats.

Separate question involving Kaedrin’s PrC pack. It seems that spells based on elements have been given an additional label by Kaedrin, such as Fire, Water, Air, Earth, Sound/Sonic. I would like to create a feat or spell that looks for those tags specifically in order to enhance their effects in some way. Say, a Prestice Class of Fire Mage. Fire based spells would have an increased damage effect based on the spell level and the fire tag. Say whenthe Fire Mage cassts a Fire Based spell that does damage, the damage is increased by 1D4 per Spell Level.

Please forgive me for possibly not explaining myself so all of you are able to help me. I was always the guy that waited for a game to age a little before buying simply for the sake of saving money. As a result, a lot of the assistance that I am looking for now was probably easier to come by when the game was still new. Since I am now unable to work due to disability, I have a lot of time on my hands and have gone back to the games I enjoy. Even so far as going back to Pool of Radiance, etc. from the Gold Box Collections.

Thank you all for your help. Seriously.

1 Like

There is no generic spellscript template. There are commonalities like

if (!X2PreSpellCastCode())
    return;

which is used primarily to stop the spell when crafting on a workbench.

All scripts have an entity called OBJECT_SELF which is an ingame object that ‘owns’ a script as it executes.

object is an NwScript ‘type’ – like int and string etc.

 
Spellscripts will have a line like this →

SignalEvent(oTarget, EventSpellCastAt(OBJECT_SELF, SPELL_FIREBALL));

which forces a target’s OnSpellCastAt event to fire. Usually it means nothing but it does allow a module-designer to have objects react with custom behaviors to (certain) spells (or to spells generally).

This means that the spell’s potency is capped at level 10 →

int nCasterLvl = GetCasterLevel(oCaster);
if (nCasterLvl > 10)
	nCasterLvl = 10;

And this will be used to modify its effects based on what metamagic was used →

int nMetaMagic = GetMetaMagicFeat();

If a spell is an AoE it will have a loop of some sort (there are many different ways to loop over targets) →

object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
while (GetIsObjectValid(oTarget))
{
    // apply efffect(s) here, then find next target ->

    oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
}

but if a spell is single-target only there won’t be a loop.

Some spells are cast at a location on the ground →

location lTarget = GetSpellTargetLocation();

but others are cast on an object →

object oTarget = GetSpellTargetObject();

A visual effect might be applied to a target →

effect eVis = EffectVisualEffect(VFX_IMP_FROST_S);

VFX_IMP_FROST_S is an NwScript constant (see NwScript.nss) that references VisualEffects.2da

sometimes they are applied before any Resist/Save, sometimes only if those fail. It is applied with code like this →

ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);

After damage (if it is a damaging spell) is calculated it’s applied with similar code. The duration-type can be DURATION_TYPE_INSTANT, DURATION_TYPE_TEMPORARY, or DURATION_TYPE_PERMANENT (again, those are NwScript constants).

Delays can be used to give some visual realism.

If a spell can be resisted, it will have code like so (before applying negative effects) →

if (!MyResistSpell(OBJECT_SELF, oTarget, fDelay))
{
    // do bad stuff
}

MyResistSpell() is a function in the #include file nw_i0_spells that applies a visual effect based on whether a target has a Globe of Invulnerability, a Spell Mantle, or has resisted naturally.

 
Since Fireball uses reflex-adjusted damage, it uses this for a saving throw →

nDamage = GetReflexAdjustedDamage(nDamage, oTarget, GetSpellSaveDC(), SAVING_THROW_TYPE_COLD);

SAVING_THROW_TYPE_COLD is another NwScript constant.

GetSpellSaveDC() is a hardcoded function that automatically calculates DC. It’s not always accurate, particularly when using add-ons like the kPrC Pack.

For non-reflex-adjusted spells, that require a Save, the function MySavingThrow() is typically used (also in the #include file nw_i0_spells). It handles the fact that in Nwn2 in order to account for Immunity from an effect-type, the effect should be applied and hardcode will deal with it (rather than it being dealt with in the script itself – there are spells that use alternate Save handling since they need to do further calculations that are independent of the return from the stock MySavingThrow() – eg. spells that need to check more than one type of bad-effect).

 

if (spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, OBJECT_SELF))
{
    // target ok
}

spellsIsTarget() is also in the #include file nw_i0_spells. It’s a complicated function that determines if a target is valid given a targeting-type: SPELL_TARGET_STANDARDHOSTILE, SPELL_TARGET_SELECTIVEHOSTILE, or SPELL_TARGET_ALLALLIES. StandardHostile is based on the difficulty that player has set in Options (can affect allies). SelectiveHostile is hostile only. AllAllies is (roughly) allies.

GetSpellId() is the row-id in Spells.2da

EffectLinkEffects() can be used to link any number of effects together so that they all get removed if a spell is dispelled. It is widely used, just not in the Fireball script.

A custom spell-id can be set for effects with SetEffectSpellId() – if not set, the spell-id will be the row-id in Spells.2da. A custom spell-id can be used to later remove the specific effects from target(s) without a risk of accidentally removing other effects.

 
 
Feats are often called ‘spellabilities’ – because they are based on a spellscript (there is a reference in Feat.2da to the corresponding spell in Spells.2da). Other feats, such as a feat that enhances a spell, are merely a feat that can be checked for in a spellscript to apply an enhancement – ie. casting the spell is not dependent on having the Feat.

for example →

if (GetHasFeat(FEAT_ENHANCED_FIREBALL))
{
    nCasterLvl = nCasterLvl + 5;
}

where FEAT_ENHANCED_FIREBALL is a row-id in Feat.2da that has been added and defined (custom).

See Reserved 2da ranges.

Creating a custom Class is nontrivial. Eg Artisan PrC

 
We also have access to things like the spellhook that fires before any spellcast (eg. can stop spells when in an anti-magic area). Also, some spellabilities will call the X2PreSpellCastCode() although technically they shouldn’t because they’re not spells.

etc

ps. Some spells will check for and remove other spell-effects of similar type … eg. GreaterInvisibility should remove Invisibility. But that’s a nicety since the engine usually handles it ok

 
good luck (I may be indisposed for a while)

1 Like

all that said, you’ll want to become familiar with

Spells.2da
Feat.2da

+ others

and get familiar with resource-hierarchy

  1. stock /Data folder
  2. Module folder
  3. Campaign folder
  4. /override folder
  5. .hak file

(increasing priority)

 
 
 
I suggest, when you get an idea for a new spell or feat, think of one that is similar and copy its rows from Spells.2da and/or Feat.2da to new rows (in Spells.2da and/or Feat.2da) and copy its spellscript (if applicable). Then modify the copied stuff, in a subfolder of your /override folder … that is,

copy Spells.2da and/or Feat.2da to a new folder in your MyDocs/Nwn2/override folder. Then copy out any spellscripts also, ready for modification.

Couple of quick questions before I go playing: I see a lot of spells start with “void main ()”. What does this do?

#include “X0_I0_SPELLS”
#include “x2_inc_spellhook”

What does the above do?

As for the 2Da files, I have gotten familiar with them in my effort to add or modify character races as well as classes or Prestige classes. Aside from making the Ghostwise Halfling playable, I changed the feats they get from the start. Too bad there isn’t call for Animal Empathy, like in NWN. Seems they removed that from the game entirely. The only thing similar is the racial feat Animal Trance for the Yuan-ti Purebllood.

Thank you for the large response. I am going to try and play around a little over the weekend with it.

As for backing up files, the scripts are all in zipped folders in the install directory. I have unzipped a couple of them into separate directories, but I suppose I can just copy those directories into backup folders, instead of extracting them. When using the toolset, I can’t seem to find any of the content from Kaedrin’s PrC pack. How to I get the toolset to look for files that are elsewhere?

This is the last questiong I am going to do until I can play with all of this info over the weekend. My wife works 10 hours tomorrow, so I’ll have time alone to play with it.

Thank you so very much.

1 Like

@temyankee

From Page 9 of the Scripting Manual above … (Link copied here.) By the way, these manuals I link to are about scripting and not building a module. If you can follow the exercise in them, you should start to understand what you need … or at least know more on what you need to know.

https://neverwintervault.org/project/nwn2/other/scripting-tutorial-beginners-nwn2

TLDR: It’s a way to access functions in other libraries that you may wish to use in your own scripts.

EDIT: And See Page 5 about the script types: void main() and StartingConditional()

1 Like

Those to line “include” the scripts X0_I0_SPELLS and x2_inc_spellhook into the script you are calling them.

It means you will be able to use their content/functions in your current script.

It’s good practice to define a functionnality only once, so if you need an update on it, you have only one function to correct, and you won’t forget one which hang somewhere and stay uncorrected. An “Include” allows to import that function when you need it.

void is the return type. Ie there is no return (aka null return)
main is the name of the function – each script is effectively a function. All scripts in nwn/2 are either main() or StartingConditional(). The latter returns an intand is used for dialogs (TRUE show dialog node, FALSE hide dialog node).

Those tell the compiler to look for functions/constants/variables in dependent files.

besides what Lance and Shallina said, I wrote some brief examples here:
Basic Scripting (not so basic) | The Neverwinter Vault

What I do is create a subfolder in MyDocs/Nwn2/override. eg “Ghostwise Halfling” …

then id open the stock resources in <install>/Nwn2/Data *zip (with 7-zip) and copy out whatever files I want modified. Alternately they can be automatically copied out by modifying them in the toolset (after a save), and they should appear in the root of your /override folder. But i trust the toolset as little as possible … but maybe thats just me …

depending on the version of the kPrC Pack you have, it may not have many of the .NSS files. But the 2das should appear in the toolset ok

Or just look for the files you want in wherever it’s installed to …

'welcome

ps. be careful you don’t end up with duplicate files in /override (i use a duplicate file checker every so often)

I thought you said you had read the tutorials that were linked to. Obviously not as you would know that all normal scripts (i.e. not text appears when ones) have that and what the void part means. void main() is also found in all other C based (i.e C, C++, c#, etc.) programming languages.

TR

Actually, there is a whole folder of scripts for new spells and such created by K, which is in the MyDocuments/Override folder. When I open the toolset, and go to File / Open Conversation/Scripts, it only pulls in the information from the base game and all official expansions. It doesn’t even give me the option to look in another folder. Is there something else I need to do in order for the NWNToolset to recognize them?

Forgive me. When I breezed through the overview, I didn’t see what I was looking for on the surface. Having started to read it, I see some of the answers to 1 or 2 questions I have asked. However, your tone was unnecessary.

Kaedrin didn’t package the source code for some of the later versions of the PrC Pack … long story but i’m not really familiar with it.

So the toolset can’t find 'em. It (the toolset) probably falls back on what it can find …

 
you should look through the kPrC folders in your /override and see what .NSS files (aka scripts) are actually available


you could download this (1.42.1) and have a look (supposedly contains PrC source code)

(personally i use 1.41.4 since it has source)

1 Like

It would seem that K only placed the NCS files in the latest release. Any way to open those, or should I DL the other version you are showing me?

UPDATE: I downloaded the other version of Kaedrin you pointed me to but, as far as I can tell, it did not have any of the files I am looking for. So, back to my lacs question, how do I open/translate/decompile NCS files so I can view/modify them?

I have never decompiled/disassembled bytecode. dont want to …

but you might find the scripts you’re looking for in 1.41.4

Found the scripts that came with that version. As long as I don’t need to mess with anything that was changed in the newer version, I should be okay.

Next question: I have looked and looked everywhere I can think of. In what NSS file are Feats defined? There has to be some sort of code somewhere that tells NWN2 that the Feat Alertness adds +2 to Listen and Spot. I just can’t seem to locate where that definition is. What if I wanted to modify that feat, or other feats?

My guess is that you need to check the 2da file for that. You can find all 2da files under Data where you have installed your game. There are three zip files there with all 2das. Copy those zip files somewhere else, unpack them, and take a look at the file called feat.2da.

You can then modify and create your own version of the feat.2da that you can place in the override folder in the MyDocuments/Neverwinter nights 2/override.

EDIT: Actually, I see that @kevL_s already covered that in a post above here.

1 Like