#include <time.h> #include <situations.h> #define RANDOM(a,b) ("/handlers/random_num"->random(a,b)) #define MIN_DURATION 1 /* seconds between checks for situation changes */ #define SECTOCHECK 15 /* maximum random install delay (seconds) */ #define INSTALLDELAY 15 #define MAXROOMS 30 /* Room info * (roominfo[i])[0]=currentsits [1]=offset [2]=els [3]=sittiming * [4]=chatmin [5]=chatmax [6]=awake * int *currentsits; * string *els; * int offset; * mixed *sittiming; * int chatmin, chatmax; * int awake; 0 off 1 on */ nosave mapping roominfo; nosave mixed *rooms; /* global situation info */ nosave mapping sitdata; /** * situation changer object associated with multiple room objects. * It keeps a list of rooms and checks them all in turn. It * behaves similarly to the room_handler. It's main purpose is * to avoid duplicating a large chat database for many rooms * that all use it. */ /** * returns status of situation manager. * If it is sleeping it will turn on again if a * player enters the room. * @return status 0 off 1 on 2 sleeping */ int query_status() { return 1; } /** * returns situations currently turned on. * @return int array of situation labels */ int *query_current_situations(object room) { if (!roominfo[file_name(room)]) return 0; return (roominfo[file_name(room)])[0]; } void dest_me() { destruct(TO); } /* dest_me() */ /** * Adds a situation for the rooms. These situations can be * invoked manually with start_situation or automatically via * automate_situations. * @param num number labelling the situation * @param func function to be called at start of situation * that might be used to load NPC's. If it is * a set of two function pointers ({ f1, f2 }) * the second function is called when the * situation is ended. The start function is * passed the num label of the room and a * do_start_mess flag. If the flag is one * the situation is starting rather than * being reloaded. The end function is only * passed the num label. * @param startmess message told to the room at start of situation * @param endmess message told to the room at end of situation * @param extralookstring extra look string appended to rooms long * description during the situation * @param chats an array of chat strings to be active * during the situation * @param add_items a mixed array of ({ item, item description }) * pairs to be active during the situation * @see start_situation * @see automate_situations * @see add_item * @see room_chat * @see add_extra_look * @example * add_situation( 0, 0, "Water seeps out of the ground to form puddles.", * "", "There are large puddles on the ground here.", * ({"A hidden frog croaks quietly.", * "There is a blooping sound." }), * ({ ({"puddle", "The puddles are dark and murky. " * "They will probably dry up given time." }) }) ); */ void add_situation(int num, function *func, string startmess, string endmess, string extralookstring, string *chats, mixed *add_items ) { if (!sizeof(sitdata)) sitdata= ([ num : ({ startmess, endmess, extralookstring, chats, add_items, func }) ]); else sitdata+= ([ num : ({ startmess, endmess, extralookstring, chats, add_items, func }) ]); } /** * Starts a situation previously added for the room * that is managed by this object. * These situations can be invoked manually with * start_situation or automatically via * automate_situations. * @param num number labelling the situation * @param do_start_mess 0 to supress the start_mess string * This is to fake it that a situation has been * going for a while when really you just loaded it. * @param room room to add situation to * @see add_situation * @see end_situation * @see automate_situations */ void start_situation(int num, int do_start_mess, object room) { mixed *sdata; string *chats; mixed item,*items; int *currentsits; string *els; object chatter; if (!(sdata=sitdata[num]) || !room) return; if (!roominfo[file_name(room)]) roominfo[file_name(room)]= ({ 0,0,0,0,60,120,1 }); currentsits=(roominfo[file_name(room)])[0]; els=(roominfo[file_name(room)])[1]; if (!currentsits) currentsits=({ num }); else currentsits=currentsits+({ num }); if (sizeof(sdata[2])) { if (els && sizeof(els)) els+=({ sdata[2] }); else { els=({ sdata[2] }); room->add_extra_look(TO); } } if (do_start_mess && sizeof(sdata[0])) tell_room(room, sdata[0]+"\n"); chats=sdata[3]; if (chats && sizeof(chats)) { // tell_creator("shaggy","Adding: %O\n",chats); if (!(chatter=room->query_chatter())) { room->room_chat( ({ (roominfo[file_name(room)])[4],(roominfo[file_name(room)])[5], chats }) ); chatter=room->query_chatter(); chatter->check_chat(); } else chatter->add_room_chats(chats); } items=sdata[4]; if (sizeof(items)) { foreach(item in items) room->add_item(item[0],item[1]); } if (sdata[5]) { /* functionp */ if (functionp(sdata[5])) (*(sdata[5]))(num,do_start_mess); else if (arrayp(sdata[5]) && functionp((sdata[5])[0])) (*((sdata[5])[0]))(num,do_start_mess); } (roominfo[file_name(room)])[0]=currentsits; (roominfo[file_name(room)])[1]=els; } /* start_situation() */ /** * Ends a situation previously added and started on the room * that is managed by this object. * These situations can be invoked manually with start_situation * or automatically via automate_situations. * @param num number labelling the situation * @param room room to end situation on * @see add_situation * @see start_situation * @see automate_situations */ void end_situation(int num, object room) { mixed *sdata; string *chats; mixed item,*items; int *currentsits; string *els; object chatter; if (!(sdata=sitdata[num]) || !room) return; if (!roominfo[file_name(room)]) return; currentsits=(roominfo[file_name(room)])[0]; els=(roominfo[file_name(room)])[1]; if (currentsits) currentsits=currentsits-({ num }); if (els && sizeof(sdata[2])) { els-=({ sdata[2] }); if (!sizeof(els)) room->remove_extra_look(TO); } chats=sdata[3]; if (chats && sizeof(chats) && (chatter=room->query_chatter())) { // tell_creator("shaggy","Removing: %O\n",chats); chatter->remove_room_chats(chats); } items=sdata[4]; if (sizeof(items)) { foreach(item in items) room->remove_item(item[0],item[1]); } if (sizeof(sdata[1])) tell_room(room,sdata[1]+"\n"); if (sdata[5]) { /* functionp */ if (arrayp(sdata[5]) && functionp((sdata[5])[1])) (*((sdata[5])[1]))(num); } (roominfo[file_name(room)])[0]=currentsits; (roominfo[file_name(room)])[1]=els; } /* end_situation() */ /** * Starts and ends situations according to the information * in the sittiming mapping. It is called continuously * automatically while there are interactives in the room. */ void manage_situations(object room) { mixed *sit; int num,t,it,cnt,possible; int *currentsits,*newsits,*changes; int ttmp,tstep,offset,awake; mixed data; // tell_creator("shaggy","Managing %O %O %O \n",room,roominfo[file_name(room)], // filter_array( all_inventory( room ),(: interactive($1) :))); if (!room || !(data=roominfo[file_name(room)])) return; if (!sizeof(filter_array( INV( room ),(: interactive($1) :)))) { (roominfo[file_name(room)])[6]=0; return; } // tell_creator("shaggy","No abort \n"); currentsits=data[0]; offset=data[2]; awake=data[6]; t=time()+offset; newsits=({}); tstep=2147483647; cnt=0; foreach(sit in data[3]) { cnt++; ttmp=sit[1]; it=t/ttmp; possible=0; if (sit[2]) { if (functionp(sit[2])) possible=(*(sit[2]))(); else possible=sit[2]; possible = ( possible & ( 1 << ( TIME_H->query_hour( it * ttmp-offset ) ) ) ) && ( RANDOM( 1000, it * 1147483647 + cnt * 2047483243 ) < sit[3] ); } if (possible) { if (arrayp(sit[0])) { num=(sit[0]) [((t % ttmp)*sizeof(sit[0]))/ttmp]; ttmp=ttmp/sizeof(sit[0]); } else num=sit[0]; if (member_array(num,newsits)==-1) newsits=newsits+({ num }); } if (ttmp<tstep) tstep=ttmp; } if (currentsits && sizeof(currentsits)) { changes=currentsits-newsits; if (sizeof(changes)) foreach(num in changes) { if (awake) call_out("end_situation",random(INSTALLDELAY), num,room); else end_situation(num,room); } changes=newsits-currentsits; } else changes=newsits; if (sizeof(changes)) foreach(num in changes) { if (awake) call_out("start_situation",random(INSTALLDELAY), num,awake,room); else start_situation(num,awake,room); } if ((tstep/=SECTOCHECK)>=MAXROOMS) tstep=MAXROOMS-1; if (!rooms[tstep]) rooms[tstep]=({ room }); else rooms[tstep]=rooms[tstep]+({ room }); (roominfo[file_name(room)])[6]=1; } /* manage_situations() */ /** * Starts and ends situations according to the information * in the sittiming mapping for each room in the rooms array. * It is called continuously * automatically while there are interactives in the room. */ void manage_rooms_situations() { object room,*current_rooms; current_rooms = rooms[ 0 ]; rooms[ 0 .. <2 ] = rooms[ 1 .. <1 ]; rooms[ <1 ] = 0; call_out("manage_rooms_situations",SECTOCHECK); if ( !pointerp( current_rooms ) ) return; foreach ( room in current_rooms ) manage_situations(room); } /* manage_room_situations() */ /** * Tests for enabling situation managing. * If situation managing is already active or turned off * it does nothing. * @param room the room * @see automate_situations */ void check_situations(object room) { if (room && roominfo[file_name(room)] && !((roominfo[file_name(room)])[6]) && interactive(TP)) manage_situations(room); } /* check_situations() */ /** * Awakes starting and ending of situations. * These situations can be invoked manually with start_situation. * The awaked starting and ending is unaffected by the room * unloading. When the room reloads the situation will be * restarted unless its duration is up. * @param chatargs same as arguments to room chat: * ({ min, max, ({ chat1, chat2, ... }) }) * if you want no non-situational chats just put ({ min, max, ({ }) }) * min, max are always needed being the minimum/maximum time * between all chats. chat1 is a string containing a chat. * @param xval First random seed number (eg. x coordinate) * @param yval Second random seed number (eg. y coordinate) * @param sittiming A mixed array containing the information * about when the situations added by add_situation are * automatically started and ended: * * ({ info1, info2, info3, ... }) * * Each info is a mixed array as follows: * * ({ number, duration, when, chance }) * -- or -- * ({ ({ num0, num1, num2, ..., numN }) , when, duration, percent }) * * number (int) label of the situation to start * up if number is an array then a set of * situations are started one at a time. * The total duration is divided evenly * between the situations labelled by the * numbers in the array which are started * and ended in the order given. The * entire set always gets used. * * duration (int) time (minutes) situation should last for * * when (int) a nighttime/daytime mask * This determines when during the day in * Ankh-Morpork time the situation may occur. * The masks are defined in evolvingtime.h * It can be a function pointer in which case it is * expected to return a mask value. * * chance (int) 1/1000 chance per period of duration minutes * of getting into the situation * @see add_situation * @see start_situation * @see end_situation * @see evolvingroom.h * @example * automate_situations( room, ({ 50,70, ({""}) }), * 170, 220, * ({ ({ 0, 5, WHEN_ANY_TIME, 200 }), * ({ ({ 1,2,3 }), 6, (WHEN_NIGHT|WHEN_EVENING), 500 }) }) ); */ void automate_situations(mixed *chatargs, int xval, int yval, mixed *edata, object room) { mixed sit; mixed *sittiming; int offset; offset=xval*38547+yval*1232444311; if ( pointerp(chatargs[2]) && sizeof(chatargs[2])) { room->room_chat( chatargs ); } if (sizeof(edata)) { foreach(sit in edata) { if (sit[1]<MIN_DURATION) { write("Invalid situation duration: "+sit[1]+"\n"); return; } sit[1]*=60; /* minutes to seconds */ if (!sittiming) sittiming=({ sit }); else sittiming=sittiming+({ sit }); /* may add extra info to sittiming later */ } } roominfo[file_name(room)]=({ 0,0, offset, sittiming, chatargs[0], chatargs[1], 0 }); manage_situations(room); } /* automate_situations() */ /** * Shuts down all current and pending situations. It also turns off the * awaked situation manager so no more are added. It does not * destruct this object so all the add_situations are still loaded * and make be recommenced with automate_situations. dest_me is * the appropriate call to permanently remove all situations. * @param room room to shutdown situations on * @see automate_situations */ void shutdown_situations(object room) { int num, *currentsits; currentsits=(roominfo[file_name(room)])[0]; if (currentsits && sizeof(currentsits)) { foreach(num in currentsits) end_situation(num, room); } } /* shutdown_situations() */ string extra_look(object room) { string *els; if (!roominfo[file_name(room)]) return ""; els=(roominfo[file_name(room)])[1]; if (!els || !sizeof(els)) { return ""; } return implode(els," ")+"\n"; } /* add_extra_look() */ void create() { rooms = allocate( MAXROOMS ); manage_rooms_situations(); roominfo= ([ ]); } /* create() */ object *query_rooms() { return rooms; }