This has been a request that I think everyone has made at one point or another, but I initially looked at it and said "too complicated". Well, today I decided to finally code the thing.
Basically, this is an adjustment to the Reset NPC that gives you the option to only reset your current class, leaving your previous classes untouched.
This requires code changes to support it, and you'll have to recompile your server afterwards. A quick caveat, though this includes code to support third and fourth classes (for Renewal), that code is untested. It's similar to the second class logic so it should work, but if you have some kind of weird issue it's probably there.
In pc.cpp, find the pc_resetskill method
Find this line:
int i, skill_point=0;
Replace it with this:
int i, j, k, l, baseClass, secondTransClass, thirdClass, currentClass, skid, skill_point = 0;
bool previousClassSkill, isSecondClass = false, isThirdClass = false, isFourthClass = false;
Find this line:
if( flag&4 && (sd->class_&MAPID_UPPERMASK) != MAPID_BARDDANCER )
return 0;
Insert this below it (don't replace the code above). If your server has fourth classes, uncomment that line.
baseClass = pc_mapid2jobid(sd->class_ & MAPID_BASEMASK, sd->status.sex);
secondTransClass = pc_mapid2jobid(sd->class_ & MAPID_UPPERMASK | JOBL_UPPER, sd->status.sex);
thirdClass = pc_mapid2jobid(sd->class_ | JOBL_THIRD, sd->status.sex);
currentClass = pc_mapid2jobid(sd->class_, sd->status.sex);
if ((sd->class_ & MAPID_BASEMASK) != sd->class_ && (sd->class_ & MAPID_BASEMASK | JOBL_UPPER) != sd->class_ && (sd->class_ & MAPID_BASEMASK | JOBL_BABY) != sd->class_) isSecondClass = true;
if ((sd->class_ | JOBL_THIRD) == sd->class_ && (sd->class_ & MAPID_BASEMASK) != sd->class_ && (sd->class_ & MAPID_UPPERMASK) != sd->class_) isThirdClass = true;
//if ((sd->class_ | JOBL_FOURTH) == sd->class_ && (sd->class_ & MAPID_BASEMASK) != sd->class_ && (sd->class_ & MAPID_UPPERMASK) != sd->class_ && (sd->class_ | JOBL_THIRD) != sd->class_) isFourthClass = true;
currentClass = pc_class2idx(currentClass);
baseClass = pc_class2idx(baseClass);
secondTransClass = pc_class2idx(secondTransClass);
thirdClass = pc_class2idx(thirdClass);
Find this line
if (lv == 0 || skill_id == 0)
continue;
What you need to add next depends on your version of eathena. If you are on latest, which has the new skill tree, add this below that line:
if (flag & 16) {
previousClassSkill = false;
if (baseClass != currentClass) {
std::shared_ptr<s_skill_tree> firstTree = skill_tree_db.find(baseclass);
if (tree != nullptr && !tree->skills.empty()) {
for (const auto &it : tree->skills) {
skid = skill_get_index(it.first);
if (skid == skill_id) previousClassSkill = true;
}
}
}
if (isThirdClass) {
std::shared_ptr<s_skill_tree> secondTree = skill_tree_db.find(secondTransClass);
if (tree != nullptr && !tree->skills.empty()) {
for (const auto &it : tree->skills) {
skid = skill_get_index(it.first);
if (skid == skill_id) previousClassSkill = true;
}
}
}
if (isFourthClass) {
std::shared_ptr<s_skill_tree> thirdTree = skill_tree_db.find(thirdClass);
if (tree != nullptr && !tree->skills.empty()) {
for (const auto &it : tree->skills) {
skid = skill_get_index(it.first);
if (skid == skill_id) previousClassSkill = true;
}
}
}
}
If you are on an older version, which has the old form of skill_tree, add this below that line instead:
if (flag & 16) {
previousClassSkill = false;
if (baseClass != currentClass) {
for (j = 0; j < MAX_SKILL_TREE && (skid = skill_tree[baseClass][j].skill_id) > 0; j++) {
if (skid == skill_id) previousClassSkill = true;
}
}
if (isThirdClass) {
for (k = 0; k < MAX_SKILL_TREE && (skid = skill_tree[secondTransClass][k].skill_id) > 0; k++) {
if (skid == skill_id) previousClassSkill = true;
}
}
if (previousClassSkill) continue;
}
We're pretty much done with pc_resetskill, but if you want, you can add this to the comment at the top of the method, as the final line:
* if flag&16, Only reset skills that the current class can learn
Almost done with code changes, now we just need to add the script function.
The remainder of the changes are in script.cpp:
First, find the definitions of the script functions by searching for this:
struct script_function buildin_func[]
And add the following line somewhere, ideally below the one for resetskill
BUILDIN_DEF(resetskillcurrentonly, "?"),
Now add this method somewhere, ideally below resetskill but it can in theory go anywhere.
/**
* Reset player's skill, leaving previous classes untouched
* resetskillcurrentonly({<char_id>});
**/
BUILDIN_FUNC(resetskillcurrentonly)
{
TBL_PC* sd;
if (!script_charid2sd(2, sd))
return SCRIPT_CMD_FAILURE;
pc_resetskill(sd, 17);
return SCRIPT_CMD_SUCCESS;
}
With this done, you can recompile the client and everything should be set. Now all you need is the new version of the resetnpc script below and you'll have the new functionality. Man, this was a lot harder than it should have been. Rathena should really just add this option by default.
resetnpc.txt