/*___________________________________________________________________________*
)()( 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. ||
|| ----------------------------------------------------------------- ||
|| callback.c ||
|| Callback functions for events. ||
*_/<>\_________________________________________________________________/<>\_*/
#include "mud.h"
#include "event.h"
/* Externals */
void show_char_to_char args( ( CHAR_DATA *list, CHAR_DATA *ch ) );
void show_list_to_char args( ( OBJ_DATA *list, CHAR_DATA *ch, bool fShort,
bool fShowNothing ) );
DECLARE_DO_FUN( mp_cast );
int evn_picklock;
int evn_picktrap;
int evn_mercenary;
int evn_gate_demon;
int evn_raised_undead;
int evn_swan_song;
int evn_char_fall;
int evn_char_sink;
int evn_char_drown;
int evn_char_suffocate;
int evn_web_struggle;
int evn_stand_up;
int evn_regeneration;
int evn_plant_grow;
int evn_plant_die;
int evn_explode;
int evn_obj_decay;
int evn_imp_grab;
int evn_obj_fall;
int evn_spec_fun;
int evn_class_action;
int evn_hunt_fleer;
int evn_scavenge;
int evn_wander;
int evn_cone_remove;
int evn_room_violence;
int evn_prog_trigger;
int evn_prog_wait;
int evn_area_reset;
struct event_table_type event_table [] = {
/* Player actions */
{ "pick lock", &evn_picklock, ecb_finish_pick,
EVENT_TEMPORARY },
{ "pick trap", &evn_picktrap, ecb_pick_trap,
EVENT_TEMPORARY },
{ "mercenary", &evn_mercenary, ecb_mercenary,
0 },
{ "gate demon", &evn_gate_demon, ecb_gate_demon,
0 },
{ "raised undead", &evn_raised_undead, ecb_raised_undead,
0 },
{ "swan song", &evn_swan_song, ecb_swan_song,
EVENT_DEATH_REMOVE },
{ "char fall", &evn_char_fall, ecb_char_fall,
EVENT_DEATH_REMOVE },
{ "char sink", &evn_char_sink, ecb_char_sink,
EVENT_DEATH_REMOVE },
{ "char drown", &evn_char_drown, ecb_char_drown,
EVENT_DEATH_REMOVE },
{ "char suffocate", &evn_char_suffocate, ecb_char_suffocate,
EVENT_DEATH_REMOVE },
{ "web struggle", &evn_web_struggle, ecb_web_struggle,
EVENT_DEATH_REMOVE },
{ "stand up", &evn_stand_up, ecb_stand_up,
EVENT_DEATH_REMOVE | EVENT_TEMPORARY },
{ "regeneration", &evn_regeneration, ecb_regeneration,
EVENT_DEATH_REMOVE | EVENT_STACKABLE },
/* Object actions */
{ "plant die", &evn_plant_grow, ecb_plant_grow,
EVENT_NO_QUIT },
{ "plant die", &evn_plant_die, ecb_plant_die,
EVENT_NO_QUIT },
{ "explode", &evn_explode, ecb_explode,
EVENT_TEMPORARY },
/* Update replacements */
{ "object decay", &evn_obj_decay, ecb_obj_decay,
0 },
{ "imp grab", &evn_imp_grab, ecb_imp_grab,
EVENT_TEMPORARY },
{ "object fall", &evn_obj_fall, ecb_obj_fall,
EVENT_TEMPORARY },
{ "special function", &evn_spec_fun, ecb_spec_fun,
EVENT_DEATH_REMOVE | EVENT_TEMPORARY },
{ "class action", &evn_class_action, ecb_class_action,
EVENT_DEATH_REMOVE | EVENT_TEMPORARY },
{ "hunt fleer", &evn_hunt_fleer, ecb_hunt_fleer,
EVENT_TEMPORARY | EVENT_DEATH_REMOVE },
{ "scavenge", &evn_scavenge, ecb_scavenge,
EVENT_TEMPORARY | EVENT_DEATH_REMOVE },
{ "wander", &evn_wander, ecb_wander,
EVENT_TEMPORARY | EVENT_DEATH_REMOVE },
{ "remove cone of silence", &evn_cone_remove, ecb_cone_remove,
0 },
{ "room violence", &evn_room_violence, ecb_room_violence,
0 },
/* Mud prog callback */
{ "mud program", &evn_prog_trigger, ecb_prog_trigger,
EVENT_TEMPORARY|EVENT_STACKABLE|EVENT_DEATH_REMOVE },
{ "mud prog wait", &evn_prog_wait, ecb_prog_wait,
EVENT_TEMPORARY|EVENT_STACKABLE|EVENT_DEATH_REMOVE },
/* Area reset */
{ "area reset", &evn_area_reset, ecb_area_reset,
EVENT_TEMPORARY },
{ "", NULL, NULL, 0 }
};
/*
* Finds an event by name.
*/
int event_lookup( const char *name )
{
int i;
for( i = 0; event_table[i].type >= 0; ++i )
{
if( !str_cmp( event_table[i].name, name ) )
return *event_table[i].type;
}
return -1;
}
struct event_table_type *event_table_lookup( const char *name )
{
int i;
for( i = 0; event_table[i].type >= 0; ++i )
{
if( !str_cmp( event_table[i].name, name ) )
return (event_table + i);
}
return NULL;
}
void ecb_finish_pick( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
EXIT_DATA *pexit;
EXIT_DATA *pexit_rev;
OBJ_DATA *obj;
ROOM_INDEX_DATA *to_room;
/* Check skill roll for player-char, make sure mob isn't charmed */
if( ( !IS_NPC( ch ) && !get_success( ch, gsn_pick_lock, 100 ) )
|| ( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) ) )
{
send_to_char( "You failed.\n\r", ch );
return;
}
if( e->data[0] == 0 ) /* pick a door */
{
if( e->data[1] < 0 || e->data[1] >= MAX_DIR
|| !( pexit = ch->in_room->exit[e->data[1]] ) )
{
bug( "%s picking invalid door %d.", ch->name, e->data[1] );
return;
}
if( pexit->key < 0 )
{
act( "$d can't be picked.", ch, NULL, pexit->keyword,
TO_CHAR );
return;
}
if( IS_SET( pexit->exit_info, EX_PICKPROOF )
|| ( IS_SET( pexit->exit_info, EX_HARDPICK )
&& number_bits( 2 ) ) )
{
act( "You failed to pick the $d.", ch, NULL, pexit->keyword,
TO_CHAR );
return;
}
REMOVE_BIT( pexit->exit_info, EX_LOCKED );
act( "&y*Click* The $d's lock springs open.", ch, NULL,
pexit->keyword, TO_CHAR );
act( "&y$n picks the $d.&n", ch, NULL, pexit->keyword, TO_ROOM );
/* pick the other side */
if( ( to_room = pexit->to_room )
&& ( pexit_rev = to_room->exit[rev_dir[e->data[1]]] )
&& pexit_rev->to_room == ch->in_room )
{
REMOVE_BIT( pexit_rev->exit_info, EX_LOCKED );
}
}
else /* picking a container */
{
if( !( obj = e->extra.target.obj ) || obj->deleted )
{
bug( "%s picking invalid object.", ch->name );
return;
}
if( obj->value[2] < 0 )
{
act( "$p can't be unlocked.", ch, obj, NULL, TO_CHAR );
return;
}
if( IS_SET( obj->value[1], CONT_PICKPROOF )
|| ( IS_SET( obj->value[1], CONT_HARDPICK )
&& number_bits( 2 ) ) )
{
act( "You failed to pick $p.", ch, obj, NULL, TO_CHAR );
return;
}
REMOVE_BIT( obj->value[1], CONT_LOCKED );
act( "&y*Click* $p's lock springs open.", ch, obj,
NULL, TO_CHAR );
act( "$n picks $p.", ch, obj, NULL, TO_ROOM );
}
return;
}
/*
* Called in between starting a pick and the finish event.
*/
void ecb_pick_trap( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
int trap, dam;
act( "&rYou set off a trap!", ch, NULL, NULL, TO_CHAR );
trap = number_range( gsn_first_trap, gsn_last_trap );
dam = percent_fuzzy( 4 + ch->level / 2 + ch->hit / 10, 10 );
if( number_percent( ) < power( 8, 7, get_curr_dex( ch ) - 16 ) )
{
act( "&rA $t shoots out and barely misses $n!&n", ch,
skill_table[trap].noun_damage, NULL, TO_ROOM );
act( "&rA $t shoots out and barely misses you!&n", ch,
skill_table[trap].noun_damage, NULL, TO_CHAR );
}
else
{
act( "&rA $t shoots out and strikes $n!&n", ch,
skill_table[trap].noun_damage, NULL, TO_ROOM );
act( "&rA $t shoots out and strikes you!&n", ch,
skill_table[trap].noun_damage, NULL, TO_CHAR );
damage( ch, ch, dam, trap, WEAR_NONE );
}
return;
}
/*
* A mercenary finishing his hire.
*/
void ecb_mercenary( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
ROOM_INDEX_DATA *pRoom;
do_say( ch, "I have completed my work, now I must leave." );
xREMOVE_BIT( ch->affected_by, AFF_CHARM );
do_follow( ch, "self" );
ch->master = NULL;
if( !( pRoom = get_room_index( e->data[0] ) ) )
{
raw_kill( ch, ch );
return;
}
act( "$n disappears!", ch, NULL, NULL, TO_ROOM );
char_from_room( ch );
char_to_room( ch, pRoom );
}
/*
* A gate demon returning to the underworld.
*/
void ecb_gate_demon( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
act( "$n screams a heart rending cry and is sucked back into the void.",
ch, NULL, NULL, TO_ROOM );
extract_char( ch, TRUE );
}
/*
* An undead creature that was raised up.
*/
void ecb_raised_undead( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
act( "$n suddenly slows, then slumps to the ground lifeless.",
ch, NULL, NULL, TO_ROOM );
raw_kill( ch, ch );
}
/*
* A player that refused to die (swan song skill).
*/
void ecb_swan_song( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
xREMOVE_BIT( ch->affected_by, AFF_DEAD );
if( number_bits( 6 ) == 0 )
{
do_shout( ch, "Hey, I'm not dead yet!" );
ch->hit = UMAX( number_range( 1, 20 ), ch->hit );
}
else
{
act( "&R$n suffer$% a MASSIVE haemorrage "
"and $e crumple$%, stone cold dead.",
ch, NULL, NULL, TO_ALL );
raw_kill( ch, ch );
}
}
/*
* Player/mob is falling, this continues the downward spiral...
* The character should already be ready to fall, but we have to check.
*/
void ecb_char_fall( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
EXIT_DATA *pexit;
ROOM_INDEX_DATA *to_room;
/* check if falling is still possible */
if( xIS_SET( ch->affected_by, AFF_FLYING )
|| IS_SET( ch->body_parts, BODY_PART_WINGS )
|| !( pexit = ch->in_room->exit[DIR_DOWN] )
|| ( IS_SET( pexit->exit_info, EX_CLOSED )
&& ( ( !IS_SET( race_table[ch->race].race_abilities, RACE_PASSDOOR )
&& !IS_AFFECTED( ch, AFF_PASS_DOOR ) )
|| IS_SET( pexit->exit_info, EX_PASSPROOF ) ) )
|| !( to_room = pexit->to_room ) )
return;
send_to_char( "&rYou fall!&n ARRRRGGGGHHHH.........\n\r", ch );
act( "$n falls.", ch, NULL, NULL, TO_ROOM );
stop_fighting( ch, TRUE );
char_from_room( ch );
char_to_room( ch, to_room );
act( "$n has just dropped in.", ch, NULL, NULL, TO_ROOM );
if( ch->desc )
{
charprintf( ch, "&y%s %s&n\n\r", ch->in_room->name,
sector_string[ch->in_room->sector_type] );
show_list_to_char( ch->in_room->contents, ch, FALSE, FALSE );
show_char_to_char( ch->in_room->people, ch );
}
char_fall_check( ch, e->data[0] );
}
/*
* Player/mob is sinking, this continues the downward spiral...
* The character should already be ready to fall.
*/
void ecb_char_sink( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
OBJ_DATA *boat;
EXIT_DATA *pexit;
ROOM_INDEX_DATA *toroom;
if( ch->in_room->sector_type != SECT_WATER_SWIM
&& ch->in_room->sector_type != SECT_WATER_NOSWIM
&& ch->in_room->sector_type != SECT_UNDERWATER
&& !IS_SET( ch->in_room->room_flags, ROOM_FLOODED ) )
return;
for( boat = ch->carrying; boat; boat = boat->next_content )
if( !boat->deleted && boat->item_type == ITEM_BOAT )
break;
if( !( pexit = ch->in_room->exit[boat ? DIR_UP : DIR_DOWN] )
|| ( IS_SET( pexit->exit_info, EX_CLOSED )
&& ( ( !IS_SET( race_table[ch->race].race_abilities, RACE_PASSDOOR )
&& !IS_AFFECTED( ch, AFF_PASS_DOOR ) )
|| IS_SET( pexit->exit_info, EX_PASSPROOF ) ) )
|| !( toroom = ch->in_room->exit[boat ? DIR_UP : DIR_DOWN]->to_room ) )
return;
if( toroom->sector_type != SECT_WATER_SWIM
&& toroom->sector_type != SECT_WATER_NOSWIM
&& toroom->sector_type != SECT_UNDERWATER
&& !IS_SET( toroom->room_flags, ROOM_FLOODED ) )
return;
if( boat )
{
act( "&bYour boat forces you to float higher in the water.",
ch, NULL, NULL, TO_CHAR );
act( "&b$n floats up, bouyed by $s boat.", ch, NULL, NULL, TO_ROOM );
}
else
act( "&b$n slowly sink$% in the water.", ch, NULL, NULL, TO_ALL );
char_from_room( ch );
char_to_room( ch, toroom );
if( boat )
act( "&b$n has bobbed up from below.", ch, NULL, NULL, TO_ROOM );
else
act( "&b$n has drifted in from above.", ch, NULL, NULL, TO_ROOM );
do_look( ch, AUTOLOOK );
return;
}
/*
* Player/mob is underwater and they can't breathe...
* The character should already be ready to fall.
*/
void ecb_char_drown( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
if( IS_AFFECTED( ch, AFF_BREATHING )
|| !ch->in_room
|| ch->in_room->sector_type == SECT_WATER_SWIM
|| ( ( ( ch->in_room->sector_type != SECT_UNDERWATER
&& !IS_SET( ch->in_room->room_flags, ROOM_FLOODED ) )
|| IS_SET( ch->body_parts, BODY_PART_GILLS ) )
&& ( ch->in_room->sector_type == SECT_UNDERWATER
|| IS_SET( ch->body_parts, BODY_PART_LUNGS ) ) ) )
return;
send_to_char( "&RYou are drowning!&n\n\r", ch );
act( "$n sputters and chokes!", ch, NULL, NULL, TO_ROOM );
if( e->data[0] == 5 )
ch->position = POS_STUNNED;
else if( e->data[0] == 10 )
{
act( "$n is DEAD!", ch, NULL, NULL, TO_ROOM );
if( !IS_NPC( ch ) )
{
SysInfo->deaths++;
sprintf( log_buf, "%s drowned at %s",
ch->name, ch->in_room->name );
talk_channel( NULL, log_buf, CHANNEL_INFO, "INFO" );
log_string( "%s (%d)", log_buf, ch->in_room->vnum );
wiznetf( ch, WIZ_DEATHS, get_trust( ch ), "%s (%d)",
log_buf, ch->in_room->vnum );
ch->hit = 1;
}
raw_kill( ch, ch );
return;
}
damage( ch, ch, ch->level + e->data[0], gsn_breathing, WEAR_NONE );
e->data[0]++;
duplicate_event( e, number_fuzzy( 4 * PULSE_PER_SECOND ) );
return;
}
/*
* A player who is stranded in space without the means to breathe.
*/
void ecb_char_suffocate( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
if( IS_AFFECTED( ch, AFF_BREATHING )
|| ch->in_room->sector_type != SECT_SPACE )
return;
send_to_char( "&RYou can't breathe!&n\n\r", ch );
act( "$n can't breathe, $e is turning red!", ch, NULL, NULL, TO_ROOM );
if( e->data[0] == 2 )
ch->position = POS_STUNNED;
else if( e->data[0] == 5 )
{
act( "$n is DEAD!", ch, NULL, NULL, TO_ROOM );
if( !IS_NPC( ch ) )
{
SysInfo->deaths++;
sprintf( log_buf, "%s died due to lack of air at %s",
ch->name, ch->in_room->name );
talk_channel( NULL, log_buf, CHANNEL_INFO, "INFO" );
log_string( "%s (%d)", log_buf, ch->in_room->vnum );
wiznetf( ch, WIZ_DEATHS, get_trust( ch ), "%s (%d)",
log_buf, ch->in_room->vnum );
ch->hit = 1;
}
raw_kill( ch, ch );
return;
}
damage( ch, ch, ch->level * ( e->data[0] / 3 + 1 ),
gsn_breathing, WEAR_NONE );
e->data[0]++;
duplicate_event( e, number_fuzzy( 3 * PULSE_PER_SECOND ) );
return;
}
/*
* An entangled character struggling in a web.
*/
void ecb_web_struggle( EVENT *e )
{
web_update( e->actor.target.ch );
}
/*
* For all those mobs you throw, trip and smash.
*/
void ecb_stand_up( EVENT *e )
{
do_stand( e->actor.target.ch, "" );
}
/*
* Healing effect for the regeneration spell.
*/
void ecb_regeneration( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
int heal;
heal = e->data[1] * 3 / 100;
if( ch->hit < ch->max_hit / 5 )
heal *= 2;
heal = UMIN( heal, e->data[0] );
if( ch->hit >= ch->max_hit )
heal = 0;
else
{
heal = UMIN( heal, ch->max_hit - ch->hit );
ch->hit += heal;
update_pos( ch );
}
e->data[0] -= heal + e->data[1] / 250 + 1;
if( e->data[0] > 0 )
duplicate_event( e, PULSE_PER_SECOND );
else
send_to_char( "You stop regenerating.\n\r", ch );
}
/*
* For all those mobs who would like to cast spells and use their skills in
* combat.
*/
void ecb_class_action( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
CHAR_DATA *victim;
int sk, i = 0;
char buf[MAX_INPUT_LENGTH];
char buf2[MAX_INPUT_LENGTH];
if( ch->class < 0 || !( victim = ch->fighting ) )
return;
/* Pick a skill */
do
{
i++;
sk = number_range( 1, MAX_SKILL - 1 );
}
while( i < MAX_SKILL / 4
&& ( ch->level < skill_table[sk].skill_level[ch->class]
|| !IS_SET( skill_table[sk].skill_type,
SKILL_TYPE_ACTION ) ) );
if( i < MAX_SKILL / 4 )
{
if( skill_table[sk].spell_fun == spell_null )
{
one_argument( skill_table[sk].name, buf );
if( !str_suffix( " combo", skill_table[sk].name ) )
{
sprintf( buf2, "combo %s", buf );
interpret( ch, buf2 );
}
else
interpret( ch, buf );
}
else
{
sprintf( buf, "'%s'", skill_table[sk].name );
if( !str_cmp( "dispel magic", skill_table[sk].name ) )
{
sprintf( buf, "'%s' '%s'", skill_table[sk].name, victim->name );
}
mp_cast( ch, buf );
}
}
if( !ch->deleted && ch->fighting )
create_char_event( ch, evn_class_action,
( i < MAX_SKILL / 4 )
? skill_table[sk].beats : PULSE_VIOLENCE );
return;
}
/*
* Plant specific code, when they finally mature.
*/
void ecb_plant_grow( EVENT *e )
{
OBJ_DATA *obj = e->actor.target.obj;
CHAR_DATA *rch;
obj->item_type = ITEM_PLANT;
if( obj->carried_by )
{
act( "$p is now fully grown.", obj->carried_by, obj, NULL, TO_CHAR );
}
else if( obj->in_room && ( rch = obj->in_room->people ) )
{
act( "$p is now fully grown.", rch, obj, NULL, TO_ALL );
}
}
/*
* Not all plants make it to maturity.
*/
void ecb_plant_die( EVENT *e )
{
OBJ_DATA *obj = e->actor.target.obj;
CHAR_DATA *rch;
if( obj->carried_by )
{
act( "$p slowly withers and dies.", obj->carried_by, obj, NULL, TO_CHAR );
}
else if( obj->in_room && ( rch = obj->in_room->people ) )
{
act( "$p slowly withers and dies.", rch, obj, NULL, TO_ALL );
}
}
/*
* Explosion event.
*/
void ecb_explode( EVENT *e )
{
OBJ_DATA *obj = e->actor.target.obj;
if( obj->carried_by )
act( "&r$p violently explodes!", obj->carried_by, obj, NULL, TO_CHAR );
else if( obj->in_room && obj->in_room->people )
act( "&r$p violently explodes!", obj->in_room->people,
obj, NULL, TO_ALL );
explode( obj );
}
/*
* Object decay.
* Added a container effect where items fall out and decay as well.
*/
void ecb_obj_decay( EVENT *e )
{
OBJ_DATA *obj = e->actor.target.obj;
const char *message;
OBJ_DATA *obj_content, *obj_next;
OBJ_DATA *cont_obj = NULL;
CHAR_DATA *cont_ch = NULL;
ROOM_INDEX_DATA *cont_room = NULL;
int delay = 25;
switch( obj->item_type )
{
default:
message = "$p vanishes.";
break;
case ITEM_PLANT:
message = "$p slowly withers and dies.";
break;
case ITEM_FOUNTAIN:
message = "$p dries up.";
break;
case ITEM_CORPSE_NPC:
message = "$p decays into dust.";
delay = 3;
break;
case ITEM_CORPSE_PC:
message = "$p is taken by a beam of light from heaven.";
break;
case ITEM_FOOD:
message = "$p decomposes.";
break;
case ITEM_EXPLOSIVE:
message = "$p fizzles and sparks weakly.";
break;
case ITEM_PORTAL:
message = "$p shimmers and fades back into nothingness.";
break;
}
if( obj->in_room )
{
if( ( cont_ch = obj->in_room->people ) )
act( message, cont_ch, obj, NULL, TO_ALL );
cont_room = obj->in_room;
obj_from_room( obj );
}
else if( obj->carried_by )
{
act( message, obj->carried_by, obj, NULL, TO_CHAR );
cont_ch = obj->carried_by;
obj_from_char( obj );
}
else if( obj->in_obj )
{
cont_obj = obj->in_obj;
obj_from_obj( obj );
}
for( obj_content = obj->contains; obj_content; obj_content = obj_next )
{
obj_next = obj_content->next_content;
if( obj_content->deleted )
continue;
obj_from_obj( obj_content );
if( cont_room )
obj_to_room( obj_content, cont_room );
else if( cont_ch )
obj_to_char( obj_content, cont_ch );
else if( cont_obj )
obj_to_obj( obj_content, cont_obj );
else
{
extract_obj( obj_content );
continue;
}
/* rely on the imp grab to get rid of money */
if( obj->item_type != ITEM_MONEY )
set_timer_tick( obj_content, percent_fuzzy( delay, 20 ) );
}
extract_obj( obj );
return;
}
void ecb_imp_grab( EVENT *e )
{
OBJ_DATA *obj = e->actor.target.obj;
CHAR_DATA *rch;
if( ( rch = obj->in_room->people ) )
act( "&yA small imp runs in and grabs $p.",
rch, obj, NULL, TO_ALL );
extract_obj( obj );
}
void ecb_obj_fall( EVENT *e )
{
OBJ_DATA *obj = e->actor.target.obj;
CHAR_DATA *rch;
ROOM_INDEX_DATA *new_room;
EXIT_DATA *pexit = obj->in_room->exit[DIR_DOWN];
/* check that falling is still possible*/
if( !pexit || !( new_room = pexit->to_room )
|| IS_SET( pexit->exit_info, EX_CLOSED ) )
return;
if( ( rch = obj->in_room->people ) != NULL )
act( "$p falls away.", rch, obj, NULL, TO_ALL );
obj_from_room( obj );
obj_to_room( obj, new_room );
if( ( rch = obj->in_room->people ) != NULL )
act( "$p drops from the sky.", rch, obj, NULL, TO_ALL );
if( !obj->in_room ) /* really now, not necessary :p */
return;
/* check that the fall continues */
obj_fall_check( obj, e->data[0] );
}
/*
* Mobile spec procedures.
*/
void ecb_spec_fun( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
if( !ch->in_room
|| IS_AFFECTED( ch, AFF_CHARM ) )
return;
if( IS_SET( spec_table[ch->spec_fun].usage, SPEC_FIGHT )
&& !ch->fighting )
return;
(*spec_table[ch->spec_fun].spec_fun)
( ch, NULL, spec_table[ch->spec_fun].usage, NULL );
if( !ch->deleted )
create_char_event( ch, evn_spec_fun,
number_range( PULSE_MOBILE / 2,
3 * PULSE_MOBILE / 2 ) );
}
/*
* A mob will follow a fleeing PC.
*/
void ecb_hunt_fleer( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
if( IS_AFFECTED( ch, AFF_CHARM )
|| !ch->tracking || ch->position < POS_STANDING )
return;
if( mob_track_update( ch ) )
create_char_event( ch, evn_hunt_fleer,
number_range( PULSE_MOBILE / 2,
3 * PULSE_MOBILE / 2 ) );
}
/*
* Pick up that rubbish, used to be in mobile_update.
*/
void ecb_scavenge( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
OBJ_DATA *obj;
OBJ_DATA *obj_best = NULL;
int max = 1;
if( IS_AFFECTED( ch, AFF_CHARM )
|| ch->position < POS_STANDING )
return;
if( ch->in_room->contents )
{
for( obj = ch->in_room->contents; obj; obj = obj->next_content )
{
if( CAN_WEAR( obj, ITEM_TAKE )
&& obj->cost > max && can_see_obj( ch, obj ) )
{
obj_best = obj;
max = obj->cost;
}
}
if( obj_best )
{
obj_from_room( obj_best );
obj_to_char( obj_best, ch );
act( "$n gets $p.", ch, obj_best, NULL, TO_ROOM );
}
}
create_char_event( ch, evn_scavenge,
number_range( PULSE_MOBILE / 2,
5 * PULSE_MOBILE ) );
}
/*
* The aimless wandering of mobiles.
* This event callback is the most called event, accounting for over half the
* events generated, very performance sensitive.
*/
void ecb_wander( EVENT *e )
{
CHAR_DATA *ch = e->actor.target.ch;
EXIT_DATA *pexit;
CHAR_DATA *rch;
char buf[MAX_INPUT_LENGTH];
int rnum;
int door;
if( IS_AFFECTED( ch, AFF_CHARM )
|| ch->position < POS_STANDING )
return;
if( ch->hit < ch->max_hit / 2 )
rnum = 3;
else
rnum = 5;
if( ( door = number_bits( rnum ) ) <= 5
&& ( pexit = ch->in_room->exit[door] )
&& pexit->to_room
&& !IS_SET( pexit->exit_info, EX_CLOSED )
&& !IS_SET( pexit->to_room->room_flags, ROOM_NO_MOB )
&& ( !xIS_SET( ch->act, ACT_STAY_AREA )
|| pexit->to_room->area == ch->in_room->area ) )
{
/* Give message if hurt */
if( rnum == 3 )
act( "$n flees in terror!", ch, NULL, NULL, TO_ROOM );
stop_fighting_room( ch, TRUE );
move_char( ch, door );
if( ch->deleted )
return;
for( rch = ch->in_room->people;
rch && ch->position >= POS_STANDING
&& rnum == 3;
rch = rch->next_in_room )
{
int direction;
if( rch->deleted || IS_NPC( rch ) )
continue;
if( ch->fearing == rch )
{
switch( number_bits( 2 ) )
{
default:
sprintf( buf, "May I have some peace %s?", rch->name );
break;
case 0:
sprintf( buf, "Get away from me, %s!", rch->name );
break;
case 1:
sprintf( buf, "Leave me be, %s!", rch->name );
break;
case 2:
sprintf( buf, "%s is trying to kill me! Help!", rch->name );
break;
case 3:
sprintf( buf, "Someone save me from %s!", rch->name );
break;
}
do_yell( ch, buf );
}
act( "$n flee$% in terror!", ch, NULL, NULL, TO_ALL );
/* Find an exit giving each one an equal chance */
door = -1;
for( direction = 0; direction <= 5; direction++ )
{
if( ch->in_room->exit[direction]
&& number_range( 0, direction ) == 0 )
door = direction;
}
/* If no exit, attack. Else flee! */
if( door == -1
|| ( IS_AFFECTED( rch, AFF_TAUNT )
&& number_bits( 2 ) == 0 ) )
{
act( "$n screams and attacks $N.",
ch, NULL, rch, TO_NOTVICT );
act( "$n screams and attacks you.",
ch, NULL, rch, TO_VICT );
multi_hit( ch, rch, TYPE_UNDEFINED );
}
else
{
stop_fighting( ch, TRUE );
move_char( ch, door );
}
break;
} /* for( rch ... ) */
}
create_char_event( ch, evn_wander,
number_range( PULSE_MOBILE / 2,
rnum * PULSE_MOBILE ) );
}
/*
* Remove the temporary cone of silence on a room.
*/
void ecb_cone_remove( EVENT *e )
{
ROOM_INDEX_DATA *room = e->actor.target.room;
REMOVE_BIT( room->room_flags, ROOM_TEMP_CONE_OF_SILENCE );
}
/*
* Room violence update.
*/
void ecb_room_violence( EVENT *e )
{
room_violence_update( e->actor.target.room );
}
/*
* Triggers for rand_progs and time_progs can be entered here.
* NOTE: Time progs are called one pulse after the hour, this is
* enabled by making pulse_point global.
*/
void ecb_prog_trigger( EVENT *e )
{
CHAR_DATA *ch;
OBJ_DATA *obj;
ROOM_INDEX_DATA *room;
EVENT *enew = NULL;
switch( e->actor.type )
{
case TARGET_CHAR:
ch = e->actor.target.ch;
/* It's alright to break here on these cases, however be careful
* that you don't break on temporary effects, the event must be
* created for next time. -- Symposium
*/
if( !ch || ch->deleted || !IS_NPC( ch ) )
break;
switch( e->data[0] )
{
case RAND_PROG:
if( ch->in_room->area->nplayer > 0
&& !IS_AFFECTED( ch, AFF_CHARM ) )
mprog_percent_check( ch, NULL, NULL, NULL, RAND_PROG );
enew = create_char_event( e->actor.target.ch, evn_prog_trigger,
percent_fuzzy( PULSE_TICK, 10 ) );
break;
case TIME_PROG:
if( ch->in_room->area->nplayer > 0
&& !IS_AFFECTED( ch, AFF_CHARM ) )
mprog_time_trigger( ch );
enew = create_char_event( e->actor.target.ch, evn_prog_trigger,
pulse_point + 1 + number_bits( 2 ) );
break;
case HITPRCNT_PROG:
mprog_hitprcnt_trigger( ch );
if( !ch->deleted && ch->fighting )
enew = create_char_event( e->actor.target.ch, evn_prog_trigger,
number_fuzzy( PULSE_VIOLENCE ) );
break;
case FIGHT_PROG:
if( ch->fighting )
mprog_percent_check( ch, ch->fighting,
NULL, NULL, FIGHT_PROG );
if( !ch->deleted && ch->fighting )
enew = create_char_event( e->actor.target.ch, evn_prog_trigger,
number_fuzzy( PULSE_VIOLENCE ) );
break;
case DELAY_PROG:
mprog_percent_check( ch, NULL, NULL, NULL, DELAY_PROG );
break;
default:
bug( "Bad trigger for mob in event: %d.", e->data[0] );
break;
}
break;
case TARGET_OBJ:
obj = e->actor.target.obj;
if( !obj || obj->deleted )
break;
switch( e->data[0] )
{
case RAND_PROG:
if( obj->carried_by
|| ( obj->in_room && obj->in_room->area->nplayer > 0 ) )
oprog_percent_check( NULL, obj, NULL, RAND_PROG );
enew = create_obj_event( e->actor.target.obj, evn_prog_trigger,
percent_fuzzy( PULSE_TICK, 10 ) );
break;
case TIME_PROG:
if( obj->carried_by
|| ( obj->in_room && obj->in_room->area->nplayer > 0 ) )
oprog_time_trigger( obj );
enew = create_obj_event( e->actor.target.obj, evn_prog_trigger,
pulse_point + 1 + number_bits( 2 ) );
break;
default:
bug( "Bad trigger for object in event: %d.", e->data[0] );
break;
}
break;
case TARGET_ROOM:
room = e->actor.target.room;
if( !room )
break;
for( ch = room->people; ch; ch = ch->next_in_room )
if( !ch->deleted && !IS_NPC( ch ) )
break;
switch( e->data[0] )
{
case RAND_PROG:
if( ch )
rprog_percent_check( room, ch, NULL, NULL, RAND_PROG );
enew = create_room_event( e->actor.target.room, evn_prog_trigger,
percent_fuzzy( PULSE_TICK, 10 ) );
break;
case TIME_PROG:
if( ch )
rprog_time_trigger( room, ch );
enew = create_room_event( e->actor.target.room, evn_prog_trigger,
pulse_point + 1 + number_bits( 2 ) );
break;
default:
bug( "Bad trigger for room in event: %d.", e->data[0] );
break;
}
break;
}
if( enew )
enew->data[0] = e->data[0];
}
void ecb_prog_wait( EVENT *e )
{
MPROG_CALLBACK *cb = (MPROG_CALLBACK *)e->extra.target.typeless;
switch( e->actor.type )
{
case TARGET_CHAR:
break;
case TARGET_OBJ:
cb->info.mob = oset_supermob( e->actor.target.obj );
break;
case TARGET_ROOM:
cb->info.mob = rset_supermob( e->actor.target.room );
break;
default:
delete_all_locals( &cb->info );
bug( "ecb_prog_wait: bad target type" );
return;
}
parse( cb->commands, &cb->info, cb->stack, cb->line_no );
switch( e->actor.type )
{
case TARGET_CHAR:
break;
case TARGET_OBJ:
case TARGET_ROOM:
release_supermob();
break;
}
/* parse takes care of the stack objects, but we have to deal
* with the info struct */
delete_all_locals( &cb->info );
}
void ecb_area_reset( EVENT *e )
{
AREA_DATA *area = e->actor.target.area;
DESCRIPTOR_DATA *d;
CHAR_DATA *pch;
int time;
if( area->nplayer > 0 )
{
for( d = descriptor_list; d; d = d->next )
{
pch = CH( d );
if( !xIS_SET( pch->act, PLR_BUSY )
&& IS_AWAKE( pch ) && pch->in_room
&& pch->in_room->area == area )
{
if( area->repop && area->repop[0] != '\0' )
send_to_char( area->repop, pch );
else
send_to_char( "You hear the patter of little feet.\n\r",
pch );
}
}
}
reset_area( area );
time = area->age;
if( area->nplayer <= 0 )
time = UMIN( 4, time );
time = time * PULSE_PER_SECOND * 60;
time = number_range( time * 2 / 3, time * 4 / 3 );
create_area_event( area, evn_area_reset, time );
}