1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-11-17 18:54:48 +00:00
hyperrogue/system.cpp
2017-05-27 21:40:40 +02:00

1401 lines
38 KiB
C++

// Hyperbolic Rogue
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
// routines for: initializing/closing, loading/saving, and cheating
bool cblind;
bool autocheat;
int truelotus;
int gamecount;
time_t timerstart, savetime;
bool timerstopped;
int savecount;
bool showoff = false, doCross = false;
// initialize the game
void initgame() {
DEBB(DF_INIT, (debugfile,"initGame\n"));
yendor::init(1);
if(safety && safetyseed) {
shrand(safetyseed);
firstland = safetyland;
}
if(tactic::on && (euclid || sphere)) euclidland = firstland;
if(firstland == laNone || firstland == laBarrier)
firstland = laCrossroads;
if(firstland == laCrossroads5 && !tactic::on) firstland = laCrossroads2; // could not fit!
if(firstland == laOceanWall) firstland = laOcean;
if(firstland == laHauntedWall) firstland = laGraveyard;
if(firstland == laMountain && !tactic::on) firstland = laJungle;
if(isGravityLand(firstland) && !tactic::on) firstland = laCrossroads;
cwt.c = currentmap->gamestart(); cwt.spin = 0; cwt.mirrored = false;
cwt.c->land = (euclid || sphere) ? euclidland : firstland;
chaosAchieved = false;
if(firstland == laElementalWall) cwt.c->land = randomElementalLand();
if(tactic::on && (isGravityLand(firstland) || firstland == laOcean) && firstland != laMountain)
cwt.c->land = purehepta ? laCrossroads : laCrossroads2;
createMov(cwt.c, 0);
setdist(cwt.c, BARLEV, NULL);
if((tactic::on || yendor::on) && isCyclic(firstland)) {
anthraxBonus = items[itHolyGrail];
cwt.c->mov[0]->land = firstland;
if(firstland == laWhirlpool) cwt.c->mov[0]->wall = waSea;
setdist(cwt.c->mov[0], BARLEV-1, cwt.c);
heptagon *h = createAlternateMap(cwt.c, 2, hsA);
if(!h) printf("FAIL\n");
}
if(tactic::on && firstland == laPower) {
items[itOrbSpeed] = 30;
items[itOrbWinter] = 30;
items[itOrbFlash] = 30;
}
if(tactic::on && firstland == laCaribbean) {
if(hiitemsMax(itRedGem) >= 25) items[itRedGem] = min(hiitemsMax(itRedGem), 50);
if(hiitemsMax(itFernFlower) >= 25) items[itFernFlower] = min(hiitemsMax(itFernFlower), 50);
if(hiitemsMax(itWine) >= 25) items[itWine] = min(hiitemsMax(itWine), 50);
}
if(tactic::on && tactic::trailer)
items[treasureType(firstland)] = 5;
tactic::lasttactic = firstland;
yendor::lastchallenge = yendor::challenge;
yendor::init(2);
for(int i=0; i<65536; i++) euland[i] = laNone;
if(euclid && euclidland == laPrincessQuest) {
cell *c = euclideanAtCreate(EPX, EPY);
princess::generating = true;
c->land = laPalace;
for(int j=BARLEV; j>=0; j--) setdist(c, j, NULL);
princess::generating = false;
}
if(cwt.c->land == laCrossroads2) {
cwt.c->landparam = 12;
createMov(cwt.c, 0)->landparam = 44;
createMov(cwt.c, 0)->land = laCrossroads2;
}
for(int i=0; i<numplayers(); i++) sword::angle[i] = 11;
if(!safety) multi::players = vid.scfg.players;
if(multi::players < 1 || multi::players > MAXPLAYER)
multi::players = 1;
multi::whereto[0].d = MD_UNDECIDED;
multi::cpid = 0;
if(shmup::on) shmup::init();
// extern int sightrange; sightrange = 9;
// cwt.c->land = laHell; items[itHell] = 10;
for(int i=BARLEV; i>=0; i--) {
if(tactic::trailer && cwt.c->land != laClearing) safety = true;
setdist(cwt.c, i, NULL);
if(tactic::trailer) safety = false;
currentmap->verify();
}
if(doCross) {
for(int i=0; i<ittypes; i++) if(itemclass(eItem(i)) == IC_TREASURE) items[i] = 50;
for(int i=0; i<motypes; i++) kills[i] = 30;
items[itSavedPrincess] = 0;
kills[moPrincessMoved] = 0;
kills[moPrincessArmedMoved] = 0;
kills[moPlayer] = 0;
}
if(quotient && generateAll(firstland)) {
for(int i=0; i<size(currentmap->allcells()); i++)
setdist(currentmap->allcells()[i], 8, NULL);
}
if(multi::players > 1 && !shmup::on) for(int i=0; i<numplayers(); i++) {
int idir = (3 * i) % cwt.c->type;
multi::player[i].c = cwt.c->mov[idir];
// special case -- otherwise they land on a wall
if(firstland == laCrossroads2 && i == 1)
multi::player[1].c = cwt.c;
if(firstland == laCrossroads2 && i == 6)
multi::player[6].c = createMov(createMov(cwt.c, 0), 3);
setdist(cwt.c->mov[idir], 0, cwt.c);
multi::player[i].spin = 0;
multi::flipped[i] = true;
multi::whereto[i].d = MD_UNDECIDED;
}
if(tactic::on && tactic::trailer)
items[treasureType(firstland)] = 15;
yendor::init(3);
multi::revive_queue.clear();
#ifdef TOUR
if(tour::on) tour::presentation(tour::pmRestart);
#endif
if(multi::players > 1 && !shmup::on) {
for(int i=0; i<numplayers(); i++)
makeEmpty(playerpos(i));
}
else {
for(int i=0; i<numplayers(); i++)
makeEmpty(cwt.c);
}
if(!safety) {
usedSafety = false;
timerstart = time(NULL); turncount = 0; rosewave = 0; rosephase = 0;
if(!quotient) mapeditor::whichPattern = 0;
mapeditor::whichShape = 0;
noiseuntil = 0;
sagephase = 0; hardcoreAt = 0;
timerstopped = false;
savecount = 0; savetime = 0;
cheater = 0;
if(autocheat) cheater = 1;
hauntedWarning = false;
timerghost = true;
truelotus = 0;
survivalist = true;
if(!randomPatternsMode && !tactic::on && !yendor::on) {
if(firstland != (princess::challenge ? laPalace : laIce)) cheater++;
}
if(tactic::trailer) ;
#ifdef TOUR
else if(tour::on) ; // displayed by tour
#endif
else if(princess::challenge) {
kills[moVizier] = 1;
princess::forceMouse = true;
if(yendor::everwon)
items[itGreenStone] = 99;
addMessage(XLAT("Welcome to %the1 Challenge!", moPrincess));
addMessage(XLAT("The more Hypersian Rugs you collect, the harder it is.", moPrincess));
}
/* if(tactic::on && isCrossroads(firstland)) {
for(int i=0; i<ittypes; i++)
if(itemclass(eItem(i)) == IC_TREASURE && i != itHyperstone)
items[i] = 10;
kills[moYeti] = 1000;
} */
else if(randomPatternsMode)
addMessage(XLAT("Welcome to the Random Pattern mode!"));
else if(tactic::on)
addMessage(XLAT("You are playing %the1 in the Pure Tactics mode.", firstland));
else if(yendor::on)
addMessage(XLAT("Welcome to the Yendor Challenge %1!", its(yendor::challenge)));
else if(shmup::on) ; // welcome message given elsewhere
else if(euclid)
addMessage(XLAT("Welcome to the Euclidean mode!"));
else if(sphere && euclidland == laHalloween)
addMessage(XLAT("Welcome to Halloween!"));
else if(elliptic)
addMessage(XLAT("Good luck in the elliptic plane!"));
else if(sphere)
addMessage(XLAT("Welcome to Spherogue!"));
#ifdef ROGUEVIZ
else if(rogueviz::on)
addMessage(XLAT("Welcome to RogueViz!"));
#endif
else {
addMessage(XLAT("Welcome to HyperRogue!"));
#ifndef MOBILE
#ifdef IOS
addMessage(XLAT("Press F1 or right-shift-click things for help."));
#else
addMessage(XLAT("Press F1 or right-click things for help."));
#endif
#endif
}
}
else {
usedSafety = true;
safety = false;
}
havewhat = hadwhat = 0; rosemap.clear();
elec::lightningfast = 0;
lastsafety = gold();
bfs();
}
bool havesave = true;
#ifndef NOSAVE
#define MAXBOX 300
#define POSSCORE 258 // update this when new boxes are added!
struct score {
string ver;
int box[MAXBOX];
};
int savebox[MAXBOX], boxid;
bool saving, loading, loadingHi;
string boxname[MAXBOX];
bool fakebox[MAXBOX];
bool monsbox[MAXBOX];
void applyBox(int& t) {
if(saving) savebox[boxid++] = t;
else if(loading) t = savebox[boxid++];
else boxid++;
}
void applyBoxNum(int& i, string name = "") {
fakebox[boxid] = (name == "");
boxname[boxid] = name;
monsbox[boxid] = false;
applyBox(i);
}
void applyBoxBool(bool& b, string name = "") {
int i = b;
applyBoxNum(i, name);
monsbox[boxid] = false;
b = i;
}
// just skips the value when loading
void applyBoxSave(int i, string name = "") {
fakebox[boxid] = (name == "");
boxname[boxid] = name;
applyBox(i);
}
int applyBoxLoad(string name = "") {
fakebox[boxid] = (name == "");
boxname[boxid] = name;
int i=0; applyBox(i);
return i;
}
void applyBoxI(eItem it, bool f = false) {
boxname[boxid] = iinf[it].name;
fakebox[boxid] = f;
monsbox[boxid] = false;
if(loadingHi) {
updateHi(it, savebox[boxid++]);
}
else applyBox(items[it]);
}
void applyBoxM(eMonster m, bool f = false) {
fakebox[boxid] = f;
boxname[boxid] = minf[m].name;
monsbox[boxid] = true;
applyBox(kills[m]);
}
void applyBoxes() {
eLand lostin = laNone;
applyBoxSave((int) timerstart, "time elapsed");
time_t timer = time(NULL);
applyBoxSave((int) timer, "date");
applyBoxSave(gold(), "treasure collected");
applyBoxSave(tkills(), "total kills");
applyBoxNum(turncount, "turn count");
applyBoxNum(cellcount, "cells generated");
if(loading) timerstart = time(NULL);
for(int i=0; i<itOrbLightning; i++)
if(i == 0) items[i] = 0, applyBoxI(itFernFlower);
else applyBoxI(eItem(i));
for(int i=0; i<43; i++) {
if(loading) kills[i] = 0;
bool fake =
i == moLesserM || i == moNone || i == moWolfMoved || i == moTentacletail ||
i == moIvyNext;
if(i == moWormtail) applyBoxM(moCrystalSage);
else if(i == moWormwait) applyBoxM(moFireFairy);
else if(i == moTentacleEscaping) applyBoxM(moMiner);
else if(i == moGolemMoved) applyBoxM(moIllusion);
else if(i == moTentaclewait) applyBoxI(itOrbThorns, true);
else if(i == moGreater) applyBoxI(itOrbDragon, true);
else if(i == moGreaterM) applyBoxI(itOrbIllusion, true);
else applyBoxM(eMonster(i), fake);
}
if(saving) {
int totaltime = savetime;
if(!timerstopped) totaltime += timer - timerstart;
applyBoxSave((int) totaltime, "time played");
}
else if(loading) savetime = applyBoxLoad("time played");
else boxname[boxid] = "time played", boxid++;
if(saving) savecount++;
applyBoxNum(savecount, "number of saves");
if(saving) savecount--;
applyBoxNum(cheater, "number of cheats");
fakebox[boxid] = true;
if(saving) applyBoxSave(items[itOrbSafety] ? safetyland : cwt.c->land, "");
else if(loading) firstland = safetyland = eLand(applyBoxLoad());
else lostin = eLand(savebox[boxid++]);
for(int i=itOrbLightning; i<25; i++) applyBoxI(eItem(i), true);
applyBoxI(itRoyalJelly);
applyBoxI(itWine);
applyBoxI(itSilver);
applyBoxI(itEmerald);
applyBoxI(itPower);
applyBoxI(itOrbFire, true);
applyBoxI(itOrbInvis, true);
applyBoxI(itOrbAether, true);
applyBoxI(itOrbPsi, true);
applyBoxM(moBug0);
applyBoxM(moBug1);
applyBoxM(moBug2);
applyBoxM(moVineBeast);
applyBoxM(moVineSpirit);
applyBoxM(moLancer);
applyBoxM(moFlailer);
applyBoxM(moEarthElemental);
applyBoxM(moDarkTroll);
applyBoxM(moWitch);
applyBoxM(moWitchFire);
applyBoxM(moWitchFlash);
applyBoxM(moWitchGhost);
applyBoxM(moWitchSpeed);
applyBoxM(moEvilGolem);
applyBoxM(moWitchWinter);
applyBoxI(itHolyGrail);
applyBoxI(itGrimoire);
applyBoxM(moKnight);
applyBoxM(moCultistLeader);
applyBoxM(moPirate);
applyBoxM(moCShark);
applyBoxM(moParrot);
applyBoxI(itPirate);
applyBoxI(itOrbTime, true);
applyBoxM(moHexSnake);
applyBoxM(moRedTroll);
applyBoxI(itRedGem);
applyBoxI(itOrbSpace, true);
int geo = geometry;
applyBoxNum(geo, ""); geometry = eGeometry(geo);
applyBoxBool(hardcore, "hardcore");
applyBoxNum(hardcoreAt, "");
applyBoxBool(shmup::on, "shmup");
if(saving) applyBoxSave(euclidland, "euclid land");
else if(loading) euclidland = eLand(applyBoxLoad("euclid land"));
else fakebox[boxid++] = true;
applyBoxI(itCoast);
applyBoxI(itWhirlpool);
applyBoxI(itBombEgg);
applyBoxM(moBomberbird);
applyBoxM(moTameBomberbird);
applyBoxM(moAlbatross);
applyBoxI(itOrbFriend, true);
applyBoxI(itOrbAir, true);
applyBoxI(itOrbWater, true);
applyBoxI(itPalace);
applyBoxI(itFjord);
applyBoxI(itOrbFrog, true);
applyBoxI(itOrbDiscord, true);
applyBoxM(moPalace);
applyBoxM(moFatGuard);
applyBoxM(moSkeleton);
applyBoxM(moVizier);
applyBoxM(moViking);
applyBoxM(moFjordTroll);
applyBoxM(moWaterElemental);
applyBoxI(itSavedPrincess);
applyBoxI(itOrbLove, true);
applyBoxM(moPrincess);
applyBoxM(moPrincessMoved, false); // live Princess for Safety
applyBoxM(moPrincessArmedMoved, false); // live Princess for Safety
applyBoxM(moMouse);
applyBoxNum(princess::saveArmedHP, "");
applyBoxNum(princess::saveHP, "");
applyBoxI(itIvory);
applyBoxI(itElemental);
applyBoxI(itZebra);
applyBoxI(itFireShard);
applyBoxI(itWaterShard);
applyBoxI(itAirShard);
applyBoxI(itEarthShard);
applyBoxM(moAirElemental);
applyBoxM(moFireElemental);
applyBoxM(moFamiliar);
applyBoxM(moGargoyle);
applyBoxM(moOrangeDog);
applyBoxI(itOrbSummon, true);
applyBoxI(itOrbMatter, true);
applyBoxM(moForestTroll);
applyBoxM(moStormTroll);
applyBoxM(moOutlaw);
applyBoxM(moMutant);
applyBoxM(moMetalBeast);
applyBoxM(moMetalBeast2);
applyBoxI(itMutant);
applyBoxI(itFulgurite);
applyBoxI(itBounty);
applyBoxI(itOrbLuck, true);
applyBoxI(itOrbStunning, true);
applyBoxBool(tactic::on, "");
applyBoxNum(elec::lightningfast, "");
// if(savebox[boxid]) printf("lotus = %d (lost = %d)\n", savebox[boxid], isHaunted(lostin));
if(loadingHi && isHaunted(lostin)) boxid++;
else applyBoxI(itLotus);
applyBoxI(itOrbUndeath, true);
applyBoxI(itWindstone);
applyBoxI(itOrbEmpathy, true);
applyBoxM(moWindCrow);
applyBoxI(itMutant2);
applyBoxI(itOrbFreedom, true);
applyBoxM(moRedFox);
applyBoxBool(survivalist);
if(loadingHi) applyBoxI(itLotus);
else applyBoxNum(truelotus, "lotus/escape");
applyBoxBool(purehepta, "heptagons only");
applyBoxI(itRose);
applyBoxI(itOrbBeauty, true);
applyBoxI(itCoral);
applyBoxI(itOrb37, true);
applyBoxI(itOrbEnergy, true);
applyBoxM(moRatling);
applyBoxM(moFalsePrincess);
applyBoxM(moRoseLady);
applyBoxM(moRoseBeauty);
applyBoxBool(chaosmode, "Chaos mode");
applyBoxNum(multi::players, "shmup players");
if(multi::players < 1 || multi::players > MAXPLAYER)
multi::players = 1;
applyBoxM(moRatlingAvenger);
// printf("applybox %d\n", shmup::players);
applyBoxI(itApple);
applyBoxM(moSparrowhawk);
applyBoxM(moResearcher);
applyBoxI(itDragon);
applyBoxM(moDragonHead);
applyBoxI(itOrbDomination, true);
applyBoxI(itBabyTortoise);
applyBoxNum(tortoise::seekbits, "");
applyBoxM(moTortoise);
applyBoxI(itOrbShell, true);
applyBoxNum(safetyseed);
// (+18)
for(int i=0; i<6; i++) {
applyBoxNum(multi::treasures[i]);
applyBoxNum(multi::kills[i]);
applyBoxNum(multi::deaths[i]);
}
// (+8)
applyBoxM(moDragonTail);
applyBoxI(itKraken);
applyBoxM(moKrakenH);
applyBoxM(moKrakenT);
applyBoxI(itOrbSword, true);
applyBoxI(itBarrow);
applyBoxM(moDraugr);
applyBoxI(itOrbSword2, true);
applyBoxI(itTrollEgg);
applyBoxI(itOrbStone, true);
bool sph;
sph = false; applyBoxBool(sph, "sphere"); if(sph) geometry = gSphere;
sph = false; applyBoxBool(sph, "elliptic"); if(sph) geometry = gElliptic;
applyBox(princess::reviveAt);
applyBoxI(itDodeca);
applyBoxI(itAmethyst);
applyBoxI(itSlime);
applyBoxI(itOrbNature, true);
applyBoxI(itOrbDash, true);
// itOrbRecall should not be here
applyBoxM(moBat);
applyBoxM(moReptile);
applyBoxM(moFriendlyIvy);
applyBoxI(itGreenGrass);
applyBoxI(itBull);
applyBoxI(itOrbHorns, true);
applyBoxI(itOrbBull, true);
applyBoxM(moSleepBull);
applyBoxM(moRagingBull);
applyBoxM(moHerdBull);
applyBoxM(moButterfly);
applyBoxM(moGadfly);
if(POSSCORE != boxid) printf("ERROR: %d boxes\n", boxid);
}
void saveBox() {
boxid = 0; saving = true; applyBoxes(); saving = false;
}
void loadBox() {
// have boxid
boxid = 0; loading = true; applyBoxes(); loading = false;
}
void loadBoxHigh() {
dynamicval<int> sp1(multi::players, savebox[197]);
dynamicval<eGeometry> sp2(geometry, (eGeometry) savebox[116]);
dynamicval<bool> sp3(shmup::on, savebox[119]);
dynamicval<bool> sp4(chaosmode, savebox[196]);
dynamicval<bool> sp5(purehepta, savebox[186]);
if(savebox[238]) geometry = gSphere;
if(savebox[239]) geometry = gElliptic;
if(multi::players < 1 || multi::players > MAXPLAYER)
multi::players = 1;
if(shmup::on && multi::players == 1) ;
else {
// have boxid
boxid = 0; loadingHi = true; applyBoxes(); loadingHi = false;
}
}
// certify that saves and achievements were received
// in an official version of HyperRogue
#ifdef CERTIFY
#include "certify.cpp"
#else
namespace anticheat {
bool tampered;
void save(FILE *f) {}
bool load(FILE *f, score& sc, const string& ver) {return true; }
int certify(const string& s, int a, int b, int c, int d=0) { return d; }
int check(int cv, const string& ver, const string& s, int a, int b, int c, int d=0) { return cv==d; }
void nextid(int& tid, const string& ver, int cert) { tid++; }
};
#endif
long long saveposition = -1;
#include <unistd.h>
#include <sys/types.h>
void remove_emergency_save() {
#ifndef WINDOWS
if(saveposition >= 0) {
/* if(!timerghost)
addMessage(XLAT("Emergency truncate to ") + its(saveposition)); */
if(truncate(scorefile, saveposition)) {}
saveposition = -1;
}
#endif
}
void saveStats(bool emergency = false) {
DEBB(DF_INIT, (debugfile,"saveStats [%s]\n", scorefile));
if(autocheat) return;
#ifdef TOUR
if(tour::on) return;
#endif
if(randomPatternsMode) return;
remove_emergency_save();
FILE *f = fopen(scorefile, "at");
if(!f) {
// printf("Could not open the score file '%s'!\n", scorefile);
addMessage(XLAT("Could not open the score file: ") + scorefile);
return;
}
if(emergency) {
saveposition = ftell(f);
// if(!timerghost) addMessage(XLAT("Emergency save at ") + its(saveposition));
}
if(showoff) { fclose(f); return; }
time_t timer;
timer = time(NULL);
char sbuf[128]; strftime(sbuf, 128, "%c", localtime(&timerstart));
char buf[128]; strftime(buf, 128, "%c", localtime(&timer));
if((tactic::on || yendor::on) && !items[itOrbSafety] && !cheater) {
int t = (int) (timer - timerstart);
int xcode = modecode();
if(tactic::on) {
int score = items[treasureType(tactic::lasttactic)];
if(score) {
int c =
anticheat::certify(dnameof(tactic::lasttactic), turncount, t, (int) timerstart,
xcode*999 + tactic::id + 256 * score);
fprintf(f, "TACTICS %s %d %d %d %d %d %d %d date: %s\n", VER,
tactic::id, tactic::lasttactic, score, turncount, t, int(timerstart),
c, buf);
tactic::record(tactic::lasttactic, score);
anticheat::nextid(tactic::id, VER, c);
}
}
if(yendor::on)
fprintf(f, "YENDOR %s %d %d %d %d %d %d %d date: %s\n", VER,
yendor::lastchallenge, items[itOrbYendor], yendor::won, turncount, t, int(timerstart),
anticheat::certify(yendor::won ? "WON" : "LOST", turncount, t, (int) timerstart,
xcode*999 + yendor::lastchallenge + 256 * items[itOrbYendor]),
buf);
fclose(f);
return;
}
fprintf(f, "HyperRogue: game statistics (version " VER ")\n");
if(cheater)
fprintf(f, "CHEATER! (cheated %d times)\n", cheater);
if(true) {
fprintf(f, VER);
if(!shmup::on) items[itOrbLife] = countMyGolems(moGolem);
if(!shmup::on) items[itOrbFriend] = countMyGolems(moTameBomberbird);
if(!shmup::on) kills[moPrincessMoved] = countMyGolems(moPrincess);
if(!shmup::on) kills[moPrincessArmedMoved] = countMyGolems(moPrincessArmed);
if(!shmup::on) princess::saveHP = countMyGolemsHP(moPrincess);
if(!shmup::on) princess::saveArmedHP = countMyGolemsHP(moPrincessArmed);
saveBox();
for(int i=0; i<boxid; i++) fprintf(f, " %d", savebox[i]);
anticheat::save(f);
fprintf(f, "\n");
}
fprintf(f, "Played on: %s - %s (%d %s)\n", sbuf, buf, turncount,
shmup::on ? "knives" : "turns");
fprintf(f, "Total wealth: %d\n", gold());
fprintf(f, "Total enemies killed: %d\n", tkills());
fprintf(f, "cells generated: %d\n", cellcount);
if(pureHardcore()) fprintf(f, "Pure hardcore mode\n");
if(purehepta) fprintf(f, "Heptagons only mode\n");
if(chaosmode) fprintf(f, "Chaos mode\n");
if(shmup::on) fprintf(f, "Shoot-em up mode\n");
if(multi::players > 1) fprintf(f, "Multi-player (%d players)\n", multi::players);
fprintf(f, "Number of cells explored, by distance from the player:\n");
{for(int i=0; i<10; i++) fprintf(f, " %d", explore[i]);} fprintf(f, "\n");
/*for(int j=0; j<landtypes; j++) {
bool haveland = false;
for(int i=0; i<10; i++)
if(exploreland[i][j])
haveland = true;
if(haveland)
for(int i=0; i<10; i++)
fprintf(f, " %d", exploreland[i][j]);
fprintf(f, " %s\n", linf[j].name);
} */
if(kills[0]) fprintf(f, "walls melted: %d\n", kills[0]);
fprintf(f, "cells travelled: %d\n", celldist(cwt.c));
fprintf(f, "\n");
for(int i=0; i<ittypes; i++) if(items[i])
fprintf(f, "%4dx %s\n", items[i], iinf[i].name);
fprintf(f, "\n");
for(int i=1; i<motypes; i++) if(kills[i])
fprintf(f, "%4dx %s\n", kills[i], minf[i].name);
fprintf(f, "\n\n\n");
#ifndef MOBILE
DEBB(DF_INIT, (debugfile, "Game statistics saved to %s\n", scorefile));
if(!tactic::trailer)
addMessage(XLAT("Game statistics saved to %1", scorefile));
#endif
fclose(f);
}
// load the save
void loadsave() {
if(autocheat) return;
#ifdef TOUR
if(tour::on) return;
#endif
DEBB(DF_INIT, (debugfile,"loadSave\n"));
for(int xc=0; xc<MODECODES; xc++)
for(int i=0; i<landtypes; i++) for(int j=0; j<MAXTAC; j++)
tactic::lsc[xc][i][j] = -1;
gamecount = 0;
FILE *f = fopen(scorefile, "rt");
havesave = f;
if(!f) return;
score sc;
bool ok = false;
bool tamper = false;
while(!feof(f)) {
char buf[120];
if(fgets(buf, 120, f) == NULL) break;
if(buf[0] == 'H' && buf[1] == 'y') {
gamecount++;
if(fscanf(f, "%s", buf) <= 0) break;
sc.ver = buf;
if(sc.ver < "4.4" || sc.ver == "CHEATER!") { ok = false; continue; }
ok = true;
for(int i=0; i<MAXBOX; i++) {
if(fscanf(f, "%d", &sc.box[i]) <= 0) {
boxid = i;
tamper = anticheat::load(f, sc, sc.ver);
for(int i=0; i<boxid; i++) savebox[i] = sc.box[i];
for(int i=boxid; i<MAXBOX; i++) savebox[i] = 0, sc.box[i] = 0;
loadBoxHigh();
break;
}
}
}
if(buf[0] == 'A' && buf[1] == 'C' && buf[2] == 'H') {
char buf1[80], buf2[80];
sscanf(buf, "%70s%70s", buf1, buf2);
if(buf2 == string("PRINCESS1")) princess::everSaved = true;
if(buf2 == string("YENDOR2")) yendor::everwon = true;
if(buf2 == string("CR4")) chaosUnlocked = true;
}
if(buf[0] == 'T' && buf[1] == 'A' && buf[2] == 'C') {
ok = false;
char buf1[80], ver[10];
int tid, land, score, tc, t, ts, cert;
sscanf(buf, "%70s%10s%d%d%d%d%d%d%d",
buf1, ver, &tid, &land, &score, &tc, &t, &ts, &cert);
for(int xc=0; xc<MODECODES; xc++)
if(tid == tactic::id && (anticheat::check(cert, ver, dnameof(eLand(land)), tc, t, ts, xc*999+tid + 256 * score))) {
if(score != 0 && !(land == laOcean && ver < string("8.0f")))
tactic::record(eLand(land), score, xc);
anticheat::nextid(tactic::id, ver, cert);
break;
}
}
if(buf[0] == 'Y' && buf[1] == 'E' && buf[2] == 'N') {
char buf1[80], ver[10];
int cid, oy, won, tc, t, ts, cert=0;
sscanf(buf, "%70s%10s%d%d%d%d%d%d%d",
buf1, ver, &cid, &oy, &won, &tc, &t, &ts, &cert);
if(won) for(int xc=0; xc<MODECODES; xc++)
if(anticheat::check(cert, ver, won ? "WON" : "LOST", tc, t, ts, xc*999 + cid + 256 * oy)) {
if(xc == 19 && cid == 25) xc = 0;
if(cid > 0 && cid < YENDORLEVELS)
if(!(ver < string("8.0f") && oy > 1 && cid == 15))
if(!(ver < string("9.3b") && oy > 1 && (cid == 27 || cid == 28)))
{
yendor::bestscore[xc][cid] = max(yendor::bestscore[xc][cid], oy);
}
break;
}
}
}
fclose(f);
if(ok && sc.box[65 + 4 + itOrbSafety - itOrbLightning]) {
anticheat::tampered = tamper;
// printf("box = %d (%d)\n", sc.box[65 + 4 + itOrbSafety - itOrbLightning], boxid);
// printf("boxid = %d\n", boxid);
for(int i=0; i<boxid; i++) savebox[i] = sc.box[i];
for(int i=boxid; i<MAXBOX; i++) savebox[i] = 0;
// for(int i=160; i<200; i++) printf("%d: %d ", i, savebox[i]);
loadBox();
// printf("boxid = %d\n", boxid);
if(items[itHolyGrail]) {
items[itHolyGrail]--;
knighted = newRoundTableRadius();
items[itHolyGrail]++;
}
else knighted = 0;
safety = true;
if(items[itSavedPrincess] < 0) items[itSavedPrincess] = 0;
addMessage(XLAT("Game loaded."));
}
}
#endif
namespace gamestack {
struct gamedata {
hrmap *hmap;
cellwalker cwt;
heptspin viewctr;
transmatrix View;
eGeometry geometry;
bool shmup;
bool hepta;
};
vector<gamedata> gd;
bool pushed() { return size(gd); }
void push() {
if(geometry) {
printf("ERROR: push implemented only in non-hyperbolic geometry\n");
exit(1);
}
gamedata gdn;
gdn.hmap = currentmap;
gdn.cwt = cwt;
gdn.viewctr = viewctr;
gdn.View = View;
gdn.geometry = geometry;
gdn.shmup = shmup::on;
gdn.hepta = purehepta;
gd.push_back(gdn);
}
void pop() {
gamedata& gdn = gd[size(gd)-1];
currentmap = gdn.hmap;
cwt = gdn.cwt;
viewctr = gdn.viewctr;
View = gdn.View;
geometry = gdn.geometry;
purehepta = gdn.hepta;
if(shmup::on) shmup::clearMonsters();
shmup::on = gdn.shmup;
resetGeometry();
gd.pop_back();
bfs();
}
};
void restartGame(char switchWhat, bool push) {
DEBB(DF_INIT, (debugfile,"restartGame\n"));
if(push)
gamestack::push();
else if(gamestack::pushed()) {
gamestack::pop();
return;
}
else {
achievement_final(true);
#ifndef NOSAVE
saveStats();
#endif
for(int i=0; i<ittypes; i++) items[i] = 0;
lastkills = 0; for(int i=0; i<motypes; i++) kills[i] = 0;
for(int i=0; i<10; i++) explore[i] = 0;
for(int i=0; i<10; i++) for(int l=0; l<landtypes; l++)
exploreland[i][l] = 0;
for(int i=0; i<numplayers(); i++)
if(multi::playerActive(i))
multi::deaths[i]++;
#ifndef NOSAVE
anticheat::tampered = false;
#endif
achievementsReceived.clear();
princess::saved = false;
princess::reviveAt = 0;
princess::forceVizier = false;
princess::forceMouse = false;
knighted = 0;
// items[itGreenStone] = 100;
cellcount = 0;
clearMemory();
}
if(switchWhat == 'C') {
geometry = gNormal;
yendor::on = tactic::on = princess::challenge = false;
resetGeometry();
chaosmode = !chaosmode;
}
#ifdef TOUR
if(switchWhat == 'T') {
geometry = gNormal;
yendor::on = tactic::on = princess::challenge = false;
chaosmode = purehepta = randomPatternsMode = false;
shmup::on = false;
resetGeometry();
tour::on = !tour::on;
}
#endif
if(switchWhat == '7') {
if(euclid) geometry = gNormal;
purehepta = !purehepta;
resetGeometry();
}
if(switchWhat == 'g') {
if(geometry == targetgeometry) geometry = gNormal;
else geometry = targetgeometry;
if(chaosmode && geometry != gNormal) chaosmode = false;
if(purehepta && euclid) purehepta = false;
resetGeometry();
}
if(switchWhat == 'y') {
yendor::on = !yendor::on;
tactic::on = false;
princess::challenge = false;
randomPatternsMode = false;
chaosmode = false;
if(!yendor::on) firstland = laIce;
}
if(switchWhat == 't') {
tactic::on = !tactic::on;
yendor::on = false;
randomPatternsMode = false;
princess::challenge = false;
chaosmode = false;
if(!tactic::on) firstland = laIce;
}
if(switchWhat == 's') {
shmup::on = !shmup::on;
princess::challenge = false;
}
if(switchWhat == 'r') {
randomPatternsMode = !randomPatternsMode;
tactic::on = false;
yendor::on = false;
princess::challenge = false;
}
if(switchWhat == 'p') {
princess::challenge = !princess::challenge;
firstland = princess::challenge ? laPalace : laIce;
shmup::on = false;
tactic::on = false;
yendor::on = false;
chaosmode = false;
}
initcells();
if(randomPatternsMode) {
for(int i=0; i<landtypes; i++) {
randompattern[i] = hrandpos();
// change probability 1/5 to 2/6
if(hrand(5) == 0) {
randompattern[i] -= (randompattern[i] % 5);
}
}
if(randomPatternsMode) firstland = pickLandRPM(laNone);
clearMemoRPM();
}
initgame();
canmove = true;
restartGraph();
resetmusic();
resetmusic();
}
void clearGameMemory() {
#ifdef HASLINEVIEW
conformal::renderAutoband();
conformal::on = false;
#endif
DEBB(DF_INIT, (debugfile,"clearGameMemory\n"));
pathq.clear();
dcal.clear();
yendor::yii = NOYENDOR; yendor::yi.clear();
clearshadow();
offscreen.clear();
princess::clear();
buggycells.clear();
mirrors.clear();
clearing::bpdata.clear();
tortoise::emap.clear();
tortoise::babymap.clear();
seenSevenMines = false;
#ifdef HASLINEVIEW
conformal::killhistory.clear();
conformal::findhistory.clear();
conformal::movehistory.clear();
conformal::includeHistory = false;
#endif
recallCell = NULL;
prairie::lasttreasure = NULL;
prairie::enter = NULL;
prairie::tchoices.clear();
prairie::beaststogen.clear();
butterflies.clear();
#ifdef ROGUEVIZ
rogueviz::close();
#endif
}
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.c->land)) q -= 8;
if(a == itElixir && isCrossroads(cwt.c->land)) q -= 7;
if(a == itIvory && isCrossroads(cwt.c->land)) q -= 6;
if(a == itPalace && isCrossroads(cwt.c->land)) q -= 5;
if(a == itIvory && cwt.c->land == laJungle) q -= 5;
if(a == itIvory && cwt.c->land == laPalace) q -= 5;
if(q < bq) bq = q, cq = 0;
if(q == bq) { cq++; if(hrand(cq) == 0) best = i; }
}
return best;
}
bool isTechnicalLand(eLand l) {
return l == laNone || l == laOceanWall || l == laBarrier || l == laCanvas ||
l == laHauntedWall || l == laHauntedBorder || l == laCA;
}
eLand cheatdest;
void cheatMoveTo(eLand l) {
cheatdest = l;
if(l == laCrossroads5) l = laCrossroads;
activateSafety(l);
cheatdest = laNone;
}
bool applyCheat(char u, cell *c = NULL) {
if(u == 'M' && cwt.c->type == 6) {
addMessage(XLAT("You summon some Mirages!"));
cheater++;
mirror::createMirrors(cwt.c, cwt.spin, moMirage),
mirror::createMirages(cwt.c, cwt.spin, moMirage);
return true;
}
if(u == 'G') {
addMessage(XLAT("You summon a golem!"));
cheater++;
int i = cwt.spin;
if(passable(cwt.c->mov[i], NULL, 0))
cwt.c->mov[i]->monst = hrand(2) ? moGolem : moTameBomberbird;
return true;
}
if(u == 'L') {
do {
if(firstland == eLand(landtypes-1))
firstland = eLand(2);
else
firstland = eLand(firstland+1);
}
while(isTechnicalLand(firstland) || isCyclic(firstland));
euclidland = firstland;
cheater++; addMessage(XLAT("You will now start your games in %1", firstland));
return true;
}
if(u == 'C') {
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] = 10;
}
kills[moYeti] = 200;
kills[moDesertman] = 200;
kills[moRunDog] = 200;
kills[moZombie] = 200;
kills[moMonkey] = 200;
kills[moCultist] = 200;
kills[moTroll] = 200;
return true;
}
if(u == 'P') {
for(int i=0; i<ittypes; i++)
if(itemclass(eItem(i)) == IC_ORB)
items[i] = 0;
cheater++; addMessage(XLAT("Orb power depleted!"));
return true;
}
if(u == 'O') {
cheater++; addMessage(XLAT("Orbs summoned!"));
for(int i=0; i<cwt.c->type; i++)
if(passable(cwt.c->mov[i], NULL, 0)) {
eItem it = nextOrb();
cwt.c->mov[i]->item = it;
}
return true;
}
if(u == 'F') {
if(hardcore && !canmove) {
canmove = true;
addMessage(XLAT("Revived!"));
}
else {
items[itOrbFlash] += 1;
items[itOrbTeleport] += 1;
items[itOrbLightning] += 1;
items[itOrbSpeed] += 1;
items[itOrbShield] += 1;
cheater++; addMessage(XLAT("Orb power gained!"));
canmove = true;
if(cmode == emQuit) cmode = emNormal;
}
return true;
}
if(u == 'D') {
items[itGreenStone] += 10;
cheater++; addMessage(XLAT("Dead orbs gained!"));
return true;
}
if(u == 'R'-64) buildRosemap();
#ifndef NOEDIT
if(u == 'A') {
lastexplore = turncount;
cmode = emMapEditor;
return true;
}
if(u == 'A'-64) {
mapeditor::drawcell = mouseover ? mouseover : cwt.c;
cmode = emDraw;
return true;
}
#endif
if(u == 'Y') {
items[itOrbYendor] ++;
cheater++; addMessage(XLAT("Orb of Yendor gained!"));
return true;
}
if(u == 'T') {
items[randomTreasure2(10)] += 10;
cheater++; addMessage(XLAT("Treasure gained!"));
return true;
}
if(u == 'T'-64) {
items[randomTreasure2(100)] += 100;
cheater++; addMessage(XLAT("Lots of treasure gained!"));
return true;
}
if(u == 'I'-64) {
items[randomTreasure2(10)] += 25;
cheater++; addMessage(XLAT("Treasure gained!"));
return true;
}
if(u == 'U'-64) {
items[randomTreasure2(10)] += 50;
cheater++; addMessage(XLAT("Treasure gained!"));
return true;
}
if(u == 'W') {
addMessage(XLAT("You summon a sandworm!"));
cheater++;
int i = cwt.spin;
if(passable(cwt.c->mov[i], NULL, 0))
cwt.c->mov[i]->monst = moWorm,
cwt.c->mov[i]->mondir = NODIR;
return true;
}
if(u == 'I') {
addMessage(XLAT("You summon an Ivy!"));
cheater++;
int i = cwt.spin;
int j = cwt.c->spn(i);
cell* c = cwt.c->mov[i]->mov[(j+3)%cwt.c->mov[i]->type];
if(passable(c, NULL, 0)) buildIvy(c, 0, 1);
return true;
}
if(u == 'E') {
addMessage(XLAT("You summon a monster!"));
cheater++;
int i = cwt.spin;
if(cwt.c->mov[i]->wall == waChasm)
cwt.c->mov[i]->wall = waNone;
if(passable(cwt.c->mov[i], NULL, P_MONSTER)) {
eMonster mo[9] = { moEagle, moPyroCultist, moGhost, moTroll, moMiner, moVineBeast, moBug0,
moBomberbird, moSkeleton };
cwt.c->mov[i]->monst = mo[hrand(9)];
cwt.c->mov[i]->stuntime = 3;
cwt.c->mov[i]->hitpoints = 3;
}
return true;
}
if(u == 'E'-64) {
if(geometry) {
restartGame(0, false);
}
else {
euclidland = cwt.c->land;
printf("target geometry = %d\n", targetgeometry);
restartGame('g', true);
}
return true;
}
if(u == 'H') {
addMessage(XLAT("You summon some Thumpers!"));
cheater++;
for(int i=0; i<cwt.c->type; i++)
if(passable(cwt.c->mov[i], NULL, 0)) {
eWall ws[3] = { waThumperOff, waBigStatue, waBoat };
cwt.c->mov[i]->wall = ws[hrand(3)];
}
return true;
}
if(u == 'B') {
addMessage(XLAT("You summon a bonfire!"));
cheater++;
int i = cwt.spin;
if(passable(cwt.c->mov[i], NULL, 0))
cwt.c->mov[i]->wall = waBonfireOff;
return true;
}
if(u == 'Z') {
flipplayer = false;
mirror::spin(1);
cwspin(cwt, 1);
return true;
}
if(u == 'J') {
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!"));
return true;
}
if(u == 'K') {
for(int i=0; i<motypes; i++) kills[i] += 10;
kills[moPlayer] = 0;
cheater++; addMessage(XLAT("Kills gained!"));
return true;
}
if(u == 'S') {
canmove = true;
cheatMoveTo(cwt.c->land);
items[itOrbSafety] += 3;
cheater++; addMessage(XLAT("Activated Orb of Safety!"));
return true;
}
if(u == 'U') {
canmove = true;
cheatMoveTo(firstland);
cheater++; addMessage(XLAT("Teleported to %1!", firstland));
return true;
}
if(u == 'W'-64) {
cmode = emLinepattern;
return true;
}
if(u == 'G'-64) {
timerghost = !timerghost;
cheater++;
addMessage(XLAT("turn count = %1 last exploration = %2 ghost timer = %3",
its(turncount), its(lastexplore), ONOFF(timerghost)));
return true;
}
if(u == 'F'-64) {
items[itOrbShield] += 30;
return true;
}
if(u == 'Y'-64) {
int i = cwt.spin;
cwt.c->mov[i]->item = itOrbYendor;
return true;
}
if(u == 'B'-64) {
int i = cwt.spin;
sword::angle[0]++;
cwt.c->mov[i]->item = hrand(2) ? itOrbSword2 : itOrbSword;
return true;
}
if(u == 'X'-64) {
items[itOrbNature] += 50;
cheater++;
return true;
}
if(u == 'V'-64) {
viewdists = !viewdists;
return true;
}
if(u == 'L'-64) {
cell *c = mouseover;
describeCell(c);
printf("Neighbors:"); for(int i=0; i<c->type; i++) printf("%p ", c->mov[i]);
printf("Barrier: dir=%d left=%d right=%d\n",
c->bardir, c->barleft, c->barright);
return true;
}
if(u == 'C'-64) {
cblind = !cblind;
return true;
}
#ifdef LOCAL
if(u == 'K'-64) {
printf("viewctr = %p.%d\n", viewctr.h, viewctr.spin);
display(View);
}
if(u == 'D'-64) {
cheater = 0; autocheat = 0;
return true;
}
#endif
return false;
}