* MAT_CONTAINER - match a container I'm in
* MAT_REMOTE_CONTENTS - match the contents of a remote location
* MAT_ENGLISH - match natural english 'my 2nd flower'
+ * MAT_TYPE - match only objects of the given type(s)
* MAT_EVERYTHING - me,here,absolute,player,neighbor,possession,exit
* MAT_NEARBY - everything near
* MAT_OBJECTS - me,absolute,player,neigbor,possession
#include "parse.h"
#include "flags.h"
#include "dbdefs.h"
+#include "confmagic.h"
+#include "attrib.h"
-static dbref match_result_internal
- (dbref who, const char *name, int type, long flags);
-static dbref simple_matches(dbref who, const char *name, long flags);
static int parse_english(char **name, long *flags);
-static dbref match_me(const dbref who, const char *name);
-static dbref match_here(const dbref who, const char *name);
-/** Convenience alias for parse_objid */
-#define match_absolute(name) parse_objid(name)
-static dbref match_player(const dbref matcher, const char *match_name);
-static dbref choose_thing(const dbref match_who, const int preferred_type,
- long int flags, dbref thing1, dbref thing2);
+static dbref match_player(dbref who, const char *name);
extern int check_alias(const char *command, const char *list); /* game.c */
+static int match_aliases(dbref who, const char *name);
+static dbref choose_thing(const dbref who, const int preferred_type, long flags, dbref thing1, dbref thing2);
-
-/** A wrapper for returning a match, AMBIGUOUS, or NOTHING.
- * This function attempts to match a name for who, and
- * can return the matched dbref, AMBIGUOUS, or NOTHING.
- * \param who the looker.
- * \param name name to try to match.
- * \param type type of object to match.
- * \param flags match flags.
- * \return dbref of matched object, or AMBIGUOUS, or NOTHING.
- */
dbref
-match_result(const dbref who, const char *name, const int type,
- const long flags)
+noisy_match_result(const dbref who, const char *name, const int type, const long flags)
{
- return match_result_internal(who, name, type, flags);
-}
+ dbref match;
-/** A noisy wrapper for returning a match or NOTHING.
- * This function attempts to match a name for who, and
- * can return the matched dbref or NOTHING (in ambiguous cases,
- * NOTHING is returned). If no match is made, the looker is notified
- * of the failure to match or ambiguity.
- * \param who the looker.
- * \param name name to try to match.
- * \param type type of object to match.
- * \param flags match flags.
- * \return dbref of matched object, or NOTHING.
- */
-dbref
-noisy_match_result(const dbref who, const char *name, const int type,
- const long flags)
-{
- return match_result_internal(who, name, type, flags | MAT_NOISY);
+ match = match_result(who, name, type, flags | MAT_NOISY);
+ if (!GoodObject(match))
+ return NOTHING;
+ else
+ return match;
}
-/** A noisy wrapper for returning a match or NOTHING.
- * This function attempts to match a name for who, and
- * can return the matched dbref or NOTHING. In ambiguous cases,
- * the last matched thing is returned.
- * \param who the looker.
- * \param name name to try to match.
- * \param type type of object to match.
- * \param flags match flags.
- * \return dbref of matched object, or NOTHING.
- */
dbref
-last_match_result(const dbref who, const char *name, const int type,
- const long flags)
+last_match_result(const dbref who, const char *name, const int type, const long flags)
{
- return match_result_internal(who, name, type, flags | MAT_LAST);
+ return match_result(who, name, type, flags | MAT_LAST);
}
-/** Wrapper for a noisy match with control checks.
- * This function performs a noisy_match_result() and then checks that
- * the looker controls the matched object before returning it.
- * If the control check fails, the looker is notified and NOTHING
- * is returned.
- * \param player the looker.
- * \param name name to try to match.
- * \return dbref of matched controlled object, or NOTHING.
- */
dbref
match_controlled(dbref player, const char *name)
{
- dbref match;
- match = noisy_match_result(player, name, NOTYPE, MAT_EVERYTHING);
- if (GoodObject(match) && !controls(player, match)) {
- notify(player, T("Permission denied."));
- return NOTHING;
- } else {
- return match;
- }
+ return noisy_match_result(player, name, NOTYPE, MAT_EVERYTHING | MAT_CONTROL);
}
+
/* The real work. Here's the spec:
* str --> "me"
* --> "here"
* c. *player
* 2. Parse for adj-phrases and restrict further matching and/or
* remember the object count
- * 3. Look for matches (remote contents, neighbor, inventory, exits,
+ * 3. Look for matches (remote contents, neighbor, inventory, exits,
* containers, carried exits)
* a. If we don't have an object count, collect the number of exact
* and partial matches and the best partial match.
* e. If we got no matches, complain
*/
-#define MATCH_NONE 0x0 /**< No matches were found */
-#define MATCH_EXACT 0x1 /**< At least one exact match found */
-#define MATCH_PARTIAL 0x2 /**< At least one partial match found, no exact */
-/** Prototype for matching functions */
-#define MATCH_FUNC_PROTO(fun_name) \
- /* ARGSUSED */ /* try to keep lint happy */ \
- static int fun_name(const dbref who, const char *name, const int type, \
- const long flags, dbref first, \
- dbref *match, int *exact_matches_to_go, int *matches_to_go)
-/** Common declaration for matching functions */
-#define MATCH_FUNC(fun_name) \
- static int fun_name(const dbref who, const char *name, const int type, \
- const long flags, dbref first __attribute__ ((__unused__)), \
- dbref *match, int *exact_matches_to_go, int *matches_to_go)
-/** Macro to execute matching and store some results */
-#define RUN_MATCH_FUNC(fun,first) \
- { \
- result = fun(who, name, type, flags, first, &match, \
- &exact_matches_to_go, &matches_to_go); \
- if (result == MATCH_EXACT) { \
- exact_match = match; \
- /* If it's the nth exact match, we're done */ \
- if (matchnum && !exact_matches_to_go) \
- goto finished; \
- /* If it's the n'th match, remember it */ \
- if (matchnum && !matches_to_go) \
- last_match = match; \
- } else if (result == MATCH_PARTIAL) { \
- if (!matchnum || !matches_to_go) \
- last_match = match; \
+/*
+#define DEBUG_OBJECT_MATCHING
+/**/
+#ifdef DEBUG_OBJECT_MATCHING
+static dbref debugMatchTo = 1;
+#endif
+
+#define MATCHED(full) \
+ { \
+ if (!MATCH_CONTROLS) { \
+ /* Found a matching object, but we lack necessary control */ \
+ nocontrol = 1; \
+ continue; \
+ } \
+ if (!final) { \
+ bestmatch = BEST_MATCH; \
+ if (bestmatch != match) { \
+ /* Previously matched item won over due to type, @lock, etc, checks */ \
+ continue; \
+ } \
+ if (full) { \
+ if (exact) { \
+ /* Another exact match */ \
+ curr++; \
+ } else { \
+ /* Ignore any previous partial matches now we have an exact match */ \
+ exact = 1; \
+ curr = 1; \
+ } \
+ } else { \
+ /* Another partial match */ \
+ curr++; \
+ } \
+ } else { \
+ curr++; \
+ if (curr == final) { \
+ /* we've successfully found the Nth item */ \
+ bestmatch = match; \
+ done = 1; \
+ break; \
+ } \
+ } \
+ }
+
+
+#define MATCH_LIST(start) \
+ { \
+ if (done) \
+ break; /* already found the Nth object we needed */ \
+ match = start; \
+ DOLIST(match, match) { \
+ if (!MATCH_TYPE) { \
+ /* Exact-type match required, but failed */ \
+ continue; \
+ } else if (match == abs) { \
+ /* absolute dbref match in list */ \
+ MATCHED(1); \
+ } else if (!can_interact(match, who, INTERACT_MATCH)) { \
+ /* Not allowed to match this object */ \
+ continue; \
+ } else if (match_aliases(match, name) || (!IsExit(match) && !strcasecmp(Name(match), name))) { \
+ /* exact name match */ \
+ MATCHED(1); \
+ } else if ((!exact || !GoodObject(bestmatch)) && !IsExit(match) && string_match(Name(match), name)) { \
+ /* partial name match */ \
+ MATCHED(0); \
+ } \
} \
- }
+ }
+
+#define MATCH_CONTROLS (!(flags & MAT_CONTROL) || controls(who, match))
+
+#define MATCH_TYPE ((type & Typeof(match)) ? 1 : ((flags & MAT_TYPE) ? 0 : -1))
-MATCH_FUNC_PROTO(match_possession);
-MATCH_FUNC_PROTO(match_neighbor);
-MATCH_FUNC_PROTO(match_exit);
-MATCH_FUNC_PROTO(match_exit_internal);
-MATCH_FUNC_PROTO(match_container);
-MATCH_FUNC_PROTO(match_list);
+#define BEST_MATCH choose_thing(who, type, flags, bestmatch, match)
static dbref
-match_result_internal(dbref who, const char *xname, int type, long flags)
+choose_thing(const dbref who, const int preferred_type, long flags, dbref thing1, dbref thing2)
{
- dbref match = NOTHING, last_match = NOTHING, exact_match = NOTHING;
- int exact_matches_to_go, matches_to_go;
- int matchnum = 0;
- int result;
- char *name, *sname;
+ int key;
+ /* If there's only one valid thing, return it */
+ /* Check == NOTHING, not GoodObject, to make sure we favour AMBIGUOUS over NOTHING */
+ /* (Apologies to Theodor Geisel) */
+ if (thing1 == NOTHING) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo,"Picking #%d over #%d (a nothing)", thing2, thing1);
+#endif
+ return thing2;
+ } else if (thing2 == NOTHING) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo,"Picking #%d over #%d (a nothing)", thing1, thing2);
+#endif
+ return thing1;
+ }
- /* The quick ones that can never be ambiguous */
- match = simple_matches(who, xname, flags);
- if (GoodObject(match))
- return match;
+ /* If a type is given, and only one thing is of that type, return it */
+ if (preferred_type != NOTYPE) {
+ if (Typeof(thing1) & preferred_type) {
+ if (!(Typeof(thing2) & preferred_type)) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo,"Picking #%d over #%d (type)", thing1, thing2);
+#endif
+ return thing1;
+ }
+ } else if (Typeof(thing2) & preferred_type) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo,"Picking #%d over #%d (type)", thing2, thing1);
+#endif
+ return thing2;
+ }
+ }
- sname = name = mush_strdup(xname, "mri.string");
+ if (flags & MAT_CHECK_KEYS) {
+ key = could_doit(who, thing1);
+ if (!key && could_doit(who, thing2)) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo,"Picking #%d over #%d (unlocked)", thing2, thing1);
+#endif
+ return thing2;
+ } else if (key && !could_doit(who, thing2)) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo,"Picking #%d over #%d (unlocked)", thing1, thing2);
+#endif
+ return thing1;
+ }
+ }
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo,"Picking #%d over #%d (last matched)", thing2, thing1);
+#endif
+ /* No luck. Return last match */
+ return thing2;
+}
- if (flags & MAT_ENGLISH) {
- /* Check for adjective phrases */
- matchnum = parse_english(&name, &flags);
+static dbref
+match_player(dbref who, const char *name) {
+ dbref match;
+
+ if (*name == LOOKUP_TOKEN) {
+ name++;
}
- /* Perform matching. We've already had flags restricted by any
- * adjective phrases. If matchnum is set, collect the matchnum'th
- * exact match (and stop) and the matchnum'th match (exact or partial,
- * and store this in case we don't get enough exact matches).
- * If not, collect the number of exact and partial matches and the
- * last exact and partial matches.
- */
- exact_matches_to_go = matches_to_go = matchnum;
- if (flags & MAT_POSSESSION)
- RUN_MATCH_FUNC(match_possession, NOTHING);
- if (flags & MAT_NEIGHBOR)
- RUN_MATCH_FUNC(match_neighbor, NOTHING);
- if (flags & MAT_REMOTE_CONTENTS)
- RUN_MATCH_FUNC(match_possession, NOTHING);
- if (flags & MAT_EXIT)
- RUN_MATCH_FUNC(match_exit, NOTHING);
- if (flags & MAT_CONTAINER)
- RUN_MATCH_FUNC(match_container, NOTHING);
- if (flags & MAT_CARRIED_EXIT)
- RUN_MATCH_FUNC(match_exit_internal, who);
-
-finished:
- /* Set up the default match_result behavior */
- if (matchnum) {
- /* nth exact match? */
- if (!exact_matches_to_go)
- match = exact_match;
- else if (GoodObject(last_match))
- match = last_match; /* nth exact-or-partial match, or nothing? */
- /* This shouldn't happen, but just in case we have a valid match,
- * and an invalid last_match in the matchnum case, fall through and
- * use the match.
- */
- } else if (GoodObject(exact_match)) {
- /* How many exact matches? */
- if (exact_matches_to_go == -1)
- match = exact_match; /* Good */
- else if (flags & MAT_LAST)
- match = exact_match; /* Good enough */
- else
- match = AMBIGUOUS;
- } else {
- if (!matches_to_go)
- match = NOTHING; /* No matches */
- else if (matches_to_go == -1)
- match = last_match; /* Good */
- else if (flags & MAT_LAST)
- match = last_match; /* Good enough */
- else
- match = AMBIGUOUS;
+ while(isspace((unsigned char) *name)) {
+ name++;
}
- /* Handle noisy_match_result */
- if (flags & MAT_NOISY) {
- mush_free(sname, "mri.string");
- switch (match) {
- case NOTHING:
- notify(who, T("I can't see that here."));
- return NOTHING;
- case AMBIGUOUS:
- notify(who, T("I don't know which one you mean!"));
- return NOTHING;
- default:
- return match;
+ match = lookup_player(name);
+ if (match != NOTHING) {
+ return match;
+ }
+ return visible_short_page(who, name);
+}
+
+static int
+match_aliases(dbref who, const char *name) {
+
+ if (!IsPlayer(who) && !IsExit(who)) {
+ return 0;
+ }
+
+ if (IsExit(who)) {
+ return check_alias(name, Name(who));
+ } else {
+ char tbuf1[BUFFER_LEN];
+ ATTR *a = atr_get_noparent(who, "ALIAS");
+ if (!a) {
+ return 0;
}
+ mush_strncpy(tbuf1, atr_value(a), BUFFER_LEN);
+ return check_alias(name, tbuf1);
}
- mush_free(sname, "mri.string");
- return match;
+
}
-static dbref
-simple_matches(dbref who, const char *name, long flags)
+dbref
+match_result(dbref who, const char *xname, int type, long flags)
{
- dbref match = NOTHING;
- if (flags & MAT_ME) {
- match = match_me(who, name);
- if (GoodObject(match))
- return match;
+ dbref match, loc;
+ dbref bestmatch = NOTHING;
+ dbref abs = parse_objid(xname);
+ int curr = 0, final = 0, nocontrol = 0, exact = 0, done = 0;
+ int goodwho = GoodObject(who);
+ char *name, *sname;
+#ifdef DEBUG_OBJECT_MATCHING
+ debugMatchTo = (IsPlayer(who) ? who : 1);
+ notify(debugMatchTo, "ENTERING MATCH_RESULT");
+ notify_format(debugMatchTo, "FLAGS: %ld, TYPE: %d", flags, (type == NOTYPE));
+#endif
+ /* match "me" */
+ match = who;
+ if (MATCH_TYPE && (flags & MAT_ME) && !strcasecmp(xname, "me")) {
+ return match;
}
- if (flags & MAT_HERE) {
- match = match_here(who, name);
- if (GoodObject(match))
+
+ /* match "here" */
+ match = Location(who);
+ if ((flags & MAT_HERE) && !strcasecmp(xname, "here") && GoodObject(match) && MATCH_TYPE) {
+ if(MATCH_CONTROLS) {
return match;
+ } else {
+ nocontrol = 1;
+ }
}
- if (!(flags & MAT_NEAR) || Long_Fingers(who)) {
- if (flags & MAT_ABSOLUTE) {
- match = match_absolute(name);
- if (GoodObject(match) && (flags & MAT_NEAR) && !CanRemote(who, match))
- goto mat_near;
- if (GoodObject(match)) {
- if (flags & MAT_CONTROL) {
- /* Check for control */
- if (controls(who, match) || nearby(who, match))
- return match;
- } else {
- return match;
- }
+
+ /* match *<player>, or <player> */
+ match = match_player(who, xname);
+ if (((flags & MAT_PMATCH) || ((flags & MAT_PLAYER) && *xname == LOOKUP_TOKEN)) && MATCH_TYPE) {
+ if (GoodObject(match)) {
+ if (MATCH_CONTROLS) {
+ return match;
+ } else {
+ nocontrol = 1;
}
+ } else {
+ bestmatch = BEST_MATCH;
}
- if (flags & MAT_PLAYER) {
- match = match_player(who, name);
- if (GoodObject(match))
- return match;
+ }
+
+ /* dbref match */
+ match = abs;
+ if (GoodObject(match) && MATCH_TYPE) {
+ if (!(flags & MAT_NEAR) || Long_Fingers(who) || (nearby(who, match) || controls(who, match))) {
+ /* valid dbref match */
+ if (MATCH_CONTROLS) {
+ return match;
+ } else {
+ nocontrol = 1;
+ }
}
- } else {
- /* We're doing a nearby match and the player doesn't have
- * long_fingers, so it's a controlled absolute
- */
-mat_near:
- match = match_absolute(name);
- if (GoodObject(match) && (controls(who, match) || nearby(who, match)))
- return match;
}
- return NOTHING;
+
+ sname = name = mush_strdup(xname, "mri.string");
+ if (flags & MAT_ENGLISH) {
+ /* English-style matching */
+ final = parse_english(&name, &flags);
+ }
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo, "AFTER ENGLISH, we have: name = %s, curr = %d, flags = %ld", name, curr, flags);
+#endif
+
+ while (1) {
+ loc = (goodwho ? (IsRoom(who) ? who : Location(who)) : NOTHING);
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo, "Running for #%d in #%d", who, loc);
+#endif
+ if (goodwho && ((flags & MAT_POSSESSION) || (flags & MAT_REMOTE_CONTENTS))) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify(debugMatchTo, "STARTING POSSESSION");
+#endif
+ MATCH_LIST(Contents(who));
+ }
+ if (GoodObject(loc) && (flags & MAT_NEIGHBOR)) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify(debugMatchTo, "STARTING NEIGHBOURS");
+#endif
+ MATCH_LIST(Contents(loc));
+ }
+ if ((type & TYPE_EXIT) || !(flags & MAT_TYPE)) {
+ if (GoodObject(loc) && IsRoom(loc) && (flags & MAT_EXIT)) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo, "STARTING EXIT");
+#endif
+ if (flags & MAT_REMOTES && GoodObject(Zone(loc)) && IsRoom(Zone(loc))) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify(debugMatchTo, "STARTING EXIT-REMOTE");
+#endif
+ MATCH_LIST(Exits(Zone(loc)));
+ }
+ if (flags & MAT_GLOBAL) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify(debugMatchTo, "STARTING EXIT-GLOBAL");
+#endif
+ MATCH_LIST(Exits(MASTER_ROOM));
+ }
+ if (GoodObject(loc) && IsRoom(loc)) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify(debugMatchTo, "STARTING EXITS");
+#endif
+ MATCH_LIST(Exits(loc));
+ }
+ }
+ }
+ if ((flags & MAT_CONTAINER) && goodwho) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify(debugMatchTo, "STARTING CONTAINER");
+#endif
+ MATCH_LIST(loc);
+ }
+ if ((type & TYPE_EXIT) || !(flags & MAT_TYPE)) {
+ if ((flags & MAT_CARRIED_EXIT) && goodwho && IsRoom(who)) {
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo, "STARTING CEXIT");
+#endif
+ MATCH_LIST(Exits(who));
+ }
+ }
+ break;
+ }
+#ifdef DEBUG_OBJECT_MATCHING
+ notify_format(debugMatchTo, "AT END, we have: final = %d, curr = %d, bestmatch = %d", final, curr, bestmatch);
+#endif
+ if (!GoodObject(bestmatch) && final) {
+ /* we never found the Nth item */
+ bestmatch = NOTHING;
+ } else if (!final && curr > 1) {
+ if (!(flags & MAT_LAST)) {
+ bestmatch = AMBIGUOUS;
+ }
+ }
+
+ if (!GoodObject(bestmatch) && (flags & MAT_NOISY)) {
+ /* give error message */
+ if (bestmatch == AMBIGUOUS) {
+ notify(who, T("I don't know which one you mean!"));
+ } else if (nocontrol) {
+ notify(who, T("Permission denied."));
+ } else {
+ notify(who, T("I can't see that here."));
+ }
+ }
+
+ return bestmatch;
}
-/*
+/*
* adj-phrase --> adj
* --> adj count
* --> count
while (**name == ' ')
(*name)++;
- /* If the name was just 'toward' (with no object name), reset
+ /* If the name was just 'toward' (with no object name), reset
* everything and press on.
*/
if (!**name) {
return count;
}
-
-static dbref
-match_me(const dbref who, const char *name)
-{
- return (!strcasecmp(name, "me")) ? who : NOTHING;
-}
-
-static dbref
-match_here(const dbref who, const char *name)
-{
- return (!strcasecmp(name, "here") && GoodObject(Location(who))) ?
- Location(who) : NOTHING;
-}
-
-
-static dbref
-match_player(const dbref matcher, const char *match_name)
-{
- dbref match;
- const char *p;
-
- if (*match_name != LOOKUP_TOKEN)
- return NOTHING;
- for (p = match_name + 1; isspace((unsigned char) *p); p++) ;
- /* If lookup_player fails, try a partial match on connected
- * players, 2.0 style. That can return match, NOTHING, AMBIGUOUS
- */
- match = lookup_player(p);
- return (match != NOTHING) ? match : visible_short_page(matcher, p);
-}
-
-/* Run down a contents list and try to match */
-MATCH_FUNC(match_list)
-{
- dbref absolute;
- dbref alias_match;
- int match_type = MATCH_NONE;
- int nth_match = (*exact_matches_to_go != 0);
-
- /* If we were given an absolute dbref, remember it */
- absolute = match_absolute(name);
- /* If we were given a player name, remember it */
- alias_match = lookup_player(name);
-
- DOLIST(first, first) {
- if (first == absolute) {
- /* Got an absolute match, return it */
- *match = first;
- (*exact_matches_to_go)--;
- (*matches_to_go)--;
- return MATCH_EXACT;
- } else if( can_interact(first, who, INTERACT_MATCH) && (!strcasecmp(Name(first), name) ||
- (GoodObject(alias_match) && (alias_match == first)))) {
- /* An exact match, but there may be others */
- (*exact_matches_to_go)--;
- (*matches_to_go)--;
- if (nth_match) {
- if (!(*exact_matches_to_go)) {
- /* We're done */
- *match = first;
- return MATCH_EXACT;
- }
- } else {
- if (match_type == MATCH_EXACT)
- *match = choose_thing(who, type, flags, *match, first);
- else
- *match = first;
- match_type = MATCH_EXACT;
- }
- } else if ((match_type != MATCH_EXACT)
- && string_match(Name(first), name)
- && can_interact(first, who, INTERACT_MATCH)) {
- /* A partial match, and we haven't done an exact match yet */
- (*matches_to_go)--;
- if (nth_match) {
- if (!(*matches_to_go))
- *match = first;
- } else if (match_type == MATCH_PARTIAL)
- *match = choose_thing(who, type, flags, *match, first);
- else
- *match = first;
- match_type = MATCH_PARTIAL;
- }
- }
- /* If we've made the nth partial match in this round, there's none to go */
- if (nth_match && *matches_to_go < 0)
- *matches_to_go = 0;
- return match_type;
-}
-
-/* We recursively search upwards trying to match exits */
-MATCH_FUNC(match_exit)
-{
- dbref loc;
- int mret = MATCH_NONE; /* FIXME: temporary workaround, see #250 */
-
- loc = (IsRoom(who)) ? who : Location(who);
- if (flags & MAT_REMOTES) {
- if (GoodObject(loc))
- return match_exit_internal(who, name, type, flags, Zone(loc),
- match, exact_matches_to_go, matches_to_go);
- else
- return NOTHING;
- } else if (flags & MAT_GLOBAL)
- return match_exit_internal(who, name, type, flags, MASTER_ROOM,
- match, exact_matches_to_go, matches_to_go);
- /* Recursively search upwards in locations parent tree */
- while(GoodObject(loc) && !IsGarbage(loc) && mret != MATCH_EXACT ) {
- mret = match_exit_internal(who, name, type, flags, loc,
- match, exact_matches_to_go, matches_to_go);
- loc = Parent(loc);
- }
- return mret;
-}
-
-MATCH_FUNC(match_exit_internal)
-{
- dbref exit_tmp;
- dbref absolute;
- int match_type = MATCH_NONE;
- int nth_match = (*exact_matches_to_go != 0);
-
- if (!GoodObject(first) || !IsRoom(first) || !name || !*name)
- return NOTHING;
- /* Store an absolute dbref match if given */
- absolute = match_absolute(name);
- DOLIST(exit_tmp, Exits(first)) {
-
- if ((exit_tmp == absolute) && (can_interact(exit_tmp, who, INTERACT_MATCH))) {
- /* Absolute match. Return immediately */
- *match = exit_tmp;
- (*exact_matches_to_go)--;
- (*matches_to_go)--;
- return MATCH_EXACT;
- } else if (check_alias(name, Name(exit_tmp))
- && (can_interact(exit_tmp, who, INTERACT_MATCH))) {
- /* Matched an exit alias, but there may be more */
- (*exact_matches_to_go)--;
- (*matches_to_go)--;
- if (nth_match) {
- if (!(*exact_matches_to_go)) {
- /* We're done */
- *match = exit_tmp;
- return MATCH_EXACT;
- }
- } else {
- if (match_type == MATCH_EXACT)
- *match = choose_thing(who, type, flags, *match, exit_tmp);
- else
- *match = exit_tmp;
- match_type = MATCH_EXACT;
- }
- }
- }
- /* If we've made the nth partial match in this round, there's none to go */
- if (nth_match && *matches_to_go < 0)
- *matches_to_go = 0;
- return match_type;
-}
-
-
-MATCH_FUNC(match_possession)
-{
- if (!GoodObject(who))
- return NOTHING;
- return match_list(who, name, type, flags, Contents(who), match,
- exact_matches_to_go, matches_to_go);
-}
-
-MATCH_FUNC(match_container)
-{
- if (!GoodObject(who))
- return NOTHING;
- return match_list(who, name, type, flags, Location(who), match,
- exact_matches_to_go, matches_to_go);
-}
-
-MATCH_FUNC(match_neighbor)
-{
- dbref loc;
- if (!GoodObject(who))
- return NOTHING;
- loc = Location(who);
- if (!GoodObject(loc))
- return NOTHING;
- return match_list(who, name, type, flags, Contents(loc), match,
- exact_matches_to_go, matches_to_go);
-}
-
-
-static dbref
-choose_thing(const dbref match_who, const int preferred_type, long flags,
- dbref thing1, dbref thing2)
-{
- int has1;
- int has2;
- /* If there's only one valid thing, return it */
- /* (Apologies to Theodor Geisel) */
- if (thing1 == NOTHING)
- return thing2;
- else if (thing2 == NOTHING)
- return thing1;
-
- /* If a type is given, and only one thing is of that type, return it */
- if (preferred_type != NOTYPE) {
- if (Typeof(thing1) == preferred_type) {
- if (Typeof(thing2) != preferred_type)
- return thing1;
- } else if (Typeof(thing2) == preferred_type)
- return thing2;
- }
-
- /* If we've asked for a basic lock check, and only one passes, use that */
- if (flags & MAT_CHECK_KEYS) {
- has1 = could_doit(match_who, thing1);
- has2 = could_doit(match_who, thing2);
- if (has1 && !has2)
- return thing1;
- else if (has2 && !has1)
- return thing2;
- }
-
- /* No luck. Return the higher dbref */
- return (thing1 > thing2 ? thing1 : thing2);
-}