Bläddra i källkod

Implemented @warp suggestions.
This will display map name suggestions when attempting to @warp to a non-existent map.
To enable, turn on 'feature.warp_suggestions' in 'conf/battle/feature.conf'.

Signed-off-by: Euphy <euphy.raliel@rathena.org>

Euphy 11 år sedan
förälder
incheckning
f3777cce0e
7 ändrade filer med 110 tillägg och 1 borttagningar
  1. 4 0
      conf/battle/feature.conf
  2. 25 0
      src/common/utils.c
  3. 2 0
      src/common/utils.h
  4. 1 1
      src/config/core.h
  5. 76 0
      src/map/atcommand.c
  6. 1 0
      src/map/battle.c
  7. 1 0
      src/map/battle.h

+ 4 - 0
conf/battle/feature.conf

@@ -19,6 +19,10 @@ feature.search_stores: on
 // Show suggestions when typing an incomplete command?
 feature.atcommand_suggestions: off
 
+// Warp suggestions (Note 1)
+// Show suggestions when attempting to @warp to a non-existent map?
+feature.warp_suggestions: off
+
 // Banking (Note 1)
 // Requires: 2013-07-24aRagexe or later
 feature.banking: on

+ 25 - 0
src/common/utils.c

@@ -324,3 +324,28 @@ unsigned int get_percentage(const unsigned int A, const unsigned int B)
 
 	return (unsigned int)floor(result);
 }
+
+/**
+ * Calculates the Levenshtein distance of two strings.
+ * @author http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C
+ */
+int levenshtein(const char *s1, const char *s2) {
+	unsigned int s1len, s2len, x, y, lastdiag, olddiag, i;
+	unsigned int *column;
+	s1len = strlen(s1);
+	s2len = strlen(s2);
+	column = malloc((s1len+1) * sizeof(unsigned int));
+	for (y = 1; y <= s1len; y++)
+		column[y] = y;
+	for (x = 1; x <= s2len; x++) {
+		column[0] = x;
+		for (y = 1, lastdiag = x-1; y <= s1len; y++) {
+			olddiag = column[y];
+			column[y] = min(min(column[y] + 1, column[y-1] + 1), lastdiag + (s1[y-1] == s2[x-1] ? 0 : 1));
+			lastdiag = olddiag;
+		}
+	}
+	i = column[s1len];
+	free(column);
+	return i;
+}

+ 2 - 0
src/common/utils.h

@@ -31,4 +31,6 @@ extern uint32 MakeDWord(uint16 word0, uint16 word1);
 
 uint32 date2version(int date);
 
+int levenshtein(const char *s1, const char *s2);
+
 #endif /* _UTILS_H_ */

+ 1 - 1
src/config/core.h

@@ -11,7 +11,7 @@
 /// Max number of items on @autolootid list
 #define AUTOLOOTITEM_SIZE 10
 
-/// The maximum number of atcommand suggestions
+/// The maximum number of atcommand and @warp suggestions
 #define MAX_SUGGESTIONS 10
 
 /// Comment to disable the official walk path

+ 76 - 0
src/map/atcommand.c

@@ -87,6 +87,7 @@ static char atcmd_player_name[NAME_LENGTH];
 static AtCommandInfo* get_atcommandinfo_byname(const char *name); // @help
 static const char* atcommand_checkalias(const char *aliasname); // @help
 static void atcommand_get_suggestions(struct map_session_data* sd, const char *name, bool atcommand); // @help
+static void warp_get_suggestions(struct map_session_data* sd, const char *name); // @rura, @warp, @mapmove
 
 // @commands (script-based)
 struct atcmd_binding_data* get_atcommandbind_byname(const char* name) {
@@ -370,6 +371,77 @@ ACMD_FUNC(send)
 #undef GET_VALUE
 }
 
+/**
+ * Retrieves map name suggestions for a given string.
+ * This will first check if any map names contain the given string, and will
+ *   print out MAX_SUGGESTIONS results if any maps are found.
+ * Otherwise, suggestions will be calculated through Levenshtein distance,
+ *   and up to 5 of the closest matches will be printed.
+ *
+ * @author Euphy
+ */
+static void warp_get_suggestions(struct map_session_data* sd, const char *name) {
+	char buffer[512];
+	int i, count = 0;
+
+	if (strlen(name) < 2)
+		return;
+
+	// build the suggestion string
+	strcpy(buffer, msg_txt(sd, 205)); // Maybe you meant:
+	strcat(buffer, "\n");
+
+	// check for maps that contain string
+	for (i = 0; i < MAX_MAP_PER_SERVER; i++) {
+		if (count < MAX_SUGGESTIONS && strstr(map[i].name, name) != NULL) {
+			strcat(buffer, map[i].name);
+			strcat(buffer, " ");
+			if (++count >= MAX_SUGGESTIONS)
+				break;
+		}
+	}
+
+	// if no maps found, search by edit distance
+	if (!count) {
+		unsigned int distance[MAX_MAP_PER_SERVER][2];
+		int j, min;
+
+		// calculate Levenshtein distance for all maps
+		for (i = 0; i < MAX_MAP_PER_SERVER; i++) {
+			if (strlen(map[i].name) < 4)  // invalid map name?
+				distance[i][0] = INT_MAX;
+			else {
+				distance[i][0] = levenshtein(map[i].name, name);
+				distance[i][1] = i;
+			}
+		}
+
+		// selection sort elements as needed
+		count = min(MAX_SUGGESTIONS, 5);  // results past 5 aren't worth showing
+		for (i = 0; i < count; i++) {
+			min = i;
+			for (j = i+1; j < MAX_MAP_PER_SERVER; j++) {
+				if (distance[j][0] < distance[min][0])
+					min = j;
+			}
+
+			// print map name
+			if (distance[min][0] > 4) {  // awful results, don't bother
+				if (!i) return;
+				break;
+			}
+			strcat(buffer, map[distance[min][1]].name);
+			strcat(buffer, " ");
+
+			// swap elements
+			swap(distance[i][0], distance[min][0]);
+			swap(distance[i][1], distance[min][1]);
+		}
+	}
+
+	clif_displaymessage(sd->fd, buffer);
+}
+
 /*==========================================
  * @rura, @warp, @mapmove
  *------------------------------------------*/
@@ -397,6 +469,10 @@ ACMD_FUNC(mapmove)
 
 	if (!mapindex) { // m < 0 means on different server! [Kevin]
 		clif_displaymessage(fd, msg_txt(sd,1)); // Map not found.
+
+		if (battle_config.warp_suggestions_enabled)
+			warp_get_suggestions(sd, map_name);
+
 		return -1;
 	}
 

+ 1 - 0
src/map/battle.c

@@ -7369,6 +7369,7 @@ static const struct _battle_data {
 	{ "atcommand_enable_npc",				&battle_config.atcommand_enable_npc,			0,		0,		100,			},
 	{ "path_blown_halt",                    &battle_config.path_blown_halt,                 1,      0,      1,              },
 	{ "rental_mount_speed_boost",           &battle_config.rental_mount_speed_boost,        25,     0,      100,        	},
+	{ "feature.warp_suggestions",           &battle_config.warp_suggestions_enabled,        0,      0,      1,              },
 };
 #ifndef STATS_OPT_OUT
 /**

+ 1 - 0
src/map/battle.h

@@ -532,6 +532,7 @@ extern struct Battle_Config
 	int atcommand_enable_npc;
 	int path_blown_halt;
 	int rental_mount_speed_boost;
+	int warp_suggestions_enabled;
 } battle_config;
 
 void do_init_battle(void);