HeroEngine Forums
Welcome, Guest. Please login or Register for HeroCloud Account.

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Messages - HE-JAY

Pages: 1 ... 6 7 [8] 9
I've attempted to reproduce the behavior you're seeing but have so far been unsuccessful.  Could you provide additional information about where and in which situations you're encountering this?

Keep in mind that the FX system will use targets selected by HeroBlade's 'selection' tool and will not respect any game-specific selection mechanism you've implemented. If you're selecting an NPC in game-mode and do not see the yellow bounding box around your target, the FX system will be unable to detect your selection. To see if this is the problem, begin editing your FX spec, then exit character mode and use the selection tool to select a target; finally, press the 'Preview' button in the FX Spec Editor and see if the FX acts as you would expect.

If this doesn't solve the problem, please reply with additional information and a set of repro steps we can use in our own development world.


Scripting & Programming / Re: Key Binding (from HSL)
« on: Feb 10, 11, 03:07:01 PM »
Functionality to manipulate keybindings on a per-client basis does exist, but there are currently no examples or clean engine GUIs to guide you through the process.  We will look at putting something together (whether that be documentation, explanation or examples) for you as soon as we're able.  In the mean time, know that the following HSL external functions do exist and that any game-specific GUI you create would make use of these for both loading and manipulating an individual client's keybindings.

Code: [Select]
external function GetKeyBindingProfiles() as List of String
external function SelectKeyBindingProfile(profileName as String)
external function AddKeyBindingProfile(profileName as String)
external function DeleteKeyBindingProfile(profileName as String)
external function SaveKeyBindingProfile()
external function LoadKeyBindingConfigFile(fqn as String)

external function GetKeyBindings() as List of Class keybinding
external function SetKeyBinding(b references Class keybinding) as Boolean
external function ClearKeyBinding(b references Class keybinding) as Boolean

As always, feel free to follow up during the interim with questions or comments.

Scripting & Programming / Re: Replicating lists of NodeRefs
« on: Jan 31, 11, 11:40:37 AM »
I've verified that 'list of list of noderef' does indeed replicate correctly assuming all of the prerequisite nodes were replicated prior to attempting to access the list on the client (so there does not appear to be a bug of any kind present). As stated in my previous post, though, I would definitely not go this route. Instead of NodeRefs, I would use IDs.

Scripting & Programming / Re: Replicating lists of NodeRefs
« on: Jan 28, 11, 02:06:50 PM »
In general, replicating NodeRefs is a bad idea.  The reason for this is that - at any point in time - the corresponding client-side node to which the NodeRef refers may or may not be physically present. This can happen during the replication process itself if the order of node replication is such that your list of noderefs replicates before the nodes to which it refers. If the node is currently in the process of replicating, or if at some point it disappears, your noderef will become invalidated (by pointing to a null object) and return '0' upon attempts to dereference.

It is a far better approach to replicate - if you must - lists of IDs. These IDs are scalar values that have no dependence upon the nodes to which they are to refer. You can replicate them, update them and access them at will, and they will always retain their correct value. Following this method will require you to convert them to NodeRefs when you want to use them (and then checking whether the NodeRef == None), but this is far more desirable than the undefined behavior of relying on NodeRefs to nodes that may or may not exist.

My personal guess is that you're running into a race condition of some kind when attempting to replicate a list of list of noderefs. Or, it is also possible that you truly have found a bug; I'll investigate.

In the mean-time, though, I would redesign that particular piece of code to work with IDs as opposed to NodeRefs.

Feel free to let me know how that works if you choose to go that route, and if you have any other issues, don't hesitate to follow-up.

Scripting & Programming / Re: Associations as "containers"
« on: Jan 28, 11, 01:56:06 PM »
I'm sorry about that, Scott. I'm getting my terminology mixed up. 'Exclusive' is the property I was referencing earlier, which applies to Association Groups and not the individual associations. I will edit my previous post to maintain some kind of coherence with the truth. (:

'Unique' seems logically to refer to the property of an association that "no two associations of the same type may be created between two nodes"; so - in layman's terms - I can't have a 'likes' association between 'myself' and 'ice cream' and then add another 'likes' association between myself and ice cream to denote that I "really like ice cream". No doubling up. It doesn't make logical sense.

As far as I'm aware, all associations behave this way (and 'must' behave this way by convention); the 'unique' property may simply be a reflection of this behavior in the engine.  This is something I'll have to investigate further in order to discern the Absolute Truth, but know that I'll post a follow-up when I do.

Again, apologies for any miscommunication on my part. Hopefully things are starting make sense at this juncture.

If there are any follow-ups, you know what to do!

As an addendum (or rather, a revision): you 'can' indeed destroy a system node using the external function DestroySystemNode( prototypeName as String ). Effectively, this would 'reset' a client-side system node (if desired). My previous comment stating that system nodes persist through area changes, etc, does hold true. Sorry for any miscommunication; hopefully this resolves any residual confusion (: As always, if you have any follow-ups, feel free to post them!

That's correct.  Client system nodes - once created - stick around until the client is shut down. Since these nodes are indestructible, they can't be destroyed using DestroyNode (and thus can't be 'refreshed', per se). This is the same behavior as is seen on the server and is almost always the desired behavior.

The implication of this is that any changes you've made to client-side system nodes that depend upon area-specific data will need to be cleaned up by your game systems prior to travel.  This is generally not an issue, as you can handle most area-specific behavior via replication and associations (which are relatively automatic in that your associations will vanish once the replicated nodes cease to exist), but it's something to keep in mind.

Scripting & Programming / Re: Associations as "containers"
« on: Jan 26, 11, 08:51:52 AM »
The list of associations returned by the QueryAssociation external functions may coincidentally retain the order of creation, but this is not enforced nor is it suggested you make any assumptions about ordering. Generally, you'll either need to iterate over all associations returned by these functions or you'll need to increase the specificity of your query using QueryAssociationTargetsByClass (as an example).

The 'Exclusive' property of association groups is a reference to whether each association contained within the group are exclusive.  No two associations belonging to a group which is marked 'Exclusive' may be used to associate two nodes to one-another at the same time.  So - for example: we have two association definitions - 'right_hand_ownership' and 'left_hand_ownership', each of which belongs to an association group tagged 'Exclusive'.  If we have an item associated to our character via a 'right_hand_ownership' association and we then attempt to associate the same item to the character via a 'left_hand_ownership' association, the first association will be removed and replaced by the second.  This is useful for providing an additional layer of constraints to the relationships between nodes which have some logical association with one-another.

This plays into your final question. If a second association of the same type is established between two nodes and an association already exists between them that specifies 'unique source' or 'unique target', the first association will be automatically destroyed under the hood.  So - using our above example - if our 'right_hand_ownership' association specifies that there may be only one source (unique source) and one target (unique target), attempting to associate a second item to the character via this association will eradicate the first association.  If an association whose group is marked 'Exclusive' is established between two nodes and a second association that belongs to the same association group is added, the first association will once again be destroyed automatically under the hood. This was previously covered by the 'right_hand_ownership'/'left_hand_ownership' example.

'Unique' appears to reflect the default (and only) behavior of associations in that no two nodes may be associated twice via the same association definition; logically, this behavior makes sense and - as a matter of policy - is enforced universally for all association definitions.

Does this answer your question sufficiently? If not, feel free to follow-up! (:

Just a quick follow-up here; I know you've solved your issue at a basic level, but I wanted to jump in with some additional info (caveats and whatnot).

Using the $EDIT system node is definitely the expedient and - in most cases - the 'correct' thing to do.  It prevents you from accidentally corrupting the area root node hierarchy, messing up area asset associations, etc. There are, however, a few things to keep in mind.

First, know that area edits are synchronous and - as a result - any large batch of edits you run may potentially run into issues with exceeding the configured CPU limit.

Secondly - and again this is due to the fact that area edits are synchronous - you may find your area running out of IDs before your batch has finished processing.  This is due to the fact that the area server does not have enough time to request and refresh its allocated ID pool while your script is madly churning away making edits.

The solution to both of these problems is to process large batches asynchronously in chunks.  I'm not sure if your desired functionality is to 'import' a set of area geometry from a saved file, but if this is the case, just know that large batches of edits can and will cause issues if not handled asynchronously. While this may not become an issue for you right now, just be sure to file this information away in case you do run into problems later.

Finally, please know that the set of associations established by EDITs is very particular; modifying them manually is highly dangerous and has the potential to cause irreversible corruption of the area's data. Please use EDIT or HB to perform all modifications to the area's geometry (:

If you have any other questions or concerns, please feel free to post follow-ups!

Scripting & Programming / Re: Character Selection data
« on: Jan 12, 11, 03:57:00 PM »
It seems like you have a good grasp of the steps involved in extending the $ACCOUNT system node to make use of custom player account and connection classes, but I'll summarize the steps and provide some common troubleshooting steps just to make sure we're on the same page.  I'll also provide some reference materials you can review on the wiki which - if you haven't already taken a look at - may help solidify an understanding of the way the login process and the $ACCOUNT node operate. Finally, I'll go over some of the trickier parts of getting your new custom character fully logged into the world by overriding the default CSS GUI.  As always, if this doesn't answer your question fully, feel free to respond with follow-ups.

First, I recommend that you avoid copying HJ code for your own systems, as it relies on workarounds that are unnecessary in more current versions of HeroEngine (glomming the account decorator onto a player's account node, for example). Instead, we'll build a custom CSS from the ground-up using the minimum effective number of steps. It seems most of this has given you no problems, but I'll review the process anyway for completeness.

In order to take advantage of custom player account and player connection classes, we need to perform the following steps (we're going to stop and test things after each major segment to ensure any problems you're having are 'not' due to the steps involved in that segment):
  • Create our custom Ex_PlayerAccount and Ex_PlayerConnection classes in the DOM, making sure they derive from the appropriate _PlayerAccount and _PlayerConnection clean engine classes (and making sure your server-side Ex_PlayerAccount class replicates to its client-side counterpart). Don't worry about adding any custom code to these classes yet.
  • Create a custom Ex_Account class to house our HE_FactoryAccountRootNode and HE_FactoryPlayerConnection overrides.
  • Glom this class onto the ACCOUNT prototype using the System Nodes Configuration GUI (http://wiki.heroengine.com/wiki/Customizing_HeroEngine#Using_the_HeroEngine_Control_Panel).

STOP and test this bare-bones setup:
  • Check: Do accounts logging in for the first time acquire the new Ex_PlayerAccount and Ex_PlayerConnection classes? ("\sn <ACCOUNT_ID>" in the CLI)
  • Check: Is the account replicated correctly to the client?
  • fyi: you can 'reset' an account by destroying it via $ACCOUNT._DestroyAccountRootNode(...), logging out and then logging back in.

Now that we know the basic setup of your game-specific account and connection classes works, we can move on to overriding the default CSS GUI (we're not going to worry about tacking on any additional data just yet; let's make sure the basic GUI functionality works before we start tweaking it).
  • Create your custom Ex_CharacterSelection claass both on the server and client, overriding the default GUI by implementing HE_CSSInvokeGUI on the client.
  • Ensure that your client GUI incorporates the following functionality:
    • Make sure it can process the character list from the server (by default, the CSS sends a list of characters as well as the most recently selected character when it calls the client-side Remote_CSSInvokeGUI function.
    • Make sure it calls the appropriate CSS methods when the user attempts to login (_CSSRequestSetLoadInCleanTestArea and _CSSRequestSelectCharacter), create a character (_CSSRequestCreateCharacter), and delete a character (_CSSConfirmDeleteCharacter)

STOP and test again.
  • Check: can we log in with a character?
  • Check: can we create a character?
  • Check: can we delete a character?

If successful, we should now have a working override for the CSS, our player account and player connection.  We can set aside this working code and move on to adding additional data to the character.

At this point, we can do one of two things:
  • If we need to visualize all of our characters at the same time during character selection (e.g. a line-up), we can do as Lung suggested and create a 'characters info' class (of which each account will have one instantiation) which will store the desired visualization data (name, level, outfit, etc) without requiring us to load all characters. This could be passed to the client with the initial set of character names on Remote_CSSInvoke_GUI.
  • If we only need to visualize a single character at a time, we can store the desired information on the character node, but only query it when we 'switch' our selected character (this would make an asynchronous call to the server saying 'load new character and give me the information I need to visualize it in character selection' - see the CSS method '_CSSSelectCharacter')

Both methods have their advantages and disadvantages, but both will require you to load the requisite root nodes, respond to this loading and pass along the desired information to the client.  The client will visualize the characters, offer its character selection GUI, and respond to events just like the default GUI would. Selecting a new character will call the CSS's Untrusted_CSSRequestSelectCharacter, deleting will call its paired untrusted function, etc. This should all work fairly seamlessly, as you're not modifying the core functionality provided by the CSS.

Potential problems you may be experiencing at this point when trying to send a character to the world:
  • Are you utilizing the stock CSS functionality for initiating travel to the starting area, or have you overridden it? The default CSS utilizes the $TRAVEL system node to spin up the desired area prior to sending a character to it (see $TRAVEL._ChangeAreaRequest).  If you're trying to manually send a player to an area that is not spun up, travel will fail.
  • Similarly, attempting to send a player to a new instance of an area will fail if you've reached the configured limit of simultaneously spun-up areas.
  • Are you positive the area and instance you're specifying when initiating travel is valid? Check the ID and the existence of the area in the world.

Regarding the instant disconnects:
  • If an account's root nodes are already loaded in the world and a second attempt is made to log in, the _playerConnection's '_KickPlayerConnection' method will be called on the new connection. Overriding this on your custom Ex_playerConnection class to offer exclusions may help you troubleshoot whether this is the case. Remember to unload all root nodes associated with the account before completing the logoff process. (Important: please maintain the exclusions for the 'HE-ADMIN' and 'CHRISTOPHER' accounts if you choose to override your base playerConnection _KickPlayerConnection method.)

Regarding the CSSERROR02 error:
  • To check whether characters are getting deleted or cleaned-up appropriately (and I suspect the reason they're not is due entirely to the copied HJ code which should now be gone), ensure that the account node does not associate itself via a CharacterAssociation with the character node.

Finally, a few resources for perusal in overriding the default behavior of CSS and player accounts:

Let me know if you want me to go over anything else in particular. But following these steps, you should be able to incrementally rule out where the problem is occurring.

Scripting & Programming / Re: Disable pan or rotation on camera
« on: Jan 10, 11, 04:48:20 PM »
The behavior of the camera is actually little more than a  measured response to commands passed through from the input system (http://wiki.heroengine.com/wiki/Input_system). If you were to open up the 'Input System' panel in HeroBlade, you'll notice an input layer called 'HE_Camera'; when the right mouse button (RMB) is held down, you'll notice an input command is passed through to this layer (which signals calls to various functions in the _Input_Camera script).

If you want, you can override the default behavior by creating a layer of your own and intercepting mouse (or keyboard) input before it gets to the default HE_Camera layer.

This can be accomplished by performing the following steps:
  • Open your 'gamekeybindings.ini' file (if it does not exist, create it by copying the 'HeroEngineKeyBindings.ini' file) and add a new command layer. I'll call it 'E_Camera' as an example.
  • Create a client-side script called 'E_Input_Camera' which will act as a command handler for our new command layer.
  • Copy all of the keybinding information from the 'HE_Camera' layer to your new 'E_Camera' layer and replace the default '_Input_Camera' script reference with our 'E_Input_Camera' script reference. Feel free to remove any commands you don't need. If nothing is removed, the section should look something like this:
Code: [Select]
[E_Camera] = E_Input_Camera
  DragRotate = ANY+MOUSE.RIGHT
  DragRotateCamera = ANY+MOUSE.4
  ClearRotateCamera = ANY+MOUSE.5
  ToggleFullscreen = F12
  Click = MOUSE.LEFT
  ToggleAspectRatio = SHIFT+F12
  RotateCameraUp = ANY+R
  RotateCameraDown = ANY+F
  ZoomIn = NUM+
  ZoomOut = NUM-
  • Ensure your new layer is pushed onto the command layer stack by providing an 'HE_OnSetControlledCharacter' override (see _CharacterSystemClassMethods for the original _OnSetControlledCharacter); this override should push your new layer onto the stack either directly before or directly after the default HE_Camera layer.  When we enter our 'top down' mode, the default HE_Camera layer will be disabled and our new E_Camera layer will handle the appropriate commands. Your override function might look something like this:
Code: [Select]
method HE_OnSetControlledCharacter( char as NodeRef of Class HBNode )
    PushCmdLayer("E_Camera") //<------------- NEW LAYER
    ActivateCmdLayer("GUIEditor", false)
    ActivateCmdLayer("HE_Mouse", true)
    ActivateCmdLayer("HE_Camera", true)
    ActivateCmdLayer("E_Camera", false) //<------------- NEW LAYER
    ActivateCmdLayer("HE_Movement", true)
    ActivateCmdLayer("HE_Command", true)
    SendEvent(char, "_onsetcontrolledcharacter")
  • In the code that switches to your 'top down' view, you'll need to call 'ActivateCmdLayer("HE_Camera", false)' to disable the default command layer and 'ActivateCmdLayer("E_Camera", true)' to enable the custom handler. When switching back to character view, you'll of course need to reverse the process (swapping the value of the boolean in these statements).
  • Edit your 'E_Input_Camera' script to respond appropriately to various commands coming from the input system (the default behavior can be seen in the _Input_Camera script and involves writing handlers for 'onCmdStart', 'onCmdStop', 'onMouseMove', etc); this will involve skipping rotating and panning the camera in response to mouse input (e.g. do not make calls to ACCController._setGameCameraRotationalOffset(...), etc) and either performing your own transformations or doing nothing at all.

Was this sufficient to get you pointed in the right direction? Let me know if you have follow-up questions.

Absolutely.  HE_isGUILayerValid(...) is called from the method _isGUILayerValid(...) on the $GUI system node.  All you have to do is:
  • Create a game-specific GUI class (e.g. HE_GUI, Josh_GUI, gooey_GUI)
  • Add the method HE_isGUILayerValid to that class' ClassMethods script
  • Glom your class onto the GUI class using the System Node interface (F5 menu-->Tools-->System Nodes Configuration GUI)

Then, every time $GUI._isGUILayerValid(...) is called, your custom method HE_isGUILayerValid(...) will handle the call.  Any custom rules you want to add can be stored here, such as the exclusion of "default" from the test.

Your custom method which would always flag the default layer as valid might look something like this:
Code: [Select]
method HE_isGUILayerValid(layerName copies String) as Boolean
  if layerName = "default"
    return true
  if findString(layerName, "_popupLayer") > 0
    layerName = replaceString(layerName, "_popupLayer", "")
  var layers = me._validGUILayersList()
  foreach layer in layers
    if layer == layerName
      return true
  return false

With the added snippet being:
Code: [Select]
method HE_isGUILayerValid(layerName copies String) as Boolean
  if layerName = "default"
    return true

This looks like a perfectly valid change at first glance.  However, the $GUI._isGUILayerValid(...) method does offer a hook for game-specific code to latch onto and - if for some reason there is a desire by a licensee to invalidate the default layer - this would become impossible with the proposed change.

I would suggest providing your own HE_isGUILayerValid(...) method and escape it early when it detects the "default" string.  Though this won't give you all of your cycles back, it should be far more efficient than running through that findString and searching the valid layers list.

Let me know what kind of boost you get from doing this and - if it fails to give you back a significant chunk of cycles back - we can revisit the issue.

Thanks! (:

Design & World Building / Re: New Room Bounding Box not working
« on: Dec 20, 10, 05:25:54 PM »
The decision about which room to assign the camera to (for the purpose of room transitions) is based upon two things:
  • The current position of the camera.
  • The room membership of the object directly beneath the camera.

If the camera is currently hovering over an object in Room A, then moves such that an object in Room B is positioned directly beneath it, a room transition will occur to room B.  What this means in the case where you have multiple overlapping rooms (as I suspect is the case with you) is that a transition is not necessarily triggered based upon crossing room boundaries, rather it is triggered based upon the relative position of objects in the room and the camera (namely, objects being beneath the camera).  As such, the visual representation of rooms in the 'visualize room boundaries' tool will not correspond to the boundaries for transitions.  While this may be somewhat counterintuitive, understanding what triggers a room transition can help ensure your rooms and their assets have the correct visibility settings to prevent 'disappearing objects' or other artifacts caused by unexpected transitions.

Implications of this behavior:
  • Adding an asset instance onto some area geometry in a room and then moving that instance to another room will trigger a room transition whenever the camera happens to cross over the instance. This is usually not an issue if you're smart about setting visibility for rooms that happen to intersect in this way, but it could cause strange artifacts when the camera is passed over these objects if you're not expecting this behavior.
  • Having several rooms whose bounding boxes overlap introduces a need to manually manage visible rooms much more carefully.

Hopefully this will help you isolate the cause of the problem.  If not - and you really think there's an issue with the way rooms are working (or if you'd like a more thorough explanation of how this works) - feel free to provide a more detailed description of your particular case to help me reproduce the behavior!

Design & World Building / Re: Area Duplication
« on: Dec 19, 10, 07:10:45 PM »
If the goal is to generate restore points of area geometry as a preventative measure against editing mistakes or as a means of providing rudimentary version control, you may want to leverage Area Checkpoints (http://hewiki.heroengine.com/wiki/Area_checkpoints).  Creating a restore point with the Area Checkpoints system will back up all data associated with the area root node and store it in the GOM.  The checkpoint will be associated with both the area in question and a label you provide at the time of creation.  At any time, a previously saved checkpoint may be loaded from an edit instance to effectively roll back the area's geometry to an earlier point in time.

To create an Area Checkpoint, first ensure that you are currently loaded into the edit instance of the area in question.  Then, open the hotspot menu by hitting F5.  Click on Area Checkpoints under the Areas heading to open the default clean engine GUI for checkpoints.

At this point, clicking on New will allow you to provide a name for your checkpoint and save the current state of the area's geometry.  Alternatively, clicking on Restore will allow you to select a previously saved checkpoint to restore to an earlier state.

Note: The Area Checkpoints system should only be used to manage iterations of area geometry and its associates and is not intended as a comprehensive backup tool.  Any custom game systems you've written which may manipulate or otherwise interact with an area's data will need to provide its own mechanism for backing up and dealing with changing data.

The alternative is to use the organizer's Area Import feature to import the geometry from one area to another (blank) one.  This is closer to the method you had originally suggested.  Details on how to accomplish this are located at (http://hewiki.heroengine.com/wiki/How_to_Import_an_Area).

Pages: 1 ... 6 7 [8] 9