roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * start.cpp
 *	 Player starting location code.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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-2009 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 "magic.h"
#include "login.h"
#include "commands.h"

//*********************************************************************
//						initialBind
//*********************************************************************
// setup the player for the first time they are bound

void initialBind(Player* player, bstring str) {
	const StartLoc* location = gConfig->getStartLoc(str);
	if(!location) {
		broadcast(isCt, "Invalid start location: %s", str.c_str());
		return;
	}
	CatRef cr = location->getStartingGuide();

//	if(str == "oceancrest") {
//		player->setFlag(P_HARDCORE);
//		player->setFlag(P_CHAOTIC);
//		player->setFlag(P_CHOSEN_ALIGNMENT);
//	}

	player->bind(location);
	player->room = player->bound.room;
	player->room = player->getRecallRoom().room;

	if(cr.id)
		Create::addStartingItem(player, cr.area, cr.id, false, true);
}

//*********************************************************************
//						startingChoices
//*********************************************************************
// This function will determine the player's starting location.
// choose = false	return true if we can easily pick their location
//					return false if they must choose
// choose = true	return true if they made a valid selection
//					return false if they made an invalid selection

bool startingChoices(Player* player, char* str, char* location, bool choose) {

	// if only one start location is defined, our choices are easy!
	if(gConfig->start.size() == 1) {
		std::map<bstring, StartLoc*>::iterator s = gConfig->start.begin();
		sprintf(location, "%s", (*s).first.c_str());
//		location[0] = up(location[0]);
		initialBind(player, location);
//		player->bind((*s).second);
		return(true);
	}

	std::list<bstring> options;
	int		race = player->getRace();

	// set race equal to their parent race, use player->getRace() if checking
	// for subraces
	const RaceData* r = gConfig->getRace(race);
	if(r && r->getParentRace())
		race = r->getParentRace();

//	if(player->getClass() == DRUID) {
//
//		// druidic order overrides all other starting locations
//		options.push_back("druidwood");
//
//	} else if(player->getDeity() == ENOCH || player->getRace() == SERAPH) {
//
//		// religious states
//		options.push_back("sigil");
//
//	} else if(player->getDeity() == ARAMON || player->getRace() == CAMBION) {
//
//		// religious states
//		options.push_back("caladon");
//
//	} else if(race == HUMAN && player->getClass() == CLERIC) {
//
//		// all other human clerics have to start in HP because
//		// Sigil and Caladon are very religious
//		options.push_back("highport");
//
//	} else if(race == GNOME || race == HALFLING) {
//
//		options.push_back("gnomebarrow");
//
//	} else if(race == HALFGIANT) {
//
//		options.push_back("schnai");
//		options.push_back("highport");
//
//	} else if(race == BARBARIAN) {
//
//		options.push_back("schnai");
//
//	} else if(race == DWARF) {
//
//		options.push_back("ironguard");
//
//	} else if(race == DARKELF) {
//
//		options.push_back("oakspire");
//
//	} else if(race == TIEFLING) {
//
//		options.push_back("highport");
//		options.push_back("caladon");
//
//	} else if(race == MINOTAUR) {
//
//		options.push_back("ruhrdan");
//
//	} else if(race == KATARAN) {
//
//		options.push_back("kataran");
//
//	} else if(race == HALFORC) {
//
//		// half-breeds can start in more places
//		options.push_back("highport");
//		options.push_back("caladon");
//		options.push_back("orc");
//
//	} else if(race == ORC) {
//
//		options.push_back("orc");
//
//	} else if(race == ELF || player->getDeity() == LINOTHAN) {
//
//		options.push_back("meadhil");
//
//	} else if(race == HALFELF || race == HUMAN) {
//
//		// humans and half-breeds can start in more places
//		options.push_back("highport");
//		options.push_back("sigil");
//
//		if(race == HUMAN)
//			options.push_back("caladon");
//		else if(race == HALFELF)
//			options.push_back("meadhil");
//
//
//	}
//
//	switch(player->getClass()) {
//	case RANGER:
//		options.push_back("druidwood");
//		break;
//	// even seraphs of these classes cannot start in Sigil
//	case ASSASSIN:
//	case LICH:
//	case THIEF:
//	case DEATHKNIGHT:
//	case VAMPIRE:
//	case ROGUE:
//	case WEREWOLF:
//		options.remove("sigil");
//		break;
//	default:
//		break;
//	}
//
//	// needed:
//	// troll, goblin, kobold, ogre
//
//	// these areas arent finished yet
//	options.remove("caladon");
//	options.remove("meadhil");
//	options.remove("orc");
//	options.remove("schnai");
//	options.remove("ironguard");
//	options.remove("kataran");
//	options.remove("ruhrdan");


	options.push_back("ocean");

	// if the areas aren't open, give them the default starting location,
	// which is the first one on the list
	if(!options.size())
		options.push_back(gConfig->getDefaultStartLoc()->getName());

//	options.push_back("oceancrest");

	// if they have no choice, we assign them a location and are done with it
	if(options.size() == 1) {
		sprintf(location, "%s", options.front().c_str());
		location[0] = up(location[0]);

		initialBind(player, options.front());
		return(true);
	}


	std::list<bstring>::iterator it;
	int		i=0;


	// we don't need to make any choices - we need to show them what they can choose
	if(!choose) {
		std::ostringstream oStr;
		char opt = 'A';
		for(it = options.begin(); it != options.end(); it++) {
			if(i)
				oStr << "     ";
			i++;

			sprintf(location, "%s", (*it).c_str());
			location[0] = up(location[0]);

			oStr << "[^W" << (opt++) << "^x] " << location;
		}
		sprintf(location, "%s", oStr.str().c_str());
		return(false);
	}

	// determine where they want to start
	int choice = low(str[0]) - 'a' + 1;

	for(it = options.begin(); it != options.end(); it++) {
		if(++i == choice) {
			sprintf(location, "%s", (*it).c_str());
			location[0] = up(location[0]);

			initialBind(player, *it);
			return(true);
		}
	}

	return(false);
}


//*********************************************************************
//						dmStartLocs
//*********************************************************************

int dmStartLocs(Player* player, cmd* cmnd) {
	player->print("Starting Locations:\n");

	std::map<bstring, StartLoc*>::iterator it;
	for(it = gConfig->start.begin(); it != gConfig->start.end() ; it++) {

		player->print("%s\n", (*it).first.c_str());

	}
	return(0);
}

//*********************************************************************
//						splBind
//*********************************************************************
// This function will change the player's bound room. If cast as a spell,
// it must belong to a list of rooms. If used from the floor, it can point
// to any room

int splBind(Creature* player, cmd* cmnd, SpellData* spellData) {
	Creature* creature=0;
	Player*	target=0, *pPlayer = player->getPlayer();
	const StartLoc* location=0;

	if(!pPlayer)
		return(0);

	if(spellData->how == CAST) {

		if(!pPlayer->isCt()) {
			if(	pPlayer->getClass() != MAGE &&
				pPlayer->getClass() != LICH &&
				pPlayer->getClass() != CLERIC &&
				pPlayer->getClass() != DRUID
			) {
				pPlayer->print("Your class prevents you from casting that spell.\n");
				return(0);
			}
			if(pPlayer->getLevel() < 13) {
				pPlayer->print("You are not yet powerful enough to cast that spell.\n");
				return(0);
			}
		}

		if(cmnd->num < 2) {
			pPlayer->print("Syntax: cast bind [target]%s.\n", pPlayer->isCt() ? " <location>" : "");
			return(0);
		}


		if(cmnd->num == 2)
			target = pPlayer;
		else {

			cmnd->str[2][0] = up(cmnd->str[2][0]);

			creature = pPlayer->getRoom()->findCreature(pPlayer, cmnd->str[2], cmnd->val[2], false);
			if(creature)
				target = creature->getPlayer();

			if(!target) {
				pPlayer->print("You don't see that person here.\n");
				return(0);
			}
			if(checkRefusingMagic(player, target))
				return(0);
		}


		if(pPlayer->isCt() && cmnd->num == 4)
			location = gConfig->getStartLoc(cmnd->str[3]);
		else if(pPlayer->parent_rom && pPlayer->parent_rom->info.id)
			location = gConfig->getStartLocByReq(pPlayer->parent_rom->info);

		if(	!location ||
			!location->getRequired().getId() ||
			!location->getBind().getId()
		) {
			pPlayer->print("%s is not a valid location to bind someone!\n",
				pPlayer->isCt() && cmnd->num == 4 ? "That" : "This");
			return(0);
		}

		if(!pPlayer->isCt()) {
			if(	(pPlayer->area_room && pPlayer->area_room->mapmarker != location->getRequired().mapmarker) ||
				(pPlayer->parent_rom && pPlayer->parent_rom->info != location->getRequired().room)
			) {
				pPlayer->print("To bind to this location, you must be at %s.\n", location->getRequiredName().c_str());
				return(0);
			}
			if(!dec_daily(&pPlayer->daily[DL_TELEP])) {
				pPlayer->print("You are too weak to bind again today.\n");
				return(0);
			}
		}

		if(pPlayer == target) {

			pPlayer->print("Bind spell cast.\nYou are now bound to %s.\n", location->getBindName().c_str());
			broadcast(pPlayer->getSock(), pPlayer->getRoom(), "%M casts a bind spell on %sself.", pPlayer, pPlayer->himHer());

		} else {

			// nosummon flag
			if(	target->flagIsSet(P_NO_SUMMON) &&
				!pPlayer->checkStaff("The spell fizzles.\n%M's summon flag is not set.\n", target)
			) {
				target->print("%s tried to bind you to this room!\nIf you wish to be bound, type \"set summon\".\n", pPlayer->name);
				return(0);
			}

			pPlayer->print("Bind cast on %s.\n%s is now bound to %s.\n",
				target->name, target->name, location->getBindName().c_str());
			target->print("%M casts a bind spell on you.\nYou are now bound to %s.\n",
				pPlayer, location->getBindName().c_str());
			broadcast(player->getSock(), target->getSock(), pPlayer->getRoom(),
				"%M casts a bind spell on %N.", pPlayer, target);

		}

		target->bind(location);

	} else {

		if(spellData->how != WAND) {
			pPlayer->print("Nothing happens.\n");
			return(0);
		} else {

			// we need more info from the item!
			pPlayer->print("Nothing happens (for now).\n");
			return(0);

		}
	}

	return(1);
}


//*********************************************************************
//						StartLoc
//*********************************************************************

StartLoc::StartLoc() {
	name = requiredName = bindName = "";
	primary = false;
}

//*********************************************************************
//						load
//*********************************************************************

void StartLoc::load(xmlNodePtr curNode) {
	xmlNodePtr childNode = curNode->children;
	xml::copyPropToBString(name, curNode, "Name");
	while(childNode) {
			 if(NODE_NAME(childNode, "BindName")) xml::copyToBString(bindName, childNode);
		else if(NODE_NAME(childNode, "RequiredName")) xml::copyToBString(requiredName, childNode);
		else if(NODE_NAME(childNode, "Bind")) bind.load(childNode);
		else if(NODE_NAME(childNode, "Required")) required.load(childNode);
		else if(NODE_NAME(childNode, "StartingGuide")) startingGuide.load(childNode);
		childNode = childNode->next;
	}
}

//*********************************************************************
//						save
//*********************************************************************

void StartLoc::save(xmlNodePtr curNode) const {
	xmlNodePtr childNode = xml::newStringChild(curNode, "Location");
	xml::newProp(childNode, "Name", name);

	xml::saveNonNullString(childNode, "BindName", bindName);
	xml::saveNonNullString(childNode, "RequiredName", requiredName);
	bind.save(childNode, "Bind");
	required.save(childNode, "Required");
	startingGuide.save(childNode, "StartingGuide", false);
}

bstring StartLoc::getName() const { return(name); }
bstring StartLoc::getBindName() const { return(bindName); }
bstring StartLoc::getRequiredName() const { return(requiredName); }
Location StartLoc::getBind() const { return(bind); }
Location StartLoc::getRequired() const { return(required); }
CatRef StartLoc::getStartingGuide() const { return(startingGuide); }
bool StartLoc::isDefault() const { return(primary); }
void StartLoc::setDefault() { primary = true; }

//*********************************************************************
//						bind
//*********************************************************************

void Player::bind(const StartLoc* location) {
	bound = location->getBind();
}


//*********************************************************************
//						getStartLoc
//*********************************************************************

const StartLoc *Config::getStartLoc(bstring id) const {
	std::map<bstring, StartLoc*>::const_iterator it = start.find(id);

	if(it == start.end())
		return(0);

	return((*it).second);
}


//*********************************************************************
//						getDefaultStartLoc
//*********************************************************************

const StartLoc *Config::getDefaultStartLoc() const {
	std::map<bstring, StartLoc*>::const_iterator it;
	for(it = start.begin(); it != start.end() ; it++) {
		if((*it).second->isDefault())
			return((*it).second);
	}
	return(0);
}


//*********************************************************************
//						getStartLocByReq
//*********************************************************************

const StartLoc *Config::getStartLocByReq(CatRef cr) const {
	std::map<bstring, StartLoc*>::const_iterator it;
	StartLoc* s=0;

	for(it = start.begin() ; it != start.end() ; it++) {
		s = (*it).second;
		if(cr == s->getRequired().room)
			return(s);
	}
	return(0);
}


//*********************************************************************
//						clearStartLoc
//*********************************************************************

void Config::clearStartLoc() {
	std::map<bstring, StartLoc*>::iterator it;

	for(it = start.begin() ; it != start.end() ; it++) {
		StartLoc* s = (*it).second;
		delete s;
	}
	start.clear();
}

//*********************************************************************
//						loadStartLoc
//*********************************************************************

bool Config::loadStartLoc() {
	xmlDocPtr xmlDoc;
	xmlNodePtr curNode;
	// first one loaded is the primary one
	bool primary=true;

	xmlDoc = xml::loadFile(CONFPATH "start.xml", "Locations");

	if(xmlDoc == NULL)
		return(false);

	curNode = xmlDocGetRootElement(xmlDoc);

	curNode = curNode->children;
	while(curNode && xmlIsBlankNode(curNode))
		curNode = curNode->next;

	if(curNode == 0) {
		xmlFreeDoc(xmlDoc);
		return(false);
	}

	clearStartLoc();
	bstring loc = "";
	while(curNode != NULL) {
		if(NODE_NAME(curNode, "Location")) {
			xml::copyPropToBString(loc, curNode, "Name");
			if(loc != "" && start.find(loc) == start.end()) {
				start[loc] = new StartLoc;
				start[loc]->load(curNode);
				if(primary) {
					start[loc]->setDefault();
					primary = false;
				}
			}
		}
		curNode = curNode->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();

	return(true);
}

void Config::saveStartLocs() const {
	std::map<bstring, StartLoc*>::const_iterator it;
	xmlDocPtr	xmlDoc;
	xmlNodePtr	rootNode;
	char			filename[80];

	xmlDoc = xmlNewDoc(BAD_CAST "1.0");
	rootNode = xmlNewDocNode(xmlDoc, NULL, BAD_CAST "Locations", NULL);
	xmlDocSetRootElement(xmlDoc, rootNode);

	for(it = start.begin() ; it != start.end() ; it++)
		(*it).second->save(rootNode);

	sprintf(filename, "%s/start.xml", CONFPATH);
	xml::saveFile(filename, xmlDoc);
	xmlFreeDoc(xmlDoc);
}