Incremental checkpoint for project "No prose in the game engine"
authorMartin Read <martin@blackswordsonics.com>
Tue, 21 Jan 2014 16:16:23 +0000 (16:16 +0000)
committerMartin Read <martin@blackswordsonics.com>
Tue, 21 Jan 2014 16:16:23 +0000 (16:16 +0000)
display-nc.cc
display.hh
main.cc
map.cc
objects.cc
objects.hh
sorcery.cc
u.cc
victrix-abyssi.hh

index 0c4df74..62e9eae 100644 (file)
@@ -2,7 +2,7 @@
  *  \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
@@ -34,6 +34,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <locale.h>
+#include <langinfo.h>
 #include <ncursesw/curses.h>
 #include <ncursesw/panel.h>
 
@@ -52,6 +53,7 @@ PANEL *status_panel;
 PANEL *world_panel;
 PANEL *message_panel;
 PANEL *inventory_panel;
+bool force_ascii;
 
 int wall_colour;
 int you_colour;
@@ -60,55 +62,33 @@ int map_updated;
 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];
@@ -123,6 +103,7 @@ cchar_t permon_tiles[NUM_OF_PERMONS];
 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 */
@@ -265,6 +246,86 @@ static void draw_world(void)
     }
 }
 
+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
@@ -311,23 +372,47 @@ void display_update(void)
 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();
@@ -342,48 +427,32 @@ int display_init(void)
     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);
@@ -423,18 +492,19 @@ int read_input(char *buffer, int length)
 
 /*! \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);
@@ -442,6 +512,9 @@ void print_msg(char const *fmt, ...)
 }
 
 /*! \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)
 {
@@ -460,7 +533,7 @@ 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, "                 ");
 }
@@ -474,7 +547,7 @@ void show_inv(void)
     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)
 {
@@ -486,18 +559,18 @@ 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
@@ -505,11 +578,11 @@ void update_inv(enum poclass_num filter)
             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);
@@ -550,11 +623,11 @@ int inv_select(enum poclass_num filter, char const *action, int accept_blank)
         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:
@@ -680,9 +753,11 @@ Pass_fail select_dir(Offset *pstep)
  *
  * \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);
@@ -697,19 +772,69 @@ Game_cmd get_command(void)
         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))
             {
@@ -721,77 +846,235 @@ Game_cmd get_command(void)
             {
                 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
@@ -949,5 +1232,211 @@ void touch_one_screen(Coord c)
     }
 }
 
-/* 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
index 6a185d9..6389df4 100644 (file)
@@ -38,7 +38,7 @@ extern int display_shutdown(void);
 extern void newsym(Coord c);
 extern void touch_back_buffer(void);
 extern int inv_select(enum poclass_num filter, char const *action, int accept_blank);
-extern Game_cmd get_command(void);
+extern void get_player_action(Action *act);
 extern Pass_fail select_dir(Offset *pstep);
 extern int getYN(char const *msg);
 extern int getyn(char const *msg);
@@ -54,7 +54,47 @@ extern int map_updated;
 /* "Show the player the terrain only" flag. */
 extern int show_terrain;
 
+/* Notification functions */
+void notify_level_gain(void);
+void notify_agility_gain(int amount);
+void notify_body_gain(int amount);
+void notify_hp_gain(int amount);
+void notify_player_teleport(void);
+void notify_player_telefail(void);
+void notify_player_regen(void);
+void notify_hunger_level(int severity);
+void notify_leadfoot_recovered(void);
+void notify_armourmelt_recovered(void);
+void notify_wither_recovered(void);
+void notify_protection_lost(void);
+void notify_start_lavawalk(void);
+void notify_blocked_lava(void);
+void notify_start_waterwalk(void);
+void notify_blocked_water(void);
+void notify_obj_at(Coord c);
+void notify_swing_bow(void);
+void notify_cant_go(void);
+void notify_wasted_gain(void);
+
+/* Sorcery notifications */
+void notify_summon_help(int mon, bool success);
+void notify_monster_cursing(int mon);
+void notify_mon_disappear(int mon);
+void notify_moncurse_fail(void);
+void notify_start_armourmelt(void);
+void notify_start_withering(void);
+void notify_start_leadfoot(void);
+void notify_necrosmite_fail(void);
+void notify_necrosmite_hit(void);
+void notify_hellfire_hit(bool resisted);
+
+/* Debugging notifications */
+void debug_move_oob(Coord c);
+void debug_body_gain(int amount);
+void debug_agility_gain(int amount);
+void debug_bad_monspell(int spell);
+
 #endif
 
-/* display.h */
+/* display.hh */
 // vim:cindent
diff --git a/main.cc b/main.cc
index f3deb6d..f48c359 100644 (file)
--- a/main.cc
+++ b/main.cc
@@ -224,40 +224,20 @@ void new_game(void)
     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)
@@ -272,22 +252,16 @@ Action_cost do_command(Game_cmd cmd)
         }
 
     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;
@@ -361,10 +335,6 @@ Action_cost do_command(Game_cmd cmd)
             return Cost_none;
         }
 
-    case GIVE_HELP:
-        print_help();
-        return Cost_none;
-
     case GO_DOWN_STAIRS:
         if (lvl.terrain_at(u.pos) == STAIRS)
         {
@@ -381,163 +351,38 @@ Action_cost do_command(Game_cmd cmd)
         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:
@@ -571,7 +416,6 @@ Action_cost do_command(Game_cmd cmd)
 
 void main_loop(void)
 {
-    Game_cmd cmd;
     int i;
     int action_speed;
     print_msg("Welcome to the Abyss, Princess %s.\n", u.name);
@@ -594,22 +438,17 @@ void main_loop(void)
         /* 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++)
         {
diff --git a/map.cc b/map.cc
index 09f106a..6b9a603 100644 (file)
--- a/map.cc
+++ b/map.cc
@@ -140,6 +140,10 @@ static void run_random_walk(Coord oc, rwalk_mod_funcptr func,
 
 }
 
+/*! \brief Entry point for level generation.
+ *
+ *  \todo Maybe implement support for Lua-based level generators.
+ */
 void build_level(void)
 {
     int theme_roll;
index 9059a47..2e522bf 100644 (file)
@@ -192,11 +192,11 @@ Action_cost eat_food(int obj)
         print_msg("Vile power suffuses your body as you devour the devil\nspleen.\n");
         if (zero_die(2))
         {
-            gain_body(1, 1);
+            gain_body(1);
         }
         else
         {
-            gain_agility(1, 1);
+            gain_agility(1);
         }
     }
     else if (u.food < 0)
@@ -220,10 +220,10 @@ Action_cost quaff_potion(int obj)
     switch (optr->obj_id)
     {
     case PO_BODY_POTION:
-        gain_body(1, 1);
+        gain_body(1);
         break;
     case PO_AGILITY_POTION:
-        gain_agility(1, 1);
+        gain_agility(1);
         break;
     case PO_HEALING_POTION:
         {
@@ -808,5 +808,89 @@ Action_cost zap_weapon(void)
     }
 }
 
-/* objects.c */
+/*! \brief Unwield the player's currently equipped weapon
+ *
+ *  \todo Stickycurse?
+ *  \todo Life-essential resistances?
+ */
+Action_cost player_unwield(Noisiness noisy)
+{
+    if (u.weapon == NO_OBJ)
+    {
+        print_msg("BUG: got to player_unwield() with no weapon wielded - have a quiet word with your HCI module's author, please.\n");
+        return Cost_none;
+    }
+    u.weapon = NO_OBJ;
+    if (noisy == Noise_std)
+    {
+        print_msg("Weapon unwielded.\n");
+    }
+    return Cost_std;
+}
+
+Action_cost player_wield(int slot, Noisiness noisy)
+{
+    if (u.weapon != NO_OBJ)
+    {
+        player_unwield(Noise_low);
+    }
+    u.weapon = u.inventory[slot];
+    print_msg("Wielding ");
+    print_obj_name(u.weapon);
+    print_msg(".\n");
+    recalc_defence();
+    return Cost_std;
+}
+
+Action_cost put_on_ring(int obj)
+{
+    if (u.ring != NO_OBJ)
+    {
+        print_msg("BUG: calling put_on_ring with ring already equipped.\n");
+        return Cost_none;
+    }
+    if (!objects[obj].with_you)
+    {
+        print_msg("BUG: attempting to put on uncarried ring.\n");
+        return Cost_none;
+    }
+    u.ring = obj;
+    recalc_defence();
+    return Cost_std;
+}
+
+Action_cost remove_ring(void)
+{
+    if (u.ring == NO_OBJ)
+    {
+        print_msg("BUG: calling remove_ring with no ring equipped.\n");
+        return Cost_none;
+    }
+    u.ring = NO_OBJ;
+    recalc_defence();
+    return Cost_std;
+}
+
+Pass_fail ring_removal_unsafe(Noisiness noisy)
+{
+    if ((lvl.terrain_at(u.pos) == LAVA) && (u.resistances[DT_FIRE] == RESIST_RING))
+    {
+        if (noisy != Noise_silent)
+        {
+            print_msg("That ring is your only current source of fire resistance. Removing it here would incinerate you.\n");
+        }
+        return You_fail;
+    }
+    else if ((objects[u.ring].obj_id == PO_FROST_RING) && (lvl.terrain_at(u.pos) == WATER))
+    {
+        if (noisy != Noise_silent)
+        {
+            print_msg("Since nobody ever taught you to swim, removing that ring here would result in your death by drowning.\n");
+        }
+        return You_fail;
+    }
+    return You_pass;
+}
+
+/* objects.cc */
 // vim:cindent
index 40c1506..63da4b0 100644 (file)
@@ -94,12 +94,21 @@ extern bool consume_obj(int obj);
 extern int create_obj_class(enum poclass_num pocl, int quantity, bool with_you, Coord c);
 
 extern Action_cost drop_obj(int inv_idx);
+extern Action_cost put_on_ring(int obj);
+extern Action_cost remove_ring(void);
+extern Action_cost wear_armour(int obj);
+extern Action_cost take_off_armour(void);
 extern Action_cost read_scroll(int obj);
 extern Action_cost quaff_potion(int obj);
 extern Action_cost eat_food(int obj);
 extern Action_cost magic_ring(void);
 extern Action_cost emanate_armour(void);
 extern Action_cost zap_weapon(void);
+extern Action_cost player_unwield(Noisiness noisy = Noise_std);
+extern Action_cost player_wield(int slot, Noisiness noisy = Noise_std);
+
+extern Pass_fail ring_removal_unsafe(Noisiness noisy = Noise_std);
+extern Pass_fail armour_removal_unsafe(Noisiness noisy = Noise_std);
 
 #endif
 
index 18c1b41..e25f62b 100644 (file)
@@ -339,7 +339,7 @@ int mon_use_sorcery(int mon)
     default:
         /* If this happens, we're trying to cast an unimplemented
          * spell. */
-        print_msg("Can't happen: Bogus spell %d!\n", to_cast);
+        debug_bad_monspell(to_cast);
         rval = -1;
         break;
 
@@ -367,24 +367,13 @@ int mon_use_sorcery(int mon)
 
     case MS_TELEPORT_AND_SUMMON:
         mptr->next_summon = game_tick + 1000;
-        /* Do the summoning... */
-        print_mon_name(mon, 3);
-        print_msg(" calls for help...\n");
         /* (Try to) summon 2-6 monsters. */
         i = summoning(mptr->pos, dice(2, 3));
-        if (i == 0)
-        {
-            print_msg("... luckily for you, help wasn't listening.\n");
-        }
-        else
-        {
-            print_msg("... and gets it.\n");
-        }
+        notify_summon_help(mon, (i > 0));
         /* ... and fall through. */
     case MS_TELEPORT_ESCAPE:
-        print_mon_name(mon, 3);
-        print_msg(" vanishes in a puff of smoke.\n");
         teleport_mon(mon);
+        notify_mon_disappear(mon);
         break;
 
     case MS_TELEPORT_ASSAULT:
@@ -394,68 +383,68 @@ int mon_use_sorcery(int mon)
         break;
 
     case MS_CURSE_ARMOURMELT:
-        mon_curses(mon);
+        notify_monster_cursing(mon);
         if (u.protection)
         {
-            malignant_aura();
+            notify_moncurse_fail();
         }
         else
         {
             u.armourmelt = 10 + one_die(10);
-            print_msg("Your armour seems suddenly no stronger than dust!\n");
+            recalc_defence();
+            notify_start_armourmelt();
         }
         break;
 
     case MS_CURSE_LEADFOOT:
-        mon_curses(mon);
+        notify_monster_cursing(mon);
         if (u.protection)
         {
-            malignant_aura();
+            notify_moncurse_fail();
         }
         else
         {
             u.leadfoot = 10 + one_die(10);
-            print_msg("Your feet feel like lead!\n");
+            recalc_defence();
+            notify_start_leadfoot();
         }
         break;
 
     case MS_CURSE_WITHERING:
-        mon_curses(mon);
+        notify_monster_cursing(mon);
         if (u.protection)
         {
-            malignant_aura();
+            notify_moncurse_fail();
         }
         else
         {
             u.withering = 10 + one_die(10);
-            print_msg("Your limbs twist and wither!\n");
+            recalc_defence();
+            notify_start_withering();
         }
         break;
 
     case MS_NECRO_SMITE:
-        mon_curses(mon);
+        notify_monster_cursing(mon);
         if (u.resists(DT_NECRO))
         {
-            print_msg("Darkness reaches towards you, but dissolves.\n");
+            notify_necrosmite_fail();
         }
         else
         {
-            print_msg("Soul-chilling darkness engulfs you!\n");
             damage_u(dice(1, 20), DEATH_KILLED_MON, permons[monsters[mon].mon_id].name);
         }
         break;
 
     case MS_FIRE_COLUMN:
-        mon_curses(mon);
-        print_msg("The fires of hell ");
+        notify_monster_cursing(mon);
+        notify_hellfire_hit(u.resists(DT_FIRE));
         if (u.resists(DT_FIRE))
         {
-            print_msg("lightly singe you.\n");
             damage_u(dice(1, 5), DEATH_KILLED_MON, permons[monsters[mon].mon_id].name);
         }
         else
         {
-            print_msg("burn you!\n");
             damage_u(dice(1, 20), DEATH_KILLED_MON, permons[monsters[mon].mon_id].name);
         }
         break;
@@ -463,16 +452,5 @@ int mon_use_sorcery(int mon)
     return rval;
 }
 
-void malignant_aura()
-{
-    print_msg("A malignant aura surrounds you briefly.\n");
-}
-
-void mon_curses(int mon)
-{
-    print_mon_name(mon, 3);
-    print_msg(" points at you and curses horribly.\n");
-}
-
-/* sorcery.c */
+/* sorcery.cc */
 // vim:cindent
diff --git a/u.cc b/u.cc
index 7e02d3d..ea8f2a5 100644 (file)
--- a/u.cc
+++ b/u.cc
@@ -89,7 +89,7 @@ Action_cost move_player(Offset delta)
     if ((c.y < 0) || (c.y >= DUN_HEIGHT) ||
         (c.x < 0) || (c.x >= DUN_WIDTH))
     {
-        print_msg("Attempted move out of bounds.\n");
+        debug_move_oob(c);
         return Cost_none;       /* No movement. */
     }
     if (lvl.mon_at(c) != NO_MON)
@@ -100,7 +100,7 @@ Action_cost move_player(Offset delta)
                 (objects[u.weapon].obj_id == PO_CROSSBOW) ||
                 (objects[u.weapon].obj_id == PO_THUNDERBOW))
             {
-                print_msg("You can't use that weapon in melee!\n");
+                notify_swing_bow();
                 return Cost_none;
             }
         }
@@ -114,7 +114,7 @@ Action_cost move_player(Offset delta)
     case IRON_WALL:
     case SKIN_WALL:
     case BONE_WALL:
-        print_msg("You cannot go there.\n");
+        notify_cant_go();
         return Cost_none;
     case FLOOR:
     case AMETHYST_FLOOR:
@@ -131,14 +131,14 @@ Action_cost move_player(Offset delta)
         {
             if (lvl.terrain_at(u.pos) != LAVA)
             {
-                print_msg("You walk on the lava.\n");
+                notify_start_lavawalk();
             }
             reloc_player(c);
             return Cost_std;
         }
         else
         {
-            print_msg("The fierce heat of the molten rock repels you.\n");
+            notify_blocked_lava();
             return Cost_none;
         }
     case WATER:
@@ -146,14 +146,14 @@ Action_cost move_player(Offset delta)
         {
             if (lvl.terrain_at(u.pos) != WATER)
             {
-                print_msg("You walk on the water.\n");
+                notify_start_waterwalk();
             }
             reloc_player(c);
             return Cost_std;
         }
         else
         {
-            print_msg("The idiot who raised you never taught you to swim.\n");
+            notify_blocked_water();
             return Cost_none;
         }
     }
@@ -171,17 +171,16 @@ void reloc_player(Coord c)
     display_update();
     if (lvl.obj_at(c) != NO_OBJ)
     {
-        print_msg("You see here ");
-        print_obj_name(lvl.obj_at(c));
-        print_msg(".\n");
+        notify_obj_at(c);
     }
 }
 
-int gain_body(int amount, int loud)
+int gain_body(int amount)
 {
     if (amount < 1)
     {
-        print_msg("Absurd body gain %d\n", amount);
+        debug_body_gain(amount);
+        return 0;
     }
     if (u.body < 99)
     {
@@ -191,19 +190,12 @@ int gain_body(int amount, int loud)
         }
         u.body += amount;
         status_updated = 1;
-        if (loud)
-        {
-            print_msg("You feel stronger!\n");
-        }
-        else
-        {
-            display_update();
-        }
+        notify_body_gain(amount);
         return amount;
     }
     else
     {
-        print_msg("You feel disappointed.\n");
+        notify_wasted_gain();
         return 0;
     }
 }
@@ -229,11 +221,12 @@ int drain_body(int amount, char const *what, int permanent)
     return 0;
 }
 
-int gain_agility(int amount, int loud)
+int gain_agility(int amount)
 {
     if (amount < 1)
     {
-        print_msg("Absurd agility gain %d\n", amount);
+        debug_agility_gain(amount);
+        return 0;
     }
     if (u.agility < 99)
     {
@@ -244,14 +237,7 @@ int gain_agility(int amount, int loud)
         u.agility += amount;
         status_updated = 1;
         recalc_defence();
-        if (loud)
-        {
-            print_msg("You feel more agile!\n");
-        }
-        else
-        {
-            display_update();
-        }
+        notify_agility_gain(amount);
         return amount;
     }
     else
@@ -528,18 +514,17 @@ void gain_experience(int amount)
     if (u.experience > lev_threshold(u.level))
     {
         u.level++;
-        print_msg("You gained a level!\n");
         if (!zero_die(2))
         {
-            bodygain = gain_body(2, 0);
-            agilgain = gain_agility(1, 0);
+            bodygain = gain_body(2);
+            agilgain = gain_agility(1);
         }
         else
         {
-            bodygain = gain_body(1, 0);
-            agilgain = gain_agility(2, 0);
+            bodygain = gain_body(1);
+            agilgain = gain_agility(2);
         }
-        print_msg("You gained %d body and %d agility.\n", bodygain, agilgain);
+        notify_level_gain();
         hpgain = u.body / 10 + 10;
         if (u.hpmax + hpgain > 999)
         {
@@ -551,8 +536,7 @@ void gain_experience(int amount)
              * heals you. */
             u.hpcur += hpgain;
             u.hpmax += hpgain;
-            status_updated = 1;
-            print_msg("You gained %d hit points.\n", hpgain);
+            notify_hp_gain(hpgain);
         }
     }
     else
@@ -571,12 +555,12 @@ Pass_fail teleport_u(void)
         c.x = exclusive_flat(0, DUN_WIDTH - 1);
         if ((lvl.mon_at(c) == NO_MON) && (lvl.terrain_at(c) == FLOOR) && (c != u.pos))
         {
-            print_msg("You are whisked away!\n");
             reloc_player(c);
+            notify_player_teleport();
             return You_pass;
         }
     }
-    print_msg("You feel briefly dislocated.\n");
+    notify_player_telefail();
     return You_fail;
 }
 
@@ -604,7 +588,7 @@ void update_player(void)
     {
         /* Heal player for 1d3 hit points; do not allow HP gain,
          * and don't say anything apart from the regen ring message. */
-        print_msg("Your ring pulses soothingly.\n");
+        notify_player_regen();
         heal_u(one_die(3), 0, 0);
     }
     if (u.food > MIN_FOOD)
@@ -628,25 +612,14 @@ void update_player(void)
         }
         u.food -= food_use;
         status_updated = 1;
-        switch (squeal)
-        {
-        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;
-        }
+        notify_hunger_level(squeal);
     }
     if (u.leadfoot > 0)
     {
         u.leadfoot--;
         if (!u.leadfoot)
         {
-            print_msg("You shed your feet of lead.\n");
+            notify_leadfoot_recovered();
             recalc_defence();
         }
     }
@@ -655,7 +628,7 @@ void update_player(void)
         u.armourmelt--;
         if (!u.armourmelt)
         {
-            print_msg("Your armour seems solid once more.\n");
+            notify_armourmelt_recovered();
             recalc_defence();
         }
     }
@@ -664,7 +637,7 @@ void update_player(void)
         u.withering--;
         if (!u.withering)
         {
-            print_msg("Your limbs straighten.\n");
+            notify_wither_recovered();
             recalc_defence();
         }
     }
@@ -673,7 +646,7 @@ void update_player(void)
         u.protection--;
         if (!u.protection)
         {
-            print_msg("You feel like you are no longer being helped.\n");
+            notify_protection_lost();
         }
     }
     display_update();
index b4c39e0..c861f10 100644 (file)
@@ -61,6 +61,14 @@ enum Action_cost
     Cost_std = 1
 };
 
+enum Noisiness
+{
+    Noise_silent,
+    Noise_low,
+    Noise_std
+};
+
+
 /* change WIZARD_MODE to 1 if you want the wizard mode commands. */
 #define WIZARD_MODE 0
 
@@ -95,14 +103,12 @@ enum Damtyp {
 
 /* XXX enum Game_cmd - player actions. */
 enum Game_cmd {
-    DROP_ITEM, SAVE_GAME,
-    MOVE_WEST, MOVE_SOUTH, MOVE_NORTH, MOVE_EAST,
-    MOVE_NW, MOVE_NE, MOVE_SW, MOVE_SE,
+    WALK, STAND_STILL, GO_DOWN_STAIRS, ATTACK,
+    GET_ITEM, DROP_ITEM,
     WIELD_WEAPON, WEAR_ARMOUR, TAKE_OFF_ARMOUR, PUT_ON_RING, REMOVE_RING,
-    QUAFF_POTION, READ_SCROLL, THROW_FLASK,
-    EMANATE_ARMOUR, ZAP_WEAPON, MAGIC_RING, ATTACK,
-    GET_ITEM, QUIT, GO_DOWN_STAIRS, STAND_STILL, EAT_FOOD, DUMP_CHARA,
-    GIVE_HELP, INSPECT_ITEM, EXAMINE_MONSTER, RNG_TEST, SHOW_TERRAIN,
+    QUAFF_POTION, READ_SCROLL, THROW_FLASK, EAT_FOOD,
+    EMANATE_ARMOUR, ZAP_WEAPON, MAGIC_RING,
+    SAVE_GAME, QUIT,
     WIZARD_LEVELUP, WIZARD_DESCEND
 };
 
@@ -188,7 +194,7 @@ extern int inclusive_flat(int lower, int upper); /* l ... u */
 extern Offset random_step(void);
 extern Coord inclusive_boxed(Coord topleft, Coord botright);
 extern Coord exclusive_boxed(Coord topleft, Coord botright);
-extern Action_cost do_command(Game_cmd command);
+extern Action_cost do_player_action(Action *act);
 extern int game_finished;
 extern int game_tick;
 extern int wizard_mode;
@@ -214,8 +220,8 @@ extern void write_char_dump(void);
 extern int do_death(Death d, char const *what);
 extern void heal_u(int amount, int boost, int loud);
 extern int damage_u(int amount, Death d, char const *what);
-extern int gain_body(int amount, int loud);
-extern int gain_agility(int amount, int loud);
+extern int gain_body(int amount);
+extern int gain_agility(int amount);
 extern int drain_body(int amount, char const *what, int permanent);
 extern int drain_agility(int amount, char const *what, int permanent);
 extern void gain_experience(int amount);