mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-12-27 02:20:36 +00:00
10d0ed8900
Apple Xcode has started giving `-Wdeprecated-declarations` warnings for `sprintf`, and suggesting that people migrate to `snprintf` instead. This is silly, but the warnings are spam and need to be silenced somehow. Migrating to `snprintf` and/or `hr::format` is the path of least resistance.
906 lines
27 KiB
C++
906 lines
27 KiB
C++
// Hyperbolic Rogue -- debugging routines
|
|
// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
|
|
|
|
/** \file debug.cpp
|
|
* \brief Debugging and cheating
|
|
*/
|
|
|
|
#include "hyper.h"
|
|
namespace hr {
|
|
|
|
EX int steplimit = 0;
|
|
EX int cstep;
|
|
EX bool buggyGeneration = false;
|
|
EX bool debug_cellnames = false;
|
|
|
|
EX vector<cell*> buggycells;
|
|
|
|
#if HDR
|
|
template<class... T>
|
|
void limitgen(T... args) {
|
|
if(steplimit) {
|
|
cstep++;
|
|
printf("%6d ", cstep);
|
|
printf(args...);
|
|
if(cstep == steplimit) buggyGeneration = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
EX cell *pathTowards(cell *pf, cell *pt) {
|
|
|
|
while(celldist(pt) > celldist(pf)) {
|
|
if(isNeighbor(pf, pt)) return pt;
|
|
cell *pn = NULL;
|
|
forCellEx(pn2, pt) if(celldist(pn2) < celldist(pt)) pn = pn2;
|
|
pt = pn;
|
|
}
|
|
|
|
if(isNeighbor(pf, pt)) return pt;
|
|
forCellEx(pn2, pt) if(celldist(pn2) < celldist(pt)) return pn2;
|
|
return NULL;
|
|
}
|
|
|
|
bool errorReported = false;
|
|
|
|
EX void describeCell(cell *c) {
|
|
if(!c) { printf("NULL\n"); return; }
|
|
print(hlog, "describe ", lalign(6, c), ": ");
|
|
vector<cell*> nei;
|
|
for(int i=0; i<c->type; i++) nei.push_back(c->move(i));
|
|
println(hlog, ">> ", nei);
|
|
}
|
|
|
|
static int orbid = 0;
|
|
|
|
eItem nextOrb() {
|
|
orbid++;
|
|
eItem i = eItem(orbid % ittypes);
|
|
if(itemclass(i) == IC_ORB) return i;
|
|
else return nextOrb();
|
|
}
|
|
|
|
eItem randomTreasure() {
|
|
eItem i = eItem(hrand(ittypes));
|
|
if(itemclass(i) == IC_TREASURE) return i;
|
|
else return randomTreasure();
|
|
}
|
|
|
|
eItem randomTreasure2(int cv) {
|
|
int bq = 60000, cq = 0;
|
|
eItem best = itDiamond;
|
|
eItem lt = localTreasureType();
|
|
for(int a=1; a<ittypes; a++) {
|
|
eItem i = eItem(a);
|
|
if(itemclass(i) != IC_TREASURE) continue;
|
|
int q = 2*items[i];
|
|
if(a == lt) q -= (2*cv-1);
|
|
if(a == itEmerald && bearsCamelot(cwt.at->land)) q -= 8;
|
|
if(a == itElixir && isCrossroads(cwt.at->land)) q -= 7;
|
|
if(a == itIvory && isCrossroads(cwt.at->land)) q -= 6;
|
|
if(a == itPalace && isCrossroads(cwt.at->land)) q -= 5;
|
|
if(a == itIvory && cwt.at->land == laJungle) q -= 5;
|
|
if(a == itIvory && cwt.at->land == laPalace) q -= 5;
|
|
if(q < bq) bq = q, cq = 0;
|
|
if(q == bq) { cq++; if(hrand(cq) == 0) best = i; }
|
|
}
|
|
return best;
|
|
}
|
|
|
|
EX eLand cheatdest;
|
|
|
|
EX void cheatMoveTo(eLand l) {
|
|
cheatdest = l;
|
|
if(l == laCrossroads5) l = laCrossroads;
|
|
activateSafety(l);
|
|
cheatdest = laNone;
|
|
}
|
|
|
|
struct cheatkey {
|
|
int key;
|
|
string desc;
|
|
reaction_t action;
|
|
};
|
|
|
|
vector<cheatkey> cheats = {
|
|
cheatkey{'C', "Hyperstone Quest", [] {
|
|
cheater++;
|
|
cheatMoveTo(laCrossroads);
|
|
addMessage(XLAT("Activated the Hyperstone Quest!"));
|
|
|
|
for(int t=1; t<ittypes; t++)
|
|
if(t != itHyperstone && t != itBounty && itemclass(eItem(t)) == IC_TREASURE) {
|
|
items[t] = inv::on ? 50 : 10;
|
|
}
|
|
int qkills = inv::on ? 1000 : 200;
|
|
kills[moYeti] = qkills;
|
|
kills[moDesertman] = qkills;
|
|
kills[moRunDog] = qkills;
|
|
kills[moZombie] = qkills;
|
|
kills[moMonkey] = qkills;
|
|
kills[moCultist] = qkills;
|
|
kills[moTroll] = qkills;
|
|
}},
|
|
cheatkey{'M', "deplete orb powers", [] {
|
|
for(int i=0; i<ittypes; i++)
|
|
if(itemclass(eItem(i)) == IC_ORB)
|
|
items[i] = 0;
|
|
cheater++; addMessage(XLAT("Orb power depleted!"));
|
|
}},
|
|
cheatkey{'O', "summon orbs", [] {
|
|
cheater++; addMessage(XLAT("Orbs summoned!"));
|
|
for(int i=0; i<cwt.at->type; i++)
|
|
if(passable(cwt.at->move(i), NULL, 0)) {
|
|
eItem it = nextOrb();
|
|
cwt.at->move(i)->item = it;
|
|
}
|
|
}},
|
|
cheatkey{'F', "gain orb powers", [] {
|
|
if(hardcore && !canmove) {
|
|
canmove = true;
|
|
addMessage(XLAT("Revived!"));
|
|
}
|
|
else {
|
|
items[itOrbFlash] += 1;
|
|
items[itOrbTeleport] += 1;
|
|
items[itOrbLightning] += 1;
|
|
items[itOrbSpeed] += 1;
|
|
items[itOrbShield] += 1;
|
|
kills[moPlayer] = 0;
|
|
cheater++; addMessage(XLAT("Orb power gained!"));
|
|
canmove = true;
|
|
}
|
|
}},
|
|
cheatkey{'R'-64, "advance the rose wave", buildRosemap},
|
|
#if CAP_EDIT
|
|
cheatkey{'A', "start the Map Editor", [] {
|
|
lastexplore = turncount;
|
|
pushScreen(mapeditor::showMapEditor);
|
|
}},
|
|
cheatkey{'A'-64, "start the Vector Graphics Editor", [] {
|
|
mapeditor::drawcell = mouseover ? mouseover : cwt.at;
|
|
pushScreen(mapeditor::showDrawEditor);
|
|
}},
|
|
#else
|
|
cheatkey{'A', "take screenshot", [] {
|
|
pushScreen(shot::menu);
|
|
}},
|
|
#endif
|
|
cheatkey{'T', "summon treasure", [] {
|
|
items[randomTreasure2(10)] += 10;
|
|
cheater++; addMessage(XLAT("Treasure gained!"));
|
|
}},
|
|
cheatkey{'T'-64, "summon lots of treasure", [] {
|
|
items[randomTreasure2(100)] += 100;
|
|
cheater++; addMessage(XLAT("Lots of treasure gained!"));
|
|
}},
|
|
cheatkey{'Z', "rotate the character", [] {
|
|
if (flipplayer) {
|
|
cwt += cwt.at->type/2;
|
|
flipplayer = false;
|
|
}
|
|
cwt++;
|
|
mirror::act(1, mirror::SPINSINGLE);
|
|
cwt.at->mondir++;
|
|
cwt.at->mondir %= cwt.at->type;
|
|
|
|
if(shmup::on) shmup::pc[0]->at = Id;
|
|
}},
|
|
cheatkey{'J', "lose all treasure", [] {
|
|
if(items[localTreasureType()] > 0)
|
|
items[localTreasureType()] = 0;
|
|
else for(int i=1; i<ittypes; i++)
|
|
if(itemclass(eItem(i)) == IC_TREASURE)
|
|
items[i] = 0;
|
|
cheater++; addMessage(XLAT("Treasure lost!"));
|
|
}},
|
|
cheatkey{'K', "gain kills", [] {
|
|
for(int i=0; i<motypes; i++) kills[i] += 10;
|
|
kills[moPlayer] = 0;
|
|
cheater++; addMessage(XLAT("Kills gained!"));
|
|
}},
|
|
cheatkey{'Y', "unlock Orbs of Yendor", [] {
|
|
for(auto& y: yendor::yi) {
|
|
if(y.path[YDIST-1]->item == itKey)
|
|
y.path[YDIST-1]->item = itNone;
|
|
if(!y.found) items[itKey]++;
|
|
y.found = true;
|
|
}
|
|
cheater++; addMessage(XLAT("Collected the keys!"));
|
|
}},
|
|
cheatkey{'Y'-64, "gain Orb of Yendor", [] {
|
|
yendor::collected(cwt.at);
|
|
cheater++;
|
|
}},
|
|
cheatkey{'P', "save a Princess", [] {
|
|
items[itSavedPrincess]++;
|
|
princess::saved = true;
|
|
princess::everSaved = true;
|
|
if(inv::on && !princess::reviveAt)
|
|
princess::reviveAt = gold(NO_LOVE);
|
|
cheater++; addMessage(XLAT("Saved the Princess!"));
|
|
}},
|
|
cheatkey{'S', "Safety (quick save)", [] {
|
|
canmove = true;
|
|
cheatMoveTo(cwt.at->land);
|
|
items[itOrbSafety] += 3;
|
|
cheater++; addMessage(XLAT("Activated Orb of Safety!"));
|
|
}},
|
|
cheatkey{'W'-64, "switch web display", [] {
|
|
pushScreen(linepatterns::showMenu);
|
|
}},
|
|
cheatkey{'G'-64, "switch ghost timer", [] {
|
|
timerghost = !timerghost;
|
|
cheater++;
|
|
addMessage(XLAT("turn count = %1 last exploration = %2 ghost timer = %3",
|
|
its(turncount), its(lastexplore), ONOFF(timerghost)));
|
|
}},
|
|
cheatkey{'G', "edit cell values", push_debug_screen},
|
|
cheatkey{'L'-64, "cell info", [] {
|
|
debug_cellnames = !debug_cellnames;
|
|
cell *c = mouseover;
|
|
if(!c) return;
|
|
describeCell(c);
|
|
}},
|
|
cheatkey{'P'-64, "peaceful mode", [] {
|
|
peace::on = !peace::on;
|
|
}},
|
|
#ifdef CHEAT_DISABLE_ALLOWED
|
|
cheatkey{'D'-64, "cheat disable", [] {
|
|
cheater = 0; autocheat = 0;
|
|
}
|
|
#endif
|
|
};
|
|
|
|
EX bool applyCheat(char u) {
|
|
for(auto& ch: cheats) if(u == ch.key) {
|
|
ch.action();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<class T> string dnameof2(T x) {
|
|
string s = dnameof(x);
|
|
return s + " (" + its(x) + ")";
|
|
}
|
|
|
|
template<class T> string dnameof2(T x, int p) {
|
|
string s = dnameof(x);
|
|
return s + " (" + its(x) + "/" + its(p) + ")";
|
|
}
|
|
|
|
EX vector<pair<cellwalker,int> > drawbugs;
|
|
|
|
bool debugmode = false;
|
|
|
|
// static apparently does not work in old compilers
|
|
int bitfield_v;
|
|
|
|
template<class T> void bitfield_editor(int val, T setter, string help = "") {
|
|
bitfield_v = val;
|
|
dialog::editNumber(bitfield_v, 0, 100, 1, bitfield_v, help, "");
|
|
dialog::get_di().reaction = [setter] () { setter(bitfield_v); };
|
|
}
|
|
|
|
struct debugScreen {
|
|
|
|
cell *debugged_cell;
|
|
|
|
bool show_debug_data;
|
|
|
|
debugScreen() { debugged_cell = NULL; show_debug_data = false; }
|
|
|
|
void operator () () {
|
|
cmode = sm::SIDE | sm::DIALOG_STRICT_X;
|
|
gamescreen();
|
|
getcstat = '-';
|
|
|
|
dialog::init(show_debug_data ? XLAT("debug values") : XLAT("internal details"));
|
|
|
|
for(auto& p: drawbugs)
|
|
drawBug(p.first, p.second);
|
|
|
|
cell *what = debugged_cell;
|
|
if(!what && current_display->sidescreen) what = mouseover;
|
|
|
|
if(what) {
|
|
#if CAP_SHAPES
|
|
queuepoly(gmatrix[what], cgi.shAsymmetric, 0x80808080);
|
|
#endif
|
|
dialog::addSelItem("mpdist", its(what->mpdist), 'd');
|
|
dialog::add_action([what] () {
|
|
bitfield_editor(what->mpdist, [what] (int i) { what->mpdist = 0; }, "generation level");
|
|
});
|
|
dialog::addSelItem("land", dnameof2(what->land), 0);
|
|
dialog::addSelItem("land param (int)", its(what->landparam), 'p');
|
|
dialog::add_action([what] () { dialog::editNumber(what->landparam, 0, 100, 1, what->landparam, "landparam",
|
|
"Extra value that is important in some lands. The specific meaning depends on the land."); });
|
|
dialog::addSelItem("land param (hex)", itsh8(what->landparam), 0);
|
|
dialog::addSelItem("land param (heat)", fts(HEAT(what)), 't');
|
|
dialog::addSelItem("cdata",
|
|
its(getCdata(what, 0))+"/"+its(getCdata(what,1))+"/"+its(getCdata(what,2))+"/"+its(getCdata(what,3))+"/"+itsh(getBits(what)), 't');
|
|
dialog::add_action([what] () {
|
|
static ld d = HEAT(what);
|
|
dialog::editNumber(d, -2, 2, 0.1, d, "landparam",
|
|
"Extra value that is important in some lands. The specific meaning depends on the land.");
|
|
dialog::get_di().reaction = [what] () { HEAT(what) = d; };
|
|
});
|
|
dialog::addSelItem("land flags", its(what->landflags)+"/"+itsh2(what->landflags), 'f');
|
|
dialog::add_action([what] () {
|
|
bitfield_editor(what->landflags, [what] (int i) { what->landflags = i; }, "Rarely used.");
|
|
});
|
|
dialog::addSelItem("barrier dir", its(what->bardir), 'b');
|
|
dialog::add_action([what] () {
|
|
bitfield_editor(what->bardir, [what] (int i) { what->bardir = i; });
|
|
});
|
|
dialog::addSelItem("barrier left", dnameof2(what->barleft), 0);
|
|
dialog::addSelItem("barrier right", dnameof2(what->barright), 0);
|
|
if(what->item == itBabyTortoise) {
|
|
dialog::addSelItem(XLAT("baby Tortoise flags"), itsh(tortoise::babymap[what]), 'B');
|
|
dialog::add_action([what] () {
|
|
dialog::editNumber(tortoise::babymap[what], 0, (1<<21)-1, 1, getBits(what), "", "");
|
|
dialog::use_hexeditor();
|
|
});
|
|
}
|
|
if(what->monst == moTortoise) {
|
|
dialog::addSelItem(XLAT("adult Tortoise flags"), itsh(tortoise::emap[what]), 'A');
|
|
dialog::add_action([what] () {
|
|
tortoise::emap[what] = tortoise::getb(what);
|
|
dialog::editNumber(tortoise::emap[what], 0, (1<<21)-1, 1, getBits(what), "", "");
|
|
dialog::use_hexeditor();
|
|
});
|
|
}
|
|
#if CAP_COMPLEX2
|
|
if(dice::on(what)) {
|
|
dialog::addSelItem(XLAT("die shape"), dice::die_name(dice::data[what].which), 'A');
|
|
dialog::add_action_push([what] {
|
|
dialog::init("die shape");
|
|
char key = 'a';
|
|
for(auto shape: dice::die_list) {
|
|
dialog::addItem(dice::die_name(shape), key++);
|
|
dialog::add_action([what, shape] {
|
|
dice::data[what].which = shape;
|
|
dice::data[what].val = 0;
|
|
popScreen();
|
|
});
|
|
}
|
|
dialog::display();
|
|
});
|
|
dialog::addSelItem(XLAT("die face"), its(dice::data[what].val), 'B');
|
|
dialog::add_action([what] {
|
|
auto& dd = dice::data[what];
|
|
int maxv = shape_faces(dd.which)-1;
|
|
dialog::editNumber(dd.val, 0, maxv, 1, 0, XLAT("die face"), "");
|
|
dialog::bound_low(0);
|
|
dialog::bound_up(maxv);
|
|
});
|
|
dialog::addSelItem(XLAT("die direction"), its(dice::data[what].dir), 'C');
|
|
dialog::add_action([what] {
|
|
auto& dd = dice::data[what];
|
|
dialog::editNumber(dd.dir, 0, what->type-1, 1, dd.dir, XLAT("die direction"), "");
|
|
dialog::bound_low(0);
|
|
dialog::bound_up(what->type-1);
|
|
});
|
|
dialog::addBoolItem_action(XLAT("die mirror status"), dice::data[what].mirrored, 'D');
|
|
}
|
|
#endif
|
|
dialog::addBreak(50);
|
|
|
|
if(show_debug_data) {
|
|
dialog::addSelItem("pointer", s0+hr::format("%p", hr::voidp(what))+"/"+index_pointer(what), 0);
|
|
dialog::addSelItem("cpdist", its(what->cpdist), 0);
|
|
dialog::addSelItem("celldist", its(celldist(what)), 0);
|
|
dialog::addSelItem("celldistance", its(celldistance(cwt.at, what)), 0);
|
|
dialog::addSelItem("pathdist", its(what->pathdist), 0);
|
|
dialog::addSelItem("celldistAlt", eubinary ? its(celldistAlt(what)) : "--", 0);
|
|
dialog::addSelItem("temporary", its(what->listindex), 0);
|
|
#if CAP_GP
|
|
if(GOLDBERG)
|
|
dialog::addSelItem("whirl", sprint(gp::get_local_info(what).relative), 0);
|
|
#endif
|
|
#if CAP_RACING
|
|
if(racing::on) racing::add_debug(what);
|
|
#endif
|
|
}
|
|
else {
|
|
dialog::addSelItem("wall", dnameof2(what->wall, what->wparam), 'w');
|
|
dialog::add_action([what] () {
|
|
bitfield_editor(what->wparam, [what] (int i) { what->wparam = i; },
|
|
"wall parameter");
|
|
});
|
|
dialog::addSelItem("item", dnameof(what->item), 0);
|
|
#if CAP_ARCM
|
|
if(arcm::in())
|
|
dialog::addSelItem("ID", its(arcm::id_of(what->master)), 0);
|
|
#endif
|
|
dialog::addBreak(50);
|
|
dialog::addSelItem("monster", dnameof2(what->monst, what->mondir), 'm');
|
|
dialog::add_action([what] () {
|
|
bitfield_editor(what->mondir, [what] (int i) { what->mondir = i; },
|
|
"monster direction");
|
|
dialog::get_di().extra_options = [what] () {
|
|
dialog::addBoolItem(XLAT("mirrored"), what->monmirror, 'M');
|
|
};
|
|
});
|
|
dialog::addSelItem("stuntime", its(what->stuntime), 's');
|
|
dialog::add_action([what] () {
|
|
bitfield_editor(what->stuntime, [what] (int i) { what->stuntime = i; });
|
|
});
|
|
dialog::addSelItem("hitpoints", its(what->hitpoints), 'h');
|
|
dialog::add_action([what] () {
|
|
bitfield_editor(what->hitpoints, [what] (int i) { what->hitpoints = i; });
|
|
});
|
|
dialog::addBreak(50);
|
|
dialog::addBreak(50);
|
|
dialog::addItem("show debug data", 'x');
|
|
dialog::add_action([this] () { show_debug_data = true; });
|
|
if(!debugged_cell) dialog::addItem("click a cell to edit it", 0);
|
|
}
|
|
}
|
|
else {
|
|
dialog::addItem(XLAT("click a cell to view its data"), 0);
|
|
dialog::addBreak(1000);
|
|
}
|
|
dialog::addBack();
|
|
dialog::display();
|
|
|
|
keyhandler = [this] (int sym, int uni) {
|
|
handlePanning(sym, uni);
|
|
dialog::handleNavigation(sym, uni);
|
|
if(applyCheat(uni)) ;
|
|
else if(sym == PSEUDOKEY_WHEELUP || sym == PSEUDOKEY_WHEELDOWN) ;
|
|
else if(sym == '-') debugged_cell = mouseover;
|
|
else if(doexiton(sym, uni)) {
|
|
popScreen();
|
|
if(debugmode) quitmainloop = true;
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
EX void push_debug_screen() {
|
|
debugScreen ds;
|
|
pushScreen(ds);
|
|
}
|
|
|
|
/** show the cheat menu */
|
|
EX void showCheatMenu() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
dialog::init("cheat menu");
|
|
for(auto& ch: cheats) {
|
|
dialog::addItem(XLAT(ch.desc), ch.key);
|
|
dialog::add_action([ch] { ch.action(); popScreen(); });
|
|
}
|
|
dialog::addBreak(50);
|
|
dialog::addBack();
|
|
dialog::display();
|
|
}
|
|
|
|
/** view all the monsters and items */
|
|
EX void viewall() {
|
|
celllister cl(cwt.at, 20, 2000, NULL);
|
|
|
|
vector<eMonster> all_monsters;
|
|
for(int i=0; i<motypes; i++) {
|
|
eMonster m = eMonster(i);
|
|
if(!isMultitile(m)) all_monsters.push_back(m);
|
|
}
|
|
|
|
for(cell *c: cl.lst) {
|
|
if(isPlayerOn(c)) continue;
|
|
bool can_put_monster = true;
|
|
forCellEx(c2, c) if(c2->monst || isPlayerOn(c2)) can_put_monster = false;
|
|
if(can_put_monster) {
|
|
for(int k=0; k<isize(all_monsters); k++)
|
|
if(passable_for(all_monsters[k], c, nullptr, 0)) {
|
|
c->monst = all_monsters[k];
|
|
all_monsters[k] = all_monsters.back();
|
|
all_monsters.pop_back();
|
|
}
|
|
}
|
|
}
|
|
|
|
vector<cell*> itemcells;
|
|
for(cell *c: cl.lst) {
|
|
if(isPlayerOn(c) || c->monst || c->item) continue;
|
|
itemcells.push_back(c);
|
|
}
|
|
int id = 0;
|
|
for(int it=1; it<ittypes; it++) if(it != itBarrow) {
|
|
if(id >= isize(itemcells)) break;
|
|
itemcells[id++]->item = eItem(it);
|
|
}
|
|
}
|
|
|
|
#if CAP_COMMANDLINE
|
|
/** perform a move for the -cmove command */
|
|
|
|
int cheat_move_gen = 7;
|
|
|
|
void cheat_move(char c) {
|
|
using arg::cheat;
|
|
if(c >= '0' && c <= '9' && cheat_move_gen == -1) cheat_move_gen = (c - '0');
|
|
else if(c >= '0' && c <= '9') cheat(), cwt += (c - '0');
|
|
else if(c == 's') {
|
|
cheat();
|
|
cwt += wstep;
|
|
playermoved = false;
|
|
setdist(cwt.at, cheat_move_gen, cwt.peek());
|
|
if(geometry_supports_cdata()) getCdata(cwt.at, 0);
|
|
}
|
|
else if(c == 'r') cheat(), cwt += rev;
|
|
else if(c == 'm') cheat(), cwt += wmirror;
|
|
else if(c == 'z') cheat(), cwt.spin = 0, cwt.mirrored = false;
|
|
else if(c == 'F') centering = eCentering::face, fullcenter();
|
|
else if(c == 'E') centering = eCentering::edge, fullcenter();
|
|
else if(c == 'V') centering = eCentering::vertex, fullcenter();
|
|
else if(c == 'a') cheat(), history::save_end();
|
|
else if(c == 'g') cheat_move_gen = -1;
|
|
else println(hlog, "unknown move command: ", c);
|
|
}
|
|
#endif
|
|
|
|
/** launch a debugging screen, and continue normal working only after this screen is closed */
|
|
EX void modalDebug(cell *c) {
|
|
centerover = c; View = Id;
|
|
if(noGUI) {
|
|
fprintf(stderr, "fatal: modalDebug called on %p without GUI\n", hr::voidp(c));
|
|
exit(1);
|
|
}
|
|
push_debug_screen();
|
|
debugmode = true;
|
|
mainloop();
|
|
debugmode = false;
|
|
quitmainloop = false;
|
|
}
|
|
|
|
void test_distances(int max) {
|
|
int ok = 0, bad = 0;
|
|
celllister cl(cwt.at, max, 100000, NULL);
|
|
for(cell *c: cl.lst) {
|
|
bool is_ok = cl.getdist(c) == celldistance(c, cwt.at);
|
|
if(is_ok) ok++; else bad++;
|
|
}
|
|
println(hlog, "ok=", ok, " bad=", bad);
|
|
}
|
|
|
|
EX void raiseBuggyGeneration(cell *c, const char *s) {
|
|
|
|
printf("procgen error (%p): %s\n", hr::voidp(c), s);
|
|
|
|
if(!errorReported) {
|
|
addMessage(string("something strange happened in: ") + s);
|
|
errorReported = true;
|
|
}
|
|
|
|
#ifdef BACKTRACE
|
|
void *array[1000];
|
|
size_t size;
|
|
|
|
// get void*'s for all entries on the stack
|
|
size = backtrace(array, 1000);
|
|
|
|
// print out all the frames to stderr
|
|
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
|
#endif
|
|
|
|
// return;
|
|
|
|
if(cheater || autocheat) {
|
|
drawbugs.emplace_back(cellwalker(c,0), 0xFF000080);
|
|
modalDebug(c);
|
|
drawbugs.pop_back();
|
|
}
|
|
else
|
|
c->item = itBuggy;
|
|
}
|
|
|
|
#if CAP_COMMANDLINE
|
|
|
|
int read_cheat_args() {
|
|
using namespace arg;
|
|
if(argis("-ch")) { cheat(); }
|
|
else if(argis("-rch")) {
|
|
PHASEFROM(2); cheat(); reptilecheat = true;
|
|
}
|
|
// cheats
|
|
else if(argis("-g")) {
|
|
/* debugging mode */
|
|
if(curphase == 1) {
|
|
/* use no score file */
|
|
scorefile = "";
|
|
/* set seed for reproducible results */
|
|
if(!fixseed) {
|
|
fixseed = true; autocheat = true;
|
|
startseed = 1;
|
|
}
|
|
}
|
|
PHASE(2);
|
|
/* causes problems in gdb */
|
|
mouseaim_sensitivity = 0;
|
|
/* do not any play sounds while debugging */
|
|
effvolume = 0;
|
|
musicvolume = 0;
|
|
}
|
|
else if(argis("-WS")) {
|
|
PHASE(3);
|
|
shift();
|
|
activateSafety(readland(args()));
|
|
cheat();
|
|
}
|
|
else if(argis("-WT")) {
|
|
PHASE(3);
|
|
shift();
|
|
teleportToLand(readland(args()), false);
|
|
cheat();
|
|
}
|
|
else if(argis("-W2")) {
|
|
shift(); cheatdest = readland(args()); cheat();
|
|
showstartmenu = false;
|
|
cheatdest_list.clear();
|
|
}
|
|
else if(argis("-W3")) {
|
|
shift(); cheatdest_list.push_back(readland(args())); cheat();
|
|
showstartmenu = false;
|
|
}
|
|
else if(argis("-I")) {
|
|
PHASE(3) cheat();
|
|
shift(); eItem i = readItem(args());
|
|
shift(); items[i] = argi();
|
|
}
|
|
else if(argis("-IP")) {
|
|
PHASE(3) cheat();
|
|
shift(); eItem i = readItem(args());
|
|
shift(); int q = argi();
|
|
placeItems(q, i);
|
|
}
|
|
else if(argis("-SM")) {
|
|
PHASEFROM(2);
|
|
shift(); vid.stereo_mode = eStereo(argi());
|
|
}
|
|
else if(argis("-cmove")) {
|
|
PHASE(3); shift();
|
|
for(char c: args()) cheat_move(c);
|
|
}
|
|
else if(argis("-ipd")) {
|
|
PHASEFROM(2);
|
|
shift_arg_formula(vid.ipd);
|
|
}
|
|
#if CAP_INV
|
|
else if(argis("-IU")) {
|
|
PHASE(3) cheat();
|
|
shift(); eItem i = readItem(args());
|
|
shift(); inv::usedup[i] += argi();
|
|
inv::compute();
|
|
}
|
|
else if(argis("-IX")) {
|
|
PHASE(3) cheat();
|
|
shift(); eItem i = readItem(args());
|
|
shift(); inv::extra_orbs[i] += argi();
|
|
inv::compute();
|
|
}
|
|
#endif
|
|
#if CAP_COMPLEX2
|
|
else if(argis("-ambush")) {
|
|
// make all ambushes use the given number of dogs
|
|
// example: hyper -W Hunt -IP Shield 1 -ambush 60
|
|
PHASE(3) cheat();
|
|
shift(); ambush::fixed_size = argi();
|
|
}
|
|
#endif
|
|
else if(argis("-testdistances")) {
|
|
PHASE(3); shift(); test_distances(argi());
|
|
}
|
|
else if(argis("-M")) {
|
|
PHASE(3) cheat(); start_game(); if(WDIM == 3) { drawthemap(); bfs(); }
|
|
shift(); eMonster m = readMonster(args());
|
|
shift(); int q = argi();
|
|
printf("m = %s q = %d\n", dnameof(m).c_str(), q);
|
|
restoreGolems(q, m, 7);
|
|
}
|
|
else if(argis("-MK")) {
|
|
PHASE(3) cheat();
|
|
shift(); eMonster m = readMonster(args());
|
|
shift(); kills[m] += argi();
|
|
}
|
|
else if(argis("-killeach")) {
|
|
PHASEFROM(2); start_game();
|
|
shift(); int q = argi(); cheat();
|
|
for(int m=0; m<motypes; m++)
|
|
if(monsterclass(eMonster(m)) == 0)
|
|
kills[m] = q;
|
|
}
|
|
else if(argis("-each")) {
|
|
PHASEFROM(2); start_game();
|
|
shift(); int q = argi(); cheat();
|
|
for(int i=0; i<ittypes; i++)
|
|
if(itemclass(eItem(i)) == IC_TREASURE)
|
|
items[i] = q;
|
|
}
|
|
else if(argis("-each-random")) {
|
|
PHASEFROM(2); start_game(); cheat();
|
|
for(int i=0; i<ittypes; i++)
|
|
if(itemclass(eItem(i)) == IC_TREASURE) {
|
|
items[i] = 10 + hrand(21);
|
|
if(i == itElemental) items[i] = 12;
|
|
}
|
|
else
|
|
items[i] = 0;
|
|
}
|
|
else if(argis("-viewall")) {
|
|
PHASE(3); start_game();
|
|
viewall();
|
|
}
|
|
else if(argis("-unlock-all")) {
|
|
cheat(); all_unlocked = true;
|
|
}
|
|
else if(argis("-wef")) {
|
|
PHASEFROM(2);
|
|
shift(); int index = argi();
|
|
shift_arg_formula(whatever[index]);
|
|
}
|
|
else if(argis("-wei")) {
|
|
PHASEFROM(2);
|
|
shift(); int index = argi();
|
|
shift(); whateveri[index] = argi();
|
|
}
|
|
else if(argis("-W4")) {
|
|
shift(); top_land = readland(args()); cheat();
|
|
showstartmenu = false;
|
|
}
|
|
else if(argis("-top")) {
|
|
PHASE(3); View = View * spin(-90._deg);
|
|
}
|
|
else if(argis("-idv")) {
|
|
PHASE(3); View = Id;
|
|
}
|
|
else if(argis("-gencells")) {
|
|
PHASEFROM(2); shift(); start_game();
|
|
printf("Generating %d cells...\n", argi());
|
|
celllister cl(cwt.at, 50, argi(), NULL);
|
|
printf("Cells generated: %d\n", isize(cl.lst));
|
|
for(int i=0; i<isize(cl.lst); i++)
|
|
setdist(cl.lst[i], 7, NULL);
|
|
}
|
|
else if(argis("-sr")) {
|
|
PHASEFROM(2);
|
|
shift(); sightrange_bonus = argi(); vid.use_smart_range = 0;
|
|
}
|
|
else if(argis("-srx")) {
|
|
PHASEFROM(2); cheat();
|
|
shift(); sightrange_bonus = genrange_bonus = gamerange_bonus = argi(); vid.use_smart_range = 0;
|
|
}
|
|
else if(argis("-smart")) {
|
|
PHASEFROM(2); cheat();
|
|
vid.use_smart_range = 2;
|
|
shift_arg_formula(WDIM == 3 ? vid.smart_range_detail_3 : vid.smart_range_detail);
|
|
}
|
|
else if(argis("-smartarea")) {
|
|
PHASEFROM(2); cheat();
|
|
shift(); vid.smart_area_based = argi();
|
|
}
|
|
else if(argis("-smartn")) {
|
|
PHASEFROM(2);
|
|
vid.use_smart_range = 1;
|
|
shift_arg_formula(vid.smart_range_detail);
|
|
}
|
|
else if(argis("-smartlimit")) {
|
|
PHASEFROM(2);
|
|
shift(); vid.cells_drawn_limit = argi();
|
|
}
|
|
else if(argis("-genlimit")) {
|
|
PHASEFROM(2);
|
|
shift(); vid.cells_generated_limit = argi();
|
|
}
|
|
else if(argis("-sight3")) {
|
|
PHASEFROM(2);
|
|
shift_arg_formula(sightranges[geometry]);
|
|
}
|
|
else if(argis("-sloppy")) {
|
|
PHASEFROM(2);
|
|
vid.sloppy_3d = true;
|
|
}
|
|
else if(argis("-gen3")) {
|
|
PHASEFROM(2);
|
|
shift_arg_formula(extra_generation_distance);
|
|
}
|
|
else if(argis("-quantum")) {
|
|
cheat();
|
|
quantum = true;
|
|
}
|
|
else if(argis("-lands")) {
|
|
PHASEFROM(2);
|
|
cheat();
|
|
stop_game();
|
|
shift(); land_structure = (eLandStructure) (argi());
|
|
}
|
|
else if(argis("-fix")) {
|
|
PHASE(1);
|
|
fixseed = true; autocheat = true;
|
|
}
|
|
else if(argis("-cellnames")) {
|
|
cheat(); debug_cellnames = true;
|
|
}
|
|
else if(argis("-fixx")) {
|
|
PHASE(1);
|
|
fixseed = true; autocheat = true;
|
|
shift(); startseed = argi();
|
|
}
|
|
else if(argis("-reseed")) {
|
|
PHASEFROM(2);
|
|
shift(); shrand(argi());
|
|
}
|
|
else if(argis("-steplimit")) {
|
|
fixseed = true; autocheat = true;
|
|
shift(); steplimit = argi();
|
|
}
|
|
else if(argis("-dgl")) {
|
|
#if CAP_GL
|
|
glhr::debug_gl = true;
|
|
#endif
|
|
}
|
|
else if(argis("-mgen-off")) {
|
|
PHASEFROM(3);
|
|
cheat();
|
|
gen_wandering = false;
|
|
}
|
|
else if(argis("-canvasfloor")) {
|
|
shift(); canvasfloor = argi();
|
|
for(int i=0; i<caflEND; i++) if(appears(mapeditor::canvasFloorName(i), args()))
|
|
canvasfloor = i;
|
|
}
|
|
else if(argis("-keys")) {
|
|
shift(); string s = args();
|
|
bool quote = false;
|
|
for(char c: s)
|
|
if(quote) {
|
|
quote = false;
|
|
if(c == '\\') dialog::queue_key(c), quote = false;
|
|
else if(c >= '1' && c <= '9') dialog::queue_key(SDLK_F1 + c - '1');
|
|
else if(c == 'e') dialog::queue_key(SDLK_ESCAPE);
|
|
else if(c == 'r') dialog::queue_key(SDLK_RETURN);
|
|
else if(c == 't') dialog::queue_key(SDLK_TAB);
|
|
else if(c == 'b') dialog::queue_key(SDLK_BACKSPACE);
|
|
else if(c == 'R') dialog::queue_key(SDLK_RIGHT);
|
|
else if(c == 'L') dialog::queue_key(SDLK_LEFT);
|
|
else if(c == 'U') dialog::queue_key(SDLK_UP);
|
|
else if(c == 'D') dialog::queue_key(SDLK_DOWN);
|
|
else if(c == 'H') dialog::queue_key(SDLK_HOME);
|
|
else if(c == 'E') dialog::queue_key(SDLK_END);
|
|
else if(c == 'P') dialog::queue_key(SDLK_PAGEUP);
|
|
else if(c == 'Q') dialog::queue_key(SDLK_PAGEDOWN);
|
|
}
|
|
else if(c == '\\') quote = true;
|
|
else dialog::queue_key(c);
|
|
}
|
|
else if(argis("-hroll")) {
|
|
shift();
|
|
int i = argi();
|
|
while(i>0) i--, hrand(10);
|
|
}
|
|
else if(argis("-W")) {
|
|
PHASEFROM(2);
|
|
shift();
|
|
firstland0 = firstland = specialland = readland(args());
|
|
if (!landUnlocked(firstland))
|
|
cheat();
|
|
stop_game_and_switch_mode(rg::nothing);
|
|
showstartmenu = false;
|
|
}
|
|
else return 1;
|
|
return 0;
|
|
}
|
|
|
|
auto ah_cheat = addHook(hooks_args, 0, read_cheat_args);
|
|
#endif
|
|
|
|
EX bool ldebug = false;
|
|
|
|
EX void breakhere() {
|
|
exit(1);
|
|
}
|
|
|
|
}
|