<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.dungeoncrawlchili.org/index.php?action=history&amp;feed=atom&amp;title=List_of_spells_parse-spl-data</id>
	<title>List of spells parse-spl-data - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.dungeoncrawlchili.org/index.php?action=history&amp;feed=atom&amp;title=List_of_spells_parse-spl-data"/>
	<link rel="alternate" type="text/html" href="https://wiki.dungeoncrawlchili.org/index.php?title=List_of_spells_parse-spl-data&amp;action=history"/>
	<updated>2026-08-03T11:13:40Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.8</generator>
	<entry>
		<id>https://wiki.dungeoncrawlchili.org/index.php?title=List_of_spells_parse-spl-data&amp;diff=2162&amp;oldid=prev</id>
		<title>Maintenance script: Importing CrawlWiki content</title>
		<link rel="alternate" type="text/html" href="https://wiki.dungeoncrawlchili.org/index.php?title=List_of_spells_parse-spl-data&amp;diff=2162&amp;oldid=prev"/>
		<updated>2026-06-16T09:12:49Z</updated>

		<summary type="html">&lt;p&gt;Importing CrawlWiki content&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{delete|No longer used}}&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;nowiki&amp;gt;&lt;br /&gt;
#! /usr/bin/perl -w&lt;br /&gt;
# parse-spl-data, by http://crawl.chaosforge.org/index.php?title=User:Neil&lt;br /&gt;
# Copyright (C) 2011.  No rights reserved.&lt;br /&gt;
#&lt;br /&gt;
# You may use, distribute, modify, study, fold, spindle, or mutilate this&lt;br /&gt;
# software as you see fit, but know that there is NO WARRANTY, EXPRESS&lt;br /&gt;
# OR IMPLIED (to the extent permitted by law).&lt;br /&gt;
#&lt;br /&gt;
# The latest version of this program may be found at:&lt;br /&gt;
#  http://crawl.chaosforge.org/List_of_spells/parse-spl-data&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use Getopt::Long qw(:config gnu_getopt);&lt;br /&gt;
&lt;br /&gt;
our ($PROGNAME, $DATE, $CRAWL_VERSION);&lt;br /&gt;
our $VERSION = &amp;quot;0.1.0&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub crawl_version {&lt;br /&gt;
        my ($indir) = @_;&lt;br /&gt;
	# Look for util/release_ver first&lt;br /&gt;
	if (open VERSION, &amp;quot;&amp;lt;&amp;quot;, &amp;quot;${indir}util/release_ver&amp;quot;) {&lt;br /&gt;
		my $ver = &amp;lt;VERSION&amp;gt;;&lt;br /&gt;
		chomp $ver;&lt;br /&gt;
		close VERSION;&lt;br /&gt;
		return $ver;&lt;br /&gt;
	}&lt;br /&gt;
	# TODO: maybe try running git&lt;br /&gt;
	return &amp;quot;&amp;lt;unknown&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Translate a range into something friendlier for display.  -1 (no range)&lt;br /&gt;
# becomes the empty string, while &amp;quot;LOS_RADIUS&amp;quot; becomes just LOS.&lt;br /&gt;
# TORNADO_RADIUS becomes 5 (its value), but maybe we should use &amp;quot;Tornado&amp;quot;&lt;br /&gt;
# instead.&lt;br /&gt;
sub xlate_range {&lt;br /&gt;
	my $range = shift;&lt;br /&gt;
	return &amp;quot;LOS&amp;quot; if $range eq &amp;quot;LOS_RADIUS&amp;quot;;&lt;br /&gt;
	return 6 if $range eq &amp;quot;TORNADO_RADIUS&amp;quot;;&lt;br /&gt;
	return &amp;quot;&amp;quot; if $range eq &amp;quot;-1&amp;quot;;&lt;br /&gt;
	return $range;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Wiki-format a list of spell schools, linking to the corresponding&lt;br /&gt;
# magic skill.&lt;br /&gt;
sub format_schools {&lt;br /&gt;
	join &amp;quot;/&amp;quot;, map {&lt;br /&gt;
		my $school = ucfirst(lc $_);&lt;br /&gt;
&lt;br /&gt;
		# Convert school names to skill names for linking&lt;br /&gt;
		my $skill = $school;&lt;br /&gt;
		if ($school =~ /^(Poison|Air|Fire|Ice|Earth)$/) {&lt;br /&gt;
			$skill = &amp;quot;$school Magic&amp;quot;;&lt;br /&gt;
		} elsif ($school !~ /[ys]$/) {&lt;br /&gt;
			# &amp;quot;Necromancy&amp;quot; isn&amp;#039;t pluralised as a skill,&lt;br /&gt;
			# and &amp;quot;Hexes&amp;quot; and &amp;quot;Charms&amp;quot; are already&lt;br /&gt;
			# pluralized as a magic school.  The others&lt;br /&gt;
			# are singular as a school, plural as a skill.&lt;br /&gt;
			$skill = &amp;quot;${school}s&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		$skill eq $school ? &amp;quot;[[$school]]&amp;quot; : &amp;quot;[[$skill|$school]]&amp;quot;;&lt;br /&gt;
	} @_;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub ucfirst_array {&lt;br /&gt;
    my $items = join &amp;quot;,&amp;quot;, map {&lt;br /&gt;
        my $name = ucfirst(lc $_);&lt;br /&gt;
        &amp;quot;\&amp;quot;$name\&amp;quot;&amp;quot;;&lt;br /&gt;
    } @_;&lt;br /&gt;
    &amp;quot;{ $items }&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Add a spell if it exists&lt;br /&gt;
sub maybe_add_spell {&lt;br /&gt;
	my ($spell, $by_id, $by_key, $sortkey) = @_;&lt;br /&gt;
	return undef unless $spell-&amp;gt;{name};&lt;br /&gt;
&lt;br /&gt;
	# Ignore spells that do not occur in any book&lt;br /&gt;
	return &amp;quot;&amp;quot; unless scalar @{$spell-&amp;gt;{books}};&lt;br /&gt;
	# ..and NO_SPELL, which &amp;quot;occurs&amp;quot; in books but not really.&lt;br /&gt;
	return &amp;quot;&amp;quot; if $spell-&amp;gt;{id} eq &amp;quot;NO_SPELL&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	# Sanity checks: abort if we encounter one of these, as it means&lt;br /&gt;
	# something funny is up with our book-parsing.&lt;br /&gt;
&lt;br /&gt;
	# Spells that belong to certain monster-only schools.&lt;br /&gt;
	for my $school (@{$spell-&amp;gt;{schools}}) {&lt;br /&gt;
		die &amp;quot;SPTYP_NONE for $spell-&amp;gt;{name}&amp;quot; if $school eq &amp;quot;NONE&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	# Spells that don&amp;#039;t belong to a school at all, even SPTYP_NONE.&lt;br /&gt;
	die &amp;quot;No school for $spell-&amp;gt;{name}&amp;quot; unless scalar @{$spell-&amp;gt;{schools}};&lt;br /&gt;
&lt;br /&gt;
	# Monster and testing spells.&lt;br /&gt;
	for my $flag (@{$spell-&amp;gt;{flags}}) {&lt;br /&gt;
		die &amp;quot;Monster spell $spell-&amp;gt;{name}&amp;quot; if $flag eq &amp;quot;MONSTER&amp;quot;;&lt;br /&gt;
		die &amp;quot;Testing spell $spell-&amp;gt;{name}&amp;quot; if $flag eq &amp;quot;TESTING&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	# It&amp;#039;s worth keeping.&lt;br /&gt;
	$by_id-&amp;gt;{$spell-&amp;gt;{id}} = $spell;&lt;br /&gt;
        if ($sortkey eq &amp;quot;alpha&amp;quot;) {&lt;br /&gt;
                $by_key-&amp;gt;{$spell-&amp;gt;{name}} = $spell;&lt;br /&gt;
        } elsif ($sortkey eq &amp;quot;letter&amp;quot;) {&lt;br /&gt;
                my $let = uc(substr $spell-&amp;gt;{name}, 0, 1);&lt;br /&gt;
                push @{$by_key-&amp;gt;{$let}}, $spell;&lt;br /&gt;
        } elsif ($sortkey eq &amp;quot;level&amp;quot;) {&lt;br /&gt;
                push @{$by_key-&amp;gt;{$spell-&amp;gt;{level}}}, $spell;&lt;br /&gt;
        } elsif ($sortkey eq &amp;quot;school&amp;quot;) {&lt;br /&gt;
                for my $school (@{$spell-&amp;gt;{schools}}) {&lt;br /&gt;
                        push @{$by_key-&amp;gt;{$school}}, $spell;&lt;br /&gt;
                }&lt;br /&gt;
        } elsif ($sortkey eq &amp;quot;flag&amp;quot;) {&lt;br /&gt;
                for my $flag (@{$spell-&amp;gt;{flags}}) {&lt;br /&gt;
                        push @{$by_key-&amp;gt;{$flag}}, $spell;&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
	return 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Sorting functions&lt;br /&gt;
sub sort_by_name {&lt;br /&gt;
	$a-&amp;gt;{name} cmp $b-&amp;gt;{name}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub sort_by_level {&lt;br /&gt;
	$a-&amp;gt;{level} &amp;lt;=&amp;gt; $b-&amp;gt;{level} or sort_by_name&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub sort_by_school {&lt;br /&gt;
	# A Schwartzian transform would be more efficient, but we have&lt;br /&gt;
	# few enough spells that it&amp;#039;s not necessary.&lt;br /&gt;
	join(&amp;quot;/&amp;quot;, @{$a-&amp;gt;{schools}}) cmp join(&amp;quot;/&amp;quot;, @{$b-&amp;gt;{schools}})&lt;br /&gt;
		or sort_by_name&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
### MAIN&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub main {&lt;br /&gt;
        # Input directory.&lt;br /&gt;
        my $indir = &amp;quot;./&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        # Map from letter, school, flag, book name, or level to list of spells.&lt;br /&gt;
        my %by_key = ();&lt;br /&gt;
        my %by_letter = ();&lt;br /&gt;
        my %by_school = ();&lt;br /&gt;
        my %by_flag = ();&lt;br /&gt;
        my %by_book = ();&lt;br /&gt;
        my %by_level = ();&lt;br /&gt;
&lt;br /&gt;
        # Map from ID or name to spell&lt;br /&gt;
        my %by_id = ();&lt;br /&gt;
        my %by_name = ();&lt;br /&gt;
&lt;br /&gt;
        # Map from spell name to list of book names.&lt;br /&gt;
        my %book_spells = ();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        my $sortkey = &amp;quot;alpha&amp;quot;;&lt;br /&gt;
        my $sortfn = \&amp;amp;sort_by_name;&lt;br /&gt;
        my $module = &amp;quot;default&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        GetOptions(&lt;br /&gt;
                   &amp;quot;a|alphabetic&amp;quot; =&amp;gt; sub { $sortkey = &amp;quot;letter&amp;quot;; $sortfn = \&amp;amp;sort_by_name; },&lt;br /&gt;
                   &amp;quot;b|book&amp;quot; =&amp;gt; sub { $sortkey = &amp;quot;book&amp;quot;; $sortfn = undef; },&lt;br /&gt;
                   &amp;quot;f|flag&amp;quot; =&amp;gt; sub { $sortkey = &amp;quot;flag&amp;quot;; $sortfn = \&amp;amp;sort_by_level; },&lt;br /&gt;
                   &amp;quot;l|level&amp;quot; =&amp;gt; sub { $sortkey = &amp;quot;level&amp;quot;; $sortfn = \&amp;amp;sort_by_school; },&lt;br /&gt;
                   &amp;quot;s|school&amp;quot; =&amp;gt; sub { $sortkey = &amp;quot;school&amp;quot;; $sortfn = \&amp;amp;sort_by_level; },&lt;br /&gt;
                   &amp;quot;m|module-book&amp;quot; =&amp;gt; sub { $module = &amp;quot;book&amp;quot;; $sortkey = &amp;quot;book&amp;quot; ; $sortfn = undef; },&lt;br /&gt;
                   &amp;quot;k|module-spell&amp;quot; =&amp;gt; sub { $module = &amp;quot;spell&amp;quot;; $sortkey = &amp;quot;letter&amp;quot;; $sortfn = \&amp;amp;sort_by_name; },&lt;br /&gt;
                   &amp;quot;h|help&amp;quot; =&amp;gt; sub {&lt;br /&gt;
                           print &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;;&lt;br /&gt;
Usage: $0 [options] [directory]&lt;br /&gt;
&lt;br /&gt;
Produce a wiki table of DCSS spells.  The specified directory should contain&lt;br /&gt;
spl-data.h an book-data.h.  If omitted, the current directory is used.&lt;br /&gt;
&lt;br /&gt;
Options include:&lt;br /&gt;
  -a, --alphabetic    Arrange alphabetically (default).&lt;br /&gt;
  -b, --book          Arrange by book, then level.&lt;br /&gt;
  -f, --flag          Arrange by flag, then level.&lt;br /&gt;
  -k, --module-spell  Generate a Lua table of spells.&lt;br /&gt;
  -l, --level         Arrange by level, then schools.&lt;br /&gt;
  -m, --module-book   Generate a Lua table of spellbooks.&lt;br /&gt;
  -s, --school        Arrange by school, then level.&lt;br /&gt;
  -h, --help          Display this help.&lt;br /&gt;
&lt;br /&gt;
Spells of the same schools and level are sorted by name.  With the --book,&lt;br /&gt;
--school, and --flag option, spells may appear multiple times.&lt;br /&gt;
EOF&lt;br /&gt;
                           exit 0;&lt;br /&gt;
                   },&lt;br /&gt;
                  );&lt;br /&gt;
&lt;br /&gt;
        if (@ARGV) {&lt;br /&gt;
                $indir = shift @ARGV;&lt;br /&gt;
                $indir .= &amp;quot;/&amp;quot; unless $indir eq &amp;quot;&amp;quot; or $indir =~ m!/$!;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $DATE = gmtime;&lt;br /&gt;
        $CRAWL_VERSION = crawl_version($indir);&lt;br /&gt;
        ($PROGNAME = $0) =~ s!.*/!!;&lt;br /&gt;
&lt;br /&gt;
        parse_book_data($indir, \%book_spells, \%by_book);&lt;br /&gt;
        parse_spl_data($indir, \%book_spells, \%by_id, \%by_key, $sortkey);&lt;br /&gt;
        for my $k (keys %by_book) {&lt;br /&gt;
                # Convert the list of spell ids to a list of spells, but&lt;br /&gt;
                # remove those that aren&amp;#039;t in %by_id because maybe_add_spell&lt;br /&gt;
                # skipped them.&lt;br /&gt;
                my @spells =  map { $by_id{$_} || () } @{$by_book{$k}};&lt;br /&gt;
                if (@spells) {&lt;br /&gt;
                        $by_book{$k} = \@spells;&lt;br /&gt;
                } else {&lt;br /&gt;
                        # Remove the book if it has no spells.&lt;br /&gt;
                        delete $by_book{$k};&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        # Print data&lt;br /&gt;
        if ($module eq &amp;quot;default&amp;quot;) {&lt;br /&gt;
                if ($sortkey eq &amp;quot;book&amp;quot;) {&lt;br /&gt;
                        module_default($sortkey, $sortfn, \%by_book);&lt;br /&gt;
                } else {&lt;br /&gt;
                        module_default($sortkey, $sortfn, \%by_key);&lt;br /&gt;
                }&lt;br /&gt;
        } elsif ($module eq &amp;quot;book&amp;quot;) {&lt;br /&gt;
                module_book(\%by_book);&lt;br /&gt;
        } elsif ($module eq &amp;quot;spell&amp;quot;) {&lt;br /&gt;
                module_spell($sortfn, \%by_key);&lt;br /&gt;
        }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub parse_book_data {&lt;br /&gt;
        my ($indir, $book_spells, $by_book) = @_;&lt;br /&gt;
        # Name of current book.&lt;br /&gt;
        my $book = &amp;quot;bug&amp;quot; ;&lt;br /&gt;
        open BOOKS, &amp;quot;${indir}book-data.h&amp;quot;&lt;br /&gt;
          or die &amp;quot;could not open ${indir}book-data.h: $!&amp;quot;;&lt;br /&gt;
        while (&amp;lt;BOOKS&amp;gt;) {&lt;br /&gt;
                # Skip conditional sections.  Really we need to look at&lt;br /&gt;
                # the condition, but that requires the C preprocessor and&lt;br /&gt;
                # I&amp;#039;d rather avoid that.&lt;br /&gt;
&lt;br /&gt;
                #next if /^#if/../^#endif/;&lt;br /&gt;
                next if /^#if TAG_MAJOR_VERSION == 34/../^#endif/;&lt;br /&gt;
&lt;br /&gt;
                # Kind of hackish --- quit at the first rod&lt;br /&gt;
                last if m!// Rod!;&lt;br /&gt;
&lt;br /&gt;
                if (m!^{\s*// (.*)!) {&lt;br /&gt;
                        # Get the spell name from the comment.&lt;br /&gt;
                        $book = $1;&lt;br /&gt;
                        # Disambiguation for Conjuration&lt;br /&gt;
                        my $extra = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
                        # Remove parenthesized bits&lt;br /&gt;
                        $book =~ s/\s+\([^)]*\)//g;&lt;br /&gt;
&lt;br /&gt;
                        # Remove extra description&lt;br /&gt;
                        if ($book =~ s/ - (.*)//) {&lt;br /&gt;
                                # But remember it for Conjuration&lt;br /&gt;
                                if ($1 =~ /Fire and Earth/) {&lt;br /&gt;
                                        $extra = &amp;quot; (fire+earth)&amp;quot;;&lt;br /&gt;
                                } elsif ($1 =~ /Air and Ice/) {&lt;br /&gt;
                                        $extra = &amp;quot; (ice+air)&amp;quot;;&lt;br /&gt;
                                }&lt;br /&gt;
                        }&lt;br /&gt;
&lt;br /&gt;
                        # Remove roman numeral counter&lt;br /&gt;
                        $book =~ s/\s+[IVX]+$//;&lt;br /&gt;
&lt;br /&gt;
                        # Replace &amp;quot;Tome of&amp;quot; with &amp;quot;Book of&amp;quot; (special case for Dragon)&lt;br /&gt;
                        $book =~ s/^Tome of/Book of/;&lt;br /&gt;
&lt;br /&gt;
                        # And special-case Minor Magic.&lt;br /&gt;
                        $book =~ s/^Minor/Book of Minor/;&lt;br /&gt;
&lt;br /&gt;
                        # Append disambiguation&lt;br /&gt;
                        $book .= $extra;&lt;br /&gt;
                } else {&lt;br /&gt;
                        while (/SPELL_(\w+)/g) {&lt;br /&gt;
                                push @{$book_spells-&amp;gt;{$1}}, $book;&lt;br /&gt;
                                push @{$by_book-&amp;gt;{$book}}, $1;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        close BOOKS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub parse_spl_data {&lt;br /&gt;
        # Current spell.  Members are&lt;br /&gt;
        #   {name}    : Spell name&lt;br /&gt;
        #   {id}      : SPELL_* identifier (without the SPELL_)&lt;br /&gt;
        #   {schools} : List of SPTYP_* constants (without the SPTYP_)&lt;br /&gt;
        #   {flags}   : List of SPFLAG_* constants (without the SPFLAG_)&lt;br /&gt;
        #   {books}   : List of book names.&lt;br /&gt;
        #   {level}   : Level&lt;br /&gt;
        #   {cap}     : Power cap&lt;br /&gt;
        #   {minrange}: Minimum range, or &amp;quot;LOS&amp;quot; or &amp;quot;&amp;quot;&lt;br /&gt;
        #   {maxrange}: Maximum range, or &amp;quot;LOS&amp;quot; or &amp;quot;&amp;quot;&lt;br /&gt;
        #   {noisy}   : Noise modifier&lt;br /&gt;
        #&lt;br /&gt;
        #   {data}    : List of extra data; converted to {level}-{noise} at the end&lt;br /&gt;
        #               of the spell block.&lt;br /&gt;
        my ($indir, $book_spells, $by_id, $by_key, $sortkey) = @_;&lt;br /&gt;
        my $spell = {};&lt;br /&gt;
        open SPELLS, &amp;quot;${indir}spl-data.h&amp;quot;&lt;br /&gt;
          or die &amp;quot;could not open ${indir}spl-data.h: $!&amp;quot;;&lt;br /&gt;
        while (&amp;lt;SPELLS&amp;gt;) {&lt;br /&gt;
                chomp;&lt;br /&gt;
                if (/^{/) {&lt;br /&gt;
                        $spell = {};&lt;br /&gt;
                } elsif (/^}/) {&lt;br /&gt;
                        # Unpack data&lt;br /&gt;
                        my (&lt;br /&gt;
                            $sch, $flag, $lev, $cap, $minr, $maxr, $nm, @rest&lt;br /&gt;
                           ) = @{$spell-&amp;gt;{data}};&lt;br /&gt;
&lt;br /&gt;
                        # Parse out schools and flags&lt;br /&gt;
                        $spell-&amp;gt;{schools} = [&lt;br /&gt;
                                             map { s/SPTYP_//; $_ } split /\s*\|\s*/, $sch&lt;br /&gt;
                                            ];&lt;br /&gt;
                        $spell-&amp;gt;{flags} = [&lt;br /&gt;
                                           map { s/SPFLAG_//; $_ } split /\s*\|\s*/, $flag&lt;br /&gt;
                                          ];&lt;br /&gt;
&lt;br /&gt;
                        # Include the rest of the data&lt;br /&gt;
                        $spell-&amp;gt;{level} = $lev;&lt;br /&gt;
                        $spell-&amp;gt;{cap} = $cap;&lt;br /&gt;
                        $spell-&amp;gt;{minrange} = xlate_range $minr;&lt;br /&gt;
                        $spell-&amp;gt;{maxrange} = xlate_range $maxr;&lt;br /&gt;
                        $spell-&amp;gt;{noisy} = $nm;&lt;br /&gt;
&lt;br /&gt;
                        maybe_add_spell($spell, $by_id, $by_key, $sortkey);&lt;br /&gt;
                } elsif (/^\s*SPELL_(\w+),\s+&amp;quot;([^&amp;quot;]*)&amp;quot;,/) {&lt;br /&gt;
                        $spell-&amp;gt;{id} = $1;&lt;br /&gt;
                        $spell-&amp;gt;{name} = $2;&lt;br /&gt;
                        if (exists $book_spells-&amp;gt;{$1}) {&lt;br /&gt;
                                $spell-&amp;gt;{books} = [ @{$book_spells-&amp;gt;{$1}} ];&lt;br /&gt;
                        } else {&lt;br /&gt;
                                $spell-&amp;gt;{books} = [];&lt;br /&gt;
                        }&lt;br /&gt;
                } else {&lt;br /&gt;
                        # Strip comments first.&lt;br /&gt;
                        s!\s*//.*!!;&lt;br /&gt;
                        # Get comma-delimited sections&lt;br /&gt;
                        while (/\s*([^,]+)(,|$)/g) {&lt;br /&gt;
                                if (substr($1, 0, 1) eq &amp;quot;|&amp;quot;) {&lt;br /&gt;
				# Continuation line; really we should check&lt;br /&gt;
				# whether the previous line ended with a&lt;br /&gt;
				# comma, but this is probably good enough.&lt;br /&gt;
                                        $spell-&amp;gt;{data}[-1] .= $1&lt;br /&gt;
                                } else {&lt;br /&gt;
                                        push @{$spell-&amp;gt;{data}}, $1;&lt;br /&gt;
                                }&lt;br /&gt;
                        }&lt;br /&gt;
&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
        close SPELLS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub module_default {&lt;br /&gt;
        my ($sortkey, $sortfn, $by_key) = @_;&lt;br /&gt;
        # Descriptions of spell flags&lt;br /&gt;
        my %flag_descs = (&lt;br /&gt;
                          ALLOW_SELF =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell is not helpful, but you will not receive a &amp;quot;Really target yourself?&amp;quot;&lt;br /&gt;
prompt.  You may still receive &amp;quot;That beam is likely to hit you.&amp;quot; for&lt;br /&gt;
&amp;quot;[[#Dir or target|dir or target]]&amp;quot; spells such as Mephitic Cloud.&lt;br /&gt;
EOF&lt;br /&gt;
                          AREA =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell harms an area.  Pacified fleeing monsters will not use emergency&lt;br /&gt;
spells with this flag.&lt;br /&gt;
EOF&lt;br /&gt;
                          BATTLE =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell is a non-[[Conjuration]] spell disliked by [[Elyvilon]].  There is no&lt;br /&gt;
piety penalty for using such spells, but a randart spellbook containing one of&lt;br /&gt;
these spells will never have Elyvilon&amp;#039;s name on it.&lt;br /&gt;
EOF&lt;br /&gt;
                          CHAOTIC =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell is hated by [[Zin]], who will impose penance on any player casting&lt;br /&gt;
it, and reward killing monsters who can cast it.&lt;br /&gt;
EOF&lt;br /&gt;
                          CORPSE_VIOLATING =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell is hated by [[Fedhas Madash]], who will impose penance on any player&lt;br /&gt;
casting it.&lt;br /&gt;
EOF&lt;br /&gt;
                          DIR =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell requires choosing a direction (and not a target).&lt;br /&gt;
EOF&lt;br /&gt;
                          DIR_OR_TARGET =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell requires choosing a direction or target, and is stopped by&lt;br /&gt;
interposing creatures.&lt;br /&gt;
EOF&lt;br /&gt;
                          ESCAPE =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell helps you get out of trouble.  Xom considers such spells boring, and&lt;br /&gt;
will not gift spellbooks containing them.  Furthermore, the spell is an option&lt;br /&gt;
when control-clicking yourself in tiles mode.&lt;br /&gt;
EOF&lt;br /&gt;
                          GRID =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell targets a grid square, disregarding any creatures in the way.  This&lt;br /&gt;
is a form of smite targeting that does not require a target.&lt;br /&gt;
EOF&lt;br /&gt;
                          HASTY =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell is hated by [[Cheibriados]], who will impose penance on any player&lt;br /&gt;
casting it.&lt;br /&gt;
EOF&lt;br /&gt;
                          HELPFUL =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell helps you or the target; if targeted, the targeting commands&lt;br /&gt;
cycle through friendlies rather than hostiles.  Xom considers such spells&lt;br /&gt;
boring, and will not gift spellbooks containing them.&lt;br /&gt;
EOF&lt;br /&gt;
                          NEUTRAL =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell is neither harmful nor helpful; if targeted, the targeting&lt;br /&gt;
commands cycle through all creatures, not just hostiles.  Xom considers&lt;br /&gt;
such spells boring, and will not gift spellbooks containing them.&lt;br /&gt;
EOF&lt;br /&gt;
                          NONE =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell has no special flags.  Such spells are always untargeted.&lt;br /&gt;
EOF&lt;br /&gt;
                          NOT_SELF =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell may not target you or your square.&lt;br /&gt;
EOF&lt;br /&gt;
                          RECOVERY =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell helps you recover from ill effects.  Xom considers such spells&lt;br /&gt;
boring, and will not gift spellbooks containing them.  Furthermore, the spell&lt;br /&gt;
is an option when control-clicking yourself in tiles mode.&lt;br /&gt;
EOF&lt;br /&gt;
                          TARGET =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell targets a creature, disregarding any other creatures in the way.&lt;br /&gt;
This is a form of smite targeting that requires a target creature.&lt;br /&gt;
EOF&lt;br /&gt;
                          TARG_OBJ =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell targets an object, disregarding any other creatures in the way.&lt;br /&gt;
This is a form of smite targeting that requires a target object.&lt;br /&gt;
EOF&lt;br /&gt;
                          UNCLEAN =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell is hated by [[Zin]], who will impose penance on any player casting&lt;br /&gt;
it, and reward killing monsters who can cast it.&lt;br /&gt;
EOF&lt;br /&gt;
                          UNHOLY =&amp;gt; &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;,&lt;br /&gt;
The spell is a non-necromantic unholy spell.  It is hated by [[good]] gods&lt;br /&gt;
([[Elyvilon]], [[The Shining One]], and [[Zin]]), who will impose penance on&lt;br /&gt;
any player casting it.&lt;br /&gt;
EOF&lt;br /&gt;
                         );&lt;br /&gt;
        print &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;;&lt;br /&gt;
==Spells== &amp;lt;!-- We *must* have a heading before the table, or the TOC will end up inside the table! --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- Automatically generated by $PROGNAME $VERSION&lt;br /&gt;
     from Dungeon Crawl Stone Soup version $CRAWL_VERSION&lt;br /&gt;
  --&amp;gt;&lt;br /&gt;
{| class=&amp;quot;prettytable&amp;quot;&lt;br /&gt;
!rowspan=2|Image&lt;br /&gt;
!rowspan=2|Name&lt;br /&gt;
!rowspan=2|Schools&lt;br /&gt;
!rowspan=2|Level&lt;br /&gt;
!rowspan=2|Power&amp;lt;br&amp;gt;cap&lt;br /&gt;
! colspan=2 |Range&lt;br /&gt;
!rowspan=2|Flags&lt;br /&gt;
!rowspan=2|Books&lt;br /&gt;
|----&lt;br /&gt;
!min&lt;br /&gt;
!max&lt;br /&gt;
EOF&lt;br /&gt;
&lt;br /&gt;
        # TODO: allow sorting by other criteria&lt;br /&gt;
        for my $key (sort keys %{$by_key}) {&lt;br /&gt;
                my @spells = @{$by_key-&amp;gt;{$key}};&lt;br /&gt;
                @spells = sort $sortfn @spells if $sortfn;&lt;br /&gt;
&lt;br /&gt;
                print &amp;quot;|----\n! colspan=9 style=\&amp;quot;text-align:left\&amp;quot;|\n====&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
                # Format and link the key appropriately&lt;br /&gt;
                if ($sortkey eq &amp;quot;book&amp;quot;) {&lt;br /&gt;
                        print &amp;quot;[[$key]]&amp;quot;;&lt;br /&gt;
                } elsif ($sortkey eq &amp;quot;school&amp;quot;) {&lt;br /&gt;
                        print format_schools $key;&lt;br /&gt;
                } elsif ($sortkey eq &amp;quot;level&amp;quot;) {&lt;br /&gt;
                        print &amp;quot;level $key&amp;quot;;&lt;br /&gt;
                } elsif ($sortkey eq &amp;quot;flag&amp;quot;) {&lt;br /&gt;
                        my $fl = ucfirst lc $key;&lt;br /&gt;
                        $fl =~ s/_/ /g;&lt;br /&gt;
                        print $fl;&lt;br /&gt;
                } else {&lt;br /&gt;
                        print $key;&lt;br /&gt;
                }&lt;br /&gt;
                print &amp;quot;====\n&amp;quot;;&lt;br /&gt;
                if ($sortkey eq &amp;quot;flag&amp;quot;) {&lt;br /&gt;
                        my $desc = $flag_descs{$key};&lt;br /&gt;
                        if ($desc) {&lt;br /&gt;
                                $desc =~ s/\n/ /g;&lt;br /&gt;
                                print &amp;quot;|----\n| colspan=9|$desc\n&amp;quot;;&lt;br /&gt;
                        }&lt;br /&gt;
                }&lt;br /&gt;
                for my $spell (@spells) {&lt;br /&gt;
                        # Format schools and flags&lt;br /&gt;
                        my $schools = format_schools @{$spell-&amp;gt;{schools}};&lt;br /&gt;
&lt;br /&gt;
                        my $flags = join &amp;quot;, &amp;quot;, map {&lt;br /&gt;
                                s/_/ /g; lc $_&lt;br /&gt;
                        } @{$spell-&amp;gt;{flags}}, ($spell-&amp;gt;{noisy} ? &amp;quot;noise $spell-&amp;gt;{noisy}&amp;quot; : ());&lt;br /&gt;
                        my $books = join &amp;quot;&amp;lt;br&amp;gt;&amp;quot;, map { &amp;quot;[[$_]]&amp;quot; } @{$spell-&amp;gt;{books}};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
                        print &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;;&lt;br /&gt;
|----&lt;br /&gt;
|[[File:{{lc:$spell-&amp;gt;{name}.png}}]]&lt;br /&gt;
|style=&amp;quot;padding-left:1em&amp;quot;|[[$spell-&amp;gt;{name}]]&lt;br /&gt;
|$schools&lt;br /&gt;
|$spell-&amp;gt;{level}&lt;br /&gt;
|$spell-&amp;gt;{cap}&lt;br /&gt;
|$spell-&amp;gt;{minrange}&lt;br /&gt;
|$spell-&amp;gt;{maxrange}&lt;br /&gt;
|$flags&lt;br /&gt;
|$books&lt;br /&gt;
EOF&lt;br /&gt;
                }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        print &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;;&lt;br /&gt;
|----&lt;br /&gt;
|}&lt;br /&gt;
EOF&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub module_book {&lt;br /&gt;
        my ($by_book) = @_;&lt;br /&gt;
    print &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;;&lt;br /&gt;
--[=[&lt;br /&gt;
     Table of spellbooks&lt;br /&gt;
 ]=]--&lt;br /&gt;
&lt;br /&gt;
local m = {}&lt;br /&gt;
EOF&lt;br /&gt;
&lt;br /&gt;
    my @letters = qw(a b c d e f g);&lt;br /&gt;
    my $i = 0;&lt;br /&gt;
    for my $key (sort keys %$by_book) {&lt;br /&gt;
        print &amp;quot;m[\&amp;quot;$key\&amp;quot;] = {\n&amp;quot;;&lt;br /&gt;
        my @spells = @{$by_book-&amp;gt;{$key}};&lt;br /&gt;
        $i = 0;&lt;br /&gt;
        for my $spell (@spells) {&lt;br /&gt;
            my $schools = format_schools @{$spell-&amp;gt;{schools}};&lt;br /&gt;
            print &amp;quot;  {\n&amp;quot;;&lt;br /&gt;
            print &amp;quot;    [\&amp;quot;letter\&amp;quot;] = \&amp;quot;$letters[$i++]\&amp;quot;, \n&amp;quot;;&lt;br /&gt;
            print &amp;quot;    [\&amp;quot;name\&amp;quot;] = \&amp;quot;$spell-&amp;gt;{name}\&amp;quot;, \n&amp;quot;;&lt;br /&gt;
	    my $lc_name = lc($spell-&amp;gt;{name});&lt;br /&gt;
            print &amp;quot;    [\&amp;quot;image\&amp;quot;] = \&amp;quot;[[File:${lc_name}.png]]\&amp;quot;, \n&amp;quot;;&lt;br /&gt;
            print &amp;quot;    [\&amp;quot;level\&amp;quot;] = \&amp;quot;$spell-&amp;gt;{level}\&amp;quot;, \n&amp;quot;;&lt;br /&gt;
            print &amp;quot;    [\&amp;quot;schools\&amp;quot;] = \&amp;quot;$schools\&amp;quot;, \n&amp;quot;;&lt;br /&gt;
            print &amp;quot;  },\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        print &amp;quot;}\n&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    print &amp;quot;return m\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub module_spell {&lt;br /&gt;
        my ($sortfn, $by_letter) = @_;&lt;br /&gt;
        # Table of spells&lt;br /&gt;
        print &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;;&lt;br /&gt;
--[=[&lt;br /&gt;
     Table of spells&lt;br /&gt;
 ]=]--&lt;br /&gt;
&lt;br /&gt;
local m = {}&lt;br /&gt;
EOF&lt;br /&gt;
&lt;br /&gt;
        for my $key (sort keys %$by_letter) {&lt;br /&gt;
            my @spells = @{$by_letter-&amp;gt;{$key}};&lt;br /&gt;
            @spells = sort $sortfn @spells if $sortfn;&lt;br /&gt;
            for my $spell (@spells) {&lt;br /&gt;
                my $schools = ucfirst_array @{$spell-&amp;gt;{schools}};&lt;br /&gt;
                my $flags = ucfirst_array @{$spell-&amp;gt;{flags}};&lt;br /&gt;
                $flags =~ s/_/ /g;&lt;br /&gt;
                $flags =~ s/Mr check/MR check/; # special case&lt;br /&gt;
                $flags =~ s/^{ \&amp;quot;None\&amp;quot; }$/nil/; # special case&lt;br /&gt;
                my $books = join &amp;quot;,&amp;quot;, map { &amp;quot;\&amp;quot;$_\&amp;quot;&amp;quot; } @{$spell-&amp;gt;{books}};&lt;br /&gt;
                my $range;&lt;br /&gt;
                if ($spell-&amp;gt;{minrange} eq &amp;quot;&amp;quot;) {&lt;br /&gt;
                    $range = &amp;quot;nil&amp;quot;;&lt;br /&gt;
                } elsif ($spell-&amp;gt;{minrange} eq &amp;quot;LOS&amp;quot;) {&lt;br /&gt;
                    $range = &amp;quot;\&amp;quot;LOS\&amp;quot;&amp;quot;;&lt;br /&gt;
                } elsif ($spell-&amp;gt;{minrange} eq $spell-&amp;gt;{maxrange}) {&lt;br /&gt;
                    $range = $spell-&amp;gt;{minrange};&lt;br /&gt;
                } else {&lt;br /&gt;
                    $range = &amp;quot;{$spell-&amp;gt;{minrange}, $spell-&amp;gt;{maxrange}}&amp;quot;;&lt;br /&gt;
                }&lt;br /&gt;
                print &amp;quot;m[\&amp;quot;$spell-&amp;gt;{name}\&amp;quot;] = {\n&amp;quot;;&lt;br /&gt;
                print &amp;quot;    [\&amp;quot;schools\&amp;quot;] = $schools, \n&amp;quot;;&lt;br /&gt;
                print &amp;quot;    [\&amp;quot;flags\&amp;quot;] = $flags, \n&amp;quot;;&lt;br /&gt;
                print &amp;quot;    [\&amp;quot;books\&amp;quot;] = { $books }, \n&amp;quot;;&lt;br /&gt;
                print &amp;quot;    [\&amp;quot;level\&amp;quot;] = $spell-&amp;gt;{level}, \n&amp;quot;;&lt;br /&gt;
                print &amp;quot;    [\&amp;quot;cap\&amp;quot;] = $spell-&amp;gt;{cap}, \n&amp;quot;;&lt;br /&gt;
                print &amp;quot;    [\&amp;quot;range\&amp;quot;] = $range, \n&amp;quot;;&lt;br /&gt;
                print &amp;quot;    [\&amp;quot;noise\&amp;quot;] = $spell-&amp;gt;{noisy}, \n&amp;quot;;&lt;br /&gt;
                print &amp;quot;}\n&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        print &amp;quot;return m\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
main;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
</feed>