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 (©) 2002 Mark Wedel & Crossfire Development Team |
6 | * Copyright (©) 2002 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 | /* Created July 95 to separate skill utilities from actual skills -b.t. */ |
26 | /* Created July 95 to separate skill utilities from actual skills -b.t. */ |
26 | |
27 | |
… | |
… | |
51 | # define def(uc, flags) flags, |
52 | # define def(uc, flags) flags, |
52 | # include "skillinc.h" |
53 | # include "skillinc.h" |
53 | # undef def |
54 | # undef def |
54 | }; |
55 | }; |
55 | |
56 | |
|
|
57 | vector<object_ptr> skillvec; |
|
|
58 | |
56 | static int attack_hth (object *pl, int dir, const char *string, object *skill); |
59 | static int attack_hth (object *pl, int dir, const char *string, object *skill); |
57 | static int attack_melee_weapon (object *op, int dir, const char *string, object *skill); |
60 | static int attack_melee_weapon (object *op, int dir, const char *string, object *skill); |
58 | |
61 | |
59 | /* init_skills basically just sets up the skill_names table |
62 | /* init_skills basically just sets up the skill_names table |
60 | * above. The index into the array is set up by the |
63 | * above. The index into the array is set up by the |
61 | * subtypes. |
64 | * subtypes. |
62 | */ |
65 | */ |
63 | void |
66 | void |
64 | init_skills () |
67 | init_skills () |
65 | { |
68 | { |
66 | for_all_archetypes (at) |
69 | // nop |
67 | if (at->type == SKILL) |
70 | } |
|
|
71 | |
|
|
72 | void |
|
|
73 | add_skill_archetype (object *o) |
|
|
74 | { |
|
|
75 | assert (("skill name must equal skill skill", o->name == o->skill)); |
|
|
76 | |
|
|
77 | for (vector<object_ptr>::iterator i = skillvec.begin (); i != skillvec.end (); ++i) |
|
|
78 | if ((*i)->name == o->name) |
68 | { |
79 | { |
69 | if (skill_names[at->subtype]) |
80 | // replace existing entry |
70 | LOG (llevError, "init_skills: multiple skill using same subtype %d, %s, %s\n", |
81 | SKILL_INDEX (o) = i - skillvec.begin (); |
71 | at->subtype, &skill_names[at->subtype], &at->skill); |
82 | *i = o; |
72 | else |
83 | return; |
73 | skill_names[at->subtype] = at->skill; |
|
|
74 | } |
84 | } |
75 | |
85 | |
76 | /* This isn't really an error if there is no skill subtype set, but |
86 | // add new entry |
77 | * checking for this may catch some user errors. |
87 | assert (("only CS_NUM_SKILLS skills supported by client protocol", skillvec.size () < CS_NUM_SKILLS)); |
78 | */ |
88 | SKILL_INDEX (o) = skillvec.size (); |
79 | for (int i = 1; i < NUM_SKILLS; i++) |
89 | skillvec.push_back (o); |
80 | if (!skill_names[i]) |
|
|
81 | LOG (llevError, "init_skills: skill subtype %d doesn't have a name?\n", i); |
|
|
82 | } |
90 | } |
83 | |
91 | |
84 | /* This function goes through the player inventory and sets |
92 | /* This function goes through the player inventory and sets |
85 | * up the last_skills[] array in the player object. |
93 | * up the last_skills[] array in the player object. |
86 | * the last_skills[] is used to more quickly lookup skills - |
94 | * the last_skills[] is used to more quickly lookup skills - |
87 | * mostly used for sending exp. |
95 | * mostly used for sending exp. |
88 | */ |
96 | */ |
89 | void |
97 | void |
90 | player::link_skills () |
98 | player::link_skills () |
91 | { |
99 | { |
92 | for (int i = 0; i < NUM_SKILLS; ++i) |
100 | for (int i = 0; i < CS_NUM_SKILLS; ++i) |
93 | last_skill_ob [i] = 0; |
101 | last_skill_ob [i] = 0; |
94 | |
102 | |
95 | for (object *tmp = ob->inv; tmp; tmp = tmp->below) |
103 | for (object *tmp = ob->inv; tmp; tmp = tmp->below) |
96 | if (tmp->type == SKILL) |
104 | if (tmp->type == SKILL) |
97 | { |
105 | { |
|
|
106 | int idx = SKILL_INDEX (tmp); |
|
|
107 | |
98 | assert (IN_RANGE_EXC (tmp->subtype, 0, NUM_SKILLS)); |
108 | assert (IN_RANGE_EXC (idx, 0, CS_NUM_SKILLS)); |
99 | |
109 | |
100 | if (last_skill_ob [tmp->subtype] != tmp) |
110 | if (last_skill_ob [idx] != tmp) |
101 | { |
111 | { |
102 | /* This is really a warning, hence no else below */ |
|
|
103 | if (last_skill_ob [tmp->subtype]) |
|
|
104 | LOG (llevError, "Multiple skills with the same subtype? %s, %s\n", |
|
|
105 | &last_skill_ob [tmp->subtype]->skill, &tmp->skill); |
|
|
106 | |
|
|
107 | last_skill_ob [tmp->subtype] = tmp; |
112 | last_skill_ob [idx] = tmp; |
108 | if (ns) |
113 | if (ns) |
109 | ns->last_skill_exp [tmp->subtype] = -1; |
114 | ns->last_skill_exp [idx] = -1; |
110 | } |
115 | } |
111 | } |
116 | } |
112 | } |
117 | } |
113 | |
118 | |
114 | static object * |
119 | static object * |
… | |
… | |
124 | return splay (tmp); |
129 | return splay (tmp); |
125 | |
130 | |
126 | return 0; |
131 | return 0; |
127 | } |
132 | } |
128 | |
133 | |
|
|
134 | object * |
129 | object *player::find_skill (shstr_cmp name) const |
135 | player::find_skill (shstr_cmp name) const |
130 | { |
136 | { |
131 | // might want to use last_skill_obj at one point, or maybe not |
137 | // might want to use last_skill_obj at one point, or maybe not |
132 | return ::find_skill (ob, name); |
138 | return ::find_skill (ob, name); |
133 | } |
139 | } |
134 | |
140 | |
… | |
… | |
162 | |
168 | |
163 | /* Player has a tool to use the skill. If not applied, apply it - |
169 | /* Player has a tool to use the skill. If not applied, apply it - |
164 | * if not successful, return null. If they do have the skill tool |
170 | * if not successful, return null. If they do have the skill tool |
165 | * but not the skill itself, give it to them. |
171 | * but not the skill itself, give it to them. |
166 | */ |
172 | */ |
167 | object *skill = find_skill (who, skill_tool->skill); |
173 | object *skill = who->give_skill (skill_tool->skill); |
168 | |
|
|
169 | if (!skill) |
|
|
170 | skill = give_skill_by_name (who, skill_tool->skill); |
|
|
171 | |
174 | |
172 | if (!skill_tool->flag [FLAG_APPLIED]) |
175 | if (!skill_tool->flag [FLAG_APPLIED]) |
173 | if (!who->apply (splay (skill_tool))) |
176 | if (!who->apply (splay (skill_tool))) |
174 | return 0; |
177 | return 0; |
175 | |
178 | |
… | |
… | |
193 | * one that accumulates exp, has the level, etc). |
196 | * one that accumulates exp, has the level, etc). |
194 | * |
197 | * |
195 | * It is presumed that the player will be needing to actually |
198 | * It is presumed that the player will be needing to actually |
196 | * use the skill, so thus if use of the skill requires a skill |
199 | * use the skill, so thus if use of the skill requires a skill |
197 | * tool, this code will equip it. |
200 | * tool, this code will equip it. |
198 | * |
201 | * |
199 | * This code is basically the same as find_skill_by_name() above, |
202 | * This code is basically the same as find_skill_by_name() above, |
200 | * but instead a skill name, we search by matching number. |
203 | * but instead a skill name, we search by matching number. |
201 | * this replaces find_skill. |
204 | * this replaces find_skill. |
|
|
205 | * |
|
|
206 | * MUST NOT BE USED IN NEW CODE! (schmorp) |
202 | */ |
207 | */ |
203 | object * |
208 | object * |
204 | find_skill_by_number (object *who, int skillno) |
209 | find_skill_by_number (object *who, int skillno) |
205 | { |
210 | { |
206 | for (object *tmp = who->inv; tmp; tmp = tmp->below) |
211 | for (object *tmp = who->inv; tmp; tmp = tmp->below) |
… | |
… | |
209 | return skop; |
214 | return skop; |
210 | |
215 | |
211 | return 0; |
216 | return 0; |
212 | } |
217 | } |
213 | |
218 | |
|
|
219 | object * |
|
|
220 | object::give_skill (shstr_cmp name, bool can_use) |
|
|
221 | { |
|
|
222 | object *skill = find_skill (this, name); |
|
|
223 | |
|
|
224 | if (!skill) |
|
|
225 | skill = give_skill_by_name (this, name); |
|
|
226 | |
|
|
227 | if (skill && can_use) |
|
|
228 | skill->flag [FLAG_CAN_USE_SKILL] = true; |
|
|
229 | |
|
|
230 | return skill; |
|
|
231 | } |
|
|
232 | |
214 | /* do_skill() - Main skills use function-similar in scope to cast_spell(). |
233 | /* do_skill() - Main skills use function-similar in scope to cast_spell(). |
215 | * We handle all requests for skill use outside of some combat here. |
234 | * We handle all requests for skill use outside of some combat here. |
216 | * We require a separate routine outside of fire() so as to allow monsters |
235 | * We require a separate routine outside of fire() so as to allow monsters |
217 | * to utilize skills. Returns 1 on use of skill, otherwise 0. |
236 | * to utilize skills. Returns 1 on use of skill, otherwise 0. |
218 | * This is changed (2002-11-30) from the old method that returned |
237 | * This is changed (2002-11-30) from the old method that returned |
219 | * exp - no caller needed that info, but it also prevented the callers |
238 | * exp - no caller needed that info, but it also prevented the callers |
220 | * from know if a skill was actually used, as many skills don't |
239 | * from know if a skill was actually used, as many skills don't |
221 | * give any exp for their direct use (eg, throwing). |
240 | * give any exp for their direct use (eg, throwing). |
… | |
… | |
355 | case SK_ALCHEMY: |
374 | case SK_ALCHEMY: |
356 | case SK_THAUMATURGY: |
375 | case SK_THAUMATURGY: |
357 | case SK_LITERACY: |
376 | case SK_LITERACY: |
358 | case SK_WOODSMAN: |
377 | case SK_WOODSMAN: |
359 | /* first, we try to find a cauldron, and do the alchemy thing. |
378 | /* first, we try to find a cauldron, and do the alchemy thing. |
360 | * failing that, we go and identify stuff. |
379 | * failing that, we go and identify stuff. |
361 | */ |
380 | */ |
362 | { |
381 | { |
363 | bool found_cauldron = false; |
382 | bool found_cauldron = false; |
364 | |
383 | |
365 | for (object *next, *tmp = GET_MAP_OB (op->map, op->x, op->y); tmp; tmp = next) |
384 | for (object *next, *tmp = GET_MAP_OB (op->map, op->x, op->y); tmp; tmp = next) |
… | |
… | |
430 | |
449 | |
431 | case SK_MINING: |
450 | case SK_MINING: |
432 | success = skill_mining (op, part, skill, dir, string); |
451 | success = skill_mining (op, part, skill, dir, string); |
433 | break; |
452 | break; |
434 | |
453 | |
435 | case SK_FISHING: |
|
|
436 | success = skill_fishing (op, part, skill, dir, string); |
|
|
437 | break; |
|
|
438 | |
|
|
439 | default: |
454 | default: |
440 | LOG (llevDebug, "%s attempted to use unknown skill: %d\n", query_name (op), op->chosen_skill->stats.sp); |
455 | LOG (llevDebug, "%s attempted to use unknown skill: %d\n", query_name (op), op->chosen_skill->stats.sp); |
441 | break; |
456 | break; |
442 | } |
457 | } |
443 | |
458 | |
444 | /* For players we now update the speed_left from using the skill. |
459 | /* For players we now update the speed_left from using the skill. |
445 | * Monsters have no skill use time because of the random nature in |
460 | * Monsters have no skill use time because of the random nature in |
446 | * which use_monster_skill is called already simulates this. |
461 | * which use_monster_skill is called already simulates this. |
447 | * If certain skills should take more/less time, that should be |
462 | * If certain skills should take more/less time, that should be |
448 | * in the code for the skill itself. |
463 | * in the code for the skill itself. |
449 | */ |
464 | */ |
450 | if (op->type == PLAYER) |
465 | if (op->type == PLAYER) |
… | |
… | |
500 | * non-living magic obj, runes and everything else. |
515 | * non-living magic obj, runes and everything else. |
501 | * |
516 | * |
502 | * If an object is not alive and magical we set the base exp higher to |
517 | * If an object is not alive and magical we set the base exp higher to |
503 | * help out exp awards for skill_ident skills. Also, if |
518 | * help out exp awards for skill_ident skills. Also, if |
504 | * an item is type RUNE, we give out exp based on stats.Cha |
519 | * an item is type RUNE, we give out exp based on stats.Cha |
505 | * and level (this was the old system) -b.t. |
520 | * and level (this was the old system) -b.t. |
506 | */ |
521 | */ |
507 | if (!op) |
522 | if (!op) |
508 | { /* no item/creature */ |
523 | { /* no item/creature */ |
509 | op_lvl = max (1, who->map->difficulty); |
524 | op_lvl = max (1, who->map->difficulty); |
510 | op_exp = 0; |
525 | op_exp = 0; |
… | |
… | |
574 | } |
589 | } |
575 | |
590 | |
576 | /* Learn skill. This inserts the requested skill in the player's |
591 | /* Learn skill. This inserts the requested skill in the player's |
577 | * inventory. The skill field of the scroll should have the |
592 | * inventory. The skill field of the scroll should have the |
578 | * exact name of the requested skill. |
593 | * exact name of the requested skill. |
579 | * This one actually teaches the player the skill as something |
594 | * This one actually teaches the player the skill as something |
580 | * they can equip. |
595 | * they can equip. |
581 | * Return 0 if the player knows the skill, 1 if the |
596 | * Return 0 if the player knows the skill, 1 if the |
582 | * player learns the skill, 2 otherwise. |
597 | * player learns the skill, 2 otherwise. |
583 | */ |
598 | */ |
584 | int |
599 | int |
… | |
… | |
635 | return 100; |
650 | return 100; |
636 | |
651 | |
637 | return rv; |
652 | return rv; |
638 | } |
653 | } |
639 | |
654 | |
|
|
655 | static int |
|
|
656 | cmp_skillp (const void *sk1, const void *sk2) |
|
|
657 | { |
|
|
658 | return strcmp (&((*(const object **) sk1)->name), |
|
|
659 | &((*(const object **) sk2)->name)); |
|
|
660 | } |
|
|
661 | |
640 | /* show_skills() - Meant to allow players to examine |
662 | /* show_skills() - Meant to allow players to examine |
641 | * their current skill list. |
663 | * their current skill list. |
642 | * This shows the amount of exp they have in the skills. |
664 | * This shows the amount of exp they have in the skills. |
643 | * we also include some other non skill related info (god, |
665 | * we also include some other non skill related info (god, |
644 | * max weapon improvments, item power). |
666 | * max weapon improvments, item power). |
645 | * Note this function is a bit more complicated becauase we |
667 | * Note this function is a bit more complicated because we |
646 | * we want ot sort the skills before printing them. If we |
668 | * we want to sort the skills before printing them. If we |
647 | * just dumped this as we found it, this would be a bit |
669 | * just dumped this as we found it, this would be a bit |
648 | * simpler. |
670 | * simpler. |
649 | */ |
671 | */ |
650 | //TODO: egad, do it in perl, do not suffer from the big buffer on stack, make it one single drawinfo. |
|
|
651 | void |
672 | void |
652 | show_skills (object *pl, const char *search) |
673 | show_skills (object *pl, const char *search) |
653 | { |
674 | { |
654 | const char *cp; |
675 | const char *cp; |
655 | int i, num_skills_found = 0; |
676 | int i, num_skills_found = 0; |
656 | const char *const periods = ".............................."; // 30 |
677 | object *skills[CS_NUM_SKILLS]; |
657 | |
|
|
658 | /* Need to have a pointer and use strdup for qsort to work properly */ |
|
|
659 | char skills[NUM_SKILLS][128]; // d'oh |
|
|
660 | |
|
|
661 | object *op = pl->contr->ob; |
678 | object *op = pl->contr->ob; |
662 | |
679 | |
|
|
680 | /* find the skills */ |
663 | for (object *tmp = op->inv; tmp; tmp = tmp->below) |
681 | for (object *tmp = op->inv; tmp; tmp = tmp->below) |
664 | { |
682 | { |
665 | if (tmp->type == SKILL) |
683 | if (tmp->type == SKILL) |
666 | { |
684 | { |
667 | if (search && !tmp->name.contains (search)) |
685 | if (search && !tmp->name.contains (search)) |
668 | continue; |
686 | continue; |
669 | |
687 | |
670 | char buf[30]; |
688 | skills[num_skills_found++] = tmp; |
671 | |
|
|
672 | /* Basically want to fill this out to 30 spaces with periods */ |
|
|
673 | snprintf (buf, sizeof (buf), "%s%s", &tmp->name, periods); |
|
|
674 | |
|
|
675 | if (settings.permanent_exp_ratio) |
|
|
676 | snprintf (skills[num_skills_found++], sizeof (skills [0]), "%slvl:%3d (xp:%" PRId64 "/%" PRId64 "/%d%%)", |
|
|
677 | buf, tmp->level, tmp->stats.exp, |
|
|
678 | level_exp (tmp->level + 1, op->expmul), clipped_percent (tmp->perm_exp, tmp->stats.exp)); |
|
|
679 | else |
|
|
680 | snprintf (skills[num_skills_found++], sizeof (skills [0]), "%slvl:%3d (xp:%" PRId64 "/%" PRId64 ")", |
|
|
681 | buf, tmp->level, tmp->stats.exp, level_exp (tmp->level + 1, op->expmul)); |
|
|
682 | |
689 | |
683 | /* I don't know why some characters get a bunch of skills, but |
690 | /* I don't know why some characters get a bunch of skills, but |
684 | * it sometimes happens (maybe a leftover from bugier earlier code |
691 | * it sometimes happens (maybe a leftover from bugier earlier code |
685 | * and those character are still about). In any case, lets handle |
692 | * and those character are still about). In any case, lets handle |
686 | * it so it doesn't crash the server - otherwise, one character may |
693 | * it so it doesn't crash the server - otherwise, one character may |
687 | * crash the server numerous times. |
694 | * crash the server numerous times. |
688 | */ |
695 | */ |
689 | if (num_skills_found >= NUM_SKILLS) |
696 | if (num_skills_found >= CS_NUM_SKILLS) |
690 | { |
697 | { |
691 | new_draw_info (NDI_RED | NDI_REPLY, 0, op, "Your character has too many skills."); |
698 | new_draw_info (NDI_RED | NDI_REPLY, 0, op, "Your character has too many skills."); |
692 | new_draw_info (NDI_RED | NDI_REPLY, 0, op, "Something isn't right - contact the server admin"); |
699 | new_draw_info (NDI_RED | NDI_REPLY, 0, op, "Something isn't right - contact the server admin"); |
693 | break; |
700 | break; |
694 | } |
701 | } |
… | |
… | |
697 | |
704 | |
698 | dynbuf_text &msg = msg_dynbuf; msg.clear (); |
705 | dynbuf_text &msg = msg_dynbuf; msg.clear (); |
699 | |
706 | |
700 | msg << "T<Player skills:>\n\n"; |
707 | msg << "T<Player skills:>\n\n"; |
701 | if (num_skills_found > 1) |
708 | if (num_skills_found > 1) |
702 | qsort (skills, num_skills_found, sizeof (skills [0]), (int (*)(const void *, const void *)) std::strcmp); |
709 | qsort (skills, num_skills_found, sizeof (skills [0]), cmp_skillp); |
703 | |
710 | |
|
|
711 | char buf[31]; /* +1 for '\0' */ |
|
|
712 | const char *const periods = ".............................."; // 30 |
|
|
713 | |
704 | for (i = 0; i < num_skills_found; i++) |
714 | for (i = 0; i < num_skills_found; i++) { |
705 | msg << " C<" << skills [i] << ">\n"; |
715 | object *tmp = skills[i]; |
|
|
716 | |
|
|
717 | /* Basically want to fill this out to 30 spaces with periods */ |
|
|
718 | snprintf (buf, sizeof (buf), "%s%s", &tmp->name, periods); |
|
|
719 | |
|
|
720 | msg << " C<"; |
|
|
721 | |
|
|
722 | if (settings.permanent_exp_ratio) |
|
|
723 | msg.printf ("%slvl:%3d (xp:%" PRId64 "/%" PRId64 "/%d%%)", |
|
|
724 | buf, tmp->level, tmp->stats.exp, level_exp (tmp->level + 1, op->expmul), |
|
|
725 | clipped_percent (tmp->perm_exp, tmp->stats.exp)); |
|
|
726 | else |
|
|
727 | msg.printf ("%slvl:%3d (xp:%" PRId64 "/%" PRId64 ")", |
|
|
728 | buf, tmp->level, tmp->stats.exp, level_exp (tmp->level + 1, op->expmul)); |
|
|
729 | |
|
|
730 | msg << ">\n"; |
|
|
731 | } |
706 | |
732 | |
707 | msg << "\nYou can handle " << op->level / 5 + 5 << " weapon improvements.\r"; |
733 | msg << "\nYou can handle " << op->level / 5 + 5 << " weapon improvements.\r"; |
708 | |
734 | |
709 | cp = determine_god (op); |
735 | cp = determine_god (op); |
710 | msg << "You worship " << (cp ? cp : "no god at current time") << ".\r"; |
736 | msg << "You worship " << (cp ? cp : "no god at current time") << ".\r"; |
… | |
… | |
714 | << ".\n"; |
740 | << ".\n"; |
715 | |
741 | |
716 | pl->contr->infobox (MSG_CHANNEL ("skills"), msg); |
742 | pl->contr->infobox (MSG_CHANNEL ("skills"), msg); |
717 | } |
743 | } |
718 | |
744 | |
719 | /* use_skill() - similar to invoke command, it executes the skill in the |
745 | /* use_skill() - similar to invoke command, it executes the skill in the |
720 | * direction that the user is facing. Returns false if we are unable to |
746 | * direction that the user is facing. Returns false if we are unable to |
721 | * change to the requested skill, or were unable to use the skill properly. |
747 | * change to the requested skill, or were unable to use the skill properly. |
722 | * This is tricky because skills can have spaces. We basically roll |
748 | * This is tricky because skills can have spaces. We basically roll |
723 | * our own find_skill_by_name so we can try to do better string matching. |
749 | * our own find_skill_by_name so we can try to do better string matching. |
724 | */ |
750 | */ |
725 | int |
751 | int |
726 | use_skill (object *op, const char *string) |
752 | use_skill (object *op, const char *string) |
727 | { |
753 | { |
… | |
… | |
872 | } |
898 | } |
873 | } |
899 | } |
874 | |
900 | |
875 | if (!op->apply (pl->combat_ob)) |
901 | if (!op->apply (pl->combat_ob)) |
876 | return 0; |
902 | return 0; |
|
|
903 | |
|
|
904 | if (!op->chosen_skill) |
|
|
905 | { |
|
|
906 | LOG (llevError, "do_skill_attack: weapon has no skill (%s)", pl->combat_ob->debug_desc ()); |
|
|
907 | new_draw_info (NDI_RED | NDI_REPLY, 0, op, "You hit a bug in the server, please contact an admin!"); |
|
|
908 | return 0; |
|
|
909 | } |
877 | } |
910 | } |
878 | |
911 | |
879 | /* lose invisiblity/hiding status for running attacks */ |
912 | /* lose invisiblity/hiding status for running attacks */ |
880 | if (pl->tmp_invis) |
913 | if (pl->tmp_invis) |
881 | { |
914 | { |
… | |
… | |
920 | int mflags; |
953 | int mflags; |
921 | |
954 | |
922 | if (!dir) |
955 | if (!dir) |
923 | dir = pl->facing; |
956 | dir = pl->facing; |
924 | |
957 | |
925 | tx = freearr_x[dir]; |
958 | tx = DIRX (dir); |
926 | ty = freearr_y[dir]; |
959 | ty = DIRY (dir); |
927 | |
960 | |
928 | /* If we don't yet have an opponent, find if one exists, and attack. |
961 | /* If we don't yet have an opponent, find if one exists, and attack. |
929 | * Legal opponents are the same as outlined in move_player_attack() |
962 | * Legal opponents are the same as outlined in move_player_attack() |
930 | */ |
963 | */ |
931 | if (!tmp) |
964 | if (!tmp) |
932 | { |
965 | { |
933 | m = pl->map; |
966 | m = pl->map; |
934 | tx = pl->x + freearr_x[dir]; |
967 | tx = pl->x + DIRX (dir); |
935 | ty = pl->y + freearr_y[dir]; |
968 | ty = pl->y + DIRY (dir); |
936 | |
969 | |
937 | mflags = get_map_flags (m, &m, tx, ty, &tx, &ty); |
970 | mflags = get_map_flags (m, &m, tx, ty, &tx, &ty); |
938 | if (mflags & P_OUT_OF_MAP) |
971 | if (mflags & P_OUT_OF_MAP) |
939 | return 0; |
972 | return 0; |
940 | |
973 | |