summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcookiecrumbs <cookiecrumbs@54d463be-8e91-2dee-dedb-b68131a5f0ec>2012-07-22 05:15:32 +0000
committercookiecrumbs <cookiecrumbs@54d463be-8e91-2dee-dedb-b68131a5f0ec>2012-07-22 05:15:32 +0000
commit52ed37bd877a4f462171a1501ec1ab2c4fd19eea (patch)
treeef42647de9b19016dac41e1a6899891a8d69074c
parent04ce57ecc743ec95e79fedab7e00bc650e386d23 (diff)
downloadhercules-52ed37bd877a4f462171a1501ec1ab2c4fd19eea.tar.gz
hercules-52ed37bd877a4f462171a1501ec1ab2c4fd19eea.tar.bz2
hercules-52ed37bd877a4f462171a1501ec1ab2c4fd19eea.tar.xz
hercules-52ed37bd877a4f462171a1501ec1ab2c4fd19eea.zip
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://rathena.svn.sourceforge.net/svnroot/rathena/trunk@16471 54d463be-8e91-2dee-dedb-b68131a5f0ec
-rw-r--r--doc/script_commands.txt22
-rw-r--r--src/map/atcommand.c42
-rw-r--r--src/map/atcommand.h13
-rw-r--r--src/map/npc.c66
-rw-r--r--src/map/npc.h3
-rw-r--r--src/map/script.c105
-rw-r--r--src/map/script.h3
7 files changed, 253 insertions, 1 deletions
diff --git a/doc/script_commands.txt b/doc/script_commands.txt
index 33df8a59b..d862acd9d 100644
--- a/doc/script_commands.txt
+++ b/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.|
======================================
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index afa22b392..15f75d55a 100644
--- a/src/map/atcommand.c
+++ b/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) {
diff --git a/src/map/atcommand.h b/src/map/atcommand.h
index aaf477a97..9bc844664 100644
--- a/src/map/atcommand.h
+++ b/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_ */
diff --git a/src/map/npc.c b/src/map/npc.c
index 3d46a6876..d629ac1b3 100644
--- a/src/map/npc.c
+++ b/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)
diff --git a/src/map/npc.h b/src/map/npc.h
index 6f8737c65..f5dd9011c 100644
--- a/src/map/npc.h
+++ b/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_ */
diff --git a/src/map/script.c b/src/map/script.c
index 0329f4928..6c34af6f2 100644
--- a/src/map/script.c
+++ b/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"),
diff --git a/src/map/script.h b/src/map/script.h
index eed4c20c8..41c686660 100644
--- a/src/map/script.h
+++ b/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_ */