Freak is one the original backgrounds we've been wanting to add to Wayfar for a long time. We've finally started completing some of the pieces. As a freak, the player is wanted by law enforcement, and his or her goal is to locate and enter a heavily guarded lab in order to remove their warp powers - before they end up an experiment in the same lab, forever.
Freak powers:
* Warpstorm - 100%
Unleash your psychic powers in an area around your target. 2 minute cooldown.
* Teleport - 25%
Zap yourself to another tile on the same planet. 5 minute cooldown.
* Mind Ray - 0%
Focus your mind on a single target to deal energy damage. 2 minute cooldown.
Freak objective:
Corporate lab - 10%
* map complete
Wayfar 1444 is a text based online multiplayer game. You are a colonist, sent to the surface of an alien world with a few basic supplies. Join up with other colonists, or plot against them, while surviving and building a self sufficient colony.
Showing posts with label $action. Show all posts
Showing posts with label $action. Show all posts
Tuesday, April 1, 2014
Tuesday, October 9, 2012
The Advanced Hellcore $action tutorial, Part 2
This article covers the following in the first section: pro-tips for action use, common problems, and suggested trouble shooting. This tutorial is third in the series: Part 1, Part 2.
Extra utility verbs on $creatures and $players:
* is_doing(OBJ action, [ ?OBJ callback ]) - this requires the first argument, and the second is optional. It checks a player or creature to see if they are executing the action sent as the first argument. If the callback argument is also present, that is checked against the player's executing action. If the creature or player matches the criteria, is_doing returns 1, otherwise 0.
Example:
" pretend this is a sanity check in an eat command";
if( player:is_doing($actions.mock_attack) )
player:tell("You can't eat right now, because you are attacking!");
endif
"and this one checks to see if the player is attacking with a sword";
"$weapons.sword would be a valid weapon object, and we're assuming the first callback in $actions.mock_attack is the weapon being used.";
if( player:is_doing($actions.mock_attack, $weapons.sword) )
player:tell("You use your sword to chop your food before eating it. Oh boy!");
endif
Changing call back arguments in an $action
Scenario: You want to loop through an action exactly 5 times.
"you queue it like this";
player:queue_action($actions.mock_loop, {1, 5});
"in your _finish code you check the stage status and increment stage (in this examples, args[2][1])";
who = args[1];
stage = args[2][1];
max_stage = args[2][2];
if(stage < max_stage)
stage = stage + 1;
"keep looping, by returning an action object from _finish. _continue will be called on the action you return.";
"Usually you return the action you're executing!";
return {this, {stage, max_stage}};
else
who:tell("Looping complete!");
"we can return E_NONE to finish the action, or fall through to more codes dealing with the specifics";
return E_NONE;
endif
Broadcasting events from within an $action
Events and callbacks should be familiar topics for anyone who has done any event driven programming. The idea is simple: code 'broadcasts' an 'event', and other code 'listens' and then reacts to the event. You broadcast with this verb, which is defined on #1 or $room depending on your hellcore version:
broadcast_event(INT is_start, OBJ action, OBJ who, LIST callbacks)
is_start is 0 for a completing action, and 1 for a beginning action. action is the action object (or really whatever object) being broadcast, who is the action executor, and callbacks is a list of the callbacks from the action (exactly the same as args[2] within the $action itself).
Example:
$actions.mock_walk
"let's announce we're going to walk around - pretend this is in a _start verb";
who = args[1];
direction = args[2][1];
"we broadcast a 'hey i'm going to be walking here' event";
who.location:broadcast_event(1, this, who, {direction});
"now pretend this is in _finish, we announce the completion of the move";
who = args[1];
direction = args[2][1];
who.location:broadcast_event(0, this, who, {direction});
These events can then be received by other objects in the same location as who from our example above. The two main ways to receive these are:
forbid_action_* - this should be specific to the action you want to forbid. For our example, the verb would be named forbid_action_mock_walk. If forbid_action returns 1, the action is halted and the actor cannot execute it.
hear_event_* - For our example the verb would be hear_event_mock_walk.
Example on a player or creature, to receive walking messages:
hear_event_mock_walk
{starting, action, who, callbacks} = args;
direction = callbacks[1];
" as you can see we receive the args just like they are sent to broadcast_event";
if(!starting)
"if the walk is actually occuring, let's tell ourselves about it";
this:tell(who:name(), " walks off to the ", direction, ".");
endif
Calling an impromptu action from any object
You have an object, lets call it MEDICAL TERMINAL. You have a verb allowing the player to INVESTIGATE MEDICAL TERMINAL, and you want a short action (not require start, continue, finish, but you'd like the delay and action messaging). Here's how you do that.
@verb terminal:investigate
player:queue_action($actions.verb, {this, "_search_terminal", {player}, 3.0, 1}, "searching the terminal for valuable information");
@verb terminal:_search_terminal
{is_start, object, actor, callbacks} = args;
"object" will be equal to the medical terminal's object number
actor will be equal to the player who queued the action
and callbacks will be array containing the player who queued the action
This will display the player as "searching the terminal for valuable information, and within this _search_terminal verb you can do whatever simple operations you like without having to write a full action. It will take 3 seconds for the action to begin (specified by the 3.0).
$action Pro Tips
Extra utility verbs on $creatures and $players:
* is_doing(OBJ action, [ ?OBJ callback ]) - this requires the first argument, and the second is optional. It checks a player or creature to see if they are executing the action sent as the first argument. If the callback argument is also present, that is checked against the player's executing action. If the creature or player matches the criteria, is_doing returns 1, otherwise 0.
Example:
" pretend this is a sanity check in an eat command";
if( player:is_doing($actions.mock_attack) )
player:tell("You can't eat right now, because you are attacking!");
endif
"and this one checks to see if the player is attacking with a sword";
"$weapons.sword would be a valid weapon object, and we're assuming the first callback in $actions.mock_attack is the weapon being used.";
if( player:is_doing($actions.mock_attack, $weapons.sword) )
player:tell("You use your sword to chop your food before eating it. Oh boy!");
endif
Changing call back arguments in an $action
Scenario: You want to loop through an action exactly 5 times.
"you queue it like this";
player:queue_action($actions.mock_loop, {1, 5});
"in your _finish code you check the stage status and increment stage (in this examples, args[2][1])";
who = args[1];
stage = args[2][1];
max_stage = args[2][2];
if(stage < max_stage)
stage = stage + 1;
"keep looping, by returning an action object from _finish. _continue will be called on the action you return.";
"Usually you return the action you're executing!";
return {this, {stage, max_stage}};
else
who:tell("Looping complete!");
"we can return E_NONE to finish the action, or fall through to more codes dealing with the specifics";
return E_NONE;
endif
Broadcasting events from within an $action
Events and callbacks should be familiar topics for anyone who has done any event driven programming. The idea is simple: code 'broadcasts' an 'event', and other code 'listens' and then reacts to the event. You broadcast with this verb, which is defined on #1 or $room depending on your hellcore version:
broadcast_event(INT is_start, OBJ action, OBJ who, LIST callbacks)
is_start is 0 for a completing action, and 1 for a beginning action. action is the action object (or really whatever object) being broadcast, who is the action executor, and callbacks is a list of the callbacks from the action (exactly the same as args[2] within the $action itself).
Example:
$actions.mock_walk
"let's announce we're going to walk around - pretend this is in a _start verb";
who = args[1];
direction = args[2][1];
"we broadcast a 'hey i'm going to be walking here' event";
who.location:broadcast_event(1, this, who, {direction});
"now pretend this is in _finish, we announce the completion of the move";
who = args[1];
direction = args[2][1];
who.location:broadcast_event(0, this, who, {direction});
These events can then be received by other objects in the same location as who from our example above. The two main ways to receive these are:
forbid_action_* - this should be specific to the action you want to forbid. For our example, the verb would be named forbid_action_mock_walk. If forbid_action returns 1, the action is halted and the actor cannot execute it.
hear_event_* - For our example the verb would be hear_event_mock_walk.
Example on a player or creature, to receive walking messages:
hear_event_mock_walk
{starting, action, who, callbacks} = args;
direction = callbacks[1];
" as you can see we receive the args just like they are sent to broadcast_event";
if(!starting)
"if the walk is actually occuring, let's tell ourselves about it";
this:tell(who:name(), " walks off to the ", direction, ".");
endif
Calling an impromptu action from any object
You have an object, lets call it MEDICAL TERMINAL. You have a verb allowing the player to INVESTIGATE MEDICAL TERMINAL, and you want a short action (not require start, continue, finish, but you'd like the delay and action messaging). Here's how you do that.
@verb terminal:investigate
player:queue_action($actions.verb, {this, "_search_terminal", {player}, 3.0, 1}, "searching the terminal for valuable information");
@verb terminal:_search_terminal
{is_start, object, actor, callbacks} = args;
"object" will be equal to the medical terminal's object number
actor will be equal to the player who queued the action
and callbacks will be array containing the player who queued the action
This will display the player as "searching the terminal for valuable information, and within this _search_terminal verb you can do whatever simple operations you like without having to write a full action. It will take 3 seconds for the action to begin (specified by the 3.0).
Monday, September 24, 2012
The Advanced Hellcore $action Tutorial
This post is a continuation of the Hellcore $action tutorial. It is broken up into three parts: a complete listing of the default $action properties, a complete breakdown of the extended $action verbs that can be overridden by your custom actions, and special usage of action on non $action objects.
$action properties
These properties can be set on your action objects to control messages and behavior:
$action properties
These properties can be set on your action objects to control messages and behavior:
- .duration (FLOAT) - number of seconds the action takes (from _start to _finish or _continue to _finish)
- .preemptible (INT) - can this action be pre-empted by another action? [unused]
- .unstoppable (INT) - can this action be stopped by the player?
- .doing_msg (STR, default "%ting") - the doing message (eg the action play would become "playing")
- .doing_to_msg (STR, default "%ting %il") - the message printed when doing an action to something (eg the action play, on space bagpipes, would print "playing the space bagpipes")
- .oabort_msg (STR) - the message displayed to other players when the player executing the action types STOP.
$action verbs
These verbs can be overridden with your own versions for customized behavior.
- _forbidden(OBJ source, LIST callbacks) - do any clean up when the action is forbidden by the room or area.
- is_being_done_by(OBJ who) - returns 1 if who is executing this action.
- unstoppable(OBJ who) - by default, returns this.unstoppable. Can be overridden to allow stopping of an action in certain circumstances.
- _abort(OBJ who) - by default tells who that the action has been stopped. If this.oabort_msg is valid, prints it to the room.
- duration() - by default returns this.duration. Customize and pass arguments to vary duration based on circumstances.
- doing_msg(OBJ who, LIST callbacks) - by default prints doing_msg or doing_to_msg based on the length of callbacks.
Special $action usage
* Executing non $action objects as an $action
Aside from using $action objects, you can use any object, providing it has appropriately defined _start and _finish verbs. An example might be creating a throwing spear item with a THROW verb on it:
player:queue_action(this, {player.target});
The above example would queue the spear itself as an action (first calling _start, which would then return duration as normal) and passing player.target as the first callback argument.
* $actions.verb
A default action called verb is available in hellcore. Similiar to executing a non action, this allows you to call any verb as an action (instead of requiring _start or _finish). Example:
player:queue_action($actions.verb, {some_object, "_start_action", {callback1, callback2}, 5.0, 1}, "custom action message");
Where some_object is an object number or variable of type OBJ, "_start_action" is the actual name of the verb you want to call, callbacks are the callback arguments, 5.0 is any float representing the duration, and custom action message is a string to display as the doing_msg.
player:queue_action($actions.verb, {some_object, "_start_action", {callback1, callback2}, 5.0, 1}, "custom action message");
Where some_object is an object number or variable of type OBJ, "_start_action" is the actual name of the verb you want to call, callbacks are the callback arguments, 5.0 is any float representing the duration, and custom action message is a string to display as the doing_msg.
The Hellcore $action Tutorial
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.
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.
Subscribe to:
Posts (Atom)
