1 | /* |
1 | /* |
2 | * This file is part of Deliantra, the Roguelike Realtime MMORPG. |
2 | * This file is part of Deliantra, the Roguelike Realtime MMORPG. |
3 | * |
3 | * |
|
|
4 | * Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team |
4 | * Copyright (©) 2005,2006,2007,2008,2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team |
5 | * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team |
5 | * Copyright (©) 2001 Mark Wedel & Crossfire Development Team |
6 | * Copyright (©) 2001 Mark Wedel & Crossfire Development Team |
6 | * Copyright (©) 1992 Frank Tore Johansen |
7 | * Copyright (©) 1992 Frank Tore Johansen |
7 | * |
8 | * |
8 | * Deliantra is free software: you can redistribute it and/or modify it under |
9 | * Deliantra is free software: you can redistribute it and/or modify it under |
9 | * the terms of the Affero GNU General Public License as published by the |
10 | * the terms of the Affero GNU General Public License as published by the |
10 | * Free Software Foundation, either version 3 of the License, or (at your |
11 | * Free Software Foundation, either version 3 of the License, or (at your |
11 | * option) any later version. |
12 | * option) any later version. |
12 | * |
13 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
17 | * GNU General Public License for more details. |
17 | * |
18 | * |
18 | * You should have received a copy of the Affero GNU General Public License |
19 | * You should have received a copy of the Affero GNU General Public License |
19 | * and the GNU General Public License along with this program. If not, see |
20 | * and the GNU General Public License along with this program. If not, see |
20 | * <http://www.gnu.org/licenses/>. |
21 | * <http://www.gnu.org/licenses/>. |
21 | * |
22 | * |
22 | * The authors can be reached via e-mail to <support@deliantra.net> |
23 | * The authors can be reached via e-mail to <support@deliantra.net> |
23 | */ |
24 | */ |
24 | |
25 | |
25 | #include <global.h> |
26 | #include <global.h> |
26 | #include <living.h> |
27 | #include <living.h> |
… | |
… | |
174 | { |
175 | { |
175 | new_draw_info (NDI_UNIQUE, 0, pl, "Could not create more groups."); |
176 | new_draw_info (NDI_UNIQUE, 0, pl, "Could not create more groups."); |
176 | return shstr_tmp (); |
177 | return shstr_tmp (); |
177 | } |
178 | } |
178 | |
179 | |
179 | force = get_archetype (FORCE_NAME); |
180 | force = archetype::get (FORCE_NAME); |
180 | force->slaying = pl->map->path; |
181 | force->slaying = pl->map->path; |
181 | force->msg = rune->msg; |
182 | force->msg = rune->msg; |
182 | force->race = id; |
183 | force->race = id; |
183 | force->set_speed (0); |
184 | force->set_speed (0); |
184 | |
185 | |
… | |
… | |
222 | { |
223 | { |
223 | new_draw_info (NDI_UNIQUE, 0, pl, "You need to put a book or scroll with the message."); |
224 | new_draw_info (NDI_UNIQUE, 0, pl, "You need to put a book or scroll with the message."); |
224 | return -1; |
225 | return -1; |
225 | } |
226 | } |
226 | |
227 | |
|
|
228 | if (tmp->type == MAGIC_EAR) |
|
|
229 | { |
|
|
230 | snprintf (buf, sizeof (buf), "@match %s", &(book->msg)); |
|
|
231 | tmp->msg = buf; |
|
|
232 | } |
|
|
233 | else |
227 | tmp->msg = book->msg; |
234 | tmp->msg = book->msg; |
228 | |
235 | |
229 | if (tmp->invisible) |
236 | if (tmp->invisible) |
230 | { |
237 | { |
231 | if (book->custom_name) |
238 | if (book->custom_name) |
232 | snprintf (buf, sizeof (buf), "talking %s", &book->custom_name); |
239 | snprintf (buf, sizeof (buf), "talking %s", &book->custom_name); |
… | |
… | |
307 | if (x > 0 && get_wall (map, x - 1, y)) connect |= 1; |
314 | if (x > 0 && get_wall (map, x - 1, y)) connect |= 1; |
308 | if (x < map->width - 1 && get_wall (map, x + 1, y)) connect |= 2; |
315 | if (x < map->width - 1 && get_wall (map, x + 1, y)) connect |= 2; |
309 | if (y > 0 && get_wall (map, x, y - 1)) connect |= 4; |
316 | if (y > 0 && get_wall (map, x, y - 1)) connect |= 4; |
310 | if (y < map->height - 1 && get_wall (map, x, y + 1)) connect |= 8; |
317 | if (y < map->height - 1 && get_wall (map, x, y + 1)) connect |= 8; |
311 | |
318 | |
312 | // one bit per dir, 1 left, 2 right, 4 up, 8 down |
|
|
313 | static const char *walltype[16] = { |
|
|
314 | "0", |
|
|
315 | "1_3", |
|
|
316 | "1_4", |
|
|
317 | "2_1_2", |
|
|
318 | "1_2", |
|
|
319 | "2_2_4", |
|
|
320 | "2_2_1", |
|
|
321 | "3_1", |
|
|
322 | "1_1", |
|
|
323 | "2_2_3", |
|
|
324 | "2_2_2", |
|
|
325 | "3_3", |
|
|
326 | "2_1_1", |
|
|
327 | "3_4", |
|
|
328 | "3_2", |
|
|
329 | "4" |
|
|
330 | }; |
|
|
331 | |
|
|
332 | strcat (archetype, walltype [connect]); |
319 | strcat (archetype, wall_suffix [connect]); |
333 | |
320 | |
334 | /* |
321 | /* |
335 | * Before anything, make sure the archetype does exist... |
322 | * Before anything, make sure the archetype does exist... |
336 | * If not, prolly an error... |
323 | * If not, prolly an error... |
337 | */ |
324 | */ |
338 | new_arch = archetype::find (archetype); |
325 | new_arch = archetype::find (archetype); |
339 | |
326 | |
340 | if (!new_arch) |
327 | if (!new_arch) |
341 | return; |
328 | return; |
342 | |
329 | |
343 | /* Now delete current wall, and insert new one |
330 | /* Now delete current wall, and insert new one |
344 | * We save flags to avoid any trouble with buildable/non buildable, and so on |
331 | * We save flags to avoid any trouble with buildable/non buildable, and so on |
345 | */ |
332 | */ |
346 | object::flags_t old_flags = wall->flag; // elmex: this is where C++ pays off |
333 | object::flags_t old_flags = wall->flag; // elmex: this is where C++ pays off |
347 | |
334 | |
348 | wall->destroy (); |
335 | wall->destroy (); |
… | |
… | |
374 | char message[MAX_BUF]; |
361 | char message[MAX_BUF]; |
375 | |
362 | |
376 | sprintf (message, "You change the floor to better suit your tastes."); |
363 | sprintf (message, "You change the floor to better suit your tastes."); |
377 | |
364 | |
378 | /* |
365 | /* |
379 | * Now the building part... |
366 | * Now the building part... |
380 | * First, remove wall(s) and floor(s) at position x, y |
367 | * First, remove wall(s) and floor(s) at position x, y |
381 | */ |
368 | */ |
382 | above_floor = NULL; |
369 | above_floor = NULL; |
383 | new_wall = NULL; |
370 | new_wall = NULL; |
384 | floor_removed = 0; |
371 | floor_removed = 0; |
… | |
… | |
434 | * Next step: make sure there are either walls or floors around the new square |
421 | * Next step: make sure there are either walls or floors around the new square |
435 | * Since building, you can have: blocking view / floor / wall / nothing |
422 | * Since building, you can have: blocking view / floor / wall / nothing |
436 | */ |
423 | */ |
437 | for (i = 1; i <= 8; i++) |
424 | for (i = 1; i <= 8; i++) |
438 | { |
425 | { |
439 | xt = x + freearr_x[i]; |
426 | xt = x + DIRX (i); |
440 | yt = y + freearr_y[i]; |
427 | yt = y + DIRY (i); |
441 | tmp = GET_MAP_OB (pl->map, xt, yt); |
428 | tmp = GET_MAP_OB (pl->map, xt, yt); |
442 | if (!tmp) |
429 | if (!tmp) |
443 | { |
430 | { |
444 | /* Must insert floor & wall */ |
431 | /* Must insert floor & wall */ |
445 | tmp = new_floor->instance (); |
432 | tmp = new_floor->instance (); |
… | |
… | |
661 | apply_builder_remove (object *pl, int dir) |
648 | apply_builder_remove (object *pl, int dir) |
662 | { |
649 | { |
663 | object *item; |
650 | object *item; |
664 | int x, y; |
651 | int x, y; |
665 | |
652 | |
666 | x = pl->x + freearr_x[dir]; |
653 | x = pl->x + DIRX (dir); |
667 | y = pl->y + freearr_y[dir]; |
654 | y = pl->y + DIRY (dir); |
668 | |
655 | |
669 | /* Check square */ |
656 | /* Check square */ |
670 | item = GET_MAP_OB (pl->map, x, y); |
657 | item = GET_MAP_OB (pl->map, x, y); |
671 | if (!item) |
658 | if (!item) |
672 | { |
659 | { |
… | |
… | |
690 | new_draw_info_format (NDI_UNIQUE, 0, pl, "You remove the %s", query_name (item)); |
677 | new_draw_info_format (NDI_UNIQUE, 0, pl, "You remove the %s", query_name (item)); |
691 | item->destroy (); |
678 | item->destroy (); |
692 | } |
679 | } |
693 | } |
680 | } |
694 | |
681 | |
|
|
682 | |
|
|
683 | static void |
|
|
684 | replace_open_space_floor (object *os, object *material) |
|
|
685 | { |
|
|
686 | object *new_floor = archetype::get (material->slaying); |
|
|
687 | insert_ob_in_map_at (new_floor, os->map, os, |
|
|
688 | INS_BELOW_ORIGINATOR, os->x, os->y); |
|
|
689 | os->destroy (); |
|
|
690 | material->decrease (); |
|
|
691 | } |
|
|
692 | |
|
|
693 | /** |
|
|
694 | * Quad building. |
|
|
695 | */ |
|
|
696 | static void |
|
|
697 | apply_builder_quad (object *pl, object *material, mapxy &pos) |
|
|
698 | { |
|
|
699 | object *open_space = 0; |
|
|
700 | object *floor = 0; |
|
|
701 | |
|
|
702 | object *tmp = pos.ms ().bot; |
|
|
703 | bool floor_exists = false; |
|
|
704 | while (tmp) |
|
|
705 | { |
|
|
706 | if (floor_exists && tmp->flag [FLAG_IS_QUAD]) |
|
|
707 | { |
|
|
708 | pl->failmsg ("You can't build there, there is a block in the way."); |
|
|
709 | return; |
|
|
710 | } |
|
|
711 | |
|
|
712 | if (IS_FLOOR (tmp)) |
|
|
713 | { |
|
|
714 | floor = tmp; |
|
|
715 | floor_exists = true; |
|
|
716 | |
|
|
717 | if (floor->arch->archname == shstr_quad_open_space) |
|
|
718 | open_space = floor; |
|
|
719 | else if (!floor->flag [FLAG_IS_QUAD]) |
|
|
720 | { |
|
|
721 | pl->failmsg ("You can't build there, the floor is not suited."); |
|
|
722 | return; |
|
|
723 | } |
|
|
724 | } |
|
|
725 | |
|
|
726 | tmp = tmp->above; |
|
|
727 | } |
|
|
728 | |
|
|
729 | if (open_space) |
|
|
730 | { |
|
|
731 | if (!material->slaying) |
|
|
732 | { |
|
|
733 | pl->failmsg ( |
|
|
734 | "The floor is open and you can't fill it with that material." |
|
|
735 | "H<Use another material to fill the ceiling.>" |
|
|
736 | ); |
|
|
737 | return; |
|
|
738 | } |
|
|
739 | |
|
|
740 | replace_open_space_floor (open_space, material); |
|
|
741 | pl->contr->fire_on = 0; // TODO: stopgap, do not add more than one per keypress |
|
|
742 | return; |
|
|
743 | } |
|
|
744 | else if (floor) |
|
|
745 | { |
|
|
746 | |
|
|
747 | maptile *upper_floor = pos.m->tile_available (TILE_UP); |
|
|
748 | if (!upper_floor) |
|
|
749 | { |
|
|
750 | pl->failmsg ("Whoops, you can't see the ceiling.. H<You may try again.>"); |
|
|
751 | return; |
|
|
752 | } |
|
|
753 | |
|
|
754 | mapxy above_pos (upper_floor, pos.x, pos.y); |
|
|
755 | if (!above_pos.normalise ()) |
|
|
756 | { |
|
|
757 | pl->failmsg ("Whoops, you can't access the ceiling. H<You may try again.>"); |
|
|
758 | return; |
|
|
759 | } |
|
|
760 | |
|
|
761 | mapspace &above_ms = above_pos.ms (); |
|
|
762 | for (object *quad_obj = above_ms.top; quad_obj; quad_obj = quad_obj->below) |
|
|
763 | { |
|
|
764 | if (quad_obj->arch->archname == shstr_quad_open_space) |
|
|
765 | { |
|
|
766 | if (!material->slaying) |
|
|
767 | { |
|
|
768 | pl->failmsg ("The ceiling is open and you can't fill it with that material." |
|
|
769 | "H<Use another material to fill the ceiling.>"); |
|
|
770 | return; |
|
|
771 | } |
|
|
772 | |
|
|
773 | replace_open_space_floor (quad_obj, material); |
|
|
774 | break; |
|
|
775 | } |
|
|
776 | } |
|
|
777 | |
|
|
778 | if (material->destroyed ()) |
|
|
779 | { |
|
|
780 | pl->failmsg ("You don't have enough build material to build a wall here."); |
|
|
781 | return; |
|
|
782 | } |
|
|
783 | |
|
|
784 | object *quad_wall = material->other_arch->instance (); |
|
|
785 | material->decrease (); |
|
|
786 | |
|
|
787 | insert_ob_in_map_at (quad_wall, floor->map, 0, |
|
|
788 | INS_ABOVE_FLOOR_ONLY, floor->x, floor->y); |
|
|
789 | } |
|
|
790 | else |
|
|
791 | pl->failmsg ("You can't build a quad block here."); |
|
|
792 | } |
|
|
793 | |
695 | /** |
794 | /** |
696 | * Global building function |
795 | * Global building function |
697 | * |
796 | * |
698 | * This is the general map building function. Called when the player 'fires' a builder |
797 | * This is the general map building function. Called when the player 'fires' a builder |
699 | * or remover object. |
798 | * or remover object. |
700 | */ |
799 | */ |
701 | void |
800 | void |
702 | apply_map_builder (object *pl, int dir) |
801 | apply_map_builder (object *pl, int dir) |
703 | { |
802 | { |
704 | int x, y; |
|
|
705 | |
|
|
706 | if (!pl->type == PLAYER) |
803 | if (!pl->type == PLAYER) |
707 | return; |
804 | return; |
708 | |
805 | |
709 | /*if ( !player->map->unique ) |
|
|
710 | { |
|
|
711 | new_draw_info( NDI_UNIQUE, 0, player, "You can't build outside a unique map." ); |
|
|
712 | return; |
|
|
713 | } */ |
|
|
714 | |
|
|
715 | if (dir == 0) |
806 | if (dir == 0) |
716 | { |
807 | { |
717 | new_draw_info (NDI_UNIQUE, 0, pl, "You can't build or destroy under yourself."); |
808 | new_draw_info (NDI_UNIQUE, 0, pl, "You can't build or destroy under yourself."); |
718 | return; |
809 | return; |
719 | } |
810 | } |
720 | |
811 | |
721 | x = pl->x + freearr_x[dir]; |
812 | mapxy pos (pl); pos.move (dir); |
722 | y = pl->y + freearr_y[dir]; |
|
|
723 | |
813 | |
724 | if ((1 > x) || (1 > y) || ((pl->map->width - 2) < x) || ((pl->map->height - 2) < y)) |
814 | if (!pos.normalise ()) |
725 | { |
815 | { |
726 | new_draw_info (NDI_UNIQUE, 0, pl, "Can't build on map edge..."); |
816 | pl->failmsg ("You can't build here. H<There is nothing in this direction.>"); |
727 | return; |
817 | return; |
728 | } |
818 | } |
729 | |
819 | |
730 | /* |
820 | /* |
731 | * Check specified square |
821 | * Check specified square |
732 | * The square must have only buildable items |
822 | * The square must have only buildable items |
733 | * Exception: marking runes are all right, |
823 | * Exception: marking runes are all right, |
734 | * since they are used for special things like connecting doors / buttons |
824 | * since they are used for special things like connecting doors / buttons |
735 | */ |
825 | */ |
736 | |
826 | |
737 | object *tmp = GET_MAP_OB (pl->map, x, y); |
|
|
738 | if (!tmp) |
|
|
739 | { |
|
|
740 | /* Nothing, meaning player is standing next to an undefined square... */ |
|
|
741 | LOG (llevError, "apply_map_builder: undefined square at (%d, %d, %s)\n", x, y, &pl->map->path); |
|
|
742 | new_draw_info (NDI_UNIQUE, 0, pl, "You'd better not build here, it looks weird."); |
|
|
743 | return; |
|
|
744 | } |
|
|
745 | |
|
|
746 | object *builder = pl->contr->ranged_ob; |
827 | object *builder = pl->contr->ranged_ob; |
747 | |
828 | |
748 | object *tmp2 = pl->mark (); |
829 | object *tmp2 = pl->mark (); |
749 | |
830 | |
750 | while (tmp) |
831 | object *tmp = 0; |
|
|
832 | for (tmp = pos.ms ().bot; tmp; tmp = tmp->above) |
751 | { |
833 | { |
|
|
834 | if (!tmp->flag [FLAG_IS_BUILDABLE] |
752 | if (!tmp->flag [FLAG_IS_BUILDABLE] && (tmp->type != SIGN || tmp->arch->archname != shstr_rune_mark)) |
835 | && (tmp->type != SIGN || tmp->arch->archname != shstr_rune_mark)) |
753 | { |
836 | { |
754 | /* The item building function already has it's own special |
837 | /* The item building function already has it's own special |
755 | * checks for this |
838 | * checks for this. And so does the quad building function. |
756 | */ |
839 | */ |
757 | if (!tmp2 || tmp2->subtype != ST_MAT_ITEM) |
840 | if (!tmp2 || (tmp2->subtype != ST_MAT_ITEM && tmp2->subtype != ST_MAT_QUAD)) |
758 | { |
841 | { |
759 | if (!INVOKE_PLAYER (BUILD, pl->contr, ARG_OBJECT (builder), ARG_MAP (pl->map), ARG_INT (x), ARG_INT (y), ARG_INT (0))) |
842 | if (!INVOKE_PLAYER (BUILD, pl->contr, ARG_OBJECT (builder), |
|
|
843 | ARG_MAP (pl->map), |
|
|
844 | ARG_INT (pos.y), ARG_INT (pos.y), |
|
|
845 | ARG_INT (0))) |
760 | new_draw_info (NDI_UNIQUE, 0, pl, "You can't build here."); |
846 | new_draw_info (NDI_UNIQUE, 0, pl, "You can't build here."); |
761 | |
847 | |
762 | return; |
848 | return; |
763 | } |
849 | } |
764 | } |
850 | } |
765 | |
|
|
766 | tmp = tmp->above; |
|
|
767 | } |
851 | } |
768 | |
852 | |
769 | /* Now we know the square is ok */ |
853 | /* Now we know the square is ok */ |
770 | if (INVOKE_PLAYER (BUILD, pl->contr, ARG_OBJECT (builder), ARG_MAP (pl->map), ARG_INT (x), ARG_INT (y), ARG_INT (1))) |
854 | if (INVOKE_PLAYER (BUILD, pl->contr, ARG_OBJECT (builder), |
|
|
855 | ARG_MAP (pl->map), ARG_INT (pos.x), ARG_INT (pos.y), ARG_INT (1))) |
771 | return; |
856 | return; |
772 | |
857 | |
773 | if (builder->subtype == ST_BD_REMOVE) |
858 | if (builder->subtype == ST_BD_REMOVE) |
774 | /* Remover -> call specific function and bail out */ |
859 | /* Remover -> call specific function and bail out */ |
775 | { |
860 | { |
… | |
… | |
797 | } |
882 | } |
798 | |
883 | |
799 | switch (tmp->subtype) |
884 | switch (tmp->subtype) |
800 | { |
885 | { |
801 | case ST_MAT_FLOOR: |
886 | case ST_MAT_FLOOR: |
802 | apply_builder_floor (pl, tmp, x, y); |
887 | apply_builder_floor (pl, tmp, pos.x, pos.y); |
803 | return; |
888 | return; |
804 | |
889 | |
805 | case ST_MAT_WALL: |
890 | case ST_MAT_WALL: |
806 | apply_builder_wall (pl, tmp, x, y); |
891 | apply_builder_wall (pl, tmp, pos.x, pos.y); |
807 | return; |
892 | return; |
808 | |
893 | |
809 | case ST_MAT_ITEM: |
894 | case ST_MAT_ITEM: |
810 | apply_builder_item (pl, tmp, x, y); |
895 | apply_builder_item (pl, tmp, pos.x, pos.y); |
|
|
896 | return; |
|
|
897 | |
|
|
898 | case ST_MAT_QUAD: |
|
|
899 | apply_builder_quad (pl, tmp, pos); |
811 | return; |
900 | return; |
812 | |
901 | |
813 | default: |
902 | default: |
814 | new_draw_info (NDI_UNIQUE, 0, pl, "Don't know how to apply this material, sorry."); |
903 | new_draw_info (NDI_UNIQUE, 0, pl, "Don't know how to apply this material, sorry."); |
815 | LOG (llevError, "apply_map_builder: invalid material subtype %d\n", tmp->subtype); |
904 | LOG (llevError, "apply_map_builder: invalid material subtype %d\n", tmp->subtype); |