/* $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;
}
*/