/* * MusicMUD - Action Making module * Copyright (C) 1998-2003 Abigail Brady * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #include <fcntl.h> #include <ctype.h> #ifdef CONFIG_THREAD #include <pthread.h> #endif #include "musicmud.h" #include "State.h" #include "verbs.h" #include "util.h" #include "zoneload.h" #include "misc.h" #include "msi.h" #include "Socket.h" #include "pflags.h" #include "paths.h" #include "emsg.h" #include "actions.h" #define MODULE "actions" string Action::*asrings[] = { &Action::u_me, &Action::u_rest, &Action::t_me, &Action::t_rest, &Action::t_you, &Action::x_me, &Action::x_rest, &Action::o_me, &Action::o_rest, }; static bool verb_actions(MudObject *who, int argc, const char **argv) { if (argc==1) { Divert d(who, "actions"); Header h(who, "Actions Index", 1); Verb *v; int i; size_t maxlen = 0; foreach_alpha(verbs, v, i) { if (!streq(v->get("module"), "action")) continue; if (strlen(v->id)>maxlen) maxlen = strlen(v->id); } int cols = columns(who); cols /= (maxlen + 1); cols--; if (cols<2) cols = 2; int j = 0; foreach_alpha(verbs, v, i) { if (!streq(v->get("module"), "action")) continue; if (j == (cols)) { j = 0; who->printf("\n"); } who->printf("%-*s ", maxlen, v->id); j++; } who->printf("\n"); return true; } Object *testing = verbs->get(argv[1]); if (!testing && (strchr(argv[1], '?') || strchr(argv[1], '*'))) { Verb *v; int i; int found = 0; string s; foreach_alpha(verbs, v, i) { if (!streq(v->get("module"), "action")) continue; int f = 0; if (wcmatch(make_lower(argv[1]).c_str(), make_lower(v->id).c_str())) { f = 1; } if (!f) { Action a(v->id); #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) for (size_t i=0;i<ARRAY_SIZE(asrings);i++) { if (wcmatch(make_lower(argv[1]).c_str(), make_lower(lose_colour((a.*asrings[i]).c_str()) .c_str()).c_str())) { f = 1; continue; } } } if (f) { s += ssprintf("%s ", v->id); found = 1; } } if (found) { who->printf("All actions matching '%s'.\n\n ^Z%s\n", argv[1], s.c_str()); return true; } } if (testing && streq(testing->get("module"), "action")) { Action a(argv[1]); string title = "help "; title += argv[1]; who->printf("%s\n\n", title_for(title.c_str(), who).c_str()); if (a.u_me.length()) { who->printf("^WUntargetted:^n\n"); who->printf(" to you : ^Z%s\n", sprinta(who, a.u_me.c_str(), who, 0, 0, 2)); who->printf(" to others : ^Z%s\n\n", sprinta(0, a.u_rest.c_str(), who, 0)); } if (a.x_me.length()) { who->printf("^WUntargetted (with text):^n\n"); who->printf(" to you : ^Z%s\n", sprinta(who, a.x_me.c_str(), who, 0, "[some text]", 2)); who->printf(" to others : ^Z%s\n\n", sprinta(0, a.x_rest.c_str(), who, 0, "[some text]")); } if (a.s_me.length()) { who->printf("^WTargetted at person:^n\n"); MudObject tmp("@tmp"); who->printf(" to you : ^Z%s\n", sprinta(who, a.s_me.c_str(), who, 0, "[some text]", 2)); who->printf(" to target : ^Z%s\n", sprinta(&tmp, a.s_you.c_str(), who, &tmp, "[some text]", 2)); who->printf(" to others : ^Z%s\n\n", sprinta(0, a.s_rest.c_str(), who, 0, "[some text]")); } if (a.t_me.length()) { who->printf("^WTargetted at person:^n\n"); MudObject tmp("@tmp"); who->printf(" to you : ^Z%s\n", sprinta(who, a.t_me.c_str(), who, 0, 0, 2)); who->printf(" to target : ^Z%s\n", sprinta(&tmp, a.t_you.c_str(), who, &tmp, 0, 2)); who->printf(" to others : ^Z%s\n\n", sprinta(0, a.t_rest.c_str(), who, 0)); } if (a.p_me.length()) { who->printf("^WTargetted with prop:^n\n"); MudObject tmp("@tmp"); who->printf(" to you : ^Z%s\n", sprinta(who, a.p_me.c_str(), who, 0, 0, 2)); who->printf(" to target : ^Z%s\n", sprinta(&tmp, a.p_you.c_str(), who, &tmp, 0, 2)); who->printf(" to others : ^Z%s\n\n", sprinta(0, a.p_rest.c_str(), who, 0)); } if (a.o_me.length()) { who->printf("^WTargetted at object:^n\n"); who->printf(" to you : ^Z%s\n", sprinta(who, a.o_me.c_str(), who, 0, 0, 2)); who->printf(" to others : ^Z%s\n\n", sprinta(0, a.o_rest.c_str(), who, 0)); } who->printf("%s\n", footer_for(who).c_str()); return true; } who->printf("Cannot find action : %s.\n", argv[1]); return true; } static bool verb_addaction(Player *who, int, const char **) { who->push_state("action.name"); return true; } static void state_action_name(Player *who, const char *what) { if (!what || !*what) { who->pop_state(); who->printf("Ok then.\n"); return; } const char *w = what; while (*w) { if (!isalpha(*w)) { who->printf("Actions can only have letters in their names.\n"); who->pop_state(); return; } w++; } int can_obj = 1; int can_tar = 1; int can_txt = 1; int can_unt = 1; int can_xto = 1; Action act; try { Action a(what); act = a; } catch (emsg e) { if (verbs->get(what)) { who->printf("There's already a non-action verb called that.\n"); return; } } if (act.x_rest.length()) { can_obj = 0; can_txt = 0; can_tar = 0; } if (act.u_rest.length()) { can_unt = 0; } if (act.t_rest.length()) { can_txt = 0; can_tar = 0; } if (act.o_rest.length()) { can_txt = 0; can_obj = 0; } if (act.s_rest.length()) { can_xto = 0; } if (!(can_obj || can_tar || can_txt || can_unt)) { who->printf("Action is full.\n"); who->pop_state(); return; } int types = can_unt | can_tar<<1 | can_txt<<2 | can_obj<<3 | can_xto<<4; who->set_state("action.type"); who->set("!action.types", types); if (can_unt) who->printf("1 - untargetted.\n"); if (can_tar) who->printf("2 - targetted at person.\n"); if (can_txt) who->printf("3 - with text.\n"); if (can_obj) who->printf("4 - targetted at object.\n"); if (can_xto) who->printf("5 - at person with text.\n"); who->set("!action.name", what); } static void state_action_type(Player *who, const char *what) { int which = atoi(what); int avail = who->get_int("!action.types"); switch(which) { case 0: who->pop_state(); return; case 1: if (!(avail & 1)) { who->printf("Can't.\n"); who->pop_state(); return; } who->printf("%%1 == name of sender\n" "%%2 == he/she/they/it\n" "%%3 == him/her/them/it\n" "%%4 == his/her/their/its\n" "%%5 == his/hers/theirs/its\n" "%%6 == himself/herself/theirself/itself\n"); who->set_state("action.unt.others"); break; case 2: if (!(avail & 2)) { who->printf("Can't.\n"); who->pop_state(); return; } who->printf("%%1 == name of sender %%a == name of target\n" "%%2 == he/she/they/it %%b == he/she/they/it\n" "%%3 == him/her/them/it %%c == him/her/them/it\n" "%%4 == his/her/their/its %%d == his/her/their/its\n" "%%5 == his/hers/theirs/its %%e == his/hers/theirs/its\n" "%%6 == himself/herself/theirself/itself %%f == himself/herself/theirself/itself\n"); who->set_state("action.tar.others"); break; case 3: if (!(avail & 4)) { who->printf("Can't.\n"); who->pop_state(); return; } who->printf("%%s == text passed to action\n" "%%1 == name of sender\n" "%%2 == he/she/they/it\n" "%%3 == him/her/them/it\n" "%%4 == his/her/their/its\n" "%%5 == his/hers/their/its\n" "%%6 == himself/herself/theirself/itself\n"); who->set_state("action.txt.others"); return; case 4: if (!(avail & 8)) { who->printf("Can't.\n"); who->pop_state(); return; } who->set_state("action.obj.others"); break; case 5: if (!(avail & 16)) { who->printf("Can't.\n"); who->pop_state(); return; } who->set_state("action.xto.others"); break; default: who->printf("Unhandled action type: %i\n", which); who->pop_state(); return; } who->set("!action.type", which); } static void state_action_unt_user(Player *who, const char *what) { if (what) { if (*what) who->set("!action.unt.user", what); else who->set("!action.unt.user", who->get("!action.unt.others")); who->set_state("action.unt.confirm"); } else { who->pop_state(); } } static void state_action_unt_others(Player *who, const char *what) { if (what && *what) { who->set("!action.unt.others", what); who->set_state("action.unt.user"); } else { who->pop_state(); } } static void state_action_unt_confirm(Player *who, const char *what) { if (no(what) || !who->get("!action.name")) { who->printf("OK then, I won't..\n"); who->pop_state(); who->unset("!action.name"); who->unset("!action.unt.user"); who->unset("!action.unt.others"); return; } const char *actname = who->get("!action.name"); try { Action act(actname, 1); act.u_me = who->get("!action.unt.user"); act.u_rest = who->get("!action.unt.others"); act.output(actname); } catch (emsg &e) { who->printf("Couldn't open the action file.\n"); who->pop_state(); who->unset("!action.unt.user"); who->unset("!action.unt.others"); return; } log(PFL_SEEINFO, 0, "action", "new untargetted : %s", actname); who->pop_state(); who->ilc++; who->interpret("rescan"); who->ilc--; who->unset("!action.unt.user"); who->unset("!action.unt.others"); } static void state_action_obj_user(Player *who, const char *what) { if (what) { if (*what) who->set("!action.obj.user", what); else who->set("!action.obj.user", who->get("!action.obj.others")); who->set_state("action.obj.confirm"); } else { who->pop_state(); } } static void state_action_obj_others(Player *who, const char *what) { if (what && *what) { who->set("!action.obj.others", what); who->set_state("action.obj.user"); } else { who->pop_state(); } } static void state_action_obj_confirm(Player *who, const char *what) { if (no(what) || !who->get("!action.name")) { who->printf("OK then, I won't..\n"); who->pop_state(); who->unset("!action.name"); who->unset("!action.obj.user"); who->unset("!action.obj.others"); return; } const char *actname = who->get("!action.name"); try { Action act(actname, 1); act.o_me = who->get("!action.obj.user"); act.o_rest = who->get("!action.obj.others"); act.output(actname); } catch (emsg &e) { who->printf("Couldn't open the action file.\n"); who->pop_state(); who->unset("!action.obj.user"); who->unset("!action.obj.others"); return; } log(PFL_SEEINFO, 0, "action", "new targetted-at-object : %s", actname); who->pop_state(); who->ilc++; who->interpret("rescan"); who->ilc--; who->unset("!action.obj.user"); who->unset("!action.obj.others"); } static void state_action_tar_user(Player *who, const char *what) { if (what) { if (*what) who->set("!action.tar.user", what); else who->set("!action.tar.user", who->get("!action.tar.others")); who->set_state("action.tar.target"); } else { who->pop_state(); } } static void state_action_tar_target(Player *who, const char *what) { if (what) { if (*what) who->set("!action.tar.target", what); else who->set("!action.tar.target", who->get("!action.tar.others")); who->set_state("action.tar.confirm"); } else { who->pop_state(); } } static void state_action_tar_others(Player *who, const char *what) { if (what && *what) { who->set("!action.tar.others", what); who->set_state("action.tar.user"); } else { who->pop_state(); } } static void state_action_tar_confirm(Player *who, const char *what) { if (no(what) || !who->get("!action.name")) { who->printf("OK then, I won't..\n"); who->pop_state(); who->unset("!action.name"); who->unset("!action.tar.user"); who->unset("!action.tar.target"); who->unset("!action.tar.others"); return; } const char *actname = who->get("!action.name"); try { Action act(actname, 1); act.t_me = who->get("!action.tar.user"); act.t_you = who->get("!action.tar.target"); act.t_rest = who->get("!action.tar.others"); act.output(actname); } catch (emsg &e) { who->printf("Couldn't open the action file.\n"); who->pop_state(); who->unset("!action.tar.user"); who->unset("!action.tar.target"); who->unset("!action.tar.others"); return; } log(PFL_SEEINFO, 0, "action", "new targetted-at-person : %s", actname); who->pop_state(); who->ilc++; who->interpret("rescan"); who->ilc--; who->unset("!action.tar.user"); who->unset("!action.tar.target"); who->unset("!action.tar.others"); } static void state_action_xto_others(Player *who, const char *what) { if (*what) { who->set("!action.xto.others", what); who->set_state("action.xto.user"); } else { who->pop_state(); } } static void state_action_xto_user(Player *who, const char *what) { if (*what) who->set("!action.xto.user", what); else who->set("!action.xto.user", who->get("!action.xto.others")); who->set_state("action.xto.target"); } static void state_action_xto_target(Player *who, const char *what) { if (*what) who->set("!action.xto.target", what); else who->set("!action.xto.target", who->get("!action.xto.others")); who->set_state("action.xto.error"); } static void state_action_xto_error(Player *who, const char *what) { if (!*what) return; who->set("!action.xto.error", what); who->set_state("action.xto.confirm"); } static void state_action_xto_confirm(Player *who, const char *what) { if (no(what) || !who->get("!action.name")) { who->printf("OK then, I won't..\n"); who->pop_state(); who->unset("!action.name"); who->unset("!action.xto.user"); who->unset("!action.xto.target"); who->unset("!action.xto.others"); return; } const char *actname = who->get("!action.name"); try { Action act(actname, 1); act.s_me = who->get("!action.xto.user"); act.s_you = who->get("!action.xto.target"); act.s_rest = who->get("!action.xto.others"); act.s_err = who->get("!action.xto.error"); act.output(actname); } catch (emsg &e) { who->printf("Couldn't open the action file.\n"); who->pop_state(); who->unset("!action.xto.user"); who->unset("!action.xto.target"); who->unset("!action.xto.others"); return; } log(PFL_SEEINFO, 0, "action", "new targetted-at-person-with-text : %s", actname); who->pop_state(); who->ilc++; who->interpret("rescan"); who->ilc--; who->unset("!action.xto.user"); who->unset("!action.xto.target"); who->unset("!action.xto.others"); } static void state_action_txt_user(Player *who, const char *what) { if (what) { if (*what) who->set("!action.txt.user", what); else who->set("!action.txt.user", who->get("!action.txt.others")); who->set_state("action.txt.error"); } else { who->pop_state(); } } static void state_action_txt_others(Player *who, const char *what) { if (what && *what) { who->set("!action.txt.others", what); who->set_state("action.txt.user"); } else { who->pop_state(); } } static void state_action_txt_error(Player *who, const char *what) { if (what && *what) { who->set("!action.txt.error", what); who->set_state("action.txt.confirm"); } else { who->pop_state(); } } static void state_action_txt_confirm(Player *who, const char *what) { if (no(what) || !who->get("!action.name")) { who->printf("OK then, I won't..\n"); who->pop_state(); who->unset("!action.name"); who->unset("!action.txt.user"); who->unset("!action.txt.other"); who->unset("!action.txt.error"); return; } const char *actname = who->get("!action.name"); try { Action act(actname, 1); act.x_me = who->get("!action.txt.user"); act.x_err = who->get("!action.txt.error"); act.x_rest = who->get("!action.txt.others"); act.output(actname); } catch (emsg &e) { who->printf("Couldn't open the action file.\n"); who->pop_state(); who->unset("!action.txt.user"); who->unset("!action.txt.error"); who->unset("!action.txt.others"); return; } log(PFL_SEEINFO, 0, "action", "new untargetted-text : %s", actname); who->pop_state(); who->ilc++; who->interpret("rescan"); who->ilc--; who->unset("!action.txt.user"); who->unset("!action.txt.error"); who->unset("!action.txt.others"); } static bool is_compressed(MudObject *who) { #ifndef NOCOMPRESS Player *p = (Player *)who; if (p->p && p->p->compress) return 1; #endif return 0; } static bool verb_compile(MudObject *who, int, const char **) { if (!is_player(who)) { who->printf("no way..\n"); return true; } if (is_compressed(who)) { who->printf("You can't do on a compressed line.\n"); return true; } if (!who->get_priv(PFL_CODER)) { who->printf("Only Coders can do that.\n"); return true; } pid_t child; if ((child=fork())) { /* we are the parent... */ // who->interpret("quit"); } else { /* we are the child.. */ dup2(((Player *)who)->p->socket->getfd(), 1); fcntl(0, F_SETFL, 0); dup2(1, 2); fcntl(0, F_SETFD, 0); fcntl(1, F_SETFD, 0); fcntl(2, F_SETFD, 0); #ifdef CONFIG_THREAD pthread_kill_other_threads_np(); #endif execl("/bin/sh", "sh", "-c", "make", "2>&1", NULL); } return true; } static bool verb_cvsupdate(MudObject *who, int, const char**) { if (!is_player(who)) { who->printf("no way..\n"); return true; } if (is_compressed(who)) { who->printf("You can't do on a compressed line.\n"); return true; } if (!who->get_priv(PFL_CODER)) { who->printf("Only Coders can do that.\n"); return true; } pid_t child; if ((child=fork())) { /* we are the parent... */ // who->interpret("quit"); } else { /* we are the child.. */ dup2(((Player *)who)->p->socket->getfd(), 1); fcntl(0, F_SETFL, 0); dup2(1, 2); fcntl(0, F_SETFD, 0); fcntl(1, F_SETFD, 0); fcntl(2, F_SETFD, 0); #ifdef CONFIG_THREAD pthread_kill_other_threads_np(); #endif putenv("CVS_RSH=ssh"); execl("/bin/sh", "sh", "-c", "cvs update -d", NULL); } return true; } static bool verb_commit(MudObject *who, int argc, const char**argv) { if (!is_player(who)) { who->printf("no way..\n"); return true; } if (is_compressed(who)) { who->printf("You can't do on a compressed line.\n"); return true; } if (!who->get_priv(PFL_CODER)) { who->printf("Only Coders can do that.\n"); return true; } pid_t child; if (argc < 3) { who->printf("What zone do you want to commit, and what message?\n"); return true; } if (!zones->get(argv[1])) { who->printf("No such zone.\n"); return true; } who->interpretf("stz %s", argv[1]); if ((child=fork())) { /* we are the parent... */ } else { /* we are the child.. */ dup2(((Player *)who)->p->socket->getfd(), 1); fcntl(0, F_SETFL, 0); dup2(1, 2); fcntl(0, F_SETFD, 0); fcntl(1, F_SETFD, 0); fcntl(2, F_SETFD, 0); #ifdef CONFIG_THREAD pthread_kill_other_threads_np(); #endif string fn = "log."; fn += who->id; XFILE *f = xopen("tmp", fn.c_str(), "w+"); fprintf(f, "%s\n", the_rest(argc, argv, 2).c_str()); fprintf(f, "\n"); fprintf(f, "[%s]\n", bwname(who)); xclose(f); string cmd = ssprintf("cvs commit -F tmp/log.%s data/world/%s", who->id, argv[1]); execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL); exit(0); } return true; } static bool verb_update(MudObject *who, int argc, const char **argv) { if (!is_player(who)) { who->printf("no way..\n"); return true; } if (is_compressed(who)) { who->printf("You can't do on a compressed line.\n"); return true; } if (!who->get_priv(PFL_CODER)) { who->printf("Only Coders can do that.\n"); return true; } if (argc < 2) { who->printf("Usage: update wizlist\n"); return true; } if (strcasecmp(argv[1], "wizlist")!=0) { who->printf("Usage: update nwizlist\n"); return true; } pid_t child; if ((child=fork())) { /* we are the parent... */ // who->interpret("quit"); } else { /* we are the child.. */ dup2(((Player *)who)->p->socket->getfd(), 1); fcntl(0, F_SETFL, 0); dup2(1, 2); fcntl(0, F_SETFD, 0); fcntl(1, F_SETFD, 0); fcntl(2, F_SETFD, 0); #ifdef CONFIG_THREAD pthread_kill_other_threads_np(); #endif chdir("src"); if (strcmp(argv[1], "wizlist")==0) { execl("/bin/sh", "sh", "-c", "make wizlist", "2>&1", ">", "/dev/null", NULL); } } return true; } static bool verb_acdel(MudObject *who, int argc, const char **argv) { if (!argv[1]) { who->printf("Delete from what action?\n"); return true; } Action a(argv[1]); if (a.lua.length()) { who->printf("This action has lua and bits cannot be deleted manually.\n"); return true; } if (!argv[2]) { string s = ""; if (a.x_rest.length()) s += ", text"; if (a.o_rest.length()) s += ", obj"; if (a.u_rest.length()) s += ", unt"; if (a.t_rest.length()) s += ", tar"; if (s.length()) s = s.substr(2); who->printf("Delete which component of this action? It has %s.\n", s.c_str()); return true; } if (streq(argv[2], "text")) { if (a.x_rest.length()) { a.x_rest = ""; a.x_err = ""; a.x_me = ""; } else { who->printf("No text component.\n"); return true; } } if (streq(argv[2], "obj")) { if (a.o_rest.length()) { a.o_rest = ""; a.o_me = ""; } else { who->printf("No object component.\n"); return true; } } if (streq(argv[2], "unt")) { if (a.u_rest.length()) { a.u_rest = ""; a.u_me = ""; } else { who->printf("No untargetted component.\n"); return true; } } if (streq(argv[2], "tar")) { if (a.t_rest.length()) { a.t_rest = ""; a.t_you = ""; a.t_me = ""; } else { who->printf("No targetted component.\n"); return true; } } a.output(argv[1]); who->interpret("rescan"); return true; } #include "verbmodule.h" void startup() { AUTO_VERB(actions, 2, 0, PFL_NONE); AUTO_VERB(addaction, 4, 0, PFL_ACTIONS); AUTO_VERB(commit, 5, 0, PFL_CODER); AUTO_VERB(compile, 5, 0, PFL_CODER); AUTO_VERB(cvsupdate, 4, 0, PFL_CODER); AUTO_VERB(update, 6, 0, PFL_CODER); AUTO_VERB(acdel, 5, 0, PFL_ACTIONS); ADD_STATE("action.name", "Action command: ", state_action_name, false); ADD_STATE("action.type", "Type of action : ", state_action_type, false); ADD_STATE("action.unt.user", "Message to You: (opt) ", state_action_unt_user, false); ADD_STATE("action.unt.others", "Message to Others: ", state_action_unt_others, false); ADD_STATE("action.unt.confirm", "Confirm Action: ", state_action_unt_confirm, false); ADD_STATE("action.tar.user", "Message to You: (opt) ", state_action_tar_user, false); ADD_STATE("action.tar.target", "Message to Target: (opt) ", state_action_tar_target, false); ADD_STATE("action.tar.others", "Message to Others: ", state_action_tar_others, false); ADD_STATE("action.tar.confirm", "Confirm Action: ", state_action_tar_confirm, false); ADD_STATE("action.txt.user", "Message to You: (opt) ", state_action_txt_user, false); ADD_STATE("action.txt.others", "Message to Others: ", state_action_txt_others, false); ADD_STATE("action.txt.error", "No text error: ", state_action_txt_error, false); ADD_STATE("action.txt.confirm", "Confirm Action: ", state_action_txt_confirm, false); ADD_STATE("action.obj.user", "Message to You: (opt) ", state_action_obj_user, false); ADD_STATE("action.obj.others", "Message to Others: ", state_action_obj_others, false); ADD_STATE("action.obj.confirm", "Confirm Action: ", state_action_obj_confirm, false); ADD_STATE("action.xto.user", "Message to You: (opt) ", state_action_xto_user, false); ADD_STATE("action.xto.target", "Message to Target: (opt) ", state_action_xto_target, false); ADD_STATE("action.xto.others", "Message to Others: ", state_action_xto_others, false); ADD_STATE("action.xto.error", "No text error: ", state_action_xto_error, false); ADD_STATE("action.xto.confirm", "Confirm Action: ", state_action_xto_confirm, false); }