dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// lockers.cpp - dawn locker system, written Feb 2002 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 "lockers.h"
#include "ictime.h"

// Locker implementation notes:
//   * the content of all lockers are stored in a single flat file, 
//     depending on how popular the lockers are, it may make sense to 
//     save lockers based on roomvnum... but for now I haven't bothered.

/**************************************************************************/
lockers_object *lockers;
bool lockers_referenced; // if this is true, the lockers will be saved next tick
/**************************************************************************/
lockers_object::lockers_object()
{
	memset(hash, 0, sizeof(hash));
};
/**************************************************************************/
locker_data *lockers_object::get_first(int roomvnum) 
{ 
	return hash[roomvnum%LOCKER_HASH_KEY];
};	
/**************************************************************************/
// return the locker container object
OBJ_DATA *lockers_object::find_locker_object(char_data *ch, bool display_messages, int number)
{
	locker_data *locker;
	locker=find_locker(ch, display_messages, number);
	if(locker){
		locker_data_of_last_found_locker_object=locker;
		return locker->locker_object;
	}
	locker_data_of_last_found_locker_object=NULL;
	return NULL;
}
/**************************************************************************/
locker_data *lockers_object::find_locker_data(char *owner, int roomvnum, int number)
{
	locker_data *locker;
	// scan thru the list of lockers to find one
	for(locker=get_first(roomvnum); locker; locker=locker->next_locker){
		if(locker->roomvnum!=roomvnum){
			continue;
		}

		if(number){
			if(number!=locker->number){
				continue;
			}
		}else{ // no number specified, looking for the owners locker
			if(str_cmp(owner, locker->owner)){
				continue;
			}
		}
		return locker;
	}
	return NULL;
}
/**************************************************************************/
locker_data *lockers_object::find_locker(char_data *ch, bool display_messages, int number)
{
	if(!lockers->room_has_lockers(ch->in_room)){
		if(display_messages){
			ch->println("This room doesn't have any lockers.");
		}
		return NULL;
	}
	if(IS_NPC(ch)){
		ch->println("NPC's can't access lockers.");
		return NULL;
	}

	if(GAMESETTING3(GAMESET3_LOCKERS_DISABLED)){
		ch->println("The locker system is currently disabled.");
		return NULL;
	}
	
	// find a particular locker
	locker_data *locker;
	locker=find_locker_data(ch->name, ch->in_room_vnum(), number);
	if(locker){
		lockers_referenced=true;
		locker->last_accessed=current_time;
		return locker;
	}

	if(display_messages){
		if(number){
			ch->printlnf("Couldn't find rented locker%d in this room.", number);
		}else{
			ch->println("You don't own a locker in this room.");
		}
	}
	return NULL;
}
/**************************************************************************/
int lockers_object::count_used_lockers_in_room(int roomvnum)
{
	int count=0;
	locker_data *locker;

	// scan thru the list of lockers counting the matches
	for(locker=get_first(roomvnum); locker; locker=locker->next_locker){
		if(locker->roomvnum!=roomvnum){
			continue;
		}
		count++;
	}

	return count;
}

/**************************************************************************/
// return true if room player is in has lockers
bool lockers_object::room_has_lockers(ROOM_INDEX_DATA * room)
{
	if( !room || !room->lockers 
		|| !room->lockers->initial_rent)
	{
		return false;
	}
	return true;
}
/**************************************************************************/
// when a player starts renting a locker
void lockers_object::start_rent(char_data *ch, int locker_number)
{
	if(!lockers->room_has_lockers(ch->in_room)){
		ch->println("Lockers don't appear to be available in this room.");
		return;
	}
	if(IS_NPC(ch)){
		ch->println("NPC's can't rent lockers.");
		return;
	}

	if(GAMESETTING3(GAMESET3_LOCKERS_DISABLED)){
		ch->println("The locker system is currently disabled.");
		return;
	}

	if(find_locker(ch,false, 0)){
		ch->println("You already own a locker in this room.");
		return;
	}

	if(	ch->in_room->lockers->weight<1
		|| ch->in_room->lockers->capacity<1)
	{
		ch->wrapln("The lockers in this room are incorrectly setup, the locker "
			"weight and capacity must be greater than zero, talk to the admin.");
		return;
	}

	if(count_used_lockers_in_room(ch->in_room_vnum())>= ch->in_room->lockers->quantity){
		ch->println("There are no more lockers available in this room for rent.");
		return;
	}

	if(locker_number>ch->in_room->lockers->quantity){
		ch->printlnf("The locker numbering doesn't go as high as %d in this room.", locker_number);
		return;
	}

	if(locker_number){
		locker_data *l=find_locker_data("", ch->in_room_vnum(), locker_number);
		if(l){
			ch->printlnf("Locker %d is already taken.", locker_number);
			return;
		}
	}

	if(ch->gold<ch->in_room->lockers->initial_rent){
		ch->printlnf("The initial rent for lockers in this room cost %d gold... you only have %ld.",
			ch->in_room->lockers->initial_rent,
			ch->gold);
		return;
	}

	// create a new locker
	locker_data *locker=new locker_data;

	locker->owner=str_dup(ch->name);
	locker->access=str_dup("");
	locker->label=str_dup(ch->short_descr);
	locker->roomvnum=ch->in_room_vnum();
	if(locker_number){
		locker->number=locker_number;
	}else{
		locker->number=get_next_free_locker_number(locker->roomvnum);
	}
	locker->paid_until=current_time+ICTIME_IRLSECS_PER_MONTH;

	// create a locker object for the new locker
	obj_data *o;
	o=create_object(get_obj_index(OBJ_VNUM_DUMMY));	
	replace_string(o->name, FORMATF("locker%d", locker->number));
    replace_string(o->short_descr, FORMATF("locker %d", locker->number));
    replace_string(o->description, FORMATF("locker %d is here", locker->number));
    o->item_type    = ITEM_CONTAINER;
    o->extra_flags  = OBJEXTRA_NOPURGE | OBJEXTRA_BURN_PROOF | OBJEXTRA_NO_DEGRADE | OBJEXTRA_NO_RESTRING;
    o->extra2_flags = OBJEXTRA2_NODECAY | OBJEXTRA2_NOSELL | OBJEXTRA2_NOQUEST | OBJEXTRA_NOLOCATE;
    o->wear_flags	= OBJWEAR_NO_SAC;
    o->weight       = 0;
    o->cost         = 0;
    o->level        = 1;
    o->condition    = 100;
    replace_string(o->material, "metal");
    o->timer        = 0;
    o->absolute_size= 0;
    o->relative_size= 0;
	o->ospec_fun	= NULL;
	o->attune_id	= 0;
	o->attune_flags	= 0;
	o->attune_modifier= 0;
	o->attune_next	= 0;
	o->extra_descr=NULL;
	o->value[0]=ch->in_room->lockers->weight;
	
	// container flags
	if(ch->in_room->lockers->pick_proof==3){
		o->value[1]=CONT_CLOSEABLE | CONT_PICKPROOF | CONT_LOCKER;
	}else if(ch->in_room->lockers->pick_proof==2 && number_range(1,2)==1){
		// 50% chance of getting a pick proof locker if set to random
		o->value[1]=CONT_CLOSEABLE | CONT_PICKPROOF | CONT_LOCKER;
	}else{
		o->value[1]=CONT_CLOSEABLE | CONT_LOCKER; 
	}
	o->value[2]=0; // key vnum
	o->value[3]=ch->in_room->lockers->capacity;
	o->value[4]=0; // weight multiplier

	// link the locker object into the particular room hosting the locker
	o->in_room=ch->in_room;

	locker->locker_object=o;

	// put the new locker in the hash bucket based on room vnum
	locker->next_locker=hash[locker->roomvnum%LOCKER_HASH_KEY];
	hash[locker->roomvnum%LOCKER_HASH_KEY]=locker;

	ch->printlnf("You start renting locker %d for %d gold.", 
		locker->number, ch->in_room->lockers->initial_rent);
	ch->gold-=ch->in_room->lockers->initial_rent;
	ch->println("hint: don't forget to close and lock your locker when you aren't using it.");

	lockers_referenced=true;	
	locker->last_accessed=current_time;
}
/**************************************************************************/
// pay one IC year worth of rent
void lockers_object::pay_rent(char_data *ch, int locker_number)
{
	if(!lockers->room_has_lockers(ch->in_room)){
		ch->println("Lockers don't appear to be available in this room.");
		return;
	}
	if(IS_NPC(ch)){
		ch->println("NPC's can't rent lockers.");
		return;
	}

	if(GAMESETTING3(GAMESET3_LOCKERS_DISABLED)){
		ch->println("The locker system is currently disabled.");
		return;
	}

	locker_data *l=find_locker(ch,true, locker_number);
	if(!l){
		return;
	}

	if(l->paid_until> current_time + (3*ICTIME_IRLSECS_PER_YEAR)){
		ch->println("You can't pay more than 3 IC years in advance.");
		return;
	}

	if(ch->gold<ch->in_room->lockers->ongoing_rent){
		ch->printlnf("The ongoing rent for lockers in this room cost %d gold... you only have %ld.",
			ch->in_room->lockers->ongoing_rent,
			ch->gold);
		return;
	}

	ch->printlnf("You pay %d gold for an additional IC year worth of rent for your locker %d.", 
		ch->in_room->lockers->ongoing_rent, l->number);

	l->paid_until+=ICTIME_IRLSECS_PER_YEAR;
	ch->gold-=ch->in_room->lockers->ongoing_rent;

	ch->printlnf("Your locker rent is now paid until %s (%.24s)", 
		get_shorticdate_from_time(l->paid_until, "", 0),
		ctime(&l->paid_until));

	if(l->locker_object){
		SET_BIT(l->locker_object->value[1],CONT_CLOSEABLE);
	}

	ch->println("note: if your locker is not accessed for two IRL months, it will be automatically purged");

	lockers_referenced=true;	
	l->last_accessed=current_time;
}
/**************************************************************************/
GIO_START(locker_data)
	GIO_INT(roomvnum)
	GIO_SHINT(number)
	GIO_STR(owner)
	GIO_STR(access)
	GIO_STR(label)		
	GIO_LONG(paid_until)
	GIO_LONG(last_accessed)	
GIO_FINISH_STRDUP_EMPTY
/**************************************************************************/
void lockers_object::lockers_save_db()
{
	if(GAMESETTING3(GAMESET3_LOCKERS_DISABLED)){
		return;
	}

	if(!lockers_referenced){
		return;
	}

	locker_data *locker;

	char write_filename[MSL];

	sprintf(write_filename,"%s.write", LOCKERS_FILE);

	// find a free write filename - so if something stuffed up on 
	// the write, we don't go over it.
	if(file_exists(write_filename)){
		for(int i=0; i<20; i++){
			sprintf(write_filename,"%s.write%d", LOCKERS_FILE, i);
			if(!file_exists(write_filename)){
				break;
			}
		}
	}

	unlink(write_filename);
	logf("lockers_save_db(): saving lockers to %s.", write_filename);

	FILE *fp;
    if ( !( fp = fopen(write_filename, "w" ) ) )
    {
        bugf("lockers_save_db(): fopen '%s' for write - error %d (%s)",
			write_filename, errno, strerror( errno));
		exit_error( 1 , "lockers_save_db", "fopeen for write error");
    }

	// loop thru the list of lockers saving them
	time_t expire_at=current_time-(60*60*24* game_settings->days_lockers_stored_for);
	for(int h=0; h<LOCKER_HASH_KEY; h++){
		for(locker=hash[h]; locker; locker=locker->next_locker){
			if(locker->last_accessed<expire_at)
			{
				logf("Not saving expired locker for %s in room %d.", 
					locker->owner, locker->roomvnum);
				continue;
			}

			// lockers that are six IC months over due in rent (around 14 IRL days)
			if(locker->paid_until+(6*ICTIME_IRLSECS_PER_MONTH)<current_time){
				logf("Not saving unpaid locker for %s in room %d.", 
					locker->owner, locker->roomvnum);
				continue;
			}

			fprintf(fp,"#LOCKER\n");
			{
				char *tstr=locker->access;
				locker->access=ltrim_string(rtrim_string(locker->access));
				GIO_SAVE_RECORD(locker_data, locker, fp, NULL);
				locker->access=tstr;
			}
			if(locker->locker_object){
				fwrite_obj( locker->locker_object, fp, 0, "O");
			}
			fprintf(fp,"\n");
		}
	}
	int bytes_written=fprintf(fp, "EOF~\n");
	fclose( fp );
	if(bytes_written != str_len("EOF~\n") ){
        bugf("lockers_save_db(): fprintf to '%s' incomplete - error %d (%s)",
			write_filename, errno, strerror( errno));

		bugf("Incomplete write of %s, write aborted - check diskspace!", write_filename);
		{
						
			autonote(NOTE_SNOTE, 
				"lockers_save_db()", 
				"Problems saving lockers database!", 
				"code cc: imm", 
				FORMATF("Incomplete write of %s, write aborted - check diskspace!\r\n", write_filename),
				true);
		}
	}else{
		// maintain a backup
		{
			char buf[MIL];
			sprintf(buf, "%s.giobak", LOCKERS_FILE);
			logf("lockers_save_db: Renaming old %s to %s", LOCKERS_FILE, buf);
			unlink(buf);
			rename(LOCKERS_FILE, buf);
		}

		logf("Renaming new %s to %s", write_filename, LOCKERS_FILE);
		unlink(LOCKERS_FILE);
		rename(write_filename, LOCKERS_FILE);
	}
	lockers_referenced=false;
}
/**************************************************************************/
void lockers_object::lockers_load_db()
{
	int zero_numbered_lockers=0;
	// dynamically allocate object memory to itself if required
	if(!this){
		lockers= new lockers_object;
		lockers->lockers_load_db();
		return;
	}

	
	lockers_total_count=0;
	lockers_object_count=0;

	log_string("lockers_load_db(): loading lockers database.");
	lockers_referenced=false;

	locker_data *locker=NULL;

	if(!file_exists(LOCKERS_FILE)){
		logf("lockers_load_db(): lockers database file '%s' not found.", LOCKERS_FILE);
		return;
	}

	FILE *fp;
    if ( !( fp = fopen(LOCKERS_FILE, "r" ) ) )
    {
        bugf("lockers_load_db(): fopen '%s' for read - error %d (%s)",
			LOCKERS_FILE, errno, strerror( errno));
		exit_error( 1 , "lockers_load_db", "fopeen for read error");
    }

	bool morefile=true;
	locker_data *last_locker=NULL;

	// loop thru till we get the end of the file
	while (morefile && !feof(fp)) {
		char *readword= fread_word(fp);

		if (!str_cmp(readword, "EOF") || !str_cmp(readword, "EOF~")){
			morefile=false;
		}else{
			if(!str_cmp(readword, "#LOCKER")){					
				locker=new locker_data;
				// read in the data about the locker
				GIO_LOAD_RECORD(locker_data, locker, fp);

				// update the access text with spaces around the sides
				if(!IS_NULLSTR(locker->access)){
					char *tstr=locker->access;
					locker->access=str_dup(FORMATF(" %s ", ltrim_string(rtrim_string(locker->access))));
					free_string(tstr);
				}

				// confirm the locker number is unique
				if(locker->number){
					if(find_locker_data("", locker->roomvnum, locker->number)){
						bugf("Duplicated locker number %d for room %d, owner=%s, renumbering it.",
							locker->number,
							locker->roomvnum,
							locker->owner);
						locker->number=0; // set it to zero, and we can reallocate it later
						zero_numbered_lockers++;
					}
				}else{
					zero_numbered_lockers++;
				}

				// put the new locker in the hash bucket based on room vnum
				locker->next_locker=hash[locker->roomvnum%LOCKER_HASH_KEY];
				hash[locker->roomvnum%LOCKER_HASH_KEY]=locker;
				last_locker=locker;
				// initialise the locker object to NULL, as locker object will follow
				locker->locker_object=NULL;
				lockers_total_count++;
			}else if(!str_cmp(readword, "#O")){
				OBJ_DATA *o=fread_obj( fp, LOCKERS_FILE);					
				lockers_object_count++;
				if(o){
					if(last_locker->locker_object){
						obj_to_obj(o, last_locker->locker_object);
					}else{						
						last_locker->locker_object=o;
						last_locker->locker_object->in_room=get_room_index(last_locker->roomvnum);				
						// if the locker rental has expired, open it, and make it uncloseable.
						if(last_locker->paid_until<current_time){
							REMOVE_BIT(o->value[1],CONT_CLOSEABLE);
							REMOVE_BIT(o->value[1],CONT_CLOSED);
							REMOVE_BIT(o->value[1],CONT_LOCKED);
						}
					}
				}
			}else {// unexpected file format
				bugf("Unexpected fileformat in '%s' - found '%s' "
					"expecting '#LOCKER or #O'", LOCKERS_FILE, readword);
				write_shutdown_file(NULL);
				do_abort();
				return;
			}
		}
	}
	fclose( fp );

	// loop thru, allocating new locker numbers if required
	if(zero_numbered_lockers){
		logf("%d zero numbered locker%s, allocating new numbers.",
			zero_numbered_lockers,
			zero_numbered_lockers==1?"":"s");

		for(int h=0; h<LOCKER_HASH_KEY; h++){
			for(locker=hash[h]; locker; locker=locker->next_locker){
				if(!locker->number){
					locker->number=get_next_free_locker_number(locker->roomvnum);
					if(locker->locker_object){
						replace_string(locker->locker_object->name, FORMATF("locker%d", locker->number));
						replace_string(locker->locker_object->short_descr, FORMATF("locker %d", locker->number));
						replace_string(locker->locker_object->description, FORMATF("locker %d is here", locker->number));
					}
				}
			}
		}
	}

	logf("Finished reading lockers from " LOCKERS_FILE ". (read in %d lockers, %d objects)",
		lockers_total_count, lockers_object_count);
}
/**************************************************************************/
// return the next available locker number in the room
int lockers_object::get_next_free_locker_number(int roomvnum)
{
	int looking_for;
	for(looking_for=1; ;looking_for++){
		if(!find_locker_data("", roomvnum, looking_for)){
			return looking_for;
		}
	}
	return -1; // shouldn't be able to get here
}

/**************************************************************************/
void lockers_object::info(char_data *ch, int locker_number)
{
	ch->titlebar("LOCKERS");
	int lock_in_use=lockers->count_used_lockers_in_room(ch->in_room_vnum());
	ch->printlnf("This room has %d locker%s, of which %d %s in use.",
		ch->in_room->lockers->quantity,
		ch->in_room->lockers->quantity==1?"":"s",
		lock_in_use,
		lock_in_use==1?"is":"are");

	ch->printlnf("It costs %d gold to start renting a locker (1 month rent inclusive).",
		ch->in_room->lockers->initial_rent);
	ch->printlnf("It costs %d gold per year for ongoing rental.",
		ch->in_room->lockers->ongoing_rent);
	ch->printlnf("New lockers can store up to %d lbs total.",
		ch->in_room->lockers->weight);
	ch->printlnf("The largest individual item which will fit in these lockers is %d lbs.",
		ch->in_room->lockers->capacity);

	locker_data *l;
	l=find_locker(ch, false, IS_IMMORTAL(ch)?locker_number:0);
	if(l){
		if(is_exact_name(ch->name, l->owner)){
			ch->titlebarf("YOUR LOCKER (#%d)", l->number);
		}else{
			ch->titlebarf("LOCKER %d", l->number);
		}
		if(l->paid_until<current_time){
			ch->println("YOUR LOCKER RENT HAS LAPSED, THEREFORE YOU CAN'T CLOSE IT!");
			ch->printlnf("Your locker rent expired %s (%.24s)", 
				get_shorticdate_from_time(l->paid_until, "", 0),
				ctime(&l->paid_until));
		}else{
			ch->printlnf("Your locker rent is paid until %s (%.24s)", 
				get_shorticdate_from_time(l->paid_until, "", 0),
				ctime(&l->paid_until));
		}
		if(l->locker_object){
			ch->printlnf("Your locker is currently %s", 
				!IS_SET(l->locker_object->value[1], CONT_CLOSED)?"open.":
					IS_SET(l->locker_object->value[1], CONT_LOCKED)?"locked.":"closed.");
		}
		if(IS_NULLSTR(ltrim_string(rtrim_string(l->access)))){
			ch->println("Your lockers access settings are currently unset.");
		}else{
			ch->println("Your lockers access settings are currently:");
			ch->printlnf("  %s", l->access);
		}
	}
	ch->titlebar("");
}
/**************************************************************************/
void lockers_object::look(char_data *ch)
{
	ch->titlebar("ROOM LOCKERS");

	int lock_in_use=lockers->count_used_lockers_in_room(ch->in_room_vnum());
	ch->printlnf("This room has %d locker%s, of which %d %s in use.",
		ch->in_room->lockers->quantity,
		ch->in_room->lockers->quantity==1?"":"s",
		lock_in_use,
		lock_in_use==1?"is":"are");

	locker_data *locker;

	int i;
	int max=0;
	int roomvnum=ch->in_room_vnum();
	// find the maximum locker number
	for(locker=get_first(ch->in_room_vnum()); locker; locker=locker->next_locker){
		if(locker->roomvnum==roomvnum){
			max=UMAX(max, locker->number);
		}	
	}
	
	for(i=0; i<=max; i++){
		for(locker=get_first(roomvnum); locker; locker=locker->next_locker){
			if(locker->roomvnum!=roomvnum){
				continue;
			}
			if(locker->number!=i){
				continue;
			}

			if(has_access(ch, locker)){ // highlight lockers player has access to
				ch->print("`W");
			}
			if(locker->locker_object){
				ch->printf("[%3d]`x %s %s`x", 
					locker->number,
					IS_NULLSTR(locker->label)?"`S???`x":locker->label,
					!IS_SET(locker->locker_object->value[1],CONT_CLOSEABLE)?"<jammed open - expired lease>":
						!IS_SET(locker->locker_object->value[1], CONT_CLOSED)?"<open>":"<closed>");
			}else{
				ch->printlnf("Error: locker %d doesn't have a storage object!", locker->number);
			}

			if(IS_ADMIN(ch)){
				ch->printf("`S %s%s",
					locker->locker_object && IS_SET(locker->locker_object->value[1], CONT_LOCKED)?"<locked>":"<UNLOCKED>",
					locker->locker_object && IS_SET(locker->locker_object->value[1], CONT_PICKPROOF)?"<pickproof>":"");
				ch->printlnf(" [%s] {%s}`x", locker->owner, locker->access);
			}else{
				ch->println("");
			}
			break;
		}
	}
	if(IS_ADMIN(ch)){
		ch->println("`Sadminnote: [] = owner, {} = access`x");
	}

	ch->titlebar("");
}

/**************************************************************************/
void lockers_object::roomlist(char_data *ch, char *nametext)
{
	char name[MIL];
	one_argument(nametext, name);
	ch->titlebar("LOCKERS ROOMLIST");

	bool namesearch=false;
	if(!IS_NULLSTR(name)){
		namesearch=true;
	}

	int minvnum=1;
	int maxvnum=top_vnum_room;

	
	// now list all rooms with lockers in them
	int i;
	locker_data *locker;
	ROOM_INDEX_DATA *r;
	int h=0;
	bool found;
	for(i=minvnum; i<=maxvnum; i++){
		r=get_room_index(i);
		if(r && r->lockers){

			if(namesearch){
				found=false;
				for(locker=get_first(i); locker; locker=locker->next_locker){
					if(locker->roomvnum!=i){
						continue;
					}
					if(!str_cmp(locker->owner, name) || is_exact_name(name, locker->access)){
						found=true;
						break;
					}
				}
				if(!found){
					continue;
				}
			}

			h++;
			ch->printlnf("%3d) [%s%s`x] (%2d/%2d) %s (%s%s`x)",
				h, 
				colour_table[(i%14)+1].code,
				mxp_create_tagf(ch, "rmvnum", "%5d", i),				
				lockers->count_used_lockers_in_room(i),
				r->lockers->quantity,
				r->name, 
				colour_table[(r->area->vnum%14)+1].code,
				r->area->name);
		}		
	}
	if(h==0){
		if(namesearch){
			ch->printlnf("There are no lockers in any rooms which '%s' has access to (note: clan access not checked).", name);
		}else{
			ch->println("There are no rooms in the game with lockers configured.");
		}
	}

	ch->titlebar("");
}

/**************************************************************************/
bool lockers_object::has_access(char_data *ch, locker_data *l)
{
	if(!str_cmp(l->owner, ch->name) || is_exact_name(ch->name, l->access)){
		lockers_referenced=true;
		l->last_accessed=current_time;
		return true;
	}
	// check clan access
	if(ch->clan){		
		if(is_exact_name(FORMATF("clan=%s", ch->clan->notename()), l->access)){
			lockers_referenced=true;
			l->last_accessed=current_time;
			return true;
		}
	}
	return false; // they didn't have access
}
/**************************************************************************/
// admin deleting a particular locker
bool lockers_object::has_access(char_data *ch, OBJ_DATA *locker_object)
{
	if(IS_IMMORTAL(ch)){
		return true; // imms always have access to lock/unlock all lockers
	}
	if(!locker_data_of_last_found_locker_object || 
		locker_data_of_last_found_locker_object->locker_object!=locker_object)
	{
		// we dont bother searching for it, if we dont have the last
		// referenced object cached, we forget about it.
		return false;
	}
	if(IS_NPC(ch)){
		return false;
	}
	if(IS_SET(ch->dyn,DYN_IS_BEING_ORDERED)){
		// no access while being ordered
		return false;
	}
	return has_access(ch, locker_data_of_last_found_locker_object);
}
/**************************************************************************/
// remove access from a locker
void lockers_object::remove_access(char_data *ch, char *argument, int locker_number)
{
	locker_data *l;
	// find the locker
	l=find_locker(ch, true, locker_number);
	if(!l){
		return;
	}

	if(!IS_ADMIN(ch) && str_cmp(ch->name, l->owner)){
		ch->printlnf("You can't change the access settings of locker %d.",
			l->number);
		return;
	}

	char arg[MIL];
	one_argument(argument, arg);
	smash_tilde(arg);

	if(has_colour(arg)){
		ch->println("You can't use colour codes here.");
		return;
	}

	if(!is_exact_name(arg, l->access)){
		ch->printlnf("'%s' doesn't have access to locker %d.", arg, l->number);
		ch->printlnf("Access is restricted to: %s", ltrim_string(rtrim_string(l->access)));
		return;
	}

	// remove them from the locker's access list
	l->access=string_replace_all(l->access, FORMATF(" %s ", arg), " ");
	l->access=string_replace_all(l->access, "  ", " ");

	ch->printlnf("'%s' no longer has access to locker %d.", arg, l->number);
	ch->printlnf("Access is now restricted to:%s", l->access);
}
/**************************************************************************/
void lockers_object::changeowner(char_data *ch, char *argument, int locker_number)
{
	locker_data *l;
	// find the locker
	l=find_locker(ch, true, locker_number);
	if(!l){
		return;
	}
	ch->printlnf("locker %d owner changed from '%s' to '%s'",
		l->number, l->owner, argument);
	replace_string(l->owner, argument);

	lockers_referenced=true;	
	l->last_accessed=current_time;
}
/**************************************************************************/
void lockers_object::changelabel(char_data *ch, char *argument, int locker_number)
{
	locker_data *l;
	// find the locker
	l=find_locker(ch, true, locker_number);
	if(!l){
		return;
	}
	ch->printlnf("locker %d label changed from '%s' to '%s'",
		l->number, l->label, argument);
	replace_string(l->label, argument);	

	lockers_referenced=true;	
	l->last_accessed=current_time;
}
/**************************************************************************/
void lockers_object::tempopen(char_data *ch, char *argument, int locker_number)
{
	locker_data *l;
	// find the locker
	l=find_locker(ch, true, locker_number);
	if(!l){
		return;
	}

	if(!l->locker_object){
		ch->printlnf("For some reason, locker %d (%s) doesn't have a locker object!", 
			l->number, l->owner);
		return;
	}
	ch->printlnf("LOCKER %d (%s) TEMPOPEN START:", l->number, l->owner);
	
	// backup the lockers open status
	int locker_flags=l->locker_object->value[1];

	// open the locker the bit way
	REMOVE_BIT(l->locker_object->value[1],CONT_CLOSED);
	REMOVE_BIT(l->locker_object->value[1],CONT_LOCKED);

	// run the command
	interpret(ch, argument);

	// restore the lockers open status
	l->locker_object->value[1]=locker_flags;
	ch->printlnf("LOCKER %d (%s) TEMPOPEN END:", l->number, l->owner);
	
	lockers_referenced=true;	
	l->last_accessed=current_time;
}

/**************************************************************************/
// grant access to a locker
void lockers_object::grant_access(char_data *ch, char *argument, int locker_number)
{
	locker_data *l;
	// find the locker
	l=find_locker(ch, true, locker_number);
	if(!l){
		return;
	}

	if(!IS_ADMIN(ch) && str_cmp(ch->name, l->owner)){
		ch->printlnf("You can't change the access settings of locker %d.",
			l->number);
		return;
	}

	char arg[MIL];
	one_argument(argument, arg);
	smash_tilde(arg);

	if(has_colour(arg)){
		ch->println("You can't use colour codes here.");
		return;
	}

	if(is_exact_name(arg, l->access)){
		ch->printlnf("'%s' already has access to locker %d.", arg, l->number);
		ch->printlnf("Access is restricted to:%s", l->access);
		return;
	}

	// add them to the locker's access list
	if(str_len(l->access)> MIL){
		ch->printlnf("Too many names listed with access to locker %d.", l->number);
		ch->printlnf("Access is restricted to:%s", l->access);
		return;
	}

	if(IS_NULLSTR(l->access)){
		replace_string(l->access, FORMATF(" %s ", arg));
	}else{
		char *f=FORMATF("%s%s ", l->access, arg);
		replace_string(l->access, f);
	}
	
	ch->printlnf("access granted to '%s' for locker %d.", arg, l->number);
	ch->printlnf("Access is now restricted to: %s", l->access);
}

/**************************************************************************/
// admin deleting a particular locker
void lockers_object::admin_delete(char_data *ch, int locker_number)
{
	locker_data *l;
	// find the locker
	l=find_locker(ch, true, locker_number);
	if(!l){
		return;
	}

	// we have the locker object, move all objects out of it onto the floor
	ch->printlnf("Locker %d data found...", l->number);
	obj_data *obj=l->locker_object;
	obj_data *obj_next_content;
	if(obj){
		ch->println("Locker container  object exists, moving objects to floor...");
		for( obj=obj->contains; obj; obj = obj_next_content ){
			obj_next_content=obj->next_content;
			obj_from_obj(obj);
			obj_to_room(obj, ch->in_room);
			ch->printlnf("  %s`x", format_obj_to_char(obj, ch, true));
		}
		ch->println("Deallocating locker container object...");
		l->locker_object->in_room=NULL;
		extract_obj(l->locker_object);
		l->locker_object=NULL;
	}	

	ch->printlnf("Deallocating locker %d data...", l->number);
	
	// find the locker data's position in the list
	locker_data *prev=NULL;
	locker_data *node;
	for(node=get_first(l->roomvnum); node; node=node->next_locker){
		if(l!=node){
			prev=node;
			continue;
		}
		// we have found the locker, remove it from the listing
		if(prev){
			prev->next_locker=l->next_locker;
		}else{ // first in the list
			hash[l->roomvnum%LOCKER_HASH_KEY]=l->next_locker;

		}
		// deallocate locker data
		ch->printlnf("Locker %d successfully removed.", l->number);
		delete l;
		return;
	}

	// shouldn't get here
	ch->printlnf("Couldn't find locker %d to remove!?!", l->number);
	return;
}

/**************************************************************************/
void lockers_object::postletter(char_data *ch, int locker_number)
{
	obj_data *letter;

	letter= get_eq_char(ch,WEAR_HOLD);
    if( !letter || letter->item_type != ITEM_PARCHMENT )
    {
        ch->println( "You must be holding the letter you want to post in your hands to post it." );
        return;
    }

	locker_data *l;
	// find the locker
	l=find_locker(ch, true, locker_number);
	if(!l){
		return;
	}

	if(!l->locker_object){
		ch->printlnf("For some reason locker %d doesn't have a locker object, "
			"please report this to the admin.",
			l->number);
		return;
	}
	// note: by design there is no limit on the number of letters 
	// you can put in a locker.  This way a room can be setup as postboxes,
	// where the weight and capacity of the lockers are 1... not fitting any
	// decent sized object.
	obj_from_char(letter);
	obj_to_obj(letter, l->locker_object);

	act("You post a letter into $p.", ch, l->locker_object, NULL, TO_CHAR );
	act("$n posts a letter into $p.",ch,l->locker_object,NULL,TO_ROOM);
}
/**************************************************************************/
void locker_notes(char_data *ch)
{
	ch->titlebar("LOCKER NOTES");
	ch->println("syntax: buy locker           - purchase the first available locker in the room.");
	ch->println("syntax: buy locker5          - attempt to purchase locker number 5 in the room.");
	ch->println("syntax: look lockers         - list all lockers in the room.");
	ch->println("syntax: look in locker       - look inside your locker.");
	ch->println("syntax: look in locker2      - look inside locker2.");
	ch->println("syntax: lock loc             - close and lock your locker.");
	ch->println("syntax: lock locker23        - close and lock locker 23 (if you have lock access).");
	ch->println("syntax: open loc             - unlock and open your locker.");
	ch->println("syntax: open locker8         - unlock (if you have lock access) and open locker 8.");
	ch->println("syntax: put bread in locker5 - put the bread in locker 5 (assuming it is open).");
	ch->titlebar("");
}
/**************************************************************************/
void do_lockers(char_data *ch, char *argument)
{	
	char arg[MIL];
	int locker_number=0;

	if(GAMESETTING3(GAMESET3_LOCKERS_DISABLED)){
		ch->println("The locker system is currently disabled.");
		return;
	}

	smash_tilde(argument);

	// no ordering to access lockers
	if(IS_SET(ch->dyn,DYN_IS_BEING_ORDERED)){
		if(ch->master){
			ch->master->println( "Not going to happen.");
		}
		return;
	}
	

	if(IS_NULLSTR(argument)){
		ch->titlebar("LOCKER COMMANDS");
		ch->println("syntax: locker notes - display notes on using lockers");
		ch->println("syntax: locker # postletter - post the letter you are holding into locker #");
		ch->println("syntax: locker info - show info on lockers in the room, and your rent status");
		ch->println("syntax: locker payrent - pay an IC years worth of rent");
		ch->println("");
		ch->println("== access commands for those renting a locker:");
		ch->println("syntax: locker grantaccess <name> - give <name> access to locker");
		ch->println("syntax: locker grantaccess clan=<clan_notename>");
		ch->println("              - give all members in clan <clan_notename> access to locker");
		ch->println("syntax: locker removeaccess <name> - remove access to locker from <name>");
		ch->println("");
		if(IS_ADMIN(ch)){
			ch->println("== admin only locker commands:");
			ch->println("syntax: locker roomlist [name] - list rooms with lockers in them [for name]"); 
			ch->println("syntax: locker changeowner - changes the owner of a given locker");
			ch->println("syntax: locker changelabel <label text> - change a label on locker");//
			ch->println("syntax: locker # delete - delete a locker, any contents are moved to ground");
			ch->println("syntax: locker # tempopen <command> - flags a locker open for <command>");
			ch->println("                                 e.g. locker 5 tempopen look in locker5");
			ch->println("");
		}
		ch->println("note: # = locker number, if you omit this number your locker will be used");
		ch->titlebar("");
		return;
	}

	argument=one_argument(argument, arg);
	// support the optional locker number
	if(is_number(arg)){
		locker_number=atoi(arg);
		if(locker_number<1){
			ch->println("The locker numbering starts at 1.");
			return;
		}
		// shift arguments left one
		argument=one_argument(argument, arg);
	}


	// admin room list command, can be used in any room
	if(IS_ADMIN(ch)){
		if(!str_prefix(arg, "roomlist")){
			lockers->roomlist(ch, argument);
			return;
		}
	}

	if(!str_prefix(arg, "notes")){
		locker_notes(ch);
		return;
	}

	// all commands below here must be used in a room with lockers
	if(!lockers->room_has_lockers(ch->in_room)){
		ch->println("This room doesn't have any lockers.");
		return;
	}
	if(IS_NPC(ch)){
		ch->println("NPC's can't access lockers.");
		return;
	}

	if(!str_prefix(arg, "info")){
		lockers->info(ch, locker_number);
		return;
	}

	if(!str_prefix(arg, "look")){
		lockers->look(ch);
		return;
	}

	if(!str_cmp(arg, "grantaccess")){
		if(IS_NULLSTR(argument)){
			ch->println("You need to specify a name after the grantaccess command.");
			return;
		}
		lockers->grant_access(ch, argument, locker_number);
		return;
	}	
	if(!str_cmp(arg, "removeaccess")){
		if(IS_NULLSTR(argument)){
			ch->println("You need to specify a name after the removeaccess command.");
			return;
		}
		lockers->remove_access(ch, argument, locker_number);
		return;
	}		

	if(!str_cmp(arg, "postletter")){
		if(locker_number==0){
			ch->println("specify the locker number you want to post into... e.g. 'locker 5 postletter'");
			return;
		}
		lockers->postletter(ch, locker_number);
		return;
	}	

	if(!str_cmp(arg, "startrent")){
		lockers->start_rent(ch, locker_number);
		return;
	}	

	if(!str_cmp(arg, "payrent")){
		if(!IS_IMMORTAL(ch) && locker_number!=0){
			ch->println("You can not pay the rent on someone elses locker... just type 'locker payrent' to pay your rent.");
			return;
		}
		lockers->pay_rent(ch, locker_number);
		return;
	}	


	// admin only commands
	if(IS_ADMIN(ch)){
		if(!str_cmp(arg, "delete")){
			lockers->admin_delete(ch, locker_number);
			return;
		}
		if(!str_cmp(arg, "changeowner")){
			lockers->changeowner(ch, argument, locker_number);
			return;
		}

		if(!str_cmp(arg, "changelabel")){
			lockers->changelabel(ch, argument, locker_number);
			return;
		}

		if(!str_cmp(arg, "tempopen")){
			lockers->tempopen(ch, argument, locker_number);
			return;
		}
	}


	ch->printlnf("Unrecognised lockers option '%s'", arg);
	do_lockers(ch,"");

}
/**************************************************************************/
/**************************************************************************/