3 * @brief Notetaking stuff
28 #define NOTES_VERSION_NUMBER 1002
30 std::vector<Note> note_list;
32 // return the real number of the power (casting out nonexistent powers),
33 // starting from 0, or -1 if the power doesn't exist
34 static int _real_god_power(int religion, int idx)
36 if (god_gain_power_messages[religion][idx][0] == 0)
40 for (int j = 0; j < idx; ++j)
41 if (god_gain_power_messages[religion][j][0])
47 static bool _is_noteworthy_skill_level(int level)
49 for (unsigned int i = 0; i < Options.note_skill_levels.size(); ++i)
50 if (level == Options.note_skill_levels[i])
56 static bool _is_highest_skill(int skill)
58 for (int i = 0; i < NUM_SKILLS; ++i)
62 if (you.skills[i] >= you.skills[skill])
68 static bool _is_noteworthy_hp(int hp, int maxhp)
70 return (hp > 0 && Options.note_hp_percent
71 && hp <= (maxhp * Options.note_hp_percent) / 100);
74 static int _dungeon_branch_depth(uint8_t branch)
76 if (branch >= NUM_BRANCHES)
78 return brdepth[branch];
81 static bool _is_noteworthy_dlevel(unsigned short place)
83 const uint8_t branch = (place >> 8) & 0xFF;
84 const int lev = (place & 0xFF);
86 // The Abyss is noted a different way (since we care mostly about the cause).
87 if (branch == BRANCH_ABYSS)
90 // Other portal levels are always interesting.
91 if (!is_connected_branch(static_cast<branch_type>(branch)))
94 if (lev == _dungeon_branch_depth(branch)
95 || branch == BRANCH_MAIN_DUNGEON && (lev % 5) == 0
96 || branch == BRANCH_MAIN_DUNGEON && lev == 14
97 || branch != BRANCH_MAIN_DUNGEON && lev == 1)
105 // Is a note worth taking?
106 // This function assumes that game state has not changed since
107 // the note was taken, e.g. you.* is valid.
108 static bool _is_noteworthy(const Note& note)
110 // Always noteworthy.
111 if (note.type == NOTE_XP_LEVEL_CHANGE
112 || note.type == NOTE_LEARN_SPELL
113 || note.type == NOTE_GET_GOD
114 || note.type == NOTE_GOD_GIFT
115 || note.type == NOTE_GET_MUTATION
116 || note.type == NOTE_LOSE_MUTATION
117 || note.type == NOTE_GET_ITEM
118 || note.type == NOTE_ID_ITEM
119 || note.type == NOTE_BUY_ITEM
120 || note.type == NOTE_DONATE_MONEY
121 || note.type == NOTE_SEEN_MONSTER
122 || note.type == NOTE_DEFEAT_MONSTER
123 || note.type == NOTE_POLY_MONSTER
124 || note.type == NOTE_USER_NOTE
125 || note.type == NOTE_MESSAGE
126 || note.type == NOTE_LOSE_GOD
127 || note.type == NOTE_PENANCE
128 || note.type == NOTE_MOLLIFY_GOD
129 || note.type == NOTE_DEATH
130 || note.type == NOTE_XOM_REVIVAL
131 || note.type == NOTE_SEEN_FEAT
132 || note.type == NOTE_PARALYSIS
133 || note.type == NOTE_NAMED_ALLY
134 || note.type == NOTE_ALLY_DEATH
135 || note.type == NOTE_FEAT_MIMIC)
140 // Never noteworthy, hooked up for fun or future use.
141 if (note.type == NOTE_MP_CHANGE
142 || note.type == NOTE_MAXHP_CHANGE
143 || note.type == NOTE_MAXMP_CHANGE)
148 // Xom effects are only noteworthy if the option is true.
149 if (note.type == NOTE_XOM_EFFECT)
150 return Options.note_xom_effects;
152 // God powers might be noteworthy if it's an actual power.
153 if (note.type == NOTE_GOD_POWER
154 && _real_god_power(note.first, note.second) == -1)
159 // HP noteworthiness is handled in its own function.
160 if (note.type == NOTE_HP_CHANGE
161 && !_is_noteworthy_hp(note.first, note.second))
166 // Skills are noteworthy if in the skill value list or if
167 // it's a new maximal skill (depending on options).
168 if (note.type == NOTE_GAIN_SKILL || note.type == NOTE_LOSE_SKILL)
170 if (Options.note_all_skill_levels
171 || _is_noteworthy_skill_level(note.second)
172 || Options.note_skill_max && _is_highest_skill(note.first))
179 if (note.type == NOTE_DUNGEON_LEVEL_CHANGE)
180 return _is_noteworthy_dlevel(note.packed_place);
182 for (unsigned i = 0; i < note_list.size(); ++i)
184 if (note_list[i].type != note.type)
187 const Note& rnote(note_list[i]);
191 if (rnote.first == note.first && rnote.second == note.second)
196 // Not if we have a recent warning
197 // unless we've lost half our HP since then.
198 if (note.turn - rnote.turn < 5
199 && note.first * 2 >= rnote.first)
206 mpr("Buggy note passed: unknown note type");
207 // Return now, rather than give a "Buggy note passed" message
208 // for each note of the matching type in the note list.
215 static const char* _number_to_ordinal(int number)
217 const char* ordinals[5] = { "first", "second", "third", "fourth", "fifth" };
220 return "[unknown ordinal (too small)]";
222 return "[unknown ordinal (too big)]";
223 return ordinals[number-1];
226 std::string Note::describe(bool when, bool where, bool what) const
228 std::ostringstream result;
231 result << std::setw(6) << turn << " ";
235 result << "| " << chop_string(short_place_name(packed_place),
236 MAX_NOTE_PLACE_LEN) << " | ";
244 // [ds] Shortened HP change note from "Had X hitpoints" to
245 // accommodate the cause for the loss of hitpoints.
246 result << "HP: " << first << "/" << second
247 << " [" << name << "]";
249 case NOTE_XOM_REVIVAL:
250 result << "Xom revived you";
253 result << "Mana: " << first << "/" << second;
255 case NOTE_MAXHP_CHANGE:
256 result << "Reached " << first << " max hit points";
258 case NOTE_MAXMP_CHANGE:
259 result << "Reached " << first << " max mana";
261 case NOTE_XP_LEVEL_CHANGE:
262 result << "Reached XP level " << first << ". " << name;
264 case NOTE_DUNGEON_LEVEL_CHANGE:
268 result << "Entered " << place_name(packed_place, true, true);
270 case NOTE_LEARN_SPELL:
271 result << "Learned a level "
272 << spell_difficulty(static_cast<spell_type>(first))
274 << spell_title(static_cast<spell_type>(first));
277 result << "Became a worshipper of "
278 << god_name(static_cast<god_type>(first), true);
281 result << "Fell from the grace of "
282 << god_name(static_cast<god_type>(first));
285 result << "Was placed under penance by "
286 << god_name(static_cast<god_type>(first));
288 case NOTE_MOLLIFY_GOD:
289 result << "Was forgiven by "
290 << god_name(static_cast<god_type>(first));
293 result << "Received a gift from "
294 << god_name(static_cast<god_type>(first));
297 result << "Identified " << name;
299 result << " (" << desc << ")";
302 result << "Got " << name;
305 result << "Bought " << name << " for " << first << " gold piece"
306 << (first == 1 ? "" : "s");
308 case NOTE_DONATE_MONEY:
309 result << "Donated " << first << " gold piece"
310 << (first == 1 ? "" : "s") << " to Zin";
312 case NOTE_GAIN_SKILL:
313 result << "Reached skill level " << second
314 << " in " << skill_name(static_cast<skill_type>(first));
316 case NOTE_LOSE_SKILL:
317 result << "Reduced skill "
318 << skill_name(static_cast<skill_type>(first))
319 << " to level " << second;
321 case NOTE_SEEN_MONSTER:
322 result << "Noticed " << name;
324 case NOTE_DEFEAT_MONSTER:
326 result << name << " (ally) was " << desc;
328 result << uppercase_first(desc) << " " << name;
330 case NOTE_POLY_MONSTER:
331 result << name << " changed into " << desc;
334 result << "Acquired "
335 << god_name(static_cast<god_type>(first)) << "'s "
336 << _number_to_ordinal(_real_god_power(first, second)+1)
339 case NOTE_GET_MUTATION:
340 result << "Gained mutation: "
341 << mutation_name(static_cast<mutation_type>(first),
342 second == 0 ? 1 : second);
344 result << " [" << name << "]";
346 case NOTE_LOSE_MUTATION:
347 result << "Lost mutation: "
348 << mutation_name(static_cast<mutation_type>(first),
349 second == 3 ? 3 : second+1);
351 result << " [" << name << "]";
357 result << Options.user_note_prefix << name;
363 result << "Found " << name;
365 case NOTE_FEAT_MIMIC:
366 result << name <<" was a mimic.";
368 case NOTE_XOM_EFFECT:
369 result << "XOM: " << name;
370 #if defined(DEBUG_XOM) || defined(NOTE_DEBUG_XOM)
371 // If debugging, also take note of piety and tension.
372 result << " (piety: " << first;
374 result << ", tension: " << second;
379 result << "Paralysed by " << name << " for " << first << " turns";
381 case NOTE_NAMED_ALLY:
382 result << "Gained " << name << " as an ally";
384 case NOTE_ALLY_DEATH:
385 result << "Your ally " << name << " died";
388 result << "Buggy note description: unknown note type";
393 if (type == NOTE_SEEN_MONSTER || type == NOTE_DEFEAT_MONSTER)
395 if (what && first == MONS_PANDEMONIUM_LORD)
396 result << " the pandemonium lord";
403 turn = you.num_turns;
404 packed_place = get_packed_place();
407 Note::Note(NOTE_TYPES t, int f, int s, const char* n, const char* d) :
408 type(t), first(f), second(s)
411 name = std::string(n);
413 desc = std::string(d);
415 turn = you.num_turns;
416 packed_place = get_packed_place();
419 void Note::check_milestone() const
421 if (crawl_state.game_is_arena())
424 if (type == NOTE_DUNGEON_LEVEL_CHANGE)
426 const int br = place_branch(packed_place),
427 dep = place_depth(packed_place);
429 // Wizlabs report their milestones on their own.
430 if (br != -1 && br != BRANCH_WIZLAB)
432 ASSERT(br >= 0 && br < NUM_BRANCHES);
433 std::string branch = place_name(packed_place, true, false).c_str();
434 if (branch.find("The ") == 0)
435 branch[0] = tolower(branch[0]);
439 mark_milestone(br == BRANCH_ZIGGURAT ? "zig.enter" : "br.enter",
440 "entered " + branch + ".", true);
442 else if (dep == _dungeon_branch_depth(br) || dep == 14
443 || br == BRANCH_ZIGGURAT)
445 std::string level = place_name(packed_place, true, true);
446 if (level.find("Level ") == 0)
447 level[0] = tolower(level[0]);
449 std::ostringstream branch_finale;
450 branch_finale << "reached " << level << ".";
451 mark_milestone(br == BRANCH_ZIGGURAT ? "zig" :
452 dep == 14 ? "br.mid" : "br.end",
453 branch_finale.str());
459 void Note::save(writer& outf) const
461 marshallInt(outf, type);
462 marshallInt(outf, turn);
463 marshallShort(outf, packed_place);
464 marshallInt(outf, first);
465 marshallInt(outf, second);
466 marshallString4(outf, name);
467 marshallString4(outf, desc);
470 void Note::load(reader& inf)
472 type = static_cast<NOTE_TYPES>(unmarshallInt(inf));
473 turn = unmarshallInt(inf);
474 packed_place = unmarshallShort(inf);
475 first = unmarshallInt(inf);
476 second = unmarshallInt(inf);
477 unmarshallString4(inf, name);
478 unmarshallString4(inf, desc);
481 static bool notes_active = false;
483 bool notes_are_active()
488 void take_note(const Note& note, bool force)
490 if (notes_active && (force || _is_noteworthy(note)))
492 note_list.push_back(note);
493 note.check_milestone();
497 void activate_notes(bool active)
499 notes_active = active;
502 void save_notes(writer& outf)
504 marshallInt(outf, NOTES_VERSION_NUMBER);
505 marshallInt(outf, note_list.size());
506 for (unsigned i = 0; i < note_list.size(); ++i)
507 note_list[i].save(outf);
510 void load_notes(reader& inf)
512 if (unmarshallInt(inf) != NOTES_VERSION_NUMBER)
515 const int num_notes = unmarshallInt(inf);
516 for (int i = 0; i < num_notes; ++i)
520 note_list.push_back(new_note);
524 void make_user_note()
527 bool validline = !msgwin_get_line("Enter note: ", buf, sizeof(buf));
528 if (!validline || (!*buf))
530 Note unote(NOTE_USER_NOTE);