ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/build_map.C
Revision: 1.25
Committed: Mon Apr 30 04:25:30 2007 UTC (17 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_1
Changes since 1.24: +2 -1 lines
Log Message:
This is the first rough cut of the skill use system (use the STABLE tag).

Details will likely change, and combat skills do not work very well, but
it works quite well.

Players no longer have a shoottype or range slots, instead, each player
has these members:

   combat_skill/combat_ob  the currently selected skill (and weapon)
                           for direct attacks.
   ranged_skill/ranged_ob  the currently selected ranged skill (and
                           bow/spell/item)
   golem                   the currently-controlled golem, if any.

File Contents

# Content
1 /*
2 * CrossFire, A Multiplayer game for X-windows
3 *
4 * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5 * Copyright (C) 2001 Mark Wedel & Crossfire Development Team
6 * Copyright (C) 1992 Frank Tore Johansen
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * The authors can be reached via e-mail to <crossfire@schmorp.de>
23 */
24
25 #include <global.h>
26 #include <living.h>
27 #include <spells.h>
28 #include <skills.h>
29 #include <tod.h>
30 #include <sproto.h>
31
32 /**
33 * Check if objects on a square interfere with building
34 */
35 int
36 can_build_over (maptile *map, object *tmp, short x, short y)
37 {
38 object *ob;
39
40 ob = GET_MAP_OB (map, x, y);
41 while (ob)
42 {
43 /* if ob is not a marking rune or floor, then check special cases */
44 if (strcmp (ob->arch->name, "rune_mark") && ob->type != FLOOR)
45 {
46 switch (tmp->type)
47 {
48 case SIGN:
49 case MAGIC_EAR:
50 /* Allow signs and magic ears to be built on books */
51 if (ob->type != BOOK)
52 {
53 return 0;
54 }
55 break;
56 case BUTTON:
57 case DETECTOR:
58 case PEDESTAL:
59 case CF_HANDLE:
60 /* Allow buttons and levers to be built under gates */
61 if (ob->type != GATE && ob->type != DOOR)
62 {
63 return 0;
64 }
65 break;
66 default:
67 return 0;
68 }
69 }
70 ob = ob->above;
71 }
72 return 1;
73 }
74
75 /**
76 * Erases marking runes at specified location
77 */
78 void
79 remove_marking_runes (maptile *map, short x, short y)
80 {
81 object *rune;
82 object *next;
83
84 rune = GET_MAP_OB (map, x, y);
85 while (rune)
86 {
87 next = rune->above;
88
89 if ((rune->type == SIGN) && (!strcmp (rune->arch->name, "rune_mark")))
90 rune->destroy ();
91
92 rune = next;
93 }
94 }
95
96 /**
97 * Returns an unused value for 'connected'.
98 * \param map: map for which to find a value
99 * \return 'connected' value with no item, or -1 if failure.
100 *
101 * Tries 1000 random values, then returns -1.
102 */
103 int
104 find_unused_connected_value (maptile *map)
105 {
106 int connected = 0;
107 int itest = 0;
108 oblinkpt *obp;
109
110 while (itest++ < 1000)
111 {
112 connected = 1 + rand () % 20000;
113 for (obp = map->buttons; obp && (obp->value != connected); obp = obp->next);
114
115 if (!obp)
116 return connected;
117 }
118
119 return -1;
120 }
121
122
123 /**
124 * Helper function for door/button/connected item building.
125 *
126 * Will search the specified spot for a marking rune.
127 * If not found, returns -1
128 * Else, searches a force in op's inventory matching the map's name
129 * and the rune's text.
130 * If found, returns the connection value associated
131 * else searches a new connection value, and adds the force to the player.
132 */
133 int
134 find_or_create_connection_for_map (object *pl, short x, short y, object *rune)
135 {
136 object *force;
137 int connected;
138
139 if (!rune)
140 rune = get_connection_rune (pl, x, y);
141
142 if (!rune)
143 {
144 new_draw_info (NDI_UNIQUE, 0, pl, "You need to put a marking rune with the group name.");
145 return -1;
146 }
147
148 /* Now, find force in player's inventory */
149 force = pl->inv;
150 while (force
151 && ((force->type != FORCE) || !force->slaying || force->slaying != pl->map->path || !force->msg
152 || force->msg != rune->msg))
153 force = force->below;
154
155 if (!force)
156 /* No force, need to create & insert one */
157 {
158 /* Find unused value */
159 connected = find_unused_connected_value (pl->map);
160 if (connected == -1)
161 {
162 new_draw_info (NDI_UNIQUE, 0, pl, "Could not create more groups.");
163 return -1;
164 }
165
166 force = get_archetype (FORCE_NAME);
167 force->slaying = pl->map->path;
168 force->msg = rune->msg;
169 force->path_attuned = connected;
170 force->set_speed (0);
171 insert_ob_in_ob (force, pl);
172
173 return connected;
174 }
175
176 /* Found the force, everything's easy. */
177 return force->path_attuned;
178 }
179
180 /**
181 * Returns the marking rune on the square, for purposes of building connections
182 */
183 object *
184 get_connection_rune (object *pl, short x, short y)
185 {
186 object *rune;
187
188 rune = GET_MAP_OB (pl->map, x, y);
189 while (rune && ((rune->type != SIGN) || (strcmp (rune->arch->name, "rune_mark"))))
190 rune = rune->above;
191 return rune;
192 }
193
194 /**
195 * Returns the book/scroll on the current square, for purposes of building
196 */
197 object *
198 get_msg_book (object *pl, short x, short y)
199 {
200 object *book;
201
202 book = GET_MAP_OB (pl->map, x, y);
203 while (book && (book->type != BOOK))
204 book = book->above;
205 return book;
206 }
207
208 /**
209 * Returns first item of type BUILDABLE_WALL.
210 */
211 object *
212 get_wall (maptile *map, int x, int y)
213 {
214 object *wall;
215
216 wall = GET_MAP_OB (map, x, y);
217 while (wall && (BUILDABLE_WALL != wall->type))
218 wall = wall->above;
219
220 return wall;
221 }
222
223 /**
224 * Fixes walls around specified spot
225 *
226 * \param map is the map
227 * \param x
228 * \param y are the position to fix
229 *
230 * Basically it ensures the correct wall is put where needed.
231 *
232 * Note: x & y must be valid map coordinates.
233 */
234 void
235 fix_walls (maptile *map, int x, int y)
236 {
237 object *wall;
238 char archetype[MAX_BUF];
239 char *underscore;
240 struct archetype *new_arch;
241
242
243 /* First, find the wall on that spot */
244 wall = get_wall (map, x, y);
245 if (!wall)
246 /* Nothing -> bail out */
247 return;
248
249 /* Find base name */
250 assign (archetype, wall->arch->name);
251 underscore = strchr (archetype, '_');
252
253 /* search for the first _ before a number */
254 while (underscore && !isdigit (*(underscore + 1)))
255 underscore = strchr (underscore + 1, '_');
256
257 if (!underscore || !isdigit (*(underscore + 1)))
258 /* Not in a format we can change, bail out */
259 return;
260
261 underscore++;
262 *underscore = '\0';
263
264 int connect = 0;
265
266 if ((x > 0) && get_wall (map, x - 1, y))
267 connect |= 1;
268 if ((x < map->width - 1) && get_wall (map, x + 1, y))
269 connect |= 2;
270
271 if ((y > 0) && get_wall (map, x, y - 1))
272 connect |= 4;
273
274 if ((y < map->height - 1) && get_wall (map, x, y + 1))
275 connect |= 8;
276
277 switch (connect)
278 {
279 case 0:
280 strcat (archetype, "0");
281
282 break;
283 case 1:
284 strcat (archetype, "1_3");
285
286 break;
287 case 2:
288 strcat (archetype, "1_4");
289
290 break;
291 case 3:
292 strcat (archetype, "2_1_2");
293
294 break;
295 case 4:
296 strcat (archetype, "1_2");
297
298 break;
299 case 5:
300 strcat (archetype, "2_2_4");
301
302 break;
303 case 6:
304 strcat (archetype, "2_2_1");
305
306 break;
307 case 7:
308 strcat (archetype, "3_1");
309
310 break;
311 case 8:
312 strcat (archetype, "1_1");
313
314 break;
315 case 9:
316 strcat (archetype, "2_2_3");
317
318 break;
319 case 10:
320 strcat (archetype, "2_2_2");
321
322 break;
323 case 11:
324 strcat (archetype, "3_3");
325
326 break;
327 case 12:
328 strcat (archetype, "2_1_1");
329
330 break;
331 case 13:
332 strcat (archetype, "3_4");
333
334 break;
335 case 14:
336 strcat (archetype, "3_2");
337
338 break;
339 case 15:
340 strcat (archetype, "4");
341
342 break;
343 }
344
345 /*
346 * Before anything, make sure the archetype does exist...
347 * If not, prolly an error...
348 */
349 new_arch = archetype::find (archetype);
350
351 if (!new_arch)
352 return;
353
354 /* Now delete current wall, and insert new one
355 * We save flags to avoid any trouble with buildable/non buildable, and so on
356 */
357 object::flags_t old_flags = wall->flag; // elmex: this is where C++ pays off
358
359 wall->destroy ();
360
361 wall = arch_to_object (new_arch);
362 wall->type = BUILDABLE_WALL;
363 insert_ob_in_map_at (wall, map, NULL, INS_ABOVE_FLOOR_ONLY, x, y);
364 wall->flag = old_flags;
365 }
366
367 /**
368 * \brief Floor building function
369 *
370 * Floors can be build:
371 * - on existing floors, with or without a detector/button
372 * - on an existing wall, with or without a floor under it
373 *
374 * Note: this function will inconditionally change squares around (x, y)
375 * so don't call it with x == 0 for instance!
376 */
377 void
378 apply_builder_floor (object *pl, object *material, short x, short y)
379 {
380 object *tmp, *above;
381 object *above_floor; /* Item above floor, if any */
382 struct archetype *new_floor;
383 struct archetype *new_wall;
384 int i, xt, yt, floor_removed;
385 char message[MAX_BUF];
386
387 sprintf (message, "You change the floor to better suit your tastes.");
388
389 /*
390 * Now the building part...
391 * First, remove wall(s) and floor(s) at position x, y
392 */
393 above_floor = NULL;
394 new_wall = NULL;
395 floor_removed = 0;
396 tmp = GET_MAP_OB (pl->map, x, y);
397 if (tmp)
398 {
399 while (tmp)
400 {
401 above = tmp->above;
402 if (BUILDABLE_WALL == tmp->type)
403 {
404 /* There was a wall, remove it & keep its archetype to make new walls */
405 new_wall = tmp->arch;
406 tmp->destroy ();
407 sprintf (message, "You destroy the wall and redo the floor.");
408 }
409 else if ((FLOOR == tmp->type) || (QUERY_FLAG (tmp, FLAG_IS_FLOOR)))
410 {
411 tmp->destroy ();
412 floor_removed = 1;
413 }
414 else
415 {
416 if (floor_removed)
417 above_floor = tmp;
418 }
419
420 tmp = above;
421 }
422 }
423
424 /* Now insert our floor */
425 new_floor = archetype::find (material->slaying);
426 if (!new_floor)
427 {
428 /* Not found, log & bail out */
429 LOG (llevError, "apply_builder_floor: unable to find archetype %s.\n", &material->slaying);
430 return;
431 }
432
433 tmp = arch_to_object (new_floor);
434 SET_FLAG (tmp, FLAG_IS_BUILDABLE);
435 SET_FLAG (tmp, FLAG_UNIQUE);
436 SET_FLAG (tmp, FLAG_IS_FLOOR);
437 tmp->type = FLOOR;
438 insert_ob_in_map_at (tmp, pl->map, above_floor, above_floor ? INS_BELOW_ORIGINATOR : INS_ON_TOP, x, y);
439
440 /*
441 * Next step: make sure there are either walls or floors around the new square
442 * Since building, you can have: blocking view / floor / wall / nothing
443 */
444 for (i = 1; i <= 8; i++)
445 {
446 xt = x + freearr_x[i];
447 yt = y + freearr_y[i];
448 tmp = GET_MAP_OB (pl->map, xt, yt);
449 if (!tmp)
450 {
451 /* Must insert floor & wall */
452 tmp = arch_to_object (new_floor);
453 /* Better make the floor unique */
454 SET_FLAG (tmp, FLAG_UNIQUE);
455 SET_FLAG (tmp, FLAG_IS_BUILDABLE);
456 tmp->type = FLOOR;
457 insert_ob_in_map_at (tmp, pl->map, 0, 0, xt, yt);
458 /* Insert wall if exists. Note: if it doesn't, the map is weird... */
459 if (new_wall)
460 {
461 tmp = arch_to_object (new_wall);
462 SET_FLAG (tmp, FLAG_IS_BUILDABLE);
463 tmp->type = BUILDABLE_WALL;
464 insert_ob_in_map_at (tmp, pl->map, 0, 0, xt, yt);
465 }
466 }
467 }
468
469 /* Finally fixing walls to ensure nice continuous walls
470 * Note: 2 squares around are checked, because potentially we added walls around the building
471 * spot, so need to check that those new walls connect correctly
472 */
473 for (xt = x - 2; xt <= x + 2; xt++)
474 for (yt = y - 2; yt <= y + 2; yt++)
475 {
476 if (!OUT_OF_REAL_MAP (pl->map, xt, yt))
477 fix_walls (pl->map, xt, yt);
478 }
479
480 /* Now remove raw item from inventory */
481 decrease_ob (material);
482
483 /* And tell player about the fix */
484 new_draw_info (NDI_UNIQUE, 0, pl, message);
485 }
486
487 /**
488 * Wall radius fix function
489 */
490 void fix_walls_around (maptile *map, int x, int y)
491 {
492 for (int xt = x - 1; xt <= x + 1; xt++)
493 for (int yt = y - 1; yt <= y + 1; yt++)
494 {
495 if (OUT_OF_REAL_MAP (map, xt, yt))
496 continue;
497
498 fix_walls (map, xt, yt);
499 }
500 }
501
502 /**
503 * Wall building function
504 *
505 * Walls can be build:
506 * - on a floor without anything else
507 * - on an existing wall, with or without a floor
508 */
509 void
510 apply_builder_wall (object *pl, object *material, short x, short y)
511 {
512 object *current_wall;
513 object *tmp;
514 int xt, yt;
515 struct archetype *new_wall;
516 char message[MAX_BUF];
517
518 remove_marking_runes (pl->map, x, y);
519
520 /* Grab existing wall, if any */
521 current_wall = NULL;
522 tmp = GET_MAP_OB (pl->map, x, y);
523 while (tmp && !current_wall)
524 {
525 if (BUILDABLE_WALL == tmp->type)
526 current_wall = tmp;
527
528 tmp = tmp->above;
529 }
530
531 /* Find the raw wall in inventory */
532 sprintf (message, "You build a wall.");
533
534 /* Now we can actually insert the wall */
535 new_wall = archetype::find (material->slaying);
536 if (!new_wall)
537 {
538 LOG (llevError, "apply_builder_wall: unable to find archetype %s\n", &material->slaying);
539 return;
540 }
541
542 tmp = arch_to_object (new_wall);
543 tmp->type = BUILDABLE_WALL;
544 SET_FLAG (tmp, FLAG_IS_BUILDABLE);
545 insert_ob_in_map_at (tmp, pl->map, 0, INS_ABOVE_FLOOR_ONLY, x, y);
546
547 /* If existing wall, remove it, no need to fix other walls */
548 if (current_wall)
549 {
550 current_wall->destroy ();
551 fix_walls (pl->map, x, y);
552 sprintf (message, "You redecorate the wall to better suit your tastes.");
553 }
554 else
555 {
556 /* Else fix all walls around */
557 for (xt = x - 1; xt <= x + 1; xt++)
558 for (yt = y - 1; yt <= y + 1; yt++)
559 {
560 if (OUT_OF_REAL_MAP (pl->map, xt, yt))
561 continue;
562
563 fix_walls (pl->map, xt, yt);
564 }
565 }
566
567 /* Now remove item from inventory */
568 decrease_ob (material);
569
570 /* And tell player what happened */
571 new_draw_info (NDI_UNIQUE, 0, pl, message);
572 }
573
574 /**
575 * Generic item builder.
576 *
577 * Item must be put on a square with a floor, you can have something under.
578 * Archetype of created object is item->slaying (raw material).
579 * Type of inserted item is tested for specific cases (doors & such).
580 * Item is inserted above the floor, unless Str == 1 (only for detectors i guess)
581 */
582 void
583 apply_builder_item (object *pl, object *item, short x, short y)
584 {
585 object *tmp;
586 struct archetype *arch;
587 int insert_flag;
588 object *floor;
589 object *con_rune;
590 int connected;
591
592 /* Find floor */
593 floor = GET_MAP_OB (pl->map, x, y);
594 if (!floor)
595 {
596 new_draw_info (NDI_UNIQUE, 0, pl, "Invalid square.");
597 return;
598 }
599
600 while (floor && (floor->type != FLOOR) && (!QUERY_FLAG (floor, FLAG_IS_FLOOR)))
601 floor = floor->above;
602
603 if (!floor)
604 {
605 new_draw_info (NDI_UNIQUE, 0, pl, "This square has no floor, you can't build here.");
606 return;
607 }
608 /* Create item, set flag, insert in map */
609 arch = archetype::find (item->slaying);
610 if (!arch)
611 return;
612
613 tmp = arch_to_object (arch);
614
615 if ((floor->above) && (!can_build_over (pl->map, tmp, x, y)))
616 /* Floor has something on top that interferes with building */
617 {
618 new_draw_info (NDI_UNIQUE, 0, pl, "You can't build here.");
619 return;
620 }
621
622 SET_FLAG (tmp, FLAG_IS_BUILDABLE);
623 SET_FLAG (tmp, FLAG_NO_PICK);
624
625 /*
626 * This doesn't work on non unique maps. pedestals under floor will not be saved...
627 insert_flag = ( item->stats.Str == 1 ) ? INS_BELOW_ORIGINATOR : INS_ABOVE_FLOOR_ONLY;
628 */
629 insert_flag = INS_ABOVE_FLOOR_ONLY;
630
631 connected = 0;
632 switch (tmp->type)
633 {
634 case DOOR:
635 case GATE:
636 case BUTTON:
637 case DETECTOR:
638 case TIMED_GATE:
639 case PEDESTAL:
640 case CF_HANDLE:
641 case MAGIC_EAR:
642 case SIGN:
643 /* Signs don't need a connection, but but magic mouths do. */
644 if (tmp->type == SIGN && strcmp (tmp->arch->name, "magic_mouth"))
645 break;
646 con_rune = get_connection_rune (pl, x, y);
647 connected = find_or_create_connection_for_map (pl, x, y, con_rune);
648 if (connected == -1)
649 {
650 /* Player already informed of failure by the previous function */
651 tmp->destroy ();
652 return;
653 }
654 /* Remove marking rune */
655 con_rune->destroy ();
656 }
657
658 /* For magic mouths/ears, and signs, take the msg from a book of scroll */
659 if ((tmp->type == SIGN) || (tmp->type == MAGIC_EAR))
660 {
661 if (adjust_sign_msg (pl, x, y, tmp) == -1)
662 {
663 tmp->destroy ();
664 return;
665 }
666 }
667
668 insert_ob_in_map_at (tmp, pl->map, floor, insert_flag, x, y);
669 if (connected != 0)
670 add_button_link (tmp, pl->map, connected);
671
672 new_draw_info_format (NDI_UNIQUE, 0, pl, "You build the %s", query_name (tmp));
673 decrease_ob_nr (item, 1);
674 }
675
676 /**
677 * Item remover.
678 *
679 * Removes first buildable item, either under or above the floor
680 */
681 void
682 apply_builder_remove (object *pl, int dir)
683 {
684 object *item;
685 short x, y;
686
687 x = pl->x + freearr_x[dir];
688 y = pl->y + freearr_y[dir];
689
690 /* Check square */
691 item = GET_MAP_OB (pl->map, x, y);
692 if (!item)
693 {
694 /* Should not happen with previous tests, but we never know */
695 new_draw_info (NDI_UNIQUE, 0, pl, "Invalid square.");
696 LOG (llevError, "apply_builder_remove: (null) square at (%d, %d, %s)\n", x, y, &pl->map->path);
697 return;
698 }
699
700 if (item->type == FLOOR || QUERY_FLAG (item, FLAG_IS_FLOOR))
701 item = item->above;
702
703 if (!item)
704 new_draw_info (NDI_UNIQUE, 0, pl, "Nothing to remove.");
705 else if (item->type == BUILDABLE_WALL)
706 new_draw_info (NDI_UNIQUE, 0, pl, "Can't remove a wall with that, build a floor.");
707 else if (!item->flag [FLAG_IS_BUILDABLE])
708 new_draw_info_format (NDI_UNIQUE, 0, pl, "You can't remove the %s, it's not buildable!", query_name (item));
709 else
710 {
711 new_draw_info_format (NDI_UNIQUE, 0, pl, "You remove the %s", query_name (item));
712 item->destroy ();
713 }
714 }
715
716 /**
717 * Global building function
718 *
719 * This is the general map building function. Called when the player 'fires' a builder
720 * or remover object.
721 */
722 void
723 apply_map_builder (object *pl, int dir)
724 {
725 object *builder;
726 object *tmp;
727 object *tmp2;
728 short x, y;
729
730 if (!pl->type == PLAYER)
731 return;
732
733 /*if ( !player->map->unique )
734 {
735 new_draw_info( NDI_UNIQUE, 0, player, "You can't build outside a unique map." );
736 return;
737 } */
738
739 if (dir == 0)
740 {
741 new_draw_info (NDI_UNIQUE, 0, pl, "You can't build or destroy under yourself.");
742 return;
743 }
744
745 x = pl->x + freearr_x[dir];
746 y = pl->y + freearr_y[dir];
747
748 if ((1 > x) || (1 > y) || ((pl->map->width - 2) < x) || ((pl->map->height - 2) < y))
749 {
750 new_draw_info (NDI_UNIQUE, 0, pl, "Can't build on map edge...");
751 return;
752 }
753
754 /*
755 * Check specified square
756 * The square must have only buildable items
757 * Exception: marking runes are all right,
758 * since they are used for special things like connecting doors / buttons
759 */
760
761 tmp = GET_MAP_OB (pl->map, x, y);
762 if (!tmp)
763 {
764 /* Nothing, meaning player is standing next to an undefined square... */
765 LOG (llevError, "apply_map_builder: undefined square at (%d, %d, %s)\n", x, y, &pl->map->path);
766 new_draw_info (NDI_UNIQUE, 0, pl, "You'd better not build here, it looks weird.");
767 return;
768 }
769
770 tmp2 = find_marked_object (pl);
771 while (tmp)
772 {
773 if (!QUERY_FLAG (tmp, FLAG_IS_BUILDABLE) && ((tmp->type != SIGN) || (strcmp (tmp->arch->name, "rune_mark"))))
774 {
775 /* The item building function already has it's own special
776 * checks for this
777 */
778 if ((!tmp2) || (tmp2->subtype != ST_MAT_ITEM))
779 {
780 new_draw_info (NDI_UNIQUE, 0, pl, "You can't build here.");
781 return;
782 }
783 }
784 tmp = tmp->above;
785 }
786
787 /* Now we know the square is ok */
788 builder = pl->contr->ranged_ob;
789
790 if (builder->subtype == ST_BD_REMOVE)
791 /* Remover -> call specific function and bail out */
792 {
793 apply_builder_remove (pl, dir);
794 return;
795 }
796
797 if (builder->subtype == ST_BD_BUILD)
798 /*
799 * Builder.
800 * Find marked item to build, call specific function
801 */
802 {
803 tmp = tmp2;
804 if (!tmp)
805 {
806 new_draw_info (NDI_UNIQUE, 0, pl, "You need to mark raw materials to use.");
807 return;
808 }
809
810 if (tmp->type != MATERIAL)
811 {
812 new_draw_info (NDI_UNIQUE, 0, pl, "You can't use the marked item to build.");
813 return;
814 }
815
816 switch (tmp->subtype)
817 {
818 case ST_MAT_FLOOR:
819 apply_builder_floor (pl, tmp, x, y);
820 return;
821
822 case ST_MAT_WALL:
823 apply_builder_wall (pl, tmp, x, y);
824 return;
825
826 case ST_MAT_ITEM:
827 apply_builder_item (pl, tmp, x, y);
828 return;
829
830 default:
831 new_draw_info (NDI_UNIQUE, 0, pl, "Don't know how to apply this material, sorry.");
832 LOG (llevError, "apply_map_builder: invalid material subtype %d\n", tmp->subtype);
833 return;
834 }
835 }
836
837 /* Here, it means the builder has an invalid type */
838 new_draw_info (NDI_UNIQUE, 0, pl, "Don't know how to apply this tool, sorry.");
839 LOG (llevError, "apply_map_builder: invalid builder subtype %d\n", builder->subtype);
840 }
841
842 /**
843 * Make the built object inherit the msg of books that are used with it.
844 * For objects already invisible (i.e. magic mouths & ears), also make it
845 * it inherit the face and the name with "talking " prepended.
846 */
847 int
848 adjust_sign_msg (object *pl, short x, short y, object *tmp)
849 {
850 object *book;
851 char buf[MAX_BUF];
852 char buf2[MAX_BUF];
853
854 book = get_msg_book (pl, x, y);
855 if (!book)
856 {
857 new_draw_info (NDI_UNIQUE, 0, pl, "You need to put a book or scroll with the message.");
858 return -1;
859 }
860
861 tmp->msg = book->msg;
862
863 if (tmp->invisible)
864 {
865 if (book->custom_name != NULL)
866 {
867 snprintf (buf, sizeof (buf), "talking %s", &book->custom_name);
868 }
869 else
870 {
871 snprintf (buf, sizeof (buf), "talking %s", &book->name);
872 }
873 tmp->name = buf;
874
875 if (book->name_pl != NULL)
876 {
877 snprintf (buf2, sizeof (buf2), "talking %s", &book->name_pl);
878 tmp->name_pl = buf2;
879 }
880
881 tmp->face = book->face;
882 tmp->invisible = 0;
883 }
884
885 book->destroy ();
886 return 0;
887 }