Ver código fonte

Added the ability to bind atcommands to NPC events (ex: NPCNAME::OnEvent); original version by ToastOfDoom however heavily modified by I enabling command level at the invoking/character (@/#) level and fixes to prevent console errors as well as fixes aimed to ensure compatibility with rAthena.
Updated the script_commands.txt documentation with the following script commands: bindatcmd, unbindatcmd and useatcmd.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@16471 54d463be-8e91-2dee-dedb-b68131a5f0ec

cookiecrumbs 13 anos atrás
pai
commit
0fd45ac13e
7 arquivos alterados com 253 adições e 1 exclusões
  1. 22 0
      doc/script_commands.txt
  2. 41 1
      src/map/atcommand.c
  3. 13 0
      src/map/atcommand.h
  4. 66 0
      src/map/npc.c
  5. 3 0
      src/map/npc.h
  6. 105 0
      src/map/script.c
  7. 3 0
      src/map/script.h

+ 22 - 0
doc/script_commands.txt

@@ -2109,6 +2109,28 @@ array, shifting all the elements beyond this towards the beginning.
 
 ---------------------------------------
 
+*bindatcmd "command","{NPC NAME}::<event label>"{,atcommand level,charcommand level};
+*bindatcmd ("command","{NPC NAME}::<event label>"{,atcommand level,charcommand level});
+
+This command will bind a NPC event label to an atcommand. Upon execution of
+the atcommand the user will invoke the NPC event label.
+
+---------------------------------------
+
+*unbindatcmd "command";
+*unbindatcmd ("command");
+
+This command will unbind a NPC event label from an atcommand.
+
+---------------------------------------
+
+*useatcmd "command";
+*useatcmd ("command");
+
+This command will execute a custom atcommand on the attached RID from a script.
+
+---------------------------------------
+
 ======================================
 |2.- Information-retrieving commands.|
 ======================================

+ 41 - 1
src/map/atcommand.c

@@ -85,6 +85,17 @@ 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
 
+// @commands (script-based)
+struct Atcmd_Binding* get_atcommandbind_byname(const char* name)
+{
+	int i = 0;
+	if( *name == atcommand_symbol || *name == charcommand_symbol )
+		name++; // for backwards compatibility
+	ARR_FIND( 0, ARRAYLENGTH(atcmd_binding), i, strcmp(atcmd_binding[i].command, name) == 0 );
+	return ( i < ARRAYLENGTH(atcmd_binding) ) ? &atcmd_binding[i] : NULL;
+	return NULL;
+}
+
 //-----------------------------------------------------------
 // Return the message string of the specified number by [Yor]
 //-----------------------------------------------------------
@@ -8987,6 +8998,9 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message
 	TBL_PC * ssd = NULL; //sd for target
 	AtCommandInfo * info;
 
+	// @commands (script based)
+	Atcmd_Binding * binding;
+
 	nullpo_retr(false, sd);
 	
 	//Shouldn't happen
@@ -9063,7 +9077,33 @@ bool is_atcommand(const int fd, struct map_session_data* sd, const char* message
 	//check to see if any params exist within this command
 	if( sscanf(atcmd_msg, "%99s %99[^\n]", command, params) < 2 )
 		params[0] = '\0';
-	
+
+	// @commands (script based)
+	if(type == 1) {
+		// Check if the command initiated is a character command
+		if (*message == charcommand_symbol &&
+	    (ssd = map_nick2sd(charname)) == NULL && (ssd = map_nick2sd(charname2)) == NULL )
+		{
+			sprintf(output, "%s failed. Player not found.", command);
+			clif_displaymessage(fd, output);
+			return true;
+		}
+
+		// Get atcommand binding
+		binding = get_atcommandbind_byname(command);
+
+		// Check if the binding isn't NULL and there is a NPC event, level of usage met, et cetera
+		if( binding != NULL && binding->npc_event[0] &&
+			((*atcmd_msg == atcommand_symbol && pc_get_group_level(sd) >= binding->level) ||
+			 (*atcmd_msg == charcommand_symbol && pc_get_group_level(sd) >= binding->level2)))
+		{
+			// Check if self or character invoking; if self == character invoked, then self invoke.
+			bool invokeFlag = ((*atcmd_msg == atcommand_symbol) ? 1 : 0);
+			npc_do_atcmd_event((invokeFlag ? sd : ssd), command, params, binding->npc_event);
+			return true;
+		}
+	}
+
 	//Grab the command information and check for the proper GM level required to use it or if the command exists
 	info = get_atcommandinfo_byname(atcommand_checkalias(command + 1));
 	if (info == NULL) {

+ 13 - 0
src/map/atcommand.h

@@ -34,4 +34,17 @@ const char* msg_txt(int msg_number);
 int msg_config_read(const char* cfgName);
 void do_final_msg(void);
 
+#define MAX_ATCMD_BINDINGS 100
+
+// @commands (script based)
+typedef struct Atcmd_Binding {
+	char command[50];
+	char npc_event[50];
+	int level;
+	int level2;
+} Atcmd_Binding;
+
+struct Atcmd_Binding atcmd_binding[MAX_ATCMD_BINDINGS];
+struct Atcmd_Binding* get_atcommandbind_byname(const char* name);
+
 #endif /* _ATCOMMAND_H_ */

+ 66 - 0
src/map/npc.c

@@ -2750,6 +2750,72 @@ void npc_setclass(struct npc_data* nd, short class_)
 		clif_spawn(&nd->bl);// fade in
 }
 
+// @commands (script based)
+int npc_do_atcmd_event(struct map_session_data* sd, const char* command, const char* message, const char* eventname)
+{
+	struct event_data* ev = (struct event_data*)strdb_get(ev_db, eventname);
+	struct npc_data *nd;
+	struct script_state *st;
+	int i = 0, j = 0, k = 0;
+	char *temp;
+	temp = (char*)aMalloc(strlen(message) + 1);
+
+	nullpo_ret(sd);
+
+	if( ev == NULL || (nd = ev->nd) == NULL )
+	{
+		ShowError("npc_event: event not found [%s]\n", eventname);
+		return 0;
+	}
+
+	if( sd->npc_id != 0 )
+	{ // Enqueue the event trigger.
+		int i;
+		ARR_FIND( 0, MAX_EVENTQUEUE, i, sd->eventqueue[i][0] == '\0' );
+		if( i < MAX_EVENTQUEUE )
+		{
+			safestrncpy(sd->eventqueue[i],eventname,50); //Event enqueued.
+			return 0;
+		}
+
+		ShowWarning("npc_event: player's event queue is full, can't add event '%s' !\n", eventname);
+		return 1;
+	}
+
+	if( ev->nd->sc.option&OPTION_INVISIBLE )
+	{ // Disabled npc, shouldn't trigger event.
+		npc_event_dequeue(sd);
+		return 2;
+	}
+
+	st = script_alloc_state(ev->nd->u.scr.script, ev->pos, sd->bl.id, ev->nd->bl.id);
+	setd_sub(st, NULL, ".@atcmd_command$", 0, (void *)command, NULL);
+
+	// split atcmd parameters based on spaces
+	i = 0;
+	j = 0;
+	while( message[i] != '\0' )
+	{
+		if( message[i] == ' ' && k < 127 )
+		{
+			temp[j] = '\0';
+			setd_sub(st, NULL, ".@atcmd_parameters$", k++, (void *)temp, NULL);
+			j = 0;
+			++i;
+		}
+		else
+			temp[j++] = message[i++];
+	}
+
+	temp[j] = '\0';
+	setd_sub(st, NULL, ".@atcmd_parameters$", k++, (void *)temp, NULL);
+	setd_sub(st, NULL, ".@atcmd_numparameters", 0, (void *)&k, NULL);
+	aFree(temp);
+
+	run_script_main(st);
+	return 0;
+}
+
 /// Parses a function.
 /// function%TAB%script%TAB%<function name>%TAB%{<code>}
 static const char* npc_parse_function(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath)

+ 3 - 0
src/map/npc.h

@@ -172,4 +172,7 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, int count, uns
 int npc_rr_secure_timeout_timer(int tid, unsigned int tick, int id, intptr_t data);
 #endif
 
+// @commands (script-based)
+int npc_do_atcmd_event(struct map_session_data* sd, const char* command, const char* message, const char* eventname);
+
 #endif /* _NPC_H_ */

+ 105 - 0
src/map/script.c

@@ -4053,6 +4053,10 @@ int script_reload() {
 	userfunc_db->clear(userfunc_db, db_script_free_code_sub);
 	db_clear(scriptlabel_db);
 
+	// @commands (script based)
+	// Clear bindings
+	memset(atcmd_binding,0,sizeof(atcmd_binding));
+
 	if(sleep_db) {
 		struct linkdb_node *n = (struct linkdb_node *)sleep_db;
 		while(n) {
@@ -16347,6 +16351,100 @@ BUILDIN_FUNC(freeloop) {
 
 	return 0;
 }
+
+/**
+ * @commands (script based)
+ **/
+BUILDIN_FUNC(bindatcmd)
+{
+	const char* atcmd;
+	const char* eventName;
+	int i = 0, level = 0, level2 = 0;
+
+	atcmd = script_getstr(st,2);
+	eventName = script_getstr(st,3);
+
+	if( script_hasdata(st,4) ) level = script_getnum(st,4);
+	if( script_hasdata(st,5) ) level2 = script_getnum(st,5);
+
+	// check if event is already binded
+	ARR_FIND(0, MAX_ATCMD_BINDINGS, i, strcmp(atcmd_binding[i].command,atcmd) == 0);
+	if( i < MAX_ATCMD_BINDINGS )
+	{
+		safestrncpy(atcmd_binding[i].npc_event, eventName, 50);
+		atcmd_binding[i].level = level;
+		atcmd_binding[i].level2 = level2;
+	}
+	else
+	{ // make new binding
+		ARR_FIND(0, MAX_ATCMD_BINDINGS, i, atcmd_binding[i].command[0] == '\0');
+		if( i < MAX_ATCMD_BINDINGS )
+		{
+			safestrncpy(atcmd_binding[i].command, atcmd, 50);
+			safestrncpy(atcmd_binding[i].npc_event, eventName, 50);
+			atcmd_binding[i].level = level;
+			atcmd_binding[i].level2 = level2;
+		}
+	}
+
+	return 0;
+}
+
+BUILDIN_FUNC(unbindatcmd)
+{
+	const char* atcmd;
+	int i =  0;
+
+	atcmd = script_getstr(st, 2);
+
+	ARR_FIND(0, MAX_ATCMD_BINDINGS, i, strcmp(atcmd_binding[i].command, atcmd) == 0);
+	if( i < MAX_ATCMD_BINDINGS )
+		memset(&atcmd_binding[i],0,sizeof(atcmd_binding[0]));
+
+	return 0;
+}
+
+BUILDIN_FUNC(useatcmd)
+{
+	TBL_PC dummy_sd;
+	TBL_PC* sd;
+	int fd;
+	const char* cmd;
+
+	cmd = script_getstr(st,2);
+
+	if( st->rid )
+	{
+		sd = script_rid2sd(st);
+		fd = sd->fd;
+	}
+	else
+	{ // Use a dummy character.
+		sd = &dummy_sd;
+		fd = 0;
+
+		memset(&dummy_sd, 0, sizeof(TBL_PC));
+		if( st->oid )
+		{
+			struct block_list* bl = map_id2bl(st->oid);
+			memcpy(&dummy_sd.bl, bl, sizeof(struct block_list));
+			if( bl->type == BL_NPC )
+				safestrncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH);
+		}
+	}
+
+	// compatibility with previous implementation (deprecated!)
+	if( cmd[0] != atcommand_symbol )
+	{
+		cmd += strlen(sd->status.name);
+		while( *cmd != atcommand_symbol && *cmd != 0 )
+			cmd++;
+	}
+
+	is_atcommand(fd, sd, cmd, 1);
+	return 0;
+}
+
 // declarations that were supposed to be exported from npc_chat.c
 #ifdef PCRE_SUPPORT
 BUILDIN_FUNC(defpattern);
@@ -16782,6 +16880,13 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(is_function,"s"),
 	BUILDIN_DEF(get_revision,""),
 	BUILDIN_DEF(freeloop,"i"),
+	/**
+	 * @commands (script based)
+	 **/
+	BUILDIN_DEF(bindatcmd, "ss??"),
+	BUILDIN_DEF(unbindatcmd, "s"),
+	BUILDIN_DEF(useatcmd, "s"),
+
 	//Quest Log System [Inkfish]
 	BUILDIN_DEF(setquest, "i"),
 	BUILDIN_DEF(erasequest, "i"),

+ 3 - 0
src/map/script.h

@@ -187,4 +187,7 @@ int add_str(const char* p);
 const char* get_str(int id);
 int script_reload(void);
 
+// @commands (script based)
+void setd_sub(struct script_state *st, TBL_PC *sd, const char *varname, int elem, void *value, struct DBMap **ref);
+
 #endif /* _SCRIPT_H_ */