Jump to content
  • 0

[QoL] Show warning when trying to allocate skill points on a higher job tab


exequens

Question


  • Group:  Members
  • Topic Count:  2
  • Topics Per Day:  0.00
  • Content Count:  8
  • Reputation:   0
  • Joined:  03/14/24
  • Last Seen:  

I want to be able to show a warning if a player is trying to allocate a skill point on a higher job tab.
For example, a player resets their skills, normally you won't be able to allocate your skill point to your 4th job tab if you havent spent enough skpoint in all your 1st, 2nd, and 3rd job tab.
It would be very nice to be able to show a warning that basically says:
"You need to spend %d skill point(s) before spending it on your xx job tab"
I have tried doing it myself, but I just can't seem to get it right.

Am I right to tinker with this specific block on src/map/pc.cpp
 


/*==========================================
 * Update skill_lv for player sd
 * Skill point allocation
 *------------------------------------------*/
void pc_skillup(map_session_data *sd,uint16 skill_id)
{
    uint16 idx = skill_get_index(skill_id);

    nullpo_retv(sd);

    if (!idx) {
        if (skill_id)
            ShowError("pc_skillup: Player attempts to level up invalid skill '%d'\n", skill_id);
        return;
    }

    // Level up guild skill
    if (SKILL_CHK_GUILD(skill_id)) {
        guild_skillup(sd, skill_id);
        return;
    }
    // Level up homunculus skill
    else if (sd->hd && SKILL_CHK_HOMUN(skill_id)) {
        hom_skillup(sd->hd, skill_id);
        return;
    }
    else {
        if( sd->status.skill_point > 0 &&
            sd->status.skill[idx].id &&
            sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT && //Don't allow raising while you have granted skills. [Skotlex]
            sd->status.skill[idx].lv < skill_tree_get_max(skill_id, sd->status.class_) )
        {
            sd->status.skill[idx].lv++;
            sd->status.skill_point--;
            if( !skill_get_inf(skill_id) || pc_checkskill_summoner(sd, SUMMONER_POWER_LAND) >= 20 || pc_checkskill_summoner(sd, SUMMONER_POWER_SEA) >= 20 )
                status_calc_pc(sd,SCO_NONE); // Only recalculate for passive skills.
            else if( sd->status.skill_point == 0 && pc_is_taekwon_ranker(sd) )
                pc_calc_skilltree(sd); // Required to grant all TK Ranker skills.
            else
                pc_check_skilltree(sd); // Check if a new skill can Lvlup

            uint16 lv = sd->status.skill[idx].lv;
            int32 range = skill_get_range2(&sd->bl, skill_id, lv, false);
            bool upgradable = ( lv < skill_tree_get_max( sd->status.skill[idx].id, sd->status.class_ ) );
            clif_skillup( *sd, skill_id, lv, range, upgradable );
            clif_updatestatus(*sd,SP_SKILLPOINT);
            if( skill_id == GN_REMODELING_CART ) /* cart weight info was updated by status_calc_pc */
                clif_updatestatus(*sd,SP_CARTINFO);
            if (pc_checkskill(sd, SG_DEVIL) && ((sd->class_&MAPID_THIRDMASK) == MAPID_STAR_EMPEROR || pc_is_maxjoblv(sd)))
                clif_status_change(&sd->bl, EFST_DEVIL1, 1, 0, 0, 0, 1); //Permanent blind effect from SG_DEVIL.
            if (!pc_has_permission(sd, PC_PERM_ALL_SKILL)) // may skill everything at any time anyways, and this would cause a huge slowdown
                clif_skillinfoblock(sd);
        }
        //else
        //    ShowDebug("Skill Level up failed. ID:%d idx:%d (CID=%d. AID=%d)\n", skill_id, idx, sd->status.char_id, sd->status.account_id);
    }

I'm reading the function pc_calc_skilltree_normalize_job_sub as well, but I can't grasp it. Anyone willing to help me?

Edited by exequens
Link to comment
Share on other sites

5 answers to this question

Recommended Posts

  • 0

  • Group:  Members
  • Topic Count:  5
  • Topics Per Day:  0.00
  • Content Count:  56
  • Reputation:   20
  • Joined:  04/21/13
  • Last Seen:  

Hi. I'm trying to achieve this too now that I read your request, It made me remember that a while ago I wanted to do something like what you explained.

Here's what I managed to produce so far, sadly it only works with Novice as I'm checking NV_BASIC skill level.

/*==========================================
 * Update skill_lv for player sd
 * Skill point allocation
 *------------------------------------------*/
void pc_skillup(map_session_data *sd,uint16 skill_id)
{
	uint16 idx = skill_get_index(skill_id);

	nullpo_retv(sd);
	
	if (!idx) {
		if (skill_id)
			ShowError("pc_skillup: Player attempts to level up invalid skill '%d'\n", skill_id);
		return;
	}
	
	// If user tries to put points in a skill other than NV_BASIC while NV_BASIC is not maxed
	if(skill_id > NV_BASIC && sd->status.skill[NV_BASIC].lv < skill_tree_get_max(NV_BASIC, sd->status.class_)){
		StringBuf *nov_msg = StringBuf_Malloc();
		StringBuf_Printf(nov_msg, "You must spend %d skillpoints on Basic Skill in Novice skill tree first", skill_tree_get_max(NV_BASIC, sd->status.class_));
		
		clif_displaymessage(sd->fd, StringBuf_Value(nov_msg));
		
		StringBuf_Free(nov_msg);
		
		// Do not spend the point
		// return;
	}
  ... Rest of the function ...
}

Edit: I can't find a way to check how many skillpoints have been invested in a specific job by the player. With that info we could check invested points in 1st / 2nd / 3rd jobs and display a message when trying to up a skill when not all previous job skills have been invested.

Edited by eleriaqueen
Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  2
  • Topics Per Day:  0.00
  • Content Count:  8
  • Reputation:   0
  • Joined:  03/14/24
  • Last Seen:  

Thanks for telling me to use code widget, edited first post.
Also thanks for showing a clear working example.

So if that works, then what we should do is make a function that can determine what skill_id is belong to what job tab?
Something like this maybe?
 

// Returns the job tier (Novice=0, 1st=1, 2nd=2, etc.)
int pc_get_job_tier(uint64 class_) {
    if (class_ & JOBL_FOURTH) return 4;
    if (class_ & JOBL_THIRD) return 3;
    if (class_ & JOBL_2) return 2;
    if ((class_ & MAPID_BASEMASK) != MAPID_NOVICE) return 1;
    return 0; // Novice/Summoner
}

// Returns the skill's job tier
int skill_get_job_tier(uint16 skill_id) {
    if (skill_id >= NV_BASIC && skill_id <= NV_TRICKDEAD) return 0;
    if (skill_id >= SM_SWORD && skill_id <= SM_BASH) return 1;
    return 0; // Default to Novice if unknown
}

I'm honestly not sure...

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  5
  • Topics Per Day:  0.00
  • Content Count:  56
  • Reputation:   20
  • Joined:  04/21/13
  • Last Seen:  

Class skills aren't well organized into neatly defined id ranges if I recall correctly. Some SM_ skills may have high ID's. We also need a way to check skillpoints invested in a specific job, so that if user tries to skill up a 2nd Job skill we could check if they spent all their first job skills. Oh dang, we also need to have specific code to handle Second High jobs / Third High jobs as high 2nd can go to joblvl 70.

Edit: I may have found a way, but my code is very long. This time I'm using ShowError() function for testing purposes.

enum Job_Tier {
	Basic = 0,
	First,
	First_High,
	Second,
	Second_High,
	Third,
	Third_High,
	Fourth
};

// Helper Function: Returns the player character's job tier
Job_Tier pc_get_job_tier(uint64 class_) {
	if (class_ & JOBL_FOURTH) return Job_Tier::Fourth;
	
	if (class_ ==  MAPID_RUNE_KNIGHT_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_WARLOCK_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_RANGER_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_ARCH_BISHOP_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_MECHANIC_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_GUILLOTINE_CROSS_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_ROYAL_GUARD_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_SORCERER_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_MINSTRELWANDERER_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_SURA_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_GENETIC_T) return Job_Tier::Third_High;
	if (class_ ==  MAPID_SHADOW_CHASER_T) return Job_Tier::Third_High;
	
	if (class_ & JOBL_THIRD) return Job_Tier::Third;
	
	if (class_ == MAPID_LORD_KNIGHT) return Job_Tier::Second_High;
	if (class_ == MAPID_HIGH_WIZARD) return Job_Tier::Second_High;
	if (class_ == MAPID_SNIPER) return Job_Tier::Second_High;
	if (class_ == MAPID_HIGH_PRIEST) return Job_Tier::Second_High;
	if (class_ == MAPID_WHITESMITH) return Job_Tier::Second_High;
	if (class_ == MAPID_ASSASSIN_CROSS) return Job_Tier::Second_High;
	if (class_ == MAPID_PALADIN) return Job_Tier::Second_High;
	if (class_ == MAPID_PROFESSOR) return Job_Tier::Second_High;
	if (class_ == MAPID_CLOWNGYPSY) return Job_Tier::Second_High;
	if (class_ == MAPID_CHAMPION) return Job_Tier::Second_High;
	if (class_ == MAPID_CREATOR) return Job_Tier::Second_High;
	if (class_ == MAPID_STALKER) return Job_Tier::Second_High;
	
	if (class_ & JOBL_2) return Job_Tier::Second;
	
	if (class_ == MAPID_SWORDMAN_HIGH) return Job_Tier::First_High;
	if (class_ == MAPID_MAGE_HIGH) return Job_Tier::First_High;
	if (class_ == MAPID_ARCHER_HIGH) return Job_Tier::First_High;
	if (class_ == MAPID_ACOLYTE_HIGH) return Job_Tier::First_High;
	if (class_ == MAPID_MERCHANT_HIGH) return Job_Tier::First_High;
	if (class_ == MAPID_THIEF_HIGH) return Job_Tier::First_High;
	
	if ((class_ & MAPID_BASEMASK) != MAPID_NOVICE) return Job_Tier::First;
	
	return Job_Tier::Basic; // Novice/Summoner
}

// Helper Function : Returns the skill's job tier
Job_Tier skill_get_job_tier(uint16 skill_id) {
	// Novice
	if (skill_id > 0 && skill_id < SM_SWORD) return Job_Tier::Basic;
	
	// Swordman
	else if (skill_id >= SM_SWORD && skill_id <= SM_ENDURE) return Job_Tier::First;
	
	// Magician
	else if (skill_id >= MG_SRECOVERY && skill_id <= MG_THUNDERSTORM) return Job_Tier::First;
	
	// Acolyte
	else if (skill_id >= AL_DP && skill_id <= AL_CURE) return Job_Tier::First;
	
	// Merchant
	else if (skill_id >= MC_INCCARRY && skill_id <= MC_MAMMONITE) return Job_Tier::First;
	
	// Archer
	else if (skill_id >= AC_OWL && skill_id <= AC_SHOWER) return Job_Tier::First;
	
	// Thief
	else if (skill_id >= TF_DOUBLE && skill_id <= TF_DETOXIFY) return Job_Tier::First;
	
	// Taekwon Kid
	else if (skill_id >= TK_RUN && skill_id <= TK_HIGHJUMP) return Job_Tier::First;
	else if (skill_id == TK_MISSION) return Job_Tier::First;
	
	// Knight
	else if (skill_id >= KN_SPEARMASTERY && skill_id <= KN_CAVALIERMASTERY) return Job_Tier::Second;
	
	// Priest
	else if (skill_id >= PR_MACEMASTERY && skill_id <= PR_MAGNUS) return Job_Tier::Second;
	
	// Wizard
	else if (skill_id >= WZ_FIREPILLAR && skill_id <= WZ_ESTIMATION) return Job_Tier::Second;
	
	// Blacksmith
	else if (skill_id >= BS_IRON && skill_id <= BS_MAXIMIZE) return Job_Tier::Second;
	
	// Hunter
	else if (skill_id >= HT_SKIDTRAP && skill_id <= HT_SPRINGTRAP) return Job_Tier::Second;
	
	// Assassin
	else if (skill_id >= AS_RIGHT && skill_id <= AS_SPLASHER) return Job_Tier::Second;
	
	// Rogue
	else if (skill_id >= RG_SNATCHER && skill_id <= RG_PLAGIARISM) return Job_Tier::Second;
	
	// Alchemist
	else if (skill_id >= AM_AXEMASTERY && skill_id <= AM_RESURRECTHOMUN) return Job_Tier::Second;
	
	// Crusader
	else if (skill_id >= CR_TRUST && skill_id <= CR_SPEARQUICKEN) return Job_Tier::Second;
	
	// Monk
	else if (skill_id >= MO_IRONHAND && skill_id <= MO_COMBOFINISH) return Job_Tier::Second;
	
	// Sage
	else if (skill_id >= SA_ADVANCEDBOOK && skill_id <= SA_COMA) return Job_Tier::Second;
	
	// Bard & Dancer
	else if (skill_id >= BD_ADAPTATION && skill_id <= BD_RAGNAROK) return Job_Tier::Second;
	
	// Bard
	else if (skill_id >= BA_MUSICALLESSON && skill_id <= BA_APPLEIDUN) return Job_Tier::Second;
	
	// Dancer
	else if (skill_id >= DC_DANCINGLESSON && skill_id <= DC_SERVICEFORYOU) return Job_Tier::Second;
	
	// Lord Knight
	else if (skill_id >= LK_AURABLADE && skill_id <= LK_FURY) return Job_Tier::Second_High;
	else if (skill_id >= LK_SPIRALPIERCE && skill_id <= LK_JOINTBEAT) return Job_Tier::Second_High;
	
	// High Priest
	else if (skill_id >= HP_ASSUMPTIO && skill_id <= HP_MEDITATIO) return Job_Tier::Second_High;
	else if (skill_id == HP_MANARECHARGE) return Job_Tier::Second_High;
	
	// High Wizard
	else if (skill_id >= HW_SOULDRAIN && skill_id <= HW_MAGICPOWER) return Job_Tier::Second_High;
	else if (skill_id == HW_NAPALMVULCAN) return Job_Tier::Second_High;
	else if (skill_id >= HW_GANBANTEIN && skill_id <= HW_GRAVITATION) return Job_Tier::Second_High;
	
	// Paladin
	else if (skill_id >= PA_PRESSURE && skill_id <= PA_GOSPEL) return Job_Tier::Second_High;
	else if (skill_id == PA_SHIELDCHAIN) return Job_Tier::Second_High;
	
	// Champion
	else if (skill_id >= CH_PALMSTRIKE && skill_id <= CH_CHAINCRUSH) return Job_Tier::Second_High;
	else if (skill_id == CH_SOULCOLLECT) return Job_Tier::Second_High;
	
	// Professor
	else if (skill_id >= PF_HPCONVERSION && skill_id <= PF_SOULBURN) return Job_Tier::Second_High;
	else if (skill_id >= PF_MINDBREAKER && skill_id <= PF_SPIDERWEB) return Job_Tier::Second_High;
	else if (skill_id == PF_DOUBLECASTING) return Job_Tier::Second_High;
	
	// Assassin Cross
	else if (skill_id >= ASC_KATAR && skill_id <= ASC_BREAKER) return Job_Tier::Second_High;
	else if (skill_id >= ASC_METEORASSAULT && skill_id <= ASC_CDP) return Job_Tier::Second_High;
	
	// Sniper
	else if (skill_id >= SN_SIGHT && skill_id <= SN_WINDWALK) return Job_Tier::Second_High;
	
	// Whitesmith
	else if (skill_id >= WS_MELTDOWN && skill_id <= WS_SYSTEMCREATE) return Job_Tier::Second_High;
	else if (skill_id == WS_WEAPONREFINE) return Job_Tier::Second_High;
	else if (skill_id >= WS_CARTTERMINATION && skill_id <= WS_OVERTHRUSTMAX) return Job_Tier::Second_High;
	
	// Stalker
	else if (skill_id >= ST_CHASEWALK && skill_id <= ST_STEALBACKPACK) return Job_Tier::Second_High;
	else if (skill_id >= ST_PRESERVE && skill_id <= ST_FULLSTRIP) return Job_Tier::Second_High;
	
	// Creator
	else if (skill_id >= CR_ALCHEMY && skill_id <= CR_SYNTHESISPOTION) return Job_Tier::Second_High;
	else if (skill_id >= CR_SLIMPITCHER && skill_id <= CR_FULLPROTECTION) return Job_Tier::Second_High;
	else if (skill_id == CR_ACIDDEMONSTRATION) return Job_Tier::Second_High;
	
	// Clown / Gypsy
	else if (skill_id >= CG_ARROWVULCAN && skill_id <= CG_MARIONETTE) return Job_Tier::Second_High;
	else if (skill_id >= CG_LONGINGFREEDOM && skill_id <= CG_TAROTCARD) return Job_Tier::Second_High;
	else if (skill_id == CG_SPECIALSINGER) return Job_Tier::Second_High;
	
	// Star Gladiator
	else if (skill_id >= SG_FEEL && skill_id <= SG_FUSION) return Job_Tier::Second;
	
	// Soul Linker
	else if (skill_id == SL_ALCHEMIST) return Job_Tier::Second;
	else if (skill_id >= SL_MONK && skill_id <= SL_SKA) return Job_Tier::Second;
	else if (skill_id == SL_HIGH) return Job_Tier::Second;
	// else if (skill_id >= SL_DEATHKNIGHT && skill_id <= SL_GUNNER) return 2;
	
	// Gunslinger
	else if (skill_id >= GS_GLITTERING && skill_id <= GS_GROUNDDRIFT) return Job_Tier::Second_High;
	
	// Ninja
	else if (skill_id >= NJ_TOBIDOUGU && skill_id <= NJ_ISSEN) return Job_Tier::Second_High;
	
	// Rune Knight
	else if (skill_id >= RK_ENCHANTBLADE && skill_id <= RK_PHANTOMTHRUST) return Job_Tier::Third;
	else if (skill_id >= RK_DRAGONBREATH_WATER && skill_id <= RK_LUXANIMA) return Job_Tier::Third;
	
	// Guillotine Cross
	else if (skill_id >= GC_VENOMIMPRESS && skill_id <= GC_CROSSRIPPERSLASHER) return Job_Tier::Third;
	else if (skill_id == GC_DARKCROW) return Job_Tier::Third;
	
	// Arch-Bishop
	else if (skill_id >= AB_JUDEX && skill_id <= AB_SILENTIUM) return Job_Tier::Third;
	else if (skill_id == AB_SECRAMENT) return Job_Tier::Third;
	else if (skill_id == AB_OFFERTORIUM) return Job_Tier::Third;
	else if (skill_id >= AB_VITUPERATUM && skill_id <= AB_CONVENIO) return Job_Tier::Third;
	
	// Warlock
	else if (skill_id >= WL_WHITEIMPRISON && skill_id <= WL_FREEZE_SP) return Job_Tier::Third;
	else if (skill_id == WL_TELEKINESIS_INTENSE) return Job_Tier::Third;
	
	// Ranger
	else if (skill_id >= RA_ARROWSTORM && skill_id <= RA_ICEBOUNDTRAP) return Job_Tier::Third;
	else if (skill_id == RA_UNLIMIT) return Job_Tier::Third;
	
	// Mechanic
	else if (skill_id >= NC_MADOLICENCE && skill_id <= NC_DISJOINT) return Job_Tier::Third;
	else if (skill_id == NC_MAGMA_ERUPTION) return Job_Tier::Third;
	
	// Shadow Chaser
	else if (skill_id >= SC_FATALMENACE && skill_id <= SC_FEINTBOMB) return Job_Tier::Third;
	else if (skill_id == SC_ESCAPE) return Job_Tier::Third;
	
	// Royal Guard
	else if (skill_id >= LG_CANNONSPEAR && skill_id <= LG_INSPIRATION) return Job_Tier::Third;
	else if (skill_id == LG_KINGS_GRACE) return Job_Tier::Third;
	
	// Sura
	else if (skill_id >= SR_DRAGONCOMBO && skill_id <= SR_GENTLETOUCH_REVITALIZE) return Job_Tier::Third;
	else if (skill_id >= SR_HOWLINGOFLION && skill_id <= SR_RIDEINLIGHTNING) return Job_Tier::Third;
	else if (skill_id == SR_FLASHCOMBO) return Job_Tier::Third;
	
	// Wanderer
	else if (skill_id >= WA_SWING_DANCE && skill_id <= WA_MOONLIT_SERENADE) return Job_Tier::Third;
	
	// Ministrel
	else if (skill_id >= MI_RUSH_WINDMILL && skill_id <= MI_HARMONIZE) return Job_Tier::Third;
	
	// Wanderer && Ministrel
	else if (skill_id >= WM_LESSON && skill_id <= WM_UNLIMITED_HUMMING_VOICE) return Job_Tier::Third;
	else if (skill_id == WM_FRIGG_SONG)  return Job_Tier::Third;
	
	// Sorcerer
	else if (skill_id >= SO_FIREWALK && skill_id <= SO_EARTH_INSIGNIA) return Job_Tier::Third;
	else if (skill_id == SO_ELEMENTAL_SHIELD) return Job_Tier::Third;

	// Genetic
	else if (skill_id >= GN_TRAINING_SWORD && skill_id <= GN_SLINGITEM_RANGEMELEEATK) return Job_Tier::Third;
	else if (skill_id == GN_ILLUSIONDOPING) return Job_Tier::Third;
	
	// Rebellion
	else if (skill_id >= RL_GLITTERING_GREED && skill_id <= RL_B_FLICKER_ATK) return Job_Tier::Third;
	
	// Star Emperor
	else if (skill_id >= SJ_LIGHTOFMOON && skill_id <= SJ_PROMINENCEKICK) return Job_Tier::Third;
	
	// Soul Reaper
	else if (skill_id >= SP_SOULGOLEM && skill_id <= SP_KAUTE) return Job_Tier::Third;
	
	// Kagerou && Oboro
	else if (skill_id >= KO_YAMIKUMO && skill_id <= KO_IZAYOI) return Job_Tier::Third;
	
	// Kagerou
	else if (skill_id >= KG_KAGEHUMI && skill_id <= KG_KAGEMUSYA) return Job_Tier::Third;
	
	// Oboro
	else if (skill_id >= OB_ZANGETSU && skill_id <= OB_AKAITSUKI) return Job_Tier::Third;
	
	// Super Novice 2 
	else if (skill_id >= NV_BREAKTHROUGH && skill_id <= NV_TRANSCENDENCE) return Job_Tier::Third;
	
	// ALL 3rd jobs
	else if (skill_id == ALL_FULL_THROTTLE) return Job_Tier::Third;
	
	// Dragon Knight 
	else if (skill_id >= DK_SERVANTWEAPON && skill_id <= DK_STORMSLASH) return Job_Tier::Fourth;
	else if (skill_id == DK_DRAGONIC_BREATH) return Job_Tier::Fourth;
	else if (skill_id == DK_DRAGONIC_PIERCE) return Job_Tier::Fourth;
	
	// Arc Mage 
	else if (skill_id >= AG_DEADLY_PROJECTION && skill_id <= AG_FROZEN_SLASH) return Job_Tier::Fourth;
	else if (skill_id == AG_DESTRUCTIVE_HURRICANE_CLIMAX) return Job_Tier::Fourth;
	else if (skill_id == AG_ENERGY_CONVERSION) return Job_Tier::Fourth;
	
	// Inquisitor 
	else if (skill_id >= IQ_POWERFUL_FAITH && skill_id <= IQ_THIRD_EXOR_FLAME) return Job_Tier::Fourth;
	else if (skill_id == IQ_BLAZING_FLAME_BLAST) return Job_Tier::Fourth;
	
	// Imperial Guard 
	else if (skill_id >= IG_GUARD_STANCE && skill_id <= IG_CROSS_RAIN) return Job_Tier::Fourth;
	else if (skill_id >= IG_RADIANT_SPEAR && skill_id <= IG_IMPERIAL_PRESSURE) return Job_Tier::Fourth;
	
	// Cardinal 
	else if (skill_id >= CD_REPARATIO && skill_id <= CD_FRAMEN) return Job_Tier::Fourth;
	else if (skill_id == CD_DIVINUS_FLOS) return Job_Tier::Fourth;
	
	// Shadow Cross 
	else if (skill_id >= SHC_SHADOW_EXCEED && skill_id <= SHC_FATAL_SHADOW_CROW) return Job_Tier::Fourth;
	else if (skill_id == SHC_CROSS_SLASH) return Job_Tier::Fourth;
	
	// Meister 
	else if (skill_id >= MT_AXE_STOMP && skill_id <= MT_SUMMON_ABR_INFINITY) return Job_Tier::Fourth;
	else if (skill_id >= MT_SPARK_BLASTER && skill_id <= MT_MIGHTY_SMASH) return Job_Tier::Fourth;
	else if (skill_id >= MT_RUSH_STRIKE && skill_id <= MT_ENERGY_CANNONADE) return Job_Tier::Fourth;
	
	// Biolo 
	else if (skill_id >= BO_ACIDIFIED_ZONE_WATER_ATK && skill_id <= BO_ACIDIFIED_ZONE_FIRE_ATK) return Job_Tier::Fourth;
	else if (skill_id >= BO_BIONIC_PHARMACY && skill_id <= BO_HELLTREE) return Job_Tier::Fourth;
	else if (skill_id >= BO_EXPLOSIVE_POWDER && skill_id <= BO_MAYHEMIC_THORNS) return Job_Tier::Fourth;
	else if (skill_id >= BO_MYSTERY_POWDER && skill_id <= BO_DUST_EXPLOSION) return Job_Tier::Fourth;
	
	// Abyss Chaser 
	else if (skill_id >= ABC_DAGGER_AND_BOW_M && skill_id <= ABC_FRENZY_SHOT) return Job_Tier::Fourth;
	else if (skill_id >= ABC_HIT_AND_SLIDING && skill_id <= ABC_ABYSS_FLAME) return Job_Tier::Fourth;
	
	// Wind Hawk
	else if (skill_id >= WH_ADVANCED_TRAP && skill_id <= WH_FLAMETRAP) return Job_Tier::Fourth;
	else if (skill_id == WH_WILD_WALK) return Job_Tier::Fourth;
	
	// Troubadour && Trouvere
	else if (skill_id >= TR_STAGE_MANNER && skill_id <= TR_PRON_MARCH) return Job_Tier::Fourth;
	else if (skill_id == TR_RHYTHMICAL_WAVE) return Job_Tier::Fourth;
	
	// Elemental Master
	else if (skill_id >= EM_MAGIC_BOOK_M && skill_id <= EM_ELEMENTAL_VEIL) return Job_Tier::Fourth;
	else if (skill_id == EM_PSYCHIC_STREAM) return Job_Tier::Fourth;
	
	// Night Watch
	else if (skill_id >= NW_P_F_I && skill_id <= NW_MISSION_BOMBARD) return Job_Tier::Fourth;
	else if (skill_id >= NW_WILD_SHOT && skill_id <= NW_MIDNIGHT_FALLEN) return Job_Tier::Fourth;
	
	// Soul Ascetic
	else if (skill_id >= SOA_TALISMAN_MASTERY && skill_id <= SOA_SOUL_OF_HEAVEN_AND_EARTH) return Job_Tier::Fourth;
	
	// Spirit Handler
	else if (skill_id >= SH_MYSTICAL_CREATURE_MASTERY && skill_id <= SH_BLESSING_OF_MYSTICAL_CREATURES) return Job_Tier::Fourth;
	else if (skill_id >= SH_CHUL_HO_BATTERING && skill_id <= SH_HYUN_ROK_SPIRIT_POWER) return Job_Tier::Fourth;
	
	// Hyper Novice
	else if (skill_id >= HN_SELFSTUDY_TATICS && skill_id <= HN_RULEBREAK) return Job_Tier::Fourth;
	else if (skill_id == HN_OVERCOMING_CRISIS) return Job_Tier::Fourth;
	
	// Sky Emperor
	else if (skill_id >= SKE_SKY_MASTERY && skill_id <= SKE_ENCHANTING_SKY) return Job_Tier::Fourth;
	else if (skill_id >= SKE_SKY_SUN && skill_id <= SKE_STAR_LIGHT_KICK) return Job_Tier::Fourth;
	
	// Shinkiro && Shiranui
	else if (skill_id >= SS_TOKEDASU && skill_id <= SS_ANKOKURYUUAKUMU) return Job_Tier::Fourth;
	else if (skill_id == SS_FOUR_CHARM) return Job_Tier::Fourth;
	
	// Sky Emperor
	else if (skill_id >= SKE_SKY_MASTERY && skill_id <= SKE_ENCHANTING_SKY) return Job_Tier::Fourth;
	
	return Job_Tier::Basic; // Default to Novice if unknown
}

/*==========================================
 * Update skill_lv for player sd
 * Skill point allocation
 *------------------------------------------*/
void pc_skillup(map_session_data *sd,uint16 skill_id)
{
	uint16 idx = skill_get_index(skill_id);

	nullpo_retv(sd);
	
	if (!idx) {
		if (skill_id)
			ShowError("pc_skillup: Player attempts to level up invalid skill '%d'\n", skill_id);
		return;
	}
  
	int32 skill_point = pc_calc_skillpoint( sd );
	
	int32 novice_max_skillpoint = job_db.find( JOB_NOVICE )->max_job_level - 1;
	int32 summoner_max_skillpoint = job_db.find( JOB_SUMMONER )->max_job_level - 1;
	
	int32 max_skillpoint_1st = job_db.find( JOB_SWORDMAN )->max_job_level - 1;
	int32 max_skillpoint_2nd = job_db.find( JOB_KNIGHT )->max_job_level - 1;
	int32 max_skillpoint_2nd_high = job_db.find( JOB_LORD_KNIGHT )->max_job_level - 1;
	int32 max_skillpoint_3rd = job_db.find( JOB_RUNE_KNIGHT )->max_job_level - 1;
	int32 max_skillpoint_4th = job_db.find( JOB_DRAGON_KNIGHT )->max_job_level - 1;
	
	int32 skillpoint_1st = sd->change_level_2nd - 1;
	int32 skillpoint_2nd = sd->change_level_3rd - 1;
	
	int32 must_spend_novice = novice_max_skillpoint - skill_point;
	int32 must_spend_1st = min(max_skillpoint_1st, novice_max_skillpoint + skillpoint_1st - skill_point);
	int32 must_spend_2nd = min(max_skillpoint_2nd, novice_max_skillpoint + skillpoint_1st + skillpoint_2nd - skill_point);
	int32 must_spend_2nd_high = min(max_skillpoint_2nd_high, novice_max_skillpoint + skillpoint_1st + skillpoint_2nd - skill_point);
	int32 must_spend_3rd = min(max_skillpoint_3rd, novice_max_skillpoint + skillpoint_1st + skillpoint_2nd + max_skillpoint_3rd - skill_point);
	
	char helper_msg_novice[128];
	char helper_msg_novice_high[128];
	char helper_msg_1st[128];
	char helper_msg_1st_high[128];
	char helper_msg_2nd[128];
	char helper_msg_2nd_high[128];
	char helper_msg_3rd[128];
	
	sprintf(helper_msg_novice, "%s", "You need to spend %d more skillpoints in Novice Skill Tree\n");
	sprintf(helper_msg_novice_high, "%s", "You need to spend %d more skillpoints in Novice High Skill Tree\n");
	sprintf(helper_msg_1st, "%s", "You need to spend %d more skillpoints in 1st Job Skill Tree\n");
	sprintf(helper_msg_1st_high, "%s", "You need to spend %d more skillpoints in 1st High Job Skill Tree\n");
	sprintf(helper_msg_2nd, "%s", "You need to spend %d more skillpoints in 2nd Job Skill Tree\n");
	sprintf(helper_msg_2nd_high, "%s", "You need to spend %d more skillpoints in 2nd High Job Skill Tree\n");
	sprintf(helper_msg_3rd, "%s", "You need to spend %d more skillpoints in 3rd Job Skill Tree\n");
	
	if (pc_get_job_tier(sd->class_) == Job_Tier::First) {
		if (skill_get_job_tier(skill_id) == Job_Tier::First && (skill_point < novice_max_skillpoint)) {
			ShowError(helper_msg_novice, must_spend_novice);
		}
	}
	if (pc_get_job_tier(sd->class_) == Job_Tier::First_High) {
		if (skill_get_job_tier(skill_id) == Job_Tier::First && (skill_point < novice_max_skillpoint)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice, must_spend_novice);
			}
		}
	}
	if (pc_get_job_tier(sd->class_) == Job_Tier::Second) {
		if (skill_get_job_tier(skill_id) == Job_Tier::First && (skill_point < novice_max_skillpoint)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice, must_spend_novice);
			}
		}
		if (skill_get_job_tier(skill_id) == Job_Tier::Second && (skill_point < novice_max_skillpoint + skillpoint_1st)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice, must_spend_novice);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st) {
				ShowError(helper_msg_1st, must_spend_1st);
			}
		}
	}
	if (pc_get_job_tier(sd->class_) == Job_Tier::Second_High) {
		if (skill_get_job_tier(skill_id) == Job_Tier::First && (skill_point < novice_max_skillpoint)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice_high, must_spend_novice);
			}
		}
		if (skill_get_job_tier(skill_id) == Job_Tier::Second && (skill_point < novice_max_skillpoint + skillpoint_1st)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice_high, must_spend_novice);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st) {
				ShowError(helper_msg_1st_high, must_spend_1st);
			}
		}
	}
	if (pc_get_job_tier(sd->class_) == Job_Tier::Third) {
		if (skill_get_job_tier(skill_id) == Job_Tier::First && (skill_point < novice_max_skillpoint)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice, must_spend_novice);
			}
		}
		if (skill_get_job_tier(skill_id) == Job_Tier::Second && (skill_point < novice_max_skillpoint + skillpoint_1st)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice, must_spend_novice);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st) {
				ShowError(helper_msg_1st, must_spend_1st);
			}
		}
		if (skill_get_job_tier(skill_id) == Job_Tier::Third && (skill_point < novice_max_skillpoint + skillpoint_1st + max_skillpoint_2nd)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice, must_spend_novice);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st) {
				ShowError(helper_msg_1st, must_spend_1st);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st + skillpoint_2nd) {
				ShowError(helper_msg_2nd, must_spend_2nd);
			}
		}
	}
	if (pc_get_job_tier(sd->class_) == Job_Tier::Third_High) {
		if (skill_get_job_tier(skill_id) == Job_Tier::First && (skill_point < novice_max_skillpoint)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice_high, must_spend_novice);
			}
		}
		if (skill_get_job_tier(skill_id) == Job_Tier::Second && (skill_point < novice_max_skillpoint + skillpoint_1st)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice_high, must_spend_novice);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st) {
				ShowError(helper_msg_1st_high, must_spend_1st);
			}
		}
		if ((skill_get_job_tier(skill_id) == Job_Tier::Third || Job_Tier::Third_High) && (skill_point < novice_max_skillpoint + skillpoint_1st + max_skillpoint_2nd)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice_high, must_spend_novice);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st) {
				ShowError(helper_msg_1st_high, must_spend_1st);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st + skillpoint_2nd) {
				ShowError(helper_msg_2nd_high, must_spend_2nd_high);
			}
		}
	}
	if (pc_get_job_tier(sd->class_) == Job_Tier::Fourth) {
		if (skill_get_job_tier(skill_id) == Job_Tier::First && (skill_point < novice_max_skillpoint)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice, must_spend_novice);
			}
		}
		if (skill_get_job_tier(skill_id) == Job_Tier::Second && (skill_point < novice_max_skillpoint + skillpoint_1st)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice, must_spend_novice);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st) {
				ShowError(helper_msg_1st, must_spend_1st);
			}
		}
		if (skill_get_job_tier(skill_id) == Job_Tier::Third && (skill_point < novice_max_skillpoint + skillpoint_1st + max_skillpoint_2nd)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice, must_spend_novice);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st) {
				ShowError(helper_msg_1st, must_spend_1st);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st + skillpoint_2nd) {
				ShowError(helper_msg_2nd, must_spend_2nd);
			}
		}
		if (skill_get_job_tier(skill_id) == Job_Tier::Fourth && (skill_point < novice_max_skillpoint + skillpoint_1st + max_skillpoint_2nd + max_skillpoint_3rd)) {
			if (skill_point < novice_max_skillpoint) {
				ShowError(helper_msg_novice, must_spend_novice);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st) {
				ShowError(helper_msg_1st, must_spend_1st);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st + skillpoint_2nd) {
				ShowError(helper_msg_2nd, must_spend_2nd);
			}
			if (skill_point < novice_max_skillpoint + skillpoint_1st + skillpoint_2nd + max_skillpoint_3rd) {
				ShowError(helper_msg_3rd, must_spend_3rd);
			}
		}
	}
  ... Rest of Function ...
}

Summoner and Super Novice are likely badly handled.

Edited by eleriaqueen
Text cleanup
Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  2
  • Topics Per Day:  0.00
  • Content Count:  8
  • Reputation:   0
  • Joined:  03/14/24
  • Last Seen:  

Oh my God, thank you very much for going to such length. Unfortunately, I cannot contribute as much as you do as I am a newbie in the coding department. That is why I'm posting it in a source request sub-forum...
I did some digging though. I think we should be able to make use of something like this?

std::shared_ptr<s_job_info> job = job_db.find(sd->status.class_);
std::shared_ptr<s_skill_tree> tree = skill_tree_db.find(job_class);

If we make the code find the class and find the skill instead using that, it should make the code smaller no?
The only problem is, I don't know how to use it.

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  5
  • Topics Per Day:  0.00
  • Content Count:  56
  • Reputation:   20
  • Joined:  04/21/13
  • Last Seen:  

I'll look into these functions, thanks for the info. Look I'm happy to at least try and come up with a solution for the issue, since I'll likely use it too! 🙂
I tried to handle Super Novice, but at first glance it doesn't look doable, becuase the job has access to other classes' skills mixed with his skills.

I'll try to work some more on Super Novice and Summoner. If I can't do it I'll look into using the functions you mentioned, it will likely require a huge rewrite so I rather try to fix it with the "current code".

Edited by eleriaqueen
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...