summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/util/art-data.pl
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2009-06-25 02:29:14 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2009-06-25 02:29:14 +0000
commit2fad05374f70f21d4af3d147325581bf1047b5be (patch)
tree40a46254f044837f658e4dbabb07ce3c52b72804 /crawl-ref/source/util/art-data.pl
parentab0e3274d569d1cdea4010f4ed1d4b24cd3804e9 (diff)
downloadcrawl-ref-2fad05374f70f21d4af3d147325581bf1047b5be.tar.gz
crawl-ref-2fad05374f70f21d4af3d147325581bf1047b5be.zip
First part of the merger of fixed artefacts into unrandom artefacts
(further changes will be much smaller). Breaks savefile compatibility, and bumps the major savefile version up to 6. Some changes made to some tiles files, but it hasn't been tested with a tiles build. Overview of changes: * Unrand artefacts are now defined in art-data.txt and is turned into C code via util/art-data.pl. This has the dual advantage of being more readable by humans, and that if the unrand data structure changes then you can just change util/art-data.pl and regenerate the C code rather than having to change some 70 different C structs by hand. * util/art-data.pl automatically updates NO_UNRANDARTS, and also automatically generates an enumeration of all the unrands which are equal to their item.special field. * randart.cc and randart.h have been renamed to artefact.cc and artefact.h, since the files covers all types of artefacts, and the differences between randarts, unrandarts and (former) fixed arts have been minimized since the terms were introduced. Also renamed unrand.h to art-data.h * The brands and resistances of former fixed arts are now handled via artefact properties, but the rest of their special behaviours are still hardcoded. * Unrandarts are now distinguished between normal and "special", with the special ones currently just being identical to the list of the formed fixed arts. Special unrandarts are randomly generated less often than normal unrandarts, can be generated in the Abyss if they've been lost, can't be picked up by monsters, and can't be affected by Tukima's Dance. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@10035 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/util/art-data.pl')
-rwxr-xr-xcrawl-ref/source/util/art-data.pl680
1 files changed, 680 insertions, 0 deletions
diff --git a/crawl-ref/source/util/art-data.pl b/crawl-ref/source/util/art-data.pl
new file mode 100755
index 0000000000..bbd7e716dc
--- /dev/null
+++ b/crawl-ref/source/util/art-data.pl
@@ -0,0 +1,680 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+my $line_num = 0;
+my @errors = ();
+my @all_artefacts = ();
+my %used_names = ();
+my %used_appears = ();
+my %used_enums = ();
+
+my %field_type = (
+ AC => "num",
+ ACC => "num",
+ ANGRY => "num",
+ APPEAR => "str",
+ BERSERK => "num",
+ BLINK => "num",
+ BRAND => "enum",
+ CANTELEP => "num",
+ COLD => "num",
+ COLOUR => "enum",
+ CURSED => "num",
+ DAM => "num",
+ DESC => "str",
+ DESC_END => "str",
+ DESC_ID => "str",
+ DEX => "num",
+ ELEC => "num",
+ EV => "num",
+ FIRE => "num",
+ INT => "num",
+ INV => "num",
+ LEV => "num",
+ LIFE => "num",
+ MAGIC => "num",
+ MAPPING => "num",
+ METAB => "num",
+ MP => "num",
+ MUTATE => "num",
+ NAME => "str",
+ NOISES => "num",
+ NOSPELL => "num",
+ NOTELEP => "num",
+ POISON => "num",
+ RND_TELE => "num",
+ SEEINV => "num",
+ STEALTH => "num",
+ STR => "num",
+
+ plus => "num",
+ plus2 => "num",
+ base_type => "enum",
+ sub_type => "enum"
+);
+
+my @field_list = keys(%field_type);
+
+sub error
+{
+ my ($artefact, $str) = @_;
+
+ my $msg = "";
+
+ my $name = $artefact->{NAME} || $artefact->{APPEAR} || "NAMELESS";
+
+ if ($artefact->{_FINISHING})
+ {
+ $msg .= "Artefact $name starting at line "
+ . $artefact->{_START_LINE} . ": ";
+ }
+ else
+ {
+ $msg = "Artefact $name at line $line_num: "
+ }
+
+ $msg .= $str;
+
+ $artefact->{_ERRROR} = 1;
+
+ push(@errors, $msg);
+}
+
+sub is_number
+{
+ my ($num) = @_;
+ return ($num =~ /^[+-]?\d+$/);
+}
+
+sub finish_art
+{
+ my ($artefact) = @_;
+
+ $artefact->{_FINISHING} = 1;
+
+ my $must;
+ foreach $must ("NAME", "APPEAR", "OBJ", "COLOUR")
+ {
+ if (!defined($artefact->{$must}))
+ {
+ error($artefact, "Required field '$must' missing");
+ $artefact->{$must} = "_MISSING_";
+ }
+ }
+
+ # Prevent further errors caused by absence of OBJ
+ $artefact->{base_type} ||= "";
+ $artefact->{sub_type} ||= "";
+
+ if (!exists($artefact->{BRAND}))
+ {
+ my $type = $artefact->{base_type} || "";
+
+ if ($type eq "OBJ_WEAPONS")
+ {
+ $artefact->{BRAND} = "SPWPN_NORMAL";
+ }
+ elsif($type eq "OBJ_ARMOUR")
+ {
+ $artefact->{BRAND} = "SPARM_NORMAL";
+ }
+ else
+ {
+ $artefact->{BRAND} = "0";
+ }
+ }
+
+ my $field;
+ foreach $field (@field_list)
+ {
+ if (!exists($artefact->{$field}))
+ {
+ # Give default values for fields not specified.
+ my $type = $field_type{$field} || "";
+ if ($type eq "str")
+ {
+ $artefact->{$field} = "";
+ }
+ elsif($type eq "num")
+ {
+ $artefact->{$field} = "0";
+ }
+ elsif($type eq "enum")
+ {
+ error($artefact, "No enumeration for field '$field'");
+ }
+ else
+ {
+ error($artefact, "Unknown type '$type' for field '$field'");
+ }
+ }
+ elsif (!defined($artefact->{$field}))
+ {
+ error($artefact, "Field '$field' not defined");
+ }
+ }
+
+ delete($artefact->{_FINISHING});
+ $artefact->{_FINISHED} = 1;
+}
+
+sub process_line
+{
+ my ($artefact, $line) = @_;
+
+ # A line can start with whitespace if it's a continuation of a field
+ # with a string value.
+ if ($line =~ /^\s/)
+ {
+ my $prev_field = $artefact->{_PREV_FIELD} || "";
+ if ($field_type{$prev_field} eq "str")
+ {
+ $line =~ s/^\s*//;
+ $artefact->{$prev_field} .= " " . $line;
+ }
+ else
+ {
+ error($artefact, "line starts with whitespace");
+ }
+ return;
+ }
+
+ $artefact->{_START_LINE} ||= $line_num;
+
+ my ($field, $value) = ($line =~ /([^:]+):\s*(.*)/);
+
+ $field ||= "";
+ $value ||= "";
+
+ # Strip leading and trailing white space.
+ $field =~ s/^\s*|\s*$//g;
+ $value =~ s/^\s*|\s*$//g;
+
+ if ($field eq "")
+ {
+ error($artefact, "No field");
+ return;
+ }
+
+ if (defined($artefact->{$field}))
+ {
+ error($artefact, "Field '$field' already set");
+ return;
+ }
+
+ if ($value eq "true" && $field_type{$field} eq "num")
+ {
+ $value = "1";
+ }
+
+ $artefact->{_PREV_FIELD} = $field;
+ $artefact->{$field} = $value;
+
+ if ($value eq "")
+ {
+ error($artefact, "Field '$field' has no value");
+ return;
+ }
+
+ if ($field eq "OBJ")
+ {
+ my @parts = split(m!/!, $value);
+
+ if (@parts > 2)
+ {
+ error($artefact, "Too many parts to OBJ");
+ return;
+ }
+ elsif (@parts == 1)
+ {
+ error($artefact, "Too few parts to OBJ");
+ return;
+ }
+
+ if ($parts[0] !~ /^OBJ_/)
+ {
+ error($artefact, "OBJ base type must start with 'OBJ_'");
+ return;
+ }
+ $artefact->{base_type} = $parts[0];
+ $artefact->{sub_type} = $parts[1];
+ }
+ elsif($field eq "PLUS")
+ {
+ my @parts = split(m!/!, $value);
+
+ if (@parts > 2)
+ {
+ error($artefact, "Too many parts to PLUS");
+ return;
+ }
+
+ if (!is_number($parts[0]))
+ {
+ error($artefact, "'$parts[0]' in PLUS is not a number");
+ return;
+ }
+ if (@parts == 2 && !is_number($parts[1]))
+ {
+ error($artefact, "'$parts[1]' in PLUS is not a number");
+ return;
+ }
+ $artefact->{plus} = $parts[0];
+ $artefact->{plus2} = $parts[1] if (@parts == 2);
+ }
+ elsif ($field eq "ENUM")
+ {
+ }
+ else
+ {
+ if (!exists($field_type{$field}) || $field =~ /^[a-z]/)
+ {
+ error($artefact, "No such field as '$field'");
+ return;
+ }
+
+ my $num = is_number($value);
+ my $type = $field_type{$field} || "";
+
+ if (($type eq "num" && !$num) || ($type eq "str" && $num))
+ {
+ error($artefact, "'$value' invalid value type for field '$field'.");
+ return;
+ }
+ }
+
+ my $enum = "";
+
+ if ($field eq "NAME")
+ {
+ if (exists($used_names{$value}))
+ {
+ error($artefact, "Name \"$value\" already used at line " .
+ $used_names{$value});
+ return;
+ }
+ $used_names{$value} = $line_num;
+
+ if (!exists($artefact->{_ENUM}))
+ {
+ $enum = $value;
+ $enum =~ s/'//g;
+ # If possible, make the enum literal the part of the name between
+ # double quotes, or the part of the name after " of " or
+ # " of the ".
+ if ($enum =~ /"(.*)"/)
+ {
+ $enum = $1;
+ }
+ elsif ($enum =~ / of (?:the )?(.*)/)
+ {
+ $enum = $1;
+ }
+ $enum = uc($enum);
+ $enum =~ s/[ -]/_/g;
+ }
+ }
+ elsif ($field eq "APPEAR")
+ {
+ if (exists($used_appears{$value}))
+ {
+ error($artefact, "Name '$value' already used at line " .
+ $used_appears{$value});
+ return;
+ }
+ $used_appears{$value} = $line_num;
+ }
+ elsif ($field eq "ENUM")
+ {
+ if (exists($artefact->{NAME}))
+ {
+ eror($artefact, "ENUM must be before NAME");
+ return;
+ }
+ $enum = "$value";
+ }
+
+ if ($enum ne "")
+ {
+ if (exists($used_enums{$enum}))
+ {
+ error($artefact, "Enum \"$enum\" already used by artefact " .
+ "\"$used_enums{$enum}\"");
+ return;
+ }
+
+ $used_enums{$enum} = $value;
+ $artefact->{_ENUM} = $enum;
+ }
+}
+
+my @art_order = (
+ "NAME", "APPEAR", "\n",
+ "base_type", "sub_type", "plus", "plus2", "COLOUR",
+
+ "{", "BRAND", "AC", "EV", "STR", "INT", "DEX", "\n",
+ "FIRE", "COLD", "ELEC", "POISON", "LIFE", "MAGIC", "\n",
+ "SEEINV", "INV", "LEV", "BLINK", "CANTELEP", "BERSERK", "\n",
+ "MAPPING", "NOISES", "NOSPELL", "RND_TELE", "NOTELEP", "\n",
+ "ANGRY", "METAB", "MUTATE", "ACC", "DAM", "\n",
+ "CURSED", "STEALTH", "MP", "}",
+
+ "DESC", "\n",
+ "DESC_ID", "\n",
+ "DESC_END"
+);
+
+sub art_to_str
+{
+ my ($artefact) = @_;
+
+ my $indent = 1;
+
+ my $str = "{\n ";
+
+ my $part;
+ foreach $part (@art_order)
+ {
+ if (length($part) == 1)
+ {
+ if ($part eq "{")
+ {
+ $str .= "\n" . (" " x ($indent * 4)) . "{";
+ $indent++;
+ $str .= "\n" . (" " x ($indent * 4));
+ }
+ elsif($part eq "}")
+ {
+ $indent--;
+ $str .= "\n" . (" " x ($indent * 4)) . "},";
+ $str .= "\n" . (" " x ($indent * 4));
+ }
+ else
+ {
+ $str .= "\n" . (" " x ($indent * 4));
+ }
+ next;
+ }
+
+ if (!defined($field_type{$part}))
+ {
+ print STDERR "No field type for part '$part'\n";
+ next;
+ }
+
+ if ($field_type{$part} eq "str")
+ {
+ my $temp = $artefact->{$part};
+ $temp =~ s/"/\\"/g;
+ $str .= "\"$temp\"";
+ }
+ else
+ {
+ $str .= $artefact->{$part};
+ }
+ $str .= ", ";
+ }
+
+ $str .= "\n},\n\n";
+
+ return ($str);
+}
+
+sub write_data
+{
+ unless (open(HEADER, ">art-data.h"))
+ {
+ die "Couldn't open 'art-data.h' for writing: $!\n";
+ }
+
+ print HEADER <<"ENDofTEXT";
+/*
+ * File: art-data.h
+ * Summary: Definitions for unrandom artefacts.
+ * Written by: ????
+ *
+ * Modified for Crawl Reference by \$Author: dploog \$ on \$Date: 2009-06-17 22:29:07 -0700 (Wed, 17 Jun 2009) \$
+ */
+
+/*
+ * This file is automatically generated from art-data.txt via
+ * util/art-data.pl. Do not directly edit this file, but rather change
+ * art-data.txt.
+ *
+ * If the unrandart_entry struct is changed or a new artefact property is
+ * added to artefact_prop_type, then change art-data.pl so that the
+ * art-data.h file it produces matches up with the new structure.
+ */
+
+#ifdef ART_DATA_H
+#error "art-data.h included twice!"
+#endif
+
+#define ART_DATA_H
+
+ENDofTEXT
+
+ my $artefact;
+ my $art_num = 1;
+ foreach $artefact (@all_artefacts)
+ {
+ print HEADER "/* $art_num: UNRAND_$artefact->{_ENUM} */\n";
+ print HEADER art_to_str($artefact);
+ $art_num++;
+ }
+
+ close(HEADER);
+}
+
+sub write_enums
+{
+ unless (open(ENUM_IN, "<artefact.h"))
+ {
+ die "Couldn't open 'artefact.h' for reading: $!";
+ }
+
+ my $changed = 0;
+ my $out = "";
+
+ while(<ENUM_IN>)
+ {
+ last if (/^#define NO_UNRANDARTS/);
+ $out .= $_;
+ }
+
+ if (!/^#define NO_UNRANDARTS (\d+)/)
+ {
+ die "Couldn't find NO_UNRANDARTS in artefact.h\n";
+ }
+
+ my $orig_num = $1;
+ my $new_num = scalar(@all_artefacts);
+
+ if ($orig_num == $new_num)
+ {
+ print "Number of unrandarts unchanged.\n";
+ $out .= $_;
+ }
+ else
+ {
+ print "Number of unrandarts changed from $orig_num to $new_num\n";
+ $out .= "#define NO_UNRANDARTS $new_num\n";
+ $changed = 1;
+ }
+
+ while(<ENUM_IN>)
+ {
+ $out .= $_;
+ last if (/^enum unrand_type/);
+ }
+
+ if (!/^enum unrand_type/)
+ {
+ die "Couldn't find 'enum unrand_type' in artefact.h\n";
+ }
+
+ $out .= <ENUM_IN>; # {
+ $out .= <ENUM_IN>; # UNRAND_START = 180,
+
+ my @enum_list = ();
+ while(<ENUM_IN>)
+ {
+ last if (/\bUNRAND_LAST\b/);
+ /^\s*(\w+)/;
+ push(@enum_list, $1);
+ }
+
+ <ENUM_IN>; # discard "};"
+
+ # Suck in rest of file
+ undef($/);
+ my $remainder = <ENUM_IN>;
+ close(ENUM_IN);
+
+ if (@enum_list != @all_artefacts)
+ {
+ print "Enumeartion list changed.\n";
+ $changed = 1;
+ }
+ else
+ {
+ my $i;
+ for ($i = 0; $i < @enum_list; $i++)
+ {
+ if ($enum_list[$i] ne "UNRAND_$all_artefacts[$i]->{_ENUM}")
+ {
+ print "Enumeartion list changed.\n";
+ $changed = 1;
+ last;
+ }
+ }
+ }
+
+ if (!$changed)
+ {
+ print "No changes made to artefact.h\n";
+ return;
+ }
+
+ print "Updating artefact.h...\n";
+
+ unless(open(ENUM_OUT, ">artefact.h"))
+ {
+ die "Couldn't open 'artefact.h' for writing: $!\n";
+ }
+
+ print ENUM_OUT $out;
+
+ my $i;
+ my $longest_enum = 0;
+ for ($i = 0; $i < @all_artefacts; $i++)
+ {
+ my $enum = $all_artefacts[$i]->{_ENUM};
+ my $len = length($enum);
+ $longest_enum = $len if ($len > $longest_enum);
+ }
+
+ my $enum;
+ for ($i = 0; $i < @all_artefacts; $i++)
+ {
+ $enum = $all_artefacts[$i]->{_ENUM};
+
+ print ENUM_OUT " UNRAND_$enum";
+
+ if ($i == 0)
+ {
+ print ENUM_OUT " = UNRAND_START,\n";
+ }
+ else
+ {
+ print ENUM_OUT ", ";
+ print ENUM_OUT " " x ($longest_enum - length($enum));
+ print ENUM_OUT "// $all_artefacts[$i]->{NAME}\n";
+ }
+ }
+
+ print ENUM_OUT " UNRAND_LAST = UNRAND_$enum\n";
+ print ENUM_OUT "};\n";
+
+ print ENUM_OUT $remainder;
+
+ close(ENUM_OUT);
+}
+
+###############################################################3
+###############################################################3
+###############################################################3
+
+chdir("..") if (-e "../art-data.txt");
+chdir("source") if (-e "source/art-data.txt");
+
+die "Couldn't find art-data.txt\n" unless (-e "art-data.txt");
+die "Couldn't find artefact.h\n" unless (-e "artefact.h");
+die "Can't read art-data.txt\n" unless (-r "art-data.txt");
+die "Can't read artefact.h\n" unless (-r "artefact.h");
+die "Can't write to artefact.h\n" unless (-w "artefact.h");
+die "Can't write to art-data.h\n" if (-e "art-data.h" && !-w "art-data.h");
+
+unless(open(INPUT, "<art-data.txt"))
+{
+ die "Couldn't open art-data.txt for reading: $!\n";
+}
+
+my $prev_line = "";
+my $curr_art = {};
+
+while (<INPUT>)
+{
+ chomp;
+ $line_num++;
+
+ # Skip comment-only lines
+ next if (/^#/);
+
+ # Strip comments.
+ s/#.*//;
+
+ # Strip trailing whitspace; leading whitespace indicates the
+ # continuation of a string field.
+ s/\s*$//;
+
+ if ($_ =~ /^\s*$/)
+ {
+ if ($prev_line !~ /^\s*$/)
+ {
+ finish_art($curr_art);
+ push(@all_artefacts, $curr_art);
+ $curr_art = {};
+ }
+ }
+ else
+ {
+ process_line($curr_art, $_);
+ }
+ $prev_line = $_;
+}
+close(INPUT);
+
+if (keys(%$curr_art) > 0)
+{
+ finish_art($curr_art);
+ push(@all_artefacts, $curr_art);
+}
+
+if (@errors > 0)
+{
+ print STDERR "Error(s) processing art-data.txt:\n\n";
+
+ my $err;
+ foreach $err (@errors)
+ {
+ print STDERR "$err\n";
+ }
+ exit (1);
+}
+
+write_data();
+write_enums();
+
+exit (0);