/
mudtem/
mudtem/area/scripts/
mudtem/bin/
mudtem/log/
mudtem/player/
mudtem/slang/autoconf/
mudtem/slang/doc/
mudtem/slang/doc/OLD/help/
mudtem/slang/doc/internal/
mudtem/slang/doc/text/
mudtem/slang/doc/tm/tools/
mudtem/slang/examples/
mudtem/slang/modules/
mudtem/slang/slsh/
mudtem/slang/slsh/lib/
mudtem/slang/slsh/scripts/
mudtem/slang/src/mkfiles/
mudtem/slang/src/util/
mudtem/src/CVS/
mudtem/src/include/
mudtem/src/include/CVS/
mudtem/src/var/CVS/
/* Rudimentary event code
 *
 * Copyright 1997 Oliver Jowett <oliver@sa-search.massey.ac.nz>. You can
 * freely use this code, so long as this notice remains intact.
 */

#include "include.h"
#include "events.h"
#include "tables.h"

int event_lookup_callback( ev_callback );

/* much tweakage will be needed here :)
 *
 * stuff not supplied here:
 *  new_event, free_event (just allocate/free the structure)
 *  typedef struct _event EVENT; (I have this in my merc.h so I don't have
 *   to pull in events.h there)
 *  a 'EVENT *events;' field in CHAR_DATA/ROOM_INDEX_DATA/OBJ_DATA
 *
 * in handler.c, extract_obj and extract_char need to remove any pending
 * events on that obj/char, ie:
 *
 * EVENT *ev, *ev_next;
 *
 * ...
 *
 * for (ev=ch->events; ev; ev=ev_next)
 * {
 *   ev_next=ev->nextitem;
 *   event_delete(ev);
 * }
 *
 * Also be sure that clone_object etc. set the events pointer of the new
 * object etc. to NULL.
 *
 */

EVENT *event_list[MAX_KEY_HASH];
int events_pending=0;
int events_total=0;
int events_hw=0;
long pulseclock=0;
int pulsebucket=0;

/* remove an event from an itemlist */
static void event_delete_itemlist(EVENT *ev)
{
  EVENT *search;
  EVENT **last;

  if (ev->start)
  {
    for (last=ev->start, search=*last;
	 search && search!=ev;
	 last=&search->nextitem, search=*last)
      ;
    
    if (!search)
    {
	int sn = event_lookup_callback(ev->callback);

	bugf("event_delete_itemlist: can't find event (%s) in itemlist for removal",
		sn != -1 ? event_table[sn].name : "???" );
    }
    else
      *last=ev->nextitem;
    
    ev->start=NULL;
  }
}

void event_update(void)
{
  EVENT *ev;

  pulseclock++; /* new stuff queued will queue in the future */

  while ((ev=event_list[pulsebucket])!=NULL &&
	 ev->when<pulseclock)
  {
    event_list[pulsebucket]=ev->next; /* remove from main list */
    ev->next=NULL;

    /* extract from the itemlist */
    event_delete_itemlist(ev);

    /* Event extracted, dispatch it */

    if (!ev->callback)
      bugf("event_update: null callback?!");
    else
      (*ev->callback)(ev);

    free_event(ev);
    events_pending--;
  }

  pulsebucket=(pulsebucket+1)%MAX_KEY_HASH;

  /* all done, move on to the next pulse */
}

void event_add(EVENT *ev)
{
  EVENT *search;
  EVENT **last;
  int bucket;

  if (!ev)
  {
    bugf("event_add: NULL");
    return;
  }

  /* ev should already be in the itemlist if necessary */

  if (ev->when<pulseclock)
  {
    bugf("event_add: timewarp!");
    event_delete_itemlist(ev);
    return;
  }

  bucket=ev->when % MAX_KEY_HASH;

  for (last=&event_list[bucket], search=*last;
       search && search->when<ev->when;
       last=&search->next, search=*last)
    ;

  ev->next=search;
  *last=ev;

  events_total++;
  events_pending++;
  if (events_pending > events_hw)
    events_hw=events_pending;
}

void event_delete(EVENT *ev)
{
  EVENT *search;
  EVENT **last;
  int bucket;

  if (!ev)
  {
    bugf("event_delete: NULL");
    return;
  }

  bucket=ev->when % MAX_KEY_HASH;

  for (last=&event_list[bucket], search=*last;
       search && search!=ev;
       last=&search->next, search=*last)
    ;

  if (!search)
    bugf("event_delete: event not in list");
  else
    *last=ev->next;

  event_delete_itemlist(ev);

  free_event(ev);
  events_pending--;
}

EVENT *event_pending(EVENT *list, ev_callback fn)
{
  EVENT *search;

  for (search=list;
       search;
       search=search->nextitem)
    if (search->callback==fn)
      return search;

  return NULL;
}

void char_event_add(CHAR_DATA *ch, int when, void *p, ev_callback fn)
{
  EVENT *ev;

  ev=new_event();
  ev->when=pulseclock+when;
  ev->item.ch=ch;
  ev->param=p;
  ev->callback=fn;

  /* hook into itemlist */
  ev->start=&ch->events;
  ev->nextitem=ch->events;
  ch->events=ev;

  event_add(ev);
}

void ent_event_add(Entity *ent, int when, void *p, ev_callback fn)
{
  EVENT *ev;

  ev		= new_event();
  ev->when	= pulseclock + when;
  ev->item.ent	= ent;
  ev->param	= p;
  ev->callback	= fn;
  ev->start	= NULL;

  event_add(ev);
}

void generic_event_add(int when, void *p, ev_callback fn)
{
  EVENT *ev;

  ev		= new_event();
  ev->when	= pulseclock+when;
  ev->param	= p;
  ev->callback	= fn;
  ev->item.ch	= NULL;
  ev->start	= NULL;

  event_add(ev);
}

void desc_event_add(DESCRIPTOR_DATA *desc, int when, void *p, ev_callback fn)
{
  EVENT *ev;

  ev=new_event();
  ev->when=pulseclock+when;
  ev->item.desc = desc;
  ev->param=p;
  ev->callback=fn;

  /* hook into itemlist */
  ev->start=&desc->events;
  ev->nextitem=desc->events;
  desc->events=ev;

  event_add(ev);
}

void obj_event_add(OBJ_DATA *obj, int when, void *p, ev_callback fn)
{
  EVENT *ev;

  ev=new_event();
  ev->when=pulseclock+when;
  ev->item.obj=obj;
  ev->param=p;
  ev->callback=fn;

  /* hook into itemlist */
  ev->start=&obj->events;
  ev->nextitem=obj->events;
  obj->events=ev;

  event_add(ev);
}

void room_event_add(ROOM_INDEX_DATA *room, int when, void *p, ev_callback fn)
{
  EVENT *ev;

  ev=new_event();
  ev->when=pulseclock+when;
  ev->item.room=room;
  ev->param=p;
  ev->callback=fn;

  /* hook into itemlist */
  ev->start=&room->events;
  ev->nextitem=room->events;
  room->events=ev;

  event_add(ev);
}

const char *event_stats(void)
{
  static char buf[300];

  sprintf(buf,
	  "Event statistics:\n\r"
	  "\n\r"
	  " Pending events:       %d\n\r"
	  " Max pending events:   %d\n\r"
	  " Total events queued:  %d\n\r"
	  " Pulseclock:           %ld\n\r",
	  events_pending,
	  events_hw,
	  events_total,
	  pulseclock);

  return buf;
}

int event_lookup_callback( ev_callback cb )
{
	int sn;

	for ( sn = 0; event_table[sn].name; sn++ )
		if ( event_table[sn].callback == cb )
			return sn;

	return -1;
}

void show_event_list( CHAR_DATA * ch, EVENT * pEv )
{
	EVENT * ev = pEv;
	int sn;

	for ( ; ev; ev = ev->nextitem )
	{
		sn = event_lookup_callback( ev->callback );

		printf_to_char( ch, "Evento %s, when %d\n\r",
			sn != -1 ? event_table[sn].name : "INDEF",
			ev->when );
	}
}

void event_get_obj( EVENT *ev )
{
	CHAR_DATA *ch = ev->item.ch;
	OBJ_DATA *obj;
	
	for ( obj = ch->in_room->contents; obj; obj = obj->next_content )
	{
		if ( obj->item_type == (int) ev->param && obj->level <= getNivelPr(ch) )
		{
			obj_from_room( obj );
			obj_to_char( obj, ch );
			act( "$n toma $p.", ch, objToEnt(obj), NULL, TO_ROOM );
			return;
		}
	}
}

void event_wield( EVENT *ev )
{
	CHAR_DATA *ch = ev->item.ch;
	OBJ_DATA *obj, *arma = NULL;
	int promedio = 0;

	for ( obj = ch->carrying; obj; obj = obj->next_content )
	{
		if ( (obj->wear_loc == WEAR_NONE) && (obj->item_type == ITEM_WEAPON) )
		{
			if ( PROMEDIO_OBJ(obj) > promedio )
			{
				promedio	= PROMEDIO_OBJ(obj);
				arma		= obj;
			}
		}
	}

	if ( arma && (PROMEDIO_CH(ch) < PROMEDIO_OBJ(arma)))
		wear_obj( ch, arma, FALSE );
}

void stc_event( EVENT *ev )
{
	CHAR_DATA *ch = ev->item.ch;
	char *msg = (char *) ev->param;

	send_to_char( msg, ch );
}

void ent_stc_event( EVENT *ev)
{
	Entity * ent = ev->item.ent;
	char *msg = (char *) ev->param;

	send_to_ent( msg, ent );
}

void to_room_act_event( EVENT *ev )
{
	CHAR_DATA *ch = ev->item.ch;
	char *msg = (char *) ev->param;
	
	act( msg, ch, NULL, NULL, TO_ROOM );
	return;
}

void room_update_event( EVENT *ev )
{
	ROOM_INDEX_DATA *room = ev->item.room;
	AFFECT_DATA *paf, *paf_next;
	CHAR_DATA *ch;

	/* go through affects and decrement */
	for ( paf = room->affected; paf != NULL; paf = paf_next )
	{
		paf_next	= paf->next;
		if ( paf->duration > 0 )
		{
			paf->duration--;
			if (number_range(0,4) == 0 && paf->level > 0)
				paf->level--;  /* spell strength fades with time */
		}
		else if ( paf->duration < 0 )
			;
		else
		{
			if ( paf_next == NULL
			||   paf_next->type != paf->type
			||   paf_next->duration > 0 )
				if ( paf->type > 0
				&& !IS_NULLSTR(skill_table[paf->type].msg_room)
				&& (ch = room->people) )
					act( skill_table[paf->type].msg_room, ch, 0, 0, TO_ALL );

			affect_remove_room( room, paf );
		}
	}

	if ( room->affected )
		room_event_add( room, PULSE_TICK, 0, room_update_event );
}

void autosave_event( EVENT *ev )
{
	CHAR_DATA *ch = ev->item.ch;

	if ( IS_NPC(ch) )
		return;

	if ( is_in_room(ch, ch->in_room) )
	{
		send_to_char( "#BG#brabando #BA#butomaticamente.\n\r", ch );
		save_char_obj( ch );
	}

	char_event_add( ch, MINUTOS(5)*PULSE_PER_SECOND, (void *) ch->id, autosave_event );
}

bool event_borrable( EVENT * ev )
{
	int pos;

	pos = event_lookup_callback( ev->callback );

	if ( pos != -1
	&&   event_table[pos].borrable == FALSE )
		return FALSE;

	return TRUE;
}

void actremove( EVENT *ev )
{
	CHAR_DATA *ch = ev->item.ch;
	int arg = (int) ev->param;
	
	REMOVE_BIT(ch->act, arg);
}

void actset( EVENT * ev )
{
	CHAR_DATA * ch = ev->item.ch;
	int arg = (int) ev->param;

	SET_BIT(ch->act, arg);
}

void obj_extract_event( EVENT * ev )
{
	OBJ_DATA * obj = ev->item.obj;

	extract_obj(obj, TRUE);
}