ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/build_map.C
Revision: 1.28
Committed: Sun Jul 1 05:00:19 2007 UTC (16 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.27: +10 -11 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

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