logic operations

logic actions:
base action CLogicAction that has a status (true or false)

each derived action can combine a set of other actions and evaluate their status to generate an own status, then optionally trigger a follow-up action if the status is either true or false.

fcts:
and, or, nand, nor, xor, etc..
set (true / false), toggle

performance optimizations and code-structure cleanup

The problem

each object potentially carries one or more triggers.

When evaluating triggers, this means iterating through every object available and test for any triggers. That’s the naïve approach and has a huge drawback: only a tiny subset of the available objects are interactive and actually carry triggers.

Having to check every single object is a big performance hit, since we’re performing 99% of unnecessary checks.

The solution

Triggers

Since the introduction of the action- and trigger-manager classes, continuing to carry trigger-evaluation within the scenegraph-manager has become sub-optimal.

The new approach would add a method to the trigger manager that checks all of its created triggers for any matches and, if applicable, fires them.

Since any triggers are instanciated using the manager, we can store their pointers and evaluate them whenever necessary. This is the minimum effort needed for trigger evaluation: perform a check on every (active) trigger that is potentially able to fire.

Actions

A similar mechanism will be implemented in the action-manager: Blended actions shouldn’t be carried within the scenegraph-manager anymore, but should be managed by the action-manager from now on.

Meaning whenever an action is triggered that is in ‘bleded’ state, it is added to the action-manager’s internal processing list instead, leaving the scenegraph-manager with only having to call the action-manager’s new ‘process’ method.

actions&triggers: local and global IDs

an action needs to be able to reference any action or trigger that has been created in order to manipulate it.

For example:

if the player is standing in front of a door and presses X, the door should open.

If he presses X again, the door shouldn’t open again but close instead.

This means the ‘open’ action, once triggered, needs to deactivate itself and activate its sibling action instead.

In order to allow actions such a behaviour, each action needs a global unique identifier.

The action manager manages those IDs and is able to find the according pointer when queried with an ID.

Problem

This is all fine and well if an action wants to manipulate a very specific action or trigger in the global context. But what if the action wants to manipulate a local action?

The above example works fine with global IDs, but imagine what happens when the object containing the ‘open’ and ‘close’ actions gets copied and moved elsewhere.

If the player approaches the copied door and presses X, the door opens. That’s ok so far. The just triggered ‘open’ action deactivates itself as expected, but instead of activating its sibling action, it activates the ‘close’ action of the original door object.

Why? Because the just triggered ‘open’ action references the ‘close’ action by its global ID, which got copied along with the rest of the object.

Introducing Local IDs

In order to prevent exactly this problem from happening, aside from its global ID, each action also contains a ‘local ID‘.

This ID identifies the action within its parent object.

So if the door object’s ‘open’-action references its sibling action by its local ID,  copying the object would be no problem, the copied action would enable the correct sibling action.

The same system will be implemented for triggers.

technical details

the actionmanager class contains a map: “id <-> action pointer” for global IDs

the parent object contains a map: “id <-> action pointer” for local IDs

Whenever a local or global ID changes, the actionmanager or parent object gets notified.

If an action changes objects from one parent to another, the old parent gets notified and deregisters the action, before the new parent registers it.

both actionmanager and object get new methods that return the action pointer corresponding to a certain ID.

in addition to these changes, actions and triggers get the ability to reference target object not by their global ID but by using a constant or string telling them to use their parent object as their target.

tasks

  • when new text is displayed on a target object, clear/erase the old text bubble first
  • separate actions in defaultactions.cpp
  • introduce stream versioning
  • replace stream/destream operators by save/load functions
  • implement clone function in context menu
  • add spawn window to spawn button
  • open creator window upon button press
  • allow for new actions that enable/disable target actions or triggers (see next post)
  • new member for actions/triggers: m_bEnabled
  • clean up source of group and window classes – lots of unnecessary code now that they contain a grid that does the layouting for them