/
LIB3/
LIB3/D/ADMIN/
LIB3/D/ADMIN/OBJ/
LIB3/D/ADMIN/ROOM/W/
LIB3/D/HOME/
LIB3/D/HOME/CITY/ARENA/
LIB3/D/HOME/CITY/ITEMS/
LIB3/D/HOME/CITY/POSTOFFI/
LIB3/DOC/
LIB3/GLOBAL/SPECIAL/
LIB3/GLOBAL/VIRTUAL/
LIB3/NET/
LIB3/NET/CONFIG/
LIB3/NET/DAEMON/CHARS/
LIB3/NET/GOPHER/
LIB3/NET/INHERIT/
LIB3/NET/OBJ/
LIB3/NET/SAVE/
LIB3/NET/VIRTUAL/
LIB3/OBJ/B_DAY/
LIB3/OBJ/HANDLERS/TERM_TYP/
LIB3/PLAYERS/B/
LIB3/PLAYERS/N/
LIB3/ROOM/
LIB3/SAVE/
LIB3/SAVE/BOARDS/
LIB3/SAVE/ENVIRON/
LIB3/SAVE/POST/
LIB3/STD/COMMANDS/SHADOWS/
LIB3/STD/CREATOR/
LIB3/STD/DOM/
LIB3/STD/EFFECTS/
LIB3/STD/EFFECTS/HEALING/
LIB3/STD/EFFECTS/OTHER/
LIB3/STD/EFFECTS/POISONS/
LIB3/STD/ENVIRON/
LIB3/STD/GUILDS/
LIB3/STD/LIQUIDS/
LIB3/STD/ROOM/
LIB3/STD/TRIGGER/SHADOW/
LIB3/W/
LIB3/W/BANNOR/
LIB3/W/NEWSTYLE/
#pragma save_binary
#include <goal.h>

/* goals is an array stored internally as
   goal_ob1, arg1, goal_ob2, arg2 ...
 */


static mixed *goals;


/* actions maps a raised action to a series of action handlers as
   ([ "action1" : ({ pri3ob, pri2ob, pri1ob }), ... ])
 */


static mapping actions;


/* strategies is a list of the current strategies in operation stored as
   ({ strategy1_ob, goal1_ob, strategy2_ob, goal2_ob ... })
   where the goal obs are the goals which initiated the strategy
   (for determining priorities of strategies)
 */


static mixed *strategies;


void    create()
{
    goals = ({ });
    strategies = ({ });
    actions = ([ ]);
    call_out( "check_goals", 300 );
}				/* create() */


mixed * query_goals()
{
    return goals;
}
mixed * query_strategies()
{
    return strategies;
}
mapping query_actions()
{
    return actions;
}


void    set_goals( mixed *g )
{
    int     i;
    mixed   ob;

    goals = allocate( sizeof( g ) * 2 );
    for( i = 0; i < sizeof( g ); i++ )
    {
	ob = g[ i ];
	if( stringp( ob ) )
	{
	    ob = find_object( ob );
	    if( !objectp( ob ) )
	    {
		(void)g[ i ]->load_please();
		ob = find_object( g[ i ] );
	    }
	}
	if( !objectp( ob ) )
	    throw( "wibble wibble wibble" );
	goals[ i * 2 ] = g[ i ];
	goals[ i * 2 + 1 ] = ({ });
    }
}				/* set_goals() */


int     raise( string ev, mixed arg )
/* raise the event 'ev'.  Check all the goals (in descending priority order)
   to see if they are handling it */
{
    int     i, j;
    string  func_to_call;
    object  ob;
    mixed * result;

    func_to_call = "handle_" + ev;

    i = 0;

    while( i < sizeof( goals ) )
    {
	ob = goals[ i ];
	if( !ob )
	{			/* ooops bye bye goal */
	    goals = goals[ 0..i - 1 ] + goals[ i + 2..10000 ];
	    continue;
	}
	result = (mixed *)call_other( ob, func_to_call, this_object(),
				      arg, goals[ i + 1 ] );
	if( pointerp( result ) )
	{
	    for( j = 0; j < sizeof( result ); )
	    {
		switch( result[ j ] )
		{
		    case EVENT_HANDLED:
			return 1;	/* yayayay, someone dealt with it */
		    case EVENT_NOT_HANDLED:
		    case EVENT_PARTIALLY_HANDLED:	/* effectively the same */
		    case EVENT_USED:
			/* however, we may generate different return values from them */
			j = sizeof( result );
			break;	/* ok, well, maybe the next goal will do it */
		    case SET_GOAL_ARG:
			goals[ i + 1 ] = result[ j + 1 ];
			j += 2;
			break;
		    case GOAL_ACHIEVED:
			goals = goals[ 0..i - 1 ] + goals[ i + 2..10000 ];
			i -= 2;	/* ick ... so if they don't happen to have
				 * 'handled' */
			/* the event, we can pass it to the remaining goals */
			j++;
			break;
		}
	    }
	}
	i += 2;
    }
    return 0;
}				/* raise() */


/* action stuff ... */


void    add_action_handler( string action, mixed ob )
{
    mixed * old_handlers;

    if( !ob )
	ob = previous_object();
    old_handlers = actions[ action ];
    if( !pointerp( old_handlers ) )
	old_handlers = ({ });
    actions[ action ] = old_handlers + ({ ob });
}				/* add_action_handler() */


void    remove_action_handler( string action, mixed ob )
{
    int     i;
    mixed * handlers;

    if( !ob )
	ob = previous_object();
    if( objectp( ob ) )
	ob = file_name( ob );

    handlers = actions[ action ];
    if( !handlers )
	return;
    for( i = sizeof( handlers ) - 1; i >= 0; i-- )
    {
	if( (objectp( handlers[ i ] ) && ob == file_name( handlers[ i ] )) ||
		ob == handlers[ i ] )
	    handlers = handlers[ 0..i - 1 ] + handlers[ i + 1..10000 ];
    }
    actions[ action ] = handlers;
}				/* remove_action_handler() */



int     feasibility( string action, mixed arg )
/* determines the feasibility of
 * doing the action specified ... returns 1 if it appears currently
 * feasible, and 0 if not */
{
    mixed * handlers;
    int     i;
    string  func_to_call;

    handlers = actions[ action ];
    if( !handlers )
	return 0;

    func_to_call = "feasible_" + action;

    for( i = sizeof( handlers ) - 1; i >= 0; i-- )
	if( !handlers[ i ] )
	    handlers = handlers[ 0..i - 1 ] + handlers[ i + 1..10000 ];
	else
	    if( (int)call_other( handlers[ i ], func_to_call, this_object(),
				 arg ) )
	    {
		return 1;
	    }

    return 0;
}				/* feasibility() */


int     not_vetoed( mixed goal, string action, mixed arg )
/* checks higher priority goals and strategies of higher priority
   goals to see if any of them veto the action ... if none of them
   do not_vetoed returns 1 */
{
    int     i, j, k;

    if( objectp( goal ) )
	goal = file_name( goal );

    /* goals first */
    for( i = 0; i < sizeof( goals ); i += 2 )
    {
	if( !goals[ i ] )
	    continue;
	if( (objectp( goals[ i ] ) && file_name( goals[ i ] ) == goal) ||
		goals[ i ] == goal )
	    break;		/* got down to our goal, so all the rest are lower pri */
	if( (int)goals[ i ]->veto_action( action, arg ) )
	    return 0;
    }

    for( j = 0; j < sizeof( strategies ); j += 2 )
    {
	if( !strategies[ j ] )
	    continue;		/* hmmm */
	k = member_array( strategies[ j + 1 ], goals );
	if( k == -1 )
	    continue;		/* hmmm */
	if( k < i )		/* higher priority goal */
	    if( (int)strategies[ j ]->veto_action( action, arg ) )
		return 0;
    }

    return 1;
}				/* not_vetoed() */


void    cull_conflicting_strategies( string action, mixed arg )
/* culls any strategies which have a problem with the new (now accepted)
   strategy's attempts to use action 'action' */
{
    int     i;

    for( i = 0; i < sizeof( strategies ); i += 2 )
    {
	if( !strategies[ i ] )
	{
	    strategies = strategies[ 0..i - 1 ] + strategies[ i + 2..10000 ];
	    i -= 2;
	    continue;
	}
	if( (int)strategies[ i ]->conflict_action( action, arg ) )
	{
	    /* remove this strategy */
	    (void)strategies[ i ]->halt( this_object() );
	    strategies = strategies[ 0..i - 1 ] + strategies[ i + 2..10000 ];
	    i -= 2;
	}
    }
}				/* cull_conflicting_strategies() */


void    add_strategy( mixed strategy, mixed goal )
{
    strategies += ({ strategy, goal });
}				/* add_strategy() */


void    remove_strategy( mixed strategy )
{
    int     i;

    if( objectp( strategy ) )
	strategy = file_name( strategy );

    for( i = 0; i < sizeof( strategies ); i += 2 )
    {
	if( !strategies[ i ] )
	{
	    strategies = strategies[ 0..i - 1 ] + strategies[ i + 2..10000 ];
	    i -= 2;
	    continue;
	}

	if( (objectp( strategies[ i ] ) && file_name( strategies[ i ] ) ==
	     strategy) ||
		strategies[ i ] == strategy )
	{
	    (void)strategies[ i ]->remove( this_object() );
	    strategies = strategies[ 0..i - 1 ] + strategies[ i + 2..10000 ];
	    i -= 2;
	    continue;
	}
    }

    if( sizeof( strategies ) == 0 )
    {
	remove_call_out( "check_goals" );
	call_out( "check_goals", 1 );
    }
}				/* remove_strategy() */


void    check_goals()
{
    int     i;

    call_out( "check_goals", 300 );	/* at least every five minutes I think */

    if( sizeof( strategies ) > 0 )
	return;

    for( i = 0; i < sizeof( goals ); i += 2 )
    {
	if( !goals[ i ] )
	{
	    goals = goals[ 0..i - 1 ] + goals[ i + 2..10000 ];
	    i -= 2;
	    continue;
	}
	(void)goals[ i ]->reassess_strategies( this_object(), goals[ i + 1 ] );
	if( sizeof( strategies ) > 0 )
	    return;
    }
}				/* check_goals() */


int     action( string action, mixed arg )
/* does the action specified ... returns 1 if it seemed to be
   successful, but this is not utterly reliable I think */
{
    mixed * handlers;
    int     i;
    string  func_to_call;

    handlers = actions[ action ];
    if( !handlers )
	return 0;

    func_to_call = "action_" + action;

    for( i = sizeof( handlers ) - 1; i >= 0; i-- )
	if( !handlers[ i ] )
	    handlers = handlers[ 0..i - 1 ] + handlers[ i + 1..10000 ];
	else
	    if( (int)call_other( handlers[ i ], func_to_call, this_object(),
				 arg ) )
		return 1;

    return 0;
}				/* action() */