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_highest_skill(int skill)
49 for (int i = 0; i < NUM_SKILLS; ++i)
53 if (you.skills[i] >= you.skills[skill])
59 static bool _is_noteworthy_hp(int hp, int maxhp)
61 return (hp > 0 && Options.note_hp_percent
62 && hp <= (maxhp * Options.note_hp_percent) / 100);
65 static int _dungeon_branch_depth(uint8_t branch)
67 if (branch >= NUM_BRANCHES)
69 return brdepth[branch];
72 static bool _is_noteworthy_dlevel(unsigned short place)
74 const uint8_t branch = (place >> 8) & 0xFF;
75 const int lev = (place & 0xFF);
77 // The Abyss is noted a different way (since we care mostly about the cause).
78 if (branch == BRANCH_ABYSS)
81 // Other portal levels are always interesting.
82 if (!is_connected_branch(static_cast<branch_type>(branch)))
85 if (lev == _dungeon_branch_depth(branch)
86 || branch == BRANCH_MAIN_DUNGEON && (lev % 5) == 0
87 || branch == BRANCH_MAIN_DUNGEON && lev == 14
88 || branch != BRANCH_MAIN_DUNGEON && lev == 1)
96 // Is a note worth taking?
97 // This function assumes that game state has not changed since
98 // the note was taken, e.g. you.* is valid.
99 static bool _is_noteworthy(const Note& note)
101 // Always noteworthy.
102 if (note.type == NOTE_XP_LEVEL_CHANGE
103 || note.type == NOTE_LEARN_SPELL
104 || note.type == NOTE_GET_GOD
105 || note.type == NOTE_GOD_GIFT
106 || note.type == NOTE_GET_MUTATION
107 || note.type == NOTE_LOSE_MUTATION
108 || note.type == NOTE_GET_ITEM
109 || note.type == NOTE_ID_ITEM
110 || note.type == NOTE_BUY_ITEM
111 || note.type == NOTE_DONATE_MONEY
112 || note.type == NOTE_SEEN_MONSTER
113 || note.type == NOTE_DEFEAT_MONSTER
114 || note.type == NOTE_POLY_MONSTER
115 || note.type == NOTE_USER_NOTE
116 || note.type == NOTE_MESSAGE
117 || note.type == NOTE_LOSE_GOD
118 || note.type == NOTE_PENANCE
119 || note.type == NOTE_MOLLIFY_GOD
120 || note.type == NOTE_DEATH
121 || note.type == NOTE_XOM_REVIVAL
122 || note.type == NOTE_SEEN_FEAT
123 || note.type == NOTE_PARALYSIS
124 || note.type == NOTE_NAMED_ALLY
125 || note.type == NOTE_ALLY_DEATH
126 || note.type == NOTE_FEAT_MIMIC)
131 // Never noteworthy, hooked up for fun or future use.
132 if (note.type == NOTE_MP_CHANGE
133 || note.type == NOTE_MAXHP_CHANGE
134 || note.type == NOTE_MAXMP_CHANGE)
139 // Xom effects are only noteworthy if the option is true.
140 if (note.type == NOTE_XOM_EFFECT)
141 return Options.note_xom_effects;
143 // God powers might be noteworthy if it's an actual power.
144 if (note.type == NOTE_GOD_POWER
145 && _real_god_power(note.first, note.second) == -1)
150 // HP noteworthiness is handled in its own function.
151 if (note.type == NOTE_HP_CHANGE
152 && !_is_noteworthy_hp(note.first, note.second))
157 // Skills are noteworthy if in the skill value list or if
158 // it's a new maximal skill (depending on options).
159 if (note.type == NOTE_GAIN_SKILL || note.type == NOTE_LOSE_SKILL)
161 if (Options.note_all_skill_levels
162 || note.second <= 27 && Options.note_skill_levels[note.second]
163 || Options.note_skill_max && _is_highest_skill(note.first))
170 if (note.type == NOTE_DUNGEON_LEVEL_CHANGE)
171 return _is_noteworthy_dlevel(note.packed_place);
173 for (unsigned i = 0; i < note_list.size(); ++i)
175 if (note_list[i].type != note.type)
178 const Note& rnote(note_list[i]);
182 if (rnote.first == note.first && rnote.second == note.second)
187 // Not if we have a recent warning
188 // unless we've lost half our HP since then.
189 if (note.turn - rnote.turn < 5
190 && note.first * 2 >= rnote.first)
197 mpr("Buggy note passed: unknown note type");
198 // Return now, rather than give a "Buggy note passed" message
199 // for each note of the matching type in the note list.
206 static const char* _number_to_ordinal(int number)
208 const char* ordinals[5] = { "first", "second", "third", "fourth", "fifth" };
211 return "[unknown ordinal (too small)]";
213 return "[unknown ordinal (too big)]";
214 return ordinals[number-1];
217 std::string Note::describe(bool when, bool where, bool what) const
219 std::ostringstream result;
222 result << std::setw(6) << turn << " ";
226 result << "| " << chop_string(short_place_name(packed_place),
227 MAX_NOTE_PLACE_LEN) << " | ";
235 // [ds] Shortened HP change note from "Had X hitpoints" to
236 // accommodate the cause for the loss of hitpoints.
237 result << "HP: " << first << "/" << second
238 << " [" << name << "]";
240 case NOTE_XOM_REVIVAL:
241 result << "Xom revived you";
244 result << "Mana: " << first << "/" << second;
246 case NOTE_MAXHP_CHANGE:
247 result << "Reached " << first << " max hit points";
249 case NOTE_MAXMP_CHANGE:
250 result << "Reached " << first << " max mana";
252 case NOTE_XP_LEVEL_CHANGE:
253 result << "Reached XP level " << first << ". " << name;
255 case NOTE_DUNGEON_LEVEL_CHANGE:
259 result << "Entered " << place_name(packed_place, true, true);
261 case NOTE_LEARN_SPELL:
262 result << "Learned a level "
263 << spell_difficulty(static_cast<spell_type>(first))
265 << spell_title(static_cast<spell_type>(first));
268 result << "Became a worshipper of "
269 << god_name(static_cast<god_type>(first), true);
272 result << "Fell from the grace of "
273 << god_name(static_cast<god_type>(first));
276 result << "Was placed under penance by "
277 << god_name(static_cast<god_type>(first));
279 case NOTE_MOLLIFY_GOD:
280 result << "Was forgiven by "
281 << god_name(static_cast<god_type>(first));
284 result << "Received a gift from "
285 << god_name(static_cast<god_type>(first));
288 result << "Identified " << name;
290 result << " (" << desc << ")";
293 result << "Got " << name;
296 result << "Bought " << name << " for " << first << " gold piece"
297 << (first == 1 ? "" : "s");
299 case NOTE_DONATE_MONEY:
300 result << "Donated " << first << " gold piece"
301 << (first == 1 ? "" : "s") << " to Zin";
303 case NOTE_GAIN_SKILL:
304 result << "Reached skill level " << second
305 << " in " << skill_name(static_cast<skill_type>(first));
307 case NOTE_LOSE_SKILL:
308 result << "Reduced skill "
309 << skill_name(static_cast<skill_type>(first))
310 << " to level " << second;
312 case NOTE_SEEN_MONSTER:
313 result << "Noticed " << name;
315 case NOTE_DEFEAT_MONSTER:
317 result << name << " (ally) was " << desc;
319 result << uppercase_first(desc) << " " << name;
321 case NOTE_POLY_MONSTER:
322 result << name << " changed into " << desc;
325 result << "Acquired "
326 << god_name(static_cast<god_type>(first)) << "'s "
327 << _number_to_ordinal(_real_god_power(first, second)+1)
330 case NOTE_GET_MUTATION:
331 result << "Gained mutation: "
332 << mutation_name(static_cast<mutation_type>(first),
333 second == 0 ? 1 : second);
335 result << " [" << name << "]";
337 case NOTE_LOSE_MUTATION:
338 result << "Lost mutation: "
339 << mutation_name(static_cast<mutation_type>(first),
340 second == 3 ? 3 : second+1);
342 result << " [" << name << "]";
348 result << Options.user_note_prefix << name;
354 result << "Found " << name;
356 case NOTE_FEAT_MIMIC:
357 result << name <<" was a mimic.";
359 case NOTE_XOM_EFFECT:
360 result << "XOM: " << name;
361 #if defined(DEBUG_XOM) || defined(NOTE_DEBUG_XOM)
362 // If debugging, also take note of piety and tension.
363 result << " (piety: " << first;
365 result << ", tension: " << second;
370 result << "Paralysed by " << name << " for " << first << " turns";
372 case NOTE_NAMED_ALLY:
373 result << "Gained " << name << " as an ally";
375 case NOTE_ALLY_DEATH:
376 result << "Your ally " << name << " died";
379 result << "Buggy note description: unknown note type";
384 if (type == NOTE_SEEN_MONSTER || type == NOTE_DEFEAT_MONSTER)
386 if (what && first == MONS_PANDEMONIUM_LORD)
387 result << " the pandemonium lord";
394 turn = you.num_turns;
395 packed_place = get_packed_place();
398 Note::Note(NOTE_TYPES t, int f, int s, const char* n, const char* d) :
399 type(t), first(f), second(s)
402 name = std::string(n);
404 desc = std::string(d);
406 turn = you.num_turns;
407 packed_place = get_packed_place();
410 void Note::check_milestone() const
412 if (crawl_state.game_is_arena())
415 if (type == NOTE_DUNGEON_LEVEL_CHANGE)
417 const int br = place_branch(packed_place),
418 dep = place_depth(packed_place);
420 // Wizlabs report their milestones on their own.
421 if (br != -1 && br != BRANCH_WIZLAB)
423 ASSERT(br >= 0 && br < NUM_BRANCHES);
424 std::string branch = place_name(packed_place, true, false).c_str();
425 if (branch.find("The ") == 0)
426 branch[0] = tolower(branch[0]);
430 mark_milestone(br == BRANCH_ZIGGURAT ? "zig.enter" : "br.enter",
431 "entered " + branch + ".", true);
433 else if (dep == _dungeon_branch_depth(br) || dep == 14
434 || br == BRANCH_ZIGGURAT)
436 std::string level = place_name(packed_place, true, true);
437 if (level.find("Level ") == 0)
438 level[0] = tolower(level[0]);
440 std::ostringstream branch_finale;
441 branch_finale << "reached " << level << ".";
442 mark_milestone(br == BRANCH_ZIGGURAT ? "zig" :
443 dep == 14 ? "br.mid" : "br.end",
444 branch_finale.str());
450 void Note::save(writer& outf) const
452 marshallInt(outf, type);
453 marshallInt(outf, turn);
454 marshallShort(outf, packed_place);
455 marshallInt(outf, first);
456 marshallInt(outf, second);
457 marshallString4(outf, name);
458 marshallString4(outf, desc);
461 void Note::load(reader& inf)
463 type = static_cast<NOTE_TYPES>(unmarshallInt(inf));
464 turn = unmarshallInt(inf);
465 packed_place = unmarshallShort(inf);
466 first = unmarshallInt(inf);
467 second = unmarshallInt(inf);
468 unmarshallString4(inf, name);
469 unmarshallString4(inf, desc);
472 static bool notes_active = false;
474 bool notes_are_active()
479 void take_note(const Note& note, bool force)
481 if (notes_active && (force || _is_noteworthy(note)))
483 note_list.push_back(note);
484 note.check_milestone();
488 void activate_notes(bool active)
490 notes_active = active;
493 void save_notes(writer& outf)
495 marshallInt(outf, NOTES_VERSION_NUMBER);
496 marshallInt(outf, note_list.size());
497 for (unsigned i = 0; i < note_list.size(); ++i)
498 note_list[i].save(outf);
501 void load_notes(reader& inf)
503 if (unmarshallInt(inf) != NOTES_VERSION_NUMBER)
506 const int num_notes = unmarshallInt(inf);
507 for (int i = 0; i < num_notes; ++i)
511 note_list.push_back(new_note);
515 void make_user_note()
518 bool validline = !msgwin_get_line("Enter note: ", buf, sizeof(buf));
519 if (!validline || (!*buf))
521 Note unote(NOTE_USER_NOTE);