* \brief ncurses display support
*/
-/* Copyright 2005-2013 Martin Read
+/* Copyright 2005-2014 Martin Read
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include <string.h>
#include <stdlib.h>
#include <locale.h>
+#include <langinfo.h>
#include <ncursesw/curses.h>
#include <ncursesw/panel.h>
PANEL *world_panel;
PANEL *message_panel;
PANEL *inventory_panel;
+bool force_ascii;
int wall_colour;
int you_colour;
int show_terrain;
int hard_redraw;
-attr_t colour_attrs[1 + LAST_COLOUR] =
-{
- 0,
- 0 | A_BOLD,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- A_BOLD,
- 0 | A_BOLD,
- 0 | A_BOLD,
- 0 | A_BOLD,
- 0 | A_BOLD,
- 0 | A_BOLD,
- 0 | A_BOLD,
- 0,
- A_BOLD,
- A_BOLD
-};
-
-short colour_cpairs[1 + LAST_COLOUR] =
-{
- Gcol_l_grey,
- Gcol_d_grey,
- Gcol_red,
- Gcol_blue,
- Gcol_green,
- Gcol_purple,
- Gcol_brown,
- Gcol_cyan,
- Gcol_l_grey,
- Gcol_red,
- Gcol_blue,
- Gcol_green,
- Gcol_purple,
- Gcol_brown,
- Gcol_cyan,
- /* Fancy colours */
- Gcol_cyan, // Iron = dark cyan
- Gcol_brown, // Gold = yellow
- Gcol_l_grey // Silver = white
+struct Attr_wrapper
+{
+ attr_t attr;
+ short cpair;
};
-attr_t terrain_attrs[NUM_TERRAINS] =
+Attr_wrapper colour_data[1 + LAST_COLOUR] =
{
- 0, 0, 0, 0,
- 0, 0, 0, 0
+ { 0, Gcol_l_grey },
+ { A_BOLD, Gcol_d_grey },
+ { 0, Gcol_red },
+ { 0, Gcol_blue },
+ { 0, Gcol_green },
+ { 0, Gcol_purple },
+ { 0, Gcol_brown },
+ { 0, Gcol_cyan },
+ { A_BOLD, Gcol_l_grey },
+ { A_BOLD, Gcol_red },
+ { A_BOLD, Gcol_blue },
+ { A_BOLD, Gcol_green },
+ { A_BOLD, Gcol_purple },
+ { A_BOLD, Gcol_brown },
+ { A_BOLD, Gcol_cyan },
+ /* Fancy colours */
+ { 0, Gcol_cyan }, /* Iron = dark cyan */
+ { A_BOLD, Gcol_brown }, /* Gold = yellow */
+ { A_BOLD, Gcol_l_grey }, /* Silver = white */
};
cchar_t terrain_tiles[NUM_TERRAINS];
cchar_t blank_tile;
cchar_t player_tile;
cchar_t const *back_buffer[DUN_HEIGHT][DUN_WIDTH];
+Attr_wrapper attr_override[DISP_HEIGHT][DISP_WIDTH];
cchar_t const *front_buffer[DISP_HEIGHT][DISP_WIDTH];
/* Prototypes for static funcs */
}
}
+static void load_unicode_tiles()
+{
+ int i;
+ {
+ wchar_t wch[2];
+ wch[0] = L'@';
+ wch[1] = 0;
+ setcchar(&player_tile, wch, 0, 0, NULL);
+ wch[0] = L' ';
+ setcchar(&blank_tile, wch, 0, 0, NULL);
+ }
+ for (i = 0; i < NUM_OF_PERMONS; ++i)
+ {
+ wchar_t wch[2];
+ wch[0] = permons[i].sym;
+ wch[1] = 0;
+ setcchar(permon_tiles + i, wch,
+ colour_data[permons[i].colour].attr,
+ colour_data[permons[i].colour].cpair, NULL);
+ }
+ for (i = 0; i < NUM_TERRAINS; ++i)
+ {
+ wchar_t wch[2];
+ /* policy decision: for now we don't support use of combining
+ * characters for terrain. */
+ mbtowc(wch, terrain_props[i].unicode, 4);
+ wch[1] = 0;
+ setcchar(terrain_tiles + i, wch,
+ colour_data[terrain_props[i].colour].attr,
+ colour_data[terrain_props[i].colour].cpair, NULL);
+ }
+ for (i = 0; i < NUM_OF_PERMOBJS; ++i)
+ {
+ wchar_t wch[2];
+ /* policy decision: for now we don't support use of combining
+ * characters for tiles. */
+ mbtowc(wch, permobjs[i].unicode, 4);
+ wch[1] = 0;
+ setcchar(permobj_tiles + i, wch, 0, 0, NULL);
+ }
+}
+
+static void load_ascii_tiles()
+{
+ int i;
+ {
+ wchar_t wch[2];
+ wch[0] = L'@';
+ wch[1] = 0;
+ setcchar(&player_tile, wch, 0, 0, NULL);
+ wch[0] = L' ';
+ setcchar(&blank_tile, wch, 0, 0, NULL);
+ }
+ for (i = 0; i < NUM_OF_PERMONS; ++i)
+ {
+ wchar_t wch[2];
+ wch[0] = permons[i].sym;
+ wch[1] = 0;
+ setcchar(permon_tiles + i, wch,
+ colour_data[permons[i].colour].attr,
+ colour_data[permons[i].colour].cpair, NULL);
+ }
+ for (i = 0; i < NUM_TERRAINS; ++i)
+ {
+ wchar_t wch[2];
+ wch[0] = terrain_props[i].ascii;
+ wch[1] = 0;
+ setcchar(terrain_tiles + i, wch,
+ colour_data[terrain_props[i].colour].attr,
+ colour_data[terrain_props[i].colour].cpair, NULL);
+ }
+ for (i = 0; i < NUM_OF_PERMOBJS; ++i)
+ {
+ wchar_t wch[2];
+ wch[0] = permobjs[i].sym;
+ wch[1] = 0;
+ setcchar(permobj_tiles + i, wch, 0, 0, NULL);
+ }
+}
+
/* extern funcs */
/*! \brief Wait for the user to press RETURN
int display_init(void)
{
int i;
- char const *foo;
- foo = getenv("LANG");
- if (foo)
- {
- setlocale(LC_CTYPE, foo);
- }
- else
+ int j;
+ char const *lang;
+ char const *ct;
+ char const *all;
+ char const *encoding;
+
+ /*
+ * Yes, this code will misidentify the availability of meaningful wide
+ * characters on architectures where char is larger than 8 bits.
+ *
+ * It also treats all character encodings that are not UTF-8 as "ASCII".
+ *
+ * Contributions of additional display-[foo].cc modules that provide
+ * support for such architectures and/or character encodings will be
+ * considered.
+ */
+ if (sizeof(wchar_t) > 1)
{
- foo = getenv("LC_CTYPE");
- if (foo)
+ lang = getenv("LANG");
+ ct = getenv("LC_CTYPE");
+ all = getenv("LC_ALL");
+ if (all || lang || ct)
{
- setlocale(LC_CTYPE, foo);
+ setlocale(LC_ALL, "");
}
else
{
+ /* In the name of my narrowly defined concept of sanity, if the
+ * user has not configured any of the relevant locale envvars,
+ * force the C.UTF-8 locale. */
setlocale(LC_CTYPE, "C.UTF-8");
}
+ /* The only wire encodings I am willing to support are UTF-8 and
+ * ASCII. If you want support for other wire encodings, you can
+ * maintain your own patch for it. */
+ encoding = nl_langinfo(CODESET);
+ force_ascii = strcmp(encoding, "UTF-8");
+ }
+ else
+ {
+ force_ascii = true;
}
initscr();
noecho();
init_pair(Gcol_cyan, COLOR_CYAN, COLOR_BLACK);
wall_colour = Gcol_brown;
you_colour = Gcol_white;
+ for (i = 0; i < DISP_HEIGHT; ++i)
{
- wchar_t wch[2];
- wch[0] = L'@';
- wch[1] = 0;
- setcchar(&player_tile, wch, 0, 0, NULL);
- wch[0] = L' ';
- setcchar(&blank_tile, wch, 0, 0, NULL);
- }
- for (i = 0; i < NUM_OF_PERMONS; ++i)
- {
- wchar_t wch[2];
- wch[0] = permons[i].sym;
- wch[1] = 0;
- setcchar(permon_tiles + i, wch, colour_attrs[permons[i].colour], colour_cpairs[permons[i].colour], NULL);
+ for (j = 0; j < DISP_WIDTH; ++j)
+ {
+ }
}
- for (i = 0; i < NUM_TERRAINS; ++i)
+ if (force_ascii)
{
- wchar_t wch[2];
- /* policy decision: for now we don't support use of combining
- * characters for terrain. */
- mbtowc(wch, terrain_props[i].unicode, 4);
- wch[1] = 0;
- setcchar(terrain_tiles + i, wch, terrain_attrs[i], colour_cpairs[terrain_props[i].colour], NULL);
+ load_ascii_tiles();
}
- for (i = 0; i < NUM_OF_PERMOBJS; ++i)
+ else
{
- wchar_t wch[2];
- wch[0] = permobjs[i].sym;
- wch[1] = 0;
- setcchar(permobj_tiles + i, wch, 0, 0, NULL);
+ load_unicode_tiles();
}
/* OK. We want a 21x21 viewport (player at centre), a 21x58 message
* window, and a 2x80 status line. */
- status_window = newwin(2, 80, 22, 0);
+ status_window = newwin(2, 80, DISP_HEIGHT + 1, 0);
status_panel = new_panel(status_window);
- world_window = newwin(21, 21, 0, 0);
+ world_window = newwin(DISP_HEIGHT, DISP_WIDTH, 0, 0);
world_panel = new_panel(world_window);
- message_window = newwin(21, 58, 0, 22);
+ message_window = newwin(DISP_HEIGHT, 58, 0, DISP_WIDTH + 1);
message_panel = new_panel(message_window);
- inventory_window = newwin(22, 58, 0, 22);
+ inventory_window = newwin(DISP_HEIGHT, 58, 0, DISP_WIDTH + 1);
inventory_panel = new_panel(inventory_window);
- wattrset(inventory_window, colour_attrs[Gcol_l_grey]);
+ wattr_set(inventory_window, colour_data[Gcol_l_grey].attr,
+ colour_data[Gcol_l_grey].cpair, NULL);
hide_panel(inventory_panel);
clear();
wclear(status_window);
/*! \brief Print some text in the message window
*
- * \todo Handle the message window getting resized
+ * Calling print_msg() prints formatted text (printf-style) to the message
+ * window. It also calls display_update(), so any change to the map or
+ * the player should be flagged *before* print_msg() is called with the
+ * message announcing the change.
+ *
+ * \todo Handle the message window getting resized.
+ * \todo Implement message scrollback.
+ * \todo Put line breaks at word gaps in long messages.
+ * \todo Handle large volumes of messages between player turns.
*/
void print_msg(char const *fmt, ...)
{
va_list ap;
- /* For now, assume (1) that the player will never be so inundated
- * with messages that it's dangerous to let them just fly past (2)
- * that messages will be of sane length and nicely formatted. THIS
- * IS VERY BAD CODING PRACTICE! */
- /* Note that every message forces a call to display_update().
- * Events that cause changes to the map or the player should flag
- * the change before calling printmsg. */
va_start(ap, fmt);
vw_printw(message_window, fmt, ap);
va_end(ap);
}
/*! \brief Set a message and whether 'nothing' should be listed in inventory
+ *
+ * \param s Message to set.
+ * \param allow_nil Whether the 'nothing' option should be listed.
*/
void set_inventory_message(char const *s, bool allow_nil)
{
*/
void reset_inventory_message(void)
{
- wattrset(inventory_window, colour_attrs[Gcol_l_grey]);
+ wattr_set(inventory_window, colour_data[Gcol_l_grey].attr, colour_data[Gcol_l_grey].cpair, NULL);
mvwprintw(inventory_window, 0, 0, " === INVENTORY ===");
mvwprintw(inventory_window, 20, 0, " ");
}
doupdate();
}
-/*! \brief Hide the inventory if necessary on this display type
+/*! \brief Hide the inventory if necessary on this display type
*/
void hide_inv(void)
{
/*! \brief Update the inventory window to reflect the filter in force
*
- * \todo Switch to using a function pointer + private argument for filtering
+ * \todo Switch to using a function pointer + private argument for filtering
*/
void update_inv(enum poclass_num filter)
{
int i;
char inv_line[60];
- wattr_set(inventory_window, colour_attrs[Gcol_d_grey], Gcol_d_grey, NULL);
+ wattr_set(inventory_window, colour_data[Gcol_d_grey].attr, colour_data[Gcol_d_grey].cpair, NULL);
for (i = 0; i < 19; i++)
{
if (u.inventory[i] == NO_OBJ)
{
- wattr_set(inventory_window, colour_attrs[Gcol_d_grey], Gcol_d_grey, NULL);
+ wattr_set(inventory_window, colour_data[Gcol_d_grey].attr, colour_data[Gcol_d_grey].cpair, NULL);
mvwprintw(inventory_window, i + 1, 0, "%c) -----\n", 'a' + i);
}
else
if ((filter == POCLASS_NONE) ||
(permobjs[objects[u.inventory[i]].obj_id].poclass == filter))
{
- wattr_set(inventory_window, colour_attrs[Gcol_l_grey], Gcol_l_grey, NULL);
+ wattr_set(inventory_window, colour_data[Gcol_l_grey].attr, colour_data[Gcol_l_grey].cpair, NULL);
}
else
{
- wattr_set(inventory_window, colour_attrs[Gcol_d_grey], Gcol_d_grey, NULL);
+ wattr_set(inventory_window, colour_data[Gcol_d_grey].attr, colour_data[Gcol_d_grey].cpair, NULL);
}
sprintf(inv_line, "%c) ", 'a' + i);
sprint_obj_name(inv_line + 3, u.inventory[i], 45);
print_msg("You have nothing to %s.\n", action);
return SLOT_CANCEL;
}
- wattrset(inventory_window, colour_attrs[Gcol_l_grey]);
+ wattr_set(inventory_window, colour_data[Gcol_l_grey].attr, colour_data[Gcol_l_grey].cpair, NULL);
snprintf(msg, 58, "What do you want to %s?\n", action);
set_inventory_message(msg, accept_blank);
update_inv(filter);
- wattrset(inventory_window, colour_attrs[Gcol_l_grey]);
+ wattr_set(inventory_window, colour_data[Gcol_l_grey].attr, colour_data[Gcol_l_grey].cpair, NULL);
mvwprintw(inventory_window, 21, 17, "[ESC or SPACE to cancel]");
show_inv();
tryagain:
*
* \todo Consider redesigning in a way friendlier to non-terminal platforms
*/
-Game_cmd get_command(void)
+void get_player_action(Action *act)
{
int ch;
+ Pass_fail pf;
+ Offset step;
while (1)
{
ch = wgetch(message_window);
switch (ch)
{
case 'a':
- return ATTACK;
+ {
+ act->cmd = ATTACK;
+ pf = select_dir(&step);
+ if (pf != You_fail)
+ {
+ act->details[0] = step.y;
+ act->details[1] = step.x;
+ return;
+ }
+ }
+ break;
case '0':
case ',':
case 'g':
- return GET_ITEM;
+ act->cmd = GET_ITEM;
+ /*! \todo revisit if we ever implement multiple items/square */
+ return;
case 'd':
- return DROP_ITEM;
+ {
+ if (lvl.obj_at(u.pos) != NO_OBJ)
+ {
+ print_msg("There is already an item here.\n");
+ }
+ else
+ {
+ int slot = inv_select(POCLASS_NONE, "drop", 0);
+ if (slot >= 0)
+ {
+ if ((u.inventory[slot] == u.ring) ||
+ (u.inventory[slot] == u.armour))
+ {
+ print_msg("You cannot drop something you are wearing.\n");
+ }
+ else
+ {
+ act->cmd = DROP_ITEM;
+ act->details[0] = slot;
+ return;
+ }
+ }
+ }
+ }
+ break;
case '@':
- return DUMP_CHARA;
+ write_char_dump();
+ break;
case 'S':
- return SAVE_GAME;
+ act->cmd = SAVE_GAME;
+ return;
case 'X':
- return QUIT;
+ {
+ int j = getYN("Really quit?\n");
+ if (j > 0)
+ {
+ act->cmd = QUIT;
+ return;
+ }
+ else
+ {
+ print_msg("Never mind.\n");
+ }
+ }
+ break;
case 'i':
if (panel_hidden(inventory_panel))
{
{
hide_inv();
}
- continue;
+ break;
case 'I':
- return INSPECT_ITEM;
+ {
+ int slot = inv_select(POCLASS_NONE, "inspect", 0);
+ if ((slot != SLOT_CANCEL) && (u.inventory[slot] != NO_OBJ))
+ {
+ describe_object(u.inventory[slot]);
+ }
+ }
+ break;
case ';':
- return EXAMINE_MONSTER;
+ print_msg("Monster examination not implemented yet.\n");
+ break;
case '#':
- return SHOW_TERRAIN;
- case '\x12':
- return RNG_TEST;
+ show_terrain = 1;
+ touch_back_buffer();
+ display_update();
+ print_msg("Display of monsters and objects suppressed.\n");
+ press_enter();
+ show_terrain = 0;
+ touch_back_buffer();
+ display_update();
+ break;
case '4':
case 'h':
- return MOVE_WEST;
+ {
+ act->cmd = WALK;
+ act->details[0] = West.y;
+ act->details[1] = West.x;
+ }
+ return;
case '2':
case 'j':
- return MOVE_SOUTH;
+ {
+ act->cmd = WALK;
+ act->details[0] = South.y;
+ act->details[1] = South.x;
+ }
+ return;
case '8':
case 'k':
- return MOVE_NORTH;
+ {
+ act->cmd = WALK;
+ act->details[0] = North.y;
+ act->details[1] = North.x;
+ }
+ return;
case '6':
case 'l':
- return MOVE_EAST;
+ {
+ act->cmd = WALK;
+ act->details[0] = East.y;
+ act->details[1] = East.x;
+ }
+ return;
case '7':
case 'y':
- return MOVE_NW;
+ {
+ act->cmd = WALK;
+ act->details[0] = Northwest.y;
+ act->details[1] = Northwest.x;
+ }
+ return;
case '9':
case 'u':
- return MOVE_NE;
+ {
+ act->cmd = WALK;
+ act->details[0] = Northeast.y;
+ act->details[1] = Northeast.x;
+ }
+ return;
case '1':
case 'b':
- return MOVE_SW;
+ {
+ act->cmd = WALK;
+ act->details[0] = Southwest.y;
+ act->details[1] = Southwest.x;
+ }
+ return;
case '3':
case 'n':
- return MOVE_SE;
+ {
+ act->cmd = WALK;
+ act->details[0] = Southeast.y;
+ act->details[1] = Southeast.x;
+ }
+ return;
case 'q':
- return QUAFF_POTION;
+ {
+ int slot = inv_select(POCLASS_POTION, "quaff", 0);
+ if (slot != SLOT_CANCEL)
+ {
+ act->cmd = QUAFF_POTION;
+ act->details[0] = slot;
+ return;
+ }
+ }
+ break;
case 'r':
- return READ_SCROLL;
+ {
+ int slot = inv_select(POCLASS_SCROLL, "read", 0);
+ if (slot != SLOT_CANCEL)
+ {
+ act->cmd = READ_SCROLL;
+ act->details[0] = slot;
+ return;
+ }
+ }
+ break;
case 'e':
- return EAT_FOOD;
- case 't':
- return THROW_FLASK;
+ {
+ int slot = inv_select(POCLASS_FOOD, "eat", 0);
+ if (slot != SLOT_CANCEL)
+ {
+ act->cmd = EAT_FOOD;
+ act->details[0] = slot;
+ return;
+ }
+ }
+ break;
+ /*case 't':
+ // nobbling flask-throwing for now, as part of the general
+ // revisting of ranged combat.
+ return THROW_FLASK;*/
case 'w':
- return WIELD_WEAPON;
+ {
+ int slot = inv_select(POCLASS_WEAPON, "wield", 1);
+ if (slot != SLOT_CANCEL)
+ {
+ if (slot == SLOT_NOTHING && u.weapon == NO_OBJ)
+ {
+ print_msg("Already empty-handed.\n");
+ }
+ else
+ {
+ act->cmd = WIELD_WEAPON;
+ act->details[0] = slot;
+ return;
+ }
+ }
+ }
+ break;
case 'z':
- return ZAP_WEAPON;
+ if (u.weapon == NO_OBJ)
+ {
+ print_msg("You cannot zap your weapon when you aren't wielding one.\n");
+ }
+ else
+ {
+ act->cmd = ZAP_WEAPON;
+ return;
+ }
+ break;
case 'W':
- return WEAR_ARMOUR;
+ {
+ int slot = inv_select(POCLASS_ARMOUR, "wear", 1);
+ if (slot != SLOT_CANCEL)
+ {
+ act->cmd = WEAR_ARMOUR;
+ act->details[0] = slot;
+ return;
+ }
+ }
+ break;
case 'T':
- return TAKE_OFF_ARMOUR;
+ act->cmd = TAKE_OFF_ARMOUR;
+ return;
case 'E':
- return EMANATE_ARMOUR;
+ act->cmd = EMANATE_ARMOUR;
+ return;
case 'P':
- return PUT_ON_RING;
+ if (u.ring != NO_OBJ)
+ {
+ print_msg("You are already wearing a magic ring. Remove it first.\n");
+ }
+ else
+ {
+ int slot = inv_select(POCLASS_RING, "put on", 0);
+ if (slot != SLOT_CANCEL)
+ {
+ act->cmd = PUT_ON_RING;
+ act->details[0] = slot;
+ return;
+ }
+ }
+ break;
case 'R':
- return REMOVE_RING;
+ if (u.ring == NO_OBJ)
+ {
+ print_msg("You have no ring to remove!\n");
+ }
+ else if (ring_removal_unsafe(Noise_std) == You_pass)
+ {
+ act->cmd = REMOVE_RING;
+ return;
+ }
+ break;
case 'm':
- return MAGIC_RING;
+ if (u.ring == NO_OBJ)
+ {
+ print_msg("You are not wearing a ring.\n");
+ }
+ else
+ {
+ act->cmd = MAGIC_RING;
+ return;
+ }
+ break;
case '?':
- return GIVE_HELP;
+ print_help();
+ break;
case '>':
- return GO_DOWN_STAIRS;
+ act->cmd = GO_DOWN_STAIRS;
+ return;
case '5':
case '.':
- return STAND_STILL;
+ act->cmd = STAND_STILL;
+ return;
case '\x04':
- return WIZARD_DESCEND;
+ act->cmd = WIZARD_DESCEND;
+ return;
case '\x05':
- return WIZARD_LEVELUP;
+ act->cmd = WIZARD_LEVELUP;
+ return;
}
}
- return QUIT;
+ print_msg("BUG: broke out of input loop!\n");
+ act->cmd = QUIT;
+ return;
}
/*! \brief Shut down the display subsystem
}
}
-/* display.c */
+/* And now for the API fun that is "moving all print_msg() calls out of the
+ * engine code". Which we will do one step at a time. */
+
+void notify_level_gain(void)
+{
+ status_updated = 1;
+ print_msg("You gained a level!\n");
+}
+
+void notify_agility_gain(int amount)
+{
+ status_updated = 1;
+ print_msg("You gained %d Agility.\n", amount);
+}
+
+void notify_body_gain(int amount)
+{
+ status_updated = 1;
+ print_msg("You gained %d Body.\n", amount);
+}
+
+void notify_hp_gain(int amount)
+{
+ status_updated = 1;
+ print_msg("You gained %d hit points.\n", amount);
+}
+
+void notify_player_teleport(void)
+{
+ print_msg("You are whisked away!\n");
+}
+
+
+void notify_player_telefail(void)
+{
+ print_msg("You feel briefly dislocated.\n");
+}
+
+void notify_player_regen(void)
+{
+ print_msg("Your ring pulses soothingly.\n");
+}
+
+void notify_hunger_level(int severity)
+{
+ switch (severity)
+ {
+ case 0:
+ default:
+ break;
+ case 1:
+ print_msg("You are getting quite hungry.\n");
+ break;
+ case 2:
+ print_msg("You are feeling hunger pangs, and will recover\nmore slowly from your injuries.\n");
+ break;
+ }
+}
+
+void notify_leadfoot_recovered(void)
+{
+ print_msg("You shed your feet of lead.\n");
+}
+
+void notify_armourmelt_recovered(void)
+{
+ print_msg("Your armour seems solid once more.\n");
+}
+
+void notify_wither_recovered(void)
+{
+ print_msg("Your limbs straighten.\n");
+}
+
+void notify_protection_lost(void)
+{
+ print_msg("You feel like you are no longer being helped.\n");
+}
+
+void notify_start_lavawalk(void)
+{
+ print_msg("You walk on the lava.\n");
+}
+
+void notify_blocked_lava(void)
+{
+ print_msg("The fierce heat of the molten rock repels you.\n");
+}
+
+void notify_start_waterwalk(void)
+{
+ print_msg("You walk on the water.\n");
+}
+
+void notify_blocked_water(void)
+{
+ print_msg("The idiot who raised you never taught you to swim.\n");
+}
+
+void notify_obj_at(Coord c)
+{
+ print_msg("You see here ");
+ print_obj_name(lvl.obj_at(c));
+ print_msg(".\n");
+}
+
+void debug_move_oob(Coord c)
+{
+ print_msg("NOTICE: Attempted move out of bounds (dest %d, %d)\n", c.y, c.x);
+}
+
+void debug_agility_gain(int amount)
+{
+ print_msg("BUG: Attempt to cause negative agility gain %d\n", amount);
+}
+
+void debug_body_gain(int amount)
+{
+ print_msg("BUG: Attempt to cause negative body gain %d\n", amount);
+}
+
+void notify_swing_bow(void)
+{
+ print_msg("You can't use that weapon in melee!\n");
+}
+
+void notify_cant_go(void)
+{
+ print_msg("You cannot go there.\n");
+}
+
+void notify_wasted_gain(void)
+{
+ print_msg("You feel disappointed.\n");
+}
+
+void debug_bad_monspell(int spell)
+{
+ print_msg("BUG: Attempt by monster to cast bogus/unimplemented spell %d!\n", spell);
+}
+
+void notify_summon_help(int mon, bool success)
+{
+ /* Do the summoning... */
+ print_mon_name(mon, 3);
+ print_msg(" calls for help...\n");
+ if (success)
+ {
+ print_msg("... and gets it.\n");
+ }
+ else
+ {
+ print_msg("... luckily for you, help wasn't listening.\n");
+ }
+}
+
+void notify_mon_disappear(int mon)
+{
+ print_mon_name(mon, 3);
+ print_msg(" vanishes in a puff of smoke.\n");
+}
+
+void notify_start_armourmelt(void)
+{
+ status_updated = 1;
+ print_msg("Your armour seems suddenly no stronger than dust!\n");
+}
+
+void notify_start_leadfoot(void)
+{
+ status_updated = 1;
+ print_msg("Your feet feel like lead!\n");
+}
+
+void notify_start_withering(void)
+{
+ status_updated = 1;
+ print_msg("Your limbs twist and wither!\n");
+}
+
+void notify_necrosmite_fail(void)
+{
+ print_msg("Darkness reaches towards you, but dissolves.\n");
+}
+
+void notify_necrosmite_hit(void)
+{
+ print_msg("Soul-chilling darkness engulfs you!\n");
+}
+
+void notify_moncurse_fail(void)
+{
+ print_msg("A malignant aura surrounds you briefly.\n");
+}
+
+void notify_hellfire_hit(bool resisted)
+{
+ print_msg("The fires of hell %s\n", resisted ? "lightly singe you." : "burn you!");
+}
+
+void notify_monster_cursing(int mon)
+{
+ print_mon_name(mon, 3);
+ print_msg(" points at you and curses horribly.\n");
+}
+
+/* display-nc.cc */
// vim:cindent
print_msg("Initialisation complete.\n");
}
-Action_cost do_command(Game_cmd cmd)
+Action_cost do_player_action(Action *act)
{
- int i;
- int j;
- Pass_fail pf;
int slot;
Action_cost cost;
Offset step;
- switch (cmd)
+ switch (act->cmd)
{
- case MOVE_NORTH:
- return move_player(North);
- case MOVE_SOUTH:
- return move_player(South);
- case MOVE_EAST:
- return move_player(East);
- case MOVE_WEST:
- return move_player(West);
- case MOVE_NW:
- return move_player(Northwest);
- case MOVE_NE:
- return move_player(Northeast);
- case MOVE_SE:
- return move_player(Southeast);
- case MOVE_SW:
- return move_player(Southwest);
+ case WALK:
+ step.y = act->details[0];
+ step.x = act->details[1];
+ return move_player(step);
case ATTACK:
- pf = select_dir(&step);
- if (pf != You_fail)
- {
- return player_attack(step);
- }
- return Cost_none;
+ return player_attack(step);
case GET_ITEM:
if (lvl.obj_at(u.pos) != NO_OBJ)
}
case WIELD_WEAPON:
- cost = Cost_none;
- slot = inv_select(POCLASS_WEAPON, "wield", 1);
+ slot = act->details[0];
if (slot == SLOT_NOTHING)
{
- u.weapon = NO_OBJ;
- print_msg("Weapon unwielded.\n");
+ return player_unwield(Noise_std);
}
- else if (slot != SLOT_CANCEL)
+ else
{
- u.weapon = u.inventory[slot];
- cost = Cost_std;
- print_msg("Wielding ");
- print_obj_name(u.weapon);
- print_msg(".\n");
+ return player_wield(slot, Noise_std);
}
- return cost;
+ break;
case WEAR_ARMOUR:
cost = Cost_none;
return Cost_none;
}
- case GIVE_HELP:
- print_help();
- return Cost_none;
-
case GO_DOWN_STAIRS:
if (lvl.terrain_at(u.pos) == STAIRS)
{
return Cost_std;
case READ_SCROLL:
- slot = inv_select(POCLASS_SCROLL, "read", 0);
- if (slot != SLOT_CANCEL)
- {
- return read_scroll(u.inventory[slot]);
- }
- return Cost_none;
+ slot = act->details[0];
+ return read_scroll(u.inventory[slot]);
case EAT_FOOD:
- slot = inv_select(POCLASS_FOOD, "eat", 0);
- if (slot != SLOT_CANCEL)
- {
- return eat_food(u.inventory[slot]);
- }
- return Cost_none;
+ slot = act->details[0];
+ return eat_food(u.inventory[slot]);
case QUAFF_POTION:
- slot = inv_select(POCLASS_POTION, "quaff", 0);
- if (slot != SLOT_CANCEL)
- {
- return quaff_potion(u.inventory[slot]);
- }
- return Cost_none;
+ slot = act->details[0];
+ return quaff_potion(u.inventory[slot]);
case THROW_FLASK:
- slot = inv_select(POCLASS_FLASK, "throw", 0);
- if (slot != SLOT_CANCEL)
- {
- pf = select_dir(&step);
- if (pf != You_fail)
- {
- return throw_flask(u.inventory[slot], step);
- }
- }
return Cost_none;
case REMOVE_RING:
- cost = Cost_none;
- if (u.ring == NO_OBJ)
- {
- print_msg("You have no ring to remove!\n");
- }
- else if ((lvl.terrain_at(u.pos) == LAVA) && (u.resistances[DT_FIRE] == RESIST_RING))
- {
- print_msg("That ring is your only current source of fire resistance. Removing\nit here would incinerate you.\n");
- }
- else if ((objects[u.ring].obj_id == PO_FROST_RING) && (lvl.terrain_at(u.pos) == WATER))
- {
- print_msg("Since nobody ever taught you to swim, removing that ring\nhere would result in your death by drowning.\n");
- }
- else
- {
- print_msg("You remove your ring.\n");
- u.ring = NO_OBJ;
- cost = Cost_std;
- }
- return cost;
+ return remove_ring();
+
case PUT_ON_RING:
- cost = Cost_none;
- if (u.ring != NO_OBJ)
- {
- print_msg("You are already wearing a ring.\n");
- }
- else
- {
- slot = inv_select(POCLASS_RING, "put on", 0);
- if (slot != SLOT_CANCEL)
- {
- u.ring = u.inventory[slot];
- print_msg("You put on ");
- print_obj_name(u.ring);
- print_msg(".\n");
- cost = Cost_std;
- }
- }
- return cost;
- case INSPECT_ITEM:
- slot = inv_select(POCLASS_NONE, "inspect", 0);
- if ((slot != SLOT_CANCEL) && (u.inventory[slot] != NO_OBJ))
- {
- describe_object(u.inventory[slot]);
- }
- return Cost_none;
- case EXAMINE_MONSTER:
- print_msg("Monster examination not implemented yet.\n");
- return Cost_none;
- case SHOW_TERRAIN:
- show_terrain = 1;
- touch_back_buffer();
- display_update();
- print_msg("Display of monsters and objects suppressed.\n");
- press_enter();
- show_terrain = 0;
- touch_back_buffer();
- display_update();
- return Cost_none;
- case RNG_TEST:
- {
- int odds = 0;
- int evens = 0;
- for (i = 0; i < 100000; i++)
- {
- if (zero_die(2))
- {
- odds++;
- }
- else
- {
- evens++;
- }
- }
- print_msg("100k rolls: 0 %d, 1 %d\n", odds, evens);
- }
- print_msg(" 2: %d %d %d %d %d %d %d %d\n", zero_die(2), zero_die(2), zero_die(2), zero_die(2), zero_die(2), zero_die(2), zero_die(2), zero_die(2));
- print_msg(" 8: %d %d %d %d %d %d %d %d\n", zero_die(8), zero_die(8), zero_die(8), zero_die(8), zero_die(8), zero_die(8), zero_die(8), zero_die(8));
- print_msg("32: %d %d %d %d %d %d %d %d\n", zero_die(32), zero_die(32), zero_die(32), zero_die(32), zero_die(32), zero_die(32), zero_die(32), zero_die(32));
- return Cost_none;
+ slot = act->details[0];
+ return put_on_ring(u.inventory[slot]);
+
case DROP_ITEM:
- cost = Cost_none;
- if (lvl.obj_at(u.pos) != NO_OBJ)
- {
- print_msg("There is already an item here.\n");
- }
- else
- {
- slot = inv_select(POCLASS_NONE, "drop", 0);
- if (slot >= 0)
- {
- if ((u.inventory[slot] != NO_OBJ) &&
- ((u.inventory[slot] == u.ring) ||
- (u.inventory[slot] == u.armour)))
- {
- print_msg("You cannot drop something you are wearing.\n");
- }
- else
- {
- cost = drop_obj(slot);
- }
- }
- }
- return cost;
- case DUMP_CHARA:
- write_char_dump();
- return Cost_none;
+ slot = act->details[0];
+ return drop_obj(slot);
+
case SAVE_GAME:
game_finished = 1;
save_game();
return Cost_none;
+
case QUIT:
- j = getYN("Really quit?\n");
- if (j > 0)
- {
- game_finished = 1;
- }
- else
- {
- print_msg("Never mind.\n");
- }
+ game_finished = 1;
return Cost_none;
case WIZARD_DESCEND:
void main_loop(void)
{
- Game_cmd cmd;
int i;
int action_speed;
print_msg("Welcome to the Abyss, Princess %s.\n", u.name);
/* Player is always speed 1, for now. */
if (action_speed <= u.speed)
{
- i = 0;
- while (!i)
+ Action act;
+ Action_cost cost = Cost_none;
+ do
{
- /* Take commands until the player does
- * something that uses an action. */
- cmd = get_command();
- i = do_command(cmd);
+ get_player_action(&act);
+ cost = do_player_action(&act);
if (game_finished)
{
break;
}
- }
- if (game_finished)
- {
- break;
- }
+ } while (cost == Cost_none);
}
for (i = 0; i < 100; i++)
{