Spells
From NWN1 Custom Content Guide
Written by Jasperre. This is an uncomplete documentation of how spells work, how to write and edit spells and how the spell functions and internal parts work. Once complete, it will be pretty comprehensive of how to write stuff from scratch and how exactly the game does spells (and spell like abilities, which are a damn sight easier!)
Contents |
[edit] Spells
Spells can be quite almost anything in NwN.
The Spells.2da contains:
- Player Spells that can be learnt by the different spellcasting classes, such as Magic Missile and Bless.
- Creature Abilties and spells, such as Dragon Breaths, Gazes, Howls and Auras
- Feat abilties, such as Bard Song and Barbarian Rage
- Misc items, such as Unique Power, Deck of Cards, spell sequencer, On Hit: Cast Spell, and so on.
Each one is considered a "spell", and normally have only one thing in common: they fire a certain script. Sometimes this is all they do!
[edit] Editing Exisiting Spells
Spells are much more easily edited then added - even alterations to the spells.2da only requires a few entries changed and it being put in a hakpack, while just changing a spell script (Which applies the spells effects) is even easier, and can be done on a by-module basis.
If you create a spell, you obviously need a new name - this requires talk table editing, and it will usually require Icons, new Item Property entries (For scrolls, wands). This means editing is much easier, if you want the Bioware spells using Bioware Icons and Bioware names.
[edit] Changing the impact effects only
Editing an existing Bioware spell, ability or other thing normally requires only one thing: the name of the script that is run.
If you wish, for example, to change the damage done by Magic Missile, you will have to look in Spells.2da and find the right Label (which is used for Humans to read the name of the spell), or the Column/Spell ID, looking up what Magic Missile is in the nwscript.nss file.
Once found (Magic Missile is NW_S0_MagMiss) open it in the module you want it to change in. For this, finding the line which does the calculation of damage (which is normally 1d4 + 1) and changing it.
Once saved (overriding the Bioware resource of the same name) the game engine will then fire that script and used the edited file, not the orignial, therefore, will run what changes you want!
Changes to the spell will not show up in the description - that requires a hakpack as described below.
[edit] Changing other information
If you have one, or want to change anything but the scripting side of a spell or ability, you have to edit the spells.2da file.
For example, changing the description of the spell for Magic Missile requires a new talk table file, and an edit to the description number of the Magic Missile spell line.
The current reference to a talk table entry is 6204. You need to have 16777216 + new talk table entry for a custom talk table to change this, however.
Other changes, I would look at the Spells.2da entry in the wiki CCG.
[edit] Adding New spells
Placeholder until I can be bothered to add to this. Basically, making a new Monster abiltity is the easiest, a new Item Ability is easy, a new Spell is not very easy, and a feat abiltity the hardest.
It comes down to the fact the scripts become harder to make or do, and the amount of 2da edits. For monster abilties, it requires 1 2da edit and some tlk entries, Item abilties require 2 2da edits at least, however, very few and also no tlk edits. Spells require at least 1 2da edit, and 1 more for adding it to items, and countless tlk entires (especially for each new adding to items) and finally, feats require 2 2da edits, and the feats.2da is a large and complicated 2da by itself.
EDIT:
Hugie has created a Custom Spell Creation Tutorial (For Dummies) which covers icon creation, 2da-editing, tlk-editing, and packing it all up in a hak after you're done. See the "Links" section at the bottom of this page for more details...
[edit] Internal Workings of spells and Scripting Functions to do with them
How does a spell fire? What exactly does happen with the Spells.2da entries? There are some very basic things here, which are incredibly easy to understand, and can be useful for creating new spells.
Most of this information in How Scripts Fire is using the Spells.2da information. The rest is more on the Scripting side of it.
[edit] How Spells Fire in game - Order of events
Spells can be cast by a Player Character (PC) a Dungeon Master (DM) or a Non Player Character (NPC), or a Possessed Creature (PC Familiar, DM possessed monster). Each one chooses a spell to cast differently:
The player character accesses the spell via. the radical menu, selecting what spells are prepared in thier spellbook. For this section, Epic Spells and Spell-like Feats are conidered to be feats. A PC can go through the r-click radial menu each time, or r-click onto a quickbar space (Where there are 36 possible entries) and add it there to quickly select it. Players can be considered the ultimate norm for casting spells (As they have restriced spells, feats, and statistics and thus follow some rules, NPC's do not).
Dungeon Masters can use any spell from any of the default classes as if they were a sorceror with unlimited spellcastings/day. They normally add them to quickbar if they are used frequently, but are not often used to battle with (DM's do not battle, and are considered friendly to everyone). A note that DM's, while they cast the spell as normal, seem to have a Spell Save DC of 10 + Spell level, as if they had no statistic that is used to cast spells.
Non Player Characters (NPC's) cast the spells, in a script, via. the use of ActionCastSpellAtObject(), ActionCastSpellAtLocation(), and Talents. Warning: These scripting functions break the TargetType column. This is one reason why NPC's may break a perfectly safe PC script, by casting it at something which is not a valid target.
Possessed NPC's, considering if they were not possessed with full DM powers, follow the rules for a normal PC who can cast spells. A bats "Gaze of Fear" which a PC character wants to use, can possess thier familar and look under "Creature Abilties" to find it - complete with description, and the ability to add it (temporarily) to the quickbar. DM's can do the same - noting that any spells in default spellcaster slots (Wizard, Sorceror, Cleric, Druid, Paladin, Ranger) will have the spells conviently listed, highest to lowest level, on the quickbar already (with multiples of the same spell taking up multiple slots). Possessed NPC's do have a restricted TargetType.
This is a basic run through of how a PC will cast a spell in-game, and what sections of the Spells.2da is used. Columns in Spells.2da will be highlighted with a link to the Spells.2da file itself. For this example, perhaps the PC wishes to cast Fireball:
- The caster picks the spell to cast and the target (or location). The game stops things not in the TargetType bitype category being targeted by a PC. If it were fireball, they couldn't target themselves, but could target the ground, a creature, or perhaps a door or placeable.
- Report in Combat Information: XXX is casting Fireball (Or "casts an unknown spell" on a failed Spellcraft check).
- The sound ConjSoundMale or ConjSoundFemale fires instantly (As the function Playsound had been used - it will play even if the spell is interrupted) and will also play ConjSoundVFX instantly the same. For Fireball, a firey sound and approprate school male/female conjuration sound will fire.
- The animation ConjAnim (And associated visuals ConjHeadVisual, ConjHandVisual, ConjGrndVisual) will play for the length of ConjTime (Default: 1500 milliseconds). For a Fireball, orange and red flames will leap around the casters hands.
- Note here, once casting has started, that if a concentration check is failed, the animations and visuals stop and the action basically is cancled.
- Remember that Defensive Casting mode has its own special concentration Check. this is done at the end of ConjTime.
- The column UseConcentration will determine if concentration checks are made. This is the casting of a PC spell, so it will have it. Fireballs Concentration Check would be 10 + 3 (Spell level) + Damage taken.
- Report in Combat Information: XXX casts Fireball (Or "casts an unknown spell" on a failed Spellcraft check).
- Once the ConjTime is complete then, if valid (A 1 in the Proj column) the ProjModel will fly towards the target or location using ProjType, ProjSpwnPoint, ProjOrientation and ProjSound. For a Fireball Spell, it would fire a nasty fireball at the target location. Cones such as for Color Spray would appear in these columns too.
- If there is no spell projectile, or once the Spell Projectile reaches the target, the ImpactScript will fire. This does any additional special visual effects and apply damage/other effect of the spell.
- The impact script will run totally independantly of the 2da file - if it exsists! The file, however, will have such variables as the spell level, target and target location (the location may even change if a projectile is fired, like a fireball, specifically at a target and the target moves) and so on.
- For the Fireball spell, once the Fireball projectile reaches the target, it will fire the script (The default Spells.2da impact script is "nw_s0_fireball"), which should do an AOE blast visual, do some reflex saves, and apply some fire damage.
- The sound CastSound will play instantly, as ConjsoundVFX did before.
- The CastAnim animation will then play (with associated CastHeadVisual, CastHandVisual, CastGrndVisual) which lasts for CastTime (Default: 1500, or 1700 for "cones") and stops any actions until it is complete.
- They are considered in combat, but will now stop provoking casting Attacks Of Oppotunity Concentration Checks.
- Once the CastTime is complete, a new action can take place, making sure, of course, the game rules are kept (IE: 1 spell per round, maximum of 2 when hasted or using Quicken Spell).
- As the time taken, by default, is 3000 milliseconds, a second spell, if hasted, could happen within the 6 second round. If the total time to cast took over 3000 milliseconds, then it would have to wait until the next round to cast another spell or perform a new attack.
I hope that is comprehensive enough to know when the engine fires what parts of the Spells.2da file, and what happens in game regarding that. Also note that Feats and Epic spells, as well as Creature Abilites will not follow these rules exactly, but are similar.
[edit] Spell Associated Scripting Functions
This will discuss how these functions work, and what they do in detail.
Some of the scripting functions used in spell scripts are:
- ResistSpell()
- GetSpellSaveDC()
- GetCasterLevel()
- GetSpellTargetObject()
- GetSpellTargetLocation()
- Fort/Will/ReflexSave()
- GetSpellId()
- GetMetaMagicFeat()
- METAMAGIC_* constants
- SignalEvent() and EventSpellCastAt()
- ApplyEffect***() (Location/Object)
- Effect***()
- GetHasSpellEffect()
- GetSpellCastItem()
[edit] ResistSpell()
ResistSpell (object oCaster, object oTarget);
This will only ever be valid if the spell is cast and is a valid player spell. If oCaster of oTarget is invalid, it won't function at all (return value of 0), which is the same as if the spell passes without fail.
Note: oCaster's Race, Alignment and so on are obviously checked for this, and applied approprately if an effect is against a specific alignment or race.
I will go through what each return value is in each case. I am still unsure of the order they are checked in, however.
The main use of this is to resist the spell with Spell Resistance, or stop it via a Spell Mantal or Globe of Invunrability. It also includes, with those checks, nautral immunity, and direct immunity to the specific spell via. item properties.
* Return value if oCaster or oTarget is an invalid object: FALSE
This usually will happen if the Caster or the Target is not in exsistance, in an Area of Effect spell (Such as Cloudkill) or a Recursive call (Such as how Melf's Acid Arrow works).
* Return value if the spell is not resisted: FALSE
This happens when it is a player spell, and none of the things below are valid. Basically, all that may be shown to the player in the combat information would be "Resist Spell: Failure", if they tested spell resistance.
This information is not in the Bioware default description of the function, but it does return FALSE if it is the case.
* Return value if spell cast is not a player spell: - 1
For example, calling this function within a Barbarian Rage script, or a Turn Undead script, which are feats.
I am only guessing, but I think that any monster ability may well act as if it a "player spell", that is, it will use the Innate Level. Of course, it is likely those also will not correctly work - as there is in the Spells.2da a special value for Monster Spells and Abilties (So you cannot give them a caster level - thus rendering any Spell Resistance checks impossible!).
* Return value if spell resisted: 1
This will happen if the target has any spell resistance, and passes the check.
The Bioware spells use a special Spell Resistance Successful visual effect for this result.
As we cannot put in anything but who the caster is, it is likely the base GetCasterLevel(oCaster), thus uneditable by us (For example, to raise or lower it). Warning: This may also mean any Spell Resistance checks in Area of Effect spells may change if, at one stage, Acid Fog is cast from a level 20 mage caster, then the same PC casts a level 1 Bless spells from thier 1 level of cleric, it will use the newer "They are a level 1 caster" value.
Spell reistance Checks are made on a roll: You roll 1d20 and add the Caster Level, and the DC to pass is the targets Spell Resistance. If you equal or beat it, it passes, else, ResistSpell will return 1.
* Return value if spell resisted via magic immunity: 2
This is normally for total immunity to the spell and no bad effects, such as from a Globe of Invunrability, or a Immunity: Fireball amulet.
A Globe visual effect is played in the Bioware spells when this value is returned.
This is used for any of the following:
- Immunity: "Spell Name"
- If they have a Shield Spell cast, which provides "Immunity Spell: Magic Missile", (EffectSpellImmunity()) any Magic Missile spells cast at it will fail a ResistSpell check, so it'll return 2. This also is the same as if they had an item with Immunity to a specific spell. Warning: The spell passed into ResistSpell will be the spell got via. GetSpellId(), thus sub-dial spells will function incorrectly with this! (Sub-dial spells are meant for friendly spells)
- Immunity: "Spell School"
- If they have Immunity to Spell School: Necromancy, all Necromantic spells are resisted. Shadow Shield provides Immunity to the Necromantic spell school, so any Horrid Wilting spells cast at them will be Resisted with this return value. See EffectSpellLevelAbsorption(), which would use the SPELL_SCHOOL_NECROMANCY part. Items can have this too. Also note that the parameters must match the ones below for Globes to be returned as 2.
- Immunity: Spells by Level
- This is any EffectSpellLevelAbsorption which do not have a maximum limit, or any Item Proprties which provide immunity to all spells of X level or below, such as a Golems total spell immunity to all spells level 9 and below.
* Return value if spell resisted via spell absorption: 3
This is strictly for Mantals, using EffectSpellLevelAbsorption(), which means a cirtain amount of spell levels have been used on the mantal (and 0 for cantrips). The spell level will be got from the spells.2da approprate to the caster, of course.
Only if EffectSpellLevelAbsorption actually has a limit to the amount of spell levels will it be used. No Item property will return this, as they can't be set to have limits.
Bioware applies the Mantal effect when this result is returned.
[edit] GetSpellSaveDC()
GetSpellSaveDC ();
This will return the value of the save for the spell - it will be 'valid' only for Player spells and spells which can have a caster level.
The return value is 10 + Spell Level + Relivant Attribute Bonus + Feats.
This may be, for example, burning hands, level 1 spell, from a Wizard with 18 intelligence (+4 bonus to save DC's) and with Spell Focus: Evocation (+1 to DC's for Evocation spells) bring a total of 10 + 1 + 4 + 1 = 16.
This is done internally, and is much faster then checking for all possible feats and checking them against the spell school and so forth.
A usuful thing to note is that, as the PHB says, any "Monster Abilties" which are spells use Charisma as the castng attribute - this can mean the DC's can go under 10!
The best way to change such a value like this is to edit it after its been called, perhaps adding more to it to make the save harder, or decreasing it to make it easier.
[edit] GetCasterLevel()
GetCasterLevel (object oCreature);
This can only be called for Player Spells with a valid caster level. It will return a default value of 0 because of there being no caster level to return!
Placeables will also return 0, because of them not having a caster level, and is likly true for most of the ActionCastSpellAtObject() for bCheat being TRUE.
Note: You cannot edit this value and put it into such functions like ResistSpell (where it is used for checking spell resistance), which is a bit silly. This usually is the best and only way to get the level of the spell cast, as variables such as Monster Ability spells have an independant caster level never based on Hit Dice.
[edit] GetSpellTargetObject()
Returns the target of the spell, that is, if a target was targeted and the caster is valid. So, if you cast it at a location (and not a valid object) it will return OBJECT_INVALID.
This won't check if the target object is correct according to the TargetType column. It mearly finds and returns the object which has been targeted by the spellcaster.
A small note that GetLocation(GetSpellTargetObject()); and GetSpellTargetLocation() could be unequal.
Note: This should only be called from a frest ImpactScript column script, no where else at all.
[edit] GetSpellTargetLocation()
This, of course, returns the location of the point where the spell will impact. It doesn't have to be an exact point where the spell was targeted - if you cast a spell such as fireball at a target directly, and they move, the impact location will move with them.
This Location will always be valid, even if they can, for example, only target items or only target creatures. If it is targeted against an object, the location of the target and this location should be close, if not the same, normally.
A small note that GetLocation(GetSpellTargetObject()); and GetSpellTargetLocation() could be unequal.
Note: This should only be called from a frest ImpactScript column script, no where else at all.
[edit] Fortitude/Will/ReflexSave()
FortitudeSave (object oCreature, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF);
ReflexSave (object oCreature, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF);
WillSave (object oCreature, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF);
These 3 functions should normally be called in some kind of wrapper in a spell script. Biowares own are not perfect (MySavingThrow() function) but do have the basis of why to do it - it can add the visual effects you see for the appropiate saving throw. Any text you see in-game is automatic game-engine text, and is normally sent to the caster and target.
Do not delay any calls of these functions from an impact script as they will not be considered spells and cirtainly may not work correctly or the way they are intended to.
More information on these directly. Firstly, they can take into account only cirtain information - unlike ResistSpell, almost all of the inputs are there. The only thing which you cannot control is the addition of SKILL_SPELLCRAFT, and the +1 to spell saves. If it is a "Spell" is determined by what class cast it, I think.
The other things which may change the return value from what you'd have with a simple GetFortitudeSavingThrow() and rolling a d20() against it is the different Saving Throw Types, and the fact the caster may be of a cirtain alignment or race.
The values which may return 2, "Immunity to the spell saving throw type specified", are SAVING_THROW_TYPE_DEATH, SAVING_THROW_TYPE_TRAP, SAVING_THROW_TYPE_DISEASE, SAVING_THROW_TYPE_FEAR, SAVING_THROW_TYPE_POISON and SAVING_THROW_TYPE_MIND_SPELLS. These only apply if you have the right "Immunity: XXX", and do *not* stop any effects being applied. If you have a poison save, but yet only are applying a -2 to decrease abilities, then even if they are immune to the poison save type specified, they will always have the -2 ability damage done. This was one of the errors in the MySavingThrow() Bioware uses.
The caster object is also used to determine EffectVersusAlignment() and EffectVersusRacialType() effects, such as +2 will saves against evil people.
Basically, you should only apply effects if they fail, and thus only apply effects from spells if they return 0 with this function, which is when they simply fail to beat the DC put in by rolling the dice and adding the right modifiers.
Note: This has an in-built, as of newer patches, an auto fail on a 1 and auto pass on a 20 dispite the nDC passed into the function.
[edit] GetSpellId()
GetSpellId ();
Simply put, if you want to, you can use this instead of constants for the EventSpellCastAt() and SignalEvent().
It returns mearly the line in the spells.2da which triggered the Impact Script, nothing else. It always has a value for a spell file, and can be easily compared with lots of things - such as one script can be used for sub-spells - such as Polymorph.
There is very little to add here. Normally, you know what spell is being used. In Hordes of the Underdark, it was seen to
Note: You can only call it in an Impact Script, no where else. Bioware made this error and added it to Darkness for the SignalEvent(), and it returned -1, because the AOE didn't cast a "last spell".
Note 2: There is no way to get the last spell cast by an object unless you use local variables.
Note 3: GetLastSpell() works only on an objects OnSpellCastAt event, and only returns the value of the SignalEvent()'s EventSpellCastAt() number, nothing else.
[edit] GetMetaMagicFeat()
GetMetaMagicFeat ();
With only 1 return value, this mearly returns the metamagic purposly chosen from the spellbook, not automatic feats. It returns a METAMAGIC_* constant, and is used, obviously, to change rolled values, durations and maximise dice.
Bioware, in Shadows of Ulrentide, added MaximiseOrEmpower() and used that to get different changes in dice rolls. You can do the same for durations, and it simply makes it easier to code spells. In most Bioware spells, you can see it was done manually.
[edit] METAMAGIC_* Constants
A few notes on the Metamagic Constants which would change your spell scripts:
METAMAGIC_MAXIMIZE - This maximises any dice thrown. A magic missile which has been maximised will do 5 damage, a total of 4 + 1. It will only affect any dice rolled (which can apply for random durations too), nothing else.
METAMAGIC_EMPOWER - If a random variable (usually a dice roll) such as 1d4 + 1 damage for each Magic Missile, and 2d4 for the spell Sleep is increased by 50%. I (Jasperre) got this wrong before - the entire amount, be it (2d6) or (1d4 + 1) is multiplied by 1.5.
Example: The calculations for a spell such as Slay Living, for the damage part (not the death) which does, in its description:
You can slay any one living creature. You must succeed on a melee touch attack to touch the subject, and it can avoid death with a successful Fortitude save. If it succeeds, it instead takes 3d6 points of damage +1 point per caster level.
So, 3d6 + X damage. Therefore, for example, we use a level 20 caster, it might do 3d6 + 20 damage. We are able to get:
Minimum: (1 + 1 + 1 + 20) * 1.5 = 23 * 1.5 = 34.5 = 34 Damage (Round down) Maximum: (6 + 6 + 6 + 20) * 1.5 = 38 * 1.5 = 57 = 57 Damage (!)
Average: 57 + 34.5 = 91.5. Divide by 2 = 45.75 = 45 damage.
Compared to a Maximization of the spell:
Maximized: (6 + 6 + 6 + 20) = 38.
So in this case (although not many others, because the dice usually determine the greater of the two numbers which make the whole, in this case the 20 is always bigger then the 3d6) Empowering gives a much greater total on average.
For magic missile, at 1d4 + 1 per roll, it does (1 + 1) * 1.5, or 2 * 1.5 = 3, minimum, and (4 + 1) * 1.5, or 5 * 1.5 = 7.5 = 7 maximum, with Maximising giving 4 + 1 = 5, which of course is in the middle (or so).
Also remember you can maximize anything you can empower, and vice versa. Randomly declared durations can also be affected if they use dice (Such as the aformentioned Sleep spell).
METAMAGIC_EXTEND - Doubles the duration of a spell. If the duration is random (for example, 2d4 rounds or similar) then of course you could maximize it or empower it. For this, simply double the duration you have to start.
A note here: If we could apply 2 or more metamagic feats at once, they do stack - as in, we would empower any dice, then double it, if it was extended and empowered.
[edit] SignalEvent() and EventSpellCastAt()
SignalEvent ( object oObject, event evToRun);
EventSpellCastAt (object oCaster, int nSpell, int bHarmful = TRUE );
These two constitute an important part of NPC AI, and the fact these are the only things (apart from damage) that will trigger the AI to go hostile against a spellcaster, or even notice a spell was cast on them (such as casting a cure spell specifically on something). Using the EventSpellCastAt() inside a SignalEvent() will cause the On Spell Cast At Event to fire, with the parameters listed changing what is picked up by functions for the Spell Cast At event itself'
The parameters are indepentant of any 2da entries - HostileSetting in the Spells.2da file will not change what is done here (that should, although untested, mearly do things to your invisibility status and suchlike).
So, it simply is an integer, for the spell (Usually a static constant such as SPELL_MAGIC_MISSILE) and who cast it (Usually OBJECT_SELF, or an area of effect creator) and if it was harmful, or not harmful.
Any spell which is "Neutral" should be FALSE for bHalmful, as it mearly is the absence of a harmful effect - such as, for example, Identify, which while providing no real benifits to stats, healing and so on, helps identify things - it is a neutral bonus.
For spells such as Dispel Magic - which, hey, can be considered good (Gets rid of Bad spells on a target) and bad/harmful (removes spell protections and bonuses) is normally done by checking the faction reputations - so, if the target is allied with the caster, don't make it hostile, else, make it hostile.
By putting TRUE as the bHarmful, it will usually activate the AI into attacking the caster - of course, it can be scripted not to (as to not attack allies), and shouldn't affect hostility if no script is present.
[edit] Internally involved workings
This will describe the internally involved parts of spells, and approprate other things which are not specifically scripting functions.
Some of the internal parts of spells are:
- Projectiles and timing
- Concentration, Spellcraft, Dispelling
- Area-Of-Effects
- Spell Resistance, Mantals, Globes
- Limitations of Effects and overlaps of spells
- Metamagic, spellbooks and player spells
- Domain Spells
- Counterspelling, Counterspells
- Spell Icons, informations, names, etc.
- AI and Talents
- Haste
- Time stop
Note that I don't have all the answers, but I will try and update this so it is easy to create new spells as long as the Spells.2da reference is used!
[edit] Haste
This is quicky and simply talked about. Haste as an effect, EffectHaste(), or as a permament magical property, of course called Haste, it will do several things to spells and attacks.
Here is what it does, in terms which may be useful:
- Adds +4 Dodge AC bonus (thus stacking with other dodge AC, and lost when prone)
- Adds 1 full Attack, at the primart Base Attack Bonus
- Adds +50% speed bonus, as if EffectMovementSpeedIncrease(50) was applied.
- Will cut casting times in half (this is explained below)
- Does NOT stack with any other haste. It is also cancled by any application of Slow.
It is quite powerful stuff. For the Spellmans Project, which is using the fairer 3.5E version of haste, it is still possible to use EffectHaste(), and gain the benifits of it automatically doing all of the above, while still forcing only 1 spell per round.
The casting time of a spell is "ConjTime" added to "CastTime". The former is how long it takes to conjure up the magical power, and thus can be interrupted in that time. The latter is how long your hand stays outstretched and so on.
Haste mearly cuts the values in the spells.2da, for Player Spells (not feats or srolls or special abilties) in half. This means that, a default spell with a ConjTime of 1500, and a cast time of 1000, which is 2500, becomes 1250.
If the game sees that there is enough time to perform another spell in the time left in the round, it'll allow another spell to be cast.
By mearly increasing the Conjuration and Casting times in the spells.2da to an appopriate amount, it would make it so that the game couldn't cut it in half and perform two castings a round.
Of course, a good thing for casters is that the amount of time conjuring the spell is still half the time it was, and so half the time for concentration to be interrupted.
I've yet to get a perfect example of an increase in time, or properly test this fully wihtout the aid of another PC player, who can more accuratly line up spells while paused. I will finish this fully, with numbered examples, and the benifits of it (I agree to the fact Haste was meant to be a fighter buff, not a mage buff, for the intents and purposes of an additional attack not an additional action).
[edit] Time Stop
The effect in NwN is basically very very cool. Its insanely powerful, a brilliant example of a level 9 spell.
In defintion it actually speeds the caster up soooo fast, for a cirtian number of "Real time seconds" in appearnace it slows everyone else down to almost stopping time - time freezes almost (when in reality, the caster is moving insanely fast).
For the Spellmans Project, I've taken scripting procautions to make sure it fits with the 3.5E rules that no hostile actions can be taken in time stop - no hostile spells or attacks or anything.
However, in NwN, the effect doesn't do this automatically (and is inline with 3E rules) but for some reason they made it 9 seconds long.
The workings of Time Stop are many - first, it basically pauses the game for everyone but the caster. DM's are not affected, and there is no way to make a creature unaffected apart from the person the effect is applied to.
Secondly, any visuals, sounds and scripts are paused - just as in Pause, and will not fire until the time stop is over. Heartbeats won't run, AI won't work (Apart from if the caster is an NPC of course!) and so on. This means, of course, you can't detect when timestop is running in a script.
Thirdly, because of everyone being stopped, it is considered that everyone is almost held - it is very easy to get sneak attacks (which is probably one reason why 3.5E made it into a buffing, not a hostile action taking, spell), and no Attacks of oppotunity are made against spells being cast. Also note that because of AI scripts not firing (but will do when timestop is over as appropiate) it is like an incredible hiding spell - you can run past the frozen creatures without so much as them knowing you've past.
Because of the pause, any spells cast will not take effect (and durations will not expire - the in game clock is also paused) until the time stop ends. A creature summoned will not appear, and cannot attack in time stop. A fireball will be conjured, but not start moving. Magic missiles will be there, but be waiting until the time stop has ended - they are all not speeded up to the speed of the caster.
A strange bug also occurs, and until I get more information on it to reproduce it I won't send it in personally (although others are welcome to) that because of the strange timing (1.5 rounds) any spell being cast in the last 3 seconds, IE: Cast time stop, cast another spell in the first 6 seconds of the Stop, and then start casting in the next 3, it seems the game gets confused - you stand there (or the NPC does) waving thier hands around, unable to complete the spell for a number of rounds (one to three it appears) before it catches up. Of course, when hasted, you can complete a spell in those 3 seconds and so will not notice anything. It also doesn't seem to happen all the time.
[edit] Links
(Shameful publicity link) You can contact Jasperre (Writer) at the Spellman's Project who are doing new spells for NwN.
There is the Spells.2da file, obviously needed and contains all the hardcoded things we can get our claws on.
Look at the Neverwinter Nights Lexicon for more information on general scripting and tutorials.
Also, Hugie has created a Custom Spell Creation Tutorial (For Dummies) which includes:
-a step-by-step walkthrough on making a new spell (from designing a spell-icon to 2da-editing to tlk-editing to packing your finished resources into a hakpack);
-a pre-padded version of spells.2da for easy editing;
-and a template for a typical offensive area-of-effect spell's impactscript which checks for spell resistance/immunity/absorbtion/saving throws.
