//===== eAthena Script ======================================= //= Kafra Express - Job Changing Module //===== By: ================================================== //= Skotlex //===== Current Version: ===================================== //= 4.0 //===== Compatible With: ===================================== //= eAthena 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$+"!",bc_npc; 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])); } }