server-1.12/lib/adm/getfaces.pl

343 lines
16 KiB
Perl

#!/usr/bin/perl
# getfaces.pl, a script to retrive lists of specific faces from crossfire
# archtypes
#
# Copyright (C) 2005 Alexander Schultz
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# The author can be reached via e-mail to aldragon@gmail.com
use Getopt::Long;
use File::Find;
$result = GetOptions ("inclusive!" => \$inclusiveflags, "showfile!" => \$showfileflag, "showobject!" => \$showobjectflag, "duplicate!" => \$duplicateflag, "nopass" => \$nopass, "alive" => \$alive, "player" => \$player, "monster" => \$monster, "floor" => \$floor, "spelleffect" => \$spelleffect, "searchname=s" => \$searchstring, "searchbody=s" => \$searchbstring, "race=s" => \$racestring, "slay=s" => \$slaystring, "help|h" => \$showhelp, "stdin=s" => \$stdinlimit, "imagefile" => \$imagefileflag, "imagemagick" => \$imagemagickflag, "imagelabels" => \$imagelabelsflag, "type=i", => \$checktype, "subtype=i", => \$checkstype, "exit" => \$exitflag, "teleporter" => \$teleflag, "weapon" => \$weapflag, "bow" => \$bowflag, "arrow" => \$arrowflag, "armour" => \$armourflag, "currency" => \$currencyflag);
if (!$result) { usage(); }
if ($showhelp) { help(); }
if (!($stdinlimit=~/limit/i) and !($stdinlimit=~/extend/i) and !($stdinlimit=~/exclude/i) and $stdinlimit) { print "--stdin must be either 'limit', 'exclude' or 'extend'.\n"; usage(); }
$curpath = "../arch";
if ($searchstring) { $namere = qr/$searchstring/i; }
if ($searchbstring) { $bodyre = qr/$searchbstring/i; }
if ($racestring) { $racere = qr/$racestring/i; }
if ($slaystring) { $slayre = qr/$slaystring/i; }
if ($checktype) { $typenum = int($checktype); }
if ($checkstype) { $stypenum = int($checkstype); }
if ($ARGV[0]) { $curpath=$ARGV[0]; }
%animfaces = ();
%unique = ();
%unique2 = ();
%stdinlist = ();
%pngloc = ();
@outfacefiles = ();
$fcount = 0;
#create an internal database of animations defined in .face files to reference to
File::Find::find({wanted => \&facehandle, follow => 1, no_chdir => 1 }, $curpath);
if ($stdinlimit) {
while (<>) {
$_ =~ s/\s+$//;
$oldface = $_;
if ($imagefileflag or $imagemagickflag) {$_ =~ s/\./\.base\./; $_=$_.".png";}
$stdinlist{$_}++;
if ($stdinlimit=~/extend/i) {
if (!$imagemagickflag) {
print "$_\n";
}
$unique{$_}++;
$fcount++;
if ($imagelabelsflag) {$imprams="-label ".$_." ";} else {$imprams="";}
$outfacefiles[$fcount] = $imprams.$pngloc{$_};
}
if ($stdinlimit=~/exclude/i) { $unique{$_}++; }
}
}
#look through all .arc files for data
File::Find::find({wanted => \&archhandle, follow => 1, no_chdir => 1 }, $curpath);
if ($imagemagickflag) { imagemagickdisp(); }
exit;
sub usage {
$usagemessage = "Usage: getfaces.pl [OPTION]... [PATH]
Try `./getface.pl --help' for more information.
";
die $usagemessage;
}
sub help {
$usagemessage = "Usage: getfaces.pl [OPTION]... [PATH]
List faces used by archtypes that fit given criteria.
Settings:
--showfile before each face, list the .arc that is using it
--showobject before each face, list the name of the object
that is using it
--duplicate show duplicate entries of faces for each object
using the face
--inclusive normally the logic of the criteria is exclusive,
this option makes the criteria behave inclusively
--imagefile insert tileset data (assuming tileset 'base') and
.png extension into the image name such that it can
be used to easily locate the image file.
--stdin=MODE read data from stdin. MODE can either be 'extend'
'exclude' or 'limit'. The limit mode means that we
will only output faces that are are listed in what
stdin. The extend mode prepends stdin to the output
and will not repeat those faces again unless
duplicate is enabled. Exclude will cause it to not
output faces that appear in stdin, but exclude will
not do anything at all if duplicate is enabled. Note
that this will hang the program is nothing is passed
to stdin and that include has no effect on this.
Graphical Display:
--imagemagick Use the external imagemagick program to create and
display (defaultly using eog) a montage of outputted
faces. Stores temporary montage file in
/tmp/cffaces.png normally.
--imagelabels When using the imagemagick flag, this will cause
labels for the face to be inserted under the face.
Note that this does nothing outside of imagemagick
mode, and the rendering of the labels can take a
*long* time.
Criteria:
--nopass Only list faces for objects with move_block set
--floor Only list faces for objects with is_floor 1
--alive Only list faces for objects with alive 1 (note that
this is not an accurate way to detect monsters or
players)
--player Only list faces for objects that are players
--monster Only list faces for objects that are monsters
--spelleffect Only list faces for objects that are spelleffects
--exit Only list faces for objects that are exits
--teleporter Only list faces for objects that are teleporters
--weapon Only list faces for objects that are weapons
--bow Only list faces for objects that are bows
--arrow Only list faces for objects that are arrows
--weapon Only list faces for objects that are some type of
armour
--currency Only list faces for objects that are money or gems
--type=NUM Only list faces for objects which have a type of NUM
--subtype=NUM Only list faces for objects which have a subtype of
NUM
--race=PATTERN Only list faces for objects with a race that
matches PATTERN
--slay=PATTERN Only list faces for objects with a slaying that
matches PATTERN
--searchname=PATTERN Only list faces for objects with a name that
matches PATTERN
--searchbody=PATTERN Only list faces for objects with a object data that
matches PATTERN
PATH is the path of the archtypes directory, and if no PATH is given it
defaults to ../arch
PATTERN is always in the form of a perl regexp, though plaintext usually
works too.
";
die $usagemessage;
}
sub archhandle {
if (/^.*\.arc\z/s) {
open (ARCHDATA, $File::Find::name) || die "Couldn't open $File::Find::name \n";
while (<ARCHDATA>) {
@words =split(/ /);
if ($_ =~ /^mina/) { $countframes=0; $animcount=0; }
if (($countframes==1) && ($_ =~ /^facings /)) {next;}
if ($countframes==1) {
$animcount++;
#skip the first face of animation for gates if we're looking for nopass,
#because that's the fully open frame.
if ((($itype =~ /^91$/) or ($itype =~ /^26$/)) and ($animcount==1) and $nopass) {next;} else {
$_ =~ s/\s+$//; $_=$_."\n";
$faces = $faces.$_;
next;
}
}
if ($_ =~ /^More/) { $morestatus=1; }
if ($_ =~ /^Object /) { chomp($name=$words[1]); $obstatus=1; $doneobject=0;
#reset variables unless "More" just happened (as in some multi-tile monsters)
if (not $morestatus) {
$ialive=0;
$imonster=0;
$ifloor=0;
$iteardown=0;
$itype="";
$istype="";
$race="";
$slay="";
$face="";
$pname="";
$bodyinfo="";
}
$animcount=0;
$inopass=0;
$faces="";
}
if ($_ =~ /^name /) { chomp($pname=$words[1]); }
if ($_ =~ /^end$/) { $newob=1; $obstatus=0; $countframes=0; $doneobject=1; }
if ($_ =~ /^anim$/) { $countframes=1; $hasobject=1 }
if ($_ =~ /^move_block [^0]/) {$inopass=1;}
if ($_ =~ /^alive 1/) {$ialive=1;}
if ($_ =~ /^is_floor 1/) {$ifloor=1;}
if ($_ =~ /^monster 1/) {$imonster=1;}
if ($_ =~ /^no_pass 0/) {$inopass=0;}
if ($_ =~ /^alive 0/) {$ialive=0;}
if ($_ =~ /^is_floor 0/) {$ifloor=0;}
if ($_ =~ /^monster 0/) {$imonster=0;}
if ($_ =~ /^type /) { $words[1] =~ s/\s+$//; $itype=$words[1]; }
if ($_ =~ /^subtype /) { $words[1] =~ s/\s+$//; $istype=$words[1]; }
if ($_ =~ /^race /) { $words[1] =~ s/\s+$//; $race=$words[1]; }
if ($_ =~ /^slaying /) { $words[1] =~ s/\s+$//; $slay=$words[1]; }
if ($_ =~ /^tear_down 1/) {$iteardown=1;}
if (($_ =~ /animation /) && ($obstatus==1)) { $words[1] =~ s/\s+$//; $faces=$faces."".$animfaces{$words[1]}.""; }
if ($_ =~ /^face/) { $words[1] =~ s/\s+$//; chomp($face=$words[1]); }
if (($obstatus==1) and not ($_ =~ /^end$/)) { $bodyinfo=$bodyinfo.$_; }
next if ($newob==0);
if ($doneobject>=1) {
#we're done with an object.
$faces = $faces."$face\n";
#check if it fits critera
if ($inclusiveflags) {
$nopasscheck = ($inopass and $nopass);
$monstercheck = ((($imonster and not ($iteardown or ($itype =~ /^62$/) or ($itype =~ /^101$/)))) and $monster);
$alivecheck = ($ialive and $alive);
$playercheck = (((($itype =~ /^37$/) or ($itype =~ /^1$/)) or (($itype =~ /^74$/) and ($istype =~ /^31$/))) and $player);
$bsearchcheck = (($bodyinfo =~ /$bodyre/) and $searchbstring);
$searchcheck = ((($name =~ /$namere/) or ($pname =~ /$namere/)) and $searchstring);
$racecheck = (($race =~ /$racere/) and $racestring);
$slaycheck = (($slay =~ /$slayre/) and $slaystring);
$floorcheck = ($ifloor and $floor);
$spellcheck = (($itype =~ /^102$/) and $spelleffect);
$typecheck1 = (($itype == $typenum) and $checktype);
$typecheck2 = (($istype == $stypenum) and $checkstype);
$exitcheck = (($itype =~ /^66$/) and $exitflag);
$telecheck = (($itype =~ /^41$/) and $teleflag);
$weapcheck = (($itype =~ /^15$/) and $weapflag);
$bowcheck = (($itype =~ /^14$/) and $bowflag);
$arrowcheck = (($itype =~ /^13$/) and $arrowflag);
$armourcheck = ((($itype =~ /^99$/) or ($itype =~ /^100$/) or ($itype =~ /^104$/) or ($itype =~ /^87$/) or ($itype =~ /^113$/) or ($itype =~ /^34$/) or ($itype =~ /^16$/) or ($itype =~ /^133$/)) and $armourflag);
$currcheck = ((($itype =~ /^36$/) or ($itype =~ /^60$/)) and $currencyflag);
$criteria = ($nopasscheck or $monstercheck or $alivecheck or $playercheck or $searchcheck or $bsearchcheck or $racecheck or $slaycheck or $floorcheck or $spellcheck or $typecheck1 or $typecheck2 or $exitcheck or $telecheck or $weapcheck or $bowcheck or $arrowcheck or $armourcheck or $currcheck);
} else {
$nopasscheck = ($inopass or not $nopass);
$monstercheck = (($imonster and not ($iteardown or ($itype =~ /^62$/) or ($itype =~ /^101$/))) or not $monster);
$alivecheck = ($ialive or not $alive);
$playercheck = ((($itype =~ /^37$/) or ($itype =~ /^1$/)) or (($itype =~ /^74$/) and ($istype =~ /^31$/)) or not $player);
$bsearchcheck = (($bodyinfo =~ /$bodyre/) or not $searchbstring);
$searchcheck = ((($name =~ /$namere/) or ($pname =~ /$namere/)) or not $searchstring);
$racecheck = (($race =~ /$racere/) or not $racestring);
$slaycheck = (($slay =~ /$slayre/) or not $slaystring);
$floorcheck = ($ifloor or not $floor);
$spellcheck = (($itype =~ /^102$/) or not $spelleffect);
$typecheck1 = (($itype == $typenum) or not $checktype);
$typecheck2 = (($istype == $stypenum) or not $checkstype);
$exitcheck = (($itype =~ /^66$/) or not $exitflag);
$telecheck = (($itype =~ /^41$/) or not $teleflag);
$weapcheck = (($itype =~ /^15$/) or not $weapflag);
$bowcheck = (($itype =~ /^14$/) or not $bowflag);
$arrowcheck = (($itype =~ /^13$/) or not $arrowflag);
$armourcheck = ((($itype =~ /^99$/) or ($itype =~ /^100$/) or ($itype =~ /^104$/) or ($itype =~ /^87$/) or ($itype =~ /^113$/) or ($itype =~ /^34$/) or ($itype =~ /^16$/) or ($itype =~ /^133$/)) or not $armourflag);
$currcheck = ((($itype =~ /^36$/) or ($itype =~ /^60$/)) or not $currencyflag);
$criteria = ($nopasscheck and $monstercheck and $alivecheck and $playercheck and $searchcheck and $bsearchcheck and $racecheck and $slaycheck and $floorcheck and $spellcheck and $typecheck1 and $typecheck2 and $exitcheck and $telecheck and $weapcheck and $bowcheck and $arrowcheck and $armourcheck and $currcheck);
}
if ($criteria) {
#split the face list into an array and go through the elements
@facearray = split(/\n/, $faces);
foreach $face2(@facearray) {
$veryoldface=$face2;
if ($imagefileflag or $imagemagickflag) {$face2 =~ s/\./\.base\./; $face2=$face2.".png";}
$unique{$face2}++;
#check that we haven't already outputted this face
if ((($unique{$face2}<=1) or $duplicateflag) and not (($face2 =~ /^blank.111$/) or ($face2 =~ /^empty.111$/))) {
$oldface = $face2;
if ($showobjectflag) { $face2=$name.": ".$face2; }
if ($showfileflag) { $face2=$File::Find::name.": ".$face2; }
#make sure that we never output the EXACT same line twice
$unique2{($File::Find::name.": ".$name.": ".$face2)}++;
if (($unique2{($File::Find::name.": ".$name.": ".$face2)}<=1) and (($stdinlist{$oldface}>=1) or not ($stdinlimit=~/limit/))) {
if (!$imagemagickflag) {print $face2."\n";}
$fcount++;
if ($imagelabelsflag) {$imprams="-label ".$veryoldface." ";} else {$imprams="";}
$outfacefiles[$fcount] = $imprams.$pngloc{$oldface};
}
}
}
}
$morestatus=0;
$doneobject=0;
}
}
close (ARCHDATA);
}
}
sub facehandle {
if (/^.*\.face\z/s) {
open (FACEDATA, $File::Find::name) || die "Couldn't open $File::Find::name";
while ($record = <FACEDATA>) {
@words = split(/ /,$record);
if ($record =~ /^mina/) {
$countframes=0;
$animfaces{$aname} = $afaces;
$afaces="";
$aname="";
}
if (($countframes==1) && ($record =~ /^facings /)) {next;}
if ($countframes==1) { $record =~ s/\s+$//; $afaces = $afaces.$record."\n"; next; }
if ($record =~ /^animation /) { $countframes=1; $words[1] =~ s/\s+$//; $aname=$words[1]; next; }
}
close(FACEDATA);
} elsif (/^.*\.png\z/s) {
#record exact locations of png images
$tmpname = $File::Find::name;
$tmpdir = $File::Find::dir."/";
$tmpname =~ s/$tmpdir//;
$tmpname =~ s/\s+$//;
$pngloc{$tmpname} = $File::Find::name;
}
}
sub imagemagickdisp {
$ifiles = "";
foreach $ifloc(@outfacefiles) {
$ifloc =~ s/\s+$//;
$ifiles=$ifiles.$ifloc." ";
}
if ($imagelabelsflag) {$geometry="110x80+0+0>";} else {$geometry="70x70+0+0>";}
$montagecmd = "montage -geometry ".$geometry." -frame 2x2+0+0 -tile 8 -background grey -mattecolor grey ";
$montagefile = "/tmp/cffaces.png";
$viewcommand = "eog ";
print "Face list assembled.\n";
#print "Command:\n".$montagecmd.$ifiles.$montagefile."\n";
system(split(/ /, ($montagecmd.$ifiles.$montagefile) ));
print "Face images read. Displaying faces...\n";
exec(split(/ /, ($viewcommand.$montagefile) ));
}