Generic Wandering Automaton (#502)

(an instance of Generic Automaton made by Zon)

     You see a homeless, nomadic robot. Its memory circuits were affected by being stored too close to a television set and may now be seen roaming about shouting "Exterminate! Exterminate!".

Go to location of this object, wooden box.


HELP MANUAL:
     The Generic Wandering Automaton is a wandering version of the Generic Automaton, 
which is described below.  A child of the Generic Wandering Automaton will randomly 
wander from room to room, unless it has been interacted with during its last cycle 
(see @setcycle below).
     
     Commands to Generic Wandering Automata:
     
     @start 
        This causes the automaton to begin wandering.  It will pick a random exit in the 
room it is currently in and take it every 120 seconds (the default).  If someone 
has interacted with the automaton during that 120 seconds (or the time set by @setcycle), 
it will not take an exit. 
     
     @stop 
     	This causes the automaton to stop wandering.
     
     @setcycle  to 
     	This sets the move interval of the automaton
     
     shoo 
     	This cause the automaton to leave the current location and not
     	to come back for a while.
     
     Programming note:
     	The ways_out property of the Generic Wandering Automaton contains
     a list of exit names that the automaton will try to use when leaving
     a room.  By modifying this, and giving the exits in a particular area
     matching names, the area of movement of the automaton can be constrained.
     
     Also note that the act verb may be overridden to periodically perform some action, 
other than or in addition to moving the 'bot around.
     ------------------------------------------------------------------------
     
     Help for Generic Automaton
     
     The Generic Automaton is an easy-to-program robot which responds to simple commands. 
 It can have multiple MOODS, each one of which has a different description and set 
of RESPONSES.  Commands trigger an announced response and can also trigger a TRANSITION 
to another mood. For example, a trivial robot might be made as follows:
     
        @create #192 named robot
        @describe robot as "A crash-test dummy."
        @setresponse robot with hug,kiss: pets the Generic Wandering Automaton.  It smiles.
        @setresponse robot with hit,kick: pets the Generic Wandering Automaton.  It frowns.
     
     The pattern pet will be substituted with the verb name, and pets will be substituted 
with the third-person form of the verb.  So, if Calkins types "kiss robot" or "hit 
robot", we will see:
     
        Calkins kisses the robot.  It smiles.
        Calkins hits the robot.  It smiles.
     
     This trivial automaton has only one mood (called "initial"), but things don't really 
get interesting until multiple moods are created.  Moods and transitions between 
moods form a sort of MENTAL LANDSCAPE or personality which other players can explore, 
somewhat like rooms and exits.  To make a new mood (or switch to an existing mood), 
use @setmood.  For example, we can make our trivial robot slightly more interesting:
     
        @setmood robot to initial
        @transition robot with hit,kick:angry
        @setmood robot to angry
        @describe robot as "A very angry test dummy."
        @setresponse robot with hug,kiss: pets the Generic Wandering Automaton.  It relaxes.
        @transition robot with hug,kiss:initial
        @setresponse robot with hit,kick: pets the Generic Wandering Automaton.  It pets 
visitor!
     
     Now kicking the robot will make it mad, and when it's mad it will react more violently 
to being kicked again.  Kissing or hugging it will calm it down (transition to initial 
mood).  If an automaton is left alone in a room, it automatically returns to the 
"initial" mood, if it has one.
     
     To design an automaton think of what moods or states it should have. Pick a set of 
basic commands to trigger responses and mood transitions. Draw a TRANSITION DIAGRAM! 
 Complex automatons have over a dozen moods and are impossible to design without 
making a diagram. Here is the diagram of a three-mood stray dog:
     
                    +-------<---------------<---------------+
                    |                                       |
       feed +---+   V        +-->--+        +---+           |
        pet |   |   |        | pet |        |   | kick      |
            \   V   |        \     V   pet  \   V           |
            HAPPY---+        INITIAL<---<---ANGRY------>----+
            |   ^            |     |        ^   ^     feed
            |   |   feed     |     |  kick  |   |
            |   +---<-----<--+     +--->----+   |
            +------->------->------->------->---+
                            kick
     
     Besides setting responses and transitions, you can use @rminputs to erase a response 
and @actions  to review the programming of a given mood. @rmmoods will erase 
whole moods, and @cpmood will copy the responses and transisions of another mood 
to the current mood (sometimes a convenient starting point for a mood that is similar). 
 Once the basic logic of an automaton is finished, @alias can be used to beef up 
the number of verbs the robot will respond to:
     
        @alias robot with slug,punch,slap=hit
     
     Aliases apply to all moods, but are overridden by any set responses in any particular 
mood. Useful tip: if you are using @notedit, beware that the automaton will return 
to its "initial" mood when you depart to the note editor.  So it is a good idea to 
be carrying the robot while you are programming it.



VERB SOURCE CODE:

find_exits:
"find_exits() -- return a list of exit object numbers that are valid for the bot 
to take from the current room.  If the bot doesn't set ways_out, those exits are 
the obvious_exits() - 'banned exits'.  Otherwise, they are the exits named by ways_out 
- 'banned exits'.    ";
exits = {};
room = this:this_room();
if (this.ways_out != {})
    for name in (this.ways_out)
        exit = room:match_exit(name);
        if ((valid(exit) && (!(exit in exits))) && (!this:banned(exit.dest)))
            exits = listappend(exits, exit);
        endif
    endfor
else
    obvious_exits = room:obvious_exits();
    for exit in (obvious_exits)
        if (valid(exit) && (!this:banned(exit.dest)))
            exits = listappend(exits, exit);
        endif
    endfor
endif
return exits;
.


act_loop:
if (!this.stopped)
    fork (0)
        this:act();
        suspend(this.act_interval);
        this:act_loop();
    endfork
endif
.


act:
"act(): the repeating action of the automaton goes here.  It can be a movement, as 
here, or be overridden to do some other repeating action";
if (this.should_act && ($object_utils:has_property(this.location, "exits") || is_player(this.location)))
    exits = this:find_exits();
    if (length(exits))
        destination = exits[random(length(exits))];
        destination:move(this);
    endif
else
    this.should_act = 1;
endif
.


@start:
if (player != this.owner)
    player:tell("Only the owner of ", this.name, " can start it.");
    return;
endif
if (this.stopped)
    player:tell(this.name, " is now running.");
    this.stopped = 0;
    this.should_act = 1;
    this:act_loop();
else
    player:tell(this.name, " is already running.");
endif
.


@stop:
if (player != this.owner)
    player:tell("Only the owner of ", this.name, " can stop it.");
    return;
endif
if (this.stopped)
    player:tell(this.name, " is already stopped.");
else
    this.stopped = 1;
    player:tell(this.name, " is now stopped.");
endif
.


accept_input:
"accept_input(): this keeps the Wandering automaton from walking off (acting), if 
it has recently accepted input";
this.should_act = 0;
return pass(@args);
.


@setc*ycle:
if (player != this.owner)
    player:tell("Only the owner of ", this.name, " can set its cycle time");
    return;
endif
cycletime = tonum(iobjstr);
if (cycletime < 30)
    player:tell("Cycle times must be greater than 30 seconds.");
    return;
endif
this.act_interval = cycletime;
player:tell("Cycle time of ", this.name, " set to ", cycletime, " seconds ");
.


shoo:
current_room = this:this_room();
if (this.stopped)
    player:tell(this.name, " isn't wandering, so can't be shoo'ed away.");
    return;
endif
if (current_room in this.banned_rooms)
    player:tell(this.name, " has already been shoo'ed away from ", current_room.name);
else
    this.banned_rooms = setadd(this.banned_rooms, current_room);
    this.ban_times = setadd(this.ban_times, time());
    player:tell("You shoo ", this.name, " away from ", current_room.name, " for a 
while.");
    current_room:announce(player.name, " shoo's ", this.name, " away from ", current_room.name, 
" for a while.");
    this.should_act = 1;
    this:act();
    this.should_act = 0;
endif
.


banned:
"Boolean banned(room object #) -- is the room in the banned list?";
position = args[1] in this.banned_rooms;
if (position == 0)
    return 0;
endif
"if the bots ban interval has expired, delete ban info and return 0, else return 
1";
if ((time() - this.ban_times[position]) > this.ban_interval)
    this.banned_rooms = listdelete(this.banned_rooms, position);
    this.ban_times = listdelete(this.ban_times, position);
    return 0;
else
    return 1;
endif
.


this_room:
"is someone holding the wanderer?";
if (is_player(this.location))
    return this.location.location;
else
    return this.location;
endif
.



PROPERTY DATA:
      ways_out
      should_act
      stopped
      act_interval
      ban_times
      banned_rooms
      ban_interval

CHILDREN:
robot Strays mangy cat Generic Speaking Wandering Automaton brown goat Jeweled Scorpion lithe and winsome Afsheeni Breast Implants Hugo emma Lancelot