3 * Summary: Functions used to print messages.
18 #include "mon-stuff.h"
29 #include "tagstring.h"
42 static bool _ends_in_punctuation(const std::string
& text
)
44 switch (text
[text
.size() - 1])
58 static unsigned int msgwin_line_length();
62 msg_channel_type channel
; // message channel
63 int param
; // param for channel (god, enchantment)
64 std::string text
; // text of message (tagged string...)
67 bool join
; // may this message be joined with
70 message_item() : channel(NUM_MESSAGE_CHANNELS
), param(0),
71 text(""), repeats(0), turn(-1), join(true)
75 message_item(std::string msg
, msg_channel_type chan
, int par
, bool jn
)
76 : channel(chan
), param(par
), text(msg
), repeats(1),
79 // Don't join long messages.
80 join
= jn
&& pure_text().length() < 40;
83 // Constructor for restored messages.
84 message_item(std::string msg
, msg_channel_type chan
, int par
,
86 : channel(chan
), param(par
), text(msg
), repeats(rep
),
87 turn(trn
), join(false)
96 std::string
pure_text() const
98 return formatted_string::parse_string(text
).tostring();
101 std::string
with_repeats() const
103 // TODO: colour the repeats indicator?
104 std::string rep
= "";
106 rep
= make_stringf(" x%d", repeats
);
110 // Tries to condense the argument into this message.
111 // Either *this needs to be an empty item, or it must be the
112 // same as the argument.
113 bool merge(const message_item
& other
)
121 if (!Options
.msg_condense_repeats
)
123 if (other
.channel
== channel
&& other
.param
== param
)
125 if (Options
.msg_condense_repeats
&& other
.text
== text
)
127 repeats
+= other
.repeats
;
130 else if (Options
.msg_condense_short
131 && turn
== other
.turn
132 && repeats
== 1 && other
.repeats
== 1
133 && join
&& other
.join
134 && _ends_in_punctuation(pure_text())
135 == _ends_in_punctuation(other
.pure_text()))
137 // Note that join stays true.
139 std::string sep
= "<lightgrey>";
141 if (!_ends_in_punctuation(pure_text()))
146 sep
+= " </lightgrey>";
147 if (pure_text().length() + seplen
+ other
.pure_text().length()
148 > msgwin_line_length())
162 static int _mod(int num
, int denom
)
165 div_t res
= div(num
, denom
);
166 return (res
.rem
>= 0 ? res
.rem
: res
.rem
+ denom
);
169 template <typename T
, int SIZE
>
174 int end
; // first unfilled index
176 static void inc(int* index
)
178 ASSERT(*index
>= 0 && *index
< SIZE
);
179 *index
= _mod(*index
+ 1, SIZE
);
182 static void dec(int* index
)
184 ASSERT(*index
>= 0 && *index
< SIZE
);
185 *index
= _mod(*index
- 1, SIZE
);
189 circ_vec() : end(0) {}
194 for (int i
= 0; i
< SIZE
; ++i
)
205 ASSERT(_mod(i
, SIZE
) < size());
206 return data
[_mod(end
+ i
, SIZE
)];
209 const T
& operator[](int i
) const
211 ASSERT(_mod(i
, SIZE
) < size());
212 return data
[_mod(end
+ i
, SIZE
)];
215 void push_back(const T
& item
)
221 void roll_back(int n
)
223 for (int i
= 0; i
< n
; ++i
)
231 static void readkey_more(bool user_forced
=false);
233 // Types of message prefixes.
234 // Higher values override lower.
240 P_NEW_CMD
, // new command, but no new turn
242 P_FULL_MORE
, // single-character more prompt (full window)
243 P_OTHER_MORE
, // the other type of --more-- prompt
246 // Could also go with coloured glyphs.
247 glyph
prefix_glyph(prefix_type p
)
267 g
.col
= channel_to_colour(MSGCH_PROMPT
);
281 static bool _pre_more();
283 static bool _temporary
= false;
288 int temp_line
; // starting point of temporary messages
289 int input_line
; // last line-after-input
290 std::vector
<formatted_string
> lines
;
291 prefix_type prompt
; // current prefix prompt
295 return crawl_view
.msgsz
.y
;
298 int use_last_line() const
300 return (first_col_more());
305 return crawl_view
.msgsz
.x
;
308 void out_line(const formatted_string
& line
, int n
) const
310 cgotoxy(1, n
+ 1, GOTO_MSG
);
312 cprintf("%*s", width() - line
.length(), "");
315 // Place cursor at end of last non-empty line to handle prompts.
316 // TODO: might get rid of this by clearing the whole window when writing,
317 // and then just writing the actual non-empty lines.
318 void place_cursor() const
321 for (i
= lines
.size() - 1; i
>= 0 && lines
[i
].length() == 0; --i
);
322 if (i
>= 0 && (int) lines
[i
].length() < crawl_view
.msgsz
.x
)
323 cgotoxy(lines
[i
].length() + 1, i
+ 1, GOTO_MSG
);
326 // Whether to show msgwin-full more prompts.
327 bool more_enabled() const
329 return (crawl_state
.show_more_prompt
330 && (Options
.clear_messages
|| Options
.show_more
));
333 int make_space(int n
)
335 int space
= out_height() - next_line
;
343 s
= std::min(input_line
, n
- space
);
354 // We could consider just scrolling off after --more--;
355 // that would require marking the last message before
357 if (!Options
.clear_messages
&& !more_enabled())
360 return (s
+ n
- space
);
369 void add_line(const formatted_string
& line
)
371 resize(); // TODO: get rid of this
372 lines
[next_line
] = line
;
376 void output_prefix(prefix_type p
)
378 if (!use_first_col())
385 formatted_string line
;
386 line
.add_glyph(prefix_glyph(prompt
));
387 line
+= lines
[next_line
-1].substr(1);
388 lines
[next_line
-1] = line
;
395 : next_line(0), temp_line(0), input_line(0), prompt(P_NONE
)
397 clear_lines(); // initialize this->lines
402 // XXX: broken (why?)
403 lines
.resize(height());
406 unsigned int out_width() const
408 return (width() - (use_first_col() ? 1 : 0));
411 unsigned int out_height() const
413 return (height() - (use_last_line() ? 0 : 1));
419 lines
.resize(height());
422 bool first_col_more() const
424 return (use_first_col() && Options
.small_more
);
427 bool use_first_col() const
429 return (!Options
.clear_messages
);
432 void set_starting_line()
434 // TODO: start at end (sometimes?)
449 ASSERT(next_line
>= n
);
451 for (i
= 0; i
< height() - n
; ++i
)
452 lines
[i
] = lines
[i
+ n
];
453 for (; i
< height(); ++i
)
460 // write to screen (without refresh)
463 // XXX: this should not be necessary as formatted_string should
465 textcolor(LIGHTGREY
);
466 for (size_t i
= 0; i
< lines
.size(); ++i
)
467 out_line(lines
[i
], i
);
470 tiles
.set_need_redraw();
474 // temporary: to be overwritten with next item, e.g. new turn
475 // leading dash or prompt without response
476 void add_item(std::string text
, prefix_type first_col
= P_NONE
,
477 bool temporary
= false)
479 prompt
= P_NONE
; // reset prompt
481 std::vector
<formatted_string
> newlines
;
482 linebreak_string2(text
, out_width());
483 formatted_string::parse_string_to_multiple(text
, newlines
);
485 for (size_t i
= 0; i
< newlines
.size(); ++i
)
487 temp_line
-= make_space(1);
488 formatted_string line
;
490 line
.add_glyph(prefix_glyph(first_col
));
503 temp_line
= std::max(temp_line
, 0);
504 for (int i
= temp_line
; i
< next_line
; ++i
)
506 next_line
= temp_line
;
511 temp_line
= next_line
;
516 input_line
= next_line
;
519 void new_cmdturn(bool new_turn
)
521 output_prefix(new_turn
? P_NEW_TURN
: P_NEW_CMD
);
526 return (next_line
> input_line
);
530 * Handling of more prompts (both types).
532 void more(bool full
, bool user
=false)
538 int last_row
= crawl_view
.msgsz
.y
;
539 if (first_col_more())
541 cgotoxy(1, last_row
, GOTO_MSG
);
542 glyph g
= prefix_glyph(full
? P_FULL_MORE
: P_OTHER_MORE
);
546 // Move cursor back for nicer display.
547 cgotoxy(1, last_row
, GOTO_MSG
);
548 // Need to read_key while cursor_control in scope.
549 cursor_control
con(true);
554 cgotoxy(use_first_col() ? 2 : 1, last_row
, GOTO_MSG
);
555 textcolor(channel_to_colour(MSGCH_PROMPT
));
556 if (crawl_state
.game_is_hints())
558 std::string more_str
= "--more-- Press Space ";
560 more_str
+= "or click ";
562 more_str
+= "to continue. You can later reread messages with "
564 cprintf(more_str
.c_str());
574 message_window msgwin
;
576 void display_message_window()
581 void clear_message_window()
583 msgwin
= message_window();
586 void scroll_message_window(int n
)
594 return msgwin
.any_messages();
597 typedef circ_vec
<message_item
, NUM_STORED_MESSAGES
> store_t
;
602 message_item prev_msg
;
604 int temp
; // number of temporary messages
607 message_store() : last_of_turn(false), temp(0) {}
609 void add(const message_item
& msg
)
611 if (msg
.channel
!= MSGCH_PROMPT
&& prev_msg
.merge(msg
))
615 if (msg
.channel
== MSGCH_PROMPT
|| _temporary
)
624 void store_msg(const message_item
& msg
)
626 prefix_type p
= P_NONE
;
632 msgwin
.add_item(msg
.with_repeats(), p
, _temporary
);
637 msgs
.roll_back(temp
);
650 message_item msg
= prev_msg
;
651 // Clear prev_msg before storing it, since
652 // writing out to the message window might
653 // in turn result in a recursive flush_prev.
654 prev_msg
= message_item();
658 msgwin
.new_cmdturn(true);
659 last_of_turn
= false;
668 msgwin
.new_cmdturn(true);
671 // XXX: this should not need to exist
672 const store_t
& get_store()
680 prev_msg
= message_item();
681 last_of_turn
= false;
686 // Circular buffer for keeping past messages.
687 message_store messages
;
689 static FILE* _msg_dump_file
= NULL
;
691 static bool suppress_messages
= false;
692 static msg_colour_type
prepare_message(const std::string
& imsg
,
693 msg_channel_type channel
,
696 no_messages::no_messages() : msuppressed(suppress_messages
)
698 suppress_messages
= true;
701 no_messages::~no_messages()
703 suppress_messages
= msuppressed
;
706 msg_colour_type
msg_colour(int col
)
708 return static_cast<msg_colour_type
>(col
);
711 static int colour_msg(msg_colour_type col
)
713 if (col
== MSGCOL_MUTED
)
716 return static_cast<int>(col
);
719 // Returns a colour or MSGCOL_MUTED.
720 static msg_colour_type
channel_to_msgcol(msg_channel_type channel
, int param
)
723 return (MSGCOL_DARKGREY
);
727 switch (Options
.channels
[channel
])
730 // Note that if the plain channel is muted, then we're protecting
731 // the player from having that spread to other channels here.
732 // The intent of plain is to give non-coloured messages, not to
734 if (Options
.channels
[MSGCH_PLAIN
] >= MSGCOL_DEFAULT
)
735 ret
= MSGCOL_LIGHTGREY
;
737 ret
= Options
.channels
[MSGCH_PLAIN
];
741 case MSGCOL_ALTERNATE
:
746 ret
= (Options
.channels
[channel
] == MSGCOL_DEFAULT
)
747 ? msg_colour(god_colour(static_cast<god_type
>(param
)))
748 : msg_colour(god_message_altar_colour(static_cast<god_type
>(param
)));
752 ret
= MSGCOL_LIGHTBLUE
;
761 ret
= MSGCOL_LIGHTRED
;
765 if (param
) // positive change
771 case MSGCH_INTRINSIC_GAIN
:
776 ret
= MSGCOL_LIGHTGREEN
;
780 case MSGCH_TALK_VISUAL
:
785 ret
= MSGCOL_LIGHTRED
;
788 case MSGCH_MONSTER_SPELL
:
789 case MSGCH_MONSTER_ENCHANT
:
790 case MSGCH_FRIEND_SPELL
:
791 case MSGCH_FRIEND_ENCHANT
:
792 ret
= MSGCOL_LIGHTMAGENTA
;
797 case MSGCH_BANISHMENT
:
798 ret
= MSGCOL_MAGENTA
;
801 case MSGCH_MONSTER_DAMAGE
:
802 ret
= ((param
== MDAM_DEAD
) ? MSGCOL_RED
:
803 (param
>= MDAM_SEVERELY_DAMAGED
) ? MSGCOL_LIGHTRED
:
804 (param
>= MDAM_MODERATELY_DAMAGED
) ? MSGCOL_YELLOW
812 case MSGCH_DIAGNOSTICS
:
813 case MSGCH_MULTITURN_ACTION
:
814 ret
= MSGCOL_DARKGREY
; // makes it easier to ignore at times -- bwr
818 case MSGCH_FRIEND_ACTION
:
819 case MSGCH_ROTTEN_MEAT
:
820 case MSGCH_EQUIPMENT
:
822 case MSGCH_EXAMINE_FILTER
:
824 ret
= param
> 0 ? msg_colour(param
) : MSGCOL_LIGHTGREY
;
834 // Setting to a specific colour is handled here, special
835 // cases should be handled above.
836 if (channel
== MSGCH_MONSTER_DAMAGE
)
838 // A special case right now for monster damage (at least until
839 // the init system is improved)... selecting a specific
840 // colour here will result in only the death messages coloured.
841 if (param
== MDAM_DEAD
)
842 ret
= Options
.channels
[channel
];
843 else if (Options
.channels
[MSGCH_PLAIN
] >= MSGCOL_DEFAULT
)
844 ret
= MSGCOL_LIGHTGREY
;
846 ret
= Options
.channels
[MSGCH_PLAIN
];
849 ret
= Options
.channels
[channel
];
856 int channel_to_colour(msg_channel_type channel
, int param
)
858 return colour_msg(channel_to_msgcol(channel
, param
));
861 static void do_message_print(msg_channel_type channel
, int param
,
862 const char *format
, va_list argp
)
867 size_t len
= vsnprintf(buff
, sizeof(buff
), format
, argp
);
868 if (len
< sizeof(buff
))
870 mpr(buff
, channel
, param
);
874 char *heapbuf
= (char*)malloc(len
+ 1);
875 vsnprintf(heapbuf
, len
+ 1, format
, ap
);
876 mpr(heapbuf
, channel
, param
);
882 void mprf(msg_channel_type channel
, int param
, const char *format
, ...)
885 va_start(argp
, format
);
886 do_message_print(channel
, param
, format
, argp
);
890 void mprf(msg_channel_type channel
, const char *format
, ...)
893 va_start(argp
, format
);
894 do_message_print(channel
, channel
== MSGCH_GOD
? you
.religion
: 0,
899 void mprf(const char *format
, ...)
902 va_start(argp
, format
);
903 do_message_print(MSGCH_PLAIN
, 0, format
, argp
);
907 #ifdef DEBUG_DIAGNOSTICS
908 void dprf(const char *format
, ...)
911 va_start(argp
, format
);
912 do_message_print(MSGCH_DIAGNOSTICS
, 0, format
, argp
);
917 static bool _updating_view
= false;
919 static bool check_more(const std::string
& line
, msg_channel_type channel
)
921 for (unsigned i
= 0; i
< Options
.force_more_message
.size(); ++i
)
922 if (Options
.force_more_message
[i
].is_filtered(channel
, line
))
927 static bool check_join(const std::string
& line
, msg_channel_type channel
)
931 case MSGCH_EQUIPMENT
:
939 static void debug_channel_arena(msg_channel_type channel
)
949 case MSGCH_INTRINSIC_GAIN
:
951 case MSGCH_ROTTEN_MEAT
:
952 case MSGCH_EQUIPMENT
:
953 case MSGCH_FLOOR_ITEMS
:
954 case MSGCH_MULTITURN_ACTION
:
956 case MSGCH_EXAMINE_FILTER
:
959 die("Invalid channel '%s' in arena mode",
960 channel_to_str(channel
).c_str());
967 void msgwin_set_temporary(bool temp
)
969 flush_prev_message();
973 messages
.reset_temp();
978 void msgwin_clear_temporary()
980 messages
.roll_back();
984 static long _last_msg_turn
= -1; // Turn of last message.
986 void mpr(std::string text
, msg_channel_type channel
, int param
, bool nojoin
)
988 if (_msg_dump_file
!= NULL
)
989 fprintf(_msg_dump_file
, "%s\n", text
.c_str());
991 if (crawl_state
.game_crashed
)
994 if (crawl_state
.game_is_arena())
995 debug_channel_arena(channel
);
997 if (!crawl_state
.io_inited
)
999 if (channel
== MSGCH_ERROR
)
1000 fprintf(stderr
, "%s\n", text
.c_str());
1004 // Flush out any "comes into view" monster announcements before the
1005 // monster has a chance to give any other messages.
1006 if (!_updating_view
)
1008 _updating_view
= true;
1009 flush_comes_into_view();
1010 _updating_view
= false;
1013 if (channel
== MSGCH_GOD
&& param
== 0)
1014 param
= you
.religion
;
1016 msg_colour_type colour
= prepare_message(text
, channel
, param
);
1018 if (colour
== MSGCOL_MUTED
)
1021 bool domore
= check_more(text
, channel
);
1022 bool join
= !domore
&& !nojoin
&& check_join(text
, channel
);
1024 if (you
.duration
[DUR_QUAD_DAMAGE
])
1026 // No sound, so we simulate the reverb with all caps.
1027 formatted_string fs
= formatted_string::parse_string(text
);
1029 text
= fs
.to_colour_string();
1032 std::string col
= colour_to_str(colour_msg(colour
));
1033 text
= "<" + col
+ ">" + text
+ "</" + col
+ ">"; // XXX
1034 message_item msg
= message_item(text
, channel
, param
, join
);
1036 _last_msg_turn
= msg
.turn
;
1038 if (channel
== MSGCH_ERROR
)
1039 interrupt_activity(AI_FORCE_INTERRUPT
);
1041 if (channel
== MSGCH_PROMPT
|| channel
== MSGCH_ERROR
)
1042 set_more_autoclear(false);
1048 static std::string
show_prompt(std::string prompt
)
1050 mpr(prompt
, MSGCH_PROMPT
);
1052 // FIXME: duplicating mpr code.
1053 msg_colour_type colour
= prepare_message(prompt
, MSGCH_PROMPT
, 0);
1054 return colour_string(prompt
, colour_msg(colour
));
1057 static std::string _prompt
;
1058 void msgwin_prompt(std::string prompt
)
1060 msgwin_set_temporary(true);
1061 _prompt
= show_prompt(prompt
);
1064 void msgwin_reply(std::string reply
)
1066 msgwin_clear_temporary();
1067 msgwin_set_temporary(false);
1068 reply
= replace_all(reply
, "<", "<<");
1069 mpr(_prompt
+ "<lightgrey>" + reply
+ "</lightgrey>", MSGCH_PROMPT
);
1073 void msgwin_got_input()
1078 int msgwin_get_line(std::string prompt
, char *buf
, int len
,
1079 input_history
*mh
, int (*keyproc
)(int& c
))
1082 msgwin_prompt(prompt
);
1084 int ret
= cancelable_get_line(buf
, len
, mh
, keyproc
);
1089 void msgwin_new_turn()
1091 messages
.new_turn();
1094 void msgwin_new_cmd()
1096 flush_prev_message();
1097 bool new_turn
= (you
.num_turns
> _last_msg_turn
);
1098 msgwin
.new_cmdturn(new_turn
);
1101 static unsigned int msgwin_line_length()
1103 return msgwin
.out_width();
1106 unsigned int msgwin_lines()
1108 return msgwin
.out_height();
1111 // mpr() an arbitrarily long list of strings without truncation or risk
1113 void mpr_comma_separated_list(const std::string prefix
,
1114 const std::vector
<std::string
> list
,
1115 const std::string
&andc
,
1116 const std::string
&comma
,
1117 const msg_channel_type channel
,
1120 std::string out
= prefix
;
1122 for (int i
= 0, size
= list
.size(); i
< size
; i
++)
1126 if (size
> 0 && i
< (size
- 2))
1128 else if (i
== (size
- 2))
1130 else if (i
== (size
- 1))
1133 mpr(out
, channel
, param
);
1137 // Checks whether a given message contains patterns relevant for
1138 // notes, stop_running or sounds and handles these cases.
1139 static void mpr_check_patterns(const std::string
& message
,
1140 msg_channel_type channel
,
1143 for (unsigned i
= 0; i
< Options
.note_messages
.size(); ++i
)
1145 if (channel
== MSGCH_EQUIPMENT
|| channel
== MSGCH_FLOOR_ITEMS
1146 || channel
== MSGCH_MULTITURN_ACTION
1147 || channel
== MSGCH_EXAMINE
|| channel
== MSGCH_EXAMINE_FILTER
1148 || channel
== MSGCH_TUTORIAL
)
1153 if (Options
.note_messages
[i
].matches(message
))
1155 take_note(Note(NOTE_MESSAGE
, channel
, param
, message
.c_str()));
1160 if (channel
!= MSGCH_DIAGNOSTICS
&& channel
!= MSGCH_EQUIPMENT
)
1161 interrupt_activity(AI_MESSAGE
, channel_to_str(channel
) + ":" + message
);
1163 // Any sound has a chance of waking the PC if the PC is asleep.
1164 if (channel
== MSGCH_SOUND
)
1165 you
.check_awaken(5);
1167 if (!Options
.sound_mappings
.empty())
1168 for (unsigned i
= 0; i
< Options
.sound_mappings
.size(); i
++)
1170 // Maybe we should allow message channel matching as for
1171 // force_more_message?
1172 if (Options
.sound_mappings
[i
].pattern
.matches(message
))
1174 play_sound(Options
.sound_mappings
[i
].soundfile
.c_str());
1180 static bool channel_message_history(msg_channel_type channel
)
1185 case MSGCH_EQUIPMENT
:
1186 case MSGCH_EXAMINE_FILTER
:
1193 // Returns the default colour of the message, or MSGCOL_MUTED if
1194 // the message should be suppressed.
1195 static msg_colour_type
prepare_message(const std::string
& imsg
,
1196 msg_channel_type channel
,
1199 if (suppress_messages
)
1200 return MSGCOL_MUTED
;
1202 if (silenced(you
.pos())
1203 && (channel
== MSGCH_SOUND
|| channel
== MSGCH_TALK
))
1205 return MSGCOL_MUTED
;
1208 msg_colour_type colour
= channel_to_msgcol(channel
, param
);
1210 if (colour
!= MSGCOL_MUTED
)
1211 mpr_check_patterns(imsg
, channel
, param
);
1213 const std::vector
<message_colour_mapping
>& mcm
1214 = Options
.message_colour_mappings
;
1215 typedef std::vector
<message_colour_mapping
>::const_iterator mcmci
;
1217 for (mcmci ci
= mcm
.begin(); ci
!= mcm
.end(); ++ci
)
1219 if (ci
->message
.is_filtered(channel
, imsg
))
1221 colour
= ci
->colour
;
1229 void flush_prev_message()
1231 messages
.flush_prev();
1234 void mesclr(bool force
)
1236 if (!crawl_state
.io_inited
)
1238 // Unflushed message will be lost with clear_messages,
1239 // so they shouldn't really exist, but some of the delay
1240 // code appears to do this intentionally.
1241 // ASSERT(!messages.have_prev());
1242 flush_prev_message();
1244 msgwin
.got_input(); // Consider old messages as read.
1246 if (Options
.clear_messages
|| force
)
1249 // TODO: we could indicate indicate mesclr with a different
1250 // leading character than '-'.
1253 static bool autoclear_more
= false;
1255 void set_more_autoclear(bool on
)
1257 autoclear_more
= on
;
1260 static void readkey_more(bool user_forced
)
1265 mouse_control
mc(MOUSE_MODE_MORE
);
1267 keypress
= getch_ck();
1268 while (keypress
!= ' ' && keypress
!= '\r' && keypress
!= '\n'
1269 && !key_is_escape(keypress
)
1270 && (user_forced
|| keypress
!= CK_MOUSE_CLICK
));
1272 if (key_is_escape(keypress
))
1273 set_more_autoclear(true);
1277 * more() preprocessing.
1279 * @return Whether the more prompt should be skipped.
1281 static bool _pre_more()
1283 if (crawl_state
.game_crashed
|| crawl_state
.seen_hups
)
1286 #ifdef DEBUG_DIAGNOSTICS
1291 if (crawl_state
.game_is_arena())
1293 delay(Options
.arena_delay
);
1297 if (crawl_state
.is_replaying_keys())
1301 if (luaterp_running())
1305 if (!crawl_state
.show_more_prompt
|| suppress_messages
)
1311 void more(bool user_forced
)
1313 if (!crawl_state
.io_inited
)
1315 flush_prev_message();
1316 msgwin
.more(false, user_forced
);
1320 static bool is_channel_dumpworthy(msg_channel_type channel
)
1322 return (channel
!= MSGCH_EQUIPMENT
1323 && channel
!= MSGCH_DIAGNOSTICS
1324 && channel
!= MSGCH_TUTORIAL
);
1327 void clear_message_store()
1332 std::string
get_last_messages(int mcount
)
1334 flush_prev_message();
1337 // XXX: should use some message_history iterator here
1338 const store_t
& msgs
= messages
.get_store();
1339 // XXX: loop wraps around otherwise. This could be done better.
1340 mcount
= std::min(mcount
, NUM_STORED_MESSAGES
);
1341 for (int i
= -1; mcount
> 0; --i
)
1343 const message_item msg
= msgs
[i
];
1346 if (is_channel_dumpworthy(msg
.channel
))
1348 text
= msg
.pure_text() + "\n" + text
;
1353 // An extra line of clearance.
1359 // We just write out the whole message store including empty/unused
1360 // messages. They'll be ignored when restoring.
1361 void save_messages(writer
& outf
)
1363 store_t msgs
= messages
.get_store();
1364 marshallInt(outf
, msgs
.size());
1365 for (int i
= 0; i
< msgs
.size(); ++i
)
1367 marshallString4(outf
, msgs
[i
].text
);
1368 marshallInt(outf
, msgs
[i
].channel
);
1369 marshallInt(outf
, msgs
[i
].param
);
1370 marshallInt(outf
, msgs
[i
].repeats
);
1371 marshallInt(outf
, msgs
[i
].turn
);
1375 void load_messages(reader
& inf
)
1377 unwind_var
<bool> save_more(crawl_state
.show_more_prompt
, false);
1379 int num
= unmarshallInt(inf
);
1380 for (int i
= 0; i
< num
; ++i
)
1383 unmarshallString4(inf
, text
);
1385 msg_channel_type channel
= (msg_channel_type
) unmarshallInt(inf
);
1386 int param
= unmarshallInt(inf
);
1387 int repeats
= unmarshallInt(inf
);
1388 int turn
= unmarshallInt(inf
);
1390 message_item
msg(message_item(text
, channel
, param
, repeats
, turn
));
1392 messages
.store_msg(msg
);
1394 // With Options.message_clear, we don't want the message window
1399 void replay_messages(void)
1401 formatted_scroller
hist(MF_START_AT_END
| MF_ALWAYS_SHOW_MORE
, "");
1402 hist
.set_more(formatted_string::parse_string(
1403 "<cyan>[up/<< : Page up. down/Space/> : Page down."
1404 " Esc exits.]</cyan>"));
1406 const store_t msgs
= messages
.get_store();
1407 for (int i
= 0; i
< msgs
.size(); ++i
)
1408 if (channel_message_history(msgs
[i
].channel
))
1410 std::string text
= msgs
[i
].with_repeats();
1411 linebreak_string2(text
, cgetsize(GOTO_CRT
).x
- 1);
1412 std::vector
<formatted_string
> parts
;
1413 formatted_string::parse_string_to_multiple(text
, parts
);
1414 for (unsigned int j
= 0; j
< parts
.size(); ++j
)
1416 formatted_string line
;
1417 prefix_type p
= P_NONE
;
1418 if (j
== parts
.size() - 1 && i
+ 1 < msgs
.size()
1419 && msgs
[i
+1].turn
> msgs
[i
].turn
)
1423 line
.add_glyph(prefix_glyph(p
));
1425 hist
.add_item_formatted_string(line
);
1432 void set_msg_dump_file(FILE* file
)
1434 _msg_dump_file
= file
;
1438 void formatted_mpr(const formatted_string
& fs
,
1439 msg_channel_type channel
, int param
)
1441 mpr(fs
.to_colour_string(), channel
, param
);