scummvm/engines/glk/quest/geas_runner.cpp
2020-11-17 07:22:49 +01:00

3685 lines
101 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "glk/quest/geas_runner.h"
#include "glk/quest/read_file.h"
#include "glk/quest/geas_state.h"
#include "glk/quest/geas_util.h"
#include "glk/quest/reserved_words.h"
#include "glk/quest/geas_impl.h"
#include "glk/quest/quest.h"
#include "glk/quest/streams.h"
#include "glk/quest/string.h"
namespace Glk {
namespace Quest {
class GeasInterface;
static const char *dir_names[] = {"north", "south", "east", "west", "northeast", "northwest", "southeast", "southwest", "up", "down", "out"};
static const char *short_dir_names[] = {"n", "s", "e", "w", "ne", "nw", "se", "sw", "u", "d", "out"};
const ObjectRecord *get_obj_record(const Common::Array<ObjectRecord> &v, const String &name) {
for (uint i = 0; i < v.size(); i ++)
if (ci_equal(v[i].name, name))
return &v[i];
return nullptr;
}
GeasRunner *GeasRunner::get_runner(GeasInterface *gi) {
return new geas_implementation(gi);
}
bool geas_implementation::find_ivar(String name, uint &rv) const {
for (uint n = 0; n < state.ivars.size(); n ++)
if (ci_equal(state.ivars[n].name, name)) {
rv = n;
return true;
}
return false;
}
bool geas_implementation::find_svar(String name, uint &rv) const {
//name = lcase (name);
for (uint n = 0; n < state.svars.size(); n ++)
if (ci_equal(state.svars[n].name, name)) {
rv = n;
return true;
}
return false;
}
void geas_implementation::set_svar(String varname, String varval) {
cerr << "set_svar (" << varname << ", " << varval << ")\n";
int i1 = varname.find('[');
if (i1 == -1)
return set_svar(varname, 0, varval);
if (varname[varname.length() - 1] != ']') {
gi->debug_print("set_svar: Badly formatted name " + varname);
return;
}
String arrayname = varname.substr(0, i1);
String indextext = varname.substr(i1 + 1, varname.length() - i1 - 2);
cerr << "set_svar(" << varname << ") --> set_svar (" << arrayname << ", " << indextext << ")\n";
for (uint c3 = 0; c3 < indextext.size(); c3 ++)
if (indextext[c3] < '0' || indextext[c3] > '9') {
set_svar(arrayname, get_ivar(indextext), varval);
return;
}
set_svar(arrayname, parse_int(indextext), varval);
return;
}
void geas_implementation::set_svar(String varname, uint index, String varval) {
uint n, m;
if (!find_svar(varname, n)) {
if (find_ivar(varname, m)) {
gi->debug_print("Defining " + varname + " as String variable when there is already a numeric variable of that name.");
return;
}
SVarRecord svr;
svr.name = varname;
n = state.svars.size();
state.svars.push_back(svr);
}
state.svars[n].set(index, varval);
if (index == 0) {
for (uint varn = 0; varn < gf.size("variable"); varn ++) {
const GeasBlock &go(gf.block("variable", varn));
if (ci_equal(go.name, varname)) {
String script = "";
uint c1, c2;
for (uint j = 0; j < go.data.size(); j ++)
// SENSITIVE ?
if (first_token(go.data[j], c1, c2) == "onchange")
script = trim(go.data[j].substr(c2 + 1));
if (script != "")
run_script(script);
}
}
}
}
String geas_implementation::get_svar(String varname) const {
int i1 = varname.find('[');
if (i1 == -1)
return get_svar(varname, 0);
if (varname[varname.length() - 1] != ']') {
gi->debug_print("get_svar: badly formatted name " + varname);
return "";
}
String arrayname = varname.substr(0, i1);
String indextext = varname.substr(i1 + 1, varname.length() - i1 - 2);
cerr << "get_svar(" << varname << ") --> get_svar (" << arrayname << ", " << indextext << ")\n";
for (uint c3 = 0; c3 < indextext.size(); c3 ++)
if (indextext[c3] < '0' || indextext[c3] > '9')
return get_svar(arrayname, get_ivar(indextext));
return get_svar(arrayname, parse_int(indextext));
}
String geas_implementation::get_svar(String varname, uint index) const {
for (uint i = 0; i < state.svars.size(); i ++) {
if (ci_equal(state.svars[i].name, varname))
return state.svars[i].get(index);
}
gi->debug_print("get_svar (" + varname + ", " + string_int(index) + "): No such variable defined.");
return "";
}
int geas_implementation::get_ivar(String varname) const {
int i1 = varname.find('[');
if (i1 == -1)
return get_ivar(varname, 0);
if (varname[varname.length() - 1] != ']') {
gi->debug_print("get_ivar: Badly formatted name " + varname);
return -32767;
}
String arrayname = varname.substr(0, i1);
String indextext = varname.substr(i1 + 1, varname.length() - i1 - 2);
cerr << "get_ivar(" << varname << ") --> get_ivar (" << arrayname << ", " << indextext << ")\n";
for (uint c3 = 0; c3 < indextext.size(); c3 ++)
if (indextext[c3] < '0' || indextext[c3] > '9')
return get_ivar(arrayname, get_ivar(indextext));
return get_ivar(arrayname, parse_int(indextext));
}
int geas_implementation::get_ivar(String varname, uint index) const {
for (uint i = 0; i < state.ivars.size(); i ++)
if (ci_equal(state.ivars[i].name, varname))
return state.ivars[i].get(index);
gi->debug_print("get_ivar: Tried to read undefined int '" + varname +
"' [" + string_int(index) + "]");
return -32767;
}
void geas_implementation::set_ivar(String varname, int varval) {
int i1 = varname.find('[');
if (i1 == -1)
return set_ivar(varname, 0, varval);
if (varname[varname.length() - 1] != ']') {
gi->debug_print("set_ivar: Badly formatted name " + varname);
return;
}
String arrayname = varname.substr(0, i1);
String indextext = varname.substr(i1 + 1, varname.length() - i1 - 2);
cerr << "set_svar(" << varname << ") --> set_svar (" << arrayname << ", " << indextext << ")\n";
for (uint c3 = 0; c3 < indextext.size(); c3 ++)
if (indextext[c3] < '0' || indextext[c3] > '9') {
set_ivar(arrayname, get_ivar(indextext), varval);
return;
}
set_ivar(arrayname, parse_int(indextext), varval);
}
void geas_implementation::set_ivar(String varname, uint index, int varval) {
uint n, m;
if (!find_ivar(varname, n)) {
if (find_svar(varname, m)) {
gi->debug_print("Defining " + varname + " as numeric variable when there is already a String variable of that name.");
return;
}
IVarRecord ivr;
ivr.name = varname;
n = state.ivars.size();
state.ivars.push_back(ivr);
}
state.ivars[n].set(index, varval);
if (index == 0) {
for (uint varn = 0; varn < gf.size("variable"); varn ++) {
const GeasBlock &go(gf.block("variable", varn));
//if (go.lname == varname)
if (ci_equal(go.name, varname)) {
String script = "";
uint c1, c2;
for (uint j = 0; j < go.data.size(); j ++)
// SENSITIVE?
if (first_token(go.data[j], c1, c2) == "onchange")
script = trim(go.data[j].substr(c2 + 1));
if (script != "")
run_script(script);
}
}
}
}
Common::WriteStream &operator<<(Common::WriteStream &o, const match_binding &mb) {
o << "MB['" << mb.var_name << "' == '" << mb.var_text << "' @ "
<< mb.start << " to " << mb.end << "]";
return o;
}
String match_binding::tostring() {
ostringstream oss;
oss << *this;
return oss.str();
}
Common::WriteStream &operator<<(Common::WriteStream &o, const Set<String> &s) {
o << "{ ";
for (Set<String>::const_iterator i = s.begin(); i != s.end(); i ++) {
if (i != s.begin())
o << ", ";
o << (*i);
}
o << " }";
return o;
}
bool geas_implementation::has_obj_action(String obj, String prop) const {
String tmp;
return get_obj_action(obj, prop, tmp);
}
bool geas_implementation::get_obj_action(String objname, String actname,
String &rv) const {
//String backup_object = this_object;
//this_object = objname;
cerr << "get_obj_action (" << objname << ", " << actname << ")\n";
String tok;
uint c1, c2;
for (uint i = state.props.size() - 1; i + 1 > 0; i --)
if (state.props[i].name == objname) {
String line = state.props[i].data;
// SENSITIVE?
if (first_token(line, c1, c2) != "action")
continue;
tok = next_token(line, c1, c2);
if (!is_param(tok) || ci_equal(param_contents(tok), actname))
continue;
rv = trim(line.substr(c2));
cerr << " g_o_a: returning true, \"" << rv << "\".";
return true;
}
return gf.get_obj_action(objname, actname, rv);
//bool bool_rv = gf.get_obj_action (objname, actname, rv);
//this_object = backup_object;
//return bool_rv;
}
bool geas_implementation::has_obj_property(String obj, String prop) const {
String tmp;
return get_obj_property(obj, prop, tmp);
}
bool geas_implementation::get_obj_property(String obj, String prop,
String &string_rv) const {
String is_prop = "properties " + prop;
String not_prop = "properties not " + prop;
for (uint i = state.props.size() - 1; i + 1 > 0; i --)
if (ci_equal(state.props[i].name, obj)) {
String dat = state.props[i].data;
//cerr << "In looking for " << obj << ":" << prop << ", got line "
// << dat << endl;
if (ci_equal(dat, not_prop)) {
//cerr << " not_prop, returning false\n";
string_rv = "!";
return false;
}
if (ci_equal(dat, is_prop)) {
//cerr << " is_prop, returning true\n";
string_rv = "";
return true;
}
int index = dat.find('=');
if (index != -1 && ci_equal(dat.substr(0, index), is_prop)) {
string_rv = dat.substr(index + 1);
return true;
}
}
return gf.get_obj_property(obj, prop, string_rv);
}
void geas_implementation::set_obj_property(String obj, String prop) {
state.props.push_back(PropertyRecord(obj, "properties " + prop));
if (ci_equal(prop, "hidden") || ci_equal(prop, "not hidden") ||
ci_equal(prop, "invisible") || ci_equal(prop, "not invisible")) {
gi->update_sidebars();
regen_var_objects();
}
}
void geas_implementation::set_obj_action(String obj, String act) {
state.props.push_back(PropertyRecord(obj, "action " + act));
}
void geas_implementation::move(String obj, String dest) {
for (uint i = 0; i < state.objs.size(); i ++)
if (ci_equal(state.objs[i].name, obj)) {
state.objs[i].parent = dest;
gi->update_sidebars();
regen_var_objects();
return;
}
gi->debug_print("Tried to move nonexistent object '" + obj +
"' to '" + dest + "'.");
}
String geas_implementation::get_obj_parent(String obj) {
//obj = lcase (obj);
for (uint i = 0; i < state.objs.size(); i ++)
if (ci_equal(state.objs[i].name, obj))
return state.objs[i].parent;
gi->debug_print("Tried to find parent of nonexistent object " + obj);
return "";
}
void geas_implementation::goto_room(String room) {
state.location = room;
regen_var_room();
regen_var_dirs();
regen_var_look();
regen_var_objects();
String scr;
if (get_obj_action(room, "script", scr))
run_script_as(room, scr);
//run_script (scr);
look();
}
void geas_implementation::display_error(String errorname, String obj) {
cerr << "display_error (" << errorname << ", " << obj << ")\n";
if (obj != "") {
String tmp;
if (!get_obj_property(obj, "gender", tmp))
tmp = "it";
set_svar("quest.error.gender", tmp);
if (!get_obj_property(obj, "article", tmp))
tmp = "it";
set_svar("quest.error.article", tmp);
cerr << "In erroring " << errorname << " / " << obj << ", qeg == "
<< get_svar("quest.error.gender") << ", qea == "
<< get_svar("quest.error.article") << endl;
// TODO quest.error.charactername
}
const GeasBlock *game = gf.find_by_name("game", "game");
assert(game != NULL);
String tok;
uint c1, c2;
for (uint i = 0; i < game->data.size(); i ++) {
String line = game->data[i];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "error") {
tok = next_token(line, c1, c2);
if (is_param(tok)) {
String text = param_contents(tok);
int index = text.find(';');
String errortype = trim(text.substr(0, index));
// SENSITIVE?
if (errortype == errorname) {
print_eval_p(trim(text.substr(index + 1)));
return;
}
} else
gi->debug_print("Bad error line: " + line);
}
}
//print_formatted ("Default error " + errorname);
// ARE THESE SENSITIVE?
if (errorname == "badcommand")
print_eval("I don't understand your command. Type HELP for a list of valid commands.");
else if (errorname == "badgo")
print_eval("I don't understand your use of 'GO' - you must either GO in some direction, or GO TO a place.");
else if (errorname == "badgive")
print_eval("You didn't say who you wanted to give that to.");
else if (errorname == "badcharacter")
print_eval("I can't see anybody of that name here.");
else if (errorname == "noitem")
print_eval("You don't have that.");
else if (errorname == "itemunwanted")
print_eval_p("#quest.error.gender# doesn't want #quest.error.article#.");
else if (errorname == "badlook")
print_eval("You didn't say what you wanted to look at.");
else if (errorname == "badthing")
print_eval("I can't see that here.");
else if (errorname == "defaultlook")
print_eval("Nothing out of the ordinary.");
else if (errorname == "defaultspeak")
print_eval_p("#quest.error.gender# says nothing.");
else if (errorname == "baditem")
print_eval("I can't see that anywhere.");
else if (errorname == "defaulttake")
print_eval("You pick #quest.error.article# up.");
else if (errorname == "baduse")
print_eval("You didn't say what you wanted to use that on.");
else if (errorname == "defaultuse")
print_eval("You can't use that here.");
else if (errorname == "defaultout")
print_eval("There's nowhere you can go out to around here.");
else if (errorname == "badplace")
print_eval("You can't go there.");
else if (errorname == "defaultexamine")
print_eval("Nothing out of the ordinary.");
else if (errorname == "badtake")
print_eval("You can't take #quest.error.article#.");
else if (errorname == "cantdrop")
print_eval("You can't drop that here.");
else if (errorname == "defaultdrop")
print_eval("You drop #quest.error.article#.");
else if (errorname == "baddrop")
print_eval("You are not carrying such a thing.");
else if (errorname == "badpronoun")
print_eval("I don't know what '#quest.error.pronoun#' you are referring to.");
else if (errorname == "badexamine")
print_eval("You didn't say what you wanted to examine.");
else
gi->debug_print("Bad error name " + errorname);
}
String geas_implementation::displayed_name(String obj) const {
String rv = obj, tmp;
if (get_obj_property(obj, "alias", tmp))
rv = tmp;
else {
for (uint i = 0; i < gf.blocks.size(); i ++)
if (ci_equal(gf.blocks[i].name, obj)) {
rv = gf.blocks[i].name;
break;
}
}
return rv;
}
/* For each destination, give:
* - printed name
* - accepted name, with prefix
* - accepted name, without prefix
* - destination, internal format
* - script (optional)
*/
Common::Array<Common::Array<String> > geas_implementation::get_places(String room) {
Common::Array<Common::Array<String> > rv;
const GeasBlock *gb = gf.find_by_name("room", room);
if (gb == NULL)
return rv;
String line, tok;
uint c1, c2;
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
tok = first_token(line, c1, c2);
if (tok == "place") {
tok = next_token(line, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after 'place' in " + line);
continue;
}
String dest_param = eval_param(tok);
if (dest_param == "") {
gi->debug_print("Parameter empty in " + line);
continue;
}
int j = dest_param.find(';');
String dest, prefix = "";
if (j == -1)
dest = trim(dest_param);
else {
dest = trim(dest_param.substr(j + 1));
prefix = trim(dest_param.substr(0, j));
}
String displayed = displayed_name(dest);
String printed_dest = (prefix != "" ? prefix + " " : "") +
"|b" + displayed + "|xb";
Common::Array<String> tmp;
tmp.push_back(printed_dest);
tmp.push_back(prefix + " " + displayed);
tmp.push_back(displayed);
tmp.push_back(dest);
String rest = trim(line.substr(c2));
if (rest != "")
tmp.push_back(rest);
rv.push_back(tmp);
}
}
for (uint i = 0; i < state.exits.size(); i ++) {
if (state.exits[i].src != room)
continue;
line = state.exits[i].dest;
tok = first_token(line, c1, c2);
if (tok == "exit") {
tok = next_token(line, c1, c2);
if (!is_param(tok))
continue;
tok = next_token(line, c1, c2);
assert(is_param(tok));
tok = param_contents(tok);
Common::Array<String> args = split_param(tok);
if (args.size() != 2) {
gi->debug_print("Expected two arguments in " + tok);
continue;
}
assert(args[0] == room);
Common::Array<String> tmp;
String displayed = displayed_name(args[1]);
tmp.push_back("|b" + displayed + "|xb");
tmp.push_back(displayed);
tmp.push_back(displayed);
tmp.push_back(args[1]);
rv.push_back(tmp);
} else if (tok == "destroy") {
tok = next_token(line, c1, c2);
assert(tok == "exit");
tok = next_token(line, c1, c2);
for (v2string::iterator j = rv.begin(); j != rv.end(); j ++)
if ((*j)[3] == tok) {
rv.erase(j);
break;
}
}
}
cerr << "get_places (" << room << ") -> " << rv << "\n";
return rv;
}
String geas_implementation::exit_dest(String room, String dir, bool *is_script) const {
uint c1, c2;
String tok;
if (is_script != NULL)
*is_script = false;
for (uint i = state.exits.size() - 1; i + 1 > 0; i --)
if (state.exits[i].src == room) {
String line = state.exits[i].dest;
cerr << "Processing exit line '" << state.exits[i].dest << "'\n";
tok = first_token(line, c1, c2);
cerr << " first tok is " << tok << " (vs. exit)\n";
// SENSITIVE?
if (tok != "exit")
continue;
tok = next_token(line, c1, c2);
cerr << " second tok is " << tok << " (vs. " << dir << ")\n";
if (tok != dir)
continue;
tok = next_token(line, c1, c2);
cerr << " third tok is " << tok << " (expecting parameter)\n";
assert(is_param(tok));
Common::Array<String> p = split_param(param_contents(tok));
assert(p.size() == 2);
assert(ci_equal(p[0], room));
return p[1];
}
/*
if (gf.get_obj_action (room, dir, tok))
{
if (is_script != NULL)
*is_script = true;
return tok;
}
if (gf.get_obj_property (room, dir, tok))
return tok;
else
return "";
*/
const GeasBlock *gb = gf.find_by_name("room", room);
if (gb == NULL) {
gi->debug_print(String("Trying to find exit <") + dir +
"> of nonexistent room <" + room + ">.");
return "";
}
// TODO: what's the priority on this?
for (uint i = 0; i < gb->data.size(); i ++) {
String line = gb->data[i];
tok = first_token(line, c1, c2);
if (tok == dir) {
uint line_start = c2;
tok = next_token(line, c1, c2);
if (is_param(tok))
return param_contents(tok);
if (tok != "") {
if (is_script != NULL)
*is_script = true;
return trim(line.substr(line_start + 1));
}
return "";
}
}
return "";
}
void geas_implementation::look() {
String tmp;
if (get_obj_action(state.location, "description", tmp))
run_script_as(state.location, tmp);
//run_script(tmp);
else if (get_obj_property(state.location, "description", tmp))
print_formatted(tmp);
else if (get_obj_action("game", "description", tmp))
run_script_as("game", tmp);
//run_script (tmp);
else if (get_obj_property("game", "description", tmp))
print_formatted(tmp);
else {
String in_desc;
if (get_obj_property(state.location, "indescription", tmp))
in_desc = tmp;
else
in_desc = "You are in";
print_formatted(in_desc + " " + get_svar("quest.formatroom"));
if ((tmp = get_svar("quest.formatobjects")) != "")
//print_formatted ("There is " + tmp + " here.");
print_eval("There is #quest.formatobjects# here.");
if ((tmp = get_svar("quest.doorways.out")) != "")
print_formatted("You can go out to " + tmp + ".");
if ((tmp = get_svar("quest.doorways.dirs")) != "")
//print_formatted ("You can go " + tmp + ".");
print_eval("You can go #quest.doorways.dirs#.");
if ((tmp = get_svar("quest.doorways.places")) != "")
print_formatted("You can go to " + tmp + ".");
if ((tmp = get_svar("quest.lookdesc")) != "")
print_formatted(tmp);
}
}
void geas_implementation::set_game(const String &fname) {
cerr << "set_game (...)\n";
gf = read_geas_file(gi, fname);
if (gf.blocks.size() == 0) {
is_running_ = false;
return;
}
//print_formatted ("Ready...|n|cbblack|crred|clblue|cggreen|cyyellow|n|uunderlined: |cbblack|crred|clblue|cggreen|cyyellow|xu|n");
//cerr << "Read game " << gf << endl;
uint tok_start, tok_end;
outputting = true;
state = GeasState(*gi, gf);
state.running = true;
for (uint gline = 0; gline < gf.block("game", 0).data.size(); gline ++) {
String s = gf.block("game", 0).data[gline];
String tok = first_token(s, tok_start, tok_end);
// SENSITIVE?
if (tok == "asl-version") {
String ver = next_token(s, tok_start, tok_end);
if (!is_param(ver)) {
gi->debug_print("Version " + s + " has invalid version " +
ver);
continue;
}
int vernum = parse_int(param_contents(ver));
if (vernum < 311 || vernum > 353)
gi->debug_print("Warning: Geas only supports ASL "
" versions 3.11 to 3.53");
}
// SENSITIVE?
else if (tok == "background") {
tok = next_token(s, tok_start, tok_end);
if (!is_param(tok))
gi->debug_print(nonparam("background color", s));
else
gi->set_background(param_contents(tok));
}
// SENSITIVE?
else if (tok == "default") {
tok = next_token(s, tok_start, tok_end);
// SENSITIVE?
if (tok == "fontname") {
tok = next_token(s, tok_start, tok_end);
if (!is_param(tok))
gi->debug_print(nonparam("font name", s));
else
gi->set_default_font(param_contents(tok));
}
// SENSITIVE?
else if (tok == "fontsize") {
tok = next_token(s, tok_start, tok_end);
if (!is_param(tok))
gi->debug_print(nonparam("font size", s));
else
gi->set_default_font_size(param_contents(tok));
}
}
// SENSITIVE?
else if (tok == "foreground") {
tok = next_token(s, tok_start, tok_end);
if (!is_param(tok))
gi->debug_print(nonparam("foreground color", s));
else
gi->set_foreground(param_contents(tok));
}
// SENSITIVE?
else if (tok == "gametype") {
tok = next_token(s, tok_start, tok_end);
// SENSITIVE?
if (tok == "singleplayer")
continue;
// SENSITIVE?
if (tok == "multiplayer")
error("Error: geas is single player only.");
gi->debug_print("Unexpected game type " + s);
}
// SENSITIVE?
else if (tok == "nodebug") {
}
// SENSITIVE?
else if (tok == "start") {
tok = next_token(s, tok_start, tok_end);
if (!is_param(tok))
gi->debug_print(nonparam("start room", s));
else {
state.location = param_contents(tok);
}
}
}
const GeasBlock &game = gf.block("game", 0);
cerr << gf << endl;
//print_formatted ("Done loading " + game.name);
uint c1, c2;
String tok;
/* TODO do I run the startscript or print the opening text first? */
run_script("displaytext <intro>");
for (uint i = 0; i < game.data.size(); i ++)
// SENSITIVE?
if (first_token(game.data[i], c1, c2) == "startscript") {
run_script_as("game", game.data[i].substr(c2 + 1));
//run_script (game.data[i].substr (c2 + 1));
break;
}
regen_var_room();
regen_var_objects();
regen_var_dirs();
regen_var_look();
look();
cerr << "s_g: done with set_game (...)\n\n";
}
void geas_implementation::regen_var_objects() {
String tmp;
Common::Array <String> objs;
for (uint i = 0; i < state.objs.size(); i ++) {
//cerr << "r_v_o: Checking '" << state.objs[i].name << "' (" << state.objs[i].parent << "): " << ((state.objs[i].parent == state.location) ? "YES" : "NO") << endl;
if (ci_equal(state.objs[i].parent, state.location) &&
!get_obj_property(state.objs[i].name, "hidden", tmp) &&
!get_obj_property(state.objs[i].name, "invisible", tmp))
//!state.objs[i].hidden &&
//!state.objs[i].invisible)
objs.push_back(state.objs[i].name);
}
String qobjs = "", qfobjs = "";
String objname, prefix, main, suffix, propval, print1, print2;
for (uint i = 0; i < objs.size(); i ++) {
objname = objs[i];
if (!get_obj_property(objname, "alias", main))
main = objname;
print1 = main;
print2 = "|b" + main + "|xb";
if (get_obj_property(objname, "prefix", prefix)) {
print1 = prefix + " " + print1;
print2 = prefix + " " + print2;
}
if (get_obj_property(objname, "suffix", suffix)) {
print1 = print1 + " " + suffix;
print2 = print2 + " " + suffix;
}
qobjs = qobjs + print1;
qfobjs = qfobjs + print2;
if (i + 2 < objs.size()) {
qobjs = qobjs + ", ";
qfobjs = qfobjs + ", ";
} else if (i + 2 == objs.size()) {
qobjs = qobjs + " and ";
qfobjs = qfobjs + " and ";
}
}
set_svar("quest.objects", qobjs);
set_svar("quest.formatobjects", qfobjs);
}
void geas_implementation::regen_var_room() {
set_svar("quest.currentroom", state.location);
String tmp, formatroom;
if (!get_obj_property(state.location, "alias", formatroom))
formatroom = state.location;
formatroom = "|cr" + formatroom + "|cb";
if (get_obj_property(state.location, "prefix", tmp))
formatroom = tmp + " " + formatroom;
if (get_obj_property(state.location, "suffix", tmp))
formatroom = formatroom + " " + tmp;
//set_svar ("quest.formatroom", displayed_name (state.location));
set_svar("quest.formatroom", formatroom);
// regen_var_objects();
/*
String out_dest = exit_dest (state.location, "out");
if (out_dest == "")
{
set_svar ("quest.doorways.out", "");
set_svar ("quest.doorways.out.display", "");
}
else
{
cerr << "Updating quest.doorways.out; out_dest == {" << out_dest << "}";
uint i = out_dest.find (';');
cerr << ", i == " << i;
String prefix = "";
if (i != -1)
{
prefix = trim (out_dest.substr (0, i-1));
out_dest = trim (out_dest.substr (i + 1));
cerr << "; prefix == {" << prefix << "}, out_dest == {" << out_dest << "}";
}
cerr << " quest.doorways.out == {" << out_dest << "}";
set_svar ("quest.doorways.out", out_dest);
cerr << endl;
String tmp = displayed_name (out_dest);
cerr << ", tmp == {" << tmp << "}";
if (tmp != "")
tmp = "|b" + tmp + "|xb";
else if (prefix != "")
tmp = prefix + " |b" + out_dest + "|xb";
else
tmp = "|b" + out_dest + "|xb";
cerr << ", final value {" << tmp << "}" << endl;
set_svar ("quest.doorways.out.display", tmp);
}
*/
}
void geas_implementation::regen_var_look() {
String look_tag;
if (!get_obj_property(state.location, "look", look_tag))
look_tag = "";
set_svar("quest.lookdesc", look_tag);
}
void geas_implementation::regen_var_dirs() {
Common::Array <String> dirs;
// the -1 is so that it skips 'out'
for (uint i = 0; i < ARRAYSIZE(dir_names) - 1; i ++)
if (exit_dest(state.location, dir_names[i]) != "")
dirs.push_back(dir_names[i]);
String exits = "";
if (dirs.size() == 1)
exits = "|b" + dirs[0] + "|xb";
else if (dirs.size() > 1) {
for (uint i = 0; i < dirs.size(); i ++) {
exits = exits + "|b" + dirs[i] + "|xb";
if (i < dirs.size() - 2)
exits = exits + ", ";
else if (i == dirs.size() - 2)
exits = exits + " or ";
}
}
set_svar("quest.doorways.dirs", exits);
/*
String tmp;
if ((tmp = exit_dest (state.location, "out")) != "")
set_svar ("quest.doorways.out", displayed_name (tmp));
else
set_svar ("quest.doorways.out", "");
*/
String out_dest = exit_dest(state.location, "out");
if (out_dest == "") {
set_svar("quest.doorways.out", "");
set_svar("quest.doorways.out.display", "");
} else {
cerr << "Updating quest.doorways.out; out_dest == {" << out_dest << "}";
int i = out_dest.find(';');
cerr << ", i == " << i;
String prefix = "";
if (i != -1) {
prefix = trim(out_dest.substr(0, i - 1));
out_dest = trim(out_dest.substr(i + 1));
cerr << "; prefix == {" << prefix << "}, out_dest == {" << out_dest << "}";
}
cerr << " quest.doorways.out == {" << out_dest << "}";
set_svar("quest.doorways.out", out_dest);
cerr << endl;
String tmp = displayed_name(out_dest);
cerr << ", tmp == {" << tmp << "}";
if (tmp != "")
tmp = "|b" + tmp + "|xb";
else if (prefix != "")
tmp = prefix + " |b" + out_dest + "|xb";
else
tmp = "|b" + out_dest + "|xb";
cerr << ", final value {" << tmp << "}" << endl;
set_svar("quest.doorways.out.display", tmp);
}
/* TODO handle this */
//set_svar ("quest.doorways.places", "");
current_places = get_places(state.location);
String printed_places = "";
for (uint i = 0; i < current_places.size(); i ++) {
if (i == 0)
printed_places = current_places[i][0];
else if (i < current_places.size() - 1)
printed_places = printed_places + ", " + current_places[i][0];
else if (current_places.size() == 2)
printed_places = printed_places + " or " + current_places[i][0];
else
printed_places = printed_places + ", or " + current_places[i][0];
}
set_svar("quest.doorways.places", printed_places);
}
// TODO: SENSITIVE???
String geas_implementation::substitute_synonyms(String s) const {
String orig = s;
cerr << "substitute_synonyms (" << s << ")\n";
const GeasBlock *gb = gf.find_by_name("synonyms", "");
if (gb != NULL) {
/* TODO: exactly in what order does it try synonyms?
* Does it have to be flanked by whitespace?
*/
for (uint i = 0; i < gb->data.size(); i ++) {
String line = gb->data[i];
int index = line.find('=');
if (index == -1)
continue;
Common::Array<String> words = split_param(line.substr(0, index));
String rhs = trim(line.substr(index + 1));
if (rhs == "")
continue;
for (uint j = 0; j < words.size(); j ++) {
String lhs = words[j];
if (lhs == "")
continue;
int k = 0;
while ((k = s.find(lhs, k)) != -1) {
uint end_index = k + lhs.length();
if ((k == 0 || s[k - 1] == ' ') &&
(end_index == s.length() || s[end_index] == ' ')) {
s = s.substr(0, k) + rhs + s.substr(k + lhs.length());
k = k + rhs.length();
} else
k ++;
}
}
}
}
cerr << "substitute_synonyms (" << orig << ") -> '" << s << "'\n";
return s;
}
bool geas_implementation::is_running() const {
return is_running_;
}
String geas_implementation::get_banner() {
String banner;
const GeasBlock *gb = gf.find_by_name("game", "game");
if (gb) {
String line = gb->data[0];
uint c1, c2;
String tok = first_token(line, c1, c2);
tok = next_token(line, c1, c2);
tok = next_token(line, c1, c2);
if (is_param(tok)) {
banner = eval_param(tok);
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
if (first_token(line, c1, c2) == "game" &&
next_token(line, c1, c2) == "version" &&
is_param(tok = next_token(line, c1, c2))) {
banner += ", v";
banner += eval_param(tok);
}
}
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
if (first_token(line, c1, c2) == "game" &&
next_token(line, c1, c2) == "author" &&
is_param(tok = next_token(line, c1, c2))) {
banner += " | ";
banner += eval_param(tok);
}
}
}
}
return banner;
}
void geas_implementation::run_command(String s) {
/* if s == "restore" or "restart" or "quit" or "undo" */
if (!is_running_)
return;
print_newline();
print_normal("> " + s);
print_newline();
if (s == "dump status") {
//cerr << state << endl;
ostringstream oss;
oss << state;
print_normal(oss.str());
return;
} else if (s == "undo") {
if (undo_buffer.size() < 2) {
print_formatted("(No more undo information available!)");
return;
}
undo_buffer.pop();
state = undo_buffer.peek();
print_formatted("Undone.");
return;
} else if (s == "save") {
if (g_vm->saveGame().getCode() == Common::kNoError)
print_formatted("Saved.");
return;
} else if (s == "restore") {
if (g_vm->loadGame().getCode() == Common::kNoError)
run_command("look");
return;
}
if (!state.running)
return;
// TODO: does this get the original command, or the lowercased version?
set_svar("quest.originalcommand", s);
s = substitute_synonyms(lcase(s));
set_svar("quest.command", s);
bool overridden = false;
dont_process = false;
const GeasBlock *gb = gf.find_by_name("room", state.location);
if (gb != NULL) {
String line, tok;
uint c1, c2;
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "beforeturn") {
uint scr_starts = c2;
tok = next_token(line, c1, c2);
// SENSITIVE?
if (tok == "override") {
overridden = true;
scr_starts = c2;
}
String scr = line.substr(scr_starts);
run_script(state.location, scr);
//run_script (scr);
}
}
} else
gi->debug_print("Unable to find block " + state.location + ".\n");
if (!overridden) {
gb = gf.find_by_name("game", "game");
if (gb != NULL) {
String line, tok;
uint c1, c2;
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "beforeturn") {
uint scr_starts = c2;
tok = next_token(line, c1, c2);
// SENSITIVE?
if (tok == "override") {
overridden = true;
scr_starts = c2;
}
String scr = line.substr(scr_starts);
run_script_as("game", scr);
//run_script (scr);
}
}
} else
gi->debug_print("Unable to find block game.\n");
}
if (!dont_process) {
if (try_match(s, false, false)) {
/* TODO TODO */
// run after turn events ???
} else
display_error("badcommand");
}
overridden = false;
gb = gf.find_by_name("room", state.location);
if (gb != NULL) {
String line, tok;
uint c1, c2;
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "afterturn") {
uint scr_starts = c2;
tok = next_token(line, c1, c2);
// SENSITIVE?
if (tok == "override") {
overridden = true;
scr_starts = c2;
}
String scr = line.substr(scr_starts);
run_script_as(state.location, scr);
//run_script (scr);
}
}
}
if (!overridden) {
gb = gf.find_by_name("game", "game");
if (gb != NULL) {
String line, tok;
uint c1, c2;
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "afterturn") {
uint scr_starts = c2;
tok = next_token(line, c1, c2);
// SENSITIVE?
if (tok == "override") {
overridden = true;
scr_starts = c2;
}
String scr = line.substr(scr_starts);
run_script_as("game", scr);
//run_script (scr);
}
}
}
}
if (state.running)
undo_buffer.push(state);
}
Common::WriteStream &operator<< (Common::WriteStream &o, const match_rv &rv) {
//o << "match_rv {" << (rv.success ? "TRUE" : "FALSE") << ": " << rv.bindings << "}";
o << "match_rv {" << (rv.success ? "TRUE" : "FALSE") << ": [";
//o << rv.bindings.size();
o << rv.bindings;
//for (uint i = 0; i < rv.bindings.size(); i ++)
// o << rv.bindings[i] << ", ";
o << "]}";
return o;
}
match_rv geas_implementation::match_command(String input, String action) const {
//cerr << "match_command (\"" << input << "\", \"" << action << "\")" << endl;
match_rv rv = match_command(input, 0, action, 0, match_rv());
cerr << "match_command (\"" << input << "\", \"" << action << "\") -> " << rv << endl;
return rv;
//return match_command (input, 0, action, 0, match_rv ());
}
match_rv geas_implementation::match_command(String input, uint ichar, String action, uint achar, match_rv rv) const {
//cerr << "match_command (\"" << input << "\", " << ichar << ", \"" << action << "\", " << achar << ", " << rv << ")" << endl;
for (;;) {
if (achar == action.length()) {
//cerr << "End of action, returning " << (ichar == input.length()) << "\n";
return match_rv(ichar == input.length(), rv);
}
if (action[achar] == '#') {
achar ++;
String varname;
while (achar != action.length() && action[achar] != '#') {
varname += action[achar];
achar ++;
}
if (achar == action.length())
error("Unpaired hashes in command String %s", action.c_str());
//rv.bindings.push_back (varname);
int index = rv.bindings.size();
rv.bindings.push_back(match_binding(varname, ichar));
achar ++;
varname = "";
//rv.bindings.push_back (varname);
rv.bindings[index].set(varname, ichar);
while (ichar < input.length()) {
match_rv tmp = match_command(input, ichar, action, achar, rv);
if (tmp.success)
return tmp;
varname += input[ichar];
ichar ++;
//rv.bindings[index] = varname;
rv.bindings[index].set(varname, ichar);
}
return match_rv(achar == action.length(), rv);
}
// SENSITIVE?
if (ichar == input.length() || !c_equal_i(input[ichar], action[achar]))
return match_rv();
//cerr << "Matched " << input[ichar] << " to " << action[achar] << endl;
++ achar;
++ ichar;
}
}
bool match_object_alts(String text, const Common::Array<String> &alts, bool is_internal) {
for (uint i = 0; i < alts.size(); i ++) {
cerr << "m_o_a: Checking '" << text << "' v. alt '" << alts[i] << "'.\n";
if (starts_with(text, alts[i])) {
uint len = alts[i].length();
if (text.length() == len)
return true;
if (text.length() > len && text[len] == ' ' &&
match_object_alts(text.substr(len + 1), alts, is_internal))
return true;
}
}
return false;
}
bool geas_implementation::match_object(String text, String name, bool is_internal) const {
cerr << "* * * match_object (" << text << ", " << name << ", "
<< (is_internal ? "true" : "false") << ")\n";
String alias, alt_list, prefix, suffix;
if (is_internal && ci_equal(text, name)) return true;
if (get_obj_property(name, "prefix", prefix) &&
starts_with(text, prefix + " ") &&
match_object(text.substr(prefix.length() + 1), name, false))
return true;
if (get_obj_property(name, "suffix", suffix) &&
ends_with(text, " " + suffix) &&
match_object(text.substr(0, text.length() - suffix.length() - 1), name, false))
return true;
if (!get_obj_property(name, "alias", alias))
alias = name;
if (ci_equal(text, alias))
return true;
const GeasBlock *gb = gf.find_by_name("object", name);
if (gb != NULL) {
String tok, line;
uint c1, c2;
for (uint ln = 0; ln < gb->data.size(); ln ++) {
line = gb->data[ln];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "alt") {
tok = next_token(line, c1, c2);
if (!is_param(tok))
gi->debug_print("Expected param after alt in " + line);
else {
Common::Array<String> alts = split_param(param_contents(tok));
cerr << " m_o: alt == " << alts << "\n";
return match_object_alts(text, alts, is_internal);
}
}
}
}
return false;
}
bool geas_implementation::dereference_vars(Common::Array<match_binding> &bindings, bool is_internal) const {
/* TODO */
Common::Array<String> where;
where.push_back("inventory");
where.push_back(state.location);
return dereference_vars(bindings, where, is_internal);
}
bool geas_implementation::dereference_vars(Common::Array<match_binding> &bindings, const Common::Array<String> &where, bool is_internal) const {
bool rv = true;
for (uint i = 0; i < bindings.size(); i ++)
if (bindings[i].var_name[0] == '@') {
String obj_name = get_obj_name(bindings[i].var_text, where, is_internal);
if (obj_name == "!") {
print_formatted("You don't see any " + bindings[i].var_text + ".");
rv = false;
} else {
bindings[i].var_text = obj_name;
bindings[i].var_name = bindings[i].var_name.substr(1);
}
}
return rv;
}
String geas_implementation::get_obj_name(String name, const Common::Array<String> &where, bool is_internal) const {
Common::Array<String> objs, printed_objs;
for (uint objnum = 0; objnum < state.objs.size(); objnum ++) {
bool is_used = false;
for (uint j = 0; j < where.size(); j ++) {
cerr << "Object #" << objnum << ": " << state.objs[objnum].name
<< "@" << state.objs[objnum].parent << " vs. "
<< where[j] << endl;
// SENSITIVE?
if (where[j] == "game" || state.objs[objnum].parent == where[j])
is_used = true;
}
if (is_used && !has_obj_property(state.objs[objnum].name, "hidden") &&
match_object(name, state.objs[objnum].name, is_internal)) {
String printed_name, tmp, oname = state.objs[objnum].name;
objs.push_back(oname);
if (!get_obj_property(oname, "alias", printed_name))
printed_name = oname;
if (get_obj_property(oname, "detail", tmp))
printed_name = tmp;
printed_objs.push_back(printed_name);
}
}
cerr << "objs == " << objs << ", printed_objs == " << printed_objs << "\n";
if (objs.size() > 1) {
//bindings[i].var_name = bindings[i].var_name.substr(1);
uint num = 0;
//if (objs.size() > 1)
num = gi->make_choice("Which " + name + " do you mean?", printed_objs);
//bindings[i].var_text = objs[num];
return objs[num];
}
if (objs.size() == 1)
return objs[0];
return "!";
}
void geas_implementation::set_vars(const Common::Array<match_binding> &v) {
for (uint i = 0; i < v.size(); i ++)
set_svar(v[i].var_name, v[i].var_text);
}
bool geas_implementation::run_commands(String cmd, const GeasBlock *room, bool is_internal) {
uint c1, c2;
String line, tok;
match_rv match;
if (room != NULL) {
for (uint i = 0; i < room->data.size(); i++) {
line = room->data[i];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "command") {
tok = next_token(line, c1, c2);
if (is_param(tok)) {
Common::Array<String> tmp = split_param(param_contents(tok));
for (uint j = 0; j < tmp.size(); j++)
if ((match = match_command(cmd, tmp[j]))) {
if (!dereference_vars(match.bindings, is_internal))
return false;
set_vars(match.bindings);
run_script_as(state.location, line.substr(c2 + 1));
//run_script (line.substr (c2+1));
return true;
}
/*
if (match = match_command (cmd, param_contents(tok)))
{
if (!dereference_vars (match.bindings))
return false;
set_vars (match.bindings);
run_script (line.substr (c2+1));
return true;
}
*/
} else {
gi->debug_print("Bad command line: " + line);
}
}
}
} else
gi->debug_print("room is null\n");
return false;
}
bool geas_implementation::try_match(String cmd, bool is_internal, bool is_normal) {
//print_formatted ("geas_impl registers " + cmd);
String line, tok;
match_rv match;
if (!is_normal) {
if (run_commands(cmd, gf.find_by_name("room", state.location)) ||
run_commands(cmd, gf.find_by_name("game", "game")))
return true;
}
if ((match = match_command(cmd, "look at #@object#")) ||
(match = match_command(cmd, "look #@object#"))) {
if (!dereference_vars(match.bindings, is_internal))
return true;
String object = match.bindings[0].var_text;
if (get_obj_action(object, "look", tok))
run_script_as(object, tok);
//run_script (tok);
else if (get_obj_property(object, "look", tok))
print_formatted(tok);
else
display_error("defaultlook", object);
return true;
}
if ((match = match_command(cmd, "examine #@object#")) ||
(match = match_command(cmd, "x #@object#"))) {
if (!dereference_vars(match.bindings, is_internal))
return true;
String object = match.bindings[0].var_text;
if (get_obj_action(object, "examine", tok))
run_script_as(object, tok);
//run_script (tok);
else if (get_obj_property(object, "examine", tok))
print_formatted(tok);
else if (get_obj_action(object, "look", tok))
run_script_as(object, tok);
//run_script (tok);
else if (get_obj_property(object, "look", tok))
print_formatted(tok);
else
display_error("defaultexamine", object);
return true;
}
if ((match = match_command(cmd, "look"))) {
look();
return true;
}
if ((match = match_command(cmd, "give #@first# to #@second#"))) {
if (!dereference_vars(match.bindings, is_internal))
return true;
String script, first = match.bindings[0].var_text, second = match.bindings[1].var_text;
if (! ci_equal(get_obj_parent(first), "inventory"))
display_error("noitem", first);
else if (get_obj_action(second, "give " + first, script))
run_script(second, script);
//run_script (script);
else if (get_obj_action(first, "give to " + second, script))
run_script_as(first, script);
//run_script (script);
else if (get_obj_action(second, "give anything", script)) {
set_svar("quest.give.object.name", first);
run_script_as(second, script);
//run_script (script);
} else if (get_obj_action(first, "give to anything", script)) {
set_svar("quest.give.object.name", second);
run_script_as(first, script);
//run_script (script);
} else {
String tmp;
if (!get_obj_property(second, "gender", tmp))
tmp = "it";
set_svar("quest.error.gender", tmp);
if (!get_obj_property(first, "article", tmp))
tmp = "it";
set_svar("quest.error.article", tmp);
display_error("itemunwanted");
}
return true;
}
if ((match = match_command(cmd, "use #@first# on #@second#")) ||
(match = match_command(cmd, "use #@first# with #@second#"))) {
if (!dereference_vars(match.bindings, is_internal))
return true;
String script, first = match.bindings[0].var_text, second = match.bindings[1].var_text;
if (! ci_equal(get_obj_parent(first), "inventory"))
display_error("noitem", first);
else if (get_obj_action(second, "use " + first, script)) {
//set_svar ("quest.use.object", first);
run_script_as(second, script);
//run_script (script);
} else if (get_obj_action(first, "use on " + second, script)) {
//set_svar ("quest.use.object", second);
run_script_as(first, script);
//run_script (script);
} else if (get_obj_action(second, "use anything", script)) {
set_svar("quest.use.object", first);
run_script(second, script);
//run_script (script);
} else if (get_obj_action(first, "use on anything", script)) {
set_svar("quest.use.object", second);
run_script_as(first, script);
//run_script (script);
} else
display_error("defaultuse");
return true;
}
if ((match = match_command(cmd, "use #@first#"))) {
if (!dereference_vars(match.bindings, is_internal))
return true;
String tmp, obj = match.bindings[0].var_text;
if (!ci_equal(get_obj_parent(obj), "inventory"))
display_error("noitem", obj);
else if (get_obj_action(obj, "use", tmp))
run_script_as(obj, tmp);
//run_script (tmp);
else if (get_obj_property(obj, "use", tmp))
print_formatted(tmp);
else
display_error("defaultuse", obj);
return true;
}
if ((match = match_command(cmd, "take #@object#")) ||
(match = match_command(cmd, "get #@object#"))) {
if (!dereference_vars(match.bindings, is_internal))
return true;
String object = match.bindings[0].var_text;
if (get_obj_action(object, "take", tok)) {
cerr << "Running script '" << tok << "' for take " << object << endl;
run_script_as(object, tok);
//run_script (tok);
} else if (get_obj_property(object, "take", tok)) {
cerr << "Found property '" << tok << "' for take " << object << endl;
if (tok != "")
print_formatted(tok);
else
display_error("defaulttake", object);
String tmp;
move(object, "inventory");
if (get_obj_action(object, "gain", tmp))
run_script(object, tmp);
//run_script (tmp);
else if (get_obj_property(object, "gain", tmp))
print_formatted(tmp);
} else {
cerr << "No match found for take " << object << endl;
// TODO set variable with object name
display_error("badtake", object);
}
return true;
}
if ((match = match_command(cmd, "drop #@object#"))) {
if (!dereference_vars(match.bindings, is_internal))
return true;
String scr, obj = match.bindings[0].var_text;
if (get_obj_action(obj, "drop", scr)) {
run_script_as(obj, scr);
//run_script (scr);
return true;
}
const GeasBlock *gb = gf.find_by_name("object", obj);
if (gb != NULL) {
uint c1, c2, script_begins;
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "drop") {
script_begins = c2;
tok = next_token(line, c1, c2);
// SENSITIVE?
if (tok == "everywhere") {
tok = next_token(line, c1, c2);
move(obj, state.location);
if (is_param(tok))
print_eval(tok);
else
gi->debug_print("Expected param after drop everywhere in " + line);
return true;
}
// SENSITIVE?
if (tok == "nowhere") {
if (is_param(tok))
print_eval(tok);
else
gi->debug_print("Expected param after drop nowhere in " + line);
return true;
}
run_script_as(obj, line.substr(script_begins));
//run_script (line.substr (script_begins));
return true;
}
}
}
move(obj, state.location);
display_error("defaultdrop", obj);
return true;
}
if ((match = match_command(cmd, "speak to #@object#")) ||
(match = match_command(cmd, "speak #@object#")) ||
(match = match_command(cmd, "talk to #@object#")) ||
(match = match_command(cmd, "talk #@object#"))) {
//print_formatted ("Talk to <" + String (match.bindings[0]) + ">");
if (!dereference_vars(match.bindings, is_internal))
return true;
String obj = match.bindings[0].var_text;
String script;
if (get_obj_action(obj, "speak", script))
run_script_as(obj, script);
//run_script (script);
else
display_error("defaultspeak", obj);
//print_formatted ("Talk to <" + String (match.bindings[0]) + ">");
return true;
}
if (cmd == "exit" || cmd == "out" || cmd == "go out") {
const GeasBlock *gb = gf.find_by_name("room", state.location);
if (gb == NULL) {
gi->debug_print("Bad room");
return true;
}
line = "";
int c1 = -1, c2 = -1;
uint uc1, uc2;
// TODO: Use the first matching line or the last?
for (uint i = 0; i < gb->data.size(); i ++) {
if (first_token(gb->data[i], uc1, uc2) == "out")
line = gb->data[i];
c1 = uc1;
c2 = uc2;
}
//gi->debug_print ("COMMAND " + cmd + ": line == " + line);
if (line == "")
display_error("defaultout");
else {
c1 = line.find('<');
if (c1 != -1)
c2 = line.find('>', c1);
if (c1 == -1 || c2 == -1)
gi->debug_print("Bad out line: " + line);
else {
String tmp = trim(line.substr(c2 + 1));
//gi->debug_print ("tmp1 == {" + tmp + "}");
if (tmp != "")
run_script_as(state.location, tmp);
//run_script (tmp);
else {
tmp = line.substr(c1, c2 - c1 + 1);
//gi->debug_print ("tmp2 == {" + tmp + "}");
assert(is_param(tmp));
tmp = param_contents(tmp);
c1 = tmp.find(';');
if (c1 == -1)
goto_room(trim(tmp));
else
goto_room(trim(tmp.substr(c1 + 1)));
}
}
}
return true;
}
for (uint i = 0; i < ARRAYSIZE(dir_names); i ++)
if (cmd == dir_names[i] || cmd == (String("go ") + dir_names[i]) ||
cmd == short_dir_names[i] || cmd == (String("go ") + short_dir_names[i])) {
bool is_script = false;
//print_formatted ("Trying to go " + dir_names[i]);
if ((tok = exit_dest(state.location, dir_names[i], &is_script)) == "") {
// TODO Which display_error do I use?
print_formatted("You can't go that way.");
return true;
}
if (is_script)
run_script_as(state.location, tok);
//run_script (tok);
else {
int index = tok.find(';');
if (index == -1)
goto_room(trim(tok));
else
goto_room(trim(tok.substr(index + 1)));
}
return true;
}
if ((match = match_command(cmd, "go to #@room#")) ||
(match = match_command(cmd, "go #@room#"))) {
assert(match.bindings.size() == 1);
String destination = match.bindings[0].var_text;
for (uint i = 0; i < current_places.size(); i ++) {
if (ci_equal(destination, current_places[i][1]) ||
ci_equal(destination, current_places[i][2])) {
if (current_places[i].size() == 5)
run_script_as(state.location, current_places[i][4]);
//run_script (current_places[i][4]);
else
goto_room(current_places[i][3]);
return true;
}
}
display_error("badplace", destination);
return true;
}
if (ci_equal(cmd, "inventory") || ci_equal(cmd, "i")) {
Common::Array<Common::Array<String> > inv = get_inventory();
if (inv.size() == 0)
print_formatted("You are carrying nothing.");
else
print_formatted("You are carrying:");
for (uint i = 0; i < inv.size(); i ++) {
print_normal(inv[i][0]);
print_newline();
}
return true;
}
if (ci_equal(cmd, "help")) {
print_formatted("|b|cl|s14Quest Quick Help|xb|cb|s00|n|n|cl|bMoving|xb|cb Type |bGO NORTH|xb, |bSOUTH|xb, |bE|xb, etc. |xnTo go into a place, type |bGO TO ...|xb . To leave a place, type |bOUT, EXIT|xb or |bLEAVE|xb, or press the '|crOUT|cb' button.|n|cl|bObjects and Characters|xb|cb Use |bTAKE ...|xb, |bGIVE ... TO ...|xb, |bTALK|xb/|bSPEAK TO ...|xb, |bUSE ... ON|xb/|bWITH ...|xb, |bLOOK AT ...|xb, etc.|n|cl|bExit Quest|xb|cb Type |bQUIT|xb to leave Quest.|n|cl|bMisc|xb|cb Type |bABOUT|xb to get information on the current game.");
return true;
}
if (ci_equal(cmd, "about")) {
const GeasBlock *gb = gf.find_by_name("game", "game");
if (gb == NULL)
return true;
cerr << *gb << endl;
uint c1, c2;
//print_formatted ("Game name: ");
line = gb->data[0];
tok = first_token(line, c1, c2); // game
tok = next_token(line, c1, c2); // name
tok = next_token(line, c1, c2); // <whatever>
if (is_param(tok))
print_formatted("Game name: " + eval_param(tok));
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
// SENSITIVE?
if (first_token(line, c1, c2) == "game" &&
next_token(line, c1, c2) == "version" &&
is_param(tok = next_token(line, c1, c2)))
print_formatted("Version " + eval_param(tok));
}
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
// SENSITIVE?
if (first_token(line, c1, c2) == "game" &&
next_token(line, c1, c2) == "author" &&
is_param(tok = next_token(line, c1, c2)))
print_formatted("Author: " + eval_param(tok));
}
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
// SENSITIVE?
if (first_token(line, c1, c2) == "game" &&
next_token(line, c1, c2) == "copyright" &&
is_param(tok = next_token(line, c1, c2)))
print_formatted("Copyright: " + eval_param(tok));
}
for (uint i = 0; i < gb->data.size(); i ++) {
line = gb->data[i];
// SENSITIVE?
if (first_token(line, c1, c2) == "game" &&
next_token(line, c1, c2) == "info" &&
is_param(tok = next_token(line, c1, c2)))
print_formatted(eval_param(tok));
}
return true;
}
if (ci_equal(cmd, "quit")) {
is_running_ = false;
return true;
}
return false;
}
void geas_implementation::run_script_as(String obj, String scr) {
String backup_object, garbage;
backup_object = this_object;
this_object = obj;
run_script(scr, garbage);
this_object = backup_object;
}
void geas_implementation::run_script(String s) {
String garbage;
run_script(s, garbage);
}
void geas_implementation::run_script(String s, String &rv) {
//print_formatted (" Running script " + s + ".");
cerr << "Script line '" << s << "'\n";
String tok;
uint c1, c2;
tok = first_token(s, c1, c2);
if (tok == "") return;
if (tok[0] == '{') {
uint brace1 = c1 + 1, brace2;
for (brace2 = s.length() - 1; brace2 >= brace1 && s[brace2] != '}'; brace2 --)
;
if (brace2 >= brace1)
run_script(s.substr(brace1, brace2 - brace1));
else
gi->debug_print("Unterminated brace block in " + s);
return;
}
// SENSITIVE?
if (tok == "action") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after action in " + s);
return;
}
tok = eval_param(tok);
int index = tok.find(';');
if (index == -1) {
gi->debug_print("Error: no semicolon in " + s);
return;
}
set_obj_action(trim(tok.substr(0, index)),
"<" + trim(tok.substr(index + 1)) + "> " + s.substr(c2 + 1));
return;
}
// SENSITIVE?
else if (tok == "animate") {
}
// SENSITIVE?
else if (tok == "background") {
tok = next_token(s, c1, c2);
if (is_param(tok))
gi->set_background(eval_param(tok));
else
gi->debug_print("Expected parameter after foreground in " + s);
return;
}
// SENSITIVE?
else if (tok == "choose") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after choose in " + s);
return;
}
tok = eval_param(tok);
const GeasBlock *gb = gf.find_by_name("selection", tok);
if (gb == NULL) {
gi->debug_print("No selection called " + tok + " found");
return;
}
String question, line;
Common::Array<String> choices, actions;
for (uint ln = 0; ln < gb->data.size(); ln ++) {
line = gb->data[ln];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "info") {
tok = next_token(line, c1, c2);
if (is_param(tok))
question = eval_param(tok);
else
gi->debug_print("Expected parameter after info in " + line);
}
// SENSITIVE?
else if (tok == "choice") {
tok = next_token(line, c1, c2);
if (is_param(tok)) {
choices.push_back(eval_param(tok));
actions.push_back(line.substr(c2));
} else
gi->debug_print("Expected parameter after choice in " + line);
} else
gi->debug_print("Bad line " + line + " in selection");
}
if (choices.size() == 0)
//gi->debug_print ("No choices in selection " + gb->lname);
gi->debug_print("No choices in selection " + gb->name);
else
run_script(actions[gi->make_choice(question, choices)]);
return;
}
// SENSITIVE?
else if (tok == "clear") {
gi->clear_screen();
return;
}
// SENSITIVE?
else if (tok == "clone") {
/* TODO */
}
// SENSITIVE?
else if (tok == "create") {
tok = next_token(s, c1, c2);
// SENSITIVE?
if (tok == "exit") { // create exit
String dir = "";
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
dir = tok;
tok = next_token(s, c1, c2);
}
if (!is_param(tok)) {
gi->debug_print("Expected param after create exit in " + s);
return;
}
tok = eval_param(tok);
Common::Array<String> args = split_param(tok);
if (args.size() != 2) {
gi->debug_print("Expected 2 elements in param in " + s);
return;
}
if (dir != "")
state.exits.push_back(ExitRecord(args[0],
"exit " + dir + " <" + tok + ">"));
else
state.exits.push_back(ExitRecord(args[0], "exit <" + tok + ">"));
//gi->debug_print ("Not yet able to create place type exits");
regen_var_dirs();
return;
}
// SENSITIVE?
else if (tok == "object") { // create object
/* TODO */
}
// SENSITIVE?
else if (tok == "room") { // create room
/* TODO */
} else
gi->debug_print("Bad create line " + s);
return;
}
// SENSITIVE?
else if (tok == "debug") {
tok = next_token(s, c1, c2);
if (is_param(tok))
gi->debug_print(eval_param(tok));
else
gi->debug_print("Expected param after debug in " + s);
return;
}
// SENSITIVE?
else if (tok == "destroy") {
tok = next_token(s, c1, c2);
if (tok != "exit") {
gi->debug_print("expected 'exit' after 'destroy' in " + s);
return;
}
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected param after 'destroy exit' in " + s);
return;
}
tok = eval_param(tok);
Common::Array<String> args = split_param(tok);
if (args.size() != 2) {
gi->debug_print("Expected two arguments in " + s);
return;
}
//state.exits.push_back (ExitRecord (args[0], "destroy exit <" + tok + ">"));
state.exits.push_back(ExitRecord(args[0], "destroy exit " + args[1]));
regen_var_dirs();
return;
}
// SENSITIVE?
else if (tok == "disconnect") {
/* QNSO */
}
// SENSITIVE?
else if (tok == "displaytext") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after displaytext in " + s);
return;
}
const GeasBlock *gb = gf.find_by_name("text", param_contents(tok));
if (gb != NULL) {
for (uint i = 0; i < gb->data.size(); i ++) {
print_formatted(gb->data[i]);
print_newline();
}
} else
gi->debug_print("No such text block " + tok);
return;
}
// SENSITIVE?
else if (tok == "do") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after do in " + s);
return;
}
String fname = eval_param(tok);
int index = fname.find('(');
if (index != -1) {
int index2 = fname.find(')');
run_procedure(trim(fname.substr(0, index)),
split_f_args(fname.substr(index + 1, index2 - index - 1)));
} else
run_procedure(fname);
//run_procedure (fname);
return;
}
// SENSITIVE?
else if (tok == "doaction") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after doaction in " + s);
return;
}
String line = eval_param(tok);
int index = line.find(';');
String obj = trim(line.substr(0, index));
String act = trim(line.substr(index + 1));
String old_object = this_object;
this_object = obj;
if (get_obj_action(obj, act, tok))
run_script_as(obj, tok);
//run_script (tok);
else
gi->debug_print("No action defined for " + obj + " // " + act);
this_object = old_object;
return;
}
// SENSITIVE?
else if (tok == "dontprocess") {
dont_process = true;
return;
}
// SENSITIVE?
else if (tok == "enter") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after enter in " + s);
return;
}
tok = eval_param(tok);
set_svar(tok, gi->get_string());
return;
}
// SENSITIVE?
else if (tok == "exec") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after exec in " + s);
return;
}
tok = eval_param(tok);
int index = tok.find(';');
if (index != -1) {
String tmp = trim(tok.substr(index + 1));
// SENSITIVE?
if (tmp == "normal") {
//run_command (trim (tok.substr (0, index)), true, true);
try_match(trim(tok.substr(0, index)), true, true);
} else {
gi->debug_print("Bad " + tmp + " in exec in " + s);
//run_command (trim (tok.substr (0, index)), true, false);
try_match(trim(tok.substr(0, index)), true, false);
}
} else {
//run_command (trim (tok.substr (0, index)), true, false);
try_match(trim(tok.substr(0, index)), true, false);
}
return;
}
// SENSITIVE?
else if (tok == "flag") {
tok = next_token(s, c1, c2);
bool is_on;
// SENSITIVE?
if (tok == "on")
is_on = true;
// SENSITIVE?
else if (tok == "off")
is_on = false;
else {
gi->debug_print("Expected 'on' or 'off' after flag in " + s);
return;
}
String onoff = tok;
tok = next_token(s, c1, c2);
if (is_param(tok))
set_obj_property("game", (is_on ? "" : "not ") + eval_param(tok));
else
gi->debug_print("Expected param after flag " + onoff + " in " + s);
return;
}
// SENSITIVE?
else if (tok == "font") {
/* TODO */
}
// SENSITIVE?
else if (tok == "for") {
tok = next_token(s, c1, c2);
// SENSITIVE?
if (tok == "each") {
// SENSITIVE?
if (next_token(s, c1, c2) == "object" &&
next_token(s, c1, c2) == "in") {
tok = next_token(s, c1, c2);
// SENSITIVE?
if (tok == "game") {
/* TODO: This will run over the game, rooms, and objects */
/* It should just do the objects. */
String script = s.substr(c2);
// Start at 1 to skip game
for (uint i = 1; i < state.objs.size(); i ++) {
cerr << " quest.thing -> " + state.objs[i].name + "\n";
set_svar("quest.thing", state.objs[i].name);
run_script(script);
}
return;
} else if (is_param(tok)) {
tok = trim(eval_param(tok));
String script = s.substr(c2);
for (uint i = 0; i < state.objs.size(); i ++)
if (state.objs[i].parent == tok) {
set_svar("quest.thing", state.objs[i].name);
run_script(script);
}
return;
}
}
} else if (is_param(tok)) {
Common::Array<String> args = split_param(eval_param(tok));
String varname = args[0];
String script = s.substr(c2);
int startindex = parse_int(args[1]);
int endindex = parse_int(args[2]);
int step = 1;
if (args.size() > 3)
step = parse_int(args[3]);
for (set_ivar(varname, startindex); get_ivar(varname) < endindex;
set_ivar(varname, get_ivar(varname) + step))
run_script(script);
return;
}
}
// SENSITIVE?
else if (tok == "foreground") {
tok = next_token(s, c1, c2);
if (is_param(tok))
gi->set_foreground(eval_param(tok));
else
gi->debug_print("Expected parameter after foreground in " + s);
return;
}
// SENSITIVE?
else if (tok == "give") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after give in " + s);
return;
}
tok = eval_param(tok);
move(tok, "inventory");
String tmp;
if (get_obj_action(tok, "gain", tmp))
run_script_as(tok, tmp);
//run_script (tmp);
else if (get_obj_property(tok, "gain", tmp))
print_formatted(tmp);
return;
}
// SENSITIVE?
else if (tok == "goto") {
tok = next_token(s, c1, c2);
if (is_param(tok))
goto_room(trim(eval_param(tok)));
else
gi->debug_print("Expected parameter after goto in " + s);
return;
}
// SENSITIVE?
else if (tok == "helpclear") {
}
// SENSITIVE?
else if (tok == "helpclose") {
}
// SENSITIVE?
else if (tok == "helpdisplaytext") {
}
// SENSITIVE?
else if (tok == "helpmsg") {
}
// SENSITIVE?
else if (tok == "hide") {
tok = next_token(s, c1, c2);
if (is_param(tok))
set_obj_property(eval_param(tok), "hidden");
else
gi->debug_print("Expected param after conceal in " + s);
return;
}
// SENSITIVE?
else if (tok == "show") {
tok = next_token(s, c1, c2);
if (is_param(tok))
set_obj_property(eval_param(tok), "not hidden");
else
gi->debug_print("Expected param after conceal in " + s);
return;
}
// SENSITIVE?
else if (tok == "if") {
/* TODO TODO */
uint begin_cond = c2 + 1, end_cond, begin_then, end_then;
do {
tok = next_token(s, c1, c2);
// SENSITIVE?
} while (tok != "then" && tok != "");
if (tok == "") {
gi->debug_print("Expected then in if: " + s);
return;
}
end_cond = c1;
String cond_str = s.substr(begin_cond, end_cond - begin_cond);
begin_then = c2 + 1;
int brace_count = 0;
do {
tok = next_token(s, c1, c2);
for (uint i = 0; i < tok.length(); i ++)
if (tok[i] == '{')
brace_count ++;
else if (tok[i] == '}')
brace_count --;
// SENSITIVE?
} while (tok != "" && !(brace_count == 0 && tok == "else"));
end_then = c1;
if (eval_conds(cond_str))
run_script(s.substr(begin_then, end_then - begin_then), rv);
else if (c2 < s.length())
run_script(s.substr(c2), rv);
return;
}
// SENSITIVE?
else if (tok == "inc" || tok == "dec") {
// SENSITIVE?
bool is_dec = (tok == "dec");
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after inc in " + s);
return;
}
tok = eval_param(tok);
int diff;
int index = tok.find(';');
String varname;
if (index == -1) {
varname = trim(tok);
diff = 1;
} else {
varname = trim(tok.substr(0, index));
diff = eval_int(tok.substr(index + 1));
}
if (is_dec)
set_ivar(varname, get_ivar(varname) - diff);
else
set_ivar(varname, get_ivar(varname) + diff);
return;
}
// SENSITIVE?
else if (tok == "lose") {
/* TODO TODO */
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after lose in " + s);
return;
}
tok = eval_param(tok);
/* TODO: is the object always moved to location, or only
* when it had been in the inventory ?
*/
bool was_lost = (ci_equal(get_obj_parent(tok), "inventory"));
if (was_lost) {
move(tok, state.location);
String tmp;
if (get_obj_action(tok, "lose", tmp))
run_script_as(tok, tmp);
//run_script (tmp);
else if (get_obj_property(tok, "lose", tmp))
print_formatted(tmp);
}
return;
}
// SENSITIVE?
else if (tok == "mailto") {
}
// SENSITIVE?
else if (tok == "modvolume") {
}
// SENSITIVE?
else if (tok == "move") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after move in " + s);
return;
}
tok = eval_param(tok);
int index = tok.find(';');
if (index == -1) {
gi->debug_print("No semi in " + tok + " in " + s);
return;
}
move(trim(tok.substr(0, index)), trim(tok.substr(index + 1)));
return;
}
// SENSITIVE?
else if (tok == "msg") {
tok = next_token(s, c1, c2);
if (is_param(tok))
print_eval(param_contents(tok));
else
gi->debug_print("Expected parameter after msg in " + s);
return;
}
// SENSITIVE?
else if (tok == "msgto") {
/* QNSO */
}
// SENSITIVE?
else if (tok == "outputoff") {
//print_formatted ("<<");
outputting = false;
return;
}
// SENSITIVE?
else if (tok == "outputon") {
outputting = true;
//print_formatted (">>");
return;
}
// SENSITIVE?
else if (tok == "panes") {
/* TODO */
}
// SENSITIVE?
else if (tok == "pause") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after pause in " + s);
return;
}
int i = eval_int(param_contents(tok));
gi->pause(i);
return;
}
// SENSITIVE?
else if (tok == "picture") {
}
// SENSITIVE?
else if (tok == "playerlose") {
run_script("displaytext <lose>");
state.running = false;
return;
}
// SENSITIVE?
else if (tok == "playerwin") {
run_script("displaytext <win>");
state.running = false;
return;
}
// SENSITIVE?
else if (tok == "playmidi") {
}
// SENSITIVE?
else if (tok == "playmod") {
}
// SENSITIVE?
else if (tok == "playwav") {
}
// SENSITIVE?
else if (tok == "property") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter in '" + s + "'");
return;
}
Common::Array<String> args = split_param(eval_param(tok));
for (uint i = 1; i < args.size(); i ++) {
String val = args[i];
/*
if (val[0] == '[' && val[val.length() - 1] == ']')
val = val.substr (1, val.length() - 2);
//state.props.push_back (PropertyRecord (args[0], val));
*/
val = trim_braces(val);
set_obj_property(args[0], val);
}
return;
}
// SENSITIVE?
else if (tok == "repeat") {
/* TODO TODO: assumes script is a "do ..." */
tok = next_token(s, c1, c2);
// SENSITIVE?
if (tok != "while" && tok != "until") {
gi->debug_print("Expected while or until after repeat in " + s);
return;
}
bool is_while = (tok == "while");
int start_cond = c2, end_cond = -1;
while ((tok = next_token(s, c1, c2)) != "") {
// SENSITIVE?
if (tok == "do") {
end_cond = c1;
break; // TODO: Do I break here?
}
}
if (end_cond == -1) {
gi->debug_print("No script found after condition in " + s);
return;
}
String cond = trim(s.substr(start_cond, end_cond - start_cond));
String script = trim(s.substr(end_cond));
cerr << "Interpreting '" << s << "' as ("
<< (is_while ? "WHILE" : "UNTIL") << ") ("
<< cond << ") {" << script << "}\n";
while (eval_conds(cond) == is_while)
run_script(script);
return;
}
// SENSITIVE?
else if (tok == "return") {
tok = next_token(s, c1, c2);
if (is_param(tok))
rv = eval_param(tok);
else
gi->debug_print("Expected parameter after return in " + s);
return;
}
// SENSITIVE?
else if (tok == "reveal") {
tok = next_token(s, c1, c2);
if (is_param(tok))
set_obj_property(eval_param(tok), "not invisible");
else
gi->debug_print("Expected param after reveal in " + s);
return;
}
// SENSITIVE?
else if (tok == "conceal") {
tok = next_token(s, c1, c2);
if (is_param(tok))
set_obj_property(eval_param(tok), "invisible");
else
gi->debug_print("Expected param after conceal in " + s);
return;
}
// SENSITIVE?
else if (tok == "say") {
tok = next_token(s, c1, c2);
if (is_param(tok)) {
tok = eval_param(tok);
print_formatted("\"" + tok + "\"");
} else
gi->debug_print("Expected param after say in " + s);
return;
}
// SENSITIVE?
else if (tok == "set") {
String vartype = "";
tok = next_token(s, c1, c2);
// SENSITIVE?
if (tok == "interval") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected param after set interval in " + s);
return;
}
tok = eval_param(tok);
int index = tok.find(';');
if (index == -1) {
gi->debug_print("No semicolon in param in " + s);
return;
}
//String timer_name = lcase (trim (tok.substr (0, index)));
String timer_name = trim(tok.substr(0, index));
uint time_val = parse_int(trim(tok.substr(index + 1)));
for (uint i = 0; i < state.timers.size(); i ++)
if (state.timers[i].name == timer_name) {
state.timers[i].interval = time_val;
return;
}
gi->debug_print("no interval named " + timer_name + " found!");
return;
}
// SENSITIVE?
if (tok == "String" || tok == "numeric") {
vartype = tok;
tok = next_token(s, c1, c2);
}
if (!is_param(tok)) {
gi->debug_print("Expected parameter in " + s);
return;
}
if (tok.find(';') == -1) {
gi->debug_print("Only one expression in set in " + s);
return;
}
tok = eval_param(tok);
int index = tok.find(';');
//String varname = lcase (trim (tok.substr (0, index)));
String varname = trim(tok.substr(0, index));
if (vartype == "") {
for (uint varn = 0; varn < state.ivars.size(); varn ++)
if (state.ivars[varn].name == varname) {
vartype = "numeric";
break;
}
if (vartype == "")
for (uint varn = 0; varn < state.svars.size(); varn ++)
if (state.svars[varn].name == varname) {
vartype = "String";
break;
}
}
if (vartype == "") {
gi->debug_print("Undefined variable " + varname + " in " + s);
return;
}
if (vartype == "String")
set_svar(varname, trim_braces(trim(tok.substr(index + 1))));
else
set_ivar(varname, eval_int(tok.substr(index + 1)));
return;
}
// SENSITIVE?
else if (tok == "setstring") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter in " + s);
return;
}
if (tok.find(';') == -1) {
gi->debug_print("Only one expression in set in " + s);
return;
}
tok = eval_param(tok);
int index = tok.find(';');
String varname = trim(tok.substr(0, index));
set_svar(varname, trim_braces(trim(tok.substr(index + 1))));
return;
}
// SENSITIVE?
else if (tok == "setvar") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter in " + s);
return;
}
if (tok.find(';') == -1) {
gi->debug_print("Only one expression in set in " + s);
return;
}
tok = eval_param(tok);
int index = tok.find(';');
String varname = trim(tok.substr(0, index));
set_ivar(varname, eval_int(tok.substr(index + 1)));
return;
}
// SENSITIVE?
else if (tok == "shell") {
}
// SENSITIVE?
else if (tok == "shellexe") {
}
// SENSITIVE?
else if (tok == "speak") {
tok = next_token(s, c1, c2);
if (is_param(tok))
gi->speak(eval_param(tok));
else
gi->debug_print("Expected param after speak in " + s);
return;
}
// SENSITIVE?
else if (tok == "stop") {
state.running = false;
return;
}
// SENSITIVE?
else if (tok == "timeron" || tok == "timeroff") {
// SENSITIVE?
bool running = (tok == "timeron");
//tok = lcase (next_token (s, c1, c2));
tok = next_token(s, c1, c2);
if (is_param(tok)) {
tok = eval_param(tok);
for (uint i = 0; i < state.timers.size(); i ++)
if (state.timers[i].name == tok) {
if (running)
state.timers[i].timeleft = state.timers[i].interval;
state.timers[i].is_running = running;
return;
}
gi->debug_print("No timer " + tok + " found");
return;
}
gi->debug_print(String("Expected parameter after timer") +
(running ? "on" : "off") + " in " + s);
return;
}
// SENSITIVE?
else if (tok == "type") {
/* TODO */
}
// SENSITIVE?
else if (tok == "wait") {
tok = next_token(s, c1, c2);
if (tok != "") {
if (!is_param(tok)) {
gi->debug_print("Expected parameter after wait in " + s);
return;
}
tok = eval_param(tok);
}
gi->wait_keypress(tok);
return;
}
// SENSITIVE?
else if (tok == "with") {
// QNSO
}
gi->debug_print("Unrecognized script " + s);
}
bool geas_implementation::eval_conds(String s) {
cerr << "if (" + s + ")" << endl;
uint c1, c2;
String tok = first_token(s, c1, c2);
if (tok == "") return true;
bool rv = eval_cond(s);
while (tok != "" && tok != "and")
tok = next_token(s, c1, c2);
if (tok == "and")
rv = rv && eval_conds(s.substr(c2));
else {
tok = first_token(s, c1, c2);
while (tok != "" && tok != "or")
tok = next_token(s, c1, c2);
if (tok == "or")
rv = rv || eval_conds(s.substr(c2));
}
cerr << "if (" << s << ") --> " << (rv ? "true" : "false") << endl;
return rv;
}
bool geas_implementation::eval_cond(String s) {
uint c1, c2;
String tok = first_token(s, c1, c2);
// SENSITIVE?
if (tok == "not")
return !eval_cond(s.substr(c2));
// SENSITIVE?
else if (tok == "action") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("expected parameter after property in " + s);
return false;
}
tok = eval_param(tok);
int index = tok.find(';');
if (index == -1) {
gi->debug_print("Only one argument to property in " + s);
return false;
}
String obj = trim(tok.substr(0, index));
String act = trim(tok.substr(index + 1));
return has_obj_action(obj, act);
}
// SENSITIVE?
else if (tok == "ask") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("expected parameter after ask in " + s);
return false;
}
tok = eval_param(tok);
return gi->choose_yes_no(tok);
}
// SENSITIVE?
else if (tok == "exists") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("expected parameter after exists in " + s);
return false;
}
Common::Array<String> args = split_param(eval_param(tok));
bool do_report = false;
for (uint i = 1; i < args.size(); i ++)
// SENSITIVE?
if (args[i] == "report")
do_report = true;
else
gi->debug_print("Got modifier " + args[i] + " after exists");
//args[0] = lcase (args[0]);
for (uint i = 0; i < state.objs.size(); i ++)
if (ci_equal(state.objs[i].name, args[0]))
return state.objs[i].parent != "";
if (do_report)
gi->debug_print("exists " + args[0] + " failed due to nonexistence");
return false;
}
// SENSITIVE?
else if (tok == "flag") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("expected parameter after flag in " + s);
return false;
}
tok = trim(eval_param(tok));
return has_obj_property("game", tok);
}
// SENSITIVE?
else if (tok == "got") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("expected parameter after got in " + s);
return false;
}
//tok = lcase (trim (eval_param (tok)));
tok = trim(eval_param(tok));
for (uint i = 0; i < state.objs.size(); i ++)
if (ci_equal(state.objs[i].name, tok))
return ci_equal(state.objs[i].parent, "inventory");
gi->debug_print("No object " + tok + " found while evaling " + s);
return false;
}
// SENSITIVE?
else if (tok == "here") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("expected parameter after here in " + s);
return false;
}
//tok = lcase (trim (eval_param (tok)));
tok = trim(eval_param(tok));
for (uint i = 0; i < state.objs.size(); i ++)
if (ci_equal(state.objs[i].name, tok)) {
//return (ci_equal (state.objs[i].parent, state.location) &&
// !has_obj_property (tok, "invisible"));
return (ci_equal(state.objs[i].parent, state.location));
}
/* TODO: is it invisible or hidden? */
gi->debug_print("No object " + tok + " found while evaling " + s);
return false;
}
// SENSITIVE?
else if (tok == "is") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("expected parameter after is in " + s);
return false;
}
tok = eval_param(tok);
int index;
// SENSITIVE?
if ((index = tok.find("!=;")) != -1) {
uint index1 = index;
do {
-- index1;
} while (index1 > 0 && tok[index1] != ';');
cerr << "Comparing <" << trim_braces(trim(tok.substr(0, index1)))
<< "> != <" << trim_braces(trim(tok.substr(index + 3)))
<< ">\n";
return ci_notequal(trim_braces(trim(tok.substr(0, index - 1))),
trim_braces(trim(tok.substr(index + 3))));
}
if ((index = tok.find("lt=;")) != -1) {
cerr << "Comparing <" << trim_braces(trim(tok.substr(0, index)))
<< "> < <" << trim_braces(trim(tok.substr(index + 4)))
<< ">\n";
return eval_int(tok.substr(0, index - 1))
<= eval_int(tok.substr(index + 4));
}
if ((index = tok.find("gt=;")) != -1)
return eval_int(tok.substr(0, index))
>= eval_int(tok.substr(index + 4));
if ((index = tok.find("lt;")) != -1)
return eval_int(tok.substr(0, index))
< eval_int(tok.substr(index + 3));
if ((index = tok.find("gt;")) != -1)
return eval_int(tok.substr(0, index))
> eval_int(tok.substr(index + 3));
if ((index = tok.find(";")) != -1) {
cerr << "Comparing <" << trim_braces(trim(tok.substr(0, index)))
<< "> == <" << trim_braces(trim(tok.substr(index + 1)))
<< ">\n";
return ci_equal(trim_braces(trim(tok.substr(0, index))),
trim_braces(trim(tok.substr(index + 1))));
}
gi->debug_print("Bad is condition " + tok + " in " + s);
return false;
}
// SENSITIVE?
else if (tok == "property") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("expected parameter after property in " + s);
return false;
}
tok = eval_param(tok);
int index = tok.find(';');
if (index == -1) {
gi->debug_print("Only one argument to property in " + s);
return false;
}
String obj = trim(tok.substr(0, index));
String prop = trim(tok.substr(index + 1));
return has_obj_property(obj, prop);
}
// SENSITIVE?
else if (tok == "real") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("expected parameter after real in " + s);
return false;
}
Common::Array<String> args = split_param(eval_param(tok));
bool do_report = false;
for (uint i = 1; i < args.size(); i ++)
// SENSITIVE?
if (args[i] == "report")
do_report = true;
else
gi->debug_print("Got modifier " + args[i] + " after exists");
//args[0] = lcase (args[0]);
for (uint i = 0; i < state.objs.size(); i ++)
if (ci_equal(state.objs[i].name, args[0]))
return true;
if (do_report)
gi->debug_print("real " + args[0] + " failed due to nonexistence");
return false;
}
// SENSITIVE?
else if (tok == "type") {
tok = next_token(s, c1, c2);
if (!is_param(tok)) {
gi->debug_print("Expected parameter after type in " + s);
return false;
}
Common::Array<String> args = split_param(eval_param(tok));
if (args.size() != 2) {
gi->debug_print("Expected two parameters to type in " + s);
return false;
}
return gf.obj_of_type(args[0], args[1]);
}
gi->debug_print("Bad condition " + s);
return false;
}
void geas_implementation::run_procedure(String pname, Common::Array<String> args) {
cerr << "run_procedure " << pname << " (" << args << ")\n";
Common::Array<String> backup = function_args;
function_args = args;
run_procedure(pname);
function_args = backup;
}
void geas_implementation::run_procedure(String pname) {
for (uint i = 0; i < gf.size("procedure"); i ++)
if (ci_equal(gf.block("procedure", i).name, pname)) {
const GeasBlock &proc = gf.block("procedure", i);
//cerr << "Running procedure " << proc << endl;
for (uint j = 0; j < proc.data.size(); j ++) {
//cerr << " Running line #" << j << ": " << proc.data[j] << endl;
run_script(proc.data[j]);
}
return;
}
gi->debug_print("No procedure " + pname + " found.");
}
String geas_implementation::run_function(String pname, Common::Array<String> args) {
cerr << "run_function (w/ args) " << pname << " (" << args << ")\n";
/* Parameter is handled specially because it can't change the stack */
// SENSITIVE?
if (pname == "parameter") {
if (args.size() != 1) {
gi->debug_print("parameter called with " + string_int(args.size())
+ " args");
return "";
}
uint num = parse_int(args[0]);
if (0 < num && num <= function_args.size()) {
cerr << " --> " << function_args[num - 1] << "\n";
return function_args[num - 1];
}
cerr << " --> too many arguments\n";
return "";
}
Common::Array<String> backup = function_args;
function_args = args;
for (uint i = 0; i < args.size(); i ++)
set_svar("quest.function.parameter." + string_int(i + 1), args[i]);
String rv = run_function(pname);
function_args = backup;
return rv;
}
String geas_implementation::bad_arg_count(String fname) {
gi->debug_print("Called " + fname + " with " +
string_int(function_args.size()) + " arguments.");
return "";
}
String geas_implementation::run_function(String pname) {
cerr << "geas_implementation::run_function (" << pname << ", " << function_args << ")\n";
//pname = lcase (pname);
// SENSITIVE?
if (pname == "getobjectname") {
if (function_args.size() == 0)
return bad_arg_count(pname);
//return get_obj_name (function_args);
Common::Array<String> where;
for (uint i = 1; i < function_args.size(); i ++)
where.push_back(function_args[i]);
if (where.size() == 0) {
where.push_back(state.location);
where.push_back("inventory");
}
bool is_internal = false;
return get_obj_name(function_args[0], where, is_internal);
}
// SENSITIVE?
else if (pname == "loadmethod") {
/* TODO TODO */
return "normal";
// "loaded" on restore from qsg
}
// SENSITIVE?
else if (pname == "locationof") {
if (function_args.size() != 1)
return bad_arg_count(pname);
return get_obj_parent(function_args[0]);
}
// SENSITIVE?
else if (pname == "objectproperty") {
if (function_args.size() != 2)
return bad_arg_count(pname);
String rv;
get_obj_property(function_args[0], function_args[1], rv);
return rv;
}
// SENSITIVE?
else if (pname == "timerstate") {
if (function_args.size() != 1)
return bad_arg_count(pname);
//String timername = lcase (function_args[0]);
String timername = function_args[0];
for (uint i = 0; i < state.timers.size(); i ++)
if (state.timers[i].name == timername)
return state.timers[i].is_running ? "1" : "0";
return "!";
}
// SENSITIVE?
else if (pname == "displayname") {
if (function_args.size() != 1)
return bad_arg_count(pname);
return displayed_name(function_args[0]);
}
// SENSITIVE?
else if (pname == "capfirst") {
if (function_args.size() != 1)
return bad_arg_count(pname);
return pcase(function_args[0]);
}
// SENSITIVE?
else if (pname == "instr") {
/* TODO What if it's not present? */
if (function_args.size() != 2 && function_args.size() != 3)
return bad_arg_count(pname);
int rv;
if (function_args.size() == 2)
rv = function_args[0].find(function_args[1]);
else
rv = function_args[1].find(function_args[2],
parse_int(function_args[0]));
if (rv == -1)
return string_int(rv); // TODO What goes here?
else
return string_int(rv);
}
// SENSITIVE?
else if (pname == "lcase") {
if (function_args.size() != 1)
return bad_arg_count(pname);
String rv = function_args[0];
for (uint i = 0; i < rv.size(); i ++)
rv[i] = tolower(rv[i]);
return rv;
}
// SENSITIVE?
else if (pname == "left") {
if (function_args.size() != 2)
return bad_arg_count(pname);
uint i = parse_int(function_args[1]);
if (i > function_args[0].length())
return function_args[0];
else
return function_args[0].substr(0, i);
}
// SENSITIVE?
else if (pname == "lengthof") {
if (function_args.size() != 1)
return bad_arg_count(pname);
return string_int(function_args[0].length());
}
// SENSITIVE?
else if (pname == "mid") {
if (function_args.size() != 3)
return bad_arg_count(pname);
uint start = parse_int(function_args[1]),
len = parse_int(function_args[2]);
if (start > function_args[0].length())
return "";
if (start + len > function_args[0].length())
return function_args[0].substr(start);
return function_args[0].substr(start, len);
}
// SENSITIVE?
else if (pname == "right") {
if (function_args.size() != 2)
return bad_arg_count(pname);
uint size = parse_int(function_args[1]);
if (size > function_args[0].length())
return function_args[0];
return function_args[0].substr(function_args[0].length() - size);
} else if (pname == "ubound") {
if (function_args.size() != 1)
return bad_arg_count(pname);
/* TODO TODO */
return "";
}
// SENSITIVE?
else if (pname == "ucase") {
if (function_args.size() != 1)
return bad_arg_count(pname);
String rv = function_args[0];
for (uint i = 0; i < rv.length(); i ++)
rv[i] = toupper(rv[i]);
return rv;
}
// SENSITIVE?
else if (pname == "rand") {
if (function_args.size() != 2)
return bad_arg_count(pname);
uint lower = parse_int(function_args[0]),
upper = parse_int(function_args[1]);
// TODO: change this to use the high order bits of the random # instead
return string_int(lower + (g_vm->getRandomNumber(0x7fffff) % (upper + 1 - lower)));
}
// SENSITIVE?
else if (pname == "speechenabled") {
if (function_args.size() != 0)
return bad_arg_count(pname);
return "0";
/* TODO: return 1 if speech is enabled */
}
// SENSITIVE?
else if (pname == "symbol") {
if (function_args.size() != 1)
return bad_arg_count(pname);
// SENSITIVE?
if (function_args[0] == "gt")
return ">";
// SENSITIVE?
else if (function_args[0] == "lt")
return "<";
gi->debug_print("Bad symbol argument: " + function_args[0]);
return "";
}
// SENSITIVE?
else if (pname == "numberparameters")
return string_int(function_args.size());
// SENSITIVE?
else if (pname == "thisobject")
return this_object;
/* disconnectedby, id, name, removeformatting */
String rv = "";
for (uint i = 0; i < gf.size("function"); i ++)
if (ci_equal(gf.block("function", i).name, pname)) {
const GeasBlock &proc = gf.block("function", i);
cerr << "Running function " << proc << endl;
for (uint j = 0; j < proc.data.size(); j ++) {
cerr << " Running line #" << j << ": " << proc.data[j] << endl;
run_script(proc.data[j], rv);
}
return rv;
}
gi->debug_print("No function " + pname + " found.");
return "";
}
v2string geas_implementation::get_inventory() {
return get_room_contents("inventory");
}
v2string geas_implementation::get_room_contents() {
return get_room_contents(state.location);
}
v2string geas_implementation::get_room_contents(String room) {
v2string rv;
String objname;
for (uint i = 0; i < state.objs.size(); i ++)
if (state.objs[i].parent == room) {
objname = state.objs[i].name;
if (!has_obj_property(objname, "invisible") &&
!has_obj_property(objname, "hidden")) {
vstring tmp;
String print_name, temp_str;
if (!get_obj_property(objname, "alias", print_name))
print_name = objname;
/*
if (get_obj_property (objname, "prefix", temp_str))
print_name = temp_str + " " + print_name;
if (get_obj_property (objname, "suffix", temp_str))
print_name = print_name + " " + temp_str;
*/
tmp.push_back(print_name);
String otype;
if (!get_obj_property(objname, "displaytype", otype))
otype = "object";
tmp.push_back(otype);
rv.push_back(tmp);
}
}
return rv;
}
vstring geas_implementation::get_status_vars() {
vstring rv;
String tok, line;
uint c1, c2;
for (uint i = 0; i < gf.size("variable"); i ++) {
const GeasBlock &gb = gf.block("variable", i);
bool nozero = false;
String disp;
bool is_numeric = true;
cerr << "g_s_v: " << gb << endl;
for (uint j = 0; j < gb.data.size(); j ++) {
line = gb.data[j];
cerr << " g_s_v: " << line << endl;
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "display") {
tok = next_token(line, c1, c2);
// SENSITIVE?
if (tok == "nozero") {
nozero = true;
tok = next_token(line, c1, c2);
}
if (!is_param(tok))
gi->debug_print("Expected param after display: " + line);
else
disp = tok;
}
// SENSITIVE?
else if (tok == "type") {
tok = next_token(line, c1, c2);
// SENSITIVE?
if (tok == "String")
is_numeric = false;
}
}
cerr << " g_s_v, block 2, tok == '" << tok << "'" << endl;
if (!(is_numeric && nozero && get_ivar(gb.name) == 0) && disp != "") {
disp = param_contents(disp);
String outval = "";
for (uint j = 0; j < disp.length(); j ++)
if (disp[j] == '!') {
if (is_numeric)
outval = outval + string_int(get_ivar(gb.name));
//outval = outval + string_int (get_ivar (gb.lname));
else
outval = outval + get_svar(gb.name);
//outval = outval + get_svar (gb.lname);
} else if (disp[j] == '*') {
uint k;
for (k = j + 1; k < disp.length() && disp[k] != '*'; k ++)
;
//if (!is_numeric || get_ivar (gb.lname) != 1)
if (!is_numeric || get_ivar(gb.name) != 1)
outval = outval + disp.substr(j + 1, k - j - 1);
j = k;
} else
outval = outval + disp[j];
rv.push_back(eval_string(outval));
}
}
return rv;
}
Common::Array<bool> geas_implementation::get_valid_exits() {
Common::Array<bool> rv;
cerr << "Getting valid exits\n";
rv.push_back(exit_dest(state.location, "northwest") != "");
rv.push_back(exit_dest(state.location, "north") != "");
rv.push_back(exit_dest(state.location, "northeast") != "");
rv.push_back(exit_dest(state.location, "west") != "");
rv.push_back(exit_dest(state.location, "out") != "");
rv.push_back(exit_dest(state.location, "east") != "");
rv.push_back(exit_dest(state.location, "southwest") != "");
rv.push_back(exit_dest(state.location, "south") != "");
rv.push_back(exit_dest(state.location, "southeast") != "");
rv.push_back(exit_dest(state.location, "up") != "");
rv.push_back(exit_dest(state.location, "down") != "");
cerr << "Done getting valid exits\n";
return rv;
}
void geas_implementation::print_eval_p(String s) {
print_formatted(pcase(eval_string(s)));
}
void geas_implementation::print_eval(String s) {
print_formatted(eval_string(s));
}
String geas_implementation::eval_string(String s) {
String rv;
uint i, j;
bool do_print = (s.find('$') != -1);
if (do_print) cerr << "eval_string (" << s << ")\n";
for (i = 0; i < s.length(); i ++) {
//if (do_print) cerr << "e_s: i == " << i << ", s[i] == '" << s[i] << "'\n";
if (i + 1 < s.length() && s[i] == '#' && s[i + 1] == '@') {
for (j = i + 1; j < s.length() && s[j] != '#'; j ++)
;
if (j == s.length()) {
gi->debug_print("eval_string: Unmatched hash in " + s);
break;
}
//cerr << "dereferencing " + s.substr (i+2, j-i-2) << endl;
rv = rv + displayed_name(get_svar(s.substr(i + 2, j - i - 2)));
i = j;
} else if (s[i] == '#') {
for (j = i + 1; j < s.length() && s[j] != '#'; j ++)
;
if (j == s.length()) {
gi->debug_print("eval_string: Unmatched hash in " + s);
break;
}
uint k;
for (k = i + 1; k < j && s[k] != ':'; k ++)
;
if (k == j && j == i + 1)
rv += "#";
else if (k == j)
rv += get_svar(s.substr(i + 1, j - i - 1));
else {
String propname = s.substr(k + 1, j - k - 1);
if (s[i + 1] == '(') {
if (s[k - 1] != ')') {
gi->debug_print("e_s: Missing paren in '" +
s.substr(i, j - i) + "' of '" + s + "'");
break;
}
String objvar = s.substr(i + 2, k - i - 3);
String objname = get_svar(objvar);
/*
cerr << "e_s: Getting prop [(" << objvar << ")] --> ["
<< objname
<< "]:[" << propname << "] for " << s << endl;
*/
String tmp;
if (get_obj_property(objname, propname, tmp))
rv += tmp;
else
gi->debug_print("e_s: Evaluating nonexistent object prop "
"{" + objname + "}:{" + propname + "}");
} else {
String objname = s.substr(i + 1, k - i - 1);
/*
cerr << "e_s: Getting prop [" << objname << "]:["
<< propname << "] for " << s << endl;
*/
String tmp;
if (get_obj_property(objname, propname, tmp))
rv += tmp;
else
gi->debug_print("e_s: Evaluating nonexistent var " + objname);
}
}
i = j;
} else if (s[i] == '%') {
for (j = i + 1; j < s.length() && s[j] != '%'; j ++)
;
if (j == s.length()) {
gi->debug_print("e_s: Unmatched %s in " + s);
break;
}
//cerr << "e_s: Getting ivar [" << s.substr (i+1, j-i-1) << "] for " << s << endl;
if (j == i + 1)
rv += "%";
else
rv += string_int(get_ivar(s.substr(i + 1, j - i - 1)));
i = j;
} else if (s[i] == '$') {
j = s.find('$', i + 1);
/*
for (j = i + 1; j < s.length() && s[j] != '$'; j ++)
{
cerr << " In searching for partner, j == " << j
<< ", s[j] == '" << s[j] << "', (s[j] == '$') == "
<< ((s[j] == '$') ? "true" : "false") << "\n";
}
*/
//if (j == rv.size())
if (j == (uint)-1) {
gi->debug_print("Unmatched $s in " + s);
return rv + s.substr(i);
}
String tmp1 = s.substr(i + 1, j - i - 1);
cerr << "e_s: first substr was '" << tmp1 << "'\n";
String tmp = eval_string(tmp1);
//String tmp = eval_string (s.substr (i + 1, j - i - 2));
//cerr << "Taking substring of '" + s + "': '" + tmp + "'\n";
cerr << "e_s: eval substr " + s + "': '" + tmp + "'\n";
String func_eval;
int paren_open, paren_close;
if ((paren_open = tmp.find('(')) == -1)
func_eval = run_function(tmp);
else {
paren_close = tmp.find(')', paren_open);
if (paren_close == -1)
gi->debug_print("No matching right paren in " + tmp);
else {
String f_name = tmp.substr(0, paren_open);
String f_args = tmp.substr(paren_open + 1,
paren_close - paren_open - 1);
func_eval = run_function(f_name, split_f_args(f_args));
}
}
rv = rv + func_eval;
i = j;
} else
rv += s[i];
}
//cerr << "eval_string (" << s << ") -> <" << rv << ">\n";
return rv;
}
void geas_implementation::tick_timers() {
if (!state.running)
return;
//cerr << "tick_timers()\n";
for (uint i = 0; i < state.timers.size(); i ++) {
TimerRecord &tr = state.timers[i];
//cerr << " Examining " << tr << "\n";
if (tr.is_running) {
//cerr << " Advancing " << tr.name << ": " << tr.timeleft
// << " / " << tr.interval << "\n";
if (tr.timeleft != 0)
tr.timeleft --;
else {
tr.is_running = false;
tr.timeleft = tr.interval;
const GeasBlock *gb = gf.find_by_name("timer", tr.name);
if (gb != NULL) {
//cout << "Running it!\n";
String tok, line;
uint c1, c2;
for (uint j = 0; j < gb->data.size(); j ++) {
line = gb->data[j];
tok = first_token(line, c1, c2);
// SENSITIVE?
if (tok == "action") {
run_script(line.substr(c2));
break;
}
}
}
}
}
}
}
/***********************************
* *
* *
* *
* GeasInterface related functions *
* *
* *
* *
***********************************/
GeasResult GeasInterface::print_formatted(String s, bool with_newline) {
unsigned int i, j;
cerr << "print_formatted (" << s << ", " << with_newline << ")" << endl;
for (i = 0; i < s.length(); i ++) {
//std::cerr << "i == " << i << std::endl;
if (s[i] == '|') {
// changed indicated whether cur_style has been changed
// and update_style should be called at the end.
// it is true unless cleared (by |n or |w).
bool changed = true;
j = i;
i ++;
if (i == s.length())
continue;
// Are the | codes case-sensitive?
switch (s[i]) {
case 'u':
cur_style.is_underlined = true;
break;
case 'i':
cur_style.is_italic = true;
break;
case 'b':
cur_style.is_bold = true;
break;
case 'c':
i ++;
if (i == s.length()) {
clear_screen();
break;
}
switch (s[i]) {
case 'y':
cur_style.color = "#ffff00";
break;
case 'g':
cur_style.color = "#00ff00";
break;
case 'l':
cur_style.color = "#0000ff";
break;
case 'r':
cur_style.color = "#ff0000";
break;
case 'b':
cur_style.color = "";
break;
default:
clear_screen();
--i;
}
break;
case 's': {
i ++;
if (i == s.length() || !(s[i] >= '0' && s[i] <= '9'))
continue;
i ++;
if (i == s.length() || !(s[i] >= '0' && s[i] <= '9'))
continue;
int newsize = parse_int(s.substr(j, i - j));
if (newsize > 0)
cur_style.size = newsize;
else
cur_style.size = default_size;
}
break;
case 'j':
i ++;
if (i == s.length() ||
!(s[i] == 'l' || s[i] == 'c' || s[i] == 'r'))
continue;
if (s[i] == 'l') cur_style.justify = JUSTIFY_LEFT;
else if (s[i] == 'r') cur_style.justify = JUSTIFY_RIGHT;
else if (s[i] == 'c') cur_style.justify = JUSTIFY_CENTER;
break;
case 'n':
print_newline();
changed = false;
break;
case 'w':
wait_keypress("");
changed = false;
break;
case 'x':
i ++;
if (s[i] == 'b')
cur_style.is_bold = false;
else if (s[i] == 'u')
cur_style.is_underlined = false;
else if (s[i] == 'i')
cur_style.is_italic = false;
else if (s[i] == 'n' && i + 1 == s.length())
changed = with_newline = false;
break;
default:
cerr << "p_f: Fallthrough " << s[i] << endl;
changed = false;
}
if (changed)
update_style();
} else {
for (j = i; i != s.length() && s[i] != '|'; i ++)
;
print_normal(s.substr(j, i - j));
if (i != s.length() && s[i] == '|')
-- i;
}
}
if (with_newline)
print_newline();
return r_success;
}
void GeasInterface::set_default_font_size(String size) {
default_size = parse_int(size);
}
void GeasInterface::set_default_font(String font) {
default_font = font;
}
bool GeasInterface::choose_yes_no(String question) {
Common::Array<String> v;
v.push_back("Yes");
v.push_back("No");
return (make_choice(question, v) == 0);
}
} // End of namespace Quest
} // End of namespace Glk