roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * swap.cpp
 *	 Code to swap rooms/monsters/objects between areas
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
#include "mud.h"
#include "commands.h"
#include "dm.h"
#include "property.h"
#include "ships.h"
#include "tokenizer.h"
#include "effects.h"
#include <signal.h>
#include <dirent.h>
#include <iomanip>

const char* sepType = " ";
#define SWAP_QUEUE_LIMIT	100

//*********************************************************************
//							Swap
//*********************************************************************
// The swap process works like this.
//		1. Player requests to swap a mud object with another.
//			a. Checks to make sure the mud object can be swapped.
//		2. If an area is specified, fork and find the next available slot in that area.
//		3. Checks to make sure target mud object can be swapped.
//		4. Fork to find all affected mud objects.
//			a. The fork sends the info to the main mud as soon as it finds it.
//			b. On the main mud, any mud object that is saved or modified from start to
//             finish of this process will be inspected and put in the queue if they're
//             interesting.
//		5. As the main mud receives info from the fork, it puts it into the queue.
//			a. Mud objects received will be loaded into memory immediately to prevent
//			   many mud objects loading at once at the end of the process.
//		6. Once the fork finishes its search, it loops through everything found as well
//		   as everything already in memory:
//			a. Areas (zones)
//			b. StartLocs
//			c. Ships
//			d. CatRefInfo
//		   Everything is updated and saved to disk.
//		   Property is not examined for room swap because all rooms belong to restricted ranges.
//
//	If another swap is attempted, it will enter a queue. Once the first process is
//	complete, the next one will execute.
//

//*********************************************************************
//							swapName
//*********************************************************************

bstring swapName(SwapType type) {
	if(type == SwapRoom)
		return("room");
	if(type == SwapObject)
		return("object");
	if(type == SwapMonster)
		return("monster");
	return("");
}

//*********************************************************************
//							swap
//*********************************************************************

void swap(Player* player, cmd* cmnd, SwapType type) {
	bstring str = getFullstrText(cmnd->fullstr, 1);
	bstring name="";
	bstring cmd = " *swap";
	char padding[50];
	int queueSize = gConfig->swapQueueSize();

	Swap s;
	s.player = player->getName();
	s.type = type;

	bstring swapType = "";
	if(type == SwapRoom) {
		swapType = "room";
		cmd = "*rswap";
	} else if(type == SwapObject) {
		swapType = "object";
		cmd = "*oswap";
	} else if(type == SwapMonster) {
		swapType = "monster";
		cmd = "*mswap";
	}
	swapType = swapName(type);

	if(str == "") {
		bstring prefix = "  ^e";
		prefix += cmd;

		if(type == SwapObject) {
			prefix += " [object area.id]";
		} else if(type == SwapMonster) {
			prefix += " [monster area.id]";
		}

		player->print("Commands:\n");
		if(type == SwapNone) {
			player->printColor("  ^e*rswap [area]      ^x- swap rooms\n");
			player->printColor("  ^e*oswap [area]      ^x- swap objects\n");
			player->printColor("  ^e*mswap [area]      ^x- swap monsters\n");
		} else {
			player->printColor("%s [area]      ^x- move to next open %s in [area].\n", prefix.c_str(), swapType.c_str());
			player->printColor("%s [area.id]   ^x- move to specific %s (will swap if %s exists).\n", prefix.c_str(), swapType.c_str(), swapType.c_str());
			player->printColor("%s [id]        ^x- move to specific %s (will swap if %s exists).\n", prefix.c_str(), swapType.c_str(), swapType.c_str());
			player->printColor("  ^e%s [^x-range source target loop^e]\n", cmd.c_str());
			player->printColor("         ^e[^x-range source.low:high target^e]\n");
			memset(padding, ' ', sizeof(padding));
			padding[16 + prefix.getLength() - 2] = '\0';
			player->print("%s- swap a range of %ss.\n", padding, swapType.c_str());
		}
		player->printColor("%s [^x-info^e]     ^x- print swap info.\n", prefix.c_str());
		player->printColor("%s [^x-cancel #^e] ^x- cancel a swap job, # is 1 by default.\n", prefix.c_str());
		player->printColor("%s [^x-abort^e]    ^x- abort all swap jobs (empty queue).\n", prefix.c_str());
		return;
	} else if(str == "-info") {
		gConfig->swapInfo(player);
		gServer->swapInfo(player);
		return;
	} else if(str.left(6) == "-range" && type != SwapNone) {
		bstring o = getFullstrText(cmnd->fullstr, 2);
		getCatRef(o, &s.origin, 0);
		getCatRef(getFullstrText(cmnd->fullstr, 3), &s.target, 0);

		player->printColor("^YRS: ^xLooking to swap %s ranges starting at %s to %s.\n", swapType.c_str(),
			s.origin.rstr().c_str(), s.target.id == -1 ? s.target.area.c_str() : s.target.rstr().c_str());

		int loop=0;
		bstring::size_type pos = o.Find(":");
		if(pos != bstring::npos) {
			// *rswap -range misc.100:120 test.1
			loop = atoi(o.substr(pos+1).c_str()) - s.origin.id;
		} else {
			// *rswap -range misc.100 test.1 20
			loop = atoi(getFullstrText(cmnd->fullstr, 4).c_str());
		}
		loop = MAX(0, loop);

		if(!loop) {
			player->printColor("^YRS: ^RError: ^xNo upper bound found: which %ss would you like to loop over?\n", swapType.c_str());
			return;
		}

		if(!gConfig->swapChecks(player, s))
			return;

		loop++;
		player->printColor("^YRS: ^xAdding %d job%s to the queue... ", loop, loop != 1 ? "s" : "");

		if(loop + queueSize > SWAP_QUEUE_LIMIT) {
			player->printColor("\n^YRS: ^RError: ^xThe queue has a limit of %d; you may only add %d more.\n",
				SWAP_QUEUE_LIMIT, SWAP_QUEUE_LIMIT - queueSize);
			return;
		}

		loop = s.origin.id + loop;
		for(; s.origin.id < loop; s.origin.id++) {
			gConfig->swapAddQueue(s);
			if(s.target.id != -1)
				s.target.id++;
		}
		player->print("done.\n");

		// begin if nothing has started!
		if(!gConfig->isSwapping())
			gConfig->swapNextInQueue();
		return;
	}

	if(gConfig->isSwapping()) {
		name = gServer->swapName();
		if(str == "-abort") {
			broadcast(isStaff, "^YRS: Swap queue has been cleared.");
			gConfig->swapAbort();
			return;
		} if(str.left(7) == "-cancel") {
			int id = atoi(getFullstrText(cmnd->fullstr, 2).c_str());
			if(id <= 1)
				id = 1;
			player->printColor("^YRS: ^eSwap canceled.\n");
			if(name != player->getName() && name != "Someone") {
				Player* p = gServer->findPlayer(name.c_str());
				if(p)
					player->printColor("^RRS: ^eSwap canceled by %s.\n", player->getCName());
			}
			if(id==1)
				gServer->endSwap();
			gConfig->endSwap(id);
			return;
		}
	}

	if(str.getAt(0) == '-') {
		player->printColor("^YRS: ^RError: ^xFlag not understood.\n");
		return;
	}

	if(type == SwapRoom) {
		if(!needUniqueRoom(player))
			return;
		if(!player->checkBuilder(player->getUniqueRoomParent())) {
			player->printColor("^YRS: ^RError: ^xRoom number not inside any of your alotted ranges.\n");
			return;
		}

		s.origin = player->getConstUniqueRoomParent()->info;
	} else {
		getCatRef(str, &s.origin, player);
		str = getFullstrText(str, 1);
	}

	if(!s.origin.id) {
		player->printColor("^YRS: ^RError: ^xUnable to locate something to swap.\n");
		return;
	}

	getCatRef(str, &s.target, player);

	// these checks done here for convenience of user,
	// not to ensure data validity: this is why the checks
	// are run again in Server::swap
	if(!gConfig->swapChecks(player, s))
		return;

	if(type == SwapRoom) {
		if(!s.target.isArea(s.origin.area) && getFullstrText(cmnd->fullstr, 2) != "confirm") {
			for(Exit* ext : player->getConstUniqueRoomParent()->exits) {
				if(ext->flagIsSet(X_LOCKABLE) && ext->getKey()) {
					player->printColor("^YRS: ^RError: ^xthis room contains a lockable exit and is being moved to a different area.\n");
					player->printColor("   To continue, type ^W*rswap %s confirm^x. Make sure all keys work correctly.\n", s.target.area.c_str());
					return;
				}
			}
		}

		if(player->getConstUniqueRoomParent()->flagIsSet(R_SHOP))
			player->printColor("^YRS: ^GThis room is a shop - don't forget to swap the storage room: %s.\n",
				shopStorageRoom(player->getConstUniqueRoomParent()).rstr().c_str());
		else if(player->getConstUniqueRoomParent()->getTrapExit().id)
			player->printColor("^YRS: ^GThis room has a trap exit set: %s.\n",
				player->getConstUniqueRoomParent()->getTrapExit().rstr().c_str());
	}

	if(gConfig->isSwapping()) {
		player->printColor("^YRS: ^x%s is currently running the swap utility.\n", name.c_str());
		if(!gConfig->inSwapQueue(s.origin, s.type)) {
			if(queueSize >= SWAP_QUEUE_LIMIT) {
				player->printColor("^YRS: ^RError: ^xThe queue has a limit of %d; you cannot add any more.\n",
					SWAP_QUEUE_LIMIT);
				return;
			}
			player->printColor("^YRS: Your request has been added to the queue.\n");
			gConfig->swapAddQueue(s);
		} else
			player->printColor("^YRS: ^RError: ^xThat %s is already in the queue.\n", swapType.c_str());
	} else
		gServer->swap(s);

}

int dmSwap(Player* player, cmd* cmnd) {
	swap(player, cmnd, SwapNone);
	return(0);
}
int dmRoomSwap(Player* player, cmd* cmnd) {
	swap(player, cmnd, SwapRoom);
	return(0);
}
int dmObjSwap(Player* player, cmd* cmnd) {
	swap(player, cmnd, SwapObject);
	return(0);
}
int dmMobSwap(Player* player, cmd* cmnd) {
	swap(player, cmnd, SwapMonster);
	return(0);
}


//*********************************************************************
//							swap
//*********************************************************************

bool Server::swap(Swap s) {
	Player *player = gServer->findPlayer(s.player.c_str());
	gConfig->setMovingRoom(s.origin, s.target);
	bstring output;

	// check validity here
	if(!gConfig->swapChecks(player, s)) {
		gConfig->endSwap();
		return(false);
	}

	// find the room ourselves!
	if(s.target.id == -1) {
		// only one forked process at a time
		if(gServer->swapName() != "Someone")
			return(false);


		Async async;
		if(async.branch(player, CHILD_SWAP_FIND) == AsyncExternal) {
			output = findNextEmpty("room", s.target.area).rstr();
			printf("%s", output.c_str());
			exit(0);
		} else {
			if(player)
				player->printColor("^YRS: ^eBeginning search for next empty %s in area \"%s\".\n", ::swapName(s.type).c_str(), s.target.area.c_str());
		}

	} else
		gConfig->finishSwap(s.player);

	return(true);
}

//*********************************************************************
//							simpleChildRead
//*********************************************************************

bstring Server::simpleChildRead(Server::childProcess &child) {
	char tmpBuf[4096];
	bstring toProcess;
	int n;
	for(;;) {
		// read in all of the data
		memset(tmpBuf, '\0', sizeof(tmpBuf));
		n = read(child.fd, tmpBuf, sizeof(tmpBuf)-1);
		if(n <= 0)
			break;
		toProcess += tmpBuf;
	}
	return(toProcess.trim());
}

//*********************************************************************
//							findNextEmpty
//*********************************************************************
// gets output from findNextEmpty

void Config::findNextEmpty(Server::childProcess &child, bool onReap) {
	if(!isSwapping())
		return;

	Player* player = gServer->findPlayer(child.extra.c_str());

	// a target has not yet been found
	if(currentSwap.target.id == -1) {
		bstring toProcess = gServer->simpleChildRead(child);

		if(toProcess != "") {
			getCatRef(toProcess, &currentSwap.target, 0);
			if(player)
				player->printColor("^YRS: ^eRoom found: %s.\n", currentSwap.target.rstr().c_str());
		}
	}

	if(onReap) {
		if(currentSwap.target.id == -1) {
			if(player)
				player->printColor("^YRS: No empty rooms were found!\n");
			endSwap();
		} else {
			UniqueRoom* uRoom=0;

			// did we actually get a room where we were expecting none?
			if(loadRoom(currentSwap.target, &uRoom)) {
				if(roomSearchFailure) {
					// we're only allowed 1 failure per go
					if(player)
						player->printColor("^RRS: ^xMove search failed; aborting this job.\n");
					endSwap();
				} else {
					roomSearchFailure = true;

					if(player) {
						player->printColor("^RRS: ^xMove search failed; this room is not empty.\n");
						player->printColor("^RRS: ^xAttempting room search again.\n");
					}
					currentSwap.target.id = -1;
					uRoom->saveToFile(0);
					gServer->swap(currentSwap);
				}
				return;
			}

			finishSwap(child.extra);
		}
	}
}

//*********************************************************************
//							finishSwap
//*********************************************************************

void Config::finishSwap(bstring mover) {
	Player* player = gServer->findPlayer(mover.c_str());
	bool online=true;

	if(!player) {
		if(!loadPlayer(mover.c_str(), &player)) {
			broadcast(isStaff, "^YRS: ^RError: ^xNon-existent player %s attempting to move rooms.\n", mover.c_str());
			endSwap();
			return;
		}
		online=false;
		player->fd = -1;
	}

	// this range is restricted
	if(moveRoomRestrictedArea(currentSwap.target.area)) {
		if(online)
			player->printColor("^YRS: ^RError: ^x""%s"" is a restricted range. You cannot swap unique rooms into or out that area.\n", currentSwap.target.area.c_str());
		else
			free_crt(player);
		endSwap();
		return;
	}

	if(!player->checkBuilder(currentSwap.target)) {
		if(online)
			player->printColor("^YRS: ^RError: ^xRoom number not inside any of your alotted ranges.\n");
		else
			free_crt(player);
		endSwap();
		return;
	}

	// no moving the builder waiting room!
	if(currentSwap.origin.isArea("test") && currentSwap.origin.id == 1) {
		if(online)
			player->printColor("^YRS: ^RError: ^xSorry, you cannot swap this room (%s) (Builder Waiting Room).\n", currentSwap.origin.str().c_str());
		else
			free_crt(player);
		endSwap();
		return;
	}
	if(currentSwap.target.isArea("test") && currentSwap.target.id == 1) {
		if(online)
			player->printColor("^YRS: ^RError: ^xSorry, you cannot swap that room (%s) (Builder Waiting Room).\n", currentSwap.target.str().c_str());
		else
			free_crt(player);
		endSwap();
		return;
	}

	// no moving special rooms out of their areas!
	if(!currentSwap.target.isArea(currentSwap.origin.area)) {
		// endSwap() is handled inside these functions
		if(!checkSpecialArea(currentSwap.origin, currentSwap.target, &CatRefInfo::recall, player, online, "Recall"))
			return;
		if(!checkSpecialArea(currentSwap.origin, currentSwap.target, &CatRefInfo::limbo, player, online, "Limbo"))
			return;
		if(!checkSpecialArea(currentSwap.target, currentSwap.origin, &CatRefInfo::recall, player, online, "Recall"))
			return;
		if(!checkSpecialArea(currentSwap.target, currentSwap.origin, &CatRefInfo::limbo, player, online, "Limbo"))
			return;
	}

	player->printColor("^YRS: ^eSwapping %s %s with %s %s.\n",
		swapName(currentSwap.type).c_str(), currentSwap.origin.str().c_str(),
		swapName(currentSwap.type).c_str(), currentSwap.target.str().c_str());
	gServer->finishSwap(player, online, currentSwap.origin, currentSwap.target);
}

void Server::finishSwap(Player* player, bool online, CatRef origin, CatRef target) {
	// only one forked process at a time
	if(gServer->swapName() != "Someone") {
		if(!online)
			free_crt(player);
		return;
	}

	log_immort(true, player, "%s has begun swapping %s with %s.\n", player->getCName(), origin.str().c_str(), target.str().c_str());


	Async async;
	if(async.branch(player, CHILD_SWAP_FINISH) == AsyncExternal) {
		gConfig->offlineSwap();
		exit(0);
	} else {
		if(online) {
			player->printColor("^YRS: ^eBeginning offline search sequence.\n");
			player->printColor("^YRS: ^eThis may take several minutes.\n");
		} else
			free_crt(player);
		gConfig->swapLog((bstring)"r" + origin.rstr(), false);
		gConfig->swapLog((bstring)"r" + target.rstr(), false);
	}
}

//*********************************************************************
//							offlineSwap
//*********************************************************************
// the offline search function

void Config::offlineSwap() {
	std::list<Area*>::iterator aIt;
	std::map<bstring, AreaRoom*>::iterator rIt;
	struct	dirent *dirp=0, *dirq=0;
	DIR		*dir=0, *subdir=0;
	bstring filename = "";
	bstring output = "";

	UniqueRoom* uRoom=0;
	AreaRoom* aRoom=0;
	Player* player=0;
	Monster* monster=0;
	// id = -1 tells the loadFromFile functions to rely on the monster/room
	CatRef placeholder;
	placeholder.id = -1;

	// get a list of all players that need updating
	if((dir = opendir(Path::Player)) != NULL) {

		while((dirp = readdir(dir)) != NULL) {
			if(dirp->d_name[0] == '.')
				continue;
			if(!isupper(dirp->d_name[0]))
				continue;

			dirp->d_name[strlen(dirp->d_name)-4] = 0;

			if(!loadPlayer(dirp->d_name, &player))
				continue;

			if(player->swap(currentSwap))
				printf("p%s%s", player->getCName(), sepType);

			free_crt(player);
		}
	}

	// check player backups
	if((dir = opendir(Path::PlayerBackup)) != NULL) {

		while((dirp = readdir(dir)) != NULL) {
			if(dirp->d_name[0] == '.')
				continue;
			if(!isupper(dirp->d_name[0]))
				continue;

			dirp->d_name[strlen(dirp->d_name)-8] = 0;

			if(!loadPlayer(dirp->d_name, &player, LS_BACKUP))
				continue;

			if(player->swap(currentSwap))
				printf("b%s%s", player->getCName(), sepType);

			free_crt(player);
		}
	}

	// get a list of all unique rooms
	if((dir = opendir(Path::UniqueRoom)) != NULL) {

		while((dirp = readdir(dir)) != NULL) {
			if(dirp->d_name[0] == '.')
				continue;

			filename = Path::UniqueRoom;
			filename += dirp->d_name;
			filename += "/";

			if((subdir = opendir(filename.c_str())) != NULL) {
				while((dirq = readdir(subdir)) != NULL) {
					if(dirq->d_name[0] != 'r')
						continue;

					filename = Path::UniqueRoom;
					filename += dirp->d_name;
					filename += "/";
					filename += dirq->d_name;

					if(!loadRoomFromFile(placeholder, &uRoom, filename))
						continue;

					// we check origin and target already, so forget about it here
					if(	uRoom->info != currentSwap.origin &&
						uRoom->info != currentSwap.target &&
						uRoom->swap(currentSwap)
					) {
						output = uRoom->info.rstr();
						printf("r%s%s", output.c_str(), sepType);
					}

				}
			}
		}
	}

	// get a list of all monsters
	if((dir = opendir(Path::Monster)) != NULL) {

		while((dirp = readdir(dir)) != NULL) {
			if(dirp->d_name[0] == '.')
				continue;

			filename = Path::Monster;
			filename += dirp->d_name;
			filename += "/";

			if((subdir = opendir(filename.c_str())) != NULL) {
				while((dirq = readdir(subdir)) != NULL) {
					if(dirq->d_name[0] != 'r')
						continue;

					filename = Path::Monster;
					filename += dirp->d_name;
					filename += "/";
					filename += dirq->d_name;

					if(!loadMonsterFromFile(placeholder, &monster, filename))
						continue;

					// we check origin and target already, so forget about it here
					if(monster->swap(currentSwap)) {
						output = monster->info.rstr();
						printf("m%s%s", output.c_str(), sepType);
					}

					free_crt(monster);
				}
			}
		}
	}

	// get a list of all area rooms
	for(aIt = areas.begin(); aIt != areas.end() ; aIt++) {
		for(rIt = (*aIt)->rooms.begin(); rIt != (*aIt)->rooms.end() ; rIt++) {
			aRoom = (*rIt).second;
			if(aRoom->swap(currentSwap)) {
				output = aRoom->mapmarker.str();
				printf("a%s%s", output.c_str(), sepType);
			}
		}
	}
}

// gets output from offlineSwap
void Config::offlineSwap(Server::childProcess &child, bool onReap) {
	if(!isSwapping())
		return;
	Player* player = gServer->findPlayer(child.extra.c_str());
	bstring toProcess = gServer->simpleChildRead(child);

	if(toProcess != "") {
		UniqueRoom *uRoom=0;
		Monster *monster=0;
		CatRef cr;
		bstring input;

		charTokenizer::iterator it;
		boost::char_separator<char> sep(sepType);
		charTokenizer tok(toProcess, sep);

		// load the rooms as we go
		// last one will always be loaded in target
		for(it = tok.begin() ; it != tok.end() ; it++) {
			input = *it;

			swapLog(input);

			if(input.getAt(0) == 'r') {
				getCatRef(input.right(input.getLength()-1), &cr, 0);
				// this will put rooms in the queue
				loadRoom(cr, &uRoom);
			} else if(input.getAt(0) == 'm') {
				getCatRef(input.right(input.getLength()-1), &cr, 0);
				// this will put monsters in the queue
				if(loadMonster(cr, &monster))
					free_crt(monster);
			}
		}
	}
	if(onReap)
		swap(player, child.extra);
}

//*********************************************************************
//							swap
//*********************************************************************

void Config::swap(Player* player, bstring name) {
	std::map<bstring, rsparse>::iterator rIt;
	std::list<bstring>::iterator bIt;
	std::list<Swap>::iterator qIt;
	UniqueRoom *uOrigin=0, *uTarget=0;
	bool found=false;

	if(player)
		player->printColor("^YRS: ^eSearch complete. Beginning final sequence.\n");

	// clear this now or it interferes with saving below
	swapping = false;

	// loop through each area
	found = false;
	std::list<Area*>::iterator aIt;
	for(aIt = areas.begin() ; aIt != areas.end() ; aIt++) {
		if((*aIt)->swap(currentSwap))
			found = true;
	}
	if(found)
		saveAreas(false);


	// loop through each starting location
	found = false;
	std::map<bstring, StartLoc*>::iterator lIt;
	for(lIt = start.begin() ; lIt != start.end() ; lIt++) {
		if((*lIt).second->swap(currentSwap)) {
			if(player)
				player->printColor("^GRS: Be sure to update the starting locations file for %s.\n", (*lIt).second->getName().c_str());
			found = true;
		}
	}
	if(found)
		saveStartLocs();


	// loop through each ship
	// TODO: Dom: midnight ship file?
	found = false;
	std::list<Ship*>::iterator sIt;
	for(sIt = ships.begin() ; sIt != ships.end() ; sIt++) {
		if((*sIt)->swap(currentSwap)) {
			if(player)
				player->printColor("^GRS: Be sure to update the midnight ship file for %s.\n", (*sIt)->name.c_str());
			found = true;
		}
	}
	if(found)
		saveShips();


	// loop through catrefinfo
	found = false;
	std::list<CatRefInfo*>::iterator cIt;
	for(cIt = catRefInfo.begin() ; cIt != catRefInfo.end() ; cIt++) {
		if((*cIt)->swap(currentSwap)) {
			if(player)
				player->printColor("^GRS: Be sure to update the catrefinfo file for %s.\n", (*cIt)->getArea().c_str());
			found = true;
		}
	}
	if(found)
		saveCatRefInfo();


	// check all players online
	Player* ply=0;
	for(std::pair<bstring, Player*> p : gServer->players) {
		ply = p.second;
		if(ply->swap(currentSwap))
			ply->save(true);
		swapList.remove((bstring)"p" + ply->getName());
	}

	// remove the swapped rooms from the queue
	if(roomInQueue(currentSwap.origin)) {
		getRoomQueue(currentSwap.origin, &uOrigin);
		delRoomQueue(currentSwap.origin);
	}
	if(roomInQueue(currentSwap.target)) {
		getRoomQueue(currentSwap.target, &uTarget);
		delRoomQueue(currentSwap.target);
	} else {
		// the original room won't exist anymore
		unlink(roomPath(currentSwap.origin));
	}

	// readd them to the queue under their new names
	// DO NOT update now: the loop just after this will do it
	if(uOrigin)
		addRoomQueue(currentSwap.target, &uOrigin);
	if(uTarget)
		addRoomQueue(currentSwap.origin, &uTarget);

	// go through players, rooms, and arearooms saved while the offline search
	// was running, also includes results of the offline search
	for(bIt = swapList.begin() ; bIt != swapList.end() ; bIt++)
		swap(*bIt);

	for(qIt = swapQueue.begin() ; qIt != swapQueue.end() ; qIt++)
		(*qIt).match(currentSwap.origin, currentSwap.target);

	found = false;
	if(player) {
		if(player->currentLocation.room == currentSwap.origin || player->currentLocation.room == currentSwap.target)
			display_rom(player);
		player->printColor("^YRS: Room swap complete.\n");
		found = true;
	} else
		loadPlayer(name.c_str(), &player);

	if(player)
		log_immort(true, player, "%s has finished swapping %s with %s.\n", name.c_str(), currentSwap.origin.str().c_str(), currentSwap.target.str().c_str());
	else
		broadcast(isStaff, "^y%s has finished swapping %s with %s.", name.c_str(), currentSwap.origin.str().c_str(), currentSwap.target.str().c_str());

	if(!found && player)
		free_crt(player);

	endSwap();
}

//*********************************************************************
//							Swap
//*********************************************************************

Swap::Swap() { player = ""; }

void Swap::set(bstring mover, CatRef swapOrigin, CatRef swapTarget, SwapType swapType) {
	player = mover;
	origin = swapOrigin;
	target = swapTarget;
	type = swapType;
}

bool Swap::match(CatRef o, CatRef t) {
	bool found=false;

	if(origin == o) {
		origin = t;
		found = true;
	} else if(origin == t) {
		origin = t;
		found = true;
	}

	if(target == o) {
		target = t;
		found = true;
	} else if(target == t) {
		target = t;
		found = true;
	}

	return(found);
}


//*********************************************************************
//							swapChecks
//*********************************************************************

bool Config::swapChecks(const Player* player, Swap s) {
	if(s.type == SwapNone) {
		player->printColor("^YRS: ^RError: ^xInvalid swap type.\n");
		return(false);
	}

	if(s.type == SwapRoom) {
		if(moveRoomRestrictedArea(s.origin.area)) {
			if(player)
				player->printColor("^YRS: ^RError: ^x""%s"" is a restricted range. You cannot swap unique rooms into or out of that area.\n", s.origin.area.c_str());
			return(false);
		}
		if(moveRoomRestrictedArea(s.target.area)) {
			if(player)
				player->printColor("^YRS: ^RError: ^x""%s"" is a restricted range. You cannot swap unique rooms into or out of that area.\n", s.target.area.c_str());
			return(false);
		}
	} else if(s.type == SwapObject) {
		if(moveObjectRestricted(s.origin)) {
			if(player)
				player->printColor("^YRS: ^RError: ^x""%s"" is a hardcoded object and cannot be swapped.\n", s.origin.rstr().c_str());
			return(false);
		}
		if(moveObjectRestricted(s.target)) {
			if(player)
				player->printColor("^YRS: ^RError: ^x""%s"" is a hardcoded object and cannot be swapped.\n", s.target.rstr().c_str());
			return(false);
		}
	}

	if(s.target == s.origin) {
		if(player)
			player->printColor("^YRS: ^RError: ^xYou cannot swap a room with itself!\n");
		return(false);
	}
	if(!s.origin.id) {
		if(player)
			player->printColor("^YRS: ^RError: ^xYou cannot move The Void (room 0).\n");
		return(false);
	}
	if(!s.target.id) {
		if(player)
			player->printColor("^YRS: ^RError: ^xYou cannot swap a room with The Void (room 0).\n");
		return(false);
	}

	return(true);
}

//*********************************************************************
//							moveObjectRestricted
//*********************************************************************

bool Config::moveObjectRestricted(CatRef cr) const {
	if(cr.isArea("misc")) {
		if(	cr.id == SHIT_OBJ ||
			cr.id == CORPSE_OBJ ||
			cr.id == BODYPART_OBJ ||
			cr.id == STATUE_OBJ ||
			cr.id == TICKET_OBJ
		) {
			return(true);
		}
	}

	return(false);
}

//*********************************************************************
//							moveRoomRestrictedArea
//*********************************************************************

bool Config::moveRoomRestrictedArea(bstring area) const {
	return(area == "area" || area == "stor" || area == "shop" || area == "guild");
}


//*********************************************************************
//							checkSpecialArea
//*********************************************************************

bool Config::checkSpecialArea(CatRef origin, CatRef target, int (CatRefInfo::*toCheck), Player* player, bool online, bstring type) {
	Location l = getSpecialArea(toCheck, origin);
	bool t = origin == l.room;
	if(t || target == l.room) {
		if(online) {
			player->printColor("^YRS: ^RError: ^xRoom (%s) room is set as a %s Room under CatRefInfo.\n",
				t ? origin.str().c_str() : target.str().c_str(), type.c_str());
			player->print("It cannot be moved out of its area.\n");
		} else
			free_crt(player);
		endSwap();
		return(false);
	}
	return(true);
}


//*********************************************************************
//							swap
//*********************************************************************

void Config::swap(bstring str) {
	char type = str.getAt(0);
	str = str.right(str.getLength()-1);
	if(type == 'p' || type == 'b') {
		// at this point, the player will always be offline
		Player* player=0;
		LoadType saveType = type == 'p' ? LS_NORMAL : LS_BACKUP;

		if(!loadPlayer(str.c_str(), &player, saveType))
			return;

		if(player->swap(currentSwap))
			player->save(false, saveType);
		free_crt(player);
	} else if(type == 'm') {
		// the monster should have been loaded into the queue by now
		Monster* monster=0;
		CatRef cr;
		getCatRef(str, &cr, 0);

		if(!loadMonster(cr, &monster))
			return;

		if(monster->swap(currentSwap))
			monster->saveToFile();
		free_crt(monster);
	} else if(type == 'r') {
		// the room should have been loaded into the queue by now
		UniqueRoom* uRoom=0;
		CatRef cr;
		getCatRef(str, &cr, 0);

		if(!loadRoom(cr, &uRoom))
			return;

		if(uRoom->swap(currentSwap))
			uRoom->saveToFile(0);
	} else if(type == 'a') {
		// arearooms are always in memory
		Area *area=0;
		AreaRoom* aRoom=0;
		MapMarker m;

		m.load(str);
		area = gConfig->getArea(m.getArea());
		if(!area)
			return;
		aRoom = area->loadRoom(0, &m, false);
		if(!aRoom)
			return;

		if(aRoom->swap(currentSwap))
			aRoom->save();
	}
}

//*********************************************************************
//							swapLog
//*********************************************************************
// record players and rooms that are saved during roomMove

void Config::swapLog(const bstring log, bool external) {
	char type = log.getAt(0);
	if(type != 'b' && type != 'p' && type != 'm' && type != 'r' && type != 'a')
		return;

	swapList.remove(log);
	swapList.push_back(log);

	Player* player = gServer->findPlayer(gServer->swapName().c_str());

	if(player) {
		bstring mvName = log.right(log.getLength()-1);

		// are they allowed to see this?
		if(type == 'p' || type == 'b') {
			if(player->getClass() == BUILDER)
				return;
			if(player->getClass() == CARETAKER && isdm(mvName))
				return;
		}

		player->printColor("^%cRS: ^eReceiving %s%s%s.\n", external ? 'M' : 'Y',
			type == 'm' ? "Monster " : "",
			mvName.c_str(),
			type == 'b' ? " (backup)" : "");
	}
}

//*********************************************************************
//							isSwapping functions
//*********************************************************************

bool Config::isSwapping() const { return(swapping); }
void Config::setMovingRoom(CatRef o, CatRef t) {
	swapping = true;
	currentSwap.origin = o;
	currentSwap.target = t;
}

//*********************************************************************
//							swapName
//*********************************************************************

bstring Server::swapName() {
	for(childProcess & child : children)
		if(child.type == CHILD_SWAP_FIND || child.type == CHILD_SWAP_FINISH)
			return(child.extra);
	return("Someone");
}

//*********************************************************************
//							endSwap
//*********************************************************************

void Server::endSwap() {
	std::list<childProcess>::iterator it;
	for(it = children.begin(); it != children.end() ;) {
		if((*it).type == CHILD_SWAP_FIND || (*it).type == CHILD_SWAP_FINISH) {
			close((*it).fd);
			kill((*it).pid, 9);
			it = children.erase(it);
		} else
			it++;
	}
}

void Config::endSwap(int id) {
	if(id <= 1) {
		roomSearchFailure = false;
		swapping = false;
		swapList.clear();
		currentSwap.target.id = 0;
		swapNextInQueue();
		return;
	}
	std::list<Swap>::iterator qIt;
	int i=1;
	for(qIt = swapQueue.begin() ; qIt != swapQueue.end() ; qIt++) {
		if(++i==id) {
			swapQueue.erase(qIt);
			return;
		}
	}
}

//*********************************************************************
//							swapInfo
//*********************************************************************

void Config::swapInfo(const Player* player) {
	bool canSee = true;
	char type;
	bstring mvName;

	player->printColor("^WWwap Config Info\n");
	player->printColor("   Swapping: %s^x  What: ^e%s\n", isSwapping() ? "^gYes" : "^rNo", swapName(currentSwap.type).c_str());
	player->printColor("   Queue Size: ^c%d\n", SWAP_QUEUE_LIMIT);
	player->print("   Data In Memory:\n");

	std::list<bstring>::iterator bIt;
	for(bIt = swapList.begin() ; bIt != swapList.end() ; bIt++) {
		canSee = true;
		type = (*bIt).getAt(0);
		mvName = (*bIt).right((*bIt).getLength()-1);

		if(type == 'p' || type == 'b') {
			if(player->getClass() == BUILDER)
				canSee = false;
			if(player->getClass() == CARETAKER && isdm(mvName))
				canSee = false;
		}

		if(canSee)
			player->printColor("      ^e%s%s\n", mvName.c_str(),
				(*bIt).getAt(0) == 'b' ? " (backup)" : "");
	}
	std::list<Swap>::iterator qIt;
	int id=1;
	player->print("   The Queue:\n");
	for(qIt = swapQueue.begin() ; qIt != swapQueue.end() ; qIt++) {
		player->printColor("      %d) Player: ^e%s^x   Origin: ^e%s^x   Target: ^e%s\n", ++id,
			(*qIt).player.c_str(), (*qIt).origin.str().c_str(),
			(*qIt).target.id == -1 ? (*qIt).target.area.c_str() : (*qIt).target.str().c_str());
	}
}

void Server::swapInfo(const Player* player) {
	player->printColor("^WSwap Server Info\n");
	player->print("   Child Processes Being Watched:\n");
	for(childProcess & child : children) {
		if(child.type == CHILD_SWAP_FIND || child.type == CHILD_SWAP_FINISH) {
			player->printColor("      Player: ^e%s^x   Pid: ^e%d^x   Fd: ^e%d^x   Purpose: ^e%s\n",
				child.extra.c_str(), child.pid, child.fd,
				child.type == CHILD_SWAP_FIND ? "Finding next empty slot." :
					"Finding things that need updating.");
		}
	}
}

//*********************************************************************
//							swap queue functions
//*********************************************************************

void Config::swapEmptyQueue() { swapQueue.clear(); }
int Config::swapQueueSize() { return(swapQueue.size()); }

void Config::swapNextInQueue() {
	if(swapQueue.empty())
		return;
	Swap s = swapQueue.front();
	swapQueue.pop_front();
	gServer->swap(s);
}

void Config::swapAddQueue(Swap s) {
	swapQueue.push_back(s);
}

bool Config::inSwapQueue(CatRef origin, SwapType type, bool checkTarget) {
	std::list<Swap>::iterator it;
	for(it = swapQueue.begin() ; it != swapQueue.end() ; it++) {
		if(type == (*it).type) {
			if(origin == (*it).origin)
				return(true);
			if(checkTarget && origin == (*it).target)
				return(true);
		}
	}
	return(false);
}

//*********************************************************************
//							swapAbort
//*********************************************************************

void Config::swapAbort() {
	swapEmptyQueue();
	gServer->endSwap();
	endSwap();
}

//*********************************************************************
//							Player swap
//*********************************************************************

bool Config::swapIsInteresting(const MudObject* target) const {
	if(!isSwapping())
		return(false);

	const Monster* monster = target->getAsConstMonster();
	if(monster && monster->swapIsInteresting(currentSwap))
		return(true);

	const Player* player = target->getAsConstPlayer();
	if(player && player->swapIsInteresting(currentSwap))
		return(true);

	const Object* object = target->getAsConstObject();
	if(object && object->swapIsInteresting(currentSwap))
		return(true);

	const AreaRoom* aRoom = target->getAsConstAreaRoom();
	if(aRoom && aRoom->swapIsInteresting(currentSwap))
		return(true);

	const UniqueRoom* uRoom = target->getAsConstUniqueRoom();
	if(uRoom && uRoom->swapIsInteresting(currentSwap))
		return(true);

	return(false);
}

//*********************************************************************
//							nextDelim
//*********************************************************************

char whichDelim(const bstring& code) {
	bstring::size_type qPos, aPos;

	qPos = code.find("\"");
	aPos = code.find("'");

	if(qPos == bstring::npos && aPos == bstring::npos)
		return(0);
	if(qPos == bstring::npos)
		return('\'');
	if(aPos == bstring::npos)
		return('"');
	if(qPos < aPos)
		return('"');
	return('\'');
}

//*********************************************************************
//							getParamFromCode
//*********************************************************************

bstring getParamFromCode(const bstring& pythonCode, bstring function, SwapType type) {
	bstring code = pythonCode;
	bstring::size_type pos;
	char delim;

	// find the function name
	pos = code.find(function);
	if(pos == bstring::npos)
		return("");

	if(function == "spawnObjects") {
		// room is the first param, objects is the second param
		code = code.substr(pos, pos - code.getLength());

		// find the delimiter for the first parameter
		delim = whichDelim(code);
		if(delim == 0)
			return("");
		pos = code.find(delim);

		code = code.substr(pos + 1, pos - code.getLength() - 1);

		// for objects, move on to the next parameter
		if(type == SwapObject) {
			pos = code.find(delim);
			if(pos == bstring::npos)
				return("");
			code = code.substr(pos + 1, pos - code.getLength() - 1);

			// find the delimiter for the second parameter
			delim = whichDelim(code);
			if(delim == 0)
				return("");
			pos = code.find(delim);

			code = code.substr(pos + 1, pos - code.getLength() - 1);
		}

		pos = code.find(delim);
		if(pos == bstring::npos)
			return("");
		code = code.substr(0, pos);

		return(code);
	}

	return("");
}

//*********************************************************************
//							setParamInCode
//*********************************************************************

bstring setParamInCode(const bstring& pythonCode, bstring function, SwapType type, bstring param) {
	bstring code = pythonCode;
	bstring::size_type pos;

	pos = code.find(function);
	if(pos == bstring::npos)
		return(code);

	if(function == "spawnObjects") {
		// room is the first param, objects is the second param
	}

	return(code);
}

//*********************************************************************
//							Hooks swap
//*********************************************************************

bool Hooks::swap(Swap s) {
	bool found=false;
	bstring param;
	CatRef cr;

	for(std::pair<bstring,bstring> p : hooks ) {
		if(s.type == SwapRoom) {
			param = getParamFromCode(p.second, "spawnObjects", s.type);
			if(param != "") {
				getCatRef(param, &cr, 0);
				if(cr == s.origin) {
					p.second = setParamInCode(p.second, "spawnObjects", s.type, param);
					found = true;
				} else if(cr == s.target) {
					found = true;
				}
			}
		}
	}

	return(found);
}

//*********************************************************************
//							Hooks swapIsInteresting
//*********************************************************************

bool Hooks::swapIsInteresting(Swap s) const {
	bstring param;
	CatRef cr;

	for(std::pair<bstring, bstring> p : hooks) {
		if(s.type == SwapRoom) {
			param = getParamFromCode(p.second, "spawnObjects", s.type);
			if(param != "") {
				getCatRef(param, &cr, 0);
				if(cr == s.origin || cr == s.target)
					return(true);
			}
		} else if(s.type == SwapObject) {
			bstring obj;
			int i=0;

			param = getParamFromCode(p.second, "spawnObjects", s.type);
			if(param != "") {
				do {
					obj = getFullstrTextTrun(param, i++);
					if(obj != "")
					{
						getCatRef(obj, &cr, 0);
						if(cr == s.origin || cr == s.target)
							return(true);
					}
				} while(obj != "");
			}
		}
	}

	return(false);
}

//*********************************************************************
//							Player swap
//*********************************************************************

bool Player::swap(Swap s) {
	bool found=false;

	if(bound.room == s.origin) {
		bound.room = s.target;
		found = true;
	} else if(bound.room == s.target) {
		bound.room = s.origin;
		found = true;
	}
	if(currentLocation.room == s.origin) {
		currentLocation.room = s.target;
		found = true;
	} else if(currentLocation.room == s.target) {
		currentLocation.room = s.origin;
		found = true;
	}

	for(int i=0; i<MAX_DIMEN_ANCHORS; i++) {
		if(anchor[i]) {
			if(anchor[i]->getRoom() == s.origin) {
				anchor[i]->setRoom(s.target);
				found = true;
			} else if(anchor[i]->getRoom() == s.target) {
				anchor[i]->setRoom(s.origin);
				found = true;
			}
		}
	}

	std::list<CatRef>::iterator it;
	for(it = roomExp.begin() ; it != roomExp.end() ; it++) {
		if(*it == s.origin) {
			*it = s.target;
			found = true;
		} else if(*it == s.target) {
			*it = s.origin;
			found = true;
		}
	}

	if(hooks.swap(s))
		found = true;

	return(found);
}

//*********************************************************************
//							Player swapIsInteresting
//*********************************************************************

bool Player::swapIsInteresting(Swap s) const {
	if(!gConfig->isSwapping())
		return(false);

	if(bound.room == s.origin || bound.room == s.target)
		return(true);
	if(currentLocation.room == s.origin || currentLocation.room == s.target)
		return(true);

	for(int i=0; i<MAX_DIMEN_ANCHORS; i++) {
		if(anchor[i]) {
			if(anchor[i]->getRoom() == s.origin || anchor[i]->getRoom() == s.target)
				return(true);
		}
	}

	std::list<CatRef>::const_iterator it;
	for(it = roomExp.begin() ; it != roomExp.end() ; it++) {
		if(*it == s.origin || *it == s.target)
			return(true);
	}

	if(hooks.swapIsInteresting(s))
		return(true);

	return(false);
}

//*********************************************************************
//							Monster swap
//*********************************************************************

bool Monster::swap(Swap s) {
	bool found=false;

	if(jail == s.origin) {
		jail = s.target;
		found = true;
	} else if(jail == s.target) {
		jail = s.origin;
		found = true;
	}
	if(currentLocation.room == s.origin) {
		currentLocation.room = s.target;
		found = true;
	} else if(currentLocation.room == s.target) {
		currentLocation.room = s.origin;
		found = true;
	}

	if(hooks.swap(s))
		found = true;

	return(found);
}

//*********************************************************************
//							Monster swapIsInteresting
//*********************************************************************

bool Monster::swapIsInteresting(Swap s) const {
	if(jail == s.origin || jail == s.target)
		return(true);
	if(currentLocation.room == s.origin || currentLocation.room == s.target)
		return(true);

	if(hooks.swapIsInteresting(s))
		return(true);

	return(false);
}

//*********************************************************************
//							Object swap
//*********************************************************************

bool Object::swap(Swap s) {
	bool found=false;

	if(hooks.swap(s))
		found = true;

	return(found);
}

//*********************************************************************
//							Object swapIsInteresting
//*********************************************************************

bool Object::swapIsInteresting(Swap s) const {

	if(hooks.swapIsInteresting(s))
		return(true);

	return(false);
}

//*********************************************************************
//							Room swap
//*********************************************************************

bool UniqueRoom::swap(Swap s) {
	bool found=false;

	if(info == s.origin || info == s.target) {
		// if shop relies on next room for storage
		if(flagIsSet(R_SHOP) && !trapexit.id) {
			// manually set
			trapexit = info;
			trapexit.id++;
		}

		if(info == s.origin) {
			info = s.target;
		} else {
			info = s.origin;
		}
		found = true;
	}

	for(Monster* monster : monsters) {
		if(monster->swap(s))
			found = true;
	}

	if(trapexit == s.origin) {
		trapexit = s.target;
		found = true;
	} else if(trapexit == s.target) {
		trapexit = s.origin;
		found = true;
	}

	for(Exit* ext : exits) {
		if(ext->target.room == s.origin) {
			ext->target.room = s.target;
			found = true;
		} else if(ext->target.room == s.target) {
			ext->target.room = s.origin;
			found = true;
		}
	}

	if(hooks.swap(s))
		found = true;

	return(found);
}

//*********************************************************************
//							Room swapIsInteresting
//*********************************************************************

bool UniqueRoom::swapIsInteresting(Swap s) const {
	if(info == s.origin || info == s.target)
		return(true);

	for(const Monster* monster : monsters) {
		if(monster->swapIsInteresting(s))
			return(true);
	}

	if(trapexit == s.origin || trapexit == s.target)
		return(true);

	for(Exit* ext : exits) {
		if(ext->target.room == s.origin || ext->target.room == s.target)
			return(true);
	}

	if(hooks.swapIsInteresting(s))
		return(true);

	return(false);
}

//*********************************************************************
//							AreaRoom swap
//*********************************************************************

bool AreaRoom::swap(Swap s) {
	bool found=false;

	if(unique == s.origin) {
		unique = s.target;
		found = true;
	} else if(unique == s.target) {
		unique = s.origin;
		found = true;
	}

	for(Exit* ext : exits) {
		if(ext->target.room == s.origin) {
			ext->target.room = s.target;
			found = true;
		} else if(ext->target.room == s.target) {
			ext->target.room = s.origin;
			found = true;
		}
	}
	for(Monster* monster : monsters) {
		if(monster->swap(s))
			found = true;
	}

	if(hooks.swap(s))
		found = true;

	return(found);
}

//*********************************************************************
//							AreaRoom swapIsInteresting
//*********************************************************************

bool AreaRoom::swapIsInteresting(Swap s) const {
	if(unique == s.origin || unique == s.target)
		return(true);

	for(Exit* ext : exits) {
		if(ext->target.room == s.origin || ext->target.room == s.target)
			return(true);
	}

	for(const Monster* monster : monsters) {
		if(monster->swapIsInteresting(s))
			return(true);
	}

	if(hooks.swapIsInteresting(s))
		return(true);

	return(false);
}

//*********************************************************************
//							AreaZone swap
//*********************************************************************

bool AreaZone::swap(Swap s) {
	bool found=false;

	if(unique == s.origin) {
		unique = s.target;
		found = true;
	} else if(unique == s.target) {
		unique = s.origin;
		found = true;
	}

	return(found);
}

//*********************************************************************
//							Area swap
//*********************************************************************

bool Area::swap(Swap s) {
	//std::map<bstring, AreaRoom*>::iterator rIt;
	std::list<AreaZone*>::iterator zIt;
	bool found=false;

	for(zIt = zones.begin() ; zIt != zones.end() ; zIt++) {
		if((*zIt)->swap(s))
			found = true;
	}

	return(found);
}

//*********************************************************************
//							ShipRaid swap
//*********************************************************************

bool ShipRaid::swap(Swap s) {
	bool found=false;

	if(prison == s.origin) {
		prison = s.target;
		found = true;
	} else if(prison == s.target) {
		prison = s.origin;
		found = true;
	}
	if(dump == s.origin) {
		dump = s.target;
		found = true;
	} else if(dump == s.target) {
		dump = s.origin;
		found = true;
	}

	return(found);
}

//*********************************************************************
//							ShipExit swap
//*********************************************************************

bool ShipExit::swap(Swap s) {
	bool found=false;

	if(origin.room == s.origin) {
		origin.room = s.target;
		found = true;
	} else if(origin.room == s.target) {
		origin.room = s.origin;
		found = true;
	}
	if(target.room == s.origin) {
		target.room = s.target;
		found = true;
	} else if(target.room == s.target) {
		target.room = s.origin;
		found = true;
	}

	return(found);
}

//*********************************************************************
//							ShipStop swap
//*********************************************************************

bool ShipStop::swap(Swap s) {
	std::list<ShipExit*>::iterator it;
	bool found=false;

	for(it = exits.begin() ; it != exits.end() ; it++) {
		if((*it)->swap(s))
			found = true;
	}

	if(raid && raid->swap(s))
		found = true;

	return(found);
}

//*********************************************************************
//							Ship swap
//*********************************************************************

bool Ship::swap(Swap s) {
	std::list<ShipStop*>::iterator it;
	bool found=false;

	for(it = stops.begin() ; it != stops.end() ; it++) {
		if((*it)->swap(s))
			found = true;
	}

	return(found);
}

//*********************************************************************
//							StartLoc swap
//*********************************************************************

bool StartLoc::swap(Swap s) {
	bool found=false;

	if(bind.room == s.origin) {
		bind.room = s.target;
		found = true;
	} else if(bind.room == s.target) {
		bind.room = s.origin;
		found = true;
	}
	if(required.room == s.origin) {
		required.room = s.target;
		found = true;
	} else if(required.room == s.target) {
		required.room = s.origin;
		found = true;
	}

	return(found);
}

//*********************************************************************
//							CatRefInfo swap
//*********************************************************************

bool CatRefInfo::swap(Swap s) {
	bool found=false;

	if(s.origin.isArea(area) && limbo == s.origin.id) {
		limbo = s.target.id;
		found = true;
	} else if(s.target.isArea(area) && limbo == s.target.id) {
		limbo = s.origin.id;
		found = true;
	}

	if(s.origin.isArea(area) && recall == s.origin.id) {
		recall = s.target.id;
		found = true;
	} else if(s.target.isArea(area) && recall == s.target.id) {
		recall = s.origin.id;
		found = true;
	}

	return(found);
}