#!/usr/bin/perl $release = 8; # release 8 # # SLAG -- a Z-machine hint system # # Written by Brian 'Beej' Hall, 1999 # I hereby grant this source to the public domain. # # REQUIRES that the Inform compiler be installed on your system # # Run with the "-s" flag to generate code you can bundle into your own # programs. # # To build a complete hint file: # # slag hint.slg hint.inf # inform hint.inf # # Homepage: http://www.piratehaven.org/~beej/slag/ # $strip = 0; $quiet = 0; while(($arg = shift(@ARGV))) { if ($arg eq "-s") { $strip = 1; } elsif ($arg eq "-q") { $quiet = 1; } elsif (!defined($infile)) { $infile = $arg; } elsif (!defined($outfile)) { $outfile = $arg; } else { &usage(); } } if (!defined($infile) || !defined($outfile)) { &usage(); } open(ZHS, "<$infile") || die "slag: $infile: $!\n"; open(OUT, ">$outfile") || die "slag: $outfile: $!\n"; $fixed = 0; # not fixed mode to start $state = 0; # not in anything $cur_menu_item = 0; $line = 0; $have_intro = 0; $counter = 0; &write_boiler(); while() { $line++; chop(); tr/\"/~/; if (/^\.INTRO/) { &intro_head(); $state = 1; # INTRO $have_intro = 1; } elsif (/^\.MENU\s+(.*)/) { $menu_name = normalize($1); if ($state == 2) { # MENU &menu_foot(); } elsif ($state == 1) { # INTRO &intro_foot(); } elsif ($state == 3) { # HINT &hint_foot(); } elsif ($state == 4) { # TEXT &text_foot(); } $state = 2; &menu_head($menu_name, lead_strip($1)); if (!defined($main_link_name)) { $main_link_name = $menu_name; } } elsif (/^\.LINK\s+(.*)/) { $link_name = normalize($1); if ($state != 2) { # MENU errorexit("found LINK outside MENU"); } if ($cur_menu_item == 0) { print OUT "\t\ttext"; } print OUT "\n\t\t\t\"", lead_strip($1), "\""; $item[$cur_menu_item] = $link_name; $cur_menu_item++; } elsif (/^\.HINT\s+(.*)/) { $link_name = normalize($1); if ($state == 1) { #INTRO &intro_foot(); } elsif ($state == 2) { #MENU &menu_foot(); } elsif ($state == 3) { # HINT &hint_foot(); } elsif ($state == 4) { # TEXT &text_foot(); } $state = 3; &hint_head($link_name, lead_strip($1)); } elsif (/^\.CLUE\s+(.*)/) { if ($state != 3) { #HINT errorexit("found CLUE outside HINT"); } print OUT "\n\t\t\t\"$1\""; } elsif (/^\.FIX/) { if ($state != 1 && $state != 4) { # !INTRO && !TEXT errorexit("found FIX outside INTRO or TEXT"); } $fixed = 1; print OUT "\tfont off;\n"; } elsif (/^\.UNFIX/) { if ($state != 1 && $state != 4) { # !INTRO && !TEXT errorexit("found UNFIX outside INTRO or TEXT"); } $fixed = 0; print OUT "\tfont on;\n"; } elsif (/^\.TEXT\s+(.*)/) { $text_name = &normalize($1); if ($state == 1) { #INTRO &intro_foot(); } elsif ($state == 2) { #MENU &menu_foot(); } elsif ($state == 3) { # HINT &hint_foot(); } elsif ($state == 4) { # TEXT &text_foot(); } $state = 4; &text_head($text_name, lead_strip($1)); } elsif (/^\.CENTER\s+(.*)/) { if ($state != 1 && $state != 4) { errorexit("CENTER must be in TEXT or INTRO"); } print OUT "\tSLAG_Center(\"$1\", ", length($1), ");\n"; } elsif (/^\.ENDTEXT/) { if ($state != 4) { errorexit("found ENDTEXT outside TEXT"); } &text_foot(); $state = 0; } elsif (/^\.ENDINTRO/) { if ($state != 1) { errorexit("found ENDINTRO outside INTRO"); } &intro_foot(); $state = 0; } elsif (/^\.END/) { last; } else { if ($state == 4 || $state == 1) { # TEXT or INTRO if ($fixed == 0) { if (/^$/) { print OUT "\tprint \"^^\";\n"; } else { print OUT "\tprint \"$_"; if (substr($_, -1) ne "^") { print OUT " "; } print OUT "\";\n"; } } else { print OUT "\tprint \"$_^\";\n"; } } } } if ($state == 1) { #INTRO &intro_foot(); } elsif ($state == 2) { #MENU &menu_foot(); } elsif ($state == 3) { # HINT &hint_foot(); } elsif ($state == 4) { # TEXT &text_foot(); } if ($strip == 0) { print OUT "[ Main;\n"; if ($have_intro == 1) { print OUT "\tSLAG_Intro();\n"; } print OUT "\tSLAG_RunMenu($main_link_name, 0);\n"; print OUT "\t\@erase_window \$ffff;\n"; print OUT "];\n"; } else { print OUT "[ SLAG_RunMenus;\n"; if ($have_intro == 1) { print OUT "\tSLAG_Intro();\n"; } print OUT "\tSLAG_RunMenu($main_link_name, 0);\n"; print OUT "\t\@set_window 0;\n"; print OUT "\t\@erase_window \$ffff;\n"; print OUT "\tprint \"^\";\n"; print OUT "\t<>;\n"; print OUT "];\n"; if ($quiet != 1) { print "Code the following line in your source to call the menus:\n\n"; print " SLAG_RunMenus();\n\n"; } } exit(0); ############### # END OF MAIN # ############### # # intro # sub intro_head { print OUT <<_EOF_; [ SLAG_Intro pkey; \@erase_window \$ffff; _EOF_ } sub intro_foot { print OUT <<_EOF_; print "^[press any key]"; \@read_char 1 -> pkey; ]; _EOF_ } # # menu # sub menu_head { local($normal_name, $title) = @_; print OUT "SLAG_Menu $normal_name\n\twith\n"; print OUT "\t\ttitle \"$title\",\n"; } sub menu_foot { local($i); print OUT ",\n"; print OUT "\t\titem"; for($i = 0; $i < $cur_menu_item; $i++) { print OUT "\n\t\t\t$item[$i]"; } print OUT ";\n\n"; $cur_menu_item = 0; } # # hint # sub hint_head { local($normal_name, $title) = @_; print OUT "SLAG_Hint $normal_name\n\twith\n"; print OUT "\t\ttitle \"$title\",\n"; print OUT "\t\ttext"; } sub hint_foot { print OUT ";\n\n"; } # # text # sub text_head { local($normal_name, $title) = @_; print OUT "SLAG_Other $normal_name\n\twith\n"; print OUT "\t\ttitle \"$title\",\n"; print OUT "\t\trun\n\t\t[;\n"; } sub text_foot { print OUT "\t\t];\n\n"; } # # write_boiler # # writes out boilerplate code needed for everything. # sub write_boiler { print OUT "Switches d2;\n\n" if $strip == 0; print OUT<<_EOF_; ! ! Inform source generated by SLAG release $release ! ! http://www.piratehaven.org/~beej/slag/ ! Class SLAG_Menu with title, text, item; Class SLAG_Hint with title, text; Class SLAG_Other with title, run; SLAG_Menu Dummy_Menu; SLAG_Hint Dummy_Hint; SLAG_Other Dummy_Other; [ SLAG_DrawHeader title showmenu j; \@erase_window \$ffff; \@split_window 1; \@set_window 1; \@set_cursor 1 1; style reverse; j = 0->33; if (j == 0) j = 80; spaces(j); \@set_cursor 1 2; print (string)title; if (showmenu > 0) { j = j - 31; \@set_cursor 1 j; print "N=Next P=Prev ENTER=Read Q="; if (showmenu == 1) print "Quit"; else print "Back"; } style roman; ]; [ SLAG_RunMenu _m nest i j count cur key target redraw_needed; redraw_needed = 1; cur = 0; count = _m.#text / 2; for(::) { if (redraw_needed) { SLAG_DrawHeader(_m.title, nest+1); redraw_needed = 0; } for(i = 0: i < count: i++) { j = i + 3; \@set_cursor j 5; print (string)_m.&text-->i; } j = cur + 3; \@set_cursor j 2; print ">"; \@set_cursor j 2; \@read_char 1 -> key; print " "; switch(key) { 'k', 'p', '-', '_', 'e', 129: cur--; if (cur < 0) cur = count-1; break; 'j', 'n', '=', '+', 'x', 130: cur++; if (cur >= count) cur = 0; break; 'q', 'Q', 's', 27, 131, 10, 8: return; 132, 13, 'd', ' ': target = _m.&item-->cur; if (target ofclass SLAG_Menu) SLAG_RunMenu(target, nest+1); else if (target ofclass SLAG_Hint) SLAG_RunHint(target); else SLAG_RunOther(target); redraw_needed = 1; break; } } ]; [ SLAG_RunHint _h count cur key done; count = _h.#text / 2; cur = 0; done = 0; SLAG_DrawHeader(_h.title, 0); \@set_window 0; print "^[press 'H' for a hint (", count, " total)"; print ", or 'Q' to stop]^^"; do { \@read_char 1 -> key; switch(key) { 'h', 'H': cur++; print "(", cur, "/", count, ") ", (string)_h.&text-->(cur-1), "^^"; break; 'q', 'Q', 27, 131, 10, 8: return; } } until(cur >= count); print "[press any key to continue]"; \@read_char 1 -> key; ]; [ SLAG_RunOther _o key; SLAG_Center(0); ! dummy call to Center() to shut up the compiler warnings SLAG_DrawHeader(_o.title, 0); \@set_window 0; print "^"; _o.run(); print "^[press any key to continue]"; \@read_char 1 -> key; ]; [ SLAG_Center s len wid off; if (s == 0) return; wid = 0->33; if (wid == 0) wid = 80; off = (wid - len) / 2; spaces(off); print (string)s, "^"; ]; _EOF_ } # # normalizes a name # sub normalize { local($name) = @_; local($normal); $name =~ s/\s+//g; $name =~ tr/A-Z/a-z/; $name =~ tr/a-zA-Z0-9_//cd; if (defined($normal_lookup{$name})) { return $normal_lookup{$name}; } $normal = "SLAG_link_$counter"; $counter++; $normal_lookup{$name} = $normal; return $normal; } # # errorexit # sub errorexit { local($str) = @_; print STDERR "slag: $infile line $line: $str\n"; exit(1); } # # usage # sub usage { print STDERR "usage: slag [-s] [-q] infile.slg outfile.inf\n"; print STDERR " -s Strip code (if you want to use menus in Inform)\n"; print STDERR " -q Quiet (don't write to standard output, ever)\n"; exit(1); } # # lead_strip # sub lead_strip { local($str) = @_; $str =~ s/^[0-9]+:(.*)/$1/; return $str; }