/* $Id: event.c,v 1.666 2004/09/20 10:49:48 shrike Exp $ */ /************************************************************************************ * Contains information on the event system structures etc... * ************************************************************************************/ /************************************************************************************ * Copyright 2004 Astrum Metaphora consortium * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * * * ************************************************************************************/ /************************************************************************************ * Original code by DalekenMUD 1.12 (C) 2000 * * Adapted for current codebase by Shrike aka Sauron at 2004 * ***********************************************************************************/ #include <sys/types.h> #if !defined (WIN32) # include <sys/socket.h> # include <netinet/in.h> # include <arpa/telnet.h> # include <arpa/inet.h> # include <unistd.h> # include <netdb.h> # include <sys/wait.h> # include <sys/time.h> #else # include <winsock.h> # include <sys/timeb.h> #endif #include <ctype.h> #include <errno.h> #include <fcntl.h> #include <locale.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "merc.h" #include "db/db.h" extern void *alloc_perm (int sMem); extern void log_printf (const char *str, ...); extern int pulse_point; 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, ENV_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 = ENV_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 = ENV_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 - current_time ) / 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 **ev; if( e->when < current_time ) { log_printf("Adding event after time: %s\n", print_event(e)); e->when = current_time + 1; } ev = &event_hash[(e->when) % MAX_EVENT_HASH]; if( *ev == NULL || (*ev)->when > e->when ) { e->next_global = *ev; *ev = e; } else { EVENT *prev; for( prev = *ev; 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_printf("Event not on global list: %s", print_event(e)); } } /* * Regular update of events. */ void event_update() { EVENT *e; while(( e = event_hash[ current_time % MAX_EVENT_HASH ]) && e->when == current_time ) { event_remove_global( e ); switch( e->actor.type ) { case ENV_TARGET_VOID: event_remove_local( &event_global, e ); break; case ENV_TARGET_CHAR: event_remove_local( &e->actor.target.ch->events, e ); break; case ENV_TARGET_OBJ: event_remove_local( &e->actor.target.obj->events, e ); break; case ENV_TARGET_ROOM: event_remove_local( &e->actor.target.room->events, e ); break; case ENV_TARGET_AREA: event_remove_local( &e->actor.target.area->events, e ); break; /*case ENV_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 = ENV_TARGET_CHAR; // e->when += current_time; 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 = ENV_TARGET_OBJ; e->when += current_time; 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; 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 ) ); if( EXT_IS_SET( mob->act, ACT_SCAVENGER ) ) create_char_event( mob, evn_scavenge, number_range( PULSE_MOBILE / 2, 5 * PULSE_MOBILE ) ); if( !EXT_IS_SET( mob->act, ACT_SENTINEL ) ) create_char_event( mob, evn_wander, number_range( PULSE_MOBILE / 2, 5 * PULSE_MOBILE ) ); if( EXT_IS_SET( mob->pIndexData->progtypes, RAND_PROG ) ) { e = create_char_event( mob, evn_prog_trigger, percent_fuzzy( PULSE_TICK, 5 ) ); e->data[0] = RAND_PROG; } if( EXT_IS_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; 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( EXT_IS_SET( mob->pIndexData->progtypes, HITPRCNT_PROG ) ) { e = create_char_event( mob, evn_prog_trigger, number_fuzzy( PULSE_VIOLENCE ) ); e->data[0] = HITPRCNT_PROG; } if( EXT_IS_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( EXT_IS_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( EXT_IS_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( EXT_IS_SET( room->progtypes, RAND_PROG ) ) { e = create_room_event( room, evn_prog_trigger,percent_fuzzy( PULSE_TICK, 5 ) ); e->data[0] = RAND_PROG; } if( EXT_IS_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, ENV_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 = current_time + delay; e->actor.type = tgt->type; switch( e->actor.type ) { case ENV_TARGET_OBJ: e->actor.target.obj = tgt->target.obj; break; case ENV_TARGET_CHAR: e->actor.target.ch = tgt->target.ch; break; case ENV_TARGET_ROOM: e->actor.target.room = tgt->target.room; break; case ENV_TARGET_AREA: e->actor.target.area = tgt->target.area; break; case ENV_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 ENV_TARGET_CHAR: list = &e->actor.target.ch->events; break; case ENV_TARGET_OBJ: list = &e->actor.target.obj->events; break; case ENV_TARGET_ROOM: list = &e->actor.target.room->events; break; case ENV_TARGET_AREA: list = &e->actor.target.area->events; break; case ENV_TARGET_PLANE: list = &e->actor.target.plane->events; break; } if( !list ) { log_printf( "***** [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 ) { ENV_TARGET_TYPE t; // if( ch->deleted ) if (ch == NULL) return NULL; t.type = ENV_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 ) { ENV_TARGET_TYPE t; if( obj->deleted ) return NULL; t.type = ENV_TARGET_OBJ; t.target.obj = obj; return create_generic_event( &obj->events, &t, type, delay ); } EVENT *create_room_event( ROOM_INDEX_DATA *room, int type, int delay ) { ENV_TARGET_TYPE t; t.type = ENV_TARGET_ROOM; t.target.room = room; return create_generic_event( &room->events, &t, type, delay ); } EVENT *create_area_event( AREA_DATA *area, int type, int delay ) { ENV_TARGET_TYPE t; t.type = ENV_TARGET_AREA; t.target.area = area; return create_generic_event( &area->events, &t, type, delay ); } EVENT *create_plane_event( PLANE_DATA *plane, int type, int delay ) { ENV_TARGET_TYPE t; t.type = ENV_TARGET_PLANE; t.target.plane = plane; return create_generic_event( &plane->events, &t, type, delay ); } EVENT *create_typeless_event( void *vo, int type, int delay ) { ENV_TARGET_TYPE t; t.type = ENV_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 ); } } } void set_timer( OBJ_DATA *obj, int pulses ) { EVENT *e; if( pulses < 1 ) { log_printf( "***** [BUG] event.c set_timer: 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 = ENV_TARGET_OBJ; e->actor.target.obj = obj; e->next_local = obj->events; obj->events = e; } e->when = current_time + pulses; event_add_global( e ); } void set_timer_tick( OBJ_DATA *obj, int ticks ) { set_timer( obj, percent_fuzzy( ticks * PULSE_TICK, 5 ) ); } 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 = ENV_TARGET_OBJ; e->actor.target.obj = obj; e->next_local = obj->events; obj->events = e; } e->when = current_time + 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 - current_time; 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" ); buf_printf( buf, "{gNo events here.{x\n\r" ); return; } for( ; e; e = e->next_local ) { tab = event_table + e->type; buf_printf( buf, "{w{ {gEvent Type: {D[{c%s{D] {x", tab->name ); tmp = ( e->when - current_time ) / PULSE_PER_SECOND; buf_printf( buf, " {gTime: {D[{c%3d{D:{c%02d{D]{x r", tmp / 60, tmp % 60 ); tmp = e->flags ^ tab->flags; buf_printf( buf, " {YFlags: {D[{c%s{D]{x ", flag_string(event_extra_flags, tmp)); buf_printf( buf, " {YData: {D[{c" ); for( tmp = 0; tmp < MAX_EVENT_DATA; tmp++ ) buf_printf( buf, "%d, ", e->data[tmp] ); buf_printf( buf, "{D]{x\n\r" ); if( e->text && e->text[0] ) buf_printf( buf, " {YText:\n\r {W%s{W }{x\n\r", e->text ); else //buffer_strcat( buf, "&w}&n\n\r" ); buf_printf( buf, "{W}{x\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 = buf_new(-1); buf_printf( buf, "{WEvent stat for character {Y%s {D[{R%5d{D]{x\n\r", PERS( victim, ch ), victim->pIndexData ? victim->pIndexData->vnum : 0 ); show_events( buf, victim->events ); page_to_char(buf_string(buf), ch ); buf_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 = buf_new(-1); buf_printf( buf, "{WEvent stat for object {x%s {D[{R%5d{D]{x\n\r", mlstr_val(obj->short_descr, ch->lang), obj->pIndexData->vnum ); show_events( buf, obj->events ); send_to_char(buf_string(buf), ch ); buf_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 = buf_new(-1); buf_printf( buf, "{WEvent stat for room {x%s {D[{R%5d{D]{x\n\r", mlstr_val(room->name, ch->lang), room->vnum ); show_events( buf, room->events ); send_to_char(buf_string(buf), ch ); buf_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 = area_lookup(atoi(argument )))) { send_to_char( "That area doesn't exist.\n\r", ch ); return; } buf = buf_new(-1); buf_printf( buf, "{WEvent stat for area {x%s{x\n\r", area->name ); show_events( buf, area->events ); send_to_char(buf_string(buf), ch ); buf_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 = buf_new(-1); buf_printf( buf, "{WEvent stat for plane {x%{x.\n\r", pl->name ); show_events( buf, pl->events ); send_to_char( buf->data, ch ); buffer_free( buf ); return; } */