PennMUSH Community

root/1.8.3/trunk/src/create.c

Revision 1278, 19.5 kB (checked in by shawnw, 2 months ago)

Prep for 1.8.3p7

Line 
1 /**
2  * \file create.c
3  *
4  * \brief Functions for creating objects of all types.
5  *
6  *
7  */
8
9 #include "copyrite.h"
10 #include "config.h"
11 #include <string.h>
12 #include "conf.h"
13 #include "externs.h"
14 #include "mushdb.h"
15 #include "attrib.h"
16 #include "match.h"
17 #include "extchat.h"
18 #include "log.h"
19 #include "flags.h"
20 #include "dbdefs.h"
21 #include "lock.h"
22 #include "parse.h"
23 #include "game.h"
24 #include "command.h"
25 #include "confmagic.h"
26
27 static dbref parse_linkable_room(dbref player, const char *room_name);
28 static dbref check_var_link(const char *dest_name);
29 static dbref clone_object(dbref player, dbref thing, const char *newname,
30                           int preserve);
31
32 struct db_stat_info current_state; /**< Current stats for database */
33
34 /* utility for open and link */
35 static dbref
36 parse_linkable_room(dbref player, const char *room_name)
37 {
38   dbref room;
39
40   /* parse room */
41   if (!strcasecmp(room_name, "here")) {
42     room = IsExit(player) ? Source(player) : Location(player);
43   } else if (!strcasecmp(room_name, "home")) {
44     return HOME;                /* HOME is always linkable */
45   } else {
46     room = parse_objid(room_name);
47   }
48
49   /* check room */
50   if (!GoodObject(room)) {
51     notify(player, T("That is not a valid object."));
52     return NOTHING;
53   } else if (Going(room)) {
54     notify(player, T("That room is being destroyed. Sorry."));
55     return NOTHING;
56   } else if (!can_link_to(player, room)) {
57     notify(player, T("You can't link to that."));
58     return NOTHING;
59   } else {
60     return room;
61   }
62 }
63
64 static dbref
65 check_var_link(const char *dest_name)
66 {
67   /* This allows an exit to be linked to a 'variable' destination.
68    * Such exits can be linked by anyone but the owner's ability
69    * to link to the destination is checked when it's computed.
70    */
71
72   if (!strcasecmp("VARIABLE", dest_name))
73     return AMBIGUOUS;
74   else
75     return NOTHING;
76 }
77
78 /** Create an exit.
79  * This function opens an exit and optionally links it.
80  * \param player the enactor.
81  * \param direction the name of the exit.
82  * \param linkto the room to link to, as a string.
83  * \param pseudo a phony location for player if a back exit is needed. This is bpass by do_open() as the source room of the back exit.
84  * \return dbref of the new exit, or NOTHING.
85  */
86 dbref
87 do_real_open(dbref player, const char *direction, const char *linkto,
88              dbref pseudo)
89 {
90   dbref loc =
91     (pseudo !=
92      NOTHING) ? pseudo : (IsExit(player) ? Source(player) : Location(player));
93   dbref new_exit;
94   if (!command_check_byname(player, "@dig")) {
95     notify(player, "Permission denied.");
96     return NOTHING;
97   }
98   if ((loc == NOTHING) || (!IsRoom(loc))) {
99     notify(player, T("Sorry you can only make exits out of rooms."));
100     return NOTHING;
101   }
102   if (Going(loc)) {
103     notify(player, T("You can't make an exit in a place that's crumbling."));
104     return NOTHING;
105   }
106   if (!*direction) {
107     notify(player, T("Open where?"));
108     return NOTHING;
109   } else if (!ok_name(direction)) {
110     notify(player, T("That's a strange name for an exit!"));
111     return NOTHING;
112   }
113   if (!Open_Anywhere(player) && !controls(player, loc)) {
114     notify(player, T("Permission denied."));
115   } else if (can_pay_fees(player, EXIT_COST)) {
116     /* create the exit */
117     new_exit = new_object();
118
119     /* initialize everything */
120     set_name(new_exit, direction);
121     Owner(new_exit) = Owner(player);
122     Zone(new_exit) = Zone(player);
123     Source(new_exit) = loc;
124     Type(new_exit) = TYPE_EXIT;
125     Flags(new_exit) = string_to_bits("FLAG", options.exit_flags);
126
127     /* link it in */
128     PUSH(new_exit, Exits(loc));
129
130     /* and we're done */
131     notify_format(player, T("Opened exit %s"), unparse_dbref(new_exit));
132
133     /* check second arg to see if we should do a link */
134     if (linkto && *linkto != '\0') {
135       notify(player, T("Trying to link..."));
136       if ((loc = check_var_link(linkto)) == NOTHING)
137         loc = parse_linkable_room(player, linkto);
138       if (loc != NOTHING) {
139         if (!payfor(player, LINK_COST)) {
140           notify_format(player, T("You don't have enough %s to link."), MONIES);
141         } else {
142           /* it's ok, link it */
143           Location(new_exit) = loc;
144           notify_format(player, T("Linked exit #%d to #%d"), new_exit, loc);
145         }
146       }
147     }
148     current_state.exits++;
149     local_data_create(new_exit);
150     return new_exit;
151   }
152   return NOTHING;
153 }
154
155 /** Open a new exit.
156  * \verbatim
157  * This is the top-level function for @open. It calls do_real_open()
158  * to do the real work of opening both the exit forward and the exit back.
159  * \endverbatim
160  * \param player the enactor.
161  * \param direction name of the exit forward.
162  * \param links 1-based array containing name of destination and optionally name of exit back.
163  */
164 void
165 do_open(dbref player, const char *direction, char **links)
166 {
167   dbref forward;
168   forward = do_real_open(player, direction, links[1], NOTHING);
169   if (links[2] && GoodObject(forward) && GoodObject(Location(forward))) {
170     do_real_open(player, links[2], "here", Location(forward));
171   }
172 }
173
174 /** Unlink an exit or room.
175  * \verbatim
176  * This is the top-level function for @unlink, which can unlink an exit
177  * or remove a drop-to from a room.
178  * \endverbatim
179  * \param player the enactor.
180  * \param name name of the object to unlink.
181  */
182 void
183 do_unlink(dbref player, const char *name)
184 {
185   dbref exit_l, old_loc;
186   long match_flags = MAT_EXIT | MAT_HERE | MAT_ABSOLUTE;
187
188   if (!Wizard(player)) {
189     match_flags |= MAT_CONTROL;
190   }
191   switch (exit_l = match_result(player, name, TYPE_EXIT, match_flags)) {
192   case NOTHING:
193     notify(player, T("Unlink what?"));
194     break;
195   case AMBIGUOUS:
196     notify(player, T("I don't know which one you mean!"));
197     break;
198   default:
199     if (!controls(player, exit_l)) {
200       notify(player, T("Permission denied."));
201     } else {
202       switch (Typeof(exit_l)) {
203       case TYPE_EXIT:
204         old_loc = Location(exit_l);
205         Location(exit_l) = NOTHING;
206         notify_format(player, T("Unlinked exit #%d (Used to lead to %s)."),
207                       exit_l, unparse_object(player, old_loc));
208         break;
209       case TYPE_ROOM:
210         Location(exit_l) = NOTHING;
211         notify(player, T("Dropto removed."));
212         break;
213       default:
214         notify(player, T("You can't unlink that!"));
215         break;
216       }
217     }
218   }
219 }
220
221 /** Link an exit, room, player, or thing.
222  * \verbatim
223  * This is the top-level function for @link, which is used to link an
224  * exit to a destination, set a player or thing's home, or set a
225  * drop-to on a room.
226  *
227  * Linking an exit usually seizes ownership of the exit and costs 1 penny.
228  * 1 penny is also transferred to the former owner.
229  * \endverbatim
230  * \param player the enactor.
231  * \param name the name of the object to link.
232  * \param room_name the name of the link destination.
233  * \param preserve if 1, preserve ownership and zone data on exit relink.
234  */
235 void
236 do_link(dbref player, const char *name, const char *room_name, int preserve)
237 {
238   /* Use this to link to a room that you own.
239    * It usually seizes ownership of the exit and costs 1 penny,
240    * plus a penny transferred to the exit owner if they aren't you.
241    * You must own the linked-to room AND specify it by room number.
242    */
243
244   dbref thing;
245   dbref room;
246
247   if (!room_name || !*room_name) {
248     do_unlink(player, name);
249     return;
250   }
251   if (!IsRoom(player) && GoodObject(Location(player)) &&
252       IsExit(Location(player))) {
253     notify(player, T("You somehow wound up in a exit. No biscuit."));
254     return;
255   }
256   if ((thing = noisy_match_result(player, name, TYPE_EXIT, MAT_EVERYTHING))
257       != NOTHING) {
258     switch (Typeof(thing)) {
259     case TYPE_EXIT:
260       if ((room = check_var_link(room_name)) == NOTHING)
261         room = parse_linkable_room(player, room_name);
262       if (room == NOTHING)
263         return;
264       if (GoodObject(room) && !can_link_to(player, room)) {
265         notify(player, T("Permission denied."));
266         break;
267       }
268       /* We may link an exit if it's unlinked and we pass the link-lock
269        * or if we control it.
270        */
271       if (!(controls(player, thing)
272             || ((Location(thing) == NOTHING)
273                 && eval_lock(player, thing, Link_Lock)))) {
274         notify(player, T("Permission denied."));
275         return;
276       }
277       if (preserve && !Wizard(player)) {
278         notify(player, T("Permission denied."));
279         return;
280       }
281       /* handle costs */
282       if (Owner(thing) == Owner(player)) {
283         if (!payfor(player, LINK_COST)) {
284           notify_format(player, T("It costs %d %s to link this exit."),
285                         LINK_COST, ((LINK_COST == 1) ? MONEY : MONIES));
286           return;
287         }
288       } else {
289         if (!payfor(player, LINK_COST + EXIT_COST)) {
290           int a = LINK_COST + EXIT_COST;
291           notify_format(player, T("It costs %d %s to link this exit."), a,
292                         ((a == 1) ? MONEY : MONIES));
293           return;
294         } else if (!preserve) {
295           /* pay the owner for his loss */
296           giveto(Owner(thing), EXIT_COST);
297           chown_object(player, thing, player, 0);
298         }
299       }
300
301       /* link has been validated and paid for; do it */
302       if (!preserve) {
303         Owner(thing) = Owner(player);
304         Zone(thing) = Zone(player);
305       }
306       Location(thing) = room;
307
308       /* notify the player */
309       notify_format(player, T("Linked exit #%d to %s"), thing,
310                     unparse_object(player, room));
311       break;
312     case TYPE_PLAYER:
313     case TYPE_THING:
314       if ((room =
315            noisy_match_result(player, room_name, NOTYPE, MAT_EVERYTHING)) < 0) {
316         notify(player, T("No match."));
317         return;
318       }
319       if (IsExit(room)) {
320         notify(player, T("That is an exit."));
321         return;
322       }
323       if (thing == room) {
324         notify(player, T("You may not link something to itself."));
325         return;
326       }
327       /* abode */
328       if (!controls(player, room) && !Abode(room)) {
329         notify(player, T("Permission denied."));
330         break;
331       }
332       if (!controls(player, thing)) {
333         notify(player, T("Permission denied."));
334       } else if (room == HOME) {
335         notify(player, T("Can't set home to home."));
336       } else {
337         /* do the link */
338         Home(thing) = room;     /* home */
339         if (!Quiet(player) && !(Quiet(thing) && (Owner(thing) == player)))
340           notify(player, T("Home set."));
341       }
342       break;
343     case TYPE_ROOM:
344       if ((room = parse_linkable_room(player, room_name)) == NOTHING)
345         return;
346       if ((room != HOME) && (!IsRoom(room))) {
347         notify(player, T("That is not a room!"));
348         return;
349       }
350       if (!controls(player, thing)) {
351         notify(player, T("Permission denied."));
352       } else {
353         /* do the link, in location */
354         Location(thing) = room; /* dropto */
355         notify(player, T("Dropto set."));
356       }
357       break;
358     default:
359       notify(player, "Internal error: weird object type.");
360       do_log(LT_ERR, NOTHING, NOTHING,
361              T("Weird object! Type of #%d is %d"), thing, Typeof(thing));
362       break;
363     }
364   }
365 }
366
367 /** Create a room.
368  * \verbatim
369  * This is the top-level interface for @dig.
370  * \endverbatim
371  * \param player the enactor.
372  * \param name the name of the room to create.
373  * \param argv array of additional arguments to command (exit forward,exit back)
374  * \param tport if 1, teleport the player to the new room.
375  * \return dbref of new room, or NOTHING.
376  */
377 dbref
378 do_dig(dbref player, const char *name, char **argv, int tport)
379 {
380   dbref room;
381
382   /* we don't need to know player's location!  hooray! */
383   if (*name == '\0') {
384     notify(player, T("Dig what?"));
385   } else if (!ok_name(name)) {
386     notify(player, T("That's a silly name for a room!"));
387   } else if (can_pay_fees(player, ROOM_COST)) {
388     room = new_object();
389
390     /* Initialize everything */
391     set_name(room, name);
392     Owner(room) = Owner(player);
393     Zone(room) = Zone(player);
394     Type(room) = TYPE_ROOM;
395     Flags(room) = string_to_bits("FLAG", options.room_flags);
396
397     notify_format(player, T("%s created with room number %d."), name, room);
398     if (argv[1] && *argv[1]) {
399       char nbuff[MAX_COMMAND_LEN];
400       sprintf(nbuff, "#%d", room);
401       do_real_open(player, argv[1], nbuff, NOTHING);
402     }
403     if (argv[2] && *argv[2]) {
404       do_real_open(player, argv[2], "here", room);
405     }
406     current_state.rooms++;
407     local_data_create(room);
408     if (tport) {
409       /* We need to use the full command, because we need NO_TEL
410        * and Z_TEL checking */
411       char roomstr[MAX_COMMAND_LEN];
412       sprintf(roomstr, "#%d", room);
413       do_teleport(player, "me", roomstr, 0, 0); /* if flag, move the player */
414     }
415     return room;
416   }
417   return NOTHING;
418 }
419
420 /** Create a thing.
421  * \verbatim
422  * This is the top-level function for @create.
423  * \endverbatim
424  * \param player the enactor.
425  * \param name name of thing to create.
426  * \param cost pennies spent in creation.
427  * \return dbref of new thing, or NOTHING.
428  */
429 dbref
430 do_create(dbref player, char *name, int cost)
431 {
432   dbref loc;
433   dbref thing;
434
435   if (*name == '\0') {
436     notify(player, T("Create what?"));
437     return NOTHING;
438   } else if (!ok_name(name)) {
439     notify(player, T("That's a silly name for a thing!"));
440     return NOTHING;
441   } else if (cost < OBJECT_COST) {
442     cost = OBJECT_COST;
443   }
444   if (can_pay_fees(player, cost)) {
445     /* create the object */
446     thing = new_object();
447
448     /* initialize everything */
449     set_name(thing, name);
450     if (!IsExit(player))        /* Exits shouldn't have contents! */
451       Location(thing) = player;
452     else
453       Location(thing) = Source(player);
454     Owner(thing) = Owner(player);
455     Zone(thing) = Zone(player);
456     s_Pennies(thing, cost);
457     Type(thing) = TYPE_THING;
458     Flags(thing) = string_to_bits("FLAG", options.thing_flags);
459
460     /* home is here (if we can link to it) or player's home */
461     if ((loc = Location(player)) != NOTHING &&
462         (controls(player, loc) || Abode(loc))) {
463       Home(thing) = loc;        /* home */
464     } else {
465       Home(thing) = Home(player);       /* home */
466     }
467
468     /* link it in */
469     if (!IsExit(player))
470       PUSH(thing, Contents(player));
471     else
472       PUSH(thing, Contents(Source(player)));
473
474     /* and we're done */
475     notify_format(player, T("Created: Object %s."), unparse_dbref(thing));
476     current_state.things++;
477     local_data_create(thing);
478     return thing;
479   }
480   return NOTHING;
481 }
482
483 /* Clone an object. The new object is owned by the cloning player */
484 static dbref
485 clone_object(dbref player, dbref thing, const char *newname, int preserve)
486 {
487   dbref clone;
488
489   clone = new_object();
490
491   memcpy(REFDB(clone), REFDB(thing), sizeof(struct object));
492   Owner(clone) = Owner(player);
493   Name(clone) = NULL;
494   if (newname && *newname)
495     set_name(clone, newname);
496   else
497     set_name(clone, Name(thing));
498   s_Pennies(clone, Pennies(thing));
499   AttrCount(clone) = 0;
500   atr_cpy(clone, thing);
501   Locks(clone) = NULL;
502   clone_locks(player, thing, clone);
503   Zone(clone) = Zone(thing);
504   Parent(clone) = Parent(thing);
505   Flags(clone) = clone_flag_bitmask("FLAG", Flags(thing));
506   if (!preserve) {
507     clear_flag_internal(clone, "WIZARD");