1 /* $Id$ */
2 /* File: skills.c */
3 
4 /* Purpose: player skills */
5 
6 /*
7  * Copyright (c) 2001 DarkGod
8  *
9  * This software may be copied and distributed for educational, research, and
10  * not for profit purposes provided that this copyright and statement are
11  * included in all such copies.
12  */
13 
14 /* added this for consistency in some (unrelated) header-inclusion,
15    it IS a server file, isn't it? */
16 #define SERVER
17 #include "angband.h"
18 
19 #if 0
20 /* Initialze the s_info array at server start */
21 bool init_s_info()
22 {
23         int i;
24         int order = 1;
25 
26         for (i = 0; i < MAX_SKILLS; i++)
27         {
28                 /* Is there a skill to process here ? */
29                 if (skill_tree_init[i][1] > 0)
30                 {
31                         /* Set it's father and order in the tree */
32                         s_info[skill_tree_init[i][1]].father = skill_tree_init[i][0];
33                         s_info[skill_tree_init[i][1]].order = order++;
34                 }
35         }
36         return FALSE;
37 }
38 #endif	// 0
39 
40 /*
41  * Initialize a skill with given values
42  */
init_skill(player_type * p_ptr,u32b value,s16b mod,int i)43 void init_skill(player_type *p_ptr, u32b value, s16b mod, int i) {
44         p_ptr->s_info[i].base_value = value;
45         p_ptr->s_info[i].value = value;
46         p_ptr->s_info[i].mod = mod;
47 	p_ptr->s_info[i].touched = TRUE;
48 #if 0 //SMOOTHSKILLS
49         if (s_info[i].flags1 & SKF1_HIDDEN)
50 		p_ptr->s_info[i].hidden = TRUE;
51         else
52 		p_ptr->s_info[i].hidden = FALSE;
53         if (s_info[i].flags1 & SKF1_DUMMY)
54 		p_ptr->s_info[i].dummy = TRUE;
55         else
56 		p_ptr->s_info[i].dummy = FALSE;
57 #else
58 	p_ptr->s_info[i].flags1 = (char)(s_info[i].flags1 & 0xFF);
59 
60 	/* hack: Rangers can train limited Archery skill */
61 	if (p_ptr->pclass == CLASS_RANGER && i == SKILL_ARCHERY)
62 		p_ptr->s_info[i].flags1 |= SKF1_MAX_10;
63 	/* hack: Druids/Vampires can't train Mimicry skill */
64 	if ((p_ptr->pclass == CLASS_DRUID || p_ptr->prace == RACE_VAMPIRE)
65 	    && i == SKILL_MIMIC)
66 		p_ptr->s_info[i].flags1 |= SKF1_MAX_1;
67 #endif
68 }
69 
70 /*
71  *
72  */
get_skill(player_type * p_ptr,int skill)73 s16b get_skill(player_type *p_ptr, int skill)
74 {
75 #if 0
76 	/* prevent breaking the +2 limit */
77 	int s;
78 	s =  (p_ptr->s_info[skill].value / SKILL_STEP);
79 	if (s > p_ptr->lev + 2) s = p_ptr->lev + 2;
80 	return s;
81 #else
82 	return (p_ptr->s_info[skill].value / SKILL_STEP);
83 #endif
84 }
85 
86 
87 /*
88  *
89  */
get_skill_scale(player_type * p_ptr,int skill,u32b scale)90 s16b get_skill_scale(player_type *p_ptr, int skill, u32b scale)
91 {
92 #if 0
93 	/* prevent breaking the +2 limit */
94 	int s;
95 /*	s =  ((p_ptr->s_info[skill].value * 1000) / SKILL_STEP);
96 	if (s > (p_ptr->lev * 1000) + 2000) s = (p_ptr->lev * 1000) + 2000;
97 	s = (s * SKILL_STEP) / 1000;
98 	return (((s / 10) * (scale * (SKILL_STEP / 10)) / (SKILL_MAX / 10)) / (SKILL_STEP / 10)); */
99 
100 	/* Cleaning up this mess too... - mikaelh */
101 	s = p_ptr->s_info[skill].value;
102 	if (s > (p_ptr->lev + 2) * SKILL_STEP) s = (p_ptr->lev + 2) * SKILL_STEP;
103 	return ((s * scale) / SKILL_MAX);
104 
105 #else
106 	/* XXX XXX XXX */
107 	/* return (((p_ptr->s_info[skill].value / 10) * (scale * (SKILL_STEP / 10)) /
108 	         (SKILL_MAX / 10)) /
109 	        (SKILL_STEP / 10)); */
110 	/* Simpler formula suggested by Potter - mikaelh */
111 	return ((p_ptr->s_info[skill].value * scale) / SKILL_MAX);
112 
113 #endif
114 }
115 
116 /* Allow very rough resolution of skills into very small scaled values
117    (Added this for minus_... in dungeon.c - C. Blue) */
get_skill_scale_fine(player_type * p_ptr,int skill,u32b scale)118 s16b get_skill_scale_fine(player_type *p_ptr, int skill, u32b scale)
119 {
120 	return (((p_ptr->s_info[skill].value * scale) / SKILL_MAX) +
121 		(magik(((p_ptr->s_info[skill].value * scale * 100) / SKILL_MAX) % 100) ? 1 : 0));
122 }
123 
124 /* Will add, sub, .. */
modify_aux(s32b a,s32b b,char mod)125 static s32b modify_aux(s32b a, s32b b, char mod)
126 {
127 	switch (mod)
128 	{
129 		case '+':
130 			return (a + b);
131 			break;
132 		case '-':
133 			return (a - b);
134 			break;
135 		case '=':
136 			return (b);
137 			break;
138 		case '%':
139 			return ((a * b) / 100);
140 			break;
141 		default:
142 			return (0);
143 	}
144 }
145 
146 
147 /*
148  * Gets the base value of a skill, given a race/class/...
149  */
compute_skills(player_type * p_ptr,s32b * v,s32b * m,int i)150 void compute_skills(player_type *p_ptr, s32b *v, s32b *m, int i)
151 {
152 	s32b value = 0, mod = 0, j;
153 
154 	/***** class skills *****/
155 
156 	/* find the skill mods for that class */
157 	for (j = 0; j < MAX_SKILLS; j++) {
158 		if (p_ptr->cp_ptr->skills[j].skill == i) {
159 			value = p_ptr->cp_ptr->skills[j].value;
160 			mod = p_ptr->cp_ptr->skills[j].mod;
161 
162 			*v = modify_aux(*v,
163 					value, p_ptr->cp_ptr->skills[j].vmod);
164 			*m = modify_aux(*m,
165 					mod, p_ptr->cp_ptr->skills[j].mmod);
166 		}
167 	}
168 
169 	/* Race later (b/c of its modificative nature) */
170 	for (j = 0; j < MAX_SKILLS; j++) {
171 		if (p_ptr->rp_ptr->skills[j].skill == i) {
172 			value = p_ptr->rp_ptr->skills[j].value;
173 			mod = p_ptr->rp_ptr->skills[j].mod;
174 
175 			*v = modify_aux(*v,
176 					value, p_ptr->rp_ptr->skills[j].vmod);
177 			*m = modify_aux(*m,
178 					mod, p_ptr->rp_ptr->skills[j].mmod);
179 		}
180 	}
181 }
182 
183 /* Display messages to the player, telling him about newly gained abilities
184    from increasing a skill */
msg_gained_abilities(int Ind,int old_value,int i)185 void msg_gained_abilities(int Ind, int old_value, int i) {
186 	player_type *p_ptr = Players[Ind];
187 	int new_value = get_skill_scale(p_ptr, i, 500);
188 
189 //	int as = get_archery_skill(p_ptr);
190 //	int ws = get_weaponmastery_skill(p_ptr);
191 
192 	/* Tell the player about new abilities that he gained from the skill increase */
193 	if (old_value == new_value) return;
194 	switch (i) {
195 	case SKILL_CLIMB:	if (new_value == 10) msg_print(Ind, "\374\377GYou learn how to climb mountains!");
196 				break;
197 	case SKILL_LEVITATE: 	if (new_value == 10) msg_print(Ind, "\374\377GYou learn how to levitate!");
198 				break;
199 	case SKILL_FREEACT:	if (new_value == 10) msg_print(Ind, "\374\377GYou learn how to resist paralysis and move freely!");
200 				break;
201 	case SKILL_RESCONF:	if (new_value == 10) msg_print(Ind, "\374\377GYou learn how to keep a focussed mind and avoid confusion!");
202 				break;
203 	case SKILL_DODGE:
204 		if (old_value == 0 && new_value > 0 &&
205 		    p_ptr->inventory[INVEN_ARM].k_idx && p_ptr->inventory[INVEN_ARM].tval == TV_SHIELD)
206 			msg_print(Ind, "\377oYou cannot dodge attacks while wielding a shield!");
207 		break;
208 	case SKILL_MARTIAL_ARTS:
209 		if (old_value == 0 && new_value > 0) {
210 			bool warn_takeoff = FALSE;
211 			/* display some warnings if an item will severely conflict with Martial Arts skill */
212 			if (p_ptr->inventory[INVEN_WIELD].k_idx ||
213 			    is_weapon(p_ptr->inventory[INVEN_ARM].tval) || /* for dual-wielders */
214 #ifndef ENABLE_MA_BOOMERANG
215 			    p_ptr->inventory[INVEN_BOW].k_idx) {
216 #else
217 			    p_ptr->inventory[INVEN_BOW].tval == TV_BOW) {
218 #endif
219 #ifndef ENABLE_MA_BOOMERANG
220 				msg_print(Ind, "\374\377RWarning: Using any sort of weapon renders Martial Arts skill effectless.");
221 #else
222 				msg_print(Ind, "\374\377RWarning: Using any melee weapon or bow renders Martial Arts skill effectless.");
223 #endif
224 				warn_takeoff = TRUE;
225 			}
226 			if (p_ptr->inventory[INVEN_ARM].k_idx && p_ptr->inventory[INVEN_ARM].tval == TV_SHIELD) {
227 				msg_print(Ind, "\377RWarning: You cannot use special martial art styles with a shield!");
228 				warn_takeoff = TRUE;
229 			}
230 			if (warn_takeoff) msg_print(Ind, "\374\377R         Press 't' key to take off your weapons or shield.");
231 
232 			/* Martial artists shouldn't get a weapon-wield warning */
233 			p_ptr->warning_wield = 1;
234 			/* also don't send any weapon-bpr related warnings since their
235 			   suggested remedies don't work for us as MA user */
236 			p_ptr->warning_bpr = 1;
237 			p_ptr->warning_bpr2 = 1;
238 			p_ptr->warning_bpr3 = 1;
239 		}
240 		if (old_value < 10 && new_value >= 10) { /* the_sandman */
241 //			msg_print(Ind, "\374\377GYou feel as if you could take on the world!");
242 			msg_print(Ind, "\374\377GYou learn to use punching techniques.");
243 			msg_print(Ind, "\374\377GYour attack speed has become faster due to your training!");
244 		}
245 		if (old_value < 20 && new_value >= 20)
246 			msg_print(Ind, "\374\377GYou get the hang of using kicks.");
247 		if (old_value < 30 && new_value >= 30)
248 			msg_print(Ind, "\374\377GYou get the hang of using hand side strikes.");
249 		if (old_value < 50 && new_value >= 50)
250 			msg_print(Ind, "\374\377GYou get the hang of knee-based attacks.");
251 		if (old_value < 70 && new_value >= 70)
252 			msg_print(Ind, "\374\377GYou get the hang of elbow-based attacks.");
253 		if (old_value < 90 && new_value >= 90)
254 			msg_print(Ind, "\374\377GYou get the hang of butting techniques.");
255 		if (old_value < 100 && new_value >= 100) {
256 			msg_print(Ind, "\374\377GYou learn how to fall safely!");
257 			msg_print(Ind, "\374\377GYour attack speed has become faster due to your training!");
258 		}
259 		if (old_value < 110 && new_value >= 110)
260 			msg_print(Ind, "\374\377GYour kicks have improved.");
261 		if (old_value < 130 && new_value >= 130)
262 			msg_print(Ind, "\374\377GYou get the hang of well-timed uppercuts.");
263 		if (old_value < 150 && new_value >= 150)
264 			msg_print(Ind, "\374\377GYou learn how to tame your fear!");
265 		if (old_value < 160 && new_value >= 160)
266 			msg_print(Ind, "\374\377GYou get the hang of difficult double-kicks.");
267 		if (old_value < 200 && new_value >= 200) {
268 			msg_print(Ind, "\374\377GYou learn to use the Cat's Claw technique.");
269 			msg_print(Ind, "\374\377GYou learn how to keep your mind focussed and avoid confusion!");
270 			msg_print(Ind, "\374\377GYour attack speed has become faster due to your training!");
271 		}
272 		if (old_value < 250 && new_value >= 250) {
273 			msg_print(Ind, "\374\377GYou learn to use jump kicks effectively.");
274 			msg_print(Ind, "\374\377GYou learn how to resist paralysis and move freely!");
275 		}
276 		if (old_value < 290 && new_value >= 290)
277 			msg_print(Ind, "\374\377GYou learn to use the Eagle's Claw technique.");
278 		if (old_value < 300 && new_value >= 300) {
279 			msg_print(Ind, "\374\377GYou learn how to swim easily!");
280 			msg_print(Ind, "\374\377GYour attack speed has become faster due to your training!");
281 		}
282 		if (old_value < 330 && new_value >= 330) {
283 			msg_print(Ind, "\374\377GYou get the hang of circle kicks.");
284 		}
285 /*		} if (old_value < 350 && new_value >= 350) {  <- this one is now at skill 1.000 already
286 			msg_print(Ind, "\374\377GYour attack speed has become faster due to your training!"); */
287 		if (old_value < 370 && new_value >= 370)
288 			msg_print(Ind, "\374\377GYou learn the Iron Fist technique.");
289 		if (old_value < 400 && new_value >= 400) {
290 			msg_print(Ind, "\374\377GYou learn how to climb mountains!");
291 			msg_print(Ind, "\374\377GYour attack speed has become faster due to your training!");
292 		}
293 		if (old_value < 410 && new_value >= 410)
294 			msg_print(Ind, "\374\377GYou get the hang of difficult flying kicks.");
295 		if (old_value < 450 && new_value >= 450) {
296 			msg_print(Ind, "\374\377GYou learn the Dragon Fist technique.");
297 			msg_print(Ind, "\374\377GYour attack speed has become faster due to your training!");
298 		}
299 		if (old_value < 480 && new_value >= 480) {
300 			msg_print(Ind, "\374\377GYou get the hang of effective Crushing Blows.");
301 			if (p_ptr->total_winner) {
302 				msg_print(Ind, "\374\377GYou learn the Royal Titan's Fist technique.");
303 				msg_print(Ind, "\374\377GYou learn the Royal Phoenix Claw technique.");
304 			}
305 		}
306 		if (old_value < 500 && new_value >= 500) {
307 			msg_print(Ind, "\374\377GYou learn the technique of Levitation!");
308 		/* The final +ea has been moved down from lvl 50 to lvl 1 to boost MA a little - the_sandman - moved it to 350 - C. Blue */
309 			msg_print(Ind, "\374\377GYour attack speed has become faster due to your training!");
310 		}
311 		break;
312 	case SKILL_STANCE:
313 		/* automatically upgrade currently taken stance power */
314 		switch (p_ptr->pclass) {
315 		case CLASS_WARRIOR:
316 			if (old_value < 50 && new_value >= 50) msg_print(Ind, "\374\377GYou learn how to enter a defensive stance (rank I). ('\377gm\377G' key)");
317 			if (old_value < 150 && new_value >= 150) {
318 				msg_print(Ind, "\374\377GYou learn how to enter defensive stance rank II.");
319 				if (p_ptr->combat_stance == 1) p_ptr->combat_stance_power = 1;
320 			}
321 			if (old_value < 350 && new_value >= 350) {
322 				msg_print(Ind, "\374\377GYou learn how to enter defensive stance rank III.");
323 				if (p_ptr->combat_stance == 1) p_ptr->combat_stance_power = 2;
324 			}
325 			if (old_value < 100 && new_value >= 100) msg_print(Ind, "\374\377GYou learn how to enter an offensive stance (rank I).");
326 			if (old_value < 200 && new_value >= 200) {
327 				msg_print(Ind, "\374\377GYou learn how to enter offensive stance rank II.");
328 				if (p_ptr->combat_stance == 2) p_ptr->combat_stance_power = 1;
329 			}
330 			if (old_value < 400 && new_value >= 400) {
331 				msg_print(Ind, "\374\377GYou learn how to enter offensive stance rank III.");
332 				if (p_ptr->combat_stance == 2) p_ptr->combat_stance_power = 2;
333 			}
334 			break;
335 		case CLASS_MIMIC:
336 			if (old_value < 100 && new_value >= 100) msg_print(Ind, "\374\377GYou learn how to enter a defensive stance (rank I). ('\377gm\377G' key)");
337 			if (old_value < 200 && new_value >= 200) {
338 				msg_print(Ind, "\374\377GYou learn how to enter defensive stance rank II.");
339 				if (p_ptr->combat_stance == 1) p_ptr->combat_stance_power = 1;
340 			}
341 			if (old_value < 400 && new_value >= 400) {
342 				msg_print(Ind, "\374\377GYou learn how to enter defensive stance rank III.");
343 				if (p_ptr->combat_stance == 1) p_ptr->combat_stance_power = 2;
344 			}
345 			if (old_value < 100 && new_value >= 150) msg_print(Ind, "\374\377GYou learn how to enter an offensive stance (rank I).");
346 			if (old_value < 200 && new_value >= 250) {
347 				msg_print(Ind, "\374\377GYou learn how to enter offensive stance rank II.");
348 				if (p_ptr->combat_stance == 2) p_ptr->combat_stance_power = 1;
349 			}
350 			if (old_value < 400 && new_value >= 400) {
351 				msg_print(Ind, "\374\377GYou learn how to enter offensive stance rank III.");
352 				if (p_ptr->combat_stance == 2) p_ptr->combat_stance_power = 2;
353 			}
354 			break;
355 		case CLASS_PALADIN:
356 			if (old_value < 50 && new_value >= 50) msg_print(Ind, "\374\377GYou learn how to enter a defensive stance (rank I). ('\377gm\377G' key)");
357 			if (old_value < 200 && new_value >= 200) {
358 				msg_print(Ind, "\374\377GYou learn how to enter defensive stance rank II.");
359 				if (p_ptr->combat_stance == 1) p_ptr->combat_stance_power = 1;
360 			}
361 			if (old_value < 350 && new_value >= 350) {
362 				msg_print(Ind, "\374\377GYou learn how to enter defensive stance rank III.");
363 				if (p_ptr->combat_stance == 1) p_ptr->combat_stance_power = 2;
364 			}
365 			if (old_value < 100 && new_value >= 150) msg_print(Ind, "\374\377GYou learn how to enter an offensive stance (rank I).");
366 			if (old_value < 200 && new_value >= 250) {
367 				msg_print(Ind, "\374\377GYou learn how to enter offensive stance rank II.");
368 				if (p_ptr->combat_stance == 2) p_ptr->combat_stance_power = 1;
369 			}
370 			if (old_value < 400 && new_value >= 400) {
371 				msg_print(Ind, "\374\377GYou learn how to enter offensive stance rank III.");
372 				if (p_ptr->combat_stance == 2) p_ptr->combat_stance_power = 2;
373 			}
374 			break;
375 		case CLASS_RANGER:
376 			if (old_value < 50 && new_value >= 100) msg_print(Ind, "\374\377GYou learn how to enter a defensive stance (rank I). ('\377gm\377G' key)");
377 			if (old_value < 150 && new_value >= 200) {
378 				msg_print(Ind, "\374\377GYou learn how to enter defensive stance rank II.");
379 				if (p_ptr->combat_stance == 1) p_ptr->combat_stance_power = 1;
380 			}
381 			if (old_value < 350 && new_value >= 400) {
382 				msg_print(Ind, "\374\377GYou learn how to enter defensive stance rank III.");
383 				if (p_ptr->combat_stance == 1) p_ptr->combat_stance_power = 2;
384 			}
385 			if (old_value < 100 && new_value >= 150) msg_print(Ind, "\374\377GYou learn how to enter an offensive stance (rank I).");
386 			if (old_value < 200 && new_value >= 250) {
387 				msg_print(Ind, "\374\377GYou learn how to enter offensive stance rank II.");
388 				if (p_ptr->combat_stance == 2) p_ptr->combat_stance_power = 1;
389 			}
390 			if (old_value < 400 && new_value >= 400) {
391 				msg_print(Ind, "\374\377GYou learn how to enter offensive stance rank III.");
392 				if (p_ptr->combat_stance == 2) p_ptr->combat_stance_power = 2;
393 			}
394 			break;
395 		}
396 
397 		/* learn royal stances at 45+ if winner */
398 		if (old_value < 450 && new_value >= 450 && p_ptr->total_winner) {
399 			msg_print(Ind, "\374\377GYou learn how to enter Royal Rank combat stances.");
400 			if (p_ptr->combat_stance) p_ptr->combat_stance_power = 3;
401 		}
402 		break;
403 	case SKILL_ARCHERY:
404 		if (old_value < 40 && new_value >= 40)
405 			msg_print(Ind, "\374\377GYou learn the shooting technique 'Flare missile'! (press '\377gm\377G')");
406 		if (old_value < 80 && new_value >= 80)
407 			msg_print(Ind, "\374\377GYou learn the shooting technique 'Precision shot'!");
408 		if (old_value < 100 && new_value >= 100)
409 			msg_print(Ind, "\374\377GYou learn how to create ammunition from bones and rubble!");
410 		if (old_value < 110 && new_value >= 110)
411 			msg_print(Ind, "\374\377GYou got better at recognizing the power of unknown ranged weapons and ammo!");
412 		if (old_value < 160 && new_value >= 160)
413 			msg_print(Ind, "\374\377GYou learn the shooting technique 'Double-shot'!");
414 		if (old_value < 200 && new_value >= 200)
415 			msg_print(Ind, "\374\377GYour ability to create ammunition improved remarkably!");
416 		if (old_value < 250 && new_value >= 250)
417 			msg_print(Ind, "\374\377GYou learn the shooting technique 'Barrage'!");
418 //		if (old_value < 500 && new_value >= 500)
419 //			msg_print(Ind, "\374\377GYour general shooting power gains extra might due to your training!");
420 		break;
421 	case SKILL_COMBAT:
422 		if (old_value < 110 && new_value >= 110) {
423 			msg_print(Ind, "\374\377GYou got better at recognizing the power of unknown weapons.");
424 #if 0
425 		} if (old_value < 310 && new_value >= 310) {
426 			msg_print(Ind, "\374\377GYou got better at recognizing the power of unknown ranged weapons and ammo.");
427 		} if (old_value < 410 && new_value >= 410) {
428 			msg_print(Ind, "\374\377GYou got better at recognizing the power of unknown magical items.");
429 #else /* more true messages: */
430 		} if (old_value < 310 && new_value >= 310) {
431 			if (get_skill(p_ptr, SKILL_ARCHERY) < 11)
432 				msg_print(Ind, "\374\377GYou somewhat recognize the usefulness of unknown ranged weapons and ammo.");
433 		} if (old_value < 410 && new_value >= 410) {
434 			/* message somewhat redudant with classes/other skills which also give
435 			   ok_curse, but seems impractical to sort out really */
436 			msg_print(Ind, "\374\377GYou feel able to sense curses on all types of items.");
437 #endif
438 		}
439 		break;
440 	case SKILL_MAGIC:
441 		if (old_value < 110 && new_value >= 110) msg_print(Ind, "\374\377GYou got better at recognizing the power of unknown magical items.");
442 		break;
443 	case SKILL_EARTH:
444 		if (old_value < 300 && new_value >= 300) {
445 			msg_print(Ind, "\374\377GYou feel able to prevent shards of rock from striking you.");
446 		} if (old_value < 450 && new_value >= 450) {
447 			msg_print(Ind, "\374\377GYou feel able to prevent large masses of rock from striking you.");
448 		}
449 		break;
450 	case SKILL_AIR:
451 		if (old_value < 300 && new_value >= 300) {
452 			msg_print(Ind, "\374\377GYou feel light as a feather.");
453 		} if (old_value < 400 && new_value >= 400) {
454 			msg_print(Ind, "\374\377GYou feel able to breathe within poisoned air.");
455 		} if (old_value < 500 && new_value >= 500) {
456 			msg_print(Ind, "\374\377GYou feel levitating is easy.");
457 		}
458 		break;
459 	case SKILL_WATER:
460 		if (old_value < 300 && new_value >= 300) {
461 			msg_print(Ind, "\374\377GYou feel able to prevent water streams from striking you.");
462 		} if (old_value < 400 && new_value >= 400) {
463 			msg_print(Ind, "\374\377GYou feel able to move through water easily.");
464 		} if (old_value < 500 && new_value >= 500) {
465 			msg_print(Ind, "\374\377GYou feel able to prevent tidal waves from striking you.");
466 		}
467 		break;
468 	case SKILL_FIRE:
469 		if (old_value < 300 && new_value >= 300) {
470 			msg_print(Ind, "\374\377GYou feel able to resist fire easily.");
471 		} if (old_value < 500 && new_value >= 500) {
472 			msg_print(Ind, "\374\377GYou feel that fire cannot harm you anymore.");
473 		}
474 		break;
475 	case SKILL_MANA:
476 		if (old_value < 400 && new_value >= 400) {
477 			msg_print(Ind, "\374\377GYou feel able to defend from mana attacks easily.");
478 		}
479 		break;
480 	case SKILL_CONVEYANCE:
481 		if (old_value < 300 && new_value >= 300 &&
482 		    get_skill(p_ptr, SKILL_UDUN) < 30) {
483 			msg_print(Ind, "\374\377GYou are impervious to feeble teleportation attacks.");
484 		}
485 		break;
486 	case SKILL_DIVINATION:
487 		if (old_value < 500 && new_value >= 500) {
488 			msg_print(Ind, "\374\377GYou find identifying items ridiculously easy.");
489 			identify_pack(Ind);
490 		}
491 		break;
492 	case SKILL_NATURE:
493 		if (old_value < 300 && new_value >= 300) {
494 			msg_print(Ind, "\374\377GYour magic allows you to pass trees and forests easily.");
495 		} if (old_value < 400 && new_value >= 400) {
496 			msg_print(Ind, "\374\377GYour magic allows you to pass water easily.");
497 		}
498 		/* + continuous effect */
499 		break;
500 	case SKILL_MIND:
501 		if (old_value < 300 && new_value >= 300) {
502 			msg_print(Ind, "\374\377GYou feel strong against confusion and hallucinations.");
503 		} if (old_value < 400 && new_value >= 400) {
504 			msg_print(Ind, "\374\377GYou learn to keep hold of your sanity.");
505 		} if (old_value < 500 && new_value >= 500) {
506 			msg_print(Ind, "\374\377GYou learn to keep strong hold of your sanity.");
507 		}
508 		break;
509 	case SKILL_TEMPORAL:
510 		if (old_value < 500 && new_value >= 500) {
511 			msg_print(Ind, "\374\377GYou don't fear time attacks as much anymore.");
512 		}
513 		break;
514 	case SKILL_UDUN:
515 		if (old_value < 300 && new_value >= 300 &&
516 		    get_skill(p_ptr, SKILL_CONVEYANCE) < 30) {
517 			msg_print(Ind, "\374\377GYou are impervious to feeble teleportation attacks.");
518 		} if (old_value < 400 && new_value >= 400) {
519 			msg_print(Ind, "\374\377GYou have strong control over your life force.");
520 		}
521 		break;
522 	case SKILL_META: /* + continuous effect */
523 		break;
524 	case SKILL_HOFFENSE:
525 		if (old_value < 300 && new_value >= 300) {
526 			msg_print(Ind, "\374\377GYou fight against undead with holy wrath.");
527 		} if (old_value < 400 && new_value >= 400) {
528 			msg_print(Ind, "\374\377GYou fight against demons with holy wrath.");
529 		} if (old_value < 500 && new_value >= 500) {
530 			msg_print(Ind, "\374\377GYou fight against evil with holy fury.");
531 		}
532 		break;
533 	case SKILL_HDEFENSE:
534 		if (old_value < 300 && new_value >= 300) {
535 			msg_print(Ind, "\374\377GYou stand fast against undead.");
536 		} if (old_value < 400 && new_value >= 400) {
537 			msg_print(Ind, "\374\377GYou stand fast against demons.");
538 		} if (old_value < 500 && new_value >= 500) {
539 			msg_print(Ind, "\374\377GYou stand fast against evil.");
540 		}
541 		break;
542 	case SKILL_HCURING:
543 		if (old_value < 300 && new_value >= 300) {
544 			msg_print(Ind, "\374\377GYou feel strong against blindness and poison.");
545 		} if (old_value < 400 && new_value >= 400) {
546 			msg_print(Ind, "\374\377GYou feel strong against stun and cuts.");
547 		} if (old_value < 500 && new_value >= 500) {
548 			msg_print(Ind, "\374\377GYou feel strong against hallucination and black breath.");
549 			msg_print(Ind, "\374\377GYour melee attacks inflict greater damage on undead.");
550 			msg_print(Ind, "\374\377GYour soul escapes less quickly on death.");
551 		}
552 		/* + continuous effect */
553                 break;
554 	case SKILL_HSUPPORT:
555 		if (old_value < 400 && new_value >= 400 && p_ptr->prace != RACE_MAIA) {
556 			msg_print(Ind, "\374\377GYou don't feel hunger for worldly food anymore.");
557 		} if (old_value < 500 && new_value >= 500) {
558 			msg_print(Ind, "\374\377GYou feel superior to ancient curses.");
559 		}
560 		break;
561 	case SKILL_SWORD: case SKILL_AXE: case SKILL_BLUNT: case SKILL_POLEARM:
562 		if ((old_value < 250 && new_value >= 250) || (old_value < 500 && new_value >= 500)) {
563 			msg_print(Ind, "\374\377GYour attack speed has become faster due to your training!");
564 		}
565 		break;
566 	case SKILL_SLING:
567 		if ((old_value < 100 && new_value >= 100) || (old_value < 200 && new_value >= 200) ||
568 		    (old_value < 300 && new_value >= 300) || (old_value < 400 && new_value >= 400) || (old_value < 500 && new_value >= 500)) {
569 			msg_print(Ind, "\374\377GYour shooting speed with slings has become faster due to your training!");
570 		}
571 		if (old_value < 500 && new_value >= 500)
572 			msg_print(Ind, "\374\377GYour shooting power with slings gains extra might due to your training!");
573 		break;
574 	case SKILL_BOW:
575 		if ((old_value < 125 && new_value >= 125) || (old_value < 250 && new_value >= 250) ||
576 		    (old_value < 375 && new_value >= 375) || (old_value < 500 && new_value >= 500)) {
577 			msg_print(Ind, "\374\377GYour shooting speed with bows has become faster due to your training!");
578 		}
579 		if (old_value < 500 && new_value >= 500)
580 			msg_print(Ind, "\374\377GYour shooting power with bows gains extra might due to your training!");
581 		break;
582 	case SKILL_XBOW:
583 		if ((old_value < 125 && new_value >= 125) || (old_value < 375 && new_value >= 375)) {
584 			msg_print(Ind, "\374\377GYour shooting power with crossbows gains extra might due to your training!");
585 		}
586 		if ((old_value < 250 && new_value >= 250) || (old_value < 500 && new_value >= 500)) {
587 			msg_print(Ind, "\374\377GYour shooting speed with crossbows has become faster due to your training!");
588 		}
589 		if (old_value < 500 && new_value >= 500)
590 			msg_print(Ind, "\374\377GYour shooting power with crossbows gains extra might due to your training!");
591 		break;
592 /*	case SKILL_SLING:
593 	case SKILL_BOW:
594 	case SKILL_XBOW:
595 */	case SKILL_BOOMERANG:
596 		if ((old_value < 166 && new_value >= 166) || (old_value < 333 && new_value >= 333) ||
597 		    (old_value < 500 && new_value >= 500)) {
598 			msg_print(Ind, "\374\377GYour boomerang throwing speed has become faster due to your training!");
599 		}
600 		break;
601 	case SKILL_AURA_FEAR: if (old_value == 0 && new_value > 0) p_ptr->aura[0] = TRUE; break; /* MAX_AURAS */
602 	case SKILL_AURA_SHIVER: if (old_value == 0 && new_value > 0) p_ptr->aura[1] = TRUE; break;
603 	case SKILL_AURA_DEATH: if (old_value == 0 && new_value > 0) p_ptr->aura[2] = TRUE; break;
604 #if 0 /* obsolete */
605 	case SKILL_DIG:
606 		if (old_value < 300 && new_value >= 300) {
607 			msg_print(Ind, "\374\377GYou've become much better at prospecting.");
608 		}
609 		break;
610 #endif
611 	case SKILL_HEALTH:
612 		if (old_value < 100 && new_value >= 100) p_ptr->sanity_bar = 1;
613 		else if (old_value < 200 && new_value >= 200) p_ptr->sanity_bar = 2;
614 		else if (old_value < 400 && new_value >= 400) p_ptr->sanity_bar = 3;
615 		break;
616 	}
617 }
618 
619 /* Hrm this can be nasty for Sorcery/Antimagic */
620 // void recalc_skills_theory(s16b *invest, s32b *base_val, u16b *base_mod, s32b *bonus)
621 static void increase_related_skills(int Ind, int i, bool quiet)
622 {
623 	player_type *p_ptr = Players[Ind];
624 	int j;
625 
626 	/* Modify related skills */
627 	for (j = 0; j < MAX_SKILLS; j++) {
628 		/* Ignore self */
629 		if (j == i) continue;
630 
631 		/* Exclusive skills */
632 		if (s_info[i].action[j] == SKILL_EXCLUSIVE) {
633 			/* Turn it off */
634 			p_ptr->s_info[j].value = 0;
635 		}
636 
637 		/* Non-exclusive skills */
638 		else {
639 			/* Save previous value */
640 			int old_value = get_skill_scale(p_ptr, j, 500);
641 
642 			/* Increase / decrease with a % */
643 			s32b val = p_ptr->s_info[j].value +
644 				(p_ptr->s_info[j].mod * s_info[i].action[j] / 100);
645 
646 			/* Skill value cannot be negative */
647 			if (val < 0) val = 0;
648 
649 			/* It cannot exceed SKILL_MAX */
650 			if ((p_ptr->s_info[j].flags1 & SKF1_MAX_1) && (val >= 1000)) val = 1000;
651 			else if ((p_ptr->s_info[j].flags1 & SKF1_MAX_10) && (val >= 10000)) val = 10000;
652 			else if ((p_ptr->s_info[j].flags1 & SKF1_MAX_20) && (val >= 20000)) val = 20000;
653 			else if ((p_ptr->s_info[j].flags1 & SKF1_MAX_25) && (val >= 25000)) val = 25000;
654 			else if (val > SKILL_MAX) val = SKILL_MAX;
655 
656 			/* Save the modified value */
657 			p_ptr->s_info[j].value = val;
658 
659 			/* Update the client */
660 			if (!quiet) {
661 				calc_techniques(Ind);
662 				Send_skill_info(Ind, j, TRUE);
663 			}
664 
665 			/* Take care of gained abilities */
666 			if (!quiet) msg_gained_abilities(Ind, old_value, j);
667 		}
668 	}
669 }
670 
671 
672 /*
673  * Advance the skill point of the skill specified by i and
674  * modify related skills
675  * note that we *MUST* send back a skill_info packet
676  */
677 void increase_skill(int Ind, int i, bool quiet)
678 {
679 	player_type *p_ptr = Players[Ind];
680 	int old_value;
681 //	int as, ws, new_value;
682 //	int can_regain;
683 
684 	/* Sanity check - mikaelh */
685 	if (i < 0 || i >= MAX_SKILLS)
686 	{
687 		return;
688 	}
689 
690 	/* No skill points to be allocated */
691 	if (p_ptr->skill_points <= 0) {
692 		if (!quiet) Send_skill_info(Ind, i, TRUE);
693 		return;
694 	}
695 
696 	/* The skill cannot be increased */
697 	if (p_ptr->s_info[i].mod <= 0) {
698 		if (!quiet) Send_skill_info(Ind, i, TRUE);
699 		return;
700 	}
701 
702 	/* The skill is already maxed */
703 	if ((p_ptr->s_info[i].value >= SKILL_MAX) ||
704 	    /* Some extra ability skills don't go over 1 ('1' meaning 'learnt') */
705 	    ((p_ptr->s_info[i].flags1 & SKF1_MAX_1) && (p_ptr->s_info[i].value >= 1000)) ||
706 	    /* Hack: Archery for rangers doesn't go over 10 */
707 	    ((p_ptr->s_info[i].flags1 & SKF1_MAX_10) && (p_ptr->s_info[i].value >= 10000)) ||
708 	    /* unused: doesn't go over 20 */
709 	    ((p_ptr->s_info[i].flags1 & SKF1_MAX_20) && (p_ptr->s_info[i].value >= 20000)) ||
710 	    /* unused: doesn't go over 25 */
711 	    ((p_ptr->s_info[i].flags1 & SKF1_MAX_25) && (p_ptr->s_info[i].value >= 25000)))
712 	{
713 		if (!quiet) Send_skill_info(Ind, i, TRUE);
714 		return;
715 	}
716 
717 	/* Cannot allocate more than player level + 2 levels */
718 	if ((p_ptr->s_info[i].value / SKILL_STEP) >= p_ptr->lev + 2)  /* <- this allows limit breaks at very high step values  -- handled in GET_SKILL now! */
719 //	if ((((p_ptr->s_info[i].value + p_ptr->s_info[i].mod) * 10) / SKILL_STEP) > (p_ptr->lev * 10) + 20)  /* <- this often doesn't allow proper increase to +2 at high step values */
720 	{
721 		if (!quiet) Send_skill_info(Ind, i, TRUE);
722 		return;
723 	}
724 
725 	/* Spend an unallocated skill point */
726 	p_ptr->skill_points--;
727 
728 	/* Save previous value for telling the player about newly gained
729 	   abilities later on. Round down extra-safely (just paranoia). */
730 //	old_value = (p_ptr->s_info[i].value - (p_ptr->s_info[i].value % SKILL_STEP)) / SKILL_STEP;
731 	/*multiply by 10, so we get +1 digit*/
732 	old_value = (p_ptr->s_info[i].value - (p_ptr->s_info[i].value % (SKILL_STEP / 10))) / (SKILL_STEP / 10);
733 
734 	/* Increase the skill */
735 	p_ptr->s_info[i].value += p_ptr->s_info[i].mod;
736 
737 	/* extra abilities cap at 1000 */
738 	if ((p_ptr->s_info[i].flags1 & SKF1_MAX_1) && (p_ptr->s_info[i].value >= 1000))
739 		p_ptr->s_info[i].value = 1000;
740 	/* hack: archery for rangers caps at 10000 */
741 	else if ((p_ptr->s_info[i].flags1 & SKF1_MAX_10) && (p_ptr->s_info[i].value >= 10000))
742 		p_ptr->s_info[i].value = 10000;
743 	/* unused: */
744 	else if ((p_ptr->s_info[i].flags1 & SKF1_MAX_20) && (p_ptr->s_info[i].value >= 20000))
745 		p_ptr->s_info[i].value = 20000;
746 	/* unused: */
747 	else if ((p_ptr->s_info[i].flags1 & SKF1_MAX_25) && (p_ptr->s_info[i].value >= 25000))
748 		p_ptr->s_info[i].value = 25000;
749 	/* cap at SKILL_MAX */
750 	else if (p_ptr->s_info[i].value >= SKILL_MAX) p_ptr->s_info[i].value = SKILL_MAX;
751 
752 	/* Increase the skill */
753 	increase_related_skills(Ind, i, quiet);
754 
755 	/* Update the client */
756 	if (!quiet) {
757 		calc_techniques(Ind);
758 		Send_skill_info(Ind, i, TRUE);
759 	}
760 
761 	/* XXX updating is delayed till player leaves the skill screen */
762 	p_ptr->update |= (PU_SKILL_MOD);
763 
764 	/* also update 'C' character screen live! */
765 	p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA);
766 	p_ptr->redraw |= (PR_SKILLS | PR_PLUSSES | PR_SANITY);
767 
768 	/* Take care of gained abilities */
769 	if (!quiet) msg_gained_abilities(Ind, old_value, i);
770 }
771 /*
772  * Given the name of a skill, returns skill index or -1 if no
773  * such skill is found
774  */
775 s16b find_skill(cptr name)
776 {
777 	u16b i;
778 
779 	/* Scan skill list */
780 //	for (i = 1; i < max_s_idx; i++)
781 	for (i = 0; i < MAX_SKILLS; i++)
782 	{
783 		/* The name matches */
784 		if (streq(s_info[i].name + s_name, name)) return (i);
785 	}
786 
787 	/* No match found */
788 	return (-1);
789 }
790 
791 /* return value by which a skill was auto-modified by related skills
792    instead of real point distribution by the player */
793 static s32b modified_by_related(player_type *p_ptr, int i) {
794 	int j, points;
795 	s32b val = 0, jv, jm;
796 
797 	for (j = 0; j < MAX_SKILLS; j++) {
798 		/* Ignore self */
799 		if (j == i) continue;
800 
801 		/* Ignore skills that aren't modified by related skills */
802 		if ((s_info[j].action[i] != SKILL_EXCLUSIVE) &&
803 		    s_info[j].action[i] &&
804 		    /* hack against oscillation: only take care of increase atm '> 0': */
805 		    (s_info[j].action[i] > 0)) {
806 			/* calc 'manual increase' of the increasing skill by excluding base value 'jv' */
807 			jv = 0; jm = 0;
808 			compute_skills(p_ptr, &jv, &jm, j);
809 			/* calc amount of points the user spent into the increasing skill */
810 //			if (jm)
811 			if (p_ptr->s_info[j].mod)
812 //				points = (p_ptr->s_info[j].value - p_ptr->s_info[j].base_value - modified_by_related(p_ptr, j)) / jm;
813 				points = (p_ptr->s_info[j].value - p_ptr->s_info[j].base_value - modified_by_related(p_ptr, j)) / p_ptr->s_info[j].mod;
814 			else
815 				points = 0;
816 			/* calc and stack up amount of the increase that skill 'i' experienced by that */
817 			val += ((p_ptr->s_info[i].mod * s_info[j].action[i] / 100) * points);
818 
819 			/* Skill value cannot be negative */
820 			if (val < 0) val = 0;
821 			/* It cannot exceed SKILL_MAX */
822 			if (val > SKILL_MAX) val = SKILL_MAX;
823 		}
824 	}
825 
826 	return (val);
827 }
828 
829 /* Free all points spent into a certain skill and make them available again.
830    Note that it is actually impossible under current skill functionality
831    to reconstruct the exact points the user spent on all skills in all cases.
832    Reasons:
833     - If the skill in question for example is a skill that gets
834     increased as a result of increasing another skill, we won't
835     know how many points the player actually invested into it if
836     the skill is maxed out.
837     - Similarly, we wont know how many points to subtract from
838     related skills when we erase a specific skill, if that related
839     skill is maxed, because the player might have spent more points
840     into it than would be required to max it.
841     - Even if we start analyzing all skills, we won't know which one
842     got more 'overspent' points as soon as there are two ore more
843     maximized skills in question.
844    For a complete respec function see respec_skills() below.
845    This function is still possible though, if we agree that it may
846    'optimize' the skill point spending for the users. It can't return
847    more points than the user actually could spend in an 'ideal skill chart'.
848    So it wouldn't hurt really, if we allow this to correct any waste
849    of skill points the user did. - C. Blue
850    update_skills: Change base values and base mods to up-to-date values. */
851 void respec_skill(int Ind, int i, bool update_skill, bool polymorph) {
852 	player_type *p_ptr = Players[Ind];
853 	int j;
854 	s32b v = 0, m = 0; /* base starting skill value, skill modifier */
855 	s32b jv, jm;
856 	s32b real_user_increase = 0, val;
857 	int spent_points;
858 
859 	compute_skills(p_ptr, &v, &m, i);
860 
861 	/* Calculate amount of 'manual increase' to this skill.
862 	   Manual meaning either direct increases or indirect
863 	   increases from related skills. */
864 	real_user_increase = p_ptr->s_info[i].value - p_ptr->s_info[i].base_value;
865 	/* Now get rid of amount of indirect increases we got
866 	   from related skills. */
867 	real_user_increase = real_user_increase - modified_by_related(p_ptr, i); /* recursive */
868 	/* catch overflow cap (example: skill starts at 1.000,
869 	   but is modified by a theoretical total of 50.000 from
870 	   various other skills -> would end at -1.000) */
871 	if (real_user_increase < 0) real_user_increase = 0;
872 	/* calculate skill points spent into this skill */
873 //	spent_points = real_user_increase / m;
874 	if (p_ptr->s_info[i].mod)
875 		spent_points = real_user_increase / p_ptr->s_info[i].mod;
876 	else
877 		spent_points = 0;
878 
879 	/* modify related skills */
880 	for (j = 0; j < MAX_SKILLS; j++) {
881 		/* Ignore self */
882 		if (j == i) continue;
883 
884 		/* Exclusive skills */
885 		if (s_info[i].action[j] == SKILL_EXCLUSIVE) {
886 			jv = 0; jm = 0;
887 			compute_skills(p_ptr, &jv, &jm, j);
888 			/* Turn it on again (!) */
889 //			p_ptr->s_info[j].value = jv;
890 			p_ptr->s_info[j].value = p_ptr->s_info[j].base_value;
891 		} else { /* Non-exclusive skills */
892 			/* Decrease with a % */
893 			val = p_ptr->s_info[j].value -
894 			    ((p_ptr->s_info[j].mod * s_info[i].action[j] / 100) * spent_points);
895 
896 			/* Skill value cannot be negative */
897 			if (val < 0) val = 0;
898 			/* It cannot exceed SKILL_MAX */
899 			if ((p_ptr->s_info[j].flags1 & SKF1_MAX_1) && (val > 1000)) val = 1000;
900 			else if ((p_ptr->s_info[j].flags1 & SKF1_MAX_10) && (val > 10000)) val = 10000;
901 			else if ((p_ptr->s_info[j].flags1 & SKF1_MAX_20) && (val > 10000)) val = 20000;
902 			else if ((p_ptr->s_info[j].flags1 & SKF1_MAX_25) && (val > 10000)) val = 25000;
903 			else if (val > SKILL_MAX) val = SKILL_MAX;
904 
905 			/* Save the modified value */
906 			p_ptr->s_info[j].value = val;
907 
908 			/* Update the client */
909 			Send_skill_info(Ind, j, FALSE);
910 		}
911 	}
912 
913 	/* Remove the points, ie set skill to its starting base value again
914 	   and just add synergy points it got from other related skills */
915 	p_ptr->s_info[i].value -= real_user_increase;
916 	/* Free the points, making them available */
917 	p_ptr->skill_points += spent_points;
918 
919 	if (update_skill) { /* hack: fix skill.mod */
920 		p_ptr->s_info[i].mod = m;
921 		p_ptr->s_info[i].value = v;
922 		p_ptr->s_info[i].base_value = v;
923 	}
924 	if (p_ptr->fruit_bat == 1) fruit_bat_skills(p_ptr);
925 
926 	/* in case we changed mimicry skill */
927 	if (polymorph) do_mimic_change(Ind, 0, TRUE);
928 
929 	/* hack - fix SKILL_STANCE skill */
930 	if (i == SKILL_STANCE && get_skill(p_ptr, SKILL_STANCE)) {
931 		if (p_ptr->max_plv < 50) p_ptr->s_info[SKILL_STANCE].value = p_ptr->max_plv * 1000;
932 		else p_ptr->s_info[SKILL_STANCE].value = 50000;
933 		/* Update the client */
934 		Send_skill_info(Ind, SKILL_STANCE, TRUE);
935 		Send_skill_info(Ind, SKILL_TECHNIQUE, TRUE);
936 	}
937 
938 	/* Update the client */
939 	calc_techniques(Ind);
940 	Send_skill_info(Ind, i, FALSE);
941 	/* XXX updating is delayed till player leaves the skill screen */
942 	p_ptr->update |= (PU_SKILL_MOD);
943 	/* also update 'C' character screen live! */
944 	p_ptr->update |= (PU_BONUS);
945 	p_ptr->redraw |= (PR_SKILLS | PR_PLUSSES);
946 
947 	/* Discard old "save point" for /undoskills command */
948 	memcpy(p_ptr->s_info_old, p_ptr->s_info, MAX_SKILLS * sizeof(skill_player));
949 	p_ptr->skill_points_old = p_ptr->skill_points;
950 	p_ptr->reskill_possible = TRUE;
951 }
952 
953 /* Complete skill-chart reset (full respec) - C. Blue
954    update_skill: Change base value and base mod to up-to-date values. */
955 void respec_skills(int Ind, bool update_skills) {
956 	player_type *p_ptr = Players[Ind];
957 	int i;
958 	s32b v, m; /* base starting skill value, skill modifier */
959 
960 	/* Remove the points, ie set skills to its starting base values again */
961 	for (i = 0; i < MAX_SKILLS; i++) {
962 		v = 0; m = 0;
963 		if (update_skills) {
964 			compute_skills(p_ptr, &v, &m, i);
965 			p_ptr->s_info[i].base_value = v;
966 			p_ptr->s_info[i].value = v;
967 			p_ptr->s_info[i].mod = m;
968 		} else {
969 			p_ptr->s_info[i].value = p_ptr->s_info[i].base_value;
970 		}
971 	}
972 	if (p_ptr->fruit_bat == 1) fruit_bat_skills(p_ptr);
973 	/* Update the client */
974 	for (i = 0; i < MAX_SKILLS; i++) Send_skill_info(Ind, i, FALSE);
975 
976 	/* Calculate amount of skill points that should be
977 	    available to the player depending on his level */
978 	p_ptr->skill_points = (p_ptr->max_plv - 1) * SKILL_NB_BASE;
979 
980 	/* in case we changed mimicry skill */
981 	do_mimic_change(Ind, 0, TRUE);
982 
983 	/* hack - fix SKILL_STANCE skill */
984 	if (get_skill(p_ptr, SKILL_STANCE)) {
985 		if (p_ptr->max_plv < 50) p_ptr->s_info[SKILL_STANCE].value = p_ptr->max_plv * 1000;
986 		else p_ptr->s_info[SKILL_STANCE].value = 50000;
987 		/* Update the client */
988 		Send_skill_info(Ind, SKILL_STANCE, TRUE);
989 		Send_skill_info(Ind, SKILL_TECHNIQUE, TRUE);
990 	}
991 
992 	/* Update the client */
993 	calc_techniques(Ind);
994 	/* XXX updating is delayed till player leaves the skill screen */
995 	p_ptr->update |= (PU_SKILL_MOD);
996 	/* also update 'C' character screen live! */
997 	p_ptr->update |= (PU_BONUS);
998 	p_ptr->redraw |= (PR_SKILLS | PR_PLUSSES);
999 
1000 	/* Discard old "save point" for /undoskills command */
1001 	memcpy(p_ptr->s_info_old, p_ptr->s_info, MAX_SKILLS * sizeof(skill_player));
1002 	p_ptr->skill_points_old = p_ptr->skill_points;
1003 	p_ptr->reskill_possible = TRUE;
1004 }
1005 
1006 /* return amount of points that were invested into a skill */
1007 int invested_skill_points(int Ind, int i) {
1008 	player_type *p_ptr = Players[Ind];
1009 	s32b v = 0, m = 0; /* base starting skill value, skill modifier */
1010 	s32b real_user_increase = 0;
1011 
1012 	compute_skills(p_ptr, &v, &m, i);
1013 
1014 	/* Calculate amount of 'manual increase' to this skill.
1015 	   Manual meaning either direct increases or indirect
1016 	   increases from related skills. */
1017 	real_user_increase = p_ptr->s_info[i].value - p_ptr->s_info[i].base_value;
1018 	/* Now get rid of amount of indirect increases we got
1019 	   from related skills. */
1020 	real_user_increase = real_user_increase - modified_by_related(p_ptr, i); /* recursive */
1021 	/* catch overflow cap (example: skill starts at 1.000,
1022 	   but is modified by a theoretical total of 50.000 from
1023 	   various other skills -> would end at -1.000) */
1024 	if (real_user_increase < 0) real_user_increase = 0;
1025 	/* calculate skill points spent into this skill */
1026 	return(real_user_increase / m);
1027 }
1028 
1029 /* Disable skills that fruit bats could not put to use anyway.
1030    This could be moved to tables.c to form a new type, aka body mod skills
1031    like racial/class skills currently do (applied by compute_skills()). */
1032 void fruit_bat_skills(player_type *p_ptr) {
1033 	p_ptr->s_info[SKILL_MASTERY].value = p_ptr->s_info[SKILL_MASTERY].mod = 0;
1034 	p_ptr->s_info[SKILL_SWORD].value = p_ptr->s_info[SKILL_SWORD].mod = 0;
1035 	p_ptr->s_info[SKILL_CRITS].value = p_ptr->s_info[SKILL_CRITS].mod = 0;
1036 	p_ptr->s_info[SKILL_BLUNT].value = p_ptr->s_info[SKILL_BLUNT].mod = 0;
1037 	p_ptr->s_info[SKILL_AXE].value = p_ptr->s_info[SKILL_AXE].mod = 0;
1038 	p_ptr->s_info[SKILL_POLEARM].value = p_ptr->s_info[SKILL_POLEARM].mod = 0;
1039 	p_ptr->s_info[SKILL_DUAL].value = p_ptr->s_info[SKILL_DUAL].mod = 0;
1040 
1041 	p_ptr->s_info[SKILL_ARCHERY].value = p_ptr->s_info[SKILL_ARCHERY].mod = 0;
1042 	p_ptr->s_info[SKILL_BOW].value = p_ptr->s_info[SKILL_BOW].mod = 0;
1043 	p_ptr->s_info[SKILL_XBOW].value = p_ptr->s_info[SKILL_XBOW].mod = 0;
1044 	p_ptr->s_info[SKILL_SLING].value = p_ptr->s_info[SKILL_SLING].mod = 0;
1045 	p_ptr->s_info[SKILL_BOOMERANG].value = p_ptr->s_info[SKILL_BOOMERANG].mod = 0;
1046 }
1047