/* C-style #if defined(macintosh) #include <types.h> #else #include <sys/types.h> #include <sys/time.h> #endif #include <ctype.h> #include <errno.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <stdarg.h> */ #include <cstdarg> #include <iostream> #include <vector> using namespace std; #include "merc.h" #include "recycle.h" #include "interp.h" #include "skill.h" //Extern void append_note args((NOTE_DATA *pnote)); /*Event prototypes*/ /*Print event*/ void fire_print_event args((EVENT * ev)); void destroy_print_event args((EVENT * ev)); /*Wait event*/ void fire_wait_event args((EVENT * ev)); void destroy_wait_event args((EVENT * ev)); /*Save event*/ void fire_save_event args((EVENT * ev)); void destroy_save_event args((EVENT * ev)); /*Poll event*/ void fire_poll_event args((EVENT * ev)); void destroy_poll_event args((EVENT * ev)); /*Prog (delayed) event*/ void fire_prog_event args((EVENT * ev)); void destroy_prog_event args((EVENT * ev)); /*Prog cooldown event*/ void fire_prog_cooldown_event args((EVENT * ev)); void destroy_prog_cooldown_event args((EVENT * ev)); /*Affhandle event*/ void fire_affhandle_event args((EVENT * ev)); void destroy_affhandle_event args((EVENT * ev)); /*Menu event*/ void fire_menu_event args((EVENT * ev)); void destroy_menu_event args((EVENT * ev)); /* ******************************** PRINT EVENT ******************************** * A print event is a delayed send_to_char. */ void print_event(CHAR_DATA *ch, int delay, char * fmt, ...) { char buf[2*MSL]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); EVENT *ev = new_event("Print event"); ev->fire = &fire_print_event; ev->destroy = &destroy_print_event; ev->type = EVENT_PRINT; ev->event_args.print.ch = ch; ev->event_args.print.message = str_dup(buf); ev->delay = delay; ev->next = events; events = ev; return; } void fire_print_event(EVENT * ev) { send_to_char(ev->event_args.print.message, ev->event_args.print.ch); } void destroy_print_event(EVENT * ev) { free_string(ev->event_args.print.message); //free_event(ev); -- done in update.c now } /* ******************************** /PRINT EVENT/ ******************************** */ /* ******************************** WAIT EVENT ******************************** * A wait event 'lags' the character. */ void wait_event(CHAR_DATA *ch, int delay, sh_int wait) { EVENT *ev = new_event("Wait event"); ev->fire = &fire_wait_event; ev->destroy = &destroy_wait_event; ev->type = EVENT_WAIT; ev->event_args.wait.ch = ch; ev->event_args.wait.seconds = wait; ev->delay = delay; ev->next = events; events = ev; return; } void fire_wait_event(EVENT * ev) { WAIT_STATE(ev->event_args.wait.ch, ev->event_args.wait.seconds*PULSE_PER_SECOND); } void destroy_wait_event(EVENT * ev) { //free_event(ev); -- done in update.c now } /* ******************************** /WAIT EVENT/ ******************************** */ /* ******************************** SAVE EVENT ******************************** Mud-wide 'asave changed' and player saver. Fun! This will never end, once called. */ void save_event(time_t delay) { EVENT *ev = new_event("Global, 15 min. automated save event"); ev->fire = &fire_save_event; ev->destroy = &destroy_save_event; ev->type = EVENT_SAVE; ev->delay = delay; ev->event_args.save.active = TRUE; ev->next = events; events = ev; return; } void fire_save_event(EVENT * ev) { DESCRIPTOR_DATA *d; do_asave (NULL, "changed"); /*autosave changed areas */ for (d = descriptor_list; d != NULL; d = d->next) { if (d->character != NULL && d->connected == CON_PLAYING) save_char_obj(d->character); } } void destroy_save_event(EVENT * ev) { save_event(900); /*Initiate a new instance of the save event*/ writelogf ("Successfully autosaved."); //free_event(ev); -- done in update.c now } /* ******************************** /SAVE EVENT/ ******************************** */ /* ******************************** POLL EVENT ******************************** * A poll event creates a global poll that players can vote on. When it *completes, a note will be posted to 'all' with the vote results. A single *player cannot vote twice. See do_vote for more info on voting. *This event is essentially a wrapper for a menu. */ void poll_event(MENU_DATA *menu, int delay) { EVENT *ev = new_event("Poll event"); ev->fire = &fire_poll_event; ev->destroy = &destroy_poll_event; ev->type = EVENT_POLL; ev->event_args.poll.menu = menu; ev->event_args.poll.voted = NULL; //can't hurt ev->delay = delay; ev->next = events; events = ev; mud.ispoll = TRUE; return; } void fire_poll_event(EVENT * ev) { } void destroy_poll_event(EVENT * ev) { NOTE_DATA *pnote; OPTION_DATA *option; MENU_DATA *menu; char *strtime; char buf[MSL]; BUFFER *b; b = new_buf(); strtime = ctime (¤t_time); strtime[strlen (strtime) - 1] = '\0'; menu = ev->event_args.poll.menu; pnote = new_note(); pnote->next = NULL; pnote->sender = str_dup ("[Skull]"); pnote->type = NOTE_NOTE; pnote->date = str_dup (strtime); pnote->to_list = str_dup ("all"); sprintf(buf, "The poll which asked {C%s{y has finished.{x", menu->text); pnote->subject = str_dup (buf); add_buf(b, "Here are the results of this poll:\n\r"); sprintf(buf, "{GVotes {CSelection{x\n\r"); add_buf(b, buf); for (option = menu->option; option; option = option->next) { sprintf(buf, "{G%5d{x {C%s{x\n\r", option->argi, option->args[0]); add_buf(b, buf); } pnote->text = str_dup (buf_string(b)); pnote->date_stamp = current_time; append_note(pnote); pnote = NULL; wiznet("A poll has finished.", NULL, NULL, 0, 0, 0); free_menu(ev->event_args.poll.menu); free_buf(b); //free_event(ev); -- done in update.c now mud.ispoll = FALSE; } /* ******************************** /POLL EVENT/ ******************************** */ /* ******************************** DELAYED PROG EVENT ******************************** * A prog event delays execution of a mob/obj/room program. "vpr" is a void * pointer to the mob/obj/room that the program is on. 'Type' specifies what * vpr should be cast as. When this event expires, the program will continue * where it left off. (ev->event_args.prog.line = line;) */ void prog_event (CHAR_DATA *ch, void *vpr, int type, PROG_CODE *prog_code, int line, int delay) { EVENT *ev = new_event("Prog delay event"); ev->type = EVENT_PROG; ev->fire = &fire_prog_event; ev->destroy = &destroy_prog_event; switch (type) { case MOB: ev->event_args.prog.mob = (CHAR_DATA *)vpr; break; case OBJ: ev->event_args.prog.obj = (OBJ_DATA *)vpr; break; default: ev->event_args.prog.room = (ROOM_INDEX_DATA *)vpr; break; } ev->event_args.prog.ch = ch; //The 'doer' of the program //Type: Mob prog, obj prog, or room prog? (Which is it attached to. See above.) ev->event_args.prog.type = type; ev->event_args.prog.prog_code = prog_code; ev->event_args.prog.line = line; ev->delay = delay; ev->next = events; events = ev; } void fire_prog_event(EVENT * ev) { extern void program_flow( sh_int, char *, CHAR_DATA *, OBJ_DATA *, ROOM_INDEX_DATA *, CHAR_DATA *, const void *, const void *, int ); //writelogf("timer.c:281: fire_prog_event\n\r"); /*Evaluate if the ch is still valid to receive the rest of the program. *Conditions: * - The character must be in the room of the rprog if a room prog. * - The character must be in the room of the object if a object prog, * or be carrying the object. -- CHANGED 2/5/2004 6:03PM: * The object can be in another object (host); checks for that. * * - The character must be in the room of the mob if a mob prog. * * - Also, all three will be checked to make sure they exist yet. * If a condition isn't met, program_flow won't be called and the * event will self-terminate (destroy_prog_event). */ switch (ev->event_args.prog.type) { case MOB: if (ev->event_args.prog.mob && ev->event_args.prog.ch && ev->event_args.prog.mob->in_room == ev->event_args.prog.ch->in_room && IS_VALID(ev->event_args.prog.mob)) { program_flow(ev->event_args.prog.prog_code->vnum, ev->event_args.prog.prog_code->code, ev->event_args.prog.mob, NULL, NULL, ev->event_args.prog.ch, NULL, NULL, ev->event_args.prog.line); } break; case OBJ: if (!ev->event_args.prog.obj || !ev->event_args.prog.ch) return; //Additional check because of crashyness if (!IS_VALID(ev->event_args.prog.obj)) return; OBJ_DATA *host; if ((host = find_container_or_obj(ev->event_args.prog.obj)) == NULL) return; /*Below if-check: *Object exist? Char exist? Obj in a room? If so, rooms match? *If not, is it being carried by someone? If so, rooms match? *If not, 0 -- false condition. */ //JH: for now, objprograms will execute until they finish. //That means a player doesn't need to be in the same room as the obj. //If this proves problematic in the future..well.. I don't know. :) /*if (host->in_room && host->in_room != ev->event_args.prog.ch->in_room) return; if (host->carried_by && host->carried_by->in_room != ev->event_args.prog.ch->in_room) return; */ //writelogf("timer.c:281: 332: calling program_flow\n\r"); program_flow(ev->event_args.prog.prog_code->vnum, ev->event_args.prog.prog_code->code, NULL, ev->event_args.prog.obj, NULL, ev->event_args.prog.ch, NULL, NULL, ev->event_args.prog.line); break; default: //Removed the following check because of the possibility of room at. Grumble. // JH 3/2/2004 6:57PM //if (ev->event_args.prog.ch && ev->event_args.prog.room && // ev->event_args.prog.ch->in_room == ev->event_args.prog.room) program_flow(ev->event_args.prog.prog_code->vnum, ev->event_args.prog.prog_code->code, NULL, NULL, ev->event_args.prog.room, ev->event_args.prog.ch, NULL, NULL, ev->event_args.prog.line); break; }//end switch } void destroy_prog_event(EVENT * ev) { //ch_printf(ev->event_args.prog.ch, "\n\rtimer.c:253: destroy_prog_event\n\r"); //free_event(ev); -- done in update.c now } /* ******************************** /DELAYED PROG EVENT/ ******************************** */ /* ******************************** COOLDOWN PROG EVENT ******************************** * A cooldown prog event prevents a program from being immediately called again. * An example is, say, if there is a catapult in the game and you don't want * your players firing 4,000 shots a second. A cooldown line can be written * into the program to prevent this. */ void prog_cooldown_event (PROG_LIST *prog_list, int delay, char *msg) { EVENT *ev = new_event("Prog cooldown event"); ev->type = EVENT_PROG_COOLDOWN; ev->fire = &fire_prog_cooldown_event; ev->destroy = &destroy_prog_cooldown_event; ev->event_args.prog_cooldown.prog_list = prog_list; if (msg[0] != '\0') ev->event_args.prog_cooldown.message = str_dup(msg); else ev->event_args.prog_cooldown.message = str_dup("Huh?"); ev->delay = delay; ev->next = events; events = ev; //writelogf("prog_cooldown_event called"); } void fire_prog_cooldown_event(EVENT * ev) { } void destroy_prog_cooldown_event(EVENT * ev) { free_string(ev->event_args.prog_cooldown.message); } /* ******************************** /COOLDOWN PROG EVENT/ ******************************** */ /* ******************************** AFFHANDLE EVENT ******************************** * Rather than have a affect_update or whatever, all (cast) affects have an * event wrapper. This allows much more flexibility with what you can achieve * with your affects. */ void affhandle_event(CHAR_DATA *victim, AFFECT_DATA *paf, CHAR_DATA *caster) { EVENT *ev; ev = new_event("Affhandle event"); ev->type = EVENT_AFFHANDLE; ev->fire = &fire_affhandle_event; ev->destroy = &destroy_affhandle_event; //ev->event_args.affhandle.ch = ch; //hack fixme ev->event_args.affhandle.victim = victim; if (caster) ev->event_args.affhandle.caster = caster; else ev->event_args.affhandle.caster = victim; ev->event_args.affhandle.paf = paf; ev->delay = paf->duration; ev->next = events; events = ev; } void fire_affhandle_event(EVENT * ev) { AFFECT_DATA *paf = ev->event_args.affhandle.paf; CHAR_DATA *victim = ev->event_args.affhandle.victim; if (paf->type >= 0 && paf->type < SkillTable.size()) { if (!SkillTable[paf->type].off_self.empty()) ch_printf(victim, "%s\n\r", SkillTable[paf->type].off_self.c_str()); if (!SkillTable[paf->type].off_room.empty()) act( SkillTable[paf->type].off_room.c_str(), victim, NULL, NULL, TO_ROOM ); } affect_remove (victim, paf); } void destroy_affhandle_event(EVENT * ev) { } /* ******************************** /AFFHANDLE EVENT/ ******************************** */ /* ******************************** MENU EVENT ******************************** * Make a menu for a person to respond to. See the menu_event in wiz_edit.c * for an example of how this is to be called. */ void menu_event(CHAR_DATA *ch, MENU_DATA *menu) { //this shouldn't be necessary, but meh. if (IS_NPC(ch)) return; EVENT *ev; ev = new_event("Menu handler event"); ev->type = EVENT_MENU; ev->fire = &fire_menu_event; ev->destroy = &destroy_menu_event; ev->event_args.menu.ch = ch; ev->event_args.menu.menu = menu; menu->ev = ev; ev->delay = 360; //give a player 6 minutes to respond to a menu. Shrug. //theoretically, we could not even have a delay at all, //but I decided upon this. //use the pointer rather than find it from the char's LL. Lazy. //(make the old locked menu expire) if (ch->pcdata->menu_locked) ch->pcdata->menu_locked->ev->delay = 0; menu_to_char(ch, menu); //Reset the locked menu here. if (!IS_SET(menu->menu_flags, MENU_NOLOCK)) ch->pcdata->menu_locked = menu; else ch->pcdata->menu_locked = NULL; ev->next = events; events = ev; /* writelogf("%s:%d:menu_event: Attached new menu '%s' to '%s'", __FILE__, __LINE__, menu->name, ch->name);*/ } void fire_menu_event(EVENT * ev) { } void destroy_menu_event(EVENT * ev) { //this function takes care of the freeing of the menu and, if the menu //is set to Phoenix, rebirths it. CHAR_DATA *ch = ev->event_args.menu.ch; MENU_DATA *menu = ev->event_args.menu.menu; /*nothing to do if the menu disappeared somehow*/ if (!menu) return; /*character still around? Unsafe if not. Just toast menu.*/ if (!ch) { free_menu(menu); return; } //unlock if (ch->pcdata->menu_locked == menu) ch->pcdata->menu_locked = NULL; //remove menu from char's LL. menu_from_char(ch, menu); if (IS_SET(menu->menu_flags, MENU_PHOENIX)) { /* writelogf("%s:%d:destroy_menu_event: Menu '%s' on '%s' will phoenix", __FILE__, __LINE__, menu->name, ch->name);*/ do_function(ch, menu->fun_from, itoa(menu->argi)); } //toast the menu, if applicable //'hardcoded' menus will never be freed, that'd suck. if (!IS_SET(menu->menu_flags, MENU_NOFREE)) { /* writelogf("%s:%d:destroy_menu_event: Menu '%s' on '%s' is being freed", __FILE__, __LINE__, menu->name, ch->name);*/ free_menu(menu); } } /* ******************************** /MENU EVENT/ ******************************** */