* Extended the functionality of StringBuf - length and appending a string.

* menu/select/prompt script functions support grouped and empty options.
  The selected option number is consistent with them.
* More work on ticket #41.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@10316 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
FlavioJS 2007-04-22 15:45:37 +00:00
parent 3ed79a6c03
commit f4907979c1
7 changed files with 431 additions and 235 deletions

View File

@ -3,6 +3,11 @@ Date Added
AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK.
IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
2007/04/23
* Extended the functionality of StringBuf - length and appending a string.
* menu/select/prompt script functions support grouped and empty options.
The selected option number is consistent with them.
* More work on ticket #41. [FlavioJS]
2007/04/22
* Corrected crash if itemskill is used without an attached player.
* Removed range checks for autospells as per UltraMage Aegis tests.

View File

@ -9,7 +9,7 @@
//= Maeki Rika - A section on general concepts and lots of
//= other updates and additions.
//===== Version ===========================================
//= 3.04.20070317
//= 3.05.20070423
//=========================================================
//= 1.0 - First release, filled will as much info as I could
//= remember or figure out, most likely there are errors,
@ -73,6 +73,9 @@
//= Adjusted the 'itemskill' description due to recent change [ultramage]
//= 3.04.20070409
//= Fixed the incorrect order of parameters in 'makeitem' [ultramage]
//= 3.05.20070423
//= menu/select/prompt produce consistent results for grouped and empty
//= options [FlavioJS]
//===== Description =======================================
//= A reference manual for the eAthena scripting language,
//= sorted out depending on their functionality.
@ -1116,7 +1119,7 @@ Note by FlavioJS: goto's are "evil" and should be avoided if possible (
---------------------------------------
*menu "<menu option>",<label>{,"<menu option>",<label>,...};
*menu "<option_text>",<target_label>{,"<option_text>",<target_label>,...};
This command will create a selectable menu for the invoking character. Only one
menu can be on screen at the same time.
@ -1125,25 +1128,40 @@ Depending on what the player picks from the menu, the script execution will
continue from the corresponding label. (it's string-label pairs, not label-
string)
Options can be grouped together, separated by the character ':'.
menu "A:B",L_Wrong,"C",L_Right;
It also sets a special temporary character variable @menu, which contains the
number of option the player picked. (Numbering of options starts at 1.)
This number is consistent with empty options and grouped options.
menu "I want to Start",L_Start,"I want to end",L_End;
L_Start:
//If they click "I want to Start" they will end up here
L_End:
//If they click "I want to end" they will end up here
menu "A::B",L_Wrong,"",L_Impossible,"C",L_Right;
L_Wrong:
// If they click "A" or "B" they will end up here
// @menu == 1 if "A"
// @menu == 2 will never happen because the option is empty
// @menu == 3 if "B"
L_Impossible:
// Empty options are not displayed and therefore can't be selected
// this label will never be reached from the menu command
L_Right:
// If they click "C" they will end up here
// @menu == 5
If a label is '-', the script execution will continue right after the menu
command if that option is selected, this can be used to save you time, and
optimize big scripts.
menu "I want to Start",-,"I want to end",L_End;
//If they click "I want to Start" they will end up here
L_End:
//If they click "I want to end" they will end up here
menu "A::B:",-,"C",L_Right;
// If they click "A" or "B" they will end up here
// @menu == 1 if "A"
// @menu == 3 if "B"
L_Right:
// If they click "C" they will end up here
// @menu == 5
Both these examples will perform the same task.
Both these examples will perform the exact same task.
If you give an empty string as a menu item, the item will not display. This
can effectively be used to script dynamic menus by using empty string for
@ -1242,8 +1260,8 @@ perfectly equivalent.
---------------------------------------
*select("<option>"{,"<option>"..."<option>"})
*prompt("<option>"{,"<option>"..."<option>"})
*select("<option>"{,"<option>",...})
*prompt("<option>"{,"<option>",...})
This function is a handy replacement for 'menu' for some specific cases where
you don't want a complex label structure - like, for example, asking simple yes-
@ -1251,12 +1269,10 @@ no questions. It will return the number of menu option picked, starting with 1.
Like 'menu', it will also set the variable @menu to contain the option the user
picked.
if (select("Yes","No")==1) mes "You said yes, I know.";
if (select("Yes:No")==1) mes "You said yes, I know.";
And like 'menu', this command has a problem with empty strings - if some of the
option strings given to it are empty, you won't be able to tell which one the
user really picked. The number it returns will only make sense if all the empty
strings are last in the list of options.
And like 'menu', the selected option is consistent with grouped options
and empty options.
prompt works almost the same as select, except that when a character clicks
the Cancel button, this function will return 255 instead.

View File

@ -123,6 +123,31 @@ int StringBuf_Append(struct StringBuf *buf1,const struct StringBuf *buf2)
return (int)(buf1->ptr_ - buf1->buf_);
}
// Appends str onto the end of buf
int StringBuf_AppendStr(struct StringBuf* sbuf, const char* str)
{
int available = sbuf->max_ - (sbuf->ptr_ - sbuf->buf_);
int size = (int)strlen(str);
if( size >= available )
{// not enough space, expand the buffer (minimum expansion = 1024)
int off = (int)(sbuf->ptr_ - sbuf->buf_);
sbuf->max_ += max(size, 1024);
sbuf->buf_ = (char *) aRealloc(sbuf->buf_, sbuf->max_ + 1);
sbuf->ptr_ = sbuf->buf_ + off;
}
memcpy(sbuf->ptr_, str, size);
sbuf->ptr_ += size;
return (int)(sbuf->ptr_ - sbuf->buf_);
}
// Returns the length of the data in a Stringbuf
int StringBuf_Length(struct StringBuf *sbuf)
{
return (int)(sbuf->ptr_ - sbuf->buf_);
}
// Destroy a StringBuf [MouseJstr]
void StringBuf_Destroy(struct StringBuf *sbuf)
{

View File

@ -23,6 +23,8 @@ void StringBuf_Init(struct StringBuf *);
int StringBuf_Vprintf(struct StringBuf *,const char *,va_list);
int StringBuf_Printf(struct StringBuf *,const char *,...);
int StringBuf_Append(struct StringBuf *,const struct StringBuf *);
int StringBuf_AppendStr(struct StringBuf* sbuf, const char* str);
int StringBuf_Length(struct StringBuf* sbuf);
char * StringBuf_Value(struct StringBuf *);
void StringBuf_Destroy(struct StringBuf *);
void StringBuf_Free(struct StringBuf *);

View File

@ -10014,7 +10014,7 @@ void clif_parse_WeaponRefine(int fd, struct map_session_data *sd) {
*/
void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd)
{
unsigned char select;
uint8 select;
RFIFOHEAD(fd);
select = RFIFOB(fd,6);

View File

@ -523,7 +523,7 @@ struct map_session_data {
//status_calc_pc, while special_state is recalculated in each call. [Skotlex]
struct {
unsigned auth : 1;
unsigned menu_or_input : 1;
unsigned menu_or_input : 1;// if a script is waiting for feedback from the player
unsigned dead_sit : 2;
unsigned waitingdisconnect : 1;
unsigned lr_flag : 2;

View File

@ -3876,11 +3876,15 @@ BUILDIN_FUNC(deletepset); // MouseJstr
#endif
struct script_function buildin_func[] = {
// NPC interaction
BUILDIN_DEF(mes,"s"),
BUILDIN_DEF(next,""),
BUILDIN_DEF(close,""),
BUILDIN_DEF(close2,""),
BUILDIN_DEF(menu,"*"),
BUILDIN_DEF(menu,"sl*"),
BUILDIN_DEF(select,"s*"), //for future jA script compatibility
BUILDIN_DEF(prompt,"s*"),
//
BUILDIN_DEF(goto,"l"),
BUILDIN_DEF(callsub,"i*"),
BUILDIN_DEF(callfunc,"s*"),
@ -4130,8 +4134,6 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(getpetinfo,"i"),
BUILDIN_DEF(checkequipedcard,"i"),
BUILDIN_DEF(jump_zero,"ii"), //for future jA script compatibility
BUILDIN_DEF(select,"*"), //for future jA script compatibility
BUILDIN_DEF(prompt,"*"),
BUILDIN_DEF(globalmes,"s*"),
BUILDIN_DEF(getmapmobs,"s"), //end jA addition
BUILDIN_DEF(unequip,"i"), // unequip command [Spectre]
@ -4209,36 +4211,373 @@ struct script_function buildin_func[] = {
{NULL,NULL,NULL},
};
/*==========================================
*
*------------------------------------------
*/
/////////////////////////////////////////////////////////////////////
// NPC interaction
//
/// Appends a message to the npc dialog.
/// If a dialog doesn't exist yet, one is created.
/// TODO does the client support dialogs with different oid's at the same time?
///
/// mes "<message>";
BUILDIN_FUNC(mes)
{
struct map_session_data *sd = script_rid2sd(st);
const char *mes = script_getstr(st, 2);
if (sd)
clif_scriptmes(sd, st->oid, mes);
TBL_PC* sd;
const char* msg;
sd = script_rid2sd(st);
if( sd == NULL )
return 1;
msg = script_getstr(st, 2);
clif_scriptmes(sd, st->oid, msg);
return 0;
}
/*==========================================
*
*------------------------------------------
*/
/// Displays the button 'next' in the npc dialog.
/// The dialog text is cleared and the script continues when the button is pressed.
///
/// next;
BUILDIN_FUNC(next)
{
TBL_PC* sd;
sd = script_rid2sd(st);
if( sd == NULL )
return 1;
st->state = STOP;
clif_scriptnext(sd, st->oid);
return 0;
}
/// Ends the script and displays the button 'close' on the npc dialog.
/// The dialog is closed when the button is pressed.
///
/// close;
BUILDIN_FUNC(close)
{
TBL_PC* sd;
sd = script_rid2sd(st);
if( sd == NULL )
return 1;
st->state = END;
clif_scriptclose(sd, st->oid);
return 0;
}
/// Displays the button 'close' on the npc dialog.
/// The dialog is closed and the script continues when the button is pressed.
///
/// close2;
BUILDIN_FUNC(close2)
{
TBL_PC* sd;
sd = script_rid2sd(st);
if( sd == NULL )
return 1;
st->state = STOP;
clif_scriptclose(sd, st->oid);
return 0;
}
/// Counts the number of valid and total number of options in 'str'
/// If max_count > 0 the counting stops when that valid option is reached
/// total is incremented for each option (NULL is supported)
static int menu_countoptions(const char* str, int max_count, int* total)
{
int count = 0;
int bogus_total;
if( total == NULL )
total = &bogus_total;
++(*total);
// initial empty options
while( *str == ':' )
{
++str;
++(*total);
}
// count menu options
while( *str != '\0' )
{
++count;
--max_count;
if( max_count == 0 )
break;
while( *str != ':' && *str != '\0' )
++str;
while( *str == ':' )
{
++str;
++(*total);
}
}
return count;
}
/// Displays a menu with options and goes to the target label.
/// The script is stopped if cancel is pressed.
/// Options with no text are not displayed in the client.
///
/// Options can be grouped together, separated by the character ':' in the text:
/// ex: menu "A:B:C",L_target;
/// All these options go to the specified target label.
///
/// The index of the selected option is put in the variable @menu.
/// Indexes start with 1 and are consistent with grouped and empty options.
/// ex: menu "A::B",-,"",L_Impossible,"C",-;
/// // displays "A", "B" and "C", corresponding to indexes 1, 3 and 5
///
/// NOTE: the client closes the npc dialog when cancel is pressed
///
/// menu "<option_text>",<target_label>{,"<option_text>",<target_label>,...};
BUILDIN_FUNC(menu)
{
int i;
const char* text;
TBL_PC* sd;
sd = script_rid2sd(st);
if( sd == NULL )
return 1;
// TODO detect multiple scripts waiting for input at the same time, and what to do when that happens
if( sd->state.menu_or_input == 0 )
{
struct StringBuf* buf;
struct script_data* data;
if( script_lastdata(st) % 2 == 0 )
{// argument count is not even (1st argument is at index 2)
ShowError("script:menu: illegal number of arguments (%d).\n", (script_lastdata(st) - 1));
st->state = END;
return 1;
}
buf = StringBuf_Malloc();
for( i = 2, sd->npc_menu = 0; i < script_lastdata(st); i += 2 )
{
// menu options
data = script_getdata(st, i);
get_val(st, data);
if( data_isstring(data) && data_isint(data) )
{// not a string (or compatible)
StringBuf_Free(buf);
ShowError("script:menu: argument #%d (from 1) is not a string or compatible.\n", (i - 1));
st->state = END;
return 1;
}
text = conv_str(st, data);// convert to string
// target label
data = script_getdata(st, i+1);
if( !data_islabel(data) )
{// not a label
StringBuf_Free(buf);
ShowError("script:menu: label argument of menu option #%d (from 1) is not a label.\n", (script_lastdata(st) - 1));
st->state = END;
return 1;
}
// append option(s)
if( text[0] == '\0' )
continue;// empty string, ignore
if( sd->npc_menu > 0 )
StringBuf_AppendStr(buf, ":");
StringBuf_AppendStr(buf, text);
sd->npc_menu += menu_countoptions(text, 0, NULL);
}
st->state = RERUNLINE;
sd->state.menu_or_input = 1;
clif_scriptmenu(sd, st->oid, StringBuf_Value(buf));
StringBuf_Free(buf);
//TODO what's the maximum number of options that can be displayed and/or received? -> give warning
}
else if( sd->npc_menu == 0xff )
{// Cancel was pressed
sd->state.menu_or_input = 0;
st->state = END;
}
else
{// goto target label
int menu = 0;
sd->state.menu_or_input = 0;
if( sd->npc_menu <= 0 )
{
ShowDebug("script:menu: unexpected selection (%d)\n", sd->npc_menu);
st->state = END;
return 1;
}
// get target label
for( i = 2; i < script_lastdata(st); i += 2 )
{
text = script_getstr(st, i);
sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
if( sd->npc_menu <= 0 )
break;// entry found
}
if( sd->npc_menu > 0 )
{// Invalid selection
ShowDebug("script:menu: selection is out of range, expected %d extra menu options\n", sd->npc_menu);
st->state = END;
return 1;
}
if( !data_islabel(script_getdata(st, i + 1)) )
{// TODO remove this temporary crash-prevention code (fallback for multiple scripts requesting user input)
st->state = END;
return 0;
}
pc_setreg(sd, add_str("@menu"), menu);
st->pos = script_getnum(st, i + 1);
st->state = GOTO;
}
return 0;
}
/// Displays a menu with options and returns the selected option.
/// Behaves like 'menu' without the target labels.
///
/// select(<option_text>{,<option_text>,...}) -> <selected_option>
///
/// @see menu
BUILDIN_FUNC(select)
{
int i;
const char* text;
TBL_PC* sd;
sd = script_rid2sd(st);
if( sd == NULL )
return 1;
if( sd->state.menu_or_input == 0 )
{
struct StringBuf* buf;
buf = StringBuf_Malloc();
for( i = 2, sd->npc_menu = 0; i <= script_lastdata(st); ++i )
{
text = script_getstr(st, i);
if( sd->npc_menu > 0 )
StringBuf_AppendStr(buf, ":");
StringBuf_AppendStr(buf, script_getstr(st, i));
sd ->npc_menu += menu_countoptions(text, 0, NULL);
}
st->state = RERUNLINE;
sd->state.menu_or_input = 1;
clif_scriptmenu(sd, st->oid, StringBuf_Value(buf));
StringBuf_Free(buf);
}
else if( sd->npc_menu == 0xff )
{// Cancel was pressed
sd->state.menu_or_input = 0;
st->state = END;
}
else
{// return selected option
int menu = 0;
sd->state.menu_or_input = 0;
for( i = 2; i <= script_lastdata(st); ++i )
{
text = script_getstr(st, i);
sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
if( sd->npc_menu <= 0 )
break;// entry found
}
pc_setreg(sd, add_str("@menu"), menu);
script_pushint(st, menu);
}
return 0;
}
/// Displays a menu with options and returns the selected option.
/// Behaves like 'menu' without the target labels, except when cancel is
/// pressed.
/// When cancel is pressed, the script continues and 255 is returned.
///
/// prompt(<option_text>{,<option_text>,...}) -> <selected_option>
///
/// @see menu
BUILDIN_FUNC(prompt)
{
int i;
const char *text;
TBL_PC* sd;
sd = script_rid2sd(st);
if( sd == NULL )
return 1;
if( sd->state.menu_or_input == 0 )
{
struct StringBuf* buf;
buf = StringBuf_Malloc();
for( i = 2, sd->npc_menu = 0; i <= script_lastdata(st); ++i )
{
text = script_getstr(st, i);
if( sd->npc_menu > 0 )
StringBuf_AppendStr(buf, ":");
StringBuf_AppendStr(buf, script_getstr(st, i));
sd ->npc_menu += menu_countoptions(text, 0, NULL);
}
st->state = RERUNLINE;
sd->state.menu_or_input = 1;
clif_scriptmenu(sd, st->oid, StringBuf_Value(buf));
StringBuf_Free(buf);
}
else if( sd->npc_menu == 0xff )
{// Cancel was pressed
sd->state.menu_or_input = 0;
pc_setreg(sd, add_str("@menu"), 0xff);
script_pushint(st, 0xff);
}
else
{// return selected option
int menu = 0;
sd->state.menu_or_input = 0;
for( i = 2; i <= script_lastdata(st); ++i )
{
text = script_getstr(st, i);
sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
if( sd->npc_menu <= 0 )
break;// entry found
}
pc_setreg(sd, add_str("@menu"), menu);
script_pushint(st, menu);
}
return 0;
}
/////////////////////////////////////////////////////////////////////
// ...
//
/// Jumps to the target script label.
///
/// goto <label>;
BUILDIN_FUNC(goto)
{
int pos;
if( !data_islabel(script_getdata(st,2))){
ShowMessage("script: goto: not label!\n");
st->state=END;
if( !data_islabel(script_getdata(st,2)) )
{
ShowError("script:goto: not label!\n");
st->state = END;
return 1;
}
pos=script_getnum(st,2);
st->pos=pos;
st->state=GOTO;
st->pos = script_getnum(st,2);
st->state = GOTO;
return 0;
}
@ -4390,105 +4729,6 @@ BUILDIN_FUNC(return)
return 0;
}
/*==========================================
*
*------------------------------------------
*/
BUILDIN_FUNC(next)
{
st->state=STOP;
clif_scriptnext(script_rid2sd(st),st->oid);
return 0;
}
/*==========================================
*
*------------------------------------------
*/
BUILDIN_FUNC(close)
{
st->state=END;
clif_scriptclose(script_rid2sd(st),st->oid);
return 0;
}
BUILDIN_FUNC(close2)
{
st->state=STOP;
clif_scriptclose(script_rid2sd(st),st->oid);
return 0;
}
/*==========================================
*
*------------------------------------------
*/
BUILDIN_FUNC(menu)
{
char *buf, *ptr;
int len,i;
struct map_session_data *sd = script_rid2sd(st);
nullpo_retr(0, sd);
if(sd->state.menu_or_input==0){
st->state=RERUNLINE;
sd->state.menu_or_input=1;
if( (st->end - st->start - 2) % 2 == 1 ) {
// 引数の数が奇数なのでエラー扱い
ShowError("buildin_menu: illegal argument count(%d).\n", st->end - st->start - 2);
sd->state.menu_or_input=0;
st->state=END;
return 1;
}
for(i=st->start+2,len=0;i<st->end;i+=2){
conv_str(st,& (st->stack->stack_data[i]));
len+=(int)strlen(st->stack->stack_data[i].u.str)+1;
}
buf=(char *)aMallocA((len+1)*sizeof(char));
buf[0]=0;
for(i=st->start+2,len=0;i<st->end;i+=2){
if( st->stack->stack_data[i].u.str[0] ) {
strcat(buf,st->stack->stack_data[i].u.str);
strcat(buf,":");
}
}
ptr = buf;
sd->npc_menu = 0; //Reuse to store max menu entries. Avoids the need of an extra variable.
while (ptr && (ptr = strchr(ptr, ':')) != NULL)
{ sd->npc_menu++; ptr++; }
clif_scriptmenu(sd,st->oid,buf);
aFree(buf);
} else if(sd->npc_menu==0xff){ // cancel
sd->state.menu_or_input=0;
st->state=END;
} else { // goto動作
sd->state.menu_or_input=0;
if(sd->npc_menu>0){
//Skip empty menu entries which weren't displayed on the client (blackhole89)
for(i=st->start+2;i<=(st->start+sd->npc_menu*2) && sd->npc_menu<(st->end-st->start)/2;i+=2) {
conv_str(st,& (st->stack->stack_data[i])); // we should convert variables to strings before access it [jA1983] [EoE]
if((int)strlen(st->stack->stack_data[i].u.str) < 1)
sd->npc_menu++; //Empty selection which wasn't displayed on the client.
}
if(sd->npc_menu >= (st->end-st->start)/2) {
//Invalid selection.
st->state=END;
return 0;
}
if( !data_islabel(script_getdata(st, sd->npc_menu*2+1)) ){
ShowError("script: menu: not label !\n");
st->state=END;
return 1;
}
pc_setreg(sd,add_str("@menu"),sd->npc_menu);
st->pos=script_getnum(st,sd->npc_menu*2+1);
st->state=GOTO;
}
}
return 0;
}
/// Returns a random number from 0 to <range>-1.
/// Or returns a random number from <min> to <max>.
/// If <min> is greater than <max>, their numbers are switched.
@ -10757,98 +10997,6 @@ BUILDIN_FUNC(jump_zero)
return 0;
}
BUILDIN_FUNC(select)
{
char *buf, *ptr;
int len,i;
struct map_session_data *sd;
sd=script_rid2sd(st);
nullpo_retr(0, sd);
if(sd->state.menu_or_input==0){
st->state=RERUNLINE;
sd->state.menu_or_input=1;
for(i=st->start+2,len=16;i<st->end;i++){
conv_str(st,& (st->stack->stack_data[i]));
len+=(int)strlen(st->stack->stack_data[i].u.str)+1;
}
buf=(char *)aMalloc((len+1)*sizeof(char));
buf[0]=0;
for(i=st->start+2,len=0;i<st->end;i++){
strcat(buf,st->stack->stack_data[i].u.str);
strcat(buf,":");
}
ptr = buf;
sd->npc_menu = 0; //Reuse to store max menu entries. Avoids the need of an extra variable.
while (ptr && (ptr = strchr(ptr, ':')) != NULL)
{ sd->npc_menu++; ptr++; }
clif_scriptmenu(sd,st->oid,buf);
aFree(buf);
} else if(sd->npc_menu==0xff){
sd->state.menu_or_input=0;
st->state=END;
} else {
//Skip empty menu entries which weren't displayed on the client (Skotlex)
for(i=st->start+2;i< (st->start+2+sd->npc_menu) && sd->npc_menu < (st->end-st->start-2);i++) {
conv_str(st,& (st->stack->stack_data[i])); // we should convert variables to strings before access it [jA1983] [EoE]
if((int)strlen(st->stack->stack_data[i].u.str) < 1)
sd->npc_menu++; //Empty selection which wasn't displayed on the client.
}
pc_setreg(sd,add_str("@menu"),sd->npc_menu);
sd->state.menu_or_input=0;
script_pushint(st,sd->npc_menu);
}
return 0;
}
BUILDIN_FUNC(prompt)
{
char *buf, *ptr;
int len,i;
struct map_session_data *sd;
sd=script_rid2sd(st);
nullpo_retr(0, sd);
if(sd->state.menu_or_input==0){
st->state=RERUNLINE;
sd->state.menu_or_input=1;
for(i=st->start+2,len=16;i<st->end;i++){
conv_str(st,& (st->stack->stack_data[i]));
len+=(int)strlen(st->stack->stack_data[i].u.str)+1;
}
buf=(char *)aMalloc((len+1)*sizeof(char));
buf[0]=0;
for(i=st->start+2,len=0;i<st->end;i++){
strcat(buf,st->stack->stack_data[i].u.str);
strcat(buf,":");
}
ptr = buf;
sd->npc_menu = 0; //Reuse to store max menu entries. Avoids the need of an extra variable.
while (ptr && (ptr = strchr(ptr, ':')) != NULL)
{ sd->npc_menu++; ptr++; }
clif_scriptmenu(sd,st->oid,buf);
aFree(buf);
} else {
if(sd->npc_menu != 0xff){
//Skip empty menu entries which weren't displayed on the client (Skotlex)
for(i=st->start+2;i< (st->start+2+sd->npc_menu) && sd->npc_menu < (st->end-st->start-2);i++) {
conv_str(st,& (st->stack->stack_data[i])); // we should convert variables to strings before access it [jA1983] [EoE]
if((int)strlen(st->stack->stack_data[i].u.str) < 1)
sd->npc_menu++; //Empty selection which wasn't displayed on the client.
}
}
pc_setreg(sd,add_str("@menu"),sd->npc_menu);
sd->state.menu_or_input=0;
script_pushint(st,sd->npc_menu);
}
return 0;
}
/*==========================================
* GetMapMobs
returns mob counts on a set map: