! "Player notepad" extension by David Fisher (version 1.0, November 23rd 2007)
!
! Copyright David Fisher, 2007 (
[email protected]).
!
! 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;