Implementing Speech in Blackwood
This is intended to illustrate the structure and calling
sequences of the various objects and verbs that create speech in
Blackwood.
- Root Class (#1) has a tell verb
- GUI Root (#100) has a tell verb
- generic room (#3) has a say verb
- Generic Editor (#50) has a say verb
- GUI Room (#102)
- GameRoom (#1805) has an announce verb
- generic player (#6) has a tell verb
- Frand's player class (#90),
- Generic Mail Receiving Player (#40)
- GUI Avatar (#1497) has a say, init_response,
get_response,set_resp_list, and a response_tree verb
- Generic Agent (#532) has a tell and a respond verb
- Generic Atmosphere Agent (#1443)
- Generic Buffalo Hunter (#645)
- Grizzly Adams (#647) has a tell verb
- Generic Walker (#3210)
- Otis (#1434) has a respond verb
- Login Commands (#10) has a tell verb
- Generic Utilities Package (#79)
- Game Utils (#97)
- interface_messages (#1773) has an announce verb
@list #1:tell
#1:tell this none this
1: this:notify(tostr(@args));
@list #100:tell
#100:tell none none none
1: if ($g.gen_agent in $object_utils:ancestors(this))
2: this:respond(tostr(@args));
3: else
4: pass(@args);
5: endif
@list #3:say
#3:say any any any
1: try
2: player:tell("You say, \"", argstr, "\"");
3: this:announce(player.name, " ", $gender_utils:get_conj("says", player), ", \"", argstr, "\"");
4: except (ANY)
5: "Don't really need to do anything but ignore the idiot who has a bad :tell";
6: endtry
@list #50:say
#50:say any any any
1: if (caller != player && caller_perms() != player)
2: return E_PERM;
3: endif
4: if (!(who = this:loaded(player)))
5: player:tell(this:nothing_loaded_msg());
6: else
7: this:insert_line(who, argstr);
8: endif
@list #1805:announce
#1805:announce this none this
1: old_player = player;
2: source = player;
3: if (player.gui && length(args) == 6 && args[3] == "says" && args[6] == "\"")
4: source = $string_utils:match_object(args[1], this);
5: for object in (this.contents)
6: $command_utils:suspend_if_needed(0);
7: $g.interface_messages:announce(object, source, args[5]);
8: endfor
9: endif
10: player = source;
11: pass(@args);
12: player = old_player;
@list #6:tell
#6:tell this none this
1: if (this.gaglist || this.paranoid)
2: "Check the above first, default case, to save ticks. Paranoid gaggers are cost an extra three or so ticks by this, probably a net savings.";
3: if (this:gag_p())
4: return;
5: endif
6: if (this.paranoid == 1)
7: $paranoid_db:add_data(this, {{@callers(), {player, "", player}}, args});
8: elseif (this.paranoid == 2)
9: z = this:whodunnit({@callers(), {player, "", player}}, {this, $no_one}, {})[3];
10: args = {"(", z.name, " ", z, ") ", @args};
11: endif
12: endif
13: pass(@args);
@list #1497:say
#1497:say any none none
1: "";
2: " say: to make the response_tree stuff work ";
3: "";
4: if (caller == #2 || caller == #2965)
5: #2:tell("Player is ", player, " caller is ", caller, " this is ", this);
6: #2:tell("Entering say with ", toliteral(args));
7: endif
8: if (!args || argstr == "")
9: return 0;
10: else
11: old_player = player;
12: this:tell("You say, \"", argstr, "\"");
13: " changed old DollarBay 4-arg format to Blackwood 6-arg, bms 27Jul99 ";
14: " this.location:announce(this.name, * says, **, argstr, ***); ";
15: this.location:announce(this.name, " ", $gender_utils:get_conj("says", player), ", \"", argstr, "\"");
16: player = old_player;
17: endif
@list #1497:init_response
#1497:init_response none none none
1: this.reporting = 0;
2: this.responses = {};
3: this:set_resp_list();
4: this:response_tree("init");
@list #1497:get_response
#1497:get_response this none none
1: player:tell("#CONVERSATION|" + toliteral(verb_code(this, "set_resp_list")) + "|");
@list #1497:set_resp_list
#1497:set_resp_list none none none
1: this.resp_list =
{{"init",
{},
{"@How are you?", "@What is your favourite color?", "@What will you do this weekend?"}},
{"That's good.", {}, {}},
{"Me too.", {}, {}},
{"Get some rest.", {}, {}},
{"How are you?",
{"~OR", "I'm fine.", "I'm tired.", "I'll better after finals."},
{"~OR", "@That's good.", "@Me too.", "@Get some rest."}},
{"Why?", {}, {}},
{"What is your favourite color?",
{"~R", "Green.", "Red.", "Blue.", "Pintk."},
{"@What is your favourite color?", "@Why?"}},
{"What will you do this weekend?",
{"~W", "I will ...", "... was my clothes ...", "... and some other stuff."},
{"@What will you do this weekend?"}}};
@list #1497:response_tree
#1497:response_tree this none this
1: "";
2: " response_tree: for an agent to manage a network of responses, and ";
3: " say the right thing. Original code by Chris Engel, ";
4: " Spring 97, modifed by Jozef Zelenak, Spring 99, ";
5: " ported to Blackwood, added .status_message flags ";
6: " by bms, Summer 99 ";
7: "";
8: who = args[1];
9: current = {};
10: "Flags!";
11: wait_time = 5;
12: just_random = 0;
13: order_random = 0;
14: random_num = 0;
15: if (who == "init")
16: "Do Initialization of Responses";
17: this.resp_curr = {};
18: this.resp_curr_siblings = 1;
19: for resp in (this.resp_list)
20: if ("init" in resp)
21: current = resp;
22: endif
23: endfor
24: if (current == {})
25: this.owner:tell("Init Property not setup in responses_list");
26: return;
27: endif
28: "Put initial responses in responses property";
29: for STR in (current[3])
30: this.responses = {@this.responses, strsub(strsub(STR, "#", ""), "@", "")};
31: this.resp_curr = {@this.resp_curr, {this.resp_curr_siblings, STR}};
32: endfor
33: this.resp_curr_siblings = this.resp_curr_siblings + 1;
34: return;
35: endif
36: " text = args[2]; old, Dollarbay format, bms";
37: text = args[2..$];
38: "Take only actual response out of list";
39: if (length(text) > 2)
40: " a hack to munge format differences ";
41: " text = text[3]; old, Dollarbay format, bms";
42: text = text[5];
43: else
44: #2:tell("Error: #1497:response_tree return, text=", toliteral(text));
45: return;
46: endif
47: sibling = 0;
48: for resp in (this.resp_list)
49: if (text in resp)
50: current = resp;
51: endif
52: $command_utils:suspend_if_needed(0);
53: endfor
54: if (current == {})
55: return 0;
56: endif
57: "Modify responses property for old responses";
58: "Take out response chosen";
59: for resp in (this.resp_curr)
60: if (text in resp || "@" + text in resp)
61: this.responses = setremove(this.responses, text);
62: endif
63: endfor
64: "Find responses Siblings Number";
65: for resp in (this.resp_curr)
66: if (text in resp || "@" + text in resp || "#" + text in resp)
67: sibling = resp[1];
68: if (index(resp[2], "#") != 1)
69: this.resp_curr = setremove(this.resp_curr, resp);
70: endif
71: endif
72: endfor
73: "Take out any Siblings";
74: for responses in (this.resp_curr)
75: if (responses[1] == sibling)
76: if (index(responses[2], "@") != 1 && index(responses[2], "#") != 1)
77: this.responses = setremove(this.responses, responses[2]);
78: this.resp_curr = setremove(this.resp_curr, responses);
79: endif
80: endif
81: endfor
82: "Now put in new Responses";
83: "Check Flags First";
84: if (current[3] != {} && index(current[3][1], "~") == 1)
85: flagstr = current[3][1];
86: if (index(flagstr, "OR") != 0)
87: order_random = 1;
88: random_num = random(length(current[3]) - 1);
89: elseif (index(flagstr, "R") != 0)
90: just_random = 1;
91: random_num = random(length(current[3]) - 1);
92: endif
93: endif
94: if (order_random == 1 || just_random == 1)
95: this.responses = {@this.responses, strsub(current[3][random_num + 1], "@", "")};
96: this.resp_curr = {@this.resp_curr, {this.resp_curr_siblings, current[3][random_num + 1]}};
97: else
98: for STR in (current[3])
99: if (index(STR, "~") == 1)
100: "Flag String, ignoring";
101: elseif (STR != "")
102: this.responses = {@this.responses, strsub(strsub(STR, "#", ""), "@", "")};
103: this.resp_curr = {@this.resp_curr, {this.resp_curr_siblings, STR}};
104: endif
105: endfor
106: endif
107: this.resp_curr_siblings = this.resp_curr_siblings + 1;
108: "Now say Answers to Response";
109: "Check Flags First";
110: if (current[2] != {} && index(current[2][1], "~") == 1)
111: if (this.status_messages)
112: this.owner:tell("Flags Found");
113: endif
114: flagstr = current[2][1];
115: if (index(flagstr, "OR") != 0)
116: if (this.status_messages)
117: this.owner:tell("Found OrderRandom Flag");
118: endif
119: if (order_random != 1)
120: "Flag should already be set from new responses!";
121: this.owner:tell("**** Response Tree Error : 'OR' Flag must be specified on both sayings and new responses!");
122: return;
123: endif
124: if (this.status_messages)
125: this.owner:tell("Random Number");
126: this.owner:tell(random_num);
127: endif
128: elseif (order_random == 1 && index(flagstr, "OR") == 0)
129: this.owner:tell("**** Response Tree Error : 'OR' Flag must be specified on both sayings and new responses!");
130: return;
131: elseif (index(flagstr, "R") != 0)
132: if (this.status_messages)
133: this.owner:tell("Found Random Flag");
134: endif
135: just_random = 1;
136: random_num = random(length(current[2]) - 1);
137: this.owner:tell(random_num);
138: endif
139: if (index(flagstr, "W") != 0)
140: if (this.status_messages)
141: this.owner:tell("Found Saying Wait Flag");
142: endif
143: wait_time = tonum(flagstr);
144: if (wait_time == 0)
145: this.owner:tell("**** Response Tree Error : 'W' Flag must have Number given with it - 'W'");
146: wait_time = 5;
147: endif
148: endif
149: endif
150: if (order_random == 1 || just_random == 1)
151: this:say(current[2][random_num + 1]);
152: suspend(wait_time);
153: return 1;
154: elseif (length(current[2]) != 0)
155: for STR in (current[2])
156: if (index(STR, "~") == 1)
157: "Flag String, ignoring";
158: elseif (STR != "")
159: this:say(STR);
160: suspend(wait_time);
161: endif
162: endfor
163: return 1;
164: else
165: return 0;
166: endif
167: " return 1; ";
@list #1434:respond
#1434:respond this none this
1: "";
2: " respond: respond to other agent's say verbs ";
3: " NOTE: if length(args) == 6, agent is assumed to be a generic agent";
4: " if length(args) == 7, agent is assumed to be the cop";
5: "";
6: agent = this;
7: su = $string_utils;
8: #2:tell("Otis respond args=", toliteral(args), " length=", length(args));
9: if (length(args) == 6 || length(args) == 7 && args[3] == "says")
10: if (length(args) == 6)
11: responses = agent.responses;
12: replies = agent.replies;
13: else
14: responses = agent.responses_to_cop;
15: replies = agent.replies_to_cop;
16: endif
17: speaker = agent.location:match(args[1]);
18: id = agent.name + " says, ";
19: text = args[5];
20: "";
21: len = length(responses);
22: index = 1;
23: found = 0;
24: while (index <= len)
25: response = responses[index];
26: if (typeof(response) == LIST)
27: key = response[1];
28: if (matches = $string_utils:match_string(text, key))
29: answer = replies[index];
30: agent.location:announce_all_but({agent}, id + answer);
31: index = len + 1;
32: found = 1;
33: else
34: index = index + 1;
35: endif
36: elseif (typeof(response) == STR)
37: key = response;
38: if (matches = $string_utils:match_string(text, key))
39: answer = replies[index];
40: agent.location:announce_all_but({agent}, id + answer);
41: index = len + 1;
42: found = 1;
43: else
44: index = index + 1;
45: endif
46: else
47: index = index + 1;
48: endif
49: endwhile
50: " This code will get annoying pretty quickly without the random element ";
51: if (!found && random(100) < 11)
52: agent.location:announce_all_but({agent}, id + "What's that? Speak up!");
53: endif
54: endif
@list #532:tell
#532:tell this none this
1: "";
2: " tell: generic agent calls respond in response to a 'say'";
3: " 9Feb99, bms";
4: "";
5: speaker = this;
6: tellee = this;
7: callstack = callers();
8: statusrep = "tellee=" + tostr(tellee) + " len=" + tostr(length(callstack));
9: if (length(callstack) == 2)
10: statusrep = statusrep + ", callers=" + callstack[2][2] + " @args=" + tostr(@args);
11: if (callstack[2][2] == "say")
12: fork (0)
13: tellee:respond(@args);
14: endfork
15: endif
16: elseif (length(callstack) == 3)
17: statusrep = statusrep + ", callers=" + callstack[1][2] + " @args=" + tostr(@args);
18: if (callstack[2][2] == "announce" && callstack[1][2] != "announce")
19: fork (0)
20: tellee:respond("", "", "says", "", args[5], "");
21: endfork
22: elseif (callstack[2][2] == "announce" && callstack[1][2] == "announce")
23: fork (0)
24: tellee:respond(@args);
25: endfork
26: endif
27: elseif (length(callstack) > 3)
28: statusrep = statusrep + ", callers=" + callstack[2][2] + " @args=" + tostr(@args);
29: if (callstack[2][2] == "announce")
30: fork (0)
31: tellee:respond("", "", "says", "", @args, "");
32: endfor
33: endif
34: endif
@list #532:respond
#532:respond this none this
1: "";
2: " respond: respond to other agent's say verbs ";
3: "";
4: agent = this;
5: su = $string_utils;
6: if (length(args) == 6 && args[3] == "says")
7: speaker = agent.location:match(args[1]);
8: id = agent.name + " says, ";
9: text = args[5];
10: if (this:response_tree(speaker, @args) == 1)
11: this:wait_reset();
12: else
13: responses = agent.responses;
14: len = length(responses);
15: index = 1;
16: found = 0;
17: while (index <= len)
18: response = responses[index];
19: if (typeof(response) == LIST)
20: " allow agent to have alternatives like 'hi' and 'howdy' ";
21: " key = response[1]; ";
22: " if (matches = $string_utils:match_string(text, key)) ";
23: " changed, 10Jul99, bms ";
24: if (text in response)
25: answer = agent.replies[index];
26: agent.location:announce_all_but({agent}, id + answer);
27: agent:say(answer);
28: index = len + 1;
29: found = 1;
30: else
31: index = index + 1;
32: endif
33: elseif (typeof(response) == STR)
34: key = response;
35: if (matches = $string_utils:match_string(text, key))
36: answer = agent.replies[index];
37: agent.location:announce_all_but({agent}, id + answer);
38: agent:say(answer);
39: index = len + 1;
40: found = 1;
41: else
42: index = index + 1;
43: endif
44: else
45: index = index + 1;
46: endif
47: endwhile
48: " This code will get annoying pretty quickly without the random element ";
49: if (!found && random(100) < 11)
50: agent.location:announce_all_but({agent}, id + "What's that? Speak up!");
51: agent:say("What's that? Speak up!");
52: endif
53: endif
54: endif
|