Passing variable number of params from AlcScript to Python?

If you feel like you're up to the challenge of building your own Ages in Blender or 3ds Max, this is the place for you!

Passing variable number of params from AlcScript to Python?

Postby tachzusamm » Mon Jun 27, 2011 8:53 am

I wrote a PythonFileMod which can "handle" multiple objects. With "handling" I mean move them, but this does not matter here.

The problem I ran into is, how can it be done to make the Python file mod accept a variable number of objects?

Example AlcScript snippet:
Code: Select all
                parameters:
                  - type: activator
                    ref: logicmod:logicMod_WarpEnter
                  - type: activator
                    ref: logicmod:logicMod_WarpExit
                 
                  - type: int
                    value: 12    # Number of warp objects
                 
                  - type: sceneobject
                    ref: scnobj:WarpObj_1
                  - type: sceneobject
                    ref: scnobj:WarpObj_2
                  - type: sceneobject
                    ref: scnobj:WarpObj_3
            ...
                  - type: sceneobject
                    ref: scnobj:WarpObj_n


Well, sounds easy, but getting the attributes must (AFAIK) be done in the "beginning" (the "global" part) of the Python file, like this:
Code: Select all
from Plasma import *
from PlasmaTypes import *
actEnter = ptAttribActivator(1, 'Enter region activator')
actExit = ptAttribActivator(2, 'Exit region activator')

numWarps = ptAttribInt(3)   # Number ob objects to warp
warpOb_1 = ptAttribSceneobject(4)
warpOb_2 = ptAttribSceneobject(5)
...
warpOb_n = ptAttribSceneobject(n)
...
(class definition starts here)

because trying to fetch them later, e.g. in "OnServerInitComplete()" in the class does not work.

But, in the global section, the *values* of the passed parameters are not yet available.
So an attempt like this:
Code: Select all
obList = []
for obIndex in range(numWarps.value):
    warpOb = ptAttribSceneobject(obIndex+4)
    obList.append((warpOb))

failed (obList is empty); numWarps.value is just 0 in the global section. The values become available later in the class methods.

Just omitting the numWarps int and checking the warpOb_x instead, e.g. in a try: except: block checking if it has a getName() property for example, did not work too.


How would this be handled normally? (IF it's possible, that is)

tach
User avatar
tachzusamm
 
Posts: 575
Joined: Thu May 29, 2008 2:03 am
Location: Germany

Re: Passing variable number of params from AlcScript to Pyth

Postby diafero » Mon Jun 27, 2011 9:50 am

Nexus passes a SceneObjectList or similar to the Python file, but I have no clue how to write that in AlcScript:

Code: Select all
objlistLinkPanels = ptAttribSceneobjectList(17, 'Objct: Link Panels')
...
for objPanel in objlistLinkPanels.value:
    objPanel.doSomething()
I prefer e-mails to "diafero arcor de" (after adding the at and the dot) over PMs.

"Many people's horizon is a circle with a radius of zero. They call it their point of view."

Deep Island Shard | Offline KI
diafero
Deep Island Admin
 
Posts: 2972
Joined: Mon May 05, 2008 5:50 am
Location: Germany

Re: Passing variable number of params from AlcScript to Pyth

Postby D'Lanor » Mon Jun 27, 2011 10:03 am

I would use a scene object list. The AlcScript notation for lists is a bit tricky but I found that this works (At least for activator- and responder lists. I haven't used object lists yet).
Code: Select all
                  - type: sceneobjectlist
                    refs: ['scnobj:WarpObj_1',
                           'scnobj:WarpObj_2',
                           'scnobj:WarpObj_3']

Then this goes into the global section of your Python code:
Code: Select all
warpObjs = ptAttribSceneobjectList(3, 'list of warp objects')
objList = []


And you populate your Python list during OnFirstUpdate.
Code: Select all
    def OnFirstUpdate(self):
        for warpOb in warpObjs.value:
            objList.append(warpOb)
"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: Passing variable number of params from AlcScript to Pyth

Postby tachzusamm » Tue Jun 28, 2011 5:32 am

First, thank you again.

I tried it, but... well, I would not say it did not work, but... yeah, its really weird.

The modified script did not do anything in the first place; the Python0.log gave the error:
"AttributeError: 'NoneType' object has no attribute 'isInitialStateLoaded'"

Trying to find what's wrong, I removed more and more from the Python code, until just basic stuff remained - and STILL the error exists.
Nothing left from the sceneobjectlist and warping action stuff, and even removed the object parameters from the AlcScript (only keeping the logicmod params); still "no attribute 'isInitialStateLoaded'".

The strange thing is, that my original script worked (the one which only warped one object), and this has the SAME framework; the only difference
is that it includes the warping stuff.

Totally confused, I just added the original script under a different name into the age.pak as well (it now contains a paraWarpObject.py and a paraWarpObList.py), so there are two absolutely identical scripts, just with a different name.
Calling the first one from AlcScript works, the other gives the error mentioned.

Again confused, but thinking "hey, maybe having two similar scripts in the PAK causes problems", so I removed the first one, keeping just "paraWarpObList.py" - but, you won't believe it, this did not work either. Same error.

What the hell...

Any ideas?


Code: Select all
(06/28 12:22:44) We're linking out...
(06/28 12:22:44) UamEvents.RegisterForOnServerInitComplete: <function OnServerInitComplete at 0x251C5D60>
(06/28 12:22:44) _UamVars.Reset
(06/28 12:22:44) _UamTimer.Reset
(06/28 12:22:47) _UamEvents._OnPageLoad what=2 room='Personal_District_psnlMYSTII'
(06/28 12:22:47) _UamEvents._OnPageLoad what=2 room='Personal_psnlDustAdditions'
(06/28 12:22:50) Traceback (most recent call last):
(06/28 12:22:50)   File "C:\DOKUME~1\Riker\LOKALE~1\Temp\tmp3D4.tmp\paraWarpObList.py", line 283, in glue_setParam
(06/28 12:22:50)   File "C:\DOKUME~1\Riker\LOKALE~1\Temp\tmp3D4.tmp\paraWarpObList.py", line 223, in glue_getParamDict
(06/28 12:22:50)   File "C:\DOKUME~1\Riker\LOKALE~1\Temp\tmp3D4.tmp\paraWarpObList.py", line 201, in glue_findAndAddAttribs
(06/28 12:22:50) AttributeError: 'NoneType' object has no attribute 'isInitialStateLoaded'
(06/28 12:22:50) __init__paraFogFade v.1
(06/28 12:22:50) __init__paraFogChange v.1
(06/28 12:22:51) _UamEvents._OnPageLoad what=1 room='Ani_District_mainRoom'
(06/28 12:22:51) _UamEvents._OnServerInitComplete
(06/28 12:22:51) _UamModReset.UamOnNewAgeLoaded
(06/28 12:22:51) _UamVars.OnServerInitComplete
(06/28 12:22:51) xKI.OnServerInitComplete(): age =  Ani
(06/28 12:22:51) paraFogFade.OnServerInitComplete:
(06/28 12:22:51) Traceback (most recent call last):
(06/28 12:22:51)   File "C:\DOKUME~1\Riker\LOKALE~1\Temp\tmp2C1.tmp\paraFogFade.py", line 49, in OnServerInitComplete
(06/28 12:22:51) AttributeError: 'NoneType' object has no attribute 'isInitialStateLoaded'
(06/28 12:22:51) _UamEvents._OnAvatarSpawn
(06/28 12:22:51) _UamModReltopages.UamOnNewAgeLoaded

(Sidenode: "paraFogFade.py" is a different, older script and works well (as long before) unless I include paraWarpObList.py too; but even removing it from the PAK does not change anything. And the really, REALLY, additional strange thing is: URU STILL complains about "no attribute 'isInitialStateLoaded' in the paraFogFade.py (!!) script, which is not even there any longer! Although it's included in another age's PAK...)
Maybe we can't have identical scripts in different ages? (But, this did not matter in the first place, with my original script...)
Something really weird is going on here.


By the way: What is the self.id and self.version under __init__ for?
And what does the Python glue do?
User avatar
tachzusamm
 
Posts: 575
Joined: Thu May 29, 2008 2:03 am
Location: Germany

Re: Passing variable number of params from AlcScript to Pyth

Postby diafero » Tue Jun 28, 2011 11:35 am

I have no clue what this weird error is caused by, but usually if I have weird Python errors, I forgot to change the class name to match the file name. This is absolutely crucial for Uru Python to work.
Glue Python code allows the C++ code to mess with the runtime state of this Python module... Cyan, to no surprise, chose a very strange way to integrate Python into their engine (instead of instantiating classes, the whole module is somehow instantiated - different PythonFileMods will have different values for global vriables, but modules imported are shared with other PythonFileMods).
I prefer e-mails to "diafero arcor de" (after adding the at and the dot) over PMs.

"Many people's horizon is a circle with a radius of zero. They call it their point of view."

Deep Island Shard | Offline KI
diafero
Deep Island Admin
 
Posts: 2972
Joined: Mon May 05, 2008 5:50 am
Location: Germany

Re: Passing variable number of params from AlcScript to Pyth

Postby D'Lanor » Tue Jun 28, 2011 1:25 pm

tachzusamm wrote:By the way: What is the self.id and self.version under __init__ for?

They have no use within the game itself. They exists because the Max plugin needs them.

tachzusamm wrote:And the really, REALLY, additional strange thing is: URU STILL complains about "no attribute 'isInitialStateLoaded' in the paraFogFade.py (!!) script, which is not even there any longer! Although it's included in another age's PAK...)

Yes, each module is loaded from the pak file with the latest time stamp, regardless what age it is from. Duplicates should be avoided. That is why it is the custom to add an age prefix to Python file names/classes.
This can get tricky with global files. For example if a writer heavily modifies a global module and distributes it with his own age then any other age using that module will break.
"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: Passing variable number of params from AlcScript to Pyth

Postby diafero » Wed Jun 29, 2011 12:00 am

Yes, all the pak file's contents are merged by Uru, the originating pak file does not play any role during module load. Which is the reason why fan-ages should never overwrite global files (which my scripts actually automatically check for, all the fan-ages currently in UAM are fine). Offline KI can do that since it is explicitly incompatible with all other global modifications.
I prefer e-mails to "diafero arcor de" (after adding the at and the dot) over PMs.

"Many people's horizon is a circle with a radius of zero. They call it their point of view."

Deep Island Shard | Offline KI
diafero
Deep Island Admin
 
Posts: 2972
Joined: Mon May 05, 2008 5:50 am
Location: Germany

Re: Passing variable number of params from AlcScript to Pyth

Postby tachzusamm » Wed Jun 29, 2011 1:53 am

diafero wrote:[..], but usually if I have weird Python errors, I forgot to change the class name to match the file name.

LOL !!
Diafero, you hit the nail on its head here!
Actually, I really forgot to name the class as the file in this case. Funny thing is, I already knew that it's important, but, well, sometimes I've got "Ein Brett vor dem Kopf". :lol:
To bring it to the point: Removing this mistake, suddenly everything worked fine. Thank you so much for mentioning the obvious.

diafero wrote:Glue Python code allows the C++ code to mess with the runtime state of this Python module...

Aha, so this is the Python part of the engine to Python interface; now I understand why it's called glue.


D'Lanor wrote:
tachzusamm wrote:By the way: What is the self.id and self.version under __init__ for?

They have no use within the game itself. They exists because the Max plugin needs them.

Good to know; so I can stop thinking about numbers which are not used elsewhere.

D'Lanor wrote:Yes, each module is loaded from the pak file with the latest time stamp, regardless what age it is from. Duplicates should be avoided. That is why it is the custom to add an age prefix to Python file names/classes.
This can get tricky with global files. For example if a writer heavily modifies a global module and distributes it with his own age then any other age using that module will break.

This is really useful information; I did not know that. It explains why I got this strange effects.

As said, it already works now, like a charm, and totally as expected.
Thanks again to both of you, D'Lanor and diafero.


===================================================================

Okay, now let me reveal why I did all this (warping multiple objects around):

I love to have vegetation in an age. Huge vegetation, grass, bushes, flowers and trees all around the places.
But - there's a problem with it: the number of faces of all objects in total will grow, the more you copy plants around, slowing down the engine's speed. Yes, there's the option to make use of VisRegions, which can help a lot, but putting duplicates of plants in various regions of an age increases the pak's file size and loading time.
So I came up with an idea: What if I just warp objects around between regions, depending on the player's location, instead of just hiding/unhiding them? This could keep the file size lower, while keeping the facility to have objects shown to the player everywhere.

Imagine there are 3 regions A, B and C; some plants are normally located at the (link-in) region A. The player now enters a region B, from where region A can't be seen - now we warp the plants to region B, giving the illusion there is again plenty of vegetation. Same with region C, and back to region A.
Actually dozens of regions could "hold" a vast amount of plants.
True, this is "faking", or let me call it doing illusion - but hey, isn't the whole URU game mostly working this way?

To simplify the handling, I implemented making use of "hooks" in the script. This means, you can put empties (as hooks) in the Age, working as placeholders for the destinations for the real objects. This is much easier than having to type co-ordinates into AlcScript as destinations.
(Next step could be to apply rotation as well in addition to location, if this is possible. If you know how, please tell.)

The currently used Python script:
Code: Select all
from Plasma import *
from PlasmaTypes import *

actEnter = ptAttribActivator(1, 'Enter region activator')
actExit = ptAttribActivator(2, 'Exit region activator')
doNetForce = ptAttribInt(3, 'netForce flag')
warpObjs = ptAttribSceneobjectList(4, 'list of warp objects')

obList = []

# Warp to a destination object
def WarpObjectToDestOb(warpOb, destOb):
    pos = warpOb.position()
    dest = destOb.position()
    matrix = warpOb.getLocalToWorld()
    matrix.translate(ptVector3((dest.getX() - pos.getX()), (dest.getY() - pos.getY()), (dest.getZ() - pos.getZ())))
    warpOb.netForce(doNetForce.value)
    warpOb.physics.warp(matrix)
    return pos

# Warp to a known location
def WarpObjectToPos(warpOb, dest):
    pos = warpOb.position()
    matrix = warpOb.getLocalToWorld()
    matrix.translate(ptVector3((dest.getX() - pos.getX()), (dest.getY() - pos.getY()), (dest.getZ() - pos.getZ())))
    warpOb.netForce(doNetForce.value)
    warpOb.physics.warp(matrix)


# Note: Change all "ptResponder" to "ptModifier" for a Modifier class
class paraWarpObList(ptModifier,):
    __module__ = __name__

    def __init__(self):
        ptModifier.__init__(self)
        self.id = 16005     # (the value does not really matter)
        self.version = 1
        print ('__init__%s v.%s' % (self.__class__.__name__, self.version))
       
        self.oriPosList = None


    def OnFirstUpdate(self):
        #print ("---OnFirstUpdate---")
        tog = False
        for obj in warpObjs.value:
            if not tog:      # its the warp object
                warpOb = obj
            else:         # its the destination hook
                obList.append((warpOb, obj))
            tog = not tog
        print ("numWarps=%d" % len(obList))


    def OnServerInitComplete(self):
        #print ("---OnServerInitComplete---")
        pass


    def OnNotify(self, state, id, events):
        #print ("---OnNotify---")
        if (not PtWasLocallyNotified(self.key)):
            return
        if ((id == actEnter.id) and state):
            self.MyWarpObject_onEnter()
        elif ((id == actExit.id) and state):
            self.MyWarpObject_onExit()



    def OnSDLNotify(self, VARname, SDLname, playerID, tag):
        #print ("---OnSDLNotify---")
        pass


    def MyWarpObject_onEnter(self):
        print 'Warp Trigger Region entered'
       
        bStorePos = False
        if self.oriPosList is None:
            self.oriPosList = []
            bStorePos = True
       
        #print ("ListLen:%d" % len(obList))
        for obIndex in range(len(obList)):
            #print ("obIndex=%d" % obIndex)
            warpOb = obList[obIndex][0]
            destOb = obList[obIndex][1]
            #print warpOb.getName()
           
            oriPos = WarpObjectToDestOb(warpOb, destOb)
           
            if bStorePos:
                self.oriPosList.append(oriPos)


    def MyWarpObject_onExit(self):
        print 'Warp Trigger Region left'
       
        if self.oriPosList is not None:
            for obIndex in range(len(obList)):
                warpOb = obList[obIndex][0]
                oriPos = self.oriPosList[obIndex]
                WarpObjectToPos(warpOb, oriPos)



### Python Glue starts here ###


and an example AlcScript:
Code: Select all
Region_WarpObject_B:
    logic:
        modifiers:
          - name: logicMod_WarpEnter
            cursor: up
            flags:
              - multitrigger
            conditions:
              - type: volumesensor
                satisfied: true
                direction: enter
                trignum: -1
            activators:
              - type: objectinvolume
                remote: Region_WarpObject_B
            actions:
              - type: pythonfile
                ref: :WarpObjectPythonFileMod
          - name: logicMod_WarpExit
            cursor: up
            flags:
              - multitrigger
            conditions:
              - type: volumesensor
                satisfied: true
                direction: exit
                trignum: -1
            activators:
              - type: objectinvolume
                remote: Region_WarpObject_B
            actions:
              - type: pythonfile
                ref: :WarpObjectPythonFileMod
        actions:
          - type: pythonfile
            name: WarpObjectPythonFileMod
            pythonfile:
                file: paraWarpObList
                parameters:
                  - type: activator
                    ref: logicmod:logicMod_WarpEnter
                  - type: activator
                    ref: logicmod:logicMod_WarpExit
                  - type: int
                    value: 0     # netForce(0)
                   
                  - type: sceneobjectlist
                    refs: ['scnobj:Plant_1', 'scnobj:Plant_1_Dest_B',
                           'scnobj:Plant_2', 'scnobj:Plant_2_Dest_B',
                           'scnobj:Plant_3', 'scnobj:Plant_3_Dest_B',
                           'scnobj:Plant_4', 'scnobj:Plant_4_Dest_B']



Sidenote: Currently, the scripts store the object's original location of the objects before warping is applied, and restore the location if the region is left; I made this for testing. For the behaviour described above, the storing part, restoring location parts (in Python code) and leaving region part (in Python and AlcScript) should be omitted.

Oh, and as a final but important note: Both the object(s) and the destination hook(s) have to be set as Actor in Blender to get it working (otherwise the objects don't get a coordinate system applied, to mention the technical internals).

Thanks to Sirius as well for providing code examples.

Original thread for the warping part:
viewtopic.php?f=59&t=5251
User avatar
tachzusamm
 
Posts: 575
Joined: Thu May 29, 2008 2:03 am
Location: Germany

Re: Passing variable number of params from AlcScript to Pyth

Postby dendwaler » Wed Jun 29, 2011 3:48 am

So I came up with an idea: What if I just warp objects around between regions, depending on the player's location, instead of just hiding/unhiding them? This could keep the file size lower, while keeping the facility to have objects shown to the player everywhere.

Really a good idea!
That way an age can be made much more visionable atractive.
Not only for vegetation, i think you can use it for complete buildings as well.
To simulate a long street would not cost to much effort in that way.
A sort of "dynamic array modifier"

good work!
Those wonderfull Worlds are called " Ages" , because that is what it takes to build one.



Watch my latest Video Or even better..... watch the Cathedral's Complete Walkthrough made by Suleika!
User avatar
dendwaler
 
Posts: 936
Joined: Mon Jun 22, 2009 10:49 am
Location: Nederland

Re: Passing variable number of params from AlcScript to Pyth

Postby Wamduskasapa » Wed Jun 29, 2011 5:57 am

dendwaler wrote:
So I came up with an idea: What if I just warp objects around between regions, depending on the player's location, instead of just hiding/unhiding them? This could keep the file size lower, while keeping the facility to have objects shown to the player everywhere.

Really a good idea!
That way an age can be made much more visionable atractive.
Not only for vegetation, i think you can use it for complete buildings as well.
To simulate a long street would not cost to much effort in that way.
A sort of "dynamic array modifier"

good work!

Excellent work, not only can this be used for plants and decorations, I can also see it being used for darts, spears and daggers coming from the walls, floor and ceiling. Stopping us from going down one passage and thus blocking access to that area until the puzzle was solved.
Computer = MotherBoard MSI X99S GAMING 7 - Intel I7-6950X
Dual MSI GeForce GTX 1080
64GB Kingston HyperX DDR4 Predator Memory
Dual Samsung 1TB SSD Pro - Dual Seagate 4TB SSHD
Excelvan 5.25" Multi-Function Media Dashboard
User avatar
Wamduskasapa
 
Posts: 943
Joined: Fri Apr 30, 2010 6:56 am

Next

Return to Building

Who is online

Users browsing this forum: No registered users and 1 guest