scummvm/engines/glk/agt/debugcmd.cpp
2022-10-23 22:46:19 +02:00

874 lines
19 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "glk/agt/agility.h"
#include "glk/agt/interp.h"
#include "glk/agt/exec.h"
namespace Glk {
namespace AGT {
static void d_moveobj(int obj, int dest)
/* 1=the player, -1=unknown: ask */
{
if (obj == -1) {
writestr("Which object? ");
obj = read_number();
if (obj != 1 && !tnoun(obj) && !tcreat(obj)) {
writeln("Invalid object");
return;
}
}
if (dest == -1) {
writestr("To where? ");
dest = read_number();
if (dest != 1 && dest != 0 && !tnoun(dest) && !tcreat(dest) && !troom(dest)) {
writeln("Invalid object");
return;
}
}
if (obj != 1)
it_move(obj, dest);
else {
if (!troom(dest)) {
writeln("Player can only be moved to a room");
return;
}
goto_room(dest - first_room);
}
}
static int print_objid(int obj) {
char buff[10];
char *s;
int n;
Common::sprintf_s(buff, "%4d: ", obj);
writestr(buff);
s = objname(obj);
for (n = 0; s[n] != 0; n++)
if (s[n] <= 8 || (uchar)s[n] == 0xFF) s[n] = ' '; /* Strip out format codes */
writestr(s);
n = strlen(s);
rfree(s);
return n;
}
static void d_listroom() {
int i;
writeln(" ROOM");
writeln(" ------");
writeln("");
for (i = 0; i <= maxroom - first_room; i++) {
print_objid(i + first_room);
writeln("");
}
}
#define SEPLENG 27 /* Width between beginning of object column and
location column */
static void d_listnoun() {
int i;
int len;
writestr(" NOUN ");
padout(SEPLENG - 6);
writeln(" LOCATION ");
writestr(" ------");
padout(SEPLENG - 6);
writeln(" ----------");
writeln("");
len = SEPLENG - print_objid(1);
padout(len);
writestr("[");
print_objid(loc);
writeln("]");
nounloop(i) {
len = print_objid(i + first_noun);
len = SEPLENG - len;
if (len > 0) padout(len);
writestr("[");
print_objid(noun[i].location);
writeln("]");
}
}
static void d_listcreat() {
int i;
int len;
writestr(" CREATURE ");
padout(SEPLENG - 11);
writeln(" LOCATION ");
writestr(" ----------");
padout(SEPLENG - 11);
writeln(" ----------");
writeln("");
creatloop(i) {
len = print_objid(i + first_creat);
len = SEPLENG - len;
if (len > 0) padout(len);
writestr(" [");
print_objid(creature[i].location);
writeln("]");
}
}
static void writetbl(const char *s, int width)
/* This writes out s and then prints out any additional spaces needed
to make the output string *width* wide. */
{
writestr(s);
width = width - strlen(s);
if (width > 0) padout(width);
}
static void var_edit(int vtype)
/* vtype=0 for variable, 1 for counter, 2 for flag */
{
long n;
int i;
int imax;
char sbuff[30];
switch (vtype) {
case 0:
imax = VAR_NUM;
break;
case 1:
imax = CNT_NUM;
break;
case 2:
imax = FLAG_NUM;
break;
default:
writeln("INTERNAL ERROR: Invalid vtype.");
return;
}
for (;;) {
agt_clrscr();
writeln("");
switch (vtype) {
case 0:
writeln("Variables");
break;
case 1:
writeln("Counters (-1 means the counter is off)");
break;
case 2:
writeln("Flags ( f=false [OFF] and t=true [ON] )");
break;
default:
break;
}
writeln("");
for (i = 0; i <= imax; i++) {
switch (vtype) {
case 0:
Common::sprintf_s(sbuff, "[Var%3d]=%4ld", i, (long)agt_var[i]);
break;
case 1:
Common::sprintf_s(sbuff, "[Cnt%3d]=%4ld", i, (long)agt_counter[i]);
break;
case 2:
Common::sprintf_s(sbuff, "%3d%c", i, flag[i] ? 't' : 'f');
break;
default:
break;
}
writetbl(sbuff, (vtype == 2) ? 5 : 20);
}
writeln("");
writeln("");
for (;;) {
switch (vtype) {
case 0:
writestr("Variable to change");
break;
case 1:
writestr("Counter to change");
break;
case 2:
writestr("Flag to toggle");
break;
default:
break;
}
writestr(" (-1 to quit): ");
i = read_number();
if (i < 0) return;
if (i <= imax) {
if (vtype != 2) {
if (vtype == 0)
Common::sprintf_s(sbuff, "[Var%d]=%ld", i, (long)agt_var[i]);
else Common::sprintf_s(sbuff, "[Cnt%d]=%ld (-1 means it's off)",
i, (long)agt_counter[i]);
writestr(sbuff);
writestr("; new value = ");
n = read_number();
if (vtype == 0)
agt_var[i] = n;
else if (n < -1 || n > (((long)1) << 15) - 1)
writeln("Invalid value for a counter.");
else agt_counter[i] = n;
} else flag[i] = !flag[i];
break;
} else
writeln("Invalid index.");
}
}
}
/* Routines to edit user strings */
static void edit_str() {
int i, j;
char buff[10];
char *tmpstr;
if (MAX_USTR == 0 || userstr == nullptr) {
writeln("This game doesn't contain any user strings");
return;
}
for (;;) {
agt_clrscr();
writeln("User Definable Strings");
writeln("");
for (i = 0; i < MAX_USTR; i++) {
Common::sprintf_s(buff, "%2d:", i + 1);
writestr(buff);
writeln(userstr[i]);
}
writestr(" (0 to quit): ");
i = read_number();
if (i == 0) return;
if (i > 0 && i <= MAX_USTR) {
writeln("Enter new string:");
tmpstr = agt_readline(3);
j = strlen(tmpstr) - 1;
if (j > 0 && tmpstr[j] == '\n') tmpstr[j] = 0;
strncpy(userstr[i - 1], tmpstr, 80);
} else writeln("Invalid string number");
}
}
static uchar attrcol; /* Determines which column the attribute is put in */
static uchar attrwidth; /* Number of attribute columns */
static void next_col() {
if (++attrcol == attrwidth) {
writeln("");
attrcol = 0;
} else
padout(10);
}
static void writeattr(const char *attrname, rbool attrval) {
writestr(attrname);
padout(15 - strlen(attrname));
if (attrval) writestr("yes");
else writestr("no ");
next_col();
}
static void writegender(const char *gendername, uchar genderval) {
writestr(gendername);
padout(15 - strlen(gendername) - 3);
switch (genderval) {
case 2:
writestr("Male ");
break;
case 1:
writestr("Female");
break;
case 0:
writestr("Thing");
break;
default:
break;
}
next_col();
}
static void writeprop(const char *propname, int obj) {
writestr(propname);
writestr(" [");
print_objid(obj);
writeln("]");
}
static int writedir(int index, int dir, int obj) {
char sbuff[40];
Common::sprintf_s(sbuff, "%2d.%-2s %d", index, exitname[dir], obj);
writestr(sbuff);
return strlen(sbuff);
}
void writenum(const char *propname, int n) {
char sbuff[20];
writestr(propname);
Common::sprintf_s(sbuff, "%4d", n);
writeln(sbuff);
}
static void writeflags(const char *flagname, int32 flags) {
int i;
char sbuff[5];
writestr(flagname);
for (i = 0; i < 32; i++) {
if (flags & 1) {
Common::sprintf_s(sbuff, "%2d ", i);
writestr(sbuff);
} else
writestr(" ");
flags >>= 1;
if (i % 12 == 11) {
writeln("");
padout(strlen(flagname));
}
}
writeln("");
}
static void readflags(int32 *flags) {
long n;
writestr("Room flag to toggle (0-31)? ");
n = read_number();
if (n <= 31 && n >= 0)
*flags ^= (((long)1) << n);
}
static long readval(const char *prompt, int type) {
long val;
for (;;) {
writestr(prompt);
writestr(" ");
val = read_number();
if (argvalid(type, val)) return val;
writeln("Invalid value.");
}
}
static uchar readgender() {
char c;
writestr("Gender (M/F/N): ");
for (;;) {
c = tolower(agt_getchar());
switch (c) {
case 'm':
return 2;
case 'w':
case 'f':
return 1;
case 'n':
case 't':
return 0;
default: ;/* Do nothing */
}
}
}
static void edit_objattr(int obj) {
int i, k, kprop, n;
long v;
for (;;) {
k = 1;
agt_clrscr();
print_objid(obj);
writeln("");
if (oflag_cnt > 0) {
writeln("ObjFlags:");
for (i = 0; i < oflag_cnt; i++)
if (have_objattr(0, obj, i)) {
v = op_objflag(2, obj, i);
rprintf("%2d. ObjProp%2d:%c %-40s\n", k++, i, (v ? '+' : '-'),
get_objattr_str(AGT_OBJFLAG, i, v));
}
writeln("");
}
kprop = k;
if (oprop_cnt > 0) {
writeln("ObjProps:");
for (i = 0; i < oprop_cnt; i++)
if (have_objattr(1, obj, i)) {
v = op_objprop(2, obj, i, 0);
rprintf("%2d. ObjFlag%2d: [%3ld] %-40s\n", k++, i, v,
get_objattr_str(AGT_OBJPROP, i, v));
}
writeln("");
}
writestr("Field to change (0 to return to main view)? ");
n = read_number();
if (n == 0) return;
if (n < 1 || n >= k) continue;
k = 0;
if (n < kprop) { /* Attribute */
for (i = 0; i < oflag_cnt; i++)
if (have_objattr(0, obj, i))
if (n == ++k) break;
if (n == k && have_objattr(0, obj, i))
op_objflag(3, obj, i); /* Toggle it */
} else { /* Property */
for (i = 0; i < oprop_cnt; i++)
if (have_objattr(1, obj, i))
if (n == ++k) break;
if (n == k && have_objattr(1, obj, i))
op_objprop(1, obj, i, readval("New value:", AGT_NUM));
}
}
}
static void room_edit(int i) {
int n, j;
for (;;) {
agt_clrscr();
writestr("ROOM ");
print_objid(i + first_room);
writeln("");
writeln("");
attrcol = 0;
attrwidth = 2;
writeattr("1.*WinGame:", room[i].win);
writeattr("4. Seen:", room[i].seen);
writeattr("2.*EndGame:", room[i].end);
writeattr("5. LockedDoor:", room[i].locked_door);
writeattr("3.*Die:", room[i].killplayer);
writeln("");
writeln("");
writeprop("6.*Key =", room[i].key);
writeprop("7. Light =", room[i].light);
writenum("8. Points =", room[i].points);
writeprop("9. Class = ", room[i].oclass);
writeln("");
writeln("EXITS:");
for (j = 0; j < 12; j++) {
n = writedir(j + 10, j, room[i].path[j]);
if (j % 4 == 3) writeln("");
else padout(15 - n);
}
writeprop("22. SPECIAL:", room[i].path[12]);
writeflags("23. Room Flags:", room[i].flag_noun_bits);
writeln("24. Object properties and attributes.");
writeln("");
writeln("(Fields marked with an * are not saved or restored.)");
/* writeln(""); */
writestr("Field to change (0 to exit)? ");
n = read_number();
if (n == 0) return;
switch (n) {
case 1:
room[i].win = !room[i].win;
break;
case 2:
room[i].end = !room[i].end;
break;
case 3:
room[i].killplayer = !room[i].killplayer;
break;
case 4:
room[i].seen = !room[i].seen;
break;
case 5:
room[i].locked_door = !room[i].locked_door;
break;
case 6:
room[i].key = readval("Key = ", AGT_ITEM | AGT_NONE);
break;
case 7:
room[i].light = readval("Light = ", AGT_ITEM | AGT_NONE | AGT_SELF);
break;
case 8:
room[i].points = readval("Points = ", AGT_NUM);
break;
case 9:
room[i].oclass = readval("Class = ", AGT_ROOM | AGT_NONE);
break;
case 22:
room[i].path[12] = readval("SPECIAL: ", AGT_NUM);
break;
case 23:
readflags(&room[i].flag_noun_bits);
break;
case 24:
edit_objattr(i + first_room);
break;
default:
if (n >= 10 && n < 22) { /* Direction */
room[i].path[n - 10] = readval(exitname[n - 10], AGT_NUM);
} else writeln("Invalid field");
}
}
}
#define tog(x) {x=!x;break;}
static void noun_edit(int i) {
int n;
for (;;) {
agt_clrscr();
/* writeln("");*/
writestr("NOUN ");
print_objid(i + first_noun);
/* writeln("");*/
/* writeln("");*/
writeprop(" Location=", noun[i].location);
writeln("");
attrcol = 0;
attrwidth = 3;
writeattr(" 1.*Pushable:", noun[i].pushable);
writeattr(" 8.*Lockable:", noun[i].lockable);
writeattr("15.*Drinkable:", noun[i].drinkable);
writeattr(" 2.*Pullable:", noun[i].pullable);
writeattr(" 9.*Light:", noun[i].light);
writeattr("16.*Poisonous:", noun[i].poisonous);
writeattr(" 3.*Turnable:", noun[i].turnable);
writeattr("10.*Plural:", noun[i].plural);
writeattr("17. Open:", noun[i].open);
writeattr(" 4.*Playable:", noun[i].playable);
writeattr("11. Movable:", noun[i].movable);
writeattr("18. Locked:", noun[i].locked);
writeattr(" 5.*Readable:", noun[i].readable);
writeattr("12.*Shootable:", noun[i].shootable);
writeattr("19.*Win Game:", noun[i].win);
writeattr(" 6.*Wearable:", noun[i].wearable);
writeattr("13. On:", noun[i].on);
writeattr("20.*Global:", noun[i].isglobal);
writeattr(" 7.*Closable:", noun[i].closable);
writeattr("14.*Edible:", noun[i].edible);
writeattr("21.*Proper:", noun[i].proper);
writeln("");
writenum("22. Shots =", noun[i].num_shots);
writenum("23. Points =", noun[i].points);
writenum("24. Weight =", noun[i].weight);
writenum("25. Size =", noun[i].size);
writeprop("26.*Key =", noun[i].key);
writeprop("27. Class =", noun[i].oclass);
writenum("28. Flag =", noun[i].flagnum);
writeln("");
/* writeln(""); */
writeln("29. Object properties and attributes.");
writeln("");
writeln("(Fields marked with an * are not saved or restored.)");
writestr("Field to change (0 to exit)? ");
n = read_number();
if (n == 0) return;
switch (n) {
case 1:
tog(noun[i].pushable); /* tog() macro includes break */
case 2:
tog(noun[i].pullable);
case 3:
tog(noun[i].turnable);
case 4:
tog(noun[i].playable);
case 5:
tog(noun[i].readable);
case 6:
tog(noun[i].wearable);
case 7:
tog(noun[i].closable);
case 8:
tog(noun[i].lockable);
case 9:
tog(noun[i].light);
case 10:
tog(noun[i].plural);
case 11:
tog(noun[i].movable);
case 12:
tog(noun[i].shootable);
case 13:
tog(noun[i].on);
case 14:
tog(noun[i].edible);
case 15:
tog(noun[i].drinkable);
case 16:
tog(noun[i].poisonous);
case 17:
tog(noun[i].open);
case 18:
tog(noun[i].locked);
case 19:
tog(noun[i].win);
case 20:
tog(noun[i].isglobal);
case 21:
tog(noun[i].proper);
case 22:
noun[i].num_shots = readval("Shots =", AGT_NUM);
break;
case 23:
noun[i].points = readval("Points =", AGT_NUM);
break;
case 24:
noun[i].weight = readval("Weight =", AGT_NUM);
break;
case 25:
noun[i].size = readval("Size =", AGT_NUM);
break;
case 26:
noun[i].key = readval("Key =", AGT_ITEM | AGT_NONE);
break;
case 27:
noun[i].oclass = readval("Class =", AGT_ITEM | AGT_NONE);
break;
case 28:
noun[i].flagnum = readval("Flag Number=", AGT_ROOMFLAG);
break;
case 29:
edit_objattr(i + first_noun);
break;
default:
writeln("Invalid field");
}
}
}
static void creat_edit(int i) {
int n;
for (;;) {
agt_clrscr();
writestr("CREATURE ");
print_objid(i + first_creat);
writeln("");
writeln("");
writeprop("Location =", creature[i].location);
writeln("");
attrcol = 0;
attrwidth = 2;
writeattr(" 1. Hostile:", creature[i].hostile);
writeattr(" 4. Global:", creature[i].isglobal);
writeattr(" 2. Grp member:", creature[i].groupmemb);
writeattr(" 5.*Proper:", creature[i].proper);
writegender(" 3.*Gender:", creature[i].gender);
writeln("");
writeln("");
writeprop(" 6.*Weapon = ", creature[i].weapon);
writenum(" 7. Points = ", creature[i].points);
writenum(" 8.*Attack Threshold = ", creature[i].threshold);
writenum(" 9. Attack counter = ", creature[i].counter);
writenum("10.*Attack Time Limit = ", creature[i].timethresh);
writenum("11. Attack timer = ", creature[i].timecounter);
writeprop("12. Class = ", creature[i].oclass);
writenum("13. Flag = ", creature[i].flagnum);
writeln("");
writeln("14. Object properties and attributes.");
writeln("");
writeln("(Fields marked with an * are not saved or restored.)");
writeln("");
writestr("Field to change (0 to exit)? ");
n = read_number();
if (n == 0) return;
switch (n) {
case 1:
tog(creature[i].hostile);
case 2:
tog(creature[i].groupmemb);
case 3:
tog(creature[i].isglobal);
case 4:
tog(creature[i].proper);
case 5:
creature[i].gender = readgender();
break;
case 6:
creature[i].weapon = readval("Weapon =", AGT_ITEM | AGT_NONE);
break;
case 7:
creature[i].points = readval("Points =", AGT_NUM);
break;
case 8:
creature[i].threshold = readval("Threshold =", AGT_NUM);
break;
case 9:
creature[i].counter = readval("Attack counter =", AGT_NUM);
break;
case 10:
creature[i].timethresh = readval("Time limit =", AGT_NUM);
break;
case 11:
creature[i].timecounter = readval("Timer =", AGT_NUM);
break;
case 12:
creature[i].oclass = readval("Class =", AGT_ITEM | AGT_NONE);
break;
case 13:
noun[i].flagnum = readval("Flag Number=", AGT_ROOMFLAG);
break;
case 14:
edit_objattr(i + first_creat);
break;
default:
writeln("Invalid field");
}
}
}
#undef tog
static void obj_edit() {
int n;
for (;;) {
writeln("");
do {
writestr("Enter object number (0 to exit)? ");
n = read_number();
if (n <= 0) return;
} while (!troom(n) && !tnoun(n) && !tcreat(n));
if (troom(n)) room_edit(n - first_room);
else if (tnoun(n)) noun_edit(n - first_noun);
else if (tcreat(n)) creat_edit(n - first_creat);
else writeln("[Not yet implemented]");
}
}
static const char *yesnostr[] = { "No", "Yes" };
static void set_debug_options() {
char buff[80];
int n;
for (;;) {
agt_clrscr();
writeln("DEBUGGING OPTIONS:");
writeln("");
Common::sprintf_s(buff, " 1. Trace metacommands: %s", yesnostr[DEBUG_AGT_CMD]);
writeln(buff);
Common::sprintf_s(buff, " 2. Trace ANY metacommands: %s", yesnostr[debug_any]);
writeln(buff);
Common::sprintf_s(buff, " 3. Trace during disambiguation: %s",
yesnostr[debug_disambig]);
writeln(buff);
writeln("");
writeln("(<2> and <3> are ignored if <1> is not set; option <1> can"
" also be changed from the main debugging menu)");
writeln("");
writestr("Option to toggle (0 to exit): ");
n = read_number();
switch (n) {
case 0:
return;
case 1:
DEBUG_AGT_CMD = !DEBUG_AGT_CMD;
break;
case 2:
debug_any = !debug_any;
break;
case 3:
debug_disambig = !debug_disambig;
break;
default:
writeln("Not a valid option");
}
}
}
void get_debugcmd() {
int n;
for (;;) {
writeln("DEBUGGING COMMANDS");
writeln("");
writeln("1. Move player 8. List Rooms");
writeln("2. Get Noun 9. List Nouns");
writeln("3. Move object 10. List Creatures");
writeln("4. View/Edit object 11. List/Set Flags");
writeln("5. Toggle Trace 12. List/Set Variables");
writeln("6. Set Debug Options 13. List/Set Counters");
writeln("7. Edit User Strings");
writeln("");
writestr("Enter choice (0 to exit): ");
n = read_number();
switch (n) {
case -1:
case 0:
return;
case 1:
d_moveobj(1, -1);
break;
case 2:
d_moveobj(-1, 1);
break;
case 3:
d_moveobj(-1, -1);
break;
case 4:
obj_edit();
break;
case 5:
DEBUG_AGT_CMD = !DEBUG_AGT_CMD;
break;
case 6:
set_debug_options();
break;
case 7:
edit_str();
break;
case 8:
d_listroom();
break;
case 9:
d_listnoun();
break;
case 10:
d_listcreat();
break;
case 11:
var_edit(2);
break;
case 12:
var_edit(0);
break;
case 13:
var_edit(1);
break;
default:
writeln("Not a valid option");
}
writeln("");
};
}
} // End of namespace AGT
} // End of namespace Glk