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