--- deliantra/server/lib/cf/match.pm 2009/10/20 05:57:08 1.20 +++ deliantra/server/lib/cf/match.pm 2010/10/12 20:15:48 1.33 @@ -1,3 +1,25 @@ +# +# This file is part of Deliantra, the Roguelike Realtime MMORPG. +# +# Copyright (©) 2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team +# +# Deliantra is free software: you can redistribute it and/or modify it under +# the terms of the Affero GNU General Public License as published by the +# Free Software Foundation, either version 3 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 Affero GNU General Public License +# and the GNU General Public License along with this program. If not, see +# . +# +# The authors can be reached via e-mail to +# + =head1 NAME cf::match - object matching language @@ -94,9 +116,9 @@ not applied in inv -is true if there is I non-object in the inventory. To negate a whole -match, you have to use a sub-match. To check whether there is I -applied object in someones inventory, write this: +is true if there is I non-applied object in the inventory. To negate +a whole match, you have to use a sub-match: To check whether there is +I applied object in someones inventory, write this: not (applied in inv) @@ -108,6 +130,10 @@ type=HORN or type=ROD +Example: see if the originator is a player. + + type=PLAYER of originator + =item in ... The in operator takes the context set and modifies it in various ways. As @@ -138,6 +164,10 @@ Replaces all objects by the objects that are on the same mapspace as them. +=item in head + +Replaces all objects by their head objects. + =item in Finds all context objects matching the condition, and then puts their @@ -201,6 +231,11 @@ but submatches start at the current object, and in this case C can be used to start at the original object once more. +=item of self + +Starts with the object initiating/asking for the match - this is basically +always the object that the match expression is attached to. + =item of source Starts with the I object - this object is sometimes passed to @@ -217,11 +252,6 @@ This object is often identical to the I (e.g. when a player casts a spell, the player is both source and originator). -=item of self - -Starts with the object initiating/asking for the match - this is basically -always the object that the match expression is attached to. - =back =head2 EXPRESSIONS @@ -240,7 +270,7 @@ Object attributes that consist of a single value (C, C, C<value> and so on) can be specified by simply using their name, in which -acse their corresponding value is used. +case their corresponding value is used. =item array objects attributes @@ -254,7 +284,7 @@ =item functions Some additional functions with or without arguments in parentheses are -available. +available. They are documented in their own section, below. =item { BLOCK } @@ -299,6 +329,32 @@ This simply evaluates to true, and simply makes matching I<any> object a bit easier to read. +=item none + +This simply evaluates to false, and simply makes matching I<never> a bit +easier to read. + +=item archname + +The same as C<< { $_->arch->archname } >> - the archetype name is commonly +used to match items, so this shortcut is provided. + +=item resist_xxx + +Resistancy values such as C<resist_physical>, C<resist_magic>, +C<resists_fire> etc. are directly available (but can also be accessed via +array syntax, i.e. C<resists[ATNR_FIRE]>). + +=item body_xxx_info and body_xxx_used + +Every body location (e.g. C<body_neck_info>, C<body_arm_used> etc.) can +be accessed via these functions (these are aliases to more cumbersome C<< { +$_->slot_info (body_xxx) } >> and C<slot_used> method calls). + +Example: (e.g. on a door) match only players that have no arms. + + match type=PLAYER and body_arm_info=0 + =item has(condition) True iff the object has a matching inventory object. @@ -332,12 +388,12 @@ | chain 'of' root root = 'object' | 'self' | 'source' | 'originator' chain = condition - | chain also deep 'in' set + | chain also deep 'in' modifier also = nothing | 'also' deep = nothing | 'deep' - set = 'inv' | 'env' | 'arch' | 'map' + modifier ='inv' | 'env' | 'arch' | 'map' | 'head' - empty = + nothing = # boolean matching condition @@ -355,6 +411,7 @@ expr = flag | sattr | aattr '[' <constant> ']' + | 'stat.' statattr | special | func '(' args ')' | '{' perl code block '}' @@ -363,6 +420,7 @@ sattr = <any scalar object attribute> aattr = <any array object attribute> flag = <any object flag> + statattr = <any stat attribute: exp, food, str, dex, hp, maxhp...> special = <any ()-less "function"> constant = <number> | '"' <string> '"' | <uppercase cf::XXX name> @@ -421,8 +479,27 @@ any => sub { 1 }, + none => sub { + 0 + }, + archname => sub { + '$_->arch->archname' + }, ); + # resist_xxx + for my $atnr (0 .. cf::NROFATTACKS - 1) { + $special{"resist_" . cf::attacktype_name ($atnr)} = sub { "\$_->resist ($atnr)" }; + } + + # body_xxx_info and _used + for my $slot (0 .. cf::NUM_BODY_LOCATIONS - 1) { + my $name = cf::object::slot_name $slot; + + $special{"body_$name\_info"} = sub { "\$_->slot_info ($slot)" }; + $special{"body_$name\_used"} = sub { "\$_->slot_used ($slot)" }; + } + sub constant { ws; @@ -438,6 +515,7 @@ our $flag = $cf::REFLECT{object}{flags}; our $sattr = $cf::REFLECT{object}{scalars}; our $aattr = $cf::REFLECT{object}{arrays}; + our $lattr = $cf::REFLECT{living}{scalars}; sub expr { # ws done by factor @@ -450,6 +528,16 @@ $res .= $expr =~ /\{([^;]+)\}/ ? $1 : "do $expr"; + } elsif (/\Gstats\.([A-Za-z0-9_]+)/gc) { + + if (exists $lattr->{$1}) { + $res .= "\$_->stats->$1"; + } elsif (exists $lattr->{"\u$1"}) { + $res .= "\$_->stats->\u$1"; + } else { + die "living statistic name expected (str, pow, hp, sp...)\n"; + } + } elsif (/\G([A-Za-z0-9_]+)/gc) { if (my $func = $func{$1}) { @@ -570,7 +658,6 @@ # if nothing follows, we have a simple condition, so # optimise a comon case. if ($defctx eq '$_' and /\G\s*(?=\)|$)/gc) { - warn "shortcut<$res>$wantarray\n";#d# return $wantarray ? "$res ? \$_ : ()" : $res; @@ -587,11 +674,14 @@ if (/\Gin\s+/gc) { my $expand; - if (/\G(inv|env|map|arch)\b/gc) { + if (/\G(inv|env|map|arch|head)\b/gc) { if ($1 eq "inv") { $expand = "map \$_->inv,"; } elsif ($1 eq "env") { $expand = "map \$_->env // (),"; + } elsif ($1 eq "head") { + $expand = "map \$_->head,"; + $deep = 0; # infinite loop otherwise } elsif ($1 eq "arch") { $expand = "map \$_->arch,"; $deep = 0; # infinite loop otherwise @@ -663,7 +753,7 @@ } if (0) {#d# - die parse 1, 'applied in inv'; + die parse 1, 'type=PLAYER and body_arm_info=0'; exit 0; } @@ -672,7 +762,7 @@ sub compile($$) { my ($wantarray, $match) = @_; my $expr = parse $wantarray, $match; - warn "MATCH DEBUG $match,$wantarray => $expr\n";#d# +# warn "MATCH DEBUG $match,$wantarray => $expr\n";#d# $expr = eval " package cf::match::exec; sub {