#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() */