Part 48: Stats and skills Part 1: Skills levels

Downloads

Source code
Executable for the map editor - exactly the same as in part 40 (Windows 32bits)
Executable for the game (Windows 32bits)

Before you try to compile the code go to the "Projects" tab on the left menu, select the "Run" settings for your kit,
and set the "Working directory" to the path of "editor\data" for the editor or "game\data" for the game.

Skills

There are 20 skills in the game.
4 of them are visible in the character's sheet: fighter, ninja, priest and wizard.
The 16 others are hidden but each of them is linked to a visible one.

		00: Fighter
		01: Ninja
		02: Priest
		03: Wizard
		
		04: Swing  (Fighter)
		05: Thrust (Fighter)
		06: Club   (Fighter)
		07: Parry  (Fighter)
		
		08: Steal (Ninja)
		09: Fight (Ninja)
		10: Throw (Ninja)
		11: Shoot (Ninja)
		
		12: Identify  (Priest)
		13: Heal      (Priest)
		14: Influence (Priest)
		15: Defense   (Priest)
		
		16: Fire  (Wizard)
		17: Air   (Wizard)
		18: Earth (Wizard)
		19: Water (Wizard)
				
In the code we will define them with an enum in "character.h".

		enum ESkillsNames
		{
			eSkillFighter = 0,
			eSkillNinja,
			eSkillPriest,
			eSkillWizard,
			eSkillSwing,
			eSkillThrust,
			eSkillClub,
			eSkillParry,
			eSkillSteal,
			eSkillFight,
			eSkillThrow,
			eSkillShoot,
			eSkillIdentify,
			eSkillHeal,
			eSkillInfluence,
			eSkillDefend,
			eSkillFire,
			eSkillAir,
			eSkillEarth,
			eSkillWater,
			eSkillCount
		};
				
Each of these skills will contain: At the moment the 2 levels will be equal. We will see the adjustments later.
The fact that hidden skills are linked to visible ones means that every experience gained in an hidden skill adds
up to the corresponding visible skill.
In the code we will store these values in an array of structures in the CCharacter class:

		struct SSkill
		{
			int value;
			int startValue;
			int experience;
		};

		[...]
		SSkill      skills[eSkillCount];
				

Skills levels

For the main skills the first level begins at 500 experience points. Each subsequent level needs the double of XP
of the preceding one.
That gives us this table:

		<nothing>        0 -     499 XP
		Neophite       500 -     999 XP
		Novice        1000 -     199 XP
		Apprentice    2000 -    3999 XP
		Journeyman    4000 -    7999 XP
		Craftsman     8000 -   15999 XP
		Artisan      16000 -   31999 XP
		Adept        32000 -   63999 XP
		Expert       64000 -  127999 XP
		 Master    128000 -  255999 XP
		 Master    256000 -  511999 XP
		 Master    512000 - 1023999 XP
		 Master   1024000 - 2047999 XP
		 Master   2048000 - 4095999 XP
		 Master   4096000 - 8191999 XP
		Archmaster 8192000 -         XP
				
We'll see later how to convert the XP points to a level.
But the hidden skills follow another rule. For them, the first level begins at 250 experience points:

		Level 0:    0 -  249 XP
		Level 1:  250 -  499 XP
		Level 2:  500 -  999 XP
		Level 3: 1000 - 1999 XP
		Level 4: 2000 - 3999 XP
		...
				

Champions starting skills

The champions' database only defines hidden skills levels.

		<!-- ELIJA -->
		<champion firstName="CHAMP_FNAME00" lastName="CHAMP_LNAME00">
			[...]
			<!-- swing:1 thrust:1 club:2 parry:0 -->
			<skill num="4">1</skill>
			<skill num="5">1</skill>
			<skill num="6">2</skill>
			<!-- identify:2 heal:1 influence:4 defend:2 -->
			<skill num="12">2</skill>
			<skill num="13">1</skill>
			<skill num="14">4</skill>
			<skill num="15">2</skill>
		</champion>

		<!-- HALK -->
		<champion firstName="CHAMP_FNAME01" lastName="CHAMP_LNAME01">
			[...]
			<!-- swing:4 thrust:0 club:4 parry:0 -->
			<skill num="4">4</skill>
			<skill num="6">4</skill>
		</champion>
		[...]
				
These values are read in readCharactersDB() and stored in a SChampDBData structure like the other champion's
values.
When we click on a mirror to choose a champion, we copy these values in the CCharacter class in the fromDB() function.
First we copy the hidden skills levels and compute their experience according to the table above.

		// set hidden skills levels
		for (size_t i = 4; i < eSkillCount; ++i)
		{
			skills[i].value = skills[i].startValue = db.skills[i];

			if (skills[i].value == 0)
				skills[i].experience = 0;
			else
				skills[i].experience = 250 << (skills[i].value - 1);
		}
				
Then as the visible skills, are linked to the hidden ones, their experience is the sum of the corresponding hidden
skills XP.

		// set main skills levels
		for (size_t i = 0; i < 4; ++i)
		{
			skills[i].experience = 0;

			for (size_t j = 0; j < 4; ++j)
				skills[i].experience += skills[4 * i + 4 + j].experience;
				
To compute the level of the skill from the experience we will use a loop that divides the experience by 2 until we
reach 500, the value of the first level. This algorithm is equivalent to computing a logarithm.

			skills[i].value = 0;
			int exp = skills[i].experience;

			while (exp >= 500)
			{
				exp /= 2;
				skills[i].value++;
			}

			skills[i].startValue = skills[i].value;
		}
				

Display and save

Now we can display the visible skills in the character's info.


I added the needed texts in "texts.xml":

		<text id="SKILL00">FIGHTER</text>
		<text id="SKILL01">NINJA</text>
		<text id="SKILL02">PRIEST</text>
		<text id="SKILL03">WIZARD</text>
		<text id="LEVEL00">NEOPHYTE</text>
		<text id="LEVEL01">NOVICE</text>
		<text id="LEVEL02">APPRENTICE</text>
		<text id="LEVEL03">JOURNEYMAN</text>
		<text id="LEVEL04">CRAFTSMAN</text>
		<text id="LEVEL05">ARTISAN</text>
		<text id="LEVEL06">ADEPT</text>
		<text id="LEVEL07">EXPERT</text>
		<text id="LEVEL08">a MASTER</text>
		<text id="LEVEL09">b MASTER</text>
		<text id="LEVEL10">c MASTER</text>
		<text id="LEVEL11">d MASTER</text>
		<text id="LEVEL12">e MASTER</text>
		<text id="LEVEL13">f MASTER</text>
		<text id="LEVEL14">ARCHMASTER</text>
				
And the text is simply displayed in CInterface::drawInfos():

		CCharacter::SSkill CInterface::getSkill(int num, CCharacter::ESkillsNames skillName)
		{
			CCharacter* c = &game.characters[num];
			return c->skills[skillName];
		}

		void    CInterface::drawInfos(QImage* image)
		{
			int objType = mouse.mObjectInHand.getType();

			if (objType == 0)
			{
				// character's infos
				QImage  infoBg = fileCache.getImage("gfx/interface/EmptyInfo.png");
				graph2D.drawImage(image, CVec2(80, 85), infoBg);

				int y = 87;
				for (int i = 0; i < 4; ++i)
				{
					CCharacter::SSkill  skill = getSkill(currentChampion, (CCharacter::ESkillsNames)i);

					if (skill.value != 0)
					{
						static char skillStr[16];
						static char levelStr[16];
						static char text[256];

						sprintf(skillStr, "SKILL%02d", i);
						sprintf(levelStr, "LEVEL%02d", skill.value - 1);
						sprintf(text, "%s %s", getText(levelStr).c_str(), getText(skillStr).c_str());
						drawText(image, CVec2(108, y), eFontStandard, text, DEFAULT_TEXT_COLOR);
						y += 7;
					}
				}
				
Finally, there is just a little thing to add.
We need to save the skills when the character's data are saved.
So I added a few lines to CCharacter::save()

		void CCharacter::save(FILE* handle)
		{
			[...]

			// save the skills
			for (int i = 0; i < eSkillCount; ++i)
			{
				fwrite(&skills[i].value, sizeof(skills[i].value), 1, handle);
				fwrite(&skills[i].startValue, sizeof(skills[i].startValue), 1, handle);
				fwrite(&skills[i].experience, sizeof(skills[i].experience), 1, handle);
			}
				
And of course I did the opposite in the load() function.