summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--npc/annuals/tree_beard.txt1035
-rw-r--r--npc/functions/confused-tree-dict.txt515
-rw-r--r--npc/scripts.conf1
3 files changed, 1486 insertions, 65 deletions
diff --git a/npc/annuals/tree_beard.txt b/npc/annuals/tree_beard.txt
index a7866f2b..e0a4d1b2 100644
--- a/npc/annuals/tree_beard.txt
+++ b/npc/annuals/tree_beard.txt
@@ -1,60 +1,21 @@
-020-1,90,78,0 script #GhostTreeOne NPC133,{
- callfunc "HalloweenTree";
- close;
-}
-
-020-1,90,78,0 script #Tree NPC380,{
- end;
-}
-
-020-1,90,78,0 script #XmasTree NPC379,{
- end;
-}
-
020-1,90,79,0 script Present#1 NPC386,{
callfunc "XmasStates";
callfunc "XmasMainItemReward";
close;
}
-009-1,54,38,0 script #GhostTreeTwo NPC133,{
- callfunc "HalloweenTree";
- close;
-}
-
-009-1,54,38,0 script #TreeNoSnow NPC394,{
- end;
-}
-
-009-1,54,38,0 script #XmasTreeNoSnow NPC381,{
- end;
-}
-
009-1,54,39,0 script Present#2 NPC386,{
callfunc "XmasStates";
callfunc "XmasMainItemReward";
close;
}
-001-1,57,75,0 script #GhostTreeThree NPC133,{
- callfunc "HalloweenTree";
- close;
-}
-
-001-1,58,75,0 script #PalmTree NPC384,{
- end;
-}
-
001-1,58,76,0 script Present#3 NPC386,{
callfunc "XmasStates";
callfunc "XmasMainItemReward";
close;
}
-001-1,58,75,0 script #XmasPalmTree NPC383,{
- end;
-}
-
function script SpawnMobs {
donpcevent "XmasSpawnManager::OnCommandSpawnStart";
return;
@@ -82,40 +43,984 @@ L_Return:
}
function script ReplaceTrees {
- disablenpc "#Tree";
- disablenpc "#TreeNoSnow";
- disablenpc "#PalmTree";
- disablenpc "#GhostTreeOne";
- disablenpc "#GhostTreeTwo";
- disablenpc "#GhostTreeThree";
- disablenpc "#XmasTree";
- disablenpc "#XmasTreeNoSnow";
- disablenpc "#XmasPalmTree";
-
if ($@xmas_time)
goto L_XmasTree;
if ($@halloween_time)
goto L_HalloweenLive;
- goto L_Tree;
+
+ setnpcdisplay("#ConfusedTree", NPC394);
+ setnpcdisplay("#ConfusedIcedTree", NPC380);
+ setnpcdisplay("#ConfusedPalmTree", NPC384);
+ return;
L_XmasTree:
- enablenpc "#XmasTree";
- enablenpc "#XmasTreeNoSnow";
- enablenpc "#XmasPalmTree";
- goto L_Return;
+ setnpcdisplay("#ConfusedTree", NPC381);
+ setnpcdisplay("#ConfusedIcedTree", NPC379);
+ setnpcdisplay("#ConfusedPalmTree", NPC383);
+ return;
L_HalloweenLive:
- enablenpc "#GhostTreeOne";
- enablenpc "#GhostTreeTwo";
- enablenpc "#GhostTreeThree";
- goto L_Return;
+ setnpcdisplay("#ConfusedTree", NPC133);
+ setnpcdisplay("#ConfusedIcedTree", NPC133);
+ setnpcdisplay("#ConfusedPalmTree", NPC133);
+ return;
+}
-L_Tree:
- enablenpc "#Tree";
- enablenpc "#TreeNoSnow";
- enablenpc "#PalmTree";
- goto L_Return;
+// Evol scripts.
+// Author:
+// gumi
+// Based on CrazyTree, originally made by:
+// gumi
+// pclouds
+// veryape
+// wushin
+// Description:
+// emulated confused tree prototype
-L_Return:
- return;
+// ~t lowercase hot word regex
+
+009-1,54,38,0 script #ConfusedTree NPC394,14,14,{
+
+ function tree_panel {
+ if (is_trusted() == false && #Tree_Trusted == false)
+ {
+ narrator(l("You see a tree."));
+ if (getq(HurnscaldQuests_Inspector) == 2)
+ {
+ select(
+ l("Have you seen anything strange lately?"),
+ l("Do you know anything about the recent robberies?"));
+
+ narrator(S_FIRST_BLANK_LINE,
+ l("..."),
+ l("It doesn't reply."));
+ }
+ close;
+ }
+
+ function clear_db {
+ clear();
+ mes(l("##BWARNING:##b you are about to permanently empty the quote database."));
+ next();
+ mes(l("Do you want to continue?"));
+
+ select(
+ l("Abort!"),
+ l("Empty the quote DB"));
+
+ if (@menu == 2)
+ {
+ .@sentence$ = "I am an idiot";
+ mes(l("Please write the following sentence:"));
+ mes("");
+ mesf(" ##B%s.", .@sentence$);
+ input(.@confirm$);
+
+ if (!startswith(strtoupper(.@confirm$), strtoupper(.@sentence$))) {
+ mes(l("Invalid!"));
+ close;
+ }
+
+ query_sql("TRUNCATE TABLE tree_quotes;");
+ mes(l("Database erased."));
+ next();
+ }
+
+ return;
+ }
+
+ function list_commands {
+ clear();
+ mes(l("To grab a quote:"));
+ mes(col(" ~grab ##Bplayer name##b", 7));
+ next();
+ mes(l("To get a quote:"));
+ mes(col(" ~quote anyone", 7));
+ mes(col(" ~quote ##Bplayer name##b", 7));
+ mes(col(" ~quote ##B#number##b", 7));
+ next();
+ mes(l("To remove a quote:"));
+ mes(col(" ~remove quote ##B#number##b", 7));
+ mes(col(" ~remove last quote", 7));
+ next();
+ mes(l("Last seen:"));
+ mes(col(" ~seen ##Bplayer name##b", 7));
+ next();
+ mes(l("To ignore a player:"));
+ mes(col(" ~ignore ##Bplayer name##b", 7));
+ next();
+ mes(l("To unignore a player:"));
+ mes(col(" ~unignore ##Bplayer name##b", 7));
+ next();
+
+ if (is_admin())
+ {
+ mes(l("To trust a player:"));
+ mes(col(" ~trust ##Bplayer name##b", 7));
+ next();
+ mes(l("To de-trust a player:"));
+ mes(col(" ~untrust ##Bplayer name##b", 7));
+ next();
+ }
+ return;
+ }
+
+ do
+ {
+ clear();
+ setnpcdialogtitle(l("Tree Control Panel"));
+ mes(l("Oh noes! You found my secret backdoor!"));
+ next();
+ mes(l("Please select an option:"));
+
+ select(
+ l("List the commands"),
+ rif(is_admin(), l("Empty the quote DB")),
+ l("Dance for me"));
+
+ switch (@menu)
+ {
+ case 1: list_commands(); break;
+ case 2: clear_db(); break;
+ default: speech(l("Too lazy.")); close;
+ }
+
+ } while (true);
+
+ end;
+ }
+
+ // utility functions below
+
+ function check_is_ignored {
+ .@val = htget(.ignore_ht, strcharinfo(PC_NAME), 0);
+
+ if (.@val > gettimetick(2))
+ {
+ ++.ignored_times;
+ end;
+ }
+
+ else if (.@val > 0)
+ {
+ htput(.ignore_ht, strcharinfo(PC_NAME), 0); // remove expired entries
+ }
+
+ return;
+ }
+
+ function special_name {
+ .@name$ = strcharinfo(PC_NAME);
+ .@low$ = strtolower(.@name$);
+
+ if (rand(.sname_rate) == 0)
+ {
+ for (.@i = 0; .@i < .alias; .@i += 2)
+ {
+ if (.@low$ ~= .alias$[.@i])
+ {
+ explode(.@aliases$, .alias$[.@i+1], "`");
+ .@name$ = .@aliases$[rand(getarraysize(.@aliases$))];
+ break;
+ }
+ }
+ }
+
+ return .@name$;
+ }
+
+ function face {
+ if (gettimetick(2) - .last_emote < .emote_rate)
+ {
+ ++.ignored_times;
+ return;
+ }
+
+ .last_emote = gettimetick(2);
+ return emotion(getarg(0, E_SURPRISE));
+ }
+
+ function rp {
+ // used for queries
+ return replacestr(getarg(0,""), "~t", strtolower("(?:" + .name$ + "|" + .hotwords$ + ")"));
+ }
+
+ function format_reply {
+ // used for replies
+ .@str$ = getarg(0, "");
+
+ // search for {{mustaches}}
+ while (.@str$ ~= "{{([^}]+)}}")
+ {
+ .@sub$ = replacestr($@regexmatch$[1], " ", ""); // remove whitespaces
+ .@sub$ = strtolower(.@sub$); // always lowercase the var name
+ .@capitalize = .@titlecase = .@allcaps = false;
+
+ if (charat(.@sub$, 0) == "^")
+ {
+ .@capitalize = true;
+ .@sub$ = substr(.@sub$, 1, getstrlen(.@sub$) - 1); // strip first char
+ }
+
+ else if (charat(.@sub$, 0) == "+")
+ {
+ .@titlecase = true;
+ .@sub$ = substr(.@sub$, 1, getstrlen(.@sub$) - 1); // strip first char
+ }
+
+ else if (charat(.@sub$, 0) == "!")
+ {
+ .@allcaps = true;
+ .@sub$ = substr(.@sub$, 1, getstrlen(.@sub$) - 1); // strip first char
+ }
+
+ if (compare(.@sub$, ",")) {
+ .@var$ = sprintf(".H%s$", substr(md5(.@sub$), 0, 25));
+
+ if (getelementofarray(getd(.@var$), 1) == "") {
+ explode(.@sub2$, .@sub$, ",");
+ .@size = 1;
+
+ for (.@i = 0; .@i < getarraysize(.@sub2$); ++.@i) {
+ .@subsize = getd(sprintf(".D_%s", .@sub2$));
+ copyarray(getelementofarray(getd(.@var$), .@size), getd(sprintf(".D_%s$[1]", .@sub2$)), .@subsize);
+ .@size += .@subsize;
+ }
+ }
+ } else {
+ .@var$ = sprintf(".D_%s$", .@sub$);
+ }
+
+ .@rep$ = relative_array_random(getd(.@var$));
+
+ if (.@capitalize) .@rep$ = capitalize(.@rep$);
+ else if (.@titlecase) .@rep$ = titlecase(.@rep$);
+ else if (.@allcaps) .@rep$ = strtoupper(.@rep$);
+
+ .@str$ = replacestr(.@str$, $@regexmatch$[0], .@rep$); // remove the mustache, replace by value
+ }
+
+ // search for emotes
+ if (.@str$ ~= "%%([^ ])")
+ {
+ // only handling a few of them
+ switch (ord($@regexmatch$[1]))
+ {
+ case 73: face(any(E_WINK, E_ANGEL)); break;
+ case 83: face(any(E_SAD, E_CRYING)); break;
+ case 85: face(E_SURPRISE); break;
+ case 93: face(any(E_HEARTEYE, E_HEART)); break;
+ case 94: face(E_DISGUST); break;
+ case 99: face(E_DEAD); break;
+ case 105: face(E_CRYING); break;
+ case 106:
+ case 91: face(any(E_SPEECH, E_BLAH)); break;
+ case 107: face(E_INSULTBUBBLE); break;
+ default: .@unhandled = true;
+ }
+
+ if (.@unhandled != true)
+ {
+ if (.@str$ == $@regexmatch$[0]) end; // don't send handled, emote-only messages
+ .@str$ = replacestr(.@str$, " "+ $@regexmatch$[0], ""); // otherwise strip the emote
+ }
+ }
+
+ // built-in variables
+ .@str$ = replacestr(.@str$, "~n", .name$); // npc name
+ .@str$ = replacestr(.@str$, "~p", special_name()); // player name or special name
+ .@str$ = replacestr(.@str$, "~P", strcharinfo(PC_NAME)); // unaltered player name
+
+ return rp(.@str$);
+ }
+
+ function strip_colors {
+ .@str$ = replacestr(getarg(0, ""), "##0", "");
+ .@str$ = replacestr(.@str$, "##1", "");
+ .@str$ = replacestr(.@str$, "##2", "");
+ .@str$ = replacestr(.@str$, "##3", "");
+ .@str$ = replacestr(.@str$, "##4", "");
+ .@str$ = replacestr(.@str$, "##5", "");
+ .@str$ = replacestr(.@str$, "##6", "");
+ .@str$ = replacestr(.@str$, "##7", "");
+ .@str$ = replacestr(.@str$, "##8", "");
+ .@str$ = replacestr(.@str$, "##9", "");
+ return replacestr(.@str$, "##a", "");
+ }
+
+ function strip_formatting {
+ .@str$ = strip_colors(getarg(0, ""));
+ .@str$ = replacestr(.@str$, "##B", "");
+ return replacestr(.@str$, "##b", "");
+ }
+
+ function delayed_reply {
+ ++.answered_times;
+ @tree_reply$ = getarg(0, "");
+ addtimer(.delay_reply, .name$ + "::OnDoReply");
+ return;
+ }
+
+ function reply {
+ .@reply$ = format_reply(getarg(0, ""));
+ getmapxy(.@pc_map$, .@pc_x, .@pc_y, UNITTYPE_PC); // get char location
+
+ if (((.@reply$ == .last_reply$ && gettimetick(2) - .last_reply < .repeat_rate)
+ || gettimetick(2) - .last_reply < .talk_rate
+ || (gettimetick(2) - .blocked < .block_time && is_trusted() == false)
+ || .@pc_map$ != .map$
+ || distance(.x, .y, .@pc_x, .@pc_y) > .distance
+ || .@reply$ == "")
+ && is_dev() == false)
+ {
+ ++.ignored_times;
+ return;
+ }
+
+ .last_reply = gettimetick(2);
+ .last_reply$= .@reply$;
+
+ delayed_reply(.@reply$);
+ return;
+ }
+
+ function seen_me {
+ if (playerattached() > 0 && htexists(.seen_ht))
+ {
+ htput(.seen_ht, strcharinfo(PC_NAME), gettimetick(2));
+ }
+ return;
+ }
+
+ function have_you_seen {
+ .@player$ = getarg(0, "");
+ .@player = getcharid(CHAR_ID_ACCOUNT, .@player$);
+
+ if (.@player > 0)
+ {
+ // nested if, because they don't short-circuit
+ if (checkoption(Option_Invisible, .@player) == false) {
+ delayed_reply(sprintf("Player `%s` is currently online.", .@player$));
+ end;
+ }
+ }
+
+ .@time = htget(.seen_ht, .@player$, 0);
+
+ if (.@time < 1)
+ delayed_reply(sprintf("I haven't seen player `%s` today.", .@player$));
+
+ else
+ delayed_reply(sprintf("Player `%s` was last seen %s.", .@player$, FuzzyTime(.@time, 0, 99)));
+
+ end;
+ }
+
+ function special_drops {
+ .@drop$ = relative_array_random(.drops$);
+ .@name$ = strcharinfo(PC_NAME);
+ .@low$ = strtolower(.@name$);
+
+ if (rand(.sdrop_rate) == 0)
+ {
+ for (.@i = 0; .@i < .sdrops; .@i += 2)
+ {
+ if (.@low$ ~= .sdrops$[.@i])
+ {
+ explode(.@d$, .sdrops$[.@i+1], "`");
+ .@drop$ = .@d$[rand(getarraysize(.@d$))];
+ break;
+ }
+ }
+ }
+
+ return .@drop$;
+ }
+
+ function roll_dice {
+ .@dices = max(min(getarg(0, 1), 8), 1); // 1..8
+ .@sides = max((getarg(1, 6) < 1 ? 6 : getarg(1, 6)), 1); // 1..MAX_INT
+
+ .@result$ = sprintf("*rolls the dice%s: %d",
+ rif(.@dices > 1, "s"), rand(1, .@sides)); // first dice
+
+ for (.@d = 1; .@d < .@dices; ++.@d)
+ {
+ .@result$ += ", " + rand(1, .@sides);
+ }
+
+ return .@result$ + ".*";
+ }
+
+ function flip_coin {
+ .@coins = getarg(0, 1);
+
+ .@result$ = sprintf("*flips the coin%s: %s",
+ rif(.@coins > 1, "s"), (rand(2) == 1 ? "heads" : "tails")); // first coin
+
+ for (.@c = 1; .@c < .@coins; ++.@c)
+ {
+ .@result$ += ", " + (rand(2) == 1 ? "heads" : "tails");
+ }
+
+ return .@result$ + ".*";
+ }
+
+ function roulette {
+ if (.roulette == 1)
+ {
+ npctalk("*pulls the trigger: *##BBANG##b*.*");
+ delayed_reply("*reloads and spins the chambers.*");
+ .roulette = rand(1, 7); // the Nagant_M1895 has 7 chambers
+
+ // now the fun part
+ nude();
+ percentheal(-100, 0);
+ }
+
+ else
+ {
+ delayed_reply("*pulls the trigger: *click*.*");
+ .roulette = (.roulette == 7 ? 1 : .roulette + 1);
+ }
+
+ end;
+ }
+
+ function monologue_player {
+ return sprintf("Your current monologue is at least %d line%s long.",
+ @monologue, rif(@monologue != 1, "s"));
+ }
+
+ function who_player {
+ return sprintf("You seem to be ##B~P##b [%i:%i].",
+ getcharid(CHAR_ID_ACCOUNT), getcharid(CHAR_ID_CHAR));
+ }
+
+ function make_quote_table {
+ // Do not modify this
+ query_sql("CREATE TABLE IF NOT EXISTS `tree_quotes` ("
+ " `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,"
+ " `char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',"
+ " `grabber` INT(11) UNSIGNED NOT NULL DEFAULT '0',"
+ " `timestamp` INT(10) UNSIGNED NOT NULL DEFAULT '0',"
+ " `message` VARCHAR(150) NOT NULL DEFAULT '',"
+ " PRIMARY KEY (`id`),"
+ " KEY `char_id` (`char_id`),"
+ " KEY `grabber` (`grabber`)"
+ ") ENGINE=MyISAM;");
+
+ .last_query = gettimetick(2);
+ return;
+ }
+
+ function grab_quote {
+ .@name$ = getarg(0, "");
+
+ if (gettimetick(2) - .last_query < (is_trusted() ? .qpoll_rate : .qpoll_rate2))
+ {
+ ++.ignored_times;
+ end;
+ }
+
+ if (.@name$ == strcharinfo(PC_NAME))
+ {
+ delayed_reply("##BError: You may not grab yourself.");
+ end;
+ }
+
+ explode(.@tmp$[0], htget(.msg_ht, .@name$, ""), ":"); // get last message, if any
+ htput(.msg_ht, .@name$, ""); // ensure you can't grab twice the same message
+
+ .@char_id = atoi(.@tmp$[0]); // grab the char id part
+
+ if (.@char_id < 1)
+ {
+ delayed_reply(sprintf("##BError: I couldn't find anything to grab from player `%s`.", .@name$));
+ end;
+ }
+
+ .@msg$ = implode(.@tmp$, ":"); // put it back together
+ .@start = getstrlen(.@tmp$[0]) + getstrlen(.@tmp$[1]) + 2; // char:time:msg <= we just want the msg part
+ .@msg$ = escape_sql(strip_formatting(substr(.@msg$, .@start, getstrlen(.@msg$) - 1))); // sanitize
+
+ if (.@msg$ == "")
+ {
+ delayed_reply("##BError: Message is empty or malformed. It cannot be grabbed.");
+ end;
+ }
+
+ else if (.@msg$ ~= "^[!#~@]?(?:grab)?shield(?:ed)?(?:[:.!]? .*)?$")
+ {
+ delayed_reply("##BError: Message is shielded.");
+ end;
+ }
+
+ query_sql(sprintf("INSERT INTO tree_quotes (char_id,grabber,timestamp,message) VALUES (%i,%i,%i,'%s');",
+ .@char_id, getcharid(CHAR_ID_CHAR), gettimetick(2), .@msg$));
+
+ query_sql("SELECT MAX(id) FROM tree_quotes;", .q_last_id); // get the last quote id
+
+ .last_query = gettimetick(2);
+
+ delayed_reply(sprintf("Success: Quote grabbed. (#%i)", .q_last_id));
+ end;
+ }
+
+ function remove_quote {
+ .@tmp = getarg(0, 0);
+
+ if (gettimetick(2) - .last_query < (is_trusted() ? .qpoll_rate : .qpoll_rate2))
+ {
+ ++.ignored_times;
+ end;
+ }
+
+ query_sql(sprintf("SELECT id FROM tree_quotes WHERE id = %i ORDER BY id DESC LIMIT 1;", .@tmp), .@id); // check if it exists
+
+ if (.@id < 1)
+ {
+ delayed_reply(sprintf("##BError: I couldn't find quote #%i in the database.", .@tmp));
+ end;
+ }
+
+ query_sql(sprintf("DELETE FROM tree_quotes WHERE id = %i ORDER BY id DESC LIMIT 1;", .@id));
+
+ .last_query = gettimetick(2);
+
+ delayed_reply(sprintf("Success: Quote removed. (#%i)", .@id));
+ end;
+ }
+
+ function cite_quote {
+ .@id = getarg(0,0);
+
+ if (gettimetick(2) - .last_query < (is_trusted() ? .qpoll_rate : .qpoll_rate2))
+ {
+ ++.ignored_times;
+ end;
+ }
+
+ query_sql(sprintf("SELECT t.id, c.name AS grabee, d.name AS grabber, t.timestamp, t.message "
+ "FROM `tree_quotes` t "
+ "JOIN `char` c ON t.char_id = c.char_id "
+ "JOIN `char` d ON t.grabber = d.char_id "
+ "WHERE t.id=%i ORDER BY t.id DESC LIMIT 1;",
+ .@id),
+ .@nid[0], .@grabee$[0], .@grabber$[0], .@time[0], .@msg$[0]);
+
+ .last_query = gettimetick(2);
+
+ if (.@nid[0] < 1)
+ {
+ delayed_reply(sprintf("##BError: I couldn't find quote #%i in the database.", .@id));
+ end;
+ }
+
+ delayed_reply(sprintf("<%s> ##B%s##b ##a— grabbed by %s %s.",
+ .@grabee$[0], .@msg$[0], .@grabber$[0], FuzzyTime(.@time[0],0,1)));
+ end;
+ }
+
+ function random_quote {
+ .@name$ = escape_sql(getarg(0, ""));
+
+ if (gettimetick(2) - .last_query < (is_trusted() ? .qpoll_rate : .qpoll_rate2))
+ {
+ ++.ignored_times;
+ end;
+ }
+
+ query_sql("SELECT t.id, c.name AS grabee, d.name AS grabber, t.timestamp, t.message "
+ "FROM `char` c "
+ "JOIN `tree_quotes` t ON t.char_id = c.char_id "
+ "JOIN `char` d ON d.char_id = t.grabber " +
+ rif(.@name$ != "", sprintf("WHERE c.name='%s' ", .@name$)) +
+ "ORDER BY RAND() LIMIT 1;",
+ .@nid[0], .@grabee$[0], .@grabber$[0], .@time[0], .@msg$[0]);
+
+ .last_query = gettimetick(2);
+
+ if (.@nid[0] < 1)
+ {
+ if (.@name$ != "")
+ delayed_reply(sprintf("##BError: I couldn't find any quote from `%s` in the database.", getarg(0, "")));
+ else
+ delayed_reply("##BError: The quote database is empty.");
+ end;
+ }
+
+ delayed_reply(sprintf("<%s> ##B%s##b ##a— grabbed by %s %s. (#%i)",
+ .@grabee$[0], .@msg$[0], .@grabber$[0], FuzzyTime(.@time[0],0,1), .@nid[0]));
+ end;
+ }
+
+ function trigger_hotword {
+ .@o$ = getarg(0, ""); // original lowercase
+ .@m$ = replacestr(.@o$, "*", ""); // original lowercase clean
+
+
+ if (.@m$ ~= "(?:^| )tell(?: (?:me|him|her|us|them))? a(?:n ?other| lame| bad| boring)? joke")
+ reply(relative_array_random(.jokes$));
+
+ else if (.@m$ ~= "(?:^| )heal me(?:$|[^a-z])")
+ reply(relative_array_random(.healing$));
+ // XXX: maybe actually heal the player once in a while
+
+ else if (.@m$ ~= "(?:^| )(?:what|who) are you")
+ reply(relative_array_random(.whoami$));
+
+ else if (.@m$ ~= rp("(?:^| )(?:hi+|hello|heya?|hiya|good (?:morning|afternoon))[^a-z]* .*~t|~t.* (?:hi+|hello|heya?|hiya)"))
+ {
+ .blocked = 0;
+ .@rpl$ = relative_array_random(.greetings$);
+ reply(.@rpl$);
+ }
+
+ else if (.@o$ ~= rp("(?:^[*]| )(?:kicks?|shakes?) .*~t"))
+ reply(special_drops());
+
+ else if (.@o$ ~= rp("(?:^[*]| )(?:cuts?|nukes?|kills?|chops? down|saws?|hews?|murders?) .*~t"))
+ reply(relative_array_random(.kill$));
+
+ else if (.@o$ ~= rp("(?:^[*]| )pokes? .*~t"))
+ reply(relative_array_random(.poke$));
+
+ else if (.@o$ ~= rp("(?:^[*]| )(?:waters?|pees?|licks?) .*~t"))
+ reply(relative_array_random(.disgusting$));
+
+ else if (compare(.@m$, " answer ") && .@m$ ~= "(?:life|universe|everything)(?:$|[^a-z])")
+ reply(relative_array_random(.answer$));
+
+ else if (.@o$ ~= rp("(?:^[*]| )(?:burns?|incinerates?|ignites?) .*~t"))
+ reply(relative_array_random(.burning$));
+ // XXX: maybe here send a fire particle effect
+
+ else if (.@m$ ~= rp("(?:^| )die ~t"))
+ reply(relative_array_random(.die$));
+
+ else if (.@o$ ~= rp("(?:^[*]| )bites? .*~t|(?:^[*]| )drops? .* on ~t"))
+ reply(relative_array_random(.silly$));
+
+ else if (.@m$ ~= rp("(?:^| )(?:loves?|hugs?|kiss(es)?) .*~t|~t.* love(?:$|[^a-z])"))
+ reply(relative_array_random(.love$));
+
+ else if (.@m$ ~= rp("(?:^| )dance .*~t|~t.* dance(?:$|[^a-z])"))
+ reply(relative_array_random(.dance$));
+
+ else if (.@m$ ~= rp("(?:^| )hates? .*~t"))
+ reply(relative_array_random(.hate$));
+
+ else if (.@o$ ~= rp("(?:^[*]| )(?:eats?|shoots?|plucks?|tortures?|slaps?|slaps?|poisons?|breaks?|stabs?|throws?|punch(?:es)?) .*~t"))
+ reply(relative_array_random(.pain$));
+
+ else if (.@o$ ~= rp("(?:^[*]| )(?:climbs?|rides?|mounts?) .*~t"))
+ reply(relative_array_random(.climb$));
+
+ else if (.@m$ ~= "(?:^| )(?:see y(?:a|ou)|good night|(?:bye)?bye+)(?:$|[^a-z])")
+ reply(relative_array_random(.bye$));
+
+ else if (.@m$ ~= rp("(?:^| )bad ~t"))
+ reply(relative_array_random(.bad$));
+
+ else if (.@m$ ~= "(?:^| )(?:how old are you|uptime)(?:$|[^a-z])")
+ reply("%%B Server uptime: " + FuzzyTime(.uptime, 1, 99) + ".");
+
+ else if (.@m$ ~= "(?:^| )how chatty are you(?:$|[^a-z])")
+ reply("%%B Answered " + .answered_times + " times, ignored " + .ignored_times + " times.");
+
+ else if (.@m$ ~= "(?:^| )what.* version(?:$|[^a-z])")
+ reply("%%B ~n, version " + .version + "."); // XXX: maybe return Hercules version and serverdata commit instead
+
+ else if (.@m$ ~= "(?:^| )(?:(?:8|eight)[ -]?ball|(?:should|would|will|do|does) (?:i|you|he|she|it|we|they))(?:$|[^a-z])")
+ reply(relative_array_random(.eightball$));
+
+ else if (.@m$ ~= "(?:^| )roll(?: a| the)? dice(?:$|[^a-z])")
+ reply(roll_dice(1, 6));
+
+ else if (.@m$ ~= "(?:^| )roll(?: a)? ([1-8])d((?:[1-9][0-9]{0,10})?)(?:$|[^0-9a-z])")
+ reply(roll_dice(atoi($@regexmatch$[1]), atoi($@regexmatch$[2])));
+
+ else if (.@m$ ~= "(?:^| )roll ([1-8]) dices?(?:$|[^a-z])")
+ reply(roll_dice(atoi($@regexmatch$[1]), 6));
+
+ else if (.@m$ ~= "(?:^| )(?:flip|toss)(?: a| the)? coin(?:$|[^a-z])")
+ reply(flip_coin(1));
+
+ else if (.@m$ ~= "(?:^| )(?:flip|toss) ([1-8]) coins?(?:$|[^a-z])")
+ reply(flip_coin(atoi($@regexmatch$[1])));
+
+ else if (.@m$ ~= "(?:^| )(?:press|pull)(?: the)? trigger(?:$|[^a-z])")
+ roulette();
+
+ else if (.@m$ ~= "(?:^| )(?:how long|what) is(?: my)? monologue(?:$|[^a-z])")
+ reply(monologue_player());
+
+ else if (.@m$ ~= "(?:^| )who am i(?:$|[^a-z])")
+ reply(who_player());
+
+ else if (.@m$ ~= "(?:^| )shut up(?:$|[^a-z])")
+ {
+ reply(relative_array_random(.shut_up$));
+ .blocked = gettimetick(2);
+ }
+
+ else if (rand(.dunno_rate) == 0)
+ reply(relative_array_random(.no_idea$));
+
+ else
+ ++.ignored_times;
+
+ end;
+ }
+
+ function trigger_hiall {
+ if (rand(.hiall_rate) == 0)
+ reply(relative_array_random(.greetings$));
+
+ else
+ ++.ignored_times;
+
+ end;
+ }
+
+OnClick:
+ //if ($@xmas_time)
+ // goto L_XmasTree;
+ if ($@halloween_time)
+ callfunc "HalloweenTree";
+ tree_panel();
+ end;
+
+
+OnTalkNearby:
+ .@no_nick$ = strip(strip_formatting(substr($@p0$, getstrlen(strcharinfo(PC_NAME)) + 3, getstrlen($@p0$) - 1))); // not very obvious stuff
+ .@no_nick_lower$ = strtolower(.@no_nick$); // FIXME: hercules doesn't have a way to do case insensitive regex yet
+ .@no_nick_clean$ = replacestr(.@no_nick_lower$, "*", "");
+
+ htput(.msg_ht, strcharinfo(PC_NAME), getcharid(CHAR_ID_CHAR) + ":" + gettimetick(2) + ":" + .@no_nick$); // log last message, for quotegrabs
+ .lastsender = getcharid(CHAR_ID_CHAR); // for monologue
+
+ .last_activity = gettimetick(2); // for the auto-janitor
+
+ if ((is_trusted() || #Tree_Trusted) && charat(.@no_nick$, 0) == .symbol$)
+ {
+ if (.@no_nick$ ~= "^.grab \"?([^#:@\"]{4,23})\"?$")
+ reply(grab_quote($@regexmatch$[1]));
+
+ else if (.@no_nick$ ~= "^.(?:ungrab|remove|delete)(?: quote)? #([0-9]+)$")
+ reply(remove_quote(atoi($@regexmatch$[1])));
+
+ else if (.@no_nick$ ~= "^.(?:ungrab|remove|delete)(?: last(?: quote)?)?$")
+ reply(remove_quote(.q_last_id));
+
+ else if (.@no_nick$ ~= "^.(?:quote|cite) #([0-9]+)$")
+ reply(cite_quote(atoi($@regexmatch$[1])));
+
+ else if (.@no_nick$ ~= "^.(?:(?:random )?quote|cite)(?: anyone| someone| random)?$")
+ reply(random_quote());
+
+ else if (.@no_nick$ ~= "^.(?:quote|cite) \"?([^#:@\"]{4,23})\"?$")
+ reply(random_quote($@regexmatch$[1]));
+
+ else if (.@no_nick$ ~= "^.seen \"?([^#:@\"]{4,23})\"?$")
+ reply(have_you_seen($@regexmatch$[1]));
+
+ // to allow trusted testers to reboot without knowing the exit code
+ else if (debug && .@no_nick$ ~= "^.re(?:boot|load|start)(?:(?: the)? server)?$")
+ {
+ announce("The server is rebooting. This may take a couple minutes.", bc_all);
+ sleep2(1000);
+ atcommand("@serverexit 104");
+ }
+
+ // exit, pull all, clean, build, reboot
+ else if (debug && .@no_nick$ ~= "^.re-?build(?:(?: the)? server)?$")
+ {
+ announce("The server is rebuilding. This will take several minutes.", bc_all);
+ sleep2(1000);
+ atcommand("@serverexit 108");
+ }
+
+ else if (.@no_nick$ ~= "^.(?:add )?ignored? \"?([^#:@\"]{4,23})\"?$")
+ {
+ .@chr = getcharid(CHAR_ID_ACCOUNT, $@regexmatch$[1]);
+ if (.@chr < 1)
+ {
+ reply("##BError: Player not found or not online.");
+ end;
+ }
+ htput(.ignore_ht, strcharinfo(PC_NAME, .@chr), gettimetick(2) + 3600);
+ reply(sprintf("Success: Player `%s` is now ignored for 1 hour.",
+ strcharinfo(PC_NAME, .@chr)));
+ }
+
+ else if (.@no_nick$ ~= "^.(?:un|de-?|remove )ignored? \"?([^#:@\"]{4,23})\"?$")
+ {
+ .@chr = getcharid(CHAR_ID_ACCOUNT, $@regexmatch$[1]);
+ if (.@chr < 1)
+ {
+ reply("##BError: Player not found or not online.");
+ end;
+ }
+ htput(.ignore_ht, strcharinfo(PC_NAME, .@chr), 0);
+ reply(sprintf("Success: Player `%s` is no longer ignored.",
+ strcharinfo(PC_NAME, .@chr)));
+ }
+
+ else if (is_admin() && .@no_nick$ ~= "^.(?:add )?trust(?:ed)? \"?([^#:@\"]{4,23})\"?$")
+ {
+ .@chr = getcharid(CHAR_ID_ACCOUNT, $@regexmatch$[1]);
+ if (.@chr < 1)
+ {
+ reply("##BError: Player not found or not online.");
+ end;
+ }
+ set(getvariableofpc(#Tree_Trusted, .@chr), true);
+ reply(sprintf("Success: Player `%s` can now use restricted commands.",
+ strcharinfo(PC_NAME, .@chr)));
+ }
+
+ else if (is_admin() && .@no_nick$ ~= "^.(?:un|de-?|remove )trust(?:ed)? \"?([^#:@\"]{4,23})\"?$")
+ {
+ .@chr = getcharid(CHAR_ID_ACCOUNT, $@regexmatch$[1]);
+ if (.@chr < 1)
+ {
+ reply("##BError: Player not found or not online.");
+ end;
+ }
+ set(getvariableofpc(#Tree_Trusted, .@chr), false);
+ reply(sprintf("Success: Player `%s` can no longer use restricted commands.",
+ strcharinfo(PC_NAME, .@chr)));
+ }
+
+ else
+ reply("##BError: Command not found or invalid syntax.");
+ }
+
+ else if (.@no_nick_lower$ ~= rp("^(~t[^a-z ]* .*|(?:.* (?:~t[^a-z ]* .*|~t[^ a-z]*)))$"))
+ {
+ check_is_ignored();
+ trigger_hotword($@regexmatch$[1]);
+ }
+
+ else if (.@no_nick_clean$ ~= "^(hi(ya)?|hello|heya?) (all|friends|every(one|body))")
+ {
+ check_is_ignored();
+ trigger_hiall();
+ }
+
+ else
+ {
+ if (.lastsender == getcharid(CHAR_ID_CHAR))
+ @monologue++;
+
+ else
+ @monologue = 1;
+ }
+
+ // TODO: eliza mode, whisper eliza mode
+ end;
+
+OnTouch:
+ if (rand(.touch_rate) == 0) {
+ face();
+ }
+ end;
+
+OnDoReply:
+ if (@tree_reply$ != "") {
+ npctalk(@tree_reply$);
+ @tree_reply$ = "";
+ }
+ end;
+
+OnPCLogoutEvent:
+ seen_me();
+ end;
+
+OnTimer3600000:
+ // scheduled janitor
+ .@now = gettimetick(2);
+ initnpctimer(); // schedule next
+
+ if (.last_activity > (.@now - 3600)) {
+ end; // last activity is too recent
+ }
+
+ // cleanup routine below
+ .lastsender = 0;
+ .last_activity = 0;
+ .last_reply = 0;
+ .last_emote = 0;
+ .last_query = 0;
+ .blocked = 0;
+ .enable_janitor = 0;
+
+ htclear(.msg_ht); // empty the message table (quotegrabs)
+ htclear(.ignore_ht); // empty the ignore table
+
+ .@it = htiterator(.seen_ht); // allocate new iterator
+ for (.@key$ = htinextkey(.@it); hticheck(.@it); .@key$ = htinextkey(.@it)) {
+ if (.@key$ == "") {
+ continue;
+ }
+
+ if (htget(.seen_ht, .@key$, 0) < (.@now - 86400)) {
+ htput(.seen_ht, .@key$, 0); // remove from hash table if older than 24h
+ }
+ }
+ htidelete(.@it); // free the iterator
+
+ face(); // do an emote (because why not)
+ end;
+
+
+OnInit:
+ // config below
+ .hotwords$ = "tree"; // what hot words the npc should listen to, besides its own name (regex)
+ .distance = 14; // the npc will only listen to player within X tiles
+ //.dir = season_direction(); // sprite direction according to the season
+ .talk_rate = 1; // min number of seconds to wait between replies
+ .repeat_rate = 1; // min number of seconds to wait before sending the same message twice in a row
+ .block_time = 600; // how long to stay quiet after someone says shut up, in seconds
+ .emote_rate = 3; // min number of seconds to wait between emotes
+ .sdrop_rate = 8; // 1 in X chances to get a special drop
+ .sname_rate = 8; // 1 in X chances to get a special name
+ .dunno_rate = 2; // 1 in X chances to get a reply when the command is not found
+ .hiall_rate = 2; // 1 in X chances to reply to a "hi everyone"
+ .touch_rate = 4; // 1 in X chances to trigger the OnTouch action
+ .qpoll_rate = 1; // min number of seconds to wait before calling the sql db again for GMs
+ .qpoll_rate2 = 5; // min number of seconds to wait before calling the sql db again for non-GMs (currently unused)
+ .delay_reply = 250; // number of ms to wait to reply
+ .enable_janitor = true; // automatically free memory when idle
+ .symbol$ = "~"; // symbol for GM-only commands
+
+ // register some arrays
+ callfunc("TREE_dictionaries");
+
+ // do random stuff
+ make_quote_table();
+ face();
+
+ // boring stuff below
+ .version[0] = 21; // increase this when you make a change
+ .version[1] = 1;
+ .uptime = gettimetick(2);
+ .alwaysVisible = true; // the NPC doesn't de-spawn when moving away
+ .pid = 1; // regex pattern id
+ .msg_ht = htnew(); // hashtable id for message history
+ .seen_ht = htnew(); // hashtable id for seen log
+ .ignore_ht = htnew(); // hashtable id for ignored players
+ .roulette = rand(1, 7); // spin the chambers
+ defpattern(.pid, "^(.*)$", "OnTalkNearby");
+ activatepset(.pid);
+ if (.enable_janitor) {
+ initnpctimer();
+ }
}
+
+// Duplicates below
+001-1,57,75,0 duplicate(#ConfusedTree) #ConfusedPalmTree NPC384,14,14
+020-1,90,78,0 duplicate(#ConfusedTree) #ConfusedIcedTree NPC380,14,14
+
diff --git a/npc/functions/confused-tree-dict.txt b/npc/functions/confused-tree-dict.txt
new file mode 100644
index 00000000..e1c0f555
--- /dev/null
+++ b/npc/functions/confused-tree-dict.txt
@@ -0,0 +1,515 @@
+// Evol scripts.
+// Author:
+// gumi
+// rein
+// Based on CrazyTree, originally made by:
+// gumi
+// pclouds
+// veryape
+// wushin
+// Description:
+// dictionaries for confused tree
+
+// Built-in variables:
+// ~t lowercase hot word regex
+// ~n npc name
+// ~p player name or special name
+// ~P player name only
+//
+// Custom variables:
+// {{var}} random from array .D_var$
+// {{^var}} same but capitalize
+// {{+var}} same but title case
+// {{!var}} same but all caps
+// You can also specify multiple variables, separated by a comma (,)
+//
+// Example:
+// "*drops a {{! size }} {{ color }} {{^ sizeable object, someone }} on ~p's head*"
+
+function script TREE_dictionaries {
+ .npc$ = strnpcinfo(0);
+
+ // special aliases below (regex of lowercase char names)
+ // the substitutions are an array separated by backticks (`)
+ // XXX: this could become a hashtable at some point if it gets too big
+
+ setarray(getvariableofnpc(.alias$[0], .npc$),
+ "^veryape(?:gm)?$", "hairyape",
+
+ "^wu-?shin$", "Dwarven Princess`"
+ "She-Ra",
+
+ "^reid$", "Mistress`"
+ "Milady`"
+ "R'eid",
+
+ "^(?:slicer|madcamel)$", "Camel Toe",
+
+ "^4144(?:4d494e)?$", "NPC",
+
+ "^omatt$", "@@https://youtu.be/S2qiZoqH9OY|omatt@@`"
+ "o'matt",
+
+ "^prsm$", "Refractor`"
+ "Overlord");
+ set(getvariableofnpc(.alias, .npc$), getarraysize(getvariableofnpc(.alias$, .npc$)));
+
+
+
+ // special drops below (regex of lowercase char names)
+ // the substitutions are an array of standard replies separated by backticks (`)
+ // and allow {{variables}}
+ // XXX: this could become a hashtable at some point if it gets too big
+
+ setarray(getvariableofnpc(.sdrops$[0], .npc$),
+ "^reid$", "*drops an empty jar of Nutella on ~p*",
+ "^omatt$", "*drops Elisabeth Granneman on ~p*");
+ set(getvariableofnpc(.sdrops, .npc$), getarraysize(getvariableofnpc(.sdrops$, .npc$)));
+
+
+ // variables below
+
+ setarray(getvariableofnpc(.D_size$[1], .npc$), // {{size}}
+ "tiny", 1,
+ "small", 1,
+ "perfectly sized", 1,
+ "large", 1,
+ "huge", 1,
+ "humongous", 1,
+ "ginormous", 1);
+ set(getvariableofnpc(.D_size, .npc$), getarraysize(getvariableofnpc(.D_size$, .npc$)));
+
+ setarray(getvariableofnpc(.D_color$[1], .npc$), // {{color}}
+ "red", 1,
+ "orange", 1,
+ "yellow", 1,
+ "pink", 1,
+ "aqua", 1,
+ "cyan", 1,
+ "blue", 1,
+ "indigo", 1,
+ "violet", 1,
+ "purple", 1,
+ "magenta", 1,
+ "pink", 1,
+ "black", 1,
+ "white", 1,
+ "grey", 1,
+ "greyscale", 1,
+ "brown", 1,
+ "maroon", 1,
+ "turquoise", 1,
+ "lime", 1,
+ "sky blue", 1,
+ "invisible", 1);
+ set(getvariableofnpc(.D_color, .npc$), getarraysize(getvariableofnpc(.D_color$, .npc$)));
+
+ setarray(getvariableofnpc(.D_violentadverb$[1], .npc$), // {{violent adverb}}
+ "violently", 1,
+ "repeatedly", 1,
+ "casually", 1,
+ "forcefully", 1,
+ "slowly", 1,
+ "carefully", 1,
+ "hopefully", 1,
+ "dangerously", 1,
+ "shockingly", 1,
+ "religiously", 1);
+ set(getvariableofnpc(.D_violentadverb, .npc$), getarraysize(getvariableofnpc(.D_violentadverb$, .npc$)));
+
+ setarray(getvariableofnpc(.D_hello$[1], .npc$), // {{hello}}
+ "hi", 4,
+ "hey", 3,
+ "yo", 2,
+ "hello", 10,
+ "howdy", 1,
+ "bonjour", 1);
+ set(getvariableofnpc(.D_hello, .npc$), getarraysize(getvariableofnpc(.D_hello$, .npc$)));
+
+ setarray(getvariableofnpc(.D_violentverb$[1], .npc$), // {{violent verb}}
+ "slaps", 5,
+ "hits", 1,
+ "pummels", 1,
+ "beats", 1,
+ "flattens", 1,
+ "taunts", 1,
+ "liquidates", 1,
+ "spanks", 1,
+ "affronts", 1,
+ "tranquilizes", 1,
+ "atomizes", 1,
+ "impales", 1,
+ "dismembers", 1);
+ set(getvariableofnpc(.D_violentverb, .npc$), getarraysize(getvariableofnpc(.D_violentverb$, .npc$)));
+
+ // FIXME: Is this even used anywhere?
+ setarray(getvariableofnpc(.D_location$[1], .npc$), // {{location}}
+ "Artis", 1,
+ "Hurnscald", 1,
+ "Tulimshar", 1,
+ "Nivalis", 1,
+ "Candor", 1,
+ "Drasil", 1);
+ set(getvariableofnpc(.D_location, .npc$), getarraysize(getvariableofnpc(.D_location$, .npc$)));
+
+ setarray(getvariableofnpc(.D_sizeableobject$[1], .npc$), // {{sizeable object}}
+ "trout", 1,
+ "whale", 1,
+ "space whale", 1,
+ "penguin", 1,
+ "coelacanth", 1,
+ "squid", 1,
+ "shrimp", 1,
+ "crab", 1,
+ "tentacle", 1,
+ "dictionary", 1,
+ "grammar book", 1,
+ "textbook", 1,
+ "dinosaur", 1,
+ "t-rex", 1,
+ "star-nosed mole", 1,
+ "chimpanzee", 1,
+ "mermaid", 1,
+ "merman", 1,
+ "piano", 1,
+ "prince", 1,
+ "princess", 1,
+ "pinkie", 1,
+ "squirrel", 1,
+ "mouboo", 1,
+ "wet mop", 1,
+ "drunken pirate", 1,
+ "cake", 1,
+ "cookie", 1,
+ "chocobo", 1,
+ "restraining order", 1,
+ "freight train", 1,
+ "carnival hammer", 1,
+ "crate", 1,
+ "bomb", 1,
+ "bowl of petinuas", 1,
+ "box", 1,
+ "platypus", 1,
+ "magic eightball", 1,
+ "vase", 1);
+ set(getvariableofnpc(.D_sizeableobject, .npc$), getarraysize(getvariableofnpc(.D_sizeableobject$, .npc$)));
+
+ setarray(getvariableofnpc(.D_nsizeableobject$[1], .npc$), // {{n sizeable object}}
+ "octopus", 1,
+ "elephant", 1,
+ "angry cat", 1,
+ "anvil", 1,
+ "encyclopedia set", 1);
+ set(getvariableofnpc(.D_nsizeableobject, .npc$), getarraysize(getvariableofnpc(.D_nsizeableobject$, .npc$)));
+
+ setarray(getvariableofnpc(.D_someone$[1], .npc$), // {{someone}}
+ "Voldemort", 1,
+ "Cthulhu", 1,
+ "Platyna", 1,
+ "Hitler", 1,
+ "Luvia", 1,
+ "General Krukan", 1,
+ "Borg Queen", 1,
+ "Freeyorp", 1,
+ "MadCamel", 1);
+ set(getvariableofnpc(.D_someone, .npc$), getarraysize(getvariableofnpc(.D_someone$, .npc$)));
+
+ // replies below
+
+ setarray(getvariableofnpc(.greetings$[1], .npc$),
+ "{{^ hello }} ~p!", 4,
+ "{{^ hello }} ~p.", 6,
+ "{{^ hello }} ~p, what's up?", 1,
+ "{{^ hello }} ~p, anything new?", 1,
+ "{{^ hello }} ~p, how are you?", 1,
+ "~p!!!!", 1,
+ "~p!!!", 1,
+ "~p!!", 1,
+ "{{^ hello }} ~p! You are looking lovely today!", 1,
+ "Welcome back, ~p.", 3,
+ "~p is back!!", 1,
+ "Hello and welcome to the Aperture Science computer-aided enrichment center.", 1,
+ "Greetings ~p.", 1,
+ "What's up ~p?", 2,
+ "How are you ~p?", 1);
+ set(getvariableofnpc(.greetings, .npc$), getarraysize(getvariableofnpc(.greetings$, .npc$)));
+
+ setarray(getvariableofnpc(.jokes$[1], .npc$),
+ "How did the tree get drunk? On root beer.", 1,
+ "Do you think I'm lazy?", 1,
+ "I miss CrazyTree %%S.", 1,
+ "I miss LazyTree %%S.", 1,
+ "I'm not telling you!", 1,
+ "*sighs.*", 1,
+ "If I do it for you, then I have to do it for everybody.", 1,
+ "What did the beaver say to the tree? It's been nice gnawing you.", 1,
+ "What did the little tree say to the big tree? Leaf me alone.", 1,
+ "What did the tree wear to the pool party? Swimming trunks.", 1,
+ "What do trees give to their dogs? Treets.", 1,
+ "What do you call a tree that only eats meat? Carniforous.", 1,
+ "What do you call a tree who's always envious? Evergreen.", 1,
+ "What is the tree's least favourite month? Sep-timber!", 1,
+ "What kind of tree can fit into your hand? A palm-tree.", 1,
+ "What was the tree's favorite subject in school? Chemistree.", 1,
+ "Why did the leaf go to the doctor? It was feeling green.", 1,
+ "Why doesn't the tree need sudo? Because it has root.", 1,
+ "Why was the cat afraid of the tree? Because of its bark.", 1,
+ "Why was the tree executed? For treeson.", 1,
+ "How do trees get on the internet? They log in.", 1,
+ "Why did the pine tree get into trouble? Because it was being knotty.", 1,
+ "Did you hear the one about the oak tree? It's a corn-y one!", 1,
+ "What do you call a blonde in a tree with a briefcase? Branch Manager.", 1,
+ "How is an apple like a lawyer? They both look good hanging from a tree.", 1,
+ "Why did the sheriff arrest the tree? Because its leaves rustled.", 1,
+ "I'm too tired, ask someone else.", 1,
+ "If you are trying to get me to tell jokes you are barking up the wrong tree!", 1,
+ "You wooden think they were funny anyhow. Leaf me alone!", 1,
+ "What is brown and sticky? A stick.", 1,
+ "What's the best way to carve wood? Whittle by whittle.", 1,
+ "What did the tree do when the bank closed? It started its own branch.", 1,
+ "Do you want a brief explanation of an acorn? In a nutshell, it’s an oak tree.", 1,
+ "A snare drum and a crash cymbal fell out of a tree. *BA-DUM TSSSHH*", 1,
+ "How do you properly identify a dogwood tree? By the bark!", 1,
+ "Where do saplings go to learn? Elementree school.", 1,
+ "Why do trees make great thieves? Sticky fingers.", 1,
+ "What is green, has leaves, and a trunk? A houseplant going on vacation.", 1,
+ "Where can Adansonia trees go for a quick trim? To the baobarber.", 1,
+ "What looks like half a spruce tree? The other half.", 1,
+ "What do you give to a sick citrus tree? Lemon aid.", 1,
+ "What did the tree say to the drill? You bore me.", 1,
+ "What happened to the wooden car with wooden wheels and a wooden engine? It wooden go.", 1,
+ "How do trees keep you in suspense? I'll tell you tomorrow.", 1,
+ "Where do birch trees keep their valuables? In a river bank.", 1,
+ "What kind of stories do giant sequoia trees tell? Tall tales.", 1,
+ "What is the most frustrating thing about being a tree? Having so many limbs and not being able to walk.", 1,
+ "What's black, highly dangerous, and lives in a tree? A crow with a machine gun.", 1,
+ "What kind of wood doesn't float? Natalie Wood.", 1,
+ "Two men passed a sign while looking for work. It was for tree fellers. They said: “what a shame, there are only two of us”.", 1);
+ set(getvariableofnpc(.jokes, .npc$), getarraysize(getvariableofnpc(.jokes$, .npc$)));
+
+ setarray(getvariableofnpc(.healing$[1], .npc$),
+ "Eat an apple, they're good for you.", 1,
+ "If I do it for you, then I have to do it for everybody.", 1,
+ "Oh, go drink a potion or something.", 1,
+ "Whoops! I lost my spellbook.", 1,
+ "No mana.", 1);
+ set(getvariableofnpc(.healing, .npc$), getarraysize(getvariableofnpc(.healing$, .npc$)));
+
+ setarray(getvariableofnpc(.whoami$[1], .npc$),
+ "An undercover GM.", 1,
+ "An exiled GM.", 1,
+ "I'm not telling you!", 1,
+ "I'm a bot! I'll be level 99 one day! Mwahahahaaha!!!111!", 1,
+ "Somebody said I'm a Chinese copy of CrazyTree.", 1,
+ "I am your evil twin.", 1,
+ "I don't remember anything after I woke up! What happened to me?", 1,
+ "I don't know. Why am I here??", 1,
+ "Who are you?", 1,
+ "On the 8th day, God was bored and said 'There will be bots'. So here I am.", 1,
+ "♪ I'm your hell, I'm your dream, I'm nothing in between ♪♪", 1,
+ "♪♪ Aperture Science. We do what we must, because... we can ♪", 1,
+ "I'm just a reincarnation of a copy.", 1);
+ set(getvariableofnpc(.whoami, .npc$), getarraysize(getvariableofnpc(.whoami$, .npc$)));
+
+ setarray(getvariableofnpc(.drops$[1], .npc$),
+ "*drops a {{ sizeable object }} on ~p's head.*", 8,
+ "*drops an {{ n sizeable object }} on ~p's head.*", 2,
+ "*drops {{ someone }} on ~p's head.*", 1,
+ "*drops a coin on ~p's head.*", 1,
+ "*drops a fruit on ~p's head.*", 1,
+ "*drops an apple on ~p's head.*", 1,
+ "*drops an iten on ~p's head.*", 1,
+ "*drops a GM on ~p.*", 1,
+ "*drops a piece of moon rock on ~p's head.*", 1,
+ "*drops a pin on ~p's head.*", 1,
+ "*drops a rock on ~p's head.*", 1,
+ "*drops a tub of paint on ~p's head.*", 1,
+ "*drops a sandworm on ~p.*", 1,
+ "*drops an idea in ~p's head.*", 1,
+ "*drops The Hitchhiker's Guide to the Galaxy on ~p's head.*", 1,
+ "Ouch.", 1,
+ "Ouchy.", 1,
+ "*drops dead.*", 1,
+ "*sighs.*", 1,
+ "Leaf me alone.", 1,
+ "Stop it! I don't drop branches, try the Druid tree for once!", 1);
+ set(getvariableofnpc(.drops, .npc$), getarraysize(getvariableofnpc(.drops$, .npc$)));
+
+ setarray(getvariableofnpc(.die$[1], .npc$),
+ "*drops an iten on ~p's head.*", 1,
+ "*drops a piece of moon rock on ~p's head.*", 1,
+ "*drops {{ someone }} on ~p's head.*", 1,
+ "*drops a {{ sizeable object }} on ~p's head.*", 3,
+ "*drops an {{ n sizeable object }} on ~p's head.*", 1,
+ "*drops a {{ size }} {{ sizeable object, n sizeable object }} on ~p's head.*", 1,
+ "*drops a {{ size }} {{ color }} {{ sizeable object, n sizeable object }} on ~p's head.*", 1,
+ "*{{ violent adverb }} {{ violent verb }} ~p.*", 1,
+ "*drops dead.*", 1,
+ "*sighs.*", 1,
+ "Avada Kedavra!", 1,
+ "Make me!", 1,
+ "Never!!", 1,
+ "You die, ~p!", 4,
+ "No!", 1,
+ "In a minute.", 1,
+ "Suuure... I'll get right on it...", 1);
+ set(getvariableofnpc(.die, .npc$), getarraysize(getvariableofnpc(.die$, .npc$)));
+
+ setarray(getvariableofnpc(.poke$[1], .npc$),
+ "*tickles.*", 1);
+ set(getvariableofnpc(.poke, .npc$), getarraysize(getvariableofnpc(.poke$, .npc$)));
+
+ setarray(getvariableofnpc(.disgusting$[1], .npc$),
+ "Ewwwww %%^.", 1);
+ set(getvariableofnpc(.disgusting, .npc$), getarraysize(getvariableofnpc(.disgusting$, .npc$)));
+
+ setarray(getvariableofnpc(.answer$[1], .npc$),
+ "42.", 1,
+ "Kittens.", 1);
+ set(getvariableofnpc(.answer, .npc$), getarraysize(getvariableofnpc(.answer$, .npc$)));
+
+ setarray(getvariableofnpc(.burning$[1], .npc$),
+ "*curses ~p and dies %%c.*", 1,
+ "Help! I'm on fire!", 1,
+ "Oh hot.. hot hot!", 1,
+ "*is glowing.*", 1,
+ "*is flaming.*", 1,
+ "Ehemm. Where are firefighters? I need them now!", 1,
+ "*is so hot!.*", 1,
+ "*slowly catches fire.*", 1,
+ "*trembles with trepidation.*", 1,
+ "*is immune to fire.*", 1);
+ set(getvariableofnpc(.burning, .npc$), getarraysize(getvariableofnpc(.burning$, .npc$)));
+
+ setarray(getvariableofnpc(.kill$[1], .npc$),
+ "*curses ~p and dies %%c.*", 1);
+ set(getvariableofnpc(.kill, .npc$), getarraysize(getvariableofnpc(.kill$, .npc$)));
+
+ setarray(getvariableofnpc(.silly$[1], .npc$),
+ "Hahaha, good one!", 1);
+ set(getvariableofnpc(.silly, .npc$), getarraysize(getvariableofnpc(.silly$, .npc$)));
+
+ setarray(getvariableofnpc(.love$[1], .npc$),
+ "♪♪ and IIII.. will alwayyyys loooovvve youuuuu. ♪♪ %%]", 1,
+ "♪♪ nothing's gonna change my love for you, you oughta know by now how much I love you.. ♪ %%]", 1,
+ "♪ ..and then I go and spoil it all, by saying something stupid like: “I love you.” ♪", 1,
+ "♪ ..won't you find a place for me? somewhere in your heart... ♪♪", 1,
+ "Thank you.", 1,
+ "♪♪ ..I can't love another when my heart is somewhere far away.. ♪", 1,
+ "%%]", 1);
+ set(getvariableofnpc(.love, .npc$), getarraysize(getvariableofnpc(.love$, .npc$)));
+
+ setarray(getvariableofnpc(.dance$[1], .npc$),
+ "I would but I am rooted to the ground.", 1,
+ "Have you ever seen a tree dance before?", 1,
+ "Hahaha, good one!", 1);
+ set(getvariableofnpc(.dance, .npc$), getarraysize(getvariableofnpc(.dance$, .npc$)));
+
+ setarray(getvariableofnpc(.hate$[1], .npc$),
+ "Right back at you!", 1,
+ "Ok...", 1,
+ "*pats ~p, let it go...*", 1,
+ "Hu hu hu, ~p hates me.", 1);
+ set(getvariableofnpc(.hate, .npc$), getarraysize(getvariableofnpc(.hate$, .npc$)));
+
+ setarray(getvariableofnpc(.bye$[1], .npc$),
+ "*waves goodbye to ~p in tears, come back soon!*", 1);
+ set(getvariableofnpc(.bye, .npc$), getarraysize(getvariableofnpc(.bye$, .npc$)));
+
+ setarray(getvariableofnpc(.pain$[1], .npc$),
+ "Ouch.", 1,
+ "Ouchy.", 1,
+ "Argh.", 1,
+ "Eek.", 1,
+ "*howls.*", 1,
+ "*screams.*", 1,
+ "*groans.*", 1,
+ "*cries.*", 1,
+ "*faints.*", 1,
+ "*shrieks.*", 1,
+ "*hides behind itself.*", 1,
+ "%%k", 1,
+ "Why, what did I do to you? %%i", 1);
+ set(getvariableofnpc(.pain, .npc$), getarraysize(getvariableofnpc(.pain$, .npc$)));
+
+ setarray(getvariableofnpc(.eightball$[1], .npc$),
+ "It is possible.", 1,
+ "Yes!", 1,
+ "Of course.", 1,
+ "Naturally.", 1,
+ "Obviously.", 1,
+ "It shall be.", 1,
+ "The outlook is good.", 1,
+ "It is so.", 1,
+ "One would be wise to think so.", 1,
+ "The answer is certainly yes.", 1,
+ "In your dreams.", 1,
+ "I doubt it very much.", 1,
+ "No chance.", 1,
+ "The outlook is very poor.", 1,
+ "Unlikely.", 1,
+ "About as likely as pigs flying.", 1,
+ "You're kidding, right?", 1,
+ "NO!", 1,
+ "NO.", 1,
+ "No.", 1,
+ "Maybe...", 1,
+ "No clue.", 1,
+ "I don't know.", 1,
+ "The outlook is hazy, please ask again later.", 1,
+ "What are you asking me for?", 1,
+ "Come again?", 1,
+ "You know the answer better than I.", 1,
+ "The answer is def-- oooh! shiny thing!", 1,
+ "No idea.", 1,
+ "Perhaps.", 1,
+ "I think it is better not to tell you.", 1,
+ "Error 417: Expectation failed.", 1);
+ set(getvariableofnpc(.eightball, .npc$), getarraysize(getvariableofnpc(.eightball$, .npc$)));
+
+ setarray(getvariableofnpc(.bad$[1], .npc$),
+ "I'm not bad! You are bad!", 1,
+ "OK, I'm bad.", 1,
+ "I'm just a littttle bad.", 1,
+ "Not as bad as the people that made me.", 1);
+ set(getvariableofnpc(.bad, .npc$), getarraysize(getvariableofnpc(.bad$, .npc$)));
+
+ setarray(getvariableofnpc(.no_idea$[1], .npc$),
+ "What?", 2,
+ "What??", 1,
+ "Whatever.", 1,
+ "Hmm...", 2,
+ "Huh?", 1,
+ "*yawns.*", 1,
+ "Wait a minute...", 1,
+ "What are you talking about?", 1,
+ "Who are you?", 1,
+ "What about me?", 1,
+ "I don't know what you are talking about", 1,
+ "Excuse me?", 1,
+ "Very interesting.", 1,
+ "Really?", 1,
+ "Go on...", 1,
+ "*scratches its leafy head.*", 1,
+ "*feels a disturbance in the force.*", 1,
+ "%%j", 1,
+ "*senses a disturbance in the force.*", 1,
+ "I'm bored...", 1,
+ "%%U", 1,
+ "%%[", 1);
+ set(getvariableofnpc(.no_idea, .npc$), getarraysize(getvariableofnpc(.no_idea$, .npc$)));
+
+ setarray(getvariableofnpc(.shut_up$[1], .npc$),
+ "*goes hide in a corner %%S.*", 1);
+ set(getvariableofnpc(.shut_up, .npc$), getarraysize(getvariableofnpc(.shut_up$, .npc$)));
+
+ setarray(getvariableofnpc(.climb$[1], .npc$),
+ "*sways violently.*", 1,
+ "*bends all the way to the ground.*", 1,
+ "*creaks and bends.*", 1,
+ "*welcomes those who come to play %%I.*", 1,
+ "*beams with pride.*", 1);
+ set(getvariableofnpc(.climb, .npc$), getarraysize(getvariableofnpc(.climb$, .npc$)));
+
+ return;
+}
diff --git a/npc/scripts.conf b/npc/scripts.conf
index 6ad4e69b..cddf86da 100644
--- a/npc/scripts.conf
+++ b/npc/scripts.conf
@@ -107,6 +107,7 @@
// Events
"npc/functions/gm_island.txt",
+"npc/functions/confused-tree-dict.txt",
"npc/annuals/fathertime.txt",
"npc/annuals/check_time.txt",
"npc/annuals/tree_beard.txt",