A course on SFO - chapter 3.3

ui_virtual_class - reorganizing the display of classes and kits

Back to contents

This tool provides a number of different functions to rearrange the character-generation system in order to (among other things) create the appearance of extra classes and extra selection options for the classes. (It does not and cannot add real new classes, but it can make it seem to the player that a new class has been added.) The tool also automatically alphabetises lists of classes and kits.

3.3.1 Setting up ui_virtual_class

You set up this component by doing

LAF virtual_class_setup END
(Calling any of the functions described on this page will do it automatically for you.) Once this is done, the class selection screen will automatically be displayed in alphabetical order, and multiclasses will be displayed in a separate sub-menu.

3.3.2 Creating a new class

The basic idea here is that a kit is presented to the player as if it were a class. To illustrate with an existing kit: suppose we want to treat barbarians as a class in their own right, not just as a fighter kit. (They are presented this way in oBG2.) We do so by first creating a new 'virtual class', DW_BARBARIAN, which is displayed to the player as a class, and then assigning the Barbarian kit to it, like so:
LAF add_virtual_class 
	INT_VAR 
		name_strref=45859 
		desc_strref=45869 
	STR_VAR 
		ident=DW_BARBARIAN 
		parent=fighter 
END

LAF assign_kit_to_virtual_class STR_VAR class=DW_BARBARIAN kit=BARBARIAN END
The first function call creates the virtual class and assigns it a name and a description. ‘parent=fighter’ indicates that the virtual class is really a subset of fighter kits, but the player doesn’t see that. ‘ident’ can be any string you like, provided it’s unique to the virtual class (so use a modder prefix - although frankly if two different mods both add a virtual class called 'barbarian', they are probably incompatible anyway.) The second function call assigns the Barbarian kit to the virtual class. If the player creates a new character, ‘Barbarian’ will be offered as a class (and the barbarian kit will not be offered for fighters). If they select that class, they’ll automatically be assigned the barbarian kit. To the player, it will (mostly) appear as if Barbarian is a class, not a kit.

You can assign names and descriptions via the currently-loaded .tra file rather than by strrefs, and that’s usually more convenient for entirely new classes. For instance, here’s how ToF creates the Favored Soul virtual class:

LAF add_virtual_class 
INT_VAR 
	name=4000 
	desc=4001 
STR_VAR 
	ident="favored_soul" 
	parent=SHAMAN 
END
The name and description are at @4000 and @4001. (Yes, I should probably have used a modder prefix.)

3.3.3 Assigning kits to new classes

You can assign more than one kit to a virtual class, and if one kit has the same name as the virtual class and the others do not, the new kits will appear as if they are kits for the new class.

For instance, suppose we want to create a new Barbarian kit, ‘Amazon’. First we create this kit as an ordinary Fighter kit, say with rowname DW_AMAZON (this I leave as an exercise to the reader). Then we just assign it to the Barbarian virtual class:

LAF assign_kit_to_virtual_class STR_VAR class=DW_BARBARIAN kit=DW_AMAZON END

Now when the player selects ‘Barbarian’, they’ll be taken to a kit menu and offered either ‘Barbarian’ or ‘Amazon’ as kit choices.

Incidentally, there is no requirement that a virtual class has one kit that’s the ‘real’ class, and indeed SFO doesn’t know which kit you intend as the real class.

3.3.4 Overriding alphabetical display order for kits

By default (if this tool is installed), kits are displayed in alphabetical order. Sometimes you might want to override this: for instance, for virtual classes, you might want the ‘real’ class displayed first, so it looks like a class and not just one more kit. You can do so like this:

LAF set_kit_display_priority INT_VAR priority=0 STR_VAR kit=BARBARIAN END
This assigns a priority number (in this case zero) to the kit. Kits are displayed in increasing order of priority number, and in alphabetical order when they have the same priority number; kits without a priority number get displayed after kits with a priority number.

3.3.5 Restricting kits by gender

The ui_virtual_class tool lets you restrict a kit only to males, or only to females, like this:

LAF require_gender STR_VAR kit=DW_AMAZON gender=female END
This will hide the Amazon kit from the player (so that it cannot be selected) unless the character being created is female.

3.3.6 Renaming the kit-selection menu

By default, the kit selection menu has the title ‘Choose Kit’ .The ‘override_class_kit’ function lets you override this: for instance, we might want the kit-selection screen for clerics to be called ‘choose god’. It works like this:

LAF override_class_kit_menu
	INT_VAR title_tra=100
	STR_VAR class="CLERIC" 
END
Here ‘CLERIC’ is the name of the class from class.ids, and title_tra is the number of the new title in your current .tra file. (You can also specify a string ‘title’ directly, but normally you won’t want to as it doesn’t play nice with translations.)

You can also override virtual classes’ kit titles. For instance, for Favored Souls, I again want that menu to be titled ‘Choose God’. We need to tell the function explicitly that our class is virtual, and then use its ident as set by add_virtual_class, like this:

LAF override_class_kit_menu
INT_VAR 
	title_tra=100
	virtual=1
STR_VAR class="favored soul" 
END

3.3.7 Adding a kit-selection submenu

Sometimes it makes sense to gather kits together into groups, either for convenience or to create the appearance of selecting some option for the kit. (For instance, we might want to offer a Dragon Disciple character a choice of dragon, and implement this by having one kit per dragon). Here’s an example of how this works in ui_virtual_class, used to organize all specialist mages into one list:

LAF add_kit_menu 
INT_VAR 	
	name=1
	desc=2
	title=3
STR_VAR 
	kits=”ABJURER CONJURER DIVINER ENCHANTER ILLUSIONIST INVOKER NECROMANCER TRANSMUTER”
	class=mage
	id=dw_specialist_mage
END
Here ‘name’, ‘desc’ and ‘title’ are entries in your tra file. ‘Name’ might be ‘Specialist Mage’, ‘desc’ might be a generalized description of specialists, and ‘title’ might be ‘Choose specialty’. All the kits for class ‘class’ in the list are gathered into the sub-menu. (‘id’ can be any unique identifier.)

3.3.8 Overriding the kit name on the kit-selection screen

Sometimes (especially in kit-selection submenus) it's useful to display something other than the name of the kit. For instance, if you are selecting a cleric's god, it may look better just to have 'Helm', 'Talos', 'Tyr' etc displayed, rather than 'Cleric of Helm', 'Cleric of Talos', etc. Similarly, if you have created a submenu to select which elemental type an elementalist specializes in, it may look better to display 'Fire' rather than 'Fire elementalist'.

You can do this with SFO's 'override_chargen_kit_name' function, like this:

LAF override_chargen_kit_name
INT_VAR 	
	override=1011 // by default, an entry in your .tra file
STR_VAR 
	kit=”DW_ELEMENTALIST_FIRE”
	class=MAGE
END
You can get WEIDU to interpret 'override' as a strref in dialog.tlk, by setting INT_VAR use_tra to 0.

3.3.9 Overriding displayed class names

ui_virtual_class also has functionality to override the displayed classes on a character sheet. This is useful for (e.g.) multiclass kits that combine single-class kits (so that the single-class kit names should show on the character sheet instead of the base classes).

All this is implemented by the override_kit_desc_data function. The 'kit' STR_VAR (which should be a kitlist 2da rowname) specifies the kit for which the override should be applied. You can specify the override itself several ways. The simplest is to set 'swap' to a list of class=>kit swaps, e.g. swap="fighter=>kensai thief=>assasin". Here 'class' should be a class.ids entry, and 'kit' should be a kitlist.2da rowname. You can also directly specify strings to be swapped out and in, either by strref (old_[123]_strref is swapped for new_[123]_strref) or directly by string (old_[123] is swapped for new_[123]).

3.3.10 How it works

Complicatedly(!) but basically by overriding the standard class/kit tables in the character generation UI with its own. All the engine cares about is the eventual class and kit id that you select; by hiding and rearranging the data that lets the player choose those, we create a convincing illusion!

The character-sheet override just does a search-and-replace on the displayed class name(s) if the character has the appropriate kit.

3.3.11 Edits to ui.menu

  • CHARGEN_CLASS (the screen in character generation where you choose your class) is modified as follows:
    • chargen.class (the engine-constructed array of available classes) is replaced by dwSingleClassList, which is created on opening the menu by the function dwMakeSingleClassList()
    • On opening the menu, the variable 'dwMulticlassSelect' is set to 'false'
    • The onClassSelectButtonClick() action is replaced by a call to the function dwOnClassSelectButtonClick()
    • The 'done' button can be pressed if the variable 'dwMulticlassSelect' is set to 'true' (as well as if a class has been selected)
    • If the 'done' button is pressed, it launches CHARGEN_KIT (if dual-classing) or the new CHARGEN_MULTICLASS menu (if multiclassing); if neither holds it defaults to its usual setting.
  • CHARGEN_KIT (the screen in character generation where you choose your kit) is modified as follows:
    • chargen.kit (the engine-constructed array of available kits) is replaced by dwKitList, which is created on opening the menu by the function dwMakeKitList()
    • Some more code is added to the onOpen() block, which skips the kit list if only one kit is available and also initializes some variables
    • The onSelectButtonClick() action is replaced by a call to the dwOnSelectKitButtonClick() function
    • A hardcoded call to Infinity_FetchString() to get a kit's name is replaced by a call to dwChargenKitName()
    • The 'done' button is only clickable if a kit selection has been made
    • Pressing the 'done' button can now start the new CHARGEN_KIT_SECONDARY menu, if the dwMenu variable is set
    • The hardcoded string for the menu title is replaced by dwChargenClassKitTitle()
  • CHARGEN_GENDER (the screen in character generation where you choose which mage spells to memorize) is modified to record the selected gender in the dwGender variable.
  • A new CHARGEN_MULTICLASS menu is added (based on CHARGEN_CLASS)
  • A new CHARGEN_KIT_SECONDARY menu is added (based on CHARGEN_KIT)
  • The classOrGeneralHelp() function is modified to refer to dwSingleclassSelect
  • The kitOrGeneralHelp() function is replaced entirely
  • The updateAttrTable(), getClassString(), and (on IWDEE) buildCharacterDetails() functins are altered to apply the dwOverrideClassNames() function to class names.