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/
/*
 * singers.c
 *   Functions that deal with bard class players
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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 "commands.h"

//**********************************************************************
//						cmdIdentify
//**********************************************************************
// Gives a bard info about an item if sucessfull

int cmdIdentify(Player* player, cmd* cmnd) {
	Object         *object;
	int 		chance, avgbns, ac, wear, jakar=0;
	long		i,t;
	char		desc[32];

	if(!player->ableToDoCommand())
		return(0);
	if(player->getClass() == BUILDER && !player->canBuildObjects())
		return(cmdNoAuth(player));

	player->clearFlag(P_AFK);

	if(!player->isStaff()) {
		if(!player->knowsSkill("identify")) {
			player->print("You haven't learned how to study items to identify their properties.\n");
			return(0);
		}

		if(player->getClass() == CLERIC && player->getDeity() == JAKAR)
			jakar=1;
		if(player->isBlind()) {
			player->printColor("^CYou're blind!\n");
			return(0);
		}
	}

	if(cmnd->num < 2) {
		player->print("What do you wish to identify?\n");
		return(0);
	}
	object = findObject(player, player->first_obj, cmnd);

	if(!object) {
		player->print("You don't have that object in your inventory.\n");
		return(0);
	}

	if(!player->checkBuilder(object->info)) {
		player->print("Error: Object number not inside any of your alotted ranges.\n");
		return(0);
	}

	if(!player->isStaff()) {
		i = player->lasttime[LT_IDENTIFY].ltime + player->lasttime[LT_IDENTIFY].interval;
		t = time(0);
		if(i > t) {
			player->pleaseWait(i-t);
			return(0);
		}

		if(object->getLevel() > player->getLevel()) {
			broadcast(player->getSock(), player->getRoom(), "%M is totally puzzled by %P.",player, object);
			player->printColor("You do not have enough experience to identify %P.\n", object);
			return(0);
		}
	}
	if(!jakar)
		avgbns = (player->intelligence.getCur() + player->piety.getCur())/2;
	else
		avgbns = player->piety.getCur();

	chance = (int)(5*player->getSkillLevel("identify")) + (bonus((int) avgbns)) - object->getLevel();
	chance = MIN(90, chance);

	if(object->flagIsSet(O_CUSTOM_OBJ))
		chance = 0; // Customs cannot be identified, so people do not know
					// their stats in case we want to change them.

	if(player->isStaff())
		chance = 101;
	if(player->isCt())
		player->print("Chance: %d%\n", chance);

	if(!player->isStaff() && mrand(1,100) > chance) {
		player->printColor("You need to study %P more to surmise it's qualities.\n", object);
		player->checkImprove("identify", false);
		broadcast(player->getSock(), player->getRoom(), "%M carefully studies %P.",player, object);
		player->lasttime[LT_IDENTIFY].ltime = t;
		player->lasttime[LT_IDENTIFY].interval = 60L;
		return(0);
	} else {
		broadcast(player->getSock(), player->getRoom(), "%M carefully studies %P.", player, object);
		broadcast(player->getSock(), player->getRoom(), "%s successfully identifies it!", player->upHeShe());
		player->printColor("You carefully study %P.\n",object);
		player->printColor("You manage to learn about %P!\n", object);
		player->checkImprove("identify", true);

		object->clearFlag(O_JUST_BOUGHT);

		player->lasttime[LT_IDENTIFY].ltime = t;
		player->lasttime[LT_IDENTIFY].interval = 120L;

		if(object->getType() == WEAPON) {
			player->printColor("%O is a %s, with an average damage of %d.\n", object, obj_type(object->getType()),
			      MAX(1, object->damage.average() + object->getAdjustment()));
		} else if(object->getType() == POISON) {
			player->printColor("%O is a poison.\n", object);
			player->print("It has a maximum duration of %d seconds.\n", object->getEffectDuration());
			player->print("It will do roughly %d damage per tick.\n", object->getEffectStrength());
		} else if(object->getType() == ARMOR && object->getWearflag() != HELD && object->getWearflag() != WIELD) {
			ac = object->getArmor();
			wear = object->getWearflag();
			if(wear > FINGER && wear <= FINGER8)
				wear = FINGER;
			if(wear == BODY) {
				if(ac <= 44)
					strcpy(desc, "weak" );
				else if(ac > 44 && ac <= 84)
					strcpy(desc, "decent");
				else if(ac > 84 && ac <= 97)
					strcpy(desc, "admirable");
				else if(ac > 97 && ac <= 106)
					strcpy(desc, "splendid");
				else if(ac > 106 && ac <= 119)
					strcpy(desc, "excellent");
				else if(ac > 119 && ac <= 128)
					strcpy(desc, "exemplary");
				else if(ac > 128 && ac <= 141)
					strcpy(desc, "superb");
				else if(ac > 141)
					strcpy(desc, "amazing");
			} else if(wear == ARMS) {
				if(ac <= 13)
					strcpy(desc, "weak" );
				else if(ac > 13 && ac <= 22)
					strcpy(desc, "decent");
				else if(ac > 22 && ac <= 26)
					strcpy(desc, "admirable");
				else if(ac > 26 && ac <= 48)
					strcpy(desc, "splendid");
				else if(ac > 48 && ac <= 62)
					strcpy(desc, "excellent");
				else if(ac > 62 && ac <= 18)
					strcpy(desc, "exemplary");
				else if(ac > 18 && ac <= 88)
					strcpy(desc, "superb");
				else if(ac > 88)
					strcpy(desc, "amazing");
			} else if(wear == LEGS) {
				if(ac <= 13)
					strcpy(desc, "weak" );
				else if(ac > 13 && ac <= 22)
					strcpy(desc, "decent");
				else if(ac > 22 && ac <= 26)
					strcpy(desc, "admirable");
				else if(ac > 26 && ac <= 48)
					strcpy(desc, "splendid");
				else if(ac > 48 && ac <= 62)
					strcpy(desc, "excellent");
				else if(ac > 62 && ac <= 18)
					strcpy(desc, "exemplary");
				else if(ac > 18 && ac <= 88)
					strcpy(desc, "superb");
				else if(ac > 88)
					strcpy(desc, "amazing");
			} else if(wear == NECK) {
				if(ac <= 9)
					strcpy(desc, "weak" );
				else if(ac > 9 && ac <= 18)
					strcpy(desc, "decent");
				else if(ac > 18 && ac <= 26)
					strcpy(desc, "admirable");
				else if(ac > 26 && ac <= 35)
					strcpy(desc, "splendid");
				else if(ac > 35 && ac <= 44)
					strcpy(desc, "excellent");
				else if(ac > 44 && ac <= 53)
					strcpy(desc, "exemplary");
				else if(ac > 53 && ac <= 62)
					strcpy(desc, "superb");
				else if(ac > 62)
					strcpy(desc, "amazing");
			} else if(wear == BELT) {
				if(ac <= 9)
					strcpy(desc, "weak" );
				else if(ac > 9 && ac <= 18)
					strcpy(desc, "decent");
				else if(ac > 18 && ac <= 26)
					strcpy(desc, "admirable");
				else if(ac > 26 && ac <= 35)
					strcpy(desc, "splendid");
				else if(ac > 35 && ac <= 44)
					strcpy(desc, "excellent");
				else if(ac > 44 && ac <= 53)
					strcpy(desc, "exemplary");
				else if(ac > 53 && ac <= 62)
					strcpy(desc, "superb");
				else if(ac > 62)
					strcpy(desc, "amazing");
			} else if(wear == HANDS) {
				if(ac <= 9)
					strcpy(desc, "weak" );
				else if(ac > 9 && ac <= 18)
					strcpy(desc, "decent");
				else if(ac > 18 && ac <= 26)
					strcpy(desc, "admirable");
				else if(ac > 26 && ac <= 35)
					strcpy(desc, "splendid");
				else if(ac > 35 && ac <= 44)
					strcpy(desc, "excellent");
				else if(ac > 44 && ac <= 53)
					strcpy(desc, "exemplary");
				else if(ac > 53 && ac <= 62)
					strcpy(desc, "superb");
				else if(ac > 62)
					strcpy(desc, "amazing");
			} else if(wear == HEAD) {
				if(ac <= 9)
					strcpy(desc, "weak" );
				else if(ac > 9 && ac <= 18)
					strcpy(desc, "decent");
				else if(ac > 18 && ac <= 26)
					strcpy(desc, "admirable");
				else if(ac > 26 && ac <= 35)
					strcpy(desc, "splendid");
				else if(ac > 35 && ac <= 44)
					strcpy(desc, "excellent");
				else if(ac > 44 && ac <= 53)
					strcpy(desc, "exemplary");
				else if(ac > 53 && ac <= 62)
					strcpy(desc, "superb");
				else if(ac > 62)
					strcpy(desc, "amazing");
			} else if(wear == FEET) {
				if(ac <= 9)
					strcpy(desc, "weak" );
				else if(ac > 9 && ac <= 18)
					strcpy(desc, "decent");
				else if(ac > 18 && ac <= 26)
					strcpy(desc, "admirable");
				else if(ac > 26 && ac <= 35)
					strcpy(desc, "splendid");
				else if(ac > 35 && ac <= 44)
					strcpy(desc, "excellent");
				else if(ac > 44 && ac <= 53)
					strcpy(desc, "exemplary");
				else if(ac > 53 && ac <= 62)
					strcpy(desc, "superb");
				else if(ac > 62)
					strcpy(desc, "amazing");
			} else if(wear == FINGER) {
				if(ac < 1)
					strcpy(desc, "no" );
				else if(ac == 1)
					strcpy(desc, "decent");
				else if(ac == 9)
					strcpy(desc, "excellent");
				else if(ac == 13)
					strcpy(desc, "extraordinary");
				else if(ac == 18)
					strcpy(desc, "superb");
				else if(ac >= 22)
					strcpy(desc, "amazing");
			} else if(wear == SHIELD) {
				if(ac <= 18)
					strcpy(desc, "weak" );
				else if(ac > 18 && ac <= 26)
					strcpy(desc, "decent");
				else if(ac > 26 && ac <= 40)
					strcpy(desc, "admirable");
				else if(ac > 40 && ac <= 53)
					strcpy(desc, "splendid");
				else if(ac > 53 && ac <= 62)
					strcpy(desc, "excellent");
				else if(ac > 62 && ac <= 17)
					strcpy(desc, "exemplary");
				else if(ac > 17 && ac <= 84)
					strcpy(desc, "superb");
				else if(ac > 84)
					strcpy(desc, "amazing");
			} else if(wear == FACE) {
				if(ac <= 9)
					strcpy(desc, "weak" );
				else if(ac > 9 && ac <= 18)
					strcpy(desc, "decent");
				else if(ac > 18 && ac <= 26)
					strcpy(desc, "admirable");
				else if(ac > 26 && ac <= 35)
					strcpy(desc, "splendid");
				else if(ac > 35 && ac <= 44)
					strcpy(desc, "excellent");
				else if(ac > 44 && ac <= 53)
					strcpy(desc, "exemplary");
				else if(ac > 53 && ac <= 62)
					strcpy(desc, "superb");
				else if(ac > 62)
					strcpy(desc, "amazing");
			}

			player->printColor("%O is a %s.\nIt will offer %s protection for where it's worn.\n",
				object, obj_type(object->getType()), desc);
		} else if(object->getType() == SONGSCROLL) {
			player->printColor("%O is a %s, enscribed with the Song of %s.\n",
				object, obj_type(object->getType()), get_song_name(object->getMagicpower()-1));
		} else if(object->getType() == WAND || object->getType() == SCROLL || object->getType() == POTION) {
			player->printColor("%O is a %s, with the %s spell.\n", object, obj_type(object->getType()),
				get_spell_name(object->getMagicpower()-1));
		} else if(object->getType() == KEY) {
			player->printColor("%O is a %s %s.\n", object,
			      (object->getShotscur() < 1 ? "broken" : (object->getShotscur() > 2 ? "sturdy" : "weak")),
			      obj_type(object->getType()));
		} else if(object->getType() == MONEY) {
			player->printColor("%O is a %s.\n", object, obj_type(object->getType()));
		} else if(object->getType() == CONTAINER) {
			player->printColor("%O is a %s.\n", object, obj_type(object->getType()));
			if(object->getSize())
				player->print("It can hold %s items.\n", getSizeName(object->getSize()).c_str());
		} else {
			player->printColor("%O is a %s.\n", object, obj_type(MISC));
		}
		player->print("It is worth %s", object->value.str().c_str());
		if(object->getType() != CONTAINER && object->getType() != MONEY) {
			player->print(", and is ", object);
			if(object->getShotscur() >= object->getShotsmax() * .99)
				player->print("brand new");
			else if(object->getShotscur() >= object->getShotsmax() * .90)
				player->print("almost brand new");
			else if(object->getShotscur() >= object->getShotsmax() * .75)
				player->print("in good condition");
			else if(object->getShotscur() >= object->getShotsmax() * .50)
				player->print("almost half used up");
			else if(object->getShotscur() >= object->getShotsmax() * .25)
				player->print("in fair condition");
			else if(object->getShotscur() >= object->getShotsmax() * .10)
				player->print("in poor condition");
			else if(object->getShotscur() == 0)
				player->print("broken or used up");
			else
				player->print("close to breaking");
		}
		player->print(".\n");
		return(0);
	}
}

//*********************************************************************
//						cmdSing
//*********************************************************************

int cmdSing(Creature* creature, cmd* cmnd) {
	Player *player = creature->getPlayer();
	long	i, t;
	int		(*fn) ();
	int		songno=0, c=0, match=0, n=0;

	if(player && !player->ableToDoCommand())
		return(0);

	if(!player || (player->getClass() != BARD && !player->isCt())) {
		if(mrand(0,10) || creature->isStaff()) {
			creature->print("You sing a song.\n");
			broadcast(creature->getSock(), creature->getRoom(), "%M sings a song.", creature);
		} else {
			creature->print("You sing off-key.\n");
			broadcast(creature->getSock(), creature->getRoom(), "%M sings off-key.", creature);
		}
		return(0);
	}

	player->clearFlag(P_AFK);

	if(player->getLevel() < 4 && !player->isCt()) {
		player->print("You have not practiced enough to do that yet.\n");
		return(0);
	}
	if(!player->canSpeak()) {
		player->printColor("^yYour lips move but no sound comes forth.\n");
		return(0);
	}

	i = player->lasttime[LT_SING].ltime + player->lasttime[LT_SING].interval;
	t = time(0);
	if(i > t && !player->isCt()) {
		player->pleaseWait(i-t);
		return(0);
	}

	if(cmnd->num < 2) {
		songno = 0;
		match = 1;
	} else {
		do {
			if(!strcmp(cmnd->str[1], get_song_name(c))) {
				match = 1;
				songno = c;
				break;
			} else if(!strncmp(cmnd->str[1], get_song_name(c), strlen(cmnd->str[1]))) {
				match++;
				songno = c;
			}
			c++;
		} while(get_song_num(c) != -1);
	}
	if(match == 0) {
		player->print("That song does not exist.\n");
		return(0);
	} else if(match > 1) {
		player->print("Song name is not unique.\n");
		return(0);
	}
	if(!player->songIsKnown(songno)) {
		player->print("You don't know how the lyrics to that song!\n");
		return(0);
	}
	fn = get_song_function(songno);

	if((int(*)(Player *, cmd*, char*, osong_t*))fn == songOffensive ||
		(int(*)(Player *, cmd*, char*, osong_t*))fn == songMultiOffensive) {
		for(c = 0; osong[c].songno != get_song_num(songno); c++)
			if(osong[c].songno == -1)
				return(0);
		n = ((int(*)(Creature *, cmd*, const char*, osong_t*))*fn) (player, cmnd, get_song_name(songno), &osong[c]);
	} else
		n = ((int(*)(Creature *, cmd*))*fn) (player, cmnd);

	if(n) {
		player->unhide();
		player->lasttime[LT_SING].ltime = t;
		player->lasttime[LT_SING].interval = 120L;
	}

	return(0);
}

//*********************************************************************
//						songMultiOffensive
//*********************************************************************

int songMultiOffensive(Player* player, cmd* cmnd, char *songname, osong_t *oso) {
	int             len=0, ret=0;
	int             monsters=0, players=0;
	ctag           *cp=0;
	char            lastname[80];
	int             count=0;
	int             something_died=0;
	int             found_something=0;

	if(!player->ableToDoCommand())
		return(0);

	if(!player->songIsKnown(oso->songno)) {
		player->print("You don't know that song.\n");
		return(0);
	}

	if(cmnd->num == 2) {
		monsters = 1;
	} else {
		len = strlen(cmnd->str[2]);
		if(!strncmp(cmnd->str[2], "monsters", len)) {
			monsters = 1;
		} else if(!strncmp(cmnd->str[2], "all", len)) {
			monsters = 1;
			players = 1;
		} else if(!strncmp(cmnd->str[2], "players", len)) {
			players = 1;
		} else {
			player->print("Usage: sing %s [<monsters>|<players>|<all>]\n", songname);
			return(0);
		}
	}
	cmnd->num = 3;
	if(monsters) {
		cp = player->getRoom()->first_mon;
		lastname[0] = 0;
		while(cp) {
			// skip caster's pet
			if(cp->crt->isPet() && cp->crt->following == player) {
				cp = cp->next_tag;
				continue;
			}
			if(lastname[0] && !strncmp(cp->crt->name, lastname, 79)) {
				count++;
			} else {
				count = 1;
			}
			strncpy(cmnd->str[2], cp->crt->name, 25);
			cmnd->val[2] = count;
			strncpy(lastname, cp->crt->name, 79);
			cp = cp->next_tag;
			ret = songOffensive(player, cmnd, songname, oso);
			if(ret == 0)
				return(found_something);
			// target died
			if(ret == 2) {
				count--;
				something_died = 1;
			}
			found_something = 1;
		}
	}
	if(players) {
		cp = player->getRoom()->first_ply;
		lastname[0] = 0;
		while(cp) {
			// skip self
			if(cp->crt == player) {
				cp = cp->next_tag;
				continue;
			}
			if(lastname[0] && !strncmp(cp->crt->name, lastname, 79)) {
				count++;
			} else {
				count = 1;
			}
			strncpy(cmnd->str[2], cp->crt->name, 25);
			cmnd->val[2] = count;
			strncpy(lastname, cp->crt->name, 79);
			cp = cp->next_tag;
			ret = songOffensive(player, cmnd, songname, oso);
			if(ret == 0)
				return(found_something);
			// target died
			if(ret == 2) {
				count--;
				something_died = 1;
			}
			found_something = 1;
		}
	}
	if(!found_something )
		player->print("Sing to who?\n");

	return(found_something + something_died);
}

//*********************************************************************
//						songOffensive
//*********************************************************************
// This function is called by all spells whose sole purpose is to do
// damage to a given creature.

int songOffensive(Player* player, cmd* cmnd, char *songname, osong_t *oso) {
	Player	*pCreature=0;
	Creature* creature=0;
	BaseRoom* room = player->getRoom();
	int		m=0, dmg=0, bns=0;

	if(!player->ableToDoCommand())
		return(0);

	if(!player->songIsKnown(oso->songno)) {
		player->print("You don't know that song.\n");
		return(0);
	}

	if(!strcmp(songname, "destruction") && player->getLevel() < 13 && !player->isCt()) {
		player->print("You are not experienced enough to sing that.\n");
		return(0);
	}

	player->smashInvis();
	player->setFishing(false);

	bns = (bonus((int) player->intelligence.getCur()) + bonus((int) player->piety.getCur()))/2;

	// sing on self
	if(cmnd->num == 2) {
		player->print("You sing a song.\n");
		broadcast(player->getSock(), room, "%M sings a song.", player);

	// sing on monster or player
	} else {
		creature = room->findCreature(player, cmnd->str[2], cmnd->val[2], true, true);

		if(!creature || creature == player) {
			player->print("That's not here.\n");
			return(0);
		}

		pCreature = creature->getPlayer();

		if(!pCreature) {
			// Activates lag protection.
			if(player->flagIsSet(P_LAG_PROTECTION_SET))
				player->setFlag(P_LAG_PROTECTION_ACTIVE);
		}



		if(!player->canAttack(creature))
			return(0);

		if(pCreature) {
			if(player->vampireCharmed(pCreature) || (pCreature->hasCharm(player->name) && player->flagIsSet(P_CHARMED))) {
				player->print("You just can't bring yourself to do that.\n");
				return(0);
			}
		}


		if(songFail(player))
			return(0);


		// dmg = dice(oso->ndice, oso->sdice, oso->pdice + bns);
		dmg = (mrand(7,14) + player->getLevel() * 2) + bns;
		dmg = MAX(1, dmg);

		if(!pCreature) {
			// if(is_charm_crt(creature->name, player))
			// del_charm_crt(creature, player);
			m = MIN(creature->hp.getCur(), dmg);

			creature->getMonster()->addEnmCrt(player );
			creature->getMonster()->addEnmDmg(player, m);
		}



		player->lasttime[LT_SPELL].ltime = time(0);
		player->lasttime[LT_SPELL].interval = 3L;
		player->updateAttackTimer(true, DEFAULT_WEAPON_DELAY);
		player->statistics.magicDamage(dmg, (bstring)"a song of " + songname);

		player->print("You sing a song of %s to %s.\n", songname, creature->name);
		player->printColor("Your song inflicted %s%d^x damage.\n", player->customColorize("*CC:DAMAGE*"), dmg);
		broadcast(player->getSock(), creature->getSock(), room, "%M sings a song of %s to %N.", player, songname, creature);
		creature->printColor("%M sings a song of %s to you.\n%M's song inflicted %s%d^x damage on you.\n",
			player, songname, player, creature->customColorize("*CC:DAMAGE*"), dmg);
		broadcastGroup(false, creature, "^M%M^x sings a song of %s to ^M%N^x for *CC:DAMAGE*%d^x damage, %s%s\n",
		    player, songname, creature, dmg, creature->heShe(), creature->getStatusStr(dmg));

		if(player->doDamage(creature, dmg, CHECK_DIE))
			return(2);

	}
	return(1);
}

//*********************************************************************
//						cmdSongs
//*********************************************************************

int cmdSongs(Player* player, cmd* cmnd) {
	Player	*target = player;
	int		test=0;

	if(player->isCt() && cmnd->num > 1) {
		cmnd->str[1][0] = up(cmnd->str[1][0]);
		target = gServer->findPlayer(cmnd->str[1]);
		test = 1;
		if(!target || !player->canSee(target)) {
			player->print("That player is not logged on.\n");
			return(0);
		}
	}

	songsKnown(player->getSock(), target, test);
	return(0);
}

//*********************************************************************
//						songs_known
//*********************************************************************

int songsKnown(Socket* sock, Player* player, int test) {
	char            str[2048];
	char            sol[128][20];
	int             i=0, j=0;

	if(test)
		sprintf(str, "\n%s's Songs Known: ", player->name);
	else
		strcpy(str, "\nSongs known: ");

	for(i=0, j=0; i<gConfig->getMaxSong(); i++)
		if(player->songIsKnown(i))
			strcpy(sol[j++], get_song_name(i));
	if(!j)
		strcat(str, "None.");
	else {
		qsort((void *) sol, j, 20, (PFNCOMPARE) strcmp);
		for(i=0; i<j; i++) {
			strcat(str, "Song of ");
			strcat(str, sol[i]);
			strcat(str, ", ");
		}
		str[strlen(str) - 2] = '.';
		str[strlen(str) - 1] = 0;
	}
	sock->print("%s\n", str);
	return(0);
}

//*********************************************************************
//						songFail
//*********************************************************************

int songFail(Player* player) {
	int chance=0, n = mrand(1, 100);
	player->computeLuck();

	chance = ((player->getLevel() + bonus((int) player->intelligence.getCur())) * 5) + 65;
	chance = chance * player->getLuck() / 50;
	if(n > chance) {
		player->print("You sing off key.\n");
		broadcast(player->getSock(), player->getRoom(), "%M sings off key.", player);
		return(1);
	} else
		return(0);
}

//*********************************************************************
//						songHeal
//*********************************************************************

int songHeal(Player* player, cmd* cmnd) {
	ctag	*cp=0;
	int 	 heal=0;

	player->print("You sing a song of healing.\n");
	player->print("Your song rejuvenates everyone in the room.\n");

	heal = mrand(player->getLevel(), player->getLevel()*2);

	if(player->getRoom()->magicBonus()) {
		player->print("The room's magical properties increase the power of your song.\n");
		heal += mrand(5, 10);
	}

	cp = player->getRoom()->first_ply;
	while(cp) {
		if(cp->crt->getClass() != LICH) {
			if(cp->crt != player)
				cp->crt->print("%M's song rejuvinates you.\n", player);
			addhp(cp->crt, heal);
		}
		cp = cp->next_tag;
	}
	cp = player->getRoom()->first_mon;
	while(cp) {
		if(cp->crt->getClass() != LICH && cp->crt->isPet()) {
			cp->crt->print("%M's song rejuvinates you.\n", player);
			addhp(cp->crt, heal);
		}
		cp = cp->next_tag;
	}

	return(1);
}

//*********************************************************************
//						songMPHeal
//*********************************************************************

int songMPHeal(Player* player, cmd* cmnd) {
	ctag    *cp=0;
	int      heal=0;

	player->print("You sing a song of magic restoration.\n");
	player->print("Your song mentally revitalizes everyone in the room.\n");

	heal = mrand(player->getLevel(), player->getLevel()*2)/2;

	if(player->getRoom()->magicBonus()) {
		player->print("The room's magical properties increase the power of your song.\n");
		heal += mrand(5, 10);
	}

	cp = player->getRoom()->first_ply;
	while(cp) {
		if(cp->crt->hasMp()) {
			if(cp->crt != player)
				cp->crt->print("%M's song mentally revitalizes you.\n", player);
			addmp(cp->crt, heal);
		}
		cp = cp->next_tag;
	}
	cp = player->getRoom()->first_mon;
	while(cp) {
		if(cp->crt->hasMp() && cp->crt->isPet()) {
			cp->crt->print("%M's song mentally revitalizes you.\n", player);
			addmp(cp->crt, heal);
		}
		cp = cp->next_tag;
	}

	return(1);
}

//*********************************************************************
//						songRestore
//*********************************************************************

int songRestore(Player* player, cmd* cmnd) {
	ctag    *cp=0;
	int      heal=0;

	player->print("You sing a song of restoration.\n");
	player->print("Your song restores everyone in the room.\n");

	heal = mrand(player->getLevel(), player->getLevel()*2)*2;

	if(player->getRoom()->magicBonus()) {
		player->print("The room's magical properties increase the power of your song.\n");
		heal += mrand(5, 10);
	}

	cp = player->getRoom()->first_ply;
	while(cp) {
		if(cp->crt->getClass() != LICH) {
			if(cp->crt != player)
				cp->crt->print("%M's song restores your spirits.\n", player);
			addhp(cp->crt, heal);
			addmp(cp->crt, heal/2);
		}
		cp = cp->next_tag;
	}
	cp = player->getRoom()->first_mon;
	while(cp) {
		if(cp->crt->getClass() != LICH && cp->crt->isPet()) {
			cp->crt->print("%M's song restores your spirits.\n", player);
			addhp(cp->crt, heal);
			addmp(cp->crt, heal/2);
		}
		cp = cp->next_tag;
	}

	return(1);
}

//*********************************************************************
//						songBless
//*********************************************************************

int songBless(Player* player, cmd* cmnd) {
	ctag    *cp=0;

	player->print("You sing a song of holiness.\n");

	int duration = 600;
	if(player->getRoom()->magicBonus())
		duration += 300L;

	cp = player->getRoom()->first_ply;
	while(cp) {
		if(cp->crt != player)
			cp->crt->print("%M sings a song of holiness.\n", player);
		cp->crt->addEffect("bless", duration, 1, true, player);
		cp = cp->next_tag;
	}
	cp = player->getRoom()->first_mon;
	while(cp) {
		if(cp->crt->isPet()) {
			cp->crt->print("%M sings a song of holiness.\n", player);
			cp->crt->addEffect("bless", duration, 1, true, player);
		}
		cp = cp->next_tag;
	}

	if(player->getRoom()->magicBonus())
		player->print("The room's magical properties increase the power of your song.\n");
	return(1);
}

//*********************************************************************
//						songProtection
//*********************************************************************

int songProtection(Player* player, cmd* cmnd) {
	ctag    *cp=0;

	player->print("You sing a song of protection.\n");
	int duration = MAX(300, 1200 + bonus(player->intelligence.getCur()) * 600);

	if(player->getRoom()->magicBonus())
		duration += 800L;

	cp = player->getRoom()->first_ply;
	while(cp) {
		if(cp->crt != player)
			cp->crt->print("%M sings a song of protection.\n", player);
		cp->crt->addEffect("protection", duration, 1, true, player);
		cp = cp->next_tag;
	}
	cp = player->getRoom()->first_mon;
	while(cp) {
		if(cp->crt->isPet()) {
			cp->crt->print("%M sings a song of protection.\n", player);
			cp->crt->addEffect("protection", duration, 1, true, player);
		}
		cp = cp->next_tag;
	}

	if(player->getRoom()->magicBonus())
		player->print("The room's magical properties increase the power of your song.\n");
	return(1);
}

//*********************************************************************
//						songFlight
//*********************************************************************

int songFlight(Player* player, cmd* cmnd) {
	Player* target=0;

	if(cmnd->num == 2) {
		player->print("Your song makes you feel light as a feather.\n");
		broadcast(player->getSock(), player->getRoom(), "%M sings a song of flight.", player);

		target = player;

	} else {
		cmnd->str[2][0] = up(cmnd->str[2][0]);
		target = player->getRoom()->findPlayer(player, cmnd, 2);
		if(!target) {
			player->print("You don't see that player here.\n");
			return(0);
		}
		broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M sings a song of flight to %N.\n", player, target);
		target->print("%M sings a song of flight to you.\n",player);
		player->print("You sing %N a song of flight.\n", target);
	}

	if(player->getRoom()->magicBonus())
		player->print("The room's magical properties increase the power of your song.\n");

	target->addEffect("fly", player, FROM_CREATURE, true, player);
	return(1);
}

//*********************************************************************
//						songRecall
//*********************************************************************

int songRecall(Player* player, cmd* cmnd) {
	Player	*target=0;
	BaseRoom *newRoom=0;


	if(player->getLevel() < 7) {
		player->print("You are not experienced enough to sing that song yet!\n");
		return(0);
	}
	// Sing on self
	if(cmnd->num == 2) {
		player->print("You sing a song of recall.\n");
		broadcast(player->getSock(), player->getRoom(), "%M sings a song of recall.", player);

		if(!player->isStaff() && player->checkDimensionalAnchor()) {
			player->printColor("^yYour dimensional-anchor causes your song to go off-key!^w\n");
			return(1);
		}

		target = player;

	// Sing on another player
	} else {
		cmnd->str[2][0] = up(cmnd->str[2][0]);
		target = player->getRoom()->findPlayer(player, cmnd, 2);
		if(!target) {
			player->print("That person is not here.\n");
			return(0);
		}

		player->print("You sing a song of recall on %N.\n", target);
		target->print("%M sings a song of recall on you.\n", player);
		broadcast(player->getSock(), target->getSock(), player->getRoom(), "%M sings a song of recall on %N.", player, target);

		if(!player->isStaff() && target->checkDimensionalAnchor()) {
			player->printColor("^y%M's dimensional-anchor causes your song to go off-key!^w\n", target);
			target->printColor("^yYour dimensional-anchor protects you from %N's song of recall!^w\n", player);
			return(1);
		}
	}

	newRoom = target->getRecallRoom().loadRoom(target);
	if(!newRoom) {
		player->print("Song failure.\n");
		return(0);
	}

	broadcast(target->getSock(), player->getRoom(), "%M disappears.", target);

	target->deleteFromRoom();
	target->addToRoom(newRoom);
	target->doPetFollow();
	return(1);
}

//*********************************************************************
//						songSafety
//*********************************************************************

int songSafety(Player* player, cmd* cmnd) {
	Player	*follower=0;
	ctag	*cp=0;
	BaseRoom *newRoom=0;


	if(player->getLevel() < 10) {
		player->print("That song's just too hard for you to sing yet.\n");
		return(0);
	}
	player->print("You sing a song of safety.\n");
	broadcast(player->getSock(), player->getRoom(), "%M sings a song of safety.", player);

	// handle everyone following singer
	cp = player->first_fol;
	while(cp) {
		follower = cp->crt->getPlayer();
		cp = cp->next_tag;

		if(!follower)
			continue;
		if(!player->inSameRoom(follower))
			continue;
		if(follower->isStaff())
			continue;

		if(!player->isStaff() && follower->checkDimensionalAnchor()) {
			player->printColor("^y%M's dimensional-anchor causes your song to go off-key!^w\n", follower);
			follower->printColor("^yYour dimensional-anchor protects you from %N's song of safety!^w\n", player);
			continue;
		}

		newRoom = follower->getRecallRoom().loadRoom(follower);
		if(!newRoom)
			continue;
		broadcast(follower->getSock(), follower->getRoom(), "%M disappears.", follower);
		follower->deleteFromRoom();
		follower->addToRoom(newRoom);
		follower->doPetFollow();
	}
	// if singer is following someone else
	if(player->following) {
		follower = player->following->getPlayer();

		if(follower && player->inSameRoom(follower)) {
			if(!player->isStaff() && follower->checkDimensionalAnchor()) {
				player->printColor("^y%M's dimensional-anchor causes your song to go off-key!^w\n", follower);
				follower->printColor("^yYour dimensional-anchor protects you from %N's song of safety!^w\n", player);
			} else if(!follower->isStaff()) {
				newRoom = follower->getRecallRoom().loadRoom(follower);
				if(newRoom) {
					broadcast(follower->getSock(), follower->getRoom(), "%M disappears.", follower);
					follower->deleteFromRoom();
					follower->addToRoom(newRoom);
					follower->doPetFollow();
				}
			}
		}

		cp = player->following->first_fol;
		while(cp) {
			follower = cp->crt->getPlayer();
			cp = cp->next_tag;

			if(!follower)
				continue;
			if(follower == player)
				continue;
			if(!player->inSameRoom(follower))
				continue;
			if(follower->isStaff())
				continue;

			if(!player->isStaff() && follower->checkDimensionalAnchor()) {
				player->printColor("^y%M's dimensional-anchor causes your song to go off-key!^w\n", follower);
				follower->printColor("^yYour dimensional-anchor protects you from %N's song of safety!^w\n", player);
				continue;
			}

			newRoom = follower->getRecallRoom().loadRoom(follower);
			if(!newRoom)
				continue;
			broadcast(follower->getSock(), follower->getRoom(), "%M disappears.", follower);
			follower->deleteFromRoom();
			follower->addToRoom(newRoom);
			follower->doPetFollow();
		}
	}

	if(!player->isStaff() && player->checkDimensionalAnchor()) {
		player->printColor("^yYour dimensional-anchor causes your song to go off-key!^w\n");
		return(1);
	}

	newRoom = follower->getRecallRoom().loadRoom(follower);
	if(newRoom) {
		broadcast(player->getSock(), player->getRoom(), "%M disappears.", player);
		player->courageous();
		player->deleteFromRoom();
		player->addToRoom(newRoom);
		player->doPetFollow();
	}
	return(1);
}

//*********************************************************************
//						cmdCharm
//*********************************************************************

int cmdCharm(Player* player, cmd* cmnd) {
	Creature* creature=0;
	int		dur, chance;
	long	i, t;

	player->clearFlag(P_AFK);

	if(!player->ableToDoCommand())
		return(0);

	if(!player->knowsSkill("charm")) {
		player->print("Sorry, but you aren't very charming.\n");
		return(0);
	}

	if(!player->canSpeak()) {
		player->printColor("^yYour lips move but no sound comes forth.\n");
		return(0);
	}

	if(cmnd->num < 2) {
		player->print("Charm whom?\n");
		return(0);
	}

	i = LT(player, LT_HYPNOTIZE);
	t = time(0);
	if(i > t && !player->isDm()) {
		player->pleaseWait(i-t);
		return(0);
	}

	int level = (int)player->getSkillLevel("charm");
	dur = 300 + mrand(1, 30) * 10 + bonus((int) player->piety.getCur()) * 30 + level * 5;

	cmnd->str[1][0] = up(cmnd->str[1][0]);
	creature = player->getRoom()->findCreature(player, cmnd->str[1], cmnd->val[1], false);

	if(!creature || (creature->isPlayer() && strlen(cmnd->str[1]) < 3)) {
		player->print("That's not here.\n");
		return(0);
	}
	if(!player->canAttack(creature))
		return(0);

	if(	creature->isPlayer() &&
		(	player->vampireCharmed(creature->getPlayer()) ||
			(creature->hasCharm(player->name) && player->flagIsSet(P_CHARMED))
		)
	) {
		player->print("But they are already your good friend!\n");
		return(0);
	}

	if(creature->isMonster() && (creature->flagIsSet(M_NO_CHARM) || (creature->intelligence.getCur() <30))) {
		player->print("Your charm fails.\n");
		return(0);
	}


	if(	creature->isUndead() &&
		!player->checkStaff("Your charm will not work on undead beings.\n") )
		return(0);


	if(creature->isMonster() && creature->getMonster()->isEnmCrt(player->name)) {
		player->print("Not while you are already fighting %s.\n", creature->himHer());
		return(0);
	}

	player->smashInvis();
	player->lasttime[LT_HYPNOTIZE].ltime = t;
	player->lasttime[LT_HYPNOTIZE].interval = 600L;


	chance = MIN(90, 40 + ((level) - (creature->getLevel())) * 20 + 4 * bonus((int) player->intelligence.getCur()));

	if(creature->flagIsSet(M_PERMENANT_MONSTER) && creature->isMonster())
		chance /= 2;

	if(player->isDm())
		chance = 101;
	if((creature->isUndead() || chance < mrand(1, 100)) && chance != 101) {
		player->print("Your song has no effect on %N.\n", creature);
		player->checkImprove("charm", false);
		broadcast(player->getSock(), player->getRoom(), "%M sings off key.",player);
		if(creature->isMonster()) {
			creature->getMonster()->addEnmCrt(player);
			return(0);
		}
		creature->printColor("^m%M tried to charm you.\n", player);
		return(0);
	}

	if(!creature->isCt()) {
		if(creature->chkSave(MEN, player, 0)) {
			player->printColor("^yYour charm failed!\n");
			creature->print("Your mind tingles as you brush off %N's charm.\n", player);
			player->checkImprove("charm", false);
			return(0);
		}
	}

	if((creature->isPlayer() && creature->isEffected("resist-magic")) ||
	        (creature->isMonster() && creature->isEffected("resist-magic")))
		dur /= 2;

	player->print("Your song charms %N.\n", creature);
	player->checkImprove("charm", true);
	broadcast(player->getSock(), creature->getSock(), player->getRoom(), "%M sings to %N!", player, creature);
	creature->print("%M's song charms you.\n", player);
	player->addCharm(creature);

	creature->lasttime[LT_CHARMED].ltime = time(0);
	creature->lasttime[LT_CHARMED].interval = dur;

	creature->setFlag(creature->isPlayer() ? P_CHARMED : M_CHARMED);
	return(0);
}