- Introduction
1.1 Purpose of Document
1.2 Getting Started
1.3 Horses in the Official Campaign
1.4 Acknowledgements
1.5 Disclaimer
1.6 Change History
- Bioware Horse System
2.1. Documentation
2.2. How Horses are Implemented
2.2.1 How are Horses Identified?
2.2.2 Who can Ride a Horse?
2.2.3 Owning a Horse
2.2.4 Mounting a Horse
2.3. Speed and Skills When Mounted
2.4. Radial Menus
2.5. Paladin Mounts
2.6. Ownership versus Assignment
2.6.1. Radial Menu
2.6.2. Horse Functions
2.6.3. Dismissed Horses
2.6.4. Non-Party Horse Owners
2.6.5. Mount Restrictions
- Existing Modules
3.1. Overview
3.2. Summoned Horses
3.3. Scripts and 2da Files
3.4. Henchmen
3.5. Custom Animations
3.6. PC Creature Skins
3.7. Item Stripping
3.8. Monsters
3.9. Custom Races
3.10. Community Expansion Pack (CEP)
3.11. Wyvern Crown of Cormyr (WCoC)
3.12. Horse Sense
- Module Building
4.1. Include File
4.2. Module Options
4.3. Module Events and Imported PCs
4.4. Exported PCs
4.5. Areas - Horse Control
4.6. Area Design
4.7. Transitions
4.8. Triggers
4.9. Stables and Battle Lines
4.10. Hitching
4.11. Horse Functions
4.12. Horse Management
4.13. Inaccessible Horses
4.14. Animations and Jousting
4.15. Mounted NPCs
4.16. Mounted Combat
4.17. Death While Mounted
4.18. Loading Saved Games
4.18.1 Mounted Archery
4.18.2 Space Requirements After a Mounted Save
4.18.3 Dismissed Henchmen
4.18.4 Polymorph and PC Appearance
4.19. Horse Variables
4.20. Script Hooks
4.21. Custom Scripts
4.22. Saddlebags
4.23. Nightmares
4.24. Persistent Worlds
- Creature Scaling
5.1. Humanoids
5.2. Dragons
5.3. Ponies
5.4. Other Creatures
5.5. Custom Creatures
5.6. Limitations
- Player Tools and DM Tools
- Known Issues - Horses
7.1. Animation and Graphics
7.2. Gameplay
7.3. Toolset
7.4. Scripting
7.5. DM Client
- Examples
8.1. Proleric's Horse Demo
8.1.1 Demo Module
8.1.2 Things to Try
8.1.3 Conversations
8.1.4 Scripts
8.2. Horse Market
8.3. OnClientEnter Script for Persistent Worlds
8.4. Pre-mount Script
8.5. Customization
8.6. Blackguard Mounts
8.7. Fix for Transition Bugs
8.8. Fix for Restoration Bug
8.9. Fix for Paladin Mounts
8.10. Fix for Domination
8.11. Persistent Worlds
- Lexicon
9.1. Functions
9.2. Module Variables
9.2.1 Area Control
9.2.2 Abilities and Combat
9.2.3 Henchman Control
9.2.4 Saddlebags
9.2.5 Paladin Mounts
9.2.6 Mounting System
9.2.7 Database
9.3. Area Variables
9.4. Horse Variables
9.4.1 Mount Control
9.4.2 Saddlebags
9.4.3 Script Hooks
9.4.4 Custom Mounts
9.4.5 Custom Races
9.5. Debugging and Tweaking Scripts
9.6. Constants
- Modified Bioware Scripts
This unofficial guide has been produced to help module builders to use the Horse system and creature scaling introduced in NWN 1.69.
In addition to the standard functions, it explains how to customize the system, and outlines the integration of custom creatures.
It assumes a basic understanding of the Aurora toolset, NWScript and 2da files.
3D modeling will not be covered.
If you want to get started without reading the rest of this Guide:
- If you're only interested in
Creature Scaling, you can jump to that section.
- You can exclude horses from your existing module (see
Existing modules - Overview).
- Alternatively, here is a no-frills approach which will allow your module to support the new Summon Mount feat (and other horses, if you wish).
Set the module's OnModuleLoad script to x3_mod_def_load, to ensure that the mount animation looks right.
Add this line to x3_mod_def_load, to exclude horses from indoor areas:
SetLocalInt(GetModule(), "X3_MOUNTS_EXTERNAL_ONLY", TRUE);
Set the module's OnClientEnter script to x3_mod_def_enter, to ensure that PCs have the Mount Actions feat.
Now set the module's OnHeartbeat script to x3_mod_def_hb, to support the Mounted Combat feat.
The screenshot above shows what the default scripts should be when this is all done.
If you want some horses that the party can ride at will using the radial menu, you'll find them in the Monsters/Animals/Other section of the creature palette in the toolset.
To create mounted opponents with a standard race appearance, open the creature properties in the toolset. Do the following steps in exactly the order specified.
Firstly, to ensure that the horse is scaled correctly later on, change the Appearance to the mounted appearance for the race and gender:
Now make any changes you want to the head and other body parts.
Change the Phenotype to "Normal Mounted":
Finally, change the Tail to the horse appearance you require:
That's it, for simple modules. If your module is customized, the key points are:
Custom transition scripts must include the new code from nw_g0_transition.
Customized Bioware scripts usually need to be rebuilt by inserting the custom code in the standard 1.69 script.
Customized 2da files should generally be reconstructed from the standard 1.69 2da files.
The Official Campaign (OC) has not been modified to support horses.
To use them, you'll need a new PC created in 1.69 module(i.e. not a pre-generated PC or one you created in an earlier release).
Paladins can summon a mount at level 5; officially, that's it.
Some aspects of the horse system don't work correctly - for example, Paladin mounts are not unsummoned when you rest.
If you're desperate to ride a horse in the OC, you can cheat.
Enter the following in the chat window:
##DebugMode 1
##dm_spawncreature x3_horse001
##DebugMode 0
|
If you don't like x3_horse001, enter the resref of another horse (you can look it up in the toolset -
it's on the Advanced tab of the horse properties under Paint Creatures > Monsters > Animals > Other).
Much of the material in the guide is adapted from original notes by DevaWinblood (revised by Azbest),
which can be found in the Bioware include file
x3_inc_horse (Copyright Bioware 2007, 2008).
I've included this material so that it can be referenced quickly while working with the toolset.
The narrative, examples and remarks were written with input from Brian Chung, Azbest, OldMansBeard,
Barry 1066, Ollebroc, The Krit, Fluffyamoeba, FriendlyFire, JP, Thrikreen, Hardpoints, Invisig0th,
Axe Murderer and others too numerous to mention.
A major "thank you" to Bioware for releasing this wonderful system!
The guide is provided as-is. The author cannot be held responsible for damage done to your computer,
applications and/or game installations by following these guidelines.
The Bioware system is complicated. I've done my best to record the nuances, but don't be surprised if you discover minor errors or omissions.
Version | Changes |
1.06 | Lexicon 1.69 Version. Changes made to adjust style to fit with the Lexicon's style.
Minor spelling changes to section headers.
Added 2.2.#, 2.6.#, 4.18.#, 8.1.#, 9.2.# and 9.4.# sections to the table of contents.
Added links to Lexicon pages.
Minor consistency changes.
|
1.05 | Added Axe Murderer's transition bug fix for secret doors to "Known Issues" and "Examples".
Corrected "Speed and Skills when mounted". The speed increase when mounted is typically 50% (not 99% as stated in the Bioware documentation).
Updated "PC Creature Skins" to mention a workaround if skins are found in monster loot.
"Custom Scripts" section rewritten.
Known Issue and fix - lag when horse animations load.
Known Issue - save/load while mounted with a ranged weapon equipped can result in a permanent attack penalty.
Known Issue - DM client does not show class abilities correctly on the radial menu.
Known Issue - ELC and ILR server options affect PC Creature Skins. |
1.04 | Added a section on "Ponies".
Added a Horse Market example.
Update on "Horses in the Official Campaign".
Known Issue - in a PW, the PC skin is created again from scratch on data base load.
Added some comments to Ollebroc's examples.
Added x0_inc_henai to the list of Bioware scripts known to have been modified for horses.
|
1.03 | Added a section on "Horses in the Official Campaign" (how to cheat).
Added Ollebroc's working example of an OnClientEnter script for PW.
Added the undocumented Item Property constants for the Mounted Feats under "Mounted Combat".
Added a comment on timing to "Script Hooks".
Minor update on "Custom Animations".
Known Issue and fix - horses attack on sight and bash locks.
Known Issue and fix - horses can be dominated when flagged as "not rideable", allowing a horse market exploit.
Known Issue - HorseCreateHorse - nTail parameter doesn't work.
Noted that Bioware's pre-generated PCs (e.g. Aluvian Darkstar) can't ride horses, unless given the Mount Actions feat.
Removed the incorrect statement that dismissed Paladin mounts can't be recovered or unsummoned from "Known Issues".
Added x2_mod_def_rest to the list of Bioware scripts known to have been modified for horses (doh!). |
1.02 | Correction to "Speed and Skills when mounted". |
1.01 | Added a section on "Player Tools and DM Tools".
Added "Speed and Skills when mounted" to the "Bioware Horse system" section.
"Existing Modules" section ("Overview", "Custom Animations" and "PC Creature Skins") updated.
"Persistent Worlds" section now has a comment about HorseHitchHorse timing.
"Creature Scaling" section includes a clearer warning that tail and appearance model types must match. |
1.00 | Known Issue - certain spells (e.g. Whirlwind) may look silly when mounted.
Known Issue - HP and AC boost options don't work for Paladin mounts.
Known Issue - imported PCs reset to normal phenotype.
Added a provisional list of "Modified Bioware Scripts".
"Existing Modules" section ("Overview" and "PC Creature Skins") updated.
"Module Events and Imported PCs" section and "Persistent Worlds" section clarified. |
0.26 | Existing Modules section (Overview and Custom Animations) updated significantly.
Known Issue - a PC can mount a horse which is owned by someone else.
Known Issue - a Paladin mount which is removed from the party can never be used again or dismissed.
Known Issue - polymorphed PCs may have a random appearance after save/load.
Correction - the owner cannot use a horse if X3_HORSE_NOT_RIDEABLE_OWNER is set.
Added some more examples provided by Ollebroc. |
0.25 | "Stables and Battle Lines" updated with a more robust technique for keeping horses in line when a saved game is loaded.
Expanded the Lexicon Known Bugs entry for HorseGetCanBeMounted.
Added some new entries to "Known Issues - Gameplay". |
0.24 | For Beta 10, I've added sections on "Stables and Battle Lines" and "Hitching" (also a section on "Persistent Worlds", based on Ollebroc's findings).
If you have been using earlier Betas, change your OnModuleLoad script to x3_mod_def_load (the x2 version is now out of date). There have been significant changes to many core Bioware scripts (especially module and creature event scripts) and 2da files (appearance.2da, for example), so customisations need to be refreshed. Bioware has indicated that Beta 10 will be final if there are no major issues.
The following sections of the Guide have been updated significantly : Getting Started, Radial Menus, Paladin Mounts, Ownership v Assignment, Horse Sense, Transitions, Death While Mounted, Known Issues, Creature Scaling Limitations.
There are also some Lexicon additions and refinements, which are primarily for advanced users.
These include script hooks for Paladin mounts, animation fine-tuning (sundry new local variables and scripts) and some new functions in the Utility section. |
0.23 | Added a clarification to the Item Stripping section.
HorseSetOwner with bAssign=TRUE now grants the Mount Actions feat even if the owner has an invalid model type.
Known issue - the module OnHeartbeat script should default to x3_mod_def_hb, but it doesn't.
Known issue - mounting a Paladin summoned horse fails to modify speed, AC or skills.
Known issue - if a "dismount" area leads to a "no horses" area, there is no way to recover the horse on return. |
0.22 | Scaling large creatures is now possible.
It now seems likely that the Summon Mount spell for paladins will work for all imported PCs in the final release.
Added HorseRestoreHenchmenLocations() to the Lexicon.
Guide now mentions the quick fix to transition scripts available in Travel Builder.
Known issue - mounted PC can mount another horse, corruption occurs on dismount.
Known issue - mount animation works intermittently, and may not look right on low-spec hardware. |
0.21 | Update for 1.69 Beta 8.
New section on loading saved games. |
0.20 | Update on the new tails in Beta 7, which are not mentioned in the release notes.
Creatures created in Beta 6 may now have the wrong tail (but it's a moment's work to fix this).
Update on how to ensure that an NPC's horses appear when a saved game is loaded (see Known Issues). |
0.19 | Significant revision for 1.69 Beta 7.
Expanded sections on Custom Scripts and Nightmares. |
0.18 | Update on custom animations and known issues.
Minor corrections. |
0.17 | Added section on Triggers.
Minor clarifications on assignment, transitions and creature scaling. |
0.16 | Added section on Creature Scaling. |
0.15 | Significant revision for 1.69 Beta 6. |
0.14 | Added screenshots and a section on Mounted Combat, explaining the Ride skill. |
0.13 | "Getting Started" section, which covers the essentials.
Minor corrections on script hooks, HorseDismount and HorseInstantMount. |
0.12 | Updated description of HorseInstantMount and HorseInstantDismount.
Minor clarification on script hooks, setting horse variables OnSpawn and known issues. |
0.11 | Minor clarifications on local variables, script hooks, quickslots, characters with tails, death and known issues. |
0.10 | Minor clarifications on combat, monsters and how to disable the Paladin Summon Mount feat. |
0.09 | Clarification on the difference between owning and assigning a horse.
Minor updates on DM Client, HorseSummonPaladinMount, HorseCreateHorse and HorseRemoveOwner. |
0.08 | Added party horse management examples
Minor clarifications regarding OnClientEnter, feats, triggers, Paladin mounts and HorseRemoveOwner. |
0.07 | Major revision for 1.69 Beta 5 - most of the known issues in Beta 4 have been fixed.
New variables have been introduced for area control, mount / dismount script over-rides, and mount speed.
There is now an additional parameter for HorseSetOwner.
Lexicon extended to cover constants and variables. |
0.06 | Minor changes to known issues and examples.
Correction to HorseSummonPaladinMount example. |
0.05 | Lexicon added. |
0.04 | Confirmed that existing modules will usually work under 1.69, but will not support summoned horses without modification.
Clarifications and corrections on NPCs, excluding horses, PC skins, custom animations, PRC, WCoC, Paladin mounts and known issues. |
0.03 | Minor clarifications on party dismount, jousting, no-horse areas and skins. |
0.02 | Added sections on Saddlebags, CEP, script hooks and some example scripts.
Confirmed that jousting is not provided as standard and that horse-owns-horse is a bug.
Reinforced the message that the final release of 1.69 may differ significantly from the Beta.
Corrected some typos and added a workaround for the HorseRemoveHorse bug
Section references are now hyperlinks. |
The official Bioware horse system introduced in NWN 1.69 is documented in outline in the release notes.
The script
x3_inc_horse includes detailed information for module builders, which is adapted with additional
observations in the
Lexicon section of this guide.
It's good to start with a simple understanding of how the 1.69 horse system works.
If you are familiar with the horse systems in the Community Expansion Pack (CEP) or the premium module Wyvern Crown of Cormyr (WCoC), be aware that the 1.69 system is different.
In this Guide, the term "horse" means any mountable creature. It's much easier to use the standard
interface to the horse system correctly if you know what's going on behind the scenes.
I've underlined some key concepts below.
Any creature with an
appearance type in the range reserved by Bioware for standard horses
is regarded as mountable (see
Horse Variables for how to over-ride this).
PCs created prior to 1.69 need to be given a
creature skin which enables the Mount Actions feat and the radial menu (see
Module Events and Imported PCs).
The Mount Actions feat is sometimes referred to as the Horse Menu feat in the Bioware documentation - it's the same thing.
Henchmen and other standard NPCs automatically acquire the Mount Actions feat, and so do PCs created with 1.69.
The supported races are: Dwarf, Elf, Gnome, Halfling, Half-Elf, Half-Orc, Human.
Other creatures (including monsters and fixed appearance NPCs such as Innkeepers) can't perform Mount Actions (unless you provide custom models).
A PC or NPC can become the
owner of a horse, in which case its name changes (e.g. "Horse" becomes "Proleric's Horse") and it joins the party, following its master.
Before a horse can be mounted by a henchman, it has to be
assigned to them.
A character can own many horses, but no more than one can be assigned for mounting at any given time (see
Ownership versus Assignment for an in-depth discussion).
Unlike henchmen, a PC can mount any available horse without assigning it first.
Note that a PC's horse becomes a henchman (not an animal companion).
A henchman's horse becomes a henchman of the henchman, not a henchman of the PC.
By default, horses have standard associate scripts that cause them to follow their owner:
When a rider mounts a horse, the rider's
phenotype is changed to the mounted posture, and they acquire
a
tail which looks like the horse, with appropriate
racial scaling, before the original horse is destroyed.
In game, this all happens automatically. The underlying process is illustrated by the screenshots in
Getting Started which show how to create a pre-mounted character in the toolset. See
Mount Restrictions for constraints on mounting.
There is now one set of phenotypes for characters on foot and another set for mounted characters.
In addition, there's a third set for
jousting, in which the character rides and holds the lance in a slightly
different posture. Each set has "normal" and "large" variants as before, so there are now 6 phenotypes in total.
If the rider already has a tail, it is not visible when mounted.
Racial scaling means, for example, that the horse gets bigger for a half-orc, but smaller for a halfling, automatically.
Local variables are maintained on horses and on the rider's skin to keep track of all this.
Remember that while the rider is mounted, the horse doesn't exist as an object - information about it is stored as local variables. This is
a vital consideration when scripting.
It is not necessary to understand the internal local variables in detail, because they are managed by the standard interface described in this Guide.
Custom animations are used for mounting, dismounting and jousting.
That's it, in a nutshell.
By default, riding a horse looks cool, but doesn't provide a huge benefit.
A mounted creature typically moves 50% faster - you'll notice the difference when exploring large outdoor areas.
Wearing Boots of Speed or casting Haste while mounted doesn't increase your movement speed.
As we might expect, monks lose their speed bonus when mounted, i.e. a mounted monk is no faster than any other rider. Monks above level 30 move faster on foot.
Unlike Haste or Boots of Speed, the mounted bonus only affects movement speed, not combat speed.
Certain skills are effectively disabled by a massive penalty while mounted (Disable Trap, Open Lock, Hide, Move Silently, Pick Pocket, Set Trap and Tumble).
The Ride skill and the mounted combat feats are discussed in the Mounted Combat section.
All of these defaults can be over-ridden to make owing a horse more or less attractive - see Module Variables and Horse Variables.
Horse actions can be performed using the radial menus on the PC, henchmen and horses.
This is
much easier if you allocate the horse commands to quickslots (by right-clicking on the quickslot and selecting
an icon from the radial menu).
Note that the radial menu commands have a target cursor, so that you can left-click on the quickslot, then identify the target using the cursor.
Actions |
Command |
Target |
Comment |
Assign a horse to a character |
1. Assign Mount
2. Assign Mount |
Horse
Character |
Note this requires two commands.
Horses must be assigned to henchmen.
You don't need to assign a horse to your PC unless you want to own multiple horses personally.
The radial menu allows a PC to own many horses, but does not allow a henchman to own more than one.
So, assigning a second horse to a dismounted henchman reassigns the first horse to the PC, and you
can't assign a second horse to a mounted henchman. |
Release a dismounted horse |
1. Assign Mount
2. Assign Mount |
Horse
Horse |
Use Assign Mount on the horse twice. |
Mount a horse |
Individual Mount |
Horse |
This works for a PC without assigning the horse first.
All mounting is subject to Mount Restrictions. |
Ask a henchman to mount |
Individual Mount |
Henchman |
The henchman must already have an assigned horse, otherwise nothing happens. |
Dismount |
Individual Dismount |
PC or Henchman |
|
Mount the whole party |
Party Mount |
Any available horse |
Only henchmen with assigned horses will mount.
All mounting is subject to Mount Restrictions. |
Mount henchmen only |
Party Mount |
PC or Henchman |
If the target is a horse assigned to a henchman, the command is interpreted as "Mount henchmen only".
Any henchmen with assigned horses mount, but the PC does not. |
Dismount the whole party |
Party Dismount |
PC or Henchman |
This works even if the target is not mounted but other party members are. |
These commands work sensibly in untidy situations.
For example, if some of the party are mounted,
while others are dismounted, a Party Dismount only affects the mounted characters.
Paladins acquire the Summon Mount feat automatically at level 5.
Paladin mounts are implemented using much the same system as regular horses, but there is an important difference - they are
summoned creatures, not henchmen.
So, you can't change the owner of a Paladin mount - it is owned by the Paladin who summoned it, period.
The Paladin can dismiss it by resting or releasing it (see
Radial Menus) - otherwise, it continues to be owned by the Paladin until the summoning
duration expires.
The distinction between ownership and assignment can be important when using the horse functions.
This is an advanced topic which you may wish to
skip on first reading.
Remember:
a character can own many horses, but no more than one is assigned for mounting.
even if you use scripts to establish ownership and assignment, a player can still
change the situation by using the radial menu.
A horse can be owned, or owned-and-assigned (for which we will use the shorthand "assigned").
This distinction is a useful feature that permits the ownership of multiple horses, but it
can require careful management.
When a horse is assigned to a character (PC or henchman) using the radial menu, two things happen - the character becomes the owner, and
the horse is assigned as the potential mount.
The player can then assign another horse to the PC, in which case only the new horse is assigned, but the old horse is still owned.
A PC may acquire a string of horses this way.
The radial menu will only allow a henchman to have one horse at a time.
As we've already seen, the radial menu allows a PC to mount any available horse, whether assigned or not.
By default, the horse functions used in scripts set ownership, but do
not assign the horse for mounting.
Unlike the radial menu, functions allow henchmen and other creatures to own more than one horse.
There are optional parameters which assign the horse.
It's generally good practice to assign the horse, but have exception code to handle the possibility that a character owns horses but has none assigned. One example of the latter is a Paladin Mount that has been summoned on the radial menu.
When a horse is merely owned by a henchman, it will follow its master as a party member, but neither radial menu mount commands nor a Party Mount spell will result in mounting.
If you want the horse to be mounted by a henchman in these situations, you have to assign it explicitly.
Fortunately, the horse functions just do what they're told - if you use a function to mount a character on a legitimate target horse, it happens, regardless of assignment.
Since a horse is a henchman, it can be dismissed.
This does
not change ownership or assignment.
A PC can dismiss their horse on its radial menu, just like any other henchman.
The radial menu for a henchman's horse doesn't have this option, but the PC can reassign the horse to them self and then dismiss it.
In a script, RemoveHenchman will dismiss a horse.
The horse system will dismiss a horse automatically when entering an area where horses are forbidden (see
Areas - Horse Control).
Since assignment hasn't changed, the horse can be recovered by mounting or reassigning (see
Areas - Horse Control for exceptions).
In a script, you can confer horse ownership on an independent NPC, such as a noble or a horse dealer, or even a monster.
They don't need the Mount Actions feat merely to own a horse, so they don't have to have the standard race appearance that would be required
for riding.
This was intended to stop players stealing any horse that takes their fancy, but there is a
Known Issue - in practice,
a PC can mount a horse which is owned by someone else.
Also, it changes the name of the horse (for example, "King Arthur's Heavy Warhorse").
Note that since the horse becomes a henchman of the NPC when ownership is established, by default it won't stay where you placed it
in the toolset - it will seek out its master unless you RemoveHenchman immediately.
Mounting is not allowed when:
The target is not a mount or otherwise flagged as unmountable (see Horse Variables - Mount Control).
The area is flagged as unsuitable for mounting (see Areas - Horse Control).
The horse is owned by a creature who is not in the party (there is a Known Issue with this - in practice, a PC can mount a horse which is owned by someone else).
The horse is assigned to another member of the party and the command is issued from the radial menu.
A henchman is told to mount via the radial menu, but has no assigned horse.
The rider does not have the Mount Actions feat (for example, standard monsters and simple appearance types
such as Innkeepers cannot ride).
For example, if a PC targets the radial menu Party Mount command on a horse assigned to a henchman, the henchmen mount,
but the PC does not. The PC's mount request is illegal, but since the target is a party member, the command is still
regarded as a legitimate Party Mount instruction. The target is actually mounted by the assigned henchman, not the PC.
Subject to these constraints, a PC can mount any horse using the radial menu.
This includes horses which are owned by, but not assigned to, other party members.
When using scripts, it also includes horses which are assigned to other party members.
Existing modules which have not been modified for 1.69 will usually work, but there is no "quick fix" to turn the horse
system off entirely.
If the module alters a PC's appearance (by script or Polymorph) it may be corrupted by save / load
- see Loading Saved Games for a fix.
Players may complain of finding a creature skin item called "PC Properties" in an
inventory, or getting feedback that they just lost this item. This is harmless - it can be ignored if horses are not
allowed in the module. It may be revealed by:
- Scripts which strip the player of all items.
- PC load from database in a Persistent World.
- Monster loot (but normally only if the monster has been DM-possessed at some point).
- Spells like True Sight.
Code which checks that the player has no items may fail unless the module is changed to remove or ignore any item in the
PC's creature skin slot.
Some Custom Animations may no longer work correctly.
Authors may wish to discourage players from using the new Summon Mount feat, or disable it (see Summoned Horses).
Similarly, you may want to state that imported PCs with a mounted appearance are not supported (or disable it by using x3_mod_def_enter in the OnClientEnter event).
If you decide to take the above steps to ensure that the module remains horse-free, there's probably no need to read the rest of this Guide.
To support the excellent horse system in 1.69, modules created in earlier releases will need significant changes, which are described in the following sections.
Beware of "quick fixes" to introduce horses or manage the above-mentioned issues. The horse system is complicated, and easily broken by the uninitiated:
- If you decide to go beyond the simple steps in Getting Started, you'll need to understand most of the sections in Module Building. Introducing one tweak in isolation can sometimes break other things.
- It's a really bad idea to change the Bioware include files (unless you're very sure of what you're doing).
The reason is that they are included in many standard Bioware scripts. When you rebuild your module,
standard Bioware scripts are not recompiled. So, for example, if you change a function in x3_inc_horse,
the change only applies to your custom scripts; standard scripts still execute the old code.
This is a common cause of bizarre bugs.
- So, where possible, it's better to use the script hooks provided to customize the system.
- If you must change an include file, you need to make a local copy of every Bioware script that references it,
so that they are all recompiled. There is a provisional list in the section Modified Bioware Scripts.
- Attempting to remove the new PC skin almost certainly won't work, because every Bioware script that reads
information from the skin will attempt to replace it if it's missing. This includes some scripts that are present
in almost every existing module, such as the default AI.
Players can introduce horses, whether you want them or not. By default, Paladins acquire the Summon Mount feat automatically
at level 5, which will create a horse anywhere in the module.
Bear in mind that imported PCs might already have this feat.
If you don't want horses in your module, you may just want to announce that Summon Mount isn't supported, and could have
unpredictable consequences.
Alternatively, you can keep horses out of your module completely by changing the spell script
(x3_s3_palmount.nss) to tell the PC that the feat is disabled (or do the same thing in a spell hook specified in OnModuleLoad).
If you decide to exclude horses altogether, you don't need to read the rest of section 3.
To exclude horses from certain areas, see Areas.
Bioware has updated many of the default scripts significantly to support horses. I recommend that you review every script that
you have customized, as this is a much bigger issue than on previous releases. Ideally, your custom code should be reintroduced
to a vanilla 1.69 Bioware script. There is a provisional list in the section Modified Bioware Scripts.
For example, the default transition script nw_g0_transition now supports the no-go areas for horses. You don't think you've modified this? Think again. Any custom transition script you've written is implicitly over-riding nw_g0_transition, so, unless you change your transition
script, horses will be allowed in the new area, regardless of the switch settings - see Transitions. A "quick fix" for this
issue is available on my Travel Builder
page.
Similar considerations apply to 2da files.
For example, there is a major update to portraits.2da, which provides portraits for horses and many other creatures introduced with 1.69. In existing modules which over-ride portaits.2da via a hakpak, horses
will have blank or inappropriate portraits. This is highly visible in gameplay, as horses are listed as party members.
There are many other examples, including the many 2da files that support feats, appearances and tails. Horses simply won't work
correctly if these 2da files are over-ridden by existing haks.
The safe way to proceed is to review every customized 2da, reintroducing custom lines to a vanilla 1.69 file.
PC horses become henchmen, so scripts that reference henchmen will often need to exclude horses using the HorseGetIsAMount function.
By default, the maximum number of henchmen allowed in the module is permanently increased without limit whenever necessary to
accommodate horse ownership.
So, if a PC is allowed to own a string of horses at some point, they can use this as an exploit
to get more henchmen when the horses are released.
This rule can be modified (see Module Options), but it may be more convenient
to manage the number of henchmen explicitly in your scripts in future, rather than rely on the SetMaxHenchmen() and GetMaxHenchmen()
functions.
A script that cycles through PC associates using GetHenchman() or GetAssociate() may miss any horses owned by henchmen,
because they are associates of the henchman.
If your module was written for only one henchman at a time, be aware that there could
now be at least two - the normal henchman, and one or more horses.
If you write your own henchman heartbeat script, make sure it
respects the NW_ASC_IS_BUSY flag. The mount/dismount scripts set and clear this as a signal to heartbeats not to interrupt the
animations.
New animation slots have been added for horse mounting and dismounting, so existing custom animations 1 and 2 normally work
fine (but see final bullet).
You may have problems if you are using custom animations 3-10. In 1.69, by default, these are
used for jousting animations.
Provisionally, it seems that 7 and 8 are untouched, but 3, 4, 5, 6 and 10 have definitely been
used for jousting.
The simplest fix is to use the new slots 11-20 instead. Change all internal text references in the mdl
files to range to 11 to 20, and remap all scripts with new constants for animations 11-20.
If you don't do that, there are at least two different problems that can occur if you over-ride custom animations 3-10 in a hakpak:
- You may see unwanted frames from the jousting set, unless the custom animation explicitly specifies what is
to happen at the start and end of the sequence. Some old animations only specify the middle of the sequence.
- NWN animations can appear in different model files depending on whether they are related to combat, to casting,
etc. If you're over-riding the "wrong" model file, the engine will load both versions, and play whichever happens
to load last. Jousting animations 3 and 4 are in a_ba.mdl, 6 is in both a_ba_custom.mdl and a_ba_coat_cus.mdl.
5 and 10 are in h_ba.mdl, which is only used by mounted characters and unlikely to cause this issue.
There's a lively discussion about this in the Custom
Animation forum forum. It was recently reported that there is a custom1start inside a_fa_coat.mdl as well
as in a_ba_coat_cus.mdl, which can cause issues with custom animation 1.
PCs created prior to 1.69 need a creature skin to acquire the Mount Actions feat and radial menu. This also applies to the
pre-generated characters supplied with the game (e.g. Aluvian Darkstar).
This is done for you by the OnClientEnter script x3_mod_def_enter, but this is only the default for new modules.
Actually, all PCs will eventually acquire a creature skin during gameplay (see WARNING below). Don't rely on this, though, because the skin may not have the Mount Actions feat.
Note that DM-possessed creatures and PC-possessed familiars are regarded as PCs, so they acquire skins which may show up
later as loot when the creature is killed.
The paladin Summon Mount script will automatically create a skin where necessary, so if that's the only way horses can enter your module, you don't need to change anything here.
x3_mod_def_enter uses the HorseAddHorseMenu function, which will create a skin, and will also modify an existing skin.
So, custom systems like PRC which use creature skins on PCs should still work (see Module Events and Imported PCs for caveats).
See Player Tools and DM Tools for an example of how to add feats and other properties to the skin without breaking the horse system.
The new PC skin is not in the toolset, but if necessary, it can be created from the resref x3_it_pchide. It has the name PC Properties
and tag x3_it_pchide. It is just an empty skin with no properties (the Bioware scripts add the feats and variables).
Once created, the Bioware scripts don't care what it's called, so a custom skin with a different name, tag and resref
will work just as well.
WARNING: Do not attempt to remove the PC creature skin functions from 1.69. Any script which references variables on this
skin (including the default AI) may attempt to replace the skin if it's missing. If the skin item is causing problems
in your module, change your custom code to ignore the creature skin inventory slot (see next section for an example).
The server options Enforce Legal Character (ELC) and Item level restriction (ILR) will remove the PC skin on entry.
The standard OnClientEnter script then replaces the skin with a new one which has the riding feats. Custom skin systems may need a tweak in x3_mod_pre_enter to load the correct skin first.
There have been reports of PC skins being found in monster loot in SoU and some PWs. This may be the result of an
incorrect implementation, but some PWs have introduced a unique OnDeath script in which all the BASE_ITEM_CREATUREITEM gets destroyed before treasure is dropped.
There has been an isolated report from a PW administrator that the PC skin sometimes unequips into the PC's inventory.
This may be a module-specific bug. It was fixed by putting the item in a quickslot and double-clicking to equip (or relogging).
Modules that strip all items from the PC will need to explicitly exclude the creature skin, otherwise the Mount Actions feat will be lost.
INVENTORY_SLOT_CARMOUR should not be unequipped. Also, beware of removing an item with tag x3_it_pchide from inventory (it might not be
equipped until shortly after the OnClientEnter event). The skin can be reinstated, by recreating the skin on the PC and equipping it to their skin slot.
Only PCs and standard NPCs can have the Mount Actions feat.
Other creatures, including monsters and fixed appearance NPCs (e.g. Innkeeper) don't. It is not possible for them to mount a horse - the Horse functions may produce odd results if you try to do this.
Horse ownership is allowed, however. The way to make other creatures that can ride horses in this system is to create a Custom Race.
Custom races will not work with the horse system unless you provide suitable models and animations for mounting. This is beyond the scope
of this Guide, so suffice it to say here that the creature will also need the Mount Actions feat and local variable settings as described
in the Lexicon.
The CEP team has released CEP 2.1, at the time of this writing, which is compatible with 1.69.
Suffice it to say here that the 1.69 horse system is fundamentally different from the CEP horse system.
It is possible to have a custom horse with a CEP appearance, but it needs the 1.69 script set and variable settings described in this guide to work.
The 1.69 horse system is not the same as the one used in WCoC.
A character saved while mounted in WCoC will not work in 1.69 (and vice versa).
Unless you exclude horses (see Summoned Horses) you may have to review everything that happens in your module, asking the question
"does this make sense on horseback?".
Fortunately, as far as I can see, all the actions you can take on foot work on horseback - the question is one of immersion - will it seem silly?
Most of the issues concern keeping horses out of restricted areas. Have a good look at all your area transitions. A good technique is to search for every script that contains "Jump", rename them all, then rebuild the module with the option to report missing area resources. This identifies all the transitions with custom scripts. The aim should be to remove all the custom transition scripts from doors and triggers (implementing the custom code in some other way). Any "Jump" scripts which are not identified as door / trigger scripts in this way need to be examined case-by-case.
Travel Builder provides some "quick
fixes". I personally found that removing the custom transitions was very easy (I just had to move the custom code into the
Travel Builder exit and delete the old scripts) but the case-by-case work took a long time, even with these tools.
Watch out for existing code that sets or tests appearance - a mounted character does not have the standard appearance!
You can take a horse to water - but will it spoil your immersion?
To use any of the horse functions or horse system constants, your script needs the line
#include "x3_inc_horse"
This include file also contains documentation of the horse variables and functions.
You can set options for the horse system in the
OnModuleLoad event script, though this is not mandatory
- see
Module Variables for full details and
Examples for a ready-made template.
To illustrate the general scripting format,
SetLocalInt(GetModule(), "X3_HORSE_ENABLE_ACBOOST", TRUE);
X3_HORSE_ENABLE_ACBOOST specifies that the rider will benefit from the armor class of the horse while mounted.
By default, there is no change to the rider AC. Beware - there is a known exploit with this particular switch - see
Abilities and Combat.
You can specify that the rider can take more damage when mounted. There are two ways of doing this - it doesn't make sense
to enable them both in the same module.
- X3_HORSE_ENABLE_HPBOOST transfers half of the horse's hit points to the rider on mounting. These are temporary
hit points. On dismounting, the horse is undamaged, and the rider loses any temporary hit points remaining.
- X3_ENABLE_MOUNT_DAMAGE does not increase the rider's hit points on mounting. On dismounting, the horse takes
a proportion of the damage sustained while mounted, and the rider is healed by the same amount.
There are many other options in the horse system. In summary
- You can prevent horses from entering certain areas (see Areas).
- By default, a mounted rider gains an increase in speed, but suffers a massive penalty to skills
(Disable Trap, Open Lock, Hide, Move Silently, Pick Pocket, Set Trap and Tumble). This can be disabled.
- Rest while mounted is normally disabled, but can be enabled.
- By default, mounted combat is modified along similar lines to the Player's handbook by means of the Mounted
Combat feat. If this is disabled, mounted combat is the same as normal combat. Either way, a rider gains little
or no combat advantage from riding a horse other than speed, so you may want to use a post-mount script to enhance
combat ability, or enable AC and HP benefits as mentioned above.
- The increase in the maximum number of henchmen to accommodate horses can be disabled. In this event, a horse
is counted like a normal henchman when assessing how many henchmen they can have at any given time.
- Alternatively, an upper limit can be set on the maximum number of henchmen. In this case, horse ownership
will increase the maximum until it reaches the limit, after which both horses and henchmen are counted in the same way when assessing how many are allowed.
- Shapeshifting is normally disabled on mounted creatures, but you can enable it.
- If you're confident you know what you're doing, you can change the way in which mount and dismount are implemented.
There are many area and PC variables here, too. I won't describe this in any detail, but it's included in the Lexicon
for good order. These facilities were originally provided because the mount and dismount animations may not work exactly
as intended in all situations and on all hardware. They were useful for debugging during Beta testing, but may have
limited value now. If you're not too concerned with the fine detail of the animations, you can ignore this completely.
- By default, Paladin mounts are summoned for 24 hours. You can enforce the mount summoning durations as specified
in the Player's Handbook 3.5 edition if you prefer.
- Paladin mounts normally vanish when the player rests, but this can be disabled.
The
OnModuleLoad event script should be x3_mod_def_load(which is not the default), to ensure that mounting looks right on sloping terrain.
The module
OnHeartbeat script x3_mod_def_hb should be added, to support the Mounted Combat feat.
PCs created prior to 1.69 do not have the Mount Actions feat by default.
Fortunately, you can create the riding skin in the
OnClientEnter event.
For new modules only, the
OnClientEnter module event script now defaults to x3_mod_def_enter, which does this for you, using the
HorseAddHorseMenu function.
For existing modules, set the
OnClientEnter script to x3_mod_def_enter, or copy the code into your existing script.
Caveat for PW builders: x3_mod_def_enter may have timing issues in your PW. If so, try Ollebroc's alternative
OnClientEnter
script for PW. There is a
Known Issue with PC skins.
x3_mod_def_enter will try to call a script "x3_mod_pre_enter" before doing anything. You can write a script with that name if you need any pre-processing, such as modifying a custom skin system. It's a good idea to put all your custom
OnClientEnter code into that script. Warning: any code in x3_mod_pre_enter must either execute immediately or have a delay of about 3 seconds, otherwise there can be conflict with x3_mod_def_enter.
If your module strips items from the imported PC, explicitly exclude
INVENTORY_SLOT_CARMOUR, to avoid stripping the riding feats skin. It's also wise to explicitly exclude item tag
x3_it_pchide from inventory processing, as the skin is not equipped instantly by
HorseAddHorseMenu.
WARNING: do not try to disable the PC skin processing or remove the skin. Any Bioware script that references skin variables (including the default AI) will attempt to reinstate a missing skin, but will not necessarily add the Mount Actions correctly.
A player can export their character with a mounted appearance.
If a player imports a mounted character into a module from the Local Vault, the PC will be dismounted immediately, and the horse will be lost.
If a mounted character is imported from the Server Vault, the PC will appear mounted and normal dismount works. However, saddlebag contents are lost.
You can over-ride these defaults by changing the logic in x3_mod_def_enter to call your own function rather than
HorseIfNotDefaultAppearanceChange.
By default, horses are allowed in all areas, and horses can be summoned anywhere.
However, horses can be restricted to external areas only by setting the module switch X3_MOUNTS_EXTERNAL_ONLY in the
OnModuleLoad event.
Alternatively, horses in underground areas can be forbidden by setting X3_MOUNTS_NO_UNDERGROUND.
In addition, there are three options you can set for an area:
X3_NO_HORSES = Horses not allowed in this area. On entry, horses will be left behind.
X3_NO_MOUNTING = Horses may not be ridden in this area. Anyone attempting to do so will be forcibly dismounted.
X3_MOUNT_OK_EXCEPTION = Horses allowed in this area, regardless of the module switch settings.
You can set the area variables on the area objects, but I prefer to set them explicitly in the module
OnModuleLoad script. For example,
SetLocalInt(GetObjectByTag("MyArea"), "X3_NO_HORSES", TRUE);
See
Examples for a ready-made template.
Remember that the switches must be set
before transition into the area, because they are referenced by the default transition script nw_g0_transition.
If you want these rules to apply to
part of an area, see
Triggers.
When horses are forced to remain in the old area, their ownership and assignment status is unaltered (so that they can be remounted later),
but they cease to be party members.
If a placeable or waypoint in the old area has the tag X3_HITCHING_POST, horses will move to this object and switch to STAND_GUARD mode (see
Hitching).
Note that if you place a "no horses" area next to a "no mounting" area, on return to the "no mounting" area the player will be unable
to mount the horse which was left behind. In fact, they won't be able to lead it away, either, because the horse has ceased to be a
party member. The player can work around this by reassigning the horse and leading it away. It is not possible to assign a horse to
a henchman in this situation, so the player has to reassign all the horses to the PC and go to an unrestricted area before reassigning
the horses to henchmen. You may prefer to avoid this situation in your area design, or else write a script that causes horses to rejoin
the party in these circumstances.
If your module sometimes jumps the PC to a different area (for a cutscene, perhaps), you need to consider the impact if the PC is mounted.
To respect area switches, your transition code should mimic nw_g0_transition.
Tilesets generally support horses, but there are sensible exceptions when it comes to confined spaces - for example,
you can ride a horse onto a docked ship, but you can't ride up the narrow steps fore and aft, even if you can do so on foot.
So, setting aside aesthetic considerations, it's not a good idea to allow horses into every area.
If you do, the PC may get stuck and have to dismount, lead the horse elsewhere, or even leave the area and return.
I've seen this happen in the Bordello tile and the narrow cave exit in the Forest tileset - no doubt there are other examples.
You will need to ensure that the entry point for an area is not cluttered with objects (such as hitching posts !)
- remember that mounted henchmen need a lot more space than before.
The more horses and henchmen you have, the bigger issue this becomes.
Remember that the default transition script nw_g0_transition has changed significantly to support horse movement.
The Bioware script nw_g0_tranpconly (which prevents monsters from following the PC across a transition) supports horses in the same way.
The above-mentioned scripts handle any dismounting and hitching required as quickly as possible (see
Hitching). If you prefer
to see full animations, use x3_g0_transition and x3_g0_tranpconly instead.
If you use a custom script in the
OnAreaTransitionClick event for a door, you will need to include the new code from nw_g0_transition. A quick fix for this issue is available on my
Travel Builder page.
Alternatively, you might find it easier to check the party for horses, and stop the transition with suitable feedback or conversation if the
move would violate the switch settings for the new area. This is also easier with the
Travel Builder fix
(but more work than using nw_g0_transition).
Same applies to any script that relocates the PC, including
OnClick and
OnEnter
scripts for triggers used to connect areas, and plot-related jumps to new areas.
A watch point on Area Transition Triggers:
some module builders put a custom transition script in both
OnClick and
OnEnter, because the Bioware
OnClick event is somewhat
unreliable - the player may have to click more than once to launch the transition. However, only the
OnClick event runs nw_g0_transition by default.
There are two standard triggers in the palette which allow you to control the use of horses within an area.
Trigger name |
Function |
Individual Dismount |
Dismount anyone entering the trigger |
Party Dismount |
Dismount and hitch anyone entering and their associates |
The most important difference is that Individual Dismount merely dismounts the rider, whereas Party Dismount removes the horse.
The Individual Dismount trigger does not prevent anyone from getting back on their horse.
In contrast, there is no way a player can sneak a horse into a Party Dismount trigger.
Obviously, you need to ensure that there's no way to ride around the Party Dismount trigger into the sub-area you want to control, which may mean laying down more than one trigger.
In a similar way to area transitions, Party Dismount will hitch the horses if there is a placeable with the tag X3_HITCHING_POST in the area. This
will be fast, with no animation, if the hitching post has the local integer bDismountFast set to 1 (see
Hitching).
The impact on parties versus individuals is largely academic. As a party moves through an Individual Dismount trigger, they are all dismounted,
one by one. When a PC enters a Party Dismount trigger, all horses are removed, though if a henchman happens to be the first to
enter, only their horse is removed - it does exactly what it says.
You can adapt the OnEnter scripts if these defaults are not convenient, of course.
There are times when you want a row of horses, perhaps in stables, or for a battle, or just to organize party horses so that they don't get in the way.
Obviously, this won't happen as long as the henchman AI is active, so the first thing to do is RemoveHenchman if the horse or rider has a master.
You can place a line of horses or riders close together in the toolset, but in game the horses will not spawn in a straight line unless they are at least 2.3m apart.
Spaces for horses in tilesets and placeables are usually 3m wide, so there's no problem if you place a row of horses in these.
However, this takes up a huge amount of space. For example, if your party has 5 horses, you need at least 11.5m to place them in a line (15m, if using placeables such as the market stall or hitching post).
Fortunately, we can cheat. Spawn the horses somewhere else, then apply
EffectCutsceneGhost just long enough for them to walk or jump into the desired position.
With this technique, you can form a row of horses which are (say) 1m apart. Up to 5 horses now look OK on just one 3m hitching post.
Caveats: with horses this close together, you'll definitely want to script Party Mounts rather than individual mount animations. Your neat line will disintegrate when a saved game is loaded, unless you store the horse positions, and move them back to the correct position in the
OnModuleLoad event. You may want to tweak the OnConversation script so that the horses don't turn - they no longer have a decent turning circle.
Technically, the NWN engine avoids collisions using two parameters in appearance.2da - CREPERSPACE and PERSPACE. You might want to customize these parameters to achieve the effects you want - but be aware that this will affect things like whether horses can go through doors or get embedded in walls. Bioware has stated that PERSPACE (Personal Space) is used to prevent wall collisions, whereas CREPERSPACE (Creature Personal Space) is used to prevent creature collisions. This is why horses clip into walls, but can't pass between two guards unless the gap is really large. The actual implementation is more complicated than that, as illustrated by the discussion in this section. It seems that CREPERSPACE is used for spawning, but
EffectCutsceneGhost by-passes collision checking, and no further check is made until the creature tries to move. Evidently, there are also implications for combat; CREPERSPACE seems to be used between blows to keep the opponents apart, but PERSPACE governs the distance at which blows are struck, to allow the opponents get close enough for the kill.
We've seen in earlier sections (
Areas,
Transitions,
Triggers) that riders are forced to dismount and hitch when entering a no-horse area etc.
By default, the horses are dispersed, to keep the doorway clear for your return. This can mean that the party has to look around for their
horses when they come back. Also, the horses can get embedded in walls - they're still mountable, but it doesn't look good.
As we've seen, a better solution is to have a placeable or waypoint in the old area with the tag X3_HITCHING_POST. By default, hitched horses will cluster around this.
I've found it better to use a waypoint, because the cluster looks odd around a hitching post. Also, it's best to place the
waypoint in an open space, well away from doors and walls, so that mounting is easy.
The Bioware scripts will always use the nearest hitching point. In a small village with buildings clustered around a green, it's probably OK to have one hitching point at the centre. However, in a city with lots of doors, you'll need multiple hitching points to ensure that the horses end up close to the last door used (maybe even one hitching point per door).
This starts to become hard work! My personal solution for cities is to have usable hitching posts (see technique in
Stables and Battle Lines)
at a few locations in the city, so that the player can hitch the party's horses neatly before entering buildings. This also works well from
an immersion perspective - the player rides from town to town, but hitches the horses before exploring the many buildings in the city.
The built-in functions which come with
x3_inc_horse are documented in the
Lexicon.
In the toolset, just filter the function list on "horse" to see a complete list.
The actions you'll probably use most often are
HorseSetOwner (to set ownership and, optionally,
assign a horse for mounting),
HorseMount,
HorseDismount and
HorseRemoveOwner.
To determine which horses a creature owns, there are two functions:
HorseGetMyHorse and
HorseGetHorse.
The first is used to determine the assigned horse (if any), whereas the second will return all horses owned.
Remember that any ownership and assignment you establish using
HorseSetOwner can be changed by a player using the radial menu.
HorseGetHasAHorse tells you whether a creature owns a dismounted horse.
HorseGetIsMounted tells you whether a creature is currently mounted.
HorseGetIsAMount tells you if a creature can be mounted (but use
HorseGetCanBeMounted if you want to check that the potential rider is eligible, too).
See
Lexicon for a comprehensive list of functions.
By default, all horses are freely available, but for dramatic purposes, you might want to restrict this - for example,
perhaps the horses have to be purchased at a horse market, or donated by a noble.
This can be achieved by flagging horses as unavailable:
SetLocalInt(oHorse, "X3_HORSE_NOT_RIDEABLE_OWNER", TRUE);
Be careful not to set this particular variable on the horse template or
OnSpawn - otherwise, the horse will become unavailable
again on dismount.
Alternatively, you can use
HorseSetOwner to restrict horses to an NPC (but there is a Known Issue with this
- in practice, a PC can mount a horse which is owned by someone else).
If you establish an owner then set the "not rideable" switch, even the owner cannot use the horse.
The process of assigning horses to a large party can be long-winded if you rely on the radial menu.
You may want to script a loop through PCs and henchmen with calls to
HorseSetOwner - as part of a horse purchase dialog, for example.
In this event, if X3_HORSE_NOT_RIDEABLE_OWNER and/or NPC ownership was set earlier, you need to switch it off in your script.
You'll probably want to modify your henchman dialog to include horse commands - on an individual basis, this is easily achieved with:
HorseMount,
HorseDismount and
HorseRemoveOwner.
There is no standard function for a Party Mount, but you can achieve the same thing with
AssignCommand(oPC, ActionCastSpellAtObject(
SPELL_HORSE_PARTY_MOUNT, HorseGetMyHorse(oPC),
METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
where oPC is the PC object. Note that this only works for PCs, and the target horse must be available to the PC (see
Mount Restrictions).
Remember that only assigned horses will be mounted by henchmen - horses which are merely owned will not be mounted (see
Ownership versus Assignment).
Party dismount is similar:
AssignCommand(oPC, ActionCastSpellAtObject(
SPELL_HORSE_PARTY_DISMOUNT, oPC, METAMAGIC_ANY, TRUE, 0,
PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
The equivalent FEAT constants can't be used here, because characters never acquire these feats - they are empowered by the Mount Actions feat.
By default, an inaccessible horse can still be mounted.
So, even if a horse is on the other side of a ravine, water feature or locked door, radial menu commands and horse functions will allow it to be mounted, effectively teleporting it to the rider's position.
If your area design makes this unavoidable, there are two options.
You can flag the horse as temporarily unavailable:
SetLocalInt(oHorse, "X3_HORSE_NOT_RIDEABLE_OWNER", TRUE);
or you can specify on the module that mounting is to be performed using Actions rather than Delays:
SetLocalInt(GetModule(), "X3_HORSE_ACT_VS_DELAY", TRUE);
The latter option will prevent mounting of inaccessible horses, but it might also result in mount failures in legitimate situations,
because actions can be disrupted by obstacles.
The following looping animations are provided.
HORSE_ANIMATION_MOUNT
HORSE_ANIMATION_DISMOUNT
HORSE_ANIMATION_LOOPING_JOUST_VIOLENT_FALL
HORSE_ANIMATION_LOOPING_JOUST_GLANCE
HORSE_ANIMATION_LOOPING_JOUST_FALL
HORSE_ANIMATION_LOOPING_JOUST_STAB
HORSE_ANIMATION_LOOPING_JOUST_HELMOFF
As always, they're just animations - you need to script anything else that happens.
For example, by itself, a mount animation will play, but then the rider will appear next to the horse again, because you haven't scripted the actual mounting.
The first two are rarely needed, because they're built into functions.
The remainder are for jousting, which isn't built in to standard combat. You need to build a cutscene in which you script the actual jousting, using the functions provided.
The basic jousting stance and the jousting animations are available when you specify the jousting phenotype in either
HorseSetPhenotype or
HorseInstantMount.
There are no lances in the standard palette, but there is a cool new item type "Lance" which you can customize with the Item Wizard.
When you just need a simple mounted NPC, you can do this in the toolset without scripting or creating a horse (see
Getting Started).
For henchmen and major NPCs, you will almost certainly want to create the NPC and horse as separate objects, so that you can have them
mounted and dismounted at appropriate times.
When working with separate objects, it's not necessary to change the NPC phenotype,
tail or appearance in the toolset - the horse feats and functions take care of that.
Existing henchman and NPC scripts should work fine in most cases, though of course you might want to add dialog for mounting and dismounting etc.
By default, combat while mounted follows rules close to the Player's Handbook. This is implemented as the Mounted Combat and Mounted
Archery feats.
The
Mounted Combat feat automatically confers a temporary increase in Armor Class while mounted.
The
Mounted Archery feat automatically reduces the missile weapon attack penalty while mounted from -4 (the default) to -2.
The
Ride skill is pre-requisite for these feats, and affects the Mounted Combat bonus. Of course, the module builder can add custom Ride skill checks, too.
The detail is as follows:
- If the module switch X3_NO_MOUNTED_COMBAT_FEAT is set to TRUE, the Mounted Combat feat has no effect.
- The Mounted Archery feat is not disabled by this switch.
- On level up, a Ride skill of 1 or better is pre-requisite for the Mounted Combat and Mounted Archery feats,
and Mounted Combat is pre-requisite for Mounted Archery.
- These are class feats for Fighters, but they can also be item bonus feats for any class with no pre-requisites
(unless you place a class restriction on the item).
- The Mounted Combat feat automatically performs a Ride skill check while mounted once per round, to increase the
rider's Armor Class by 1 point for every 5 of the check result.
- For example, a Ride skill of 5 with the Mounted Combat feat will increase Armor Class by (d20+5-10)/5 to give
a bonus in the range 0 to +3.
- This is done without feedback, to maintain immersion, but the AC on the character sheet will fluctuate over time.
- The module needs the default OnHeartbeat script x3_mod_def_hb to support the Mounted Combat feat.
If you want to add these feats to an item by scripting, you'll need the following constants, which are not defined in the toolset:
Feat |
IP_CONST value |
Mount Actions |
40 |
Mounted Combat |
41 |
Mounted Archery |
42 |
Normally, when an NPC dies, the corpse appearance includes the horse.
However, when a mounted PC respawns, their horse is not resurrected - by default, it leaves a lootable corpse.
If X3_HORSE_NO_CORPSES is set on the module, the horse is destroyed, and any saddlebag items are dropped on the ground.
SetLocalInt(GetModule(), "X3_HORSE_NO_CORPSES", TRUE);
Mounted NPCs can be resurrected, in which case the horse normally revives, too.
One exception is that henchmen running the original NWN scripts behave like respawning PCs.
If you want henchman horses to survive, use the X2 henchman scripts instead. These scripts pass ownership of the horse to the PC when the henchman dies.
There are some known issues with loading saved games.
Save/load while mounted with a ranged weapon equipped can result in a permanent attack penalty until you rest or use Restoration.
If you save while mounted then reload, whenever your character is mounted it requires more space than normal. For example, suddenly
you can't ride between two guards to reach a door transition if they're too close together.
This is fixed by saving while dismounted and reloading.
The impact can be greatly reduced by changing appearance.2da to set CREPERSPACE for the mounted race-gender appearances
(e.g. Mounted Human, Male) to the same value as a dismounted horse (0.9).
See also
Stables and Battle Lines for a similar (but different) issue affecting dismounted horses.
If a dismissed henchman or independent NPC owns a horse, it may fail to spawn when a saved game is loaded, then suddenly appear much later.
This is due to a long-standing bug in the game engine - associates of an unattached NPC are loaded with no location, so they don't appear
in game until they run their heartbeat script (which may not be for a long time).
Since the module builder knows in advance which NPCs might own horses, a workaround is to change the
OnModuleLoad script to check whether any of these NPCs own horses, and, if so, action the horses to jump to the owner (setting the AI level higher if necessary). See
Demo Module Scripts for an example.
The problem is corrected if you rehire the henchman (though you may have to save and reload for this to take effect immediately).
If a PC's appearance is changed using Polymorph, they may have a random appearance after save/load.
If
SetCreatureAppearanceType is used, they may revert to their default appearance after save/load.
You can work round both issues by storing the intended appearance and re-applying it in the
OnClientEnter event.
Neither issue occurs if the PC was mounted when the game was saved.
There are a number of options for customising horses and horse actions (see
Lexicon - Horse Variables for details).
They are intended to be set as local variables on the horse
blueprint. They can be changed temporarily in a script, but on dismount they are reset to the original template values.
Bear in mind that the same applies to any other local variables you set on a horse.
Of course, you can use the OnSpawn script on the template to set the variables, so that the initial values are restored on
dismount. For example :
SetLocalInt(OBJECT_SELF, "bX3_HAS_SADDLEBAGS", TRUE);
To avoid creating lots of custom templates, you might prefer to over ride the standard
OnSpawn script nw_ac_ch9
(see
Examples for a ready-made template). You can use
HorseGetIsAMount to ensure that the variables are only set for horses,
avoiding any risk of impacting other associates who use the same standard script. This technique will set the same variable list on all
horses in your module, which could save a lot of time.
If you need to preserve the
current settings, you can store the variable on the rider's skin in the
pre-mount script, and reset
it on the horse in the post-dismount script (which runs after
OnSpawn).
In summary:
- You can specify scripts that run before or after mounting / dismounting. This gives you almost total control over how the horse
system works (see Script Hooks).
- Alternatively, you can supply your own scripts for assigning, mounting and dismounting.
- You can prevent a horse from being used by anyone (including the owner) by setting the local integer X3_HORSE_NOT_RIDEABLE_OWNER to 1.
- You can disable the mount animations for a particular horse by setting local integer X3_NO_MOUNT_ANIMATE to 1.
- To disable mount animations in general, it's easier to use the parameters in the horse functions (though these do not affect the radial menus).
- You can prevent racial groups from using a particular horse by setting X3_HORSE_RESTRICT_race.
- You can specify that a horse is a henchman of an NPC by setting the local string X3_HORSE_OWNER_TAG on the horse to the NPC's tag.
- You can specify that a horse is still mountable even when it's a henchman by setting local integer bX3_IS_MOUNT to 1.
- If you create a custom creature which can be mounted (which involves modeling beyond the scope of this Guide) you need to set a
local integer on the creature bX3_IS_MOUNT to 1 for the riding system to work.
- If necessary, for custom creatures which are very different from horses, you can specify non-standard appearances, footsteps and
animation speeds.
- I suggest that you don't alter the animation speeds for existing blueprints - Bioware has gone to a lot of trouble to achieve
the optimum visual impact (though you may still see "blips" which are very difficult to eradicate entirely).
- Custom races can be introduced - there are a number of parameters for this, which are set on the rider rather than the horse.
You can specify a script to run before or after mounting / dismounting by setting local strings on the horse (X3_HORSE_PREMOUNT_SCRIPT,
X3_HORSE_POSTMOUNT_SCRIPT, X3_HORSE_PREDISMOUNT_SCRIPT, X3_HORSE_POSTDISMOUNT_SCRIPT).
In all cases, the script is run on the rider, so your script will usually start with
object oRider = OBJECT_SELF;
In all cases except pre-dismount, you can refer to the horse which is being mounted or dismounted using
object oHorse = HorseGetMyHorse(oRider);
Pre-dismount, the horse does not exist.
Post-dismount, delay commands to allow dismounting to finish cleanly (see HorseDismount for timing).
See Example.
If you need OnClientEnter event processing, you can now define a script named x3_mod_pre_enter. This runs prior to the Bioware skin processing
in x3_mod_def_enter. One application is to equip a custom skin, but it's a useful place to put all custom content. Commands in this script
either need to execute and complete immediately, or be delayed by at least 3 seconds, to avoid conflict with the skin processing.
The easiest way to change the radial menu scripts is to edit x3_s3_horse, taking care to respect the integrity of the internal horse system variables. It's easily broken!
A less useful option is that you can completely replace the Bioware radial menu scripts for a specific horse
(X3_HORSE_SCRIPT_ASSIGN, X3_HORSE_SCRIPT_MOUNT, X3_HORSE_SCRIPT_DISMOUNT). Problem is, these do not modify the Party Mount / Dismount
radial menu.
The horse functions (which are called by the radial menu script x3_s3_horse) are in x3_inc_horse. This is not so easy to change.
It's an include file, which is embedded in many Bioware scripts. Rebuilding your module with a new version does not recompile the Bioware scripts,
so changes can have bizarre consequences. My recommended approach is to make a new function, based on the code in x3_inc_horse, then call that
new function from custom scripts and x3_inc_horse. Alternatively, you can make local copies of all the Bioware scripts that use x3_inc_horse,
change x3_inc_horse, and rebuild - but I can't guarantee that my provisional list of the Modified Bioware Scripts is complete.
Saddlebags are implemented as an inventory on the horse.
By default, they are disabled. The NWN inventory system for characters is already generous, so adding more capacity in the form of saddlebags could unbalance existing modules.
To enable saddlebags, include the following line in the
OnModuleLoad event script:
SetLocalInt(GetModule(), "X3_HORSE_ENABLE_SADDLEBAGS", TRUE);
Ensure conversation X3_DLG_SADDLEBAG is set on your horse
templates. This is the default for the standard Bioware horses, though it does
nothing unless saddlebags are enabled.
You also need to set the integer variable bX3_HAS_SADDLEBAGS to 1 on each template, or include the following line in the
OnSpawn script:
SetLocalInt(OBJECT_SELF, "bX3_HAS_SADDLEBAGS", TRUE);
This switch is pre-set on a few standard templates which have saddlebags in their appearance, but it can be set on any horse,
regardless of the artwork.
Now, when a PC clicks on a horse which they own, the horse's inventory opens immediately.
There is no actual dialog (though of course you can substitute your own conversation, if you want a talking horse).
Remember, the dialog must be set on the horse template, not on an instance of the horse, because the horse instance is recreated from the template on dismount.
It is not possible for a PC to view the inventory of a horse which is owned by an associate.
To work round this, the associate's horse must first be assigned to the PC.
By default, when the horse is mounted (and no longer exists as an object), the saddlebag contents are stored on a database, and
restored on dismount, automatically.
You can over-ride the default database name in the
OnModuleLoad event script:
SetLocalString(GetModule(), "X3_SADDLEBAG_DATABASE", "EnterDatabaseNameHere");
This storage technique uses the standard NWN databases, which can be slow.
As a faster alternative, if you place a waypoint with the tag
X3_HORSE_INVENTORY_STORAGE in an inaccessible area, saddlebags will be stored as containers nearby.
You only need one such waypoint per module, because each container is uniquely named to reflect the rider and the horse which owns it.
Nightmares (evil horses) can be found in the creature palette under Monsters > Planar > Other.
There are three models in the toolset. Only the second two ("saddle" and "barding" variants) are set up as horses you can ride.
The standard Nightmare x3_nightmare002 (the one with no saddle or barding) is set up as a monster.
It can be ridden, if you change its faction to something other than Hostile.
It won't follow its master, unless you give it the associate script set.
It will occasionally spawn with random treasure in its "saddlebags", unless X2_L_NOTREASURE is set to 1 on the module or the Nightmare.
This section is based on Ollebroc's findings (see also
Examples) and notes in the Bioware scripts.
The specific changes needed for a PW will depend on how it's set up in the first place, of course.
- x3_mod_def_enter may have timing issues. If so, try the alternative OnClientEnter script for PW.
- The standard data base functions for horses are enabled by setting the switch X3_ENABLE_MOUNT_DB in the OnModuleLoad event.
- This will automatically enable data base save and load in all the key horse functions.
- The functions may need to be changed for performance reasons, or to comply with the data base approach used for the particular PW.
- As previously noted, PCs can have an item called "PC Properties" in their creature skin slot. So, existing data base export routines should ignore this slot, and the data base import may need to ignore creature skin items, too. The PC skin should be managed as noted in the section Module Events and Imported PCs. There is a Known Issue with loading PC skins from the data base.
- If the PW allows a player to log on to a "no horse" area, it's possible for the player to import a horse, unless HORSE_SupportRestoreHenchmanFromDatabase is changed to apply the same checks as NW_G0_Transition. The horse could then be hitched at a standard position in a legal area. Depending on your set up, the call to HorseIfNotDefaultAppearanceChange in x3_mod_def_enter may not be sufficient.
- The one second delay in x3_mod_def_enter between HorseReloadFromDatabase and HorseIfNotDefaultAppearanceChange may or may not be sufficient to prevent the PC turning into a dwarf (actually I think it's a gnome, as if that mattered) owing to the data based being deleted before it's loaded.
- The delay around HorseHitchHorse in nw_g0_transition may be insufficient.
- Changes may be needed to ensure that a player logging on with a horse receives the right bonuses.
Did you ever want to create a baby dragon, or a giant human with weapons to scale? Now you can!
The trick is to create an
invisible creature on the right scale, then give it a
tail with the right appearance.
How does this work? If you've read the rest of this Guide, you'll understand - if not, just accept that it does!
Let's start by making a giant Aribeth.
In the toolset, make a creature which has all the properties you want except appearance.
I started with a Waitress, but it could be anything.
Change the appearance to Invisible_Human_Female_200.
Now change the tail to "NWN, Aribeth", and voila! In game, this Aribeth will appear to be 12 feet tall.
(The tail model name has changed slightly since the screen shot below was taken).
Don't worry if the creature appears to be the wrong size in the toolset. It works fine in-game.
If you equip weapons and shields, they will scale correctly.
There are also some new wings that look like backpacks, scabbards, etc.
So what did we just do?
Invisible_Human_Female_200 is a model that scales an invisible human female to 200% of normal size.
When you add a tail, NWN automatically scales it to match the model.
Note that there is a set of models for each race and gender,
with a suffix in the range 010 to 200, corresponding to scaling in 10% increments between 10% (very small) and 200% (very large).
Choose a race and gender which is close in size to the humanoid you want to scale - in this case, human.
The process for dragons is very similar - we'll illustrate this by making a baby dragon.
Make a copy of an Adult Green Dragon.
Set the appearance to Invisible_Dragon_30 to adjust the size to 30%.
Note that the portrait changes - just reset it to a Green Dragon.
Now set the tail to Dragon, Green.
You now have a dragon which is slightly smaller than a human.
Notice that the default armor class has changed - smaller creatures are harder to hit.
In other respects, it still has the properties of an adult, including 20 levels in the Dragon class, special abilities and combat feats - so you might want to adjust that.
Horses are automatically scaled to pony size when mounted by the smaller races.
You can also make a pony which is smaller from the outset.
For example, the appearance Horse_Invis_Halfling with a horse tail makes a pony you can ride.
(Technically, horses are model type F, so you have to use an invisible human appearance to scale a horse tail).
By default, the pony will scale back to horse size if mounted
by a human, but you can stop larger races riding the pony by setting the X3_HORSE_RESTRICT_race switches (see
Horse Variables).
You can stop smaller races riding regular horses in the same way.
For creatures which are not approximately humanoid, dragons or horses, you need to use the appearance Invisible_CreatureS_nnn,
where nnn = 010 to 200 for 10% to 200% as before.
Technically, these are creatures which have model type S in appearance.2da.
For very large creatures with model type L, Invisible_CreatureL_nnn must be used instead.
For example, to make a giant cow, all you have to do is edit a copy of the standard cow in the toolset, change the appearance to Invisible_CreatureS_200,
and add the tail Cow.
Tails are provided for almost all of the standard creatures, but if you don't find the tail you need,
it's easy to make one, as explained in the next section.
Horses are a special case - they are designed to work with humanoid riders (model type F), so you need to use the humanoid
invisible appearances to scale them.
Making custom models is beyond the scope of this Guide, so I'll just illustrate the principle here.
Assuming we already have a custom creature appearance, it's very easy to make it into a tail we can scale.
Look for the model name in appearance.2da - for example, it might be c_custom_monster, model type S.
Add a line to tailmodel.2da with the model name and envmap used in appearance.2da:
488 "Wyvern, Juvenile" c_wyvern_j ****
489 "Wyvern, Young" c_wyvern_y ****
490 "Custom Monster" c_custom_monster ****
You'll need to put the new tailmodel.2da in a hakpak and add this to your module's custom content in the usual way.
Now all you have to do is edit the custom monster in the toolset, change the appearance to Invisible_Creature_S200, add the tail Custom Monster,
and you have a giant monster!
Remember to use the appropriate invisible appearance - humanoid for model type F, creatureS for model type S, creatureL for model type L, and dragon for dragons.
- There is no way to scale part-based creatures, such as standard race appearances. So, for example, while you can have a tiny Old Man or a giant Innkeeper, you can't scale a human with your favourite head and armor.
- The scaled creature will appear to be completely rigid in game unless the model type in appearance.2da is the same for both the invisible creature and the tail model. So, invisible humanoid (model type F) only animates correctly with tails of model type F, and so on. For example, you might think a Minotaur is humanoid, but it's actually model type L, so you have to use Invisible_Creature_Lxxx.
- The highlight on a creature is not scaled. This is the "glow" which appears when you position the cursor over the creature or press the Tab key. So, for example, a baby dragon still has a normal-sized dragon highlight. The highlight can be switched off by setting "Targetable" to 0 in the appearance.2da line for the invisible appearance. It's worth doing this for appearances scaled to less than 100% - there's no known downside to this tweak (in fact Bioware already implemented it for horses).
- Only single-handed weapons are supported. Two-handed weapons and pole arms don't have the right tail animations, so the weapons move as they should, but the creature doesn't move with them.
- Only dragons get an automatic AC modifier for size. The reason is that SIZECATEGORY in the default appearance.2da for invisible humanoids and creatures is set to 3 throughout - these generic appearances can be used to scale creatures of various initial sizes, so it wouldn't make sense to anticipate the final size of the scaled creature. The AC can be modified manually in the toolset. In general, scaling changes appearance only, so you might want to adjust other properties to sensible values.
- The built-in size range is 10% to 200% in steps of 10%. If you need a custom size, you can copy an invisible appearance which is close to what you need to a new line in appearance.2da, then change the Wing/Tail Scale parameter to the value you need. You might need to tweak a few other fields, such as Perspace, if your custom size is radically different from the original line.
- Humanoids which aren't close in size to any of the invisible races provided may require fine-tuning. They should scale in appearance with any of the humanoid invisible appearances, but if you equip a weapon or a shield, it may be wrongly positioned in game. The reason is that it's actually the invisible creature that wields the items. One way to deal with this is to copy an invisible humanoid appearance which fits quite well to a new line in appearance.2da, then adjust the Wing/Tail Scale parameter until the weapons look right in game.
- Custom Mounts can't easily be made this way. The reason is that the standard models used for mounted appearances only work with model type F mounts. So, you can configure a humanoid - Commoner, Male say - as a custom mount, which, if ridden, gives a convincing piggy-back, but you can't ride model type S, which applies to almost every creature you might conceivably want to ride except horses. If you want a custom mount, you need to provide a model type F in appearance.2da and then add it as a tail to tailmodel.2da.
1.69 allows us to add up to 10 custom Player feats and 10 custom DM feats.
These feats appear under the Special Abilities radial menu, so they can be loaded into quick slots in the usual way.
They can be used as "hot buttons" to launch a menu conversation or other action quickly.
The advantage over Unique Power items is that there's no spell-casting animation or delay when they're used.
One handy application is to give a DM a horse action menu - there are many others.
As an example, let's make a custom menu using Player Tool 1.
- We want this feat to target the player only, so that the target cursor is suppressed:
- In feat.2da, find PLAYER_TOOL_01 (line 1106) and change TARGET_SELF from **** to 1.
- In spells.2da, find PLAYER_TOOL_01 (line 830) and change TargetType from 0x7F to 0x01.
- Optionally,
- We can make a custom icon for the feat by over-riding ife_x3_pltool01.tga.
- We can change the feat name by specifying a custom tlk table entry in feat.2da.
- To give the feat to the player,
- The example below adds some code to x3_mod_pre_enter that updates the PC skin, exactly like the horse system.
- Alternatively, you can just add a bonus feat to an item (or use ActionCastSpell).
- Finally, we add some content to the script x3_pl_tool01 which executes when the feat is activated.
Target Type |
Bit Switch |
self |
0x01 |
creature |
0x02 |
area/ground |
0x03 |
items |
0x04 |
doors |
0x10 |
placeables |
0x20 |
trap triggers |
0x40 |
Examples :
// x3_mod_pre_enter
void main()
{
object oPC = GetEnteringObject();
object oSkin;
// Create or update creature skin as necessary on PC for Player Tool 01 feat.
// This relies on the Bioware code to equip the skin if necessary, owing to timing issues.
oSkin = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oPC);
if(!GetIsObjectValid(oSkin))
{
oSkin = CreateItemOnObject("x3_it_pchide", oPC);
}
if(!GetHasFeat(FEAT_PLAYER_TOOL_01, oPC))
{
AddItemProperty(DURATION_TYPE_PERMANENT, ItemPropertyBonusFeat(IP_CONST_FEAT_PLAYER_TOOL_01), oSkin);
}
}
//::///////////////////////////////////////////////
//:: Player Tool 1 Instant Feat
//:: x3_pl_tool01
//:: Copyright (c) 2007 Bioware Corp.
//:://////////////////////////////////////////////
/*
This is a blank feat script for use with the
10 Player instant feats provided in NWN v1.69.
Look up feats.2da, spells.2da and iprp_feats.2da
*/
//:://////////////////////////////////////////////
//:: Created By: Brian Chung
//:: Created On: 2007-12-05
//:://////////////////////////////////////////////
// Customized to launch Enigma Island PC menu.
void main()
{
object oPC = OBJECT_SELF;
// object oTarget = GetSpellTargetObject();
// location lTarget = GetSpellTargetLocation() ;
// SendMessageToPC(oUser, "Player Tool 01 activated.");
if (IsInConversation(oPC))
{
SendMessageToPC(oPC, "My Player Tool is disabled when I'm in conversation.");
return;
}
AssignCommand(oPC, ActionStartConversation(oPC, "bh_menu", FALSE, FALSE));
}
Some players experience lag when a horse first appears. This is often fixed by an nwnplayer.ini setting under [Game Options] - add the line
"Max Memory Usage=32" to increase to 32Mb. If you have it, 64Mb is even better.
If a rider is blocked when running to their horse, the mount animation plays at that point, even though there's no horse there.
When mounting and dismounting, you will inevitably see a very slight discontinuity in the animation at the moment when the old horse is destroyed.
Mounting while wearing a robe results in the rider visibly dipping into the horse for an instant.
Clipping may occur when both parties in a conversation are mounted, or when several henchmen are mounted. Horses and riders can also clip walls to a considerable extent. There is a delicate balance between preventing clipping altogether, and allowing the horses to move around. Increasing the distance at which henchmen follow you when mounted, and placing hitching posts for forced dismounts, help somewhat.
Equipped shields and cloaks are not visible in the standard mounted phenotypes. This is intentional, to prevent clipping. It's purely visual, with no impact on Armor Class. Shields are visible in the joust phenotypes.
If a cloak is equipped then unequipped while mounted, it appears on the rider when they dismount, even though it's still unequipped in inventory.
If a character has a tail, it disappears when mounted, because the "horse" is actually a tail.
Horses are demanding on the engine, like part-based creatures. This is only an issue on older, low-spec machines, which may grind to a halt if confronted with a large herd of horses.
A PC can mount a horse which is owned by another PC, or an NPC who is not in the party.
By default, horses attack on sight and bash locks (they'd disarm traps, too, if they had the skill). It is reported that a one-second delay is required in the OnSpawn script before executing the scripts x0_d1_hen_defnd, nw_ch_lock_off and x2_ch_trap_off to correct this.
The HP and AC boost options don't work for Paladin mounts (see Fix for Paladin Mounts).
Imported PCs are reset to from large to normal phenotype (see HorseChangeToDefault).
Henchmen are not allowed to recover their horse if it is dismissed in a no-mount area (either intentionally or by entering an adjacent no-horse area). The horse has to be assigned to the player and led to an area where mounting is permitted.
If you save while mounted and reload, your mounted character requires more space until you dismount, save, and reload (see Loading Saved Games).
If you change a PC's appearance (by script or Polymorph) it may be corrupted by save / load (see Loading Saved Games).
Save/load while mounted with a ranged weapon equipped can result in a permanent attack penalty until you rest or use Restoration.
In PW, the PC skin is destroyed almost immediately after data base load, and is replaced with a new skin. This happens when the ELC and ILR server options are enabled (see PC Creature Skins).
Party Mount doesn't always work first time. Any henchman who joined the party after the PC acquired their assigned horse will not mount. Fortunately, this issue is self-correcting; a second attempt will work. The reason is that once the PC is mounted, they have no assigned horse; once they dismount, their horse joins the party after the henchmen, so in both cases the problem goes away.
A Restoration spell while mounted will remove both the speed increase and the skill penalties (see Fix for Restoration Bug).
Certain spells (e.g. Whirlwind) may look silly when mounted. It seems to be quite safe to block such spells (in the spell hook or dedicated spell script) by testing HorseGetIsMounted and giving the player feedback that the spell has failed. The Bioware scripts already do this for attempts to polymorph a rider (though EffectPolymorph on a rider appears to have no adverse consequences).
Summoning a creature using a spell prevents mounting (or re-mounting, if already mounted) until you rest. Other forms of summoning (e.g. animal companions) work normally.
Dominating a horse is allowed. If it's flagged as "not rideable", the player can lead it away, but can't mount or assign it, which doesn't make sense. If you have a horse market, this can create an exploit; the player can dominate a horse, then keep selling it to the vendor, because the horse simply follows the player again as long as it's dominated (see Fix for Domination).
Resting henchmen are not subject to the same rules as PCs. So, if a PC is dismounted and a henchman is mounted, resting is allowed, with full benefit to both. Also, a resting henchman's Paladin Mount is not unsummoned.
The speed increase when mounted is not 99%, as specified in the Bioware documentation, but 50%. This is probably only an issue for monks at the highest level, who are better off on foot anyway.
The horse radial menu is duplicated under each class for a multiclass PC, by design.
If you make mounted creatures in the toolset, you may find that you can't change the head once you save the template with the phenotype and tail set, or that the head changes when you select the mounted appearance. The most robust approach is to make a normal dismounted creature in the toolset, then use HorseMount or HorseInstantMount in game to mount it. If you need to work in the toolset only, it's best to select the mounted appearance first, change the head, and finally set the phenotype and tail. If you need to change the head later, you have to switch back to the dismounted phenotype and remove the tail first.
When scaling a creature in the toolset, on selecting the tail, the scaling sometimes appears to be the opposite of what is required (so, a dragon scaled to 200% appears to be tiny; a halfling mount appears to be huge). Nevertheless, the models work fine in the game.
If horses are placed close together in the toolset, they will move apart when spawned in game (see Stables and Battle Lines).
The default module event scripts generated by the toolset are out-of-date. The OnModuleLoad script should be x3_mod_def_load (to fix an issue with mounting on sloping terrain). The OnHeartbeat script must be x3_mod_def_hb, for the Mounted Combat feat to work.
Axe Murderer reports that there are about 50 Bioware scripts which perform transitions without handling horses correctly, e.g. Stone of Recall and secret doors (see Fix for Transition Bugs).
The Mount Actions feat is all a PC or NPC needs to use horses. Although FEAT constants are defined for Assign, Mount, Dismount, Party Mount and Party Dismount, they are not fully implemented as feats in the usual sense - when scripting, the equivalent functions (such as HorseSetOwner, HorseMount and HorseDismount) should be used instead. Party Mount and Party Dismount can be invoked as spells by a PC using the equivalent SPELL constants. It is possible to assign these feats to a character in the toolset, and you can even assign Mount Actions to a creature which has no suitable model for riding, but this is not recommended!
The Summon Mount feat doesn't actually assign the mount to the Paladin. See HorseGetMyHorse for a general example of how to make code robust enough to handle the "no horse assigned" issue.
The HorseCreateHorse nTail parameter doesn't work.
The optional AC Boost feature doesn't react if armor is changed while mounted.
There is a save/load issue which affects dismissed henchmen and independent NPCs who own horses (see Loading Saved Games).
There is a save/load issue which affects horses which are close to one another (see Stables and Battle Lines).
There are some specific issues for Persistent Worlds, as you'd expect.
The DM does not have horse feats. As far as I know, it's not possible to make the radial menus work for DMs, even by scripting.
Barry_1066 has verified that you can enable the DM and DM-possessed creatures to mount and dismount horses using the standard
horse functions in conversation. He used a Unique Power item to drive this and GetIsDM to stop players using it.
Presumably, you could also use one of the new DM Tool feats or a chat menu.
It has been reported that the DM client does not show class abilities correctly on the radial menu. This may have been the result
of a customisation to work around the issue above. The symptom is that one of the classes only shows the horse menu and not the
class abilities. A workaround is to assign Commoner as the first class.
Some simple examples are provided as a demo module and erf file on the
NWVault.
Key features include
- OnModuleLoad and horse OnSpawn scripts with templates for all the optional switches and variables.
- Basic conversations which allow the PC, a henchman, or the whole party to interact with horses.
- An example of a custom menu using the new chat functions in 1.69.
The chat menu is just a bit of fun - you can achieve the same thing by enabling one of the new user-defined feats on the radial
menu (or with a Unique Power item, if you don't mind the spell-casting animation).
These examples will need to be polished for any real module, but all the basics are there.
Install the .mod file in the nwn modules directory and the .erf file in the nwn erf directory.
On entry, notice that you can't do anything with the horses yet.
Talk to the Horse Dealer to make the horses available.
Ask the two henchmen nearby to join you.
Talk to a henchman and try out the conversation - one of commands is issued to the whole party, while the second set is issued to the henchman you're addressing.
Context-sensitive conditional scripts are used to limit the conversation to valid options.
Notice that you can mount in conversation before a horse is assigned (whereas the radial menu requires horses to be assigned to henchmen before they can mount).
Try taking the horses through the two doors to the north. To the north-east, you are forced to dismount; to the north-west, your horses are hitched in the original area before you enter the new area.
Enter the module as a 5th level Paladin if you want to try out summoning a Paladin mount.
What if you had no henchmen?
Well, in 1.69, you can still use the conversation, thanks to the new chat functions in 1.69.
Right-click on a quickslot and enter the chat command
HORSE as a custom text macro.
Now click on the quickslot. The PC can now use the conversation.
Note that the default follow distance of 6 feet is increased to 12 feet if the leader is mounted and the follower is a horse or mounted henchmen.
Try using the conversations and radial menus in different combinations to create hybrid situations in which some of the party are mounted,
while others are not.
To see the save/load bug, give a henchman a horse, dismiss them via the radial menu, save and load. The horse has vanished!
There is a fix for this in the script bhh_mod_def_load, but it's commented out, because it's module-specific.
You can activate the fix and rebuild the module if you want to see how this works.
You may want to use the demo as a sandpit to try out some of the advanced features mentioned in this Guide, such as enabling saddlebags.
In the erf file, the conversation
bhh_horse_menu is a skeleton for henchman dialog and the chat menu.
bhh_horse_sell is a simple conversation that frees up all the horses in the area for riding. In a real module, you'd want to check that
the PC has sufficient gold, and take their money. The technique used here works fine for single player. In multiplayer, once one PC has
bought the horses, any PC can ride them. If you want to establish individual ownership, you can use
HorseSetOwner instead - see example
in the script comments.
Main Scripts |
|
bhh_mod_def_load |
This script is to be used in the OnModuleLoad event. It contains the standard Bioware code,
plus templates for all the module and area switches for the horse system. A few examples have been enabled.
There is also a block of code (commented out) that provides an example of how to ensure that horses belonging
to unattached NPCs spawn correctly when a saved game is loaded. This must be modified to refer explicitly to
any NPC in your module who might be in this situation (such as henchmen and horse dealers). |
nw_ch_ac9 |
This is an over-ride to the Bioware OnSpawn script for associates. It contains the standard code, plus templates
for all the horse variables. If you enable the variables, they will only be applied to horses, not to other associates.
The reason for over-riding the standard script is that this allows you to set defaults for your module without making
custom templates for the horses you wish to use. |
bhh_assign |
Assigns an available horse to the caller. If there is a choice, the horse with the most hit points is selected.
Horses which have no owner, and any spare horses the PC may have, are regarded as "available" as long as they
can be seen. The script will recover a currently assigned horse that has ceased to be a henchman for some reason,
even in a "no mount" area. |
bhh_assign_all |
Assigns an available horse to anyone in the party who doesn't have one, following the same rules. Spare horses are
assigned to the PC. |
bhh_mount |
Orders the caller to mount if they can. A horse is assigned, if necessary. |
bhh_mount_all |
Orders the party to mount if they can. Horses are assigned, if necessary. |
bhh_dismount |
Orders the caller to dismount if they can. |
bhh_dismount_all |
Orders the party to dismount if they can. |
bhh_release |
Orders the caller to release any dismounted horses they may have. |
bhh_release_all |
Orders the party to release any dismounted horses they may have. |
bhh_chat |
This script is to be used in the module OnPlayerChat event. It illustrates how chat can be intercepted to trigger a custom menu. |
bhh_lock_all |
This script is to be used in an area OnEnter event to make all horses in the area unavailable. |
bhh_unlock_all |
Used in conversation to make all horses available. |
Conditional Scripts |
|
bhhc_no_horse |
True if the caller has no horse, or if a better horse is available. |
bhhc_any_nohorse |
True if anyone in the party has no horse, or a better horse is available. |
bhhc_has_horse |
True if the caller has a dismounted horse. |
bhhc_any_horse |
True if anyone in the party has a dismounted horse. |
bhhc_can_mount |
True if the caller can mount (i.e. is dismounted but has, or could have, an assigned horse). |
bhhc_any_mount |
True if anyone in the party can mount. |
bhhc_mounted |
True if the caller is mounted. |
bhhc_any_mounted |
True if anyone in the party is mounted. |
Axe Murderer provided the following simple example of a conversation Action Taken script for a horse market :
// ActionTaken script -- buy a horse
//::///////////////////////////////////////////////////
#include "x3_inc_horse"
const string RESREF_OF_HORSE = "x3_horse001";
const int COST_OF_HORSE = 5;
void main()
{
object oPC = GetPCSpeaker();
if ( !GetIsPC( oPC) || (GetGold( oPC) < COST_OF_HORSE))
{
FloatingTextStringOnCreature( "Too Expensive", oPC);
SendMessageToPC( oPC, "You cannot afford the horse.");
return;
}
object oHorse = HorseCreateHorse( RESREF_OF_HORSE, GetLocation( oPC), oPC);
if( !GetIsObjectValid( oHorse))
{
SendMessageToPC( oPC, "Unable to create the horse.");
return;
}
TakeGold( COST_OF_HORSE, oPC);
}
This can be refined by routine scripting. For example, you might want to replace the floating text and feedback messages
with dialog, using conditional scripts. You could align the horse price with D&D rules based on the number of hit points
it has, and so on.
In this example, the horse is created when purchased. If you wanted to see the horses prior to purchase, you could use the
script
bhh_lock_all to make the horses unavailable on entry to the area. You'd need to add some dialog to establish which
horse oHorse should refer to, then replace the HorseCreateHorse line with
DeleteLocalInt(oHorse, "X3_HORSE_NOT_RIDEABLE_OWNER");
Many Persistent World authors find that the Bioware script x3_mod_def_enter doesn't work for PW, owing to timing issues.
Ollebroc provided the following example on an
OnClientEnter script that does work.
You might have to tweak it a bit for your PW, though.
It includes custom code for Palemasters and Blackguards that you may want to omit.
#include "nw_i0_plot"
#include "x3_inc_horse"
#include "x2_inc_itemprop"
//Add vulnerable properties to full leveled PMs
void AddPMSkin(object pc);
//If mounted on entry set bonuses, else fix appearance.
void CheckHorseMount(object pc);
///////////////////////////////////////////////////////////////////////////////
void main()
{
object pc = GetEnteringObject();
//ExecuteScript("x3_mod_pre_enter", OBJECT_SELF); Override for other skin systems
//Give the player skin to those that need horse feats
if ((GetIsPC(pc) || GetIsDM(pc)) && !GetHasFeat(FEAT_HORSE_MENU, pc))
{ // add horse menu
HorseAddHorseMenu(pc);
} // add horse menu
// Check the database
if (GetLocalInt(GetModule(), "X3_ENABLE_MOUNT_DB"))
{ // restore PC horse status from database
DelayCommand(5.0, HorseReloadFromDatabase(pc, X3_HORSE_DATABASE));
DelayCommand(7.0, CheckHorseMount(pc));
} // restore PC horse status from database
// pre-cache horse animations for player as attaching a tail to the model
DelayCommand(8.0, HorsePreloadAnimations(pc));
//Give PMs their bonuses on their skin
if (GetLevelByClass(CLASS_TYPE_PALEMASTER, pc) > 9)
{
DelayCommand(9.0, AddPMSkin(pc));
}
/////Give Blackguard Player Horse Summon Tool if it doesn't exit///////////////
if (GetLevelByClass(CLASS_TYPE_BLACKGUARD, pc) > 0)
{
if (GetItemPossessedBy(pc, "BGHorseSummon") == OBJECT_INVALID)
{
CreateItemOnObject("bghorsesummon", pc);
}
}
///////////////////////////////////////////////////////////////////////////////
}
//Add divine and bludgeon vulnerability to the skin of full leveled PMs
void AddPMSkin(object oPC)
{
//First make sure they have a skin
object oSkin = SKIN_SupportGetSkin(oPC);
int nDivine = IP_CONST_DAMAGETYPE_DIVINE;
int nVulDivine = IP_CONST_DAMAGEVULNERABILITY_25_PERCENT;
int nVulBludge = IP_CONST_DAMAGEVULNERABILITY_10_PERCENT;
int nBludge = IP_CONST_DAMAGETYPE_BLUDGEONING;
itemproperty ipDivine = ItemPropertyDamageVulnerability(nDivine, nVulDivine);
itemproperty ipBludge = ItemPropertyDamageVulnerability(nBludge, nVulBludge);
IPSafeAddItemProperty(oSkin, ipDivine);
IPSafeAddItemProperty(oSkin, ipBludge);
}
//If mounted on entry set bonuses, else fix appearance.
void CheckHorseMount(object pc)
{
object oHorse;
if (GetSkinInt(pc, "bX3_IS_MOUNTED"))
{ // Mounted, set bonuses
HORSE_SupportIncreaseSpeed(pc, oHorse);
HORSE_SupportAdjustMountedArcheryPenalty(pc);
DelayCommand(1.0, HORSE_SupportApplyMountedSkillDecreases(pc));
HORSE_SupportApplyACBonus(pc, oHorse);
HORSE_SupportApplyHPBonus(pc, oHorse);
SetLocalInt(pc, "bX3_HORSE_MODIFIERS", 1);
}
// fix the appearance since we're unmounted
else
{
HorseIfNotDefaultAppearanceChange(pc);
}
}
Ollebroc developed the following pre-mount script to unequip shields. This illustrates the general principle.
Add this variable to the horse: X3_HORSE_PREMOUNT_SCRIPT || string || noshield
The script noshield.nss reads:
// Shield is removed before mounting on a horse
//#include "x3_inc_horse"
void main()
{
object oRider = OBJECT_SELF;
object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oRider);
if((GetBaseItemType(oShield) == BASE_ITEM_SMALLSHIELD)||
(GetBaseItemType(oShield) == BASE_ITEM_LARGESHIELD)||
(GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD))
{
AssignCommand(oRider, ActionUnequipItem(oShield));
}
}
The module's OnEquip script contains this section:
if(HorseGetIsMounted(oPC))
{
object oShield = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oPC);
if((GetBaseItemType(oShield) == BASE_ITEM_SMALLSHIELD)||
(GetBaseItemType(oShield) == BASE_ITEM_LARGESHIELD)||
(GetBaseItemType(oShield) == BASE_ITEM_TOWERSHIELD))
{
DelayCommand(0.1, AssignCommand(oPC, ActionUnequipItem(oShield)));
DelayCommand(0.6, AssignCommand(oPC, ActionUnequipItem(oShield)));
DelayCommand(1.1, AssignCommand(oPC, ActionUnequipItem(oShield)));
}
}
The purpose of the delayed commands is to thwart attempts to by-pass the script by re-equipping from a quickslot.
It is recommended that you don't customize Bioware scripts unless you really know what you're doing - the horse system is complex,
and has been through a lot of tuning and testing.
In most cases, the results you need can be achieved using script hooks.
However, sometimes there's no alternative.
Ollebroc provided the following example. In the
x3_inc_horse script he changed the bonus AC so the horse's AC is added to the rider,
not the difference if more.
void HORSE_SupportApplyACBonus(object oRider, object oHorse)
{
int nRiderAC = GetAC(oRider);
int nHorseAC = GetAC(oHorse);
int nDiff = nHorseAC - nRiderAC;
eEffect = SupernaturalEffect(EffectACIncrease(/*nDiff*/nHorseAC, AC_NATURAL_BONUS));
}
// I used the nHorseAC instead of nDiff. Then design your horses with AC from 10-20.
Ollebroc comments that although AC_NATURAL_BONUS is not stackable, AC_DODGE_BONUS gives a higher AC than he wanted.
Ollebroc has written scripts to support
Blackguard
mounts, which work in a similar fashion to Paladin mounts.
Axe Murderer reports that there are about 50 Bioware scripts that involve transitions but don't handle horses correctly
(Stone of Recall, for example).
The following (rather long) example shows how to fix secret door transitions.
The first block of code is to be saved under the name "_transition_inc":
// DoTransition library
//::////////////////////////////////////////////////////////
// _transition_inc
//::////////////////////////////////////////////////////////
#include "x3_inc_horse"
#include "x0_inc_henai"
void DoTransition( object oClicker, object oTarget)
{
if( !GetIsObjectValid( oClicker) || !GetIsObjectValid( oTarget)) return;
float fX3_MOUNT_MULTIPLE = GetLocalFloat( GetArea( oClicker), "fX3_MOUNT_MULTIPLE");
float fX3_DISMOUNT_MULTIPLE = GetLocalFloat( GetArea( oClicker), "fX3_DISMOUNT_MULTIPLE");
if( GetLocalFloat( oClicker, "fX3_MOUNT_MULTIPLE") > fX3_MOUNT_MULTIPLE)
{
fX3_MOUNT_MULTIPLE = GetLocalFloat( oClicker, "fX3_MOUNT_MULTIPLE");
}
if( fX3_MOUNT_MULTIPLE <= 0.0) fX3_MOUNT_MULTIPLE = 1.0;
if( GetLocalFloat( oClicker, "fX3_DISMOUNT_MULTIPLE") > 0.0)
{
fX3_DISMOUNT_MULTIPLE = GetLocalFloat( oClicker, "fX3_DISMOUNT_MULTIPLE");
}
if( fX3_DISMOUNT_MULTIPLE > 0.0) fX3_MOUNT_MULTIPLE = fX3_DISMOUNT_MULTIPLE;
object oAreaTarget = GetArea( oTarget);
int bNoMounts = FALSE;
float fDelay = 0.1*fX3_MOUNT_MULTIPLE;
if( !GetLocalInt( oAreaTarget, "X3_MOUNT_OK_EXCEPTION"))
{
if( GetLocalInt( GetModule(), "X3_MOUNTS_EXTERNAL_ONLY") && GetIsAreaInterior( oAreaTarget))
{
bNoMounts = TRUE;
}
else if( GetLocalInt( GetModule(), "X3_MOUNTS_NO_UNDERGROUND") && !GetIsAreaAboveGround( oAreaTarget))
{
bNoMounts = TRUE;
}
}
int bDelayedJump = FALSE;
int bAnim = GetLocalInt( OBJECT_SELF, "bDismountFast");
if( GetLocalInt( oAreaTarget, "X3_NO_MOUNTING") || GetLocalInt( oAreaTarget, "X3_NO_HORSES") || bNoMounts)
{ // make sure all transitioning are not mounted
if( HorseGetIsMounted( oClicker))
{ // dismount clicker
bDelayedJump = TRUE;
AssignCommand( oClicker, HORSE_SupportDismountWrapper( bAnim, TRUE));
fDelay = fDelay +0.2*fX3_MOUNT_MULTIPLE;
}
int nN = 1;
object oOb = GetAssociate( ASSOCIATE_TYPE_HENCHMAN, oClicker, nN);
while( GetIsObjectValid( oOb))
{ // check each associate to see if mounted
if( HorseGetIsMounted( oOb))
{ // dismount associate
bDelayedJump = TRUE;
DelayCommand( fDelay, AssignCommand( oOb, HORSE_SupportDismountWrapper( bAnim, TRUE)));
fDelay = fDelay +0.2*fX3_MOUNT_MULTIPLE;
}
oOb = GetAssociate( ASSOCIATE_TYPE_HENCHMAN, oClicker, ++nN);
}
if( fDelay > 0.1) SendMessageToPCByStrRef( oClicker, 111989);
if( bDelayedJump)
{ // some of the party has/have been mounted, so delay the time to hitch
fDelay = fDelay +2.0*fX3_MOUNT_MULTIPLE;
// non-animated dismount lasts 1.0+1.0=2.0 by default, so wait at least that!
if( bAnim) fDelay = fDelay +2.8*fX3_MOUNT_MULTIPLE;
//animated dismount lasts (X3_ACTION_DELAY+HORSE_DISMOUNT_DURATION+1.0)*fX3_MOUNT_MULTIPLE = 4.8
//by default, so wait at least that!
}
}
if( GetLocalInt( oAreaTarget, "X3_NO_HORSES") || bNoMounts)
{ // make sure no horses/mounts follow the clicker to this area
bDelayedJump = TRUE;
object oHitch = GetNearestObjectByTag( "X3_HITCHING_POST", oClicker);
location lPreJump = HORSE_SupportGetMountLocation( oClicker, oClicker, 0.0);
DelayCommand( fDelay, HorseHitchHorses( oHitch, oClicker, lPreJump));
if( bAnim) fDelay = fDelay +1.8 *fX3_MOUNT_MULTIPLE;
}
SetAreaTransitionBMP( AREA_TRANSITION_RANDOM);
if( bDelayedJump)
{ // delayed jump
DelayCommand( fDelay, AssignCommand( oClicker, ClearAllActions()));
DelayCommand( fDelay +0.1*fX3_MOUNT_MULTIPLE, AssignCommand( oClicker, JumpToObject( oTarget)));
}
else
{ // quick jump
AssignCommand( oClicker, JumpToObject( oTarget));
}
DelayCommand( fDelay +4.0*fX3_MOUNT_MULTIPLE, HorseMoveAssociates( oClicker));
}
void DoTransitionByLocation( object oClicker, location lTarget)
{
if( !GetIsObjectValid( oClicker) || !GetIsObjectValid( GetAreaFromLocation( lTarget))) return;
float fX3_MOUNT_MULTIPLE = GetLocalFloat( GetArea( oClicker), "fX3_MOUNT_MULTIPLE");
float fX3_DISMOUNT_MULTIPLE = GetLocalFloat( GetArea( oClicker), "fX3_DISMOUNT_MULTIPLE");
if( GetLocalFloat( oClicker, "fX3_MOUNT_MULTIPLE") > fX3_MOUNT_MULTIPLE)
{
fX3_MOUNT_MULTIPLE = GetLocalFloat( oClicker, "fX3_MOUNT_MULTIPLE");
}
if( fX3_MOUNT_MULTIPLE <= 0.0) fX3_MOUNT_MULTIPLE = 1.0;
if( GetLocalFloat( oClicker, "fX3_DISMOUNT_MULTIPLE") > 0.0)
{
fX3_DISMOUNT_MULTIPLE = GetLocalFloat( oClicker, "fX3_DISMOUNT_MULTIPLE");
}
if( fX3_DISMOUNT_MULTIPLE > 0.0) fX3_MOUNT_MULTIPLE = fX3_DISMOUNT_MULTIPLE;
object oAreaTarget = GetAreaFromLocation( lTarget);
int bNoMounts = FALSE;
float fDelay = 0.1*fX3_MOUNT_MULTIPLE;
if( !GetLocalInt( oAreaTarget, "X3_MOUNT_OK_EXCEPTION"))
{
if( GetLocalInt( GetModule(), "X3_MOUNTS_EXTERNAL_ONLY") && GetIsAreaInterior( oAreaTarget))
{
bNoMounts = TRUE;
}
else if( GetLocalInt( GetModule(), "X3_MOUNTS_NO_UNDERGROUND") && !GetIsAreaAboveGround( oAreaTarget))
{
bNoMounts = TRUE;
}
}
int bDelayedJump = FALSE;
int bAnim = GetLocalInt( OBJECT_SELF, "bDismountFast");
if( GetLocalInt( oAreaTarget, "X3_NO_MOUNTING") || GetLocalInt( oAreaTarget, "X3_NO_HORSES") || bNoMounts)
{ // make sure all transitioning are not mounted
if( HorseGetIsMounted( oClicker))
{ // dismount clicker
bDelayedJump = TRUE;
AssignCommand( oClicker, HORSE_SupportDismountWrapper( bAnim, TRUE));
fDelay = fDelay +0.2*fX3_MOUNT_MULTIPLE;
}
int nN = 1;
object oOb = GetAssociate( ASSOCIATE_TYPE_HENCHMAN, oClicker, nN);
while( GetIsObjectValid( oOb))
{ // check each associate to see if mounted
if( HorseGetIsMounted( oOb))
{ // dismount associate
bDelayedJump = TRUE;
DelayCommand( fDelay, AssignCommand( oOb, HORSE_SupportDismountWrapper( bAnim, TRUE)));
fDelay = fDelay +0.2*fX3_MOUNT_MULTIPLE;
}
oOb = GetAssociate( ASSOCIATE_TYPE_HENCHMAN, oClicker, ++nN);
}
if( fDelay > 0.1) SendMessageToPCByStrRef( oClicker, 111989);
if( bDelayedJump)
{ // some of the party has/have been mounted, so delay the time to hitch
fDelay = fDelay +2.0*fX3_MOUNT_MULTIPLE;
// non-animated dismount lasts 1.0+1.0=2.0 by default, so wait at least that!
if( bAnim) fDelay = fDelay +2.8*fX3_MOUNT_MULTIPLE;
// animated dismount lasts (X3_ACTION_DELAY+HORSE_DISMOUNT_DURATION+1.0)*fX3_MOUNT_MULTIPLE=4.8
// by default, so wait at least that!
}
}
if( GetLocalInt( oAreaTarget, "X3_NO_HORSES") || bNoMounts)
{ // make sure no horses/mounts follow the clicker to this area
bDelayedJump = TRUE;
object oHitch = GetNearestObjectByTag( "X3_HITCHING_POST", oClicker);
location lPreJump = HORSE_SupportGetMountLocation( oClicker, oClicker, 0.0);
DelayCommand( fDelay, HorseHitchHorses( oHitch, oClicker, lPreJump));
if( bAnim) fDelay = fDelay +1.8*fX3_MOUNT_MULTIPLE;
}
SetAreaTransitionBMP( AREA_TRANSITION_RANDOM);
if( bDelayedJump)
{ // delayed jump
DelayCommand( fDelay, AssignCommand( oClicker, ClearAllActions()));
DelayCommand( fDelay +0.1*fX3_MOUNT_MULTIPLE, AssignCommand( oClicker, JumpToLocation( lTarget)));
}
else
{ // quick jump
AssignCommand( oClicker, JumpToLocation( lTarget));
}
DelayCommand( fDelay +4.0*fX3_MOUNT_MULTIPLE, HorseMoveAssociates( oClicker));
}
//void main() {}
The second block is to be saved under the name "x0_i0_transport":
//:://////////////////////////////////////////////////
//:: X0_I0_TRANSPORT
//:: Copyright (c) 2002 Floodgate Entertainment
//:://////////////////////////////////////////////////
/*
Functions for making creatures travel/transport to new locations.
*/
//:://////////////////////////////////////////////////
//:: Created By: Naomi Novik
//:: Created On: 09/12/2002
//:://////////////////////////////////////////////////
/**********************************************************************
* CONSTANTS
**********************************************************************/
/**********************************************************************
* FUNCTION PROTOTYPES
**********************************************************************/
#include "_transition_inc"
// Target goes to specified destination object intelligently.
// If location is in same area, walk (or run) there.
// If location is in different area, walk (or run) to
// most appropriate door, area transition, or waypoint,
// then jump.
// If either of these fail, jump after fDelay seconds.
void TravelToObject(object oDest, object oTarget=OBJECT_SELF, int bRun=FALSE, float fDelay=10.0);
// Target goes to specified location intelligently. See
// TravelToObject for description.
void TravelToLocation(location lDest, object oTarget=OBJECT_SELF, int bRun=FALSE, float fDelay=10.0);
// Find nearest exit to target (either door or waypoint).
object GetNearestExit(object oTarget=OBJECT_SELF);
// Find best exit based on desired target area
object GetBestExit(object oTarget=OBJECT_SELF, object oTargetArea=OBJECT_INVALID);
// Transport a player and his/her associates to a waypoint.
// This does NOT transport the rest of the player's party,
// only their henchman, summoned, dominated, etc.
void TransportToWaypoint(object oPC, object oWaypoint);
// Transport a player and his/her associates to a location.
// This does NOT transport the rest of the player's party,
// only their henchman, summoned, dominated, etc.
void TransportToLocation(object oPC, location oLoc);
// Transport an entire party to a waypoint
void TransportAllToWaypoint(object oPC, object oWay);
// Transport an entire party to a location
void TransportAllToLocation(object oPC, location lLoc);
/**********************************************************************
* FUNCTION PROTOTYPES
**********************************************************************/
// Target goes to specified destination object intelligently.
// If location is in same area, walk (or run) there.
// If location is in different area, walk (or run) to
// nearest waypoint or door, then jump.
// If either of these fail, jump after a timeout.
void TravelToObject(object oDest, object oTarget=OBJECT_SELF, int bRun=FALSE, float fDelay=10.0)
{
TravelToLocation(GetLocation(oDest), oTarget, bRun, fDelay);
}
// Target goes to specified location intelligently. See
// TravelToObject for description.
void TravelToLocation(location lDest, object oTarget=OBJECT_SELF, int bRun=FALSE, float fDelay=10.0)
{
object oDestArea = GetAreaFromLocation(lDest);
if (oDestArea == GetArea(oTarget)) {
AssignCommand(oTarget,
ActionForceMoveToLocation(lDest, bRun, fDelay));
} else {
object oBestExit = GetBestExit(oTarget, oDestArea);
AssignCommand(oTarget,
ActionForceMoveToObject(oBestExit, bRun, 1.0, fDelay));
int nObjType = GetObjectType(oBestExit);
if (nObjType == OBJECT_TYPE_DOOR) {
AssignCommand(oTarget, ActionOpenDoor(oBestExit));
}
AssignCommand(oTarget,
ActionJumpToLocation(lDest));
}
AssignCommand(oTarget, DelayCommand(fDelay, ClearAllActions()));
AssignCommand(oTarget, DelayCommand(fDelay, JumpToLocation(lDest)));
}
// Find nearest exit to target (either door or trigger or
// (failing those) waypoint).
object GetNearestExit(object oTarget=OBJECT_SELF)
{
object oCurArea = GetArea(oTarget);
object oNearDoor = GetNearestObject(OBJECT_TYPE_DOOR, oTarget);
if (GetArea(oNearDoor) != oCurArea)
oNearDoor = OBJECT_INVALID;
// Find nearest area transition trigger
int nTrig = 1;
object oNearTrig = GetNearestObject(OBJECT_TYPE_TRIGGER, oTarget);
while (GetIsObjectValid(oNearTrig)
&& GetArea(oNearTrig) == oCurArea
&& !GetIsObjectValid(GetTransitionTarget(oNearTrig)))
{
nTrig++;
oNearTrig = GetNearestObject(OBJECT_TYPE_TRIGGER, oTarget, nTrig);
}
if (GetArea(oNearTrig) != oCurArea)
oNearTrig = OBJECT_INVALID;
float fMaxDist = 10000.0;
float fDoorDist = fMaxDist;
float fTrigDist = fMaxDist;
if (GetIsObjectValid(oNearDoor)) {
fDoorDist = GetDistanceBetween(oNearDoor, oTarget);
}
if (GetIsObjectValid(oNearTrig)) {
fTrigDist = GetDistanceBetween(oNearTrig, oTarget);
}
if (fTrigDist < fDoorDist)
return oNearTrig;
if (fDoorDist < fTrigDist || fDoorDist < fMaxDist)
return oNearDoor;
// No door/area transition -- use waypoint as a backup exit
return GetNearestObject(OBJECT_TYPE_WAYPOINT, oTarget);
}
// Private function: find the best exit of the desired type.
object GetBestExitByType(object oTarget=OBJECT_SELF, object oTargetArea=OBJECT_INVALID, int nObjType=OBJECT_TYPE_DOOR)
{
object oCurrentArea = GetArea(oTarget);
int nDoor = 1;
object oDoor = GetNearestObject(nObjType, oTarget);
object oNearestDoor = oDoor;
object oDestArea = OBJECT_INVALID;
object oBestDoor = OBJECT_INVALID;
object oBestDestArea = OBJECT_INVALID;
while (GetIsObjectValid(oDoor) && GetArea(oDoor) == oCurrentArea) {
oDestArea = GetArea(GetTransitionTarget(oDoor));
// If we find a door that leads to the target
// area, use it
if (oDestArea == oTargetArea) {
return oDoor;
}
// If we find a door that leads to a different area,
// that might be good if we haven't already found one
// that leads to the desired area, or a closer door
// that leads to a different area.
if (oDestArea != oCurrentArea && !GetIsObjectValid(oBestDoor)) {
oBestDoor = oDoor;
}
// try the next door
nDoor++;
oDoor = GetNearestObject(nObjType, oTarget, nDoor);
}
// If we found a door that leads to a different area,
// return that one.
if (GetIsObjectValid(oBestDoor))
return oBestDoor;
// Otherwise, return the nearest, if it's in this area.
if (GetArea(oNearestDoor) == oCurrentArea)
return oNearestDoor;
return OBJECT_INVALID;
}
// Find best exit based on desired target area
object GetBestExit(object oTarget=OBJECT_SELF, object oTargetArea=OBJECT_INVALID)
{
if (!GetIsObjectValid(oTargetArea))
return GetNearestExit(oTarget);
// Try and find a door
object oBestDoor = GetBestExitByType(oTarget,
oTargetArea,
OBJECT_TYPE_DOOR);
if (GetIsObjectValid(oBestDoor)) {
if (GetTransitionTarget(oBestDoor) == oTargetArea) {
return oBestDoor;
}
}
// Try and find a trigger
object oBestTrigger = GetBestExitByType(oTarget,
oTargetArea,
OBJECT_TYPE_TRIGGER);
if (GetIsObjectValid(oBestTrigger)) {
if (GetTransitionTarget(oBestTrigger) == oTargetArea) {
return oBestTrigger;
}
}
if (GetIsObjectValid(oBestDoor))
return oBestDoor;
if (GetIsObjectValid(oBestTrigger))
return oBestTrigger;
return GetNearestExit(oTarget);
}
// Transport a player and his/her associates to a waypoint.
// This does NOT transport the rest of the player's party,
// only their henchman, summoned, dominated, etc.
void TransportToWaypoint(object oPC, object oWaypoint)
{
if (!GetIsObjectValid(oWaypoint)) {
return;
}
TransportToLocation(oPC, GetLocation(oWaypoint));
}
// Transport a player and his/her associates to a location.
// This does NOT transport the rest of the player's party,
// only their henchman, summoned, dominated, etc.
void TransportToLocation(object oPC, location lLoc)
{
if( !GetIsObjectValid( oPC) || !GetIsObjectValid( GetAreaFromLocation( lLoc))) return;
// Jump the player and his henchmen.
DoTransitionByLocation( oPC, lLoc);
// Get all the possible non-henchmen associates of this PC
object oDomin = GetAssociate( ASSOCIATE_TYPE_DOMINATED, oPC);
object oFamil = GetAssociate( ASSOCIATE_TYPE_FAMILIAR, oPC);
object oSummon = GetAssociate( ASSOCIATE_TYPE_SUMMONED, oPC);
object oAnimalComp = GetAssociate( ASSOCIATE_TYPE_ANIMALCOMPANION, oPC);
// Jump any non-henchman associates
if( GetIsObjectValid( oDomin)) DoTransitionByLocation( oDomin, lLoc);
if( GetIsObjectValid( oFamil)) DoTransitionByLocation( oFamil, lLoc);
if( GetIsObjectValid( oSummon)) DoTransitionByLocation( oSummon, lLoc);
if( GetIsObjectValid( oAnimalComp)) DoTransitionByLocation( oAnimalComp, lLoc);
}
// Transport an entire party with all associates to a waypoint.
void TransportAllToWaypoint(object oPC, object oWaypoint)
{
if (!GetIsObjectValid(oWaypoint)) {
return;
}
TransportAllToLocation(oPC, GetLocation(oWaypoint));
}
// Transport an entire party with all associates to a location.
void TransportAllToLocation(object oPC, location oLoc)
{
object oPartyMem = GetFirstFactionMember(oPC, TRUE);
while (GetIsObjectValid(oPartyMem)) {
TransportToLocation(oPartyMem, oLoc);
oPartyMem = GetNextFactionMember(oPC, TRUE);
}
TransportToLocation(oPC, oLoc);
}
From Ollebroc :
For those of you planning on using the Skill penalties while mounted, you should do this to your restoration scripts
(nw_s0_grrestore.nss, nw_s0_lsrestor.nss, nw_s0_restore.nss):
int GetIsSupernaturalCurse(effect eEff)
{
//object oCreator = GetEffectCreator(eEff);
//if(GetTag(oCreator) == "q6e_ShaorisFellTemple")
int nEff = GetEffectSubType(eEff);
if(nEff == SUBTYPE_SUPERNATURAL)
return TRUE;
return FALSE;
}
You'll find this function at the bottom of each of these scripts. Unless you are using the Shaoris Temple guy, it should be commented out.
This is how the spells should be. It won't remove Supernatural effects (some cruel and unusual traps are more effective this way).
Doing this prevents the removal of negative effects from Negative Energy Traps.
What we do have is a pool that will remove all negative effects when you enter if you're not mounted.
Ollebroc provided a version of x3_s2_palmount that enables AC and HP Boost correctly :
#include "x3_inc_horse"
void main()
{
object oRider = OBJECT_SELF;
object oHorse = GetLocalObject(oRider, "oX3_TempHorse");
string sScript = GetLocalString(GetModule(), "X3_EXTEND_PALMOUNT");
SetLocalObject(oRider, "oX3_PALADIN_MOUNT", oRider);
HORSE_SupportIncreaseSpeed(oRider, OBJECT_INVALID);
HORSE_SupportAdjustMountedArcheryPenalty(oRider);
HORSE_SupportApplyACBonus(oRider, oHorse);
HORSE_SupportApplyHPBonus(oRider, oHorse);
DelayCommand(0.5, HORSE_SupportApplyMountedSkillDecreases(oRider));
SetLocalInt(oRider, "bX3_HORSE_MODIFIERS", TRUE);
HORSE_SupportMonitorPaladinUnsummon(oRider);
if (GetStringLength(sScript) > 0) ExecuteScript(sScript, oRider);
}
Ollebroc provided the following fix to prevent horses from being dominated (the usual trick of setting the plot flag may confuse the horse system).
Add this to
nw_s0_doman and
nw_s0_dommon.
#include "NW_I0_SPELLS"
#include "x2_inc_spellhook"
#include "x3_inc_horse"
void main()
{
/*
Spellcast Hook Code
Added 2003-06-23 by GeorgZ
If you want to make changes to all spells,
check x2_inc_spellhook.nss to find out more
*/
if (!X2PreSpellCastCode())
{
// If code within the PreSpellCastHook (i.e. UMD) reports FALSE, do not run this spell
return;
}
// End of Spell Cast Hook
//Declare major variables
object oTarget = GetSpellTargetObject();
effect eDom = EffectDominated();
eDom = GetScaledEffect(eDom, oTarget);
effect eMind = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_DOMINATED);
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
//add this after the major variables
if (HorseGetIsAMount(oTarget)) return;
//rest of script
}
Ollebroc provided the following examples of changes to x3_mod_def_load and routines to restore mounted player abilities
(caution : these were written for Beta 9 and might need modification now).
//::///////////////////////////////////////////////
//:://:: x3_mod_def_enter
//:://:://////////////////////////////////////////////
//
// For PWs that want to have the horse bonuses and penalties added when logging on.
// Horse speed variable could be added to the database if not using the default speed.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "x3_inc_horse"
void CheckHorseMount(object oPC)
{
object oHorse;
if (GetSkinInt(oPC, "bX3_IS_MOUNTED"))
{
//AssignCommand(oPC,HORSE_SupportDismountWrapper(FALSE, TRUE));
HORSE_SupportIncreaseSpeed(oPC, oHorse);
HORSE_SupportAdjustMountedArcheryPenalty(oPC);
DelayCommand(1.0, HORSE_SupportApplyMountedSkillDecreases(oPC));
HORSE_SupportApplyACBonus(oPC, oHorse);
HORSE_SupportApplyHPBonus(oPC, oHorse);
SetLocalInt(oPC, "bX3_HORSE_MODIFIERS", 1);
}
else HorseIfNotDefaultAppearanceChange(oPC);
}
void main()
{
object oPC=GetEnteringObject();
//ExecuteScript("x3_mod_pre_enter",OBJECT_SELF); // Override for other skin systems
if ((GetIsPC(oPC) || GetIsDM(oPC)) && !GetHasFeat(FEAT_HORSE_MENU,oPC))
{ // add horse menu
HorseAddHorseMenu(oPC);
} // add horse menu
if (GetLocalInt(GetModule(), "X3_ENABLE_MOUNT_DB"))
{ // restore PC horse status from database
//HORSE_SupportMountCleanVariables(oPC);
DelayCommand(2.0, HorseReloadFromDatabase(oPC, X3_HORSE_DATABASE));
DelayCommand(3.0, CheckHorseMount(oPC));
} // restore PC horse status from database
// DelayCommand(5.0, HorseRestoreHenchmenLocations(oPC));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Add the following lines to each function: Horse AC added
void HORSE_SupportStoreMountedPCInDatabase(object oPC, string sDatabase){
SetCampaignInt(sDatabase, "nX3_HAC", GetSkinInt(oPC, "nX3_HorseAC"), oPC);}
void HORSE_SupportReloadMountedPCFromDatabase(object oPC, string sDatabase){
SetSkinInt(oPC, "nX3_HorseAC", GetCampaignInt(sDatabase, "nX3_HAC", oPC));}
void HORSE_SupportDeleteMountedPCFromDatabase(object oPC, string sDatabase){
DeleteCampaignVariable(sDatabase, "nX3_HAC", oPC);}
void HorseMount(object oHorse, int bAnimate=TRUE, int bInstant=FALSE, int nState=0){
switch(nState)
{ // main mounting switch
case 0: //Add this next line to the rest of the variables in this section
SetSkinInt(oRider, "nX3_HorseAC", GetAC(oHorse));
void HORSE_SupportCleanVariables(object oTarget)
DeleteSkinInt(oTarget, "nX3_HorseAC");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Add to these functions:
void HORSE_SupportApplyHPBonus(object oRider, object oHorse)
{ // PURPOSE: Apply HP bonus
effect eEffect;
int nHP = GetCurrentHitPoints(oHorse);
if (!GetLocalInt(GetModule(), "X3_HORSE_ENABLE_HPBOOST")) return;
//Use the number from the player's skin if logging in mounted
if (GetSkinInt(oRider, "nX3_HorseHP"))
{
nHP = GetSkinInt(oRider, "nX3_HorseHP");
}
nHP = nHP/2;
if (nHP < 1) nHP = 1;
eEffect = SupernaturalEffect(EffectTemporaryHitpoints(nHP));
AssignCommand(GetModule(), ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oRider));
} // HORSE_SupportApplyHPBonus()
void HORSE_SupportApplyACBonus(object oRider, object oHorse)
{ // PURPOSE: Apply AC bonus
effect eEffect;
int nRiderAC = GetAC(oRider);
int nHorseAC = GetAC(oHorse);
int nDiff = nHorseAC - nRiderAC;
if (!GetLocalInt(GetModule(), "X3_HORSE_ENABLE_ACBOOST")) return;
if (nDiff < 1) return;
//Use the number from the player's skin if logging in mounted
if (GetSkinInt(oRider, "nX3_HorseAC"))
{
nHorseAC = GetSkinInt(oRider, "nX3_HorseAC");
}
eEffect = SupernaturalEffect(EffectACIncrease(nDiff, AC_NATURAL_BONUS));
AssignCommand(GetModule(), ApplyEffectToObject(DURATION_TYPE_PERMANENT, eEffect, oRider));
} // HORSE_SupportApplyACBonus()
9.1 Functions
9.2 Module Variables
9.3 Area Variables
9.4 Horse Variables
9.5 Debugging and Tweaking Scripts
9.6 Constants
This section only covers the public functions which Bioware has documented. The include files
x3_inc_horse,
x3_inc_skin and
x3_inc_string also contain some private functions which may be useful (some of which begin with the
Horse prefix, e.g.
HorseHitchHorse). Since 1.69 is the final patch, in theory, it may be safe to use private functions, but I've assumed it isn't.
This section deviates from Proleric's 1.05 version. The various functions have been removed from the guide and made into separate linked pages within the Lexicon.
These can be set on the module object in the
OnModuleLoad event script to over-ride the system defaults.
9.2.1 Area Control
9.2.2 Abilities and Combat
9.2.3 Henchman Control
9.2.4 Saddlebags
9.2.5 Paladin Mounts
9.2.6 Mounting System
9.2.7 Database
Variable | Type | Description | Comment |
9.2.1. Area Control | | | |
X3_MOUNTS_EXTERNAL_ONLY | Int |
If TRUE, horses are not allowed in interior areas. |
By default, mounts can be ridden in all areas.
If this switch is set, mounts cannot be taken into an internal area unless an exception variable is set on the area to over-ride this. |
X3_MOUNTS_NO_UNDERGROUND | Int |
If TRUE, horses are not allowed in underground areas. |
By default, mounts can be ridden in all areas.
If this switch is set, mounts cannot be taken into an underground area unless an exception variable is set on the area to over-ride this. |
9.2.2. Abilities and Combat | | | |
X3_HORSE_DISABLE_SPEED | Int |
If TRUE, a speed increase is not applied when a person mounts. |
|
X3_HORSE_DISABLE_SKILL | Int |
If TRUE, skill decreases are not applied when a person mounts. |
By default, a massive penalty is applied to Disable Trap, Open Lock, Hide, Move Silently, Pick Pocket, Set Trap and Tumble, rendering these skills virtually unusable while mounted. |
X3_MOUNT_NO_REST_DISMOUNT | Int |
If TRUE, you are able to rest while mounted. |
By default, mounted creatures cannot rest. |
X3_HORSE_ENABLE_ACBOOST | Int |
If TRUE, the PC's AC is increased if need be to at least match that of the horse that is being mounted. |
This is in addition to the Mounted Combat feat and will stack.
This optional feature works on mount and dismount, but doesn't react to a change of armor while mounted.
So, there is an exploit - the player can improve their AC more than intended by mounting in ordinary clothes then equipping armor; this gives a higher AC than mounting in armor.
Also, it doesn't work for Paladin mounts.
|
X3_NO_MOUNTED_COMBAT_FEAT | Int |
If TRUE, disables the special code added to Bioware scripts to try to support Mounted Combat close to how it is in Player's Handbook. |
Alternative to Mount Damage - it doesn't make sense to enable both.
These are temporary hit points. On dismounting, the horse is undamaged, and the rider loses any temporary hit points remaining.
Known issue - this doesn't work for Paladin mounts.
|
X3_ENABLE_MOUNT_DAMAGE | Int |
If TRUE, the module attempts to transfer some damage to the mount when a rider dismounts if damage occurred while they were mounted. |
Alternative to HP Boost - it doesn't make sense to enable both.
On dismounting, the horse takes a proportion of the damage sustained while mounted, and the rider is healed by the same amount. The feedback messages say "[Rider] damaged [horse] x" and "[Rider] healed x". This might be confusing for some players unless you explain this feature in the module ReadMe.
|
X3_HORSE_NO_CORPSES | Int |
If TRUE, lootable horse corpses will not be created when a mounted PC or NPC dies. |
|
X3_NO_SHAPESHIFT_SPELL_CHECK | Int |
If TRUE, prevents the script from checking to see if a shapeshifted spell is targeted on a mounted creature. The x2_inc_spellhook scripts will work exactly like they did before horses were introduced, with no concern whether the target is mounted or not. |
By default, shapeshifting spells are not allowed to be cast on mounted creatures. Bear in mind that if you change this, rider and mount are one single object, so a rider transformed into a goblin will be appear to be just that, not a goblin on a horse.
EffectPolymorph works normally on a mounted creature, even in the default setting. Again, the horse vanishes while the creature is polymorphed. There are no known issues with doing this - it doesn't seem to break the horse system.
|
9.2.3. Henchman Control | | | |
X3_RESTORE_HENCHMEN_LOCATIONS | Int |
If TRUE, henchmen's henchmen will be restored to a location near the henchman that is their master when a PC master of the henchman connects. |
This is NOT enabled by default to prevent problems with older modules.
It only works for current henchmen, not for dismissed henchmen or NPC horse owners.
|
X3_HORSE_NO_HENCHMAN_INCREASE | Int |
If TRUE, prevents the henchmen from being increased to make room for the horse. |
For example, if a maximum of 2 henchmen is allowed, a PC can have no more than 2 henchmen, or 1 henchman and 1 dismounted horse, or 2 dismounted horses. By default, there is no limit on the number of horses, but assigning horses may create an exploit that permits additional henchmen to be acquired beyond the number intended by the module designer.
Bear in mind that if saddlebags are enabled, it is desirable to allow the PC to reassign henchman horses to themselves in order to examine the saddlebags, so this limit might be inconvenient.
Horses assigned to henchmen do not count towards the maximum.
|
X3_HORSE_MAX_HENCHMEN | Int |
If set to a non-zero value, this indicates the maximum number of henchmen to allow it to be increased to in order to make room for horses. |
This is another way to manage the potential exploit mentioned above.
For example, if a maximum of 2 henchmen is allowed, and this value is set to 3, a PC can acquire up to 3 horses in addition to 2 henchmen, after which they can have up to 5 horses and/or henchmen.
|
9.2.4. Saddlebags | | | |
X3_HORSE_ENABLE_SADDLEBAGS | Int |
If TRUE, enables inventory support for horses. |
If you want it to use a quick non-database method for storing the inventory, place a waypoint with the tag X3_HORSE_INVENTORY_STORAGE somewhere in an area that a PC can never get to.
If this waypoint does not exist then it will assume that the database is to be used. If you are using a database it is advisable that you change the support functions because they use the standard databases and it will often prove slower than you may like. Additional settings are required on the horse - see saddlebags.
|
X3_SADDLEBAG_DATABASE | String |
The name of the database to use for storing saddlebag inventory. |
If no name is specified it will use the module tag and a small modifier. |
9.2.5. Paladin Mounts | | | |
X3_HORSE_PALADIN_USE_PHB | Int |
If TRUE, paladin mount summoning durations are as specified in the Player's Handbook 3.5 edition rather than just defaulting to 24 hours. |
|
X3_MOUNT_NO_REST_DESPAWN | Int |
If TRUE, a paladin mount is not despawned when you rest and adheres strictly to his summoned duration. If time is advanced by resting then it is still possible it will despawn. |
By default, a summoned mount is despawned on resting. |
X3_PALMOUNT_SUMMONOVR | String |
Custom "summon mount" script that will run after normal checks that the action is valid. |
Can also be set on the PC (which takes priority over the module setting). Beware: If you use this then handling all other aspects of this mount becomes your responsibility. |
X3_EXTEND_PALMOUNT | String |
Custom script that will run after a summoned horse is mounted. |
|
X3_EXTEND_PALDMOUNT | String |
Custom script that will run after a summoned horse is dismounted. |
|
9.2.6. Mounting System | | | |
X3_NO_MOUNT_COMMANDABLE | Int |
If TRUE, the SetCommandable commands are not used. |
By default, rider and horse are not commandable during mounting and dismounting. Setting this switch allows you to clear these actions or queue other actions during the event. Don't enable this unless you have a good understanding of the states in which the horse system might be left if interrupted, and make provision in your scripts for recovery. |
X3_HORSE_ACT_VS_DELAY | Int |
If TRUE, the system will use Actions as opposed to delays in some portions of the mounting sequence. |
Doing this might provide another way to handle inaccessible horses besides using X3_HORSE_NOT_RIDEABLE_OWNER. It may result in not being able to access accidentally poorly placed horses due to scripts or other factors, but it may be desired by some module designers, so it has been provided as an option. |
fX3_MOUNT_DELAY | Float |
If non-zero, this number will be added to the mounting speed. |
This is provided so a module designer can speed up or slow down the mounting if needed for any reason due to hardware, area, or specific player needs. A dialog could be provided to allow players to adjust this themselves to set the mounting speed to be what they get the best performance from. If you use negative numbers Bioware recommends doing no lower than -0.1. This variable can also be set on a PC. |
fX3_TIMEOUT_TO_MOUNT | Float |
Value to set on the module or PC to indicate how long the PC/NPC should attempt to move into a proper mounting animation to perform the mounting animation. When this time is reached if it is still not in position it will instant mount instead and will not animate. If this value is not set then the default value will be used. |
Can also be set on the PC. |
bX3_MOUNT_NO_ZAXIS | Int |
Set to TRUE to indicate when calculating the proper mounting location you do not want the Z Axis to be included in the measurement. |
This is set by default in x3_mod_def_load. It's really a bug fix which should never need to be changed. It affects the appearance of the mount animation on sloping terrain.
Can also be set on a PC and an area.
|
fX3_FREQUENCY | Float |
Frequency of recursive call of the HorseMount() function to try and initiate new pathfinding to the horse every time until the character reaches the mounting position or until the time limit for mounting is up or unless X3_HORSE_ACT_VS_DELAY is set to TRUE, in which case the action queue is not locked and moving towards a horse is interruptible, i.e. by clicking. If set larger than 9.0 or less than 1.0 the value defaults to 2.0 seconds. |
Can also be set on a PC. |
9.2.7. Database | | | |
X3_ENABLE_MOUNT_DB | Int |
If TRUE, enables database and persistent world support. |
You will want to modify the HORSE_Support functions related to the database in x3_inc_horse so that they write and read properly however you have the database set up in your module. You will also want to plan on using something like the x3_mod_def_hb script for your module heartbeat script. |
Variable | Type | Description | Comment |
X3_NO_HORSES | Int |
If TRUE, horses are not allowed in this area. |
By default, horses are allowed in all areas.
Module switches can be used to prevent horses from entering all interior or underground areas.
If a placeable or waypoint in the area a person was in before entering the new area has a tag of X3_HITCHING_POST then their horse(s) will jump to this object in STAND_GUARD mode.
|
X3_NO_MOUNTING | Int |
If TRUE, horses may not be ridden in this area. |
Anyone attempting to do so will be forcibly dismounted. |
X3_MOUNT_OK_EXCEPTION | Int |
If TRUE, horses may enter this area, regardless of the module settings. |
This can be set in conjunction with X3_NO_MOUNTING if only dismounted horses are allowed to enter. |
fX3_MOUNT_MULTIPLE | Float |
In really busy areas, you can make mounting animation faster or slower. For example, 0.5 is twice as fast, 2.0 is twice as slow. The parameter acts as a multiplier on delays throughout the animation. |
This can be set on the PC, too, and the larger (slower) value takes priority. |
fX3_DISMOUNT_MULTIPLE | Float |
This is a similar tweak for dismount. By default, the mount multiple applies to dismount, too. |
This can also be set on the PC, but in this case the PC value takes priority. |
X3_ABORT_WHEN_STUCK | Int |
If set, mounting is aborted if the rider is stuck in one position while running towards the horse.
This doesn't need to be used when using X3_HORSE_ACT_VS_DELAY option because the action will time out anyway.
|
This can also be set on a horse.
By default, mounting is forced in this situation - the horse jumps to the rider, even if it is inaccessible. The dismount scripts create horses without checking whether the location is accessible. So, if you use this switch, you must also find a way of ensuring that horses are always available after dismount.
If a horse is inaccessible by design, you may prefer to set X3_HORSE_NOT_RIDEABLE_OWNER on the horse instead.
|
These are intended to be set as local variables on the horse
blueprint. They can be changed temporarily in a script, but on dismount they are reset to the original blueprint values.
Of course, you can use the
OnSpawn script on the blueprint to set the variables, so that the
initial values are restored on dismount.
If you need to preserve the
current settings, you can store the variable on the rider in the pre-mount script, and reset it on the horse in the post-dismount script (which runs after
OnSpawn).
9.4.1 Mount Control
9.4.2 Saddlebags
9.4.3 Script Hooks
9.4.4 Custom Mounts
9.4.5 Custom Races
Variable | Type | Description | Comment |
9.4.1. Mount Control | | | |
X3_HORSE_OWNER_TAG | String |
If set, the horse will add itself as a henchman to an NPC with the specified tag. |
Does the same thing as HorseSetOwner. |
X3_HORSE_NOT_RIDEABLE_OWNER | Int |
If TRUE, the mount will not be useable. |
The error it will return if asked is that it is NOT rideable due to it being owned by someone else. This is useful if you want horses around that the PCs and Henchmen cannot mount for reasons such as they are owned by a store, etc.
You can't allocate an owner to a horse when this is set.
If an owner is established before this switch is set, they can ride the horse.
Setting this particular variable on the horse template or OnSpawn is good for making the horse permanently unusable. If you need the horse to be temporarily unavailable, set the switch in a different way (e.g. a one-shot area OnEnter script), otherwise the horse will become unavailable again on dismount.
|
bX3_IS_MOUNT | Int |
If TRUE, when the mount is added as a henchman it is still mountable. |
In general, this switch indicates that the creature is mountable. It is not necessary to set this switch on the standard Bioware horses. |
X3_NO_MOUNT_ANIMATE | Int |
If TRUE, this mount does not EVER animate mounting or dismounting. |
This only suppresses the animation - mounting and dismounting are still allowed. |
X3_HORSE_RESTRICT_race | Int |
If TRUE, this horse cannot be mounted by the specified race. |
Supported races are ELF, HUMAN, HALFELF, DWARF, HALFORC, HALFLING, and GNOME, CUSTOM# = racial type number. |
9.4.2. Saddlebags | | | |
bX3_HAS_SADDLEBAGS | Int |
If TRUE, the horse has saddlebags. |
This supports inventory control (which is disabled, by default, unless X3_HORSE_ENABLE_SADDLEBAGS is set on the module). You will also want to set the dialog X3_DLG_SADDLEBAG on the horse blueprint (the default for the standard templates) or create your own dialog that handles what the saddlebags one does. You will only be able to access saddlebags of associates in your party. |
9.4.3. Script Hooks | | The following hooks are available for custom scripting in the mount and dismount events. | |
X3_HORSE_PREMOUNT_SCRIPT | String |
Script to fire before the horse is mounted. |
If this script determines it cannot mount then it will set X3_HORSE_NOMOUNT to TRUE on the mount. You can use a script like this to create support for saddlebags or to use this to extend the mounting system. This will be executed by the rider and the variable oX3_TempHorse will be set pointing to the horse so that the custom script knows which horse this relates to. |
X3_HORSE_POSTMOUNT_SCRIPT | String |
Script to fire after the horse is mounted. |
This may be useful for adding things like feats and other after mounting needs. If this script does not exist then the standard Speed, Skill Decreases, Mounted Archery Adjustments, etc. will be applied. If you want your own post mount script then it is important to note you will have to apply these modifiers if you also want them to be used within your own script. The horse that was mounted will be referenced by oX3_TempHorse as stored on the Rider. If you use such a custom hook script make sure to set bX3_IS_MOUNTED to TRUE by using the SetSkinInt() function from x3_inc_skin on the rider after they mount. This is required in some cases for the dismount radial to work. (mainly when working with custom mounts). |
X3_HORSE_PREDISMOUNT_SCRIPT | String |
Script that needs to be executed before the dismount portion continues. If X3_HORSE_NODISMOUNT is set to TRUE then the horse dismount will be aborted. |
|
X3_HORSE_POSTDISMOUNT_SCRIPT | String |
Can be set to a script to be executed after dismount to reverse steps that may have occurred with a POSTMOUNT script such as adding feats. This script could be used to remove feats. |
In practice, this script can be used to make any desired adjustments to the dismounted rider and horse. |
| |
Alternatively, you can replace the Bioware scripts completely. The following make it easier to implement existing mount support systems in conjunction with the Bioware system. |
|
X3_HORSE_SCRIPT_MOUNT | String |
Script to call for mounting instead of using the default one called by the horse menu feat. |
This still checks to make sure mounting in the area is legal first. |
X3_HORSE_SCRIPT_DISMOUNT | String |
Script to call for dismounting instead of using the default one called by the horse menu feat. |
|
X3_HORSE_SCRIPT_ASSIGN | String |
Script to call for assign mount instead of using the default one called by the horse menu feat. |
|
9.4.4. Custom Mounts | | It is possible you might want to add a new horse/mount type that does not fit neatly into the appearance.2da or tails.2da with the other horse types.
The following variables if set to any number greater than 0 will override the default settings and use what you specify instead.
| |
bX3_IS_MOUNT | Int |
Should be set to TRUE if this is a custom blueprint mount that for some reason the script indicates is not a mount. |
In practice this needs to be set on custom mounts, period. |
X3_HORSE_TAIL | Int |
The tail to use with the tailmodel.2da that defines this horse. |
This defines the appearance of the horse after mounting.
It must be set for all custom mounts to the tail number in tailmodel.2da.
|
X3_HORSE_NULL_APPEARANCE | Int |
The appearance to use when scaling the horse for the mounting animation. |
This controls the size of the horse during the mounting animation.
By default, the horse is scaled to match the rider's race, using the standard null appearance Horse_Invis_* from appearance.2da, where * is the race. If a different scaling is required for a custom mount, this variable can identify a custom line in appearance.2da, which will be similar to Horse_Invis_Human, but with a different Wing/Tail scaling factor. |
X3_HORSE_FOOTSTEP | Int |
The footstep number to use when this horse is mounted. |
This defines the line to use in footstepsounds.2da. By default, horse footstep sounds will be used. |
X3_HORSE_MOUNT_DURATION | Float |
The duration in seconds that the mount animation should take with this horse. |
This only needs to be set if the mounting animation for this blueprint is faster or longer than the default animations. |
X3_HORSE_MOUNT_SPEED | Float |
The mount speed increase or decrease that should be used with this mount. |
If the value is 0 then it will use the default value 50 (meaning 50% faster). Valid range is -150 to +50. Out-of-range values are reset to those limits. The Bioware documentation which states that the default is 99 is incorrect. |
X3_HORSE_DISMOUNT_DURATION | Float |
The duration in seconds that the dismount animation should take with this horse. |
This only needs to be set if the dismounting animation for this blueprint is faster or longer than the default animations. |
X3_TOTAL_MOUNT_ANIMATION_DELAY | Float |
A variable containing a time lot indicating how much time the routine has before it needs to be finished. It is used for the sake of synchronizing animation and the process running in the background, exclusively used in mounting animation portion of the HorseMount routine, but can be used elsewhere. Note, that the variable is artificially set even in case no animation is desired so that the code does not happen instantly. It is not meant to be changed, unless something bad is happening timing-wise. The value is precalculated and in our particular case it is supposed to hold the total animation length. |
Probably don't need to change this. |
9.4.5. Custom Races | | | |
X3_CUSTOM_RACE_APPEARANCE | Int |
Set on a rider if they use a custom racial appearance. |
This value should be set to what appearance number they use from appearance.2da. This will prevent custom races from being denied mounting rights due to the script thinking the rider is shape shifted. |
X3_CUSTOM_RACE_MOUNTED_PHENO | Int |
Set on the rider if they should use a special phenotype when mounted. |
|
X3_CUSTOM_RACE_JOUST_PHENO | Int |
Set on the rider if they need a special phenotype when mounted in joust mode. |
|
X3_CUSTOM_RACE_PHENOTYPE | Int |
Set on the rider if they need a special phenotype when not mounted. |
|
X3_CUSTOM_RACE_MOUNTED_APPEARANCE | Int |
Set on the rider to indicate which appearance they should use when mounted. |
|
X3_HORSE_RESTRICT_race | Int |
See Mount Control. |
|
These scripts are provided for use in Debug mode, but might have applications in modules. They could be used by players to resolve unwanted situations such as poor animation performance on specific hardware.
Script | Comment |
x3_fix_horseout |
If the module was not designed for horses, this will ensure they can only enter outside areas. Won't work if module has custom transition scripts. |
x3_fix_horse |
If the system is treating the PC as mounted when they're not, this will fix it. |
x3_fix_slowpc |
Mount / dismount settings to try if the PC is slow (possibly on older hardware). |
x3_fix_faster |
Make mounting/dismounting a little faster. |
x3_fix_slower |
Make mounting/dismounting a little slower. |
x3_fix_default |
Resets mounting/dismounting speed to the default. |
x3_fix_speed100 |
Mount/dismount speed multiple set to normal 100%. |
x3_fix_speed125 |
Mount/dismount speed multiple set to 125% (25% slower). |
x3_fix_speed150 |
Mount/dismount speed multiple set to 150% (50% slower). |
x3_fix_speed200 |
Mount/dismount speed multiple set to 200% (100% slower). |
For the advanced user: |
x3_fix_nocmd |
Toggles use of SetCommandable during mounting and dismounting. |
x3_fix_act |
Toggles use of Actions during mounting and dismounting. |
These are provided for reference.
Note that for most customisation purposes, it is sufficient to set the appropriate variables on the horse template. These constants have a global effect on the Bioware horse system as a whole, so don't change them unless you really know what you're doing.
Constant | Type | Purpose |
HORSE_ANIMATION_* |
Int |
Looping animation constants to be used with ActionPlayAnimation and PlayAnimation. |
HORSE_MOUNT_DURATION |
Float |
The duration in seconds that it should take to complete the default mount animation. |
HORSE_DISMOUNT_DURATION |
Float |
The duration in seconds that it should take to complete the default dismount animation. |
HORSE_DEFAULT_SPEED_INCREASE |
Int |
The default speed increase when mounted. Note that this value (99%) is ignored - the speed increase is capped at 50%. |
IP_CONST_HORSE_MENU |
Int |
The Horse Menu feat that can be added to an item. |
X3_HORSE_DATABASE |
String |
Default name of the data base to be used for persistent worlds etc. |
The following constants will only be required when extending the 2da files or customising the horse system. Normally, the standard functions take care of all this. |
HORSE_APPEARANCE_OFFSET |
Int |
The location in appearance.2da where the dismounted horse appearances occur. Provided to facilitate extending the 2da. |
HORSE_TAIL_OFFSET |
Int |
The location in tails.2da where the mounted horse appearances occur. |
HORSE_NUMBER_OF_HORSES |
Int |
Number of horse entries in appearance.2da and tails.2da. |
HORSE_PALADIN_PREFIX |
String |
A prefix that should be used with paladin mounts when spawning them. |
HORSE_NULL_RACE_* |
Int |
The appearance to use for a specific race when scaling the horse as a tail during mounting. |
HORSE_RACE_MOUNTED_*M HORSE_RACE_MOUNTED_*F |
Int |
The appearance that should be used for a specific race and gender during mounting. These appearances are often required to set the proper speeds, radiuses, etc. They also have the complete phenotypes and animations associated with them.
|
HORSE_PHENOTYPE_* |
Int |
Phenotype numbers to be used by the mounting system. _N specifies the mounting race started as a normal phenotype, and _L specifies the race started as a large phenotype. |
HORSE_FOOTSTEP_SOUND |
Int |
The footstep sound to be used when the horse is mounted. |
Approach this section with caution. It's a
provisional list of scripts that Bioware changed or introduced in 1.69.
Typically, scripts with an x3 prefix are new - the rest could existing in earlier modules.
It only covers the horse system, and it only includes the ones I know about - there may well be others!
The only really safe way to proceed is to check every Bioware script you have customized.
Remember - if you change an include file, you have to recompile all scripts that reference it using Build in the toolset.
A standard Bioware script that references an include file will
not be recompiled unless you make a copy of it in your module,
so changing a Bioware include file is potentially dangerous.
The following include files have changed:
x0_inc_henai
The new include files are:
x3_inc_horse
x3_inc_skin
x3_inc_string
Scripts that reference the horse functions in x3_inc_horse include:
NW_C2_DEFAULT6
NW_C2_DEFAULT7
NW_CH_AC7
NW_G0_TranPCOnly
NW_G0_Transition
nw_o0_death
X0_CH_HEN_DEATH
X2_HEN_DEATH
x2_mod_def_equ
x2_mod_def_rest
x2_mod_def_unequ
x3_c2_pm_hb
x3_ct_saddlebags
x3_fix_horse
X3_G0_TranPCOnly
X3_G0_Transition
x3_mod_def_enter
x3_mod_def_hb
x3_s2_paldmount
x3_s2_palmount
x3_s3_horse
x3_s3_palmount
x3_tr_dismount
x3_tr_dismounth
Other scripts that change horse system variables include:
nw_ch_ac1
nw_ch_ac9
x3_c2_pm_death
x3_fix_act
x3_fix_horseout
x3_fix_nocmd
x3_fix_speed100
x3_fix_speed125
x3_fix_speed150
x3_fix_speed200
x3_mod_def_load
END OF DOCUMENT
author: Proleric, editors: Jimmy Buffit, Mistress, Baragg, Kookoo