summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/notes.cc
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/notes.cc')
-rw-r--r--crawl-ref/source/notes.cc320
1 files changed, 320 insertions, 0 deletions
diff --git a/crawl-ref/source/notes.cc b/crawl-ref/source/notes.cc
new file mode 100644
index 0000000000..922ae901d8
--- /dev/null
+++ b/crawl-ref/source/notes.cc
@@ -0,0 +1,320 @@
+#include <vector>
+
+#include "AppHdr.h"
+#include "notes.h"
+
+#include "files.h"
+#include "message.h"
+#include "mutation.h"
+#include "religion.h"
+#include "skills2.h"
+#include "spl-util.h"
+#include "stash.h"
+
+std::vector<Note> note_list;
+
+/* I can't believe I'm writing code this bad */
+static int real_god_power( int religion, int idx ) {
+ switch ( religion ) {
+ case GOD_NO_GOD:
+ case GOD_XOM:
+ case GOD_NEMELEX_XOBEH:
+ return -1;
+ case GOD_ZIN:
+ case GOD_SHINING_ONE:
+ case GOD_YREDELEMNUL:
+ case GOD_MAKHLEB:
+ case GOD_ELYVILON:
+ return idx;
+ case GOD_KIKUBAAQUDGHA:
+ if ( idx < 3 )
+ return idx;
+ if ( idx == 3 )
+ return -1;
+ return idx-1;
+ case GOD_VEHUMET:
+ return ( idx == 4 ? -1 : idx );
+ case GOD_OKAWARU:
+ if ( idx < 2 )
+ return idx;
+ if ( idx == 2 || idx == 3 )
+ return -1;
+ return idx - 2;
+ case GOD_SIF_MUNA:
+ if ( idx == 0 || idx == 2 || idx == 4 ) return -1;
+ if ( idx == 1 ) return 0;
+ if ( idx == 3 ) return 1;
+ case GOD_TROG:
+ if ( idx == 2 || idx == 4 ) return -1;
+ if ( idx < 2 ) return idx;
+ if ( idx == 3 ) return idx-1;
+ default:
+ return -1;
+ }
+}
+
+static bool is_noteworthy_skill_level( int level ) {
+ unsigned i;
+ for ( i = 0; i < Options.note_skill_levels.size(); ++i )
+ if ( level == Options.note_skill_levels[i] )
+ return true;
+ return false;
+}
+
+static bool is_highest_skill( int skill ) {
+ for ( int i = 0; i < NUM_SKILLS; ++i ) {
+ if ( i == skill )
+ continue;
+ if ( you.skills[i] >= you.skills[skill] )
+ return false;
+ }
+ return true;
+}
+
+static bool is_noteworthy_hp( int hp, int maxhp ) {
+ return (hp > 0 && Options.note_hp_percent &&
+ hp <= (maxhp * Options.note_hp_percent) / 100);
+}
+
+/* Is a note worth taking?
+ This function assumes that game state has not changed since
+ the note was taken, e.g. you.* is valid.
+ */
+static bool is_noteworthy( const Note& note ) {
+ /* should really make this more user-configurable */
+
+ /* always noteworthy */
+ if ( note.type == NOTE_XP_LEVEL_CHANGE ||
+ note.type == NOTE_GET_GOD ||
+ note.type == NOTE_GOD_GIFT ||
+ note.type == NOTE_GET_MUTATION ||
+ note.type == NOTE_LOSE_MUTATION ||
+ note.type == NOTE_KILL_MONSTER ||
+ note.type == NOTE_USER_NOTE ||
+ note.type == NOTE_LOSE_GOD )
+ return true;
+
+ /* never noteworthy, hooked up for fun or future use */
+ if ( note.type == NOTE_GET_ITEM ||
+ note.type == NOTE_MP_CHANGE ||
+ note.type == NOTE_MAXHP_CHANGE ||
+ note.type == NOTE_MAXMP_CHANGE )
+ return false;
+
+ /* god powers might be noteworthy if it's an actual power */
+ if ( note.type == NOTE_GOD_POWER &&
+ real_god_power(note.first, note.second) == -1 )
+ return false;
+
+ /* hp noteworthiness is handled in its own function */
+ if ( note.type == NOTE_HP_CHANGE &&
+ !is_noteworthy_hp(note.first, note.second) )
+ return false;
+
+ /* skills are noteworthy if in the skill value list or if
+ it's a new maximal skill (depending on options) */
+ if ( note.type == NOTE_GAIN_SKILL ) {
+ if ( is_noteworthy_skill_level(note.second) )
+ return true;
+ if ( Options.note_skill_max && is_highest_skill(note.first) )
+ return true;
+ return false;
+ }
+
+ /* entering dlevel 1 is not noteworthy */
+ if ( note.type == NOTE_DUNGEON_LEVEL_CHANGE &&
+ note.first == BRANCH_MAIN_DUNGEON && note.second == 1 )
+ return false;
+
+ unsigned i;
+ for ( i = 0; i < note_list.size(); ++i ) {
+ if ( note_list[i].type != note.type )
+ continue;
+ const Note& rnote( note_list[i] );
+ switch ( note.type ) {
+ case NOTE_DUNGEON_LEVEL_CHANGE:
+ if ( rnote.first == note.first && rnote.second == note.second &&
+ !(note.first == LEVEL_LABYRINTH && note.second == 0xFF) )
+ return false;
+ break;
+ case NOTE_LEARN_SPELL:
+ if (spell_difficulty(rnote.first) >= spell_difficulty(note.first))
+ return false;
+ break;
+ case NOTE_GOD_POWER:
+ if ( rnote.first == note.first && rnote.second == note.second )
+ return false;
+ break;
+ case NOTE_ID_ITEM:
+ /* re-id'ing an item, e.g. second copy of book, isn't
+ noteworthy */
+ if ( rnote.name == note.name )
+ return false;
+ break;
+ case NOTE_HP_CHANGE:
+ /* not if we have a recent warning */
+ if ( (note.turn - rnote.turn < 5) &&
+ /* unless we've lost half our HP since then */
+ (note.first * 2 >= rnote.first) )
+ return false;
+ break;
+ default:
+ mpr("Buggy note passed");
+ break;
+ }
+ }
+ return true;
+}
+
+const char* number_to_ordinal( int number ) {
+ const char* ordinals[5] = { "first", "second", "third", "fourth",
+ "fifth" };
+ if ( number < 1)
+ return "[unknown ordinal (too small)]";
+ if ( number > 5 )
+ return "[unknown ordinal (too big)]";
+ return ordinals[number-1];
+}
+
+std::string describe_note( const Note& note ) {
+ char buf[200], buf2[50];
+ switch ( note.type ) {
+ case NOTE_HP_CHANGE:
+ sprintf(buf, "Had %d/%d hit points", note.first, note.second);
+ break;
+ case NOTE_MP_CHANGE:
+ sprintf(buf, "Had %d/%d mana", note.first, note.second);
+ break;
+ case NOTE_MAXHP_CHANGE:
+ sprintf(buf, "Reached %d max hit points", note.first);
+ break;
+ case NOTE_MAXMP_CHANGE:
+ sprintf(buf, "Reached %d max mana", note.first);
+ break;
+ case NOTE_XP_LEVEL_CHANGE:
+ sprintf(buf, "Reached XP level %d. %s", note.first, note.name.c_str());
+ break;
+ case NOTE_DUNGEON_LEVEL_CHANGE:
+ sprintf(buf, "Entered %s",
+ branch_level_name(note.first, note.second).c_str());
+ break;
+ case NOTE_LEARN_SPELL:
+ sprintf(buf, "Learned a level %d spell: %s",
+ spell_difficulty(note.first), spell_title(note.first));
+ break;
+ case NOTE_GET_GOD:
+ sprintf(buf, "Became a worshipper of %s",
+ god_name(note.first, true));
+ break;
+ case NOTE_LOSE_GOD:
+ sprintf(buf, "Fell from the grace of %s",
+ god_name(note.first));
+ break;
+ case NOTE_GOD_GIFT:
+ sprintf(buf, "Received a gift from %s",
+ god_name(note.first));
+ break;
+ case NOTE_ID_ITEM:
+ sprintf(buf, "Identified %s", note.name.c_str());
+ break;
+ case NOTE_GET_ITEM:
+ sprintf(buf, "Got %s", note.name.c_str());
+ break;
+ case NOTE_GAIN_SKILL:
+ sprintf(buf, "Reached skill %d in %s",
+ note.second, skill_name(note.first));
+ break;
+ case NOTE_KILL_MONSTER:
+ sprintf(buf, "Defeated %s", note.name.c_str());
+ break;
+ case NOTE_GOD_POWER:
+ sprintf(buf, "Acquired %s's %s power", god_name(note.first),
+ number_to_ordinal(real_god_power(note.first, note.second)+1));
+ break;
+ case NOTE_GET_MUTATION:
+ sprintf(buf, "Gained mutation: %s",
+ mutation_name(note.first, note.second == 0 ? 1 : note.second));
+ break;
+ case NOTE_LOSE_MUTATION:
+ sprintf(buf, "Lost mutation: %s",
+ mutation_name(note.first,
+ note.second == 3 ? 3 : note.second+1));
+ break;
+ case NOTE_USER_NOTE:
+ sprintf(buf, "%s", note.name.c_str());
+ break;
+ default:
+ sprintf(buf, "Buggy note description");
+ break;
+ }
+ sprintf(buf2, "Turn %ld: ", note.turn );
+ return std::string(buf2) + std::string(buf);
+}
+
+Note::Note() {
+ turn = you.num_turns;
+}
+
+Note::Note( NOTE_TYPES t, int f, int s, const char* n ) :
+ type(t), first(f), second(s) {
+ if (n)
+ name = std::string(n);
+ turn = you.num_turns;
+}
+
+void Note::save( FILE* fp ) const {
+ writeLong( fp, type );
+ writeLong( fp, turn );
+ writeLong( fp, first );
+ writeLong( fp, second );
+ writeString( fp, name );
+}
+
+void Note::load( FILE* fp ) {
+ type = (NOTE_TYPES)(readLong( fp ));
+ turn = readLong( fp );
+ first = readLong( fp );
+ second = readLong( fp );
+ name = readString( fp );
+}
+
+bool notes_active = false;
+
+bool notes_are_active() {
+ return notes_active;
+}
+
+void take_note( const Note& note ) {
+ if ( notes_active && is_noteworthy( note ) )
+ note_list.push_back( note );
+}
+
+void activate_notes( bool active ) {
+ notes_active = active;
+}
+
+void save_notes( FILE* fp ) {
+ writeLong( fp, note_list.size() );
+ for ( unsigned i = 0; i < note_list.size(); ++i )
+ note_list[i].save(fp);
+}
+
+void load_notes( FILE* fp ) {
+ long num_notes = readLong(fp);
+ for ( long i = 0; i < num_notes; ++i ) {
+ Note new_note;
+ new_note.load(fp);
+ note_list.push_back(new_note);
+ }
+}
+
+void make_user_note() {
+ mpr("Enter note: ", MSGCH_PROMPT);
+ char buf[400];
+ bool validline = cancelable_get_line(buf, sizeof(buf));
+ if ( !validline || (!*buf) )
+ return;
+ Note unote(NOTE_USER_NOTE);
+ unote.name = std::string(buf);
+ take_note(unote);
+}