Custom signals

Simple notification events

If you need to add an event with handlers that don't take any parameters and don't return anything, then easiest way it to use SimpleSignal / SingleSlot classes:

class MyEventClass extends AcediaObject;

var private SimpleSignal onMyEventSignal;

protected function Constructor()
{
    onMyEventSignal = SimpleSignal(_.memory.Allocate(class'SimpleSignal'));
}

protected function Finalizer()
{
    _.memory.Free(onMyEventSignal);
    onMyEventSignal = none;
}

public function SimpleSlot OnMyEvent(AcediaObject receiver)
{
    return SimpleSlot(onMyEventSignal.NewSlot(receiver));
}

//  Suppose you want to emit the `signal` when this function is called...
public function SimpleSlot FireOffMyEvent(AcediaObject receiver)
{
    //  ...simply call this and all the slots will have their handlers called
    onMyEventSignal.Emit();
}

Then you can use OnMyEvent() as a signal function:

//  To add handlers
myEventClassInstance.OnMyEvent(self).connect = handler;
//  To remove handlers
myEventClassInstance.OnMyEvent(self).Disconnect();

Events with parameters

Some of the events, like OnNetDamage(), can take parameters. To create signals like that, follow the template and define new classes like so:

class MySignal extends Signal;

public final function Emit(<PARAMETERS>)
{
    local Slot nextSlot;
    StartIterating();
    nextSlot = GetNextSlot();
    while (nextSlot != none)
    {
        MySlot(nextSlot).connect(<PARAMETERS>);
        nextSlot = GetNextSlot();
    }
    CleanEmptySlots();
}

defaultproperties
{
    relatedSlotClass = class'MySlot'
}
class MySlot extends Slot;

delegate connect(<PARAMETERS>)
{
    DummyCall(); // This allows Acedia to cleanup slots without set handlers
}

protected function Constructor()
{
    connect = none;
}

protected function Finalizer()
{
    super.Finalizer();
    connect = none;
}

defaultproperties
{
}

Here you can use any set of parameters instead of <PARAMETERS>.

Events with return values

Sometimes you want your handlers to respond in some way to the event. You can either allow them to modify input parameters (e.g. by declaring them as out) or allow them to have return value. OnNetDamage(), for example, is allowed to modify incoming damage by returning a new value.

To add signals / slots that handle return value use following templates:

class MySignal extends Signal;

public final function <RETURN_TYPE> Emit(<PARAMETERS>)
{
    local <RETURN_TYPE> newValue;
    local Slot          nextSlot;
    StartIterating();
    nextSlot = GetNextSlot();
    while (nextSlot != none)
    {
        newValue = <SLOT_CLASS>(nextSlot).connect(<PARAMETERS>);
        //  This check is necessary before using returned value
        if (!nextSlot.IsEmpty())
        {
            //  Now handle `newValue` however you see fit
        }
        nextSlot = GetNextSlot();
    }
    CleanEmptySlots();
    //  Return whatever you see fit after handling all the slots
    return <END_RETURN_VALUE>;
}

defaultproperties
{
    relatedSlotClass = class'MySlot'
}
class MySlot extends Slot;

delegate <RETURN_TYPE> connect(<PARAMETERS>)
{
    DummyCall();
    //  Return anything you want:
    //  this value will be filtered inside corresponding `Signal`
    //  if no handler is set to the associated slot
    return <???>;
}

protected function Constructor()
{
    connect = none;
}

protected function Finalizer()
{
    super.Finalizer();
    connect = none;
}