1 /**
2  * @file
3  * @brief Webtiles implementation of the tiles interface
4 **/
5 
6 #pragma once
7 
8 #ifdef USE_TILE_WEB
9 
10 #include <bitset>
11 #include <map>
12 #include <vector>
13 
14 #include <sys/un.h>
15 
16 #include "cursor-type.h"
17 #include "equipment-type.h"
18 #include "map-cell.h"
19 #include "map-knowledge.h"
20 #include "status.h"
21 #include "text-tag-type.h"
22 #include "tiledoll.h"
23 #include "tilemcache.h"
24 #include "tileweb-text.h"
25 #include "viewgeom.h"
26 
27 using std::vector;
28 
29 class Menu;
30 
31 enum WebtilesUIState
32 {
33     UI_INIT = -1,
34     UI_NORMAL,
35     UI_CRT,
36     UI_VIEW_MAP,
37 };
38 
39 struct player_info
40 {
41     player_info();
42     bool _state_ever_synced;
43 
44     string name;
45     string job_title;
46     bool wizard;
47     string species;
48     string god;
49     bool under_penance;
50     uint8_t piety_rank;
51 
52     uint8_t form;
53 
54     int hp, hp_max, real_hp_max, poison_survival;
55     int mp, mp_max, dd_real_mp_max;
56     int contam;
57     int noise;
58     int adjusted_noise;
59 
60     int armour_class;
61     int evasion;
62     int shield_class;
63 
64     int8_t strength, strength_max;
65     int8_t intel, intel_max;
66     int8_t dex, dex_max;
67 
68     int experience_level;
69     int8_t exp_progress;
70     int gold;
71     int zot_points;
72     int elapsed_time;
73     int num_turns;
74     int lives, deaths;
75 
76     string place;
77     int depth;
78     coord_def position;
79 
80     vector<status_info> status;
81 
82     FixedVector<item_def, ENDOFPACK> inv;
83     FixedVector<int8_t, NUM_EQUIP> equip;
84     int8_t quiver_item;
85     int8_t launcher_item;
86     string quiver_desc;
87     string unarmed_attack;
88     uint8_t unarmed_attack_colour;
89     bool quiver_available;
90 };
91 
92 class TilesFramework
93 {
94 public:
95     TilesFramework();
96     virtual ~TilesFramework();
97 
98     bool initialise();
99     void shutdown();
100     void load_dungeon(const crawl_view_buffer &vbuf, const coord_def &gc);
101     void load_dungeon(const coord_def &gc);
102     int getch_ck();
103     void resize();
104     void clrscr();
105     void layout_reset();
106 
107     void cgotoxy(int x, int y, GotoRegion region = GOTO_CRT);
108 
109     void update_minimap(const coord_def &gc);
110     void clear_minimap();
111     void update_minimap_bounds();
112     void update_tabs();
113 
114     void mark_for_redraw(const coord_def& gc);
115     void set_need_redraw(unsigned int min_tick_delay = 0);
116     bool need_redraw() const;
117     void redraw();
118 
119     void place_cursor(cursor_type type, const coord_def &gc);
120     void clear_text_tags(text_tag_type type);
121     void add_text_tag(text_tag_type type, const string &tag,
122                       const coord_def &gc);
123     void add_text_tag(text_tag_type type, const monster_info& mon);
124 
125     const coord_def &get_cursor() const;
126 
127     void draw_doll_edit();
128 
129     // Webtiles-specific
130     void textcolour(int col);
131     void textbackground(int col);
132     void put_ucs_string(char32_t *str);
133     void clear_to_end_of_line();
134 
135     void push_menu(Menu* m);
136     void push_crt_menu(string tag);
137     bool is_in_crt_menu();
138     bool is_in_menu(Menu* m);
139     void pop_menu();
140     void push_ui_layout(const string& type, unsigned num_state_slots);
141     void pop_ui_layout();
142     void pop_all_ui_layouts();
143     void ui_state_change(const string& type, unsigned state_slot);
144     void push_ui_cutoff();
145     void pop_ui_cutoff();
146 
147     void send_exit_reason(const string& type, const string& message = "");
148     void send_dump_info(const string& type, const string& filename);
149 
150     string get_message();
151     void write_message(PRINTF(1, ));
152     void finish_message();
153     void send_message(PRINTF(1, ));
154     void flush_messages();
155 
has_receivers()156     bool has_receivers() { return !m_dest_addrs.empty(); }
is_controlled_from_web()157     bool is_controlled_from_web() { return m_controlled_from_web; }
158 
159     /* Webtiles can receive input both via stdin, and on the
160        socket. Also, while waiting for input, it should be
161        able to handle other control messages (for example,
162        requests to re-send data when a new spectator joins).
163 
164        This function waits until input is available either via
165        stdin or from a control message. If the input came from
166        a control message, it will be written into c; otherwise,
167        it still has to be read from stdin.
168 
169        If block is false, await_input will immediately return,
170        even if no input is available. The return value indicates
171        whether input can be read from stdin; c will be non-zero
172        if input came via a control message.
173      */
174     bool await_input(wint_t& c, bool block);
175 
176     void check_for_control_messages();
177 
178     // Helper functions for writing JSON
179     void write_message_escaped(const string& s);
180     void json_open_object(const string& name = "");
181     void json_close_object(bool erase_if_empty = false);
182     void json_open_array(const string& name = "");
183     void json_close_array(bool erase_if_empty = false);
184     void json_write_comma();
185     void json_write_name(const string& name);
186     void json_write_int(int value);
187     void json_write_int(const string& name, int value);
188     void json_write_bool(bool value);
189     void json_write_bool(const string& name, bool value);
190     void json_write_null();
191     void json_write_null(const string& name);
192     void json_write_string(const string& value);
193     void json_write_string(const string& name, const string& value);
194     /* Causes the current object/array to be erased if it is closed
195        with erase_if_empty without writing any other content after
196        this call */
197     void json_treat_as_empty();
198     void json_treat_as_nonempty();
199     bool json_is_empty();
200 
201     string m_sock_name;
202     bool m_await_connection;
203 
204     void set_text_cursor(bool enabled);
205     void set_ui_state(WebtilesUIState state);
get_ui_state()206     WebtilesUIState get_ui_state() { return m_ui_state; }
207 
208     void dump();
209     void update_input_mode(mouse_mode mode, bool force=false);
210 
211     void send_mcache(mcache_entry *entry, bool submerged,
212                      bool send = true);
213     void write_tileidx(tileidx_t t);
214 
215     void zoom_dungeon(bool in);
216 
217     void send_doll(const dolls_data &doll, bool submerged, bool ghost);
218 
219 protected:
220     int m_sock;
221     int m_max_msg_size;
222     string m_msg_buf;
223     vector<sockaddr_un> m_dest_addrs;
224 
225     bool m_controlled_from_web;
226     bool m_need_flush;
227 
228     bool _send_lock; // not thread safe
229 
230     void _await_connection();
231     wint_t _handle_control_message(sockaddr_un addr, string data);
232     wint_t _receive_control_message();
233 
234     struct JsonFrame
235     {
236         int start;
237         int prefix_end;
238         char type; // '}' or ']'
239     };
240     vector<JsonFrame> m_json_stack;
241 
242     void json_open(const string& name, char opener, char type);
243     void json_close(bool erase_if_empty, char type);
244 
245     struct UIStackFrame
246     {
247         enum { MENU, CRT, UI, } type;
248         Menu* menu;
249         string crt_tag;
250         vector<string> ui_json;
251         bool centred;
252     };
253     vector<UIStackFrame> m_menu_stack;
254 
255     WebtilesUIState m_ui_state;
256     WebtilesUIState m_last_ui_state;
257     vector<int> m_ui_cutoff_stack;
258 
259     unsigned int m_last_tick_redraw;
260     bool m_need_redraw;
261     bool m_layout_reset;
262 
263     coord_def m_origin;
264 
265     bool m_view_loaded;
266     bool m_player_on_level;
267 
268     crawl_view_buffer m_current_view;
269     coord_def m_current_gc;
270 
271     crawl_view_buffer m_next_view;
272     coord_def m_next_gc;
273     coord_def m_next_view_tl;
274     coord_def m_next_view_br;
275 
276     bitset<GXM * GYM> m_dirty_cells;
277     bitset<GXM * GYM> m_cells_needing_redraw;
278     void mark_dirty(const coord_def& gc);
279     void mark_clean(const coord_def& gc);
280     bool is_dirty(const coord_def& gc);
281     bool cell_needs_redraw(const coord_def& gc);
282 
283     int m_current_flash_colour;
284     int m_next_flash_colour;
285 
286     FixedArray<map_cell, GXM, GYM> m_current_map_knowledge;
287     map<uint32_t, coord_def> m_monster_locs;
288     bool m_need_full_map;
289 
290     coord_def m_cursor[CURSOR_MAX];
291     coord_def m_last_clicked_grid;
292     bool m_text_cursor;
293     bool m_last_text_cursor;
294 
295     bool m_has_overlays;
296 
297     WebTextArea m_text_menu;
298 
299     GotoRegion m_cursor_region;
300 
301     WebTextArea *m_print_area;
302     int m_print_x, m_print_y;
303     int m_print_fg, m_print_bg;
304 
305     dolls_data last_player_doll;
306 
307     player_info m_current_player_info;
308 
309     void _send_version();
310     void _send_options();
311     void _send_layout();
312 
313     void _send_everything();
314 
315     bool m_mcache_ref_done;
316     void _mcache_ref(bool inc);
317 
318     void _send_cursor(cursor_type type);
319     void _send_map(bool force_full = false);
320     void _send_cell(const coord_def &gc,
321                     const screen_cell_t &current_sc, const screen_cell_t &next_sc,
322                     const map_cell &current_mc, const map_cell &next_mc,
323                     map<uint32_t, coord_def>& new_monster_locs,
324                     bool force_full);
325     void _send_monster(const coord_def &gc, const monster_info* m,
326                        map<uint32_t, coord_def>& new_monster_locs,
327                        bool force_full);
328     void _send_player(bool force_full = false);
329     void _send_item(item_def& current, const item_def& next,
330                     bool force_full);
331     void _send_messages();
332 };
333 
334 // Main interface for tiles functions
335 extern TilesFramework tiles;
336 
337 class tiles_crt_popup
338 {
339 public:
340     tiles_crt_popup(string tag = "")
341     {
342         tiles.push_crt_menu(tag);
343     }
344 
~tiles_crt_popup()345     ~tiles_crt_popup()
346     {
347         tiles.pop_menu();
348     }
349 };
350 
351 class tiles_ui_control
352 {
353 public:
tiles_ui_control(WebtilesUIState state)354     tiles_ui_control(WebtilesUIState state)
355         : m_new_state(state), m_old_state(tiles.get_ui_state())
356     {
357         tiles.set_ui_state(state);
358     }
359 
~tiles_ui_control()360     ~tiles_ui_control()
361     {
362         if (tiles.get_ui_state() == m_new_state)
363             tiles.set_ui_state(m_old_state);
364     }
365 
366 private:
367     WebtilesUIState m_new_state;
368     WebtilesUIState m_old_state;
369 };
370 
371 #endif
372