SIEGE UNIVERSITY 2

Siege University II Tutorials
Modding FAQ
095: Upgrading DSII
100: The Basics of Siege Editor
201: Compass Map Radar
202: Conversations
203: Journal
204: Quest Indicator Icons
205: Start Positions
206: Teleporters
207: Town Portal Restrictions
208: Weapon Effects
209: Flick
210: Tuning Grids
211: Setting Up Good Map Lighting
212: Setting Up Simple Node Fading
215: Building Data Tables

Siege University I Tutorials
200: Concepts and Terminology
201: Templates
203: Triggers
204: Moods
205: Fades
206: Elevators
211: Naming Key
213: Dungeon Siege Resource System
301: Introduction to Dungeon Siege Architecture

Third Party Tutorials
A Simple Mod Part One - Armor Textures
A Simple Mod Part Two - A New Armor
Beginners Guide to Stitching Regions
How to Open and Create Tanks
Making Chants Work in a New Map
Ornaments
Understanding the NKK

Useful Links
Siegetheday.org
Dungeon Siege Outpost
Dungeon Raider
Kdawg.org - List of useful Links
MCarp DS Nodes
Dungeon Siege 2 at Gamefront
Broken World at Gamefront

215: Dungeon Siege 2 Data Tables

A brief tutorial by SnowFox

All hail GPG for including these really, really, really data structures. DS2 allows you to make use of tables, allowing you to create, query and update them from Skrit.

Player_Experience

 ID

Character_Name

XP

1

Xaa

100

2

Narco

105

3

XMen90s

200

4

SnowFox

75

5

Mcarp

125

6

Sambrkopc

130

It is now possible to define tables, like the one above, that can has many records (i.e. rows) of information.

When creating a table, the first thing you need to do is define what the table format is (i.e. what it looks like). In the example above, you'd need to specify that there are three fields (i.e. columns), and for each field specify the field title, types, whether they are unique or not, default values and documentation.

A table's schema, or definition, defines a table. The schemas are kept in /world/contentdb/tables/tables.gas. In there is info about tables' field names, type, default value and which field(s) have to be unique or non-unique.

Below is an extract of the entry_schema schema used by the DS2 Journal and handbook.

// entry schema is used for both the journal lorebook and the handbook
[t:tableschema,n:entry_schema]
{
[id] {flags = uniquekey; type = int; default = *; doc = "entry ID ( set by system )";}
[entry_type] {flags = nonuniquekey; type = string; doc = "REQUIRED: entry type";}
[screen_type] {flags = nonuniquekey; type = lstring; doc = "REQUIRED: screen type";}
[template] {flags = nonuniquekey; type = lstring; doc = "REQUIRED: entry template ";}
[icon] {flags = nonuniquekey; type = lstring; doc = "REQUIRED: entry icon ( texture
:
:
[page obj anim 1] {flags = nonuniquekey; type = lstring; doc = "object anim ... obj";}
}

Once a table schema has been defined, a table and records can be created/added by creating a table, specifying the schema it uses (which defines which fields can be assigned values), as well as setting up values for records (i.e. the table's rows). Let’s look at an example.

Below is an extract from /world/maps/ds2 world/info/lorebook_info.gas.

t:table,n:lore_table]
{
schema = entry_schema;
[ *]
{
entry_type = quest_item;
template = medallion drevin;
title = "Drevin's Medallion";
icon = "b_gui_ig_m_i_jnl_drevinmed";
summary = "An ornate medallion of unknown origin. It has been in Drevin's family for generations.";
description_0 = "Your best friend Drevin gave you this medallion during the Siege of Greilyn Beach.\n\nYou felt a pang of dread at the time; he was unusually insistent that you take it -- what could he foresee that you could not? He seems to value this object more than anything else, and is very concerned for its safety.\n\nPutting your objections aside, you promised him that you would keep it safe. If this ancient object is as powerful as Drevin seems to believe, you will probably need it.";
description_1 = "";
page_type = "page_book";
page_image_0 = b_gui_ig_m_i_jnl_drevinmed;
}
[*]
{
entry_type = quest_item;
template = key_tut_dogs;
title = "Kennel Key";
icon = "b_gui_ig_m_i_jnl_kennelkey";
summary = "A key of Morden make. It smells of Nawl Beasts.";
description_0 = "You were given this key by Beast Trainer Malden. It opens the kennels used in the trenches on Greilyn beach.\n\nWhile waiting for battle, the Morden Nawl Beasts are usually kept in kennels to prevent their hunger and brutality from inadvertently souring their relationship with their masters. These kennels are always kept locked.";
description_1 = "";
page_type = "page_book";
page_image_0 = b_gui_ig_m_i_jnl_kennelkey;
}
:
:
}

Each new record in the above table data is defined in a
[*]
{
...
}
block.

The above table has two records. And, because of the “default= *” part, the id field is automatically assigned a unique value that is unique within the table.

Let’s take a look at what all that means, with some screenshots for the Drevin's medal block.

The code above tells us that we’re dealing with the Lore book (n:lore), so open up the in-game journal and click on the Lore tab. We’re dealing with a quest item (per the entry_type), so click on the Quest Items text.

Looking in the Quest Item's page 1 (because you get the medallion early in the game), we see the item’s Title and Icon data displayed. If you select (single left-click) on that journal entry, you’ll see the summary text off on the right side of the journal.

If you double-click on the entry, the journal opens up and you’ll see the Description_0 (left page) and Description_1 (right page). The right page is blank because there no text assigned to Description_1.

Also, notice the “\n\n” used within the description text in the n:lore_table code. Each ‘\n’ is a “new line”, which breaks the text up into paragraphs.


Manipulating Tables

There are a number of different methods with which tables can be manipulated from within Skrit. These are:

Skrit::TableWrapper::GetName(void)
Skrit::TableWrapper::GetGroup(void)
Skrit::TableWrapper::GetSchema(void)
Skrit::TableWrapper::Query(char const *)
Skrit::TableWrapper::AddRecord(void)
Skrit::RecordWrapper::Delete(void)
Skrit::RecordWrapper::GetString(char const *)
Skrit::RecordWrapper::SetString(char const *, gpbstring const &)
Skrit::RecordWrapper::GetBool(char const *)
Skrit::RecordWrapper::SetBool(char const *, bool)
Skrit::RecordWrapper::Getlnt(char const *)
Skrit::RecordWrapper::Setlnt(char const *, int)
Skrit::RecordWrapper::GetFloat(char const *)
Skrit::RecordWrapper::SetFloat(char const *, double)
Skrit::DataSetWrapper::GetRecordCount(void)
Skrit::DataSetWrapper::GetRecord(int)
Skrit::SchemaWrapper::GetName(void)
Skrit::SchemaWrapper::GetGroup(void)
Skrit::SchemaWrapper::GetColumnCount(void)
Skrit::SchemaWrapper::GetColumnName(int)
Skrit::TableManager::GetSchemaCount(void)
Skrit::TableManager::GetSchema(int)
Skrit::TableManager::GetSchemaNamed(char const *)
Skrit::TableManager::GetTableCount(void)
Skrit::TableManager::GetTable(int)
Skrit::TableManager::GetTableNamed(char const *)

Below is an extract from /ui/interfaces/backend/journal/books/lorebook/lorebook.gas showing the tables being used:

// Obtaining a reference to the map:lore_table table. If there is no such table (e.g. you gave
// an incorrect name), then a NULL reference is returned
table lore_table$ = TableManager.GetTableNamed("map:lore_table");
if( lore_table$ == NULL )
{
report.errorf("ERROR getting table named[ lore_ table]\n" );
return;
}
// Assuming the table could be opened, then you can query it to get a dataset
// returned. the dataset will contain all the records that matched the query.
// See below for examples of queries.
dataset entry_data$ = lore_table$.Query( m_sDatabaseQuery$ );
// get the number of entries
m_iNumEntries$ = entry_data$.GetRecordCount();
//Now that we have the entry numbers, for every record in the dataset....
i$ = 0;
while( i$ < m_iNumEntries$)
{
// Get the entry data, if there is no such record, then NULL is returned
record r$ = entry_data$.GetRecord( i$ );
if( r$ == NULL )
{
report.errorf("CONTENT ERROR: Got an invalid record from index[%d]\n", i$);
i$ += 1;
continue;
}
// the table had a field called "template", so to get the entry template value
// from the current record
entry_template$ = r$.GetString("template");
// Get the rest of the entry's data
entry_id$ = r$.GetInt("id");
entry type$ = r$.GetString("entry_type");
entry_icon$ = r$.GetString("icon");
entry_title$ = Report.Translate( r$.GetString("title") );
entry_ location$ = Report.Translate( r$.GetString("location") );
entry_summary$ = Report.Translate( r$.GetString("summary") );
entry_desc$ = Report.Translate( r$.GetString("description_0") );
entry_page_type$ = r$.GetString("page_type");
entry_page_obj_0$ = r$.GetString("page_obj_0");
entry_page_obj_1$ = r$.GetString("page_obj_1");

Tables can be queried to locate records that match certain criteria by making using of the Query method.

Skrit::TableWrapper::Query(char const *)

The char string pointed to by the argument is used to find records whose fields match the conditions given in the string. A dataset containing the selected records is returned, which can be traversed.

Below are a couple of example queries:

//from lorebook.gas
query$ = "+id(*)"; //select all unique records. sorted in ascending order using id
query$ = "entry_type=quest_item and (+id(*))"; //select all records with entry_type = quest_item,
// in ascending order using id
// Descending order would've been a minus sign (-id(*)) instead of the plus sign (+id(*))
// If wanted to sort by a number of keys, the syntax is +/-[key1] ([ key2] (*)),
// For example, +id1(id2(*)) will sort by id1, then id2, in ascending order
//Here's an example of the two key sort, from bestiary.gas
query$ = "+id( beast_set == act_1)"; //here, sort, in ascending order on id, all records that have beast set == act _1

Used as in the following:

dataset entry_data$ = lore table$.Query(query$);

Regards, SnowFox