.crawlrc).
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1610 c06c8d41-db1a-0410-9941-cceddc491573
IIN7AVA6JYRBXH6ZYRR7BY7TV6PW7ANAQ2A3PD55FKBKKQFEEF2AC
SOGYI2OBNZHL6HZN3ZJR5ITQHTYITILKR4D4EZPKBO23UYZW3JQAC
HXWGGNMMVGM6XIO4SBHZETFXTBPJV7OWRZKMED7BCFI4AVE7VFRAC
6POB4N3ASH4YL2O5ZWCQ5X2RURGTC2KXX7R7IKCJT6ZV6VKQKJMQC
KFZYPFHHOWRUZEK2PW26EI73Z6I6DLHW2YEJV2CB5XBWFRRNBFXQC
TRNEOO3YKZSUGYTJZDDXZTJFQJXY6PWKBDJXAOLY3FZAD6WHTUTQC
TAHSTXR7ROOMDFUSBUU4ZAIEWQLAS5CIRCTARLD4Q2BGNLSL7E5QC
NEECVIIAOBP72T6O44DWAA6HFSDY3KSWYCFMKAEMDMVOI7XASD7QC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
HFEFKHVV2ULXS6ZEFNX6ZXXUJKME5L2PITQ3VRTKMRUOU3DHXXWQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
AJJ6D6JRV6ZAZOAUHYUM2IQG42V6PBALOD4KEMNKSVVAOJXAUCPQC
PL6I2CMSTHY5ZHWVMIQE5YTM5S5VPKBNZM6QJVHZSSKOJGIJ5W4AC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
7NDXS36TE7QVXTXJWMYSVG5UHCCLPIO4VL6NXFGTDK3ZNKE3A2IAC
77H4BWWPPGLM3PLZH4QTAJRXIZTSDVNCOKZE223I437FN2UJ34RQC
M47QBURKKRV2JFK6U7GQNVWK7RUTBVPBJ5FHURC55SKQSXAUFSBAC
3SVWI6KK6IFQ6SB6L3TIC7QIGDITU5QLVAG5C7X57HH75ZHW7T2AC
NXVPOFYKJFWQWKVPQUMWH2Y2KJEZX44BUOBFJ4JD4KFGPEGYHG4QC
5UVDIVD4NSXA52U4QMQIVST3GSZJ2A2YZK3RUEXKPM43YVQ7LI5AC
LAMIVDKY7LO5ONX5Z273ZCCEA5UBENOJD5VWNE4AK2EXGFED6BFQC
MQ62OAMLGJVRW2QIL4PAZRAU6PC52ZVGY2FCOBIY6IWGQIHMU5CAC
UXBKTJSK6DEFSCQ4HB36YM6KVFGWYVCEORXIMOKQ3JZL4NNEOKGQC
UPJVSMMMHGRDUIJG4MZX6IBLQ4ODBF5Z3PF3RHDYTSAEOCVDZM5AC
YHSVOROKPYS33Y4RYZRVZTE3G5LXOFX52HEDNLV6HIXOJYNOKH3QC
VIFRP3HZEONFR6PQRYZYM3YUEJOQ7T4F5CZY4MN4YJMB23FMU7XAC
YRY2TC3VHOYE47M23UJGUWDGF7H7WGU7WLWI4SUNM4EDNTGUPHGAC
TOKBONNNPTP2CIEHMMR4QAJZTXYETS55OGGDA6FY6NIMNDYMWJDAC
MOT3YZFRY3JG2MH3GN2VD6IY3IJSAXJUXMXS222TXWKRFCENOCDQC
22RFWMSJGG26Z2MQEEXGKVTFSTLREHQIG46WYOTMDRKI5YVMRNVAC
JQFQX7IWSJ4TYWVUVXAFMCPSAN67PRMNECDQI5WMON2JFMQVVUEQC
6HG6JFO47Y3BZLU7Y6G3R2CX6JFGN4X5PKK6S5IGUXUYQ5GVZYFQC
W6IY6LF3MREPXC23AAKA2BJNUCJYCSOWY55DIWJWFLUEE2Y3LGNQC
GCIZIUXO5TYROKDUYB3HAY7H7MRDTJNM7HR7DGSH7KXDIZC2LCDAC
OFAVQUIGMKBKXMEMRMNPHJK75RNXJVN3OYWNBKCT6EUUAG6U23DAC
TV3ZC6WOZKSQQJQN26JIVKCHK6UK7WMDBYZDUYRWEAZ4JB4YVNAAC
EOMCPVNQLX3IMLC46EAO67DPBH5KEG2FQTPBLGU62HIRWA3UQ7XQC
CVC5FFFLGVRR3KPYDNB6RF4FNACV3LI3HPSR4MCUNZ4C3FSQYBDAC
LW4N5EHKL776DURXZMAM6JEW3JPWWX5BSNP7TCZHTLCDOQTTGFCAC
CRX4RG35C3DH57NIXCW4CG4X7SBMSX7TAJR2BAFQ4LQKCLYUNANQC
TJRYL3NXPW5IUGEV3YOC7JYWEXCZDBFPLT4AUG4P227WVKVB72ZAC
J6QMBZJOAWTIERCY5VXKYYXMR5HDSJS7K564X6LEMQMEELARN55AC
VD4KDTGHVKCN35AWREYB4TEOUMCTW7SAUPAMTMF5ABC7VBHVKP4AC
4PUWNQO7QMEWY3GSUHLBKMYOAI7ASYSRM32KDGTA7DLNDIGFAWFAC
YAAJ6PTN6QUSWE52URI5AENOGD366FIHOIFUOXFUJLVZYE4OG6HQC
NQMXQ6OQVUSC7Y7F7IL252QW4A5JED224EECNHWAM4ZZYVNY745AC
ZHFUXYUHS6V47WK2NRH7OU6RX77NRKTXOZC3MND2GG7PEEWSGFTAC
UL7XFKMUX3WIU4O2LZANK4ECJ654UZPDBFGNXUEYZYOLKBYBCG6AC
ZP2KE7A2LE7Z2S7AC45WE4CXDSEVDTWIMV2EM4IBUKXYJIDU6R7QC
547JREUJXTZNYVGHNNAET5F5O5JYYGNTDQB6ABZNT7YX5EY64OHAC
case CK_MOUSE_MOVE:
move_x = move_y = 0;
break;
case CK_MOUSE_CLICK:
{
const c_mouse_event cme = get_mouse_event();
const coord_def curp(start_x + curs_x - 1, start_y + curs_y - 1);
const coord_def grdp =
cme.pos + coord_def(start_x - 1, start_y - top);
if (cme.left_clicked() && in_bounds(grdp))
{
spec_place[0] = grdp.x;
spec_place[1] = grdp.y;
map_alive = false;
}
else if (cme.scroll_up())
{
move_y = -block_step * ((curs_y - top + 1) / block_step + 1);
move_x = 0;
}
else if (cme.scroll_down())
{
move_y =
block_step *
((get_number_of_lines() - curs_y + top - 1) / block_step
+ 1);
move_x = 0;
}
else if (cme.right_clicked())
{
const coord_def delta = grdp - curp;
move_y = delta.y;
move_x = delta.x;
}
break;
}
int i;
int x;
menu_letter lcount;
bool show_aptitudes = false;
menu_letter lcount = 'a';
textcolor(MAGENTA);
gotoxy(1, bottom_line-5);
formatted_string::parse_block(
"This screen shows the skill set of your character. You can pick up new" EOL
"skills by performing the corresponding actions. The number next to the" EOL
"skill is your current level, the higher the better. The blue percent " EOL
"value shows your progress towards the next skill level. You can toggle" EOL
"which skills to train by pressing their slot letters. A <darkgrey>greyish<magenta> skill " EOL
"will increase at a decidedly slower rate and ease training of others. ",
false).display();
textcolor(MAGENTA);
gotoxy(1, bottom_line-5);
formatted_string::parse_block(
"This screen shows the skill set of your character. You can pick up new" EOL
"skills by performing the corresponding actions. The number next to the" EOL
"skill is your current level, the higher the better. The blue percent " EOL
"value shows your progress towards the next skill level. You can toggle" EOL
"which skills to train by pressing their slot letters. A <darkgrey>greyish<magenta> skill " EOL
"will increase at a decidedly slower rate and ease training of others. ",
false).display();
// if any more skills added, must adapt letters to go into caps
gotoxy(1, bottom_line-1);
textcolor(LIGHTGREY);
cprintf("Press the letter of a skill to choose whether you want to practise it.");
if (!player_genus(GENPC_DRACONIAN) || you.max_level >= 7)
{
gotoxy(1, bottom_line);
formatted_string::parse_string("Press '!' to toggle between "
"<blue>progress</blue> and "
"<red>aptitude</red> "
"display.").display();
}
// if any more skills added, must adapt letters to go into caps
gotoxy(1, bottom_line-1);
textcolor(LIGHTGREY);
cprintf("Press the letter of a skill to choose "
"whether you want to practise it.");
if (!player_genus(GENPC_DRACONIAN) || you.max_level >= 7)
{
gotoxy(1, bottom_line);
formatted_string::parse_string("Press '!' to toggle between "
"<blue>progress</blue> and "
"<red>aptitude</red> "
"display.").display();
}
mpr( "(m)acro, keymap [(k) default, (x) level-map or (t)argeting], (s)ave?", MSGCH_PROMPT );
input = getch();
if (input == 0)
input = getch();
mpr("(m)acro, keymap [(k) default, (x) level-map or (t)argeting], (s)ave?",
MSGCH_PROMPT);
input = m_getch();
void cursorxy(int x, int y);
// Converts a key to a direction key, converting keypad and other sequences
// to vi key sequences (shifted/control key directions are also handled). Non
// direction keys (hopefully) pass through unmangled.
int unmangle_direction_keys(int keyin, int keymap = 0,
bool fake_ctrl = true, bool fake_shift = true);
void get_input_line( char *const buff, int len );
class input_history;
// In view.cc, declared here for default argument to cancelable_get_line()
int get_number_of_cols(void);
// Returns zero if user entered text and pressed Enter, otherwise returns the
// key pressed that caused the exit, usually Escape.
//
// If keyproc is provided, it must return 1 for normal processing, 0 to exit
// normally (pretend the user pressed Enter), or -1 to exit as if the user
// pressed Escape
int cancelable_get_line( char *buf,
int len,
int wrapcol = get_number_of_cols(),
input_history *mh = NULL,
int (*keyproc)(int &c) = NULL );
// Keys that getch() must return for keys Crawl is interested in.
enum KEYS
{
CK_ENTER = '\r',
CK_BKSP = 8,
CK_ESCAPE = ESCAPE,
// 128 is off-limits because it's the code that's used when running
CK_DELETE = 129,
// This sequence of enums should not be rearranged.
CK_UP,
CK_DOWN,
CK_LEFT,
CK_RIGHT,
CK_INSERT,
CK_HOME,
CK_END,
CK_CLEAR,
CK_PGUP,
CK_PGDN,
CK_SHIFT_UP,
CK_SHIFT_DOWN,
CK_SHIFT_LEFT,
CK_SHIFT_RIGHT,
CK_SHIFT_INSERT,
CK_SHIFT_HOME,
CK_SHIFT_END,
CK_SHIFT_CLEAR,
CK_SHIFT_PGUP,
CK_SHIFT_PGDN,
CK_CTRL_UP,
CK_CTRL_DOWN,
CK_CTRL_LEFT,
CK_CTRL_RIGHT,
CK_CTRL_INSERT,
CK_CTRL_HOME,
CK_CTRL_END,
CK_CTRL_CLEAR,
};
class cursor_control
{
public:
cursor_control(bool cursor_enabled)
: cstate(is_cursor_enabled()), smartcstate(is_smart_cursor_enabled())
{
enable_smart_cursor(false);
set_cursor_enabled(cursor_enabled);
}
~cursor_control() {
set_cursor_enabled(cstate);
enable_smart_cursor(smartcstate);
}
private:
bool cstate;
bool smartcstate;
// Reads lines of text; used internally by cancelable_get_line.
class line_reader
{
public:
line_reader(char *buffer, size_t bufsz,
int wrap_col = get_number_of_cols());
typedef int (*keyproc)(int &key);
int read_line(bool clear_previous = true);
std::string get_text() const;
void set_input_history(input_history *ih);
void set_keyproc(keyproc fn);
protected:
void cursorto(int newcpos);
int process_key(int ch);
void backspace();
void killword();
bool is_wordchar(int c);
private:
char *buffer;
size_t bufsz;
input_history *history;
int start_x, start_y;
keyproc keyfn;
int wrapcol;
// These are subject to change during editing.
char *cur;
int length;
int pos;
};
#endif
#ifdef UNIX
static keycode_type numpad2vi(keycode_type key)
{
if (key >= '1' && key <= '9')
{
const char *vikeys = "bjnh.lyku";
return keycode_type(vikeys[key - '1']);
}
return (key);
}
#endif
int unmangle_direction_keys(int keyin, int km, bool fake_ctrl, bool fake_shift)
{
const KeymapContext keymap = static_cast<KeymapContext>(km);
#ifdef UNIX
// Kludging running and opening as two character sequences
// for Unix systems. This is an easy way out... all the
// player has to do is find a termcap and numlock setting
// that will get curses the numbers from the keypad. This
// will hopefully be easy.
/* can we say yuck? -- haranp */
if (fake_ctrl && keyin == '*')
{
keyin = getchm(keymap);
// return control-key
keyin = CONTROL(toupper(numpad2vi(keyin)));
}
else if (fake_shift && keyin == '/')
{
keyin = getchm(keymap);
// return shift-key
keyin = toupper(numpad2vi(keyin));
}
#else
// Old DOS keypad support
if (keyin == 0)
{
/* FIXME haranp - hackiness */
const char DOSidiocy[10] = { "OPQKSMGHI" };
const char DOSunidiocy[10] = { "bjnh.lyku" };
const int DOScontrolidiocy[9] =
{
117, 145, 118, 115, 76, 116, 119, 141, 132
};
keyin = getchm(keymap);
for (int j = 0; j < 9; ++j )
{
if (keyin == DOSidiocy[j])
{
keyin = DOSunidiocy[j];
break;
}
if (keyin == DOScontrolidiocy[j])
{
keyin = CONTROL(toupper(DOSunidiocy[j]));
break;
}
}
}
#endif
// [dshaligram] More lovely keypad mangling.
switch (keyin)
{
#ifdef UNIX
case '1': return 'b';
case '2': return 'j';
case '3': return 'n';
case '4': return 'h';
case '6': return 'l';
case '7': return 'y';
case '8': return 'k';
case '9': return 'u';
#else
case '1': return 'B';
case '2': return 'J';
case '3': return 'N';
case '4': return 'H';
case '6': return 'L';
case '7': return 'Y';
case '8': return 'K';
case '9': return 'U';
}
void get_input_line( char *const buff, int len )
{
buff[0] = 0; // just in case
#if defined(UNIX)
get_input_line_from_curses( buff, len ); // implemented in libunix.cc
#elif defined(WIN32CONSOLE)
getstr( buff, len );
#else
// [dshaligram] Turn on the cursor for DOS.
#ifdef DOS
_setcursortype(_NORMALCURSOR);
#endif
fgets( buff, len, stdin ); // much safer than gets()
#endif
buff[ len - 1 ] = 0; // just in case
// Removing white space from the end in order to get rid of any
// newlines or carriage returns that any of the above might have
// left there (ie fgets especially). -- bwr
const int end = strlen( buff );
int i;
for (i = end - 1; i >= 0; i++)
{
if (isspace( buff[i] ))
buff[i] = 0;
else
break;
}
#ifdef DOS
static int getch_ck()
{
int c = getch();
if (!c)
{
switch (c = getch())
{
case 'O': return CK_END;
case 'P': return CK_DOWN;
case 'I': return CK_PGUP;
case 'H': return CK_UP;
case 'G': return CK_HOME;
case 'K': return CK_LEFT;
case 'Q': return CK_PGDN;
case 'M': return CK_RIGHT;
case 119: return CK_CTRL_HOME;
case 141: return CK_CTRL_UP;
case 132: return CK_CTRL_PGUP;
case 116: return CK_CTRL_RIGHT;
case 118: return CK_CTRL_PGDN;
case 145: return CK_CTRL_DOWN;
case 117: return CK_CTRL_END;
case 115: return CK_CTRL_LEFT;
case 'S': return CK_DELETE;
}
}
return c;
}
#endif
// Hacky wrapper around getch() that returns CK_ codes for keys
// we want to use in cancelable_get_line() and menus.
int c_getch()
{
#if defined(DOS) || defined(UNIX) || defined(WIN32CONSOLE)
return getch_ck();
#else
return getch();
#endif
}
// Wrapper around gotoxy that can draw a fake cursor for Unix terms where
// cursoring over darkgray or black causes problems.
void cursorxy(int x, int y)
{
#ifdef UNIX
if (Options.use_fake_cursor)
fakecursorxy(x, y);
else
gotoxy(x, y);
#else
gotoxy(x, y);
#endif
}
// cprintf that knows how to wrap down lines (primitive, but what the heck)
int wrapcprintf( int wrapcol, const char *s, ... )
{
char buf[1000]; // Hard max
va_list args;
va_start(args, s);
// XXX: If snprintf isn't available, vsnprintf probably isn't, either.
int len = vsnprintf(buf, sizeof buf, s, args);
int olen = len;
va_end(args);
char *run = buf;
while (len > 0)
{
int x = wherex(), y = wherey();
if (x > wrapcol) // Somebody messed up!
return 0;
int avail = wrapcol - x + 1;
int c = 0;
if (len > avail)
{
c = run[avail];
run[avail] = 0;
}
cprintf("%s", run);
if (len > avail)
run[avail] = c;
if ((len -= avail) > 0)
gotoxy(1, y + 1);
run += avail;
}
return (olen);
}
int cancelable_get_line( char *buf, int len, int maxcol,
input_history *mh, int (*keyproc)(int &ch) )
{
line_reader reader(buf, len, maxcol);
reader.set_input_history(mh);
reader.set_keyproc(keyproc);
return reader.read_line();
}
/////////////////////////////////////////////////////////////
// input_history
//
input_history::input_history(size_t size)
: history(), pos(), maxsize(size)
{
if (maxsize < 2)
maxsize = 2;
pos = history.end();
}
void input_history::new_input(const std::string &s)
{
history.remove(s);
if (history.size() == maxsize)
history.pop_front();
history.push_back(s);
// Force the iterator to the end (also revalidates it)
go_end();
}
const std::string *input_history::prev()
{
if (history.empty())
return NULL;
if (pos == history.begin())
pos = history.end();
return &*--pos;
}
const std::string *input_history::next()
{
if (history.empty())
return NULL;
if (pos == history.end() || ++pos == history.end())
pos = history.begin();
return &*pos;
}
void input_history::go_end()
{
pos = history.end();
}
void input_history::clear()
{
history.clear();
go_end();
}
/////////////////////////////////////////////////////////////////////////
// line_reader
line_reader::line_reader(char *buf, size_t sz, int wrap)
: buffer(buf), bufsz(sz), history(NULL), start_x(0),
start_y(0), keyfn(NULL), wrapcol(wrap), cur(NULL),
length(0), pos(-1)
{
}
std::string line_reader::get_text() const
{
return (buffer);
}
void line_reader::set_input_history(input_history *i)
{
history = i;
}
void line_reader::set_keyproc(keyproc fn)
{
keyfn = fn;
}
void line_reader::cursorto(int ncx)
{
int x = (start_x + ncx - 1) % wrapcol + 1;
int y = start_y + (start_x + ncx - 1) / wrapcol;
::gotoxy(x, y);
}
int line_reader::read_line(bool clear_previous)
{
if (bufsz <= 0) return false;
cursor_control coff(true);
if (clear_previous)
*buffer = 0;
start_x = wherex();
start_y = wherey();
length = strlen(buffer);
// Remember the previous cursor position, if valid.
if (pos < 0 || pos > length)
pos = length;
cur = buffer + pos;
if (length)
wrapcprintf(wrapcol, "%s", buffer);
if (pos != length)
cursorto(pos);
if (history)
history->go_end();
for ( ; ; )
{
int ch = c_getch();
if (keyfn)
{
int whattodo = (*keyfn)(ch);
if (whattodo == 0)
{
buffer[length] = 0;
if (history && length)
history->new_input(buffer);
return (0);
}
else if (whattodo == -1)
{
buffer[length] = 0;
return (ch);
}
}
int ret = process_key(ch);
if (ret != -1)
return (ret);
}
}
void line_reader::backspace()
{
if (pos)
{
--cur;
char *c = cur;
while (*c)
{
*c = c[1];
c++;
}
--pos;
--length;
cursorto(pos);
buffer[length] = 0;
wrapcprintf( wrapcol, "%s ", cur );
cursorto(pos);
}
}
bool line_reader::is_wordchar(int c)
{
return isalnum(c) || c == '_' || c == '-';
}
void line_reader::killword()
{
if (!pos || cur == buffer)
return;
bool foundwc = false;
while (pos)
{
if (is_wordchar(cur[-1]))
foundwc = true;
else if (foundwc)
break;
backspace();
}
}
int line_reader::process_key(int ch)
{
switch (ch)
{
case CONTROL('G'):
case CK_ESCAPE:
return (CK_ESCAPE);
case CK_UP:
case CK_DOWN:
{
if (!history)
break;
const std::string *text =
ch == CK_UP? history->prev() : history->next();
if (text)
{
int olen = length;
length = text->length();
if (length >= (int) bufsz)
length = bufsz - 1;
memcpy(buffer, text->c_str(), length);
buffer[length] = 0;
cursorto(0);
int clear = length < olen? olen - length : 0;
wrapcprintf(wrapcol, "%s%*s", buffer, clear, "");
pos = length;
cur = buffer + pos;
cursorto(pos);
}
break;
}
case CK_ENTER:
buffer[length] = 0;
if (history && length)
history->new_input(buffer);
return (0);
case CONTROL('K'):
{
// Kill to end of line
int erase = length - pos;
if (erase)
{
length = pos;
buffer[length] = 0;
wrapcprintf( wrapcol, "%*s", erase, "" );
cursorto(pos);
}
break;
}
case CK_DELETE:
if (pos < length)
{
char *c = cur;
while (c - buffer < length)
{
*c = c[1];
c++;
}
--length;
cursorto(pos);
buffer[length] = 0;
wrapcprintf( wrapcol, "%s ", cur );
cursorto(pos);
}
break;
case CK_BKSP:
backspace();
break;
case CONTROL('W'):
killword();
break;
case CK_LEFT:
if (pos)
{
--pos;
cur = buffer + pos;
cursorto(pos);
}
break;
case CK_RIGHT:
if (pos < length)
{
++pos;
cur = buffer + pos;
cursorto(pos);
}
break;
case CK_HOME:
case CONTROL('A'):
pos = 0;
cur = buffer + pos;
cursorto(pos);
break;
case CK_END:
case CONTROL('E'):
pos = length;
cur = buffer + pos;
cursorto(pos);
break;
default:
if (isprint(ch) && length < (int) bufsz - 1)
{
if (pos < length)
{
char *c = buffer + length - 1;
while (c >= cur)
{
c[1] = *c;
c--;
}
}
*cur++ = (char) ch;
++length;
++pos;
putch(ch);
if (pos < length)
{
buffer[length] = 0;
wrapcprintf( wrapcol, "%s", cur );
}
cursorto(pos);
}
break;
}
return (-1);
}
}
void set_mouse_enabled(bool enabled)
{
#ifdef NCURSES_MOUSE_VERSION
const int mask = enabled? ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION : 0;
mousemask(mask, NULL);
#endif
}
#ifdef NCURSES_MOUSE_VERSION
static int proc_mouse_event(int c, const MEVENT *me)
{
crawl_view.mousep.x = me->x + 1;
crawl_view.mousep.y = me->y + 1;
if (!crawl_state.mouse_enabled)
return (CK_MOUSE_MOVE);
c_mouse_event cme(crawl_view.mousep);
if (me->bstate & BUTTON1_CLICKED)
cme.bstate |= c_mouse_event::BUTTON1;
else if (me->bstate & BUTTON1_DOUBLE_CLICKED)
cme.bstate |= c_mouse_event::BUTTON1_DBL;
else if (me->bstate & BUTTON2_CLICKED)
cme.bstate |= c_mouse_event::BUTTON2;
else if (me->bstate & BUTTON2_DOUBLE_CLICKED)
cme.bstate |= c_mouse_event::BUTTON2_DBL;
else if (me->bstate & BUTTON3_CLICKED)
cme.bstate |= c_mouse_event::BUTTON3;
else if (me->bstate & BUTTON3_DOUBLE_CLICKED)
cme.bstate |= c_mouse_event::BUTTON3_DBL;
else if (me->bstate & BUTTON4_CLICKED)
cme.bstate |= c_mouse_event::BUTTON4;
else if (me->bstate & BUTTON4_DOUBLE_CLICKED)
cme.bstate |= c_mouse_event::BUTTON4_DBL;
if (cme)
{
new_mouse_event(cme);
return (CK_MOUSE_CLICK);
}
return (CK_MOUSE_MOVE);
}
int getch_ck()
{
int c = getch();
if (!c)
{
switch (c = getch())
{
case 'O': return CK_END;
case 'P': return CK_DOWN;
case 'I': return CK_PGUP;
case 'H': return CK_UP;
case 'G': return CK_HOME;
case 'K': return CK_LEFT;
case 'Q': return CK_PGDN;
case 'M': return CK_RIGHT;
case 119: return CK_CTRL_HOME;
case 141: return CK_CTRL_UP;
case 132: return CK_CTRL_PGUP;
case 116: return CK_CTRL_RIGHT;
case 118: return CK_CTRL_PGDN;
case 145: return CK_CTRL_DOWN;
case 117: return CK_CTRL_END;
case 115: return CK_CTRL_LEFT;
case 'S': return CK_DELETE;
}
}
return c;
}
int m_getch()
{
return getch();
game_state() : waiting_for_command(false), terminal_resized(false),
io_inited(false), need_save(false), saving_game(false),
updating_scores(false), shopping(false), seen_hups(0),
unicode_ok(false), glyph2strfn(NULL), multibyte_strlen(NULL),
terminal_resize_handler(NULL), terminal_resize_check(NULL)
game_state() : mouse_enabled(false), waiting_for_command(false),
terminal_resized(false), io_inited(false), need_save(false),
saving_game(false), updating_scores(false), shopping(false),
seen_hups(0), unicode_ok(false), glyph2strfn(NULL),
multibyte_strlen(NULL), terminal_resize_handler(NULL),
terminal_resize_check(NULL)
// Don't give out information for things outside LOS
if (!see_grid(moves.tx, moves.ty))
break;
mid = mgrd[moves.tx][moves.ty];
oid = igrd[moves.tx][moves.ty];
if (mid != NON_MONSTER && player_monster_visible(&menv[mid]))
{
// First priority: monsters
describe_monsters(menv[mid]);
}
else if (oid != NON_ITEM)
{
// Second priority: objects
describe_item( mitm[oid] );
}
else
{
// Third priority: features
describe_feature_wide(moves.tx, moves.ty);
}
full_describe_square(moves.target());
void terse_describe_square(const coord_def &c)
{
if (!see_grid(c.x, c.y))
describe_oos_square(c.x, c.y);
else if (in_bounds(c) )
describe_cell(c.x, c.y);
}
void full_describe_square(const coord_def &c)
{
// Don't give out information for things outside LOS
if (!see_grid(c.x, c.y))
return;
const int mid = mgrd(c);
const int oid = igrd(c);
if (mid != NON_MONSTER && player_monster_visible(&menv[mid]))
{
// First priority: monsters
describe_monsters(menv[mid]);
}
else if (oid != NON_ITEM)
{
// Second priority: objects
describe_item( mitm[oid] );
}
else
{
// Third priority: features
describe_feature_wide(c.x, c.y);
}
redraw_screen();
mesclr(true);
}
/*
* File: cio.h
* Summary: System independent console IO functions
*
* Modified for Crawl Reference by $Author: dshaligram $ on $Date: 2007-06-14T18:14:26.828542Z $
*/
#ifndef CIO_H
#define CIO_H
#include "AppHdr.h"
#include "externs.h"
#include "defines.h"
#include <cctype>
#include <string>
#include <vector>
void cursorxy(int x, int y);
// Read one key, flag it as a mouse event if appropriate, but apply no
// other conversions. Defined in lib$OS.cc, not in cio.cc.
int m_getch();
// A getch() that flags mouse events and returns a consistent set of key codes
// for all platforms for a small subset of the keys Crawl is interested in.
int c_getch();
// Converts a key to a direction key, converting keypad and other sequences
// to vi key sequences (shifted/control key directions are also handled). Non
// direction keys (hopefully) pass through unmangled.
int unmangle_direction_keys(int keyin, int keymap = 0,
bool fake_ctrl = true, bool fake_shift = true);
void get_input_line( char *const buff, int len );
// In view.cc, declared here for default argument to cancelable_get_line()
int get_number_of_cols(void);
class input_history;
// Returns zero if user entered text and pressed Enter, otherwise returns the
// key pressed that caused the exit, usually Escape.
//
// If keyproc is provided, it must return 1 for normal processing, 0 to exit
// normally (pretend the user pressed Enter), or -1 to exit as if the user
// pressed Escape
int cancelable_get_line( char *buf,
int len,
int wrapcol = get_number_of_cols(),
input_history *mh = NULL,
int (*keyproc)(int &c) = NULL );
struct c_mouse_event
{
coord_def pos;
int bstate;
enum button_state_type
{
BUTTON1 = 0x1,
BUTTON1_DBL = 0x2,
BUTTON2 = 0x4,
BUTTON2_DBL = 0x8,
BUTTON3 = 0x10,
BUTTON3_DBL = 0x20,
BUTTON4 = 0x40,
BUTTON4_DBL = 0x80,
BUTTON_SCRL_UP = 0x100,
BUTTON_SCRL_DN = 0x200
};
c_mouse_event() : pos(-1, -1), bstate(0)
{
}
c_mouse_event(const coord_def &c, int state = 0) : pos(c), bstate(state)
{
}
// Returns true for valid events.
operator bool () const
{
return (bstate);
}
bool left_clicked() const
{
return (bstate & BUTTON1);
}
bool right_clicked() const
{
return (bstate & BUTTON3);
}
bool scroll_up() const
{
return (bstate & (BUTTON4 | BUTTON4_DBL | BUTTON_SCRL_UP));
}
bool scroll_down() const
{
return (bstate & (BUTTON2 | BUTTON2_DBL | BUTTON_SCRL_DN));
}
};
coord_def get_mouse_pos();
c_mouse_event get_mouse_event();
void new_mouse_event(const c_mouse_event &ce);
void flush_mouse_events();
void c_input_reset(bool enable_mouse, bool flush = false);
// Keys that getch() must return for keys Crawl is interested in.
enum KEYS
{
CK_ENTER = '\r',
CK_BKSP = 8,
CK_ESCAPE = ESCAPE,
// 128 is off-limits because it's the code that's used when running
CK_DELETE = 129,
// This sequence of enums should not be rearranged.
CK_UP,
CK_DOWN,
CK_LEFT,
CK_RIGHT,
CK_INSERT,
CK_HOME,
CK_END,
CK_CLEAR,
CK_PGUP,
CK_PGDN,
CK_SHIFT_UP,
CK_SHIFT_DOWN,
CK_SHIFT_LEFT,
CK_SHIFT_RIGHT,
CK_SHIFT_INSERT,
CK_SHIFT_HOME,
CK_SHIFT_END,
CK_SHIFT_CLEAR,
CK_SHIFT_PGUP,
CK_SHIFT_PGDN,
CK_CTRL_UP,
CK_CTRL_DOWN,
CK_CTRL_LEFT,
CK_CTRL_RIGHT,
CK_CTRL_INSERT,
CK_CTRL_HOME,
CK_CTRL_END,
CK_CTRL_CLEAR,
CK_CTRL_PGUP,
CK_CTRL_PGDN,
// Mouse codes.
CK_MOUSE_MOVE = 10001,
CK_MOUSE_CLICK
};
class cursor_control
{
public:
cursor_control(bool cursor_enabled)
: cstate(is_cursor_enabled()), smartcstate(is_smart_cursor_enabled())
{
enable_smart_cursor(false);
set_cursor_enabled(cursor_enabled);
}
~cursor_control() {
set_cursor_enabled(cstate);
enable_smart_cursor(smartcstate);
}
private:
bool cstate;
bool smartcstate;
};
// Reads lines of text; used internally by cancelable_get_line.
class line_reader
{
public:
line_reader(char *buffer, size_t bufsz,
int wrap_col = get_number_of_cols());
typedef int (*keyproc)(int &key);
int read_line(bool clear_previous = true);
std::string get_text() const;
void set_input_history(input_history *ih);
void set_keyproc(keyproc fn);
protected:
void cursorto(int newcpos);
int process_key(int ch);
void backspace();
void killword();
bool is_wordchar(int c);
private:
char *buffer;
size_t bufsz;
input_history *history;
int start_x, start_y;
keyproc keyfn;
int wrapcol;
// These are subject to change during editing.
char *cur;
int length;
int pos;
};
#endif
/*
* File: cio.cc
* Summary: Platform-independent console IO functions.
*
* Modified for Crawl Reference by $Author: dshaligram $ on $Date: 2007-06-14T18:14:26.828542Z $
*/
#include "AppHdr.h"
#include "cio.h"
#include "externs.h"
#include "macro.h"
#include <queue>
#ifdef UNIX
static keycode_type numpad2vi(keycode_type key)
{
if (key >= '1' && key <= '9')
{
const char *vikeys = "bjnh.lyku";
return keycode_type(vikeys[key - '1']);
}
return (key);
}
#endif
int unmangle_direction_keys(int keyin, int km, bool fake_ctrl, bool fake_shift)
{
const KeymapContext keymap = static_cast<KeymapContext>(km);
#ifdef UNIX
// Kludging running and opening as two character sequences
// for Unix systems. This is an easy way out... all the
// player has to do is find a termcap and numlock setting
// that will get curses the numbers from the keypad. This
// will hopefully be easy.
/* can we say yuck? -- haranp */
if (fake_ctrl && keyin == '*')
{
keyin = getchm(keymap);
// return control-key
keyin = CONTROL(toupper(numpad2vi(keyin)));
}
else if (fake_shift && keyin == '/')
{
keyin = getchm(keymap);
// return shift-key
keyin = toupper(numpad2vi(keyin));
}
#else
// Old DOS keypad support
if (keyin == 0)
{
/* FIXME haranp - hackiness */
const char DOSidiocy[10] = { "OPQKSMGHI" };
const char DOSunidiocy[10] = { "bjnh.lyku" };
const int DOScontrolidiocy[9] =
{
117, 145, 118, 115, 76, 116, 119, 141, 132
};
keyin = getchm(keymap);
for (int j = 0; j < 9; ++j )
{
if (keyin == DOSidiocy[j])
{
keyin = DOSunidiocy[j];
break;
}
if (keyin == DOScontrolidiocy[j])
{
keyin = CONTROL(toupper(DOSunidiocy[j]));
break;
}
}
}
#endif
// [dshaligram] More lovely keypad mangling.
switch (keyin)
{
#ifdef UNIX
case '1': return 'b';
case '2': return 'j';
case '3': return 'n';
case '4': return 'h';
case '6': return 'l';
case '7': return 'y';
case '8': return 'k';
case '9': return 'u';
#else
case '1': return 'B';
case '2': return 'J';
case '3': return 'N';
case '4': return 'H';
case '6': return 'L';
case '7': return 'Y';
case '8': return 'K';
case '9': return 'U';
#endif
}
return (keyin);
}
void get_input_line( char *const buff, int len )
{
buff[0] = 0; // just in case
#if defined(UNIX)
get_input_line_from_curses( buff, len ); // implemented in libunix.cc
#elif defined(WIN32CONSOLE)
getstr( buff, len );
#else
// [dshaligram] Turn on the cursor for DOS.
#ifdef DOS
_setcursortype(_NORMALCURSOR);
#endif
fgets( buff, len, stdin ); // much safer than gets()
#endif
buff[ len - 1 ] = 0; // just in case
// Removing white space from the end in order to get rid of any
// newlines or carriage returns that any of the above might have
// left there (ie fgets especially). -- bwr
const int end = strlen( buff );
int i;
for (i = end - 1; i >= 0; i++)
{
if (isspace( buff[i] ))
buff[i] = 0;
else
break;
}
}
// Hacky wrapper around getch() that returns CK_ codes for keys
// we want to use in cancelable_get_line() and menus.
int c_getch()
{
#if defined(DOS) || defined(UNIX) || defined(WIN32CONSOLE)
return getch_ck();
#else
return m_getch();
#endif
}
// Wrapper around gotoxy that can draw a fake cursor for Unix terms where
// cursoring over darkgray or black causes problems.
void cursorxy(int x, int y)
{
#ifdef UNIX
if (Options.use_fake_cursor)
fakecursorxy(x, y);
else
gotoxy(x, y);
#else
gotoxy(x, y);
#endif
}
// cprintf that knows how to wrap down lines (primitive, but what the heck)
int wrapcprintf( int wrapcol, const char *s, ... )
{
char buf[1000]; // Hard max
va_list args;
va_start(args, s);
// XXX: If snprintf isn't available, vsnprintf probably isn't, either.
int len = vsnprintf(buf, sizeof buf, s, args);
int olen = len;
va_end(args);
char *run = buf;
while (len > 0)
{
int x = wherex(), y = wherey();
if (x > wrapcol) // Somebody messed up!
return 0;
int avail = wrapcol - x + 1;
int c = 0;
if (len > avail)
{
c = run[avail];
run[avail] = 0;
}
cprintf("%s", run);
if (len > avail)
run[avail] = c;
if ((len -= avail) > 0)
gotoxy(1, y + 1);
run += avail;
}
return (olen);
}
int cancelable_get_line( char *buf, int len, int maxcol,
input_history *mh, int (*keyproc)(int &ch) )
{
line_reader reader(buf, len, maxcol);
reader.set_input_history(mh);
reader.set_keyproc(keyproc);
return reader.read_line();
}
/////////////////////////////////////////////////////////////
// input_history
//
input_history::input_history(size_t size)
: history(), pos(), maxsize(size)
{
if (maxsize < 2)
maxsize = 2;
pos = history.end();
}
void input_history::new_input(const std::string &s)
{
history.remove(s);
if (history.size() == maxsize)
history.pop_front();
history.push_back(s);
// Force the iterator to the end (also revalidates it)
go_end();
}
const std::string *input_history::prev()
{
if (history.empty())
return NULL;
if (pos == history.begin())
pos = history.end();
return &*--pos;
}
const std::string *input_history::next()
{
if (history.empty())
return NULL;
if (pos == history.end() || ++pos == history.end())
pos = history.begin();
return &*pos;
}
void input_history::go_end()
{
pos = history.end();
}
void input_history::clear()
{
history.clear();
go_end();
}
/////////////////////////////////////////////////////////////////////////
// line_reader
line_reader::line_reader(char *buf, size_t sz, int wrap)
: buffer(buf), bufsz(sz), history(NULL), start_x(0),
start_y(0), keyfn(NULL), wrapcol(wrap), cur(NULL),
length(0), pos(-1)
{
}
std::string line_reader::get_text() const
{
return (buffer);
}
void line_reader::set_input_history(input_history *i)
{
history = i;
}
void line_reader::set_keyproc(keyproc fn)
{
keyfn = fn;
}
void line_reader::cursorto(int ncx)
{
int x = (start_x + ncx - 1) % wrapcol + 1;
int y = start_y + (start_x + ncx - 1) / wrapcol;
::gotoxy(x, y);
}
int line_reader::read_line(bool clear_previous)
{
if (bufsz <= 0) return false;
cursor_control coff(true);
if (clear_previous)
*buffer = 0;
start_x = wherex();
start_y = wherey();
length = strlen(buffer);
// Remember the previous cursor position, if valid.
if (pos < 0 || pos > length)
pos = length;
cur = buffer + pos;
if (length)
wrapcprintf(wrapcol, "%s", buffer);
if (pos != length)
cursorto(pos);
if (history)
history->go_end();
for ( ; ; )
{
int ch = c_getch();
if (keyfn)
{
int whattodo = (*keyfn)(ch);
if (whattodo == 0)
{
buffer[length] = 0;
if (history && length)
history->new_input(buffer);
return (0);
}
else if (whattodo == -1)
{
buffer[length] = 0;
return (ch);
}
}
int ret = process_key(ch);
if (ret != -1)
return (ret);
}
}
void line_reader::backspace()
{
if (pos)
{
--cur;
char *c = cur;
while (*c)
{
*c = c[1];
c++;
}
--pos;
--length;
cursorto(pos);
buffer[length] = 0;
wrapcprintf( wrapcol, "%s ", cur );
cursorto(pos);
}
}
bool line_reader::is_wordchar(int c)
{
return isalnum(c) || c == '_' || c == '-';
}
void line_reader::killword()
{
if (!pos || cur == buffer)
return;
bool foundwc = false;
while (pos)
{
if (is_wordchar(cur[-1]))
foundwc = true;
else if (foundwc)
break;
backspace();
}
}
int line_reader::process_key(int ch)
{
switch (ch)
{
case CONTROL('G'):
case CK_ESCAPE:
return (CK_ESCAPE);
case CK_UP:
case CK_DOWN:
{
if (!history)
break;
const std::string *text =
ch == CK_UP? history->prev() : history->next();
if (text)
{
int olen = length;
length = text->length();
if (length >= (int) bufsz)
length = bufsz - 1;
memcpy(buffer, text->c_str(), length);
buffer[length] = 0;
cursorto(0);
int clear = length < olen? olen - length : 0;
wrapcprintf(wrapcol, "%s%*s", buffer, clear, "");
pos = length;
cur = buffer + pos;
cursorto(pos);
}
break;
}
case CK_ENTER:
buffer[length] = 0;
if (history && length)
history->new_input(buffer);
return (0);
case CONTROL('K'):
{
// Kill to end of line
int erase = length - pos;
if (erase)
{
length = pos;
buffer[length] = 0;
wrapcprintf( wrapcol, "%*s", erase, "" );
cursorto(pos);
}
break;
}
case CK_DELETE:
if (pos < length)
{
char *c = cur;
while (c - buffer < length)
{
*c = c[1];
c++;
}
--length;
cursorto(pos);
buffer[length] = 0;
wrapcprintf( wrapcol, "%s ", cur );
cursorto(pos);
}
break;
case CK_BKSP:
backspace();
break;
case CONTROL('W'):
killword();
break;
case CK_LEFT:
if (pos)
{
--pos;
cur = buffer + pos;
cursorto(pos);
}
break;
case CK_RIGHT:
if (pos < length)
{
++pos;
cur = buffer + pos;
cursorto(pos);
}
break;
case CK_HOME:
case CONTROL('A'):
pos = 0;
cur = buffer + pos;
cursorto(pos);
break;
case CK_END:
case CONTROL('E'):
pos = length;
cur = buffer + pos;
cursorto(pos);
break;
default:
if (isprint(ch) && length < (int) bufsz - 1)
{
if (pos < length)
{
char *c = buffer + length - 1;
while (c >= cur)
{
c[1] = *c;
c--;
}
}
*cur++ = (char) ch;
++length;
++pos;
putch(ch);
if (pos < length)
{
buffer[length] = 0;
wrapcprintf( wrapcol, "%s", cur );
}
cursorto(pos);
}
break;
}
return (-1);
}
/////////////////////////////////////////////////////////////////////////////
// Of mice and other mice.
static std::queue<c_mouse_event> mouse_events;
coord_def get_mouse_pos()
{
// lib$(OS) has to maintain mousep. This function is just the messenger.
return (crawl_view.mousep);
}
c_mouse_event get_mouse_event()
{
if (mouse_events.empty())
return c_mouse_event();
c_mouse_event ce = mouse_events.front();
mouse_events.pop();
return (ce);
}
void new_mouse_event(const c_mouse_event &ce)
{
mouse_events.push(ce);
}
void flush_mouse_events()
{
while (!mouse_events.empty())
mouse_events.pop();
}
void c_input_reset(bool enable_mouse, bool flush)
{
crawl_state.mouse_enabled = (enable_mouse && Options.mouse_input);
set_mouse_enabled(crawl_state.mouse_enabled);
if (flush)
flush_mouse_events();
}
if (Options.tut_explored)
Options.tut_explored = 0;
if (you.level_type == LEVEL_LABYRINTH || you.level_type == LEVEL_ABYSS)
{
mpr("It would help if you knew where you were, first.");
break;
}
yell(); /* in effects.cc */
yell();
break;
case CMD_MOUSE_MOVE:
break;
case CMD_MOUSE_CLICK:
{
// XXX: We should probably use specific commands such as
// CMD_MOUSE_TRAVEL and get rid of CMD_MOUSE_CLICK and
// CMD_MOUSE_MOVE.
c_mouse_event cme = get_mouse_event();
if (cme && crawl_view.in_view_viewport(cme.pos))
{
const coord_def dest = view2grid(cme.pos);
if (cme.left_clicked())
{
if (in_bounds(dest))
start_travel(dest.x, dest.y);
}
else if (cme.right_clicked())
{
if (see_grid(dest.x, dest.y))
full_describe_square(dest);
else
mpr("You can't see that place.");
}
}