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