From 258e57c1621533e206610453047d829cc8aa13fe Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Sat, 6 Dec 2014 06:34:32 +0100
Subject: [PATCH] Add "look" mode to query things on any cell via new
 THINGS_HERE command.

---
 SERVER_COMMANDS                               |   7 +-
 confclient/commands                           |   3 +
 confclient/interface_conf                     |   3 +-
 confclient/single_wins/info                   |   2 +-
 confclient/single_wins/inventory              |   2 +-
 confclient/single_wins/log                    |   2 +-
 confclient/single_wins/map                    |   2 +-
 .../single_wins/{standing_on => things_here}  |   2 +-
 src/client/cleanup.c                          |   2 +-
 src/client/control.c                          |  11 +-
 src/client/draw_wins.c                        |  55 ++++++----
 src/client/io.c                               |  28 +++--
 src/client/io.h                               |   8 +-
 src/client/main.c                             |   2 +-
 src/client/map.c                              | 100 +++++++++++++++++-
 src/client/map.h                              |  10 ++
 src/client/world.h                            |   6 +-
 src/server/run.c                              |  32 +++---
 18 files changed, 203 insertions(+), 74 deletions(-)
 rename confclient/single_wins/{standing_on => things_here} (98%)

diff --git a/SERVER_COMMANDS b/SERVER_COMMANDS
index b9e3cd2..dba4b95 100644
--- a/SERVER_COMMANDS
+++ b/SERVER_COMMANDS
@@ -53,9 +53,10 @@ Write "PONG" line to ./server/out file.
 QUIT
 Shut down server.
 
-STACK
-Write line-by-line list of items the player stands on into ./server/out file,
-enclosed by two lines "THINGS_BELOW_PLAYER START" and "THINGS_BELOW_PLAYER END".
+THINGS_HERE [0 to 255] [0 to 255]
+Write line-by-line list of items at y position of first argument, x position of
+second argument into ./server/out file, enclosed by two lines "THINGS_HERE START"
+and "THINGS_HERE END".
 
 Player commands
 ---------------
diff --git a/confclient/commands b/confclient/commands
index b8b9387..e64bdf5 100644
--- a/confclient/commands
+++ b/confclient/commands
@@ -187,3 +187,6 @@ DESCRIPTION 'window keybinding keys'
 
 COMMAND to_autofocus
 DESCRIPTION 'toggle auto-center'
+
+COMMAND to_look
+DESCRIPTION 'toggle look mode'
diff --git a/confclient/interface_conf b/confclient/interface_conf
index d2b02e6..2cc3648 100644
--- a/confclient/interface_conf
+++ b/confclient/interface_conf
@@ -74,7 +74,7 @@ KEY 258 inv_d
 KEY 117 use
 
 WINDOW s
-NAME 'Standing on'
+NAME 'Things here'
 BREAK 1
 WIDTH 14
 HEIGHT -12
@@ -111,3 +111,4 @@ KEY 260 map_l
 KEY 261 map_r
 KEY 46 map_c
 KEY 70 to_autofocus
+KEY 108 to_look
diff --git a/confclient/single_wins/info b/confclient/single_wins/info
index 4999883..78a63f9 100644
--- a/confclient/single_wins/info
+++ b/confclient/single_wins/info
@@ -92,7 +92,7 @@ KEY 258 inv_d
 KEY 117 use
 
 WINDOW s
-NAME 'Standing on'
+NAME 'Things here'
 BREAK 1
 WIDTH 14
 HEIGHT -12
diff --git a/confclient/single_wins/inventory b/confclient/single_wins/inventory
index 067b27d..b831f50 100644
--- a/confclient/single_wins/inventory
+++ b/confclient/single_wins/inventory
@@ -84,7 +84,7 @@ KEY 259 keyb_uk
 KEY 10 keyb_mk
 
 WINDOW s
-NAME 'Standing on'
+NAME 'Things here'
 BREAK 1
 WIDTH 14
 HEIGHT -12
diff --git a/confclient/single_wins/log b/confclient/single_wins/log
index 81eea84..40b9be4 100644
--- a/confclient/single_wins/log
+++ b/confclient/single_wins/log
@@ -92,7 +92,7 @@ KEY 258 inv_d
 KEY 117 use
 
 WINDOW s
-NAME 'Standing on'
+NAME 'Things here'
 BREAK 1
 WIDTH 14
 HEIGHT -12
diff --git a/confclient/single_wins/map b/confclient/single_wins/map
index d9d6e38..56e7f34 100644
--- a/confclient/single_wins/map
+++ b/confclient/single_wins/map
@@ -96,7 +96,7 @@ KEY 258 inv_d
 KEY 117 use
 
 WINDOW s
-NAME 'Standing on'
+NAME 'Things here'
 BREAK 1
 WIDTH 14
 HEIGHT -12
diff --git a/confclient/single_wins/standing_on b/confclient/single_wins/things_here
similarity index 98%
rename from confclient/single_wins/standing_on
rename to confclient/single_wins/things_here
index 59ee7b4..ea87f9f 100644
--- a/confclient/single_wins/standing_on
+++ b/confclient/single_wins/things_here
@@ -87,7 +87,7 @@ KEY 258 inv_d
 KEY 117 use
 
 WINDOW s
-NAME 'Standing on'
+NAME 'Things here'
 BREAK 1
 WIDTH 0
 HEIGHT 0
diff --git a/src/client/cleanup.c b/src/client/cleanup.c
index d889ae9..4db02a2 100644
--- a/src/client/cleanup.c
+++ b/src/client/cleanup.c
@@ -26,7 +26,7 @@ extern void cleanup()
     free(world.map.cells);
     free(world.mem_map);
     free(world.log);
-    free(world.things_below_player);
+    free(world.things_here);
     free(world.queue);
     free(world.player_inventory);
     if (cleanup_flags & CLEANUP_INTERFACE)
diff --git a/src/client/control.c b/src/client/control.c
index 5d90919..76d69ac 100644
--- a/src/client/control.c
+++ b/src/client/control.c
@@ -17,7 +17,9 @@
 #include "keybindings.h" /* get_command_to_keycode(), get_keycode_to_command(),
                           * mod_selected_keyb(), move_keyb_selection()
                           */
-#include "map.h" /* map_scroll(), map_center(), toggle_autofocus() */
+#include "map.h" /* map_scroll(), map_center(), toggle_autofocus(),
+                  * toggle_lookmode(), lookmode_nav()
+                  */
 #include "wincontrol.h" /* shift_active_win(), resize_active_win(),
                          * toggle_win_size_type(), toggle_window(),
                          * cycle_active_win(), scroll_v_screen(),
@@ -166,6 +168,7 @@ static uint8_t try_client_commands(struct Command * command)
             || try_1args(command, "shift_b", shift_active_win, 'b')
             || try_0args(command, "reload_conf", reload_interface_conf)
             || try_0args(command, "save_conf", save_interface_conf)
+            || try_0args(command, "to_look", toggle_lookmode)
             || try_kb_manip(command->dsc_short));
 }
 
@@ -263,7 +266,7 @@ extern uint8_t try_key(uint16_t key)
     }
     if (command)
     {
-        if      (try_server_commands(command))
+        if (world.look && lookmode_nav(command->dsc_short))
         {
             return 1;
         }
@@ -271,6 +274,10 @@ extern uint8_t try_key(uint16_t key)
         {
             return 1;
         }
+        else if (try_server_commands(command))
+        {
+            return 1;
+        }
         else if (!strcmp("quit", command->dsc_short))
         {
             return 2;
diff --git a/src/client/draw_wins.c b/src/client/draw_wins.c
index 63fb8a0..594b707 100644
--- a/src/client/draw_wins.c
+++ b/src/client/draw_wins.c
@@ -65,6 +65,9 @@ static char * get_keyname_to_command(char * command_name);
 static char * winconf_geom_helper(struct Win * win, char axis, char * sep,
                                   char * newlines, char * value_prefix);
 
+/* Draw map cell "c" into win "w" at position "y"/"x" with attribute "a". */
+static void draw_mapcell(struct Win * w, char c, uint8_t y, uint8_t x,attr_t a);
+
 
 
 static void try_resize_winmap(struct Win * win, int new_size_y, int new_size_x)
@@ -336,6 +339,17 @@ static char * winconf_geom_helper(struct Win * win, char axis, char * sep,
 
 
 
+static void draw_mapcell(struct Win * w, char c, uint8_t y, uint8_t x, attr_t a)
+{
+    set_ch_on_yx(w, y, x * 2 + (y % 2), c | a);
+    if (x + (y % 2) < world.map.length)
+    {
+        set_ch_on_yx(w, y, x * 2 + (y % 2) + 1, ' ' | a);
+    }
+}
+
+
+
 extern void draw_win_log(struct Win * win)
 {
     if (!world.log)
@@ -349,43 +363,40 @@ extern void draw_win_log(struct Win * win)
 
 extern void draw_win_map(struct Win * win)
 {
+    uint16_t x, y;
     init_pair(1, COLOR_WHITE, COLOR_BLUE);
     init_pair(2, COLOR_BLUE, COLOR_BLACK);
-    attr_t attr_fov = 0;
     attr_t attr_mem = COLOR_PAIR(2);
     attr_t attr_sha = COLOR_PAIR(1);
     try_resize_winmap(win, world.map.length, world.map.length * 2);
-    uint16_t x, y, z;
-    for (y = 0, z = 0; y < world.map.length; y++)
+    for (y = 0; y < world.map.length; y++)
     {
         for (x = 0; x < world.map.length; x++)
         {
-            attr_t attr_c = ' ' == world.mem_map[z] ? attr_sha : attr_mem;
-            chtype c = world.mem_map[z] | attr_c;
-            set_ch_on_yx(win, y, x * 2 + (y % 2), c);
-            if (x + (y % 2) < world.map.length)
-            {
-                set_ch_on_yx(win, y, x * 2 + (y % 2) + 1, ' ' | attr_c);
-            }
-            z++;
+            attr_t a=' '==world.mem_map[y*world.map.length+x]?attr_sha:attr_mem;
+            char c = world.mem_map[y*world.map.length + x];
+            draw_mapcell(win, c, y, x, a);
         }
     }
-    for (y = 0, z = 0; y < world.map.length; y++)
+    for (y = 0; y < world.map.length; y++)
     {
         for (x = 0; x < world.map.length; x++)
         {
-            if (' ' != world.map.cells[z])
+            if (' ' != world.map.cells[y*world.map.length + x])
             {
-                chtype c = world.map.cells[z] | attr_fov;
-                set_ch_on_yx(win, y, x * 2 + (y % 2), c);
-                if (x + (y % 2) < world.map.length)
-                {
-                    set_ch_on_yx(win, y, x * 2 + (y % 2) + 1, ' ' | attr_fov);
-                }
+                char c = world.map.cells[y*world.map.length + x];
+                draw_mapcell(win, c, y, x, 0);
             }
-            z++;
         }
     }
+    if (world.look)
+    {
+        y = world.look_pos.y;
+        x = world.look_pos.x;
+        char c = world.map.cells[y * world.map.length + x];
+        c = ' ' == c ? world.mem_map[y * world.map.length + x] : c;
+        draw_mapcell(win, c, y, x, A_REVERSE);
+    }
 }
 
 
@@ -436,9 +447,9 @@ extern void draw_win_inventory(struct Win * win)
 
 extern void draw_win_terrain_stack(struct Win * win)
 {
-    if (world.things_below_player)
+    if (world.things_here)
     {
-        add_text_with_linebreaks(win, world.things_below_player);
+        add_text_with_linebreaks(win, world.things_here);
     }
 }
 
diff --git a/src/client/io.c b/src/client/io.c
index a46f91f..45a1de7 100644
--- a/src/client/io.c
+++ b/src/client/io.c
@@ -259,31 +259,31 @@ static void nl_append_string(char * append, char ** string)
 
 static uint8_t read_queue()
 {
-    static uint8_t things_below_player_parsing = 0;
+    static uint8_t things_here_parsing = 0;
     uint8_t ret = 0;
     char * msg;
     while (NULL != (msg = get_message_from_queue(&world.queue)))
     {
         char * log_prefix = "LOG ";
-        if      (!strcmp(msg, "THINGS_BELOW_PLAYER START"))
+        if      (!strcmp(msg, "THINGS_HERE START"))
         {
             ret = 1;
-            things_below_player_parsing = 1;
-            free(world.things_below_player);
-            world.things_below_player = NULL;
+            things_here_parsing = 1;
+            free(world.things_here);
+            world.things_here = NULL;
         }
-        else if (!strcmp(msg, "THINGS_BELOW_PLAYER END"))
+        else if (!strcmp(msg, "THINGS_HERE END"))
         {
-            things_below_player_parsing = 0;
-            if (!world.things_below_player)
+            things_here_parsing = 0;
+            if (!world.things_here)
             {
-                nl_append_string("(nothing)", &world.things_below_player);
+                nl_append_string("(none known)", &world.things_here);
             }
         }
-        else if (things_below_player_parsing)
+        else if (things_here_parsing)
         {
             ret = 1;
-            nl_append_string(msg, &world.things_below_player);
+            nl_append_string(msg, &world.things_here);
         }
         else if (!strncmp(msg, log_prefix, strlen(log_prefix)))
         {
@@ -298,9 +298,7 @@ static uint8_t read_queue()
         }
         else if (!strcmp(msg, "WORLD_UPDATED"))
         {
-            free(world.things_below_player);
-            world.things_below_player = NULL;
-            send("STACK");
+            query_mapcell();
         }
         free(msg);
     }
@@ -338,7 +336,7 @@ extern char * io_loop()
         }
         if (change_in_client || read_worldstate() || read_queue())
         {
-            if (world.turn != last_focused_turn && world.focus_each_turn)
+            if (world.turn != last_focused_turn && world.autofocus)
             {
                 last_focused_turn = world.turn;
                 map_center();
diff --git a/src/client/io.h b/src/client/io.h
index 36d04c6..041ed67 100644
--- a/src/client/io.h
+++ b/src/client/io.h
@@ -30,10 +30,10 @@ extern void send(char * msg);
  * appropriate quit message to write to stdout when the client winds down. Call
  * reset_windows() on receiving a SIGWINCH. Abort on assumed server death if the
  * server's out file does not get updated, even on PING requests. Re-focus map
- * view on player if world.focus_each_turn is set. Messages from the out file
- * are put together on the queue first, from which only complete (\n-delimited)
- * messages are read. Queues of messages are worked through completely / emptied
- * before any re-drawing or further server polling happens.
+ * view on player if world.autofocus is set. Messages from the out file are put
+ * together on the queue first, from which only complete (\n-delimited) messages
+ * are read. Queues of messages are worked through completely / emptied before
+ * any re-drawing or further server polling happens.
  */
 extern char * io_loop();
 
diff --git a/src/client/main.c b/src/client/main.c
index 2c1fbf4..acb592b 100644
--- a/src/client/main.c
+++ b/src/client/main.c
@@ -56,7 +56,7 @@ int main(int argc, char * argv[])
     keypad(world.winDB.t_screen, TRUE);
     init_command_db();      /* The command DB needs to be initialized before  */
     load_interface_conf();  /* the interface, whose keybindings depend on it. */
-    world.focus_each_turn = 1;
+    world.autofocus = 1;
 
     /* Set handler for terminal window resizing. */
     struct sigaction act;
diff --git a/src/client/map.c b/src/client/map.c
index e08e7b3..75dd6de 100644
--- a/src/client/map.c
+++ b/src/client/map.c
@@ -7,6 +7,13 @@
 
 #include "map.h"
 #include <stdint.h> /* uint8_t */
+#include <stdlib.h> /* free() */
+#include <stdio.h> /* sprintf() */
+#include <string.h> /* strlen(), strncmp() */
+#include "../common/try_malloc.h" /* try_malloc() */
+#include "../common/rexit.h" /* exit_trouble() */
+#include "../common/yx_uint8.h" /* yx_uint8 */
+#include "io.h" /* send() */
 #include "windows.h" /* struct Win, center_offset(), get_win_by_id() */
 #include "world.h" /* for global world */
 
@@ -14,7 +21,7 @@
 
 extern void map_scroll(char d)
 {
-    world.focus_each_turn = 0;
+    world.autofocus = 0;
     struct Win * win = get_win_by_id('m');
     uint16_t offset;
     if (('8' == d || '2' == d) && world.map.length > win->frame_size.y)
@@ -48,13 +55,98 @@ extern void map_scroll(char d)
 extern void map_center()
 {
     struct Win * win_map = get_win_by_id('m');
-    win_map->center.y = world.player_pos.y;
-    win_map->center.x = world.player_pos.x * 2 + (world.player_pos.y % 2);
+    struct yx_uint8 pos = world.look ? world.look_pos : world.player_pos;
+    win_map->center.y = pos.y;
+    win_map->center.x = pos.x * 2 + (pos.y % 2);
 }
 
 
 
 extern void toggle_autofocus()
 {
-    world.focus_each_turn = world.focus_each_turn ? 0 : 1;
+    world.autofocus = world.autofocus ? 0 : 1;
+}
+
+
+
+extern void toggle_lookmode()
+{
+    if (!world.look)
+    {
+        world.look_pos = world.player_pos;
+        world.look = 1;
+    }
+    else
+    {
+        world.look = 0;
+    }
+    query_mapcell();
+}
+
+
+
+extern uint8_t lookmode_nav(char * command)
+{
+    char * prefix = "move_";
+    uint8_t len_pref = strlen(prefix);
+    if (!strncmp(prefix, command, len_pref) && strlen(command) - 1 == len_pref)
+    {
+        uint8_t open_north = world.look_pos.y > 0;
+        uint8_t open_south = world.look_pos.y < world.map.length - 1;
+        uint8_t open_west  = world.look_pos.x > 0;
+        uint8_t open_east  = world.look_pos.x < world.map.length - 1;
+        uint8_t indent     = world.look_pos.y % 2;
+        if      ('s' == command[len_pref])
+        {
+            world.look_pos.x = world.look_pos.x - open_west;
+        }
+        else if ('d' == command[len_pref])
+        {
+            world.look_pos.x = world.look_pos.x + open_east;
+        }
+        else if ('w' == command[len_pref])
+        {
+            world.look_pos.y = world.look_pos.y - open_north;
+            world.look_pos.x = world.look_pos.x - !indent * open_west;
+        }
+        else if ('e' == command[len_pref])
+        {
+            world.look_pos.y = world.look_pos.y - open_north;
+            world.look_pos.x = world.look_pos.x + indent * open_east;
+        }
+        else if ('x' == command[len_pref])
+        {
+            world.look_pos.y = world.look_pos.y + open_south;
+            world.look_pos.x = world.look_pos.x - !indent * open_west;
+        }
+        else if ('c' == command[len_pref])
+        {
+            world.look_pos.y = world.look_pos.y + open_south;
+            world.look_pos.x = world.look_pos.x + indent * open_east;
+        }
+        else
+        {
+            return 0;
+        }
+        map_center();
+        query_mapcell();
+        return 1;
+    }
+    return 0;
+}
+
+
+
+extern void query_mapcell()
+{
+    free(world.things_here);
+    world.things_here = NULL;
+    char * stack = "THINGS_HERE";
+    char * stack_query = try_malloc(strlen(stack) +1+3 +1+3 +1, __func__);
+    uint8_t y = world.look ? world.look_pos.y : world.player_pos.y;
+    uint8_t x = world.look ? world.look_pos.x : world.player_pos.x;
+    int test = sprintf(stack_query, "%s %d %d", stack, y, x);
+    exit_trouble(test < 0, __func__, "sprintf");
+    send(stack_query);
+    free(stack_query);
 }
diff --git a/src/client/map.h b/src/client/map.h
index bd919d8..4b1eb8d 100644
--- a/src/client/map.h
+++ b/src/client/map.h
@@ -10,6 +10,7 @@
 #ifndef MAP_H_CLIENT
 #define MAP_H_CLIENT
 
+#include <stdint.h> /* uint8_t */
 
 
 /* Try changing map window's focus into direction "d" (north = "N" etc.). Unset
@@ -23,6 +24,15 @@ extern void map_center();
 /* Toggle world.focus_each_turn (auto-centering of map on player each turn). */
 extern void toggle_autofocus();
 
+/* Toggle world.look (moving look cursor instead of player over map). */
+extern void toggle_lookmode();
+
+/* Read "command" as look cursor move command, act on it.*/
+extern uint8_t lookmode_nav(char * command);
+
+/* Send THINGS_HERE query message to server.*/
+extern void query_mapcell();
+
 
 
 #endif
diff --git a/src/client/world.h b/src/client/world.h
index e907922..19497d2 100644
--- a/src/client/world.h
+++ b/src/client/world.h
@@ -33,19 +33,21 @@ struct World
     struct Map map; /* game map geometry and content of player's map view */
     time_t last_update; /* used for comparison with worldstate file's mtime */
     char * log; /* log of player's activities */
-    char * things_below_player; /* list of things below the player */
+    char * things_here; /* list of things below the player */
     char * path_interface; /* path of interface configuration file */
     char * path_commands; /* path of commands config file */
     char * player_inventory; /* one-item-per-line string list of owned items */
     char * mem_map; /* map cells of player's map memory */
     char * queue; /* stores un-processed messages read from the input file */
     struct yx_uint8 player_pos; /* coordinates of player on map */
+    struct yx_uint8 look_pos; /* coordinates of look cursor */
     uint16_t turn; /* world/game turn */
     uint8_t halfdelay; /* how long to wait for getch() input in io_loop() */
     uint8_t player_inventory_select; /* index of selected item in inventory */
     uint8_t player_lifepoints; /* how alive the player is */
     uint8_t winch; /* if set, SIGWINCH was registered; trigger reset_windows()*/
-    uint8_t focus_each_turn; /* if !0, re-focus map on player each new turn */
+    uint8_t autofocus; /* if !0, re-focus map each new turn / look focus move */
+    uint8_t look; /* if set, move look cursor over map intead of player */
 };
 
 
diff --git a/src/server/run.c b/src/server/run.c
index a1bf343..339a849 100644
--- a/src/server/run.c
+++ b/src/server/run.c
@@ -10,12 +10,13 @@
 #include <stddef.h> /* NULL */
 #include <stdint.h> /* uint8_t, uint16_t, uint32_t, int16_t */
 #include <stdio.h> /* FILE, printf(), fflush() */
-#include <stdlib.h> /* free() */
+#include <stdlib.h> /* atoi(), free() */
 #include <string.h> /* strlen(), strcmp(), strncmp(), strdup() */
 #include <time.h> /* time_t, time() */
 #include <unistd.h> /* access() */
 #include "../common/parse_file.h" /* set_err_line_options(), token_from_line(),
-                                   * err_line(), err_line_inc(), parse_val()
+                                   * err_line(), err_line_inc(), parse_val(),
+                                   * parestest_int()
                                    */
 #include "../common/readwrite.h" /* try_fopen(), try_fcose(), try_fwrite(),
                                   * try_fgets(), textfile_width(), try_fputc(),
@@ -207,23 +208,26 @@ static uint8_t parse_command_meta(char * tok0)
         send_to_outfile("PONG\n", 1);
         return 1;
     }
-    if (!strcmp("STACK", tok0))
+    if (!strcmp("THINGS_HERE", tok0))
     {
-        send_to_outfile("THINGS_BELOW_PLAYER START\n", 1);
-        struct Thing * player = get_player();
-        struct Thing * t;
-        for (t = world.things; t; t = t->next)
+        char * tok1 = token_from_line(NULL);
+        char * tok2 = token_from_line(NULL);
+        if (!parsetest_int(tok1, '8') && !parsetest_int(tok2, '8'))
         {
-            if (   t->pos.y == player->pos.y && t->pos.x == player->pos.x
-                && t != player)
+            send_to_outfile("THINGS_HERE START\n", 1);
+            struct Thing * t;
+            for (t = world.things; t; t = t->next)
             {
-                struct ThingType * tt = get_thing_type(t->type);
-                send_to_outfile(tt->name, 0);
-                send_to_outfile("\n", 1);
+                if (t->pos.y == atoi(tok1) && t->pos.x == atoi(tok2))
+                {
+                    struct ThingType * tt = get_thing_type(t->type);
+                    send_to_outfile(tt->name, 0);
+                    send_to_outfile("\n", 1);
+                }
             }
+            send_to_outfile("THINGS_HERE END\n", 1);
+            return 1;
         }
-        send_to_outfile("THINGS_BELOW_PLAYER END\n", 1);
-        return 1;
     }
     return 0;
 }
-- 
2.30.2