#include "copyright.h" /* link.c,v 2.6 1997/08/10 03:12:20 dmoore Exp */ #include "config.h" #include <ctype.h> #include "db.h" #include "params.h" #include "match.h" #include "buffer.h" #include "externs.h" /* The code in this file is essentially the 2.2 code w/ bug fixes. -- David */ /* Should really be in a header file. */ int link_exit(const dbref player, const dbref exit, const char *dest_name, dbref *dest_list); /* do_link * * Use this to link to a room that you own. It also sets home for * objects and things, and drop-to's for rooms. * It seizes ownership of an unlinked exit, and costs 1 penny * plus a penny transferred to the exit owner if they aren't you * * All destinations must either be owned by you, or be LINK_OK. */ void do_link(const dbref player, const char *thing_name, const char *dest_name, const char *ignore) { dbref thing; dbref dest; dbref good_dest[MAX_LINKS]; dbref *temp; struct match_data md; int ndest, i; init_match(player, thing_name, TYPE_EXIT, &md); match_everything(&md, Wizard(player)); if ((thing = noisy_match_result(&md)) == NOTHING) return; switch (Typeof(thing)) { case TYPE_EXIT: /* we're ok, check the usual stuff */ if (GetNDest(thing) > 0) { if (controls(player, thing, Wizard(player))) { notify(player, "That exit is already linked."); return; } else { notify(player, "Permission denied."); return; } } /* handle costs */ if (GetOwner(thing) == player) { if (!payfor(player, LINK_COST)) { notify(player, "It costs a penny to link this exit."); return; } } else { if (!payfor(player, LINK_COST + EXIT_COST)) { notify(player, "It costs two pennies to link this exit."); return; } else if (!Builder(player)) { notify(player, "Only authorized builders may seize exits."); return; } else { /* pay the owner for his loss */ IncPennies(GetOwner(thing), EXIT_COST); } } /* link has been validated and paid for; do it */ chown_object(thing, player); ndest = link_exit(player, thing, (char *) dest_name, good_dest); if (ndest == 0) { /* refund */ notify(player, "No destinations linked."); IncPennies(player, LINK_COST); break; } SetNDest(thing, ndest); MALLOC(temp, dbref, ndest); SetDest(thing, temp); for (i = 0; i < ndest; i++, temp++) { *temp = good_dest[i]; } break; case TYPE_THING: case TYPE_PLAYER: init_match(player, dest_name, TYPE_ROOM, &md); match_me(&md); match_neighbor(&md); match_absolute(&md); match_here(&md); if ((dest = noisy_match_result(&md)) == NOTHING) return; if (!controls(player, thing, Wizard(player)) || !can_link_to(player, Typeof(thing), dest)) { notify(player, "Permission denied."); return; } /* do the link */ SetLink(thing, dest); notify(player, "Home set."); break; case TYPE_ROOM: /* room dropto's */ init_match(player, dest_name, TYPE_ROOM, &md); match_neighbor(&md); match_absolute(&md); match_home(&md); if ((dest = noisy_match_result(&md)) == NOTHING) break; if (!controls(player, thing, Wizard(player)) || !can_link_to(player, Typeof(thing), dest) || (thing == dest)) { notify(player, "Permission denied."); } else { SetLink(thing, dest); /* Set a dropto. */ notify(player, "Dropto set."); } break; case TYPE_PROGRAM: notify(player, "You can't link programs to things!"); break; default: notify(player, "Internal error: weird object type."); log_status("PANIC: weird object: Typeof(%d) = %d", thing, Typeof(thing)); break; } return; } /* parse_linkable_dest() * * A utility for open and link which checks whether a given destination * string is valid. It returns a parsed dbref on success, and NOTHING * on failure. */ /* 2/2/92 by dmoore to not overflow static buffers. */ static dbref parse_linkable_dest(const dbref player, const dbref exit, const char *dest_name) { dbref dobj; /* destination room/player/thing/link */ struct match_data md; init_match(player, dest_name, NOTYPE, &md); match_home(&md); match_everything(&md, Wizard(player)); if ((dobj = match_result(&md)) == NOTHING || dobj == AMBIGUOUS) { notify(player, "I couldn't find '%s'.", dest_name); return NOTHING; #ifndef TELEPORT_TO_PLAYER } if (Typeof(dobj) == TYPE_PLAYER) { notify(player, "You can't link to players. Destination %u ignored.", player, dobj); return NOTHING; #endif /* TELEPORT_TO_PLAYER */ } if (!can_link(player, exit)) { notify(player, "You can't link that."); return NOTHING; } if (!can_link_to(player, Typeof(exit), dobj)) { notify(player, "You can't link to %u.", player, dobj); return NOTHING; } else { return dobj; } } /* exit_loop_check() * * Recursive check for loops in destinations of exits. Checks to see * if any circular references are present in the destination chain. * Returns 1 if circular reference found, 0 if not. */ static int exit_loop_check(const dbref source, const dbref dest) { int ndest, i; dbref *temp; if (source == dest) return 1; /* That's an easy one! */ if (Typeof(dest) != TYPE_EXIT) return 0; ndest = GetNDest(dest); temp = GetDest(dest); for (i = 0; i < ndest; i++, temp++) { if (*temp == source) return 1; /* Found a loop. */ if (Typeof(*temp) == TYPE_EXIT) { /* Check recursively. */ if (exit_loop_check(source, *temp)) return 1; } } return 0; /* No loops found */ } /* * link_exit() * * This routine connects an exit to a bunch of destinations. * * 'player' contains the player's name. * 'exit' is the the exit whose destinations are to be linked. * 'dest_name' is a character string containing the list of exits. * * 'dest_list' is an array of dbref's where the valid destinations are * stored. * */ int link_exit(const dbref player, const dbref exit, const char *dest_name, dbref *dest_list) { int prdest; dbref dest; int ndest; static Buffer buf; prdest = 0; ndest = 0; while (*dest_name) { /* Skip white space. */ while (*dest_name && isspace(*dest_name)) dest_name++; /* Reset buffer to hold exit name. */ Bufcpy(&buf, ""); /* Stuff characters of the exit name into the buffer. */ while (*dest_name && (*dest_name != EXIT_DELIMITER)) { Bufcat_char(&buf, *dest_name); dest_name++; } /* If the last char was the delimiter, skip over it. */ if (*dest_name) dest_name++; dest = parse_linkable_dest(player, exit, Buftext(&buf)); if (dest == NOTHING) continue; switch (Typeof(dest)) { case TYPE_PLAYER: case TYPE_ROOM: case TYPE_PROGRAM: if (prdest) { notify(player, "Only one player, room, or program destination allowed. Destination %u ignored.", player, dest); continue; } dest_list[ndest++] = dest; prdest = 1; break; case TYPE_THING: dest_list[ndest++] = dest; break; case TYPE_EXIT: if (exit_loop_check(exit, dest)) { notify(player, "Destination %u would create a loop, ignored.", player, dest); continue; } dest_list[ndest++] = dest; break; default: notify(player, "Internal error: weird object type."); log_status("PANIC: weird object: Typeof(%d) = %d", dest, Typeof(dest)); break; } if (dest == HOME) { notify(player, "Linked to HOME."); } else { notify(player, "Linked to %u.", player, dest); } if (ndest >= MAX_LINKS) { notify(player, "Too many destinations, rest ignored."); break; } } return ndest; }