ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/skill_util.C
Revision: 1.29
Committed: Wed Mar 14 04:12:29 2007 UTC (17 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.28: +1 -1 lines
Log Message:
- rewrote more face handling code
- automatically send smooth faces, as the client will need them anyways
  and it makes little sense to wait for the client to axk for it. of course,
  gcfclient suffers from weird ordering problems again.
- UP_OBJ_FACE was often abused in situations where other things changed,
  updated lots of spaces, probably more to be done.
- update_smooth became so small that inlining it actually clarified
  the code. similar for update_space, which is not inlined for other reasons.
- faces were not initialised properly
- add versioncheck for face data
- rewrite invisibility handling a bit: god finger etc. now makes you blink,
  blinking routine has changed to be less annoying and more useful while
  still indicating invisibleness.

File Contents

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