HexenmeisterRaven said:
That would really interest me, too.
So for what exactly shall this script be in the end?
Sorry, guys! I edited my first post to post the answer, but I'll put it here again, with a fuller explanation of how we're resolving between tags and NPC 'Res Refs' (Resource references, actually filenames).The actual problem I've asked about is solved by FirstSubTag, here:
Code:
// Identify the first part in this tag, if it is a compound tag; otherwise return// the whole tag.string FirstSubTag( string compoundTag) { int separator = FindSubString( compoundTag, ";"); string result = compoundTag; if ( separator > -1) { result = GetStringLeft( compoundTag, separator); } // PrintString( "FirstSubTag in = '" + compoundTag + "'; out = '" + result + "'"); return result;}
I could have done a recursive function to find any sub-tag, but that wasn't what I needed; our convention is that the character's own tag is their first sub-tag. To explain how all this works I'll include the whole of our characters include file and discuss what things do:
Code:
////////////////////////////////////////////////////////////////////////// inc_bv_chars.nss// Include file for things which manipulate characters // in Birth and Virgins.//// (c) 2008 Petra Prinz, Carrol Dufault, Simon Brooke, Cory Kerens// The Medusa Collective////////////////////////////////////////////////////////////////////////// ------------------------------------------------------------------------------------------------// -------- Magic constant property name on which to look for characters'// -------- NPC resrefs// ------------------------------------------------------------------------------------------------const string NPC_RESREF="NPC_RESREF";
The reason for declaring a constant which has the same name as its string value is that the compiler can check constants and will therefore catch spelling mistakes and typos. Seeing I make a lot of typos that's useful to me!
Code:
// ------------------------------------------------------------------------------------------------// -------- Magic constants for reading the dramatis personae file// ------------------------------------------------------------------------------------------------const string DRAMATISPERSONAE="bv_characters";const string DPCOLUMNTAG="tag";const string DPCOLUMNTEMPLATERESREF="template";const string DPCOLUMNSTORYNPCRESREF="npc";const string DPCOLUMNNAME="name";const string DPCOLUMNALTTEMPLRESREF="alttemplate";
This stanza describes the structure of our 'bv_characters' data table, which actually holds the resolution information.
Code:
// ------------------------------------------------------------------------------------------------// -------- Character tags: principal characters// ------------------------------------------------------------------------------------------------const string abigTag = "bv_npct_abigail";const string abiiTag = "bv_npct_abiinn";const string agnsTag = "bv_npct_agn_s";const string agnTag = "bv_npct_agnieszka";const string barnTag = "bv_npct_barnabas";const string dandTag = "bv_npct_dandelion";const string granTag = "bv_npct_grandpa";const string gutsTag = "bv_npct_gut_s";const string gutkTag = "bv_npct_gutka";const string helaTag = "bv_npct_hela";const string josTag = "bv_npct_josef";const string martTag = "bv_npct_martyna";const string revsTag = "bv_npct_rev_s";const string revTag = "bv_npct_reverend";const string rozTag = "bv_npct_rozalia";const string szsTag = "bv_npct_sz_s";const string szczTag = "bv_npct_szczepan";// ------------------------------------------------------------------------------------------------// -------- Character tags: extras// ------------------------------------------------------------------------------------------------/* random villagers - women */const string annaTag = "bv_npct_anna";const string barbTag = "bv_npct_barbora";const string hannTag = "bv_npct_hanna";const string jadwTag = "bv_npct_jadwiga";const string jolaTag = "bv_npct_jolanta";const string kataTag = "bv_npct_katarzyna";const string krysTag = "bv_npct_krysia";const string ksenTag = "bv_npct_ksenia";const string tabiTag = "bv_npct_tabitha";/* random villagers - men */const string jakuTag = "bv_npct_jakub";const string jerzTag = "bv_npct_jerzy";const string karoTag = "bv_npct_karol";const string miesTag = "bv_npct_mieszko";const string paweTag = "bv_npct_pawel";const string radoTag = "bv_npct_radomil";const string tobiTag = "bv_npct_tobias"; /* random villagers - girls */const string alinTag = "bv_npct_alina";const string celiTag = "bv_npct_celina";const string elzbTag = "bv_npct_elzbieta";const string rutaTag = "bv_npct_ruta";/* random villagers - boys */const string aronTag = "bv_npct_aron";const string eriaTag = "bv_npct_eriash";const string krstTag = "bv_npct_krysztof";const string rafaTag = "bv_npct_rafal";/*travelers*/const string andrTag = "bv_npct_andrzej";const string hawtTag = "bv_npct_haw";const string jaceTag = "bv_npct_jacek";const string tomzTag = "bv_npct_tomasz";
Once again, declaring constants means the compiler will catch any spelling errors or typos. All our characters, even the extras, are declared with full 'Story NPC' files; this is so that we can have different behaviours for each character in different phases of our story.
Code:
// ------------------------------------------------------------------------------------------------// -------- Utility functions// ------------------------------------------------------------------------------------------------// Identify the first part in this tag, if it is a compound tag; otherwise return// the whole tag.string FirstSubTag( string compoundTag) { int separator = FindSubString( compoundTag, ";"); string result = compoundTag; if ( separator > -1) { result = GetStringLeft( compoundTag, separator); } // PrintString( "FirstSubTag in = '" + compoundTag + "'; out = '" + result + "'"); return result;}
This is the one I've already discussed above...
Code:
// a wrapper around GetNearestObjectByTag which allows for// the fact that the object we're interested in could be OBJECT_SELF// argument tag: a string, presumed to be the tag of an NPC// returns: that NPC (or null if not found, but we hope that// won't happenobject GetNPCByTag( string tag) { object result; if ( GetTag( OBJECT_SELF) == tag) { result = OBJECT_SELF; } else { result = GetNearestObjectByTag( tag); } return result;}
This is simply a useful function to have...
Code:
// get the NPC resref associated with this tag; note, this data is initialised// from bv_characters.2da - it ain't magic.string GetNPCResRefFromTag( string tag) { int row = Get2DARow( DRAMATISPERSONAE, FirstSubTag( tag), 0); string result = Get2DAString( DRAMATISPERSONAE, DPCOLUMNSTORYNPCRESREF, row); // PrintString( "GetNPCResRefFromTag resolved '" + // tag + "' to '" + result + "'"); return result;}
This is the function which actually does the lookup in the [tt]DRAMATISPERSONAE [/tt]table. [tt]Get2DARow [/tt]returns the record in the table which describes the character we're interested in. Get2DAString pulls the content of the [tt]DPCOLUMNSTORYNPCRESREF[/tt] field out of that record. I've included an extract from the [tt]DRAMATISPERSONAE [/tt]table at the end of this post.
Code:
// get the NPC resref associated with this object; note, this data is initialised// from bv_characters.2da - it ain't magic.string GetNPCResRefFromObject( object o) { string result = GetNPCResRefFromTag( GetTag( o)); return result;}
This is just a wrapper around the previous function to allow us to pass an object.
Code:
// move this npc to this station immediately. If they're too far away, jump them there// even if it causes a visual glitchvoid MoveToStation( object npc, object station) { if ( GetDistanceBetween( npc, station) > 8.0) { /* disaster - they aren't nearby, we'll have to jump them there */ AssignCommand( npc, JumpToLocation( GetLocation( station))); } else { /* more or less there, walk them to station - but immediately! */ AssignCommand( npc, ClearAllActions(TRUE)); AssignCommand( npc, ActionForceMoveToLocation( GetLocation( station))); }}
This is just a utility function the purpose of which should be obvious.Finally, here's the first few lines of DRAMATISPERSONAE:
Code:
2DA V2.0 tag template npc name alttemplate 0 bv_npct_abigail bvabigail bv_npc_abigail Abigail bvabiinn1 bv_npct_abiinn bvabiinn bv_npc_abigail Abigail **** 2 bv_npct_agnieszka bvagnieszka bv_npc_agnieszka Agnieszka bvagn_s 3 bv_npct_agn_s bvagn_s bv_npc_agnieszka Agnieszka **** 4 bv_npct_alina bvalina bv_npc_alina Alina **** 5 bv_npct_anna bvanna bv_npc_anna Anna **** 6 bv_npct_aron bvaron bv_npc_aron Aron **** 7 bv_npct_barbora bvbarbora bv_npc_barbora Barbora **** 8 bv_npct_barnabas bvbarnabas bv_npc_barnabas Barnabas **** 9 bv_npct_celina bvcelina bv_npc_celina Celina **** 10 bv_npct_dandelion bvdandelion bv_npc_dandelio Dandelion ****
The '[tt]tag[/tt]' column contains the character's tag. The [tt]template [/tt]column contains the 'Res Ref' (file name) of character's default character template. The [tt]npc [/tt]column contains the res ref of the character's Story NPC file. The [tt]name [/tt]column contains the characters name; and the [tt]alttemplate [/tt]column contains the res ref of an alternative template for that character if she has one. For example, when Abigail is in her home, she has a store at which she sells alchemic components; when she's in the inn she sells food and beer. As a character template can only have one store, she needs two character templates.