Skip to content

Commit

Permalink
Merge pull request #3236 from guilherme-gm/itemlink
Browse files Browse the repository at this point in the history
Add logic to generate item links and getitemlink script command
  • Loading branch information
MishimaHaruna authored Oct 16, 2023
2 parents bc54b2d + 06ed027 commit 3ac3c97
Show file tree
Hide file tree
Showing 37 changed files with 897 additions and 5 deletions.
44 changes: 44 additions & 0 deletions doc/script_commands.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3547,6 +3547,50 @@ Example:

---------------------------------------

*getitemlink(<item_id>{, <refine = 0>{, <cards_array = 0>{, <options_array = 0>{, <grade = 0>}}}})
*getitemlink("<item_name>"{, <refine = 0>{, <cards_array = 0>{, <options_array = 0>{, <grade = 0>}}}})

Get specially formatted client string that represents an item with the given properties.

Parameters:
- item_id: Item DB id. May be the constant (e.g.: 4001 or Poring_Card)
- item_name: Item DB name.
- refine: Item refine amount (optional, defaults to 0)
- cards_array: An array containing the cards item IDs. (optional, defaults to 0 / no cards)
- options_array: An array containing option information. (optional, defaults to 0 / no options)
Structure: <idx1>, <val1>, <param1>, <idx2>, <val2>, <param2>, ... <idx5>, <val5>, <param5>
- grade: Item grade (optional, defaults to 0)


Example:

setarray(.@cards, Fabre_Card, 0, 0, Agility1);
setarray(.@options,
VAR_MAXHPAMOUNT, 10, 0, // option 1
VAR_MAXSPAMOUNT, 20, 0 // option 2
);

// In a recent client, shows something like:
// Here is my very dangerous item: <+10 Vital Knife [Agi +1]>
//
// The text between <> is made by the client and is clickable, showing the item options and grade too.
dispbottom(sprintf("Here is my very dangerous item: %s", getitemlink(Knife, 10, .@cards, .@options, 3)));
end;


Client support:
- PACKETVER_MAIN_NUM < 20150923 and PACKETVER_RE_NUM < 20150819
Only item name is returned. As pure string.
- PACKETVER_MAIN_NUM < 20200916, PACKETVER_RE_NUM < 20200723, PACKETVER_ZERO
Grade is ignored. Returns a clickable text.
- PACKETVER_MAIN_NUM >= 20200916, PACKETVER_RE_NUM >= 20200723
Grade is also supported. Returns a clickable text.

Note: The clickable text also depends on where it is used. For example, a "mes" will properly format the item name,
but it won't be clickable. While a "dispbottom" shows it formatted and clickable. This is client-defined.

---------------------------------------

*getequipisenableopt(<equipment slot>)

This function checks if the equipped item allows the use of bonus options.
Expand Down
93 changes: 93 additions & 0 deletions npc/dev/test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,94 @@ function script F_TestCalendarNextTime {
return .@local_check && .@utc_check;
}

function script F_TestGetItemLink_Etc {
.@str$ = getitemlink(Jellopy, 0, 0, 0, 0);
.@str2$ = getitemlink(Jellopy);

// Sanity check, optional parameters should not affect result
if (.@str$ != .@str2$) {
return false;
}

.@pass = false;
if (PACKETVER < 20150923) {
.@pass = (.@str$ == getiteminfo(Jellopy, ITEMINFO_NAME));
} else if (PACKETVER < 20161116) {
.@pass = (.@str$ == "<ITEM>000000eF'00'00'00'00</ITEM>");
} else if (PACKETVER < 20200916) {
.@pass = (.@str$ == "<ITEML>000000eF&00(00(00(00(00</ITEML>");
} else {
.@pass = (.@str$ == "<ITEML>000000eF&00'00)00)00)00)00</ITEML>");
}

return .@pass;
}

function script F_TestGetItemLink_Headgear {
.@str$ = getitemlink(Hat, 0, 0, 0, 0);
.@str2$ = getitemlink(Hat);

// Sanity check, optional parameters should not affect result
if (.@str$ != .@str2$) {
return false;
}

.@pass = false;
if (PACKETVER < 20150923) {
.@pass = (.@str$ == getiteminfo(Hat, ITEMINFO_NAME));
} else if (PACKETVER < 20161116) {
.@pass = (.@str$ == "<ITEM>000481zO'00'00'00'00</ITEM>");
} else if (PACKETVER < 20200916) {
.@pass = (.@str$ == "<ITEML>000481zO&0g(00(00(00(00</ITEML>");
} else {
.@pass = (.@str$ == "<ITEML>000481zO&0g'00)00)00)00)00</ITEML>");
}

return .@pass;
}

function script F_TestGetItemLink_BaseWeapon {
.@str$ = getitemlink(Knife, 0, 0, 0, 0);
.@str2$ = getitemlink(Knife);

// Sanity check, optional parameters should not affect result
if (.@str$ != .@str2$) {
return false;
}

.@pass = false;
if (PACKETVER < 20150923) {
.@pass = (.@str$ == getiteminfo(Knife, ITEMINFO_NAME));
} else if (PACKETVER < 20161116) {
.@pass = (.@str$ == "<ITEM>000021jn'00'00'00'00</ITEM>");
} else if (PACKETVER < 20200916) {
.@pass = (.@str$ == "<ITEML>000021jn&00(00(00(00(00</ITEML>");
} else {
.@pass = (.@str$ == "<ITEML>000021jn&00'00)00)00)00)00</ITEML>");
}

return .@pass;
}

function script F_TestGetItemLink_FullWeapon {
setarray(.@cards[0], Fabre_Card, Captain_Felock_Card, 0, Agility1);
setarray(.@options[0], WEAPON_ATTR_GROUND, 1, 0, VAR_MAXHPAMOUNT, 10, 0);
.@str$ = getitemlink(Knife, 10, .@cards, .@options, 2);

.@pass = false;
if (PACKETVER < 20150923) {
.@pass = (.@str$ == getiteminfo(Knife, ITEMINFO_NAME));
} else if (PACKETVER < 20161116) {
.@pass = (.@str$ == "<ITEM>000021jn%0a'12y'74q'00'1ei)2R*00+01)01*00+0a</ITEM>");
} else if (PACKETVER < 20200916) {
.@pass = (.@str$ == "<ITEML>000021jn%0a&00(12y(74q(00(1ei*2R+00,01*01+00,0a</ITEML>");
} else {
.@pass = (.@str$ == "<ITEML>000021jn%0a&00'02)12y)74q)00)1ei+2R,00-01+01,00-0a</ITEML>");
}

return .@pass;
}

function script HerculesSelfTestHelper {
if (.once > 0)
return .errors;
Expand Down Expand Up @@ -885,6 +973,11 @@ function script HerculesSelfTestHelper {

callsub(OnCheck, "getcalendartime: next occurence of current Hour/Minute", F_TestCalendarNextTime(), true);

callsub(OnCheck, "getitemlink: etc tag", F_TestGetItemLink_Etc(), true);
callsub(OnCheck, "getitemlink: headgear tag", F_TestGetItemLink_Headgear(), true);
callsub(OnCheck, "getitemlink: basic weapon tag", F_TestGetItemLink_BaseWeapon(), true);
callsub(OnCheck, "getitemlink: complete weapon tag", F_TestGetItemLink_FullWeapon(), true);

if (.errors) {
consolemes(CONSOLEMES_DEBUG, "Script engine self-test [ \033[0;31mFAILED\033[0m ]");
consolemes(CONSOLEMES_DEBUG, "**** The test was completed with " + .errors + " errors. ****");
Expand Down
1 change: 1 addition & 0 deletions src/api/HPMapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "common/cbasetypes.h"

#include "common/HPMi.h"
#include "common/base62.h"
#include "common/chunked/rfifo.h"
#include "common/conf.h"
#include "common/console.h"
Expand Down
1 change: 1 addition & 0 deletions src/char/HPMchar.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "char/pincode.h"

#include "common/HPMi.h"
#include "common/base62.h"
#include "common/conf.h"
#include "common/console.h"
#include "common/core.h"
Expand Down
1 change: 1 addition & 0 deletions src/common/HPM.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "config/core.h" // CONSOLE_INPUT
#include "HPM.h"

#include "common/base62.h"
#include "common/cbasetypes.h"
#include "common/conf.h"
#include "common/console.h"
Expand Down
5 changes: 5 additions & 0 deletions src/common/HPMDataCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,11 @@ HPExport const struct s_HPMDataCheck HPMDataCheck[] = {
#else
#define COMMON_APIPACKETS_H
#endif // COMMON_APIPACKETS_H
#ifdef COMMON_BASE62_H
{ "base62_interface", sizeof(struct base62_interface), SERVER_TYPE_ALL },
#else
#define COMMON_BASE62_H
#endif // COMMON_BASE62_H
#ifdef COMMON_CHARMAPPACKETS_H
{ "PACKET_CHARMAP_AGENCY_JOIN_PARTY", sizeof(struct PACKET_CHARMAP_AGENCY_JOIN_PARTY), SERVER_TYPE_ALL },
{ "PACKET_CHARMAP_GUILD_EMBLEM", sizeof(struct PACKET_CHARMAP_GUILD_EMBLEM), SERVER_TYPE_ALL },
Expand Down
7 changes: 7 additions & 0 deletions src/common/HPMSymbols.inc.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ struct api_interface *api;
#ifdef MAP_ATCOMMAND_H /* atcommand */
struct atcommand_interface *atcommand;
#endif // MAP_ATCOMMAND_H
#ifdef COMMON_BASE62_H /* base62 */
struct base62_interface *base62;
#endif // COMMON_BASE62_H
#ifdef MAP_BATTLE_H /* battle */
struct battle_interface *battle;
#endif // MAP_BATTLE_H
Expand Down Expand Up @@ -397,6 +400,10 @@ HPExport const char *HPM_shared_symbols(int server_type)
if ((server_type&(SERVER_TYPE_MAP)) != 0 && !HPM_SYMBOL("atcommand", atcommand))
return "atcommand";
#endif // MAP_ATCOMMAND_H
#ifdef COMMON_BASE62_H /* base62 */
if ((server_type&(SERVER_TYPE_ALL)) != 0 && !HPM_SYMBOL("base62", base62))
return "base62";
#endif // COMMON_BASE62_H
#ifdef MAP_BATTLE_H /* battle */
if ((server_type&(SERVER_TYPE_MAP)) != 0 && !HPM_SYMBOL("battle", battle))
return "battle";
Expand Down
4 changes: 2 additions & 2 deletions src/common/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ MT19937AR_D = $(THIRDPARTY_D)/mt19937ar
MT19937AR_OBJ = $(MT19937AR_D)/mt19937ar.o
MT19937AR_H = $(MT19937AR_D)/mt19937ar.h

COMMON_SHARED_C = conf.c db.c des.c ers.c extraconf.c grfio.c HPM.c mapindex.c md5calc.c \
COMMON_SHARED_C = base62.c conf.c db.c des.c ers.c extraconf.c grfio.c HPM.c mapindex.c md5calc.c \
mutex.c nullpo.c packets.c random.c showmsg.c strlib.c \
sysinfo.c thread.c timer.c utils.c
COMMON_C = $(COMMON_SHARED_C)
COMMON_SHARED_OBJ = $(patsubst %.c,%.o,$(COMMON_SHARED_C))
COMMON_OBJ = $(addprefix obj_all/, $(COMMON_SHARED_OBJ) \
console.o core.o memmgr.o socket.o)
COMMON_C += console.c core.c memmgr.c socket.c
COMMON_H = atomic.h cbasetypes.h conf.h console.h core.h db.h des.h ers.h extraconf.h \
COMMON_H = atomic.h cbasetypes.h base62.h conf.h console.h core.h db.h des.h ers.h extraconf.h \
grfio.h hercules.h HPM.h HPMi.h memmgr.h memmgr_inc.h mapindex.h \
md5calc.h mmo.h mutex.h nullpo.h packets.h packets_len.h packets_struct.h random.h \
showmsg.h socket.h spinlock.h sql.h strlib.h sysinfo.h thread.h \
Expand Down
108 changes: 108 additions & 0 deletions src/common/base62.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* This file is part of Hercules.
* http://herc.ws - http://github.com/HerculesWS/Hercules
*
* Copyright (C) 2023 Hercules Dev Team
* Copyright (C) 2023 KirieZ
*
* Hercules is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define HERCULES_CORE

#include "base62.h"

#include "common/nullpo.h"
#include "common/utils.h"

static struct base62_interface base62_s;
struct base62_interface *base62;

/**
* Base 62 conversion table
*/
static char base62tbl[62] = {
'0','1','2','3','4','5','6','7','8',
'9','a','b','c','d','e','f','g','h',
'i','j','k','l','m','n','o','p','q',
'r','s','t','u','v','w','x','y','z',
'A','B','C','D','E','F','G','H','I',
'J','K','L','M','N','O','P','Q','R',
'S','T','U','V','W','X','Y','Z'
};

/**
* base62-Encode `value` into `buf`, which has a size of `buf_len`.
* If the base62 string is shorter than `min_len`, left pads it with 0 characters.
*
* @remark
* - `buf` will be NULL-terminated, so the maximum number of characters in buffer is always `buf_len` - 1.
*
* @param value value to be encoded
* @param buf buffer where to write the values
* @param min_len minimum length of the string (left padded with 0 when needed)
* @param buf_len buffer size (including the NULL termination)
* @returns true if value was fully encoded, false if something went wrong
*/
static bool base62_encode_int_padded(int value, char *buf, int min_len, int buf_len)
{
nullpo_retr(false, buf);
Assert_retr(false, min_len < buf_len);
Assert_retr(false, buf_len >= 2);

// if caller ignores an error, at least it gets a NULL-terminated string
buf[0] = '\0';

char temp_buf[BASE62_INT_BUFFER_LEN] = { 0 };
int max_idx = cap_value(buf_len - 2, 0, BASE62_INT_BUFFER_LEN - 2);

int idx = 0;
do {
temp_buf[idx] = base62tbl[value % 62];
value /= 62;
idx++;
} while (idx <= max_idx && value > 0);

Assert_retr(false, (value == 0));

int final_buf_idx = 0;

if (idx < min_len) {
int pad_size = min_len - idx;
for (int i = 0; i < pad_size; ++i) {
buf[final_buf_idx] = base62tbl[0];
final_buf_idx++;
}
}

idx--;

// reverse the string to get the base62-encoded value.
while (idx >= 0) {
buf[final_buf_idx] = temp_buf[idx];
final_buf_idx++;
idx--;
}
buf[final_buf_idx] = '\0';

return (value == 0);
}

/**
*
**/
void base62_defaults(void)
{
base62 = &base62_s;
base62->encode_int_padded = base62_encode_int_padded;
}
43 changes: 43 additions & 0 deletions src/common/base62.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* This file is part of Hercules.
* http://herc.ws - http://github.com/HerculesWS/Hercules
*
* Copyright (C) 2023-2023 Hercules Dev Team
*
* Hercules is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMMON_BASE62_H
#define COMMON_BASE62_H

#include "common/hercules.h"

/**
* Minimum size for a buffer to encode UINT32_MAX, including NULL-terminator
*/
#define BASE62_INT_BUFFER_LEN 7

/**
* The base62 interface
**/
struct base62_interface {
bool (*encode_int_padded) (int value, char *buf, int min_len, int buf_len);
};

#ifdef HERCULES_CORE
void base62_defaults(void);
#endif // HERCULES_CORE

HPShared struct base62_interface *base62;

#endif // COMMON_BASE62_H
Loading

0 comments on commit 3ac3c97

Please sign in to comment.