343 lines
16 KiB
Perl
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) ));
|
|
}
|
|
|