Important! Please read the announcement at http://myst.dustbird.net/uru-account.htm
Also! Please read the retirement announcement at http://myst.dustbird.net/uru-retirement.htm

Creating Linking Books And Journals

From UamWiki

This is not really that hard, but it does take some thinking and extra work on your part, especially if it is a Journal.


What You'll Need

Obviously Blender with the GoW plugin for making Ages.

PlasmaShop or another program that will allow you to edit or make Python files.

That's it.


Getting Started

Okay, the first thing we need to do is: Think.

What is it you are trying to do? Journal? or Linking Book?

For most of this, the work you do to create either will be the same. But their are some differences.

For a journal, you need to sit down, think of what you want to say. Are there sketches or pics?

For a linking book, you'll need to think of what it is you want the player to see. When they first walk upon it: is the book laying there closed or open? What picture will they see on the linking panel.

Once you've thought out these things, it's time to get busy in Blender.


Blender Stuff

It should be rather obvious that you'll need to make a book and have it in your Age.

Jrnl1.png

How you make your book is really up to you. But you MUST do things to make it clickable. If you don't, no clicky I'm afraid.

First, once you get the book built and textured (open or closed), you need to go to the logic panel for the book, or for where the avatar is going to click, and make sure you have "Actor" set and Bounds set to convex Hull.

Jrnl4.png

Okay, now you need to make a click region:

Jrnl2.png

Make sure the click region's logic is set up like this:

Jrnl3.png

okay, now you also could of set it up to where your book is laying open. Most linking book in Uru are this way: laying there with their panels showing:

Jrnl5.png

Now, there's a few more things you need to do:

If your book is going to be a journal, and close when it pop's up, you need to put a plane mesh somewhere out of sight, and put the book cover texture on it. Make sure you rename the Material and the Texture to match the covername.

If your book is a Linking book, you will need a linking panel, a pic showing the player where to link to. To do this, again, you'd make a plane out of sight and texture it with an image. However, you're going to be doing this slightly different.

Jrnl7.png

As you can see from the pic above, I have a lot of different planes with pics on them. That's because this example is from my Nexus I made.

Each texture that is on the planes is 512 x 256. But wait! Let me explain a bit better.

The OVERALL image size is 512 x 256. The actual picture or image is 400 x 160 centered on a transparent background that is 512 x 256.

This gives it a stretched out look, but in game, when the linking book pops up, it will look just fine.

EDIT: Make sure you have the Mipmap and Interpol buttons in the texture panel turned off! If you don't, you won't see the linking panel when the book pops up. This also goes for any pics or sketches you do for a Journal.

Okay, last in Blender: ALCScript:

Jrnl6.png

As you can see the ALCScript is not that hard. What is hard is the name of the Python file, in that you need to think up a name, then right it down. I would HIGHLY recommend that whatever name you come up with, that it ends with "BookGUI"

Okay, you're done in Blender. Time to get down and dirty with Python.


Python: Ssssssssssssss......

Yah, I know. A lot of you out there hate Python. But it's important, and can be very powerfull. I've been doing it a while now and I want to run away screaming too!

D'Lanor took some time and made a template for us to use. He did an outstanding job, and the more you use his template, the easier it gets to understand.

First however, you are going to need a "Dummy" python file. This is the python file that is the same name as your Age, but doesn't have anything else in it (unless you've put something there.)

If you are using PlasmaShop, just go up to where it says "File" and click on "New" and then select "Python"

Pyth1.png

You should have a new python file come up:

Pyth2.png

There are a couple of things you need to change here. You need to change "myResponder" that is in blue to what ever the name of you Age is. And you need to insert a unique id number. Here's a pic, and my arrows point to where you need to change:

Pyth3.png

Once you've done that, just save the python file with the EXACT same name as your Age.

We are done that that file for now. Time to look at and use D'Lanor's templates.

To make a Linking Book, you'll need at least 2 files:

A "BookGUI" file and a "PageDefs" file.

For a Journal, you'll need both of those up there AND a 3rd file called "Journals"

Now, they are not called just that! You need to make each of these files unique. So you add your Age name to them! Or what ever you want, just remember to make them UNIQUE you don't want to conflict with Cyan's or someone else's Age!

So you're file names will look something like: "AndyNexusBookGUI" and "AndyNexusPageDefs" and "AndyNexusJournals"

Of course the BookGUI one needs to be THE SAME NAME AS WHAT YOU PUT IN THE ALCSCRIPT OF BLENDER!!!

Now, here is the first python file, the BookGUI file. You can simply copy and paste all of this into PlasmaShop in a new python file. I've included the Glue for you folks, so you don't need to do that:

###########################################
#                                         #
#  Dynamic Book Template v3.0 by D'Lanor  #
#                                         #
###########################################

# For each occurance of *YourAge* you have to replace this with the actual name of your age.

from Plasma import *
from PlasmaTypes import *
from PlasmaKITypes import *
from PlasmaNetConstants import *
import xLinkingBookDefs
actClickableObject = ptAttribActivator(1, 'Act: Clickable Object')
ObjectMsg = ptAttribString(2, 'Object String')

### DO NOT CHANGE THESE 2 VARIABLES ###
ourBook = None
bkLinks = []

### CHANGE THE GLOBAL CONSTANTS BELOW TO FIT YOUR AGE ######################################
#                                                                                          #
# modPageDefs: Replace *YourAge*PageDefs with the name of the module that contains your    #
#              page definitions. Do not remove the quotes!                                 #
# ageBooks:    These are your clickable objects. They must be defined in *YourAge*PageDefs #
#              in the AgeBooks Dictionary Section by the same name(s).                     #
# bookPages:   Names in bookPages must be defined in the *YourAge*PageDefs file under      #
#              BookPages. An ageBook can have multiple bookPages as long as they are       #
#              enclosed together between the same square brackets.                         #
#              Please keep the nesting structure [square brackets] intact!                 #
#                                                                                          #
# If there are multiple books in an age the code will find the right one automagically.    #
# You can add as many books as you like. Just define them in the global variables ageBooks #
# and bookPages. The order is important here. The first book will be matched to the first  #
# page list, the second book to the second page list etc.                                  #
############################################################################################

modPageDefs = __import__('*YourAge*PageDefs')
ageBooks = ['Journal01', 'Journal02']
bookPages = [['PradJournal', 'CleftSpecial'], ['FontTest']]

class *YourAge*BookGUI(ptModifier,):

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



    def OnNotify(self, state, id, events):
        print ('%s: OnNotify called' % self.__class__.__name__)
        if ((id == actClickableObject.id) and state):
            print ('Someone clicked on object %s' % ObjectMsg.value)
            if PtWasLocallyNotified(self.key):
                print 'It was you'
                for (a, b,) in zip(ageBooks, bookPages):
                    print ('Trying book %s with page(s) %s' % (a, b))
                    if (ObjectMsg.value == a):
                        print 'Match found! Start opening book...'
                        self.IOpenBook(a, b)
                        break
                    else:
                        print 'No match'

        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):
                        print ('BookNotify: Linking panel id=%d, event=%d' % (event[2], event[1]))
                        ourBook.hide()
                        if (event[2] == 0):
                            print 'Warning: No link id, define proper link destination or use non-clickable image'
                        elif (event[2] >= xLinkingBookDefs.kFirstLinkPanelID):
                            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], bkLinks[i][5])
                                    except Exception, detail:
                                        print ('ERROR: Unable to initialize link - %s' % detail)
                                    break

                    elif (event[1] == PtBookEventTypes.kNotifyShow):
                        print ('BookNotify: Show book, event=%d' % event[1])
                        PtToggleAvatarClickability(0)
                    elif (event[1] == PtBookEventTypes.kNotifyHide):
                        print ('BookNotify: Hide book, event=%d' % event[1])
                        PtToggleAvatarClickability(1)
                    elif (event[1] == PtBookEventTypes.kNotifyNextPage):
                        print ('BookNotify: To next page %d, event=%d' % (ourBook.getCurrentPage(), event[1]))
                    elif (event[1] == PtBookEventTypes.kNotifyPreviousPage):
                        print ('BookNotify: To previous page %d, event=%d' % (ourBook.getCurrentPage(), event[1]))
                    elif (event[1] == PtBookEventTypes.kNotifyCheckUnchecked):
                        print ('BookNotify: Relto page toggle, event=%d' % event[1])
                    elif (event[1] == PtBookEventTypes.kNotifyClose):
                        print ('BookNotify: Close book, event=%d' % event[1])




    def IOpenBook(self, ageBook, bkPages = None):
        global ourBook
        global bkLinks
        print ('%s: IOpenBook: Page(s) requested %s' % (self.__class__.__name__, bkPages))
        if (type(bkPages) == type(None)):
            print 'ERROR: no pages defined'
            return
        if (not (ageBook in modPageDefs.AgeBooks)):
            print ('ERROR: Definition %s does not exist in AgeBooks' % ageBook)
            return
        bkParams = modPageDefs.AgeBooks[ageBook]
        (bkCover, bkFont, startOpen, forceOwned, bookGUI, width, height,) = bkParams
        PageDef = (bkCover + bkFont)
        if (not startOpen):
            if (not self.IsThereACover(PageDef)):
                print 'Warning: Missing cover, forcing book open'
                startOpen = 1
        PageCount = xLinkingBookDefs.kFirstLinkPanelID
        bkLinks = []
        for bkPage in bkPages:
            if (not (bkPage in modPageDefs.LinkDestinations)):
                print ('Warning: %s skipped, definition does not exist in LinkDestinations' % bkPage)
                continue
            pgParams = modPageDefs.LinkDestinations[bkPage]
            (bkAge, spawnPoint, spTitle, linkRule,) = pgParams
            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 %s, link destination %s' % (bkPage, bkAge))
                if (type(bkAge) != type(None)):
                    t = (PageCount, bkPage, bkAge, spawnPoint, spTitle, linkRule)
                    bkLinks.append(t)
                    PageDef = ((PageDef + (modPageDefs.BookPages[bkPage] % PageCount)) + '<pb>')
                else:
                    PageDef = ((PageDef + modPageDefs.BookPages[bkPage]) + '<pb>')
                PageCount = (PageCount + 1)
            else:
                print ('No owner of age %s so we are not showing %s' % (bkAge, bkPage))

        if (PageCount == xLinkingBookDefs.kFirstLinkPanelID):
            print 'No pages created...'
            return
        else:
            TotalCount = (PageCount - xLinkingBookDefs.kFirstLinkPanelID)
        print ('%d item(s) created, linking page(s): %d' % (TotalCount, len(bkLinks)))
        PageDef = PageDef[:-4]
        ourBook = ptBook(PageDef, self.key)
        ourBook.setSize(width, height)
        ourBook.setGUI(bookGUI)
        ourBook.show(startOpen)



    def IsThereACover(self, bookHtml):
        idx = bookHtml.find('<cover')
        if (idx >= 0):
            return 1
        return 0



    def IlinkToAge(self, bookPage, ageName, spawnPoint, spTitle, linkRule = PtLinkingRules.kBasicLink):
        print ('%s: ILinkToAge: Link request from page %s to age %s' % (self.__class__.__name__, bookPage, ageName))
        if ((type(ageName) == type(None)) or (len(ageName) == 0)):
            print 'ERROR: Cannot link to age without name'
            return
        if ((type(spawnPoint) == type(None)) or (len(spawnPoint) == 0)):
            print 'No spawnpoint defined, checking special actions...'
            self.IDoSpecialAction(bookPage)
            return
        als = ptAgeLinkStruct()
        ainfo = ptAgeInfoStruct()
        ainfo.setAgeFilename(ageName)
        ainfo.setAgeInstanceName(self.IConvertAgeInstanceName(ageName))
        als.setAgeInfo(ainfo)
        if ((type(spTitle) == type(None)) or (len(spTitle) == 0)):
            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 'Warning: Empty spawnpoint title not allowed, check your link destinations!'
                    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



    def IDoSpecialAction(self, bookPage):
    #######################################################
    # Replace *YourPage* with the bookPage that contains  #
    # the clickable image triggering the special action.  #
    #######################################################
        if (bookPage == '*YourPage*'):
            print ('Special action found for %s' % bookPage)
            # Do some special action here
        else:
            print 'No special actions found'



# Insert the standard GLUE section for your Uru version here. You can copy this from another Python file.



### Python Glue ###

glue_cl = None
glue_inst = None
glue_params = None
glue_paramKeys = None
try:
    x = glue_verbose
except NameError:
    glue_verbose = 0

def glue_getClass():
    global glue_cl
    if (glue_cl == None):
        try:
            cl = eval(glue_name)
            if issubclass(cl, ptModifier):
                glue_cl = cl
            elif glue_verbose:
                print ('Class %s is not derived from modifier' % cl.__name__)
        except NameError:
            if glue_verbose:
                try:
                    print ('Could not find class %s' % glue_name)
                except NameError:
                    print 'Filename/classname not set!'
    return glue_cl


def glue_getInst():
    global glue_inst
    if (type(glue_inst) == type(None)):
        cl = glue_getClass()
        if (cl != None):
            glue_inst = cl()
    return glue_inst


def glue_delInst():
    global glue_inst
    global glue_cl
    global glue_paramKeys
    global glue_params
    if (type(glue_inst) != type(None)):
        del glue_inst
    glue_cl = None
    glue_params = None
    glue_paramKeys = None


def glue_getVersion():
    inst = glue_getInst()
    ver = inst.version
    glue_delInst()
    return ver


def glue_findAndAddAttribs(obj, glue_params):
    if isinstance(obj, ptAttribute):
        if glue_params.has_key(obj.id):
            if glue_verbose:
                print 'WARNING: Duplicate attribute ids!'
                print ('%s has id %d which is already defined in %s' %
                      (obj.name, obj.id, glue_params[obj.id].name))
        else:
            glue_params[obj.id] = obj
    elif type(obj) == type([]):
        for o in obj:
            glue_findAndAddAttribs(o, glue_params)
    elif type(obj) == type({}):
        for o in obj.values():
            glue_findAndAddAttribs(o, glue_params)
    elif type(obj) == type(()):
        for o in obj:
            glue_findAndAddAttribs(o, glue_params)


def glue_getParamDict():
    global glue_paramKeys
    global glue_params
    if type(glue_params) == type(None):
        glue_params = {}
        gd = globals()
        for obj in gd.values():
            glue_findAndAddAttribs(obj, glue_params)
        glue_paramKeys = glue_params.keys()
        glue_paramKeys.sort()
        glue_paramKeys.reverse()
    return glue_params


def glue_getClassName():
    cl = glue_getClass()
    if (cl != None):
        return cl.__name__
    if glue_verbose:
        print ('Class not found in %s.py' % glue_name)
    return None


def glue_getBlockID():
    inst = glue_getInst()
    if (inst != None):
        return inst.id
    if glue_verbose:
        print ('Instance could not be created in %s.py' % glue_name)
    return None


def glue_getNumParams():
    pd = glue_getParamDict()
    if (pd != None):
        return len(pd)
    if glue_verbose:
        print ('No attributes found in %s.py' % glue_name)
    return 0


def glue_getParam(number):
    pd = glue_getParamDict()
    if (pd != None):
        if type(glue_paramKeys) == type([]):
            if (number >= 0) and (number < len(glue_paramKeys)):
                return pd[glue_paramKeys[number]].getdef()
            else:
                print ('glue_getParam: Error! %d out of range of attribute list' % number)
        else:
            pl = pd.values()
            if (number >= 0) and (number < len(pl)):
                return pl[number].getdef()
            elif glue_verbose:
                print ('glue_getParam: Error! %d out of range of attribute list' % number)
    if glue_verbose:
        print 'GLUE: Attribute list error'
    return None


def glue_setParam(id, value):
    pd = glue_getParamDict()
    if (pd != None):
        if pd.has_key(id):
            try:
                pd[id].__setvalue__(value)
            except AttributeError:
                if isinstance(pd[id], ptAttributeList):
                    try:
                        if type(pd[id].value) != type([]):
                            pd[id].value = []
                    except AttributeError:
                        pd[id].value = []
                    pd[id].value.append(value)
                else:
                    pd[id].value = value
        elif glue_verbose:
            print "setParam: can't find id=", id
    else:
        print 'setParma: Something terribly has gone wrong. Head for the cover.'


def glue_isNamedAttribute(id):
    pd = glue_getParamDict()
    if (pd != None):
        try:
            if isinstance(pd[id], ptAttribNamedActivator):
                return 1
            if isinstance(pd[id], ptAttribNamedResponder):
                return 2
        except KeyError:
            if glue_verbose:
                print ('Could not find id=%d attribute' % id)
    return 0


def glue_isMultiModifier():
    inst = glue_getInst()
    if isinstance(inst, ptMultiModifier):
        return 1
    return 0


def glue_getVisInfo(number):
    pd = glue_getParamDict()
    if pd != None:
        if type(glue_paramKeys) == type([]):
            if (number >= 0) and (number < len(glue_paramKeys)):
                return pd[glue_paramKeys[number]].getVisInfo()
            else:
                print ('glue_getVisInfo: Error! %d out of range of attribute list' % number)
        else:
            pl = pd.values()
            if (number >= 0) and (number < len(pl)):
                return pl[number].getVisInfo()
            elif glue_verbose:
                print ('glue_getVisInfo: Error! %d out of range of attribute list' % number)
    if glue_verbose:
        print 'GLUE: Attribute list error'
    return None

I know it's a lot of stuff. Don't get scared, there are only a few things you need to change in here, and then you can save it.

Okay, here's a pic of the first thing you need to change:

Pyth4.png

Change *YourAge*PageDefs to the name of the PageDefs file you will be making. Normally its just like you see, the name of your Age, for example: "AndyNexusPageDefs"

Next, look at this pic:

Pyth5.png

On that line there, you need to change Journal01 and Journal02 to the names of your books, or the name of the object you click on. So if the name of the book was "HarrysJournal" then change it to that. If there is more than one (as shown here, D'Lanor is showing us 2 books that you click on) you would use that comma. For example:

ageBooks = ['Journal01', 'Journal02'] 
would be changed to:
ageBooks = ['HarrysJournal'] 
if there was just the one book, or
ageBooks = ['HarrysJournal', 'JoesLinkBook']
if there were two. Using the comma, you can keep adding until you have all your books.

Okay, now for the 3rd thing to change:

Pyth6.png

bookPages are names you are going to give the pages for your books, be they Journals or Linking books, in your PageDefs file. Now I know, you have not made your PageDefs file yet. No problem. Get some paper and a pen and write them down as you make them up.

Now here I'll give you and example, from the ones I used above, Harry and Joe:

bookPages = [['HarrysPage'], ['JoesPage']]

Notice the double brackets. You need to leave those in. Notice too that I have each page for a different book in their own bracket. Let's say you wanted to get fancy, and have a linking panel in Harry's journal. Well, you'd need to define that page, but with in his bracket:

bookPages = [['HarrysPage', 'HarrysLinkPage'], ['JoesPage']]

That's ALL you need to do with this file. IF you want to get even fancier, you can scroll down and read D'Lanor's comments within the Python file. He shows you some other things you can do here, but I'm not going to cover those as this is a basic tutorial.

Save this Python file, making sure you have BookGUI in the last part of the file name.


PageDefs Python File

Okay, now we need to work on the PageDefs python file. This is the one where you'll be doing most of your work.

Here is the template. Again, you can just copy and past this into a new Python file in Plasmashop:

# For each occurance of *YourAge* and *YourTexture* you have to replace this with
# the actual name of your age and your textures.

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 *

# General layout elements that can be used in BookPages:
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>'
# Retrieve local player name in case you want to fake a "personal" note
plyrName = PtGetLocalPlayer().getPlayerName()

###############################################################################
# AgeBooks Dictionary Section:                                                #
# 1 =    same names as the clickable book objects!                            #
# 2 =    book cover and margin                                                #
# 3 =    main font                                                            #
# 4 =    start book open or closed. 0 closed book, 1 open book                #
# 5 =    force owned setting. 0 off, 1 on                                     #
# 6 =    book GUI. 'BkBook' or 'bkNotebook'                                   #
# 7 =    width                                                                #
# 8 -    height                                                               #
#                                                                             #
# Notes: - The cover can have the same texture as the book object itself.     #
#        - The main font can be changed later in the BookPages definition.    #
#          If you don't need a main font (because you are changing it later), #
#          you can set an empty string.                                       #
#        - force owned: If 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. If you need this restriction set force owned to 1.  #
#        - the 'bkBahroRockBook' GUI should work as well but it may not be a  #
#          good idea to use it with this multi-page template.                 #
###############################################################################

AgeBooks = {'Journal01': ('<cover src="*YourTexture*"><margin right=32 left=32>', '<font size=12 face=Arial color=000000>', 0, 0, 'BkBook', 0.8, 1.0),
'Journal02': ('', '', 1, 0, 'bkNotebook', 1.0, 1.0)}

###############################################################################################
# BookPages Dictionary Section:                                                               #
# 1 =    Same names as 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 on a hidden Blender object.      #
#        (or use PRP Explorer to add a plain texture to the prp without modeling an object)   #
#        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.    #
#        - Use ImgEndNoLink to add images without a hotspot.                                  #
###############################################################################################

BookPages = {'BevinBalcony01': (PageStart + ImgStart + 'xLinkPanelBevinBalc01*1#0.hsm' + ImgEnd + AlignCenter),
'CleftDefault': (PageStart + ImgStart + 'xLinkPanelCleftDesert*1#0.hsm' + ImgEnd + AlignCenter),
'CleftFissureDrop': (PageStart + ImgStart + 'xLinkPanelCleftDesert*1#0.hsm' + ImgEnd + AlignCenter),
'CleftSpecial': (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))}

###############################################################################
# LinkDestinations Dictionary Section:                                        #
# 1 =    Same names as book pages!                                            #
# 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')               #
#          CleftFissureDrop in this example will fail due to a missing title  #
#          (the correct title is 'FissureDrop')                               #
#        - You can tie special actions to linking panels or other journal     #
#          images by defining only the age name. The other definitions must   #
#          be set to None (see 'CleftSpecial')                                #
###############################################################################

LinkDestinations = {'BevinBalcony01': ('Neighborhood', 'LinkInPointBevinBalcony01', 'nb01BevinBalcony01', PtLinkingRules.kOriginalBook),
'CleftDefault': ('Cleft', 'LinkInPointDefault', None, PtLinkingRules.kOriginalBook),
'CleftFissureDrop': ('Cleft', 'LinkInPointFissureDrop', None, PtLinkingRules.kOriginalBook),
'CleftSpecial': ('Dummy', None, None, None),
'KadishPyramid': ('Kadish', 'LinkInPointGlowRmBalcony', 'kdshGlowRmBalcony', PtLinkingRules.kOwnedBook),
'FontTest': (None, None, None, None),
'PradJournal': (None, None, None, None)}

As you can see, D'Lanor gives instructions here. I'm not too sure why people get confused by this, but I'll use Arrows to point things out.

Take a look at the top of the file:

Pyth7.png

If you don't have any journals, you can delete the line I point to. If you are going to use Journals, this will be the name of your 3rd file, the Journals file. Normally the name of this file is your age name with "Journals" stuck into the end of it.

Okay, scroll down to the next area of the file:

Pyth8.png

As you can see, D'Lanor gave good instructions here. My arrows show you what he was talking about: Journal01 is the same name as the clickable object. So change Journal01 to the same name as your book or clickable object.

Now the next thing:

Pyth9.png

This is the same name as your texture you used for your book cover. Put it in here. You can leave the margins alone if you want.

Pyth10.png

Number 3 here is pretty easy to understand: this is where you define the name, size and color of the font that is used in a Journal.

What? You aren't making a journal? It's a linking book? Okay, keep reading I cover that further down.

Pyth11.png

This pic is showing you what to change depending on how you want the book to pop up. Do you want the cover closed? Or do you want it open?

Okay, we're going to skip "Forced Own Setting" you can leave it like it is for now.

Next pic:

File:Pyth12.png

Here is where we are saying what the pages look like: BkBook, will give you those old yellow looking Pages. BkNotebook will give you that lined ruled pages you see in a spiral notebook. Mind the Upper case and lower case letters here!!! If you don't, it won't work right!

The last 2 numbers are for the width and hight of the pop up. 1.0 is what I use for both.

Okay, now Let's say you're doing a linking book. You still use this part of course, but for Fonts, you can leave that blank. Or rather delete anything between the single quotes. You can see where D'Lanor shows this in the Journal02 example.

Okay, scroll down to the next part:

Pyth13.png

Where I have my two arrows is where you need to pay attention. The arrow to the left, this will be the same as the BookPage name you made up, back in the BookGUI file.

My 2nd arrow on the right is the name of the texture you used for your linking panel.

Hmmm? What's that? This is a journal? Ah, okay read on then:

Pyth14.png

Here is how you do it for a Journal. You still need to put in the BookPage name, but in the pic above, you also need to put in a new name. This name is the Journal Def. It's a name that you are also going to make up and write down. As it needs to go into the 3rd file that you need for Journals. This is the content of your Journal.

If you look just below that line, you'll see "%plyName" that's an option you can use to address the player of your Age.

Okay, now for the last part of this file, scroll down:

Pyth15.png

Here is where we define our linking definitions. Even if all you are doing are journals, you'll need this part.

My first Arrow, must be the BookPage name again.

Pyth16.png

Okay, the 2nd arrow is showing you the actual Age name. Becareful here. If you were making a link to Gahreesen, you don't put that. You put Garrison. If you look at the Dat files, you'll see that Gahreesen's name is Garrison. That's like Noloben. It's not actually called Noloben, but Sirahlen. So what you need to put here is the real name of the .age file for that Age.

Pyth17.png

The next two parts, just leave them like they are, unless you are and advanced user.

Okay, if you were doing linking books you are done. If you were doing Journals or have Journals too, read on. Else save this file as the name of your age followed by PageDefs.

For Journals:

Pyth18.png

Again, you need to put in the BookPage name for the Journal, but all the other parts you put "None"

NOW your're done with this file.

If you are just making Linking books, you can skip this next section.


Journal Content

Okay, the next part is a 3rd file you need for Journal contents. We do it this way because Journals can be several pages long.

Grand Master Dot of the Guild Of Maintainers offers a unique service, in that, she can make your Journal content for you, and even test your Python files for you Journals, to make sure they look correct and are laid out right ( not to mention spelling and grammer! :wink: ).

This can make it easier, as making the journal content can be hard as you need to use coding to page breaks, and using aposterphies or things of that nature. She can even get sketches and images set up for you. You just need to send her the text of the journal, and what you are trying to do. So I HIGHLY recommend you PM her here and get with her.

If instead you want to torture yourself, read on:

Here is the Python scripting for your Journals file:

# 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.'

Again, you can copy and past this into a new python file.

The only part I'm going to talk about is the Def's.

If you look in the coding, you will see DefPradJournal and DefFontTest.

Back in the PageDefs file you defined these and I told you to write them down. Well here is where you use them. You are defining what the Journal content is. After you list the name of the Journal content, you then define the font size, font name, color, all that good stuff, and then you begin to write your journal content:

\n will give you a line break.
<pb> will give you a page break.

to do aposterphies, for contractions like "Don't, Haven't, it's, Andy's, etc" you need to type it in like "Don\'t, Haven\'t, etc" that back slash must be used.


Making your Pak Package

Okay, now you're all done making your python files, and now you must make a Pak file out of them. We use PlasmaShop for this.

Go up and click on File and then New. Select Pak file.

A window will pop up, asking you to make the name of the Pak file. Save it as the name of your Age!!!!

Now you have this empty box. Look up and you'll see a button that says add file. Click on it, and it will ask which file to add.

you need to add all those Python files we just made, starting with

YourAgeName.py (this was the first dummy file we made)

YourAgeNameBookGUI.py (this was the BookGUI file we made)

YourAgeNamePageDefs.py (this was the PageDefs file we made)

YourAgeNameJournals.py (this is your Journal content if you have any. If not, don't need this).

After you've added each of these files to your Pak file, that's it! You are done! Link in and try out your books!


Return To: Andy's Blender Tutorials