/*___________________________________________________________________________* )()( 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; }