/**************************************************************************/ // track.cpp - Incomplete track system written by Kal /*************************************************************************** * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt * * >> A number of people have contributed to the Dawn codebase, with the * * majority of code written by Michael Garratt - www.dawnoftime.org * * >> To use this source code, you must fully comply with the dawn license * * in licenses.txt... In particular, you may not remove this copyright * * notice. * **************************************************************************/ #include "include.h" #include "track.h" void resort_tracks(); #define ch2int(ch) (*((int *)ch)) int rooms_with_tracks=0; unsigned short tracktime=1500; C_track_table *track_table=NULL; #define MTPR MAX_TRACKS_PER_ROOM /**************************************************************************/ void init_track_table() { log_string("init_track_table():: Initialising track_table..."); track_table=new C_track_table; if(MAX_RACE>32000){ bugf( __FILE__":init_track_table() - MAX_RACE is set to %d, which is higher than 32000...\n" "Track wont function correctly in this environment without modifications. Aborting startup.", MAX_RACE); exit_error( 1 , "init_track_table", "MAX_RACE too high"); } log_string("init_track_table():: track_table initialised."); } /**************************************************************************/ // constructor C_track_table::C_track_table() { // NULL out all the tracks to start with for(int i=0;i<MTC; i++){ character[i]=NULL; race_oldchar[i]=0; } total_tracked_characters=0; next_free_track=0; } /**************************************************************************/ // returns true if the character field is pointing to a valid char_data ch // - this will only be true if that ch is in the game currently bool C_track_table::is_active(int index) { if(IS_SET(race_oldchar[index], 0x8000)){ return false; }else{ if(character[index]==NULL){ return false; } return true; } } /**************************************************************************/ int C_track_table::add_char(char_data *ch) { int trackindex=next_free_track; character[trackindex]=ch; // MSB (most significant bit) of race_oldchar is only set to 1 after // the character has been freed, then the character pointer no longer // points to something of char_data but becomes a flags field recording // info about what the character was e.g. npc/pc, immortal etc. race_oldchar[trackindex]=ch->race & 0x7FFF; // ensure MSB is off for now total_tracked_characters++; // find where our next free track index is int count=0; do{ ++next_free_track%=MTC; count++; if(count>MTC){ bugf( __FILE__":C_track_table::add_char() - MTC is set to %d which is less than", MTC); bug("the number of players + mobs in the game! Track can't run unless MTC is increased"); bug("(MTC is short for MAX_TRACKABLE_CHARACTERS_IN_GAME which is set in params.h)"); bug("Increase it by say 2500, do a clean recompile then restart the mud.)"); do_abort(); } }while(is_active(next_free_track)); return (trackindex); } /**************************************************************************/ void C_track_table::del_char(int index) { assert(index>=0); assert(index<MTC); if(is_active(index)){ SET_BIT(race_oldchar[index], 0x8000); // mark the character field // as unused // record what we want to record from the character // before it is freed if(IS_NPC(character[index])){ ch2int(character[index])=0x01; // first bit records is_npc status }else{ ch2int(character)=0x00; if(IS_IMMORTAL(character[index])){ ch2int(character[index])|=0x02; // second bit records imm status } } total_tracked_characters--; }else{ bugf(__FILE__":C_track_table::del_char(int) - index %d was previously " "deleted!", index); } } /**************************************************************************/ void C_track_table::del_char(char_data *ch) { assert(ch->track_index>=0); assert(ch->track_index<MTC); if(character[ch->track_index]!=ch){ bugf(__FILE__":C_track_table::del_char(CD*) - ch != character[ch->track_index]"); }else{ del_char(ch->track_index); } } /**************************************************************************/ int C_track_table::get_race_value(int index) { assert(index>=0); assert(index<MTC); if(is_active(index)){ // temp hack till cleaned up race_oldchar[index]=character[index]->race & 0x7FFF; // ensure MSB is off for now } return (int)(race_oldchar[index] & 0x7fff); // result less the MSB } /**************************************************************************/ char *C_track_table::get_race(int index) { assert(index>=0); assert(index<MTC); return (race_table[get_race_value(index)]->name); } /**************************************************************************/ int C_track_table::get_total_tracked_characters() { return total_tracked_characters; } /**************************************************************************/ bool C_track_table::is_npc(int index) { assert(index>=0); assert(index<MTC); if(is_active(index)){ return (IS_NPC(character[index])); }else{ // check bit 0 of the character field return(IS_SET(ch2int(character[index]),0x80)!=false); } } /**************************************************************************/ char_data * C_track_table::get_char(int index) { assert(index>=0); assert(index<MTC); if(is_active(index)){ return character[index]; }else{ return NULL; } }; /**************************************************************************/ char * C_track_table::get_pers(int index, char_data *looker) { assert(index>=0); assert(index<MTC); if(is_active(index)){ return PERS(character[index], looker); }else{ return "(logged out or dead)"; } }; /**************************************************************************/ /**************************************************************************/ void tracktime_update() { tracktime++; if(tracktime==65535){ // this happens once every 4 days or so if PULSE_MINUTE is // once every 6 seconds. resort_tracks(); tracktime=1500; // start as if there are 2.5 hours worth of tracks } } /**************************************************************************/ void resort_tracks() { bug("resort_tracks() called - not yet implemented"); }; /**************************************************************************/ void init_room_tracks() { ROOM_INDEX_DATA *pRI; int iHash; for ( iHash = 0; iHash < MAX_KEY_HASH; iHash++ ) { for ( pRI= room_index_hash[iHash]; pRI; pRI=pRI->next ) { pRI->tracks=NULL; // add tracks as they are needed } } } /**************************************************************************/ // tracks for room constructor C_track_data::C_track_data() { memset(trackindex, 0, sizeof(trackindex)); memset(time_of_track, 0, sizeof(time_of_track)); memset(direction_type, 0, sizeof(direction_type)); nexttrack=0; } /**************************************************************************/ tracktype C_track_data::get_tracktype(int index) { return (tracktype)(direction_type[index]>>4); }; /**************************************************************************/ void C_track_data::set_tracktype(int index, tracktype type) { direction_type[index]= (direction_type[index] & 0x0F) + (type << 4); }; /**************************************************************************/ int C_track_data::get_direction(int index) { return (direction_type[index]&0x0F); }; /**************************************************************************/ void C_track_data::set_direction(int index, int direction) { direction_type[index]= (direction_type[index] & 0xF0) + (direction& 0x0F); }; /**************************************************************************/ void C_track_data::add_track(char_data *ch, int direction, tracktype type) { assertp(ch->in_room); if(this==NULL){ // allocate memory for tracks when they are needed, not before ch->in_room->tracks=new C_track_data(); rooms_with_tracks++; // logf("allocating track memory for room %d", ch->in_room->vnum); ch->in_room->tracks->add_track(ch, direction, type); return; } // ensure ch->in_room is the room we (this) belong to assert(ch->in_room->tracks==this); // record the details of the track time_of_track[nexttrack]=tracktime; trackindex[nexttrack]=ch->track_index; // record the type and direction // type is converted if the general move into a more specific type if(type==TRACKTYPE_MOVE){ if( (INVIS_LEVEL(ch)>=LEVEL_IMMORTAL) || IS_SET(TRUE_CH(ch)->act, PLR_HOLYWALK)) { type=TRACKTYPE_WIZIIMM; }else if(IS_AFFECTED(ch, AFF_FLYING)){ type=TRACKTYPE_FLY; } else if(IS_AFFECTED2(ch, AFF2_PASSWOTRACE)){ if ( IS_OUTSIDE(ch)){ type=TRACKTYPE_PASSWOTRACE; }else{ type=TRACKTYPE_WALK; } }else if(IS_AFFECTED(ch, AFF_SNEAK)){ type=TRACKTYPE_SNEAK; }else{ type=TRACKTYPE_WALK; } } set_direction(nexttrack, direction); set_tracktype(nexttrack, type); ++nexttrack%=MAX_TRACKS_PER_ROOM; } /**************************************************************************/ char *tracktype_name(tracktype type) { switch(type){ default: return "unknown type!!! - report the bug"; case(TRACKTYPE_NONE ): return "none"; case(TRACKTYPE_MOVE ): return "move"; case(TRACKTYPE_FLY ): return "fly"; case(TRACKTYPE_SNEAK ): return "sneak"; case(TRACKTYPE_WALK ): return "walk"; case(TRACKTYPE_BLOODTRAIL ): return "bloodtrail"; case(TRACKTYPE_BLOODPOOL ): return "bloodpool"; case(TRACKTYPE_PASSWOTRACE ): return "pass without trace"; case(TRACKTYPE_WIZIIMM ): return "wiziimm/holywalk"; } } /**************************************************************************/ char *tracktype_age(int time) { // time is between 0 and 600 int sw; //=URANGE(6, time, 600)/50; if(time<2) sw=0; // 12 seconds IRL (2mins IC) else if(time<5) sw=1; // 30 seconds IRL (5mins IC) else if(time<10) sw=2; // 1 minute IRL (10minsIC) else if(time<20) sw=3; // 2 minutes IRL (10 else if(time<40) sw=4; // 4 minutes IRL (40mins IC) else if(time<70) sw=5; // 7 minutes IRL (1.1hoursIC) else if(time<100) sw=6; // 10 minutes IRL (1.6hoursIC) else if(time<150) sw=7; // 15 minutes IRL (2.5hoursIC) else if(time<225) sw=8; // 22.5 minutes IRL (3,75hours IC) else if(time<300) sw=9; // 30 minutes IRL (5hours IC) else if(time<400) sw=10; // 40 minutes IRL (6.6hours IC) else if(time<500) sw=11; // 50 minutes IRL (8.3hours IC) else sw=12; switch(sw){ default: return ""; case(0): return "extremely fresh"; case(1): return "very fresh"; case(2): return "fresh"; case(3): return "rather fresh"; case(4): return "moderately fresh"; case(5): return "fairly recent"; case(6): return "recent"; case(7): return "moderately recent"; case(8): return "old"; case(9): return "very old"; case(10): return "extremely old"; case(11): return "faintly visible"; case(12): return "barely visible"; } } /**************************************************************************/ void C_track_data::show_tracks(char_data *ch) { assertp(ch->in_room); int seen=0; int sect=ch->in_room->sector_type; int sk; int main_sn; switch(sect){ case(SECT_INSIDE): case(SECT_CITY): sk=get_skill(ch, gsn_citytrack) + get_skill(ch, gsn_fieldtrack)/5; main_sn=gsn_citytrack; break; default: sk=get_skill(ch, gsn_fieldtrack) + get_skill(ch, gsn_citytrack)/8; main_sn=gsn_fieldtrack; break; } sk++; // everyone gets it basically if(sk<1){ ch->println("What would you know about tracking in this terrain?"); return; } if(this==NULL){ if(IS_ICIMMORTAL(ch)){ ch->println("No tracks in the room yet."); }else{ ch->println("You failed to see any tracks here."); WAIT_STATE( ch, skill_table[main_sn].beats ); } return; } // ensure ch->in_room is the room we (this) belong to assert(ch->in_room->tracks==this); if(!IS_IMMORTAL(ch)){ if(IS_WATER_SECTOR(sect)){ ch->println("You can't see tracks in the water."); return; } if(sect==SECT_AIR){ ch->println("Tracks arent left in the air."); return; } } WAIT_STATE( ch, skill_table[main_sn].beats ); // loop thru displaying the track info in newest to oldest order for (int tempindex=nexttrack+MTPR-1; tempindex>=nexttrack; tempindex--) { int i= tempindex%MTPR; if(!time_of_track[i]){ continue; } if(IS_ICIMMORTAL(ch)){ ch->printlnf("%2d> age=%5d, %s tracks of %s to the %s.", i, time_of_track[i], tracktype_name( (tracktype)(direction_type[i]>>4) ), track_table->get_pers(trackindex[i],ch), dir_name[(direction_type[i]&0x0F)]); seen++; }else{ int timediff; if(tracktime<time_of_track[i]){ // approx hack for now to support looping timediff= tracktime+ 65535 - 1500 -time_of_track[i]; }else{ timediff=tracktime-time_of_track[i]; } if(timediff< (6*number_range(1,sk))) // allow a range of 6->600 { tracktype type=get_tracktype(i); char *race=lowercase(track_table->get_race(trackindex[i])); if(!race){ bugf( __FILE__":C_track_data::show_tracks() - get_race() return NULL\n"); continue; } char *racea_an; if(*race=='a' || *race=='e' || *race=='i' || *race=='o' || *race=='u'){ racea_an="an"; }else{ racea_an="a"; } int dir=get_direction(i); switch(type){ default: ch->printlnf("unknown track type %d for index %d!!! - please report the bug to code", (int)type, i); break; case(TRACKTYPE_NONE ): break; case(TRACKTYPE_MOVE ): case(TRACKTYPE_SNEAK ): case(TRACKTYPE_WALK ): if(ch->track_index==trackindex[i]){ ch->printlnf("`SSome %s tracks of %s %s (possibly your own) lead %s%s.`x", tracktype_age(timediff), racea_an, race, (dir==DIR_UP || dir==DIR_DOWN)?"":"to the ", dir_name[dir]); }else{ ch->printlnf("Some %s tracks of %s %s lead %s%s.", tracktype_age(timediff), racea_an, race, (dir==DIR_UP || dir==DIR_DOWN)?"":"to the ", dir_name[dir]); } seen++; break; case(TRACKTYPE_FLY ): break; case(TRACKTYPE_BLOODTRAIL ): break; case(TRACKTYPE_BLOODPOOL ): break; case(TRACKTYPE_WIZIIMM ): break; case(TRACKTYPE_PASSWOTRACE ): break; } } } } if(seen==0){ check_improve(ch,main_sn, false, 10); ch->println("You failed to see any tracks here."); }else{ check_improve(ch,main_sn, true, 10); } } /**************************************************************************/ void do_tracks( char_data *ch, char *) { ch->in_room->tracks->show_tracks(ch); } /**************************************************************************/ void do_autotrack(char_data *ch, char *) { if (HAS_CONFIG(ch, CONFIG_AUTOTRACK)) { ch->println("Autotrack disabled."); REMOVE_CONFIG(ch, CONFIG_AUTOTRACK); } else { ch->wraplnf("Autotrack enabled, you will automatically " "look for tracks when you move and are not following others (assuming " "you have any tracking skill greater than 1%% and you arent speedwalking)."); SET_CONFIG(ch, CONFIG_AUTOTRACK); } } /**************************************************************************/ /**************************************************************************/