Архивариус, встречающий всех при входе в мир. Он отвечает за создание новых персонажей
и за вход в мир старых.

nannyMobsInit = function() {

if (.tmp.nanny == null)
    .tmp.nanny = .Map();

this = .tmp.nanny;

/*
 * global entry point for all first connected characters
 */
onConnect = function(ch) {
    ch.nanny = NannyState(ch);
    ch.nanny.thread.start();
};
    
/*
 * main command handler for nanny character
 */
onInput = function(ch, line) {
    if (ch.nanny != null
	&& ch.nanny.cmdHandler != null 
	&& ch.nanny.cmdHandler(line))
    {
	ch.interpret_cmd("say", line);
	return;
    }

    if (!ch.interpret(line))
	ch.interpret_cmd("say", line);
};


/*
 * Archivarius constructor
 */
Archivarius = function(vnum) {
    this = .get_mob_index(vnum);
    
    init = function(mob) {
	.tmp.nanny.archivarius = mob;
    };
    
    onSpeech = function(mob, ch, msg) {
	if (ch.nanny == null)
	    return;

	if (!ch.nanny.expectSpeech) 
	    return;
	
	ch.nanny.speech = msg;
	ch.nanny.expectSpeech = false;
    };
    
    return this;
};

if (.buildplot)
    Archivarius(8);
else
    Archivarius(7);

/*
 * NannyState
 */
NannyState = function(ch) {
    this = .Map();
    
    expectSpeech = false;
    speech = "";
    this.ch = ch;
    this.mob = .tmp.nanny.archivarius;

    yield = function() {
	.scheduler.yield();
	
	try {
	    if (mob.in_room != ch.in_room)
		throw "victim left the room";
	} catch (e) {
	    throw "quit";
	}
    };

    sleep = function(amt) {
	var i;

	for(i = 0; i < amt; i = i + 1)
	    yield();
    };

    thread = .scheduler.Thread(function (this) {
	try {
	    task();
	} catch(e) {
	    if (e != "extract" && e != "quit")
		.print("nanny: " + e);
	}
	
	if (ch.nanny != null) {
	    ch.nanny.thread = null;
	    ch.nanny = null;
	}
    }, this);
    
    /*
     * let archivarius express himself
     */
    say0 = function(msg) {
	ch.act("%1$^C1 произносит '{g" + msg + "{x'", mob, ch);
    };
    say = function(msg) {
	say0(msg);
	ch.recho(
  	    "%1$^C1 произносит, обращаясь к %2$C3: '{g" + msg + "{x'", mob, ch);
    };

    ask = function(msg) {
	ch.act("%1$^C1 спрашивает '{g" + msg + "{x'", mob, ch);
	ch.recho(
	    "%1$^C1 спрашивает у %2$C2 '{g" + msg + "{x'", mob, ch);
    };
    
    excl0 = function(msg) {
	ch.act("%1$^C1 восклицает '{g" + msg + "{x'", mob, ch);
    };
    excl = function(msg) {
	excl0(msg);
	ch.recho(
	    "%1$^C1 восклицает, обращаясь к %2$C3: '{g" + msg + "{x'", mob, ch);
    };

    emote = function(msg) {
	ch.act("%1$^C1 " + msg, mob, ch);
    };
    
    /*
     * leave the realm (descriptor close)
     */
    quit = function() {
	ch.interpret_raw("quit");
	throw "quit";
    };
    
    /*
     * useful  patterns
     */
    patternYes = "^да$|^yes$|^угу$|^ага$|^д$|^y$|^da$";
    patternNo = "^no$|^нет$|^н$|^n$|^net$";

    /*
     * waiting for character's speech
     */
    waitSpeech = function() {
	for (;;) {
	    speech = null;
	    expectSpeech = true;
	    
	    for ( ; speech == null; )
		yield();

	    break;
	}

	return speech;
    };

    waitConfirm = function() {
	for (;;) {
	    var msg;

	    cmdHandler = function(cmd) {
		return cmd.match(patternYes) || cmd.match(patternNo);
	    };
	    msg = waitSpeech();
	    cmdHandler = null;

	    if (msg == "")
		continue;

	    if (msg.match(patternYes))
		return true;
	    
	    if (msg.match(patternNo))
		return false;
	}
    };
    

    /*
     * initial setup for connected characters
     */
    init = function() {
	if (.buildplot)
	    ch.char_to_room(.get_room_index(8));
	else
	    ch.char_to_room(.get_room_index(7));
	initStranger();
	ch.interpret_raw("look");
    };
    
    /*
     * initialization: stranger
     */
    initStranger = function() {
	if (ch.name == "") {
	    ch.name = "Stranger";
	    ch.russianName = "Незнаком|ец|ца|цу|ца|цем|це";
	}

	ch.lines = 40;
	ch.add_comm = .tables.add_comm_flags.autostore 
		      |.tables.add_comm_flags.nocancel
		      |.tables.add_comm_flags.autolook;
	ch.config = .tables.config_flags.fightspam 
		   |.tables.config_flags.skillspam
		   |.tables.config_flags.objname_hint
		   |.tables.config_flags.newdamage;
	ch.act = .tables.plr_flags.color
		   |.tables.plr_flags.autoexit
		   |.tables.plr_flags.autoloot
		   |.tables.plr_flags.autogold
		   |.tables.plr_flags.nosummon
		   |.tables.plr_flags.nofollow;
	ch.comm = .tables.comm_flags.prompt
	           |.tables.comm_flags.combine;
    };

    /*
     * initialization: newbie or remorted character
     */
    initCreated = function() {
	ch.exp = ch.expToLevel;
	ch.level = 1;
	ch.train = 3;
	ch.practice = ch.practice + 5;
	ch.title = "%a";
	ch.setSkillLearned("recall", 75);
	ch.updateSkills();
	
	if (ch.prompt == "")
	    ch.prompt = "<%n: %h/%Hhp %m/%Mm %v/%Vmv %Xexp> ";
	if (ch.batle_prompt == "")
	    ch.batle_prompt = "<%n: %h/%Hhp %m/%Mm %v/%Vmv Opp:<%o>> ";
    };

    /*
     * main task entry point
     */
    task = function() {
	taskRemort();
	taskName();
	taskBan();

	if (.player_exists(ch.name)) {
	    taskPassword();
	    taskReconnect();
	    taskGreet();
	    taskRemort();
	    taskReadMotd();
	}
	else {
	    taskSex();
	    taskLanguage();
	    taskRace();
	    taskProfession();
	    taskAlignment();
	    taskEthos();
	    taskNewPassword();
	    taskRussianName();
	    taskGreetNewbie();
	    taskReadMotd();
	    ch.save();
	}
    };
    
    /*
     * remort
     */
    taskRemort = function() {
	if (!ch.hasAttribute("remorting"))
	    return;

	taskRace();
	taskProfession();
	taskAlignment();
	taskEthos();
	taskRussianName();
	taskGreetRemorted();
	taskReadMotd();

	ch.eraseAttribute("remorting");
	ch.save();
	throw "remorted";
    };

    /*
     * greet freshly created character
     */
    taskGreetNewbie = function() {
	excl("Добро пожаловать в Dream Land, %2$C1!");
	initCreated();
	ch.start_room = 40100;
	
	.nanny.notifyCreated(ch);
	.wiznet(.fmt("%C1@%s new player.", ch, ch.hostname), 
	        ch.trust, "newbie");
    };

    /*
     * greet remorted character
     */
    taskGreetRemorted = function() {
	initCreated();
	ch.start_room = 6;

	.wiznet(.fmt("%C1@%s has remorted.", ch, ch.hostname), 
	        ch.trust, "newbie");
    };

    /*
     * old character enters the realms
     */
    taskGreet = function() {
	ch = .nanny.load(ch);
	ch.nanny = this;
	ch.ptc("\r\n");
	excl0("Добро пожаловать в Dream Land, %2$C1!");
	say0("Последний раз я видел тебя здесь " + ch.lastAccessTime + ".");
    };
    
    /*
     * check newbie lock, wizlock, ban, deny
     */
    taskBan = function() {
	var reason;
	
	reason = .nanny.checkBan(ch);

	if (reason == null || reason == "")
	    return;

	if (reason == "newlock") {
	    if (.buildplot)
		say("Ты не записан в моих листиках! Поговори с богами, "
		    "если так сильно хочешь попасть сюда.");
	    else
		say("Сегодня мне велено пропускать только тех, кого я знаю.");
	}
	else if (reason == "deny") {
	    emote("сверяется с Книгой.");
	    say("Ты прогневил Богов, и они запретили тебе появляться в мире.");
	}
	else if (reason == "wizlock") {
	    say("Сожалею, но сегодня мне велено пропускать "
	        "только Бессмертных.");
	}
	else if ("ban".strPrefix(reason)) {
	    say("Ты пришел из плохого места.");
	    if (reason == "bannewbie")
		say("Оттуда я смогу пропустить только тех, кого знаю.");
	    else
		excl("Я никого оттуда в мир не пущу!");
	}
	    
	quit();
    };
    
    /*
     *  check if already playing or lostlinked
     */
    taskReconnect = function() {
	var twin;
	
	twin = .findPlayer(ch.name);
	
	if (twin == null)
	    return;

	if (twin.switchedTo != null)
	    twin = twin.switchedTo;

	if (!twin.connected) {
	    .nanny.reconnect(ch, twin);
	    twin.timer = 0;

	    twin.act("Reconnecting. Используй {Yreplay{x, "
	             "чтобы услышать, что тебе говорили.");
	    twin.recho("%1$^C1 восстанови%1$Gло|л|ла связь с этим миром.", twin);

	    .wiznet(.fmt("%1$^C1 восстанови%1$Gло|л|ла связь.", twin), 
	            twin.trust, "links");

	    .nanny.notifyReconnect(twin);
	    throw "extract";
	}
	else {
	    say("Этот персонаж уже в мире. Хочешь вселиться в его тело?");
	    
	    if (!waitConfirm()) {
		say("Всего хорошего.");
		quit();
	    }
	    
	    if (!.nanny.reanimate(ch, twin)) {
		say("Попытка вселиться не удалась. Заходи еще!");
		quit();
	    }
	    
	    twin.interpret_raw("look");
	    twin.recho("%1$^C1 восстанови%1$Gло|л|ла связь с этим миром.", twin);
	    .wiznet(.fmt("%1$^C1 снова вселил%1$Gось|ся|ась в свое тело.", twin), 
	            twin.trust, "links");

	    .nanny.notifyReconnect(twin);
	    throw "extract";
	}
    };

    /*
     * finish login process 
     */
    taskReadMotd = function() {
	var room;
	
	.nanny.link(ch); 
	ch.logon = .current_time;

	.infonet("{CТихий голос из $o2: {W$C1 появил$Gось|ся|ась в Dream Land.{x", 
	         ch);
	.wiznet(.fmt("%C1 входит в Dream Land.", ch), 
	        ch.trust, "logins");
	.wiznet(.fmt("Имена персонажа: %s [%s]", ch.name, ch.russianName), 
	        ch.trust, "rnames");

	if (ch.pet != null) {
            if (ch.pet.in_room != null)
                ch.pet.char_to_room(ch.pet.in_room);
            else 
	        ch.pet.char_to_room(ch.in_room);
        }
	
	room = .get_room_index(ch.start_room);
	ch.transfer(room, mob, "", 
		    "%2$^C1 переносит тебя в " + room.name + ".\n\n",
		    "%1$^C1 появил%1$Gось|ся|ась в этом мире.", "");

	if (ch.pet != null)
	    ch.pet.transfer(
	          ch.pet.in_room, mob, "", "",
	          "%1$^C1 появил%1$Gось|ся|ась в этом мире, вслед за хозяином.", 
	          "");
	
	.nanny.notifyPlaying(ch);
    };
     
    /*
     * check old password
     */
    taskPassword = function() {
	if (.buildplot) {
	    say("А ну-ка скажи пароль?");
	}
	else {
	    if (.chanceOneOf(3))
		say("%2$^C1? Точно? Тогда назови пароль!");
	    else if (.chanceOneOf(2))
		ask("Чем докажешь, что ты именно %2$^C1? Какой пароль?"); 
	    else
		say("Скажи пароль - тогда поверю, что ты %2$^C1.");
	}

	for (; !ackPassword(); )
	    ;

	.wiznet(.fmt("%C1@%s has connected.", ch, ch.hostname), 
	        ch.trust, "sites");
    };

    ackPassword = function() {
	var cnt, passwd;

	for (cnt = 1; cnt <= 3; cnt = cnt + 1) {
	    passwd = waitPassword();
	    
            if (.nanny.checkPassword(ch, passwd))
		return true;
	    
	    sleep(4);
	    
	    if (cnt < 3) {
		if (.buildplot) {
		    if (.chanceOneOf(3))
			say("А вот и не верно!");
		    else if (.chanceOneOf(2))
			emote("сверяется с листиком и отрицательно мотает "
			      "головой.");
		    else
			say("Не-а, попробуй-ка еще раз?");
		}
		else {
		    if (.chanceOneOf(3))
			excl("А вот и не верно! Но я дам тебе еще один шанс.");
		    else if (.chanceOneOf(2))
			say("Не угадалее. Следующий вариант?");
		    else
			say("Не верно. Что ты придумаешь на этот раз?");
		}
	    }
	}
	
	if (.buildplot) {
	    excl("А ну кыш отсюда!");
	}
	else {
	    if (.chanceOneOf(4))
		say("Когда передумаешь морочить мне голову - вернешься.");
	    else if (.chanceOneOf(3))
		excl("Покиньте кабинет!");
	    else if (.chanceOneOf(2))
		emote("выпихивает тебя вон.");
	    else
		excl("Прочь отсюда, мошенник!");
	}
	
	.wiznet(.fmt("%1$^C1 трижды неправильно называет пароль.", ch), 
	        ch.trust, "secure");
	quit();
    };
    
    waitPassword = function() {
	var passwd;

	ch.echoOff();
	
	cmdHandler = function(cmd) {
	    return .nanny.checkPassword(ch, cmd);
	};
	passwd = waitSpeech();
	cmdHandler = null;
	
	ch.echoOn();
	ch.ptc("\r\n");

	return passwd;
    };

    /*
     * pick name
     */
    taskName = function() {
	if (.buildplot) {
	    emote("с интересом оглядывает тебя.");
	    ask("Привет, как тебя зовут?");
	}
	else
	    ask("Как твое имя, странник?");

	for ( ; !ackName(); )
	    ;
    };

    ackName = function() {
	var name, rname;
	
	name = waitName();
        rname = "";

	if (name == null)
	    return false;

	if (.player_exists(name)) {
	    ch.name = .player_name(name);
	    ch.russianName = .player_russian_name(ch.name);
	    return true;
	}
	
	sleep(2);
	if (.buildplot) {
	    emote("озадаченно чешет репу между рогами.");
	    say("Точно '" + name + "'? Ты уверен?");
	}
	else {
	    say("В моей книге нет записей о " + name + ".");
	    ask("Я правильно тебя расслышал (да или нет)?");
	}

	if (!waitConfirm()) {
	    ask("Так кто же ты?");
	    return false;
	}
	
	if (name.isRussian()) {
	    rname = name;
	    say("Теперь назови мне свое имя латинскими буквами.");
            name = waitEnglishName();
            if (name == null)
	    	return false;
	}
	
	if (!.nanny.checkName(name)) {
	    say("Сожалею, но твое имя мне не нравится. Назови другое.");
	    return false;
	}
	
	ch.name = name;
	ch.russianName = rname;
	return true;
    };

    waitEnglishName = function() {
	var name;

	cmdHandler = function(cmd) {
	    return .player_exists(cmd.capitalize());
	};
	name = waitSpeech();
	cmdHandler = null;

	name = name.trim();
	name = name.capitalize();

	if (name.size()==0)
	    return null;
	
	if (!name.match("^[A-Za-z]+$"))
	    return null;

	return name;
    };

    waitName = function() {
	var name;

	cmdHandler = function(cmd) {
	    return .player_exists(cmd.capitalize());
	};
	name = waitSpeech();
	cmdHandler = null;

	name = name.trim();
	name = name.capitalize();

	if (name.size()==0)
	    return null;
	
	if (!name.match("^[A-Za-zЮ-Ъю-ъ]+$"))
	    return null;

	return name;
    };
    
    /*
     * pick sex 
     */
    taskSex = function() {
	sleep(2);
	emote("открывает Книгу Имен на новой странице и макает перо "
	      "в чернильницу.");
	sleep(2);
	ask(ch.name + " - это мужское или женское имя (male или female)?");
	
	for ( ; !ackSex(); )
	    ;
    };

    waitSex = function() {
	var sex;

	cmdHandler = function(cmd) {
	    return cmd.strPrefix("муж") || cmd.strPrefix("жен") 
		|| cmd.strPrefix("mal") || cmd.strPrefix("fem");
	};
	sex = waitSpeech();
	cmdHandler = null;
	return sex;
    };

    ackSex = function() {
	var s;

	s = waitSex();
	
	if (s == "")
	    return false;

	if (s.strPrefix("мужское") || s.strPrefix("male")) {
	    ch.sex = .tables.sex_table.male;
	    return true;
	}

	if (s.strPrefix("женское") || s.strPrefix("female")) {
	    ch.sex = .tables.sex_table.female;
	    return true;
	}

	sleep(4);
	ask("И все же?");
	return false;
    };
    
    /*
     * display some useful newbie info and ask about language settings
     */
    taskLanguage = function() {
	sleep(2);
	
	ask("Нуждаешься ли ты в краткой обзорной экскурсии (да или нет)?");
	if (!waitConfirm())
	    return;

	say("Тебе уже доступны некоторые команды: набери {1{Gcommands{2 "
	    "или {1{Gкоманды{2.");
	say("Справку по каждой команде покажет {1{G? имя_команды{2.");
	sleep(4);

	say("Если захочешь начать с начала, выйдешь отсюда командой "
	    "{1{Gquit{2({1{Gконец{2) и вернешься снова.");
	sleep(4);
	
	say("На каком языке ты предпочитаешь видеть названия команд, "
	    "рас и навыков: русский(russian) или английский(english)?");
	
	for ( ; !ackLanguage(); )
	    ;
	
	sleep(2);
	say("В дальнейшем ты са%2$Gмо|м|ма сможешь изменить эти настройки "
	    "по команде {1{G{lEconfig{lRрежим{lx{2.");
    };

    ackLanguage = function() {
	var lang;

	lang = waitSpeech();
	if (lang == "")
	    return false;

	if (lang.strPrefix("русский") 
	    || lang.strPrefix("по-русски")
	    || lang.strPrefix("russian"))
	{
	    ch.config = .set_bit(ch.config,
	                         .tables.config_flags.runames
				 | .tables.config_flags.ruskills
				 | .tables.config_flags.rucommands
				 | .tables.config_flags.ruexits);
	    return true;
	}

	if (lang.strPrefix("английский")
	    || lang.strPrefix("по-английски")
	    || lang.strPrefix("english"))
	{
	    return true;
	}

	return false;
    };

    /*
     * pick race 
     */
    taskRace = function() {
	sleep(4);
	emote("распахивает перед тобой оглавление Энциклопедии Существ "
	      "Мира Грез.\n");
	.nanny.help(ch, "racetable");
	sleep(2);
	say("Найди здесь свою расу и назови мне ее.");

	for ( ; !ackRace(); )
	    ;
    };

    ackRace = function() {
	var race, msg;

	msg = waitRace();
	if (msg == "")
	    return false;
	
	race = findRace(msg);
	if (race == null) {
	    say("Сожалею, но '" + msg + "' у нас в мире не водятся.");
	    return false;
	}

	if (race.name == "harpy" && ch.sex == .tables.sex_table.male) {
	    say("Но " + race.nameMlt.ruscase('1') + " бывают только женского пола!");
	    return false;
	}
	
	ch.race = race;

	ch.max_hit   = ch.max_hit + race.hpBonus;
	ch.perm_hit  = ch.hit = ch.max_hit;
	ch.max_mana  = ch.max_mana + race.manaBonus;
	ch.perm_mana = ch.mana = ch.max_mana;
	ch.practice  = ch.practice + race.pracBonus;
	
	ch.detection   = .set_bit(ch.detection, race.det);
	ch.affected_by = .set_bit(ch.affected_by, race.aff);
	ch.imm_flags   = .set_bit(ch.imm_flags, race.imm);
	ch.res_flags   = .set_bit(ch.res_flags, race.res);
	ch.vuln_flags  = .set_bit(ch.vuln_flags, race.vuln);
	
	ch.form    = race.form;
	ch.parts   = race.parts;
	ch.wearloc = race.wearloc;
	ch.size    = race.size;
	
	sleep(2);
	say("Рад приветствовать в нашем мире очередн%2$Gого|ого|ую "
	    "представител%2$Gя|я|ьницу племени " 
	    + race.nameMlt.ruscase('2') + ".");
	sleep(2);
	return true;
    };
    
    waitRace = function() {
	var msg;

	cmdHandler = function(cmd) {
	    return findRace(cmd.getOneArgument()) != null;
	};
	msg = waitSpeech();
	cmdHandler = null;

	return msg;
    };

    findRace = function(msg) {
	try {
	    .pcraces.forEach(function(msg) {
		if (msg.contains(name)
		    || msg.contains(nameMale.ruscase('1'))
		    || msg.contains(nameFemale.ruscase('1')))
		    throw name;
	    }, msg);
	} catch (e) {
	    return .Race(e);
	}

	return null;
    };

    /*
     * pick profession 
     */
    taskProfession = function() {
	say("Вот что пишут о профессиях, которыми обычно занимаются " 
	    + ch.race.nameMlt.ruscase('1'));
	emote("переворачивает несколько страниц Энциклопедии.");
	sleep(2);

	showProfessions();
	sleep(4);

	ask("А чем занимаешься ты?");

	for ( ; !ackProfession(); )
	    ;
    };

    showProfessions = function() {
	ch.act(
	   "{YПрофессия                  +Опыт  Характер  +Параметры{w\n"
	   "------------------------------------------------------------------");

	.professions.forEach(function(ch) {
	    if (!goodSex(ch))
		return;
	    if (!goodRace(ch))
		return;

	    ch.act("{y%-12s{w %-12s  %4d   %6s    %s",
	           nameRus.ruscase('1'),
		   name,
		   points,
		   alignName,
		   statPlus);

	}, ch);

	ch.act("\nПосмотреть подробности каждой профессии можно по "
	       "{W?{w {D<{wназвание{D>{w");
    };

    ackProfession = function() {
	var msg, prof;

	msg = waitProfession();
	if (msg == "")
	    return false;
	
	prof = findProfession(msg);
	if (prof == null) {
	    say("Никогда не слыхал о такой профессии.");
	    return false;
	}

	if (!prof.goodSex(ch)) {
	    if (.chanceOneOf(2))
		say("Тебе не хватает одной маленькой детали, чтобы стать " 
		    + prof.nameRus.ruscase('5') + ".");
	    else if (ch.sex == .tables.sex_table.female) 
		excl("Виданое ли дело, женщина - " 
		      + prof.nameRus.ruscase('1') + "!");
	    else
		say("Первый раз вижу мужчину - " 
		     + prof.nameRus.ruscase('4') + ".");
	    return false;
	}

	if (!prof.goodRace(ch)) {
	    if (.chanceOneOf(3))
		excl("Признаться, первый раз слышу о " 
		     + ch.race.nameMlt.ruscase('6') + " - " 
		     + prof.nameMlt.ruscase('6') + "!");
	    else if (.chanceOneOf(2))
		excl(ch.race.nameMlt.ruscase('1') + " - " 
		     + prof.nameMlt.ruscase('1') 
		     + "? Это должно быть забавно!");
	    else
		say(ch.race.nameMlt.ruscase('1') + " - " 
		    + prof.nameMlt.ruscase('1') 
		    + "? С трудом могу себе такое представить.");
	    return false;
	}
	
	ch.profession = prof;

	sleep(2);
	say("Итак, ты %2$^C1, " + ch.race.nameRus(ch).ruscase('1') + ", "
	    + "профессия - " + ch.profession.nameRus.ruscase('1') + ".");
	sleep(2);
	return true;
    };
	
    waitProfession = function() {
	var msg;

	cmdHandler = function(cmd) {
	    return findProfession(cmd) != null;
	};
	msg = waitSpeech();
	cmdHandler = null;

	return msg;
    };

    findProfession = function(msg) {
	try {
	    .professions.forEach(function(msg) {
		if (msg.strPrefix(name) 
		    || msg.strPrefix(nameRus.ruscase('1')))
		    throw name;
	    }, msg);
	} catch (e) {
	    return .Profession(e);
	}

	return null;
    };


    /*
     * pick alignment 
     */
    taskAlignment = function() {
	sleep(4);
	say("Я вижу, что твой характер колеблется между "
	    + ch.alignMin.ruscase('5') + " и "
	    + ch.alignMax.ruscase('5') + ".");
	ask("Но какой он на самом деле?");
	ch.ptc(.nanny.alignAllowed(ch));

	for ( ; !ackAlignment(); )
	    ;
    };

    ackAlignment = function() {
	var a;

	a = waitSpeech();

	if (a == "")
	    return false;

	if (!.nanny.alignChoose(ch, a)) {
	    say("Это совсем на тебя не похоже. Выбери снова.");
	    return false;
	}
	
	say("Итак, у тебя " + ch.alignName.ruscase('1') + " характер.");
	return true; 
    };

    /*
     * pick ethos 
     */
    taskEthos = function() {
	sleep(4);

	if (ch.profession.ethos == "lawful") {
	    ch.ethos = .tables.ethos_table.lawful;
	    say("Ты всегда следуешь по пути закона и порядка.");
	    return;
	}
	
	ask("Ты законопослушн%2$Gое|ый|ая граждан%2$Gин|ин|ка (lawful), "
	    "нейтрально относишься к закону (neutral), или в твое душе "
	    "царит хаос (chaotic)?");

	for ( ; !ackEthos(); )
	    ;
    };

    ackEthos = function() {
	var e;

	e = waitSpeech();

	if (e == "")
	    return false;

	if (e.strPrefix("lawful") || e.strPrefix("законопослушный")) {
	    ch.ethos = .tables.ethos_table.lawful;
	    say("Ты всегда следуешь по пути закона и порядка.");
	    return true;
	}
	
	if (e.strPrefix("neutral") || e.strPrefix("нейтральный")) {
	    ch.ethos = .tables.ethos_table.neutral;
	    say("Ты нейтрально относишься к законам.");
	    return true;
	}

	if (e.strPrefix("chaos") 
	    || e.strPrefix("chaotic")
	    || e.strPrefix("хаотический")
	    || e.strPrefix("хаос")) 
	{
	    ch.ethos = .tables.ethos_table.chaotic;
	    say("Тебе плевать на законы и правила.");
	    return true;
	}
	
	ask("И все же?");
	return false;
    };

    /*
     * pick new password
     */
    taskNewPassword = function() {
	sleep(4);
	say("Назови мне пароль, по которому я смогу узнать тебя "
	    "в следующий раз.");

	for (; !ackNewPassword(); )
	    ;
    };

    ackNewPassword = function() {
	var p, p0;
    
	p = waitNewPassword();

	if (p.size() < 5) {
	    say("Пароль должен быть длиннее пяти символов. Попробуй еще раз.");
	    return false;
	}
	
	say("Повтори этот пароль еще раз, для подтверждения");
	
	p0 = waitNewPassword();

	if (p != p0) {
	    say("Эти два пароля не совпадают. Попробуй снова.");
	    return false;
	}
        
	ch.password = p;
	return true;
    };
    
    waitNewPassword = function() {
	var passwd;

	ch.echoOff();

	cmdHandler = function(cmd) {
	    return true;
	};
	passwd = waitSpeech();
	cmdHandler = null;

	ch.echoOn();

	return passwd;
    };

    /*
     * pick russian name
     */
    sayRussianName = function(s) {
	    emote("нараспев декламирует: ");
	    ch.act("   %1$^N1 стоит здесь.\n"
		   "   Вид у %1$^N2 довольно обескураженный.\n"
		   "   Некто дает %1$^N3 пару золотых монет.\n"
		   "   Яркая вспышка рождает из себя %1$^N4.\n"
		   "   Ты гордишься %1$^N5.\n"
		   "   Вы ничего не слыхали о %1$^N6?\n", s);
	    
	    ask("Все звучит правильно?");
    };

    taskRussianName = function() {
	var s1, s2, s3, s4, s5, s6, s;
    	
	if (ch.russianName == "") {
	    ask("Ты хочешь назвать мне русский вариант своего имени (да или нет)?");
	    if (!waitConfirm())
		return;
	    say("Назови свое имя в именительном падеже: кто?");
	    s1 = waitRussian();
	} else {
	    s1 = ch.russianName;
	} 

       s2 = .tmp.system.inflect_name(s1, ch.sex, 2);
       s3 = .tmp.system.inflect_name(s1, ch.sex, 3);
       s4 = .tmp.system.inflect_name(s1, ch.sex, 4);
       s5 = .tmp.system.inflect_name(s1, ch.sex, 5);
       s6 = .tmp.system.inflect_name(s1, ch.sex, 6);
       sleep(4);
       say("Я попытался сам просклонять русский вариант твоего имени.");
       sleep(1);

       s = .makeShort(s1,s2,s3,s4,s5,s6);
       sayRussianName(s);
       if (waitConfirm()) {
           ch.russianName = s;
           return;
       }
       sleep(4);
       emote("вздыхает.");
  

	sleep(2);
	
	for(;;) {

	    try {
		if (ch.russianName == "") {
		    say("Назови свое имя в именительном падеже: кто?");
		    s1 = waitRussian();
		} else {
		    s1 = ch.russianName;
		}

		say("Назови свое имя в родительном падеже: бред кого?");
		s2 = waitRussian();

		say("Назови свое имя в дательном падеже: дать кому?");
		s3 = waitRussian();

		say("Назови свое имя в винительном падеже: убить кого?");
		s4 = waitRussian();

		say("Назови свое имя в творительном падеже: делить с кем?");
		s5 = waitRussian();

		say("Назови свое имя в предложном падеже: говорить о ком?");
		s6 = waitRussian();

	    }
	    catch (e) {
		sleep(2);
		say("Ну что ж, начнем с начала.");
		continue;
	    }
		
	    s = .makeShort(s1, s2, s3, s4, s5, s6);
	    sleep(2);
	    sayRussianName(s);
	    
	    if (waitConfirm()) {
		ch.russianName = s;
		return;
	    }
	    
	    sleep(4);
	    emote("вздыхает.");
	    say("Хорошо, попробуем еще разочек...");
	}
    };

    waitRussian = function() {
	var msg;

	msg = waitSpeech();
	
	if (msg == "") {
	    say("Но это же пустое имя.");
	    throw "error";
	}
	
	if (!msg.isRussian()) {
	    excl("Имя может содержать только русские буквы!");
	    throw "error";
	}
	
	return msg.capitalize();
    };


    init();
    return this;
};
}