//===== rAthena Script =======================================
//= Kafra Express - Job Changing Module
//===== By: ==================================================
//= Skotlex
//===== Current Version: =====================================
//= 4.0
//===== Compatible With: =====================================
//= rAthena SVN R8840+
//===== Description: =========================================
//= Part of the Kafra Express Script Package.
//= Enables job changing through the class trees.
//= Novice -> 1st Class, 1st Class -> 2nd Class, rebirths, etc
//===== Additional Comments: =================================
//= See config.txt for configuration.
//= When using Upper Job policy, previous jobs are stored in
//= the permanent variables kej_class1 and kej_class2
//============================================================
- script keInit_jobchange -1,{
OnInit: //Load Config
donpcevent "keConfig::OnLoadJobChange";
end;
}
function script F_keJobChange {
function SF_to1stJob;
function SF_to2ndJob;
function SF_getJobIndex;
function SF_getJobNames;
function SF_testChangeJob;
function SF_changeJob;
set @job, eaClass(class);
set @type, @job&~EAJ_UPPERMASK; //Because it is changed when rebirthing
set @reset, 0; //Base Level is reset only on rebirths
if ((@job&EAJ_BASEMASK) == EAJ_NOVICE)
{ //Novices
if (@job&EAJL_2) //S. Novices
goto L_FAIL;
if ($@kejc_skipNovice)
set @jobLv, 0; //jobLv is used again when checking for S.Novice's base level restriction.
else
set @jobLv, 10;
if (SF_testChangeJob(0,0,@jobLv))
SF_to1stJob(0);
} else
if(@job&EAJL_2){
//Second classes
if (@job&~EAJ_UPPERMASK) //rebirth/baby
goto L_FAIL;
if (SF_testChangeJob($@kejc_costRebirth,$@kejc_baseRebirth,$@kejc_jobRebirth)) {
set @type, EAJL_UPPER;
if ($@kejc_skipNovice)
SF_to1stJob(1);
else
SF_changeJob @job,EAJ_NOVICE_HIGH,0,0,$@kejc_costRebirth,1,$@kejc_rebirthReset;
}
} else {
//First classes
if (SF_testChangeJob($@kejc_cost2ND,$@kejc_base2ND,$@kejc_job2ND))
SF_to2ndJob();
}
return;
L_FAIL:
//Dead End
callfunc "F_keIntro", e_swt2, "I cannot change you from your current job.";
return;
//Handles changing to 1st job.
function SF_to1stJob {
setarray @classes[0],
EAJ_Acolyte|@type,
EAJ_Archer|@type,
EAJ_Mage|@type,
EAJ_Merchant|@type,
EAJ_Swordman|@type,
EAJ_Thief|@type,
EAJ_Taekwon|@type,
EAJ_Super_Novice|@type,
EAJ_GunSlinger|@type,
EAJ_Ninja|@type;
do {
set @newjob, -1;
if (@type == EAJL_UPPER && $@kejc_upperPolicy && kej_class1) {
set @newjob, eaclass(kej_class1);
set @newjob, (@newjob&EAJ_UPPERMASK)|@type;
if (roclass(@newjob) == -1)
set @newjob, -1;
set @submenu, 1;
}
if (@newjob == -1) {
SF_getJobNames getarraysize(@classes);
if (@type&EAJL_UPPER || $@kejc_disable&1)
set @names$[7], ""; //No S.Novice
if (@type&~EAJ_UPPERMASK)
{ //No TK/NJ/GS for Baby/Advanced
set @names$[6], "";
set @names$[8], "";
set @names$[9], "";
} else {
if ($@kejc_disable&2) //No TK
set @names$[6], "";
if ($@kejc_disable&4) //No GS
set @names$[8], "";
if ($@kejc_disable&8) //No NJ
set @names$[9], "";
}
set @submenu, select(
"- Cancel job change",
@names$[0], @names$[1], @names$[2],
@names$[3], @names$[4], @names$[5],
@names$[6], @names$[7], @names$[8],
@names$[9]
);
if (@submenu > 1) {
if (@submenu == 9 && //S. Novice's own change check.
SF_testChangeJob(0,$@kejc_baseSN,@jobLv) == 0)
return;
set @newjob, @classes[@submenu-2];
}
}
if (@newjob > -1) {
set @i, SF_getJobIndex(@newjob);
if (@i > -1)
set @weapon, $@kejc_weapon1[@i];
else
set @weapon, 0;
if (getarg(0)) { //Skipping High Novice, charge rebirth costs.
if (SF_changeJob(@job,@newJob,@weapon,0,$@kejc_costRebirth,2,$@kejc_rebirthReset))
return;
} else {
if (SF_changeJob(@job,@newJob,@weapon,0,0,2,0))
return;
}
}
} while (@submenu > 1);
}
function SF_to2ndJob {
do {
set @newjob, -1;
if (@type == EAJL_UPPER && $@kejc_upperPolicy && kej_class2) {
set @newjob, eaclass(kej_class2);
set @newjob, (@newjob&EAJ_UPPERMASK)|@type;
if (roclass(@newjob) == -1)
set @newjob, -1; //Invalid class.
else
if ((@newjob&EAJ_BASEMASK) != (@job&EAJ_BASEMASK))
set @newjob, -1; //Saved next job does not corresponds to current 1st!
}
if (@newjob == -1) { //Fetch from menu.
setarray @classes[0],
(@job&EAJ_UPPERMASK)|@type|EAJL_2_1,
(@job&EAJ_UPPERMASK)|@type|EAJL_2_2;
if (roclass(@classes[0]) == -1)
{ //Can't upgrade?
callfunc "F_keIntro", e_swt2, "I cant' change you from your current job.";
return;
}
SF_getJobNames 2;
set @submenu, select(
"- Cancel job change",
@names$[0],@names$[1]
);
if (@submenu > 1)
set @newjob, @classes[@submenu-2];
}
if (@newjob > -1) {
set @i, SF_getJobIndex(@newjob);
if (@i > -1) {
if (@newjob&EAJL_2_2) { //2-2 classes
set @weapon, $@kejc_weapon_22[@i];
set @weapon2,$@kejc_weapon2_22[@i];
} else { //2-1 classes
set @weapon, $@kejc_weapon_21[@i];
set @weapon2,$@kejc_weapon2_21[@i];
}
} else {
set @weapon, 0;
set @weapon2, 0;
}
if (SF_changeJob(@job,@newJob,@weapon,@weapon2,$@kejc_cost2ND,0,0))
return;
}
} while (@submenu > 1);
}
//SubFunction: SF_testChangeJob(Zeny, BaseLv, JobLv)
//Function that checks if the player qualifies for job changing.
function SF_testChangeJob {
set @fail, 0;
if (Zeny < getarg(0))
set @fail, 1;
if (BaseLevel < getarg(1))
set @fail, @fail|2;
if (JobLevel < getarg(2))
set @fail, @fail|4;
if (@fail > 0) {
if (@fail&1)
mes "You need "+getarg(0)+"z for the conversion process.";
if (@fail&2)
mes "You need to be at least Lv "+getarg(1)+".";
if (@fail&4)
mes "You need at least job Lv "+getarg(2)+".";
callfunc "F_keIntro", e_pif, "Sorry, you don't qualify for a job change yet.";
return 0;
}
if (SkillPoint > 0 && $@kejc_skillsPolicy == 0) {
callfunc "F_keIntro", e_dots, "Sorry, use your remaining Skill points before being able to change class.";
return 0;
}
return 1;
}
//SubFunction: SF_changeJob (CurrentJob, NewJob, Weapon, Weapon2,
// Zeny, WipeSkills, ResetLv)
//Attempts to change to the Jobgiven.
//CurrentJob is actual job in eA format.
//NewJob is job to change to in eA format.
//Weapon is the ID of the weapon to grant
//Weapon2 is the alternative weapon granted when your job level is above $@kejc_wBonusLv
//Zeny is the money required (if negative, it is money awarded)
//WipeSkills if 1, indicates that skills should be wiped,
//if 2, it means basic skills have to be given back
//Reset Level indicates the base lv must be reset to 1.
//Note: Zeny/Base/Job requirements should had been checked with SF_testChangeJob already!
function SF_changeJob {
set @job, getarg(0);
set @newjob,getarg(1);
set @weapon,getarg(2);
set @weapon2,getarg(3);
set @cost,getarg(4);
set @wipeSkill,getarg(5);
set @resetLv,getarg(6);
if (roclass(@newjob) == -1) { //Invalid job?
callfunc "F_keIntro", -1, "I can't change you to this job...";
return 0;
}
set @jobStr$, jobname(roclass(@newjob));
if (@wipeSkill == 0 && SkillPoint > 0 && $@kejc_skillsPolicy == 1) {
set @selection, select(
"- Do not change yet.",
"- Change to "+@jobStr$+" (skill points lost)",
"- View details"
);
} else {
set @selection, select(
"- Cancel",
"- Change to "+@jobStr$,
"- View details"
);
}
switch (@selection) {
case 3: //Details
mes "Okay.. listen up:";
next;
mes "["+@name$+"]";
mes "Changing to "+@jobStr$+" now means:";
if (@wipeSkill == 0 && SkillPoint > 0 && $@kejc_skillsPolicy == 1)
mes "- You will lose your "+SkillPoint+" unused skill points.";
else if (@wipeSkill == 1)
mes "- You will lose all your skills.";
if (@resetLv)
mes "- Your base level will be reset to 1.";
if (@cost > 0)
mes "- You will be charged "+@cost+"z.";
else if (@cost < 0)
mes "- You will be awarded with "+(0-@cost)+"z.";
if (@weapon > 0) {
if (@weapon2 > 0 && $@kejc_wBonusLv) {
if (JobLevel < $@kejc_wBonusLv) {
mes "- You will receive a "+getitemname(@weapon)+"["+getitemslots(@weapon)+"].";
mes "- If you wait until Job Lv"+$@kejc_wBonusLv+", you can receive instead a "+getitemname(@weapon2)+"["+getitemslots(@weapon2)+"].";
} else {
mes "- You will receive a "+getitemname(@weapon2)+"["+getitemslots(@weapon2)+"] for reaching Job Lv"+$@kejc_wBonusLv+".";
}
} else
mes "- You will receive a "+getitemname(@weapon)+".";
}
mes "So... will you change?";
if (select(
"- Cancel",
"- Change to "+@jobStr$
) != 2) {
callfunc "F_keIntro", e_dots, "...alright.";
return 0;
}
callfunc "F_keIntro", -1, "Enjoy your new Job.";
case 2: //Change
//Set/Unset job path variables as needed.
if($@kejc_upperPolicy) {
if((@job&EAJ_BASEMASK) == @job && @job != EAJ_NOVICE)
set kej_class1,class; //Advancing to second class, so...
if(@job&EAJL_2)
set kej_class2,class; //Only way of being here is by doing a rebirth
if(@job&~EAJ_UPPERMASK) {
set kej_class1,0; //Clear when one is a high class
if(@job&~EAJL_2 && @job&EAJ_BASEMASK != EAJ_NOVICE)
set kej_class2,0; //Clear when leaving high 1st class
}
}
if (@resetLv) {
jobchange Job_Novice_High; //Done to give players those 100 points from High classes
resetlvl(1);
}
if (@wipeSkill) {
resetskill;
setoption(0);
set SkillPoint,0;
} else if ($@kejc_skillsPolicy == 1)
set SkillPoint,0;
if (@wipeSkill>1)
skill 1,9,0;
if($@kejc_resetDye)
setlook 7,0;
if ($@kejc_weaponPolicy && @weapon > 0) {
if ($@kejc_wBonusLv && @weapon2 > 0 && JobLevel >= $@kejc_wBonusLv)
getitem @weapon2,1;
else
getitem @weapon,1;
}
jobchange roclass(@newjob);
if ($@kejc_announce)
announce strcharinfo(0)+" has been promoted to "+@jobStr$+"!",8;
set Zeny,Zeny-@cost;
emotion e_grat;
return 1;
default: //Cancel...
return 0;
}
}
//SubFunction: SF_getJobIndex(Job)
//Given a job in eA format, retrieves the basic index which is used for the
//config arrays.
function SF_getJobIndex {
set @i, getarg(0);
set @i, @i&EAJ_BASEMASK;
switch (@i) {
case EAJ_ACOLYTE:
return 0;
case EAJ_ARCHER:
return 1;
case EAJ_MAGE:
return 2;
case EAJ_MERCHANT:
return 3;
case EAJ_SWORDMAN:
return 4;
case EAJ_THIEF:
return 5;
case EAJ_TAEKWON:
return 6;
case EAJ_NOVICE: //Super Novice, actually
return 7;
case EAJ_GUNSLINGER:
return 8;
case EAJ_NINJA:
return 9;
default:
return -1;
}
}
//SubFunction: SF_getJobNames(Qty)
//Fills an array @names$ with the job names taken from the array "classes",
// making each entry start with "- " followed by the job name.
function SF_getJobNames {
set @size, getarg(0);
for (set @i, 0; @i < @size; set @i, @i+1)
setd "@names$["+@i+"]", "- "+jobname(roclass(@classes[@i]));
}
}