mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2025-10-21 00:47:40 +00:00

It now only gives the treasures you actually need for the Hyperstone Quest in your current game, rather than all of them. For example, you'll now only get Sea Glass if the Docks are in your game.
910 lines
27 KiB
C++
910 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!"));
|
|
|
|
generateLandList(isLandIngame);
|
|
for(eLand l: landlist) {
|
|
eItem t = treasureType(l);
|
|
if(required_for_hyperstones(t) && 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("-save-cheats")) {
|
|
save_cheats = true;
|
|
}
|
|
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);
|
|
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);
|
|
}
|
|
|
|
}
|