1. The User Age Chronicle Model
A vault chronicle model for fan made ages.
Sometimes a puzzle requires the storage of personal player progress which is not tied to an age state. Each avatar already has its own location in the vault to store personal variables: the ChronicleFolder.
Since we want to keep the ChronicleFolder in an orderly state I propose ONE base chronicle for all fan made ages called (for example) 'UserAges'. This chronicle should be used by ALL fan made ages. Within that chronicle age creators can add unique subchronicles for their ages.
Each user age subchronicle in turn contains 3rd layer subchronicles to store player variables. Note that in this model we will write one layer deeper than any of the Cyan subchronicles:
- Code: Select all
ChronicleFolder
|
|
|__Cyan Chronicle-1
|
|__Cyan Chronicle-2
| |
| |__Cyan Subchronicle-1
| |
| |__Cyan Subchronicle-2
|
|__Cyan Chronicle-3
|
|
|__UserAges
| |
|
|__Age-1
| |
| |__Variable-1
| |
| |__Variable-2
|
|
|__Age-2
| |
|__Variable-1
|
L1 L2 L3
This model has already been successfully used in UU for the Walker game (limited release).
UU experience has shown that multiple chronicle layers cannot be created in a single step. But more importantly, it is not desirable to let the fan made age create the base chronicle. Therefore the 3 layers are created one by one at an appropriate time.
1.1. Layer 1 - Base Chronicle
A separate function should take care of creating the base chronicle 'UserAges', preferably a function implemented by Cyan. If there is going to be a hub age for accessing user ages the best moment to create the base chronicle would be upon entering the hub. If this hub is Relto or Nexus it should be done there.
1.2. Layer 2 - Age Chronicle
In order to avoid confusion as to which user age added a chronicle, the age chronicle should have the same name as the user age (unless the puzzle spans multiple ages). The best time of creation for the age chronicle is when the player first enters the starting age of the puzzle.
1.3. Layer 3 - Player Variables
Creation and modification is determined by game events.
2. Chronicle Layers in Detail
This section contains the Python code needed to read and write chronicles. Since navigating through 3 layers of chronicle vaultnodes can get pretty elaborate the functions provided here are ready to go and do not need any alteration (at least for TPOTS or UU).
2.1. Layer 1 - Base Chronicle
Setting up the base chronicle through Python is rather straightforward. As mentioned previously I would prefer this to be implemented by Cyan. The base chronicle should be written to the vault upon entering the hub where fan made ages are accessed (if any).
- Code: Select all
# Python code by D'Lanor
from Plasma import *
from PlasmaTypes import *
from PlasmaKITypes import *
# Although the use of chronicle types seems to be obsolete we
# can define a unique type for this just in case.
# This definition should be added to the PlasmaKITypes module.
kChronicleUserAgeType = 4
def ISetBaseChron(self):
vault = ptVault()
entry = vault.findChronicleEntry('UserAges')
if (type(entry) == type(None)):
PtDebugPrint('ISetBaseChron: Creating base chronicle for all user ages')
vault.addChronicleEntry('UserAges', kChronicleUserAgeType, '')
2.2. Layer 2 - Age Chronicle
Requirement: The base chronicle 'UserAges' must exist.
The age creator implements the function which creates the chronicle for his/her age. Creation takes place when the player first enters the starting age of the puzzle.
- Code: Select all
# Python code by D'Lanor
from Plasma import *
from PlasmaTypes import *
def ISetAgeChron(self, ageName):
if (not ageName):
return
ourChild = None
vault = ptVault()
entry = vault.findChronicleEntry('UserAges')
if (type(entry) == type(None)):
PtDebugPrint('DEBUG: No UserAges chronicle')
return
chronRefList = entry.getChildNodeRefList()
for ourChron in chronRefList:
chronChild = ourChron.getChild()
chronChild = chronChild.upcastToChronicleNode()
if (chronChild.chronicleGetName() == ageName):
ourChild = chronChild
break
if (type(ourChild) == type(None)):
PtDebugPrint('ISetAgeChron: Creating subchronicle for age %s' % ageName)
newNode = ptVaultChronicleNode(0)
newNode.chronicleSetName(ageName)
newNode.chronicleSetValue('')
entry.addNode(newNode)
Example
Write the age chronicle called 'Prad' within the base chronicle 'UserAges':
- Code: Select all
self.ISetAgeChron('Prad')
Result:
- Code: Select all
ChronicleFolder
|
|
|__UserAges
|
|
|__Prad
Note that 'UserAges' must already exists for this function to work.
2.3. Layer 3 - Player Variables
Requirements: The base chronicle 'UserAges' must exist. The age chronicle must exist.
These are the chronicle core functions that age creators will be using for their variables. These Python functions are designed to work only with layer 3 chronicles!
- Code: Select all
# Python code by D'Lanor
from Plasma import *
from PlasmaTypes import *
# User Age Chronicle Core Functions (Generic for all user ages)
#
# IWriteAgeChron: Writes a layer 3 subchronicle for the user age.
# Be sure to always pass the correct name to the ageName argument.
#
# IReadAgeChron: Reads a layer 3 subchronicle and returns the value.
# Notes: The function returns an integer 0 if the subchronicle does not exist.
# Existing chronicle values are always returned as a strings.
def IWriteAgeChron(self, ageName, chronName, chronVal):
ourChild = None
vault = ptVault()
entry = vault.findChronicleEntry('UserAges')
if (type(entry) == type(None)):
PtDebugPrint('DEBUG: No UserAges chronicle')
return
chronRefList = entry.getChildNodeRefList()
for xpChron in chronRefList:
xpChild = xpChron.getChild()
xpChild = xpChild.upcastToChronicleNode()
if (xpChild.chronicleGetName() == ageName):
PtDebugPrint('IWriteAgeChron: Age chronicle found')
chronRefList2 = xpChild.getChildNodeRefList()
for xpChron2 in chronRefList2:
xpChild2 = xpChron2.getChild()
xpChild2 = xpChild2.upcastToChronicleNode()
if (xpChild2.chronicleGetName() == chronName):
ourChild = xpChild2
break
if (type(ourChild) == type(None)):
PtDebugPrint('IWriteAgeChron: Creating age subchronicle with specified value')
newNode = ptVaultChronicleNode(0)
newNode.chronicleSetName(chronName)
newNode.chronicleSetValue(chronVal)
xpChild.addNode(newNode)
else:
if (ourChild.chronicleGetValue() == chronVal):
PtDebugPrint('IWriteAgeChron: Age subchronicle value already correct, do nothing')
return
PtDebugPrint('IWriteAgeChron: Changing value of age subchronicle')
ourChild.chronicleSetValue(chronVal)
ourChild.save()
def IReadAgeChron(self, ageName, chronName):
vault = ptVault()
entry = vault.findChronicleEntry('UserAges')
if (type(entry) == type(None)):
PtDebugPrint('DEBUG: No UserAges chronicle')
return 0
chronRefList = entry.getChildNodeRefList()
for xpChron in chronRefList:
xpChild = xpChron.getChild()
xpChild = xpChild.upcastToChronicleNode()
if (xpChild.chronicleGetName() == ageName):
PtDebugPrint('IReadAgeChron: Age chronicle found')
chronRefList2 = xpChild.getChildNodeRefList()
for xpChron2 in chronRefList2:
xpChild2 = xpChron2.getChild()
xpChild2 = xpChild2.upcastToChronicleNode()
if (xpChild2.chronicleGetName() == chronName):
PtDebugPrint('IReadAgeChron: Age subchronicle found, retrieve value')
chronVal = xpChild2.chronicleGetValue()
return chronVal
PtDebugPrint('IReadAgeChron: Chronicle missing')
return 0
Example 1
Write the age subchronicle called 'JournalSeen' within the 'Walker' age chronicle and give it value 1:
- Code: Select all
self.IWriteAgeChron('Walker', 'JournalSeen', '1')
Result:
- Code: Select all
ChronicleFolder
|
|
|__UserAges
|
|
|__Walker
|
|__JournalSeen (value = 1)
The function 'IWriteAgeChron' handles all situations. It verifies if the chronicle to be modified exists. If not, it creates the chronicle with the specified value.
If the chronicle is already there the function changes its value.
If the chronicle is there and already has the correct value the function does nothing.
Note that 'UserAges' and 'Walker' must already exists for this function to work.
Example 2
Read the value of the age subchronicle 'JournalSeen' and then increase its value by 1:
- Code: Select all
jrnlLevel = self.IReadAgeChron('Walker', 'JournalSeen')
if (not jrnlLevel):
self.IWriteAgeChron('Walker', 'JournalSeen', '1')
return
nextLevel = int(jrnlLevel) + 1
self.IWriteAgeChron('Walker', 'JournalSeen', str(nextLevel))
Note 1: In order to do calculations with chronicles we have to convert the datatype to integer. In order to write the calculated value to a chronicle we convert it back to a string.
Note 2: The function IReadAgeChron returns integer 0 when it does not find the chronicle value. So we can use an "if not" statement to check for that condition and abort (return). Optionally we can do other stuff such as creating the chronicle / giving it a value (see example).