Tuesday, April 13, 2010

Ooz Object to Object communication.


Ooz is the scripting language that I wrote for Elderlands and all If-Else Software games. It is a very small yet robust scripting engine. It supports micro-threading, is extendable and is designed with performance in mind.

One thing I’m researching is a sort of ‘signals and slots’ system that would be integrated into the scripting language. The C++ framework called QT already supports signals and slots but has a pre-processor and is not very efficient from a performances standpoint. I need something that is performance oriented. It is very flexible in the sense that objects can hook to signals of other objects and receive callbacks through slots (class methods which are called). This works even across threads.

So I’ve been pondering how to create a system that provides the flexibility of a generic signal/slot system while keeping performance the #1 priority in the Ooz language.

I currently have extended the language to support an event system. Objects can register events they care about and receive a callback whenever any event is fired that they care about. Event parameters are handled through a structure which is nothing more than a container of data types that are sequentially added. Event types are defined in a large enumeration .

Example:
class ObjectA
{
  // Register Event
  declare ObjectA()
  {
    RegisterEvent(Event_SomethingHappens);
  }

  declare void OnEvent(EventType _eEvent, EventParams _kParams)
  {
    if(_eEvent==Event_SomethingHappens)
    {
      int iParam1=_kParams.Get(0);
      // Do something
    }
  }
}

class CObjectB
{
  // Someplace in another object…
  declare void MakeSomethingHappen(int _iParam)
  {
    EventParams kParams=new EventParams;
    kParams.Set(0,_iParam);
    TriggerEvent(Event_SomethingHappens,kParams);
  }
}

Now an integrated signals and slot system could make the code look much cleaner and if done correctly could be more efficient. A big benefit is the system is handled internally to the language and not as part of an extension.

Signals are defined like method prototypes and are fired off with the ‘emit’ keyword. The slots are receiving methods that are called when the signal is fired.

Example:

CObjectA objA=new CObjectA;
CObjectB objB=new CObjectB;

// Connect objA’s slot to objB’s signal.
connect objA.MySlot to objB.MySignal;

// Invoke a method of objB that will fire off a signal.
objB.DoStuff();


class ObjectA
{
  // This is the method that will be connected to a particular
  // signal.
  declare void MySlot(int _iParam)
  {
    // Do something
  }
}

class ObjectB
{
  // Declare an event that others can connect to.
  declare signal MySignal(int _iParam);

  declare void DoStuff()
  {
    // Emit the signal with a given parameter.
    emit MyEvent(50);
  }
}


Now one area where the signals/slots system seems to have a weakness compared to the event system is when I have events that are generated on the Elderlands Game server and received by objects on the Client. Of course this could be handled by creating global objects representing the ‘server’ and have all client objects connect to the server signals they care. It would take a bit of massaging, but overall this looks like a good direction to go.

Tuesday, April 6, 2010

Combat AI in Elderlands

Target Acquisition
In my previous works (Dransik) I had a very simplistic AI system. Attacking mobs looked for a target that was closest to them and went after it. Whenever they were attacked back and the attacker was closer than their current target they would switch to that target. This allowed for the ping ponging of a mob.

What Elderlands does for combat AI and agro management is a bit more complex. Each entity has a list of other entities that it is upset with and a list of entities that are upset with it. Now the list of entities that we are upset with is called the threat list or agro list. Players typically ignore this list but active npcs use this to determine which npc is the most viable target.

Whenever an entity is attacked the attacker adds agro to the list. The amount of agro is determined by the form of attack. For example a melee attack generates threat near equal to the amount of damage. When a ‘heal’ spell or any helpful spell is cast upon the any entity in our threat list will be added at 2x the value of the healed amount. This makes life a bit dangerous for support players in a battle.

Every second the agro list is sorted by the amount of agro recorded for each entity in the list. If the new top level entity contains X% more agro than the current target then we switch targets. When an entity dies, gets too far away, or meets a few other conditions they are then removed from the list.

For npcs when the list is empty they are out of combat and enter ‘homing’ mode. I have yet to refine the rules for players to determine when they are out of combat. This is important since health regeneration and some item cooldowns will be linked directly to the combat mode flag.

Maintaining Agro
Mobs stay in combat as long as they have entities on their agro list. So how do we lost agro? A mob should understand that if it was not attacked after some time it would drop that target or at least reduce the agro value for the target.

Npcs will also drop entities from the agro list when they meet certain criteria,  such as distance, death, certain protection spells, etc. When the npc’s agro list is empty they go into a ‘homing’ state where they attempt to return to their place of origin.

A big question is what to do while in this homing state. Do we allow players to attack a retreating npc? In World of Warcraft evading npcs are immune to all attacks until they return to their origin. This is something I’m going to spend some time in testing before I settle for a final solution. The goal is to prevent players from exploiting npcs and using this as a way to farm XP without subjecting themselves to danger. Another precaution taken against npcs is to have them regenerate their health once they reach home. The reason for this is a player could continue to resurrect and re-attack a mob until it finally does go down. This prevents us as developers from creating difficult mobs that require players of certain levels or a certain number of players in order to default.

Monday, April 5, 2010

Elderlands Development Blog V1.00

Elderlands finally has a blog. Here I will post articles relating to Elderlands development, general technology/programming, rants, or whatever subject comes to mind that relates to the game or my work. So Enjoy.