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