ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/c_wiz.C
Revision: 1.97
Committed: Wed Dec 5 19:31:26 2018 UTC (5 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.96: +3 -3 lines
Log Message:
improve, not fix, some sscanf madness, probably introducing more bugs

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.53 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 root 1.90 *
4 root 1.95 * Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team
5 root 1.93 * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
6 root 1.80 * Copyright (©) 2002 Mark Wedel & Crossfire Development Team
7     * Copyright (©) 1992 Frank Tore Johansen
8 root 1.90 *
9 root 1.72 * Deliantra is free software: you can redistribute it and/or modify it under
10     * the terms of the Affero GNU General Public License as published by the
11     * Free Software Foundation, either version 3 of the License, or (at your
12     * option) any later version.
13 root 1.90 *
14 root 1.49 * This program is distributed in the hope that it will be useful,
15     * but WITHOUT ANY WARRANTY; without even the implied warranty of
16     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17     * GNU General Public License for more details.
18 root 1.90 *
19 root 1.72 * You should have received a copy of the Affero GNU General Public License
20     * and the GNU General Public License along with this program. If not, see
21     * <http://www.gnu.org/licenses/>.
22 root 1.90 *
23 root 1.53 * The authors can be reached via e-mail to <support@deliantra.net>
24 pippijn 1.32 */
25 elmex 1.1
26     #include <global.h>
27 root 1.29 #include <sproto.h>
28 elmex 1.1 #include <spells.h>
29     #include <treasure.h>
30     #include <skills.h>
31    
32     /** Defines for DM item stack **/
33     #define STACK_SIZE 50 /* Stack size, static */
34 root 1.8
35 elmex 1.1 /* Values for 'from' field of get_dm_object */
36     #define STACK_FROM_NONE 0 /* Item was not found */
37     #define STACK_FROM_TOP 1 /* Item is stack top */
38     #define STACK_FROM_STACK 2 /* Item is somewhere in stack */
39     #define STACK_FROM_NUMBER 3 /* Item is a number (may be top) */
40    
41     /**
42     * Enough of the DM functions seem to need this that I broke
43     * it out to a seperate function. name is the person
44     * being saught, rq is who is looking for them. This
45     * prints diagnostics messages, and returns the
46     * other player, or NULL otherwise.
47     */
48 root 1.8 static player *
49     get_other_player_from_name (object *op, char *name)
50     {
51     if (!name)
52     return NULL;
53 elmex 1.1
54 root 1.27 for_all_players (pl)
55 root 1.8 if (!strncmp (pl->ob->name, name, MAX_NAME))
56 root 1.27 {
57     if (pl->ob == op)
58     {
59     new_draw_info (NDI_UNIQUE, 0, op, "You can't do that to yourself.");
60     return NULL;
61     }
62 elmex 1.1
63 root 1.27 if (pl->ns->state != ST_PLAYING)
64     {
65     new_draw_info (NDI_UNIQUE, 0, op, "That player is in no state for that right now.");
66     return NULL;
67     }
68 elmex 1.1
69 root 1.27 return pl;
70     }
71 root 1.25
72 root 1.27 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
73     return 0;
74 elmex 1.1 }
75    
76 root 1.74 static void
77     dm_stack_pop (player *pl)
78     {
79     if (!pl->stack_items || !pl->stack_position)
80     {
81     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
82     return;
83     }
84    
85     pl->stack_position--;
86     new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Popped item from stack, %d left.", pl->stack_position);
87     }
88    
89     /**
90     * Get current stack top item for player.
91     * Returns NULL if no stacked item.
92     * If stacked item disappeared (freed), remove it.
93     *
94     * Ryo, august 2004
95     */
96     static object *
97     dm_stack_peek (player *pl)
98     {
99     object *ob;
100    
101     if (!pl->stack_position)
102     {
103     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Empty stack!");
104     return NULL;
105     }
106    
107     ob = find_object (pl->stack_items[pl->stack_position - 1]);
108     if (!ob)
109     {
110     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Stacked item was removed!");
111     dm_stack_pop (pl);
112     return NULL;
113     }
114    
115     return ob;
116     }
117    
118     /**
119     * Push specified item on player stack.
120     * Inform player of position.
121     * Initializes variables if needed.
122     */
123     void
124     dm_stack_push (player *pl, tag_t item)
125     {
126     if (!pl->stack_items)
127     {
128     pl->stack_items = (tag_t *) malloc (sizeof (tag_t) * STACK_SIZE);
129     memset (pl->stack_items, 0, sizeof (tag_t) * STACK_SIZE);
130     }
131    
132     if (pl->stack_position == STACK_SIZE)
133     {
134     new_draw_info (NDI_UNIQUE, 0, pl->ob, "Item stack full!");
135     return;
136     }
137    
138     pl->stack_items[pl->stack_position] = item;
139     new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Item stacked as %d.", pl->stack_position);
140     pl->stack_position++;
141     }
142    
143     /**
144     * Checks 'params' for object code.
145     *
146     * Can be:
147     * * empty => get current object stack top for player
148     * * number => get item with that tag, stack it for future use
149     * * $number => get specified stack item
150     * * "me" => player himself
151     *
152     * At function exit, params points to first non-object char
153     *
154     * 'from', if not NULL, contains at exit:
155     * * STACK_FROM_NONE => object not found
156     * * STACK_FROM_TOP => top item stack, may be NULL if stack was empty
157     * * STACK_FROM_STACK => item from somewhere in the stack
158     * * STACK_FROM_NUMBER => item by number, pushed on stack
159     *
160     * Ryo, august 2004
161     */
162     static object *
163     get_dm_object (player *pl, char **params, int *from)
164     {
165     int item_tag, item_position;
166     object *ob;
167    
168     if (!pl)
169     return NULL;
170    
171     if (!params || !*params || **params == '\0')
172     {
173     if (from)
174     *from = STACK_FROM_TOP;
175     /* No parameter => get stack item */
176     return dm_stack_peek (pl);
177     }
178    
179     /* Let's clean white spaces */
180     while (**params == ' ')
181     (*params)++;
182    
183     /* Next case: number => item tag */
184     if (sscanf (*params, "%d", &item_tag))
185     {
186     /* Move parameter to next item */
187     while (isdigit (**params))
188     (*params)++;
189    
190     /* And skip blanks, too */
191     while (**params == ' ')
192     (*params)++;
193    
194     /* Get item */
195     ob = find_object (item_tag);
196     if (!ob)
197     {
198     if (from)
199     *from = STACK_FROM_NONE;
200     new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such item %d!", item_tag);
201     return NULL;
202     }
203    
204     /* Got one, let's push it on stack */
205     dm_stack_push (pl, item_tag);
206     if (from)
207     *from = STACK_FROM_NUMBER;
208     return ob;
209     }
210    
211     /* Next case: $number => stack item */
212     if (sscanf (*params, "$%d", &item_position))
213     {
214     /* Move parameter to next item */
215     (*params)++;
216    
217     while (isdigit (**params))
218     (*params)++;
219     while (**params == ' ')
220     (*params)++;
221    
222     if (item_position >= pl->stack_position)
223     {
224     if (from)
225     *from = STACK_FROM_NONE;
226     new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "No such stack item %d!", item_position);
227     return NULL;
228     }
229    
230     ob = find_object (pl->stack_items[item_position]);
231     if (!ob)
232     {
233     if (from)
234     *from = STACK_FROM_NONE;
235     new_draw_info_format (NDI_UNIQUE, 0, pl->ob, "Stack item %d was removed.", item_position);
236     return NULL;
237     }
238    
239     if (from)
240     *from = item_position < pl->stack_position - 1 ? STACK_FROM_STACK : STACK_FROM_TOP;
241     return ob;
242     }
243    
244     /* Next case: 'me' => return pl->ob */
245     if (!strncmp (*params, "me", 2))
246     {
247     if (from)
248     *from = STACK_FROM_NUMBER;
249     dm_stack_push (pl, pl->ob->count);
250    
251     /* Skip to next token */
252     (*params) += 2;
253     while (**params == ' ')
254     (*params)++;
255    
256     return pl->ob;
257     }
258    
259     /* Last case: get stack top */
260     if (from)
261     *from = STACK_FROM_TOP;
262     return dm_stack_peek (pl);
263     }
264    
265 elmex 1.1 /**
266     * This finds and returns the object which matches the name or
267     * object nubmer (specified via num #whatever).
268     */
269 root 1.8 static object *
270     find_object_both (char *params)
271     {
272     if (!params)
273     return NULL;
274 root 1.9
275 root 1.8 if (params[0] == '#')
276     return find_object (atol (params + 1));
277     else
278     return find_object_name (params);
279 elmex 1.1 }
280    
281     /**
282     * Sets the god for some objects. params should contain two values -
283     * first the object to change, followed by the god to change it to.
284     */
285 root 1.8 int
286     command_setgod (object *op, char *params)
287     {
288     object *ob, *god;
289     char *str;
290 elmex 1.1
291 root 1.8 if (!params || !(str = strchr (params, ' ')))
292     {
293     new_draw_info (NDI_UNIQUE, 0, op, "Usage: setgod object god");
294     return 0;
295 elmex 1.1 }
296    
297 root 1.8 /* kill the space, and set string to the next param */
298     *str++ = '\0';
299     if (!(ob = find_object_both (params)))
300     {
301     new_draw_info_format (NDI_UNIQUE, 0, op, "Set whose god - can not find object %s?", params);
302     return 1;
303 elmex 1.1 }
304    
305 root 1.8 /*
306     * Perhaps this is overly restrictive? Should we perhaps be able
307     * to rebless altars and the like?
308     */
309     if (ob->type != PLAYER)
310     {
311     new_draw_info_format (NDI_UNIQUE, 0, op, "%s is not a player - can not change its god", &ob->name);
312     return 1;
313 elmex 1.1 }
314    
315 root 1.8 god = find_god (str);
316     if (god == NULL)
317     {
318     new_draw_info_format (NDI_UNIQUE, 0, op, "No such god %s.", str);
319     return 1;
320 elmex 1.1 }
321    
322 root 1.83 ob->become_follower (god);
323 root 1.8 return 1;
324 elmex 1.1 }
325    
326     int
327 root 1.8 command_freeze (object *op, char *params)
328 elmex 1.1 {
329 root 1.8 int ticks;
330     player *pl;
331 elmex 1.1
332 root 1.8 if (!params)
333     {
334     new_draw_info (NDI_UNIQUE, 0, op, "Usage: freeze [ticks] <player>.");
335     return 1;
336     }
337 elmex 1.1
338 root 1.8 ticks = atoi (params);
339     if (ticks)
340     {
341     while ((isdigit (*params) || isspace (*params)) && *params != 0)
342     params++;
343     if (*params == 0)
344     {
345     new_draw_info (NDI_UNIQUE, 0, op, "Usage: freeze [ticks] <player>.");
346     return 1;
347 elmex 1.1 }
348     }
349 root 1.8 else
350     ticks = 100;
351 elmex 1.1
352 root 1.8 pl = get_other_player_from_name (op, params);
353     if (!pl)
354 elmex 1.1 return 1;
355 root 1.8
356     new_draw_info (NDI_UNIQUE | NDI_RED, 0, pl->ob, "You have been frozen by the DM!");
357     new_draw_info_format (NDI_UNIQUE, 0, op, "You freeze %s for %d ticks", &pl->ob->name, ticks);
358     pl->ob->speed_left = -(pl->ob->speed * ticks);
359     return 0;
360 elmex 1.1 }
361    
362 root 1.8 int
363     command_arrest (object *op, char *params)
364     {
365     object *dummy;
366     player *pl;
367    
368     if (!op)
369 elmex 1.1 return 0;
370 root 1.28
371 root 1.8 if (params == NULL)
372     {
373     new_draw_info (NDI_UNIQUE, 0, op, "Usage: arrest <player>.");
374     return 1;
375     }
376 root 1.28
377 root 1.8 pl = get_other_player_from_name (op, params);
378     if (!pl)
379     return 1;
380 root 1.28
381 root 1.8 dummy = get_jail_exit (pl->ob);
382     if (!dummy)
383     {
384     /* we have nowhere to send the prisoner.... */
385     new_draw_info (NDI_UNIQUE, 0, op, "can't jail player, there is no map to hold them");
386     return 0;
387     }
388 root 1.28
389 root 1.57 pl->ob->player_goto (dummy->slaying, dummy->stats.hp, dummy->stats.sp);//TODO
390 root 1.60 dummy->destroy ();
391 root 1.59
392 root 1.8 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You have been arrested.");
393     new_draw_info (NDI_UNIQUE, 0, op, "OK.");
394     LOG (llevInfo, "Player %s arrested by %s\n", &pl->ob->name, &op->name);
395     return 1;
396 elmex 1.1 }
397    
398 root 1.8 int
399     command_summon (object *op, char *params)
400     {
401     int i;
402     player *pl;
403    
404     if (!op)
405     return 0;
406    
407     if (params == NULL)
408     {
409     new_draw_info (NDI_UNIQUE, 0, op, "Usage: summon <player>.");
410     return 1;
411     }
412    
413     pl = get_other_player_from_name (op, params);
414     if (!pl)
415 elmex 1.1 return 1;
416    
417 root 1.85 maptile *m = op->map;
418     sint16 nx = op->x;
419     sint16 ny = op->y;
420    
421     i = find_free_spot (op, m, nx, ny, 1, SIZEOFFREE1+1);
422 root 1.8 if (i == -1)
423     {
424     new_draw_info (NDI_UNIQUE, 0, op, "Can not find a free spot to place summoned player.");
425     return 1;
426     }
427    
428 root 1.94 nx += DIRX (i);
429     ny += DIRY (i);
430 root 1.85
431     if (!xy_normalise (m, nx, ny))
432     {
433     new_draw_info (NDI_UNIQUE, 0, op, "Can not find a free spot to place summoned player.");
434     return 1;
435     }
436    
437     pl->ob->player_goto (m->path, nx, ny);
438 root 1.8 new_draw_info (NDI_UNIQUE, 0, pl->ob, "You are summoned.");
439     new_draw_info (NDI_UNIQUE, 0, op, "OK.");
440 elmex 1.1
441 root 1.8 return 1;
442 elmex 1.1 }
443    
444     /**
445     * This function is a real mess, because we're stucking getting
446     * the entire item description in one block of text, so we just
447     * can't simply parse it - we need to look for double quotes
448     * for example. This could actually get much simpler with just a
449     * little help from the client - if we could get line breaks, it
450     * makes parsing much easier, eg, something like:
451     * arch dragon
452     * name big nasty creature
453     * hp 5
454     * sp 30
455     * Is much easier to parse than
456     * dragon name "big nasty creature" hp 5 sp 30
457     * for example.
458     */
459 root 1.8 int
460     command_create (object *op, char *params)
461     {
462     object *tmp = NULL;
463     int nrof, i, magic, set_magic = 0, set_nrof = 0, gotquote, gotspace;
464 root 1.88 char buf[MAX_BUF], *cp, *bp = buf, *bp2, *bp3, *endline;
465 root 1.8 archetype *at, *at_spell = NULL;
466     artifact *art = NULL;
467    
468     if (!op)
469     return 0;
470    
471     if (params == NULL)
472     {
473     new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
474     return 1;
475     }
476 root 1.9
477 root 1.8 bp = params;
478    
479     /* We need to know where the line ends */
480     endline = bp + strlen (bp);
481    
482     if (sscanf (bp, "%d ", &nrof))
483     {
484     if ((bp = strchr (params, ' ')) == NULL)
485     {
486     new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
487     return 1;
488     }
489     bp++;
490     set_nrof = 1;
491     LOG (llevDebug, "%s creates: (%d) %s\n", &op->name, nrof, bp);
492     }
493 root 1.9
494 root 1.8 if (sscanf (bp, "%d ", &magic))
495     {
496     if ((bp = strchr (bp, ' ')) == NULL)
497     {
498     new_draw_info (NDI_UNIQUE, 0, op, "Usage: create [nr] [magic] <archetype> [ of <artifact>]" " [variable_to_patch setting]");
499     return 1;
500     }
501 root 1.9
502 root 1.8 bp++;
503     set_magic = 1;
504     LOG (llevDebug, "%s creates: (%d) (%d) %s\n", &op->name, nrof, magic, bp);
505     }
506 root 1.9
507 root 1.8 if ((cp = strstr (bp, " of ")) != NULL)
508     {
509     *cp = '\0';
510     cp += 4;
511     }
512 root 1.9
513 root 1.8 for (bp2 = bp; *bp2; bp2++)
514     {
515     if (*bp2 == ' ')
516     {
517     *bp2 = '\0';
518     bp2++;
519     break;
520     }
521     }
522    
523 root 1.11 if ((at = archetype::find (bp)) == NULL)
524 root 1.8 {
525     new_draw_info (NDI_UNIQUE, 0, op, "No such archetype.");
526     return 1;
527     }
528    
529     if (cp)
530     {
531     char spell_name[MAX_BUF], *fsp = NULL;
532    
533     /*
534     * Try to find a spell object for this. Note that
535     * we also set up spell_name which is only
536     * the first word.
537     */
538    
539 root 1.11 at_spell = archetype::find (cp);
540 root 1.47 if (!at_spell || at_spell->type != SPELL)
541 root 1.8 at_spell = find_archetype_by_object_name (cp);
542 root 1.47 if (!at_spell || at_spell->type != SPELL)
543 root 1.8 {
544 root 1.34 assign (spell_name, cp);
545 root 1.8 fsp = strchr (spell_name, ' ');
546     if (fsp)
547     {
548     *fsp = 0;
549     fsp++;
550 root 1.11 at_spell = archetype::find (spell_name);
551 root 1.8
552     /* Got a spell, update the first string pointer */
553 root 1.47 if (at_spell && at_spell->type == SPELL)
554 root 1.8 bp2 = cp + strlen (spell_name) + 1;
555     else
556     at_spell = NULL;
557 elmex 1.1 }
558     }
559    
560 root 1.8 /* OK - we didn't find a spell - presume the 'of'
561     * in this case means its an artifact.
562     */
563     if (!at_spell)
564     {
565 root 1.47 if (find_artifactlist (at->type) == NULL)
566     new_draw_info_format (NDI_UNIQUE, 0, op, "No artifact list for type %d\n", at->type);
567 root 1.8 else
568     {
569 root 1.47 art = find_artifactlist (at->type)->items;
570 root 1.8
571 root 1.42 while (art)
572 root 1.8 {
573 root 1.66 if (!strcmp (&art->item->name, cp))
574 root 1.8 break;
575 root 1.42
576 root 1.8 art = art->next;
577     }
578 root 1.9
579 root 1.8 if (!art)
580 root 1.47 new_draw_info_format (NDI_UNIQUE, 0, op, "No such artifact ([%d] of %s)", at->type, cp);
581 elmex 1.1 }
582 root 1.9
583 root 1.8 LOG (llevDebug, "%s creates: (%d) (%d) (%s) of (%s)\n", &op->name, set_nrof ? nrof : 0, set_magic ? magic : 0, bp, cp);
584 elmex 1.1 }
585 root 1.8 } /* if cp */
586    
587 root 1.47 if ((at->type == ROD || at->type == WAND || at->type == SCROLL ||
588     at->type == HORN || at->type == SPELLBOOK) && !at_spell)
589 root 1.8 {
590     new_draw_info_format (NDI_UNIQUE, 0, op, "Unable to find spell %s for object that needs it, or it is of wrong type", cp);
591     return 1;
592     }
593 elmex 1.1
594 root 1.8 /*
595     * Rather than have two different blocks with a lot of similar code,
596     * just create one object, do all the processing, and then determine
597     * if that one object should be inserted or if we need to make copies.
598     */
599 root 1.79 tmp = at->instance ();
600 root 1.9
601 root 1.8 if (set_magic)
602     set_abs_magic (tmp, magic);
603 root 1.9
604 root 1.8 if (art)
605     give_artifact_abilities (tmp, art->item);
606 root 1.9
607 root 1.84 if (tmp->need_identify ())
608 root 1.8 {
609 root 1.82 tmp->set_flag (FLAG_IDENTIFIED);
610     tmp->clr_flag (FLAG_KNOWN_MAGICAL);
611 root 1.8 }
612    
613     /*
614     * This entire block here tries to find variable pairings,
615     * eg, 'hp 4' or the like. The mess here is that values
616     * can be quoted (eg "my cool sword"); So the basic logic
617     * is we want to find two spaces, but if we got a quote,
618     * any spaces there don't count.
619     */
620     while (*bp2 && bp2 <= endline)
621     {
622     gotspace = 0;
623     gotquote = 0;
624 root 1.9
625 root 1.8 /* find the first quote */
626     for (bp3 = bp2; *bp3 && gotspace < 2 && gotquote < 2; bp3++)
627     {
628    
629     /* Found a quote - now lets find the second one */
630     if (*bp3 == '"')
631     {
632     *bp3 = ' ';
633     bp2 = bp3 + 1; /* Update start of string */
634     bp3++;
635     gotquote++;
636     while (*bp3)
637     {
638     if (*bp3 == '"')
639     {
640     *bp3 = '\0';
641     gotquote++;
642     }
643     else
644     bp3++;
645 elmex 1.1 }
646 root 1.8 }
647     else if (*bp3 == ' ')
648 root 1.9 gotspace++;
649 elmex 1.1 }
650    
651 root 1.8 /*
652     * If we got two spaces, send the second one to null.
653     * if we've reached the end of the line, increase gotspace -
654     * this is perfectly valid for the list entry listed.
655     */
656     if (gotspace == 2 || gotquote == 2)
657     {
658     bp3--; /* Undo the extra increment */
659     *bp3 = '\0';
660     }
661     else if (*bp3 == '\0')
662     gotspace++;
663    
664     if ((gotquote && gotquote != 2) || (gotspace != 2 && gotquote != 2))
665     {
666     /*
667     * Unfortunately, we've clobbered lots of values, so printing
668     * out what we have probably isn't useful. Break out, because
669     * trying to recover is probably won't get anything useful
670     * anyways, and we'd be confused about end of line pointers
671     * anyways.
672     */
673     new_draw_info_format (NDI_UNIQUE, 0, op, "Malformed create line: %s", bp2);
674     break;
675     }
676 root 1.9
677 root 1.8 /* bp2 should still point to the start of this line,
678     * with bp3 pointing to the end
679     */
680     if (set_variable (tmp, bp2) == -1)
681     new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", bp2);
682     else
683     new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s", &tmp->name, tmp->count, bp2);
684 root 1.9
685 root 1.8 bp2 = bp3 + 1;
686     }
687    
688 root 1.47 if (at->nrof)
689 root 1.8 {
690     if (at_spell)
691 root 1.79 tmp->insert (at_spell->instance ());
692 root 1.8
693 root 1.54 tmp->x = op->x;
694     tmp->y = op->y;
695     tmp->map = op->map;
696 root 1.9
697 root 1.8 if (set_nrof)
698     tmp->nrof = nrof;
699 root 1.9
700 root 1.54 op->insert (tmp);
701 root 1.8
702     /* Let's put this created item on stack so dm can access it easily. */
703     dm_stack_push (op->contr, tmp->count);
704    
705     return 1;
706     }
707     else
708     {
709     for (i = 0; i < (set_nrof ? nrof : 1); i++)
710     {
711 root 1.10 object *prev = 0, *head = 0;
712 root 1.8
713 root 1.48 for (archetype *atmp = at; atmp; atmp = (archetype *)atmp->more)
714 root 1.8 {
715 root 1.79 object *dup = atmp->instance ();
716 root 1.8
717     if (at_spell)
718 root 1.79 insert_ob_in_ob (at_spell->instance (), dup);
719 root 1.8
720     /*
721     * The head is what contains all the important bits,
722     * so just copying it over should be fine.
723     */
724 root 1.10 if (!head)
725 root 1.8 {
726     head = dup;
727 root 1.22 tmp->copy_to (dup);
728 elmex 1.1 }
729 root 1.9
730 root 1.47 dup->x = op->x + dup->arch->x;
731     dup->y = op->y + dup->arch->y;
732 root 1.8 dup->map = op->map;
733    
734     if (head != dup)
735     {
736     dup->head = head;
737     prev->more = dup;
738 elmex 1.1 }
739 root 1.9
740 root 1.8 prev = dup;
741 elmex 1.1 }
742    
743 root 1.82 if (head->flag [FLAG_ALIVE])
744 root 1.8 {
745     object *check = head;
746     int size_x = 0;
747     int size_y = 0;
748    
749     while (check)
750     {
751 root 1.77 size_x = max (size_x, check->arch->x);
752     size_y = max (size_y, check->arch->y);
753 root 1.8 check = check->more;
754 elmex 1.1 }
755    
756 root 1.8 if (out_of_map (op->map, head->x + size_x, head->y + size_y))
757     {
758     if (head->x < size_x || head->y < size_y)
759     {
760     dm_stack_pop (op->contr);
761 root 1.60 head->destroy ();
762 root 1.8 new_draw_info (NDI_UNIQUE, 0, op, "Object too big to insert in map, or wrong position.");
763 root 1.60 tmp->destroy ();
764 root 1.8 return 1;
765 elmex 1.1 }
766    
767 root 1.8 check = head;
768 root 1.9
769 root 1.8 while (check)
770     {
771     check->x -= size_x;
772     check->y -= size_y;
773     check = check->more;
774 elmex 1.1 }
775     }
776    
777 root 1.8 insert_ob_in_map (head, op->map, op, 0);
778     }
779     else
780     head = insert_ob_in_ob (head, op);
781    
782     /* Let's put this created item on stack so dm can access it easily. */
783     /* Wonder if we really want to push all of these, but since
784     * things like rods have nrof 0, we want to cover those.
785     */
786     dm_stack_push (op->contr, head->count);
787    
788 root 1.54 if (at->randomitems && !at_spell)
789 root 1.47 create_treasure (at->randomitems, head, GT_APPLY, op->map->difficulty, 0);
790 elmex 1.1 }
791    
792 root 1.8 /* free the one we used to copy */
793 root 1.60 tmp->destroy ();
794 elmex 1.1 }
795    
796 root 1.8 return 1;
797 elmex 1.1 }
798    
799     /*
800     * Now follows dm-commands which are also acceptable from sockets
801     */
802    
803 root 1.8 int
804     command_inventory (object *op, char *params)
805     {
806 root 1.52 int i;
807 root 1.8 object *tmp;
808 elmex 1.1
809 root 1.52 if (!params || !sscanf (params, "%d", &i) || !(tmp = find_object (i)))
810 root 1.8 {
811 root 1.52 op->contr->failmsg ("Inventory of what object (nr)?");
812     return 1;
813 elmex 1.1 }
814    
815 root 1.52 op->contr->infobox (MSG_CHANNEL ("examine"), tmp->query_inventory (op));
816 elmex 1.1
817 root 1.8 return 1;
818 elmex 1.1 }
819    
820     /* just show player's their skills for now. Dm's can
821     * already see skills w/ inventory command - b.t.
822     */
823    
824 root 1.8 int
825     command_skills (object *op, char *params)
826     {
827     show_skills (op, params);
828     return 0;
829 elmex 1.1 }
830    
831 root 1.8 int
832     command_dump (object *op, char *params)
833     {
834     object *tmp;
835 elmex 1.1
836 root 1.8 tmp = get_dm_object (op->contr, &params, NULL);
837     if (!tmp)
838 elmex 1.1 return 1;
839 root 1.8
840 root 1.16 char *dump = dump_object (tmp);
841     new_draw_info (NDI_UNIQUE, 0, op, dump);
842     free (dump);
843    
844 root 1.82 if (tmp->flag [FLAG_OBJ_ORIGINAL])
845 root 1.8 new_draw_info (NDI_UNIQUE, 0, op, "Object is marked original");
846 root 1.16
847 root 1.8 return 1;
848 elmex 1.1 }
849    
850 root 1.8 int
851     command_patch (object *op, char *params)
852     {
853     char *arg, *arg2;
854     object *tmp;
855    
856     tmp = get_dm_object (op->contr, &params, NULL);
857     if (!tmp)
858     /* Player already informed of failure */
859 elmex 1.1 return 1;
860    
861 root 1.8 /* params set to first value by get_dm_default */
862     arg = params;
863     if (arg == NULL)
864     {
865     new_draw_info (NDI_UNIQUE, 0, op, "Patch what values?");
866 elmex 1.1 return 1;
867     }
868    
869 root 1.8 if ((arg2 = strchr (arg, ' ')))
870     arg2++;
871 root 1.43
872 root 1.8 if (set_variable (tmp, arg) == -1)
873     new_draw_info_format (NDI_UNIQUE, 0, op, "Unknown variable %s", arg);
874     else
875 root 1.43 new_draw_info_format (NDI_UNIQUE, 0, op, "(%s#%d)->%s=%s", &tmp->name, tmp->count, arg, arg2);
876 elmex 1.1
877 root 1.8 return 1;
878     }
879 elmex 1.1
880 root 1.8 int
881     command_remove (object *op, char *params)
882     {
883     object *tmp;
884     int from;
885 elmex 1.1
886 root 1.8 tmp = get_dm_object (op->contr, &params, &from);
887     if (!tmp)
888     {
889     new_draw_info (NDI_UNIQUE, 0, op, "Remove what object (nr)?");
890     return 1;
891 elmex 1.1 }
892    
893 root 1.8 if (tmp->type == PLAYER)
894     {
895     new_draw_info (NDI_UNIQUE, 0, op, "Unable to remove a player!");
896     return 1;
897 elmex 1.1 }
898    
899 root 1.82 if (tmp->flag [FLAG_REMOVED])
900 root 1.8 {
901     new_draw_info_format (NDI_UNIQUE, 0, op, "%s is already removed!", query_name (tmp));
902     return 1;
903 elmex 1.1 }
904    
905 root 1.8 if (from != STACK_FROM_STACK)
906     /* Item is either stack top, or is a number thus is now stack top, let's remove it */
907     dm_stack_pop (op->contr);
908    
909     /* Always work on the head - otherwise object will get in odd state */
910     if (tmp->head)
911     tmp = tmp->head;
912 root 1.21 tmp->remove ();
913 root 1.8 return 1;
914 elmex 1.1 }
915    
916 root 1.8 int
917     command_free (object *op, char *params)
918     {
919     object *tmp;
920     int from;
921 elmex 1.1
922 root 1.8 tmp = get_dm_object (op->contr, &params, &from);
923 elmex 1.1
924 root 1.8 if (!tmp)
925     {
926     new_draw_info (NDI_UNIQUE, 0, op, "Free what object (nr)?");
927     return 1;
928 elmex 1.1 }
929    
930 root 1.8 if (from != STACK_FROM_STACK)
931     /* Item is either stack top, or is a number thus is now stack top, let's remove it */
932     dm_stack_pop (op->contr);
933 elmex 1.1
934 root 1.8 if (tmp->head)
935     tmp = tmp->head;
936 root 1.22
937 root 1.60 tmp->destroy ();
938 root 1.8 return 1;
939 elmex 1.1 }
940    
941     /**
942     * This adds exp to a player. We now allow adding to a specific skill.
943     */
944 root 1.8 int
945     command_addexp (object *op, char *params)
946     {
947 root 1.97 char buf[1024], skill[1024];
948 root 1.50 int q;
949 root 1.97 sint64 i;
950 root 1.8 object *skillob = NULL;
951 elmex 1.1
952 root 1.8 skill[0] = '\0';
953 root 1.97 if ((params == NULL) || (strlen (params) > MAX_BUF) || ((q = sscanf (params, "%1023s %" SCNd64 " %1023[^\n]", buf, &i, skill)) < 2))
954 root 1.8 {
955     new_draw_info (NDI_UNIQUE, 0, op, "Usage: addexp <who> <how much> [<skill>].");
956     return 1;
957 elmex 1.1 }
958    
959 root 1.27 for_all_players (pl)
960 root 1.8 if (!strncmp (pl->ob->name, buf, MAX_NAME))
961 root 1.27 {
962     if (q >= 3)
963     {
964     skillob = find_skill_by_name (pl->ob, skill);
965     if (!skillob)
966     {
967     new_draw_info_format (NDI_UNIQUE, 0, op, "Unable to find skill %s in %s", skill, buf);
968     return 1;
969     }
970    
971     i = check_exp_adjust (skillob, i);
972     skillob->stats.exp += i;
973     calc_perm_exp (skillob);
974     player_lvl_adj (pl->ob, skillob);
975     }
976 elmex 1.1
977 root 1.27 pl->ob->stats.exp += i;
978     calc_perm_exp (pl->ob);
979     player_lvl_adj (pl->ob, NULL);
980 elmex 1.1
981 root 1.27 return 1;
982     }
983 elmex 1.1
984 root 1.27 new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
985 root 1.8 return 1;
986 elmex 1.1 }
987    
988     /**************************************************************************/
989 root 1.8
990 elmex 1.1 /* Mods made by Tyler Van Gorder, May 10-13, 1992. */
991 root 1.8
992 elmex 1.1 /* CSUChico : tvangod@cscihp.ecst.csuchico.edu */
993 root 1.8
994 elmex 1.1 /**************************************************************************/
995    
996 root 1.8 int
997     command_stats (object *op, char *params)
998     {
999     char buf[MAX_BUF];
1000    
1001 sf-kernelpanic 1.89 if (params == NULL || params[0] == '\0')
1002 root 1.8 {
1003     new_draw_info (NDI_UNIQUE, 0, op, "Who?");
1004     return 1;
1005     }
1006    
1007 root 1.27 for_all_players (pl)
1008 sf-kernelpanic 1.89 if (!strcmp (&pl->ob->name, params))
1009 root 1.8 {
1010     sprintf (buf, "Str : %-2d H.P. : %-4d MAX : %d", pl->ob->stats.Str, pl->ob->stats.hp, pl->ob->stats.maxhp);
1011     new_draw_info (NDI_UNIQUE, 0, op, buf);
1012     sprintf (buf, "Dex : %-2d S.P. : %-4d MAX : %d", pl->ob->stats.Dex, pl->ob->stats.sp, pl->ob->stats.maxsp);
1013     new_draw_info (NDI_UNIQUE, 0, op, buf);
1014     sprintf (buf, "Con : %-2d AC : %-4d WC : %d", pl->ob->stats.Con, pl->ob->stats.ac, pl->ob->stats.wc);
1015     new_draw_info (NDI_UNIQUE, 0, op, buf);
1016     sprintf (buf, "Int : %-2d Damage : %d", pl->ob->stats.Int, pl->ob->stats.dam);
1017     new_draw_info (NDI_UNIQUE, 0, op, buf);
1018 root 1.20 sprintf (buf, "Wis : %-2d EXP : %" PRId64, pl->ob->stats.Wis, pl->ob->stats.exp);
1019 root 1.8 new_draw_info (NDI_UNIQUE, 0, op, buf);
1020     sprintf (buf, "Pow : %-2d Grace : %d", pl->ob->stats.Pow, pl->ob->stats.grace);
1021     new_draw_info (NDI_UNIQUE, 0, op, buf);
1022     sprintf (buf, "Cha : %-2d Food : %d", pl->ob->stats.Cha, pl->ob->stats.food);
1023     new_draw_info (NDI_UNIQUE, 0, op, buf);
1024 root 1.27 return 1;
1025 root 1.8 }
1026 root 1.27
1027     new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
1028 root 1.8 return 1;
1029     }
1030    
1031     int
1032     command_abil (object *op, char *params)
1033     {
1034 root 1.96 char thing[80], thing2[80];
1035 root 1.8 int iii;
1036     char buf[MAX_BUF];
1037    
1038     iii = 0;
1039     thing[0] = '\0';
1040     thing2[0] = '\0';
1041 root 1.96 if (!params || 3 != sscanf (params, "%79s %79s %d", thing, thing2, &iii))
1042 root 1.8 {
1043     new_draw_info (NDI_UNIQUE, 0, op, "Who?");
1044     return 1;
1045     }
1046    
1047 root 1.96 if (!*thing2)
1048 root 1.8 {
1049     new_draw_info (NDI_UNIQUE, 0, op, "You can't change that.");
1050     return 1;
1051     }
1052    
1053     if (iii < MIN_STAT || iii > MAX_STAT)
1054     {
1055     new_draw_info (NDI_UNIQUE, 0, op, "Illegal range of stat.\n");
1056     return 1;
1057     }
1058    
1059 root 1.27 for_all_players (pl)
1060 root 1.8 {
1061 root 1.66 if (!strcmp (&pl->ob->name, thing))
1062 root 1.8 {
1063 root 1.43 if (!strcmp ("str", thing2)) pl->ob->stats.Str = iii, pl->orig_stats.Str = iii;
1064     if (!strcmp ("dex", thing2)) pl->ob->stats.Dex = iii, pl->orig_stats.Dex = iii;
1065     if (!strcmp ("con", thing2)) pl->ob->stats.Con = iii, pl->orig_stats.Con = iii;
1066     if (!strcmp ("wis", thing2)) pl->ob->stats.Wis = iii, pl->orig_stats.Wis = iii;
1067     if (!strcmp ("cha", thing2)) pl->ob->stats.Cha = iii, pl->orig_stats.Cha = iii;
1068     if (!strcmp ("int", thing2)) pl->ob->stats.Int = iii, pl->orig_stats.Int = iii;
1069     if (!strcmp ("pow", thing2)) pl->ob->stats.Pow = iii, pl->orig_stats.Pow = iii;
1070    
1071 root 1.8 sprintf (buf, "%s has been altered.", &pl->ob->name);
1072     new_draw_info (NDI_UNIQUE, 0, op, buf);
1073 root 1.26 pl->ob->update_stats ();
1074 root 1.8 return 1;
1075 elmex 1.1 }
1076 root 1.8 }
1077    
1078     new_draw_info (NDI_UNIQUE, 0, op, "No such player.");
1079     return 1;
1080 elmex 1.1 }
1081    
1082 root 1.8 int
1083     command_invisible (object *op, char *params)
1084     {
1085     if (op)
1086     {
1087 root 1.92 op->invisible += TIME2TICK (60);
1088 root 1.41 update_object (op, UP_OBJ_CHANGE);
1089 root 1.8 new_draw_info (NDI_UNIQUE, 0, op, "You turn invisible.");
1090 elmex 1.1 }
1091    
1092 root 1.8 return 0;
1093 elmex 1.1 }
1094    
1095     /**
1096     * Returns spell object (from archetypes) by name.
1097     * Returns NULL if 0 or more than one spell matches.
1098     * Used for wizard's learn spell/prayer.
1099     *
1100     * op is the player issuing the command.
1101     */
1102 root 1.8 static object *
1103 root 1.66 get_spell_by_name (object *op, shstr_cmp spell_name)
1104 root 1.8 {
1105 root 1.69 /* First check for full name matches. */
1106     int conflict_found = 0;
1107 elmex 1.78 archetype *found = 0;
1108    
1109 root 1.47 for_all_archetypes (at)
1110 root 1.8 {
1111 root 1.47 if (at->type != SPELL)
1112 root 1.8 continue;
1113    
1114 root 1.66 if (at->object::name != spell_name)
1115 root 1.8 continue;
1116    
1117 root 1.65 if (found)
1118 root 1.8 {
1119     if (!conflict_found)
1120     {
1121     conflict_found = 1;
1122 root 1.67 new_draw_info_format (NDI_UNIQUE, 0, op, "More than one archetype matches the spell name %s:", &spell_name);
1123 root 1.46 new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &found->archname);
1124 elmex 1.1 }
1125 root 1.46
1126     new_draw_info_format (NDI_UNIQUE, 0, op, "- %s", &at->archname);
1127 root 1.69 return 0;
1128 elmex 1.1 }
1129    
1130 root 1.46 found = at;
1131 elmex 1.1 }
1132    
1133 root 1.8 /* Return if exactly one archetype matches. */
1134 root 1.68 if (found)
1135 root 1.79 return found->instance ();
1136 elmex 1.1
1137 root 1.8 /* No spell found: just print an error message. */
1138 root 1.70 new_draw_info_format (NDI_UNIQUE, 0, op, "The spell does not exist.");
1139 root 1.68
1140 root 1.71 return 0;
1141 elmex 1.1 }
1142    
1143 root 1.8 static int
1144     command_learn_spell_or_prayer (object *op, char *params, int special_prayer)
1145     {
1146     object *tmp;
1147 elmex 1.1
1148 root 1.8 if (op->contr == NULL || params == NULL)
1149     {
1150     new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to learn?");
1151     return 0;
1152 elmex 1.1 }
1153    
1154 root 1.8 tmp = get_spell_by_name (op, params);
1155     if (tmp == NULL)
1156     {
1157     return 0;
1158 elmex 1.1 }
1159    
1160 root 1.8 if (check_spell_known (op, tmp->name))
1161     {
1162     new_draw_info_format (NDI_UNIQUE, 0, op, "You already know the spell %s.", &tmp->name);
1163     return 0;
1164 elmex 1.1 }
1165    
1166 root 1.8 do_learn_spell (op, tmp, special_prayer);
1167 root 1.60 tmp->destroy ();
1168 root 1.8 return 1;
1169 elmex 1.1 }
1170    
1171 root 1.8 int
1172     command_learn_spell (object *op, char *params)
1173     {
1174     return command_learn_spell_or_prayer (op, params, 0);
1175 elmex 1.1 }
1176    
1177 root 1.8 int
1178     command_learn_special_prayer (object *op, char *params)
1179 elmex 1.1 {
1180 root 1.8 return command_learn_spell_or_prayer (op, params, 1);
1181 elmex 1.1 }
1182    
1183 root 1.8 int
1184     command_forget_spell (object *op, char *params)
1185 elmex 1.1 {
1186 root 1.8 object *spell;
1187 elmex 1.1
1188 root 1.8 if (op->contr == NULL || params == NULL)
1189     {
1190     new_draw_info (NDI_UNIQUE, 0, op, "Which spell do you want to forget?");
1191     return 0;
1192 elmex 1.1 }
1193    
1194 root 1.91 spell = op->find_spell (params);
1195 root 1.8 if (spell == NULL)
1196     {
1197     new_draw_info_format (NDI_UNIQUE, 0, op, "You do not know the spell %s.", params);
1198     return 0;
1199 elmex 1.1 }
1200    
1201 root 1.8 do_forget_spell (op, spell->name);
1202     return 1;
1203 elmex 1.1 }
1204    
1205     /**
1206     * Pop the stack top.
1207     */
1208 root 1.8 int
1209     command_stack_pop (object *op, char *params)
1210     {
1211     dm_stack_pop (op->contr);
1212     return 0;
1213 elmex 1.1 }
1214    
1215     /**
1216     * Push specified item on stack.
1217     */
1218 root 1.8 int
1219     command_stack_push (object *op, char *params)
1220     {
1221     object *ob;
1222     int from;
1223    
1224     ob = get_dm_object (op->contr, &params, &from);
1225 elmex 1.1
1226 root 1.8 if (ob && from != STACK_FROM_NUMBER)
1227     /* Object was from stack, need to push it again */
1228     dm_stack_push (op->contr, ob->count);
1229    
1230     return 0;
1231 elmex 1.1 }
1232    
1233     /**
1234     * Displays stack contents.
1235     */
1236 root 1.8 int
1237     command_stack_list (object *op, char *params)
1238     {
1239     int item;
1240     object *display;
1241     player *pl = op->contr;
1242    
1243     new_draw_info (NDI_UNIQUE, 0, op, "Item stack contents:");
1244    
1245     for (item = 0; item < pl->stack_position; item++)
1246     {
1247     display = find_object (pl->stack_items[item]);
1248     if (display)
1249     new_draw_info_format (NDI_UNIQUE, 0, op, " %d : %s [%d]", item, &display->name, display->count);
1250     else
1251     /* Item was freed */
1252     new_draw_info_format (NDI_UNIQUE, 0, op, " %d : (lost item: %d)", item, pl->stack_items[item]);
1253 elmex 1.1 }
1254    
1255 root 1.8 return 0;
1256 elmex 1.1 }
1257    
1258     /**
1259     * Empty DM item stack.
1260     */
1261 root 1.8 int
1262     command_stack_clear (object *op, char *params)
1263     {
1264     op->contr->stack_position = 0;
1265     new_draw_info (NDI_UNIQUE, 0, op, "Item stack cleared.");
1266     return 0;
1267 elmex 1.1 }
1268    
1269 root 1.8 int
1270     command_insert_into (object *op, char *params)
1271 elmex 1.1 {
1272 root 1.77 object *left, *right;
1273 root 1.8 int left_from, right_from;
1274 elmex 1.1
1275 root 1.8 left = get_dm_object (op->contr, &params, &left_from);
1276     if (!left)
1277     {
1278     new_draw_info (NDI_UNIQUE, 0, op, "Insert into what object?");
1279     return 0;
1280 elmex 1.1 }
1281    
1282 root 1.8 if (left_from == STACK_FROM_NUMBER)
1283     /* Item was stacked, remove it else right will be the same... */
1284     dm_stack_pop (op->contr);
1285 elmex 1.1
1286 root 1.8 right = get_dm_object (op->contr, &params, &right_from);
1287 elmex 1.1
1288 root 1.8 if (!right)
1289     {
1290     new_draw_info (NDI_UNIQUE, 0, op, "Insert what item?");
1291     return 0;
1292 elmex 1.1 }
1293    
1294 root 1.8 if (left_from == STACK_FROM_TOP && right_from == STACK_FROM_TOP)
1295     {
1296     /*
1297     * Special case: both items were taken from stack top.
1298     * Override the behaviour, taking left as item just below top, if exists.
1299     * See function description for why.
1300     * Besides, can't insert an item into itself.
1301     */
1302     if (op->contr->stack_position > 1)
1303     {
1304     left = find_object (op->contr->stack_items[op->contr->stack_position - 2]);
1305     if (left)
1306     new_draw_info (NDI_UNIQUE, 0, op, "(Note: item to insert into taken from undertop)");
1307     else
1308     /* Stupid case: item under top was freed, fallback to stack top */
1309     left = right;
1310 elmex 1.1 }
1311     }
1312    
1313 root 1.8 if (left == right)
1314 elmex 1.1 {
1315 root 1.8 new_draw_info (NDI_UNIQUE, 0, op, "Can't insert an object into itself!");
1316     return 0;
1317 elmex 1.1 }
1318    
1319 root 1.8 if (right->type == PLAYER)
1320 elmex 1.1 {
1321 root 1.8 new_draw_info (NDI_UNIQUE, 0, op, "Can't insert a player into something!");
1322     return 0;
1323 elmex 1.1 }
1324    
1325 root 1.82 if (!right->flag [FLAG_REMOVED])
1326 root 1.21 right->remove ();
1327 root 1.54
1328 root 1.77 object *inserted = insert_ob_in_ob (right, left);
1329 elmex 1.1
1330 root 1.8 new_draw_info_format (NDI_UNIQUE, 0, op, "Inserted %s in %s", query_name (inserted), query_name (left));
1331 elmex 1.1
1332 root 1.8 return 0;
1333 elmex 1.1
1334     }