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

# User Rev Content
1 elmex 1.1 /*
2 root 1.54 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 pippijn 1.26 *
4 root 1.54 * 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 pippijn 1.26 *
8 root 1.58 * 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 pippijn 1.26 *
13 root 1.58 * 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 pippijn 1.26 *
18 root 1.58 * 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 root 1.54 *
21     * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 pippijn 1.26 */
23 elmex 1.1
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 root 1.9
40 elmex 1.1 /* #define SKILL_UTIL_DEBUG */
41    
42     #include <global.h>
43     #include <object.h>
44 root 1.23 #include <sproto.h>
45 root 1.9 #include <living.h> /* for defs of STR,CON,DEX,etc. -b.t. */
46 elmex 1.1 #include <spells.h>
47    
48 root 1.33 const uint8_t skill_flags[NUM_SKILLS] = {
49 root 1.34 0, // SK_NONE
50 root 1.33 # define def(uc, flags) flags,
51     # include "skillinc.h"
52     # undef def
53 root 1.11 };
54    
55 root 1.9 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 elmex 1.1
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 root 1.9 void
63     init_skills (void)
64     {
65 root 1.56 for_all_archetypes (at)
66     if (at->type == SKILL)
67 root 1.10 {
68 root 1.56 if (skill_names[at->subtype])
69 root 1.10 LOG (llevError, "init_skills: multiple skill using same subtype %d, %s, %s\n",
70 root 1.56 at->subtype, &skill_names[at->subtype], &at->skill);
71 root 1.10 else
72 root 1.56 skill_names[at->subtype] = at->skill;
73 root 1.10 }
74 elmex 1.1
75 root 1.9 /* 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 root 1.33 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 elmex 1.1 }
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 root 1.9 void
89     link_player_skills (object *op)
90 elmex 1.1 {
91 root 1.25 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 elmex 1.1 }
108    
109 root 1.49 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 root 1.50 /* 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 root 1.49 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 root 1.53 return splay (tmp);
137 root 1.49 /* 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 root 1.53 if (apply_special (who, splay (skill_tool), AP_APPLY | AP_NO_READY))
161 root 1.49 return 0;
162    
163 root 1.53 return splay (skill);
164 root 1.49 }
165    
166 root 1.50 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 elmex 1.1 /* This returns the skill pointer of the given name (the
182 root 1.49 * one that accumulates exp, has the level, etc).
183 elmex 1.1 *
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 root 1.9 object *
193     find_skill_by_number (object *who, int skillno)
194 elmex 1.1 {
195 root 1.47 for (object *tmp = who->inv; tmp; tmp = tmp->below)
196 root 1.50 if (tmp->type == SKILL && tmp->subtype == skillno)
197     if (object *skop = find_skill_by_name (who, tmp->skill))
198     return skop;
199 elmex 1.1
200 root 1.50 return 0;
201 elmex 1.1 }
202    
203 root 1.51 /* This changes the objects chosen_skill to new_skill.
204 elmex 1.1 * return 1 on success, 0 on error
205     */
206 root 1.51 bool
207     object::change_skill (object *new_skill)
208 elmex 1.1 {
209 root 1.51 if (type != PLAYER)
210 root 1.9 return 0;
211    
212 root 1.40 // optimise this supposedly common case
213 root 1.51 if (new_skill == chosen_skill)
214 root 1.40 return 1;
215    
216 root 1.51 if (chosen_skill)
217 root 1.32 {
218 root 1.51 chosen_skill->flag [FLAG_APPLIED] = false;
219     change_abil (this, chosen_skill);
220 root 1.32 }
221 elmex 1.1
222 root 1.51 chosen_skill = new_skill;
223 root 1.33
224 root 1.51 if (chosen_skill)
225     {
226     chosen_skill->flag [FLAG_APPLIED] = true;
227     change_abil (this, chosen_skill);
228     }
229 root 1.16
230 root 1.52 // 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 root 1.51 update_stats ();
236 root 1.9 return 1;
237 elmex 1.1 }
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 root 1.9 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 root 1.43 for (object *tmp = op->inv; tmp; tmp = tmp->below)
266 root 1.28 if (tmp->type == SKILL && tmp->skill == skill->skill)
267 root 1.43 {
268     skill = tmp;
269     goto found;
270     }
271 root 1.28
272 root 1.43 skill = give_skill_by_name (op, skill->skill);
273     found: ;
274 elmex 1.1 }
275    
276 root 1.9 // 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 elmex 1.1
280 root 1.9 switch (skill->subtype)
281     {
282 root 1.28 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 root 1.5
303 root 1.28 case SK_STEALING:
304     exp = success = steal (op, dir, skill);
305     break;
306 root 1.5
307 root 1.28 case SK_LOCKPICKING:
308     exp = success = pick_lock (op, dir, skill);
309     break;
310 root 1.5
311 root 1.28 case SK_HIDING:
312     exp = success = hide (op, skill);
313     break;
314 root 1.5
315 root 1.28 case SK_JUMPING:
316     success = jump (op, dir, skill);
317     break;
318 root 1.5
319 root 1.28 case SK_INSCRIPTION:
320     exp = success = write_on_item (op, string, skill);
321     break;
322 root 1.5
323 root 1.28 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 root 1.5
329 root 1.28 case SK_KARATE:
330 root 1.30 attack_hth (op, dir, "karate-chopped", skill);
331 root 1.28 break;
332 root 1.5
333 root 1.28 case SK_PUNCHING:
334 root 1.30 attack_hth (op, dir, "punched", skill);
335 root 1.28 break;
336 root 1.5
337 root 1.28 case SK_FLAME_TOUCH:
338 root 1.30 attack_hth (op, dir, "flamed", skill);
339 root 1.28 break;
340 elmex 1.1
341 root 1.28 case SK_SPARK_TOUCH:
342 root 1.30 attack_hth (op, dir, "zapped", skill);
343 root 1.28 break;
344 elmex 1.1
345 root 1.28 case SK_SHIVER:
346 root 1.30 attack_hth (op, dir, "froze", skill);
347 root 1.28 break;
348 elmex 1.1
349 root 1.28 case SK_ACID_SPLASH:
350 root 1.30 attack_hth (op, dir, "dissolved", skill);
351 root 1.28 break;
352 elmex 1.1
353 root 1.28 case SK_POISON_NAIL:
354 root 1.30 attack_hth (op, dir, "injected poison into", skill);
355 root 1.28 break;
356 elmex 1.1
357 root 1.28 case SK_CLAWING:
358 root 1.30 attack_hth (op, dir, "clawed", skill);
359 root 1.28 break;
360 root 1.5
361 root 1.28 case SK_ONE_HANDED_WEAPON:
362     case SK_TWO_HANDED_WEAPON:
363 root 1.30 attack_melee_weapon (op, dir, NULL, skill);
364 root 1.28 break;
365 root 1.5
366 root 1.28 case SK_FIND_TRAPS:
367     exp = success = find_traps (op, skill);
368     break;
369 root 1.5
370 root 1.28 case SK_SINGING:
371     exp = success = singing (op, dir, skill);
372     break;
373 root 1.5
374 root 1.28 case SK_ORATORY:
375     exp = success = use_oratory (op, dir, skill);
376     break;
377 root 1.5
378 root 1.28 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 root 1.43 for (object *next, *tmp = GET_MAP_OB (op->map, op->x, op->y); tmp; tmp = next)
389 root 1.28 {
390     next = tmp->above;
391 root 1.17
392 root 1.28 if (QUERY_FLAG (tmp, FLAG_IS_CAULDRON))
393     {
394     attempt_do_alchemy (op, tmp);
395 root 1.17
396 root 1.28 if (QUERY_FLAG (tmp, FLAG_APPLIED))
397     esrv_send_inventory (op, tmp);
398 root 1.17
399 root 1.28 did_alc = 1;
400     }
401     }
402 root 1.17
403 root 1.28 if (did_alc == 0)
404     exp = success = skill_ident (op, skill);
405 root 1.17
406 root 1.28 break;
407 root 1.5
408 root 1.28 case SK_DET_MAGIC:
409     case SK_DET_CURSE:
410     exp = success = skill_ident (op, skill);
411     break;
412 root 1.5
413 root 1.28 case SK_DISARM_TRAPS:
414     exp = success = remove_trap (op, dir, skill);
415     break;
416 root 1.5
417 root 1.28 case SK_THROWING:
418     success = skill_throw (op, part, dir, string, skill);
419     break;
420 root 1.5
421 root 1.28 case SK_SET_TRAP:
422     new_draw_info (NDI_UNIQUE, 0, op, "This skill is not currently implemented.");
423     break;
424 root 1.5
425 root 1.28 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 root 1.5
430 root 1.28 case SK_PRAYING:
431     success = pray (op, skill);
432     break;
433 root 1.5
434 root 1.28 case SK_BARGAINING:
435     success = describe_shop (op);
436     break;
437 elmex 1.1
438 root 1.28 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 root 1.5
446 root 1.28 default:
447     LOG (llevDebug, "%s attempted to use unknown skill: %d\n", query_name (op), op->chosen_skill->stats.sp);
448     break;
449 elmex 1.1 }
450    
451 root 1.9 /* 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 root 1.45 op->speed_left -= 1.f;
460 root 1.9
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 elmex 1.1 }
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 root 1.9 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 root 1.5 * for the amount of experience */
524 root 1.9 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 root 1.56 base += skill->arch->stats.exp;
553 root 1.9 if (settings.simple_exp)
554     {
555 root 1.56 if (skill->arch->level)
556     lvl_mult = (float) skill->arch->level / 100.0;
557 root 1.9 else
558     lvl_mult = 1.0; /* no adjustment */
559 root 1.5 }
560 root 1.9 else
561     {
562     if (skill->level)
563 root 1.56 lvl_mult = ((float) skill->arch->level * (float) op_lvl) / ((float) skill->level * 100.0);
564 root 1.9 else
565     lvl_mult = 1.0;
566 root 1.5 }
567 root 1.9 }
568     else
569     {
570     /* Don't divide by zero here! */
571     lvl_mult = (float) op_lvl / (float) (skill->level ? skill->level : 1);
572 root 1.5 }
573 elmex 1.1 }
574 root 1.9
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 elmex 1.1 #ifdef SKILL_UTIL_DEBUG
582 root 1.9 LOG (llevDebug, "calc_skill_exp(): who: %s(lvl:%d) op:%s(lvl:%d)\n", who->name, skill->level, op->name, op_lvl);
583 elmex 1.1 #endif
584 root 1.9 return ((int) value);
585 elmex 1.1 }
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 root 1.9 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 elmex 1.1 }
603    
604 root 1.50 object *tmp = find_skill (pl, scroll->skill);
605 elmex 1.1
606 root 1.9 /* player already knows it */
607     if (tmp && QUERY_FLAG (tmp, FLAG_CAN_USE_SKILL))
608     return 0;
609 elmex 1.1
610 root 1.9 /* 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 elmex 1.1
617 root 1.9 if (!tmp)
618     tmp = give_skill_by_name (pl, scroll->skill);
619 elmex 1.1
620 root 1.9 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 elmex 1.1 }
625    
626 root 1.9 SET_FLAG (tmp, FLAG_CAN_USE_SKILL);
627     link_player_skills (pl);
628     return 1;
629 elmex 1.1 }
630    
631     /* Gives a percentage clipped to 0% -> 100% of a/b. */
632     /* Probably belongs in some global utils-type file? */
633 root 1.9 static int
634     clipped_percent (sint64 a, sint64 b)
635 elmex 1.1 {
636     int rv;
637    
638     if (b <= 0)
639     return 0;
640    
641 root 1.9 rv = (int) ((100.0f * ((float) a) / ((float) b)) + 0.5f);
642    
643 elmex 1.1 if (rv < 0)
644     return 0;
645     else if (rv > 100)
646     return 100;
647 root 1.9
648 elmex 1.1 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 root 1.57 //TODO: egad, do it in perl, do not suffer from the big buffer on stack, make it one single drawinfo.
662 root 1.9 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 root 1.57 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 root 1.9 else
689 root 1.57 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 root 1.9 /* 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 root 1.5 }
704     }
705 elmex 1.1 }
706    
707 root 1.9 new_draw_info (NDI_UNIQUE, 0, op, "Player skills:");
708     if (num_skills_found > 1)
709 root 1.27 qsort (skills, num_skills_found, MAX_BUF, (int (*)(const void *, const void *)) std::strcmp);
710 elmex 1.1
711 root 1.9 for (i = 0; i < num_skills_found; i++)
712 root 1.27 new_draw_info (NDI_UNIQUE, 0, op, skills[i]);
713 elmex 1.1
714 root 1.9 new_draw_info_format (NDI_UNIQUE, 0, op, "You can handle %d weapon improvements.", op->level / 5 + 5);
715 elmex 1.1
716 root 1.9 cp = determine_god (op);
717     new_draw_info_format (NDI_UNIQUE, 0, op, "You worship %s.", cp ? cp : "no god at current time");
718 elmex 1.1
719 root 1.9 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 elmex 1.1 }
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 root 1.9 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 root 1.50 for (skop = op->inv; skop; skop = skop->below)
739 root 1.9 {
740 root 1.50 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 root 1.9 break;
744 root 1.50 else if (skop->type == SKILL_TOOL
745     && !strncasecmp (string, skop->skill, MIN (strlen (string), (size_t) strlen (skop->skill))))
746 root 1.9 break;
747 elmex 1.1 }
748 root 1.50
749 root 1.9 if (!skop)
750     {
751     new_draw_info_format (NDI_UNIQUE, 0, op, "Unable to find skill %s", string);
752     return 0;
753 elmex 1.1 }
754    
755 root 1.9 len = strlen (skop->skill);
756 elmex 1.1
757 root 1.9 /* 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 root 1.50 string = NULL;
764 root 1.9 else
765     {
766     string += len;
767     while (*string == 0x20)
768     string++;
769 root 1.50
770 root 1.9 if (strlen (string) == 0)
771     string = NULL;
772     }
773    
774 elmex 1.1 #ifdef SKILL_UTIL_DEBUG
775 root 1.9 LOG (llevDebug, "use_skill() got skill: %s\n", sknum > -1 ? skills[sknum].name : "none");
776 elmex 1.1 #endif
777    
778 root 1.9 if (do_skill (op, op, skop, op->facing, string))
779     return 1;
780    
781     return 0;
782 elmex 1.1 }
783    
784 root 1.16 static bool
785     hth_skill_p (object *skill)
786     {
787 root 1.33 return (skill_flags [skill->subtype] & (SF_COMBAT | SF_NEED_WEAPON)) == SF_COMBAT;
788 root 1.16 }
789    
790     /* This finds the first unarmed skill the player has, and returns it.
791 elmex 1.1 */
792 root 1.9 static object *
793 root 1.16 find_player_hth_skill (object *op)
794 elmex 1.1 {
795 root 1.16 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 elmex 1.1
799 root 1.16 return 0;
800 elmex 1.1 }
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 root 1.9 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 root 1.35 if (player *pl = op->contr)
824 root 1.9 {
825 root 1.35 if (!pl->combat_ob)
826 root 1.9 {
827 root 1.35 if (QUERY_FLAG (op, FLAG_READY_WEAPON))
828 root 1.9 {
829 root 1.35 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 root 1.52 LOG (llevError, "Could not find applied weapon on %s\n", &op->name);
835 root 1.9
836 root 1.51 pl->combat_ob = tmp;
837 root 1.35 }
838 root 1.52
839     if (!pl->combat_ob)
840 root 1.35 {
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 root 1.9 {
850 root 1.35 skill = find_player_hth_skill (op);
851    
852     if (!skill)
853 root 1.52 new_draw_info (NDI_BLACK, 0, op, "You have no unarmed combat skills!");
854 root 1.5 }
855     }
856 root 1.35
857 root 1.55 if (skill)
858     {
859     op->change_skill (0);
860     apply_special (op, skill, AP_APPLY);
861     }
862 root 1.5 }
863 root 1.16
864 root 1.35 if (!pl->combat_ob)
865     {
866     LOG (llevError, "Could not find anything to attack on %s\n", &op->name);
867 root 1.16 return 0;
868 root 1.5 }
869 root 1.9 }
870 root 1.35
871 root 1.44 if (!op->change_weapon (pl->combat_ob))
872     return 0;
873 root 1.9
874 root 1.38 /* lose invisiblity/hiding status for running attacks */
875 root 1.35 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 root 1.5 }
882 elmex 1.1 }
883    
884 root 1.31 int success = attack_ob (tmp, op);
885 root 1.9
886     /* print appropriate messages to the player */
887    
888 root 1.31 if (success && string && tmp && !QUERY_FLAG (tmp, FLAG_FREED))
889 root 1.9 {
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 root 1.30
896 root 1.9 return success;
897     }
898 elmex 1.1
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 root 1.9 int
911     skill_attack (object *tmp, object *pl, int dir, const char *string, object *skill)
912     {
913     sint16 tx, ty;
914 root 1.13 maptile *m;
915 root 1.9 int mflags;
916    
917     if (!dir)
918     dir = pl->facing;
919 root 1.16
920 root 1.9 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 root 1.31 if (!tmp)
927 root 1.9 {
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 root 1.21 for (tmp = GET_MAP_OB (m, tx, ty); tmp; tmp = tmp->above)
941 root 1.9 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 root 1.31
947 root 1.9 break;
948     }
949 elmex 1.1 }
950 root 1.31
951 root 1.9 if (!tmp)
952     {
953     if (pl->type == PLAYER)
954     new_draw_info (NDI_UNIQUE, 0, pl, "There is nothing to attack!");
955 root 1.31
956 root 1.9 return 0;
957 elmex 1.1 }
958    
959 root 1.9 return do_skill_attack (tmp, pl, string, skill);
960 elmex 1.1 }
961    
962     /* attack_hth() - this handles all hand-to-hand attacks -b.t. */
963 root 1.9
964 elmex 1.1 /* 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 root 1.9 static int
969     attack_hth (object *pl, int dir, const char *string, object *skill)
970     {
971     object *enemy = NULL, *weapon;
972 elmex 1.1
973 root 1.9 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 root 1.22 pl->update_stats ();
981 root 1.9 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 root 1.37
987 root 1.9 break;
988     }
989     }
990 root 1.30
991 root 1.9 return skill_attack (enemy, pl, dir, string, skill);
992 elmex 1.1 }
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 root 1.9 static int
1004     attack_melee_weapon (object *op, int dir, const char *string, object *skill)
1005     {
1006 elmex 1.1
1007 root 1.9 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 root 1.30
1012 root 1.9 return 0;
1013 elmex 1.1 }
1014 root 1.30
1015 root 1.9 return skill_attack (NULL, op, dir, string, skill);
1016 root 1.51 }
1017 elmex 1.1