! "Player notepad" extension by David Fisher (version 1.0, November 23rd 2007) ! ! Copyright David Fisher, 2007 (davidfisher@australiaonline.net.au). ! ! This extension may be freely used, modified, archived and distributed ! as long as this copyright notice remains intact. ! ! An out-of-game notepad which the player can use to write and edit notes ! during a game. ! ! Note that this extension overrides the BeforeParsing and UnknownVerb ! entry points; if you override these routines in your game, delete the ! versions below and call nbBeforeParsing() or nbUnknownVerb() at the ! end of your routines. ! ! Type "notepad" for usage. ! [ BeforeParsing; nbBeforeParsing(); ]; [ UnknownVerb; return nbUnknownVerb(); ]; [ NotepadInfoSub; print "An inbuilt notepad may be used to take notes during the game. To add a new note, start a line with ~=~. For example:^^ @@32 =can't find the chicken ^^Use ~=~ on a line by itself (or ~notes~) to read your notes so far. ^^Each note is assigned a number, starting from 1. You can read an individual note by typing its number, or change it by following the number with ~=~ and the new text:^^ @@32 23=there is no chicken ^^Lastly, you can erase a note by saying ~erase~ or ~del/delete~ followed by its number (the rest of the notes are then renumbered).^^ To read these notes again, type ~notepad~.^"; ]; Verb meta 'notepad' 'notebook' * -> NotepadInfo; Verb meta 'notes' * -> ShowNotes; ! same thing as "=" on a line by itself Verb meta 'erase' 'delete' 'del' * -> DeleteNothing * number -> Delete * 'notes'/'notepad'/'notebook' -> DeleteAll * noun -> DeleteNoun; ! (used internally; player can't type this verb) Verb meta '.=' * -> Notepad * topic -> Notepad; ! size of notepad (number of characters + 1 byte per note) Constant NOTE_SIZE 4000; ! a warning is given if there is only this much space left in the notepad Constant WARN_REMAIN 100; ! the notes are all stored in a single array Array notes -> NOTE_SIZE + 1; Global note_end = 0; ! position of next note (length of notes array) Global note_num = 0; ! number of notes Global notepad_cmd = false; [ nbUnknownVerb; if (notepad_cmd) { return '.='; } rfalse; ]; [ nbBeforeParsing i j; notepad_cmd = false; j = bufferEndPos(); ! check for '=' or number for (i = WORDSIZE : i <= j : i++) { if (buffer->i ~= ' ') { break; } } if (i > j) { return; } if (buffer->i == '=' || isDigit(buffer->i)) { ! will be handled by the UnknownVerb entry point notepad_cmd = true; ! temporarily substitute dots and commas for (i++ : i <= j : i++) { ! will be undone in NotepadSub() if (buffer->i == '.') { buffer->i = 1; } else if (buffer->i == ',') { buffer->i = 2; } } } Tokenise__(buffer, parse); ]; [ NotepadSub i j n; j = bufferEndPos(); for (i = WORDSIZE : i <= j : i++) { if (buffer->i ~= ' ') { break; } } for (n = i : n <= j : n++) { ! undo changes done in BeforeParsing() if (buffer->n == 1) { buffer->n = '.'; } else if (buffer->n == 2) { buffer->n = ','; } } if (buffer->i == '=') { for (i++ : i <= j : i++) { if (buffer->i ~= ' ') { break; } } if (i > j) { nbList(); } else { nbAppend(i); } } else if (isDigit(buffer->i)) { nbNumber(i); } else { print "Sorry, that command was not understood.^"; } ]; [ ShowNotesSub; nbList(); ]; [ toUpper ch; if (ch >= 'a' && ch <= 'z') { return ch - 'a' + 'A'; } return ch; ]; ! lists all notes if n_only is 0 [ nbList n_only n index upper ch start; if (note_end == 0) { print "The notepad is empty.^"; return; } index = 1; upper = true; start = true; for (n = 0 : n < note_end : n++) { if (start) { if (n_only == 0 or index) { print index, ". "; } upper = true; start = false; } if (notes->n == 0) { if (n_only == 0 or index) { if (n > 0) { if (~~(notes->(n-1) == '.' or '!' or '?' || (n > 1 && notes->(n-1) == '"' && notes->(n-2) == '.' or '!' or '?'))) { print "."; } } new_line; } index++; start = true; } else { if (n_only == 0 or index) { ch = notes->n; ! ZCode makes everything lower case; start the sentence with ! an upper case letter if (upper && ch ~= '.' or '!' or '?' or ' ' or '"' or ''') { ch = toUpper(ch); upper = false; } print (char) ch; if (ch == '!' or '?') { upper = true; } else if (ch == '.') { if (n > 0 && notes->(n-1) == '.') { upper = false; } else { upper = true; } } } } } ]; [ notepadFullMsg; print "[The notepad is full. Try deleting unneeded entries.]^"; ]; [ nbAppend pos j len percent; j = bufferEndPos(); while (j >= 0 && j == ' ') { j--; } if (j < 0) { ! should never happen print "Sorry, there was a problem understanding the command.^"; return; } len = j - pos + 1; if (note_end + len + 1 > NOTE_SIZE) { notepadFullMsg(); return; } for ( : pos <= j : pos++) { notes->note_end = buffer->pos; note_end++; } notes->note_end = 0; note_end++; note_num++; print "Noted. [", note_num, "]"; if (NOTE_SIZE - note_end <= WARN_REMAIN) { ! avoid overflowing 16 bit numbers percent = 100 - ((NOTE_SIZE - note_end + (NOTE_SIZE / 100) - 1) * 100 / NOTE_SIZE); print " (", percent, "% full)"; } new_line; ]; [ isDigit ch; return (ch >= '0' && ch <= '9'); ]; [ nbNumber pos i j val; j = bufferEndPos(); for (i = pos : i <= j && isDigit(buffer->i) : i++) { if (val >= 10000) { print "Number too large.^"; return; } val = val * 10 + (buffer->i - '0'); } ! skip spaces for ( : i <= j && buffer->i == ' ' : i++) { } if (i > j) { if (checkVal(val)) { nbList(val); } } else if (buffer->i == '=') { if (checkVal(val, "To add a new entry, just say ~=~ followed by the text")) { replaceEntry(val, i + 1); } } else { print "Extra characters after command (did you mean to say ~", val, " = ...~?)^"; } ]; [ findNBEntry index n upto; upto = 1; for (n = 0 : n < note_end && upto < index : n++) { if (notes->n == 0) { upto++; } } if (n >= note_end) { print "[Internal error #1 in notepad.]^"; return -1; } return n; ]; ! find position of next zero byte in note array ! Returns -1 if goes past end of array [ findNextZeroPos pos; for ( : pos < note_end && notes->pos ~= 0 : pos++) { } if (pos >= note_end) { print "[Internal error #2 in notepad.]^"; return -1; } return pos; ]; [ bufferEndPos; return WORDSIZE + buffer->1 - 1; ]; [ replaceEntry index pos n n2 i j endpos len old_len; j = bufferEndPos(); ! skip spaces for ( : pos <= j && buffer->pos == ' ' : pos++) { } if (pos > j) { deleteEntry(index); return; } for (endpos = j : endpos >= pos && buffer->endpos == ' ' : endpos--) { } if (endpos < pos) { ! should be impossible, but just in case deleteEntry(index); return; } n = findNBEntry(index); if (n == -1) { return; } n2 = findNextZeroPos(n); if (n2 == -1) { return; } len = endpos - pos + 1; old_len = n2 - n; if (note_end + len - old_len > NOTE_SIZE) { notepadFullMsg(); return; } if (len > old_len) { moveNBup(n + old_len, len - old_len); } else if (len < old_len) { moveNBdown(n + old_len, old_len - len); } for (i = 0 : i < len : i++) { notes->(n+i) = buffer->(pos+i); } print "Replaced.^"; ]; [ moveNBup from_pos amount n; for (n = note_end - 1 : n >= from_pos : n--) { notes->(n+amount) = notes->n; } note_end = note_end + amount; ]; [ moveNBdown from_pos amount n; for (n = from_pos : n < note_end : n++) { notes->(n-amount) = notes->n; } note_end = note_end - amount; ]; [ checkVal val empty_msg_str; if (val <= 0) { print "The notepad is numbered from 1.^"; rfalse; } if (note_num == 0) { print "The notepad is empty.^"; if (empty_msg_str ~= nothing) { new_line; print (string) empty_msg_str; print ".^"; } rfalse; } if (val > note_num) { print "There "; if (note_num == 1) { print "is only one entry"; } else { print "are only ", (number) note_num, " entries"; } print " in the notepad.^"; rfalse; } rtrue; ]; [ DeleteNothingSub; if (note_num == 0) { print "The notepad is empty.^"; } else if (note_num == 1) { print "(notepad entry 1)^^"; deleteEntry(1); } else { print "You'll have to say which notepad entry to delete (1-", note_num, ").^"; } ]; [ DeleteAllSub; print "You can only erase one entry from the notepad at a time.^"; ]; [ DeleteNounSub; print "You can't delete ", (the) noun, "!^"; ]; [ DeleteSub index; index = noun; if (~~checkVal(index)) { return; } deleteEntry(index); ]; [ deleteEntry index n n2; n = findNBEntry(index); if (n == -1) { return; } n2 = findNextZeroPos(n); if (n2 == -1) { return; } moveNBdown(n2 + 1, n2 - n + 1); print "Deleted.^"; note_num--; ]; Object the_notepad "notepad" has scenery with name 'notepad' 'notebook' 'notes', found_in [; rtrue; ], before [; meta = true; ! (don't assume that Examine = Read or that there is a ##Read action) if (action == ##Examine || verb_word == 'read') { ShowNotesSub(); if (self.gave_tip == false) { self.gave_tip = true; print "^[Tip: you can just say ~=~ or ~notes~ to read your notes.]^"; } rtrue; } else { print "The notepad is not a physical object in the game, it is just a way to write notes as you play. Type ~notepad~ for more information.^"; } rtrue; ], gave_tip false;