muddy/
muddy/CVS/
muddy/area/
muddy/area/CVS/
muddy/clans/CVS/
muddy/classes/CVS/
muddy/doc/
muddy/doc/CVS/
muddy/etc/CVS/
muddy/etc/i3/
muddy/etc/i3/CVS/
muddy/imc/CVS/
muddy/lang/CVS/
muddy/licenses/CVS/
muddy/msgdb/CVS/
muddy/new/CVS/
muddy/notes/
muddy/player/
muddy/races/CVS/
muddy/religions/CVS/
muddy/src/CVS/
muddy/src/comm/CVS/
muddy/src/db/CVS/
muddy/src/intermud/
muddy/src/intermud/CVS/
muddy/src/irc/CVS/
muddy/src/olc/CVS/
/* $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;
}
*/