daleken/
daleken/data/notes/
daleken/data/player/
daleken/data/system/poses/
daleken/doc/Homepage/images/
daleken/log/
/*___________________________________________________________________________*
   )()(			  DalekenMUD 1.12 (C) 2000			)()(
   `]['		       by Martin Thomson, Lee Brooks,			`]['
    ||		       Ken Herbert and David Jacques			 ||
    || ----------------------------------------------------------------- ||
    || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan,	 ||
    || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse.		 ||
    || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael	 ||
    || Chastain, Michael Quan, and Mitchell Tse.			 ||
    || Original Diku Mud copyright (C) 1990, 1991			 ||
    || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt,	 ||
    || Tom Madsen, and Katja Nyboe.					 ||
    || ----------------------------------------------------------------- ||
    || Any use of this software must follow the licenses of the		 ||
    || creators.  Much time and thought has gone into this software and	 ||
    || you are benefitting. We hope that you share your changes too.	 ||
    || What goes around, comes around.					 ||
    || ----------------------------------------------------------------- ||
    ||				 event.c				 ||
    || Timed automatic event system code.				 ||
    || Please don't tinker with the event system unless you understand   ||
    || EVERYTHING there is to know about it there is NO protection in C! ||
 *_/<>\_________________________________________________________________/<>\_*/


#include "mud.h"
#include "event.h"


EVENT *event_hash[ MAX_EVENT_HASH ];
EVENT *event_global;
EVENT *event_free;
int top_event;


void event_add_global		args( ( EVENT *e ) );
EVENT *create_generic_event	args( ( EVENT **list, TARGET_TYPE *tgt,
					int type, int delay ) );
void obj_inject_events		args( ( OBJ_DATA *obj ) );
void show_events		args( ( BUFFER *buf, EVENT *e ) );

DECLARE_READ_FUN( read_event_data	);
DECLARE_READ_FUN( read_event_type	);


/*
 * Create a new event, or if possible recycle one.
 */
EVENT *new_event( void )
{
    EVENT *e;
    int i;

    if( event_free )
    {
	e = event_free;
	event_free = event_free->next_global;
    }
    else
    {
	e = (EVENT *)alloc_perm( sizeof( EVENT ) );
	top_event++;
    }

    e->actor.type = TARGET_VOID;
    e->actor.target.typeless = NULL;

    e->type = -1;
    e->text = &str_empty[0];
    for( i = 0; i < MAX_EVENT_DATA; ++i )
	e->data[i] = 0;
    e->extra.type = TARGET_VOID;
    e->extra.target.typeless = NULL;

    return e;
}


/*
 * Free an event, put it on the event_free list.
 */
void free_event( EVENT *e )
{
    if( e->text )
	free_string( e->text );
    e->next_global = event_free;
    event_free = e;
}


/*
 * Display an event.
 */
const char *print_event( EVENT *e )
{
    static char buf[256];
    int left = ( e->when - SysInfo->pulse ) / PULSE_PER_SECOND;

    sprintf( buf, "%-20.20s %3d:%02d",
	     event_table[e->type].name,
	     left / 60 , left % 60 );
    return buf;
}


/*
 * Add the event to the global list/hash.
 */
void event_add_global( EVENT *e )
{
    EVENT **ee;

    /* Someone tried to add an event that was to go off BEFORE now? */
    if( e->when < SysInfo->pulse )
    {
	log_string( "Adding event after time: %s\n", print_event( e ) );
	e->when = SysInfo->pulse + 1;
    }

    ee = &event_hash[(e->when) % MAX_EVENT_HASH];

    if( *ee == NULL || (*ee)->when > e->when )
    {
	e->next_global = *ee;
	*ee = e;
    }
    else
    {
	EVENT *prev;
	for( prev = *ee; prev->next_global
		 && prev->next_global->when <= e->when; prev = prev->next_global )
	    ;

	e->next_global = prev->next_global;
	prev->next_global = e;
    }
}


void event_remove_local( EVENT **list, EVENT *e )
{
    EVENT *prev;

    if( *list == e )
	*list = e->next_local;
    else
    {
	for( prev = *list; prev && prev->next_local != e;
	     prev = prev->next_local )
	    ;
	prev->next_local = e->next_local;
    }
}


void event_remove_global( EVENT *e )
{
    EVENT *prev;

    if( event_hash[e->when % MAX_EVENT_HASH] == e )
	event_hash[e->when % MAX_EVENT_HASH] = e->next_global;
    else
    {
	for( prev = event_hash[e->when % MAX_EVENT_HASH];
	     prev && prev->next_global != e;
	     prev = prev->next_global )
	    ;
	if( prev )
	    prev->next_global = e->next_global;
	else
	    log_string( "Event not on global list: %s", print_event( e ) );
    }
}


/*
 * Regular update of events.
 */
void event_update()
{
    EVENT *e;

    while( ( e = event_hash[ SysInfo->pulse % MAX_EVENT_HASH ] )
	   && e->when == SysInfo->pulse )
    {
	event_remove_global( e );
	switch( e->actor.type )
	{
	case TARGET_VOID:
	    event_remove_local( &event_global, e );
	    break;
	case TARGET_CHAR:
	    event_remove_local( &e->actor.target.ch->events, e );
	    break;
	case TARGET_OBJ:
	    event_remove_local( &e->actor.target.obj->events, e );
	    break;
	case TARGET_ROOM:
	    event_remove_local( &e->actor.target.room->events, e );
	    break;
	case TARGET_AREA:
	    event_remove_local( &e->actor.target.area->events, e );
	    break;
	case TARGET_PLANE:
	    event_remove_local( &e->actor.target.plane->events, e );
	    break;
	}
	if( e->type >= 0 && event_table[e->type].callback )
	    (*event_table[e->type].callback) ( e );
	free_event( e );
    }
}


/*
 * Activate the events on a character and his/her objects.
 */
void inject_events( CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    EVENT *e;

    for( e = ch->events; e; e = e->next_local )
    {
	e->actor.target.ch = ch;
	e->actor.type = TARGET_CHAR;

	e->when += SysInfo->pulse;
	event_add_global( e );
	REMOVE_BIT( e->flags, EVENT_LOADING );
    }

    for( obj= ch->carrying; obj; obj = obj->next_content )
	obj_inject_events( obj );
}


/*
 * Activate the events on an object (recursive).
 */
void obj_inject_events( OBJ_DATA *obj )
{
    EVENT *e;
    OBJ_DATA *obj_inside;

    for( e = obj->events; e; e = e->next_local )
    {
	e->actor.target.obj = obj;
	e->actor.type = TARGET_OBJ;

	e->when += SysInfo->pulse; /* Events are saved as offsets */
	event_add_global( e );
	REMOVE_BIT( e->flags, EVENT_LOADING );
    }

    for( obj_inside = obj->contains; obj_inside; obj_inside =
	     obj_inside->next_content )
	obj_inject_events( obj_inside );
}


void add_mob_triggers( CHAR_DATA *mob )
{
    EVENT *e;

    /* spec functions */
    if( mob->spec_fun != 0
	&& IS_SET( spec_table[mob->spec_fun].usage, SPEC_AUTONOMOUS ) )
	create_char_event( mob, evn_spec_fun,
			   number_range( PULSE_MOBILE / 2,
					 3 * PULSE_MOBILE / 2 ) );

    /* scavengers */
    if( xIS_SET( mob->act, ACT_SCAVENGER ) )
	create_char_event( mob, evn_scavenge,
			   number_range( PULSE_MOBILE / 2,
					 5 * PULSE_MOBILE ) );

    /* wanderers */
    if( !xIS_SET( mob->act, ACT_SENTINEL ) )
	create_char_event( mob, evn_wander,
			   number_range( PULSE_MOBILE / 2,
					 5 * PULSE_MOBILE ) );

    /* rand_progs */
    if( xIS_SET( mob->pIndexData->progtypes, RAND_PROG ) )
    {
	e = create_char_event( mob, evn_prog_trigger,
			       percent_fuzzy( PULSE_TICK, 5 ) );
	e->data[0] = RAND_PROG;
    }

    /* time_progs */
    if( xIS_SET( mob->pIndexData->progtypes, TIME_PROG ) )
    {
	e = create_char_event( mob, evn_prog_trigger,
			       pulse_point + 1 );
	e->data[0] = TIME_PROG;
    }
}


void add_mob_fight_triggers( CHAR_DATA *mob )
{
    EVENT *e;

    /* spec functions */
    if( mob->spec_fun != 0
	&& IS_SET( spec_table[mob->spec_fun].usage, SPEC_FIGHT ) )
	create_char_event( mob, evn_spec_fun,
			   number_range( PULSE_MOBILE / 2,
					 3 * PULSE_MOBILE / 2 ) );

    if( mob->class >= 0 )
	create_char_event( mob, evn_class_action,
			   percent_fuzzy( PULSE_VIOLENCE, 15 ) );
    if( xIS_SET( mob->pIndexData->progtypes, HITPRCNT_PROG ) )
    {
	e = create_char_event( mob, evn_prog_trigger,
			       number_fuzzy( PULSE_VIOLENCE ) );
	e->data[0] = HITPRCNT_PROG;
    }
    if( xIS_SET( mob->pIndexData->progtypes, FIGHT_PROG ) )
    {
	e = create_char_event( mob, evn_prog_trigger,
			       number_fuzzy( PULSE_VIOLENCE ) );
	e->data[0] = FIGHT_PROG;
    }
}


void add_obj_triggers( OBJ_DATA *obj )
{
    EVENT *e;

    if( xIS_SET( obj->pIndexData->progtypes, RAND_PROG ) )
    {
	e = create_obj_event( obj, evn_prog_trigger,
			      percent_fuzzy( PULSE_TICK, 5 ) );
	e->data[0] = RAND_PROG;
    }
    if( xIS_SET( obj->pIndexData->progtypes, TIME_PROG ) )
    {
	e = create_obj_event( obj, evn_prog_trigger,
			      pulse_point + 1 + number_bits( 2 ) );
	e->data[0] = TIME_PROG;
    }
}


void add_room_triggers( ROOM_INDEX_DATA *room )
{
    EVENT *e;

    if( xIS_SET( room->progtypes, RAND_PROG ) )
    {
	e = create_room_event( room, evn_prog_trigger,
			       percent_fuzzy( PULSE_TICK, 5 ) );
	e->data[0] = RAND_PROG;
    }
    if( xIS_SET( room->progtypes, TIME_PROG ) )
    {
	e = create_room_event( room, evn_prog_trigger,
			       pulse_point + 1 + number_bits( 2 ) );
	e->data[0] = TIME_PROG;
    }
}


/*
 * Add a generic event.
 */
EVENT *create_generic_event( EVENT **list, TARGET_TYPE *tgt, int type,
			     int delay )
{
    EVENT *e;
    struct event_table_type *tab = event_table + type;

    for( e = *list; e; e = e->next_local )
    {
	if( e->type == type )
	    break;
    }

    if( !e || IS_SET( tab->flags, EVENT_STACKABLE ) )
	e = new_event( );
    else
    {
	event_remove_global( e );
	event_remove_local( list, e );
    }

    e->type = type;
    e->when = SysInfo->pulse + delay;
    e->actor.type = tgt->type;
    switch( e->actor.type )
    {
    case TARGET_OBJ:
	e->actor.target.obj = tgt->target.obj;
	break;
    case TARGET_CHAR:
	e->actor.target.ch = tgt->target.ch;
	break;
    case TARGET_ROOM:
	e->actor.target.room = tgt->target.room;
	break;
    case TARGET_AREA:
	e->actor.target.area = tgt->target.area;
	break;
    case TARGET_PLANE:
	e->actor.target.plane = tgt->target.plane;
	break;
    default:
	e->actor.target.typeless = tgt->target.typeless;
	break;
    }

    e->next_local = *list;
    *list = e;
    event_add_global( e );

    return e;
}


/*
 * Create a copy of the event with a new delay.
 */
EVENT *duplicate_event( EVENT *e, int delay )
{
    EVENT **list = NULL;
    EVENT *enew;
    int i;

    switch( e->actor.type )
    {
    case TARGET_CHAR:
	list = &e->actor.target.ch->events;
	break;
    case TARGET_OBJ:
	list = &e->actor.target.obj->events;
	break;
    case TARGET_ROOM:
	list = &e->actor.target.room->events;
	break;
    case TARGET_AREA:
	list = &e->actor.target.area->events;
	break;
    case TARGET_PLANE:
	list = &e->actor.target.plane->events;
	break;
    }
    if( !list )
    {
	bug( "duplicate_event: wrong actor type." );
	return NULL;
    }

    enew = create_generic_event( list, &e->actor, e->type, delay );
    for( i = 0; i < MAX_EVENT_DATA; ++i )
	enew->data[i] = e->data[i];
    enew->text = str_dup( e->text );
    memcpy( &enew->extra, &e->extra, sizeof( enew->extra ) );

    return enew;
}


/*
 * Add an event to the character.
 */
EVENT *create_char_event( CHAR_DATA *ch, int type, int delay )
{
    TARGET_TYPE t;

    if( ch->deleted )
	return NULL;

    t.type = TARGET_CHAR;
    t.target.ch = ch;

    return create_generic_event( &ch->events, &t, type, delay );
}


/*
 * Add an event to the object.
 */
EVENT *create_obj_event( OBJ_DATA *obj, int type, int delay )
{
    TARGET_TYPE t;

    if( obj->deleted )
	return NULL;

    t.type = TARGET_OBJ;
    t.target.obj = obj;

    return create_generic_event( &obj->events, &t, type, delay );
}


/*
 * Add an event to the room.
 */
EVENT *create_room_event( ROOM_INDEX_DATA *room, int type, int delay )
{
    TARGET_TYPE t;

    t.type = TARGET_ROOM;
    t.target.room = room;

    return create_generic_event( &room->events, &t, type, delay );
}


/*
 * Add an event to the area.
 */
EVENT *create_area_event( AREA_DATA *area, int type, int delay )
{
    TARGET_TYPE t;

    t.type = TARGET_AREA;
    t.target.area = area;

    return create_generic_event( &area->events, &t, type, delay );
}


/*
 * Add an event to the plane.
 */
EVENT *create_plane_event( PLANE_DATA *plane, int type, int delay )
{
    TARGET_TYPE t;

    t.type = TARGET_PLANE;
    t.target.plane = plane;

    return create_generic_event( &plane->events, &t, type, delay );
}


/*
 * Insert an event for an unspecified thing.
 */
EVENT *create_typeless_event( void *vo, int type, int delay )
{
    TARGET_TYPE t;

    t.type = TARGET_VOID;
    t.target.typeless = vo;

    return create_generic_event( &event_global, &t, type, delay );
}


void strip_events( EVENT **list, int type )
{
    EVENT *e, *e_next;

    for( e = *list; e; e = e_next )
    {
	e_next = e->next_local;
	if( e->type == type )
	{
	    event_remove_local( list, e );
	    event_remove_global( e );
	    free_event( e );
	}
    }
}


/*
 * Make a timed object.
 */
void set_timer( OBJ_DATA *obj, int pulses )
{
    EVENT *e;

    if( pulses < 1 )
    {
	bug( "set_timer called with pulses=%d; [%5d] %s", pulses,
	     obj->pIndexData->vnum, obj->short_descr );
	pulses = 4;
    }

    if( obj->deleted )
	return;

    for( e = obj->events; e; e = e->next_local )
    {
	if( e->type == evn_obj_decay )
	    break;
    }

    if( e )
    {
	event_remove_global( e );
    }
    else
    {
	e = new_event();
	e->type = evn_obj_decay;
	e->actor.type = TARGET_OBJ;
	e->actor.target.obj = obj;
	e->next_local = obj->events;
	obj->events = e;
    }

    e->when = SysInfo->pulse + pulses;
    event_add_global( e );
}


void set_timer_tick( OBJ_DATA *obj, int ticks )
{
    set_timer( obj, percent_fuzzy( ticks * PULSE_TICK, 5 ) );
}


/*
 * Make a timed object (imp).
 */
void set_imp_timer( OBJ_DATA *obj )
{
    EVENT *e;

    if( obj->deleted )
	return;

    for( e = obj->events; e; e = e->next_local )
    {
	if( e->type == evn_imp_grab )
	    break;
    }

    if( e )
    {
	event_remove_global( e );
    }
    else
    {
	e = new_event();
	e->type = evn_imp_grab;
	e->actor.type = TARGET_OBJ;
	e->actor.target.obj = obj;
	e->next_local = obj->events;
	obj->events = e;
    }

    e->when = SysInfo->pulse + number_range( 300 * PULSE_PER_SECOND,
					     400 * PULSE_PER_SECOND );
    event_add_global( e );
}


int get_time_left( EVENT *e, int type )
{
    for( ; e ; e = e->next_local )
	if( e->type == type )
	    return e->when - SysInfo->pulse;

    return -1;
}


void show_events( BUFFER *buf, EVENT *e )
{
    struct event_table_type *tab;
    int tmp;

    if( !e )
    {
	buffer_strcat( buf, "&gNo events here.&n\n\r" );
	return;
    }

    for( ; e; e = e->next_local )
    {
	tab = event_table + e->type;

	bprintf( buf, "&w{ &gEvent Type: &b[&c%s&b]&n\n\r", tab->name );
	tmp = ( e->when - SysInfo->pulse ) / PULSE_PER_SECOND;
	bprintf( buf,   "  &gTime:       &b[&c%3d&b:&c%02d&b]&n\n\r",
	     tmp / 60, tmp % 60 );
	tmp = e->flags ^ tab->flags;
	bprintf( buf, "  &gFlags:      &b[&c%s&b]&n\n\r",
		 flag_string( event_extra_flags, &tmp ) );

	buffer_strcat( buf, "  &gData:       &b[&c" );
	for( tmp = 0; tmp < MAX_EVENT_DATA; tmp++ )
	    bprintf( buf, "%d, ", e->data[tmp] );
	buf->len -= 2;
	buf->data[buf->len] = '\0';
	buffer_strcat( buf, "&b]&n\n\r" );

	if( e->text && e->text[0] )
	    bprintf( buf, "  &gText:\n\r    &w%s&n&w }&n\n\r", e->text );
	else
	    buffer_strcat( buf, "&w}&n\n\r" );
    }
}


void do_mestat( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *rch;
    CHAR_DATA *victim;
    BUFFER *buf;

    rch = get_char( ch );

    if( !authorized( rch, "mestat" ) )
	return;

    if( argument[0] == '\0' )
    {
	send_to_char( "Mob event stat whom?\n\r", ch );
	return;
    }

    if( !( victim = get_char_world( ch, argument ) ) )
    {
	send_to_char( "They aren't here.\n\r", ch );
	return;
    }

    buf = buffer_new( MAX_STRING_LENGTH );
    bprintf( buf, "&wEvent stat for character &g%s&n &b[&r%5d&b]&n\n\r",
	     PERS( victim, ch ),
	     victim->pIndexData ? victim->pIndexData->vnum : 0 );
    show_events( buf, victim->events );
    send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}


void do_oestat( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *rch;
    OBJ_DATA *obj;
    BUFFER *buf;

    rch = get_char( ch );

    if( !authorized( rch, "oestat" ) )
	return;

    if( argument[0] == '\0' )
    {
	send_to_char( "Object event stat what?\n\r", ch );
	return;
    }

    if( !( obj = get_obj_world( ch, argument ) ) )
    {
	send_to_char( "You can't find it.\n\r", ch );
	return;
    }

    buf = buffer_new( MAX_STRING_LENGTH );
    bprintf( buf, "&wEvent stat for object &g%s&n &b[&r%5d&b]&n\n\r",
	     obj->short_descr, obj->pIndexData->vnum );
    show_events( buf, obj->events );
    send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}


void do_restat( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *rch;
    ROOM_INDEX_DATA *room;
    BUFFER *buf;

    rch = get_char( ch );

    if( !authorized( rch, "restat" ) )
	return;

    if( argument[0] == '\0' )
	room = ch->in_room;
    else if( !( room = find_location( ch, argument ) ) )
    {
	send_to_char( "You can't find that location.\n\r", ch );
	return;
    }

    buf = buffer_new( MAX_STRING_LENGTH );
    bprintf( buf, "&wEvent stat for room &y%s&n &b[&r%5d&b]&n\n\r",
	     room->name, room->vnum );
    show_events( buf, room->events );
    send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}


void do_aestat( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *rch;
    AREA_DATA *area;
    BUFFER *buf;

    rch = get_char( ch );

    if( !authorized( rch, "aestat" ) )
	return;

    if( argument[0] == '\0' )
	area = ch->in_room->area;
    else if( !is_number( argument ) )
    {
	send_to_char( "Usage: aestat [area#]\n\r", ch );
	return;
    }
    else if( !( area = get_area_data( atoi( argument ) ) ) )
    {
	send_to_char( "That area doesn't exist.\n\r", ch );
	return;
    }

    buf = buffer_new( MAX_STRING_LENGTH );
    bprintf( buf, "&wEvent stat for area %s&n&w.&n\n\r",
	     area->name );
    show_events( buf, area->events );
    send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}


void do_pestat( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *rch;
    PLANE_DATA *pl;
    BUFFER *buf;

    rch = get_char( ch );

    if( !authorized( rch, "pestat" ) )
	return;

    if( argument[0] == '\0' )
	pl = ch->in_room->area->plane;
    else if( !( pl = plane_lookup( argument ) ) )
    {
	send_to_char( "That plane doesn't exist.\n\r", ch );
	return;
    }

    buf = buffer_new( MAX_STRING_LENGTH );
    bprintf( buf, "&wEvent stat for plane %s&n&w.&n\n\r",
	     pl->name );
    show_events( buf, pl->events );
    send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}