1 /*
2  *
3  *   Copyright (c) 2002, 2003 Johannes Prix
4  *   Copyright (c) 2004-2010 Arthur Huillet
5  *
6  *  This file is part of Freedroid
7  *
8  *  Freedroid 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  *  Freedroid 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 Freedroid; see the file COPYING. If not, write to the
20  *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  *  MA  02111-1307  USA
22  *
23  */
24 
25 /**
26  * This file contains all the functions managing the character attributes
27  * and the character stats.
28  */
29 
30 #define _character_c
31 
32 #include "system.h"
33 
34 #include "defs.h"
35 #include "struct.h"
36 #include "global.h"
37 #include "proto.h"
38 
39 #define BASE_EXP_REQUIRED 500
40 #define BASE_HPMAX 20
41 #define HPMAX_GAIN_PER_LEVEL 5
42 #define HPMAX_GAIN_PER_PHY_POINT 2
43 #define Maxtemp_Gain_Per_CPU_Point 4
44 
45 #define RECHARGE_SPEED_PERCENT_PER_DEX_POINT 0
46 
47 //--------------------
48 // At first we state some geometry constants for where to insert
49 // which character stats in the character screen...
50 //
51 
52 // common x-offsets for texts
53 #define LEFT_TXT_X 25
54 #define RIGHT_TXT_X 195
55 
56 // x-offset for numbers in the "level / experience / next level" box
57 #define LEVEL_NR_X 105
58 // x-offset for numbers in the "health / temperature / stamina" box
59 #define HEALTH_NR_X 125
60 // x-offset for numbers in the "armor / attack / damage" box
61 #define ARMOR_NR_X 260
62 // x-offset for values in the skills box
63 #define SKILLS_VALUE_X 198
64 
65 // y-offsets - left
66 // STR_X_Y - VIT_Y in defs.h
67 #define POINTS_Y 245
68 #define HEALTH_STAT_Y 289
69 #define TEMP_STAT_Y 308
70 #define STAMINA_STAT_Y 327
71 
72 // y-offsets - right
73 #define TOHIT_Y 138
74 #define DAMAGE_Y 158
75 #define DAMRED_Y 178
76 #define MELEE_SKILL_Y 230
77 #define RANGED_SKILL_Y 268
78 #define SPELLCASTING_SKILL_Y 306
79 
80 /**
81  * This function adds any bonuses that might be on the influencers things
82  * concerning ONLY PRIMARY STATS, NOT SECONDARY STATS!
83  */
AddInfluencerItemAttributeBonus(item * BonusItem)84 static void AddInfluencerItemAttributeBonus(item * BonusItem)
85 {
86 	// In case of no item, the thing to do is pretty easy...
87 	//
88 	if (BonusItem->type == (-1))
89 		return;
90 
91 	// Apply the bonuses to the stats of the character.
92 	Me.strength += BonusItem->bonus_to_str + BonusItem->bonus_to_all_attributes;
93 	Me.dexterity += BonusItem->bonus_to_dex + BonusItem->bonus_to_all_attributes;
94 	Me.cooling += BonusItem->bonus_to_cooling + BonusItem->bonus_to_all_attributes;
95 	Me.physique += BonusItem->bonus_to_physique + BonusItem->bonus_to_all_attributes;
96 
97 };				// void AddInfluencerItemAttributeBonus( item* BonusItem )
98 
99 /**
100  * This function adds any bonuses that might be on the influencers things
101  * concerning ONLY SECONDARY STATS, NOT PRIMARY STATS!
102  */
AddInfluencerItemSecondaryBonus(item * BonusItem)103 static void AddInfluencerItemSecondaryBonus(item * BonusItem)
104 {
105 	// In case of no item, the thing to do is pretty easy...
106 	//
107 	if (BonusItem->type == (-1))
108 		return;
109 
110 	// Apply the bonuses to the stats of the character.
111 	Me.to_hit += BonusItem->bonus_to_attack;
112 	Me.maxenergy += BonusItem->bonus_to_health_points;
113 	Me.health_recovery_rate += BonusItem->bonus_to_health_recovery;
114 	Me.cooling_rate += BonusItem->bonus_to_cooling_rate;
115 
116 	// Apply bonuses to the special abilities of the character.
117 	Me.slowing_melee_targets += BonusItem->bonus_to_slow_enemy;
118 	Me.paralyzing_melee_targets += BonusItem->bonus_to_paralyze_enemy;
119 	Me.light_bonus_from_tux += BonusItem->bonus_to_light_radius;
120 	Me.experience_factor += BonusItem->bonus_to_experience_gain / 100.0;
121 }
122 
123 /**
124  * \brief Get the experience required for next level.
125  * \param level Current level.
126  */
get_experience_required(int lvl)127 unsigned int get_experience_required(int lvl)
128 {
129 	if (lvl < 0)
130 		return 0;
131 	return BASE_EXP_REQUIRED * (lvl * lvl) + BASE_EXP_REQUIRED;
132 }
133 
134 /**
135  * \brief Check if Tux has reached a new experience level.
136  */
check_for_new_experience_level_reached()137 static void check_for_new_experience_level_reached()
138 {
139 	unsigned int exp_required = get_experience_required(Me.exp_level);
140 
141 	if (Me.Experience >= exp_required) {
142 		Me.exp_level++;
143 		Me.points_to_distribute += 5;
144 
145 		// When a droid reaches a new experience level, all health and
146 		// force are restored to full this one time no longer.  Gothic
147 		// rulez more than Diablo rulez.
148 		//
149 		// Me .energy = Me .maxenergy ;
150 		// Me .mana   = Me .maxmana   ;
151 
152 		// Also when a new level is reached, we will display a big message
153 		// right over the combat window.
154 		//
155 		SetNewBigScreenMessage(_("Level Gained!"));
156 		Takeover_Game_Won_Sound();
157 	}
158 };				// void check_for_new_experience_level_reached ( )
159 
160 /**
161  * Update all primary stats.
162  */
update_all_primary_stats()163 void update_all_primary_stats()
164 {
165 	int i;
166 
167 	// Now we base PRIMARY stats
168 	//
169 	Me.strength = Me.base_strength;
170 	Me.dexterity = Me.base_dexterity;
171 	Me.cooling = Me.base_cooling;
172 	Me.physique = Me.base_physique;
173 
174 	// Now we add all bonuses to the influencers PRIMARY stats
175 	//
176 	AddInfluencerItemAttributeBonus(&Me.armour_item);
177 	AddInfluencerItemAttributeBonus(&Me.weapon_item);
178 	AddInfluencerItemAttributeBonus(&Me.drive_item);
179 	AddInfluencerItemAttributeBonus(&Me.shield_item);
180 	AddInfluencerItemAttributeBonus(&Me.special_item);
181 
182 	// Maybe there is some boost from a potion or magic spell going on right
183 	// now...
184 	//
185 	if (Me.dexterity_bonus_end_date > Me.current_game_date)
186 		Me.dexterity += Me.current_dexterity_bonus;
187 	if (Me.power_bonus_end_date > Me.current_game_date)
188 		Me.strength += Me.current_power_bonus;
189 
190 	// Unequip items whose requirements aren't met anymore.
191 	// This needs to be done last so that temporary bonuses, for example from
192 	// strength and dexterity capsules, allow the player to keep items equipped.
193 	item *equipment[5] = { &Me.armour_item, &Me.weapon_item, &Me.drive_item,
194 		&Me.shield_item, &Me.special_item };
195 	for (i = 0; i < 5; i++) {
196 		if (equipment[i]->type == -1) {
197 			continue;
198 		}
199 		if (!ItemUsageRequirementsMet(equipment[i], FALSE)) {
200 			give_item(equipment[i]);
201 		}
202 	}
203 
204 	// Ensure the effective primary stats never drops below 1
205 	if (Me.strength < 1)
206 		Me.strength = 1;
207 	if (Me.dexterity < 1)
208 		Me.dexterity = 1;
209 	if (Me.cooling < 1)
210 		Me.cooling = 1;
211 	if (Me.physique < 1)
212 		Me.physique = 1;
213 }
214 
215 /**
216  * This function computes secondary stats (i.e. chances for success or
217  * getting hit and the like) using ONLY THE PRIMARY STATS.  Bonuses from
218  * current 'magic' modifiers from equipped items will be applied somewhere
219  * else.
220  */
update_secondary_stats_from_primary_stats()221 static void update_secondary_stats_from_primary_stats()
222 {
223 	// The chance that this player character will score a hit on an enemy
224 	//
225 	Me.to_hit = 100 - 40 * exp(- 0.03 * Me.dexterity);
226 
227 	// How many life points can this character acquire currently
228 	//
229 	Me.maxenergy = BASE_HPMAX + (Me.exp_level) * HPMAX_GAIN_PER_LEVEL
230                                   + (Me.physique) * HPMAX_GAIN_PER_PHY_POINT;
231 
232 	// The maximum mana value computed from the primary stats
233 	//
234 	Me.max_temperature = (Me.cooling) * Maxtemp_Gain_Per_CPU_Point;
235 
236 	// How long can this character run until he must take a break and
237 	// walk a bit
238 	//
239 	Me.max_running_power = (Me.strength) + (Me.dexterity) + (Me.physique) + Me.running_power_bonus;
240 
241 	// base regeneration speed set to 0.2 points per second
242 	Me.health_recovery_rate = 0.2;
243 	Me.cooling_rate = 1.0;
244 };				// void update_secondary_stats_from_primary_stats ( )
245 
246 /**
247  * Now we compute the possible damage the player character can do.
248  * The damage value of course depends on the weapon type that the
249  * character is using.  And depending on the weapon type (melee or
250  * ranged weapon) some additional attributes will also play a role.
251  */
update_damage_tux_can_do()252 static void update_damage_tux_can_do()
253 {
254 	if (Me.weapon_item.type != (-1)) {
255 		if (ItemMap[Me.weapon_item.type].weapon_is_melee != 0) {
256 			// Damage modifier in case of MELEE WEAPON is computed:
257 			// weapon's modifier * (100+strength)%
258 			//
259 			Me.base_damage = Me.weapon_item.damage * (Me.strength + 100.0) / 100.0;
260 
261 			Me.damage_modifier = Me.weapon_item.damage_modifier * (Me.strength + 100.0) / 100.0;
262 
263 			// Damage AND damage modifier a modified by additional melee weapon
264 			// skill:  A multiplier is applied!
265 			//
266 			Me.damage_modifier *= MeleeDamageMultiplierTable[Me.melee_weapon_skill];
267 			Me.base_damage *= MeleeDamageMultiplierTable[Me.melee_weapon_skill];
268 		} else {
269 			// Damage modifier in case of RANGED WEAPON is computed:
270 			// weapon's modifier * (100+dexterity)%
271 			//
272 			Me.base_damage = Me.weapon_item.damage * (Me.dexterity + 100.0) / 100.0;
273 			Me.damage_modifier = Me.weapon_item.damage_modifier * (Me.dexterity + 100.0) / 100.0;
274 
275 			// Damage AND damage modifier a modified by additional ranged weapon
276 			// skill:  A multiplier is applied!
277 			//
278 			Me.damage_modifier *= RangedDamageMultiplierTable[Me.ranged_weapon_skill];
279 			Me.base_damage *= RangedDamageMultiplierTable[Me.ranged_weapon_skill];
280 		}
281 	} else {
282 		// In case of no weapon equipped at all, we initialize
283 		// the damage values with some simple numbers.  Currently
284 		// strength and dexterity play NO ROLE in weaponless combat.
285 		// Maybe that should be changed for something more suitable
286 		// at some point...
287 		//
288 		Me.base_damage = 0;
289 		Me.damage_modifier = 1;
290 	}
291 };				// void update_damage_tux_can_do ( )
292 
293 /**
294  * This function should re-compute all character stats according to the
295  * currently equipped items and currently distributed stats points.
296  */
UpdateAllCharacterStats()297 void UpdateAllCharacterStats()
298 {
299 	// Maybe the influencer has reached a new experience level?
300 	// Let's check this...
301 	//
302 	check_for_new_experience_level_reached();
303 
304 	// The primary status must be computed/updated first, because
305 	// the secondary status (chances and the like) will depend on
306 	// them...
307 	//
308 	update_all_primary_stats();
309 
310 	// At this point we know, that the primary stats of the influencer
311 	// have been fully computed.  So that means, that finally we can compute
312 	// all base SECONDARY stats, that are dependent upon the influencer primary
313 	// stats.  Once we are done with that, the modifiers to the secondary
314 	// stats can be applied as well.
315 	//
316 	update_secondary_stats_from_primary_stats();
317 
318 	// Now we compute the possible damage the player character can do.
319 	// The damage value of course depends on the weapon type that the
320 	// character is using.  And depending on the weapon type (melee or
321 	// ranged weapon) some additional attributes will also play a role.
322 	//
323 	update_damage_tux_can_do();
324 
325 	// Update tux armour class
326 	//
327 	update_player_armor_class();
328 
329 	// So at this point we can finally apply all the modifiers to the influencers
330 	// SECONDARY stats due to 'magical' items and spells and the like
331 	//
332 	Me.slowing_melee_targets = 0;
333 	Me.paralyzing_melee_targets = 0;
334 	Me.light_bonus_from_tux = 0;
335 	Me.experience_factor = 1.0;
336 	AddInfluencerItemSecondaryBonus(&Me.armour_item);
337 	AddInfluencerItemSecondaryBonus(&Me.weapon_item);
338 	AddInfluencerItemSecondaryBonus(&Me.drive_item);
339 	AddInfluencerItemSecondaryBonus(&Me.shield_item);
340 	AddInfluencerItemSecondaryBonus(&Me.special_item);
341 
342 	// Add light bonus if necessary
343 	if (Me.light_bonus_end_date > Me.current_game_date) {
344 		// Fade out the bonus in the last second
345 		float ratio = min(1.0, Me.light_bonus_end_date - Me.current_game_date);
346 		Me.light_bonus_from_tux += 5 * ratio;
347 	}
348 
349 	// Check player's health, temperature and stamina
350 	if (Me.energy > Me.maxenergy)
351 		Me.energy = Me.maxenergy;
352 	if (Me.temperature < 0)
353 		Me.temperature = 0;
354 	if (Me.running_power > Me.max_running_power)
355 		Me.running_power = Me.max_running_power;
356 
357 }
358 
359 /**
360  * Now we print out the current skill levels in hacking skill,
361  * spellcasting, melee combat, ranged weapon combat and repairing things
362  */
show_character_screen_skills()363 static void show_character_screen_skills()
364 {
365 
366 	// We add some security against skill values out of allowed
367 	// bounds.
368 	//
369 	if ((Me.melee_weapon_skill < 0) || (Me.melee_weapon_skill >= NUMBER_OF_SKILL_LEVELS)) {
370 		fprintf(stderr, "\nmelee_weapon_skill: %d.", Me.melee_weapon_skill);
371 		error_message(__FUNCTION__, "\
372 Error: melee weapon skill seems out of bounds.", PLEASE_INFORM | IS_FATAL);
373 	}
374 	set_current_font(Messagestat_Font);
375 	display_text(_("Melee"), RIGHT_TXT_X + CharacterRect.x, MELEE_SKILL_Y + CharacterRect.y, &CharacterRect, 1.0);
376 	set_current_font(Messagevar_Font);
377 	display_text(_(AllSkillTexts[Me.melee_weapon_skill]),
378 		    SKILLS_VALUE_X + CharacterRect.x, MELEE_SKILL_Y + 17 + CharacterRect.y, &CharacterRect, 1.0);
379 
380 	// We add some security against skill values out of allowed
381 	// bounds.
382 	//
383 	if ((Me.ranged_weapon_skill < 0) || (Me.ranged_weapon_skill >= NUMBER_OF_SKILL_LEVELS)) {
384 		fprintf(stderr, "\nranged_weapon_skill: %d.", Me.ranged_weapon_skill);
385 		error_message(__FUNCTION__, "\
386 Error: ranged weapon skill seems out of bounds.", PLEASE_INFORM | IS_FATAL);
387 	}
388 	set_current_font(Messagestat_Font);
389 	display_text(_("Ranged"), RIGHT_TXT_X + CharacterRect.x, RANGED_SKILL_Y + CharacterRect.y, &CharacterRect, 1.0);
390 	set_current_font(Messagevar_Font);
391 	display_text(_(AllSkillTexts[Me.ranged_weapon_skill]),
392 		    SKILLS_VALUE_X + CharacterRect.x, RANGED_SKILL_Y + 17 + CharacterRect.y, &CharacterRect, 1.0);
393 
394 	// We add some security against skill values out of allowed
395 	// bounds.
396 	//
397 	if ((Me.spellcasting_skill < 0) || (Me.spellcasting_skill >= NUMBER_OF_SKILL_LEVELS)) {
398 		fprintf(stderr, "\nProgramming_Skill: %d.", Me.spellcasting_skill);
399 		error_message(__FUNCTION__, "\
400 Error: Programming_Skill skill seems out of bounds.", PLEASE_INFORM | IS_FATAL);
401 	}
402 	set_current_font(Messagestat_Font);
403 	display_text(_("Programming"), RIGHT_TXT_X + CharacterRect.x, SPELLCASTING_SKILL_Y + CharacterRect.y, &CharacterRect, 1.0);
404 	set_current_font(Messagevar_Font);
405 	display_text(_(AllSkillTexts[Me.spellcasting_skill]),
406 		    SKILLS_VALUE_X + CharacterRect.x, SPELLCASTING_SKILL_Y + 17 + CharacterRect.y, &CharacterRect, 1.0);
407 
408 };				// void show_character_screen_skills ( )
409 
410 /**
411  * This function displays the character screen.
412  */
ShowCharacterScreen()413 void ShowCharacterScreen()
414 {
415 	char CharText[1000];
416 
417 	DebugPrintf(2, "\n%s(): Function call confirmed.", __FUNCTION__);
418 
419 	// If the log is not set to visible right now, we do not need to
420 	// do anything more, but to restore the usual user rectangle size
421 	// back to normal and to return...
422 	//
423 	if (GameConfig.CharacterScreen_Visible == FALSE)
424 		return;
425 
426 	set_current_font(Messagestat_Font);
427 
428 	// We define the right side of the user screen as the rectangle
429 	// for our inventory screen.
430 	//
431 	CharacterRect.x = GameConfig.screen_width - CHARACTERRECT_W;
432 	CharacterRect.y = 0;
433 	CharacterRect.w = CHARACTERRECT_W;
434 	CharacterRect.h = CHARACTERRECT_H;
435 
436 	blit_background("character.png");
437 
438 	set_current_font(Blue_Font);
439 
440 	set_current_font(Menu_Font);
441 	display_text(Me.character_name, 20 + CharacterRect.x, 12 + CharacterRect.y, &CharacterRect, 1.0);
442 
443 	set_current_font(Messagestat_Font);
444 	display_text(_("Level"), LEFT_TXT_X + CharacterRect.x, 73 + CharacterRect.y, &CharacterRect, 1.0);
445 	set_current_font(Messagevar_Font);
446 	sprintf(CharText, "%d", Me.exp_level);
447 	display_text(CharText, LEVEL_NR_X + CharacterRect.x, 73 + CharacterRect.y, &CharacterRect, 1.0);
448 
449 	set_current_font(Messagestat_Font);
450 	display_text(_("Experience"), LEFT_TXT_X + CharacterRect.x, 89 + CharacterRect.y, &CharacterRect, 1.0);
451 	set_current_font(Messagevar_Font);
452 	sprintf(CharText, "%u", Me.Experience);
453 	display_text(CharText, LEVEL_NR_X + CharacterRect.x, 89 + CharacterRect.y, &CharacterRect, 1.0);
454 
455 	set_current_font(Messagestat_Font);
456 	display_text(_("Next level"), LEFT_TXT_X + CharacterRect.x, 107 + CharacterRect.y, &CharacterRect, 1.0);
457 	set_current_font(Messagevar_Font);
458 	sprintf(CharText, "%u", get_experience_required(Me.exp_level));
459 	display_text(CharText, LEVEL_NR_X + CharacterRect.x, 107 + CharacterRect.y, &CharacterRect, 1.0);
460 
461 	set_current_font(Messagestat_Font);
462 	display_text(_("Circuits"), RIGHT_TXT_X + CharacterRect.x, 71 + CharacterRect.y, &CharacterRect, 1.0);
463 	set_current_font(Messagevar_Font);
464 	sprintf(CharText, "%6u", Me.Gold);
465 	display_text(CharText, 240 + CharacterRect.x, 71 + CharacterRect.y, &CharacterRect, 1.0);
466 
467 	set_current_font(Messagestat_Font);
468 	display_text(_("Strength"), LEFT_TXT_X + CharacterRect.x, STR_Y + CharacterRect.y, &CharacterRect, 1.0);
469 	set_current_font(Messagevar_Font);
470 	sprintf(CharText, "%d", Me.strength);
471 	if (Me.strength != Me.base_strength)
472 		sprintf(CharText + strlen(CharText), " (%+d)", Me.strength - Me.base_strength);
473 	display_text(CharText, STR_X + CharacterRect.x, STR_Y + CharacterRect.y, &CharacterRect, 1.0);
474 
475 	set_current_font(Messagestat_Font);
476 	display_text(_("Dexterity"), LEFT_TXT_X + CharacterRect.x, DEX_Y + CharacterRect.y, &CharacterRect, 1.0);
477 	set_current_font(Messagevar_Font);
478 	sprintf(CharText, "%d", Me.dexterity);
479 	if (Me.dexterity != Me.base_dexterity)
480 		sprintf(CharText + strlen(CharText), " (%+d)", Me.dexterity - Me.base_dexterity);
481 	display_text(CharText, STR_X + CharacterRect.x, DEX_Y + CharacterRect.y, &CharacterRect, 1.0);
482 
483 	set_current_font(Messagestat_Font);
484 	display_text(_("Physique"), LEFT_TXT_X + CharacterRect.x, VIT_Y + CharacterRect.y, &CharacterRect, 1.0);
485 	set_current_font(Messagevar_Font);
486 	sprintf(CharText, "%d", Me.physique);
487 	if (Me.physique != Me.base_physique)
488 		sprintf(CharText + strlen(CharText), " (%+d)", Me.physique - Me.base_physique);
489 	display_text(CharText, STR_X + CharacterRect.x, VIT_Y + CharacterRect.y, &CharacterRect, 1.0);
490 
491 	set_current_font(Messagestat_Font);
492 	display_text(_("Cooling"), LEFT_TXT_X + CharacterRect.x, MAG_Y + CharacterRect.y, &CharacterRect, 1.0);
493 	set_current_font(Messagevar_Font);
494 	sprintf(CharText, "%d", Me.cooling);
495 	if (Me.cooling != Me.base_cooling)
496 		sprintf(CharText + strlen(CharText), " (%+d)", Me.cooling - Me.base_cooling);
497 	display_text(CharText, STR_X + CharacterRect.x, MAG_Y + CharacterRect.y, &CharacterRect, 1.0);
498 
499 	SDL_Rect tmprect = CharacterRect;
500 	set_current_font(Messagestat_Font);
501 	display_text(_("Training points"), LEFT_TXT_X + CharacterRect.x, POINTS_Y + CharacterRect.y, &tmprect, 1.0);
502 	set_current_font(Messagevar_Font);
503 	sprintf(CharText, "%d", Me.points_to_distribute);
504 	display_text(CharText, 155 + CharacterRect.x, POINTS_Y + CharacterRect.y, &CharacterRect, 1.0);
505 
506 	set_current_font(Messagestat_Font);
507 	display_text(_("Health"), LEFT_TXT_X + CharacterRect.x, HEALTH_STAT_Y + CharacterRect.y, &tmprect, 1.0);
508 	set_current_font(Messagevar_Font);
509 	sprintf(CharText, "%d/%d", (int)Me.energy, (int)Me.maxenergy);
510 	display_text(CharText, HEALTH_NR_X + CharacterRect.x, HEALTH_STAT_Y + CharacterRect.y, &CharacterRect, 1.0);
511 
512 	set_current_font(Messagestat_Font);
513 	display_text(_("Temperature"), LEFT_TXT_X + CharacterRect.x, TEMP_STAT_Y + CharacterRect.y, &tmprect, 1.0);
514 	set_current_font(Messagevar_Font);
515 	sprintf(CharText, "%d/%d", (int)Me.temperature, (int)Me.max_temperature);
516 	display_text(CharText, HEALTH_NR_X + CharacterRect.x, TEMP_STAT_Y + CharacterRect.y, &CharacterRect, 1.0);
517 
518 	set_current_font(Messagestat_Font);
519 	display_text(_("Stamina"), LEFT_TXT_X + CharacterRect.x, STAMINA_STAT_Y + CharacterRect.y, &tmprect, 1.0);
520 	set_current_font(Messagevar_Font);
521 	sprintf(CharText, "%d/%d", (int)Me.running_power, (int)Me.max_running_power);
522 	display_text(CharText, HEALTH_NR_X + CharacterRect.x, STAMINA_STAT_Y + CharacterRect.y, &CharacterRect, 1.0);
523 
524 	set_current_font(Messagestat_Font);
525 	display_text(_("Attack"), RIGHT_TXT_X + CharacterRect.x, TOHIT_Y + CharacterRect.y, &CharacterRect, 1.0);
526 	set_current_font(Messagevar_Font);
527 	sprintf(CharText, "%d%%", (int)Me.to_hit);
528 	display_text(CharText, ARMOR_NR_X + CharacterRect.x, TOHIT_Y + CharacterRect.y, &CharacterRect, 1.0);
529 
530 	set_current_font(Messagestat_Font);
531 	display_text(_("Damage"), RIGHT_TXT_X + CharacterRect.x, DAMAGE_Y + CharacterRect.y, &CharacterRect, 1.0);
532 	set_current_font(Messagevar_Font);
533 
534 	// Display range of damage, or a single value if there is no range
535 	if (Me.damage_modifier)
536 		sprintf(CharText, "%d-%d", (int)Me.base_damage, (int)Me.base_damage + (int)Me.damage_modifier);
537 	else
538 		sprintf(CharText, "%d", (int)Me.base_damage);
539 
540 	display_text(CharText, ARMOR_NR_X + CharacterRect.x, DAMAGE_Y + CharacterRect.y, &CharacterRect, 1.0);
541 
542 	set_current_font(Messagestat_Font);
543 	display_text(_("Armor"), RIGHT_TXT_X + CharacterRect.x, DAMRED_Y + CharacterRect.y, &CharacterRect, 1.0);
544 	set_current_font(Messagevar_Font);
545 	sprintf(CharText, "%d", (int)Me.armor_class);
546 	display_text(CharText, ARMOR_NR_X + CharacterRect.x, DAMRED_Y + CharacterRect.y, &CharacterRect, 1.0);
547 
548 	// Now we print out the current skill levels in hacking skill,
549 	// spellcasting, melee combat, ranged weapon combat and repairing things
550 	//
551 	show_character_screen_skills();
552 	if (Me.points_to_distribute > 0) {
553 		ShowGenericButtonFromList(MORE_STR_BUTTON);
554 		ShowGenericButtonFromList(MORE_DEX_BUTTON);
555 		ShowGenericButtonFromList(MORE_VIT_BUTTON);
556 		ShowGenericButtonFromList(MORE_MAG_BUTTON);
557 	}
558 };				//ShowCharacterScreen ( )
559 
560 /**
561  * This function handles input for the character screen.
562  */
HandleCharacterScreen(void)563 void HandleCharacterScreen(void)
564 {
565 
566 	if (!GameConfig.CharacterScreen_Visible)
567 		return;
568 
569 	if (Me.points_to_distribute > 0) {
570 		if (MouseCursorIsOnButton(MORE_STR_BUTTON, GetMousePos_x(), GetMousePos_y()) && MouseLeftClicked()) {
571 			Me.base_strength++;
572 			Me.points_to_distribute--;
573 		}
574 		if (MouseCursorIsOnButton(MORE_DEX_BUTTON, GetMousePos_x(), GetMousePos_y()) && MouseLeftClicked()) {
575 			Me.base_dexterity++;
576 			Me.points_to_distribute--;
577 		}
578 		if (MouseCursorIsOnButton(MORE_MAG_BUTTON, GetMousePos_x(), GetMousePos_y()) && MouseLeftClicked()) {
579 			Me.base_cooling++;
580 			Me.points_to_distribute--;
581 		}
582 		if (MouseCursorIsOnButton(MORE_VIT_BUTTON, GetMousePos_x(), GetMousePos_y()) && MouseLeftClicked()) {
583 			Me.base_physique++;
584 			Me.points_to_distribute--;
585 		}
586 
587 	}
588 
589 };				// HandleCharacterScreen ( void )
590 
591 #undef _character_c
592