3 * Summary: Notetaking stuff
4 * Written by: Haran Pilpel
30 #define NOTES_VERSION_NUMBER 1002
32 std::vector
<Note
> note_list
;
34 // return the real number of the power (casting out nonexistent powers),
35 // starting from 0, or -1 if the power doesn't exist
36 static int _real_god_power(int religion
, int idx
)
38 if (god_gain_power_messages
[religion
][idx
][0] == 0)
42 for (int j
= 0; j
< idx
; ++j
)
43 if (god_gain_power_messages
[religion
][j
][0])
49 static bool _is_noteworthy_skill_level(int level
)
51 for (unsigned int i
= 0; i
< Options
.note_skill_levels
.size(); ++i
)
52 if (level
== Options
.note_skill_levels
[i
])
58 static bool _is_highest_skill(int skill
)
60 for (int i
= 0; i
< NUM_SKILLS
; ++i
)
64 if (you
.skills
[i
] >= you
.skills
[skill
])
70 static bool _is_noteworthy_hp(int hp
, int maxhp
)
72 return (hp
> 0 && Options
.note_hp_percent
73 && hp
<= (maxhp
* Options
.note_hp_percent
) / 100);
76 static int _dungeon_branch_depth(uint8_t branch
)
78 if (branch
>= NUM_BRANCHES
)
80 return branches
[branch
].depth
;
83 static bool _is_noteworthy_dlevel(unsigned short place
)
85 const uint8_t branch
= (place
>> 8) & 0xFF;
86 const int lev
= (place
& 0xFF);
88 // Special levels (Abyss, etc.) are always interesting.
92 if (lev
== _dungeon_branch_depth(branch
)
93 || branch
== BRANCH_MAIN_DUNGEON
&& (lev
% 5) == 0
94 || branch
!= BRANCH_MAIN_DUNGEON
&& lev
== 1)
102 // Is a note worth taking?
103 // This function assumes that game state has not changed since
104 // the note was taken, e.g. you.* is valid.
105 static bool _is_noteworthy(const Note
& note
)
107 // Always noteworthy.
108 if (note
.type
== NOTE_XP_LEVEL_CHANGE
109 || note
.type
== NOTE_GET_GOD
110 || note
.type
== NOTE_GOD_GIFT
111 || note
.type
== NOTE_GET_MUTATION
112 || note
.type
== NOTE_LOSE_MUTATION
113 || note
.type
== NOTE_GET_ITEM
114 || note
.type
== NOTE_ID_ITEM
115 || note
.type
== NOTE_BUY_ITEM
116 || note
.type
== NOTE_DONATE_MONEY
117 || note
.type
== NOTE_SEEN_MONSTER
118 || note
.type
== NOTE_KILL_MONSTER
119 || note
.type
== NOTE_POLY_MONSTER
120 || note
.type
== NOTE_USER_NOTE
121 || note
.type
== NOTE_MESSAGE
122 || note
.type
== NOTE_LOSE_GOD
123 || note
.type
== NOTE_PENANCE
124 || note
.type
== NOTE_MOLLIFY_GOD
125 || note
.type
== NOTE_DEATH
126 || note
.type
== NOTE_XOM_REVIVAL
127 || note
.type
== NOTE_SEEN_FEAT
)
132 // Never noteworthy, hooked up for fun or future use.
133 if (note
.type
== NOTE_MP_CHANGE
134 || note
.type
== NOTE_MAXHP_CHANGE
135 || note
.type
== NOTE_MAXMP_CHANGE
)
140 // Xom effects are only noteworthy if the option is true.
141 if (note
.type
== NOTE_XOM_EFFECT
)
142 return (Options
.note_xom_effects
);
144 // God powers might be noteworthy if it's an actual power.
145 if (note
.type
== NOTE_GOD_POWER
146 && _real_god_power(note
.first
, note
.second
) == -1)
151 // HP noteworthiness is handled in its own function.
152 if (note
.type
== NOTE_HP_CHANGE
153 && !_is_noteworthy_hp(note
.first
, note
.second
))
158 // Skills are noteworthy if in the skill value list or if
159 // it's a new maximal skill (depending on options).
160 if (note
.type
== NOTE_GAIN_SKILL
|| note
.type
== NOTE_LOSE_SKILL
)
162 if (Options
.note_all_skill_levels
163 || _is_noteworthy_skill_level(note
.second
)
164 || Options
.note_skill_max
&& _is_highest_skill(note
.first
))
171 if (note
.type
== NOTE_DUNGEON_LEVEL_CHANGE
)
173 if (!_is_noteworthy_dlevel(note
.packed_place
))
176 // Labyrinths and portal vaults are always interesting.
177 if ((note
.packed_place
& 0xFF) == 0xFF
178 && ((note
.packed_place
>> 8) == LEVEL_LABYRINTH
179 || (note
.packed_place
>> 8) == LEVEL_PORTAL_VAULT
))
185 // Learning a spell is always noteworthy if note_all_spells is set.
186 if (note
.type
== NOTE_LEARN_SPELL
&& Options
.note_all_spells
)
189 for (unsigned i
= 0; i
< note_list
.size(); ++i
)
191 if (note_list
[i
].type
!= note
.type
)
194 const Note
& rnote(note_list
[i
]);
197 case NOTE_DUNGEON_LEVEL_CHANGE
:
198 if (rnote
.packed_place
== note
.packed_place
)
202 case NOTE_LEARN_SPELL
:
203 if (spell_difficulty(static_cast<spell_type
>(rnote
.first
))
204 >= spell_difficulty(static_cast<spell_type
>(note
.first
)))
211 if (rnote
.first
== note
.first
&& rnote
.second
== note
.second
)
216 // Not if we have a recent warning
217 // unless we've lost half our HP since then.
218 if (note
.turn
- rnote
.turn
< 5
219 && note
.first
* 2 >= rnote
.first
)
226 mpr("Buggy note passed: unknown note type");
227 // Return now, rather than give a "Buggy note passed" message
228 // for each note of the matching type in the note list.
235 static const char* _number_to_ordinal(int number
)
237 const char* ordinals
[5] = { "first", "second", "third", "fourth", "fifth" };
240 return "[unknown ordinal (too small)]";
242 return "[unknown ordinal (too big)]";
243 return ordinals
[number
-1];
246 std::string
Note::describe(bool when
, bool where
, bool what
) const
249 std::ostringstream result
;
252 result
<< std::setw(6) << turn
<< " ";
256 if (!place_abbrev
.empty())
257 result
<< "| " << std::setw(MAX_NOTE_PLACE_LEN
) << std::left
258 << place_abbrev
<< " | ";
260 result
<< "| " << std::setw(MAX_NOTE_PLACE_LEN
) << std::left
261 << short_place_name(packed_place
) << " | ";
269 // [ds] Shortened HP change note from "Had X hitpoints" to
270 // accommodate the cause for the loss of hitpoints.
271 result
<< "HP: " << first
<< "/" << second
272 << " [" << name
<< "]";
274 case NOTE_XOM_REVIVAL
:
275 result
<< "Xom revived you";
278 result
<< "Mana: " << first
<< "/" << second
;
280 case NOTE_MAXHP_CHANGE
:
281 result
<< "Reached " << first
<< " max hit points";
283 case NOTE_MAXMP_CHANGE
:
284 result
<< "Reached " << first
<< " max mana";
286 case NOTE_XP_LEVEL_CHANGE
:
287 result
<< "Reached XP level " << first
<< ". " << name
;
289 case NOTE_DUNGEON_LEVEL_CHANGE
:
293 result
<< "Entered " << place_name(packed_place
, true, true);
295 case NOTE_LEARN_SPELL
:
296 result
<< "Learned a level "
297 << spell_difficulty(static_cast<spell_type
>(first
))
299 << spell_title(static_cast<spell_type
>(first
));
302 result
<< "Became a worshipper of "
303 << god_name(static_cast<god_type
>(first
), true);
306 result
<< "Fell from the grace of "
307 << god_name(static_cast<god_type
>(first
));
310 result
<< "Was placed under penance by "
311 << god_name(static_cast<god_type
>(first
));
313 case NOTE_MOLLIFY_GOD
:
314 result
<< "Was forgiven by "
315 << god_name(static_cast<god_type
>(first
));
318 result
<< "Received a gift from "
319 << god_name(static_cast<god_type
>(first
));
322 result
<< "Identified " << name
;
324 result
<< " (" << desc
<< ")";
327 result
<< "Got " << name
;
330 result
<< "Bought " << name
<< " for " << first
<< " gold piece"
331 << (first
== 1 ? "" : "s");
333 case NOTE_DONATE_MONEY
:
334 result
<< "Donated " << first
<< " gold piece"
335 << (first
== 1 ? "" : "s") << " to Zin";
337 case NOTE_GAIN_SKILL
:
338 result
<< "Reached skill level " << second
339 << " in " << skill_name(static_cast<skill_type
>(first
));
341 case NOTE_LOSE_SKILL
:
342 result
<< "Reduced skill "
343 << skill_name(static_cast<skill_type
>(first
))
344 << " to level " << second
;
346 case NOTE_SEEN_MONSTER
:
347 result
<< "Noticed " << name
;
349 case NOTE_KILL_MONSTER
:
351 result
<< name
<< " (ally) was defeated";
353 result
<< "Defeated " << name
;
355 case NOTE_POLY_MONSTER
:
356 result
<< name
<< " changed into " << desc
;
359 result
<< "Acquired "
360 << god_name(static_cast<god_type
>(first
)) << "'s "
361 << _number_to_ordinal(_real_god_power(first
, second
)+1)
364 case NOTE_GET_MUTATION
:
365 result
<< "Gained mutation: "
366 << mutation_name(static_cast<mutation_type
>(first
),
367 second
== 0 ? 1 : second
);
369 case NOTE_LOSE_MUTATION
:
370 result
<< "Lost mutation: "
371 << mutation_name(static_cast<mutation_type
>(first
),
372 second
== 3 ? 3 : second
+1);
378 result
<< Options
.user_note_prefix
<< name
;
384 result
<< "Found " << name
;
386 case NOTE_XOM_EFFECT
:
387 result
<< "XOM: " << name
;
388 #if defined(DEBUG_XOM) || defined(NOTE_DEBUG_XOM)
389 // If debugging, also take note of piety and tension.
390 result
<< " (piety: " << first
;
392 result
<< ", tension: " << second
;
397 result
<< "Buggy note description: unknown note type";
402 if (type
== NOTE_SEEN_MONSTER
|| type
== NOTE_KILL_MONSTER
)
404 if (what
&& first
== MONS_PANDEMONIUM_DEMON
)
405 result
<< " the pandemonium lord";
412 turn
= you
.num_turns
;
413 packed_place
= get_packed_place();
415 if (you
.level_type
== LEVEL_PORTAL_VAULT
)
416 place_abbrev
= you
.level_type_name_abbrev
;
419 Note::Note(NOTE_TYPES t
, int f
, int s
, const char* n
, const char* d
) :
420 type(t
), first(f
), second(s
), place_abbrev("")
423 name
= std::string(n
);
425 desc
= std::string(d
);
427 turn
= you
.num_turns
;
428 packed_place
= get_packed_place();
430 if (you
.level_type
== LEVEL_PORTAL_VAULT
)
431 place_abbrev
= you
.level_type_name_abbrev
;
434 void Note::check_milestone() const
436 if (crawl_state
.game_is_arena())
439 if (type
== NOTE_DUNGEON_LEVEL_CHANGE
)
441 const int br
= place_branch(packed_place
),
442 dep
= place_depth(packed_place
);
446 std::string branch
= place_name(packed_place
, true, false).c_str();
447 if (branch
.find("The ") == 0)
448 branch
[0] = tolower(branch
[0]);
451 mark_milestone("br.enter", "entered " + branch
+ ".", true);
452 else if (dep
== _dungeon_branch_depth(br
))
454 std::string level
= place_name(packed_place
, true, true);
455 if (level
.find("Level ") == 0)
456 level
[0] = tolower(level
[0]);
458 std::ostringstream branch_finale
;
459 branch_finale
<< "reached " << level
<< ".";
460 mark_milestone("br.end", branch_finale
.str());
466 void Note::save(writer
& outf
) const
468 marshallInt(outf
, type
);
469 marshallInt(outf
, turn
);
470 marshallShort(outf
, packed_place
);
471 marshallInt(outf
, first
);
472 marshallInt(outf
, second
);
473 marshallString4(outf
, name
);
474 marshallString4(outf
, place_abbrev
);
475 marshallString4(outf
, desc
);
478 void Note::load(reader
& inf
)
480 type
= static_cast<NOTE_TYPES
>(unmarshallInt(inf
));
481 turn
= unmarshallInt(inf
);
482 packed_place
= unmarshallShort(inf
);
483 first
= unmarshallInt(inf
);
484 second
= unmarshallInt(inf
);
485 unmarshallString4(inf
, name
);
486 unmarshallString4(inf
, place_abbrev
);
487 unmarshallString4(inf
, desc
);
490 bool notes_active
= false;
492 bool notes_are_active()
494 return (notes_active
);
497 void take_note(const Note
& note
, bool force
)
499 if (notes_active
&& (force
|| _is_noteworthy(note
)))
501 note_list
.push_back(note
);
502 note
.check_milestone();
506 void activate_notes(bool active
)
508 notes_active
= active
;
511 void save_notes(writer
& outf
)
513 marshallInt(outf
, NOTES_VERSION_NUMBER
);
514 marshallInt(outf
, note_list
.size());
515 for (unsigned i
= 0; i
< note_list
.size(); ++i
)
516 note_list
[i
].save(outf
);
519 void load_notes(reader
& inf
)
521 if (unmarshallInt(inf
) != NOTES_VERSION_NUMBER
)
524 const int num_notes
= unmarshallInt(inf
);
525 for (long i
= 0; i
< num_notes
; ++i
)
529 note_list
.push_back(new_note
);
533 void make_user_note()
536 bool validline
= !msgwin_get_line("Enter note: ", buf
, sizeof(buf
));
537 if (!validline
|| (!*buf
))
539 Note
unote(NOTE_USER_NOTE
);