"You're seeking the blarney stone? You'll have to find $faction.leader('Irish'),You might have a special caching layer with a temporary storage that proxied all database lookups, and reused return values for subsequent repeated lookups. In Python you could do this with the following code:
{faction.leader('Irish').pronoun_subjective}'ll know where it is."
class CachingDB:
def __init__(self, dbconn):
self.db = weakref.proxy(dbconn)
def __getattr__(self, attrName, lookupCache={}):
class StoredProcedure:
def __init__(self, db, procName):
self.db = db
self.procName = procName
def __call__(self, *args, **kwargs):
# kwargs may need flattening into a tuple and sorting for this to work.
lookupKey = (self.procName, args, kwargs)
ret = lookupCache.get(lookupKey, Exception)
if ret is Exception:
ret = self.db.procName(*args, **kwargs)
lookupCache[lookupKey] = ret
return ret
return StoredProcedure(self.db, attrName)
cachingDB = CachingDB(realDB)
parser = StringParser(cachingDB)
parser.Parse("You're seeking the blarney stone? "
"You'll have to find $faction.leader('Irish'), "
"{faction.leader('Irish').pronoun_subjective}'ll know where it is.")
factions = DB.GetFactions()The actual rows of data would be shared. So the rowset that was "factions" would be an instance of the "FactionRowset" class. And the action of looking up "factions[0]" would wrap the given row of data in an instance of "FactionRow". "factions.Index('factionID')" would index the same rows of data in a dictionary. So memory was not wasted by the caching layer. Not all tables had custom rows, many would simply use the standard "Rowset" and "Row" classes. This was mostly phased out as the later changes were applied below, with only the standard classes being left in use.
faction = factions[0]
factionsByID = factions.Index("factionID")
faction == factionsByID[faction.factionID]
$faction.leader('Irish')
faction.leader('Irish').pronoun_subjective
{faction.Irish.leader.name}
{faction.Irish.leader.pronoun_subjective}
#!/usr/bin/env python
import re
from pprint import pprint
## Regex to match a {brace enclosed keys}, {orphaned {braces, and plain text
_BRACE_REGEX_STRING = r"{([^{]*?)}|([{}])|([^{}]*)"
_BRACE_REGEX = re.compile(_BRACE_REGEX_STRING, re.MULTILINE|re.DOTALL)
def brace_lexer(text):
"""
Convert a block of text into a list of token tuples
in the format (integer, token_string) where integer is:
1 = substitution key with braces stripped
2 = unmatched open or close brace character
3 = other (plain text)
"""
pos = 0
end = len(text)
tokens = []
while pos < end:
match = _BRACE_REGEX.match(text, pos)
group_num = match.lastindex
value = match.group(group_num)
tokens.append((group_num, value,))
pos = match.end()
return tokens
if __name__ == '__main__':
test = (
"You're seeking the {bold.on}blarney stone{{bold.off}? "
"You'll have to find {faction.Irish.leader.name}, "
"{faction.Irish.leader.pronoun_subjective}'ll know where it is."
)
tokens = brace_lexer(test)
pprint(tokens, indent=4)
[ (3, "You're seeking the "),
(1, 'bold.on'),
(3, 'blarney stone'),
(2, '{'),
(1, 'bold.off'),
(3, "? You'll have to find "),
(1, 'faction.Irish.leader.name'),
(3, ', '),
(1, 'faction.Irish.leader.pronoun_subjective'),
(3, "'ll know where it is.")]
tell_player("You need to travel to {guilds.%s.home_city} and see your guild master." % player.guild)
"{0.name} is a {0.gender} {0.race}".format(actor)However, unlike in your example, you cannot have arbitrary levels of depth in the formatting brace bits. But then you can just do:
"You're seeking the blarney stone? You'll have to find {0.name},Doing it this way will make it easy to cache fetched data in the local variables, for easy reuse in the string to be formatted.
{0.pronoun_subjective}'ll know where it is.".format(faction.leader('Irish'))
For example, say the player triggers a string that looks like
The situation being that the leader of the Irish faction could change; the NPC just refers to the current leader, but that information is stored in the game database and may not be in memory.
Intuitively it seems like you don't want to be hitting the database for every string that's processed, so I'm wondering if there are better ways to handle this, either by the way you do the interpolation or by the way you make this kind of information available from the database.
I don't have any real experience with databases, so any advice is appreciated.