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

File Contents

# Content
1 /*
2 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 *
4 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Crossfire TRT is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
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, see <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 */
23
24 /* Created July 95 to separate skill utilities from actual skills -b.t. */
25
26 /* Reconfigured skills code to allow linking of skills to experience
27 * categories. This is done solely through the init_new_exp_system() fctn.
28 * June/July 1995 -b.t. thomas@astro.psu.edu
29 */
30
31 /* July 1995 - Initial associated skills coding. Experience gains
32 * come solely from the use of skills. Overcoming an opponent (in combat,
33 * finding/disarming a trap, stealing from somebeing, etc) gains
34 * experience. Calc_skill_exp() handles the gained experience using
35 * modifications in the skills[] table. - b.t.
36 */
37
38 /* define the following for skills utility debuging */
39
40 /* #define SKILL_UTIL_DEBUG */
41
42 #include <global.h>
43 #include <object.h>
44 #include <sproto.h>
45 #include <living.h> /* for defs of STR,CON,DEX,etc. -b.t. */
46 #include <spells.h>
47
48 const uint8_t skill_flags[NUM_SKILLS] = {
49 0, // SK_NONE
50 # define def(uc, flags) flags,
51 # include "skillinc.h"
52 # undef def
53 };
54
55 static int attack_hth (object *pl, int dir, const char *string, object *skill);
56 static int attack_melee_weapon (object *op, int dir, const char *string, object *skill);
57
58 /* init_skills basically just sets up the skill_names table
59 * above. The index into the array is set up by the
60 * subtypes.
61 */
62 void
63 init_skills (void)
64 {
65 for_all_archetypes (at)
66 if (at->type == SKILL)
67 {
68 if (skill_names[at->subtype])
69 LOG (llevError, "init_skills: multiple skill using same subtype %d, %s, %s\n",
70 at->subtype, &skill_names[at->subtype], &at->skill);
71 else
72 skill_names[at->subtype] = at->skill;
73 }
74
75 /* This isn't really an error if there is no skill subtype set, but
76 * checking for this may catch some user errors.
77 */
78 for (int i = 1; i < NUM_SKILLS; i++)
79 if (!skill_names[i])
80 LOG (llevError, "init_skills: skill subtype %d doesn't have a name?\n", i);
81 }
82
83 /* This function goes through the player inventory and sets
84 * up the last_skills[] array in the player object.
85 * the last_skills[] is used to more quickly lookup skills -
86 * mostly used for sending exp.
87 */
88 void
89 link_player_skills (object *op)
90 {
91 for (object *tmp = op->inv; tmp; tmp = tmp->below)
92 if (tmp->type == SKILL)
93 {
94 /* This is really a warning, hence no else below */
95 if (op->contr->last_skill_ob[tmp->subtype] && op->contr->last_skill_ob[tmp->subtype] != tmp)
96 LOG (llevError, "Multiple skills with the same subtype? %s, %s\n",
97 &op->contr->last_skill_ob[tmp->subtype]->skill, &tmp->skill);
98
99 if (tmp->subtype >= NUM_SKILLS)
100 LOG (llevError, "Invalid subtype number %d (range 0-%d)\n", tmp->subtype, NUM_SKILLS);
101 else
102 {
103 op->contr->last_skill_ob[tmp->subtype] = tmp;
104 op->contr->ns->last_skill_exp[tmp->subtype] = -1; //TODO: this should go
105 }
106 }
107 }
108
109 static object *
110 find_skill (object *who, const shstr &sh)
111 {
112 for (object *tmp = who->inv; tmp; tmp = tmp->below)
113 if (tmp->skill == sh && tmp->type == SKILL)
114 return tmp;
115
116 return 0;
117 }
118
119 /* This returns the skill pointer of the given name (the
120 * one that accumulates exp, has the level, etc).
121 *
122 * It is presumed that the player will be needing to actually
123 * use the skill, so thus if use of the skill requires a skill
124 * tool, this code will equip it.
125 */
126 object *
127 find_skill_by_name (object *who, const shstr &sh)
128 {
129 object *skill_tool = 0;
130
131 for (object *tmp = who->inv; tmp; tmp = tmp->below)
132 if (tmp->skill == sh)
133 {
134 if (tmp->type == SKILL && (tmp->flag [FLAG_CAN_USE_SKILL] || tmp->flag [FLAG_APPLIED]))
135 /* If this is a skill that can be used without applying tool, return it */
136 return splay (tmp);
137 /* Try to find appropriate skilltool. If the player has one already
138 * applied, we try to keep using that one.
139 */
140 else if (tmp->type == SKILL_TOOL && !skill_tool)
141 skill_tool = tmp;
142 }
143
144 if (!skill_tool)
145 return 0;
146
147 /* Player has a tool to use the skill. If not applied, apply it -
148 * if not successful, return null. If they do have the skill tool
149 * but not the skill itself, give it to them.
150 */
151 object *skill = find_skill (who, skill_tool->skill);
152
153 if (!skill)
154 {
155 skill = give_skill_by_name (who, skill_tool->skill);
156 link_player_skills (who);
157 }
158
159 if (!skill_tool->flag [FLAG_APPLIED])
160 if (apply_special (who, splay (skill_tool), AP_APPLY | AP_NO_READY))
161 return 0;
162
163 return splay (skill);
164 }
165
166 object *
167 find_skill_by_name (object *who, const char *name)
168 {
169 if (!name)
170 return 0;
171
172 for (object *tmp = who->inv; tmp; tmp = tmp->below)
173 if ((tmp->type == SKILL || tmp->type == SKILL_TOOL)
174 && tmp->skill.begins_with (name))
175 if (object *skop = find_skill_by_name (who, tmp->skill))
176 return skop;
177
178 return 0;
179 }
180
181 /* This returns the skill pointer of the given name (the
182 * one that accumulates exp, has the level, etc).
183 *
184 * It is presumed that the player will be needing to actually
185 * use the skill, so thus if use of the skill requires a skill
186 * tool, this code will equip it.
187 *
188 * This code is basically the same as find_skill_by_name() above,
189 * but instead a skill name, we search by matching number.
190 * this replaces find_skill.
191 */
192 object *
193 find_skill_by_number (object *who, int skillno)
194 {
195 for (object *tmp = who->inv; tmp; tmp = tmp->below)
196 if (tmp->type == SKILL && tmp->subtype == skillno)
197 if (object *skop = find_skill_by_name (who, tmp->skill))
198 return skop;
199
200 return 0;
201 }
202
203 /* This changes the objects chosen_skill to new_skill.
204 * return 1 on success, 0 on error
205 */
206 bool
207 object::change_skill (object *new_skill)
208 {
209 if (type != PLAYER)
210 return 0;
211
212 // optimise this supposedly common case
213 if (new_skill == chosen_skill)
214 return 1;
215
216 if (chosen_skill)
217 {
218 chosen_skill->flag [FLAG_APPLIED] = false;
219 change_abil (this, chosen_skill);
220 }
221
222 chosen_skill = new_skill;
223
224 if (chosen_skill)
225 {
226 chosen_skill->flag [FLAG_APPLIED] = true;
227 change_abil (this, chosen_skill);
228 }
229
230 // always clear current weapon, as the selected skill could
231 // conflict with the current weapon skill, which would go
232 // undetected and exp would be given to the wrong skill.
233 current_weapon = 0;
234
235 update_stats ();
236 return 1;
237 }
238
239 /* do_skill() - Main skills use function-similar in scope to cast_spell().
240 * We handle all requests for skill use outside of some combat here.
241 * We require a separate routine outside of fire() so as to allow monsters
242 * to utilize skills. Returns 1 on use of skill, otherwise 0.
243 * This is changed (2002-11-30) from the old method that returned
244 * exp - no caller needed that info, but it also prevented the callers
245 * from know if a skill was actually used, as many skills don't
246 * give any exp for their direct use (eg, throwing).
247 * It returns 0 if no skill was used.
248 */
249 int
250 do_skill (object *op, object *part, object *skill, int dir, const char *string)
251 {
252 int success = 0, exp = 0;
253 int did_alc = 0;
254
255 if (!skill)
256 return 0;
257
258 /* The code below presumes that the skill points to the object that
259 * holds the exp, level, etc of the skill. So if this is a player
260 * go and try to find the actual real skill pointer, and if the
261 * the player doesn't have a bucket for that, create one.
262 */
263 if (skill->type != SKILL && op->type == PLAYER)
264 {
265 for (object *tmp = op->inv; tmp; tmp = tmp->below)
266 if (tmp->type == SKILL && tmp->skill == skill->skill)
267 {
268 skill = tmp;
269 goto found;
270 }
271
272 skill = give_skill_by_name (op, skill->skill);
273 found: ;
274 }
275
276 // skill, by_whom, on_which_object, which direction, skill_argument
277 if (INVOKE_OBJECT (USE_SKILL, skill, ARG_OBJECT (op), ARG_OBJECT (part), ARG_INT (dir), ARG_STRING (string)))
278 return 0;
279
280 switch (skill->subtype)
281 {
282 case SK_LEVITATION:
283 /* Not 100% sure if this will work with new movement code -
284 * the levitation skill has move_type for flying, so when
285 * equipped, that should transfer to player, when not,
286 * shouldn't.
287 */
288 if (QUERY_FLAG (skill, FLAG_APPLIED))
289 {
290 CLEAR_FLAG (skill, FLAG_APPLIED);
291 new_draw_info (NDI_UNIQUE, 0, op, "You come to earth.");
292 }
293 else
294 {
295 SET_FLAG (skill, FLAG_APPLIED);
296 new_draw_info (NDI_UNIQUE, 0, op, "You rise into the air!.");
297 }
298
299 op->update_stats ();
300 success = 1;
301 break;
302
303 case SK_STEALING:
304 exp = success = steal (op, dir, skill);
305 break;
306
307 case SK_LOCKPICKING:
308 exp = success = pick_lock (op, dir, skill);
309 break;
310
311 case SK_HIDING:
312 exp = success = hide (op, skill);
313 break;
314
315 case SK_JUMPING:
316 success = jump (op, dir, skill);
317 break;
318
319 case SK_INSCRIPTION:
320 exp = success = write_on_item (op, string, skill);
321 break;
322
323 case SK_MEDITATION:
324 meditate (op, skill);
325 success = 1;
326 break;
327 /* note that the following 'attack' skills gain exp through hit_player() */
328
329 case SK_KARATE:
330 attack_hth (op, dir, "karate-chopped", skill);
331 break;
332
333 case SK_PUNCHING:
334 attack_hth (op, dir, "punched", skill);
335 break;
336
337 case SK_FLAME_TOUCH:
338 attack_hth (op, dir, "flamed", skill);
339 break;
340
341 case SK_SPARK_TOUCH:
342 attack_hth (op, dir, "zapped", skill);
343 break;
344
345 case SK_SHIVER:
346 attack_hth (op, dir, "froze", skill);
347 break;
348
349 case SK_ACID_SPLASH:
350 attack_hth (op, dir, "dissolved", skill);
351 break;
352
353 case SK_POISON_NAIL:
354 attack_hth (op, dir, "injected poison into", skill);
355 break;
356
357 case SK_CLAWING:
358 attack_hth (op, dir, "clawed", skill);
359 break;
360
361 case SK_ONE_HANDED_WEAPON:
362 case SK_TWO_HANDED_WEAPON:
363 attack_melee_weapon (op, dir, NULL, skill);
364 break;
365
366 case SK_FIND_TRAPS:
367 exp = success = find_traps (op, skill);
368 break;
369
370 case SK_SINGING:
371 exp = success = singing (op, dir, skill);
372 break;
373
374 case SK_ORATORY:
375 exp = success = use_oratory (op, dir, skill);
376 break;
377
378 case SK_SMITHERY:
379 case SK_BOWYER:
380 case SK_JEWELER:
381 case SK_ALCHEMY:
382 case SK_THAUMATURGY:
383 case SK_LITERACY:
384 case SK_WOODSMAN:
385 /* first, we try to find a cauldron, and do the alchemy thing.
386 * failing that, we go and identify stuff.
387 */
388 for (object *next, *tmp = GET_MAP_OB (op->map, op->x, op->y); tmp; tmp = next)
389 {
390 next = tmp->above;
391
392 if (QUERY_FLAG (tmp, FLAG_IS_CAULDRON))
393 {
394 attempt_do_alchemy (op, tmp);
395
396 if (QUERY_FLAG (tmp, FLAG_APPLIED))
397 esrv_send_inventory (op, tmp);
398
399 did_alc = 1;
400 }
401 }
402
403 if (did_alc == 0)
404 exp = success = skill_ident (op, skill);
405
406 break;
407
408 case SK_DET_MAGIC:
409 case SK_DET_CURSE:
410 exp = success = skill_ident (op, skill);
411 break;
412
413 case SK_DISARM_TRAPS:
414 exp = success = remove_trap (op, dir, skill);
415 break;
416
417 case SK_THROWING:
418 success = skill_throw (op, part, dir, string, skill);
419 break;
420
421 case SK_SET_TRAP:
422 new_draw_info (NDI_UNIQUE, 0, op, "This skill is not currently implemented.");
423 break;
424
425 case SK_USE_MAGIC_ITEM:
426 case SK_MISSILE_WEAPON:
427 new_draw_info (NDI_UNIQUE, 0, op, "There is no special attack for this skill.");
428 break;
429
430 case SK_PRAYING:
431 success = pray (op, skill);
432 break;
433
434 case SK_BARGAINING:
435 success = describe_shop (op);
436 break;
437
438 case SK_SORCERY:
439 case SK_EVOCATION:
440 case SK_PYROMANCY:
441 case SK_SUMMONING:
442 case SK_CLIMBING:
443 new_draw_info (NDI_UNIQUE, 0, op, "This skill is already in effect.");
444 break;
445
446 default:
447 LOG (llevDebug, "%s attempted to use unknown skill: %d\n", query_name (op), op->chosen_skill->stats.sp);
448 break;
449 }
450
451 /* For players we now update the speed_left from using the skill.
452 * Monsters have no skill use time because of the random nature in
453 * which use_monster_skill is called already simulates this.
454 * If certain skills should take more/less time, that should be
455 * in the code for the skill itself.
456 */
457
458 if (op->type == PLAYER)
459 op->speed_left -= 1.f;
460
461 /* this is a good place to add experience for successfull use of skills.
462 * Note that add_exp() will figure out player/monster experience
463 * gain problems.
464 */
465
466 if (success && exp)
467 change_exp (op, exp, skill->skill, 0);
468
469 return success;
470 }
471
472 /* calc_skill_exp() - calculates amount of experience can be gained for
473 * successfull use of a skill. Returns value of experience gain.
474 * Here we take the view that a player must 'overcome an opponent'
475 * in order to gain experience. Examples include foes killed combat,
476 * finding/disarming a trap, stealing from somebeing, etc.
477 * The gained experience is based primarily on the difference in levels,
478 * exp point value of vanquished foe, the relevent stats of the skill being
479 * used and modifications in the skills[] table.
480 *
481 * For now, monsters and players will be treated differently. Below I give
482 * the algorithm for *PLAYER* experience gain. Monster exp gain is simpler.
483 * Monsters just get 10% of the exp of the opponent.
484 *
485 * players get a ratio, eg, opponent lvl / player level. This is then
486 * multiplied by various things. If simple exp is true, then
487 * this multiplier, include the level difference, is always 1.
488 * This revised method prevents some cases where there are big gaps
489 * in the amount you get just because you are now equal level vs lower
490 * level
491 * who is player/creature that used the skill.
492 * op is the object that was 'defeated'.
493 * skill is the skill used. If no skill is used, it should just
494 * point back to who.
495 *
496 */
497 int
498 calc_skill_exp (object *who, object *op, object *skill)
499 {
500 int op_exp = 0, op_lvl = 0;
501 float base, value, lvl_mult = 0.0;
502
503 if (!skill)
504 skill = who;
505
506 /* Oct 95 - where we have an object, I expanded our treatment
507 * to 3 cases:
508 * non-living magic obj, runes and everything else.
509 *
510 * If an object is not alive and magical we set the base exp higher to
511 * help out exp awards for skill_ident skills. Also, if
512 * an item is type RUNE, we give out exp based on stats.Cha
513 * and level (this was the old system) -b.t.
514 */
515
516 if (!op)
517 { /* no item/creature */
518 op_lvl = who->map->difficulty < 1 ? 1 : who->map->difficulty;
519 op_exp = 0;
520 }
521 else if (op->type == RUNE || op->type == TRAP)
522 { /* all traps. If stats.Cha > 1 we use that
523 * for the amount of experience */
524 op_exp = op->stats.Cha > 1 ? op->stats.Cha : op->stats.exp;
525 op_lvl = op->level;
526 }
527 else
528 { /* all other items/living creatures */
529 op_exp = op->stats.exp;
530 op_lvl = op->level;
531 if (!QUERY_FLAG (op, FLAG_ALIVE))
532 { /* for ident/make items */
533 op_lvl += 5 * abs (op->magic);
534 }
535 }
536
537 if (op_lvl < 1)
538 op_lvl = 1;
539
540 if (who->type != PLAYER)
541 { /* for monsters only */
542 return ((int) (op_exp * 0.1) + 1); /* we add one to insure positive value is returned */
543 }
544 else
545 { /* for players */
546 base = op_exp;
547 /* if skill really is a skill, then we can look at the skill archetype for
548 * bse reward value (exp) and level multiplier factor.
549 */
550 if (skill->type == SKILL)
551 {
552 base += skill->arch->stats.exp;
553 if (settings.simple_exp)
554 {
555 if (skill->arch->level)
556 lvl_mult = (float) skill->arch->level / 100.0;
557 else
558 lvl_mult = 1.0; /* no adjustment */
559 }
560 else
561 {
562 if (skill->level)
563 lvl_mult = ((float) skill->arch->level * (float) op_lvl) / ((float) skill->level * 100.0);
564 else
565 lvl_mult = 1.0;
566 }
567 }
568 else
569 {
570 /* Don't divide by zero here! */
571 lvl_mult = (float) op_lvl / (float) (skill->level ? skill->level : 1);
572 }
573 }
574
575 /* assemble the exp total, and return value */
576
577 value = base * lvl_mult;
578 if (value < 1)
579 value = 1; /* Always give at least 1 exp point */
580
581 #ifdef SKILL_UTIL_DEBUG
582 LOG (llevDebug, "calc_skill_exp(): who: %s(lvl:%d) op:%s(lvl:%d)\n", who->name, skill->level, op->name, op_lvl);
583 #endif
584 return ((int) value);
585 }
586
587 /* Learn skill. This inserts the requested skill in the player's
588 * inventory. The skill field of the scroll should have the
589 * exact name of the requested skill.
590 * This one actually teaches the player the skill as something
591 * they can equip.
592 * Return 0 if the player knows the skill, 1 if the
593 * player learns the skill, 2 otherwise.
594 */
595 int
596 learn_skill (object *pl, object *scroll)
597 {
598 if (!scroll->skill)
599 {
600 LOG (llevError, "skill scroll %s does not have skill pointer set.\n", &scroll->name);
601 return 2;
602 }
603
604 object *tmp = find_skill (pl, scroll->skill);
605
606 /* player already knows it */
607 if (tmp && QUERY_FLAG (tmp, FLAG_CAN_USE_SKILL))
608 return 0;
609
610 /* now a random change to learn, based on player Int.
611 * give bonus based on level - otherwise stupid characters
612 * might never be able to learn anything.
613 */
614 if (random_roll (0, 99, pl, PREFER_LOW) > (learn_spell[pl->stats.Int] + (pl->level / 5)))
615 return 2; /* failure :< */
616
617 if (!tmp)
618 tmp = give_skill_by_name (pl, scroll->skill);
619
620 if (!tmp)
621 {
622 LOG (llevError, "skill scroll %s does not have valid skill name (%s).\n", &scroll->name, &scroll->skill);
623 return 2;
624 }
625
626 SET_FLAG (tmp, FLAG_CAN_USE_SKILL);
627 link_player_skills (pl);
628 return 1;
629 }
630
631 /* Gives a percentage clipped to 0% -> 100% of a/b. */
632 /* Probably belongs in some global utils-type file? */
633 static int
634 clipped_percent (sint64 a, sint64 b)
635 {
636 int rv;
637
638 if (b <= 0)
639 return 0;
640
641 rv = (int) ((100.0f * ((float) a) / ((float) b)) + 0.5f);
642
643 if (rv < 0)
644 return 0;
645 else if (rv > 100)
646 return 100;
647
648 return rv;
649 }
650
651 /* show_skills() - Meant to allow players to examine
652 * their current skill list.
653 * This shows the amount of exp they have in the skills.
654 * we also include some other non skill related info (god,
655 * max weapon improvments, item power).
656 * Note this function is a bit more complicated becauase we
657 * we want ot sort the skills before printing them. If we
658 * just dumped this as we found it, this would be a bit
659 * simpler.
660 */
661 //TODO: egad, do it in perl, do not suffer from the big buffer on stack, make it one single drawinfo.
662 void
663 show_skills (object *op, const char *search)
664 {
665 object *tmp = NULL;
666 char buf[MAX_BUF];
667 const char *cp;
668 int i, num_skills_found = 0;
669 static const char *const periods = "........................................";
670
671 /* Need to have a pointer and use strdup for qsort to work properly */
672 char skills[NUM_SKILLS][MAX_BUF];
673
674 for (tmp = op->inv; tmp != NULL; tmp = tmp->below)
675 {
676 if (tmp->type == SKILL)
677 {
678 if (search && strstr (tmp->name, search) == NULL)
679 continue;
680 /* Basically want to fill this out to 40 spaces with periods */
681 sprintf (buf, "%s%s", &tmp->name, periods);
682 buf[40] = 0;
683
684 if (settings.permanent_exp_ratio)
685 sprintf (skills[num_skills_found++], "%slvl:%3d (xp:%" PRId64 "/%" PRId64 "/%d%%)",
686 buf, tmp->level, tmp->stats.exp,
687 level_exp (tmp->level + 1, op->expmul), clipped_percent (tmp->perm_exp, tmp->stats.exp));
688 else
689 sprintf (skills[num_skills_found++], "%slvl:%3d (xp:%" PRId64 "/%" PRId64 ")",
690 buf, tmp->level, tmp->stats.exp, level_exp (tmp->level + 1, op->expmul));
691
692 /* I don't know why some characters get a bunch of skills, but
693 * it sometimes happens (maybe a leftover from bugier earlier code
694 * and those character are still about). In any case, lets handle
695 * it so it doesn't crash the server - otherwise, one character may
696 * crash the server numerous times.
697 */
698 if (num_skills_found >= NUM_SKILLS)
699 {
700 new_draw_info (NDI_RED, 0, op, "Your character has too many skills.");
701 new_draw_info (NDI_RED, 0, op, "Something isn't right - contact the server admin");
702 break;
703 }
704 }
705 }
706
707 new_draw_info (NDI_UNIQUE, 0, op, "Player skills:");
708 if (num_skills_found > 1)
709 qsort (skills, num_skills_found, MAX_BUF, (int (*)(const void *, const void *)) std::strcmp);
710
711 for (i = 0; i < num_skills_found; i++)
712 new_draw_info (NDI_UNIQUE, 0, op, skills[i]);
713
714 new_draw_info_format (NDI_UNIQUE, 0, op, "You can handle %d weapon improvements.", op->level / 5 + 5);
715
716 cp = determine_god (op);
717 new_draw_info_format (NDI_UNIQUE, 0, op, "You worship %s.", cp ? cp : "no god at current time");
718
719 new_draw_info_format (NDI_UNIQUE, 0, op, "Your equipped item power is %d out of %d\n",
720 op->contr->item_power, (int) (op->level * settings.item_power_factor));
721 }
722
723 /* use_skill() - similar to invoke command, it executes the skill in the
724 * direction that the user is facing. Returns false if we are unable to
725 * change to the requested skill, or were unable to use the skill properly.
726 * This is tricky because skills can have spaces. We basically roll
727 * our own find_skill_by_name so we can try to do better string matching.
728 */
729 int
730 use_skill (object *op, const char *string)
731 {
732 object *skop;
733 size_t len;
734
735 if (!string)
736 return 0;
737
738 for (skop = op->inv; skop; skop = skop->below)
739 {
740 if (skop->type == SKILL
741 && QUERY_FLAG (skop, FLAG_CAN_USE_SKILL)
742 && !strncasecmp (string, skop->skill, MIN (strlen (string), (size_t) strlen (skop->skill))))
743 break;
744 else if (skop->type == SKILL_TOOL
745 && !strncasecmp (string, skop->skill, MIN (strlen (string), (size_t) strlen (skop->skill))))
746 break;
747 }
748
749 if (!skop)
750 {
751 new_draw_info_format (NDI_UNIQUE, 0, op, "Unable to find skill %s", string);
752 return 0;
753 }
754
755 len = strlen (skop->skill);
756
757 /* All this logic goes and skips over the skill name to find any
758 * options given to the skill. Its pretty simple - if there
759 * are extra parameters (as deteremined by string length), we
760 * want to skip over any leading spaces.
761 */
762 if (len >= strlen (string))
763 string = NULL;
764 else
765 {
766 string += len;
767 while (*string == 0x20)
768 string++;
769
770 if (strlen (string) == 0)
771 string = NULL;
772 }
773
774 #ifdef SKILL_UTIL_DEBUG
775 LOG (llevDebug, "use_skill() got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
776 #endif
777
778 if (do_skill (op, op, skop, op->facing, string))
779 return 1;
780
781 return 0;
782 }
783
784 static bool
785 hth_skill_p (object *skill)
786 {
787 return (skill_flags [skill->subtype] & (SF_COMBAT | SF_NEED_WEAPON)) == SF_COMBAT;
788 }
789
790 /* This finds the first unarmed skill the player has, and returns it.
791 */
792 static object *
793 find_player_hth_skill (object *op)
794 {
795 for (object *tmp = op->inv; tmp; tmp = tmp->below)
796 if (tmp->type == SKILL && QUERY_FLAG (tmp, FLAG_CAN_USE_SKILL) && hth_skill_p (tmp))
797 return tmp;
798
799 return 0;
800 }
801
802 /* do_skill_attack() - We have got an appropriate opponent from either
803 * move_player_attack() or skill_attack(). In this part we get on with
804 * attacking, take care of messages from the attack and changes in invisible.
805 * Returns true if the attack damaged the opponent.
806 * tmp is the targetted monster.
807 * op is what is attacking
808 * string is passed along to describe what messages to describe
809 * the damage.
810 */
811 static int
812 do_skill_attack (object *tmp, object *op, const char *string, object *skill)
813 {
814 if (INVOKE_OBJECT (SKILL_ATTACK, op, ARG_OBJECT (tmp), ARG_STRING (string), ARG_OBJECT (skill)))
815 return RESULT_INT (0);
816
817 /* For Players only: if there is no ready weapon, and no "attack" skill
818 * is readied either then try to find a skill for the player to use.
819 * it is presumed that if skill is set, it is a valid attack skill (eg,
820 * the caller should have set it appropriately). We still want to pass
821 * through that code if skill is set to change to the skill.
822 */
823 if (player *pl = op->contr)
824 {
825 if (!pl->combat_ob)
826 {
827 if (QUERY_FLAG (op, FLAG_READY_WEAPON))
828 {
829 for (tmp = op->inv; tmp; tmp = tmp->below)
830 if (tmp->type == WEAPON && QUERY_FLAG (tmp, FLAG_APPLIED))
831 break;
832
833 if (!tmp)
834 LOG (llevError, "Could not find applied weapon on %s\n", &op->name);
835
836 pl->combat_ob = tmp;
837 }
838
839 if (!pl->combat_ob)
840 {
841 if (!skill)
842 {
843 /* See if the players chosen skill is a combat skill, and use
844 * it if appropriate.
845 */
846 if (op->chosen_skill && hth_skill_p (op->chosen_skill))
847 skill = op->chosen_skill;
848 else
849 {
850 skill = find_player_hth_skill (op);
851
852 if (!skill)
853 new_draw_info (NDI_BLACK, 0, op, "You have no unarmed combat skills!");
854 }
855 }
856
857 if (skill)
858 {
859 op->change_skill (0);
860 apply_special (op, skill, AP_APPLY);
861 }
862 }
863
864 if (!pl->combat_ob)
865 {
866 LOG (llevError, "Could not find anything to attack on %s\n", &op->name);
867 return 0;
868 }
869 }
870
871 if (!op->change_weapon (pl->combat_ob))
872 return 0;
873
874 /* lose invisiblity/hiding status for running attacks */
875 if (pl->tmp_invis)
876 {
877 pl->tmp_invis = 0;
878 op->invisible = 0;
879 op->hide = 0;
880 update_object (op, UP_OBJ_CHANGE);
881 }
882 }
883
884 int success = attack_ob (tmp, op);
885
886 /* print appropriate messages to the player */
887
888 if (success && string && tmp && !QUERY_FLAG (tmp, FLAG_FREED))
889 {
890 if (op->type == PLAYER)
891 new_draw_info_format (NDI_UNIQUE, 0, op, "You %s %s!", string, query_name (tmp));
892 else if (tmp->type == PLAYER)
893 new_draw_info_format (NDI_UNIQUE, 0, tmp, "%s %s you!", query_name (op), string);
894 }
895
896 return success;
897 }
898
899 /* skill_attack() - Core routine for use when we attack using a skills
900 * system. In essence, this code handles
901 * all skill-based attacks, ie hth, missile and melee weapons should be
902 * treated here. If an opponent is already supplied by move_player(),
903 * we move right onto do_skill_attack(), otherwise we find if an
904 * appropriate opponent exists.
905 *
906 * This is called by move_player() and attack_hth()
907 *
908 * Initial implementation by -bt thomas@astro.psu.edu
909 */
910 int
911 skill_attack (object *tmp, object *pl, int dir, const char *string, object *skill)
912 {
913 sint16 tx, ty;
914 maptile *m;
915 int mflags;
916
917 if (!dir)
918 dir = pl->facing;
919
920 tx = freearr_x[dir];
921 ty = freearr_y[dir];
922
923 /* If we don't yet have an opponent, find if one exists, and attack.
924 * Legal opponents are the same as outlined in move_player_attack()
925 */
926 if (!tmp)
927 {
928 m = pl->map;
929 tx = pl->x + freearr_x[dir];
930 ty = pl->y + freearr_y[dir];
931
932 mflags = get_map_flags (m, &m, tx, ty, &tx, &ty);
933 if (mflags & P_OUT_OF_MAP)
934 return 0;
935
936 /* space must be blocked for there to be anything interesting to do */
937 if (!OB_TYPE_MOVE_BLOCK (pl, GET_MAP_MOVE_BLOCK (m, tx, ty)))
938 return 0;
939
940 for (tmp = GET_MAP_OB (m, tx, ty); tmp; tmp = tmp->above)
941 if ((QUERY_FLAG (tmp, FLAG_ALIVE) && tmp->stats.hp >= 0) || QUERY_FLAG (tmp, FLAG_CAN_ROLL) || tmp->type == LOCKED_DOOR)
942 {
943 /* Don't attack party members */
944 if ((pl->type == PLAYER && tmp->type == PLAYER) && (pl->contr->party != NULL && pl->contr->party == tmp->contr->party))
945 return 0;
946
947 break;
948 }
949 }
950
951 if (!tmp)
952 {
953 if (pl->type == PLAYER)
954 new_draw_info (NDI_UNIQUE, 0, pl, "There is nothing to attack!");
955
956 return 0;
957 }
958
959 return do_skill_attack (tmp, pl, string, skill);
960 }
961
962 /* attack_hth() - this handles all hand-to-hand attacks -b.t. */
963
964 /* July 5, 1995 - I broke up attack_hth() into 2 parts. In the first
965 * (attack_hth) we check for weapon use, etc in the second (the new
966 * function skill_attack() we actually attack.
967 */
968 static int
969 attack_hth (object *pl, int dir, const char *string, object *skill)
970 {
971 object *enemy = NULL, *weapon;
972
973 if (QUERY_FLAG (pl, FLAG_READY_WEAPON))
974 for (weapon = pl->inv; weapon; weapon = weapon->below)
975 {
976 if (weapon->type == WEAPON && QUERY_FLAG (weapon, FLAG_APPLIED))
977 {
978 CLEAR_FLAG (weapon, FLAG_APPLIED);
979 CLEAR_FLAG (pl, FLAG_READY_WEAPON);
980 pl->update_stats ();
981 if (pl->type == PLAYER)
982 {
983 new_draw_info (NDI_UNIQUE, 0, pl, "You unwield your weapon in order to attack.");
984 esrv_update_item (UPD_FLAGS, pl, weapon);
985 }
986
987 break;
988 }
989 }
990
991 return skill_attack (enemy, pl, dir, string, skill);
992 }
993
994 /* attack_melee_weapon() - this handles melee weapon attacks -b.t.
995 * For now we are just checking to see if we have a ready weapon here.
996 * But there is a real neato possible feature of this scheme which
997 * bears mentioning:
998 * Since we are only calling this from do_skill() in the future
999 * we may make this routine handle 'special' melee weapons attacks
1000 * (like disarming manuever with sai) based on player SK_level and
1001 * weapon type.
1002 */
1003 static int
1004 attack_melee_weapon (object *op, int dir, const char *string, object *skill)
1005 {
1006
1007 if (!QUERY_FLAG (op, FLAG_READY_WEAPON))
1008 {
1009 if (op->type == PLAYER)
1010 new_draw_info (NDI_UNIQUE, 0, op, "You have no ready weapon to attack with!");
1011
1012 return 0;
1013 }
1014
1015 return skill_attack (NULL, op, dir, string, skill);
1016 }
1017