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

# User Rev Content
1 elmex 1.1 /*
2 pippijn 1.22 * 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 elmex 1.1
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 root 1.5 int
36 root 1.8 can_build_over (maptile *map, object *tmp, short x, short y)
37 root 1.5 {
38     object *ob;
39    
40     ob = GET_MAP_OB (map, x, y);
41     while (ob)
42 elmex 1.1 {
43 root 1.5 /* 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 elmex 1.1 {
46 root 1.5 switch (tmp->type)
47 root 1.2 {
48     case SIGN:
49     case MAGIC_EAR:
50 root 1.5 /* Allow signs and magic ears to be built on books */
51     if (ob->type != BOOK)
52     {
53     return 0;
54     }
55     break;
56 root 1.2 case BUTTON:
57     case DETECTOR:
58 elmex 1.1 case PEDESTAL:
59     case CF_HANDLE:
60 root 1.5 /* 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 root 1.2 default:
67 root 1.5 return 0;
68 elmex 1.1 }
69 root 1.2 }
70 root 1.5 ob = ob->above;
71 elmex 1.1 }
72 root 1.5 return 1;
73     }
74 elmex 1.1
75     /**
76     * Erases marking runes at specified location
77     */
78 root 1.5 void
79 root 1.8 remove_marking_runes (maptile *map, short x, short y)
80 root 1.5 {
81     object *rune;
82     object *next;
83    
84     rune = GET_MAP_OB (map, x, y);
85     while (rune)
86 elmex 1.1 {
87 root 1.5 next = rune->above;
88 root 1.12
89 root 1.5 if ((rune->type == SIGN) && (!strcmp (rune->arch->name, "rune_mark")))
90 root 1.12 rune->destroy ();
91    
92 root 1.5 rune = next;
93 elmex 1.1 }
94 root 1.5 }
95 elmex 1.1
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 root 1.5 int
104 root 1.8 find_unused_connected_value (maptile *map)
105 root 1.5 {
106     int connected = 0;
107     int itest = 0;
108     oblinkpt *obp;
109    
110     while (itest++ < 1000)
111 elmex 1.1 {
112 root 1.5 connected = 1 + rand () % 20000;
113     for (obp = map->buttons; obp && (obp->value != connected); obp = obp->next);
114 elmex 1.1
115 root 1.5 if (!obp)
116     return connected;
117     }
118 elmex 1.1
119 root 1.5 return -1;
120     }
121 elmex 1.1
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 root 1.5 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 elmex 1.1 {
144 root 1.5 new_draw_info (NDI_UNIQUE, 0, pl, "You need to put a marking rune with the group name.");
145     return -1;
146     }
147 elmex 1.1
148 root 1.5 /* Now, find force in player's inventory */
149     force = pl->inv;
150     while (force
151 root 1.24 && ((force->type != FORCE) || !force->slaying || force->slaying != pl->map->path || !force->msg
152     || force->msg != rune->msg))
153 root 1.5 force = force->below;
154 elmex 1.1
155 root 1.5 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 elmex 1.1 {
162 root 1.5 new_draw_info (NDI_UNIQUE, 0, pl, "Could not create more groups.");
163     return -1;
164 elmex 1.1 }
165    
166 root 1.5 force = get_archetype (FORCE_NAME);
167     force->slaying = pl->map->path;
168     force->msg = rune->msg;
169     force->path_attuned = connected;
170 root 1.19 force->set_speed (0);
171 root 1.5 insert_ob_in_ob (force, pl);
172 elmex 1.1
173 root 1.5 return connected;
174     }
175 elmex 1.1
176 root 1.5 /* Found the force, everything's easy. */
177     return force->path_attuned;
178     }
179 elmex 1.1
180     /**
181     * Returns the marking rune on the square, for purposes of building connections
182     */
183 root 1.5 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 elmex 1.1 /**
195     * Returns the book/scroll on the current square, for purposes of building
196     */
197 root 1.5 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 elmex 1.1
208     /**
209 elmex 1.13 * Returns first item of type BUILDABLE_WALL.
210 elmex 1.1 */
211 root 1.5 object *
212 root 1.8 get_wall (maptile *map, int x, int y)
213 root 1.5 {
214     object *wall;
215    
216     wall = GET_MAP_OB (map, x, y);
217 elmex 1.13 while (wall && (BUILDABLE_WALL != wall->type))
218 elmex 1.10 wall = wall->above;
219 elmex 1.1
220 root 1.5 return wall;
221     }
222 elmex 1.1
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 root 1.5 void
235 root 1.8 fix_walls (maptile *map, int x, int y)
236 root 1.5 {
237     object *wall;
238     char archetype[MAX_BUF];
239     char *underscore;
240     struct archetype *new_arch;
241    
242 elmex 1.9
243 root 1.5 /* 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 root 1.23 assign (archetype, wall->arch->name);
251 root 1.5 underscore = strchr (archetype, '_');
252    
253 elmex 1.10 /* 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 root 1.5 /* Not in a format we can change, bail out */
259     return;
260    
261     underscore++;
262     *underscore = '\0';
263    
264 elmex 1.14 int connect = 0;
265 root 1.5
266     if ((x > 0) && get_wall (map, x - 1, y))
267     connect |= 1;
268 root 1.17 if ((x < map->width - 1) && get_wall (map, x + 1, y))
269 root 1.5 connect |= 2;
270 elmex 1.1
271 root 1.5 if ((y > 0) && get_wall (map, x, y - 1))
272     connect |= 4;
273 elmex 1.1
274 root 1.17 if ((y < map->height - 1) && get_wall (map, x, y + 1))
275 root 1.5 connect |= 8;
276 elmex 1.1
277 root 1.5 switch (connect)
278     {
279 elmex 1.1 case 0:
280 root 1.5 strcat (archetype, "0");
281    
282     break;
283 elmex 1.1 case 1:
284 root 1.5 strcat (archetype, "1_3");
285    
286     break;
287 elmex 1.1 case 2:
288 root 1.5 strcat (archetype, "1_4");
289    
290     break;
291 elmex 1.1 case 3:
292 root 1.5 strcat (archetype, "2_1_2");
293    
294     break;
295 elmex 1.1 case 4:
296 root 1.5 strcat (archetype, "1_2");
297    
298     break;
299 elmex 1.1 case 5:
300 root 1.5 strcat (archetype, "2_2_4");
301    
302     break;
303 elmex 1.1 case 6:
304 root 1.5 strcat (archetype, "2_2_1");
305    
306     break;
307 elmex 1.1 case 7:
308 root 1.5 strcat (archetype, "3_1");
309    
310     break;
311 elmex 1.1 case 8:
312 root 1.5 strcat (archetype, "1_1");
313    
314     break;
315 elmex 1.1 case 9:
316 root 1.5 strcat (archetype, "2_2_3");
317    
318     break;
319 elmex 1.1 case 10:
320 root 1.5 strcat (archetype, "2_2_2");
321    
322     break;
323 elmex 1.1 case 11:
324 root 1.5 strcat (archetype, "3_3");
325    
326     break;
327 elmex 1.1 case 12:
328 root 1.5 strcat (archetype, "2_1_1");
329    
330     break;
331 elmex 1.1 case 13:
332 root 1.5 strcat (archetype, "3_4");
333    
334     break;
335 elmex 1.1 case 14:
336 root 1.5 strcat (archetype, "3_2");
337    
338     break;
339 elmex 1.1 case 15:
340 root 1.5 strcat (archetype, "4");
341 elmex 1.1
342 root 1.5 break;
343     }
344 elmex 1.1
345 root 1.5 /*
346     * Before anything, make sure the archetype does exist...
347     * If not, prolly an error...
348     */
349 root 1.6 new_arch = archetype::find (archetype);
350 root 1.5
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 root 1.15 object::flags_t old_flags = wall->flag; // elmex: this is where C++ pays off
358 root 1.12
359     wall->destroy ();
360 root 1.5
361     wall = arch_to_object (new_arch);
362 elmex 1.13 wall->type = BUILDABLE_WALL;
363 root 1.5 insert_ob_in_map_at (wall, map, NULL, INS_ABOVE_FLOOR_ONLY, x, y);
364 root 1.15 wall->flag = old_flags;
365 root 1.5 }
366 elmex 1.1
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 root 1.5 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 elmex 1.13 if (BUILDABLE_WALL == tmp->type)
403 root 1.5 {
404     /* There was a wall, remove it & keep its archetype to make new walls */
405     new_wall = tmp->arch;
406 root 1.12 tmp->destroy ();
407 root 1.5 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 root 1.12 tmp->destroy ();
412 root 1.5 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 root 1.6 new_floor = archetype::find (material->slaying);
426 root 1.5 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 elmex 1.13 tmp->type = BUILDABLE_WALL;
464 root 1.5 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 elmex 1.1
487     /**
488 elmex 1.9 * 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 elmex 1.1 * 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 root 1.5 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 elmex 1.1
518 root 1.5 remove_marking_runes (pl->map, x, y);
519 elmex 1.1
520 root 1.5 /* 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 elmex 1.13 if (BUILDABLE_WALL == tmp->type)
526 root 1.5 current_wall = tmp;
527 elmex 1.1
528 root 1.5 tmp = tmp->above;
529     }
530 elmex 1.1
531 root 1.5 /* Find the raw wall in inventory */
532     sprintf (message, "You build a wall.");
533 elmex 1.1
534 root 1.5 /* Now we can actually insert the wall */
535 root 1.6 new_wall = archetype::find (material->slaying);
536 root 1.5 if (!new_wall)
537     {
538     LOG (llevError, "apply_builder_wall: unable to find archetype %s\n", &material->slaying);
539     return;
540     }
541 elmex 1.1
542 root 1.5 tmp = arch_to_object (new_wall);
543 elmex 1.13 tmp->type = BUILDABLE_WALL;
544 root 1.5 SET_FLAG (tmp, FLAG_IS_BUILDABLE);
545     insert_ob_in_map_at (tmp, pl->map, 0, INS_ABOVE_FLOOR_ONLY, x, y);
546 elmex 1.1
547 root 1.5 /* If existing wall, remove it, no need to fix other walls */
548     if (current_wall)
549     {
550 root 1.12 current_wall->destroy ();
551 root 1.5 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 elmex 1.1
563 root 1.5 fix_walls (pl->map, xt, yt);
564     }
565     }
566 elmex 1.1
567 root 1.5 /* Now remove item from inventory */
568     decrease_ob (material);
569 elmex 1.1
570 root 1.5 /* And tell player what happened */
571     new_draw_info (NDI_UNIQUE, 0, pl, message);
572     }
573 elmex 1.1
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 root 1.5 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 elmex 1.1 {
596 root 1.5 new_draw_info (NDI_UNIQUE, 0, pl, "Invalid square.");
597     return;
598     }
599 elmex 1.1
600 root 1.5 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 root 1.6 arch = archetype::find (item->slaying);
610 root 1.5 if (!arch)
611     return;
612 elmex 1.1
613 root 1.5 tmp = arch_to_object (arch);
614 elmex 1.1
615 root 1.5 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 elmex 1.1
622 root 1.5 SET_FLAG (tmp, FLAG_IS_BUILDABLE);
623     SET_FLAG (tmp, FLAG_NO_PICK);
624 elmex 1.1
625 root 1.5 /*
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 elmex 1.1
631 root 1.5 connected = 0;
632     switch (tmp->type)
633     {
634 elmex 1.1 case DOOR:
635     case GATE:
636     case BUTTON:
637     case DETECTOR:
638     case TIMED_GATE:
639     case PEDESTAL:
640     case CF_HANDLE:
641 root 1.2 case MAGIC_EAR:
642     case SIGN:
643 root 1.5 /* 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 root 1.2 {
650 root 1.5 /* Player already informed of failure by the previous function */
651 root 1.12 tmp->destroy ();
652 root 1.5 return;
653 root 1.2 }
654 root 1.5 /* Remove marking rune */
655 root 1.12 con_rune->destroy ();
656 root 1.5 }
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 root 1.12 tmp->destroy ();
664 root 1.5 return;
665 root 1.2 }
666 root 1.5 }
667 elmex 1.1
668 root 1.5 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 elmex 1.1
672 root 1.5 new_draw_info_format (NDI_UNIQUE, 0, pl, "You build the %s", query_name (tmp));
673     decrease_ob_nr (item, 1);
674     }
675 elmex 1.1
676     /**
677     * Item remover.
678     *
679     * Removes first buildable item, either under or above the floor
680     */
681 root 1.5 void
682     apply_builder_remove (object *pl, int dir)
683     {
684     object *item;
685     short x, y;
686 elmex 1.1
687 root 1.5 x = pl->x + freearr_x[dir];
688     y = pl->y + freearr_y[dir];
689 elmex 1.1
690 root 1.5 /* 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 root 1.20 LOG (llevError, "apply_builder_remove: (null) square at (%d, %d, %s)\n", x, y, &pl->map->path);
697 root 1.5 return;
698     }
699 elmex 1.1
700 root 1.5 if (item->type == FLOOR || QUERY_FLAG (item, FLAG_IS_FLOOR))
701     item = item->above;
702 elmex 1.1
703 root 1.5 if (!item)
704 root 1.18 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 root 1.5 {
711 root 1.18 new_draw_info_format (NDI_UNIQUE, 0, pl, "You remove the %s", query_name (item));
712     item->destroy ();
713 elmex 1.1 }
714 root 1.5 }
715 elmex 1.1
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 root 1.5 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 elmex 1.1 {
741 root 1.5 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 elmex 1.1
748 root 1.17 if ((1 > x) || (1 > y) || ((pl->map->width - 2) < x) || ((pl->map->height - 2) < y))
749 root 1.5 {
750     new_draw_info (NDI_UNIQUE, 0, pl, "Can't build on map edge...");
751     return;
752     }
753 elmex 1.1
754 root 1.5 /*
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 elmex 1.1
761 root 1.5 tmp = GET_MAP_OB (pl->map, x, y);
762     if (!tmp)
763     {
764     /* Nothing, meaning player is standing next to an undefined square... */
765 root 1.20 LOG (llevError, "apply_map_builder: undefined square at (%d, %d, %s)\n", x, y, &pl->map->path);
766 root 1.5 new_draw_info (NDI_UNIQUE, 0, pl, "You'd better not build here, it looks weird.");
767     return;
768     }
769 root 1.25
770 root 1.5 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 elmex 1.1 {
775 root 1.5 /* 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 elmex 1.1 }
784 root 1.5 tmp = tmp->above;
785     }
786 elmex 1.1
787 root 1.5 /* Now we know the square is ok */
788 root 1.25 builder = pl->contr->ranged_ob;
789 elmex 1.1
790 root 1.5 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 elmex 1.1
797 root 1.5 if (builder->subtype == ST_BD_BUILD)
798 elmex 1.1 /*
799 root 1.5 * Builder.
800     * Find marked item to build, call specific function
801 elmex 1.1 */
802 root 1.5 {
803     tmp = tmp2;
804     if (!tmp)
805 elmex 1.1 {
806 root 1.5 new_draw_info (NDI_UNIQUE, 0, pl, "You need to mark raw materials to use.");
807     return;
808 elmex 1.1 }
809    
810 root 1.5 if (tmp->type != MATERIAL)
811 elmex 1.1 {
812 root 1.5 new_draw_info (NDI_UNIQUE, 0, pl, "You can't use the marked item to build.");
813     return;
814 elmex 1.1 }
815    
816 root 1.5 switch (tmp->subtype)
817 elmex 1.1 {
818 root 1.5 case ST_MAT_FLOOR:
819     apply_builder_floor (pl, tmp, x, y);
820     return;
821 elmex 1.1
822     case ST_MAT_WALL:
823 root 1.5 apply_builder_wall (pl, tmp, x, y);
824     return;
825 elmex 1.1
826     case ST_MAT_ITEM:
827 root 1.5 apply_builder_item (pl, tmp, x, y);
828     return;
829 elmex 1.1
830     default:
831 root 1.5 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 elmex 1.1 }
835 root 1.5 }
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 elmex 1.1
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 root 1.5 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 elmex 1.1 {
857 root 1.5 new_draw_info (NDI_UNIQUE, 0, pl, "You need to put a book or scroll with the message.");
858     return -1;
859     }
860 elmex 1.1
861 root 1.5 tmp->msg = book->msg;
862    
863     if (tmp->invisible)
864     {
865     if (book->custom_name != NULL)
866 elmex 1.1 {
867 root 1.5 snprintf (buf, sizeof (buf), "talking %s", &book->custom_name);
868 root 1.2 }
869 root 1.5 else
870     {
871     snprintf (buf, sizeof (buf), "talking %s", &book->name);
872     }
873     tmp->name = buf;
874    
875     if (book->name_pl != NULL)
876 elmex 1.1 {
877 root 1.5 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 elmex 1.1 }
884 root 1.12
885     book->destroy ();
886 root 1.5 return 0;
887     }