diff --git a/.mailmap b/.mailmap index e78b7564528..00afb5674dd 100644 --- a/.mailmap +++ b/.mailmap @@ -32,6 +32,8 @@ Guilherme G. Menaldo Guilherme G. Menaldo Guilherme G. Menaldo Haru Haruna +jasonch <31005928+jasonch35@users.noreply.github.com> +jasonch jsn Jenkijo KeiKun Kei Kenpachi diff --git a/AUTHORS b/AUTHORS index 8282be548f4..7becedd66f1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -71,7 +71,7 @@ jaBote jaBote jaBote Jackson -jasonch <31005928+jasonch35@users.noreply.github.com> +jasonch Jedzkie Jenkijo Jesusaves @@ -80,7 +80,6 @@ jmanfffreak joel Jônatas Andreta Jose Luis Rivera Flores -jsn j-tkay KeiKun Kenpachi @@ -180,6 +179,7 @@ tungsinpei TungSinpei ultramage Unknown +Venseer Victor vietlubu Vincent Thibault diff --git a/CHANGELOG.md b/CHANGELOG.md index ddcf69743fe..9eb0db46dbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -792,6 +792,17 @@ All these changes only affect Renewal. Pre-renewal is unchanged. - The `is_quest` argument to `pc->gainexp()` has been changed to a `flags` bitmask enum, in order to allow expansion to different flags. (#3279) +## [v2024.10] `October 2024` + +### Changed + +- Changed the `getmobdrops()` script command to avoid the use of global temporary variables. (#3319) + - The caller now specifies an array that will be filled with the requested data. + - The amount of filled entries will be returned as the command's return value. The caller should only read up to that amount of entries from the array, as it may contain previous leftover values past that point. + - See the script_commands documentation for further details. +- Improved the output of the `@mobinfo` atcommand to include item links for the mob drops, on clients that support it. Unsupported clients will gracefully fall back to just printing the names. #3328 +- Improved the output of the `@iteminfo` atcommand to include item links, on clients that support it. Unsupported clients will gracefully fall back to just printing the names. #3329 + ## [v2024.09] `September 2024` ### Added @@ -3940,6 +3951,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.10]: https://github.com/HerculesWS/Hercules/compare/v2024.09...v2024.10 [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 diff --git a/conf/messages.conf b/conf/messages.conf index c096fb2f931..c1ca477a39a 100644 --- a/conf/messages.conf +++ b/conf/messages.conf @@ -1323,7 +1323,7 @@ // @iteminfo 1276: Please enter an item name/ID (usage: @ii/@iteminfo ). -1277: Item: '%s'/'%s'[%d] (%d) Type: %s | Extra Effect: %s +1277: Item: '%s'/'%s' (%d) Type: %s | Extra Effect: %s 1278: None 1279: With script 1280: NPC Buy:%dz, Sell:%dz | Weight: %.1f diff --git a/doc/constants_pre-re.md b/doc/constants_pre-re.md index 7c46aba797a..bb62027c2f9 100644 --- a/doc/constants_pre-re.md +++ b/doc/constants_pre-re.md @@ -4880,7 +4880,7 @@ ### Server defines - `PACKETVER`: 20190530 -- `HERCULES_VERSION`: 202409000 +- `HERCULES_VERSION`: 202410000 - `MAX_LEVEL`: 175 - `MAX_STORAGE`: 600 - `MAX_GUILD_STORAGE`: 500 diff --git a/doc/constants_re.md b/doc/constants_re.md index 7f116999fb3..9d583556ef5 100644 --- a/doc/constants_re.md +++ b/doc/constants_re.md @@ -4878,7 +4878,7 @@ ### Server defines - `PACKETVER`: 20190530 -- `HERCULES_VERSION`: 202409000 +- `HERCULES_VERSION`: 202410000 - `MAX_LEVEL`: 175 - `MAX_STORAGE`: 600 - `MAX_GUILD_STORAGE`: 500 diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 939ea863f4f..58b186dcb3f 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -4364,54 +4364,25 @@ Example: --------------------------------------- -*getmobdrops() +*getmobdrops(, {, }); -This command will find all drops of the specified mob and return the item -IDs and drop percentages into arrays of temporary global variables. -getmobdrops() returns true if successful and false if the mob ID doesn't -exist. - -Upon executing this, - -$@MobDrop_item[] is a global temporary number array which contains the - item IDs of the monster's drops. - -$@MobDrop_rate[] is a global temporary number array which contains the - drop percentages of each item. (1 = .01%) - -$@MobDrop_count is the number of item drops found. - -Be sure to use $@MobDrop_count to go through the arrays, and not -'getarraysize', because the temporary global arrays are not cleared -between runs of 'getmobdrops'. If a mob with 7 item drops is looked up, -the arrays would have 7 elements. But if another mob is looked up and it -only has 5 item drops, the server will not clear the arrays for you, -overwriting the values instead. So in addition to returning the 5 item -drops, the 6th and 7th elements from the last call remain, and you will -get 5+2 item drops, of which the last 2 don't belong to the new mob. -$@MobDrop_count will always contain the correct number (5), unlike -getarraysize() which would return 7 in this case. +This command will find drops information of the specified , copy the +item ids on to and drop rate on to if provided. +Returns the number of items found. Will return 0 if is invalid. Example: - // get a Mob ID from the user input(.@mob_id); - if (getmobdrops(.@mob_id)) { // getmobdrops() returns true on success - // immediately copy global temporary variables into scope - // variables, since we don't know when getmobdrops() will get - // called again for another mob, overwriting your global temporary - // variables. - .@count = $@MobDrop_count; - copyarray(.@item[0], $@MobDrop_item[0], .@count); - copyarray(.@rate[0], $@MobDrop_rate[0], .@count); - + .@count = getmobdrops(.@mob_id, .@item, .@rate); + + if (.@count == 0) { + mes("No drops found."); + } else { mes(getmonsterinfo(.@mob_id, MOB_NAME) + " - " + .@count + " drops found:"); for (.@i = 0; .@i < .@count; ++.@i) { mes(.@item[.@i] + " (" + getitemname(.@item[.@i]) + ") " + .@rate[.@i]/100 + ((.@rate[.@i]%100 < 10) ? ".0":".") + .@rate[.@i]%100 + "%"); } - } else { - mes("Unknown monster ID."); } close(); diff --git a/src/config/core.h b/src/config/core.h index 83bd05a1c97..8b7f92bb1cf 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 202409000 +#define HERCULES_VERSION 202410000 /// Max number of items on @autolootid list #define AUTOLOOTITEM_SIZE 10 diff --git a/src/map/atcommand.c b/src/map/atcommand.c index e3fc633d86f..9eca9a09365 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -7156,14 +7156,14 @@ ACMD(mobinfo) unsigned char msize[3][7] = {"Small", "Medium", "Large"}; unsigned char mrace[12][11] = {"Formless", "Undead", "Beast", "Plant", "Insect", "Fish", "Demon", "Demi-Human", "Angel", "Dragon", "Boss", "Non-Boss"}; unsigned char melement[10][8] = {"Neutral", "Water", "Earth", "Fire", "Wind", "Poison", "Holy", "Dark", "Ghost", "Undead"}; - char atcmd_output2[CHAT_SIZE_MAX]; + StringBuf buf; struct item_data *item_data; struct mob_db *monster, *mob_array[MAX_SEARCH]; int count; int i, k; memset(atcmd_output, '\0', sizeof(atcmd_output)); - memset(atcmd_output2, '\0', sizeof(atcmd_output2)); + StrBuf->Init(&buf); if (!*message) { clif->message(fd, msg_fd(fd, MSGTBL_MOBINFO_USAGE)); // Please enter a monster name/ID (usage: @mobinfo ). @@ -7253,23 +7253,22 @@ ACMD(mobinfo) } #endif - if (item_data->slot) - snprintf(atcmd_output2, sizeof(atcmd_output2), " - %s[%d] %02.02f%%", item_data->jname, item_data->slot, (float)droprate / 100); - else - snprintf(atcmd_output2, sizeof(atcmd_output2), " - %s %02.02f%%", item_data->jname, (float)droprate / 100); - - strcat(atcmd_output, atcmd_output2); - + struct item link_item = { 0 }; + link_item.nameid = monster->dropitem[i].nameid; + StrBuf->AppendStr(&buf, " - "); + clif->format_itemlink(&buf, &link_item); + StrBuf->Printf(&buf, " %02.02f%%", (float)droprate / 100); if (++j % 3 == 0) { - clif->message(fd, atcmd_output); - strcpy(atcmd_output, " "); + clif->message(fd, StrBuf->Value(&buf)); + StrBuf->Clear(&buf); } } if (j == 0) clif->message(fd, msg_fd(fd, MSGTBL_MOBINFO_NO_DROPS)); // This monster has no drops. else if (j % 3 != 0) - clif->message(fd, atcmd_output); + clif->message(fd, StrBuf->Value(&buf)); + StrBuf->Clear(&buf); // mvp if (monster->mexp) { snprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, MSGTBL_MOBINFO_MVP_BONUS_EXP), monster->mexp); // MVP Bonus EXP:%u @@ -7282,19 +7281,21 @@ ACMD(mobinfo) continue; if (monster->mvpitem[i].p > 0) { j++; - if(item_data->slot) - snprintf(atcmd_output2, sizeof(atcmd_output2), " %s%s[%d] %02.02f%%", j != 1 ? "- " : "", item_data->jname, item_data->slot, (float)monster->mvpitem[i].p / 100); - else - snprintf(atcmd_output2, sizeof(atcmd_output2), " %s%s %02.02f%%", j != 1 ? "- " : "", item_data->jname, (float)monster->mvpitem[i].p / 100); - strcat(atcmd_output, atcmd_output2); + struct item link_item = { 0 }; + link_item.nameid = monster->mvpitem[i].nameid; + StrBuf->AppendStr(&buf, j != 1 ? " - " : ""); + clif->format_itemlink(&buf, &link_item); + StrBuf->Printf(&buf, " %02.02f%%", (float)monster->mvpitem[i].p / 100); } } + if (j == 0) clif->message(fd, msg_fd(fd, MSGTBL_MOBINFO_NO_MVP_PRIZES)); // This monster has no MVP prizes. else - clif->message(fd, atcmd_output); + clif->message(fd, StrBuf->Value(&buf)); } } + StrBuf->Destroy(&buf); return true; } @@ -7717,14 +7718,22 @@ ACMD(iteminfo) clif->message(fd, atcmd_output); count = MAX_SEARCH; } + StringBuf buf; + StrBuf->Init(&buf); for (i = 0; i < count; i++) { struct item_data *item_data = item_array[i]; if (item_data != NULL) { - snprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, MSGTBL_ITEMINFO_DETAILS), // Item: '%s'/'%s'[%d] (%d) Type: %s | Extra Effect: %s - item_data->name, item_data->jname, item_data->slot, item_data->nameid, + + struct item link_item = { 0 }; + link_item.nameid = item_data->nameid; + clif->format_itemlink(&buf, &link_item); + + snprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, MSGTBL_ITEMINFO_DETAILS), // Item: '%s'/'%s' (%d) Type: %s | Extra Effect: %s + item_data->name, StrBuf->Value(&buf), item_data->nameid, itemdb->typename(item_data->type), (item_data->script == NULL) ? msg_fd(fd, MSGTBL_ITEMINFO_NONE) : msg_fd(fd, MSGTBL_ITEMINFO_WITH_SCRIPT) // None / With script ); + StrBuf->Clear(&buf); clif->message(fd, atcmd_output); snprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, MSGTBL_ITEMINFO_NPC_DETAILS), item_data->value_buy, item_data->value_sell, item_data->weight / 10.); // NPC Buy:%dz, Sell:%dz | Weight: %.1f @@ -7741,6 +7750,7 @@ ACMD(iteminfo) clif->message(fd, atcmd_output); } } + StrBuf->Destroy(&buf); return true; } diff --git a/src/map/script.c b/src/map/script.c index 359299bc31e..070c49b05c2 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -12284,36 +12284,87 @@ static BUILDIN(monster) *------------------------------------------*/ static BUILDIN(getmobdrops) { - int class_ = script_getnum(st,2); - int i, j = 0; - struct mob_db *monster; + struct map_session_data *sd = NULL; + int mob_id = script_getnum(st, 2); + struct mob_db *monster = NULL; + struct script_data *data1 = script_getdata(st, 3); + struct script_data *data2 = NULL; + const char *varname1 = NULL; + const char *varname2 = NULL; + int varid1 = 0; + int varid2 = 0; + int num = 0; - if( !mob->db_checkid(class_) ) - { + if (!data_isreference(data1) || reference_toconstant(data1)) { + ShowError("buildin_getmobdrops: Target argument must be a variable\n"); + script->reportdata(data1); + st->state = END; + return false; + } + + varname1 = reference_getname(data1); + varid1 = reference_getid(data1); + + if (!is_int_variable(varname1)) { + ShowError("buildin_getmobdrops: Target argument must be an integer variable\n"); + script->reportdata(data1); + st->state = END; + return false; + } + + if (script_hasdata(st, 4)) { + data2 = script_getdata(st, 4); + + if (!data_isreference(data2) || reference_toconstant(data2)) { + ShowError("buildin_getmobdrops: Target argument must be a variable\n"); + script->reportdata(data1); + st->state = END; + return false; + } + + varname2 = reference_getname(data2); + varid2 = reference_getid(data2); + + if (data2 == NULL || !is_int_variable(varname2)) { + ShowError("buildin_getmobdrops: 2nd target argument must be an integer variable\n"); + script->reportdata(data2); + st->state = END; + return false; + } + } + + if (not_server_variable(*varname1) || (data2 != NULL && not_server_variable(*varname2))) { + sd = script->rid2sd(st); + if (sd == NULL) { + script_pushint(st, 0); + return true; // player variable but no player attached + } + } + + monster = mob->db(mob_id); + + if (!mob->db_checkid(mob_id) || monster == NULL) { script_pushint(st, 0); return true; } - monster = mob->db(class_); - - for( i = 0; i < MAX_MOB_DROP; i++ ) - { - if( monster->dropitem[i].nameid < 1 ) + for (int i = 0; i < MAX_MOB_DROP; i++) { + if (monster->dropitem[i].nameid < 1) continue; - if( itemdb->exists(monster->dropitem[i].nameid) == NULL ) + if (itemdb->exists(monster->dropitem[i].nameid) == NULL) continue; - mapreg->setreg(reference_uid(script->add_variable("$@MobDrop_item"), j), monster->dropitem[i].nameid); - mapreg->setreg(reference_uid(script->add_variable("$@MobDrop_rate"), j), monster->dropitem[i].p); - - j++; + script->set_reg(st, sd, reference_uid(varid1, num), varname1, (const void *)h64BPTRSIZE(monster->dropitem[i].nameid), reference_getref(data1)); + if (data2 != NULL) + script->set_reg(st, sd, reference_uid(varid2, num), varname2, (const void *)h64BPTRSIZE(monster->dropitem[i].p), reference_getref(data2)); + num++; } - mapreg->setreg(script->add_variable("$@MobDrop_count"), j); - script_pushint(st, 1); + script_pushint(st, num); return true; } + /*========================================== * Same as monster but randomize location in x0,x1,y0,y1 area *------------------------------------------*/ @@ -29110,7 +29161,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(produce,"i"), BUILDIN_DEF(cooking,"i"), BUILDIN_DEF(monster,"siisii???"), - BUILDIN_DEF(getmobdrops,"i"), + BUILDIN_DEF(getmobdrops,"ii?"), BUILDIN_DEF(areamonster,"siiiisii???"), BUILDIN_DEF(killmonster,"ss?"), BUILDIN_DEF(killmonsterall,"s?"),