MongooseWhere programmers kick back and build silly things together.

All help topics · Link to this topic

Showing help on 'refusal-programming'


-- -- -- refusal programming

This entry explains how to program new kinds of refusal for the refusal mechanism. This information is only useful for programmers. For basic information about the refusal mechanism, read the 'refusal' entry.

- making a new refusable action

Making a refusable action has several steps. You have to (1) override the verb that lists your refusable actions, (2) write a verb to handle any special cases in determining when an action should be refused, and (3) write the code that does the actual acceptance or refusal of the action.

You can put new refusable actions on yourself, or on a player class. If you put them on yourself, then you are the only one who can use them. If you put them on a player class, then everyone who belongs to that class can use them.

I'll walk you through the example of making a refusable action 'smirk'. If you '@refuse smirk from Munchkin' then anything Munchkin tells you with the word 'smirk' in it will be refused. Since this is useless, I'll assume that you're defining it on yourself, instead of on a player class.

(1) You have to make 'smirk' a refusable action. You do that by overriding your :refusable_actions verb. :Refusable_actions is a verb which returns the actions that you know how to refuse. It is a verb instead of a property so that players can add refusable actions at all levels of the player class hierarchy without interfering with each other.

@verb me:refusable_actions this none this
@program me:refusable_actions
return {@pass(@args), "smirk"};
.

'@pass (@args)' means 'include the actions that my parent knows how to refuse, because I know how to refuse them too.'

(2) The refusal mechanism provides for special cases in deciding whether an action should be refused. None of the refusals defined on Frand's Player Class actually make use of this special case, and you probably won't have to use it either. But you still have to write a verb to deal with it.

The verb should be named :refuses_action_<action>, where <action> is the action that the verb is for. In this case, it should be :refuses_action_smirk. The verb takes some arguments and returns whether the action should be refused. The details of this will someday be explained in the next section, 'fancy refusable actions', but in all simple cases the verb should simply return 1.

@verb me:refuses_action_smirk this none this
@program me:refuses_action_smirk
return 1;
.

(3) Finally you must implement the refusal itself. The code that performs the action should call <target>:refuses_action (player, <action>). <target> is the target of the action; it is you, because others are smirking at you. 'Player' is the current player, the one who is attempting to perform the action. <action> is the action the player is attempting, 'smirk'.

In this case, you want to change your :tell verb to check whether its message contains the word 'smirk', and whether you are refusing smirks from the player who's talking. It also has to do two other things: (1) inform the player whose smirk was refused, and (2) call this:report_refusal, which tests whether you have refusal reporting turned on, and if so, reports the refusal to you. The call is this:report_refusal (<player>, <message>).

@verb me:tell this none this
@program me:tell
line = tostr (@args);
if (index (line, "smirk") &&
this:refuses_action (player, "smirk"))
player:tell (this.name, " refuses your s m i r k.");
this:report_refusal (player, "You refused a s m i r k.");
else
pass (@args);
endif
.

The messages say 's m i r k' instead of 'smirk' to prevent an infinite :tell loop.

It may help to study some examples. To see how whisper refusal works, read #3133:whisper. For page refusal, see #3133:receive_page and #3133:page_echo_msg. For mail refusal, #3133:receive_message. For move and accept refusal, #3133:moveto or #3133:accept (they are the same verb).

- refusal data structures

Refusal information is stored in four main properties, all lists.

.refused_origins - who you are refusing actions from
.refused_actions - for each, what actions you're refusing
.refused_until - when the refusals expire
.refused_extra - any further information about each refused action

These properties are all readable and owned by Frand. You (or anyone) can see their values, but you can only change them by calling verbs for the purpose. The verbs are explained later on.

Your .refused_origins property remembers who you are refusing actions from. It might have the value {#47, #34567} if you are refusing actions from Frand (#47) and Stupid_Jerk (#34567). If you are refusing actions by guests, it will have entries like "xxP1Xw.nkQzft". This weird string is a code word that stands for the guest's connection site; you are refusing actions not by a certain guest character, but by any guest who may connect from that site. The verb :player_to_refusal_origin (<player>) converts a player object to a refusal origin (either the same player object or a guest's site code) suitable to be stored in .refused_origins.

Your .refused_actions property stores, for each origin in .refused_origins, a list of the actions you are refusing for that origin. Continuing the example from the last paragraph, if you are refusing page from Frand and page, whisper, and mail from Stupid_Jerk, then your .refused_actions has the value {{"page"}, {"page", "whisper", "mail"}}.

Your .refused_until stores an expiration time for each refusal origin, in the form used by time(), the number of seconds since midnight, 1 January 1970, GMT. All actions that you are refusing from a given player expire at the same time. If you are refusing Frand until 19 December 1992, and Stupid_Jerk for seven years, your .refused_until might have the value {724763397, 928384392}.

Your .refused_extra property stores any additional information needed for fancy refusals. It stores one piece of information for every action you are refusing from every player. In other words, .refused_extra contains a list of lists which are parallel to the lists in .refused_actions. None of the built-in refusals on #3133 is fancy, so for them, the extra piece of information is always 0. In the example, your .refused_extra has the value {{0}, {0, 0, 0}}. The next section explains how to use .refused_extra in fancy refusals.

Another property, .default_refusal_time, stores the duration in seconds that a refusal lasts by default. This property is readable and owned by Frand. Its value is usually 604800, seven days. You can set it via the verb :set_default_refusal_time.

- fancy refusable actions

THIS SECTION IS NOT YET WRITTEN

- selective refusal reporting

If you have done '@refusal-reporting on', then you see a message when you refuse somebody else's action (for some actions). This reporting is done by the verb :report_refusal.

:report_refusal (<player>, <message>, ...)

The first argument is the player whose action is refused, and the rest are concatenated to form the message that you see.

You can override this verb if you want more selective refusal reporting. If you're refusing pages, for example, you might find that the refusal report is just as annoying as the original page--but you still don't want to turn it off. In that case, you might decide to give no more than one refusal report per minute, using code like this:

@property me.refusal_time 0
@property me.refusal_count 0
@verb me:report_refusal this none this
@program me:report_refusal
if (time() - this.refusal_time < 60)
this.refusal_count = this.refusal_count + 1;
else
if (this.refusal_count > 0)
pass (args [1], "You've refused ", this.refusal_count+1,
" actions recently.");
else
pass (@args);
endif
this.refusal_time = time();
this.refusal_count = 0;
endif
.

The property that remembers whether refusal reporting is turned on is .report_refusal, which may have the values 0 or 1. The property is owned by Frand, so you can't change it directly.

- refusal synonyms

You can give a refusal more than one name by overriding the verb :translate_refusal_synonym. The verb takes one argument, a word from the command line, and it returns a list of refusal names that the word stands for. If the word is not a synonym, :translate_refusal_synonym should return the empty list, {}.

:translate_refusal_synonym (<word>) -> <list>

If you want 'noise' to mean both page and whisper for you, then you'd write this. You need to pass(@args) to get the synonym 'all', which stands for all refusable actions. If you type '@refuse noises from Munchkin', the plural 's' is removed automatically, so you don't have to worry about that.

@verb me:translate_refusal_synonym this none this
@program me:translate_refusal_synonym
return pass(@args) ||
(args [1] == "noise") ? {"page", "whisper"} | {};
.

- refusing actions by classes of players

THIS SECTION IS NOT YET WRITTEN