From 39f8bf798e6d6f0002a417157679de3aa8766697 Mon Sep 17 00:00:00 2001 From: skyleo Date: Mon, 16 Sep 2024 10:35:36 +0200 Subject: [PATCH 1/6] Fix reproduceskill and cloneskill being used in checks when being zero This resulted in for example skills gained temporarily by equipping which used SKILL_FLAG_REPLACED_LV_* to be not cleared when taking the equip off. --- src/map/pc.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/map/pc.c b/src/map/pc.c index 14c3a4409ae..2decb491b0e 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1657,7 +1657,8 @@ static void pc_calc_skilltree_clear(struct map_session_data *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 || sd->status.skill[i].id == sd->reproduceskill_id) //Don't touch these + || (sd->cloneskill_id != 0 && sd->status.skill[i].id == sd->cloneskill_id) + || (sd->reproduceskill_id != 0 && 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 */ @@ -1693,10 +1694,17 @@ 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 && sd->status.skill[i].id != sd->reproduceskill_id) - || sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) { + if (sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0) { + bool is_cloneskill = sd->cloneskill_id != 0 && sd->status.skill[i].id == sd->cloneskill_id; + bool is_reproduceskill = sd->reproduceskill_id != 0 && sd->status.skill[i].id == sd->reproduceskill_id; + if (is_cloneskill || is_reproduceskill) + continue; // Plagiarized and Reproduce Skills are kept. + // 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].lv = sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0; + sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; + } else if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) { + sd->status.skill[i].lv = 0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; } } From f5e11d4870ce9533a50486553515d90cbd3651c9 Mon Sep 17 00:00:00 2001 From: "Guilherme G. Menaldo" Date: Sun, 22 Sep 2024 13:10:21 -0300 Subject: [PATCH 2/6] Validate length of configuration names from add*Conf HPM commands Providing a too long name/path would silently fail otherwise. --- src/common/HPM.c | 5 +++++ src/common/HPMi.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/common/HPM.c b/src/common/HPM.c index 9608eec218e..4aa80c0ec38 100644 --- a/src/common/HPM.c +++ b/src/common/HPM.c @@ -451,6 +451,11 @@ static bool hplugins_addconf(unsigned int pluginID, enum HPluginConfType type, c return false; } + if (strnlen(name, HPM_ADDCONF_LENGTH) >= HPM_ADDCONF_LENGTH) { + ShowError("HPM->addConf:%s: config '%s' name/path is too long. Maximum is %d characters (see #define HPM_ADDCONF_LENGTH). Skipping it.\n", HPM->pid2name(pluginID), name, HPM_ADDCONF_LENGTH - 1); + return false; + } + ARR_FIND(0, VECTOR_LENGTH(HPM->config_listeners[type]), i, strcmpi(name, VECTOR_INDEX(HPM->config_listeners[type], i).key) == 0); if (i != VECTOR_LENGTH(HPM->config_listeners[type])) { ShowError("HPM->addConf:%s: duplicate '%s', already in use by '%s'!", diff --git a/src/common/HPMi.h b/src/common/HPMi.h index d7083bbf8ff..a44984495f2 100644 --- a/src/common/HPMi.h +++ b/src/common/HPMi.h @@ -35,6 +35,8 @@ struct map_session_data; struct hplugin_data_store; #define HPM_VERSION "1.2" + +// Maximum length of the configuration path for configs added with add*Conf #define HPM_ADDCONF_LENGTH 40 struct hplugin_info { From c228f3c72a3269d1a5f24f97cdbb0575cedbb599 Mon Sep 17 00:00:00 2001 From: skyleo Date: Wed, 25 Sep 2024 16:16:39 +0200 Subject: [PATCH 3/6] Fix previously plagiarized skill re-appearing after relog, when removed prior due to lower level copy failure --- src/map/skill.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index ebabd361f5a..582639b50c2 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -3690,12 +3690,13 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li switch(can_copy(tsd, copy_skill)) { case 1: // Plagiarism { - pc->clear_existing_cloneskill(tsd, false); - lv = min(skill_lv, pc->checkskill(tsd, RG_PLAGIARISM)); - if (learned_lv > lv) + if (learned_lv > lv) { + pc->clear_existing_cloneskill(tsd, true); break; // [Aegis] can't overwrite skill of higher level, but will still remove previously copied skill. + } + pc->clear_existing_cloneskill(tsd, false); tsd->cloneskill_id = copy_skill; pc_setglobalreg(tsd, script->add_variable("CLONE_SKILL"), copy_skill); pc_setglobalreg(tsd, script->add_variable("CLONE_SKILL_LV"), lv); @@ -3712,12 +3713,13 @@ 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; - pc->clear_existing_reproduceskill(tsd, false); - lv = min(lv, skill->get_max(copy_skill)); - if (learned_lv > lv) + if (learned_lv > lv) { + pc->clear_existing_reproduceskill(tsd, true); break; // unconfirmed, but probably the same behavior as for RG_PLAGIARISM + } + pc->clear_existing_reproduceskill(tsd, false); tsd->reproduceskill_id = copy_skill; pc_setglobalreg(tsd, script->add_variable("REPRODUCE_SKILL"), copy_skill); pc_setglobalreg(tsd, script->add_variable("REPRODUCE_SKILL_LV"), lv); From 1adb7c64f92b5070da2963228ebb5dda8e58f554 Mon Sep 17 00:00:00 2001 From: skyleo Date: Fri, 20 Sep 2024 08:22:40 +0200 Subject: [PATCH 4/6] Add *getunitparam to obtain values set via unit_parameters_db.conf in scripts --- doc/script_commands.txt | 31 ++++++++++ src/map/script.c | 122 ++++++++++++++++++++++++++++++++++++++++ src/map/status.h | 8 +++ 3 files changed, 161 insertions(+) diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 1c4bb8aa3ab..939ea863f4f 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -3101,6 +3101,37 @@ Examples: --------------------------------------- +*getunitparam({, {, , }}) + +Thi will return the requested parameter's value or fill the required arrays in +the case of UNIT_PARAM_MAX_HP. + +: +- UNIT_PARAM_NAME ^= name of unit_parameters_db.conf entry used by class. +- UNIT_PARAM_NATHEAL_WEIGHT_RATE ^= NaturalHealWeightRate value +- UNIT_PARAM_MAX_ASPD ^= MaxASPD +- UNIT_PARAM_MAX_HP ^= MaxHP +- UNIT_PARAM_MAX_STATS ^= MaxStats + + can be -1 if attached players class is desired, such as in the case of UNIT_PARAM_MAX_HP, +where is mandatory. + +Examples: + +// Outputs the possible maximum ASPD of attached player. + mesf("MAX_ASPD: %d", getunitparam(UNIT_PARAM_MAX_ASPD)); + +// Outputs the possible maximum stats of Job_Baby. + mesf("MAX_STATS: %d", getunitparam(UNIT_PARAM_MAX_STATS, Job_Baby)); + +// Saves the entries for MAXHP per level ranges of JOB_SUPER_NOVICE into the given arrays and prints them. + .@count = getunitparam(UNIT_PARAM_MAX_ASPD, JOB_SUPER_NOVICE, .@max_lv, .@max_hp); + for (.@i = 0; .@i < .@count; ++.@i) { + mesf("max_lv: %d, max_hp: %d", .@max_lv[.@i], .@max_hp[.@i]); + } + +--------------------------------------- + *sit({""}) *stand({""}) diff --git a/src/map/script.c b/src/map/script.c index 8ecddd7fc43..6f9541f0986 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -28628,6 +28628,120 @@ static BUILDIN(mestipbox) return true; } + +/** + * Returns a units 's values + * + * NOTE: UNIT_PARAM_MAX_HP needs two arrays. + * + * getunitparam({, {, , }}) + */ +static BUILDIN(getunitparam) +{ + int class = -1; + if (script_hasdata(st, 3)) { + class = script_getnum(st, 3); + if (class != -1) { + if (!pc->db_checkid(class)) { + ShowError("buildin_getunitparam: invalid class (%d)\n", class); + st->state = END; + return false; + } + class = pc->class2idx(class); + } + } + + struct map_session_data *sd = NULL; + if (class == -1) { + sd = script_rid2sd(st); + if (sd == NULL) { + ShowError("buildin_getunitparam: No player attached, but class == -1.\n"); + return false; + } + class = pc->class2idx(sd->status.class); + } + + struct s_unit_params *entry = status->dbs->unit_params[class]; + int param = script_getnum(st, 2); + switch (param) { + case UNIT_PARAM_NAME: + script_pushconststr(st, entry->name); + break; + case UNIT_PARAM_NATHEAL_WEIGHT_RATE: + script_pushint(st, entry->natural_heal_weight_rate); + break; + case UNIT_PARAM_MAX_ASPD: + script_pushint(st, (2000 - entry->max_aspd) / 10); // max_aspd is actually min_amotion :) + break; + case UNIT_PARAM_MAX_HP: { + if (!script_hasdata(st, 4) || !script_hasdata(st, 5)) { + ShowError("buildin_getunitparam: UNIT_PARAM_MAXP_HP requires 4 parameters: , , , \n"); + st->state = END; + return false; + } + + struct script_data *maxhp_maxlvls = script_getdata(st, 4); + struct script_data *maxhp_values = script_getdata(st, 5); + if (!data_isreference(maxhp_maxlvls) || reference_toconstant(maxhp_maxlvls)) { + ShowError("buildin_getunitparam: argument must be reference and not a reference to constant\n"); + script->reportdata(maxhp_maxlvls); + st->state = END; + return false; + } + if (!data_isreference(maxhp_values) || reference_toconstant(maxhp_values)) { + ShowError("buildin_getunitparam: argument must be reference and not a reference to constant\n"); + script->reportdata(maxhp_values); + st->state = END; + return false; + } + + const char *maxhp_maxlvls_varname = reference_getname(maxhp_maxlvls); + const char *maxhp_values_varname = reference_getname(maxhp_values); + if (!is_int_variable(maxhp_maxlvls_varname)) { + ShowError("buildin_getunitparam: argument must be of integer type\n"); + script->reportdata(maxhp_maxlvls); + st->state = END; + return false; + } + if (!is_int_variable(maxhp_values_varname)) { + ShowError("buildin_getunitparam: argument must be of integer type\n"); + script->reportdata(maxhp_values); + st->state = END; + return false; + } + + if (not_server_variable(*maxhp_maxlvls_varname) || not_server_variable(*maxhp_values_varname)) { + if (sd == NULL) { + sd = script->rid2sd(st); + if (sd == NULL) + return false; // player variable but no player attached + } + } + + int varid1 = reference_getid(maxhp_maxlvls); + int varid2 = reference_getid(maxhp_values); + int count = entry->maxhp_size; + for (int i = 0; i < count; i++) { + script->set_reg(st, sd, reference_uid(varid1, i), maxhp_maxlvls_varname, (const void *)h64BPTRSIZE(entry->maxhp[i].max_level), + reference_getref(maxhp_maxlvls)); + script->set_reg(st, sd, reference_uid(varid2, i), maxhp_values_varname, (const void *)h64BPTRSIZE(entry->maxhp[i].value), + reference_getref(maxhp_values)); + } + script_pushint(st, count); + break; + } + case UNIT_PARAM_MAX_STATS: + script_pushint(st, entry->max_stats); + break; + default: + ShowError("buildin_getunitparam: Received invalid param: %d\n", param); + st->state = END; + return false; + } + + return true; +} + /** * Adds a built-in script function. * @@ -29507,6 +29621,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(mesurl, "ss??"), BUILDIN_DEF(mestipbox, "si"), + BUILDIN_DEF(getunitparam, "i???"), }; int i, len = ARRAYLENGTH(BUILDIN); RECREATE(script->buildin, char *, script->buildin_count + len); // Pre-alloc to speed up @@ -30490,6 +30605,13 @@ static void script_hardcoded_constants(void) script->set_constant("HOMINFO_RENAME", HOMINFO_RENAME, false, false); script->set_constant("HOMINFO_LEVEL", HOMINFO_LEVEL, false, false); + script->constdb_comment("getunitparam param-types"); + script->set_constant("UNIT_PARAM_NAME", UNIT_PARAM_NAME, false, false); + script->set_constant("UNIT_PARAM_NATHEAL_WEIGHT_RATE", UNIT_PARAM_NATHEAL_WEIGHT_RATE, false, false); + script->set_constant("UNIT_PARAM_MAX_ASPD", UNIT_PARAM_MAX_ASPD, false, false); + script->set_constant("UNIT_PARAM_MAX_HP", UNIT_PARAM_MAX_HP, false, false); + script->set_constant("UNIT_PARAM_MAX_STATS", UNIT_PARAM_MAX_STATS, false, false); + script->constdb_comment("Renewal"); #ifdef RENEWAL script->set_constant("RENEWAL", 1, false, false); diff --git a/src/map/status.h b/src/map/status.h index a426a71d765..6f6712f060a 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -1323,6 +1323,14 @@ struct s_maxhp_entry { int value; ///< The actual max hp value }; +enum e_unit_params { + UNIT_PARAM_NAME, + UNIT_PARAM_NATHEAL_WEIGHT_RATE, + UNIT_PARAM_MAX_ASPD, + UNIT_PARAM_MAX_HP, + UNIT_PARAM_MAX_STATS, +}; + struct s_unit_params { char name[SCRIPT_VARNAME_LENGTH]; ///< group name as defined in conf From 0a3948d3a6fb89fc57001157b0fc9286f271f4e8 Mon Sep 17 00:00:00 2001 From: hwsapibot Date: Sun, 29 Sep 2024 21:02:43 +0000 Subject: [PATCH 5/6] Update constants documentation Signed-off-by: hwsapibot --- doc/constants_pre-re.md | 8 ++++++++ doc/constants_re.md | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/doc/constants_pre-re.md b/doc/constants_pre-re.md index 55225b28f42..9de342212d1 100644 --- a/doc/constants_pre-re.md +++ b/doc/constants_pre-re.md @@ -5868,6 +5868,14 @@ - `HOMINFO_RENAME`: 5 - `HOMINFO_LEVEL`: 6 +### getunitparam param-types + +- `UNIT_PARAM_NAME`: 0 +- `UNIT_PARAM_NATHEAL_WEIGHT_RATE`: 1 +- `UNIT_PARAM_MAX_ASPD`: 2 +- `UNIT_PARAM_MAX_HP`: 3 +- `UNIT_PARAM_MAX_STATS`: 4 + ### Renewal - `RENEWAL`: 0 diff --git a/doc/constants_re.md b/doc/constants_re.md index 40dc94ff76c..df6efd864e7 100644 --- a/doc/constants_re.md +++ b/doc/constants_re.md @@ -5868,6 +5868,14 @@ - `HOMINFO_RENAME`: 5 - `HOMINFO_LEVEL`: 6 +### getunitparam param-types + +- `UNIT_PARAM_NAME`: 0 +- `UNIT_PARAM_NATHEAL_WEIGHT_RATE`: 1 +- `UNIT_PARAM_MAX_ASPD`: 2 +- `UNIT_PARAM_MAX_HP`: 3 +- `UNIT_PARAM_MAX_STATS`: 4 + ### Renewal - `RENEWAL`: 1 From 84da0df5ea655c8339430d2065dbe3b0b1a40577 Mon Sep 17 00:00:00 2001 From: Haru Date: Mon, 30 Sep 2024 23:15:19 +0000 Subject: [PATCH 6/6] Release v2024.09 Signed-off-by: Haru --- CHANGELOG.md | 13 ++++++++++++- doc/constants_pre-re.md | 2 +- doc/constants_re.md | 2 +- src/config/core.h | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdb83a5a62f..edad21744cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,10 +22,20 @@ If you are reading this in a text editor, simply ignore this section ### Removed --> -## [v2024.08] `August 2024` +## [v2024.09] `September 2024` ### Added +- Implemented the script command `getunitparam()` to query values defined in `unit_parameters_db.conf`, and the related `UNIT_PARAM_*` constants. See the `script_commands.txt` documentation for usage details. (#3323) +- Added validation of the name length for configuration entries added through the HPM `addBattleConf()`, `addLoginConf()`, `addCharConf()`, `addCharInterConf()`, `addLogConf()`, `addScriptConf()` methods, to prevent silent truncation. (#3324) + +### Fixed + +- Fixed an issue causing item-granted skills that were overriding an existing skill level, not to be correctly cleared when unequipping the item. (#3322) +- Fixed previously plagiarized skills re-appearing on subsequent logins due to the related script variables not getting cleared properly. (#3325) + +## [v2024.08] `August 2024` + ### Changed - Converted packets `CHARLOGIN_ONLINE_ACCOUNTS`, `MAPCHAR_AUTH_REQ`, `CHARLOGIN_SET_ACCOUNT_ONLINE` to the struct format. (#3304, #3312, #3314) @@ -3160,6 +3170,7 @@ Note: everything included in this release is part of PR #3198 which consists of - New versioning scheme and project changelogs/release notes (#1853) [Unreleased]: https://github.com/HerculesWS/Hercules/compare/stable...master +[v2024.09]: https://github.com/HerculesWS/Hercules/compare/v2024.08...v2024.09 [v2024.08]: https://github.com/HerculesWS/Hercules/compare/v2024.06...v2024.08 [v2024.06]: https://github.com/HerculesWS/Hercules/compare/v2024.05...v2024.06 [v2024.05]: https://github.com/HerculesWS/Hercules/compare/v2024.04...v2024.05 diff --git a/doc/constants_pre-re.md b/doc/constants_pre-re.md index 9de342212d1..024f579e542 100644 --- a/doc/constants_pre-re.md +++ b/doc/constants_pre-re.md @@ -4878,7 +4878,7 @@ ### Server defines - `PACKETVER`: 20190530 -- `HERCULES_VERSION`: 202408000 +- `HERCULES_VERSION`: 202409000 - `MAX_LEVEL`: 175 - `MAX_STORAGE`: 600 - `MAX_GUILD_STORAGE`: 500 diff --git a/doc/constants_re.md b/doc/constants_re.md index df6efd864e7..7f116999fb3 100644 --- a/doc/constants_re.md +++ b/doc/constants_re.md @@ -4878,7 +4878,7 @@ ### Server defines - `PACKETVER`: 20190530 -- `HERCULES_VERSION`: 202408000 +- `HERCULES_VERSION`: 202409000 - `MAX_LEVEL`: 175 - `MAX_STORAGE`: 600 - `MAX_GUILD_STORAGE`: 500 diff --git a/src/config/core.h b/src/config/core.h index 657f29bbe65..83bd05a1c97 100644 --- a/src/config/core.h +++ b/src/config/core.h @@ -22,7 +22,7 @@ #define CONFIG_CORE_H /// Hercules version. From tag vYYYY.MM(+PPP) -> YYYYMMPPP -#define HERCULES_VERSION 202408000 +#define HERCULES_VERSION 202409000 /// Max number of items on @autolootid list #define AUTOLOOTITEM_SIZE 10