Hellcore provides a 'game loop' in the form of the $heart object. This object accepts registrations (for example of creatures and players) and every 30 seconds does a 'heartbeat' on every registered object. When an object beats, the heartbeat verb is called. NPCs, when doing nothing, can select an action via the verb suggest_next_action. If the creature or player is already executing an action, the action is checked for duration and moves to the next step or resolves.
When to create an $action:
- a looping or multi step process
- a process where you'd like a delay between starting and finishing
- when you would like players and creatures to share the same code for executing a process
To initiate an action on a creature or player, you call :queue_action, like so:
player:queue_action($actions.some_action, {callback1, callback2, callback3});
Callbacks can be anything that the action is setup to handle. For instance, here is a mock attack action being queued:
player:queue_action($actions.mock_attack, {player.weapon, player.target});
This would send the player's weapon and target properties to the action. The player or creature executing the action is always available inside the action.
Actions begin at the _start verb. Using the mock attack action as an example again, here is what one might look like, with commentary:
$actions.mock_attack:_start
"The player or creature executing the action is always the first argument.";
who = args[1];
"Next we retrieve the callback arguments provided when the action was queued.";
{weapon, target} = args[2];
"Print a message to the room or something, usually.";
who:aat( $su:ps( "%DN attacks %it!", who, target ) );
"At the end of start, after the duration has passed, _finish will be called with the same arguments.";
return {this:duration(), {weapon, target}};
After _start finishes and the duration has passed, the action's _finish verb is called. We'll use the mock attack as an example again:
$actions.mock_attack:_finish
"The player or creature executing the action is always the first argument.";
who = args[1];
"Next we retrieve the callback arguments provided when the action was queued.";
{weapon, target} = args[2];
"Let's roll some dice and call it a hit on an arbitrary number.";
if(random(100) <= 25)
who:aat( $su:ps( "%DN lands a hit with %p %t!", who, weapon ) );
"For this example, we'll assume weapon has two properties indicating the damage type and amount.";
target:take_damage(weapon.damage_type, weapon.hit_damage);
"Returning E_NONE from any stage of an action ends the action.";
return E_NONE;
endif
"If we got this far, we missed.";
who:aat( $su:ps( "%DN misses %it!", who, target ) );
"Now we could just print a miss message and end the action, but for example purposes, let's try hitting again until we succeed.";
"When an action object is returned from _finish, the action is not ended and instead calls _continue. This allows for repeating or looping actions.";
return {this, {weapon, target}};
You do not have to use a _continue verb. The attack could simply terminate one way or another in _finish and be resolved. But for this example, we'll use a _continue verb to loop the action until a hit is made.
$actions.mock_attack:_continue
"The player or creature executing the action is always the first argument.";
who = args[1];
"Next we retrieve the callback arguments provided when the action was queued.";
{weapon, target} = args[2];
"Print a message before passing back to _finish and rolling our attack dice again.";
who:aat( $su:ps( "%DN attempts another attack with %p %t.", who, weapon ) );
"From continue, we return a duration and our callbacks just like in _start.";
return {this:duration(), {weapon, target}};
Now we can use our mock attack action, either through a verb the player can access, or by returning it from an NPC's suggest_next_action verb. If written correctly, you can use the same $action for players and creatures without modification, and using an $action allows for great control of the timing and interaction of the executing game codes.
For more information, see the HELP $action command as an admin on Hellcore. I have also written an Advanced $action Tutorial which contains a full breakdown of the $action properties, verbs, and special uses.