Page 1 of 1

Learning spells

Posted: Tue Jan 06, 2015 11:35 am
by Delaide
So, quick question.
I have been trying to learn spells based on the ffxi wiki: http://wiki.ffxiclopedia.org/wiki/Blue_ ... _Locations

When I try to learn them at the level stated at minimum, I am never able to. For example, I have been trying to learn Blood Drain since level 11, as shown on that table. My character is maxed on blue skill, but I have had no luck and am now level 13. Still unable to learn it. But I have found there seems to be a break point. For example, the level 18 spells I learned the first time I had them used against me once I hit level 11. Seems to be a level difference of 7. But even this behavior seems to be wrong, because I know you still have a certain percent chance to learn. On retail I remember I was in my level 30's before I learned Bludgeon, and didn't get it on the first try. Now, I know retail behavior has decreasing difficulty the closer you are to the level/skill level. But, it seems on the current server code, it doesn't work as it does in retail. Is there any recommendations on where to look in the code to figure out the learn spell chance? I have tried poking around to no avail. I just want to see if I can calculate it out to see if the darkstar code matches the information on that given website, and am wondering if it becomes basically 100% after a certain point.

Re: Learning spells

Posted: Tue Jan 06, 2015 2:24 pm
by kjLotus
at the moment, the only check is if your level is within 7 levels of the spell and whether the RNG is on your side (it's always a 33% chance)

the exact code is in blueutils::TryLearningSpells (pretty much everything related to blue mages is in blueutils)

Re: Learning spells

Posted: Wed Jan 07, 2015 1:36 am
by Delaide
Got it. So if I want to try improving things with BLU, best to check in blueutils.cpp in DSGame-server project, Source Files, utils, blueutils.cpp

Seems to be this:

Code: Select all

void TryLearningSpells(CCharEntity* PChar, CMobEntity* PMob) {

	if (PMob->m_UsedSkillIds.size() == 0) { // minor optimisation.
		return;
	}

	// prune the learnable blue spells
	std::vector<CSpell*> PLearnableSpells;
	for (std::map<uint16, uint16>::iterator i=PMob->m_UsedSkillIds.begin(); i != PMob->m_UsedSkillIds.end(); ++i) {
		CSpell* PSpell = spell::GetSpellByMonsterSkillId(i->first);
		if (PSpell != NULL) {
			PLearnableSpells.push_back(PSpell);
		}
	}

	if (PLearnableSpells.size() == 0) {
		return;
	}

	std::vector<CCharEntity*> PBlueMages;

	// populate PBlueMages
	if (PChar->PParty != NULL) {
        for (uint8 i = 0; i < PChar->PParty->members.size(); i++) {
			if (PChar->PParty->members[i]->GetMJob() == JOB_BLU && PChar->PParty->members[i]->objtype == TYPE_PC) {
				PBlueMages.push_back((CCharEntity*)PChar->PParty->members[i]);
			}
		}
	}
	else if (PChar->GetMJob() == JOB_BLU) {
		PBlueMages.push_back(PChar);
	}

	// loop through the list of BLUs and see if they can learn.
	for (int i=0; i<PBlueMages.size(); i++) {
		CCharEntity* PBlueMage = PBlueMages[i];

		if (PBlueMage->isDead()) { // too dead to learn
			continue;
		}

		if (distance(PBlueMage->loc.p, PMob->loc.p) > 100) { // too far away to learn
			continue;
		}

		for (int spell=0; spell<PLearnableSpells.size(); spell++) {
			CSpell* PSpell = PLearnableSpells[spell];

			if (charutils::hasSpell(PBlueMage, PSpell->getID())) {
				continue;
			}

			uint8 learnableLevel = PSpell->getJob(JOB_BLU);
			if (learnableLevel > 0 && learnableLevel < PBlueMage->GetMLevel()+7) { // TODO: Use blue magic skill check rather than level
				if (WELL512::irand()%100 < 33) {
					if (charutils::addSpell(PBlueMage, PSpell->getID())) {
						PBlueMage->pushPacket(new CMessageBasicPacket(PBlueMage, PBlueMage, PSpell->getID(), 0, MSGBASIC_LEARNS_SPELL));
						charutils::SaveSpells(PBlueMage);
						PBlueMage->pushPacket(new CCharSpellsPacket(PBlueMage));
					}
				}
				break; // only one attempt at learning a spell, regardless of learn or not.
			}
		}

	}
}
From the looks of it, there is already a note about the level vs. skill:
TODO: Use blue magic skill check rather than level
This seems more consistent with retail, since from my observation it is skill based, not level based. One reason AF can help, by increasing your Blue Magic skill to increase the gap back to 9 levels different, instead of the 7 it becomes at later levels.

Would it be helpful if I did some research on the skill requirements? The level requirements on the wiki are aimed towards having capped Blue Magic skill, so not as helpful. Or we can just do the skill ranges per level, and use a number in that. So it seems this would be where we would be able to increase the player level for skill learning to be similar to retails:

Code: Select all

if (learnableLevel > 0 && learnableLevel < PBlueMage->GetMLevel()+7)
Is this correct? So, we change the +7 to +9, and then a level 13 character can learn a level 22 spell? What would be needed to change that code? Sorry, trying to learn, but may require much hand holding, and confirming if I am correct in my breakdowns or if I am missing something.

So, could we change this to a learnableskilllevel > 0 && learnableskillLevel < PBlueMage->GetBlueMLevel()+X
I know, I can't find the exact variable to do the BLU skill comparison, so I know some of these variable names are invalid. And the X would need to be figured out once the actual skill level is figured out.

Just want to know if I am on the write track, just with some wrong syntax.

Also, looking over the learning spell code, it seems there is nothing to indicate timing. So, once a monster uses an unknown spell that is learnable (Distance, level, etc), it triggers the 1 or 0, correct? So, it doesn't matter how long after the spell is used that the monster dies. Because while it has never been 100% proven on retail that spell/death are linked, there has always been speculation that the sooner after killing the monster, the higher the chance of learning the spell.

Also I notice this note:
// only one attempt at learning a spell, regardless of learn or not.
So, if a monster uses the learnable spell twice in one battle, it does not give a second chance? Sorry, I am unsure if the break completely closes that loop, or just sends the loop back to something else, so a second 0 or 1 may be toggled if the monster uses the spell a second time in the same battle.

Anyways, thanks for your patience, and hopefully I can learn more to help more.

Re: Learning spells

Posted: Wed Jan 07, 2015 1:52 am
by kjLotus
yes, research would be helpful (i don't play blue mage)

yes, if you changed the +7 to +9 a 13 character could learn a level 22 spell, though it's not what the required change would be. i would assume the end product would be a comparison between your skill and the skill you should have if you were the same level as the monster, and that makes the chance to learn, scaling linearly from 0% (at more than 13 levels behind on blue skill) up to some percent at the same skill (and I guess AF would boost that more in some fashion)

no, there's nothing to indicate timing, as unproven theories don't belong in DSP (yes, i know about crafting, i would have said the same thing if i was there when that was written...)

and no, the break is so you don't learn two different spells off one mob (no idea if there are mobs with 2 mobs). doesn't even look like the mob has to use the move to learn it atm, but i could be wrong on that

Re: Learning spells

Posted: Wed Jan 07, 2015 7:28 am
by Delaide
A little more searching:
http://wiki.ffxiclopedia.org/wiki/Category:Blue_Magic
The maximum skill difference to be able to learn a spell is 29 points below the skill level of the spell. Below level 50, one can learn skills as early as 9 levels prior. After level 50, when the increase in Blue Magic Skill from level to level rises from +3 to +5, the level range at which one can learn spells decreases from 9 to 5.
So, I was wrong, not from 9 to 7, but 9 to 5.

So it seems we need need to use a bluemagicskill instead of level for the learning of a spell. So, even if you are within the level range to learn a spell, if you aren't at a high enough skill, you still cannot learn. So 29 points.

From what I have seen, it seems to use the cap at a given level for the spell level cap.

For example, again using blood drain example (Sorry, I know that one right now because that is the one I am trying to learn), it is a level 20 spell.
Cap at level 20 is 63.
63-29=34
At level 10, the skill cap for blue magic is 33, so not learnable.
At level 11, the cap is 36, so it is learnable, but only once you hit 34 skill.

The level cap changes above 50 because at 50, you cap at 153, 3 above 49. At 51, you cap at 158, 5 above 50. So, the actual blue skill cap increases at 50.

I guess one tricky part though might also be attributing the skill value to the blue spells. Maybe another row will need to be added to do the comparison between skill and the spell value? Maybe it isn't an issue with the way it is coded, but we also need to make sure we do not stop skill gains based on that comparison value.

If you are willing to let me try it, I can try to fix it, but I warn you, I might need to ask alot of questions and get some hand holding through the process.

Re: Learning spells

Posted: Wed Jan 07, 2015 2:06 pm
by kjLotus
it's actually not as hard as you think: you can get the level of the blue spell with PSpell->getJob(JOB_BLU) (which is already done in there), and then you can use battleutils::GetMaxSkill(SKILL_BLU, JOB_BLU, level) to get the skill level a BLU would have at that level is. then you'd use PChar->GetSkill(SKILL_BLU) to get their current skill (excluding gear bonuses, etc) and compare those

Re: Learning spells

Posted: Mon Mar 30, 2015 5:51 am
by Delaide
Just want to check my code. Sorry, it has been a while since we discussed, and my lua's still don't seem to be that great, so I want to confirm before I try making a pull for this fix.

Currently, we have:

Code: Select all

if (learnableLevel > 0 && learnableLevel < PBlueMage->GetMLevel()+7) {
We can change to:

Code: Select all

if (learnableLevel > 0 && learnableLevel < PBlueMage->GetMaxSkill(SKILL_BLU, JOB_BLU, level)+29) {
Is this correct? But, since there is no skill level association with blue spells: https://github.com/DarkstarProject/dark ... l_list.sql

How do we make sure that it is correct? Sorry, having problems with the logic. I can do a pull for the code change if you confirm it is correct, but I want to understand what I am doing.

Especially since I don't know if the learnableLevel is still correct to use here. I don't know.

Code: Select all

uint8 learnableLevel = PSpell->getJob(JOB_BLU);;
				if (learnableLevel > 0 && learnableLevel < PBlueMage->GetMLevel()+7) {
So, we set the learnable level as the PSpell, which I assume is the level which we can use the spell. But, learning it isn't based on the usable level, it is based on the skill gap.

So, should it be:

Code: Select all

uint8 learnableLevel = PSpell->GetMaxSkill(SKILL_BLU, JOB_BLU, level);
				if (learnableLevel > 0 && learnableLevel < PBlueMage->GetMaxSkill()+29) {
Hmm, it still doesn't work.

It seems you would need to put in a skill number for each spell. Or, I guess you could do a:
Level for spell use = x
cap for that level = y
if x+29=y then chance to learn = z.

Is this correct? So, I would do the check in a variable:
uint8 spellcap = bluecapatspelluseablelevel (So, I guess need to check spell level, check the cap at this spell level, then use this number for spellcap. But how would I write that?)
uint8 learnableLevel = spellcap+29
if (PlayerBlueSkillLevel >= learnableLevel) then You may try to learn?

Sorry, not sure how I would write the cross check though xD

Re: Learning spells

Posted: Mon Mar 30, 2015 9:47 am
by kjLotus
the max skill at a specific level is battleutils::GetMaxSkill(SKILL_BLU, JOB_BLU, level);

so if you stick learnableLevel in that, that gives you the skill cap of a BLU for that level (which is i think that you're looking for? i forget how blu works)

Re: Learning spells

Posted: Mon Mar 30, 2015 11:02 am
by Delaide
Hmm, okay, I got it.

uint8 learnableLevel = PSpell->GetMaxSkill(SKILL_BLU, JOB_BLU, level);

So, the PSpell is the level of the spell, so it would get the spell level, then get the max skill at that level, correct?

Then, if (GetMaxSkill(SKILL_BLU, JOB_BLU, level)+29 > learnableLevel; { do code

So the code would look like:

Code: Select all

uint8 learnableLevel = PSpell->GetMaxSkill(SKILL_BLU, JOB_BLU, level);
			if (GetMaxSkill(SKILL_BLU, JOB_BLU, level)+29 > learnableLevel; {
That would be the replacement for what is already there?

And for learning a spell, basically, it will be the skill at the level you can use + 29 to calculate if you have a chance to learn.

There would need to be more fixes, but I don't know the math. Basically, as the skill gap between the learners current skill and the spell decrease, there is a higher chance to learn. Even once past the skill level to use though there is a chance, not a guarantee. But, no idea what those chance rates are.