Dynamic Book Template

Help bring our custom Ages to life! Share tips and tricks, as well as code samples with other developers.

Dynamic Book Template

Postby D'Lanor » Fri Dec 21, 2007 7:19 am

Here is a book template I made. You can use it if you like for your linking books or journals. The first version of this template was included in my test age Prad which was released for a short period through UAM in 2005. Since then I have been adding bits to it from time to time.

This is a template so all you have to do is define your variables and it will automagically generate the pages of the book. You can mix and match multiple linking pages and journals into the same book in any order you like. There is also support for multiple books (clickable objects). If you set up your variables correctly the template will make sure that the links point to the right age.

For each Python file you have to replace __YourAge__ in the filename and in the code with the actual name of your age. I left comments in the code to explain why I did things the way I did.

__YourAge__BookGUI.py
This is the file that the PythonFileMod for your clickable object points to. The variables to adapt here are: ageBooks, bookPages. Optional are the forceOwned setting for the call to the IOpenBook function and the IConvertAgeInstanceName function (see comments).

Code: Select all
######################################
#                                    #
#  Dynamic Book Template by D'Lanor  #
#                                    #
######################################
from Plasma import *
from PlasmaTypes import *
from PlasmaNetConstants import *
import xLinkingBookDefs
import __YourAge__PageDefs
actClickableObject = ptAttribActivator(1, 'Act: Clickable Object')
ObjectMsg = ptAttribString(2, 'Object String')
AgeStartedIn = None
ourBook = None
bkLinks = []
ageBooks = ('Journal', 'OtherJournal')
bookPages = (['PradJournal', 'CleftDrop'], ['FontTest'])
class __YourAge__BookGUI(ptModifier,):

    def __init__(self):
        ptModifier.__init__(self)
        self.version = '1'
        print ('__init__%s v.%s' % (self.__class__.__name__, self.version))



    def OnFirstUpdate(self):
        global AgeStartedIn
        print ('%s: OnFirstUpdate called' % self.__class__.__name__)
        AgeStartedIn = PtGetAgeName()



    def OnNotify(self, state, id, events):
        global ourBook
        global bkLinks
        print ('%s: OnNotify called' % self.__class__.__name__)
        if ((id == actClickableObject.id) and state):
            print 'Someone clicked an object'
            if (PtWasLocallyNotified(self.key)):
                print 'It was you'
                for i in range(0, len(ageBooks)):
                    if (ObjectMsg.value == ageBooks[i]):
                        print ('%s found! Start opening...' % ageBooks[i])
                        self.IOpenBook(bookPages[i], 0)
                        break
            ###################################################################################################
            # Pagenames in bookPages must be defined in the __YourAge__PageDefs file.                         #
            #                                                                                                 #
            # Opening the book with forceOwned set to 1: the code checks if the original book has been found. #
            # A player who does not own the age will not see the linking panel. By default forceOwned is off! #
            # If you need this restriction simply use: self.IOpenBook(bookPages[i], 1)                        #
            #                                                                                                 #
            # In case of multiple books the code will find any books automagically. You can add as many books #
            # as you like. Just define them in the global variables ageBooks and bookPages and make sure that #
            # their order in both variables matches.                                                          #
            ###################################################################################################
        else:
            for event in events:
                if (event[0] == PtEventType.kBook) and (PtWasLocallyNotified(self.key)):
                    print('BookNotify  event=%d, id=%d' % (event[1], event[2]))
                    if (event[1] == PtBookEventTypes.kNotifyImageLink):
                        if (event[2] >= xLinkingBookDefs.kFirstLinkPanelID):
                            print('BookNotify: hit linking panel %s' % event[2])
                            ourBook.hide()
                            for i in range(0, len(bkLinks)):
                                if (event[2] == bkLinks[i][0]):
                                    try:
                                        self.IlinkToAge(bkLinks[i][1], bkLinks[i][2], bkLinks[i][3], bkLinks[i][4])
                                    except Exception, detail:
                                        print('ERROR: Unable to initialize link - %s' % (detail))
                                    break
                    elif (event[1] == PtBookEventTypes.kNotifyShow):
                        print('Show Book')
                        PtSendKIMessage(kDisableKIandBB, 0)
                    elif (event[1] == PtBookEventTypes.kNotifyHide):
                        print('Hide Book')
                        PtSendKIMessage(kEnableKIandBB, 0)
                    elif (event[1] == PtBookEventTypes.kNotifyNextPage):
                        print('To Next Page %d' % (ourBook.getCurrentPage()))
                    elif (event[1] == PtBookEventTypes.kNotifyPreviousPage):
                        print('To Previous Page %d' % (ourBook.getCurrentPage()))
                    elif (event[1] == PtBookEventTypes.kNotifyCheckUnchecked):
                        print('Relto Page Toggled')
                    elif (event[1] == PtBookEventTypes.kNotifyClose):
                        print('Close Book')



    def IOpenBook(self, bkPages = None, forceOwned = 0):
        global ourBook
        global bkLinks
        print ('%s: IOpenBook: Page request for %s' % (self.__class__.__name__, bkPages))
        if (type(bkPages) == type(None)):
            return
        PageDef = __YourAge__PageDefs.BookDef + __YourAge__PageDefs.BookFont
        PageCount = xLinkingBookDefs.kFirstLinkPanelID
        bkLinks = []
        for bkPage in bkPages:
            if (not bkPage in __YourAge__PageDefs.LinkDestinations):
                print ('skipping %s because definition does not exist' % bkPage)
                continue
            # setting up the destination from the definition
            params = __YourAge__PageDefs.LinkDestinations[bkPage]
            (bkAge, spawnPoint, spTitle, linkRule,) = params
            alink = 1
            if (type(bkAge) != type(None) and forceOwned):
                print ('ownership check for %s book' % bkAge)
                vault = ptVault()
                ainfo = ptAgeInfoStruct()
                ainfo.setAgeFilename(bkAge)
                alink = vault.getOwnedAgeLink(ainfo)
            if alink:
                print ('showing page for %s' % bkAge)
                if (type(bkAge) != type(None)):
                    t = (PageCount, bkAge, spawnPoint, spTitle, linkRule)
                    bkLinks.append(t)
                    PageDef = PageDef + __YourAge__PageDefs.BookPages[bkPage] % (PageCount) + '<pb>'
                else:
                    PageDef = PageDef + __YourAge__PageDefs.BookPages[bkPage] + '<pb>'
                PageCount = PageCount + 1
            else:
                print ('No %s book on your shelf so we are not showing the link' % bkAge)
        if (PageCount == xLinkingBookDefs.kFirstLinkPanelID):
            print 'no pages created'
            return
        print ('linkingpages created: %d' % (len(bkLinks)))
        PageDef = PageDef[:-4]
        ourBook = ptBook(PageDef, self.key)
        ourBook.setSize(1.0, 1.0)
        ourBook.setGUI('BkBook')
        ourBook.show(0) # 0 = closed book, 1 = open book



    def IlinkToAge(self, ageName, spawnPoint, theTitle = None, linkRule = PtLinkingRules.kBasicLink):
        print ('%s: ILinkToAge: Link request for age %s' % (self.__class__.__name__, ageName))
        als = ptAgeLinkStruct()
        ainfo = ptAgeInfoStruct()
        ainfo.setAgeFilename(ageName)
        ainfo.setAgeInstanceName(self.IConvertAgeInstanceName(ageName))
        als.setAgeInfo(ainfo)
        if (type(theTitle) != type(None)):
            spTitle = theTitle
            ##################################################
            # Spawnpoint title is defined. Continue linking. #
            ##################################################
        else:
            if ((linkRule == PtLinkingRules.kOriginalBook) or PtIsSinglePlayerMode()):
                ##############################################################################
                # Linkingrule kOriginalBook writes spawnpoint and title to the agelink node, #
                # so we must make absolutely sure that a proper title is given!              #
                # In singleplayer mode all linkingrules behave like kOriginalBook, no matter #
                # which linkingrule is set in the definition.                                #
                ##############################################################################
                if (spawnPoint == 'LinkInPointDefault'):
                    spTitle = 'Default'
                    #################################################################################
                    # We did not define a spawnpoint title, but since the spawnpoint is the default #
                    # it is safe to continue and write it to the agelink node with default title.   #
                    #################################################################################
                else:
                    print 'Empty spawnpoint title not allowed, check your linking page definitions!'
                    return
            else:
                print 'Empty spawnpoint title allowed, continue linking'
                spTitle = ''
        als.setLinkingRules(linkRule)
        spPoint = ptSpawnPointInfo(spTitle, spawnPoint)
        als.setSpawnPoint(spPoint)
        linkMgr = ptNetLinkingMgr()
        linkMgr.linkToAge(als)
        print ('Linking to age %s, spawnpoint %s with title %s, using linkingrule %d' % (ageName, spawnPoint, spTitle, linkRule))



    def IConvertAgeInstanceName(self, ageName):
    #########################################################################################################################
    # Optional: You can add a friendly name to this list for the age you link to. This is the name that shows up in the KI. #
    # Ahra Pahts and Relto are used here only as examples.                                                                  #
    # Some Cyan age names are converted to friendly names in the KI but we cannot expect the KI to do that for user ages.   #
    # Beware: Age instance names are written to the vault if the kOriginalBook rule is used. That will happen whether you   #
    # set a name here or not. And it would happen regardless of this function.                                              #
    # By default the age instance name is the same as the age name.                                                         #
    # btw, if you plan to link to Cyan ages you should set the correct age instance name here. Correct? You figure it out!  #
    #########################################################################################################################
        if (ageName == 'Personal'):
            return 'Relto'
        if (ageName == 'Pahts'):
            return 'Ahra Pahts'
        return ageName


__YourAge__PageDefs.py
This is the file that contains the definitions for your book pages. You will do most of your editing here. __YourAge__PageDefs.py is imported into __YourAge__BookGUI.py. There are 3 sections: General layout elements, LinkDestinations and BookPages. Replace the examples with your own definitions.

Code: Select all
from Plasma import *
from PlasmaNetConstants import *
# Due to their length journal texts are usually stored in external Python files.
# The line below imports them. Remove this line if you do not use external journals.
from __YourAge__Journals import *

# Variables that can be used in BookPages:
# Retrieve local player name in case you want to fake a "personal" note
plyrName = PtGetLocalPlayer().getPlayerName()

# BookDef sets cover and margins of the book. The cover can be the same texture as used for the book object itself.
BookDef = '<cover src="__YourTexture__"><margin right=32 left=32>'

# BookFont is an example for font attributes.
BookFont = '<font size=12 face=Arial color=000000>'

PageStart = '<pb>'
ImgStart = '<img src="'
TransImgStart = '<img opacity=0.7 src="'
ImgEnd = '" align=center link=%d blend=alpha>'
ImgEndNoLink = '" align=center blend=alpha>'
AlignCenter = '<p align=center>'
AlignLeft = '<p align=left>'
AlignRight = '<p align=right>'

###############################################################################
# LinkDestinations Section:                                                   #
# 1 =    any name (this is used to call the definitions)                      #
# 2 =    age name                                                             #
# 3 =    spawnpoint                                                           #
# 4 =    spawnpoint title                                                     #
# 5 =    linkingrule                                                          #
#                                                                             #
# Notes: - Variables for journals are dummies and must be set to None.        #
#        - Spawnpoint title None is only allowed for LinkInPointDefault       #
#          (although it would be better to set it to 'Default' in that case). #
#        - CleftDrop in this example will deliberately fail (see above).      #
###############################################################################
LinkDestinations = {'BevinBalcony01': ('Neighborhood', 'LinkInPointBevinBalcony01', 'nb01BevinBalcony01', PtLinkingRules.kOriginalBook),
 'CleftDefault': ('Cleft', 'LinkInPointDefault', None, PtLinkingRules.kOriginalBook),
 'CleftDrop': ('Cleft', 'LinkInPointFissureDrop', None, PtLinkingRules.kOriginalBook),
 'KadishPyramid': ('Kadish', 'LinkInPointGlowRmBalcony', 'kdshGlowRmBalcony', PtLinkingRules.kOwnedBook),
 'FontTest': (None, None, None, None),
 'PradJournal': (None, None, None, None)}

###############################################################################################
# BookPages Section:                                                                          #
# 1 =    Same names as used under link destinations!                                          #
# 2 =    The page layout: for linking books insert the name of your linking panel image here. #
#        This can be a seperate texture in your age prp file not used by a Blender object     #
#        (not sure if Blender handles that: use PRP Explorer).                                #
#        Once you get the hang of this you can mix and match layouts into your pages.         #
#                                                                                             #
# Notes: - AlignCenter is used to center text, for example to place below a linking panel.    #
###############################################################################################
BookPages = {'BevinBalcony01': (PageStart + ImgStart + 'xLinkPanelBevinBalc01*1#0.hsm' + ImgEnd + AlignCenter),
 'CleftDefault': (PageStart + ImgStart + 'xLinkPanelCleftDesert*1#0.hsm' + ImgEnd + AlignCenter),
 'CleftDrop': (PageStart + ImgStart + 'xLinkPanelTomahnaDesert*1#0.hsm' + ImgEnd + '<font size=26 face=Uru color=221166>' + AlignCenter + 'Don\'t even think about it'),
 'KadishPyramid': (PageStart + ImgStart + 'xLinkPanelKadishGlowBalc*1#0.hsm' + ImgEnd + AlignCenter),
 'FontTest': (PageStart + DefFontTest),
 'PradJournal': (PageStart + (DefPradJournal % plyrName))}


__YourAge__Journals.py
Optional. A file that contains journal texts. Since journal texts are usually rather long we can store them in an external file which is imported into __YourAge__PageDefs.py. Replace the examples with your own texts.

Code: Select all
# Examples of journals. DefFontTest shows the fonts available in the offline version.
DefFontTest = '<font size=24 face=Courier><p align=center>Journal Test\n\n<font size=20 face=Atrus color=800000><p align=left>ATRUS\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=20 face=Uru color=008000><p align=left>URU\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Nick color=000080><p align=left>NICK\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Sam color=808000><p align=left>SAM\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Tricia color=008080><p align=left>TRICIA\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Michelle color=800080><p align=left>MICHELLE\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=12 face=DniFontDniHand color=000000><p align=left>DNI HAND\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=18 face=Yeesha color=404040><p align=left>YEESHA\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9'
DefPradJournal = '<font size=24 face=Uru color=221166><p align=left>Shorah %s,\n\nAn example which shows how to address players by their own names in a journal.'
"It is in self-limitation that a master first shows himself." - Goethe
User avatar
D'Lanor
 
Posts: 1980
Joined: Sat Sep 29, 2007 4:24 am

Re: Dynamic Book Template

Postby andylegate » Fri Dec 21, 2007 4:19 pm

Cool! Thanks D'Lanor!
"I'm still trying to find the plKey for Crud!"
Image
Blender Age Creation Tutorials
3DS Max Age Creation Tutorials
User avatar
andylegate
 
Posts: 2348
Joined: Mon Oct 01, 2007 7:47 am

Re: Dynamic Book Template

Postby andylegate » Tue Jan 29, 2008 2:05 pm

Here is the syntax error I got while simply trying to compile the _YourAge_Journals.py file into a .pak file.

EDIT: just to clarify some things, the following is the error report given to me by PlasmaShop. It is NOT code. For the code, look in the above posts, specifically D'Lanor's _YourAge_Journals.py listing. That's the code. And if you paste it into PlasmaShop, yes it does put it all on one line.

Compiling all uncompiled files...

Listing C:\DOCUME~1\Andy\LOCALS~1\Temp\tmp753.tmp ...
Compiling C:\DOCUME~1\Andy\LOCALS~1\Temp\tmp753.tmp\_YourAge_Journals.py ...
File "C:\DOCUME~1\Andy\LOCALS~1\Temp\tmp753.tmp\_YourAge_Journals.py", line 2
DefFontTest = '<font size=24 face=Courier><p align=center>Journal Test\n\n<font size=20 face=Atrus color=800000><p align=left>ATRUS\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=20 face=Uru color=008000><p align=left>URU\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Nick color=000080><p align=left>NICK\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Sam color=808000><p align=left>SAM\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Tricia color=008080><p align=left>TRICIA\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Michelle color=800080><p align=left>MICHELLE\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=12 face=DniFontDniHand color=000000><p align=left>DNI HAND\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=18 face=Yeesha color=404040><p align=left>YEESHA\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9'
^
SyntaxError: invalid syntax
US\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=20 face=Uru color=008000><p align=left>URU\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Nick color=000080><p align=left>NICK\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Sam color=808000><p align=left>SAM\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Tricia color=008080><p align=left>TRICIA\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=14 face=Michelle color=800080><p align=left>MICHELLE\nThe quick brown fox jumped over the lazy dog.\n0 1 2 3 4 5 6 7 8 9\n\n<font size=12 face=DniFontDniHand color=000000><p align=left>DNI HAND\nThe quick brown fox ju
Done! Be sure to check the output for errors...
Last edited by andylegate on Tue Jan 29, 2008 3:55 pm, edited 2 times in total.
"I'm still trying to find the plKey for Crud!"
Image
Blender Age Creation Tutorials
3DS Max Age Creation Tutorials
User avatar
andylegate
 
Posts: 2348
Joined: Mon Oct 01, 2007 7:47 am

Re: Dynamic Book Template

Postby Trylon » Tue Jan 29, 2008 3:22 pm

Looks like you used double quotes, but didn't type all the text on one line in python.... That cause errors

Within normal quotes (double or single) you can't have multiline strings..

However, you can use """ to make multiline strings:
Code: Select all
myvar = """Now I can actually
Type in a multiline string and have it
compile without errors
"""
One day I ran through the cleft for the fiftieth time, and found that uru held no peace for me anymore.
User avatar
Trylon
 
Posts: 1446
Joined: Fri Sep 28, 2007 11:08 pm
Location: Gone from Uru

Re: Dynamic Book Template

Postby andylegate » Tue Jan 29, 2008 3:52 pm

*coughs into hand*,

Uhm, yes, the content WAS all on one line in PlasmaShop.

And there were no double quotes in it at all.

The mistake, I think, is showing the error report the the forum's code box. Let me edit the post and I'll put it in quotes instead.
"I'm still trying to find the plKey for Crud!"
Image
Blender Age Creation Tutorials
3DS Max Age Creation Tutorials
User avatar
andylegate
 
Posts: 2348
Joined: Mon Oct 01, 2007 7:47 am

Re: Dynamic Book Template

Postby Paradox » Tue Jan 29, 2008 4:36 pm

Is the line of code indented in the .py file?
Paradox
 
Posts: 1290
Joined: Fri Sep 28, 2007 6:48 pm
Location: Canada

Re: Dynamic Book Template

Postby andylegate » Tue Jan 29, 2008 4:47 pm

Yes, 4 spaces and then the charactor start.

If you select D'lanor's code and paste it into a new file in PlasmaShop, it's indented.
"I'm still trying to find the plKey for Crud!"
Image
Blender Age Creation Tutorials
3DS Max Age Creation Tutorials
User avatar
andylegate
 
Posts: 2348
Joined: Mon Oct 01, 2007 7:47 am

Re: Dynamic Book Template

Postby Paradox » Tue Jan 29, 2008 5:03 pm

Odd... when I paste it into gedit it's not indented, and judging from the context, it shouldn't be indented.
Paradox
 
Posts: 1290
Joined: Fri Sep 28, 2007 6:48 pm
Location: Canada

Re: Dynamic Book Template

Postby andylegate » Tue Jan 29, 2008 5:07 pm

oh for the love of............*growl*, *grumble*, *grrrrr*!!!!!

See? See? I don't know scripting.

I took out the indent.....and tahdah, it compiled just fine with no errors. I opened up my journal one, and same result, after taking out the indent........

:roll:

Crud......

I just looked at D'lanor's other templates. They are all indented too.......

EDIT:
Okay, for PlasmaShop, for some reason, if you click on Select All in the code box here on the forum, then paste it into a new python file, it indents every line.
If, on the other hand, you manually select the code, and copy it, then paste it into a new python file, it doesn't indent it.
"I'm still trying to find the plKey for Crud!"
Image
Blender Age Creation Tutorials
3DS Max Age Creation Tutorials
User avatar
andylegate
 
Posts: 2348
Joined: Mon Oct 01, 2007 7:47 am

Re: Dynamic Book Template

Postby D'Lanor » Tue Jan 29, 2008 5:29 pm

Sorry, I got into the discussion late. So it is solved now?

btw, in UltraEdit I also get the extra indentation when I use Select All. Can that forum function be changed so that it makes a correct copy?
"It is in self-limitation that a master first shows himself." - Goethe
User avatar
D'Lanor
 
Posts: 1980
Joined: Sat Sep 29, 2007 4:24 am

Next

Return to Scripting

Who is online

Users browsing this forum: No registered users and 0 guests