Преглед изворни кода

* 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

FlavioJS пре 18 година
родитељ
комит
f4907979c1
7 измењених фајлова са 432 додато и 236 уклоњено
  1. 5 0
      Changelog-Trunk.txt
  2. 36 20
      doc/script_commands.txt
  3. 25 0
      src/common/utils.c
  4. 2 0
      src/common/utils.h
  5. 1 1
      src/map/clif.c
  6. 1 1
      src/map/map.h
  7. 362 214
      src/map/script.c

+ 5 - 0
Changelog-Trunk.txt

@@ -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.

+ 36 - 20
doc/script_commands.txt

@@ -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.)
-
-       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
+This number is consistent with empty options and grouped options.
+
+       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.

+ 25 - 0
src/common/utils.c

@@ -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) 
 {

+ 2 - 0
src/common/utils.h

@@ -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 *);

+ 1 - 1
src/map/clif.c

@@ -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);

+ 1 - 1
src/map/map.h

@@ -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;

+ 362 - 214
src/map/script.c

@@ -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;
 }
 
-/*==========================================
- *
- *------------------------------------------
- */
-BUILDIN_FUNC(goto)
+/// 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)
 {
-	int pos;
+	TBL_PC* sd;
 
-	if( !data_islabel(script_getdata(st,2))){
-		ShowMessage("script: goto: not label!\n");
-		st->state=END;
+	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;
+}
 
-	pos=script_getnum(st,2);
-	st->pos=pos;
-	st->state=GOTO;
+/// 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)
+{
+	if( !data_islabel(script_getdata(st,2)) )
+	{
+		ShowError("script:goto: not label!\n");
+		st->state = END;
+		return 1;
+	}
+
+	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: