ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/build_map.C
Revision: 1.5
Committed: Sun Sep 10 15:59:57 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.4: +643 -602 lines
Log Message:
indent

File Contents

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