Associations provide ways of linking various nodes. Associations have a source node, target node, and an association type (also known as an association definition). The term "Association" will depend on context, as to whether it is referring to the association type, or the actual linkage between nodes.
Association types describe the possible relations between various nodes. For example, an association type might be "likes". When an association of that type is present, it implies the target node "likes" the source node. As another example, an altar node might have other nodes upon it, so if the altar node were the source id, the other nodes would be target ids with an association type of "on". There are many different association types such as on, married, inventory, base_hard_association, and so forth. New association types can also be created.
Association types must be defined in the DOM before they can be used. An association type can also belong to a group, and then queries can be run to find the associations of any of the association types in a particular group. Some groups can be designated as exclusive. If an association is a part of any of the association types in an exclusive group, then only one association from that group can link two particular nodes at a time (see "Association Groups" below for more information).
When creating an association, there are three elements required:
The default value of an association variable is (0,"",0), which effectively means three wildcards.
Note that the Association data type is a special one, in that though data types are usually used for fields on a class, associations cannot be assigned to a field. Associations are only for use with variables in scripts, and even then, are used in a limited fashion.
q as list of association type as string = "" q = QueryAssociation(parentnode, type, childnode) // With a null type, all associations between the parent // and childnodes are returned foreach assoc in q when assoc.type is "on" . is "under" . is "behind" . default scripterror("Unknown association type: " + assoc.type) . . .
Hard and Soft Associations
Association types are defined as either hard or soft, which affects whether or not target nodes are loaded into the game at the same time as their source node. For example,
IN would usually be a hard association. If a backpack is loaded into the game, it would make sense for all of the objects in the backpack to be loaded into the game at the same time.
MARRIED, however, would be a soft association. Just because one married character was in the game, would not require that the other character be in the game at the same time.
It is recommended that whenever a persistent node is created, that it be associated to the area or at least one other node with a hard association, such as
base_hard_association. The reason for this is so that the node can be accessed in the future. Persistent nodes will indeed be saved to the database, but in order for them to ever be loaded into the area again from the database, they still need to have at least one hard association to something else in the game:
AddAssociation(backpackref, "base_hard_association", swordref)
Associating a Node with an Area
Each area has a root node. Getting the ID of this node is accomplished with the following exposed function, which returns the Area Root Node or World Root Node, depending on which type of server it is called in:
function GetRootNode() as noderef
To associate a node with a particular area, the new node is associated with the appropriate root node.
function MakeANode() NewNode as noderef RootNode as noderef NewNode = CreateNodeFromClass("thingie") RootNode = GetRootNode() AddAssociation(RootNode, "base_hard_association", NewNode) . function ListAllNodesAttachedToRoot(NodetoDelete references noderef) RootNode as noderef = GetRootNode() AllTheAssocs as list of association AllTheAssocs = QueryAssociation(RootNode, "base_hard_association", None) foreach a in AllTheAssocs MsgArea("Node found: " + a.target) if (a.target = NodetoDelete) DestroyNode(NodetoDelete) . . foreach a in AllTheAssocs if not IsAssociationValid(a) MsgArea("Invalid association found.") . . .
Note that the above function would not find all the associated nodes in the Area -- it would only find nodes directly associated with the root node. There might be other "children" nodes of those. A recursive loop would still be required to find all the nodes in the area.
|Caution: This page may contain information which is Hero's Journey-specific!|
An Association Group is an arbitrary grouping of association types. It is possible to make a query based on an Association Group, to see if any associations within a set of different association types are found.
Some Groups are "exclusive" Groups. This means that two nodes can only be associated by one of the association types within that group. For example, in the group "base_hard_association_group" exist the following association types:
If two nodes, Anode and Bnode, are associated with "base_hard_association", and then later they are associated with "inventory", then the original "base_hard_association" will be automatically removed.
Any given association type can only belong to one Association Group at a time.
q as list of association q = QueryAssociation(parentNode, "base_hard_association_group", childNode)
Association CLI Commands
The following commands can be used to create and manipulate associations via the CLI. Please click on the appropriate command to learn more about it.
Commands can be prefaced with one of the following characters, to made the association in the appropriate GOM.
- \ (Backslash) to create a server-side association
- | (Pipe) to create a client-side association
- / (forward slash) to create a temporary client-side association (goes away when HeroBlade closes)
For more information on Preface commands, please see the section on the CLI.
- DAD - Delete Association Definition
- DAG - Delete Association Group
- DA - Delete Association
- DAS - Delete Associations of Source
Association HeroScript Functions
There are various built-in functions that allow for the manipulation of associations.
- Main page: Add Association
AddAssociation( sourceNode as noderef, associationName as string, targetNode as noderef )
- Main page: Remove Association
RemoveAssociation( sourceNode as noderef, associationName as string, targetNode as noderef )
- Main page: Query Association
// Returns a list of all associations between the SOURCE and TARGET nodes that have the specified NAME. // Any two of the three parameters may act as wildcards if "uninitialized" (e.g., None for the NodeRefs or "" for NAME). // see documentation: http://hewiki.heroengine.com/mediawiki/index.php?title=Associations external function QueryAssociation( source as NodeRef, name as String, target as NodeRef ) as List of Association
There are also specialized external functions for common queries which are both faster at runtime and more compact to code:
// To find a node that has a hard association to the given node. // Use instead of QueryAssociation( 0, "base_hard_association_group", target) // Returns NONE if no hard association is found external function QueryAssocOwner( target as NodeRef ) as NodeRef // Use these QueryAssociationOne* functions when it is expected there is a single association that matches. // Additionally, the versions that accept a filterClass allow the query to be more selective. // Produces a script error if there are more than one match. // Returns NONE if no match is found. external function QueryAssociationOneTarget( source as NodeRef, assocType as String) as NodeRef external function QueryAssociationOneSource( assocType as String, target as NodeRef) as NodeRef external function QueryAssociationOneTargetByClass( source as NodeRef, associationType as String, filterClass as String ) as NodeRef external function QueryAssociationOneSourceByClass( filterClass as String, associationType as String, target as NodeRef ) as NodeRef // Use these functions to get the list of NodeRefs (source or target) instead of a list of associations. // This is more efficient and convenient when there is no need to check other fields of the association. // Also, use of the filterClass avoids getting list elements only to be skipped by a later test. external function QueryAssocTargetsByClass( source as NodeRef, filterClass as String ) as List of NodeRef external function QueryAssocSourcesByClass( filterClass as String, target as NodeRef ) as List of NodeRef external function QueryAssociationTargetsByClass( source as NodeRef, assocType as String, filterClass as String ) as List of NodeRef external function QueryAssociationSourcesByClass( filterClass as String, assocType as String, target as NodeRef ) as List of NodeRef
Is Association Valid
function IsAssociationValid(thing as association) as boolean
This built-in function takes an example of the complex datatype association, and checks if it's valid. For example, if you have deleted a series of nodes, and you're not sure if any of them had related associations. If either the source or target nodes have been deleted or unloaded from the game, this function returns FALSE.
List all associations
myList as list of association // Note: this would be bad since it could return a very long list myList = queryAssociation( None, "", None) // None matches any node, and "" matches any association type // This line does the same thing as queryAssociation above result as list of association = QueryAssociation(None,"",None)