From 3a62199919ded96f857bc0c0c8f9943229359fa9 Mon Sep 17 00:00:00 2001 From: skyleo Date: Fri, 31 May 2024 02:43:15 +0200 Subject: [PATCH 01/14] Add pc->is_own_skill to check for learnable or learned skills --- src/map/pc.c | 28 ++++++++++++++++++++++++++++ src/map/pc.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/src/map/pc.c b/src/map/pc.c index f203d03cf..59b399734 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -12609,6 +12609,32 @@ void pc_crimson_marker_clear(struct map_session_data *sd) } } +/** + * Checks if a skill is a permanent skill that one has prerequisites for or has learned. + * + * NOTE: Unfortunately even uninitialized skills have the flag SKILL_FLAG_PERMANENT, so we have to distinguish by id as well. + * Ideally a more saner zero value should be used for e_skill_flag. + * WARNING: This function relies on the skill tree being already calculated / filled. + * + * @param sd The player to check for. + * @param skill_id The skill to check for. + * @return True if the player meets the skill prerequisites or has it, false otherwise. + */ +static bool pc_is_own_skill(struct map_session_data *sd, uint16 skill_id) +{ + nullpo_retr(false, sd); + + int idx = skill->get_index(skill_id); + if (idx <= 0) + return false; // skill not found + if (sd->status.skill[idx].id != skill_id) + return false; // not meeting pre-requisites for skill or skill id to index mapping faulty. + if (sd->status.skill[idx].flag != SKILL_FLAG_PERMANENT) + return false; // script granted or temporary. + + return true; +} + static void do_final_pc(void) { @@ -13035,4 +13061,6 @@ void pc_defaults(void) pc->auto_exp_insurance = pc_auto_exp_insurance; pc->crimson_marker_clear = pc_crimson_marker_clear; + + pc->is_own_skill = pc_is_own_skill; } diff --git a/src/map/pc.h b/src/map/pc.h index b842717fc..951de5d58 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -1283,6 +1283,8 @@ END_ZEROED_BLOCK; /* End */ bool (*auto_exp_insurance) (struct map_session_data *sd); void (*crimson_marker_clear) (struct map_session_data *sd); + + bool (*is_own_skill) (struct map_session_data *sd, uint16 skill_id); }; #ifdef HERCULES_CORE From 7adf875d34a79280b9085c895c27befa71b63146 Mon Sep 17 00:00:00 2001 From: skyleo Date: Fri, 24 May 2024 20:26:58 +0200 Subject: [PATCH 02/14] Fix RG_PLAGIARISM deleting learned skills and all skills that required them by accident --- src/map/pc.c | 14 +++++++++++--- src/map/skill.c | 14 +++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/map/pc.c b/src/map/pc.c index 59b399734..991c98ffc 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1520,11 +1520,17 @@ static int pc_reg_received(struct map_session_data *sd) if ((i = pc->checkskill(sd,RG_PLAGIARISM)) > 0) { sd->cloneskill_id = pc_readglobalreg(sd,script->add_variable("CLONE_SKILL")); if (sd->cloneskill_id > 0 && (idx = skill->get_index(sd->cloneskill_id)) > 0) { + int learned_lv = sd->status.skill[idx].lv; + bool is_own_skill = pc->is_own_skill(sd, sd->cloneskill_id); sd->status.skill[idx].id = sd->cloneskill_id; sd->status.skill[idx].lv = pc_readglobalreg(sd,script->add_variable("CLONE_SKILL_LV")); if (sd->status.skill[idx].lv > i) sd->status.skill[idx].lv = i; - sd->status.skill[idx].flag = SKILL_FLAG_PLAGIARIZED; + + if (is_own_skill) + sd->status.skill[idx].flag = learned_lv + SKILL_FLAG_REPLACED_LV_0; + else + sd->status.skill[idx].flag = SKILL_FLAG_PLAGIARIZED; } } if ((i = pc->checkskill(sd,SC_REPRODUCE)) > 0) { @@ -1644,7 +1650,8 @@ static void pc_calc_skilltree_clear(struct map_session_data *sd) nullpo_retv(sd); for (i = 0; i < MAX_SKILL_DB; i++) { - if (sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED) //Don't touch these + if (sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED + && sd->status.skill[i].id != sd->cloneskill_id) //Don't touch these sd->status.skill[i].id = 0; //First clear skills. /* permanent skills that must be re-checked */ if (sd->status.skill[i].flag == SKILL_FLAG_PERMANENT) { @@ -1679,7 +1686,8 @@ static int pc_calc_skilltree(struct map_session_data *sd) pc->calc_skilltree_clear(sd); for (int i = 0; i < MAX_SKILL_DB; i++) { - if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0) { + if ((sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0 && sd->status.skill[i].id != sd->cloneskill_id) + || sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) { // Restore original level of skills after deleting earned skills. sd->status.skill[i].lv = (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) ? 0 : sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; diff --git a/src/map/skill.c b/src/map/skill.c index 871ff873d..1baaa442c 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -3693,10 +3693,19 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li tsd->status.skill[idx].lv = 0; tsd->status.skill[idx].flag = 0; clif->deleteskill(tsd, tsd->cloneskill_id, false); + } else if (tsd->status.skill[idx].flag >= SKILL_FLAG_REPLACED_LV_0) { + tsd->status.skill[idx].lv = tsd->status.skill[idx].flag - SKILL_FLAG_REPLACED_LV_0; + tsd->status.skill[idx].flag = SKILL_FLAG_PERMANENT; + // CAREFUL! This assumes you will only ever use SKILL_FLAG_REPLACED_LV_0 logic when copying SKILL_FLAG_PERMANENT skills!!! + clif->addskill(tsd, tsd->cloneskill_id); } } + int learned_lv = tsd->status.skill[cidx].lv; + bool copying_own_skill = pc->is_own_skill(tsd, copy_skill); lv = min(skill_lv, pc->checkskill(tsd, RG_PLAGIARISM)); + if (learned_lv > lv) + break; // [Aegis] can't overwrite skill of higher level, but will still remove previously copied skill. tsd->cloneskill_id = copy_skill; pc_setglobalreg(tsd, script->add_variable("CLONE_SKILL"), copy_skill); @@ -3704,7 +3713,10 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li tsd->status.skill[cidx].id = copy_skill; tsd->status.skill[cidx].lv = lv; - tsd->status.skill[cidx].flag = SKILL_FLAG_PLAGIARIZED; + if (copying_own_skill) + tsd->status.skill[cidx].flag = learned_lv + SKILL_FLAG_REPLACED_LV_0; + else + tsd->status.skill[cidx].flag = SKILL_FLAG_PLAGIARIZED; clif->addskill(tsd, copy_skill); } break; From 27a5ec18f03bbd33adeba3970c418c84bb1dfcff Mon Sep 17 00:00:00 2001 From: skyleo Date: Fri, 24 May 2024 21:19:38 +0200 Subject: [PATCH 03/14] Move clearing of RG_PLAGIARISM copied skill to its own function pc->clear_existing_cloneskill --- src/map/pc.c | 33 +++++++++++++++++++++++++++++++++ src/map/pc.h | 1 + src/map/skill.c | 15 +-------------- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/map/pc.c b/src/map/pc.c index 991c98ffc..016cea4f0 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -12643,6 +12643,38 @@ static bool pc_is_own_skill(struct map_session_data *sd, uint16 skill_id) return true; } +/** + * Clears / removes the existing cloned skill from RG_PLAGIARISM + * + * @param sd The player to clear the cloned skill from. + * @param clear_vars If true, the remembered clone_skill level and ID will be cleared, otherwise it will not be touched. + */ +static void pc_clear_existing_cloneskill(struct map_session_data *sd, bool clear_vars) +{ + nullpo_retv(sd); + + if (sd->cloneskill_id != 0) { + int idx = skill->get_index(sd->cloneskill_id); + if (sd->status.skill[idx].flag == SKILL_FLAG_PLAGIARIZED) { + sd->status.skill[idx].id = 0; + sd->status.skill[idx].lv = 0; + sd->status.skill[idx].flag = 0; + clif->deleteskill(sd, sd->cloneskill_id, false); + } else if (sd->status.skill[idx].flag >= SKILL_FLAG_REPLACED_LV_0) { + sd->status.skill[idx].lv = sd->status.skill[idx].flag - SKILL_FLAG_REPLACED_LV_0; + sd->status.skill[idx].flag = SKILL_FLAG_PERMANENT; + // CAREFUL! This assumes you will only ever use SKILL_FLAG_REPLACED_LV_0 logic when copying SKILL_FLAG_PERMANENT skills!!! + clif->addskill(sd, sd->cloneskill_id); + } + } + + if (clear_vars) { + sd->cloneskill_id = 0; + pc_setglobalreg(sd, script->add_variable("CLONE_SKILL"), 0); + pc_setglobalreg(sd, script->add_variable("CLONE_SKILL_LV"), 0); + } +} + static void do_final_pc(void) { @@ -13071,4 +13103,5 @@ void pc_defaults(void) pc->crimson_marker_clear = pc_crimson_marker_clear; pc->is_own_skill = pc_is_own_skill; + pc->clear_existing_cloneskill = pc_clear_existing_cloneskill; } diff --git a/src/map/pc.h b/src/map/pc.h index 951de5d58..bd5a84e54 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -1285,6 +1285,7 @@ END_ZEROED_BLOCK; /* End */ void (*crimson_marker_clear) (struct map_session_data *sd); bool (*is_own_skill) (struct map_session_data *sd, uint16 skill_id); + void (*clear_existing_cloneskill) (struct map_session_data *sd, bool clear_vars); }; #ifdef HERCULES_CORE diff --git a/src/map/skill.c b/src/map/skill.c index 1baaa442c..7551bbae7 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -3686,20 +3686,7 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li switch(can_copy(tsd, copy_skill)) { case 1: // Plagiarism { - if (tsd->cloneskill_id) { - idx = skill->get_index(tsd->cloneskill_id); - if (tsd->status.skill[idx].flag == SKILL_FLAG_PLAGIARIZED) { - tsd->status.skill[idx].id = 0; - tsd->status.skill[idx].lv = 0; - tsd->status.skill[idx].flag = 0; - clif->deleteskill(tsd, tsd->cloneskill_id, false); - } else if (tsd->status.skill[idx].flag >= SKILL_FLAG_REPLACED_LV_0) { - tsd->status.skill[idx].lv = tsd->status.skill[idx].flag - SKILL_FLAG_REPLACED_LV_0; - tsd->status.skill[idx].flag = SKILL_FLAG_PERMANENT; - // CAREFUL! This assumes you will only ever use SKILL_FLAG_REPLACED_LV_0 logic when copying SKILL_FLAG_PERMANENT skills!!! - clif->addskill(tsd, tsd->cloneskill_id); - } - } + pc->clear_existing_cloneskill(tsd, false); int learned_lv = tsd->status.skill[cidx].lv; bool copying_own_skill = pc->is_own_skill(tsd, copy_skill); From 18afc419a15ab3971807cbff958df30ada37ba3c Mon Sep 17 00:00:00 2001 From: skyleo Date: Fri, 24 May 2024 21:21:38 +0200 Subject: [PATCH 04/14] Replace code-duplication in pc_jobchange with pc->clear_existing_cloneskill and thus add support for SKILL_FLAG_REPLACED_LV_0 --- src/map/pc.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/map/pc.c b/src/map/pc.c index 016cea4f0..4b90b6221 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -8870,18 +8870,7 @@ static int pc_jobchange(struct map_session_data *sd, int class, int upper) pc_setglobalreg(sd, script->add_variable("jobchange_level_3rd"), sd->change_level_3rd); } - if(sd->cloneskill_id) { - idx = skill->get_index(sd->cloneskill_id); - if( sd->status.skill[idx].flag == SKILL_FLAG_PLAGIARIZED ) { - sd->status.skill[idx].id = 0; - sd->status.skill[idx].lv = 0; - sd->status.skill[idx].flag = 0; - clif->deleteskill(sd, sd->cloneskill_id, false); - } - sd->cloneskill_id = 0; - pc_setglobalreg(sd, script->add_variable("CLONE_SKILL"), 0); - pc_setglobalreg(sd, script->add_variable("CLONE_SKILL_LV"), 0); - } + pc->clear_existing_cloneskill(sd, true); if(sd->reproduceskill_id) { idx = skill->get_index(sd->reproduceskill_id); From a77014eff74d282e02359d331b0f79b8c6551289 Mon Sep 17 00:00:00 2001 From: skyleo Date: Fri, 24 May 2024 23:05:32 +0200 Subject: [PATCH 05/14] Move clearing of SC_REPRODUCE copied skill to its own function pc->clear_existing_reproduceskill --- src/map/pc.c | 43 ++++++++++++++++++++++++++++++------------- src/map/pc.h | 1 + src/map/skill.c | 12 ++---------- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/map/pc.c b/src/map/pc.c index 4b90b6221..ee7358627 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -8831,7 +8831,7 @@ static int jobchange_killclone(struct block_list *bl, va_list ap) static int pc_jobchange(struct map_session_data *sd, int class, int upper) { int i, fame_flag=0; - int job, idx = 0; + int job = 0; nullpo_ret(sd); @@ -8872,18 +8872,7 @@ static int pc_jobchange(struct map_session_data *sd, int class, int upper) pc->clear_existing_cloneskill(sd, true); - if(sd->reproduceskill_id) { - idx = skill->get_index(sd->reproduceskill_id); - if( sd->status.skill[idx].flag == SKILL_FLAG_PLAGIARIZED ) { - sd->status.skill[idx].id = 0; - sd->status.skill[idx].lv = 0; - sd->status.skill[idx].flag = 0; - clif->deleteskill(sd, sd->reproduceskill_id, false); - } - sd->reproduceskill_id = 0; - pc_setglobalreg(sd, script->add_variable("REPRODUCE_SKILL"),0); - pc_setglobalreg(sd, script->add_variable("REPRODUCE_SKILL_LV"),0); - } + pc->clear_existing_reproduceskill(sd, true); if ((job & MAPID_UPPERMASK) != (sd->job & MAPID_UPPERMASK)) { //Things to remove when changing class tree. const int class_idx = pc->class2idx(sd->status.class); @@ -12664,6 +12653,33 @@ static void pc_clear_existing_cloneskill(struct map_session_data *sd, bool clear } } +/** + * Clears / removes the existing reproduced skill from SC_REPRODUCE + * + * @param sd The player to clear the reproduced skill from. + * @param clear_vars If true, the remembered reproduced skill level and ID will be cleared, otherwise it will not be touched. + */ +static void pc_clear_existing_reproduceskill(struct map_session_data *sd, bool clear_vars) +{ + nullpo_retv(sd); + + if (sd->reproduceskill_id != 0) { + int idx = skill->get_index(sd->reproduceskill_id); + if (sd->status.skill[idx].flag == SKILL_FLAG_PLAGIARIZED) { + sd->status.skill[idx].id = 0; + sd->status.skill[idx].lv = 0; + sd->status.skill[idx].flag = 0; + clif->deleteskill(sd, sd->reproduceskill_id, false); + } + } + + if (clear_vars) { + sd->reproduceskill_id = 0; + pc_setglobalreg(sd, script->add_variable("REPRODUCE_SKILL"), 0); + pc_setglobalreg(sd, script->add_variable("REPRODUCE_SKILL_LV"), 0); + } +} + static void do_final_pc(void) { @@ -13093,4 +13109,5 @@ void pc_defaults(void) pc->is_own_skill = pc_is_own_skill; pc->clear_existing_cloneskill = pc_clear_existing_cloneskill; + pc->clear_existing_reproduceskill = pc_clear_existing_reproduceskill; } diff --git a/src/map/pc.h b/src/map/pc.h index bd5a84e54..6b8b32bf7 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -1286,6 +1286,7 @@ END_ZEROED_BLOCK; /* End */ bool (*is_own_skill) (struct map_session_data *sd, uint16 skill_id); void (*clear_existing_cloneskill) (struct map_session_data *sd, bool clear_vars); + void (*clear_existing_reproduceskill) (struct map_session_data *sd, bool clear_vars); }; #ifdef HERCULES_CORE diff --git a/src/map/skill.c b/src/map/skill.c index 7551bbae7..0c92bd6e6 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -3681,7 +3681,7 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li break; } - int cidx, idx, lv = 0; + int cidx, lv = 0; cidx = skill->get_index(copy_skill); switch(can_copy(tsd, copy_skill)) { case 1: // Plagiarism @@ -3710,15 +3710,7 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li case 2: // Reproduce { lv = sc ? sc->data[SC__REPRODUCE]->val1 : 1; - if (tsd->reproduceskill_id) { - idx = skill->get_index(tsd->reproduceskill_id); - if (tsd->status.skill[idx].flag == SKILL_FLAG_PLAGIARIZED) { - tsd->status.skill[idx].id = 0; - tsd->status.skill[idx].lv = 0; - tsd->status.skill[idx].flag = 0; - clif->deleteskill(tsd, tsd->reproduceskill_id, false); - } - } + pc->clear_existing_reproduceskill(tsd, false); lv = min(lv, skill->get_max(copy_skill)); tsd->reproduceskill_id = copy_skill; From e65896278a49125fa60c11a12736f592a2d101ba Mon Sep 17 00:00:00 2001 From: skyleo Date: Fri, 24 May 2024 23:22:33 +0200 Subject: [PATCH 06/14] Fix SC_REPRODUCE deleting learned skills and all skills that required them by accident --- src/map/pc.c | 23 +++++++++++++++++------ src/map/skill.c | 12 +++++++++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/map/pc.c b/src/map/pc.c index ee7358627..14c3a4409 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1536,11 +1536,17 @@ static int pc_reg_received(struct map_session_data *sd) if ((i = pc->checkskill(sd,SC_REPRODUCE)) > 0) { sd->reproduceskill_id = pc_readglobalreg(sd,script->add_variable("REPRODUCE_SKILL")); if( sd->reproduceskill_id > 0 && (idx = skill->get_index(sd->reproduceskill_id)) > 0) { + int learned_lv = sd->status.skill[idx].lv; + bool is_own_skill = pc->is_own_skill(sd, sd->reproduceskill_id); sd->status.skill[idx].id = sd->reproduceskill_id; sd->status.skill[idx].lv = pc_readglobalreg(sd,script->add_variable("REPRODUCE_SKILL_LV")); if( i < sd->status.skill[idx].lv) sd->status.skill[idx].lv = i; - sd->status.skill[idx].flag = SKILL_FLAG_PLAGIARIZED; + + if (is_own_skill) + sd->status.skill[idx].flag = learned_lv + SKILL_FLAG_REPLACED_LV_0; + else + sd->status.skill[idx].flag = SKILL_FLAG_PLAGIARIZED; } } @@ -1650,9 +1656,10 @@ static void pc_calc_skilltree_clear(struct map_session_data *sd) nullpo_retv(sd); for (i = 0; i < MAX_SKILL_DB; i++) { - if (sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED - && sd->status.skill[i].id != sd->cloneskill_id) //Don't touch these - sd->status.skill[i].id = 0; //First clear skills. + if (sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED || sd->status.skill[i].flag == SKILL_FLAG_PERM_GRANTED + || sd->status.skill[i].id == sd->cloneskill_id || sd->status.skill[i].id == sd->reproduceskill_id) //Don't touch these + continue; + sd->status.skill[i].id = 0; //First clear skills. /* permanent skills that must be re-checked */ if (sd->status.skill[i].flag == SKILL_FLAG_PERMANENT) { switch (skill->dbs->db[i].nameid) { @@ -1686,7 +1693,7 @@ static int pc_calc_skilltree(struct map_session_data *sd) pc->calc_skilltree_clear(sd); for (int i = 0; i < MAX_SKILL_DB; i++) { - if ((sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0 && sd->status.skill[i].id != sd->cloneskill_id) + if ((sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0 && sd->status.skill[i].id != sd->cloneskill_id && sd->status.skill[i].id != sd->reproduceskill_id) || sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) { // Restore original level of skills after deleting earned skills. sd->status.skill[i].lv = (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) ? 0 : sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0; @@ -8871,7 +8878,6 @@ static int pc_jobchange(struct map_session_data *sd, int class, int upper) } pc->clear_existing_cloneskill(sd, true); - pc->clear_existing_reproduceskill(sd, true); if ((job & MAPID_UPPERMASK) != (sd->job & MAPID_UPPERMASK)) { //Things to remove when changing class tree. @@ -12670,6 +12676,11 @@ static void pc_clear_existing_reproduceskill(struct map_session_data *sd, bool c sd->status.skill[idx].lv = 0; sd->status.skill[idx].flag = 0; clif->deleteskill(sd, sd->reproduceskill_id, false); + } else if (sd->status.skill[idx].flag >= SKILL_FLAG_REPLACED_LV_0) { + sd->status.skill[idx].lv = sd->status.skill[idx].flag - SKILL_FLAG_REPLACED_LV_0; + sd->status.skill[idx].flag = SKILL_FLAG_PERMANENT; + // CAREFUL! This assumes you will only ever use SKILL_FLAG_REPLACED_LV_0 logic when copying SKILL_FLAG_PERMANENT skills!!! + clif->addskill(sd, sd->reproduceskill_id); } } diff --git a/src/map/skill.c b/src/map/skill.c index 0c92bd6e6..93f66c189 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -3683,12 +3683,12 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li int cidx, lv = 0; cidx = skill->get_index(copy_skill); + int learned_lv = tsd->status.skill[cidx].lv; + bool copying_own_skill = pc->is_own_skill(tsd, copy_skill); switch(can_copy(tsd, copy_skill)) { case 1: // Plagiarism { pc->clear_existing_cloneskill(tsd, false); - int learned_lv = tsd->status.skill[cidx].lv; - bool copying_own_skill = pc->is_own_skill(tsd, copy_skill); lv = min(skill_lv, pc->checkskill(tsd, RG_PLAGIARISM)); if (learned_lv > lv) @@ -3711,7 +3711,10 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li { lv = sc ? sc->data[SC__REPRODUCE]->val1 : 1; pc->clear_existing_reproduceskill(tsd, false); + lv = min(lv, skill->get_max(copy_skill)); + if (learned_lv > lv) + break; // unconfirmed, but probably the same behavior as for RG_PLAGIARISM tsd->reproduceskill_id = copy_skill; pc_setglobalreg(tsd, script->add_variable("REPRODUCE_SKILL"), copy_skill); @@ -3719,7 +3722,10 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li tsd->status.skill[cidx].id = copy_skill; tsd->status.skill[cidx].lv = lv; - tsd->status.skill[cidx].flag = SKILL_FLAG_PLAGIARIZED; + if (copying_own_skill) + tsd->status.skill[cidx].flag = learned_lv + SKILL_FLAG_REPLACED_LV_0; + else + tsd->status.skill[cidx].flag = SKILL_FLAG_PLAGIARIZED; clif->addskill(tsd, copy_skill); } break; From 7c4cda0de514f9917c859f5160932bc647776c1d Mon Sep 17 00:00:00 2001 From: skyleo Date: Sat, 25 May 2024 17:09:03 +0200 Subject: [PATCH 07/14] Add SKILL_FLAG_REPLACED_LV_0 to non-copyable logic of can_copy to match SKILL_FLAG_PLAGIARIZED --- src/map/skill.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/map/skill.c b/src/map/skill.c index 93f66c189..67146797a 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -1262,7 +1262,8 @@ static int can_copy(struct map_session_data *sd, uint16 skill_id) if (!cidx) return 0; - if (sd->status.skill[cidx].id && sd->status.skill[cidx].flag == SKILL_FLAG_PLAGIARIZED) + if (sd->status.skill[cidx].id != 0 && (sd->status.skill[cidx].flag >= SKILL_FLAG_REPLACED_LV_0 + || sd->status.skill[cidx].flag == SKILL_FLAG_PLAGIARIZED)) return 0; // Checks if preserve is active and if skill can be copied by Plagiarism From 8790b0b4a6a0f0d2862ae7ef84aee779c0e6748e Mon Sep 17 00:00:00 2001 From: skyleo Date: Sat, 25 May 2024 18:59:27 +0200 Subject: [PATCH 08/14] Add SKILL_FLAG_PERM_GRANTED to non-copyable logic of can_copy for safety reasons as there's no clearly defined behavior for those --- src/map/skill.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/map/skill.c b/src/map/skill.c index 67146797a..ebabd361f 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -1263,7 +1263,8 @@ static int can_copy(struct map_session_data *sd, uint16 skill_id) return 0; if (sd->status.skill[cidx].id != 0 && (sd->status.skill[cidx].flag >= SKILL_FLAG_REPLACED_LV_0 - || sd->status.skill[cidx].flag == SKILL_FLAG_PLAGIARIZED)) + || sd->status.skill[cidx].flag == SKILL_FLAG_PLAGIARIZED + || sd->status.skill[cidx].flag == SKILL_FLAG_PERM_GRANTED)) return 0; // Checks if preserve is active and if skill can be copied by Plagiarism From 18ecdb1098ea4aab5754a5d16a379d1057690519 Mon Sep 17 00:00:00 2001 From: "Guilherme G. Menaldo" Date: Thu, 6 Jun 2024 20:29:27 -0300 Subject: [PATCH 09/14] Add mesnavigation script command basic command to generate navi tags following client-specific format --- doc/script_commands.txt | 73 +++++++++++++++++++ npc/dev/test.txt | 29 ++++++++ src/map/clif.c | 36 +++++++++ src/map/clif.h | 3 + src/map/script.c | 62 ++++++++++++++++ src/map/script.h | 19 ++++- src/plugins/HPMHooking/HPMHooking.Defs.inc | 4 + .../HPMHooking_map.HPMHooksCore.inc | 8 ++ .../HPMHooking_map.HookingPoints.inc | 2 + .../HPMHooking/HPMHooking_map.Hooks.inc | 53 ++++++++++++++ 10 files changed, 288 insertions(+), 1 deletion(-) diff --git a/doc/script_commands.txt b/doc/script_commands.txt index afaa90afc..2c31690d8 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -2612,6 +2612,79 @@ deleted. // This would delete all elements of the array deletearray(.@array); +--------------------------------------- +//===================================== +1.1 - Message formatting commands (tags) +//===================================== +--------------------------------------- + +*mesnavigation("