#include "AppHdr.h"
#include "ng-input.h"
#include "newgame.h"
#include "cio.h"
#include "files.h"
#include "menu.h"
#include "options.h"
#include "stuff.h"
extern std::string init_file_error; // defined in main.cc
// Eventually, this should be something more grand. {dlb}
void opening_screen(void)
{
std::string msg =
"Hello, welcome to " CRAWL " " + Version::Long() + "!" EOL
"(c) Copyright 1997-2002 Linley Henzell, "
"2002-2009 Crawl DevTeam" EOL
"Please consult crawl_manual.txt for instructions and legal details."
"" EOL;
const bool init_found = init_file_error.empty();
if (!init_found)
msg += "No init file ";
else
msg += "(Read options from ";
if (init_found)
{
#ifdef DGAMELAUNCH
// For dgl installs, show only the last segment of the .crawlrc
// file name so that we don't leak details of the directory
// structure to (untrusted) users.
msg += get_base_filename(Options.filename);
#else
msg += Options.filename;
#endif
msg += ".)";
}
else
{
msg += init_file_error;
msg += ", using defaults.";
}
msg += EOL;
formatted_string::parse_string(msg).display();
textcolor( LIGHTGREY );
}
static void _show_name_prompt(int where, bool blankOK,
const std::vector &existing_chars,
slider_menu &menu)
{
cgotoxy(1, where);
textcolor( CYAN );
if (blankOK)
{
if (Options.prev_name.length() && Options.remember_name)
{
cprintf(EOL
"Press for \"%s\", or . to be prompted later."
EOL,
Options.prev_name.c_str());
}
else
{
cprintf(EOL
"Press to answer this after species and "
"job are chosen." EOL);
}
}
cprintf(EOL "What is your name today? ");
if (!existing_chars.empty())
{
const int name_x = wherex(), name_y = wherey();
menu.set_limits(name_y + 3, get_number_of_lines());
menu.display();
cgotoxy(name_x, name_y);
}
textcolor( LIGHTGREY );
}
static void _preprocess_character_name(std::string &name, bool blankOK)
{
if (name.empty() && blankOK && Options.prev_name.length()
&& Options.remember_name)
{
name = Options.prev_name;
}
// '.', '?' and '*' are blanked.
if (name.length() == 1
&& (name[0] == '.' || name[0] == '*' || name[0] == '?'))
{
name = "";
}
}
static bool _is_good_name(std::string &name, bool blankOK, bool verbose)
{
_preprocess_character_name(name, blankOK);
// verification begins here {dlb}:
if (name.empty())
{
if (blankOK)
return (true);
if (verbose)
cprintf(EOL "That's a silly name!" EOL);
return (false);
}
// If MULTIUSER is defined, userid will be tacked onto the end
// of each character's files, making bones a valid player name.
#ifndef MULTIUSER
// This would cause big probs with ghosts.
// What would? {dlb}
// ... having the name "bones" of course! The problem comes from
// the fact that bones files would have the exact same filename
// as level files for a character named "bones". -- bwr
if (stricmp(name.c_str(), "bones") == 0)
{
if (verbose)
cprintf(EOL "That's a silly name!" EOL);
return (false);
}
#endif
return (validate_player_name(name, verbose));
}
static int newname_keyfilter(int &ch)
{
if (ch == CK_DOWN || ch == CK_PGDN || ch == '\t')
return -1;
return 1;
}
static bool _read_player_name(std::string &name,
const std::vector &existing,
slider_menu &menu)
{
const int name_x = wherex(), name_y = wherey();
int (*keyfilter)(int &) = newname_keyfilter;
if (existing.empty())
keyfilter = NULL;
char buf[kNameLen];
// XXX: Prompt displays garbage otherwise, but don't really know why.
// Other places don't do this. --rob
buf[0] = '\0';
line_reader reader(buf, sizeof(buf));
reader.set_keyproc(keyfilter);
while (true)
{
cgotoxy(name_x, name_y);
if (name_x <= 80)
cprintf("%-*s", 80 - name_x + 1, "");
cgotoxy(name_x, name_y);
int ret = reader.read_line(false);
if (!ret)
{
name = buf;
return (true);
}
if (ret == CK_ESCAPE)
return (false);
if (!existing.empty())
{
menu.set_search(name);
menu.show();
const MenuEntry *sel = menu.selected_entry();
if (sel)
{
name = static_cast(sel->data)->name;
return true;
}
}
// Go back and prompt the user.
}
}
// Reads a valid name from the player, writing it to ng.name.
void enter_player_name(newgame_def &ng, bool blankOK)
{
int prompt_start = wherey();
bool ask_name = true;
std::vector existing_chars;
slider_menu char_menu(MF_SINGLESELECT | MF_NOWRAP, false);
if (!ng.name.empty())
ask_name = false;
if (blankOK && (ask_name || !_is_good_name(ng.name, false, false)))
{
existing_chars = find_saved_characters();
if (existing_chars.empty())
{
cgotoxy(1,12);
formatted_string::parse_string(
" If you've never been here before, you might want to try out" EOL
" the Dungeon Crawl tutorial. To do this, press "
"Ctrl-T on the next" EOL
" screen.").display();
}
else
{
MenuEntry *title = new MenuEntry("Or choose an existing character:");
title->colour = LIGHTCYAN;
char_menu.set_title( title );
for (unsigned int i = 0; i < existing_chars.size(); ++i)
{
std::string desc = " " + existing_chars[i].short_desc();
if (static_cast(desc.length()) >= get_number_of_cols())
desc = desc.substr(0, get_number_of_cols() - 1);
#ifdef USE_TILE
MenuEntry *me = new PlayerMenuEntry(desc);
#else
MenuEntry *me = new MenuEntry(desc);
#endif
me->data = &existing_chars[i];
char_menu.add_entry(me);
}
}
}
do
{
// Prompt for a new name if current one unsatisfactory {dlb}:
if (ask_name)
{
_show_name_prompt(prompt_start, blankOK, existing_chars, char_menu);
// If the player wants out, we bail out.
if (!_read_player_name(ng.name, existing_chars, char_menu))
end(0);
trim_string(ng.name);
}
}
while (ask_name = !_is_good_name(ng.name, blankOK, true));
}
bool validate_player_name(const std::string &name, bool verbose)
{
#if defined(TARGET_OS_DOS) || defined(TARGET_OS_WINDOWS)
// Quick check for CON -- blows up real good under DOS/Windows.
if (stricmp(name.c_str(), "con") == 0
|| stricmp(name.c_str(), "nul") == 0
|| stricmp(name.c_str(), "prn") == 0
|| strnicmp(name.c_str(), "LPT", 3) == 0)
{
if (verbose)
cprintf(EOL "Sorry, that name gives your OS a headache." EOL);
return (false);
}
#endif
for (unsigned int i = 0; i < name.length(); i++)
{
char c = name[i];
// Note that this includes systems which may be using the
// packaging system. The packaging system is very simple
// and doesn't take the time to escape every character that
// might be a problem for some random shell or OS... so we
// play it very conservative here. -- bwr
if (!isalnum(c) && c != '-' && c != '.' && c != '_' && c != ' ')
{
if (verbose)
{
cprintf( EOL
"Alpha-numerics, spaces, dashes, periods "
"and underscores only, please."
EOL );
}
return (false);
}
}
return (true);
}