Updated the source files to 9.4c

This commit is contained in:
Zeno Rogue 2017-03-23 11:53:57 +01:00
parent 1e3612939c
commit 58e053f183
39 changed files with 44729 additions and 30565 deletions

View File

@ -1,7 +1,7 @@
// Hyperbolic Rogue -- achievements
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#define NUMLEADER 57
#define NUMLEADER 69
#define SCORE_UNKNOWN (-1)
#define NO_SCORE_YET (-2)
@ -16,7 +16,7 @@ const char* leadernames[NUMLEADER] = {
"Score", "Diamonds", "Gold", "Spice", "Rubies", "Elixirs",
"Shards", "Totems", "Daisies", "Statues", "Feathers", "Sapphires",
"Hyperstones", "Time to Win-71", "Turns to Win-71",
"Time to 10 Hyperstones-83", "Turns to 10 Hyperstones-83", "Orbs of Yendor",
"Time to 10 Hyperstones-94", "Turns to 10 Hyperstones-94", "Orbs of Yendor",
"Fern Flowers",
"Royal Jellies", "Powerstones", "Silver", "Wine", "Emeralds", "Grimoires",
"Holy Grails", "Red Gems", "Pirate Treasures",
@ -47,8 +47,23 @@ const char* leadernames[NUMLEADER] = {
"Tortoise points", // 54
"Dragon Scales", // 55
"Apples", // 56
"Heptagonal Mode", // 57
"Sunken Treasures", // 58
"Ancient Jewelry", // 59
"Golden Eggs", // 60
"Multiplayer Score", // 61
"Statistics", // 62
"Halloween", // 63
"Amethysts", // 64
"Slime Molds", // 65
"Dodecahedra", // 66
"Green Grass", // 67
"Spinel" // 68
};
#define LB_STATISTICS 62
#define LB_HALLOWEEN 63
bool haveLeaderboard(int id);
void upload_score(int id, int v);
@ -59,13 +74,18 @@ int achievementTimer;
bool wrongMode(char flags) {
if(cheater) return true;
if(flags == 'x') return false;
if(purehepta != (flags == '7')) return true;
if(euclid != (flags == 'e')) return true;
if(sphere != (flags == 'E')) return true;
if((quotient == 1) != (flags == 'q')) return true;
if((quotient == 2) != (flags == 'Q')) return true;
if(shmup::on != (flags == 's')) return true;
if(randomPatternsMode) return true;
if(yendor::on) return true;
if(tactic::on) return true;
if(chaosmode != (flags == 'C')) return true;
if((numplayers() > 1) != (flags == 'm')) return true;
return false;
}
@ -80,10 +100,13 @@ void achievement_log(const char* s, char flags) {
for(int i=0; i<size(achievementsReceived); i++)
if(achievementsReceived[i] == s) return;
achievementsReceived.push_back(s);
#ifndef NOSAVE
remove_emergency_save();
#ifndef ANDROID
FILE *f = fopen(scorefile, "at");
if(!f) return;
int t = (int) (time(NULL) - timerstart);
time_t timer = time(NULL);
@ -101,6 +124,7 @@ void improveItemScores();
#include "hypersteam.cpp"
#else
#ifndef ANDROID
#ifndef IOS
void achievement_init() {}
void achievement_close() {}
void achievement_gain(const char* s, char flags) {
@ -108,11 +132,18 @@ void achievement_gain(const char* s, char flags) {
}
#endif
#endif
#endif
void achievement_collection(eItem it, int prevgold, int newgold) {
if(cheater) return;
if(randomPatternsMode) return;
int q = items[it];
if(it == itTreat && q == 50)
achievement_gain("HALLOWEEN1", 'E');
if(it == itTreat && q == 100)
achievement_gain("HALLOWEEN2", 'E');
if(q == 1) {
if(it == itDiamond) achievement_gain("DIAMOND1");
@ -142,7 +173,7 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itPalace) achievement_gain("RUG1");
if(it == itFjord) achievement_gain("GARNET1");
if(it == itEdge) achievement_gain("TOWER1");
if(it == itIvory) achievement_gain("TOWER1");
if(it == itElemental) achievement_gain("ELEMENT1");
if(it == itZebra) achievement_gain("ZEBRA1");
@ -157,6 +188,17 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itBabyTortoise) achievement_gain("TORTOISE1");
if(it == itDragon) achievement_gain("DRAGON1");
if(it == itApple) achievement_gain("APPLE1");
if(it == itKraken) achievement_gain("KRAKEN1");
if(it == itBarrow) achievement_gain("BARROW1");
if(it == itTrollEgg) achievement_gain("TROLL1");
if(it == itAmethyst) achievement_gain("MOUNT1");
if(it == itSlime) achievement_gain("DUNG1");
if(it == itDodeca) achievement_gain("DOD1");
if(it == itGreenGrass) achievement_gain("PRAIR1");
if(it == itBull) achievement_gain("BULL1");
}
// 32
@ -194,7 +236,7 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itPalace) achievement_gain("RUG2");
if(it == itFjord) achievement_gain("GARNET2");
if(it == itEdge) achievement_gain("TOWER2");
if(it == itIvory) achievement_gain("TOWER2");
if(it == itElemental) achievement_gain("ELEMENT2");
if(it == itZebra) achievement_gain("ZEBRA2");
@ -209,6 +251,17 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itBabyTortoise) achievement_gain("TORTOISE2");
if(it == itDragon) achievement_gain("DRAGON2");
if(it == itApple) achievement_gain("APPLE2");
if(it == itKraken) achievement_gain("KRAKEN2");
if(it == itBarrow) achievement_gain("BARROW2");
if(it == itTrollEgg) achievement_gain("TROLL2");
if(it == itAmethyst) achievement_gain("MOUNT2");
if(it == itSlime) achievement_gain("DUNG2");
if(it == itDodeca) achievement_gain("DOD2");
if(it == itGreenGrass) achievement_gain("PRAIR2");
if(it == itBull) achievement_gain("BULL2");
}
if(q == 25) {
@ -239,7 +292,7 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itPalace) achievement_gain("RUG3");
if(it == itFjord) achievement_gain("GARNET3");
if(it == itEdge) achievement_gain("TOWER3");
if(it == itIvory) achievement_gain("TOWER3");
if(it == itElemental) achievement_gain("ELEMENT3");
if(it == itZebra) achievement_gain("ZEBRA3");
@ -257,6 +310,17 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itBabyTortoise) achievement_gain("TORTOISE3");
if(it == itDragon) achievement_gain("DRAGON3");
if(it == itApple) achievement_gain("APPLE3");
if(it == itKraken) achievement_gain("KRAKEN3");
if(it == itBarrow) achievement_gain("BARROW3");
if(it == itTrollEgg) achievement_gain("TROLL3");
if(it == itAmethyst) achievement_gain("MOUNT3");
if(it == itSlime) achievement_gain("DUNG3");
if(it == itDodeca) achievement_gain("DOD3");
if(it == itGreenGrass) achievement_gain("PRAIR3");
if(it == itBull) achievement_gain("BULL3");
}
if(q == 50) {
@ -287,7 +351,7 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itPalace) achievement_gain("RUG4");
if(it == itFjord) achievement_gain("GARNET4");
if(it == itEdge) achievement_gain("TOWER4");
if(it == itIvory) achievement_gain("TOWER4");
if(it == itElemental) achievement_gain("ELEMENT4");
if(it == itZebra) achievement_gain("ZEBRA4");
@ -302,6 +366,17 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itBabyTortoise) achievement_gain("TORTOISE4");
if(it == itDragon) achievement_gain("DRAGON4");
if(it == itApple) achievement_gain("APPLE4");
if(it == itKraken) achievement_gain("KRAKEN4");
if(it == itBarrow) achievement_gain("BARROW4");
if(it == itTrollEgg) achievement_gain("TROLL4");
if(it == itAmethyst) achievement_gain("MOUNT4");
if(it == itSlime) achievement_gain("DUNG4");
if(it == itDodeca) achievement_gain("DOD4");
if(it == itGreenGrass) achievement_gain("PRAIR4");
if(it == itBull) achievement_gain("BULL4");
}
if(it == itOrbYendor) {
@ -323,6 +398,8 @@ void achievement_count(const string& s, int current, int prev) {
achievement_gain("STABBER1");
if(s == "STAB" && current >= 2)
achievement_gain("STABBER2");
if(s == "SLASH" && current >= 2)
achievement_gain("SLASH2");
if(s == "STAB" && current >= 4)
achievement_gain("STABBER3");
if(s == "MIRRORKILL" && current-prev >= 1)
@ -374,6 +451,9 @@ void achievement_score(int cat, int number) {
#ifdef HAVE_ACHIEVEMENTS
if(cheater) return;
if(euclid) return;
if(sphere && cat != LB_HALLOWEEN) return;
if(quotient) return;
if(elliptic && cat != LB_HALLOWEEN) return;
if(purehepta) return;
if(randomPatternsMode) return;
if(shmup::on && cat != LB_PURE_TACTICS_SHMUP && cat != LB_PURE_TACTICS_COOP) return;
@ -403,7 +483,7 @@ void improveItemScores() {
improve_score(34, itPalace);
improve_score(35, itFjord);
improve_score(37, itEdge);
improve_score(37, itIvory);
improve_score(38, itElemental);
improve_score(39, itZebra);
@ -420,14 +500,34 @@ void improveItemScores() {
improve_score(54, itBabyTortoise);
improve_score(55, itDragon);
improve_score(56, itApple);
improve_score(58, itKraken);
improve_score(59, itBarrow);
improve_score(60, itTrollEgg);
improve_score(64, itAmethyst);
improve_score(65, itSlime);
improve_score(66, itDodeca);
improve_score(67, itGreenGrass);
improve_score(68, itBull);
}
void achievement_final(bool really_final) {
if(offlineMode) return;
#ifdef HAVE_ACHIEVEMENTS
upload_score(LB_STATISTICS, time(NULL));
if(cheater) return;
if(sphere && euclidland == laHalloween) {
if(shmup::on || chaosmode || purehepta || numplayers() > 1 || tactic::on || randomPatternsMode)
return;
achievement_score(LB_HALLOWEEN, items[itTreat]);
}
if(euclid) return;
if(purehepta) return;
if(sphere) return;
if(elliptic) return;
if(randomPatternsMode) return;
if(tactic::on) {
@ -439,15 +539,24 @@ void achievement_final(bool really_final) {
if(yendor::on) return;
if(shmup::on && chaosmode) return;
// no leaderboards for two special modes at once
int specials = 0;
if(shmup::on) specials++;
if(chaosmode) specials++;
if(purehepta) specials++;
if(specials > 1) return;
if(numplayers() > 1 && chaosmode) return;
if(numplayers() > 1 && purehepta) return;
int total_improved = 0;
specific_improved = 0;
specific_what = 0;
if(!shmup::on && !chaosmode) improveItemScores();
if(!shmup::on && !chaosmode && !purehepta && numplayers() == 1) improveItemScores();
int sid = chaosmode ? 53 : shmup::on ? (numplayers() > 1 ? 44 : 28) : 0;
int sid = purehepta ? 57 : chaosmode ? 53 : shmup::on ? (numplayers() > 1 ? 44 : 28) :
(numplayers() > 1 ? 61 : 0);
int tg = gold();
if(tg && haveLeaderboard(sid)) {
@ -488,6 +597,8 @@ void achievement_victory(bool hyper) {
#ifdef HAVE_ACHIEVEMENTS
if(cheater) return;
if(euclid) return;
if(sphere) return;
if(quotient) return;
if(purehepta) return;
if(randomPatternsMode) return;
if(hyper && shmup::on) return;
@ -572,3 +683,7 @@ void achievement_display() {
#endif
}
bool isAscending(int i) {
return i == 13 || i == 14 || i == 15 || i == 16 || i == 29 || i == 30 || i == 45;
};

650
cell.cpp
View File

@ -1,10 +1,11 @@
// Hyperbolic Rogue -- cells
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
// cells the game is played on
int fix6(int a) { return (a+96)% 6; }
int fix7(int a) { return (a+84)% 7; }
int fix7(int a) { return (a+420)%S7; }
int dirdiff(int dd, int t) {
dd %= t;
@ -15,7 +16,20 @@ int dirdiff(int dd, int t) {
struct cell : gcell {
char type; // 6 for hexagons, 7 for heptagons
unsigned char spn[7];
// wall parameter, used for remaining power of Bonfires and Thumpers
char wparam;
// 'tmp' is used for:
// pathfinding algorithm used by monsters with atypical movement (which do not use pathdist)
// bugs' pathfinding algorithm
short aitmp;
uint32_t spintable;
int spin(int d) { return tspin(spintable, d); }
int spn(int d) { return tspin(spintable, d); }
int mirror(int d) { return tmirror(spintable, d); }
heptagon *master;
cell *mov[7]; // meaning very similar to heptagon::move
};
@ -36,11 +50,11 @@ cell *newCell(int type, heptagon *master) {
return c;
}
void merge(cell *c, int d, cell *c2, int d2) {
void merge(cell *c, int d, cell *c2, int d2, bool mirrored = false) {
c->mov[d] = c2;
c->spn[d] = d2;
tsetspin(c->spintable, d, d2 + (mirrored?8:0));
c2->mov[d2] = c;
c2->spn[d2] = d;
tsetspin(c2->spintable, d2, d + (mirrored?8:0));
}
typedef unsigned short eucoord;
@ -80,55 +94,54 @@ cell *createMov(cell *c, int d) {
if(c->mov[d]) return c->mov[d];
else if(purehepta) {
heptagon *h2 = createStep(c->master, d);
c->mov[d] = h2->c7;
c->spn[d] = c->master->spin[d];
h2->c7->mov[c->spn[d]] = c;
h2->c7->spn[c->spn[d]] = d;
merge(c,d,h2->c7,c->master->spin(d),false);
}
else if(c->type == 7) {
else if(c->type != 6) {
cell *n = newCell(6, c->master);
c->mov[d] = n; n->mov[0] = c;
c->spn[d] = 0; n->spn[0] = d;
merge(c,d,n,0,false);
heptspin hs; hs.h = c->master; hs.spin = d;
heptspin hs; hs.h = c->master; hs.spin = d; hs.mirrored = false;
heptspin hs2 = hsstep(hsspin(hs, 3), 3);
int a3 = c->type/2;
int a4 = a3+1;
// merge(hs2.h->c7, hs2.spin, n, 2);
heptspin hs2 = hsstep(hsspin(hs, a3), a3);
merge(hs2.h->c7, hs2.spin, n, 2, hs2.mirrored);
hs2.h->c7->mov[hs2.spin] = n; n->mov[2] = hs2.h->c7;
hs2.h->c7->spn[hs2.spin] = 2; n->spn[2] = hs2.spin;
hs2 = hsstep(hsspin(hs, 4), 4);
// merge(hs2.h->c7, hs2.spin, n, 4);
hs2.h->c7->mov[hs2.spin] = n; n->mov[4] = hs2.h->c7;
hs2.h->c7->spn[hs2.spin] = 4; n->spn[4] = hs2.spin;
heptspin hs3 = hsstep(hsspin(hs, a4), a4);
merge(hs3.h->c7, hs3.spin, n, 4, hs3.mirrored);
extern void verifycell(cell *c);
verifycell(n);
}
else if(d == 5) {
int di = fixrot(c->spn[0]+1);
int di = fixrot(c->spin(0)+1);
cell *c2 = createMov(c->mov[0], di);
merge(c, 5, c2, fix6(c->mov[0]->spn[di] + 1));
bool mirr = c->mov[0]->mirror(di);
merge(c, 5, c2, fix6(c->mov[0]->spn(di) + (mirr?-1:1)), mirr);
// c->mov[5] = c->mov[0]->mov[fixrot(c->spn[0]+1)];
// c->spn[5] = fix6(c->mov[0]->spn[fixrot(c->spn[0]+1)] + 1);
}
else if(d == 1) {
int di = fixrot(c->spn[0]-1);
int di = fixrot(c->spn(0)-1);
cell *c2 = createMov(c->mov[0], di);
merge(c, 1, c2, fix6(c->mov[0]->spn[di] - 1));
bool mirr = c->mov[0]->mirror(di);
merge(c, 1, c2, fix6(c->mov[0]->spn(di) - (mirr?-1:1)), mirr);
// c->mov[1] = c->mov[0]->mov[fixrot(c->spn[0]-1)];
// c->spn[1] = fix6(c->mov[0]->spn[fixrot(c->spn[0]-1)] - 1);
}
else if(d == 3) {
int di = fixrot(c->spn[2]-1);
bool mirr = c->mirror(2);
int di = fixrot(c->spn(2)-(mirr?-1:1));
cell *c2 = createMov(c->mov[2], di);
merge(c, 3, c2, fix6(c->mov[2]->spn[di] - 1));
bool nmirr = mirr ^ c->mov[2]->mirror(di);
merge(c, 3, c2, fix6(c->mov[2]->spn(di) - (nmirr?-1:1)), nmirr);
// c->mov[3] = c->mov[2]->mov[fixrot(c->spn[2]-1)];
// c->spn[3] = fix6(c->mov[2]->spn[fixrot(c->spn[2]-1)] - 1);
}
@ -136,12 +149,12 @@ cell *createMov(cell *c, int d) {
}
cell *createMovR(cell *c, int d) {
d %= 42; d += 42; d %= c->type;
d %= 420; d += 420; d %= c->type;
return createMov(c, d);
}
cell *getMovR(cell *c, int d) {
d %= 42; d += 42; d %= c->type;
d %= 420; d += 420; d %= c->type;
return c->mov[d];
}
@ -149,12 +162,13 @@ cell *getMovR(cell *c, int d) {
struct cellwalker {
cell *c;
int spin;
cellwalker(cell *c, int spin) : c(c), spin(spin) {}
cellwalker() {}
bool mirrored;
cellwalker(cell *c, int spin) : c(c), spin(spin) { mirrored = false; }
cellwalker() { mirrored = false; }
};
void cwspin(cellwalker& cw, int d) {
cw.spin = (cw.spin+d + 42) % cw.c->type;
cw.spin = (cw.spin+(MIRR(cw)?-d:d) + 420) % cw.c->type;
}
bool cwstepcreates(cellwalker& cw) {
@ -162,20 +176,21 @@ bool cwstepcreates(cellwalker& cw) {
}
cell *cwpeek(cellwalker cw, int dir) {
return createMov(cw.c, (cw.spin+42+dir) % cw.c->type);
return createMov(cw.c, (cw.spin+420+dir) % cw.c->type);
}
void cwstep(cellwalker& cw) {
createMov(cw.c, cw.spin);
int nspin = cw.c->spn[cw.spin];
int nspin = cw.c->spn(cw.spin);
if(cw.c->mirror(cw.spin)) cw.mirrored = !cw.mirrored;
cw.c = cw.c->mov[cw.spin];
cw.spin = nspin;
}
void eumerge(cell* c1, cell *c2, int s1, int s2) {
if(!c2) return;
c1->mov[s1] = c2; c1->spn[s1] = s2;
c2->mov[s2] = c1; c2->spn[s2] = s1;
c1->mov[s1] = c2; tsetspin(c1->spintable, s1, s2);
c2->mov[s2] = c1; tsetspin(c2->spintable, s2, s1);
}
struct euclideanSlab {
@ -216,28 +231,99 @@ cell*& euclideanAtCreate(eucoord x, eucoord y) {
return c;
}
int spherecells() {
if(S7 == 5) return (elliptic?6:12);
if(S7 == 4) return (elliptic?3:6);
if(S7 == 3) return 4;
if(S7 == 2) return (elliptic?1:2);
if(S7 == 1) return 1;
return 12;
}
// initializer (also inits origin from heptagon.cpp)
void initcells() {
DEBB(DF_INIT, (debugfile,"initcells\n"));
origin.s = hsOrigin;
origin.emeraldval = 98;
origin.zebraval = 40;
origin.fiftyval = 0;
#ifdef CDATA
origin.rval0 = origin.rval1 = 0;
origin.cdata = NULL;
#endif
for(int i=0; i<7; i++) origin.move[i] = NULL;
origin.alt = NULL;
origin.distance = 0;
if(euclid)
origin.c7 = euclideanAtCreate(0,0);
else
origin.c7 = newCell(7, &origin);
if(sphere) {
for(int i=0; i<spherecells(); i++) {
heptagon& h = dodecahedron[i];
h.s = hsOrigin;
h.emeraldval = i;
h.zebraval = i;
h.fiftyval = i;
h.rval0 = h.rval1 = 0;
h.alt = NULL;
h.cdata = NULL;
h.spintable = 0;
for(int i=0; i<S7; i++) h.move[i] = NULL;
h.c7 = newCell(S7, &h);
}
for(int i=0; i<S7; i++) {
dodecahedron[0].move[i] = &dodecahedron[i+1];
dodecahedron[0].setspin(i, 0);
dodecahedron[i+1].move[0] = &dodecahedron[0];
dodecahedron[i+1].setspin(0, i);
dodecahedron[i+1].move[1] = &dodecahedron[(i+S7-1)%S7+1];
dodecahedron[i+1].setspin(1, S7-1);
dodecahedron[i+1].move[S7-1] = &dodecahedron[(i+1)%S7+1];
dodecahedron[i+1].setspin(S7-1, 1);
if(S7 == 5 && elliptic) {
dodecahedron[i+1].move[2] = &dodecahedron[(i+2)%S7+1];
dodecahedron[i+1].setspin(2, 3 + 8);
dodecahedron[i+1].move[3] = &dodecahedron[(i+3)%S7+1];
dodecahedron[i+1].setspin(3, 2 + 8);
}
else if(S7 == 5) {
dodecahedron[6].move[i] = &dodecahedron[7+i];
dodecahedron[6].setspin(i, 0);
dodecahedron[7+i].move[0] = &dodecahedron[6];
dodecahedron[7+i].setspin(0, i);
dodecahedron[i+7].move[1] = &dodecahedron[(i+4)%5+7];
dodecahedron[i+7].setspin(1, 4);
dodecahedron[i+7].move[4] = &dodecahedron[(i+1)%5+7];
dodecahedron[i+7].setspin(4, 1);
dodecahedron[i+1].move[2] = &dodecahedron[7+(10-i)%5];
dodecahedron[i+1].setspin(2, 2);
dodecahedron[7+(10-i)%5].move[2] = &dodecahedron[1+i];
dodecahedron[7+(10-i)%5].setspin(2, 2);
dodecahedron[i+1].move[3] = &dodecahedron[7+(9-i)%5];
dodecahedron[i+1].setspin(3, 3);
dodecahedron[7+(9-i)%5].move[3] = &dodecahedron[i+1];
dodecahedron[7+(9-i)%5].setspin(3, 3);
}
if(S7 == 4) {
dodecahedron[5].move[3-i] = &dodecahedron[i+1];
dodecahedron[5].setspin(3-i, 2);
dodecahedron[i+1].move[2] = &dodecahedron[5];
dodecahedron[i+1].setspin(2, 3-i);
}
}
}
else {
origin.s = hsOrigin;
origin.emeraldval = 98;
origin.zebraval = 40;
origin.fiftyval = 0;
origin.fieldval = 0;
origin.rval0 = origin.rval1 = 0;
origin.cdata = NULL;
for(int i=0; i<7; i++) origin.move[i] = NULL;
origin.spintable = 0;
origin.alt = NULL;
origin.distance = 0;
if(euclid)
origin.c7 = euclideanAtCreate(0,0);
else
origin.c7 = newCell(7, &origin);
}
if(quotient) quotientspace::build();
// origin.emeraldval =
}
@ -247,13 +333,13 @@ void clearcell(cell *c) {
if(!c) return;
DEBMEM ( printf("c%d %p\n", c->type, c); )
for(int t=0; t<c->type; t++) if(c->mov[t]) {
DEBMEM ( printf("mov %p [%p] S%d\n", c->mov[t], c->mov[t]->mov[c->spn[t]], c->spn[t]); )
if(c->mov[t]->mov[c->spn[t]] != NULL &&
c->mov[t]->mov[c->spn[t]] != c) {
DEBMEM ( printf("mov %p [%p] S%d\n", c->mov[t], c->mov[t]->mov[c->spn(t)], c->spn(t)); )
if(c->mov[t]->mov[c->spn(t)] != NULL &&
c->mov[t]->mov[c->spn(t)] != c) {
printf("cell error\n");
exit(1);
}
c->mov[t]->mov[c->spn[t]] = NULL;
c->mov[t]->mov[c->spn(t)] = NULL;
}
DEBMEM ( printf("DEL %p\n", c); )
delete c;
@ -261,6 +347,14 @@ void clearcell(cell *c) {
heptagon deletion_marker;
void clearHexes(heptagon *at) {
if(at->c7) {
if(!purehepta) for(int i=0; i<7; i++)
clearcell(at->c7->mov[i]);
clearcell(at->c7);
}
}
#include <queue>
void clearfrom(heptagon *at) {
queue<heptagon*> q;
@ -276,22 +370,16 @@ void clearfrom(heptagon *at) {
if(at->move[i]->alt != &deletion_marker)
q.push(at->move[i]);
at->move[i]->alt = &deletion_marker;
DEBMEM ( printf("!mov %p [%p]\n", at->move[i], at->move[i]->move[at->spin[i]]); )
if(at->move[i]->move[at->spin[i]] != NULL &&
at->move[i]->move[at->spin[i]] != at) {
DEBMEM ( printf("!mov %p [%p]\n", at->move[i], at->move[i]->move[at->spin(i)]); )
if(at->move[i]->move[at->spin(i)] != NULL &&
at->move[i]->move[at->spin(i)] != at) {
printf("hept error\n");
exit(1);
}
at->move[i]->move[at->spin[i]] = NULL;
at->move[i]->move[at->spin(i)] = NULL;
at->move[i] = NULL;
}
DEBMEM ( printf("at %p\n", at); )
if(at->c7) {
if(!purehepta) for(int i=0; i<7; i++)
clearcell(at->c7->mov[i]);
clearcell(at->c7);
}
DEBMEM ( printf("!DEL %p\n", at); )
clearHexes(at);
if(at != &origin) delete at;
}
//printf("maxq = %d\n", maxq);
@ -302,46 +390,49 @@ void verifycell(cell *c) {
for(int i=0; i<t; i++) {
cell *c2 = c->mov[i];
if(c2) {
if(t == 7 && !purehepta) verifycell(c2);
if(c2->mov[c->spn[i]] && c2->mov[c->spn[i]] != c)
printf("cell error %p %p\n", c, c2);
if(t != 6 && !purehepta) verifycell(c2);
if(c2->mov[c->spn(i)] && c2->mov[c->spn(i)] != c) {
printf("cell error %p:%d [%d] %p:%d [%d]\n", c, i, c->type, c2, c->spn(i), c2->type);
exit(1);
}
}
}
}
void verifycells(heptagon *at) {
for(int i=0; i<7; i++) if(at->move[i] && at->spin[i] == 0 && at->move[i] != &origin)
verifycells(at->move[i]);
for(int i=0; i<7; i++) if(at->move[i] && at->move[i]->move[at->spin[i]] && at->move[i]->move[at->spin[i]] != at) {
printf("hexmix error %p %p %p\n", at, at->move[i], at->move[i]->move[at->spin[i]]);
for(int i=0; i<7; i++) if(at->move[i] && at->move[i]->move[at->spin(i)] && at->move[i]->move[at->spin(i)] != at) {
printf("hexmix error %p [%d s=%d] %p %p\n", at, i, at->spin(i), at->move[i], at->move[i]->move[at->spin(i)]);
}
if(!sphere && !quotient) for(int i=0; i<7; i++) if(at->move[i] && at->spin(i) == 0 && at->move[i] != &origin)
verifycells(at->move[i]);
verifycell(at->c7);
}
int eupattern(cell *c) {
eucoord x, y;
decodeMaster(c->master, x, y);
short z = (short(y+2*x))%3;
z %= 3;
if(z<0) z += 3;
return z;
}
bool ishept(cell *c) {
// EUCLIDEAN
if(euclid) {
eucoord x, y;
decodeMaster(c->master, x, y);
return (short(y+2*x))%3 == 0;
}
else return c->type == 7;
if(euclid) return eupattern(c) == 0;
else return c->type != 6;
}
bool ishex1(cell *c) {
// EUCLIDEAN
if(euclid) {
eucoord x, y;
decodeMaster(c->master, x, y);
short z = (short(y+2*x))%3;
if(z<0) z += 3;
return z == 1;
}
if(euclid) return eupattern(c) == 1;
else return c->type == 7;
}
int emeraldval(cell *c) {
if(euclid) return 0;
if(euclid) return eupattern(c);
if(sphere) return 0;
if(c->type == 7)
return c->master->emeraldval >> 3;
else {
@ -360,6 +451,17 @@ int eudist(short sx, short sy) {
return max(max(z0,z1), z2);
}
int compdist(int dx[3]) {
int mi = min(min(dx[0], dx[1]), dx[2]);
if(dx[0] > mi+2 || dx[1] > mi+2 || dx[2] > mi+2)
return -1; // { printf("cycle error!\n"); exit(1); }
if(dx[0] == mi+2 || dx[1] == mi+2 || dx[2] == mi+2)
return mi+1;
if((dx[0] == mi+1) + (dx[1] == mi+1) + (dx[2] == mi+1) >= 2)
return mi+1;
return mi;
}
int celldist(cell *c) {
if(euclid) {
eucoord x, y;
@ -370,12 +472,7 @@ int celldist(cell *c) {
int dx[3];
for(int u=0; u<3; u++)
dx[u] = createMov(c, u+u)->master->distance;
int mi = min(min(dx[0], dx[1]), dx[2]);
if(dx[0] > mi+2 || dx[1] > mi+2 || dx[2] > mi+2)
return -1; // { printf("cycle error!\n"); exit(1); }
if(dx[0] == mi+2 || dx[1] == mi+2 || dx[2] == mi+2)
return mi+1;
return mi;
return compdist(dx);
}
#define ALTDIST_BOUNDARY 99999
@ -392,6 +489,7 @@ int celldistAlt(cell *c) {
decodeMaster(c->master, x, y);
return euclidAlt(x, y);
}
if(!c->master->alt) return 0;
if(c->type == 7) return c->master->alt->distance;
int dx[3];
for(int u=0; u<3; u++) if(createMov(c, u+u)->master->alt == NULL)
@ -420,8 +518,23 @@ unsigned bitmajority(unsigned a, unsigned b, unsigned c) {
return (a&b) | ((a^b)&c);
}
int eufifty(cell *c) {
eucoord x, y;
decodeMaster(c->master, x, y);
int ix = short(x) + 99999 + short(y);
int iy = short(y) + 99999;
if(c->land == laWildWest)
return (ix + iy * 26 + 28) % 37;
else {
ix += (iy/3) * 3;
iy %= 3; ix %= 9;
return iy * 9 + ix;
}
}
int fiftyval(cell *c) {
if(euclid) return 0;
if(euclid) return eufifty(c) * 32;
if(sphere) return 0;
if(c->type == 7)
return c->master->fiftyval;
else {
@ -433,26 +546,11 @@ int fiftyval(cell *c) {
}
int cdist50(cell *c) {
if(sphere) return 0;
if(euclid) {
eucoord x, y;
decodeMaster(c->master, x, y);
int ix = short(x) + 99999 + short(y);
int iy = short(y) + 99999;
if(c->land == laPalace) {
char palacemap[3][10] = {
"012333321",
"112322232",
"222321123"
};
ix += (iy/3) * 3;
iy %= 3; ix %= 9;
return palacemap[iy][ix] - '0';
}
else {
const char *westmap = "0123333332112332223322233211233333322";
int id = ix + iy * 26 + 28;
return westmap[id % 37] - '0';
}
if(c->land == laWildWest)
return "0123333332112332223322233211233333322"[eufifty(c)] - '0';
else return "012333321112322232222321123"[eufifty(c)] - '0';
}
if(c->type == 7) return cdist50(fiftyval(c));
int a0 = cdist50(createMov(c,0));
@ -464,6 +562,7 @@ int cdist50(cell *c) {
int land50(cell *c) {
if(c->type == 7) return land50(fiftyval(c));
else if(sphere || euclid) return 0;
else {
if(cdist50(createMov(c,0)) < 3) return land50(createMov(c,0));
if(cdist50(createMov(c,2)) < 3) return land50(createMov(c,2));
@ -474,6 +573,7 @@ int land50(cell *c) {
int polara50(cell *c) {
if(c->type == 7) return polara50(fiftyval(c));
else if(sphere || euclid) return 0;
else {
if(cdist50(createMov(c,0)) < 3) return polara50(createMov(c,0));
if(cdist50(createMov(c,2)) < 3) return polara50(createMov(c,2));
@ -485,6 +585,7 @@ int polara50(cell *c) {
int polarb50(cell *c) {
if(euclid) return true;
if(c->type == 7) return polarb50(fiftyval(c));
else if(sphere || euclid) return true;
else {
if(cdist50(createMov(c,0)) < 3) return polarb50(createMov(c,0));
if(cdist50(createMov(c,2)) < 3) return polarb50(createMov(c,2));
@ -498,7 +599,8 @@ int elhextable[28][3] = {
};
int fiftyval049(cell *c) {
if(c->type == 7) return fiftyval(c) / 32;
if(c->type == 7 || euclid) return fiftyval(c) / 32;
else if(sphere) return 0;
else {
int a[3], qa=0;
int pa = polara50(c), pb = polarb50(c);
@ -536,7 +638,9 @@ int fiftyval049(cell *c) {
int zebra40(cell *c) {
if(c->type == 7) return (c->master->zebraval/10);
else {
else if(sphere) return 0;
else if(euclid) return eupattern(c);
else {
int ii[3], z;
ii[0] = (c->mov[0]->master->zebraval/10);
ii[1] = (c->mov[2]->master->zebraval/10);
@ -558,6 +662,7 @@ int zebra40(cell *c) {
int zebra3(cell *c) {
if(c->type == 7) return (c->master->zebraval/10)/4;
else if(sphere) return 0;
else {
int ii[3];
ii[0] = (c->mov[0]->master->zebraval/10)/4;
@ -696,8 +801,6 @@ bool randpatternMajority(cell *c, int ival, int iterations) {
return memo;
}
#ifdef CDATA
#include <map>
map<heptagon*, int> spins;
@ -732,6 +835,8 @@ void setHeptagonRval(heptagon *h) {
cdata *getHeptagonCdata(heptagon *h) {
if(h->cdata) return h->cdata;
if(sphere || quotient) h = &origin;
if(h == &origin) {
return h->cdata = new cdata(orig_cdata);
}
@ -876,8 +981,108 @@ eLand getCLand(cell *c) {
return land_scape[b & 31];
}
// list all cells in distance at most maxdist, or until when maxcount cells are reached
struct celllister {
vector<cell*> lst;
vector<int> tmps;
vector<int> dists;
bool listed(cell *c) {
return c->aitmp >= 0 && c->aitmp < size(lst) && lst[c->aitmp] == c;
}
void add(cell *c, int d) {
if(listed(c)) return;
c->aitmp = size(lst);
tmps.push_back(c->aitmp);
lst.push_back(c);
dists.push_back(d);
}
int getdist(cell *c) { return dists[c->aitmp]; }
~celllister() {
for(int i=0; i<size(lst); i++) lst[i]->aitmp = tmps[i];
}
celllister(cell *orig, int maxdist, int maxcount, cell *breakon) {
lst.clear();
tmps.clear();
dists.clear();
add(orig, 0);
cell *last = orig;
for(int i=0; i<size(lst); i++) {
cell *c = lst[i];
if(maxdist) forCellCM(c2, c) {
add(c2, dists[i]+1);
if(c2 == breakon) return;
}
if(c == last) {
if(size(lst) >= maxcount || dists[i]+1 == maxdist) break;
last = lst[size(lst)-1];
maxdist--;
}
}
}
};
cell *heptatdir(cell *c, int d) {
if(d&1) {
cell *c2 = createMov(c, d);
int s = c->spin(d);
s += 3; s %= 6;
return createMov(c2, s);
}
else return createMov(c, d);
}
namespace fieldpattern {
pair<int, bool> fieldval(cell *c) {
if(c->type == 7) return make_pair(c->master->fieldval, false);
else return make_pair(btspin(c->master->fieldval, c->spin(0)), true);
}
int subpathid = fp43.matcode[fp43.strtomatrix("RRRPRRRRRPRRRP")];
int subpathorder = fp43.order(fp43.matrices[subpathid]);
pair<int, int> subval(cell *c, int _subpathid = subpathid, int _subpathorder = subpathorder) {
if(c->type == 6)
return min(min(subval(createMov(c, 0)),subval(createMov(c, 2))), subval(createMov(c, 4)));
else {
pair<int, int> pbest, pcur;
pcur.first = c->master->fieldval;
pcur.second = 0;
pbest = pcur;
for(int i=0; i<_subpathorder; i++) {
pcur.first = fp43.gmul(pcur.first, _subpathid);
pcur.second++;
if(pcur < pbest) pbest = pcur;
}
return pbest;
}
}
}
int celldistance(cell *c1, cell *c2) {
int d = 0;
if(euclid) {
eucoord x1, y1, x2, y2;
decodeMaster(c1->master, x1, y1);
decodeMaster(c2->master, x2, y2);
return eudist(x1-x2, y1-y2);
}
if(sphere || quotient == 1) {
celllister cl(c1, 64, 1000, c2);
return cl.getdist(c2);
}
if(quotient == 2)
return fp43.getdist(fieldpattern::fieldval(c1), fieldpattern::fieldval(c2));
cell *cl1=c1, *cr1=c1, *cl2=c2, *cr2=c2;
while(true) {
if(cl1 == cl2) return d;
@ -912,16 +1117,20 @@ int celldistance(cell *c1, cell *c2) {
}
}
void clearMemory() {
extern void clearGameMemory();
clearGameMemory();
if(shmup::on) shmup::clearMemory();
cleargraphmemory();
#ifndef MOBILE
mapeditor::clearModelCells();
#endif
void clearHyperbolicMemory() {
DEBMEM ( verifycells(&origin); )
clearfrom(&origin);
for(int i=0; i<size(allAlts); i++) clearfrom(allAlts[i]);
allAlts.clear();
}
void clearCellMemory() {
// EUCLIDEAN
if(euclid) {
if(sphere) {
for(int i=0; i<spherecells(); i++) clearHexes(&dodecahedron[i]);
}
else if(quotient) quotientspace::clear();
else if(euclid) {
for(int y=0; y<256; y++) for(int x=0; x<256; x++)
if(euclidean[y][x]) {
delete euclidean[y][x];
@ -929,13 +1138,182 @@ void clearMemory() {
}
eucdata.clear();
}
else {
DEBMEM ( verifycells(&origin); )
clearfrom(&origin);
for(int i=0; i<size(allAlts); i++) clearfrom(allAlts[i]);
allAlts.clear();
}
else clearHyperbolicMemory();
}
void clearMemory() {
extern void clearGameMemory();
clearGameMemory();
shmup::clearMemory();
cleargraphmemory();
#ifndef NOEDIT
mapeditor::clearModelCells();
#endif
clearCellMemory();
DEBMEM ( printf("ok\n"); )
}
#endif
void verifyDodecahedron() {
for(int i=0; i<spherecells(); i++) for(int k=0; k<S7; k++) {
heptspin hs;
hs.h = &dodecahedron[i];
hs.spin = k;
hs = hsstep(hs, 0);
hs = hsspin(hs, S7-1);
hs = hsstep(hs, 0);
hs = hsspin(hs, S7-1);
hs = hsstep(hs, 0);
hs = hsspin(hs, S7-1);
if(hs.h != &dodecahedron[i]) printf("error %d,%d\n", i, k);
}
for(int i=0; i<spherecells(); i++) verifycells(&dodecahedron[i]);
}
int getHemisphere(cell *c, int which) {
if(c->type != 6) {
int id = c->master->fiftyval;
int hemitable[3][12] = {
{ 6, 3, 3, 3, 3, 3,-6,-3,-3,-3,-3,-3},
{ 6, 3, 6, 3, 0, 0,-6,-3,-6,-3, 0, 0},
{-3, 0, 3, 0,-6,-6, 3, 0,-3, 0, 6, 6}
};
return hemitable[which][id];
}
else {
int score = 0;
for(int i=0; i<6; i+=2)
score += getHemisphere(c->mov[i], which);
return score/3;
}
}
namespace quotientspace {
vector<cell*> allcells;
struct code {
int c[8];
};
bool operator == (const code& c1, const code &c2) {
for(int i=0; i<8; i++) if(c1.c[i] != c2.c[i]) return false;
return true;
}
bool operator < (const code& c1, const code &c2) {
for(int i=0; i<8; i++) if(c1.c[i] != c2.c[i]) return c1.c[i] < c2.c[i];
return false;
}
map<code, int> reachable;
vector<heptspin> bfsq;
int cod(heptagon *h) {
return zebra40(h->c7);
}
code get(heptspin hs) {
code res;
res.c[0] = cod(hs.h);
for(int i=1; i<8; i++) {
res.c[i] = cod(hsstep(hs, 0).h);
hs = hsspin(hs, 1);
}
return res;
}
vector<int> connections;
int rvadd = 0, rvdir = 1;
int rv(int x) { return (rvadd+x*rvdir) % 7; } // if(x) return 7-x; else return x; }
void add(const heptspin& hs) {
code g = get(hs);
if(!reachable.count(g)) {
reachable[g] = bfsq.size();
bfsq.push_back(hs);
add(hsspin(hs, 1));
}
}
vector<heptagon*> allh;
void clear() {
clearfrom(origin.alt);
for(int i=0; i<size(allh); i++) {
clearHexes(allh[i]);
if(i) delete allh[i];
}
allh.clear();
allcells.clear();
}
void build() {
if(quotient == 2) {
connections = fp43.connections;
}
else {
heptspin hs; hs.h = &origin; hs.spin = 0;
reachable.clear();
bfsq.clear();
connections.clear();
add(hs);
for(int i=0; i<(int)bfsq.size(); i++) {
hs = hsstep(bfsq[i], 0);
add(hs);
connections.push_back(reachable[get(hs)]);
}
}
clearHyperbolicMemory();
origin.c7 = newCell(7, &origin);
int TOT = connections.size() / 7;
printf("heptagons = %d\n", TOT);
printf("all cells = %d\n", TOT*10/3);
if(!TOT) exit(1);
allh.resize(TOT);
for(int i=0; i<TOT; i++) allh[i] = i==0 ? &origin : new heptagon;
origin.alt = new heptagon;
*origin.alt = origin;
for(int i=0; i<7; i++) origin.alt->move[i] = NULL;
origin.alt->c7 = newCell(7, origin.alt);
for(int i=0; i<TOT; i++) {
heptagon *h = allh[i];
if(i) {
h->alt = NULL;
h->s = hsOrigin;
h->emeraldval = 0;
h->zebraval = 0;
h->fiftyval = 0;
h->fieldval = 7*i;
h->rval0 = h->rval1 = 0; h->cdata = NULL;
h->distance = 0;
h->c7 = newCell(7, h);
}
for(int j=0; j<7; j++) {
h->move[rv(j)] = allh[connections[i*7+j]/7];
h->setspin(rv(j), rv(connections[i*7+j]%7));
}
}
for(int i=0; i<TOT; i++) {
generateAlts(allh[i]);
allh[i]->emeraldval = allh[i]->alt->emeraldval;
allh[i]->zebraval = allh[i]->alt->zebraval;
allh[i]->fiftyval = allh[i]->alt->fiftyval;
allh[i]->distance = allh[i]->alt->distance;
/* for(int j=0; j<7; j++)
allh[i]->move[j]->alt = createStep(allh[i]->alt, j); */
}
celllister cl(origin.c7, 100, 100000000, NULL);
allcells = cl.lst;
}
}

View File

@ -1,11 +1,6 @@
// Hyperbolic Rogue -- items, monsters, walls, lands, descriptions, etc.
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#define GEN_M 0
#define GEN_F 1
#define GEN_N 2
#define GEN_O 3
// --- help ---
const char *wormdes =
@ -110,7 +105,7 @@ const char *foresthelp =
"Trees catch fire on the next turn. The temperature of the grass cells "
"rises once per turn for each fire nearby, and becomes fire itself "
"when its temperature has risen 10 times.\n"
"You can also cut down the trees. Big trees take two turns to cut down.";
"You can also chop down the trees. Big trees take two turns to chop down.";
const char *hivehelp =
"The Hive is filled with Hyperbugs. They are huge insects which look a bit like "
@ -216,13 +211,36 @@ const char *winddesc =
"outcoming wind. However, you can move two cells with the wind in a single turn, "
"and so can the birds.";
const char *warningdesc =
"Warnings are issued when you try to do something that appears dangerous, "
"like stepping on a known mine, or getting your boat destroyed by "
"a Kraken without having Orb of the Fish. In some cases the action "
"might actually be safe -- so you can ignore the warning and do it anyway, "
"simply by repeating the action.";
const char *hauntdesc =
"A dark forest filled with ghosts and graves. But there is also treasure hidden "
"deep within... But don't let greed make you stray from your path, as "
"you can get lost!\n\n"
"The Haunted Woods are bounded by a single equidistant curve. It is not a circle or horocycle.\n\n";
const char *bulldashdesc =
"Butterflies don't pursue you -- unless you get next to them, they just spin around the obstacles. "
"They cannot be killed conventionally, but you get treasure when a Raging Bull crashes into a Butterfly. ";
const char *prairiedesc =
"You can find safety in some places in the Prairie, but if you want treasures, "
"they can be found only on the other side of a giant herd of bulls.";
const char *cadesc =
"A land for people wanting to experiment with cellular automata in the HyperRogue grid. "
"Rules can be given on the command line; the default rules are:\n"
"-c07 00100000 -c06 0010000 -c17 00011000 -c16 0001100 -caprob 0.3\n"
"(-c0 or -c1 can be given if the same rule is to be used for hexagonal "
"and heptagonal cells).";
const char *NODESC = "No description yet.";
const char *NODESCYET = "No description yet.";
const char *GENDERSWITCH = NODESC;
// --- monsters ---
@ -259,8 +277,8 @@ const char *dragondesc =
"The head will regenerate on the "
"turns the Dragon is not moving, so you will usually have to hit it with "
"your last attack; otherwise, if the head is healthy, it may breathe "
"fire (at range 3), losing the hitpoint. Killing the Dragon gives you "
"treasure.";
"fire (at range 3), losing the hitpoint. Killing the Dragon "
"while still in the Dragon Chasms gives you treasure.";
const char *tortoisedesc =
"Galápagos is the land of Tortoises. "
@ -275,8 +293,49 @@ const char *tortoisedesc =
"Bringing back a Baby Tortoise counts as 5 $$$. The more factors agree in "
"the given location of Galápagos, the brighter it is shown on your screen.";
const char *krakendesc =
"There are Krakens in your homeland too... huge sea monsters which "
"could easily destroy ships. The geometry of this strange world "
"prevents quick movement of huge objects, "
"so there are no large ships, only small boats, and "
"hyperbolic Krakens are relatively small too. Still, you suppose they might be "
"the widest creatures which could still move at considerable speed...\n\n"
"Kraken heads can move only on hexagons. You need to attack all the tentacles to "
"kill the Kraken. A tentacle cannot attack if it has been attacked on the "
"same turn. When a Kraken attacks you while you are in a boat, it "
"destroys the boat, but does not kill you.";
const int motypes = 125;
const char *halloweendesc =
"Halloween is a special land, that is available only in the spherical "
"or elliptic geometry (press 'o' to switch). You play on the surface of "
"a jack-o'-lantern, "
"and have to collect as many Treats as possible. Each Treat you collect "
"brings new monsters to fight, and new magical powers for you. You "
"have to fight the monsters while effectively managing your limited "
"resources.";
const char *reptiledesc =
"These reptiles are quite strange creatures. They "
"spend most of their lives sleeping as floors "
"that other creatures can walk on. "
"Sometimes they wake up to hunt their prey, "
"but they will happily go back to sleep if they "
"happen to move into a hole on their way. "
"Your attacks do not kill the Reptiles, but "
"you can push and stun them.";
const char *naturedesc =
"This Orb allows you to grow like an Ivy. "
"The Ivy is always rooted in your current location; "
"moving among the Ivy cells will move the root. "
"Moving to a new location will cause the Ivy to grow "
", if an Ivy could make that movement "
"(otherwise it breaks). "
"You can also target one of the cells adjacent to your ivy "
"(not to you) to grow or attack there.";
const int motypes = 139;
struct monstertype {
char glyph;
@ -474,7 +533,7 @@ monstertype minf[motypes] = {
{ 'B', 0x909000, "Slime Beast", slimehelp},
{ '@', 0x8080FF, "Knight", camelothelp }, // knight moved
{ '@', 0x8B4513, "Illusion",
"Illusions are targetted "
"Illusions are targeted "
"by most monsters, just like yourself, Thumpers, and your friends."
},
{ 'P', 0xD00000, "Pirate",
@ -526,7 +585,7 @@ monstertype minf[motypes] = {
{ 'P', 0xFF80FF, "Princess", princessdesc},
{ 'P', 0xFF80FF, "Prince", princessdesc},
{ 'P', 0xFF80FF, "Princess", princessdesc},
{ 'S', 0xC0C0C0, "Servant", "A simple servant of the master of the Ivory Tower."},
{ 'F', 0xD03000, "Familiar", "A simple servant of the master of the Ivory Tower."},
{ 'B', 0x707070, "Gargoyle", gargdesc},
{ 'E', 0xFF0000, "Fire Elemental",
"This monster leaves a trail of fire behind."},
@ -573,7 +632,7 @@ monstertype minf[motypes] = {
{ 'T', 0x487830, "Tortoise", tortoisedesc},
{ 'D', 0xC03000, "Dragon", dragondesc},
{ 'd', 0xC03000, "Dragon", dragondesc},
{ 'N', 0x303030, "Nighthawk", NODESC},
{ 'F', 0x909090, "Gadfly", "Annoying insects. They can awaken Sleeping Bulls."},
{ 'Y', 0xFF8000, "Yendorian Researcher",
"These people study gravity and infinite trees. "
"They have no special features, other than wearing a strange hat."
@ -581,6 +640,39 @@ monstertype minf[motypes] = {
{ 'K', 0xA8A8A8, "Sparrowhawk",
"A bird who hunts in the treetops of Yendorian Forest."
},
{ 'K', 0xD0A0A0, "Kraken", krakendesc},
{ 'K', 0xC07070, "Kraken Tentacle", krakendesc},
{ 'D', 0xF09090, "Draugr",
"Animated corpses of ancient Viking warriors. They are immune to mundane weapons, "
"but they can be destroyed by your Orb of the Sword."
},
{ 'C', 0xC08000, "Friendly Ivy", naturedesc },
{ 'V', 0xC000C0, "Vampire Bat",
"Vampire Bats don't attack normally, but they drain your magical powers if "
"they are at distance at most 2 from you."
},
{ 'B', 0x404040, "Bat",
"Someone has told you that one can get battle experience safely by "
"killing tons of essentially harmless creatures, such as Bats. But "
"does this make any sense?...\n\n"
"It does not. Bats cannot hurt you, but may block your movement, or "
"toggle switches if they fall on them." },
{ 'R', 0x8080C0, "Reptile", reptiledesc },
{ 'B', 0x606020, "Herd Bull",
"Herds of these Bulls are running long distances for some reason. They become Raging Bulls if something stops them." },
{ 'B', 0xA03000, "Raging Bull",
"Raging Bulls charge in a straight line: on heptagons, when they can choose one of two possible directions, "
"they choose one closer to your current location. In the case of a tie, the cell where more neighbors is "
"closer to your current location is chosen; if still a tie, past locations are considered. "
"They can attack you in any direction, and monsters on their way are attacked even if friendly. "
"When they crash into something, the obstacle is usually destroyed, and they are stunned for three turns, after "
"which they charge at you again (in any direction). "
"Raging Bulls cannot be killed or stunned conventionally."
},
{ 'B', 0xB07000, "Sleeping Bull",
"Sleeping bulls wake up when you get into distance of two cells from them."
},
{ 'S', 0xFFD500, "Butterfly", bulldashdesc},
// shmup specials
{ '@', 0xC0C0C0, "Rogue", "In the Shoot'em Up mode, you are armed with thrown Knives."},
@ -593,6 +685,9 @@ monstertype minf[motypes] = {
{ '?', 0x00C000, "dead bug", NODESC},
{ '?', 0xFFFF00, "electric discharge", NODESC}, // appears as 'killed by electrocution'
{ '?', 0xE06000, "dead bird", NODESC},
{ '?', 0xE06000, "Energy Sword", NODESC},
{ '!', 0xFF0000, "Warning", warningdesc},
{ '*', 0, "vertex", "A vertex from rogueviz."}
};
enum eMonster {
@ -626,18 +721,23 @@ enum eMonster {
moMouse, moMouseMoved,
moPrincess, moPrincessMoved,
moPrincessArmed, moPrincessArmedMoved,
moEdgeMonkey, moGargoyle, moFireElemental, moAirElemental,
moFamiliar, moGargoyle, moFireElemental, moAirElemental,
moOrangeDog, moTentacleGhost,
moMetalBeast, moMetalBeast2, moOutlaw, moMutant,
moStormTroll, moForestTroll,
moRedFox, moWindCrow, moFriendlyGhost, moRatling, moFalsePrincess, moRoseLady,
moRoseBeauty, moRatlingAvenger,
moTortoise, moDragonHead, moDragonTail,
moNighthawk, moLemur, moKestrel,
moGadfly, moResearcher, moSparrowhawk,
moKrakenH, moKrakenT, moDraugr, moFriendlyIvy,
moVampire, moBat, moReptile,
moHerdBull, moRagingBull, moSleepBull,
moButterfly,
// shmup specials
moPlayer, moBullet, moFlailBullet, moFireball, moTongue, moAirball,
// temporary
moDeadBug, moLightningBolt, moDeadBird
moDeadBug, moLightningBolt, moDeadBird, moEnergySword, moWarning,
moRogueviz
};
struct genderswitch_t {
@ -667,7 +767,7 @@ genderswitch_t genderswitch[NUM_GS] = {
// --- items ---
const int ittypes = 92;
const int ittypes = 110;
struct itemtype {
char glyph;
@ -694,7 +794,7 @@ itemtype iinf[ittypes] = {
},
{ '!', 0xFFFF00, "Elixir of Life",
"A wonderful beverage, apparently obtained by mixing red and blue slime. You definitely feel more "
"healthy after drinking it, but you still fell that one hit of a monster is enough to kill you."},
"healthy after drinking it, but you still feel that one hit of a monster is enough to kill you."},
{ '%', 0xFF00FF, "Shard",
"A piece of a magic mirror, or a mirage cloud, that can be used for magical purposes. Only mirrors and clouds "
"in the Land of Mirrors leave these."},
@ -798,7 +898,7 @@ itemtype iinf[ittypes] = {
"Each fire drains 5 charges. You are not allowed to throw fire into adjacent cells."
},
{ 'o', 0x8B4513, "Orb of Trickery",
"This Orb allows you to create illusions of yourself. Illusions are targetted "
"This Orb allows you to create illusions of yourself. Illusions are targeted "
"by most monsters, just like yourself, Thumpers, and your friends.\n\n"
"Each illusion takes 5 charges to create, and one extra charge "
"per turn. You can also click your illusion to take it away, restoring 4 charges.\n\n"
@ -991,7 +1091,67 @@ itemtype iinf[ittypes] = {
"you have to dismount this turn -- be very careful to make this possible, "
"as your mount could attack you immediately!\n\n" "While riding, "
"click on a location to order your mount to move or attack there.",
}
},
{ 'o', 0xFFFF80, "Orb of the Sword",
"This Orb gives you a weapon made of pure magical energy. You do not hold "
"it, it simply floats in the air next to you. When you go, the energy sword moves "
"with you, pointing at the same relative angle it pointed before -- you cannot "
"move or rotate it otherwise. Most monsters can be killed by moving the sword into them, "
"and won't move into the spot with the sword."
},
{ 'x', 0x4040FF, "Sunken Treasure",
"Cargo of a ship which was once destroyed by a Kraken." },
{ 'o', 0xFF8040, "Orb of the Sword II",
"An alternative version of Orb of the Sword. If you have both of them, "
"you have two energy swords, facing in opposite directions."
},
{ '*', 0xFFFF80, "Ancient Jewelry",
"Precious belongings of ancient Viking heroes. Your Orb of the Sword can be "
"used to dig these treasures out of the barrows."
},
{ '!', 0xFFD700, "Golden Egg",
"Trolls of Trollheim are descendants of a bridge Troll, who collected "
"payments from people crossing the bridge. One of them paid with "
"golden eggs. The bridge Troll found the eggs beautiful, but he quickly lost them. "
"Golden eggs are still revered by Trolls, and you can find them in their "
"caves."
},
{ '!', 0xFF0000, "Warning", warningdesc
},
{ 'o', 0x808080, "Orb of the Stone",
"Trolls turn into stone walls when they die. When you have this Orb, "
"this happens to every monster you defeat. Statues created from this Orb "
"have slightly different properties than Trolls who petrify naturally."
},
{ 'o', 0xC08000, "Orb of Nature", naturedesc },
{ '%', 0x800080, "Treat", halloweendesc },
{ '%', 0x30A030, "Slime Mold",
"A very interesting species of slime mold."
},
{ '*', 0xFF00FF, "Amethyst", "A beatiful purple gem from the Lost Mountain." },
{ 'o', 0xC00040, "Orb of Recall",
"When the charges on this Orb expire, "
"you will be automatically returned to the place where you have found it. "
"Extra Orbs of Recall delay this without changing the recall location. "
"Pick up an Orb of Safety causes an immediate recall."},
{ ']', 0x8080FF, "Dodecahedron",
"These dodecahedra made of a mysterious material are the Reptiles' favorite toy."
},
{ 'o', 0x8080FF, "Orb of Vaulting",
"This Orb allows you to jump over an adjacent monster, killing or stunning it. "
"You can only vault in a roughly straight line. "
"Target a cell on the other side to use it."
},
{ '$', 0x80FF80, "Green Grass", prairiedesc },
{ 'o', 0x8080FF, "Orb of Horns",
"After you move while having this Orb, you immediately attack the next cell in the straight line "
"(or two cells, when moving on a heptagon). This attack is slightly stronger than your normal "
"attack: it can stun some of the monsters which cannot be killed or stunned normally."
},
{ 'o', 0x8080FF, "Orb of the Bull",
"You get the powers of Shield, Horns, and Thorns after you move two moves in a straight line "
"with this Orb." },
{ '$', 0xC060C0, "Spinel", bulldashdesc },
};
enum eItem { itNone, itDiamond, itGold, itSpice, itRuby, itElixir, itShard, itBone, itHell, itStatue,
@ -1000,32 +1160,43 @@ enum eItem { itNone, itDiamond, itGold, itSpice, itRuby, itElixir, itShard, itBo
itOrbLightning, itOrbFlash, itOrbWinter, itOrbSpeed, itOrbLife, itOrbShield, itOrbDigging,
itOrbTeleport, itOrbSafety,
itOrbThorns, itFernFlower,
itWine, itOrbGhost, itSilver, itOrbPsi,
itWine, itOrbAether, itSilver, itOrbPsi,
itRoyalJelly, itEmerald, itOrbInvis, itPower, itOrbFire,
itHolyGrail, itGrimoire,
itOrbDragon, itOrbIllusion,
itPirate, itCompass,
itRedGem, itOrbPreserve, itOrbTelekinesis,
itRedGem, itOrbTime, itOrbSpace,
itBombEgg, itCoast, itWhirlpool,
itOrbFriend, itOrbWater, itOrbAir,
itPalace, itOrbFrog,
itFjord, itOrbFish,
itOrbDiscord,
itSavedPrincess, itOrbLove,
itEdge, itZebra,
itIvory, itZebra,
itFireShard, itAirShard, itEarthShard, itWaterShard,
itElemental, itOrbSummon, itOrbMatter,
itBounty, itRevolver, itFulgurite, itMutant,
itOrbStunning, itOrbLuck,
itMutant2, itOrbFreedom, itLotus, itOrbUndeath,
itWindstone, itOrbEmpathy, itStrongWind, itBuggy, itBuggy2,
itRose, itCoral, itOrbSkunk, itOrb37, itOrbEnergy,
itBabyTortoise, itOrbShell, itApple, itDragon, itOrbDomination
itRose, itCoral, itOrbBeauty, itOrb37, itOrbEnergy,
itBabyTortoise, itOrbShell, itApple, itDragon, itOrbDomination,
itOrbSword,
itKraken, itOrbSword2, itBarrow,
itTrollEgg, itWarning,
itOrbStone, itOrbNature, itTreat,
itSlime, itAmethyst,
itOrbRecall, itDodeca,
itOrbDash,
itGreenGrass,
itOrbHorns,
itOrbBull,
itBull
};
// --- wall types ---
const int walltypes = 88;
const int walltypes = 96;
struct walltype {
char glyph;
@ -1105,7 +1276,7 @@ walltype winf[walltypes] = {
"using an Orb of Aether, your Aether power will be completely drained."
},
{ '#', 0xC0C0C0, "wall of Camelot", camelothelp },
{ '#', 0xA06000, "Round Table", camelothelp },
{ '+', 0xA06000, "Round Table", camelothelp },
{ '=', 0x0000A0, "moat of Camelot", camelothelp},
{ '+', 0x606060, "big statue of Cthulhu",
"These statues of Cthulhu are too large to carry, and they don't look too "
@ -1168,7 +1339,7 @@ walltype winf[walltypes] = {
{ '#', 0x3030FF, "charged wall", elecdesc},
{ '#', 0xFF3030, "grounded wall", elecdesc},
{ '#', 0xA0A060, "sandstone wall", elecdesc},
{ '+', 0x704000, "saloon wall", wildwestdesc},
{ '#', 0x704000, "saloon wall", wildwestdesc},
{ '#', 0x90C0C0, "metal wall", elecdesc},
{ '#', 0x607030, "dead troll", trollhelpX},
{ '+', 0xC0C0FF, "fan", winddesc},
@ -1182,13 +1353,28 @@ walltype winf[walltypes] = {
{ '#', 0xC0C000, "warp gate",
"This gate separates the warped area from the normal land."},
{ '+', 0x804000, "trunk", "The skeleton of a tree."},
{ '+', 0x804000, "solid branch", "Branches here could bear your weight easily."},
{ '+', 0x804000, "weak branch",
{ '-', 0x402000, "solid branch", "Branches here could bear your weight easily."},
{ ':', 0x804000, "weak branch",
"Branches here will bear you weight, but if you use them to move (not fall) to an unstable place, they will break."},
{ '+', 0x60C060, "canopy",
"Only thin twigs and leaves here. They may bear fruits, but for you, these cells count "
"as unstable."
}
},
{ '#', 0xD0C060, "barrow wall", "This wall is quite strong. You will need another way in."},
{ '#', 0x90A060, "barrow", "Your Orb of the Sword can be used to dig here."},
{ '#', 0xE0E0E0, "stone statue", "A petrified creature."},
{ '.', 0xE8E8E8, "tower of Camelot", camelothelp},
{ '-', 0x402000, "big bush",
"You can hold this bush to climb the Lost Mountain. "
"Bushes block the movement of birds."
},
{ ':', 0x804000, "small bush",
"You can hold this bush to climb the Lost Mountain, "
"but it is not very strong -- it will get destroyed "
"if you climb from it into an unstable location. "
"Bushes block the movement of birds."},
{ '.', 0xFFFF00, "Reptile floor", reptiledesc},
{ '.', 0xFFFF00, "Reptile bridge", reptiledesc},
};
enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCavefloor, waDeadTroll, waDune,
@ -1212,12 +1398,16 @@ enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCav
waDeadTroll2, waFan,
waTemporary, waEarthD, waElementalTmp, waElementalD,
waFloorC, waFloorD, waRose, waWarpGate,
waTrunk, waSolidBranch, waWeakBranch, waCanopy
waTrunk, waSolidBranch, waWeakBranch, waCanopy,
waBarrowWall, waBarrowDig,
waPetrified, waTower,
waBigBush, waSmallBush,
waReptile, waReptileBridge
};
// --- land types ---
const int landtypes = 56;
const int landtypes = 67;
struct landtype {
int color;
@ -1355,6 +1545,42 @@ landtype linf[landtypes] = {
},
{ 0x487830, "Galápagos", tortoisedesc},
{ 0xD04000, "Dragon Chasms", dragondesc},
{ 0xD04000, "Kraken Depths",
"A long time ago, this was a trade route. But then, Krakens have risen out of the "
"depths. Many trading ships sank here. Legend says that you can uncover the secret "
"of a magical weapon spell somewhere in the depths...\n\n"
"You can find Sunken Treasures here, but they won't appear until you have killed "
"a Kraken. You will also need Orb of the Fish to get the treasures, luckily you can "
"steal one from the Viking treasure hunters."
},
{ 0x804020, "Burial Grounds",
"Ancient Viking heroes were buried here. Their graves have barrows raised over "
"them, and are guarded by Draugar, animated corpses who are immune to mundane weapons. "
"You will need to use a magical weapon spell to defeat them, and to rob the "
"ancient jewelry buried in the graves."
},
{ 0x90A548, "Trollheim",
"Many clans of Trolls spend their lives in this kingdom. You can find many "
"statues of Trolls here. You suppose that they are not actually statues, but simply "
"elderly Trolls, who have petrified upon death. Or maybe you have killed "
"these Trolls yourself?"
},
{ 0xFF7518, "Halloween", halloweendesc},
{ 0x605040, "Dungeon",
"The result of a collaboration of the Great Vizier and the Wizard of the Ivory Tower."
},
{ 0x603000, "Lost Mountain",
"Gravitational anomalies in the Jungle create mountains "
"overgrown with ivies and bushes. "
"Will you dare to climb the ivies to get the amethysts hidden above?\n\n"
"Cells adjacent to Ivies count as stable (but Ivies "
"cannot climb themselves or other Ivies)."},
{ 0xFFFF00, "Reptiles", reptiledesc},
{ 0x0000D0, "Prairie", prairiedesc},
{ 0x800080, "Bull Dash", bulldashdesc},
{ 0xC000C0, "Crossroads V", "Extremely narrow Crossroads layout.\n"},
{ 0xC0C0C0, "Cellular Automaton", cadesc}
};
enum eLand { laNone, laBarrier, laCrossroads, laDesert, laIce, laCaves, laJungle, laAlchemist, laMirror, laGraveyard,
@ -1367,8 +1593,11 @@ enum eLand { laNone, laBarrier, laCrossroads, laDesert, laIce, laCaves, laJungle
laCanvas, laPrincessQuest,
laWildWest, laStorms, laOvergrown, laClearing,
laHaunted, laHauntedWall, laHauntedBorder,
laWhirlwind, laRose, laGridCoast, laGridSea, laCrossroads4,
laEndorian, laTortoise, laDragon
laWhirlwind, laRose, laWarpCoast, laWarpSea, laCrossroads4,
laEndorian, laTortoise, laDragon,
laKraken, laBurial, laTrollheim,
laHalloween, laDungeon, laMountain, laReptile,
laPrairie, laBull, laCrossroads5, laCA
};
// cell information for the game
@ -1387,69 +1616,104 @@ struct gcell {
unsigned ligon : 1; // is it sparkling with lightning?
unsigned
pathdist : 5, // player distance wrt usual movement
pathdist : 7, // player distance wrt usual movement
cpdist : 5, mpdist : 5; // current/minimum player distance
unsigned mondir : 3, // monster direction, for multi-tile monsters and graphics
bardir : 4, // barrier direction
stuntime : 4, // stun time left (for Palace Guards and Skeletons)
hitpoints : 3, // hitpoints left (for Palace Guards)
landflags : 2; // extra flags for land
hitpoints : 3; // hitpoints left (for Palace Guards, also reused as cpid for mirrors)
unsigned landflags : 8; // extra flags for land
char wparam; // wall parameter, used for remaining power of Bonfires and Thumpers
// 'tmp' is used for:
// pathfinding algorithm used by monsters with atypical movement (which do not use pathdist)
// bugs' pathfinding algorithm
short aitmp;
// 'landparam' is used for:
// heat in Icy/Cocytus;
// heat in Dry (0..10);
// CR2 structure;
// hive Weird Rock color / pheromones;
// Ocean/coast depth
union { int32_t landpar; float heat; char bytes[4]; } LHU;
// Ocean/coast depth;
// Bomberbird Egg hatch time / mine marking;
// number of Ancient Jewelry;
// improved tracking in Trollheim
union {
int32_t landpar;
float heat;
char bytes[4];
struct fieldinfo {
uint16_t fieldval;
unsigned rval : 4;
unsigned flowerdist : 4;
unsigned walldist : 4;
unsigned walldist2 : 4;
} fi;
} LHU;
};
#define landparam LHU.landpar
#define fval LHU.fi.fieldval
#define NODIR 7
#define NOBARRIERS 8
#define LAND_OVER 44
eLand land_over[LAND_OVER] = {
#define LAND_OVER 53
#define LAND_OVERX 55
eLand land_over[LAND_OVERX] = {
laIce, laCaves, laDesert, laMotion, laJungle, laAlchemist,
laCrossroads,
laMirror, laMinefield, laZebra, laPalace, laPrincessQuest,
laOcean, laLivefjord, laGridCoast, laCaribbean, laWhirlpool, laRlyeh, laTemple,
laMirror, laMinefield, laPalace, laPrincessQuest, laZebra, laReptile,
laOcean, laWarpCoast, laLivefjord, laKraken, laCaribbean, laWhirlpool, laRlyeh, laTemple,
laIvoryTower, laEndorian, laDungeon, laMountain,
laCrossroads2,
laDryForest, laWineyard, laDeadCaves, laGraveyard, laHaunted, laHive,
laRedRock,
laIvoryTower, laEndorian,
laDragon, laTortoise,
laOvergrown, laClearing, laStorms, laWhirlwind, laRose,
laEmerald, laCamelot, laElementalWall,
laHell, laCrossroads3, laCocytus, laPower, laCrossroads4
laOvergrown, laClearing, laStorms, laWhirlwind, laRose, laBurial,
laEmerald, laCamelot,
laPrairie, laBull,
laElementalWall, laTrollheim,
laHell, laCrossroads3, laCocytus, laPower, laCrossroads4,
laCrossroads5,
// EXTRA
laWildWest, laHalloween
};
#define LAND_EUC 42
#define LAND_EUC 49
eLand land_euc[LAND_EUC] = {
laIce, laCaves, laDesert, laMotion, laJungle,
laCrossroads,
laMirror, laMinefield, laAlchemist, laZebra, laPalace, laPrincessQuest,
laOcean, laLivefjord, laGridCoast, laCaribbean, laWhirlpool, laRlyeh, laTemple,
laElementalWall,
laOcean, laLivefjord, laWarpCoast, laCaribbean, laKraken, laWhirlpool, laRlyeh, laTemple,
laElementalWall, laTrollheim,
laDryForest, laWineyard, laDeadCaves, laGraveyard, laHive, laRedRock, laIvoryTower,
laOvergrown, laClearing, laStorms, laWhirlwind, laRose,
laOvergrown, laClearing, laStorms, laWhirlwind, laRose, laBurial,
laEmerald, laCamelot, laDragon, laTortoise,
laHell, laCrossroads3, laCocytus, laPower,
laCrossroads4,
laWildWest
laWildWest,
laReptile, laMountain, laBull, laPrairie
};
// MISSING: laCrossroads2
#define LAND_HYP 39
#define LAND_SPH 39
eLand land_sph[LAND_SPH] = {
laHalloween,
laIce, laCaves, laDesert, laMotion, laJungle,
laCrossroads,
laMirror, laMinefield, laAlchemist,
laLivefjord, laWarpCoast, laKraken, laRlyeh,
laTrollheim,
laDryForest, laDeadCaves, laGraveyard, laHive, laRedRock,
laOvergrown, laStorms, laWhirlwind, laRose, laBurial,
laEmerald, laDragon, laTortoise,
laHell, laCrossroads3, laCocytus, laPower, laElementalWall,
laCrossroads4,
laWildWest, laPalace, laBull, laPrairie, laCA
};
#define LAND_HYP 47
eLand land_hyp[LAND_HYP] = {
laHell, laCocytus, laGraveyard,
laWineyard, laDryForest, laCaves,
@ -1458,8 +1722,14 @@ eLand land_hyp[LAND_HYP] = {
laDesert, laRedRock,
laWhirlpool, laOvergrown, laClearing, laStorms,
laCaribbean, laJungle, laAlchemist, laMotion, laMirror, laMinefield,
laZebra, laElementalWall, laIvoryTower, laHaunted, laWhirlwind, laCrossroads,
laGridCoast, laRose, laDragon, laEndorian, laTortoise
laZebra, laElementalWall, laIvoryTower, laHaunted, laWhirlwind,
laWarpCoast, laRose, laDragon, laEndorian,
laReptile, laDungeon, laMountain,
laTortoise,
laKraken, laBurial, laTrollheim,
laPrairie, laBull,
// always must be last
laCrossroads
};
#define LAND_SCAPE 32
@ -1472,29 +1742,32 @@ eLand land_scape[LAND_SCAPE] = {
laOvergrown, laStorms,
laJungle, laAlchemist, laMotion, laMirror, laMinefield,
laZebra, laWhirlwind, laCrossroads,
laGridCoast, laRose,
laWarpCoast, laRose,
laCrossroads, laCrossroads2, laCrossroads3
};
#define LAND_TAC 44
#define LAND_TAC 50
struct landtacinfo { eLand l; int tries, multiplier; };
landtacinfo land_tac[LAND_TAC] = {
{laIce, 10, 1}, {laDesert, 10, 1}, {laMotion, 10, 1}, {laCaves, 10, 1}, {laAlchemist, 10, 1},
{laJungle, 10, 1}, {laMirror, 10, 1}, {laZebra, 10, 1}, {laPalace, 10, 1},
{laOcean, 10, 1}, {laLivefjord, 10, 1}, {laGridCoast, 10, 1}, {laRlyeh, 10, 1}, {laHell, 10, 1},
{laElementalWall, 10, 1}, {laDryForest, 10, 1}, {laWineyard, 10, 1},
{laOcean, 10, 1}, {laLivefjord, 10, 1}, {laWarpCoast, 10, 1}, {laRlyeh, 10, 1}, {laHell, 10, 1},
{laDryForest, 10, 1}, {laWineyard, 10, 1}, {laReptile, 10, 1},
{laDeadCaves, 10, 1}, {laGraveyard, 10, 1},
{laHaunted, 10, 1},
{laIvoryTower, 10, 1}, {laEndorian, 10, 1},
{laIvoryTower, 10, 1}, {laEndorian, 10, 1}, {laMountain, 5, 2}, {laDungeon, 5, 2},
{laEmerald, 10, 1},
{laCocytus, 10, 1},
{laCaribbean, 5, 2}, {laWhirlpool, 5, 2}, {laTemple, 5, 2}, {laMinefield, 5, 2},
{laCaribbean, 5, 2}, {laWhirlpool, 5, 2}, {laKraken, 5, 2},
{laTemple, 5, 2}, {laMinefield, 5, 2},
{laPower, 5, 2}, {laHive, 5, 2}, {laRedRock, 5, 2}, {laStorms, 5, 2}, {laOvergrown, 5, 2},
{laClearing, 5, 2},
{laWhirlwind, 5, 2}, {laRose, 5, 2}, {laDragon, 2, 5}, {laTortoise, 1, 10},
{laBurial, 5, 2},
{laElementalWall, 10, 1}, {laTrollheim, 5, 2},
{laCrossroads, 10, 1}, {laCrossroads2, 10, 1}, {laCrossroads3, 10, 1}, {laCrossroads4, 10, 1},
@ -1506,6 +1779,6 @@ landtacinfo land_tac[LAND_TAC] = {
eLand randlands[RANDLANDS] = {
laIce, laDesert, laCaves, laAlchemist, laGraveyard, laPower, laLivefjord, laZebra,
laRlyeh, laDryForest, laEmerald, laWineyard, laDeadCaves, laRedRock,
laOvergrown, laWildWest, laGridCoast
laOvergrown, laWildWest, laWarpCoast
};

File diff suppressed because it is too large Load Diff

View File

@ -4,11 +4,10 @@
namespace polygonal {
typedef long double ld;
typedef complex<long double> cld;
typedef complex<ld> cld;
int SI = 4;
double STAR = 0;
ld STAR = 0;
int deg = 20;
@ -55,7 +54,7 @@ namespace polygonal {
}
pair<ld, ld> compute(ld x, ld y, int prec) {
if(pmodel == 4) {
if(pmodel == mdPolynomial) {
cld z(x,y);
cld res (0,0);
for(int i=maxcoef; i>=0; i--) { res += coef[i]; if(i) res *= z; }
@ -77,28 +76,21 @@ namespace polygonal {
pair<ld, ld> compute(ld x, ld y) { return compute(x,y,deg); }
void drawBoundary(int color) {
#ifdef GL
if(vid.usingGL) {
qglcoords = 0;
glcolor(color);
int pts = 0;
for(int r=0; r<2000; r++) {
cld z = exp(cld(0, 2*M_PI * r / 2000.0));
pair<ld,ld> z2 = compute(real(z), imag(z), deg);
glcoords[pts][0] = vid.radius * z2.first;
glcoords[pts][1] = vid.radius * z2.second;
glcoords[pts][2] = vid.scrdist;
pts++;
}
glVertexPointer(3, GL_FLOAT, 0, glcoords);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_LINE_LOOP, 0, pts);
return;
queuereset(mdDisk, PPR_CIRCLE);
for(int r=0; r<=2000; r++) {
cld z = exp(cld(0, 2*M_PI * r / 2000.0));
pair<ld,ld> z2 = compute(real(z), imag(z), deg);
hyperpoint h;
h[0] = z2.first * vid.radius;
h[1] = z2.second * vid.radius;
h[2] = vid.scrdist;
curvepoint(h);
}
#endif
queuecurve(color, 0, PPR_CIRCLE);
queuereset(pmodel, PPR_CIRCLE);
}
}
@ -247,7 +239,7 @@ namespace conformal {
vector<cell*> movehistory;
bool includeHistory;
double lvspeed = 1;
ld lvspeed = 1;
int bandhalf = 200;
int bandsegment = 16000;
int rotation = 0;
@ -368,10 +360,9 @@ namespace conformal {
shmup::calc_relative_matrix(v[j+1]->base, v[j]->base->master) *
v[j+1]->at * C0;
int x, y, shift;
getcoord(next, x, y, shift);
tpixels += x-vid.xcenter;
hyperpoint nextscr;
applymodel(next, nextscr);
tpixels += nextscr[0] * vid.radius;
}
vid.radius = rad;
@ -447,7 +438,7 @@ namespace conformal {
v[j+1]->at * C0;
int x, y, shift;
getcoord(next, x, y, shift);
getcoord0(next, x, y, shift);
int bwidth = x-bandhalf;
@ -489,62 +480,83 @@ namespace conformal {
}
#endif
const char* directions[5][4] = {
const char* directions[MODELCOUNT][4] = {
{ "right", "up", "left", "down" },
{ "counterclockwise", "zoom out", "clockwise", "zoom in" },
{ "left to right", "spin down", "right to left", "spin up" },
{ "right", "up", "left", "down" },
{ "right", "up", "left", "down" },
{ "right", "up", "left", "down" },
{ "right", "up", "left", "down" },
{ "right", "up", "left", "down" },
{ "right", "up", "left", "down" }
};
const char *modelnames[5] = {
"disk", "half-plane", "band", "polygonal", "polynomial"
const char *modelnames[MODELCOUNT] = {
"disk", "half-plane", "band", "polygonal", "polynomial",
"azimuthal equidistant", "azimuthal equi-area",
"ball model", "hyperboloid"
};
void show() {
displayStat( 0, XLAT("conformal/history mode"), "", ' ');
dialog::init(XLAT("conformal/history mode"));
displayStat( 2, XLAT("include history"), ONOFF(includeHistory), 'i');
dialog::addBoolItem(XLAT("include history"), (includeHistory), 'i');
bool notconformal = (pmodel >= 5 && pmodel <= 6) || abs(vid.alpha-1) > 1e-3;
displayStat( 4, XLAT("model used"), modelnames[pmodel], 'm');
displayStat( 5, XLAT("rotation"), directions[pmodel][rotation&3], 'r');
dialog::addSelItem(notconformal ? XLAT("model used (not conformal!)") : XLAT("model used"), XLAT(modelnames[pmodel]), 'm');
dialog::addSelItem(XLAT("rotation"), directions[pmodel][rotation&3], 'r');
if(pmodel == 4) {
displayStat( 6, XLAT("coefficient"),
fts4(real(polygonal::coef[polygonal::coefid]))+"+"+
fts4(imag(polygonal::coef[polygonal::coefid]))+"i", 'x');
displayStat( 7, XLAT("which coefficient"), its(polygonal::coefid), 'n');
dialog::addSelItem(XLAT("coefficient"),
fts4(real(polygonal::coef[polygonal::coefid])), 'x');
dialog::addSelItem(XLAT("coefficient (imaginary)"),
fts4(imag(polygonal::coef[polygonal::coefid])), 'y');
dialog::addSelItem(XLAT("which coefficient"), its(polygonal::coefid), 'n');
}
if(pmodel == 3) {
displayStat( 6, XLAT("polygon sides"), its(polygonal::SI), 'x');
displayStat( 7, XLAT("star factor"), fts(polygonal::STAR), 'y');
displayStat( 8, XLAT("degree of the approximation"), its(polygonal::deg), 'n');
dialog::addSelItem(XLAT("polygon sides"), its(polygonal::SI), 'x');
dialog::addSelItem(XLAT("star factor"), fts(polygonal::STAR), 'y');
dialog::addSelItem(XLAT("degree of the approximation"), its(polygonal::deg), 'n');
}
displayStat(10, XLAT("prepare the line animation"), ONOFF(on), 'e');
if(on) displayStat(11, XLAT("animation speed"), fts(lvspeed), 'a');
dialog::addBoolItem(XLAT("prepare the line animation"), (on), 'e');
if(on) dialog::addSelItem(XLAT("animation speed"), fts(lvspeed), 'a');
#ifndef MOBILE
displayStat(13, XLAT("render bands automatically"), ONOFF(autoband), 'o');
dialog::addBoolItem(XLAT("render bands automatically"), (autoband), 'o');
if(autoband)
displayStat(14, XLAT("include history when auto-rendering"), ONOFF(autobandhistory), 'j');
dialog::addBoolItem(XLAT("include history when auto-rendering"), (autobandhistory), 'j');
bool renderable = on && pmodel == 2;
if(renderable || autoband) {
displayStat(15, XLAT("band width"), its(bandhalf*2), 'd');
displayStat(16, XLAT("length of a segment"), its(bandsegment), 's');
displayStat(17, XLAT("spiral on rendering"), ONOFF(dospiral), 'g');
dialog::addSelItem(XLAT("band width"), "2*"+its(bandhalf), 'd');
dialog::addSelItem(XLAT("length of a segment"), its(bandsegment), 's');
dialog::addBoolItem(XLAT("spiral on rendering"), (dospiral), 'g');
if(renderable)
displayStat(18, XLAT("render now (length: %1)", its(measureLength())), "", 'f');
dialog::addItem(XLAT("render now (length: %1)", its(measureLength())), 'f');
}
#endif
displayStat(20, XLAT("exit this menu"), "", 'q');
dialog::addItem(XLAT("exit this menu"), 'q');
dialog::display();
mouseovers = XLAT("see http://www.roguetemple.com/z/hyper/conformal.php");
}
void handleKey(int uni, int sym) {
int ib = 0;
ld compbuf;
void applyIB() {
using namespace polygonal;
cld& tgt = coef[coefid];
if(ib == 1) tgt = cld(compbuf, imag(tgt));
if(ib == 2) tgt = cld(real(tgt), compbuf);
}
void handleKey(int sym, int uni) {
dialog::handleNavigation(sym, uni);
ib = 0;
if(uni == 'e') {
if(on) clear();
@ -559,38 +571,53 @@ namespace conformal {
}
else if(uni == 'o')
autoband = !autoband;
else if(uni == 'm') {
pmodel++;
pmodel %= 5;
if(pmodel == 3) polygonal::solve();
else if(uni == 'm' || uni == 'M') {
switchagain: {
pmodel = eModel((pmodel + (shiftmul > 0 ? 1 : -1) + MODELCOUNT) % MODELCOUNT);
if(sphere)
if(pmodel == mdHalfplane || pmodel == mdBand || pmodel == mdEquidistant || pmodel == mdEquiarea)
goto switchagain;
}
if(pmodel == mdPolygonal) polygonal::solve();
/* if(pmodel && vid.usingGL) {
addMessage(XLAT("openGL mode disabled"));
vid.usingGL = false;
setvideomode();
} */
}
else if(sym == 'x' && pmodel == 3) { polygonal::SI += (shiftmul > 0 ? 1:-1); polygonal::solve(); }
else if(sym == 'y' && pmodel == 3) { polygonal::STAR += shiftmul/10; polygonal::solve(); }
else if(sym == 'n' && pmodel == 3) { polygonal::deg += (shiftmul>0?1:-1);
if(polygonal::deg < 2) polygonal::deg = 2;
if(polygonal::deg > MSI-1) polygonal::deg = MSI-1;
else if(sym == 'x' && pmodel == mdPolygonal)
dialog::editNumber(polygonal::SI, 3, 10, 1, 4, XLAT("polygon sides"), "");
else if(sym == 'y' && pmodel == mdPolygonal)
dialog::editNumber(polygonal::STAR, -1, 1, .1, 0, XLAT("star factor"), "");
else if(sym == 'n' && pmodel == mdPolygonal)
dialog::editNumber(polygonal::deg, 2, MSI-1, 1, 2, XLAT("degree of the approximation"), "");
else if(sym == 'x' && pmodel == mdPolynomial) {
polygonal::maxcoef = max(polygonal::maxcoef, polygonal::coefid);
int ci = polygonal::coefid + 1;
compbuf = real(polygonal::coef[polygonal::coefid]);
dialog::editNumber(compbuf, -10, 10, .01/ci/ci, 0, XLAT("coefficient"), "");
ib = 1;
}
else if(sym == 'x' && pmodel == 4) {
int ci = polygonal::coefid;
polygonal::coef[polygonal::coefid] += polygonal::cld(shiftmul/100/ci/ci, 0);
else if(sym == 'y' && pmodel == mdPolynomial) {
polygonal::maxcoef = max(polygonal::maxcoef, polygonal::coefid);
int ci = polygonal::coefid + 1;
compbuf = imag(polygonal::coef[polygonal::coefid]);
dialog::editNumber(compbuf, -10, 10, .01/ci/ci, 0, XLAT("coefficient (imaginary)"), "");
ib = 2;
}
else if(sym == 'y' && pmodel == 4) {
int ci = polygonal::coefid;
polygonal::coef[polygonal::coefid] += polygonal::cld(0, shiftmul/100/ci/ci);
}
else if(sym == 'n' && pmodel == 4) { polygonal::coefid += (shiftmul>0?1:-1); polygonal::maxcoef = max(polygonal::maxcoef, polygonal::coefid); }
else if(sym == 'n' && pmodel == mdPolynomial)
dialog::editNumber(polygonal::coefid, 0, MSI-1, 1, 0, XLAT("which coefficient"), "");
else if(sym == 'r') rotation += (shiftmul > 0 ? 1:3);
else if(sym == 'a') { lvspeed += shiftmul/10; }
else if(sym == 'd') { bandhalf += int(5 * shiftmul); if(bandhalf < 5) bandhalf = 5; }
else if(sym == 's') { bandsegment += int(500 * shiftmul); if(bandsegment < 500) bandsegment = 500; }
else if(sym == 'a')
dialog::editNumber(lvspeed, -5, 5, .1, 1, XLAT("animation speed"), "");
else if(sym == 'd')
dialog::editNumber(bandhalf, 5, 1000, 5, 200, XLAT("band width"), "");
else if(sym == 's')
dialog::editNumber(bandsegment, 500, 32000, 500, 16000, XLAT("band segment"), "");
else if(sym == 'g') { dospiral = !dospiral; }
#ifndef MOBILE
else if(uni == 'f' && pmodel == 2 && on) createImage(dospiral);
else if(uni == 'f' && pmodel == mdBand && on) createImage(dospiral);
#endif
else if(sym == 'q' || sym == SDLK_ESCAPE || sym == '0') { cmode = emNormal; }
else if(sym == 'i') {
@ -646,10 +673,10 @@ namespace conformal {
#ifndef MOBILE
if(celldist(cwt.c) <= 7) return;
if(!autoband) return;
int spm = pmodel;
eModel spm = pmodel;
bool ih = includeHistory;
includeHistory = autobandhistory;
pmodel = 2;
pmodel = mdBand;
create();
createImage(dospiral);
clear();

727
dialogs.cpp Normal file
View File

@ -0,0 +1,727 @@
/* Missing.
#ifndef MOBILE
dialog::addItemHelp(16, XLAT("use Shift to decrease and Ctrl to fine tune "));
dialog::addItemHelp(17, XLAT("(e.g. Shift+Ctrl+Z)"));
#endif
if(xuni == 'i') {
}
*/
namespace dialog {
namespace zoom {
int zoomf = 1, shiftx, shifty;
bool zoomoff = false;
};
#ifdef MENU_SCALING
#ifndef MOBILE
void handleZooming(SDL_Event &ev) {
using namespace zoom;
if(zoomoff || (cmode != emOverview && cmode != emTactic)) {
zoomf = 1; shiftx = shifty = 0; zoomoff = false; return;
}
if(ev.type == SDL_MOUSEBUTTONDOWN) {
zoomf = 3;
}
if(zoomf == 3) {
shiftx = -2*mousex;
shifty = -2*mousey;
}
if(ev.type == SDL_MOUSEBUTTONUP && zoomf > 1) {
zoomoff = true;
}
}
#endif
#else
inline void handleZooming(SDL_Event &ev) {}
#endif
bool displayzoom(int x, int y, int b, int size, const string &s, int color, int align) {
using namespace zoom;
return displayfr(x * zoomf + shiftx, y * zoomf + shifty, b, size * zoomf, s, color, align);
}
vector<item> items;
item& lastItem() { return items[items.size() - 1]; }
void init() { items.clear(); }
string keyname(int k) {
if(k == 0) return "";
if(k == SDLK_ESCAPE) return "Esc";
if(k == SDLK_F5) return "F5";
if(k == SDLK_F10) return "F10";
if(k == SDLK_HOME) return "Home";
if(k == 32) return "space";
if(k >= 1 && k <= 26) { string s = "Ctrl+"; s += (k+64); return s; }
if(k < 128) { string s; s += k; return s; }
return "?";
}
void addSlider(double d1, double d2, double d3, int key) {
item it;
it.type = diSlider;
it.color = 0xC0C0C0;
it.scale = 100;
it.key = key;
it.param = (d2-d1) / (d3-d1);
items.push_back(it);
}
void addSelItem(string body, string value, int key) {
item it;
it.type = diItem;
it.body = body;
it.value = value;
it.keycaption = keyname(key);
it.key = key;
it.color = 0xC0C0C0;
it.colork = 0x808080;
it.colorv = 0x80A040;
it.colors = 0xFFD500;
it.colorc = 0xFFD500;
it.colors = 0xFF8000;
if(value == ONOFF(true)) it.colorv = 0x40FF40;
if(value == ONOFF(false)) it.colorv = 0xC04040;
it.scale = 100;
items.push_back(it);
}
void addBoolItem(string body, bool value, int key) {
addSelItem(body, ONOFF(value), key);
}
void addColorItem(string body, int value, int key) {
item it;
it.type = diItem;
it.body = body;
it.value = COLORBAR;
it.keycaption = keyname(key);
it.key = key;
it.color = it.colorv = value >> 8;
it.colors = it.color ^ 0x404040;
it.colorc = it.color ^ 0x808080;
it.scale = 100;
items.push_back(it);
}
void addHelp(string body) {
item it;
it.type = diHelp;
it.body = body;
it.scale = 100;
if(size(body) >= 500) it.scale = 70;
items.push_back(it);
}
void addInfo(string body, int color) {
item it;
it.type = diInfo;
it.body = body;
it.color = color;
it.scale = 100;
items.push_back(it);
}
void addItem(string body, int key) {
item it;
it.type = diItem;
it.body = body;
it.keycaption = keyname(key);
it.key = key;
it.color = 0xC0C0C0;
it.colork = 0x808080;
it.colors = 0xFFD500;
it.colorc = 0xFF8000;
it.scale = 100;
items.push_back(it);
}
void addBreak(int val) {
item it;
it.type = diBreak;
it.scale = val;
items.push_back(it);
}
void addTitle(string body, int color, int scale) {
item it;
it.type = diTitle;
it.body = body;
it.color = color;
it.scale = scale;
items.push_back(it);
}
void init(string title, int color, int scale, int brk) {
init();
addTitle(title, color, scale);
addBreak(brk);
}
int displayLong(string str, int siz, int y, bool measure) {
int last = 0;
int lastspace = 0;
int xs = vid.xres * 618/1000;
int xo = vid.xres * 186/1000;
for(int i=0; i<=size(str); i++) {
int ls = 0;
int prev = last;
if(str[i] == ' ') lastspace = i;
if(textwidth(siz, str.substr(last, i-last)) > xs) {
if(lastspace == last) ls = i-1, last = i-1;
else ls = lastspace, last = ls+1;
}
if(str[i] == 10 || i == size(str)) ls = i, last = i+1;
if(ls) {
if(!measure) displayfr(xo, y, 2, siz, str.substr(prev, ls-prev), 0xC0C0C0, 0);
if(ls == prev) y += siz/2;
else y += siz;
lastspace = last;
}
}
y += siz/2;
return y;
}
int tothei, dialogwidth, dfsize, dfspace, leftwidth, rightwidth, innerwidth, itemx, keyx, valuex;
string highlight_text;
void measure() {
tothei = 0;
dialogwidth = 0;
innerwidth = 0;
int N = items.size();
for(int i=0; i<N; i++) {
if(items[i].type == diHelp)
tothei += displayLong(items[i].body, dfsize * items[i].scale / 100, 0, true);
else {
tothei += dfspace * items[i].scale / 100;
if(items[i].type == diItem)
innerwidth = max(innerwidth, textwidth(dfsize * items[i].scale / 100, items[i].body));
if(items[i].type == diTitle || items[i].type == diInfo)
dialogwidth = max(dialogwidth, textwidth(dfsize * items[i].scale / 100, items[i].body) * 10/9);
}
}
leftwidth = ISMOBILE ? 0 : textwidth(dfsize, "MMMMM") + dfsize/2;
rightwidth = textwidth(dfsize, "MMMMMMMM") + dfsize/2;
int fwidth = innerwidth + leftwidth + rightwidth;
dialogwidth = max(dialogwidth, fwidth);
itemx = (vid.xres - fwidth) / 2 + leftwidth;
keyx = (vid.xres - fwidth) / 2 + leftwidth - dfsize/2;
valuex = (vid.xres - fwidth) / 2 + leftwidth + innerwidth + dfsize/2;
}
void display() {
int N = items.size();
dfsize = vid.fsize;
#ifdef MOBILE
dfsize *= 3;
#endif
#ifdef PANDORA
dfsize *= 3;
#endif
dfspace = dfsize * 5/4;
measure();
while(tothei > vid.yres - 5 * vid.fsize) {
int adfsize = int(dfsize * sqrt((vid.yres - 5. * vid.fsize) / tothei));
if(adfsize < dfsize-1) dfsize = adfsize + 1;
else dfsize--;
dfspace = dfsize * 5/4;
measure();
}
while(dialogwidth > vid.xres) {
int adfsize = int(dfsize * sqrt(vid.xres * 1. / dialogwidth));
if(adfsize < dfsize-1) dfsize = adfsize + 1;
else dfsize--; // keep dfspace
measure();
}
tothei = (vid.yres - tothei) / 2;
for(int i=0; i<N; i++) {
item& I = items[i];
if(I.type == diHelp) {
tothei = displayLong(items[i].body, dfsize * items[i].scale / 100, tothei, false);
continue;
}
int top = tothei;
tothei += dfspace * I.scale / 100;
int mid = (top + tothei) / 2;
if(I.type == diTitle || I.type == diInfo) {
displayfr(vid.xres/2, mid, 2, dfsize * I.scale/100, I.body, I.color, 8);
}
else if(I.type == diItem) {
bool xthis = (mousey >= top && mousey < tothei);
#ifdef MOBILE
if(xthis && mousepressed)
I.color = I.colorc;
#else
if(xthis && mousemoved) {
highlight_text = I.body;
mousemoved = false;
}
if(highlight_text == I.body) {
I.color = (xthis&&mousepressed&&actonrelease) ? I.colorc : I.colors;
}
#endif
if(!mousing)
displayfr(keyx, mid, 2, dfsize * I.scale/100, I.keycaption, I.colork, 16);
displayfr(itemx, mid, 2, dfsize * I.scale/100, I.body, I.color, 0);
displayfr(valuex, mid, 2, dfsize * I.scale/100, I.value, I.colorv, 0);
if(xthis) getcstat = I.key;
}
else if(I.type == diSlider) {
bool xthis = (mousey >= top && mousey < tothei);
displayfr(vid.xres*1/4, mid, 2, dfsize * I.scale/100, "(", I.color, 16);
displayfr(vid.xres*1/4 + double(vid.xres/2 * I.param), mid, 2, dfsize * I.scale/100, "#", I.color, 8);
displayfr(vid.xres*3/4, mid, 2, dfsize * I.scale/100, ")", I.color, 0);
if(xthis) getcstat = I.key, inslider = true;
}
}
}
void handleNavigation(int &sym, int &uni) {
#ifndef MOBILE
if(uni == '\n' || uni == '\r' || sym == SDLK_KP5)
for(int i=0; i<size(items); i++)
if(items[i].type == diItem)
if(items[i].body == highlight_text) {
uni = sym = items[i].key;
return;
}
if(sym == SDLK_PAGEDOWN || sym == SDLK_KP3) {
for(int i=0; i<size(items); i++)
if(items[i].type == diItem)
highlight_text = items[i].body;
}
if(sym == SDLK_PAGEUP || sym == SDLK_KP9) {
for(int i=0; i<size(items); i++)
if(items[i].type == diItem) {
highlight_text = items[i].body;
break;
}
}
if(sym == SDLK_UP || sym == SDLK_KP8) {
string last = "";
for(int i=0; i<size(items); i++)
if(items[i].type == diItem)
last = items[i].body;
uni = sym = 0;
for(int i=0; i<size(items); i++)
if(items[i].type == diItem) {
if(items[i].body == highlight_text) {
highlight_text = last; return;
}
else last = items[i].body;
}
highlight_text = last;
}
if(sym == SDLK_DOWN || sym == SDLK_KP2) {
int state = 0;
for(int i=0; i<size(items); i++)
if(items[i].type == diItem) {
if(state) { highlight_text = items[i].body; return; }
else if(items[i].body == highlight_text) state = 1;
}
for(int i=0; i<size(items); i++)
if(items[i].type == diItem)
highlight_text = items[i].body;
uni = sym = 0;
}
#endif
}
unsigned int colorhistory[10] = {
0x202020FF, 0x800000FF, 0x008000FF, 0x000080FF,
0x404040FF, 0xC0C0C0FF, 0x804000FF, 0xC0C000FF,
0x408040FF, 0xFFD500FF
}, lch;
unsigned int *palette;
void drawColorDialog(int color) {
int ash = 8;
for(int j=0; j<10; j++) {
int x = vid.xres / 2 + vid.fsize * 2 * (j-5);
int y = vid.yres / 2- 5 * vid.fsize;
string s0 = ""; s0 += ('0'+j);
vid.fsize *= 2;
displayColorButton(x, y, s0, '0'+j, 0, 0, colorhistory[j] >> ash);
vid.fsize /= 2;
}
if(palette) {
int q = palette[0];
for(int i=0; i<q; i++) {
int x = vid.xres / 2 + vid.fsize * (2 * i-q);
int y = vid.yres / 2- 7 * vid.fsize;
string s0 = ""; s0 += ('a'+i);
vid.fsize *= 2;
displayColorButton(x, y, s0, 'a'+i, 0, 0, palette[i+1] >> ash);
vid.fsize /= 2;
}
}
for(int i=0; i<4; i++) {
int y = vid.yres / 2 + (2-i) * vid.fsize * 2;
displayColorButton(vid.xres / 4, y, "(", 0, 16, 0, 0xFFFFFF);
string rgt = ") "; rgt += "ABGR" [i];
displayColorButton(vid.xres * 3/4, y, rgt, 0, 0, 0, 0xFFFFFF);
displayColorButton(vid.xres /4 + vid.xres * ((color >> (8*i)) & 0xFF) / 510, y, "#", 0, 8, 0, 0xFFFFFF);
if(mousey >= y - vid.fsize && mousey < y + vid.fsize)
getcstat = 'A' + i, inslider = true;
}
displayColorButton(vid.xres/2, vid.yres/2+vid.fsize * 6, XLAT("select this color") + " : " + itsh(color), ' ', 8, 0, color >> ash);
}
// 0: nothing happened, 1: color accepted, 2: break
int handleKeyColor(int sym, int uni, int& color) {
if(uni >= 'A' && uni <= 'D') {
int x = (mousex - vid.xres/4) * 510 / vid.xres;
if(x < 0) x = 0;
if(x > 255) x = 255;
unsigned char* pts = (unsigned char*) &color;
pts[uni - 'A'] = x;
}
else if(uni == ' ') {
bool inHistory = false;
for(int i=0; i<10; i++) if(colorhistory[i] == (unsigned) color)
inHistory = true;
if(!inHistory) { colorhistory[lch] = color; lch++; lch %= 10; }
return 1;
}
else if(uni >= '0' && uni <= '9') {
color = colorhistory[uni - '0'];
}
else if(palette && uni >= 'a' && uni < 'a'+(int) palette[0]) {
color = palette[1 + uni - 'a'];
}
else if(uni || sym == SDLK_F10) return 2;
return 0;
}
int *colorPointer;
emtype lastmode;
void openColorDialog(int& col, unsigned int *pal) {
colorPointer = &col; palette = pal;
lastmode = cmode; cmode = emColor;
}
void handleColor(int sym, int uni) {
int ret = handleKeyColor(sym, uni, *colorPointer);
if(ret) cmode = lastmode;
}
struct numberEditor {
ld *editwhat;
string s;
ld vmin, vmax, step, dft;
string title, help;
ld (*scale) (ld);
ld (*inverse_scale) (ld);
int *intval; ld intbuf;
bool positive;
} ne;
bool editingDetail() {
return ne.editwhat == &geom3::highdetail || ne.editwhat == &geom3::middetail;
}
ld identity(ld x) { return x; }
void scaleSinh() {
ne.scale = ASINH;
ne.inverse_scale = sinh;
}
void scaleLog() {
ne.scale = log;
ne.inverse_scale = exp;
ne.positive = true;
}
void editNumber(ld& x, ld vmin, ld vmax, ld step, ld dft, string title, string help) {
ne.editwhat = &x;
ne.s = fts(x);
ne.vmin = vmin;
ne.vmax = vmax;
ne.step = step;
ne.dft = dft;
ne.title = title;
ne.help = help;
lastmode = cmode; cmode = emNumber;
ne.scale = ne.inverse_scale = identity;
ne.intval = NULL;
ne.positive = false;
}
void editNumber(int& x, int vmin, int vmax, int step, int dft, string title, string help) {
editNumber(ne.intbuf, vmin, vmax, step, dft, title, help);
ne.intbuf = x; ne.intval = &x; ne.s = its(x);
}
string disp(ld x) { if(ne.intval) return its((int) (x+.5)); else return fts(x); }
void affect(char kind) {
if(ne.intval) {
if(kind == 's') sscanf(ne.s.c_str(), "%d", ne.intval), *ne.editwhat = *ne.intval;
if(kind == 'v') *ne.intval = (int) (*ne.editwhat + .5), ne.s = its(*ne.intval);
}
else {
if(kind == 's') {
ld x;
sscanf(ne.s.c_str(), LDF, &x);
if(ne.positive && x <= 0) return;
*ne.editwhat = x;
}
if(kind == 'v') ne.s = fts(*ne.editwhat);
}
#ifndef NOAUDIO
if(ne.intval == &musicvolume) {
if(musicvolume < 0)
*ne.editwhat = musicvolume = 0, affect('v');
else if(musicvolume > MIX_MAX_VOLUME)
*ne.editwhat = musicvolume = MIX_MAX_VOLUME, affect('v');
#ifdef SDLAUDIO
else Mix_VolumeMusic(musicvolume);
#endif
#ifdef ANDROID
settingsChanged = true;
#endif
}
if(ne.intval == &effvolume) {
if(effvolume < 0)
*ne.editwhat = effvolume = 0, affect('v');
else if(effvolume > MIX_MAX_VOLUME)
*ne.editwhat = effvolume = MIX_MAX_VOLUME, affect('v');
#ifdef ANDROID
settingsChanged = true;
#endif
}
#endif
if(ne.intval == &vid.framelimit && vid.framelimit < 5)
*ne.editwhat = vid.framelimit = 5, affect('v');
#ifdef MOBILE
if(ne.intval == &fontscale && fontscale < 50)
*ne.editwhat = fontscale = 50, affect('v');
#endif
if(ne.intval == &sightrange && sightrange < 4)
*ne.editwhat = sightrange = 4, affect('v');
int msr = cheater ? 15 : 7;
if(ne.intval == &sightrange && sightrange > msr)
*ne.editwhat = sightrange = msr, affect('v');
if(ne.intval == &conformal::bandhalf && conformal::bandhalf < 5)
*ne.editwhat = *ne.intval = 5, affect('v');
if(ne.intval == &conformal::bandsegment && conformal::bandsegment < 500)
*ne.editwhat = *ne.intval = 500, affect('v');
if(ne.intval == &polygonal::coefid && polygonal::coefid < 0)
*ne.editwhat = *ne.intval = 0, affect('v');
if(ne.intval == &polygonal::coefid && polygonal::coefid >= MSI)
*ne.editwhat = *ne.intval = MSI-1, affect('v');
if(ne.intval == &polygonal::deg && polygonal::deg < 0)
*ne.editwhat = *ne.intval = MSI-1, affect('v');
if(ne.intval == &polygonal::deg && polygonal::deg >= MSI)
*ne.editwhat = *ne.intval = MSI-1, affect('v');
if(ne.intval == &polygonal::SI) polygonal::solve();
if(ne.editwhat == &polygonal::STAR) polygonal::solve();
conformal::applyIB();
if(ne.editwhat == &geom3::highdetail && geom3::highdetail > geom3::middetail)
geom3::middetail = geom3::highdetail;
if(ne.editwhat == &geom3::middetail && geom3::highdetail > geom3::middetail)
geom3::highdetail = geom3::middetail;
if(lastmode == em3D) buildpolys(), resetGL();
}
void drawNumberDialog() {
init(ne.title);
addInfo(ne.s);
addSlider(ne.scale(ne.vmin), ne.scale(*ne.editwhat), ne.scale(ne.vmax), 500);
addBreak(100);
#ifndef MOBILE
addHelp("You can scroll with arrow keys -- Ctrl to fine-tune");
addBreak(100);
#endif
addItem("return", ' ');
addSelItem("default value", disp(ne.dft), SDLK_HOME);
addBreak(100);
if(lastmode == em3D) ne.help = explain3D(ne.editwhat);
if(ne.help != "") {
addHelp(ne.help);
bool scal = size(ne.help) > 160;
#ifndef MOBILE
#ifndef PANDORA
scal = false;
#endif
#endif
if(scal) lastItem().scale = 30;
}
if(ne.editwhat == &vid.alpha) {
addBreak(100);
addSelItem(sphere ? "stereographic" : "Poincaré model", "1", 'p');
addSelItem(sphere ? "gnomonic" : "Klein model", "0", 'k');
addItem(sphere ? "towards orthographic" : "towards Gans model", 'o');
}
if(ne.editwhat == &ne.intbuf && ne.intval == &sightrange && cheater)
addBoolItem("overgenerate", overgenerate, 'o');
display();
}
void handleNumber(int sym, int uni) {
handleNavigation(sym, uni);
if((uni >= '0' && uni <= '9') || (uni == '.' && !ne.intval) || (uni == '-' && !ne.positive)) {
ne.s += uni;
affect('s');
}
else if(uni == '\b' || uni == '\t') {
ne.s = ne.s. substr(0, size(ne.s)-1);
sscanf(ne.s.c_str(), LDF, ne.editwhat);
affect('s');
}
#ifndef MOBILE
else if(sym == SDLK_RIGHT || sym == SDLK_KP6) {
if(ne.intval && abs(shiftmul) < .6)
(*ne.editwhat)++;
else
*ne.editwhat = ne.inverse_scale(ne.scale(*ne.editwhat) + shiftmul * ne.step);
affect('v');
}
else if(sym == SDLK_LEFT || sym == SDLK_KP4) {
if(ne.intval && abs(shiftmul) < .6)
(*ne.editwhat)--;
else
*ne.editwhat = ne.inverse_scale(ne.scale(*ne.editwhat) - shiftmul * ne.step);
affect('v');
}
#endif
else if(sym == SDLK_HOME) {
*ne.editwhat = ne.dft;
affect('v');
}
else if(uni == 500) {
ld d = (mousex - vid.xres/4 + .0) / (vid.xres/2);
*ne.editwhat =
ne.inverse_scale(d * (ne.scale(ne.vmax) - ne.scale(ne.vmin)) + ne.scale(ne.vmin));
affect('v');
}
else if(uni == 'o' && ne.editwhat == &ne.intbuf && ne.intval == &sightrange && cheater)
overgenerate = !overgenerate;
else if(uni == 'p' && ne.editwhat == &vid.alpha) {
*ne.editwhat = 1; vid.scale = 1; ne.s = "1";
}
else if(uni == 'k' && ne.editwhat == &vid.alpha) {
*ne.editwhat = 0; vid.scale = 1; ne.s = "0";
}
else if((uni == 'i' || uni == 'I' || uni == 'o' || uni == 'O') && ne.editwhat == &vid.alpha) {
double d = exp(shiftmul/10);
vid.alpha *= d;
vid.scale *= d;
ne.s = fts(vid.alpha);
}
else if(uni) {
cmode = lastmode;
}
}
int nlpage = 1;
int wheelshift = 0;
int handlePage(int& nl, int& nlm, int perpage) {
nlm = nl;
int onl = nl;
int ret = 0;
if(nlpage) {
nl = nlm = perpage;
if(nlpage == 2) ret = nlm;
int w = wheelshift;
int realw = 0;
while(w<0 && ret) {
ret--; w++; realw--;
}
while(w>0 && ret+perpage < onl) {
ret++; w--; realw++;
}
wheelshift = realw;
if(ret+nl > onl) nl = onl-ret;
}
return ret;
}
void displayPageButtons(int i, bool pages) {
int i0 = vid.yres - vid.fsize;
int xr = vid.xres / 80;
if(pages) if(displayfrZ(xr*8, i0, 1, vid.fsize, IFM("1 - ") + XLAT("page") + " 1", nlpage == 1 ? 0xD8D8C0 : 0xC0C0C0, 8))
getcstat = '1';
if(pages) if(displayfrZ(xr*24, i0, 1, vid.fsize, IFM("2 - ") + XLAT("page") + " 2", nlpage == 1 ? 0xD8D8C0 : 0xC0C0C0, 8))
getcstat = '2';
if(pages) if(displayfrZ(xr*40, i0, 1, vid.fsize, IFM("3 - ") + XLAT("all"), nlpage == 1 ? 0xD8D8C0 : 0xC0C0C0, 8))
getcstat = '3';
if(i&1) if(displayfrZ(xr*56, i0, 1, vid.fsize, IFM("0 - ") + XLAT("return"), 0xC0C0C0, 8))
getcstat = '0';
if(i&2) if(displayfrZ(xr*72, i0, 1, vid.fsize, IFM("F1 - ") + XLAT("help"), 0xC0C0C0, 8))
getcstat = SDLK_F1;
}
bool handlePageButtons(int uni) {
if(uni == '1') nlpage = 1, wheelshift = 0;
else if(uni == '2') nlpage = 2, wheelshift = 0;
else if(uni == '3') nlpage = 0, wheelshift = 0;
else if(uni == PSEUDOKEY_WHEELUP) wheelshift--;
else if(uni == PSEUDOKEY_WHEELDOWN) wheelshift++;
else return false;
return true;
}
};

234
fake-mobile.cpp Normal file
View File

@ -0,0 +1,234 @@
#define ISANDROID 0
#define ISMOBILE 1
#define ISIOS 0
#define MOBILE
#define MOBPAR_FORMAL int
#define MOBPAR_ACTUAL 0
#define FAKEMOBILE
#define MIX_MAX_VOLUME 128
const char *scorefile = "fakemobile_score.txt";
const char *conffile = "fakemobile_config.txt";
#include <SDL/SDL.h>
#include <GL/gl.h>
#include "init.cpp"
#include <SDL/SDL_ttf.h>
#include <SDL/SDL_gfxPrimitives.h>
#undef main
void playSound(cell *, const string &s, int vol) {
printf("play sound: %s vol %d\n", s.c_str(), vol);
}
SDL_Surface *s;
int gdpos = 0;
int gdpop() { return graphdata[gdpos++]; }
TTF_Font *font[256];
bool rawdisplaystr(int x, int y, int shift, int size, const char *str, int color, int align) {
if(strlen(str) == 0) return false;
if(size <= 0 || size > 255) {
return false;
}
SDL_Color col;
col.r = (color >> 16) & 255;
col.g = (color >> 8 ) & 255;
col.b = (color >> 0 ) & 255;
col.r >>= darken; col.g >>= darken; col.b >>= darken;
if(!font[size])
font[size] = TTF_OpenFont("VeraBd.ttf", size);
SDL_Surface *txt = TTF_RenderText_Solid(font[size], str, col);
if(txt == NULL) return false;
SDL_Rect rect;
rect.w = txt->w;
rect.h = txt->h;
rect.x = x - rect.w * align / 16;
rect.y = y - rect.h/2;
bool clicked = (mousex >= rect.x && mousey >= rect.y && mousex <= rect.x+rect.w && mousey <= rect.y+rect.h);
SDL_BlitSurface(txt, NULL, s,&rect);
SDL_FreeSurface(txt);
return clicked;
}
int textwidth(int siz, const string &str) {
if(size(str) == 0) return 0;
if(!font[siz]) font[siz] = TTF_OpenFont("VeraBd.ttf", siz);
int w, h;
TTF_SizeUTF8(font[siz], str.c_str(), &w, &h);
// printf("width = %d [%d]\n", w, size(str));
return w;
}
char action;
int getticks() { return SDL_GetTicks(); }
bool currentlyConnecting() { return false; }
bool currentlyConnected() { return false; }
void viewAchievements() { printf("view Achievements\n"); }
void viewLeaderboard(string id) { printf("view Leaderboard :: %s\n", id.c_str()); }
void switchGoogleConnection() { printf("sgc\n"); }
int main(int argc, char **argv) {
initAll();
vid.xres = 800; vid.yres = 450;
vid.usingGL = false;
// 450; vid.yres = 600;
s= SDL_SetVideoMode(vid.xres, vid.yres, 32, 0);
if(TTF_Init() != 0) {
printf("Failed to initialize TTF.\n");
exit(2);
}
int mx = 0; int my = 0; bool _clicked = false;
int action = 0;
firstland = laMinefield;
items[itGreenStone] = 100;
items[itDiamond] = 50;
for(int i=1; i<10; i++) kills[i] = 5;
while(true) {
SDL_LockSurface(s);
memset(s->pixels, 0, vid.xres * vid.yres * 4);
SDL_UnlockSurface(s);
mousex = mx;
mousey = my;
clicked = _clicked;
mobile_draw(MOBPAR_ACTUAL);
action = 0;
gdpos = 0;
while(gdpos < graphdata.size()) {
switch(gdpop()) {
case 2: {
int x = gdpop(), y = gdpop(), al = gdpop();
int color = gdpop();
int size = gdpop();
int b = gdpop();
int n = gdpop();
string s;
for(int i=0; i<n; i++) s += char(gdpop());
rawdisplaystr(x, y, 0, size, s.c_str(), color, al);
break;
}
case 1: {
int col = gdpop();
int otl = gdpop();
int num = gdpop();
Sint16 xpox[6000], xpoy[6000];
// printf("%4d polygon %d\n", gdpos, num);
for(int i=0; i<num; i++) xpox[i] = gdpop(), xpoy[i] = gdpop();
filledPolygonColor(s, xpox, xpoy, num, col);
aapolygonColor(s, xpox, xpoy, num, otl);
break;
}
case 3: {
int col = gdpop();
int num = gdpop();
for(int i=0; i<num; i++) polyx[i] = gdpop(), polyy[i] = gdpop();
for(int i=0; i<num-1; i++)
aalineColor(s, polyx[i], polyy[i], polyx[i+1], polyy[i+1], col);
}
case 4: {
int col = gdpop();
int x = gdpop(), y = gdpop(), rad = gdpop();
aacircleColor(s, x, y, rad, (col << 8) + 0xFF);
}
}
}
SDL_UpdateRect(s, 0, 0, vid.xres, vid.yres);
SDL_Event ev;
while(SDL_PollEvent(&ev)) {
if(ev.type == SDL_MOUSEBUTTONDOWN) {
mx = ev.button.x;
my = ev.button.y;
_clicked = true;
}
if(ev.type == SDL_MOUSEBUTTONUP) {
_clicked = false;
}
if(ev.type == SDL_MOUSEMOTION) {
mx = ev.motion.x;
my = ev.motion.y;
}
if(ev.type == SDL_KEYDOWN) {
int sym = ev.key.keysym.sym;
/* if(sym == '1') {
printf("Number of cells explored, by distance from the player:\n");
for(int i=0; i<10; i++) printf(" %d", explore[i]); printf("\n");
return 0;
}
else if(sym == '2') {
items[rand() % ittypes] += 3;
kills[rand() % motypes] += 3;
}
else if(sym == '3') {
items[itHell] = 0;
items[itGreenStone] = 100;
}
action = sym; */
extra ex;
mousing = false;
handlekey(sym, sym, ex);
}
if(ev.type == SDL_QUIT) {
SDL_Quit();
return 0;
}
}
}
SDL_Quit();
clearMemory();
}
void openURL() {
printf("< openURL > \n");
}

684
fieldpattern.cpp Normal file
View File

@ -0,0 +1,684 @@
namespace fieldpattern {
extern int subpathid;
extern int subpathorder;
bool isprime(int n) {
for(int k=2; k<n; k++) if(n%k == 0) return false;
return true;
}
struct matrix {
int a[3][3];
int* operator [] (int k) { return a[k]; }
const int* operator [] (int k) const { return a[k]; }
};
bool operator == (const matrix& A, const matrix& B) {
for(int i=0; i<3; i++) for(int j=0; j<3; j++)
if(A[i][j] != B[i][j]) return false;
return true;
}
bool operator != (const matrix& A, const matrix& B) {
for(int i=0; i<3; i++) for(int j=0; j<3; j++)
if(A[i][j] != B[i][j]) return true;
return false;
}
bool operator < (const matrix& A, const matrix& B) {
for(int i=0; i<3; i++) for(int j=0; j<3; j++)
if(A[i][j] != B[i][j]) return A[i][j] < B[i][j];
return false;
}
int btspin(int id, int d) {
return 7*(id/7) + (id + d) % 7;
}
struct fpattern {
int Prime, wsquare, Field;
// we perform our computations in the field Z_Prime[w] where w^2 equals wsquare
// (or simply Z_Prime for wsquare == 0)
#define EASY
// 'easy' assumes that all elements of the field actually used
// are of form n or mw (not n+mw), and cs and ch are both of form n
// by experimentation, such cs and ch always exist
// many computations are much simpler under that assumption
#ifndef EASY
static int neasy;
int m(int x) { x %= Prime; if(x<0) x+= Prime; return x; }
#endif
int sub(int a, int b) {
#ifdef EASY
return (a + b * (Prime-1)) % Prime;
#else
return m(a%Prime-b%Prime) + Prime * m(a/Prime-b/Prime);
#endif
}
int add(int a, int b) {
#ifdef EASY
return (a+b)%Prime;
#else
return m(a%Prime+b%Prime) + Prime * m(a/Prime+b/Prime);
#endif
}
int mul(int tx, int ty) {
#ifdef EASY
return (tx*ty*((tx<0&&ty<0)?wsquare:1)) % Prime;
#else
if(tx >= Prime && tx % Prime) neasy++;
if(ty >= Prime && ty % Prime) neasy++;
int x[2], y[2], z[3];
for(int i=0; i<3; i++) z[i] = 0;
for(int i=0; i<2; i++)
x[i] = tx%Prime, tx /= Prime;
for(int i=0; i<2; i++)
y[i] = ty%Prime, ty /= Prime;
for(int i=0; i<2; i++)
for(int j=0; j<2; j++)
z[i+j] = (z[i+j] + x[i] * y[j]) % Prime;
z[0] += z[2] * wsquare;
return m(z[0]) + Prime * m(z[1]);
#endif
}
int sqr(int x) { return mul(x,x); }
matrix mmul(const matrix& A, const matrix& B) {
matrix res;
for(int i=0; i<3; i++) for(int k=0; k<3; k++) {
#ifdef EASY
res[i][k] =
(mul(A[i][0], B[0][k]) + mul(A[i][1], B[1][k]) + mul(A[i][2], B[2][k])) % Prime;
#else
int t=0;
for(int j=0; j<3; j++) t = add(t, mul(A[i][j], B[j][k]));
res[i][k] = t;
#endif
}
return res;
}
map<matrix, int> matcode;
vector<matrix> matrices;
vector<string> qpaths;
vector<matrix> qcoords;
matrix Id, R, P;
matrix strtomatrix(string s) {
matrix res = Id;
matrix m = Id;
for(int i=size(s)-1; i>=0; i--)
if(s[i] == 'R') res = mmul(R, res);
else if (s[i] == 'P') res = mmul(P, res);
else if (s[i] == 'x') { m[0][0] = -1; res = mmul(m, res); m[0][0] = +1; }
else if (s[i] == 'y') { m[1][1] = -1; res = mmul(m, res); m[1][1] = +1; }
else if (s[i] == 'z') { m[2][2] = -1; res = mmul(m, res); m[2][2] = +1; }
return res;
}
void addas(const matrix& M, int i) {
if(!matcode.count(M)) {
matcode[M] = i;
for(int j=0; j<size(qcoords); j++)
addas(mmul(M, qcoords[j]), i);
}
}
void add(const matrix& M) {
if(!matcode.count(M)) {
int i = matrices.size();
matcode[M] = i, matrices.push_back(M);
for(int j=0; j<size(qcoords); j++)
addas(mmul(M, qcoords[j]), i);
add(mmul(R, M));
}
}
#define MXF 1000000
vector<int> connections;
vector<int> inverses;
vector<int> rrf; // rrf[i] equals gmul(i, 6)
vector<int> rpf; // rpf[i] equals gmul(i, 7)
matrix mpow(matrix M, int N) {
while((N&1) == 0) N >>= 1, M = mmul(M, M);
matrix res = M;
N >>= 1;
while(N) {
M = mmul(M,M); if(N&1) res = mmul(res, M);
N >>= 1;
}
return res;
}
int gmul(int a, int b) { return matcode[mmul(matrices[a], matrices[b])]; }
int gpow(int a, int N) { return matcode[mpow(matrices[a], N)]; }
pair<int,bool> gmul(pair<int, bool> a, int b) {
return make_pair(gmul(a.first,b), a.second);
}
int order(const matrix& M) {
int cnt = 1;
matrix Po = M;
while(Po != Id) Po = mmul(Po, M), cnt++;
return cnt;
}
string decodepath(int i) {
string s;
while(i) {
if(i % 7) i--, s += 'R';
else i = connections[i], s += 'P';
}
return s;
}
int orderstats();
int cs, sn, ch, sh;
int solve() {
for(int a=0; a<3; a++) for(int b=0; b<3; b++) Id[a][b] = a==b?1:0;
if(!isprime(Prime)) {
return 1;
}
for(int pw=1; pw<3; pw++) {
if(pw>3) break;
Field = pw==1? Prime : Prime*Prime;
if(pw == 2) {
for(wsquare=1; wsquare<Prime; wsquare++) {
int roots = 0;
for(int a=0; a<Prime; a++) if((a*a)%Prime == wsquare) roots++;
if(!roots) break;
}
} else wsquare = 0;
#ifdef EASY
int sqrts[Prime];
for(int k=0; k<Prime; k++) sqrts[k] = 0;
for(int k=1-Prime; k<Prime; k++) sqrts[sqr(k)] = k;
int fmax = Prime;
#else
int sqrts[Field];
for(int k=0; k<Field; k++) sqrts[sqr(k)] = k;
int fmax = Field;
#endif
if(Prime == 13 && wsquare) {
for(int i=0; i<Prime; i++) printf("%3d", sqrts[i]);
printf("\n");
}
for(int i=0; i<3; i++) for(int j=0; j<3; j++)
R[i][j] = P[i][j] = i==j ? 1 : 0;
for(cs=0; cs<fmax; cs++) {
int sb = sub(1, sqr(cs));
sn = sqrts[sb];
R[0][0] = cs; R[1][1] = cs;
R[0][1] = sn; R[1][0] = sub(0, sn);
matrix Z = R;
for(int i=0; i<6; i++) Z = mmul(Z, R);
if(Z != Id) continue;
if(R[0][0] == 1) continue;
for(ch=2; ch<fmax; ch++) {
int chx = sub(mul(ch,ch), 1);
sh = sqrts[chx];
P[0][0] = sub(0, ch);
P[0][2] = sub(0, sh);
P[1][1] = Prime-1;
P[2][0] = sh;
P[2][2] = ch;
matrix Z = mmul(P, R);
Z = mmul(Z, mmul(Z, Z));
if(Z == Id) return 0;
}
}
}
return 2;
}
void build() {
for(int i=0; i<size(qpaths); i++) {
matrix M = strtomatrix(qpaths[i]);
qcoords.push_back(M);
printf("Solved %s as matrix of order %d\n", qpaths[i].c_str(), order(M));
}
matcode.clear(); matrices.clear();
add(Id);
if(matrices.size() != 7) { printf("Error: rotation crash\n"); exit(1); }
connections.clear();
for(int i=0; i<(int)matrices.size(); i++) {
matrix M = matrices[i];
matrix PM = mmul(P, M);
add(PM);
if(matrices.size() % 7) { printf("Error: rotation crash\n"); exit(1); }
if(!matcode.count(PM)) { printf("Error: not marked\n"); exit(1); }
connections.push_back(matcode[PM]);
}
DEBB(DF_FIELD, (debugfile, "Computing inverses...\n"));
int N = size(matrices);
DEBB(DF_FIELD, (debugfile, "Number of heptagons: %d\n", N));
rrf.resize(N); rrf[0] = 6;
for(int i=0; i<N; i++)
rrf[btspin(i,1)] = btspin(rrf[i], 1),
rrf[connections[i]] = connections[rrf[i]];
rpf.resize(N); rpf[0] = 7;
for(int i=0; i<N; i++)
rpf[btspin(i,1)] = btspin(rpf[i], 1),
rpf[connections[i]] = connections[rpf[i]];
inverses.resize(N);
inverses[0] = 0;
for(int i=0; i<N; i++) // inverses[i] = gpow(i, N-1);
inverses[btspin(i,1)] = rrf[inverses[i]], // btspin(inverses[i],6),
inverses[connections[i]] = rpf[inverses[i]];
int errs = 0;
for(int i=0; i<N; i++) if(gmul(i, inverses[i])) errs++;
if(errs) printf("errs = %d\n", errs);
if(0) for(int i=0; i<size(matrices); i++) {
printf("%5d/%4d", connections[i], inverses[i]);
if(i%7 == 6) printf("\n");
}
DEBB(DF_FIELD, (debugfile, "Built.\n"));
}
vector<char> disthep;
vector<char> disthex;
vector<char> distwall, distriver, distwall2, distriverleft, distriverright, distflower;
vector<eItem> markers;
int getdist(pair<int,bool> a, vector<char>& dists) {
if(!a.second) return dists[a.first];
int m = 60;
int ma = dists[a.first];
int mb = dists[connections[btspin(a.first, 3)]];
int mc = dists[connections[btspin(a.first, 4)]];
m = min(m, 1 + ma);
m = min(m, 1 + mb);
m = min(m, 1 + mc);
if(m <= 2 && ma+mb+mc <= m*3-2) return m-1; // special case
m = min(m, 2 + dists[connections[btspin(a.first, 2)]]);
m = min(m, 2 + dists[connections[btspin(a.first, 5)]]);
m = min(m, 2 + dists[connections[btspin(connections[btspin(a.first, 3)], 5)]]);
return m;
}
int getdist(pair<int,bool> a, pair<int,bool> b) {
if(a.first == b.first) return a.second == b.second ? 0 : 1;
if(b.first) a.first = gmul(a.first, inverses[b.first]), b.first = 0;
return getdist(a, b.second ? disthex : disthep);
}
int maxdist, otherpole, circrad, wallid, wallorder, riverid;
int dijkstra(vector<char>& dists, vector<int> indist[64]) {
int N = connections.size();
dists.resize(N);
for(int i=0; i<N; i++) dists[i] = 60;
int maxd = 0;
for(int i=0; i<64; i++) while(!indist[i].empty()) {
int at = indist[i].back();
indist[i].pop_back();
if(dists[at] <= i) continue;
maxd = i;
dists[at] = i;
for(int q=0; q<7; q++) {
dists[at] = i;
if(purehepta)
indist[i+1].push_back(connections[at]);
else {
indist[i+2].push_back(connections[at]);
indist[i+3].push_back(connections[btspin(connections[at], 2)]);
}
at = btspin(at, 1);
}
}
return maxd;
}
void analyze() {
DEBB(DF_FIELD, (debugfile, "purehepta = %d\n", purehepta));
int N = connections.size();
markers.resize(N);
vector<int> indist[64];
indist[0].push_back(0);
int md0 = dijkstra(disthep, indist);
indist[1].push_back(0);
indist[1].push_back(connections[3]);
indist[1].push_back(connections[4]);
indist[2].push_back(connections[btspin(connections[3], 5)]);
indist[2].push_back(connections[2]);
indist[2].push_back(connections[5]);
int md1 = dijkstra(disthex, indist);
maxdist = max(md0, md1);
otherpole = 0;
for(int i=0; i<N; i+=7) {
int mp = 0;
for(int q=0; q<7; q++) if(disthep[connections[i+q]] < disthep[i]) mp++;
if(mp == 7) {
bool eq = true;
for(int q=0; q<7; q++) if(disthep[connections[i+q]] != disthep[connections[i]]) eq = false;
if(eq) {
// for(int q=0; q<7; q++) printf("%3d", disthep[connections[i+q]]);
// printf(" (%2d) at %d\n", disthep[i], i);
if(disthep[i] > disthep[otherpole]) otherpole = i;
// for(int r=0; r<7; r++) {
// printf("Matrix: "); for(int a=0; a<3; a++) for(int b=0; b<3; b++)
// printf("%4d", matrices[i+r][a][b]); printf("\n");
// }
}
}
}
circrad = 99;
for(int i=0; i<N; i++) for(int u=2; u<4; u++) if(disthep[i] < circrad)
if(disthep[connections[i]] < disthep[i] && disthep[connections[btspin(i,u)]] < disthep[i])
circrad = disthep[i];
DEBB(DF_FIELD, (debugfile, "maxdist = %d otherpole = %d circrad = %d\n", maxdist, otherpole, circrad));
matrix PRRR = strtomatrix("PRRR");
matrix PRRPRRRRR = strtomatrix("PRRPRRRRR");
matrix PRRRP = strtomatrix("PRRRP");
matrix PRP = strtomatrix("PRP");
matrix PR = strtomatrix("PR");
matrix Wall = strtomatrix("RRRPRRRRRPRRRP");
wallorder = order(Wall);
wallid = matcode[Wall];
DEBB(DF_FIELD, (debugfile, "wall order = %d\n", wallorder));
#define SETDIST(X, d, it) {int c = matcode[X]; indist[d].push_back(c); if(!it) ; else if(markers[c] && markers[c] != it) markers[c] = itBuggy; else markers[c] = it; }
matrix W = Id;
for(int i=0; i<wallorder; i++) {
SETDIST(W, 0, itAmethyst)
W = mmul(W, Wall);
}
W = P;
for(int i=0; i<wallorder; i++) {
SETDIST(W, 0, itEmerald)
W = mmul(W, Wall);
}
int walldist = dijkstra(distwall, indist);
DEBB(DF_FIELD, (debugfile, "wall dist = %d\n", walldist));
W = strtomatrix("RRRRPR");
for(int j=0; j<wallorder; j++) {
W = mmul(W, Wall);
for(int i=0; i<wallorder; i++) {
SETDIST(W, 0, itNone)
SETDIST(mmul(PRRR, W), 1, itNone)
W = mmul(Wall, W);
}
}
dijkstra(distwall2, indist);
int rpushid = matcode[PRRPRRRRR];
riverid = 0;
for(int i=0; i<N; i++) {
int j = i;
int ipush = gmul(rpushid, i);
for(int k=0; k<wallorder; k++) {
if(ipush == j) {
DEBB(DF_FIELD, (debugfile, "River found at %d:%d\n", i, k));
riverid = i;
goto riveridfound;
}
j = gmul(j, wallid);
}
}
riveridfound: ;
W = strtomatrix("RRRRPR");
for(int j=0; j<wallorder; j++) {
W = mmul(W, Wall);
for(int i=0; i<wallorder; i++) {
if(i == 7) SETDIST(W, 0, itCoast)
if(i == 3) SETDIST(mmul(PRRRP, W), 0, itWhirlpool)
W = mmul(Wall, W);
}
}
dijkstra(purehepta ? distriver : distflower, indist);
W = matrices[riverid];
for(int i=0; i<wallorder; i++) {
SETDIST(W, 0, itStatue)
W = mmul(W, Wall);
}
W = mmul(P, W);
for(int i=0; i<wallorder; i++) {
SETDIST(W, 0, itSapphire)
W = mmul(W, Wall);
}
W = mmul(PRP, matrices[riverid]);
for(int i=0; i<wallorder; i++) {
SETDIST(W, 1, itShard)
W = mmul(W, Wall);
}
W = mmul(PR, matrices[riverid]);
for(int i=0; i<wallorder; i++) {
SETDIST(W, 1, itGold)
W = mmul(W, Wall);
}
int riverdist = dijkstra(purehepta ? distflower : distriver, indist);
DEBB(DF_FIELD, (debugfile, "river dist = %d\n", riverdist));
if(!purehepta) {
W = matrices[riverid];
for(int i=0; i<wallorder; i++) {
SETDIST(W, 0, itStatue)
W = mmul(W, Wall);
}
W = mmul(PR, matrices[riverid]);
for(int i=0; i<wallorder; i++) {
SETDIST(W, 0, itGold)
W = mmul(W, Wall);
}
W = mmul(P, matrices[riverid]);
for(int i=0; i<wallorder; i++) {
SETDIST(W, 1, itSapphire)
W = mmul(W, Wall);
}
dijkstra(distriverleft, indist);
W = mmul(PRP, matrices[riverid]);
for(int i=0; i<wallorder; i++) {
SETDIST(W, 0, itShard)
W = mmul(W, Wall);
}
W = mmul(P, matrices[riverid]);
for(int i=0; i<wallorder; i++) {
SETDIST(W, 0, itSapphire)
W = mmul(W, Wall);
}
W = matrices[riverid];
for(int i=0; i<wallorder; i++) {
SETDIST(W, 1, itStatue)
W = mmul(W, Wall);
}
dijkstra(distriverright, indist);
}
else {
W = strtomatrix("RRRRPR");
for(int j=0; j<wallorder; j++) {
W = mmul(W, Wall);
for(int i=0; i<wallorder; i++) {
if(i == 7) SETDIST(W, 0, itCoast)
W = mmul(Wall, W);
}
}
dijkstra(distriverleft, indist);
W = strtomatrix("RRRRPR");
for(int j=0; j<wallorder; j++) {
W = mmul(W, Wall);
for(int i=0; i<wallorder; i++) {
if(i == 3) SETDIST(mmul(PRRRP, W), 0, itWhirlpool)
W = mmul(Wall, W);
}
}
dijkstra(distriverright, indist);
}
DEBB(DF_FIELD, (debugfile, "wall-river distance = %d\n", distwall[riverid]));
DEBB(DF_FIELD, (debugfile, "river-wall distance = %d\n", distriver[0]));
}
bool easy(int i) {
return i < Prime || !(i % Prime);
}
// 11 * 25
// (1+z+z^3) * (1+z^3+z^4) ==
// 1+z+z^7 == 1+z+z^2(z^5) == 1+z+z^2(1+z^2) = 1+z+z^2+z^4
void init(int p) {
Prime = p;
if(solve()) {
printf("error: could not solve the fieldpattern\n");
exit(1);
}
build();
}
fpattern(int p) {
if(!p) return;
init(p);
}
void findsubpath() {
int N = size(matrices);
for(int i=1; i<N; i++)
if(gpow(i, Prime) == 0) {
subpathid = i;
subpathorder = Prime;
DEBB(DF_FIELD, (debugfile, "Subpath found: %s\n", decodepath(i).c_str()));
return;
}
}
};
int fpattern::orderstats() {
int N = size(matrices);
#define MAXORD 10000
int ordcount[MAXORD];
int ordsample[MAXORD];
for(int i=0; i<MAXORD; i++) ordcount[i] = 0;
for(int i=0; i<N; i++) {
int cnt = order(matrices[i]);
if(cnt < MAXORD) {
if(!ordcount[cnt]) ordsample[cnt] = i;
ordcount[cnt]++;
}
}
printf("Listing:\n");
for(int i=0; i<MAXORD; i++) if(ordcount[i])
printf("Found %4d matrices of order %3d: %s\n", ordcount[i], i, decodepath(ordsample[i]).c_str());
return ordsample[Prime];
}
fpattern fp43(43);
void info() {
fpattern fp(0);
int cases = 0, hard = 0;
for(int p=0; p<500; p++) {
fp.Prime = p;
if(fp.solve() == 0) {
printf("%4d: wsquare=%d cs=%d sn=%d ch=%d sh=%d\n",
p, fp.wsquare, fp.cs, fp.sn, fp.ch, fp.sh);
cases++;
if(!fp.easy(fp.cs) || !fp.easy(fp.sn) || !fp.easy(fp.ch) || !fp.easy(fp.sn))
hard++;
#ifndef EASY
neasy = 0;
#endif
fp.build();
#ifndef EASY
printf("Not easy: %d\n", neasy);
#endif
int N = size(fp.matrices);
int left = N / fp.Prime;
printf("Prime decomposition: %d = %d", N, fp.Prime);
for(int p=2; p<=left; p++) while(left%p == 0) printf("*%d", p), left /= p;
printf("\n");
printf("Order of RRP is: %d\n", fp.order(fp.strtomatrix("RRP")));
printf("Order of RRRP is: %d\n", fp.order(fp.strtomatrix("RRRP")));
printf("Order of RRRPRRRRRPRRRP is: %d\n", fp.order(fp.strtomatrix("RRRPRRRRRPRRRP")));
}
}
printf("cases found = %d (%d hard)\n", cases, hard);
}
}
using fieldpattern::fp43;

224
flags.cpp
View File

@ -12,7 +12,9 @@ bool isIcyLand(cell *c) {
}
bool isGravityLand(eLand l) {
return l == laIvoryTower || l == laEndorian;
return
l == laIvoryTower || l == laEndorian ||
l == laMountain || l == laDungeon;
}
// watery
@ -32,7 +34,8 @@ bool isWateryOrBoat(cell *c) {
bool isNoFlight(cell *c) {
return
c->wall == waBoat || c->wall == waVineHalfA || c->wall == waVineHalfB ||
c->wall == waStrandedBoat || c->wall == waTrunk;
c->wall == waStrandedBoat || c->wall == waTrunk ||
c->wall == waBigBush || c->wall == waSmallBush;
}
bool boatStrandable(cell *c) {
@ -93,7 +96,7 @@ bool isNonliving(eMonster m) {
m == moMirage || m == moMirror || m == moGolem || m == moGolemMoved ||
m == moZombie || m == moGhost || m == moShadow || m == moSkeleton ||
m == moEvilGolem || m == moIllusion || m == moEarthElemental ||
m == moWaterElemental;
m == moWaterElemental || m == moDraugr;
}
bool isMetalBeast(eMonster m) {
@ -102,7 +105,8 @@ bool isMetalBeast(eMonster m) {
bool isStunnable(eMonster m) {
return m == moPalace || m == moFatGuard || m == moSkeleton || isPrincess(m) ||
isMetalBeast(m) || m == moTortoise || isDragon(m);
isMetalBeast(m) || m == moTortoise || isDragon(m) ||
m == moReptile;
}
bool hasHitpoints(eMonster m) {
@ -110,15 +114,23 @@ bool hasHitpoints(eMonster m) {
}
bool isMountable(eMonster m) {
return isWorm(m);
return isWorm(m) && m != moTentacleGhost;
}
bool isFriendly(eMonster m) {
return isMimic(m) || isGolemOrKnight(m) || m == moIllusion;
return isMimic(m) || isGolemOrKnight(m) || m == moIllusion || m == moFriendlyIvy;
}
bool isFriendlyOrPlayer(eMonster m) {
return isFriendly(m) || m == moPlayer;
}
bool isFriendly(cell *c) {
if(items[itOrbDomination] && c->monst && sameMonster(c, cwt.c)) return true;
if(items[itOrbDomination] && c->monst && c->monst != moTentacleGhost) {
for(int i=0; i<numplayers(); i++)
if(sameMonster(c, playerpos(i)))
return true;
}
return isFriendly(c->monst);
}
@ -146,13 +158,18 @@ bool isIvy(cell *c) {
}
bool isMonsterPart(eMonster m) {
return m == moMutant || (isIvy(m) && m != moIvyRoot);
return m == moMutant || (isIvy(m) && m != moIvyRoot) ||
m == moDragonTail || m == moKrakenT;
}
bool isMutantIvy(eMonster m) {
return m == moMutant;
}
bool isAnyIvy(eMonster m) {
return isIvy(m) || isMutantIvy(m) || m == moFriendlyIvy;
}
bool isBulletType(eMonster m) {
return
m == moBullet || m == moFlailBullet ||
@ -213,7 +230,7 @@ bool cellHalfvine(cell *c) {
}
bool isWarped(eLand l) {
return l == laGridCoast || l == laGridSea;
return l == laWarpCoast || l == laWarpSea;
}
bool isElementalShard(eItem i) {
@ -229,19 +246,28 @@ eMonster elementalOf(eLand l) {
return moNone;
}
eItem localshardof(eLand l) {
return eItem(itFireShard + (l - laEFire));
}
int itemclass(eItem i) {
if(i == 0) return -1;
if(i < itKey || i == itFernFlower ||
i == itWine || i == itSilver || i == itEmerald || i == itRoyalJelly || i == itPower ||
i == itGrimoire || i == itPirate || i == itRedGem || i == itBombEgg ||
i == itCoast || i == itWhirlpool || i == itPalace || i == itFjord ||
i == itElemental || i == itZebra || i == itEdge ||
i == itElemental || i == itZebra || i == itIvory ||
i == itBounty || i == itFulgurite || i == itMutant || i == itLotus || i == itMutant2 ||
i == itWindstone || i == itCoral || i == itRose ||
i == itBabyTortoise || i == itDragon || i == itApple)
i == itBabyTortoise || i == itDragon || i == itApple ||
i == itKraken || i == itBarrow || i == itTrollEgg || i == itTreat ||
i == itSlime || i == itAmethyst || i == itDodeca ||
i == itGreenGrass || i == itBull)
return IC_TREASURE;
if(i == itSavedPrincess || i == itStrongWind || i == itWarning)
return IC_NAI;
if(i == itKey || i == itOrbYendor || i == itGreenStone || i == itHolyGrail || i == itCompass ||
i == itSavedPrincess || isElementalShard(i) || i == itRevolver || i == itStrongWind)
isElementalShard(i) || i == itRevolver)
return IC_OTHER;
return IC_ORB;
}
@ -264,10 +290,11 @@ bool realred(eWall w) {
int snakelevel(eWall w) {
if(w == waRed1 || w == waDeadfloor2 || w == waRubble || w == waGargoyleFloor ||
w == waGargoyleBridge || w == waTempFloor || w == waTempBridge)
w == waGargoyleBridge || w == waTempFloor || w == waTempBridge || w == waRoundTable)
return 1;
if(w == waRed2) return 2;
if(w == waRed3) return 3;
if(w == waTower) return 3;
return 0;
}
@ -285,7 +312,9 @@ bool isWall(cell *w) {
w->wall == waStrandedBoat || w->wall == waOpenGate || w->wall == waClosePlate ||
w->wall == waOpenPlate || w->wall == waTrapdoor || w->wall == waGiantRug ||
w->wall == waLadder || w->wall == waTrunk || w->wall == waSolidBranch ||
w->wall == waWeakBranch || w->wall == waCanopy)
w->wall == waWeakBranch || w->wall == waCanopy || w->wall == waTower ||
w->wall == waSmallBush || w->wall == waBigBush ||
w->wall == waReptile || w->wall == waReptileBridge)
return false;
if(isWatery(w) || isChasmy(w) || isFire(w)) return false;
return true;
@ -293,18 +322,25 @@ bool isWall(cell *w) {
bool isAngryBird(eMonster m) {
return m == moEagle || m == moAlbatross || m == moBomberbird || m == moGargoyle ||
m == moWindCrow || m == moKestrel || m == moNighthawk;
m == moWindCrow || m == moSparrowhawk ||
m == moVampire || m == moBat || m == moButterfly || m == moGadfly;
}
bool isBird(eMonster m) {
return isAngryBird(m) || m == moTameBomberbird || m == moTameBomberbirdMoved;
return isAngryBird(m) || m == moTameBomberbird || m == moTameBomberbirdMoved;
}
bool slowMover(eMonster m) {
return
m == moLesser || m == moGreater || isMetalBeast(m) ||
m == moTortoise || m == moDraugr;
}
bool normalMover(eMonster m) {
return
m == moYeti || m == moRanger || m == moGoblin || m == moTroll || m == moDesertman ||
m == moMonkey || m == moZombie || m == moNecromancer || m == moCultist ||
m == moLesser || m == moGreater || m == moRunDog || m == moPyroCultist ||
m == moRunDog || m == moPyroCultist ||
m == moFireFairy || m == moCrystalSage || m == moHedge ||
m == moVineBeast || m == moLancer || m == moFlailer ||
m == moMiner || m == moDarkTroll ||
@ -314,12 +350,12 @@ bool normalMover(eMonster m) {
m == moRedTroll ||
m == moPalace || m == moFatGuard || m == moSkeleton || m == moVizier ||
m == moFjordTroll || m == moStormTroll || m == moForestTroll ||
m == moEdgeMonkey ||
m == moFireElemental || m == moOrangeDog ||
isMetalBeast(m) ||
m == moFamiliar ||
m == moFireElemental || m == moOrangeDog ||
m == moOutlaw || m == moRedFox || m == moFalsePrincess || m == moRoseLady ||
m == moRoseBeauty || m == moWolf ||
m == moTortoise || m == moLemur;
m == moResearcher || m == moRagingBull ||
slowMover(m);
}
// from-to
@ -339,20 +375,24 @@ bool isShark(eMonster m) {
bool isSlimeMover(eMonster m) {
return
m == moSlime || m == moSeep || m == moShark ||
m == moVineSpirit || m == moCShark || m == moParrot;
m == moSlime || m == moSeep || m == moVineSpirit || m == moParrot;
}
bool isDragon(eMonster m) { return m == moDragonHead || m == moDragonTail; }
bool isKraken(eMonster m) { return m == moKrakenH || m == moKrakenT; }
bool isBlowableMonster(eMonster m) {
return m && !(
isWorm(m) || isIvy(m) || isMutantIvy(m) || isSlimeMover(m) ||
m == moGhost || m == moGreaterShark ||
m == moWaterElemental || m == moWitchGhost || isMimic(m)
m == moWaterElemental || m == moWitchGhost || isMimic(m) ||
isKraken(m)
);
}
bool isMultitile(eMonster m) {
return isWorm(m) || isIvy(m) || isMutantIvy(m);
return isWorm(m) || isIvy(m) || isMutantIvy(m) || isKraken(m);
}
bool isSlimeMover(cell *c) {
@ -380,8 +420,7 @@ bool isLeader(eMonster m) {
}
bool isFlying(eMonster m) {
return isBird(m) || isGhost(m) || m == moAirElemental || isDragon(m) ||
(isFriendly(m) && checkOrb(m, itOrbGhost));
return isBird(m) || isGhost(m) || m == moAirElemental || isDragon(m) || checkOrb(m, itOrbAether);
}
bool survivesChasm(eMonster m) {
@ -393,7 +432,7 @@ bool ignoresPlates(eMonster m) {
}
bool itemBurns(eItem it) {
return it && it != itOrbDragon && it != itOrbFire && it != itDragon;
return it && it != itOrbDragon && it != itOrbFire && it != itDragon && it != itTreat;
}
bool attackThruVine(eMonster m) {
@ -404,6 +443,11 @@ bool attackNonAdjacent(eMonster m) {
return m == moGhost || m == moFriendlyGhost || m == moTentacleGhost;
}
bool noHighlight(eMonster m) {
return
(m == moIvyWait || m == moIvyNext || m == moIvyDead);
}
bool isInactiveEnemy(cell *w, eMonster forwho) {
if(w->monst == moWormtail || w->monst == moWormwait || w->monst == moTentacletail || w->monst == moTentaclewait || w->monst == moHexSnakeTail)
return true;
@ -429,7 +473,7 @@ bool isActiveEnemy(cell *w, eMonster forwho) {
bool isUnarmed(eMonster m) {
return
m == moMouse || m == moMouseMoved || m == moPrincess || m == moPrincessMoved ||
m == moCrystalSage;
m == moCrystalSage || m == moVampire || m == moBat;
}
bool isArmedEnemy(cell *w, eMonster forwho) {
@ -451,16 +495,17 @@ bool eternalFire(cell *c) {
bool isCyclic(eLand l) {
return
l == laWhirlpool || l == laTemple || l == laCamelot || l == laClearing;
l == laWhirlpool || l == laTemple || l == laCamelot || l == laClearing ||
l == laMountain;
}
bool haveRangedOrb() {
return
items[itOrbPsi] || items[itOrbDragon] || items[itOrbTeleport] ||
items[itOrbIllusion] || items[itOrbTelekinesis] || items[itOrbAir] ||
items[itOrbIllusion] || items[itOrbSpace] || items[itOrbAir] ||
items[itOrbFrog] || items[itOrbSummon] || items[itOrbMatter] ||
items[itRevolver] || items[itOrbStunning] || items[itStrongWind] ||
items[itOrbDomination];
items[itOrbDomination] || items[itOrbNature] || items[itOrbDash];
}
bool isOffensiveOrb(eItem it) {
@ -471,9 +516,9 @@ bool isOffensiveOrb(eItem it) {
bool isRangedOrb(eItem i) {
return i == itOrbPsi || i == itOrbDragon || i == itOrbTeleport || i == itOrbIllusion ||
i == itOrbTelekinesis || i == itOrbAir || i == itOrbFrog ||
i == itOrbSpace || i == itOrbAir || i == itOrbFrog ||
i == itOrbSummon || i == itOrbMatter || i == itRevolver || i == itOrbStunning ||
i == itOrbDomination;
i == itOrbDomination || i == itOrbNature || i == itOrbDash;
}
bool isProtectionOrb(eItem i) {
@ -484,59 +529,72 @@ bool isEmpathyOrb(eItem i) {
return
i == itOrbFire || i == itOrbDigging || i == itOrbWinter ||
i == itOrbUndeath || i == itOrbSpeed || i == itOrbShield ||
i == itOrbGhost || i == itOrbInvis || i == itOrbThorns ||
i == itOrbWater;
i == itOrbAether || i == itOrbInvis || i == itOrbThorns ||
i == itOrbWater || i == itOrbStone;
}
bool isUtilityOrb(eItem i) {
return i == itOrbSpeed || i == itOrbDigging ||
i == itOrbSafety || i == itOrbTeleport || i == itOrbGhost ||
i == itOrbPreserve || i == itOrbTelekinesis ||
i == itOrbSafety || i == itOrbTeleport || i == itOrbAether ||
i == itOrbTime || i == itOrbSpace ||
i == itOrbSummon || i == itOrbLuck || i == itOrbEnergy;
}
bool isDirectionalOrb(eItem i) {
return i == itOrbHorns || i == itOrbBull;
}
bool isRevivalOrb(eItem i) {
return i == itOrbLife || i == itOrbFriend || i == itOrbUndeath;
}
bool isFriendOrb(eItem i) {
return i == itOrbLife || i == itOrbFriend || i == itOrbDiscord || i == itOrbLove ||
i == itOrbEmpathy || i == itOrbUndeath;
}
bool isFriendlyGhost(eMonster m) {
return m == moFriendlyGhost || (markEmpathy(itOrbGhost) && isFriendly(m));
return m == moFriendlyGhost || (markEmpathy(itOrbAether) && isFriendly(m));
}
bool isDragon(eMonster m) { return m == moDragonHead || m == moDragonTail; }
bool survivesWater(eMonster m) {
return
m == moShark || m == moGreaterShark || m == moCShark ||
isGhost(m) || m == moWitchGhost ||
isGhost(m) || m == moWitchGhost || m == moShadow ||
isBird(m) || m == moWaterElemental || m == moAirElemental ||
isWorm(m) || isIvy(m) || isDragon(m) ||
isWorm(m) || isIvy(m) || isDragon(m) || isKraken(m) ||
m == moMutant || m == moFriendlyIvy ||
m == moTortoise; // Tortoises and Ivies survive, but don't go through water
}
// flying even if stunned
bool isPermanentFlying(eMonster m) {
return m == moAirElemental || isGhost(m);
}
bool survivesFire(eMonster m) {
return
isGhost(m) || m == moWitchWinter || m == moWitchGhost ||
m == moBomberbird || m == moTameBomberbird || m == moTameBomberbirdMoved ||
(isFriendly(m) && markOrb(itOrbWinter)) || isWorm(m) || m == moFireElemental ||
isDragon(m);
isDragon(m) || m == moShadow;
}
bool survivesMine(eMonster m) {
return isFlying(m);
}
/* bool survivesMine(eMonster m) {
return ignoresPlates(m) || isFlying(m);
} */
bool survivesWall(eMonster m) {
return isGhost(m);
}
bool survivesThorns(eMonster m) {
return isGhost(m) || m == moSkeleton;
return isGhost(m) || m == moSkeleton || m == moDraugr;
}
bool survivesFall(eMonster m) {
return isBird(m) || m == moAirElemental || m == moSkeleton || isDragon(m);
return isBird(m) || m == moAirElemental || m == moSkeleton || isDragon(m) || m == moShadow ||
isGhost(m);
}
bool isThorny(eWall w) { return w == waRose; }
@ -547,9 +605,75 @@ bool checkOrb(eMonster m1, eItem orb) {
return false;
}
bool checkOrb2(eMonster m1, eItem orb) {
if(m1 == moPlayer) return markOrb2(orb);
if(isFriendly(m1)) return markEmpathy2(orb);
return false;
}
bool ignoresSmell(eMonster m) {
return
m == moHexSnake || isIvy(m) || isMutantIvy(m) || isGhostMover(m) || isSlimeMover(m) ||
m == moRoseLady || checkOrb(m, itOrbSkunk) || checkOrb(m, itOrbGhost) || checkOrb(m, itOrbShield);
m == moRoseLady || checkOrb(m, itOrbBeauty) || checkOrb(m, itOrbAether) || checkOrb(m, itOrbShield);
}
bool isTroll(eMonster m) {
return
m == moTroll || m == moRedTroll || m == moDarkTroll ||
m == moForestTroll || m == moStormTroll || m == moFjordTroll;
}
bool isGrave(eWall w) {
return w == waFreshGrave || w == waAncientGrave;
}
bool isTree(cell *c) {
return false; // c->wall == waBigTree || c->wall == waSmallTree;
}
bool highwall(cell *c) {
if(c->wall == waGlass) return false;
if(wmescher && wmspatial && c->wall == waBarrier && c->land == laOceanWall)
return false;
// if(wmspatial && isTree(c)) return false;
if(isGrave(c->wall)) return true;
return winf[c->wall].glyph == '#' || c->wall == waClosedGate;
}
int chasmgraph(cell *c) {
if(c->wall == waChasm) return 2;
if(isChasmy(c)) return 1;
if(isWateryOrBoat(c)) return 1;
if(wmescher && c->wall == waBarrier && c->land == laOceanWall) return 1;
if(c->wall == waReptileBridge) return 1;
return 0;
}
bool conegraph(cell *c) {
return wmescher && wmspatial && (c->wall == waDune || c->wall == waBigTree || c->wall == waSmallTree || c->wall == waCTree || (c->wall == waBarrier && c->land == laOceanWall));
}
bool isReptile(eWall w) {
return w == waReptile || w == waReptileBridge;
}
bool isBull(eMonster m) {
return m == moRagingBull || m == moHerdBull || m == moSleepBull;
}
bool hornStuns(cell *c) {
eMonster m = c->monst;
return
m == moRagingBull || m == moSleepBull || m == moHerdBull ||
m == moButterfly || m == moGreater || m == moGreaterM || m == moDraugr ||
m == moHedge || m == moFlailer || m == moVizier ||
attackJustStuns(c);
}
// generate all the world first in the quotient geometry
bool generateAll(eLand l) {
return
l == laIce || l == laDryForest || l == laCocytus || l == laLivefjord ||
l == laCaves || l == laCA;
}

3780
game.cpp

File diff suppressed because it is too large Load Diff

View File

@ -4,21 +4,33 @@
// Copyright (C) 2011-2012 Zeno Rogue, see 'hyper.cpp' for details
ld tessf, crossf, hexf, hcrossf;
ld tessf, crossf, hexf, hcrossf, hexhexdist;
// tessf: distance from heptagon center to another heptagon center
// hexf: distance from heptagon center to heptagon vertex
// crossf: distance from heptagon center to adjacent hexagon center
// hexhexdist: distance between adjacent hexagon vertices
#define ALPHA (M_PI*2/7)
#define ALPHA (M_PI*2/S7)
hyperpoint Crad[42];
transmatrix heptmove[7], hexmove[7];
transmatrix invheptmove[7], invhexmove[7];
transmatrix spinmatrix[84];
const transmatrix& getspinmatrix(int id) {
while(id>=S84) id -= S84;
while(id<0) id += S84;
return spinmatrix[id];
}
// the results are:
// hexf = 0.378077 hcrossf = 0.620672 tessf = 1.090550
// hexhexdist = 0.566256
// the distance between two hexagon centers
void precalc() {
@ -31,15 +43,15 @@ void precalc() {
for(int p=0; p<100; p++) {
ld f = (fmin+fmax) / 2;
hyperpoint H = xpush(f) * C0;
ld v1 = intval(H, C0), v2 = intval(H, spin(2*M_PI/7)*H);
if(v1 > v2) fmin = f; else fmax = f;
ld v1 = intval(H, C0), v2 = intval(H, spin(2*M_PI/S7)*H);
if(sphere ? v1 < v2 : v1 > v2) fmin = f; else fmax = f;
}
tessf = fmin;
fmin = 0, fmax = 2;
fmin = 0, fmax = sphere ? M_PI / 2 : 2;
for(int p=0; p<100; p++) {
ld f = (fmin+fmax) / 2;
hyperpoint H = spin(M_PI/7) * xpush(f) * C0;
hyperpoint H = spin(M_PI/S7) * xpush(f) * C0;
ld v1 = intval(H, C0), v2 = intval(H, xpush(tessf) * C0);
if(v1 < v2) fmin = f; else fmax = f;
}
@ -50,7 +62,7 @@ void precalc() {
for(int p=0; p<100; p++) {
ld f = (fmin+fmax) / 2;
hyperpoint H = xpush(f) * C0;
hyperpoint H1 = spin(2*M_PI/7) * H;
hyperpoint H1 = spin(2*M_PI/S7) * H;
hyperpoint H2 = xpush(tessf-f) * C0;
ld v1 = intval(H, H1), v2 = intval(H, H2);
if(v1 < v2) fmin = f; else fmax = f;
@ -59,84 +71,180 @@ void precalc() {
// printf("hexf = %.6Lf cross = %.6Lf tessf = %.6Lf\n", hexf, crossf, tessf);
for(int i=0; i<42; i++)
Crad[i] = spin(2*M_PI*i/42) * xpush(.4) * C0;
for(int d=0; d<7; d++)
for(int i=0; i<S42; i++)
Crad[i] = spin(2*M_PI*i/S42) * xpush(.4) * C0;
for(int d=0; d<S7; d++)
heptmove[d] = spin(-d * ALPHA) * xpush(tessf) * spin(M_PI);
for(int d=0; d<7; d++)
for(int d=0; d<S7; d++)
hexmove[d] = spin(-d * ALPHA) * xpush(-crossf)* spin(M_PI);
for(int d=0; d<7; d++) invheptmove[d] = inverse(heptmove[d]);
for(int d=0; d<7; d++) invhexmove[d] = inverse(hexmove[d]);
for(int d=0; d<S7; d++) invheptmove[d] = inverse(heptmove[d]);
for(int d=0; d<S7; d++) invhexmove[d] = inverse(hexmove[d]);
hexhexdist = hdist(xpush(crossf) * C0, spin(M_PI*2/S7) * xpush(crossf) * C0);
for(int i=0; i<S84; i++) spinmatrix[i] = spin(i * M_PI / S42);
}
transmatrix ddi(ld dir, ld dist) {
// EUCLIDEAN
if(euclid)
return eupush(cos(M_PI*dir/42) * dist, -sin(M_PI*dir/42) * dist);
return eupush(cos(M_PI*dir/S42) * dist, -sin(M_PI*dir/S42) * dist);
else
return spin(M_PI*dir/42) * xpush(dist) * spin(-M_PI*dir/42);
return spin(M_PI*dir/S42) * xpush(dist) * spin(-M_PI*dir/S42);
}
// tesselation drawing
hyperpoint ddi0(ld dir, ld dist) {
if(euclid)
return hpxy(cos(M_PI*dir/S42) * dist, -sin(M_PI*dir/S42) * dist);
else
return xspinpush0(M_PI*dir/S42, dist);
}
#define NUMFACE 500
transmatrix tess[NUMFACE];
namespace geom3 {
void genTesselation() {
int N = 1;
tess[0] = Id;
for(int i=0; i<N; i++) {
for(int t=0; t<7; t++) {
ld trot = (t % 8) * M_PI * 2 / 7.0;
transmatrix T = spin(trot) * xpush(tessf) * /*spin(-trot) */ spin(M_PI) * tess[i];
for(int j=0; j<N; j++) if(intval(T*C0, tess[j]*C0) < 0.1) goto nextt;
// printf("%d:%d -> %d\n", i,t, N);
tess[N] = T; N++;
if(N == NUMFACE) return;
nextt: ;
int tc_alpha=3, tc_depth=1, tc_camera=2;
ld depth = 1; // world below the plane
ld camera = 1; // camera above the plane
ld wall_height = .3;
ld slev = .08;
ld lake_top = .25, lake_bottom = .9;
ld rock_wall_ratio = .9;
ld human_wall_ratio = .7;
ld human_height;
ld highdetail = 8, middetail = 8;
// Here we convert between the following parameters:
// abslev: level below the plane
// lev: level above the world (abslev = depth-lev)
// projection: projection parameter
// factor: zoom factor
ld abslev_to_projection(ld abslev) {
if(sphere || euclid) return camera+abslev;
return tanh(abslev) / tanh(camera);
}
ld projection_to_abslev(ld proj) {
if(sphere || euclid) return proj-camera;
// tanh(abslev) / tanh(camera) = proj
return atanh(proj * tanh(camera));
}
ld lev_to_projection(ld lev) {
return abslev_to_projection(depth - lev);
}
ld projection_to_factor(ld proj) {
return lev_to_projection(0) / proj;
}
ld factor_to_projection(ld fac) {
return lev_to_projection(0) / fac;
}
ld lev_to_factor(ld lev) {
return projection_to_factor(lev_to_projection(lev));
}
ld factor_to_lev(ld fac) {
return depth - projection_to_abslev(factor_to_projection(fac));
}
// how should we scale at level lev
ld scale_at_lev(ld lev) {
if(sphere || euclid) return 1;
return cosh(depth - lev);
}
ld INFDEEP, BOTTOM, HELLSPIKE, LAKE, WALL,
SLEV[4], FLATEYE,
LEG1, LEG, LEG3, GROIN, GROIN1, GHOST,
BODY, NECK1, NECK, NECK3, HEAD,
ABODY, AHEAD, BIRD;
string invalid;
void compute() {
// tanh(depth) / tanh(camera) == vid.alpha
invalid = "";
if(tc_alpha < tc_depth && tc_alpha < tc_camera)
vid.alpha = tanh(depth) / tanh(camera);
else if(tc_depth < tc_alpha && tc_depth < tc_camera) {
ld v = vid.alpha * tanh(camera);
if(v<-1 || v>1) invalid = "cannot adjust depth", depth = camera;
else depth = atanh(v);
}
}
}
struct ltd {
hyperpoint P1;
hyperpoint P2;
int col;
};
vector<ltd> lines;
void addline(hyperpoint P1, hyperpoint P2, int col) {
ltd L;
L.P1 = P1; L.P2 = P2; L.col = col;
lines.push_back(L);
}
void addlines() {
// change the if(0) conditions to see the underlying structure
else {
ld v = tanh(depth) / vid.alpha;
if(v<-1 || v>1) invalid = "cannot adjust camera", camera = depth;
else camera = atanh(v);
}
if(fabs(vid.alpha) < 1e-6) invalid = "does not work with perfect Klein";
if(0) for(int t =0; t<NUMFACE; t++) for(int u=1; u<8; u++) {
addline(View * tess[t] * C0, View * tess[t] * tess[u] * C0, u==1 ? 0xA000 : 0x4000);
}
if(0) for(int t =0; t<NUMFACE; t++) for(int r=0; r<7; r++) {
addline(
View * tess[t] * spin((2*r+1)*M_PI/7) * xpush(crossf) * C0,
View * tess[t] * spin((2*r+3)*M_PI/7) * xpush(crossf) * C0,
0x808080);
}
if(1) for(int t =0; t<NUMFACE; t++) for(int r=0; r<7; r++) {
addline(tess[t] * spin(M_PI*2*(r+1)/7) * xpush(hexf) * C0, tess[t] * spin(M_PI*2*r/7) * xpush(hexf) * C0, 0x404040);
addline(tess[t] * spin(M_PI*2*r/7) * xpush(hexf) * C0, tess[t] * spin(M_PI*2*r/7) * xpush(tessf/2) * C0, 0x404040);
}
if(invalid != "") {
INFDEEP = .7;
BOTTOM = .8;
HELLSPIKE = .85;
LAKE = .9;
WALL = 1.25;
SLEV[0] = 1;
SLEV[1] = 1.08;
SLEV[2] = 1.16;
SLEV[3] = 1.24;
FLATEYE = 1.03;
LEG1 = 1.025;
LEG = 1.05;
LEG3 = 1.075;
GROIN = 1.09;
GROIN1 = 1.105;
GHOST = 1.1;
BODY = 1.15;
NECK1 = 1.16;
NECK = 1.17;
NECK3 = 1.18;
HEAD = 1.19;
ABODY = 1.08;
AHEAD = 1.12;
BIRD = 1.20;
}
else {
INFDEEP = (euclid || sphere) ? 0.01 : lev_to_projection(0) * tanh(camera);
WALL = lev_to_factor(wall_height);
human_height = human_wall_ratio * wall_height;
LEG1 = lev_to_factor(human_height * .1);
LEG = lev_to_factor(human_height * .2);
LEG3 = lev_to_factor(human_height * .3);
GROIN = lev_to_factor(human_height * .4);
GROIN1= lev_to_factor(human_height * .5);
BODY = lev_to_factor(human_height * .6);
NECK1 = lev_to_factor(human_height * .7);
NECK = lev_to_factor(human_height * .8);
NECK3 = lev_to_factor(human_height * .9);
HEAD = lev_to_factor(human_height);
ABODY = lev_to_factor(human_height * .4);
AHEAD = lev_to_factor(human_height * .6);
BIRD = lev_to_factor((human_wall_ratio+1)/2 * wall_height * .8);
GHOST = lev_to_factor(human_height * .5);
FLATEYE = lev_to_factor(human_height * .15);
slev = rock_wall_ratio * wall_height / 3;
for(int s=0; s<=3; s++)
SLEV[s] = lev_to_factor(rock_wall_ratio * wall_height * s/3);
LAKE = lev_to_factor(-lake_top);
HELLSPIKE = lev_to_factor(-(lake_top+lake_bottom)/2);
BOTTOM = lev_to_factor(-lake_bottom);
}
}
}
void initgeo() {
// printf("%Lf\n", (ld) hdist0(xpush(-1)*ypush(0.01)*xpush(1)*C0));
precalc();
genTesselation();
addlines();
}

7916
graph.cpp

File diff suppressed because it is too large Load Diff

View File

@ -4,24 +4,42 @@
// heptagon here refers to underlying heptagonal tesselation
// (which you can see by changing the conditions in graph.cpp)
// automaton state
enum hstate { hsOrigin, hsA, hsB, hsError };
#define MIRR(x) x.mirrored
int fixrot(int a) { return (a+98)% 7; }
int fix42(int a) { return (a+420)% 42; }
// automaton state
enum hstate { hsOrigin, hsA, hsB, hsError, hsA0, hsA1, hsB0, hsB1, hsC };
int fixrot(int a) { return (a+490)% S7; }
int fix42(int a) { return (a+420)% S42; }
struct heptagon;
struct cell;
cell *newCell(int type, heptagon *master);
#define CDATA
// spintable functions
int tspin(uint32_t& t, int d) {
return (t >> (d<<2)) & 7;
}
int tmirror(uint32_t& t, int d) {
return (t >> ((d<<2)+3)) & 1;
}
void tsetspin(uint32_t& t, int d, int spin) {
t &= ~(15 << (d<<2));
t |= spin << (d<<2);
}
struct heptagon {
// automaton state
hstate s : 8;
// we are spin[i]-th neighbor of move[i]
unsigned char spin[7];
uint32_t spintable;
int spin(int d) { return tspin(spintable, d); }
int mirror(int d) { return tmirror(spintable, d); }
void setspin(int d, int sp) { tsetspin(spintable, d, sp); }
// neighbors; move[0] always goes towards origin,
// and then we go clockwise
heptagon* move[7];
@ -33,18 +51,18 @@ struct heptagon {
short fiftyval;
// zebra generator (1B actually)
short zebraval;
#ifdef CDATA
// field id
int fieldval;
// evolution data
short rval0, rval1;
struct cdata *cdata;
#endif
// central cell
cell *c7;
// associated generator of alternate structure, for Camelot and horocycles
heptagon *alt;
// functions
heptagon*& modmove(int i) { return move[fixrot(i)]; }
unsigned char& gspin(int i) { return spin[fixrot(i)]; }
unsigned char gspin(int i) { return spin(fixrot(i)); }
};
// the automaton is used to generate each heptagon in an unique way
@ -54,15 +72,32 @@ struct heptagon {
// and sometimes in direction 5
hstate transition(hstate s, int dir) {
if(s == hsOrigin) return hsA;
if(s == hsA && dir >= 3 && dir <= 4) return hsA;
if(s == hsA && dir == 5) return hsB;
if(s == hsB && dir == 4) return hsB;
if(s == hsB && dir == 3) return hsA;
if(sphere) {
if(S7 == 4) {
if(s == hsOrigin) return dir == 0 ? hsB0 : hsB1;
}
if(S7 == 3) {
if(s == hsOrigin) return hsB1;
}
if(s == hsOrigin) return dir == 0 ? hsA0 : hsA1;
if(s == hsA0 && dir == 2) return hsB0;
if(s == hsA1 && dir == 2) return hsB1;
if(s == hsB0 && dir == S7-2) return hsC;
return hsError;
}
else {
if(s == hsOrigin) return hsA;
if(s == hsA && dir >= 3 && dir <= 4) return hsA;
if(s == hsA && dir == 5) return hsB;
if(s == hsB && dir == 4) return hsB;
if(s == hsB && dir == 3) return hsA;
}
return hsError;
}
heptagon origin;
heptagon dodecahedron[12];
#define origin (dodecahedron[0])
vector<heptagon*> allAlts;
// create h->move[d] if not created yet
@ -74,16 +109,16 @@ heptagon *buildHeptagon(heptagon *parent, int d, hstate s, int pard = 0) {
h->alt = NULL;
h->s = s;
for(int i=0; i<7; i++) h->move[i] = NULL;
h->move[pard] = parent; h->spin[pard] = d;
parent->move[d] = h; parent->spin[d] = pard;
h->spintable = 0;
h->move[pard] = parent; tsetspin(h->spintable, pard, d);
parent->move[d] = h; tsetspin(parent->spintable, d, pard);
if(parent->c7) {
h->c7 = newCell(7, h);
h->emeraldval = emerald_heptagon(parent->emeraldval, d);
h->zebraval = zebra_heptagon(parent->zebraval, d);
#ifdef CDATA
h->fieldval = fp43.connections[fieldpattern::btspin(parent->fieldval, d)];
h->rval0 = h->rval1 = 0; h->cdata = NULL;
#endif
if(parent == &origin)
if(parent == &origin || parent == origin.alt)
h->fiftyval = fiftytable[0][d];
else
h->fiftyval = nextfiftyval(parent->fiftyval, parent->move[0]->fiftyval, d);
@ -98,10 +133,10 @@ heptagon *buildHeptagon(heptagon *parent, int d, hstate s, int pard = 0) {
if(pard == 0) {
if(purehepta) h->distance = parent->distance + 1;
else if(parent->s == hsOrigin) h->distance = 2;
else if(h->spin[0] == 5)
else if(h->spin(0) == 5)
h->distance = parent->distance + 1;
else if(h->spin[0] == 4 && h->move[0]->s == hsB)
h->distance = createStep(h->move[0], (h->spin[0]+2)%7)->distance + 3;
else if(h->spin(0) == 4 && h->move[0]->s == hsB)
h->distance = createStep(h->move[0], (h->spin(0)+2)%7)->distance + 3;
else h->distance = parent->distance + 2;
}
else h->distance = parent->distance - (purehepta?1:2);
@ -112,9 +147,9 @@ void addSpin(heptagon *h, int d, heptagon *from, int rot, int spin) {
rot = fixrot(rot);
createStep(from, rot);
h->move[d] = from->move[rot];
h->spin[d] = fixrot(from->spin[rot] + spin);
h->move[d]->move[fixrot(from->spin[rot] + spin)] = h;
h->move[d]->spin[fixrot(from->spin[rot] + spin)] = d;
h->setspin(d, fixrot(from->spin(rot) + spin));
h->move[d]->move[fixrot(from->spin(rot) + spin)] = h;
h->move[d]->setspin(fixrot(from->spin(rot) + spin), d);
//generateEmeraldval(h->move[d]); generateEmeraldval(h);
}
@ -122,7 +157,7 @@ extern int hrand(int);
heptagon *createStep(heptagon *h, int d) {
d = fixrot(d);
if(h->s != hsOrigin && !h->move[0]) {
if(!h->move[0] && h->s != hsOrigin) {
buildHeptagon(h, 0, hsA, 3 + hrand(2));
}
if(h->move[d]) return h->move[d];
@ -130,18 +165,18 @@ heptagon *createStep(heptagon *h, int d) {
buildHeptagon(h, d, hsA);
}
else if(d == 1) {
addSpin(h, d, h->move[0], h->spin[0]-1, -1);
addSpin(h, d, h->move[0], h->spin(0)-1, -1);
}
else if(d == 6) {
addSpin(h, d, h->move[0], h->spin[0]+1, +1);
addSpin(h, d, h->move[0], h->spin(0)+1, +1);
}
else if(d == 2) {
createStep(h->move[0], h->spin[0]-1);
addSpin(h, d, h->move[0]->modmove(h->spin[0]-1), 5 + h->move[0]->gspin(h->spin[0]-1), -1);
createStep(h->move[0], h->spin(0)-1);
addSpin(h, d, h->move[0]->modmove(h->spin(0)-1), 5 + h->move[0]->gspin(h->spin(0)-1), -1);
}
else if(d == 5 && h->s == hsB) {
createStep(h->move[0], h->spin[0]+1);
addSpin(h, d, h->move[0]->modmove(h->spin[0]+1), 2 + h->move[0]->gspin(h->spin[0]+1), +1);
createStep(h->move[0], h->spin(0)+1);
addSpin(h, d, h->move[0]->modmove(h->spin(0)+1), 2 + h->move[0]->gspin(h->spin(0)+1), +1);
}
else
buildHeptagon(h, d, (d == 5 || (h->s == hsB && d == 4)) ? hsB : hsA);
@ -153,20 +188,24 @@ heptagon *createStep(heptagon *h, int d) {
struct heptspin {
heptagon *h;
int spin;
bool mirrored;
heptspin() { mirrored = false; }
};
heptspin hsstep(const heptspin &hs, int spin) {
createStep(hs.h, hs.spin);
heptspin res;
res.h = hs.h->move[hs.spin];
res.spin = fixrot(hs.h->spin[hs.spin] + spin);
res.mirrored = hs.mirrored ^ hs.h->mirror(hs.spin);
res.spin = fixrot(hs.h->spin(hs.spin) + (MIRR(res)?-spin:spin));
return res;
}
heptspin hsspin(const heptspin &hs, int val) {
heptspin res;
res.h = hs.h;
res.spin = fixrot(hs.spin + val);
res.spin = fixrot(hs.spin + (MIRR(hs)?-val:val));
res.mirrored = hs.mirrored;
return res;
}
@ -174,7 +213,7 @@ heptspin hsspin(const heptspin &hs, int val) {
void backtrace(heptagon *pos) {
if(pos == &origin) return;
backtrace(pos->move[0]);
printf(" %d", pos->spin[0]);
printf(" %d", pos->spin(0));
}
void hsshow(const heptspin& t) {

582
hyper.cpp
View File

@ -15,46 +15,17 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef LOCAL
#define CDATA
#endif
#define VER "8.3j"
#define VERNUM 8310
#define VERNUM_HEX 0x8310
#define ISANDROID 0
#define ISMOBILE 0
#define ISIOS 0
#define USE_SDL
#define USE_COMMANDLINE
#include <stdio.h>
#include <SDL/SDL.h>
#ifndef MAC
#undef main
#ifdef STEAM
#define NOLICENSE
#endif
#include <SDL/SDL_ttf.h>
#include <math.h>
#include <time.h>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
FILE *debugfile;
int debugflags;
const char *scorefile = "hyperrogue.log";
const char *conffile = "hyperrogue.ini";
string levelfile = "hyperrogue.lev";
string picfile = "hyperrogue.pic";
const char *loadlevel = NULL;
const char *musicfile = "";
#include "init.cpp"
#ifdef LINUX
#include <sys/resource.h>
@ -77,58 +48,29 @@ void moreStack() {
}
#endif
string s0;
void addMessage(string s, char spamtype = 0);
eLand readland(const char *s) {
string ss = s;
if(ss == "II") return laCrossroads2;
if(ss == "III") return laCrossroads3;
if(ss == "IV") return laCrossroads4;
if(ss == "V") return laCrossroads5;
for(int l=0; l<landtypes; l++) if(strstr(linf[l].name, s) != NULL) {
return eLand(l);
break;
}
return laNone;
}
int clWidth, clHeight, clFont;
string commandline;
#include "hyperpoint.cpp"
#include "patterns.cpp"
#include "heptagon.cpp"
#include "classes.cpp"
#include "language.cpp"
#ifdef STEAM
#define NOLICENSE
#endif
#include "hyper.h"
#include "cell.cpp"
#include "flags.cpp"
#include "yendor.cpp"
#include "complex.cpp"
#include "game.cpp"
#include "landgen.cpp"
#include "orbs.cpp"
#include "system.cpp"
// #include "patterngen.cpp"
#include "geometry.cpp"
#include "polygons.cpp"
#ifndef MOBILE
#include "mapeditor.cpp"
#endif
#include "netgen.cpp"
#include "graph.cpp"
#include "achievement.cpp"
#include <unistd.h>
int main(int argc, char **argv) {
#ifdef LINUX
moreStack();
#endif
eItem readItem(const char *s) {
string ss = s;
for(int i=0; i<ittypes; i++) if(strstr(iinf[i].name, s) != NULL) {
return eItem(i);
break;
}
return itNone;
}
void initializeCLI() {
printf("HyperRogue by Zeno Rogue <zeno@attnam.com>, version " VER "\n");
#ifndef NOLICENSE
@ -136,208 +78,290 @@ int main(int argc, char **argv) {
printf("comes with absolutely no warranty; see COPYING for details\n");
#endif
// printf("cell size = %d\n", int(sizeof(cell)));
srand(time(NULL));
shrand(time(NULL));
#ifdef FHS
char sbuf[640], cbuf[640];
static string sbuf, cbuf;
if(getenv("HOME")) {
snprintf(sbuf, 640, "%s/.%s", getenv("HOME"), scorefile); scorefile = sbuf;
snprintf(cbuf, 640, "%s/.%s", getenv("HOME"), conffile); conffile = cbuf;
sbuf = getenv("HOME"); sbuf += "/."; sbuf += scorefile;
cbuf = getenv("HOME"); cbuf += "/."; cbuf += conffile;
scorefile = sbuf.c_str();
conffile = cbuf.c_str();
}
#endif
}
for(int i=1; i<argc; i++) {
if(strcmp(argv[i], "-c") == 0 && i != argc-1) {conffile = argv[i+1]; i++;}
else if(strcmp(argv[i], "-s") == 0 && i != argc-1) {scorefile = argv[i+1]; i++;}
else if(strcmp(argv[i], "-m") == 0 && i != argc-1) {musicfile = argv[i+1]; i++;}
else if(strcmp(argv[i], "-lev") == 0 && i != argc-1) {levelfile = argv[i+1]; i++;}
else if(strcmp(argv[i], "-pic") == 0 && i != argc-1) {picfile = argv[i+1]; i++;}
else if(strcmp(argv[i], "-load") == 0 && i != argc-1) {loadlevel = argv[i+1]; i++;}
// else if(strcmp(argv[i], "-P") == 0 && i != argc-1) {par = atoi(argv[i+1]); i++;}
else if(strcmp(argv[i], "-W") == 0 && i != argc-1) {
for(int l=0; l<landtypes; l++) if(strstr(linf[l].name, argv[i+1]) != NULL) {
firstland = euclidland = eLand(l);
break;
}
i++;
}
else if(strcmp(argv[i], "-L") == 0) {
printf("Treasures:\n");
for(int i=1; i<ittypes; i++)
if(itemclass(eItem(i)) == IC_TREASURE)
printf(" %s\n", iinf[i].name);
printf("\n");
printf("Orbs:\n");
for(int i=1; i<ittypes; i++)
if(itemclass(eItem(i)) == IC_ORB)
printf(" %s\n", iinf[i].name);
printf("\n");
printf("Other items:\n");
for(int i=1; i<ittypes; i++)
if(itemclass(eItem(i)) == IC_OTHER)
printf(" %s\n", iinf[i].name);
printf("\n");
printf("Monsters:\n");
for(int i=1; i<motypes; i++)
printf(" %s\n", minf[i].name);
printf("\n");
printf("Lands:\n");
for(int i=1; i<landtypes; i++)
printf(" %s\n", linf[i].name);
printf("\n");
printf("Walls:\n");
for(int i=0; i<walltypes; i++)
printf(" %s\n", winf[i].name);
printf("\n");
exit(0);
}
else if(strcmp(argv[i], "-f") == 0) { commandline += "f"; }
else if(strcmp(argv[i], "-w") == 0) { commandline += "w"; }
else if(strcmp(argv[i], "-e") == 0) { commandline += "e"; }
else if(strcmp(argv[i], "-a") == 0) { commandline += "a"; }
else if(strcmp(argv[i], "-p") == 0) { commandline += "p"; }
else if(strcmp(argv[i], "-7") == 0) { commandline += "7"; }
else if(strcmp(argv[i], "-C") == 0) { commandline += "C"; }
else if(strcmp(argv[i], "-o") == 0) { commandline += "o"; }
else if(strcmp(argv[i], "-o0") == 0) { commandline += char(200); }
else if(strcmp(argv[i], "-o1") == 0) { commandline += char(201); }
else if(strcmp(argv[i], "-E") == 0) { commandline += "E"; }
else if(strcmp(argv[i], "-S") == 0) { commandline += "S"; }
else if(strcmp(argv[i], "-H") == 0) { commandline += "H"; }
else if(strcmp(argv[i], "-P1") == 0) { commandline += "P1"; }
else if(strcmp(argv[i], "-P2") == 0) { commandline += "P2"; }
else if(strcmp(argv[i], "-P3") == 0) { commandline += "P3"; }
else if(strcmp(argv[i], "-P4") == 0) { commandline += "P4"; }
else if(strcmp(argv[i], "-T") == 0) { commandline += "T"; }
else if(strcmp(argv[i], "-R") == 0) { commandline += "R"; }
else if(strcmp(argv[i], "-D") == 0) { commandline += "D"; }
else if(strcmp(argv[i], "-PM1") == 0) { pmodel = 1; }
else if(strcmp(argv[i], "-PM2") == 0) { pmodel = 2; }
else if(strcmp(argv[i], "-offline") == 0) offlineMode = true;
else if(strcmp(argv[i], "-debugf") == 0) {
debugfile = fopen("hyperrogue-debug.txt", "w");
debugflags = atoi(argv[i+1]);
i++;
}
else if(strcmp(argv[i], "-debuge") == 0) {
debugfile = stderr;
debugflags = atoi(argv[i+1]);
i++;
}
#ifdef LOCAL
else if(strcmp(argv[i], "-auto") == 0) doAutoplay = true;
int arg::readCommon() {
if(argis("-c")) { PHASE(1); shift(); conffile = args(); }
else if(argis("-s")) { PHASE(1); shift(); scorefile = args(); }
else if(argis("-m")) { PHASE(1); shift(); musicfile = args(); }
else if(argis("-se")) { PHASE(1); shift(); wheresounds = args(); }
#ifndef NOEDIT
else if(argis("-lev")) { shift(); levelfile = args(); }
else if(argis("-pic")) { shift(); picfile = args(); }
else if(argis("-load")) { PHASE(3); shift(); mapstream::loadMap(loadlevel); }
#endif
else if(strcmp(argv[i], "-ch") == 0) { autocheat = true; }
else if(strcmp(argv[i], "-Y") == 0) {
yendor::on = true;
yendor::challenge = atoi(argv[i+1]);
i++;
}
else if(strcmp(argv[i], "-r") == 0) {
i++;
sscanf(argv[i], "%dx%dx%d", &clWidth, &clHeight, &clFont);
}
else if(strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
printf("HyperRogue version " VER "\n");
exit(0);
}
else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
printf("Press F1 while playing to get ingame options.\n\n");
printf("HyperRogue accepts the following command line options:\n");
printf(" -c FILE - use the specified configuration file\n");
printf(" -s FILE - use the specified highscore file\n");
printf(" -m FILE - use the specified soundtrack (music)\n");
printf(" -lev FILE - use the specified filename for the map editor (without loading)\n");
printf(" -load FILE - use the specified filename for the map editor\n");
printf(" --version, -v - show the version number\n");
printf(" --help, -h - show the commandline options\n");
printf(" -f, -w - start in the fullscreen or windowed mode\n");
printf(" -e, -a, -p - start in the Escher, ASCII, or Plain mode\n");
printf(" -r WxHxF - use the given resolution and font size\n");
printf(" -o - switch the OpenGL mode\n");
printf(" -o0 - switch the OpenGL mode off\n");
printf(" -o1 - switch the OpenGL mode on\n");
printf(" -W LAND - start in the given land (cheat)\n");
printf(" -ch - auto-enable cheat mode\n");
printf(" -E - switch Euclidean\n");
printf(" -S - switch Shmup\n");
printf(" -Pn - switch Shmup number of players (n=1..4)\n");
printf(" -H - switch Hardcore\n");
printf(" -T - switch Tactical\n");
printf(" -7 - switch heptagonal mode\n");
printf(" -C - switch Chaos mode\n");
printf(" -R - switch Random Pattern\n");
printf(" -Y id - switch Yendor, level id\n");
printf(" -D - disable all the special game modes\n");
printf(" -L - list of features\n");
printf(" -debugf 7 - output debugging information to hyperrogue-debug.txt\n");
printf(" -debuge 7 - output debugging information to stderr\n");
printf(" -offline - don't connect to Steam (for Steam versions)\n");
exit(0);
}
else {
printf("Unknown option: %s\n", argv[i]);
else if(argis("-canvas")) {
firstland = euclidland = laCanvas;
shift();
if(args()[1] == 0) mapeditor::whichCanvas = args()[0];
else mapeditor::canvasback = strtol(args(), NULL, 16);
}
else if(argis("-back")) {
shift(); backcolor = strtol(args(), NULL, 16);
}
else if(argis("-W2")) {
shift(); cheatdest = readland(args()); autocheat = true;
}
else if(argis("-W")) {
shift(); firstland = euclidland = readland(args());
}
else if(argis("-I")) {
PHASE(3) cheater++; timerghost = false;
shift(); eItem i = readItem(args());
shift(); items[i] = argi();
}
else if(argis("-L")) {
printf("Treasures:\n");
for(int i=1; i<ittypes; i++)
if(itemclass(eItem(i)) == IC_TREASURE)
printf(" %s\n", iinf[i].name);
printf("\n");
printf("Orbs:\n");
for(int i=1; i<ittypes; i++)
if(itemclass(eItem(i)) == IC_ORB)
printf(" %s\n", iinf[i].name);
printf("\n");
printf("Other items:\n");
for(int i=1; i<ittypes; i++)
if(itemclass(eItem(i)) == IC_OTHER)
printf(" %s\n", iinf[i].name);
printf("\n");
printf("Monsters:\n");
for(int i=1; i<motypes; i++)
printf(" %s\n", minf[i].name);
printf("\n");
printf("Lands:\n");
for(int i=1; i<landtypes; i++)
printf(" %s\n", linf[i].name);
printf("\n");
printf("Walls:\n");
for(int i=0; i<walltypes; i++)
printf(" %s\n", winf[i].name);
printf("\n");
exit(0);
}
else if(argis("-wm")) { PHASE(2); vid.wallmode = argi(); }
else if(argis("-mm")) { PHASE(2); vid.monmode = argi(); }
#define TOGGLE(x, param) \
else if(args()[0] == '-' && args()[1] == x && !args()[2]) { PHASE(2); param = !param; } \
else if(args()[0] == '-' && args()[1] == x && args()[2] == '1') { PHASE(2); param = true; } \
else if(args()[0] == '-' && args()[1] == x && args()[2] == '0') { PHASE(2); param = false; }
TOGGLE('o', vid.usingGL)
TOGGLE('C', chaosmode)
TOGGLE('7', purehepta)
TOGGLE('f', vid.full)
TOGGLE('T', tactic::on)
TOGGLE('S', shmup::on)
TOGGLE('H', hardcore)
TOGGLE('R', randomPatternsMode)
else if(argis("-geo")) { PHASE(2); shift(); geometry = targetgeometry = (eGeometry) argi(); }
else if(argis("-qs")) {
autocheat = true;
shift(); fp43.qpaths.push_back(args());
}
else if(argis("-fix")) {
fixseed = true; autocheat = true;
}
else if(argis("-qpar")) {
int p;
shift(); sscanf(args(), "%d,%d,%d",
&p, &quotientspace::rvadd, &quotientspace::rvdir
);
autocheat = true;
fp43.init(p);
}
else if(argis("-cs")) {
shift();
fieldpattern::matrix M = fp43.strtomatrix(args());
fieldpattern::subpathid = fp43.matcode[M];
fieldpattern::subpathorder = fp43.order(M);
autocheat = true;
}
else if(argis("-csp")) {
autocheat = true;
fp43.findsubpath();
}
else if(argis("-fi")) {
fieldpattern::info();
exit(0);
}
else if(argis("-P")) {
PHASE(2); shift();
vid.scfg.players = argi();
}
else if(argis("-PM")) {
PHASE(2); shift(); pmodel = eModel(argi());
}
else if(argis("-offline")) offlineMode = true;
else if(argis("-debugf")) {
debugfile = fopen("hyperrogue-debug.txt", "w");
shift(); debugflags = argi();
}
else if(argis("-debuge")) {
debugfile = stderr;
shift(); debugflags = argi();
}
else if(argis("-ch")) { autocheat = true; }
else if(argis("-zoom") && curphase == 2) {
PHASE(2); shift(); vid.scale = argf();
}
else if(argis("-zoom") && curphase == 3) {
PHASE(3); shift(); vid.scale = argf();
}
else if(argis("-Y")) {
yendor::on = true;
shift(); yendor::challenge = argi();
}
else if(argis("-r")) {
PHASE(2);
shift();
int clWidth=0, clHeight=0, clFont=0;
sscanf(args(), "%dx%dx%d", &clWidth, &clHeight, &clFont);
if(clWidth) vid.xres = clWidth;
if(clHeight) vid.yres = clHeight;
if(clFont) vid.fsize = clFont;
}
else if(argis("--version") || argis("-v")) {
printf("HyperRogue version " VER "\n");
exit(0);
}
else if(argis("--run")) {
PHASE(3); mainloop(); quitmainloop = false;
}
else if(argis("--draw")) {
PHASE(3); drawscreen();
}
else if(argis("--exit")) {
PHASE(3); printf("Success.\n");
exit(0);
}
else if(argis("-gencells")) {
PHASE(3); shift();
printf("Generating %d cells...\n", argi());
celllister cl(cwt.c, 50, argi(), NULL);
printf("Cells generated: %d\n", size(cl.lst));
for(int i=0; i<size(cl.lst); i++)
setdist(cl.lst[i], 7, NULL);
}
else if(argis("-sr")) {
if(curphase == 1) PHASE(2);
shift(); sightrange = argi();
}
else if(argis("-pngshot")) {
PHASE(3); shift();
printf("saving PNG screenshot to %s\n", args());
saveHighQualityShot(args());
}
else if(argis("-svgsize")) {
shift(); sscanf(args(), "%d/%d", &svg::svgsize, &svg::divby);
}
else if(argis("-pngsize")) {
shift(); sscanf(args(), "%d", &pngres);
}
else if(argis("-svggamma")) {
shift(); svg::gamma = argf();
}
else if(argis("-svgshot")) {
PHASE(3); shift();
printf("saving SVG screenshot to %s\n", args());
svg::render(args());
}
else if(argis("--help") || argis("-h")) {
printf("Press F1 while playing to get ingame options.\n\n");
printf("HyperRogue accepts the following command line options:\n");
printf(" -c FILE - use the specified configuration file\n");
printf(" -s FILE - use the specified highscore file\n");
printf(" -m FILE - use the specified soundtrack (music)\n");
printf(" -se DIR - the directory containing sound effects\n");
printf(" -lev FILE - use the specified filename for the map editor (without loading)\n");
printf(" -load FILE - use the specified filename for the map editor\n");
printf(" -canvas COLOR - set background color or pattern code for the canvas\n");
printf(" --version, -v - show the version number\n");
printf(" --help, -h - show the commandline options\n");
printf(" -f* - toggle fullscreen mode\n");
printf(" -wm n, -mm n - start in the given wallmode or monmode\n");
printf(" -r WxHxF - use the given resolution and font size\n");
printf(" -o* - toggle the OpenGL mode\n");
printf(" -W LAND - start in the given land (cheat)\n");
printf(" -W2 LAND - make the given land easy to find (also turns on autocheat)\n");
printf(" -ch - auto-enable cheat mode\n");
printf(" -geo n - switch geometry (1=Euclidean, 2=spherical, 3=elliptic, 4/5=quotient)\n");
printf(" -qs <desc> - fieldpattern: quotient by the given <desc> (must be followed by qpar)\n");
printf(" -qpar <prime> - fieldpattern: use the given prime instead of 43\n");
printf(" -cs <desc> - fieldpattern: set subpath to the given <desc> (cannot be followed by qpar)\n");
printf(" -csp - fieldpattern: find the subpath of order <prime> (cannot be followed by qpar)\n");
printf(" -S* - toggle Shmup\n");
printf(" -P n - switch Shmup number of players (n=1..7)\n");
printf(" -PM - switch the model index\n");
printf(" -H* - toggle Hardcore\n");
printf(" -T* - toggle Tactical\n");
printf(" -7* - toggle heptagonal mode\n");
printf(" -C* - toggle Chaos mode\n");
printf(" -R* - toggle Random Pattern\n");
printf(" -Y id - enable Yendor, level id\n");
printf(" -D - disable all the special game modes\n");
printf(" -L - list of features\n");
printf(" -debugf 7 - output debugging information to hyperrogue-debug.txt\n");
printf(" -debuge 7 - output debugging information to stderr\n");
printf(" -offline - don't connect to Steam (for Steam versions)\n");
printf(" -I ITEM n - start with n of ITEM (activates cheat and disables ghosts)\n");
printf(" -fix - fix the seed\n");
printf("Toggles: -o0 disables, -o1 enables, -o switches");
printf("Not all options are documented, see hyper.cpp");
exit(0);
}
else if(ca::readArg()) ;
else return 1;
return 0;
}
int main(int argc, char **argv) {
#ifndef WEB
#ifdef LINUX
moreStack();
#endif
arg::init(argc, argv);
initializeCLI();
#endif
initAll();
arg::read(3);
mainloop();
finishAll();
profile_info();
return 0;
}
#ifdef USE_COMMANDLINE
namespace arg {
int argc; char **argv;
void read(int phase) {
curphase = phase;
while(argc) {
int r;
r = readCommon(); if(r == 2) return; if(r == 0) { lshift(); continue; }
#ifdef LOCAL
r = readLocal(); if(r == 2) return; if(r == 0) { lshift(); continue; }
#endif
#ifdef ROGUEVIZ
r = rogueviz::readArgs(); if(r == 2) return; if(r == 0) { lshift(); continue; }
#endif
printf("Unknown option: %s\n", args());
exit(3);
}
}
achievement_init();
eLand f = firstland;
// initlanguage();
initgraph();
loadsave();
precalc();
resetGL();
initcells();
#ifdef BUILDZEBRA
firstland = laCanvas;
shmup::on = false;
#endif
shmup::safety = safety;
initgame();
#ifdef BUILDZEBRA
zebraPattern();
#endif
if(!shmup::on) {
restoreGolems(items[itOrbLife], moGolem); items[itOrbLife] = 0;
restoreGolems(items[itOrbFriend], moTameBomberbird); items[itOrbFriend] = 0;
restoreGolems(kills[moPrincessMoved], moPrincess, princess::saveHP); kills[moPrincessMoved] = 0;
restoreGolems(kills[moPrincessArmedMoved], moPrincessArmed, princess::saveArmedHP); kills[moPrincessArmedMoved] = 0;
}
firstland = f;
// verifyHell();
// exit(1);
int t1 = SDL_GetTicks();
// if(switchEuclid) restartGame('e');
if(loadlevel) mapstream::loadMap(loadlevel);
#ifdef LOCAL
// river();
autoplay();
#endif
mainloop();
achievement_final(!items[itOrbSafety]);
saveStats();
int msec = SDL_GetTicks() - t1;
DEBB(DF_INIT, (debugfile, "frame : %f ms (%f fps)\n", 1.*msec/frames, 1000.*frames/msec));
offscreen.clear();
clearMemory();
cleargraph();
achievement_close();
return 0;
}
#endif

604
hyper.h
View File

@ -7,6 +7,9 @@
#define GL
#define PSEUDOKEY_WHEELDOWN 2501
#define PSEUDOKEY_WHEELUP 2502
#ifdef NOGFX
#undef GFX
#endif
@ -18,9 +21,10 @@
// scale the Euclidean
#define EUCSCALE 2.3
// disable this if you have no access to SDL_mixer
#ifndef MOBILE
#define AUDIO
#ifndef NOAUDIO
#define SDLAUDIO
#endif
#endif
#define NUMWITCH 7
@ -29,7 +33,7 @@
#define LB_YENDOR_CHALLENGE 40
#define LB_PURE_TACTICS 41
#define NUMLEADER 57
#define NUMLEADER 69
#define LB_PURE_TACTICS_SHMUP 49
#define LB_PURE_TACTICS_COOP 50
@ -75,6 +79,7 @@ void achievement_pump();
vector<string> achievementsReceived;
// game forward declarations
typedef int flagtype;
bool mirrorkill(cell *c);
bool isNeighbor(cell *c1, cell *c2);
@ -82,7 +87,7 @@ void checkTide(cell *c);
namespace anticheat { extern bool tampered; }
int numplayers();
void removeIvy(cell *c);
bool cellEdgeUnstable(cell *c);
bool cellEdgeUnstable(cell *c, flagtype flags = 0);
int coastvalEdge(cell *c);
typedef int cellfunction(cell*);
int towerval(cell *c, cellfunction* cf = &coastvalEdge);
@ -94,13 +99,13 @@ eItem treasureType(eLand l);
void buildBarrier(cell *c, int d, eLand l = laNone);
void extendBarrier(cell *c);
bool buildBarrier4(cell *c, int d, int mode, eLand ll, eLand lr);
void makeEmpty(cell *c);
bool makeEmpty(cell *c);
bool isCrossroads(eLand l);
enum orbAction { roMouse, roKeyboard, roCheck, roMouseForce };
enum orbAction { roMouse, roKeyboard, roCheck, roMouseForce, roMultiCheck, roMultiGo };
void moveItem (cell *from, cell *to, bool activateYendor);
void uncoverMines(cell *c, int lev);
void uncoverMines(cell *c, int lev, int dist);
bool survivesMine(eMonster m);
void killMonster(cell *c);
void killMonster(cell *c, eMonster who_killed, flagtype flags = 0);
void toggleGates(cell *ct, eWall type, int rad);
bool destroyHalfvine(cell *c, eWall newwall = waNone, int tval = 6);
void buildCrossroads2(cell *c);
@ -109,12 +114,12 @@ heptagon *createAlternateMap(cell *c, int rad, hstate firststate, int special=0)
void generateAlts(heptagon *h);
void setdist(cell *c, int d, cell *from);
void checkOnYendorPath();
void killThePlayerAt(eMonster m, cell *c, int flags);
void killThePlayerAt(eMonster m, cell *c, flagtype flags);
bool notDippingFor(eItem i);
bool collectItem(cell *c2, bool telekinesis = false);
void castLightningBolt(struct cellwalker lig);
bool movepcto(int d, int subdir = 1, bool checkonly = false);
void stabbingAttack(cell *mf, cell *mt, eMonster who);
void stabbingAttack(cell *mf, cell *mt, eMonster who, int bonuskill = 0);
bool earthMove(cell *from, int dir);
void messageKill(eMonster killer, eMonster victim);
void moveMonster(cell *ct, cell *cf);
@ -122,6 +127,7 @@ int palaceHP();
void placeLocalOrbs(cell *c);
int elementalKills();
bool elementalUnlocked();
bool trollUnlocked();
bool isMultitile(eMonster m);
void checkFreedom(cell *cf);
int rosedist(cell *c);
@ -137,7 +143,16 @@ namespace mirror {
int neighborId(cell *c1, cell *c2);
struct movedir { int d; int subdir; };
struct movedir {
int d; // 0 to 6, or one of the following -- warning: not used consistently
#define MD_WAIT (-1)
#define MD_DROP (-2)
#define MD_UNDECIDED (-3)
#define MD_USE_ORB (-4)
int subdir; // for normal movement (0-6): turn left or right
cell *tgt; // for MD_USE_ORB: target cell
};
inline bool movepcto(const movedir& md) { return movepcto(md.d, md.subdir); }
void activateActiv(cell *c, bool msg);
@ -145,19 +160,65 @@ void activateActiv(cell *c, bool msg);
// shmup
struct charstyle {
int charid, skincolor, haircolor, dresscolor, swordcolor, dresscolor2;
int charid, skincolor, haircolor, dresscolor, swordcolor, dresscolor2, uicolor;
};
string csname(charstyle& cs);
void initcs(charstyle& cs);
void savecs(FILE *f, charstyle& cs);
void loadcs(FILE *f, charstyle& cs);
void savecs(FILE *f, charstyle& cs, int vernum);
void loadcs(FILE *f, charstyle& cs, int vernum);
#define MAXPLAYER 7
#define MAXJOY 8
#define MAXBUTTON 64
#define MAXAXE 16
#define MAXHAT 4
namespace multi {
void recall();
extern cell *origpos[MAXPLAYER], *origtarget[MAXPLAYER];
extern int players;
extern cellwalker player[MAXPLAYER];
extern bool flipped[MAXPLAYER];
cell *mplayerpos(int i);
extern vector<int> revive_queue; // queue for revival
extern movedir whereto[MAXPLAYER]; // player's target cell
extern int cpid; // player id -- an extra parameter for player-related functions
extern int cpid_edit; // cpid currently being edited
// treasure collection, kill, and death statistics
extern int treasures[MAXPLAYER], kills[MAXPLAYER], deaths[MAXPLAYER];
struct config {
int players;
int subconfig;
int setwhat;
char keyaction[512];
char joyaction[MAXJOY][MAXBUTTON];
char axeaction[MAXJOY][MAXAXE];
char hataction[MAXJOY][MAXHAT][4];
int deadzoneval[MAXJOY][MAXAXE];
};
charstyle scs[MAXPLAYER];
bool playerActive(int p);
int activePlayers();
cell *multiPlayerTarget(int i);
void checklastmove();
void leaveGame(int i);
}
namespace shmup {
void recall();
extern bool on;
extern bool safety;
extern int curtime;
extern int players, cpid;
void clearMemory();
void init();
void teleported();
@ -175,22 +236,8 @@ namespace shmup {
cell *playerpos(int i);
bool playerInBoat(int i);
#define MAXBUTTON 64
#define MAXAXE 16
#define MAXHAT 4
struct config {
int players;
int subconfig;
int setwhat;
char keyaction[512];
char joyaction[8][MAXBUTTON];
char axeaction[8][MAXAXE];
char hataction[8][MAXHAT][4];
};
charstyle scs[4];
void destroyBoats(cell *c);
bool boatAt(cell *c);
}
// graph
@ -206,6 +253,10 @@ void cleargraphmemory();
void drawFlash(cell* c);
void drawBigFlash(cell* c);
void drawParticle(cell *c, int col, int maxspeed = 100);
void drawParticles(cell *c, int col, int qty, int maxspeed = 100);
void drawFireParticles(cell *c, int qty, int maxspeed = 100);
int firecolor(int phase);
void drawLightning();
void drawSafety();
@ -214,8 +265,6 @@ void movepckeydir(int);
void centerpc(ld aspd);
void displayStatHelp(int y, string name);
void displayStat(int y, const string& name, const string& val, char mkey);
void displayButton(int x, int y, const string& name, int key, int align, int rad = 0);
void displayColorButton(int x, int y, const string& name, int key, int align, int rad, int color, int color2 = 0);
inline string ONOFF(bool b) { return XLAT(b ? "ON" : "OFF"); }
@ -223,17 +272,15 @@ int darkened(int c);
extern int getcstat;
bool displaychr(int x, int y, int shift, int size, char chr, int col);
bool displayfr(int x, int y, int b, int size, const string &s, int color, int align);
void saveHighQualityShot();
void saveHighQualityShot(const char *fname = NULL);
bool outofmap(hyperpoint h);
void getcoord(const hyperpoint& H, int& x, int& y, int &shift);
void drawline(const hyperpoint& H1, int x1, int y1, int s1, const hyperpoint& H2, int x2, int y2, int col);
void drawline(const hyperpoint& H1, const hyperpoint& H2, int col);
void applymodel(hyperpoint H, hyperpoint& Hscr);
void drawCircle(int x, int y, int size, int color);
void fixcolor(int& col);
int displaydir(cell *c, int d);
hyperpoint gethyper(ld x, ld y);
void resetview(); extern cell *lcenterover; extern heptspin viewctr;
void resetview(); extern heptspin viewctr; extern cell *centerover;
#ifndef MOBILE
int& qpixel(SDL_Surface *surf, int x, int y);
#endif
@ -245,31 +292,30 @@ extern int darken;
void setvideomode();
void calcparam();
string ifMousing(string key, string s);
#ifndef NOCONFIG
void saveConfig();
#endif
extern hyperpoint mouseh;
extern int webdisplay;
extern bool GL_initialized;
extern hyperpoint ccenter;
extern ld crad;
extern bool mousepressed, anyshiftclick;
extern string help;
extern int lalpha;
struct videopar {
ld scale, eye, alpha, aspeed;
ld scale, eye, alpha, sspeed, mspeed, yshift, camera_angle;
ld ballangle, ballproj;
int mobilecompasssize;
bool full;
bool goteyes; // for rendering
bool goteyes2; // for choosing colors
bool quick;
bool darkhepta;
bool shifttarget;
int shifttarget;
int xres, yres, framelimit;
@ -280,6 +326,9 @@ struct videopar {
int radius;
ld alphax, beta;
bool grid;
int particles;
int fsize;
int flashtime;
@ -292,7 +341,7 @@ struct videopar {
bool usingAA;
int joyvalue, joyvalue2, joypanthreshold;
float joypanspeed;
ld joypanspeed;
charstyle cs;
@ -301,9 +350,10 @@ struct videopar {
int killreduction, itemreduction, portreduction;
shmup::config scfg;
multi::config scfg;
bool steamscore;
int steamscore;
bool drawmousecircle; // draw the circle around the mouse
};
extern videopar vid;
@ -322,7 +372,10 @@ enum emtype {emNormal, emHelp,
emYendor, emTactic, emRugConfig,
emConformal,
emProgress,
emCheatMenu
emCheatMenu, emLeader,
emJoyConfig,
emColor, emNumber,
em3D, emRogueviz
};
extern emtype cmode, lastmode;
@ -338,14 +391,18 @@ extern struct SDL_Surface *s;
namespace mapeditor {
extern bool drawplayer;
extern char whichPattern, whichShape;
extern char whichCanvas;
extern int displaycodes;
int generateCanvas(cell *c);
void clearModelCells();
void applyModelcell(cell *c);
int realpattern(cell *c);
int patterndir(cell *c, char w = whichPattern);
int subpattern(cell *c);
extern cell *drawcell;
}
#ifndef NORUG
namespace rug {
extern bool rugged;
void init();
@ -353,6 +410,7 @@ namespace rug {
void actDraw();
void buildVertexInfo(cell *c, transmatrix V);
}
#endif
#define HASLINEVIEW
namespace conformal {
@ -372,9 +430,8 @@ namespace conformal {
namespace polygonal {
extern int SI;
extern double STAR;
extern ld STAR;
void solve();
typedef long double ld;
pair<ld, ld> compute(ld x, ld y);
}
@ -422,8 +479,9 @@ extern bool localKill(shmup::monster *m);
#define P_ROSE (1<<28) // rose smell
#define P_CLIMBUP (1<<29) // allow climbing up
#define P_CLIMBDOWN (1<<30) // allow climbing down
#define P_REPTILE (1<<31) // is reptile
bool passable(cell *w, cell *from, int flags);
bool passable(cell *w, cell *from, flagtype flags);
bool isElemental(eLand l);
int coastval(cell *c, eLand base);
@ -450,13 +508,15 @@ extern bool safety;
#define SAGEMELT .1
#define TEMPLE_EACH 6
#define PT(x, y) (tactic::on ? (y) : (x))
#define PT(x, y) ((tactic::on || quotient == 2) ? (y) : (x))
#define ROCKSNAKELENGTH 50
#define WORMLENGTH 15
#define PUREHARDCORE_LEVEL 10
#define PRIZEMUL 7
#define INF 9999
#define INFD 20
#define PINFD 125
#define BARLEV ((ISANDROID||ISIOS||purehepta)?9:10)
#define BUGLEV 15
// #define BARLEV 9
@ -469,7 +529,7 @@ bool isAlchAny(eWall w);
bool isAlchAny(cell *c);
#define YDIST 101
#define MODECODES 38
#define MODECODES 254
extern cellwalker cwt; // player character position
extern int sval;
@ -494,10 +554,14 @@ bool hellUnlocked();
bool markOrb(eItem it); // mark the orb as 'used', return true if exists
bool markEmpathy(eItem it); // mark both the given orb and Empathy as 'used', return true if exists
bool markEmpathy2(eItem it); // as above, but next turn
bool isMimic(eMonster m);
bool isMimic(cell *c);
void killWithMessage(cell *c, bool orStun = false, eMonster killer = moNone);
void fallMonster(cell *c, flagtype flags = 0); // kill monster due to terrain
bool attackMonster(cell *c, flagtype flags, eMonster killer);
bool isWorm(eMonster m);
bool isWorm(cell *c);
@ -507,10 +571,11 @@ bool isIvy(cell *c);
#define GUNRANGE 3
// 0 = basic treasure, 1 = something else, 2 = power orb
// 0 = basic treasure, 1 = other item, 2 = power orb, 3 = not an item
#define IC_TREASURE 0
#define IC_OTHER 1
#define IC_ORB 2
#define IC_NAI 3
bool playerInPower();
void activateFlash();
@ -528,7 +593,7 @@ int realstuntime(cell *c);
extern bool invismove, invisfish;
bool attackingForbidden(cell *c, cell *c2);
void killOrStunMonster(cell *c2);
void killOrStunMonster(cell *c2, eMonster who_killed);
extern vector<cell*> offscreen; // offscreen cells to take care off
@ -538,6 +603,8 @@ cell *playerpos(int i);
bool makeflame(cell *c, int timeout, bool checkonly);
void bfs();
bool isPlayerInBoatOn(cell *c);
bool isPlayerInBoatOn(cell *c, int i);
void destroyBoats(cell *c, cell *cf, bool strandedToo);
extern bool showoff;
extern int lastexplore;
extern int truelotus;
@ -583,7 +650,18 @@ bool withRose(cell *cfrom, cell *cto);
#define MF_MOUNT (1<<18) // don't do
#define MF_NOFRIEND (1<<19) // don't do it for friends
bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, int flags);
#define AF_SWORD (1<<20) // big sword
#define AF_SWORD_INTO (1<<21) // moving into big sword
#define AF_MSG (1<<22) // produce a message
#define AF_ORSTUN (1<<23) // attackMonster: allow stunning
#define AF_NEXTTURN (1<<24) // next turn -- don't count shield at power 1
#define AF_FALL (1<<25) // death by falling
#define MF_STUNNED (1<<26) // edgeunstable: ignore ladders (as stunned monsters do)
#define MF_IVY (1<<27) // edgeunstable: ignore ivy (ivy cannot climb ivy)
#define AF_HORNS (1<<28) // spear attack (always has APPROACH too)
#define AF_BULL (1<<29) // bull attack
bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags);
extern bool chaosmode;
extern bool chaosUnlocked;
@ -594,22 +672,6 @@ int getGhostcount();
void raiseBuggyGeneration(cell *c, const char *s);
void verifyMutantAround(cell *c);
extern FILE *debugfile;
extern int debugflags;
extern bool offline;
#ifdef ANDROID
#define DEBB(r,x)
#else
#define DEBB(r,x) { if(debugfile && (!(r) || (debugflags & (r)))) { fprintf x; fflush(debugfile); } }
#endif
#define DF_INIT 0 // always display these
#define DF_MSG 0 // always display these
#define DF_STEAM 1
#define DF_GRAPH 2
#define DF_TURN 4
#ifdef MOBILE
#define NOPNG
#endif
@ -633,7 +695,8 @@ void buildAirmap();
// currently works for worms only
bool sameMonster(cell *c1, cell *c2);
cell *wormhead(cell *c);
eMonster getMount();
eMonster getMount(int player_id);
eMonster haveMount();
bool isDragon(eMonster m);
@ -647,6 +710,8 @@ extern bool autocheat;
extern bool inHighQual;
void mountmove(cell *c, int spin, bool fp);
void mountmove(cell *c, int spin, bool fp, cell *ppos);
void mountswap(cell *c1, int spin1, bool fp1, cell *c2, int spin2, bool fp2);
template<class T> struct dynamicval {
T& where;
@ -655,18 +720,403 @@ template<class T> struct dynamicval {
~dynamicval() { where = backup; }
};
namespace stalemate {
struct stalemate1 {
eMonster who;
cell *moveto;
cell *killed;
cell *pushto;
cell *comefrom;
cell *swordlast[2], *swordtransit[2], *swordnext[2];
bool isKilled(cell *c);
stalemate1(eMonster w, cell *mt, cell *ki, cell *pt, cell *cf) : who(w), moveto(mt), killed(ki), pushto(pt), comefrom(cf) {}
};
namespace stalemate {
vector<stalemate1> moves;
bool nextturn;
bool isKilled(cell *c);
bool anyKilled();
bool isMoveto(cell *c);
bool isKilledDirectlyAt(cell *c);
bool isPushto(cell *c);
};
extern int turncount;
bool reduceOrbPower(eItem it, int cap);
bool checkOrb(eMonster m1, eItem orb);
movedir vectodir(const hyperpoint& P);
namespace sword {
extern int angle[MAXPLAYER];
cell *pos(cell *c, int s);
cell *pos(int id);
bool at(cell *where, bool noplayer = false);
int shift(cell *c1, cell *c2);
}
void killThePlayer(eMonster m, int id, flagtype flags);
bool attackJustStuns(cell *c2);
bool isTargetOrAdjacent(cell *c);
bool warningprotection();
bool mineMarked(cell *c);
bool minesafe();
bool hasSafeOrb(cell *c);
void placeWater(cell *c, cell *c2);
bool againstCurrent(cell *w, cell *from);
#define DEFAULTCONTROL (multi::players == 1 && !shmup::on && !multi::alwaysuse)
extern bool timerghost;
#ifdef PANDORA
#define MENU_SCALING
#endif
#ifdef MOBILE
#define MENU_SCALING
#endif
#ifdef MENU_SCALING
#define displayfrZ dialog::displayzoom
#else
#define displayfrZ displayfr
#endif
namespace dialog {
enum tDialogItem {diTitle, diItem, diBreak, diHelp, diInfo, diSlider};
struct item {
tDialogItem type;
string body;
string value;
string keycaption;
int key;
int color, colorv, colork, colors, colorc;
int scale;
double param;
};
item& lastItem();
void addSelItem(string body, string value, int key);
void addBoolItem(string body, bool value, int key);
void addColorItem(string body, int value, int key);
void addHelp(string body);
void addInfo(string body, int color = 0xC0C0C0);
void addItem(string body, int key);
void addBreak(int val);
void addTitle(string body, int color, int scale);
void init();
void init(string title, int color = 0xE8E8E8, int scale = 150, int brk = 60);
void display();
void drawColorDialog(int color);
int handleKeyColor(int sym, int uni, int& color);
void editNumber(ld& x, ld vmin, ld vmax, ld step, ld dft, string title, string help);
void editNumber(int& x, int vmin, int vmax, int step, int dft, string title, string help);
void scaleLog();
void scaleSinh();
void handleNavigation(int &sym, int &uni);
bool displayzoom(int x, int y, int b, int size, const string &s, int color, int align);
bool editingDetail();
int handlePage(int& nl, int& nlm, int perpage);
void displayPageButtons(int i, bool pages);
bool handlePageButtons(int uni);
}
void checkStunKill(cell *dest);
void clearMessages();
void resetGeometry();
namespace svg {
void circle(int x, int y, int size, int col);
void polygon(int *polyx, int *polyy, int polyi, int col, int outline);
void text(int x, int y, int size, const string& str, bool frame, int col, int align);
extern bool in;
extern string *info;
void render(const char *fname = NULL);
}
extern int sightrange;
namespace halloween {
void getTreat(cell *where);
}
// just in case if I change my mind about when Orbs lose their power
#define ORBBASE 0
transmatrix mscale(const transmatrix& t, double fac);
transmatrix mzscale(const transmatrix& t, double fac);
extern bool ivoryz;
#define mmscale(V, x) (mmspatial ? (ivoryz ? mzscale(V,x) : mscale(V, x)) : (V))
#define SHADOW_WALL 0x60
#define SHADOW_SL 0x18
#define SHADOW_MON 0x30
bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, double footphase);
void drawPlayerEffects(const transmatrix& V, cell *c, bool onPlayer);
// monster movement animations
struct animation {
int ltick;
double footphase;
transmatrix wherenow;
};
// we need separate animation layers for Orb of Domination and Tentacle+Ghost,
// and also to mark Boats
#define ANIMLAYERS 3
#define LAYER_BIG 0 // for worms and krakens
#define LAYER_SMALL 1 // for others
#define LAYER_BOAT 2 // mark that a boat has moved
extern map<cell*, animation> animations[ANIMLAYERS];
extern unordered_map<cell*, transmatrix> gmatrix, gmatrix0;
void animateMovement(cell *src, cell *tgt, int layer);
// for animations which might use the same locations,
// such as replacements or multi-tile monsters
void indAnimateMovement(cell *src, cell *tgt, int layer);
void commitAnimations(int layer);
void animateReplacement(cell *a, cell *b, int layer);
void fallingFloorAnimation(cell *c, eWall w = waNone, eMonster m = moNone);
void fallingMonsterAnimation(cell *c, eMonster m);
// ranks:
enum PPR {
PPR_ZERO, PPR_OUTCIRCLE, PPR_MOVESTAR,
PPR_MINUSINF,
PPR_BELOWBOTTOMm,
PPR_BELOWBOTTOM,
PPR_BELOWBOTTOMp,
PPR_LAKEBOTTOM, PPR_HELLSPIKE,
PPR_INLAKEWALLm, PPR_INLAKEWALL, PPR_INLAKEWALLp,
PPR_SUBLAKELEV, PPR_LAKELEV, PPR_BOATLEV, PPR_BOATLEV2, PPR_BOATLEV3,
PPR_LAKEWALLm, PPR_LAKEWALL, PPR_LAKEWALLp,
PPR_FLOOR_TOWER,
PPR_FLOOR,
PPR_FLOOR_DRAGON,
PPR_FLOORa, PPR_FLOORb, PPR_FLOORc, PPR_FLOORd,
PPR_LIZEYE,
PPR_BFLOOR,
PPR_GFLOORa, PPR_GFLOORb, PPR_GFLOORc,
PPR_WALLSHADOW,
PPR_STRUCT0, PPR_STRUCT1, PPR_STRUCT2, PPR_STRUCT3,
PPR_THORNS, PPR_WALL,
PPR_REDWALLm, PPR_REDWALLs, PPR_REDWALLp, PPR_REDWALL,
PPR_REDWALLm2, PPR_REDWALLs2, PPR_REDWALLp2, PPR_REDWALLt2,
PPR_REDWALLm3, PPR_REDWALLs3, PPR_REDWALLp3, PPR_REDWALLt3,
PPR_HEPTAMARK,
PPR_ITEM, PPR_ITEMa, PPR_ITEMb,
PPR_BIGSTATUE,
PPR_GLASSm, PPR_GLASSs, PPR_GLASSp, PPR_GLASS,
PPR_WALL3m, PPR_WALL3s, PPR_WALL3p, PPR_WALL3, PPR_WALL3A,
PPR_HIDDEN, PPR_GIANTSHADOW,
PPR_TENTACLE0, PPR_TENTACLE1,
PPR_ONTENTACLE, PPR_ONTENTACLE_EYES, PPR_ONTENTACLE_EYES2,
PPR_MONSTER_SHADOW,
PPR_MONSTER_FOOT, PPR_MONSTER_LEG, PPR_MONSTER_GROIN,
PPR_MONSTER_BODY, PPR_MONSTER_SUBWPN, PPR_MONSTER_WPN, PPR_MONSTER_ARMOR0, PPR_MONSTER_ARMOR1,
PPR_MONSTER_CLOAK, PPR_MONSTER_NECK,
PPR_MONSTER_HEAD, PPR_MONSTER_FACE, PPR_MONSTER_EYE0, PPR_MONSTER_EYE1,
PPR_MONSTER_HAIR, PPR_MONSTER_HAT0, PPR_MONSTER_HAT1,
PPR_MONSTER_HOODCLOAK1, PPR_MONSTER_HOODCLOAK2,
PPR_STUNSTARS,
PPR_CARRIED, PPR_CARRIEDa, PPR_CARRIEDb,
PPR_PARTICLE, PPR_SWORDMARK, PPR_MAGICSWORD, PPR_MISSILE,
PPR_MINEMARK, PPR_ARROW,
PPR_LINE, PPR_TEXT, PPR_CIRCLE,
PPR_MAX
};
void ShadowV(const transmatrix& V, const struct hpcshape& bp, int prio = PPR_MONSTER_SHADOW);
#define OUTLINE_NONE 0x000000FF
#define OUTLINE_FRIEND 0x00FF00FF
#define OUTLINE_ENEMY 0xFF0000FF
#define OUTLINE_TREASURE 0xFFFF00FF
#define OUTLINE_ORB 0xFF8000FF
#define OUTLINE_OTHER 0xFFFFFFFF
#define OUTLINE_DEAD 0x800000FF
#define OUTLINE_TRANS 0
extern bool audio;
extern string musiclicense;
extern string musfname[landtypes];
extern int musicvolume, effvolume;
void initAudio();
bool loadMusicInfo();
void handlemusic();
void playSeenSound(cell *c);
void playSound(cell *c, const string& fname, int vol = 100);
inline string pick123() { return cts('1' + rand() % 3); }
inline string pick12() { return cts('1' + rand() % 2); }
bool playerInBoat(int i);
extern int lowfar;
extern bool wmspatial, wmescher, wmplain, wmblack, wmascii;
extern bool mmspatial, mmhigh, mmmon, mmitem;
extern int maxreclevel, reclevel;
string explain3D(ld *param);
extern int detaillevel;
extern bool quitmainloop;
enum eGlyphsortorder {
gsoFirstTop, gsoFirstBottom,
gsoLastTop, gsoLastBottom,
gsoLand, gsoValue,
gsoMAX
};
extern eGlyphsortorder glyphsortorder;
#ifdef ROGUEVIZ
namespace rogueviz {
extern bool on;
string describe(shmup::monster *m);
void activate(shmup::monster *m);
void drawVertex(const transmatrix &V, cell *c, shmup::monster *m);
bool virt(shmup::monster *m);
void turn(int delta);
void drawExtra();
void fixparam();
int readArgs();
void close();
}
#endif
void explodeMine(cell *c);
bool mayExplodeMine(cell *c, eMonster who);
int gravityLevel(cell *c);
void fullcenter();
void movecost(cell* from, cell *to);
void checkmove();
transmatrix eumove(int x, int y);
#ifndef NOSAVE
void loadScores();
#endif
int reptilemax();
extern bool mousing;
#define IFM(x) (mousing?"":x)
extern cell *recallCell;
extern eLand cheatdest;
void cheatMoveTo(eLand l);
extern int backcolor;
extern bool overgenerate;
void doOvergenerate();
void collectMessage(cell *c2, eItem which);
namespace quotientspace {
void build();
void clear();
extern vector<int> connections;
}
void killFriendlyIvy();
void pushdown(cell *c, int& q, const transmatrix &V, double down, bool rezoom, bool repriority);
extern bool viewdists;
void preventbarriers(cell *c);
bool passable_for(eMonster m, cell *w, cell *from, flagtype extra);
void beastcrash(cell *c, cell *beast);
int angledist(int t, int d1, int d2);
int angledist(cell *c, int d1, int d2);
void setcameraangle(bool b);
enum eModel {
mdDisk, mdHalfplane, mdBand, mdPolygonal, mdPolynomial,
mdEquidistant, mdEquiarea, mdBall, mdHyperboloid, mdGUARD, mdUnchanged };
#define MODELCOUNT ((int) mdGUARD)
void drawShape(pair<ld,ld>* coords, int qty, int color);
extern eModel pmodel;
int darkena(int c, int lev, int a);
#define SHSIZE 16
extern cell *shpos[MAXPLAYER][SHSIZE];
extern int cshpos;
namespace arg {
#ifdef USE_COMMANDLINE
extern int argc; extern char **argv;
inline void lshift() {
argc--; argv++;
}
inline void shift() {
lshift(); if(!argc) { printf("Missing parameter\n"); exit(1); }
}
inline char* args() { return *argv; }
inline int argi() { return atoi(*argv); }
inline ld argf() { return atof(*argv); }
inline bool argis(const char *s) { return strcmp(*argv, s) == 0; }
inline void init(int _argc, char **_argv) { argc=_argc-1; argv=_argv+1; }
int curphase;
inline void phaseerror(int x) {
printf("Command line error: cannot read command '%s' from phase %d in phase %d\n", args(), x, curphase);
exit(1);
}
// returned values: 0 = ok, 1 = not recognized, 2 = shift phase
int readCommon();
int readLocal();
// an useful macro
#define PHASE(x) { if(arg::curphase > x) phaseerror(x); else if(arg::curphase < x) return 2; }
void read(int phase);
#else
inline void read(int phase) { }
#endif
}
extern bool generatingEquidistant;

View File

@ -1,29 +1,12 @@
// Hyperbolic Rogue
// Copyright (C) 2011-2012 Zeno Rogue, see 'hyper.cpp' for details
// basic utility functions
#ifdef MOBILE
typedef double ld;
#else
typedef long double ld;
#endif
template<class T> int size(const T& x) {return int(x.size()); }
string its(int i) { char buf[64]; sprintf(buf, "%d", i); return buf; }
string fts(float x) { char buf[64]; sprintf(buf, "%4.2f", x); return buf; }
string fts4(float x) { char buf[64]; sprintf(buf, "%6.4f", x); return buf; }
string cts(char c) { char buf[8]; buf[0] = c; buf[1] = 0; return buf; }
string llts(long long i) {
// sprintf does not work on Windows IIRC
if(i < 0) return "-" + llts(-i);
if(i < 10) return its((int) i);
return llts(i/10) + its(i%10);
}
string itsh(int i) {static char buf[16]; sprintf(buf, "%03X", i); return buf; }
// for the Euclidean mode...
bool euclid = false;
enum eGeometry {gNormal, gEuclid, gSphere, gElliptic, gQuotient, gQuotient2, gGUARD};
eGeometry geometry, targetgeometry = gEuclid;
#define euclid (geometry == gEuclid)
#define sphere (geometry == gSphere || geometry == gElliptic)
#define elliptic (geometry == gElliptic)
#define quotient (geometry == gQuotient ? 1 : geometry == gQuotient2 ? 2 : 0)
// for the pure heptagonal grid
bool purehepta = false;
@ -34,13 +17,31 @@ bool purehepta = false;
//===========================
#ifdef SINHCOSH
ld sinh(ld alpha) { return (exp(alpha) - exp(-alpha)) / 2; }
ld cosh(ld alpha) { return (exp(alpha) + exp(-alpha)) / 2; }
// ld sinh(ld alpha) { return (exp(alpha) - exp(-alpha)) / 2; }
// ld cosh(ld alpha) { return (exp(alpha) + exp(-alpha)) / 2; }
/* ld inverse_sinh(ld z) {
return log(z+sqrt(1+z*z));
}
double inverse_cos(double c) {
double s = sqrt(1-c*c);
double r = atan(s/c);
if(r < 0) r = -r;
return r;
}
// ld tanh(ld x) { return sinh(x) / cosh(x); }
ld inverse_tanh(ld x) { return log((1+x)/(1-x)) / 2; } */
#endif
#ifndef M_PI
#define M_PI 3.14159265358979
#endif
ld squar(ld x) { return x*x; }
int sig(int z) { return z<2?1:-1; }
int sig(int z) { return (sphere || z<2)?1:-1; }
// hyperbolic point:
//===================
@ -62,17 +63,17 @@ hyperpoint hpxyz(ld x, ld y, ld z) {
hyperpoint hpxy(ld x, ld y) {
// EUCLIDEAN
return hpxyz(x,y, euclid ? 1 : sqrt(1+x*x+y*y));
return hpxyz(x,y, euclid ? 1 : sphere ? sqrt(1-x*x-y*y) : sqrt(1+x*x+y*y));
}
// center of the pseudosphere
hyperpoint Hypc = { {0,0,0} };
const hyperpoint Hypc = { {0,0,0} };
// origin of the hyperbolic plane
hyperpoint C0 = { {0,0,1} };
const hyperpoint C0 = { {0,0,1} };
// a point (I hope this number needs no comments ;) )
hyperpoint Cx1 = { {1,0,1.41421356237} };
const hyperpoint Cx1 = { {1,0,1.41421356237} };
// this function returns approximate square of distance between two points
// (in the spherical analogy, this would be the distance in the 3D space,
@ -80,7 +81,17 @@ hyperpoint Cx1 = { {1,0,1.41421356237} };
// also used to verify whether a point h1 is on the hyperbolic plane by using Hypc for h2
ld intval(const hyperpoint &h1, const hyperpoint &h2) {
return squar(h1[0]-h2[0]) + squar(h1[1]-h2[1]) - squar(h1[2]-h2[2]);
return squar(h1[0]-h2[0]) + squar(h1[1]-h2[1]) + (sphere?1:euclid?0:-1) * squar(h1[2]-h2[2]);
}
ld intvalxy(const hyperpoint &h1, const hyperpoint &h2) {
return squar(h1[0]-h2[0]) + squar(h1[1]-h2[1]);
}
ld zlevel(const hyperpoint &h) {
if(euclid) return h[2];
else if(sphere) return sqrt(intval(h, Hypc));
else return sqrt(-intval(h, Hypc));
}
// display a hyperbolic point
@ -100,7 +111,8 @@ hyperpoint mid(const hyperpoint& H1, const hyperpoint& H2) {
ld Z = 2;
if(!euclid) {
if(sphere) Z = sqrt(intval(H3, Hypc));
else if(!euclid) {
Z = intval(H3, Hypc);
Z = sqrt(-Z);
}
@ -110,42 +122,20 @@ hyperpoint mid(const hyperpoint& H1, const hyperpoint& H2) {
return H3;
}
hyperpoint mid3(const hyperpoint& H1, const hyperpoint& H2, const hyperpoint& H3) {
// like mid, but take 3D into account
hyperpoint midz(const hyperpoint& H1, const hyperpoint& H2) {
hyperpoint Hx;
Hx[0] = H1[0] + H2[0] + H3[0];
Hx[1] = H1[1] + H2[1] + H3[1];
Hx[2] = H1[2] + H2[2] + H3[2];
hyperpoint H3;
H3[0] = H1[0] + H2[0];
H3[1] = H1[1] + H2[1];
H3[2] = H1[2] + H2[2];
ld Z = 2;
if(!euclid) {
Z = intval(Hx, Hypc);
Z = sqrt(-Z);
}
if(sphere || !euclid) Z = zlevel(H3) * 2 / (zlevel(H1) + zlevel(H2));
for(int c=0; c<3; c++) H3[c] /= Z;
for(int c=0; c<3; c++) Hx[c] /= Z;
return Hx;
}
hyperpoint mid4(const hyperpoint& H1, const hyperpoint& H2, const hyperpoint& H3, const hyperpoint& H4) {
hyperpoint Hx;
Hx[0] = H1[0] + H2[0] + H3[0] + H4[0];
Hx[1] = H1[1] + H2[1] + H3[1] + H4[1];
Hx[2] = H1[2] + H2[2] + H3[2] + H4[2];
ld Z = 2;
if(!euclid) {
Z = intval(Hx, Hypc);
Z = sqrt(-Z);
}
for(int c=0; c<3; c++) Hx[c] /= Z;
return Hx;
return H3;
}
// matrices
@ -161,9 +151,13 @@ struct transmatrix {
};
// identity matrix
transmatrix Id = {{{1,0,0}, {0,1,0}, {0,0,1}}};
const transmatrix Id = {{{1,0,0}, {0,1,0}, {0,0,1}}};
transmatrix Mirror = {{{1,0,0}, {0,-1,0}, {0,0,1}}};
// mirror image
const transmatrix Mirror = {{{1,0,0}, {0,-1,0}, {0,0,1}}};
// rotate by PI
const transmatrix pispin = {{{-1,0,0}, {0,-1,0}, {0,0,1}}};
hyperpoint operator * (const transmatrix& T, const hyperpoint& H) {
hyperpoint z;
@ -174,11 +168,18 @@ hyperpoint operator * (const transmatrix& T, const hyperpoint& H) {
return z;
}
transmatrix operator * (const transmatrix& T, const transmatrix& U) {
// T * C0, optimized
inline hyperpoint tC0(const transmatrix &T) {
hyperpoint z;
z[0] = T[0][2]; z[1] = T[1][2]; z[2] = T[2][2];
return z;
}
inline transmatrix operator * (const transmatrix& T, const transmatrix& U) {
transmatrix R;
for(int i=0; i<3; i++) for(int j=0; j<3; j++) R[i][j] = 0;
for(int i=0; i<3; i++) for(int j=0; j<3; j++) for(int k=0; k<3; k++)
R[i][j] += T[i][k] * U[k][j];
// for(int i=0; i<3; i++) for(int j=0; j<3; j++) R[i][j] = 0;
for(int i=0; i<3; i++) for(int j=0; j<3; j++) // for(int k=0; k<3; k++)
R[i][j] = T[i][0] * U[0][j] + T[i][1] * U[1][j] + T[i][2] * U[2][j];
return R;
}
@ -202,26 +203,52 @@ transmatrix eupush(ld x, ld y) {
transmatrix xpush(ld alpha) {
if(euclid) return eupush(alpha, 0);
transmatrix T = Id;
T[0][0] = +cosh(alpha); T[0][2] = +sinh(alpha);
T[2][0] = +sinh(alpha); T[2][2] = +cosh(alpha);
if(sphere) {
T[0][0] = +cos(alpha); T[0][2] = +sin(alpha);
T[2][0] = -sin(alpha); T[2][2] = +cos(alpha);
}
else {
T[0][0] = +cosh(alpha); T[0][2] = +sinh(alpha);
T[2][0] = +sinh(alpha); T[2][2] = +cosh(alpha);
}
return T;
}
double inverse_sinh(ld z) {
return log(z+sqrt(1+z*z));
inline hyperpoint xpush0(ld x) {
hyperpoint h;
if(euclid) return hpxy(x, 0);
else if(sphere) h[0] = sin(x), h[1] = 0, h[2] = cos(x);
else h[0] = sinh(x), h[1] = 0, h[2] = cosh(x);
return h;
}
inline hyperpoint xspinpush0(ld alpha, ld x) {
// return spin(alpha)*xpush0(x);
ld s;
hyperpoint h;
if(euclid) return hpxy(x*cos(alpha), -x*sin(alpha));
else if(sphere) s=sin(x), h[0] = s*cos(alpha), h[1] = -s*sin(alpha), h[2] = cos(x);
else s=sinh(x), h[0] = s*cos(alpha), h[1] = -s*sin(alpha), h[2] = cosh(x);
return h;
}
// push alpha units vertically
transmatrix ypush(ld alpha) {
if(euclid) return eupush(0, alpha);
transmatrix T = Id;
T[1][1] = +cosh(alpha); T[1][2] = +sinh(alpha);
T[2][1] = +sinh(alpha); T[2][2] = +cosh(alpha);
if(sphere) {
T[1][1] = +cos(alpha); T[1][2] = +sin(alpha);
T[2][1] = -sin(alpha); T[2][2] = +cos(alpha);
}
else {
T[1][1] = +cosh(alpha); T[1][2] = +sinh(alpha);
T[2][1] = +sinh(alpha); T[2][2] = +cosh(alpha);
}
return T;
}
// rotate the hyperplane around C0 such that H[1] == 0 and H[0] >= 0
transmatrix spintox(hyperpoint H) {
transmatrix spintox(const hyperpoint& H) {
transmatrix T = Id;
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
if(R >= 1e-12) {
@ -232,7 +259,7 @@ transmatrix spintox(hyperpoint H) {
}
// reverse of spintox(H)
transmatrix rspintox(hyperpoint H) {
transmatrix rspintox(const hyperpoint& H) {
transmatrix T = Id;
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
if(R >= 1e-12) {
@ -243,30 +270,42 @@ transmatrix rspintox(hyperpoint H) {
}
// for H such that H[1] == 0, this matrix pushes H to C0
transmatrix pushxto0(hyperpoint H) {
transmatrix pushxto0(const hyperpoint& H) {
if(euclid) return eupush(-H[0], -H[1]);
transmatrix T = Id;
T[0][0] = +H[2]; T[0][2] = -H[0];
T[2][0] = -H[0]; T[2][2] = +H[2];
if(sphere) {
T[0][0] = +H[2]; T[0][2] = -H[0];
T[2][0] = +H[0]; T[2][2] = +H[2];
}
else {
T[0][0] = +H[2]; T[0][2] = -H[0];
T[2][0] = -H[0]; T[2][2] = +H[2];
}
return T;
}
// reverse of pushxto0(H)
transmatrix rpushxto0(hyperpoint H) {
transmatrix rpushxto0(const hyperpoint& H) {
if(euclid) return eupush(H[0], H[1]);
transmatrix T = Id;
T[0][0] = +H[2]; T[0][2] = +H[0];
T[2][0] = +H[0]; T[2][2] = +H[2];
if(sphere) {
T[0][0] = +H[2]; T[0][2] = +H[0];
T[2][0] = -H[0]; T[2][2] = +H[2];
}
else {
T[0][0] = +H[2]; T[0][2] = +H[0];
T[2][0] = +H[0]; T[2][2] = +H[2];
}
return T;
}
// generalization: H[1] can be non-zero
transmatrix gpushxto0(hyperpoint H) {
transmatrix gpushxto0(const hyperpoint& H) {
hyperpoint H2 = spintox(H) * H;
return rspintox(H) * pushxto0(H2) * spintox(H);
}
transmatrix rgpushxto0(hyperpoint H) {
transmatrix rgpushxto0(const hyperpoint& H) {
hyperpoint H2 = spintox(H) * H;
return rspintox(H) * rpushxto0(H2) * spintox(H);
}
@ -305,18 +344,22 @@ void fixmatrix(transmatrix& T) {
void display(const transmatrix& T) {
for(int y=0; y<3; y++) {
for(int x=0; x<3; x++) printf("%10.7f", double(T[y][x]));
printf(" -> %10.7f\n", double(squar(T[y][0]) + squar(T[y][1]) - squar(T[y][2])));
printf(" -> %10.7f\n", double(squar(T[y][0]) + squar(T[y][1]) + sig(2) * squar(T[y][2])));
// printf("\n");
}
for(int x=0; x<3; x++) printf("%10.7f", double(squar(T[0][x]) + squar(T[1][x]) - squar(T[2][x]))); printf("\n");
for(int x=0; x<3; x++) printf("%10.7f", double(squar(T[0][x]) + squar(T[1][x]) + sig(2) * squar(T[2][x])));
printf("\n");
for(int x=0; x<3; x++) {
int y = (x+1) % 3;
printf("%10.7f", double(T[0][x]*T[0][y] + T[1][x]*T[1][y] - T[2][x]*T[2][y]));
printf("%10.7f", double(T[0][x]*T[0][y] + T[1][x]*T[1][y] + sig(2) * T[2][x]*T[2][y]));
}
printf("\n\n");
}
transmatrix inverse(transmatrix T) {
profile_start(7);
ld det = 0;
for(int i=0; i<3; i++)
det += T[0][i] * T[1][(i+1)%3] * T[2][(i+2)%3];
@ -330,12 +373,22 @@ transmatrix inverse(transmatrix T) {
for(int j=0; j<3; j++)
T2[j][i] = (T[(i+1)%3][(j+1)%3] * T[(i+2)%3][(j+2)%3] - T[(i+1)%3][(j+2)%3] * T[(i+2)%3][(j+1)%3]) / det;
profile_stop(7);
return T2;
}
double hdist(hyperpoint h1, hyperpoint h2) {
hyperpoint mh = gpushxto0(h1) * h2;
return inverse_sinh(sqrt(mh[0]*mh[0]+mh[1]*mh[1]));
// distance between mh and 0
double hdist0(const hyperpoint& mh) {
if(sphere) return mh[2] >= 1 ? 0 : mh[2] <= -1 ? M_PI : acos(mh[2]);
if(!euclid && mh[2] > 1.5) return acosh(mh[2]);
ld d = sqrt(mh[0]*mh[0]+mh[1]*mh[1]);
if(euclid) return d;
return asinh(d);
}
// distance between two points
double hdist(const hyperpoint& h1, const hyperpoint& h2) {
return hdist0(gpushxto0(h1) * h2);
}
namespace hyperpoint_vec {
@ -361,3 +414,49 @@ namespace hyperpoint_vec {
}
}
hyperpoint mscale(const hyperpoint& t, double fac) {
hyperpoint res;
for(int i=0; i<3; i++)
res[i] = t[i] * fac;
return res;
}
transmatrix mscale(const transmatrix& t, double fac) {
transmatrix res;
for(int i=0; i<3; i++) for(int j=0; j<3; j++)
res[i][j] = t[i][j] * fac;
return res;
}
transmatrix xyscale(const transmatrix& t, double fac) {
transmatrix res;
for(int i=0; i<3; i++) for(int j=0; j<2; j++)
res[i][j] = t[i][j] * fac;
return res;
}
transmatrix xyzscale(const transmatrix& t, double fac, double facz) {
transmatrix res;
for(int i=0; i<3; i++) for(int j=0; j<2; j++)
res[i][j] = t[i][j] * fac;
for(int i=0; i<3; i++)
res[i][2] = t[i][2] * facz;
return res;
}
// double downspin_zivory;
transmatrix mzscale(const transmatrix& t, double fac) {
// take only the spin
transmatrix tcentered = gpushxto0(tC0(t)) * t;
// tcentered = tcentered * spin(downspin_zivory);
fac -= 1;
transmatrix res = t * inverse(tcentered) * ypush(-fac) * tcentered;
fac *= .2;
fac += 1;
for(int i=0; i<3; i++) for(int j=0; j<3; j++)
res[i][j] = res[i][j] * fac;
return res;
}

173
hyperweb.cpp Normal file
View File

@ -0,0 +1,173 @@
#define WEB
#define MOBWEB
#define MINI
#define NOAUDIO
#define NOGFX
#define NOPNG
#define DEMO
#ifdef FAKEWEB
void mainloopiter();
template<class A, class B, class C> void emscripten_set_main_loop(A a, B b, C c) { while(true) mainloopiter(); }
#else
#include <emscripten.h>
#endif
void initweb();
void loadCompressedChar(int &otwidth, int &otheight, int *tpix);
#include "hyper.cpp"
void playSound(cell *c, const string& fname, int vol) { }
void initweb() {
toggleanim(false);
cmode = emMenu;
}
unsigned char fonttable[] = {
43,13,0,255,0,255,0,49,
43,16,0,133,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,236,255,4,235,0,10,206,255,4,205,0,10,176,255,4,175,0,10,146,255,4,145,0,10,116,255,4,115,0,10,86,255,4,85,0,10,55,255,4,55,0,58,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,149,
43,19,0,155,255,4,0,4,255,4,0,7,255,4,0,4,255,4,0,7,255,4,0,4,255,4,0,7,255,4,0,4,255,4,0,7,255,4,0,4,255,4,0,7,255,4,0,4,255,4,0,7,255,4,0,4,255,4,0,7,255,4,0,4,255,4,0,7,255,4,0,4,255,4,0,7,255,4,0,4,255,4,0,255,0,224,
43,30,0,251,152,255,3,72,0,3,28,255,3,188,0,17,223,255,2,247,9,0,3,100,255,3,116,0,16,38,255,3,185,0,4,172,255,3,44,0,16,110,255,3,114,0,3,3,239,255,2,227,0,17,181,255,3,42,0,3,60,255,3,156,0,16,7,245,255,2,227,0,4,132,255,3,84,0,11,255,23,0,7,255,23,0,7,255,23,0,7,255,23,0,11,51,255,3,172,0,4,187,255,3,37,0,16,114,255,3,110,0,3,5,244,255,2,230,0,17,176,255,3,47,0,3,56,255,3,169,0,16,1,237,255,2,239,2,0,3,119,255,3,108,0,16,45,255,3,179,0,4,181,255,3,46,0,11,255,23,0,7,255,23,0,7,255,23,0,7,255,23,0,11,70,255,3,157,0,4,207,255,2,252,12,0,16,131,255,3,95,0,3,15,253,255,2,203,0,17,192,255,3,34,0,3,73,255,3,142,0,16,5,246,255,2,229,0,4,134,255,3,81,0,16,57,255,3,168,0,4,195,255,3,21,0,16,117,255,3,107,0,3,7,248,255,2,215,0,17,178,255,3,46,0,3,61,255,3,154,0,255,0,27,
43,25,0,186,252,255,2,0,22,253,255,2,0,22,254,255,2,0,22,255,3,0,17,24,102,157,193,216,255,3,233,188,130,64,3,0,10,29,169,252,255,11,236,153,51,0,7,58,240,255,16,0,6,21,236,255,17,0,6,137,255,5,238,111,39,255,3,15,46,97,159,238,255,2,0,6,214,255,5,71,0,2,255,3,0,4,5,83,198,0,6,245,255,5,13,0,2,255,3,0,13,244,255,5,85,0,2,255,3,0,13,209,255,5,244,133,59,255,3,0,13,130,255,11,166,119,71,12,0,9,16,227,255,13,249,186,81,2,0,7,38,213,255,15,196,28,0,7,7,106,212,255,14,215,9,0,9,36,105,157,204,255,11,116,0,13,255,3,121,185,255,6,199,0,13,255,3,0,2,106,255,5,238,0,13,255,3,0,2,15,255,5,248,0,4,206,106,18,0,6,255,3,0,2,40,255,5,224,0,4,255,2,252,196,127,79,35,9,0,1,255,3,14,62,204,255,5,158,0,4,255,8,245,255,10,250,44,0,4,255,18,253,97,0,5,255,17,204,60,0,6,21,62,103,145,181,199,215,232,248,255,3,240,215,178,124,49,0,16,4,255,3,0,21,3,255,3,0,21,2,255,3,0,21,1,255,3,0,22,255,3,0,111,
43,36,0,255,0,37,62,163,221,246,246,221,162,62,0,11,11,224,255,2,159,0,10,8,162,255,8,159,7,0,9,141,255,2,235,19,0,10,163,255,10,159,0,8,49,252,255,2,94,0,10,65,255,3,251,109,16,15,109,252,255,3,61,0,6,2,203,255,2,189,0,11,164,255,3,145,0,4,145,255,3,163,0,6,111,255,2,247,37,0,11,223,255,3,47,0,4,48,255,3,222,0,5,28,243,255,2,125,0,12,247,255,3,14,0,4,14,255,3,246,0,5,176,255,2,214,6,0,12,246,255,3,14,0,4,15,255,3,245,0,4,81,255,2,254,61,0,13,222,255,3,48,0,4,49,255,3,221,0,3,13,227,255,2,155,0,14,163,255,3,146,0,4,146,255,3,161,0,3,146,255,2,233,17,0,14,63,255,3,251,108,15,14,107,251,255,3,60,0,2,53,253,255,2,90,0,16,163,255,10,159,0,2,4,207,255,2,185,0,17,8,162,255,8,160,7,0,2,116,255,2,246,35,0,3,60,161,221,245,246,222,163,63,0,8,63,163,222,246,246,222,162,62,0,3,31,244,255,2,121,0,2,7,159,255,8,162,8,0,17,181,255,2,211,5,0,2,160,255,10,162,0,16,86,255,2,254,58,0,2,63,255,3,251,110,16,15,109,252,255,3,64,0,14,15,230,255,2,152,0,3,164,255,3,145,0,4,145,255,3,164,0,14,151,255,2,231,16,0,3,223,255,3,47,0,4,48,255,3,222,0,13,57,254,255,2,87,0,4,247,255,3,14,0,4,14,255,3,246,0,12,5,211,255,2,182,0,5,246,255,3,14,0,4,15,255,3,245,0,12,121,255,2,245,32,0,5,221,255,3,47,0,4,47,255,3,220,0,11,34,246,255,2,117,0,6,161,255,3,144,0,4,141,255,3,161,0,11,185,255,2,208,4,0,6,60,255,3,251,107,15,14,103,250,255,3,59,0,10,91,255,2,253,55,0,8,159,255,10,158,0,10,17,233,255,2,148,0,9,7,159,255,8,160,7,0,10,156,255,2,229,14,0,11,60,161,221,246,246,222,163,62,0,255,0,73,
43,31,0,255,0,2,32,124,187,229,246,251,241,218,186,142,91,30,0,17,6,143,251,255,11,0,16,3,187,255,13,0,16,112,255,14,0,16,209,255,5,186,52,10,14,44,107,197,255,2,0,16,246,255,5,22,0,6,50,187,0,16,239,255,5,43,0,24,179,255,5,184,1,0,23,59,254,255,5,147,0,23,36,228,255,6,146,0,21,86,244,255,8,150,1,0,6,20,255,4,237,0,6,109,254,255,10,155,1,0,5,63,255,4,201,0,5,81,255,13,159,2,0,4,145,255,4,164,0,4,15,235,255,4,241,84,209,255,7,164,3,0,2,21,238,255,4,102,0,4,115,255,5,105,0,1,19,206,255,7,168,4,1,172,255,5,28,0,4,194,255,5,24,0,2,17,204,255,7,172,155,255,5,199,0,5,238,255,5,5,0,3,15,201,255,13,78,0,5,251,255,5,38,0,4,14,198,255,11,202,1,0,5,231,255,5,136,0,5,12,194,255,9,242,37,0,6,190,255,5,250,72,0,5,11,191,255,8,142,0,7,108,255,6,251,131,22,0,2,1,65,200,255,8,244,37,0,6,11,226,255,7,253,224,215,242,255,11,202,4,0,6,67,249,255,22,133,0,7,76,242,255,15,229,227,255,4,253,61,0,7,31,178,254,255,10,249,186,87,4,53,251,255,4,224,15,0,8,36,124,183,224,242,251,238,220,182,137,81,13,0,4,123,255,5,165,0,255,0,25,
43,11,0,91,255,4,0,7,255,4,0,7,255,4,0,7,255,4,0,7,255,4,0,7,255,4,0,7,255,4,0,7,255,4,0,7,255,4,0,7,255,4,0,255,0,24,
43,16,0,119,20,238,255,4,183,0,9,155,255,4,252,42,0,8,45,253,255,4,157,0,9,177,255,4,251,34,0,8,48,255,5,165,0,9,163,255,5,57,0,8,21,250,255,4,211,0,9,111,255,5,121,0,9,199,255,5,38,0,8,20,253,255,4,223,0,9,85,255,5,160,0,9,139,255,5,109,0,9,184,255,5,66,0,9,217,255,5,36,0,9,240,255,5,15,0,9,251,255,5,4,0,9,251,255,5,4,0,9,240,255,5,14,0,9,219,255,5,36,0,9,186,255,5,66,0,9,141,255,5,109,0,9,88,255,5,159,0,9,23,254,255,4,221,0,10,203,255,5,35,0,9,114,255,5,118,0,9,23,251,255,4,208,0,10,165,255,5,54,0,9,50,255,5,163,0,10,178,255,4,250,33,0,9,46,254,255,4,155,0,10,156,255,4,251,41,0,9,21,238,255,4,183,0,66,
43,16,0,114,184,255,4,238,20,0,9,43,252,255,4,153,0,10,157,255,4,253,44,0,9,34,251,255,4,176,0,10,166,255,5,48,0,9,58,255,5,162,0,10,211,255,4,249,20,0,9,122,255,5,109,0,9,39,255,5,198,0,10,224,255,4,253,19,0,9,161,255,5,84,0,9,110,255,5,138,0,9,67,255,5,183,0,9,37,255,5,216,0,9,15,255,5,238,0,9,5,255,5,249,0,9,6,255,5,250,0,9,15,255,5,239,0,9,38,255,5,217,0,9,68,255,5,184,0,9,111,255,5,139,0,9,162,255,5,85,0,9,225,255,4,253,20,0,8,40,255,5,198,0,9,123,255,5,110,0,9,212,255,4,250,21,0,8,59,255,5,162,0,9,167,255,5,48,0,8,35,251,255,4,176,0,9,158,255,4,253,45,0,8,43,252,255,4,154,0,9,184,255,4,238,20,0,71,
43,19,0,160,255,3,0,16,255,3,0,16,255,3,0,9,12,198,83,0,4,255,3,0,4,83,198,12,0,2,126,255,2,196,54,0,2,255,3,0,2,54,196,255,2,125,0,2,173,255,3,254,166,31,255,3,31,166,254,255,3,173,0,3,67,201,255,3,247,255,3,248,255,3,204,69,0,6,82,214,255,7,218,87,1,0,8,8,189,255,5,199,11,0,8,1,86,218,255,7,223,91,1,0,5,69,204,255,3,245,255,3,245,255,3,207,71,0,3,173,255,3,254,161,27,255,3,27,161,254,255,3,174,0,2,126,255,2,193,51,0,2,255,3,0,2,51,193,255,2,125,0,2,12,199,82,0,4,255,3,0,4,83,198,12,0,9,255,3,0,16,255,3,0,16,255,3,0,255,0,95,
43,30,0,255,0,118,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,17,255,22,0,8,255,22,0,8,255,22,0,8,255,22,0,17,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,26,255,4,0,255,0,28,
43,14,0,255,0,127,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,7,14,255,5,227,0,7,86,255,5,83,0,7,164,255,4,184,0,7,3,237,255,3,248,36,0,7,63,255,4,130,0,8,140,255,3,222,9,0,8,217,255,3,77,0,63,
43,15,0,255,0,62,255,11,0,4,255,11,0,4,255,11,0,4,255,11,0,4,255,11,0,255,0,2,
43,14,0,255,0,127,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,130,
43,13,0,112,11,247,255,2,216,0,8,81,255,3,138,0,8,159,255,3,60,0,7,2,234,255,2,235,2,0,7,59,255,3,160,0,8,137,255,3,82,0,8,215,255,2,248,11,0,7,36,255,3,181,0,8,115,255,3,103,0,8,193,255,2,254,26,0,7,18,252,255,2,203,0,8,93,255,3,125,0,8,171,255,3,47,0,7,6,242,255,2,224,0,8,71,255,3,147,0,8,149,255,3,68,0,8,226,255,2,240,5,0,7,49,255,3,168,0,8,127,255,3,90,0,8,205,255,2,251,16,0,7,27,255,3,190,0,8,105,255,3,112,0,8,183,255,3,33,0,7,12,248,255,2,211,0,8,83,255,3,133,0,8,161,255,3,55,0,7,3,235,255,2,231,1,0,7,61,255,3,155,0,8,139,255,3,77,0,8,217,255,2,245,9,0,73,
43,25,0,208,47,125,193,222,244,245,222,193,125,47,0,13,35,174,255,10,173,34,0,10,62,241,255,12,240,59,0,8,56,246,255,14,245,53,0,6,9,217,255,16,214,8,0,5,123,255,5,254,151,51,12,11,50,148,253,255,5,120,0,4,2,225,255,5,106,0,6,102,255,5,223,2,0,3,68,255,5,208,0,8,207,255,5,66,0,3,127,255,5,122,0,8,122,255,5,125,0,3,181,255,5,65,0,8,65,255,5,179,0,3,217,255,5,31,0,8,32,255,5,216,0,3,233,255,5,12,0,8,14,255,5,232,0,3,248,255,5,3,0,8,4,255,5,247,0,3,248,255,5,4,0,8,5,255,5,248,0,3,233,255,5,13,0,8,14,255,5,232,0,3,218,255,5,33,0,8,34,255,5,216,0,3,182,255,5,67,0,8,68,255,5,180,0,3,128,255,5,125,0,8,126,255,5,126,0,3,69,255,5,211,1,0,6,1,212,255,5,67,0,3,3,226,255,5,109,0,6,109,255,5,224,2,0,4,125,255,5,254,152,51,11,11,50,152,254,255,5,121,0,5,10,219,255,16,216,8,0,6,58,247,255,14,246,55,0,8,64,242,255,12,241,62,0,10,38,177,255,10,177,36,0,13,48,127,194,223,245,245,224,194,127,48,0,232,
43,25,0,204,21,63,106,149,191,234,255,6,0,13,255,12,0,13,255,12,0,13,255,12,0,13,255,12,0,13,235,192,149,107,64,21,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,13,255,18,0,7,255,18,0,7,255,18,0,7,255,18,0,7,255,18,0,228,
43,25,0,205,1,49,116,165,209,233,248,248,233,208,159,95,12,0,10,48,144,232,255,11,244,123,6,0,8,255,16,196,14,0,7,255,17,183,0,7,255,18,78,0,6,255,3,254,186,109,45,16,7,37,113,233,255,6,170,0,6,255,1,250,150,37,0,7,25,220,255,5,224,0,6,182,39,0,10,78,255,5,248,0,18,12,255,5,241,0,18,21,255,5,222,0,18,90,255,5,183,0,17,14,218,255,5,130,0,16,10,184,255,6,61,0,15,27,199,255,6,207,0,15,75,236,255,6,253,60,0,13,5,141,254,255,7,129,0,13,33,202,255,8,172,2,0,12,84,240,255,8,181,7,0,11,8,151,255,8,251,120,1,0,11,40,209,255,8,225,57,0,12,94,244,255,8,175,17,0,13,255,19,0,6,255,19,0,6,255,19,0,6,255,19,0,6,255,19,0,228,
43,25,0,203,28,84,132,172,204,229,244,252,248,237,218,183,138,66,4,0,10,255,14,222,92,0,9,255,16,140,0,8,255,17,76,0,7,255,17,183,0,7,224,161,111,67,38,15,5,4,22,64,149,251,255,5,233,0,18,85,255,5,246,0,18,9,255,5,217,0,18,81,255,5,162,0,14,4,21,60,139,247,255,4,253,46,0,11,255,11,251,89,0,12,255,10,175,39,0,13,255,10,252,171,34,0,12,255,12,240,60,0,11,255,13,236,20,0,13,7,22,54,113,211,255,6,128,0,17,3,158,255,5,205,0,18,23,255,5,242,0,18,21,255,5,248,0,5,201,92,8,0,9,2,155,255,5,222,0,5,255,2,244,172,106,58,24,8,3,17,49,107,207,255,6,165,0,5,255,19,65,0,5,255,18,154,0,6,255,17,150,4,0,6,55,166,248,255,12,198,69,0,10,12,84,148,195,229,246,252,241,229,201,161,110,34,0,233,
43,25,0,211,7,212,255,7,0,16,139,255,8,0,15,60,253,255,8,0,14,12,221,255,9,0,14,152,255,3,254,255,6,0,13,72,255,4,143,255,6,0,12,17,229,255,3,215,8,255,6,0,12,165,255,3,251,52,0,1,255,6,0,11,84,255,4,129,0,2,255,6,0,10,23,235,255,3,205,5,0,2,255,6,0,10,179,255,3,248,43,0,3,255,6,0,9,98,255,4,116,0,4,255,6,0,8,31,241,255,3,194,2,0,4,255,6,0,7,1,190,255,3,243,34,0,5,255,6,0,7,111,255,4,102,0,6,255,6,0,7,246,255,3,182,0,7,255,6,0,7,255,22,0,3,255,22,0,3,255,22,0,3,255,22,0,3,255,22,0,15,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,230,
43,25,0,204,255,17,0,8,255,17,0,8,255,17,0,8,255,17,0,8,255,17,0,8,255,5,0,20,255,5,0,20,255,5,0,20,255,5,163,214,239,251,237,219,175,120,35,0,11,255,13,254,181,34,0,9,255,15,244,79,0,8,255,16,248,64,0,7,255,17,219,6,0,6,255,2,238,158,93,40,15,4,21,66,150,249,255,6,94,0,6,199,86,5,0,8,45,232,255,5,172,0,18,89,255,5,221,0,18,14,255,5,244,0,18,14,255,5,244,0,18,89,255,5,220,0,5,206,106,17,0,9,43,232,255,5,168,0,5,255,2,251,193,123,74,32,12,3,19,63,146,248,255,6,86,0,5,255,18,206,2,0,5,255,17,239,47,0,6,255,16,230,54,0,7,52,155,242,255,11,246,150,16,0,10,6,66,133,178,217,237,250,247,231,207,158,100,15,0,232,
43,25,0,209,55,128,193,220,241,249,231,192,134,47,0,13,64,200,255,10,199,67,0,9,1,137,255,14,0,9,163,255,15,0,8,112,255,16,0,7,41,249,255,5,232,131,60,20,5,15,47,110,201,255,2,0,7,158,255,5,177,13,0,8,53,188,0,6,22,248,255,4,212,7,0,17,92,255,5,92,14,111,185,232,250,240,218,165,92,6,0,8,151,255,5,119,239,255,8,228,81,0,7,202,255,17,128,0,6,224,255,18,99,0,5,242,255,18,239,18,0,4,251,255,7,179,58,18,9,51,164,255,6,118,0,4,238,255,6,179,0,6,179,255,5,192,0,4,222,255,6,58,0,6,58,255,5,230,0,4,193,255,6,18,0,6,18,255,5,249,0,4,141,255,6,18,0,6,18,255,5,236,0,4,84,255,6,58,0,6,58,255,5,210,0,4,10,239,255,5,179,0,6,179,255,5,147,0,5,145,255,6,178,57,17,8,51,164,255,6,63,0,5,21,234,255,16,187,0,7,80,254,255,14,232,33,0,8,90,250,255,12,228,50,0,10,55,198,255,9,252,165,20,0,13,64,141,203,229,249,243,224,184,121,33,0,232,
43,25,0,202,255,20,0,5,255,20,0,5,255,20,0,5,255,19,253,0,5,255,19,169,0,17,108,255,6,50,0,16,3,223,255,5,187,0,17,90,255,6,68,0,17,208,255,5,205,0,17,73,255,6,86,0,17,192,255,5,219,2,0,16,55,255,6,103,0,17,174,255,5,232,8,0,16,40,253,255,5,121,0,17,157,255,5,242,16,0,16,26,249,255,5,138,0,17,139,255,5,249,26,0,16,16,242,255,5,156,0,17,122,255,5,253,39,0,16,8,232,255,5,174,0,17,104,255,6,55,0,16,3,220,255,5,191,0,17,87,255,6,72,0,17,205,255,5,208,0,17,69,255,6,90,0,17,188,255,5,223,4,0,237,
43,25,0,206,6,73,149,194,229,243,252,243,229,194,149,73,5,0,11,86,225,255,11,223,83,0,9,121,255,15,119,0,7,58,253,255,15,253,57,0,6,167,255,17,166,0,6,228,255,5,234,101,28,7,27,97,232,255,5,227,0,6,250,255,5,70,0,5,69,255,5,249,0,6,239,255,5,13,0,5,13,255,5,238,0,6,191,255,5,69,0,5,70,255,5,189,0,6,94,255,5,232,98,27,6,27,97,232,255,5,91,0,6,2,181,255,15,165,1,0,7,6,143,251,255,11,245,133,2,0,9,18,136,254,255,9,253,129,18,0,9,113,242,255,13,242,113,0,7,142,255,17,142,0,5,68,255,6,158,61,16,4,26,64,164,254,255,5,67,0,4,176,255,5,122,0,7,120,255,5,175,0,4,233,255,5,16,0,7,17,255,5,232,0,4,251,255,5,16,0,7,17,255,5,250,0,4,234,255,5,119,0,7,118,255,5,232,0,4,193,255,5,254,154,59,15,4,24,62,161,254,255,5,192,0,4,109,255,19,109,0,4,8,217,255,17,217,8,0,5,40,225,255,15,226,40,0,7,21,151,251,255,11,252,154,22,0,10,19,98,159,202,231,244,253,244,231,203,161,101,21,0,231,
43,25,0,207,34,121,184,223,242,248,227,201,137,61,0,13,20,165,252,255,9,193,50,0,10,50,228,255,12,248,81,0,8,32,232,255,14,252,74,0,7,187,255,16,230,17,0,5,63,255,6,167,52,9,9,53,170,255,6,140,0,5,147,255,5,181,0,6,170,255,5,237,8,0,4,210,255,5,58,0,6,53,255,6,82,0,4,237,255,5,18,0,6,9,255,6,138,0,4,250,255,5,18,0,6,9,255,6,191,0,4,232,255,5,58,0,6,52,255,6,220,0,4,194,255,5,179,0,6,167,255,6,237,0,4,121,255,6,179,58,18,18,58,181,255,7,251,0,4,21,242,255,18,241,0,5,106,255,18,223,0,6,136,255,17,201,0,7,88,231,255,8,236,115,255,5,150,0,8,7,95,167,220,241,250,232,185,108,11,99,255,5,90,0,17,10,218,255,4,248,21,0,6,186,50,0,8,17,185,255,5,156,0,7,255,2,195,106,44,13,4,20,61,134,235,255,5,248,40,0,7,255,16,110,0,8,255,15,162,0,9,255,14,135,1,0,9,69,203,255,10,198,63,0,13,53,138,196,234,250,241,220,192,127,54,0,234,
43,14,0,200,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,92,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,130,
43,14,0,200,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,92,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,8,255,6,0,7,14,255,5,227,0,7,86,255,5,83,0,7,164,255,4,184,0,7,3,237,255,3,248,36,0,7,63,255,4,130,0,8,140,255,3,222,9,0,8,217,255,3,77,0,63,
43,30,0,255,0,129,34,122,211,0,24,26,112,201,255,3,0,21,19,102,191,253,255,5,0,18,13,92,181,250,255,8,0,15,8,82,171,246,255,9,203,118,0,12,4,72,161,241,255,9,201,116,32,0,11,1,62,151,235,255,8,254,199,114,30,0,12,52,140,227,255,8,254,197,112,28,0,15,255,8,253,195,110,27,0,18,255,5,253,193,108,25,0,21,255,5,251,184,100,20,0,21,255,8,252,188,103,22,0,18,52,141,228,255,8,252,191,107,25,0,17,1,63,152,235,255,8,253,195,111,28,0,17,4,73,162,241,255,8,254,198,114,30,0,17,8,83,172,246,255,9,202,118,0,18,13,93,182,250,255,8,0,21,19,103,192,253,255,5,0,24,26,113,202,255,3,0,27,35,123,212,0,255,0,78,
43,30,0,255,0,255,0,4,255,23,0,7,255,23,0,7,255,23,0,7,255,23,0,127,255,23,0,7,255,23,0,7,255,23,0,7,255,23,0,255,0,168,
43,30,0,255,0,109,211,122,34,0,27,255,3,200,112,26,0,24,255,5,253,190,102,19,0,21,255,8,250,180,92,13,0,18,118,203,255,9,246,170,82,8,0,17,32,116,201,255,9,241,160,72,4,0,17,30,114,199,254,255,8,235,150,62,1,0,17,28,112,196,254,255,8,227,140,52,0,18,26,110,194,253,255,8,0,21,24,107,192,253,255,5,0,21,19,99,183,250,255,5,0,18,22,103,187,251,255,8,0,15,25,107,191,252,255,8,228,141,52,0,12,27,110,194,253,255,8,235,151,63,1,0,11,31,114,198,254,255,8,241,161,73,4,0,12,118,202,255,9,246,171,83,8,0,15,255,8,250,181,93,13,0,18,255,5,253,191,103,19,0,21,255,3,201,113,26,0,24,212,123,34,0,255,0,98,
43,21,0,170,28,84,133,175,209,235,248,251,238,216,171,103,18,0,8,255,12,242,114,0,7,255,14,145,0,6,255,15,67,0,5,255,15,171,0,5,255,2,223,136,72,26,6,23,88,227,255,5,229,0,5,194,71,1,0,6,59,255,5,249,0,14,12,255,5,231,0,14,85,255,5,181,0,13,36,233,255,5,82,0,12,53,232,255,5,180,0,12,80,246,255,5,209,17,0,11,87,252,255,5,206,23,0,11,51,249,255,5,179,10,0,12,174,255,5,183,4,0,13,236,255,5,38,0,14,255,6,1,0,77,255,6,0,15,255,6,0,15,255,6,0,15,255,6,0,15,255,6,0,15,255,6,0,198,
43,36,0,255,0,45,19,97,161,207,238,251,246,233,201,159,94,22,0,22,37,166,250,255,10,250,171,50,0,18,18,154,251,255,14,254,158,14,0,15,43,220,255,4,204,123,68,28,11,5,21,58,116,195,255,4,221,40,0,13,70,242,255,3,179,46,0,10,44,176,255,3,235,46,0,11,42,242,255,2,237,77,0,14,86,243,255,2,226,24,0,9,17,220,255,2,224,34,0,16,52,239,255,2,181,0,9,154,255,2,236,33,0,18,67,253,255,2,88,0,7,37,251,255,2,73,0,5,41,155,224,248,231,173,57,0,1,255,4,0,3,143,255,2,210,0,7,167,255,2,170,0,5,101,250,255,5,252,93,255,4,0,3,14,243,255,2,62,0,5,20,251,255,2,43,0,4,81,254,255,7,243,255,4,0,4,153,255,2,135,0,5,97,255,2,195,0,4,12,234,255,3,188,45,7,49,194,255,5,0,4,83,255,2,195,0,5,160,255,2,117,0,4,104,255,3,220,8,0,3,11,225,255,4,0,4,31,255,2,227,0,5,205,255,2,61,0,4,179,255,3,107,0,5,111,255,4,0,4,11,255,2,246,0,5,237,255,2,22,0,4,228,255,3,42,0,5,45,255,4,0,4,7,255,2,244,0,5,250,255,2,6,0,4,247,255,3,13,0,5,14,255,4,0,4,28,255,2,222,0,5,250,255,2,11,0,4,248,255,3,14,0,5,16,255,4,0,4,78,255,2,187,0,5,238,255,2,34,0,4,229,255,3,43,0,5,45,255,4,0,4,164,255,2,118,0,5,208,255,2,61,0,4,181,255,3,109,0,5,113,255,4,0,3,46,252,255,1,250,33,0,5,164,255,2,125,0,4,107,255,3,220,8,0,3,10,224,255,4,0,2,29,220,255,2,149,0,6,105,255,2,198,0,4,13,237,255,3,187,45,6,47,192,255,5,30,115,236,255,2,208,13,0,6,27,253,255,1,253,41,0,4,87,255,8,240,255,8,206,26,0,8,184,255,2,171,0,5,107,251,255,5,251,89,255,6,242,133,6,0,9,66,255,2,254,64,0,5,44,158,225,249,232,174,56,0,1,255,2,250,223,168,103,14,0,12,178,255,2,234,30,0,12,23,12,0,17,27,235,255,2,210,28,0,31,67,249,255,2,231,57,0,13,15,165,91,0,15,81,248,255,2,252,158,29,0,9,10,107,227,255,1,241,32,0,15,64,234,255,3,249,185,109,52,28,6,18,41,91,161,236,255,4,188,0,16,25,174,255,16,176,32,0,18,63,184,253,255,10,248,165,59,0,22,27,105,166,209,239,251,245,225,201,147,86,18,0,120,
43,28,0,233,77,255,8,76,0,18,174,255,8,173,0,17,21,250,255,8,250,20,0,16,113,255,10,112,0,16,210,255,10,209,0,15,52,255,12,51,0,14,149,255,5,214,214,255,5,148,0,13,8,238,255,5,117,118,255,5,238,7,0,12,88,255,5,252,24,25,252,255,5,87,0,12,185,255,5,179,0,2,180,255,5,184,0,11,29,253,255,5,81,0,2,82,255,5,253,28,0,10,124,255,5,235,5,0,2,5,235,255,5,123,0,10,220,255,5,143,0,4,144,255,5,219,0,9,63,255,6,46,0,4,47,255,6,62,0,8,160,255,5,205,0,6,206,255,5,159,0,7,13,244,255,5,108,0,6,109,255,5,244,12,0,6,99,255,20,98,0,6,196,255,20,195,0,5,38,255,22,37,0,4,135,255,22,134,0,3,3,229,255,22,228,3,0,2,74,255,6,36,0,10,36,255,6,73,0,2,171,255,5,193,0,12,194,255,5,170,0,1,19,249,255,5,95,0,12,96,255,5,248,18,110,255,5,242,11,0,12,11,243,255,5,109,207,255,5,156,0,14,157,255,5,207,0,252,
43,27,0,219,255,10,253,244,233,209,174,126,55,1,0,9,255,17,211,74,0,8,255,18,254,108,0,7,255,19,250,46,0,6,255,20,151,0,6,255,6,0,5,16,43,123,240,255,5,218,0,6,255,6,0,8,69,255,5,244,0,6,255,6,0,8,7,255,5,239,0,6,255,6,0,8,67,255,5,208,0,6,255,6,0,5,15,42,120,238,255,5,125,0,6,255,19,226,20,0,6,255,18,213,44,0,7,255,18,143,17,0,7,255,19,228,41,0,6,255,20,218,7,0,5,255,6,0,5,4,21,67,159,255,6,104,0,5,255,6,0,9,119,255,5,189,0,5,255,6,0,9,16,255,5,235,0,5,255,6,0,9,17,255,5,251,0,5,255,6,0,9,122,255,5,235,0,5,255,6,0,5,4,21,67,161,255,6,200,0,5,255,21,122,0,5,255,20,231,16,0,5,255,19,240,60,0,6,255,18,180,38,0,7,255,12,248,236,213,171,117,36,0,249,
43,26,0,218,47,110,171,209,228,246,248,233,205,158,99,21,0,11,3,86,203,255,11,253,179,62,0,8,30,197,255,16,0,7,62,237,255,17,0,6,41,239,255,18,0,5,9,221,255,6,252,167,88,33,12,6,21,47,95,155,234,255,2,0,5,118,255,6,211,41,0,9,3,80,197,0,4,11,237,255,5,209,12,0,17,77,255,5,251,37,0,18,149,255,5,162,0,19,202,255,5,83,0,19,224,255,5,30,0,19,245,255,5,9,0,19,246,255,5,8,0,19,225,255,5,29,0,19,203,255,5,81,0,19,150,255,5,161,0,19,78,255,5,251,36,0,18,11,238,255,5,207,11,0,18,119,255,6,208,38,0,9,3,79,197,0,5,9,222,255,6,252,166,86,32,11,5,20,46,94,155,234,255,2,0,6,42,239,255,18,0,7,63,237,255,17,0,8,30,198,255,16,0,9,3,87,204,255,11,253,180,63,0,12,48,111,172,210,229,247,249,234,206,159,99,22,0,238,
43,30,0,243,255,7,250,245,239,224,208,184,152,111,57,4,0,13,255,16,235,147,34,0,11,255,18,251,136,7,0,9,255,20,202,25,0,8,255,21,216,21,0,7,255,6,0,3,9,23,48,96,157,241,255,7,192,2,0,6,255,6,0,8,9,124,247,255,6,104,0,6,255,6,0,10,52,239,255,5,228,5,0,5,255,6,0,11,71,255,6,66,0,5,255,6,0,12,180,255,5,141,0,5,255,6,0,12,92,255,5,199,0,5,255,6,0,12,34,255,5,223,0,5,255,6,0,12,11,255,5,245,0,5,255,6,0,12,12,255,5,244,0,5,255,6,0,12,36,255,5,222,0,5,255,6,0,12,95,255,5,198,0,5,255,6,0,12,184,255,5,140,0,5,255,6,0,11,75,255,6,66,0,5,255,6,0,10,55,241,255,5,228,5,0,5,255,6,0,8,10,126,248,255,6,104,0,6,255,6,0,3,8,22,47,95,158,242,255,7,192,2,0,6,255,21,217,22,0,7,255,20,202,25,0,8,255,18,250,135,7,0,9,255,16,234,145,32,0,11,255,7,252,247,240,225,208,183,152,110,55,3,0,255,0,25,
43,25,0,203,255,19,0,6,255,19,0,6,255,19,0,6,255,19,0,6,255,19,0,6,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,18,0,7,255,18,0,7,255,18,0,7,255,18,0,7,255,18,0,7,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,19,0,6,255,19,0,6,255,19,0,6,255,19,0,6,255,19,0,228,
43,25,0,203,255,18,0,7,255,18,0,7,255,18,0,7,255,18,0,7,255,18,0,7,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,17,0,8,255,17,0,8,255,17,0,8,255,17,0,8,255,17,0,8,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,241,
43,30,0,250,29,92,151,200,220,237,251,243,230,206,166,124,59,4,0,14,66,178,254,255,12,241,155,54,0,10,18,174,255,18,0,9,48,228,255,19,0,8,32,231,255,20,0,7,5,214,255,7,198,110,53,23,6,10,24,48,90,137,205,254,255,2,0,7,110,255,6,223,63,0,11,21,110,207,0,6,9,235,255,5,217,19,0,21,74,255,5,252,43,0,22,147,255,5,165,0,23,201,255,5,84,0,23,224,255,5,30,0,8,255,10,0,5,245,255,5,10,0,8,255,10,0,5,245,255,5,9,0,8,255,10,0,5,224,255,5,31,0,8,255,10,0,5,202,255,5,85,0,8,255,10,0,5,148,255,5,168,0,12,255,6,0,5,76,255,5,253,48,0,11,255,6,0,5,10,236,255,5,222,24,0,10,255,6,0,6,114,255,6,228,72,0,9,255,6,0,6,7,218,255,7,205,115,59,24,8,5,18,45,97,255,6,0,7,37,235,255,21,0,8,55,233,255,20,0,9,24,187,255,17,253,179,0,10,1,79,194,255,13,221,140,35,0,14,40,104,165,207,226,245,251,238,224,194,151,107,41,0,255,0,22,
43,30,0,243,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,24,0,6,255,24,0,6,255,24,0,6,255,24,0,6,255,24,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,6,255,6,0,12,255,6,0,255,0,18,
43,13,0,107,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,121,
43,15,0,125,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,9,255,6,0,8,2,255,5,253,0,8,22,255,5,237,0,8,79,255,5,217,0,7,4,198,255,5,167,0,5,9,60,189,255,6,104,0,4,255,9,240,13,0,4,255,9,115,0,5,255,8,162,0,6,255,6,239,112,0,7,255,1,252,236,215,165,101,12,0,38,
43,29,0,235,255,6,0,9,38,223,255,6,254,115,0,4,255,6,0,8,54,235,255,6,250,91,0,5,255,6,0,7,73,244,255,6,243,70,0,6,255,6,0,6,95,251,255,6,234,52,0,7,255,6,0,5,120,255,7,222,37,0,8,255,6,0,3,1,145,255,7,207,24,0,9,255,6,0,2,6,168,255,7,190,14,0,10,255,6,0,1,14,189,255,7,170,6,0,11,255,6,24,207,255,7,147,2,0,12,255,6,222,255,7,122,0,14,255,12,251,97,0,15,255,11,245,76,0,16,255,11,169,1,0,16,255,12,157,3,0,15,255,13,171,6,0,14,255,6,248,255,7,184,10,0,13,255,6,66,243,255,7,197,16,0,12,255,6,0,1,53,236,255,7,208,23,0,11,255,6,0,2,41,227,255,7,218,31,0,10,255,6,0,3,31,217,255,7,227,40,0,9,255,6,0,4,22,206,255,7,234,50,0,8,255,6,0,5,14,193,255,7,241,61,0,7,255,6,0,6,8,178,255,7,246,74,0,6,255,6,0,7,4,162,255,7,250,88,0,5,255,6,0,8,1,145,255,7,253,103,0,4,255,6,0,10,127,255,8,119,0,255,0,6,
43,23,0,187,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,6,0,17,255,19,0,4,255,19,0,4,255,19,0,4,255,19,0,4,255,19,0,208,
43,36,0,255,0,36,255,7,121,0,13,121,255,7,0,7,255,7,229,5,0,11,6,230,255,7,0,7,255,8,94,0,11,95,255,8,0,7,255,8,208,0,11,209,255,8,0,7,255,9,68,0,9,69,255,9,0,7,255,9,183,0,9,184,255,9,0,7,255,10,43,0,7,44,255,10,0,7,255,6,197,255,3,157,0,7,158,255,3,196,255,6,0,7,255,6,82,255,3,248,23,0,5,23,248,255,3,81,255,6,0,7,255,6,2,221,255,3,130,0,5,131,255,3,220,2,255,6,0,7,255,6,0,1,109,255,3,235,9,0,3,9,236,255,3,108,0,1,255,6,0,7,255,6,0,1,11,238,255,3,104,0,3,105,255,3,238,11,0,1,255,6,0,7,255,6,0,2,135,255,3,217,1,0,1,2,218,255,3,134,0,2,255,6,0,7,255,6,0,2,26,250,255,3,78,0,1,79,255,3,250,26,0,2,255,6,0,7,255,6,0,3,162,255,3,193,0,1,194,255,3,161,0,3,255,6,0,7,255,6,0,3,47,255,4,105,255,4,47,0,3,255,6,0,7,255,6,0,4,188,255,3,253,255,3,187,0,4,255,6,0,7,255,6,0,4,74,255,7,73,0,4,255,6,0,7,255,6,0,4,1,214,255,5,213,1,0,4,255,6,0,7,255,6,0,5,100,255,5,99,0,5,255,6,0,7,255,6,0,5,8,233,255,3,233,7,0,5,255,6,0,7,255,6,0,17,255,6,0,7,255,6,0,17,255,6,0,7,255,6,0,17,255,6,0,7,255,6,0,17,255,6,0,7,255,6,0,17,255,6,0,255,0,73,
43,30,0,243,255,7,139,0,10,255,6,0,6,255,7,246,26,0,9,255,6,0,6,255,8,152,0,9,255,6,0,6,255,8,251,35,0,8,255,6,0,6,255,9,165,0,8,255,6,0,6,255,9,254,46,0,7,255,6,0,6,255,6,207,255,3,179,0,7,255,6,0,6,255,6,71,255,4,57,0,6,255,6,0,6,255,6,0,1,188,255,3,192,0,6,255,6,0,6,255,6,0,1,51,254,255,3,71,0,5,255,6,0,6,255,6,0,2,168,255,3,204,1,0,4,255,6,0,6,255,6,0,2,35,250,255,3,84,0,4,255,6,0,6,255,6,0,3,147,255,3,215,3,0,3,255,6,0,6,255,6,0,3,22,242,255,3,97,0,3,255,6,0,6,255,6,0,4,127,255,3,225,7,0,2,255,6,0,6,255,6,0,4,12,232,255,3,111,0,2,255,6,0,6,255,6,0,5,106,255,3,233,12,0,1,255,6,0,6,255,6,0,5,4,219,255,3,124,0,1,255,6,0,6,255,6,0,6,85,255,3,240,18,255,6,0,6,255,6,0,7,202,255,3,137,255,6,0,6,255,6,0,7,65,255,3,246,255,6,0,6,255,6,0,8,183,255,9,0,6,255,6,0,8,46,253,255,8,0,6,255,6,0,9,162,255,8,0,6,255,6,0,9,31,248,255,7,0,6,255,6,0,10,141,255,7,0,255,0,18,
43,31,0,255,0,2,9,73,136,193,218,236,251,236,218,194,136,73,10,0,16,17,125,235,255,11,235,125,17,0,13,63,231,255,15,231,62,0,11,101,252,255,17,251,101,0,9,71,252,255,19,252,69,0,7,23,240,255,6,232,126,55,20,5,21,56,127,233,255,6,239,22,0,6,142,255,6,163,10,0,7,11,167,255,6,141,0,5,19,246,255,5,169,0,10,1,173,255,5,246,19,0,4,89,255,5,240,16,0,11,17,242,255,5,87,0,4,156,255,5,143,0,13,145,255,5,154,0,4,206,255,5,73,0,13,75,255,5,204,0,4,226,255,5,26,0,13,28,255,5,225,0,4,246,255,5,8,0,13,10,255,5,245,0,4,246,255,5,8,0,13,8,255,5,246,0,4,227,255,5,26,0,13,27,255,5,225,0,4,206,255,5,72,0,13,74,255,5,205,0,4,158,255,5,142,0,13,144,255,5,156,0,4,90,255,5,239,14,0,11,16,241,255,5,88,0,4,20,247,255,5,166,0,11,170,255,5,247,20,0,5,144,255,6,160,9,0,7,10,164,255,6,143,0,6,24,241,255,6,230,124,54,19,4,19,55,125,232,255,6,240,23,0,7,74,253,255,19,252,73,0,9,105,252,255,17,252,104,0,11,66,233,255,15,233,66,0,13,18,128,236,255,11,236,128,19,0,16,10,75,138,195,219,238,251,238,220,195,138,75,10,0,255,0,33,
43,26,0,211,255,12,243,227,195,144,73,2,0,8,255,17,225,85,0,7,255,19,152,1,0,5,255,20,129,0,5,255,20,249,35,0,4,255,6,0,5,9,38,101,213,255,6,132,0,4,255,6,0,8,7,184,255,5,202,0,4,255,6,0,9,44,255,5,233,0,4,255,6,0,9,7,255,5,250,0,4,255,6,0,9,45,255,5,233,0,4,255,6,0,8,7,185,255,5,202,0,4,255,6,0,5,8,37,100,213,255,6,131,0,4,255,20,249,35,0,4,255,20,129,0,5,255,19,152,1,0,5,255,17,226,87,0,7,255,12,244,229,197,146,73,2,0,8,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,251,
43,31,0,255,0,2,9,73,136,193,218,236,251,237,219,195,139,78,12,0,16,17,125,235,255,11,239,134,23,0,13,63,231,255,15,238,75,0,11,101,252,255,17,254,118,0,9,71,252,255,19,254,85,0,7,23,240,255,6,232,126,55,20,5,21,56,127,233,255,6,246,30,0,6,142,255,6,163,10,0,7,11,167,255,6,153,0,5,19,246,255,5,169,0,10,1,173,255,5,250,24,0,4,88,255,5,240,16,0,11,17,242,255,5,93,0,4,156,255,5,143,0,13,145,255,5,160,0,4,205,255,5,73,0,13,75,255,5,207,0,4,226,255,5,26,0,13,28,255,5,226,0,4,246,255,5,8,0,13,10,255,5,246,0,4,246,255,5,7,0,13,8,255,5,248,0,4,227,255,5,24,0,13,27,255,5,235,0,4,207,255,5,68,0,13,74,255,5,208,0,4,160,255,5,134,0,13,144,255,5,166,0,4,92,255,5,233,9,0,11,16,241,255,5,104,0,4,22,248,255,5,150,0,11,170,255,5,253,28,0,5,148,255,6,142,5,0,7,10,164,255,6,175,0,6,27,243,255,6,223,117,50,18,4,19,55,125,232,255,6,252,49,0,7,79,253,255,20,136,0,9,108,253,255,18,177,3,0,10,67,233,255,16,156,6,0,12,18,126,233,255,12,229,78,0,16,8,71,132,190,217,235,251,255,6,241,36,0,23,78,253,255,5,207,8,0,23,108,255,6,154,0,24,140,255,6,91,0,23,1,170,255,5,244,40,0,23,8,196,255,5,212,10,0,127,
43,28,0,227,255,10,251,240,226,191,147,74,7,0,11,255,16,227,90,0,10,255,18,121,0,9,255,18,252,50,0,8,255,19,154,0,8,255,6,0,3,2,14,49,127,247,255,5,219,0,8,255,6,0,7,93,255,5,244,0,8,255,6,0,7,12,255,5,248,0,8,255,6,0,7,14,255,5,222,0,8,255,6,0,7,97,255,5,162,0,8,255,6,0,3,1,13,49,129,248,255,5,58,0,8,255,18,143,0,9,255,16,248,123,1,0,9,255,15,231,51,0,11,255,16,242,66,0,10,255,17,244,38,0,9,255,6,0,2,7,33,95,208,255,6,186,0,9,255,6,0,5,6,183,255,6,62,0,8,255,6,0,6,19,234,255,5,179,0,8,255,6,0,7,112,255,5,254,37,0,7,255,6,0,7,12,240,255,5,148,0,7,255,6,0,8,140,255,5,244,16,0,6,255,6,0,8,31,252,255,5,117,0,6,255,6,0,9,170,255,5,225,3,0,5,255,6,0,9,57,255,6,86,0,5,255,6,0,10,200,255,5,199,0,254,
43,26,0,215,27,115,172,217,237,250,249,239,221,193,160,115,68,13,0,10,21,159,253,255,12,252,203,0,8,34,224,255,16,0,7,5,212,255,17,0,7,101,255,18,0,7,186,255,5,243,127,51,15,3,12,30,72,121,190,250,255,2,0,7,231,255,5,73,0,9,15,103,205,0,7,250,255,5,11,0,19,236,255,5,97,0,19,188,255,5,253,172,91,34,1,0,15,96,255,9,244,206,166,120,59,6,0,10,3,192,255,13,239,155,42,0,9,16,182,255,14,253,143,5,0,8,1,84,204,255,14,164,0,11,40,111,171,219,253,255,10,71,0,14,9,51,106,174,248,255,6,170,0,18,22,195,255,5,226,0,19,40,255,5,248,0,5,193,67,0,12,12,255,5,241,0,5,255,2,199,97,13,0,9,110,255,5,217,0,5,255,4,245,176,117,61,31,11,3,21,67,159,254,255,5,158,0,5,255,20,70,0,5,255,19,181,0,6,255,18,201,15,0,6,62,145,225,255,13,243,126,8,0,10,33,93,144,181,216,235,247,252,241,227,195,151,85,9,0,240,
43,25,0,200,255,24,0,1,255,24,0,1,255,24,0,1,255,24,0,1,255,24,0,10,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,235,
43,29,0,235,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,255,6,0,11,255,6,0,6,250,255,5,4,0,9,4,255,5,249,0,6,233,255,5,20,0,9,21,255,5,232,0,6,213,255,5,63,0,9,65,255,5,212,0,6,164,255,5,146,0,9,149,255,5,162,0,6,101,255,5,249,57,0,7,60,250,255,5,101,0,6,18,244,255,5,247,139,56,15,3,15,57,141,248,255,5,243,18,0,7,139,255,19,138,0,8,9,205,255,17,203,9,0,9,26,201,255,15,201,26,0,11,5,125,234,255,11,233,125,5,0,14,7,84,144,198,226,241,252,241,225,197,144,84,6,0,255,0,14,
43,28,0,224,207,255,5,156,0,14,157,255,5,207,110,255,5,242,10,0,12,11,243,255,5,109,19,249,255,5,94,0,12,95,255,5,249,19,0,1,171,255,5,192,0,12,193,255,5,170,0,2,74,255,5,254,34,0,10,35,254,255,5,73,0,2,3,229,255,5,130,0,10,131,255,5,228,3,0,3,135,255,5,225,2,0,8,2,226,255,5,134,0,4,38,255,6,69,0,8,70,255,6,37,0,5,196,255,5,166,0,8,167,255,5,195,0,6,99,255,5,247,16,0,6,17,247,255,5,98,0,6,13,244,255,5,105,0,6,106,255,5,244,12,0,7,160,255,5,202,0,6,203,255,5,159,0,8,63,255,6,43,0,4,44,255,6,62,0,8,1,220,255,5,141,0,4,142,255,5,220,1,0,9,124,255,5,233,4,0,2,5,234,255,5,123,0,10,29,253,255,5,79,0,2,80,255,5,253,28,0,11,185,255,5,177,0,2,178,255,5,184,0,12,88,255,5,251,23,23,251,255,5,87,0,12,8,238,255,5,115,116,255,5,238,7,0,13,149,255,5,212,213,255,5,148,0,14,52,255,12,51,0,15,210,255,10,209,0,16,113,255,10,112,0,16,21,250,255,8,250,21,0,17,174,255,8,173,0,18,77,255,8,76,0,255,0,6,
43,40,0,255,0,66,225,255,5,75,0,8,103,255,6,114,0,8,75,255,5,225,0,2,163,255,5,137,0,8,165,255,6,176,0,8,137,255,5,163,0,2,101,255,5,199,0,8,226,255,6,236,1,0,7,199,255,5,101,0,2,38,255,5,250,10,0,6,32,255,3,245,240,255,3,44,0,6,10,250,255,5,39,0,3,231,255,5,68,0,6,94,255,3,188,181,255,3,105,0,6,66,255,5,233,0,4,170,255,5,130,0,6,156,255,3,127,119,255,3,167,0,6,128,255,5,172,0,4,107,255,5,192,0,6,217,255,3,65,57,255,3,228,0,6,190,255,5,110,0,4,45,255,5,247,7,0,4,23,255,3,250,9,5,246,255,3,35,0,4,5,246,255,5,49,0,4,1,237,255,5,61,0,4,85,255,3,198,0,2,190,255,3,96,0,4,57,255,5,240,2,0,5,176,255,5,123,0,4,146,255,3,137,0,2,128,255,3,158,0,4,119,255,5,181,0,6,114,255,5,185,0,4,208,255,3,76,0,2,66,255,3,220,0,4,181,255,5,120,0,6,52,255,5,243,4,0,2,16,253,255,2,253,16,0,2,10,250,255,3,26,0,2,2,240,255,5,58,0,6,3,242,255,5,54,0,2,75,255,3,209,0,4,199,255,3,87,0,2,48,255,5,246,6,0,7,183,255,5,116,0,2,137,255,3,147,0,4,137,255,3,149,0,2,110,255,5,191,0,8,121,255,5,178,0,2,199,255,3,86,0,4,75,255,3,211,0,2,172,255,5,129,0,8,59,255,5,238,2,9,250,255,3,24,0,4,15,253,255,2,254,18,0,1,233,255,5,67,0,8,6,246,255,5,47,66,255,3,219,0,6,207,255,3,79,40,255,5,250,10,0,9,190,255,5,109,128,255,3,157,0,6,146,255,3,140,101,255,5,200,0,10,128,255,5,171,189,255,3,96,0,6,84,255,3,202,163,255,5,138,0,10,65,255,5,234,245,255,3,35,0,6,22,255,3,251,230,255,5,76,0,10,9,249,255,8,229,0,8,216,255,8,253,17,0,11,197,255,8,168,0,8,154,255,8,209,0,12,134,255,8,106,0,8,93,255,8,147,0,12,72,255,8,45,0,8,31,255,8,86,0,12,13,252,255,6,237,1,0,9,225,255,7,24,0,13,203,255,6,178,0,10,163,255,6,218,0,255,0,112,
43,28,0,225,167,255,6,168,0,10,169,255,6,166,0,2,17,228,255,6,91,0,8,92,255,6,227,16,0,3,68,254,255,5,240,29,0,6,30,240,255,5,254,67,0,5,144,255,6,190,1,0,4,2,191,255,6,143,0,6,8,213,255,6,115,0,4,116,255,6,212,8,0,7,48,250,255,5,248,44,0,2,45,248,255,5,249,48,0,9,121,255,6,209,6,6,209,255,6,120,0,10,2,195,255,6,138,139,255,6,195,2,0,11,33,242,255,5,253,253,255,5,242,32,0,13,97,255,12,96,0,15,174,255,10,174,0,16,20,232,255,8,231,20,0,17,86,255,8,86,0,18,163,255,8,162,0,17,78,255,10,78,0,15,18,231,255,10,231,18,0,14,165,255,12,164,0,13,80,255,6,218,219,255,6,79,0,11,19,232,255,5,253,60,60,253,255,5,231,18,0,10,167,255,6,142,0,2,143,255,6,166,0,9,82,255,6,217,9,0,2,9,218,255,6,81,0,7,20,233,255,5,253,57,0,4,60,253,255,5,232,19,0,6,169,255,6,139,0,6,142,255,6,168,0,5,84,255,6,215,8,0,6,9,218,255,6,83,0,3,21,234,255,5,252,55,0,8,59,253,255,5,233,20,0,2,170,255,6,137,0,10,141,255,6,170,0,253,
43,26,0,208,171,255,6,136,0,10,137,255,6,170,21,235,255,5,252,53,0,8,54,252,255,5,234,21,0,1,87,255,6,212,6,0,6,7,213,255,6,86,0,3,173,255,6,134,0,6,135,255,6,172,0,4,22,236,255,5,251,51,0,4,52,252,255,5,235,22,0,5,89,255,6,211,6,0,2,6,212,255,6,88,0,7,175,255,6,132,0,2,133,255,6,174,0,8,23,236,255,5,251,50,51,251,255,5,236,23,0,9,90,255,6,210,210,255,6,89,0,11,176,255,12,175,0,12,24,237,255,10,237,23,0,13,92,255,10,91,0,15,178,255,8,177,0,16,25,238,255,6,238,24,0,17,93,255,6,92,0,19,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,244,
43,26,0,210,255,23,0,3,255,23,0,3,255,23,0,3,255,23,0,3,255,22,163,0,16,50,245,255,6,195,6,0,15,27,229,255,6,221,20,0,15,10,205,255,6,239,40,0,15,1,174,255,6,251,67,0,16,136,255,7,101,0,16,95,255,7,140,0,16,61,249,255,6,177,2,0,15,34,235,255,6,206,11,0,15,15,214,255,6,229,27,0,15,4,186,255,6,245,50,0,16,150,255,6,253,80,0,16,110,255,7,117,0,16,73,252,255,6,155,0,16,43,241,255,6,189,5,0,15,22,223,255,6,216,17,0,15,7,197,255,6,236,36,0,16,164,255,22,0,3,255,23,0,3,255,23,0,3,255,23,0,3,255,23,0,235,
43,16,0,115,255,11,0,5,255,11,0,5,255,11,0,5,255,11,0,5,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,11,0,5,255,11,0,5,255,11,0,5,255,11,0,66,
43,13,0,104,217,255,2,245,9,0,8,139,255,3,77,0,8,61,255,3,155,0,8,3,235,255,2,231,1,0,8,161,255,3,55,0,8,83,255,3,133,0,8,12,248,255,2,211,0,9,183,255,3,33,0,8,105,255,3,112,0,8,27,255,3,190,0,9,205,255,2,251,16,0,8,127,255,3,90,0,8,49,255,3,168,0,9,226,255,2,240,5,0,8,149,255,3,68,0,8,71,255,3,147,0,8,6,242,255,2,224,0,9,171,255,3,47,0,8,93,255,3,125,0,8,18,252,255,2,203,0,9,193,255,2,254,26,0,8,115,255,3,103,0,8,37,255,3,181,0,9,215,255,2,248,11,0,8,137,255,3,82,0,8,59,255,3,160,0,8,2,234,255,2,235,2,0,8,159,255,3,60,0,8,81,255,3,138,0,8,11,247,255,2,216,0,65,
43,16,0,114,255,11,0,5,255,11,0,5,255,11,0,5,255,11,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,5,255,11,0,5,255,11,0,5,255,11,0,5,255,11,0,67,
43,30,0,252,50,241,255,3,241,49,0,22,39,234,255,5,234,39,0,20,30,226,255,7,226,30,0,18,22,217,255,9,217,22,0,16,15,207,255,5,200,255,5,206,15,0,14,9,195,255,4,242,88,0,1,87,242,255,4,195,9,0,12,5,182,255,4,207,37,0,3,36,205,255,4,181,5,0,10,2,168,255,4,151,7,0,5,7,148,255,4,167,2,0,9,153,255,3,241,86,0,9,83,240,255,3,152,0,8,137,255,3,206,36,0,11,33,202,255,3,136,0,255,0,255,0,243,
43,18,0,255,0,255,0,192,255,54,0,18,
43,18,0,92,137,255,4,95,0,13,154,255,3,242,34,0,12,2,169,255,3,198,3,0,12,5,182,255,3,129,0,13,9,195,255,2,252,59,0,13,15,207,255,2,223,14,0,13,22,217,255,2,164,0,255,0,255,0,54,
43,24,0,255,0,85,23,71,119,167,202,228,243,252,247,234,216,173,122,36,0,10,255,14,180,31,0,8,255,15,235,39,0,7,255,16,211,2,0,6,255,2,201,117,64,26,10,4,19,57,138,249,255,5,84,0,6,185,47,0,9,90,255,5,161,0,17,8,255,5,216,0,7,10,80,150,192,226,240,251,255,9,238,0,6,107,234,255,15,253,0,5,147,255,18,0,4,71,255,19,0,4,173,255,5,241,119,44,11,0,3,1,255,6,0,4,228,255,5,72,0,6,25,255,6,0,4,249,255,5,9,0,6,108,255,6,0,4,236,255,5,67,0,5,46,239,255,6,0,4,185,255,5,231,93,25,5,35,122,244,255,7,0,4,85,255,19,0,5,174,255,11,124,255,6,0,5,8,160,255,8,238,99,0,1,255,6,0,7,57,158,219,245,247,226,189,115,21,0,2,255,6,0,218,
43,26,0,185,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,2,21,116,194,229,249,232,194,113,14,0,9,255,6,0,1,98,238,255,7,229,73,0,8,255,6,124,255,10,251,84,0,7,255,18,245,38,0,6,255,19,189,0,6,255,7,229,99,26,5,25,96,227,255,6,48,0,5,255,6,234,29,0,5,27,233,255,5,134,0,5,255,6,113,0,7,113,255,5,193,0,5,255,6,38,0,7,38,255,5,228,0,5,255,6,7,0,7,8,255,5,246,0,5,255,6,7,0,7,8,255,5,246,0,5,255,6,38,0,7,38,255,5,228,0,5,255,6,112,0,7,113,255,5,193,0,5,255,6,233,27,0,5,27,233,255,5,134,0,5,255,7,227,98,25,4,24,95,226,255,6,48,0,5,255,19,189,0,6,255,18,245,38,0,6,255,6,125,255,10,251,84,0,7,255,6,0,1,100,240,255,7,230,74,0,8,255,6,0,2,23,120,195,231,250,233,195,114,14,0,240,
43,21,0,255,0,46,6,84,147,202,227,245,246,221,164,79,2,0,8,2,114,231,255,9,219,76,0,6,20,192,255,13,0,5,10,200,255,14,0,5,144,255,15,0,4,31,249,255,6,173,74,23,5,19,64,145,243,255,1,0,4,118,255,5,253,93,0,7,20,169,0,4,188,255,5,151,0,14,224,255,5,51,0,14,245,255,5,10,0,14,245,255,5,10,0,14,224,255,5,51,0,14,188,255,5,150,0,14,118,255,5,253,89,0,7,14,163,0,4,31,249,255,6,170,73,21,4,18,58,134,238,255,1,0,5,144,255,15,0,5,10,201,255,14,0,6,20,193,255,13,0,7,2,116,231,255,9,221,77,0,9,6,85,149,204,229,246,247,222,165,80,3,0,192,
43,26,0,199,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,9,14,113,193,232,249,230,194,116,21,0,2,255,6,0,8,75,230,255,7,238,98,0,1,255,6,0,7,87,251,255,10,123,255,6,0,6,41,247,255,18,0,6,192,255,19,0,5,50,255,6,227,97,25,5,26,98,228,255,7,0,5,136,255,5,233,27,0,5,27,233,255,6,0,5,195,255,5,112,0,7,113,255,6,0,5,230,255,5,37,0,7,38,255,6,0,5,247,255,5,7,0,7,8,255,6,0,5,247,255,5,7,0,7,8,255,6,0,5,230,255,5,37,0,7,38,255,6,0,5,195,255,5,112,0,7,113,255,6,0,5,136,255,5,233,27,0,5,27,233,255,6,0,5,50,255,6,227,96,24,4,24,97,227,255,7,0,6,193,255,19,0,6,41,247,255,18,0,7,87,251,255,10,125,255,6,0,8,76,231,255,7,240,101,0,1,255,6,0,9,14,114,195,233,250,231,196,120,23,0,2,255,6,0,237,
43,24,0,255,0,88,9,92,155,209,231,248,242,221,179,112,24,0,11,3,122,236,255,9,246,140,8,0,8,22,195,255,13,202,25,0,6,10,201,255,15,202,8,0,5,145,255,5,246,129,41,7,14,74,206,255,5,140,0,4,31,249,255,4,252,67,0,5,9,203,255,4,246,24,0,3,118,255,5,146,0,7,69,255,5,112,0,3,188,255,5,60,0,7,11,255,5,179,0,3,225,255,19,221,0,3,246,255,19,242,0,3,246,255,20,0,3,225,255,20,0,3,190,255,5,25,0,17,120,255,5,109,0,17,34,251,255,4,238,45,0,9,15,103,205,0,5,150,255,5,245,137,55,15,3,15,33,75,123,191,251,255,2,0,5,13,206,255,17,0,6,24,198,255,16,0,7,3,120,233,255,14,0,9,6,85,148,202,228,245,253,246,236,216,192,159,122,76,25,0,218,
43,16,0,118,29,129,194,233,249,255,5,0,5,90,245,255,9,0,4,49,252,255,10,0,4,163,255,11,0,4,225,255,5,133,19,0,8,249,255,5,12,0,9,255,6,0,7,255,14,0,2,255,14,0,2,255,14,0,2,255,14,0,2,255,14,0,5,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,10,255,6,0,150,
43,26,0,255,0,115,11,110,192,232,249,230,196,119,23,0,2,255,6,0,8,67,226,255,7,240,101,0,1,255,6,0,7,78,249,255,10,125,255,6,0,6,35,243,255,18,0,6,186,255,19,0,5,46,255,6,233,103,26,5,27,104,233,255,7,0,5,133,255,5,238,35,0,5,35,239,255,6,0,5,193,255,5,119,0,7,119,255,6,0,5,229,255,5,40,0,7,41,255,6,0,5,247,255,5,8,0,7,9,255,6,0,5,247,255,5,7,0,7,8,255,6,0,5,229,255,5,36,0,7,40,255,6,0,5,192,255,5,108,0,7,118,255,6,0,5,132,255,5,229,23,0,5,33,237,255,6,0,5,45,255,6,224,94,23,4,26,102,232,255,7,0,6,185,255,19,0,6,35,243,255,18,0,7,78,249,255,10,124,255,6,0,8,68,226,255,7,240,100,5,255,5,248,0,9,12,110,193,233,250,231,196,120,23,0,1,26,255,5,227,0,19,79,255,5,198,0,19,183,255,5,129,0,7,189,58,0,9,120,255,5,254,45,0,7,255,2,209,120,61,22,5,10,36,92,193,255,6,160,0,8,255,16,212,19,0,8,255,15,200,24,0,9,255,13,225,110,2,0,10,32,95,146,189,219,241,251,250,236,221,184,133,68,2,0,34,
43,26,0,185,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,2,7,96,177,223,247,240,212,147,40,0,9,255,6,0,1,74,222,255,7,252,119,0,8,255,6,112,253,255,10,104,0,7,255,18,244,18,0,6,255,19,111,0,6,255,7,249,133,40,7,26,121,251,255,5,178,0,6,255,6,254,74,0,5,121,255,5,224,0,6,255,6,160,0,6,42,255,5,242,0,6,255,6,67,0,6,14,255,6,0,6,255,6,19,0,6,4,255,6,0,6,255,6,2,0,6,1,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,237,
43,12,0,87,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,30,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,111,
43,13,0,95,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,33,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,7,255,6,0,6,6,255,5,246,0,6,27,255,5,229,0,6,86,255,5,186,0,4,8,64,222,255,5,123,0,3,255,8,249,26,0,3,255,8,121,0,4,255,6,254,137,1,0,4,255,3,245,219,157,52,0,19,
43,25,0,178,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,19,255,6,0,6,38,228,255,6,118,0,4,255,6,0,5,40,230,255,5,252,100,0,5,255,6,0,4,43,232,255,5,249,83,0,6,255,6,0,3,45,234,255,5,243,68,0,7,255,6,0,2,47,235,255,5,236,54,0,8,255,6,0,1,50,237,255,5,228,42,0,9,255,6,53,239,255,5,218,31,0,10,255,6,240,255,5,207,22,0,11,255,11,194,15,0,12,255,11,116,0,13,255,12,110,0,12,255,6,241,255,6,112,0,11,255,6,60,244,255,6,114,0,10,255,6,0,1,66,247,255,6,115,0,9,255,6,0,2,74,249,255,6,117,0,8,255,6,0,3,82,251,255,6,119,0,7,255,6,0,4,89,253,255,6,121,0,6,255,6,0,5,98,254,255,6,123,0,5,255,6,0,6,106,255,7,125,0,4,255,6,0,7,115,255,7,127,0,225,
43,12,0,87,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,6,255,6,0,111,
43,38,0,255,0,255,0,25,255,6,0,2,30,128,206,237,244,214,156,52,0,5,48,149,212,243,242,216,151,45,0,9,255,6,0,1,109,244,255,7,142,4,0,1,8,147,255,7,253,124,0,8,255,6,130,255,10,149,5,189,255,10,108,0,7,255,17,254,193,255,11,245,19,0,6,255,31,112,0,6,255,7,228,90,18,15,77,233,255,7,229,91,18,16,83,236,255,5,179,0,6,255,6,242,35,0,4,99,255,6,245,37,0,4,93,255,5,224,0,6,255,6,134,0,5,40,255,6,139,0,5,20,255,5,243,0,6,255,6,55,0,5,13,255,6,60,0,6,252,255,5,0,6,255,6,15,0,5,4,255,6,18,0,6,252,255,5,0,6,255,6,2,0,6,255,6,2,0,6,255,6,0,6,255,6,0,7,255,6,0,7,255,6,0,6,255,6,0,7,255,6,0,7,255,6,0,6,255,6,0,7,255,6,0,7,255,6,0,6,255,6,0,7,255,6,0,7,255,6,0,6,255,6,0,7,255,6,0,7,255,6,0,6,255,6,0,7,255,6,0,7,255,6,0,6,255,6,0,7,255,6,0,7,255,6,0,6,255,6,0,7,255,6,0,7,255,6,0,6,255,6,0,7,255,6,0,7,255,6,0,255,0,90,
43,26,0,255,0,112,255,6,0,2,7,96,177,223,247,240,212,147,40,0,9,255,6,0,1,74,222,255,7,252,119,0,8,255,6,112,253,255,10,104,0,7,255,18,244,18,0,6,255,19,111,0,6,255,7,249,133,40,7,26,121,251,255,5,178,0,6,255,6,254,74,0,5,121,255,5,224,0,6,255,6,160,0,6,42,255,5,242,0,6,255,6,67,0,6,14,255,6,0,6,255,6,19,0,6,4,255,6,0,6,255,6,2,0,6,1,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,237,
43,25,0,255,0,102,6,86,149,204,228,246,247,229,206,151,89,8,0,11,2,116,232,255,10,233,119,3,0,8,20,192,255,14,194,22,0,6,9,200,255,16,201,9,0,5,144,255,18,145,0,4,31,249,255,5,244,127,44,10,8,42,126,243,255,5,249,31,0,3,118,255,5,243,48,0,6,48,244,255,5,117,0,3,188,255,5,126,0,8,126,255,5,187,0,3,225,255,5,42,0,8,43,255,5,223,0,3,246,255,5,8,0,8,9,255,5,245,0,3,246,255,5,8,0,8,9,255,5,245,0,3,225,255,5,42,0,8,43,255,5,223,0,3,188,255,5,125,0,8,125,255,5,187,0,3,118,255,5,243,46,0,6,45,243,255,5,117,0,3,31,249,255,5,243,125,43,8,8,41,123,242,255,5,249,31,0,4,145,255,18,145,0,5,10,201,255,16,201,10,0,6,20,192,255,14,195,22,0,8,2,117,232,255,10,234,120,3,0,11,7,86,150,205,230,247,248,231,207,153,90,8,0,231,
43,26,0,255,0,112,255,6,0,2,21,116,194,229,249,232,194,113,14,0,9,255,6,0,1,98,238,255,7,229,73,0,8,255,6,124,255,10,251,84,0,7,255,18,245,38,0,6,255,19,189,0,6,255,7,229,99,26,5,25,96,227,255,6,48,0,5,255,6,234,29,0,5,27,233,255,5,134,0,5,255,6,113,0,7,113,255,5,193,0,5,255,6,38,0,7,38,255,5,228,0,5,255,6,7,0,7,8,255,5,246,0,5,255,6,7,0,7,8,255,5,246,0,5,255,6,38,0,7,38,255,5,228,0,5,255,6,112,0,7,113,255,5,193,0,5,255,6,233,27,0,5,27,233,255,5,134,0,5,255,7,227,98,25,4,24,95,226,255,6,48,0,5,255,19,189,0,6,255,18,245,38,0,6,255,6,125,255,10,251,84,0,7,255,6,0,1,100,240,255,7,230,74,0,8,255,6,0,2,23,120,195,231,250,233,195,114,14,0,9,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,43,
43,26,0,255,0,115,14,113,193,232,249,230,196,119,23,0,2,255,6,0,8,75,230,255,7,240,101,0,1,255,6,0,7,87,251,255,10,125,255,6,0,6,41,247,255,18,0,6,193,255,19,0,5,51,255,6,227,97,25,5,26,98,228,255,7,0,5,136,255,5,233,27,0,5,27,233,255,6,0,5,195,255,5,112,0,7,113,255,6,0,5,230,255,5,37,0,7,38,255,6,0,5,247,255,5,7,0,7,8,255,6,0,5,247,255,5,7,0,7,8,255,6,0,5,230,255,5,37,0,7,38,255,6,0,5,194,255,5,112,0,7,113,255,6,0,5,134,255,5,233,27,0,5,27,233,255,6,0,5,50,255,6,227,96,24,4,24,97,227,255,7,0,6,192,255,19,0,6,41,247,255,18,0,7,87,251,255,10,125,255,6,0,8,76,231,255,7,240,101,0,1,255,6,0,9,14,114,195,233,250,231,196,120,23,0,2,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,20,255,6,0,29,
43,18,0,255,255,6,0,3,57,156,211,239,255,1,252,0,3,255,6,0,1,15,177,255,5,252,0,3,255,6,12,204,255,6,253,0,3,255,6,166,255,7,254,0,3,255,14,254,0,3,255,8,188,77,21,5,27,89,191,0,3,255,7,139,0,10,255,6,204,2,0,10,255,6,97,0,11,255,6,36,0,11,255,6,9,0,11,255,6,0,12,255,6,0,12,255,6,0,12,255,6,0,12,255,6,0,12,255,6,0,12,255,6,0,12,255,6,0,12,255,6,0,171,
43,21,0,255,0,44,44,125,181,218,239,250,250,241,221,196,162,123,76,25,0,5,20,176,255,14,0,4,11,216,255,15,0,4,129,255,16,0,4,212,255,5,212,80,25,5,18,46,98,163,240,255,2,0,4,245,255,5,35,0,7,6,87,200,0,4,244,255,4,254,14,0,14,206,255,5,178,53,6,0,12,118,255,7,251,223,187,148,99,39,0,7,8,198,255,12,215,108,4,0,5,11,136,241,255,12,189,7,0,6,10,81,144,193,231,255,9,117,0,11,10,45,100,211,255,5,206,0,14,31,255,5,244,0,3,199,85,5,0,8,38,255,5,242,0,3,255,2,240,167,104,57,25,9,6,26,85,215,255,5,202,0,3,255,17,108,0,3,255,16,186,3,0,3,255,14,246,138,5,0,4,25,76,122,160,194,218,238,248,253,245,233,204,164,98,21,0,193,
43,17,0,157,255,6,0,11,255,6,0,11,255,6,0,11,255,6,0,11,255,6,0,8,255,15,0,2,255,15,0,2,255,15,0,2,255,15,0,2,255,15,0,5,255,6,0,11,255,6,0,11,255,6,0,11,255,6,0,11,255,6,0,11,255,6,0,11,255,6,0,11,255,6,0,11,247,255,5,23,0,10,232,255,5,153,26,2,0,8,192,255,11,0,5,129,255,11,0,5,31,246,255,10,0,6,89,249,255,9,0,7,38,144,207,240,253,255,5,0,154,
43,26,0,255,0,112,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,6,0,8,255,6,0,6,255,5,254,0,8,255,6,0,6,255,5,253,0,8,255,6,0,6,255,5,252,0,8,255,6,0,6,255,5,250,0,8,255,6,0,6,255,5,248,0,8,255,6,0,6,255,5,248,0,8,255,6,0,6,255,5,248,0,7,2,255,6,0,6,255,5,253,0,7,19,255,6,0,6,255,6,9,0,6,66,255,6,0,6,244,255,5,40,0,6,158,255,6,0,6,225,255,5,122,0,5,71,253,255,6,0,6,179,255,5,250,118,25,6,38,130,248,255,7,0,6,112,255,19,0,6,19,244,255,18,0,7,105,255,10,254,113,255,6,0,8,119,252,255,7,225,77,0,1,255,6,0,9,39,146,212,241,248,226,180,101,9,0,2,255,6,0,237,
43,23,0,255,0,68,207,255,5,156,0,8,157,255,5,207,0,1,110,255,5,242,10,0,6,11,243,255,5,109,0,1,19,249,255,5,94,0,6,95,255,5,249,19,0,2,172,255,5,191,0,6,192,255,5,170,0,3,75,255,5,254,33,0,4,34,254,255,5,73,0,3,3,230,255,5,129,0,4,130,255,5,228,3,0,4,136,255,5,224,2,0,2,2,225,255,5,134,0,5,40,255,6,68,0,2,69,255,6,37,0,6,198,255,5,165,0,2,166,255,5,195,0,7,101,255,5,246,15,16,247,255,5,98,0,7,14,245,255,5,103,104,255,5,244,12,0,8,162,255,5,200,201,255,5,159,0,9,65,255,12,62,0,9,1,223,255,10,219,0,11,127,255,10,123,0,11,31,254,255,8,253,28,0,12,188,255,8,184,0,13,91,255,8,87,0,13,9,241,255,6,238,7,0,14,153,255,6,148,0,214,
43,33,0,255,0,208,222,255,5,85,0,5,71,255,5,71,0,5,86,255,5,222,0,2,155,255,5,151,0,5,134,255,5,133,0,5,153,255,5,155,0,2,88,255,5,218,0,5,196,255,5,195,0,5,220,255,5,88,0,2,22,254,255,5,29,0,3,9,249,255,5,249,8,0,3,31,255,5,254,22,0,3,210,255,5,96,0,3,65,255,7,64,0,3,98,255,5,210,0,4,143,255,5,163,0,3,127,255,7,127,0,3,165,255,5,143,0,4,76,255,5,229,0,3,189,255,3,192,255,3,189,0,3,231,255,5,76,0,4,13,251,255,5,40,0,1,5,245,255,3,67,255,3,246,5,0,1,43,255,5,251,13,0,5,198,255,5,107,0,1,58,255,3,227,0,1,227,255,3,58,0,1,110,255,5,198,0,6,131,255,5,174,0,1,120,255,3,165,0,1,165,255,3,121,0,1,177,255,5,131,0,6,64,255,5,238,2,182,255,3,102,0,1,102,255,3,183,3,240,255,5,64,0,6,7,246,255,5,54,241,255,3,40,0,1,40,255,3,242,59,255,5,246,7,0,7,186,255,5,169,255,3,233,0,3,232,255,3,175,255,5,186,0,8,119,255,5,254,255,3,172,0,3,171,255,9,119,0,8,52,255,9,110,0,3,108,255,9,52,0,8,2,238,255,8,47,0,3,46,255,8,238,2,0,9,174,255,7,239,2,0,3,1,237,255,7,174,0,10,107,255,7,179,0,5,177,255,7,107,0,10,40,255,7,117,0,5,114,255,7,40,0,11,229,255,6,55,0,5,51,255,6,229,0,255,0,48,
43,23,0,255,0,68,160,255,6,210,9,0,4,10,213,255,6,160,0,1,11,214,255,6,154,0,4,160,255,6,213,10,0,2,44,246,255,6,89,0,2,96,255,6,245,42,0,4,99,255,6,242,37,42,245,255,6,96,0,6,163,255,6,207,213,255,6,160,0,7,12,216,255,12,213,10,0,8,46,247,255,10,245,42,0,10,102,255,10,96,0,12,166,255,8,160,0,13,13,228,255,6,227,10,0,13,61,252,255,6,253,64,0,12,15,224,255,8,227,17,0,11,167,255,10,170,0,10,94,255,12,97,0,8,34,242,255,6,254,255,5,243,35,0,6,3,198,255,6,150,144,255,6,200,4,0,5,129,255,6,214,9,7,210,255,6,131,0,4,59,252,255,5,248,47,0,2,44,247,255,5,252,60,0,2,14,223,255,6,113,0,4,110,255,6,223,15,0,1,165,255,6,185,1,0,4,1,182,255,6,165,0,207,
43,23,0,255,0,67,203,255,5,176,0,8,147,255,5,209,0,1,98,255,5,252,29,0,6,5,236,255,5,115,0,1,9,238,255,5,132,0,6,80,255,5,252,24,0,2,142,255,5,232,5,0,5,174,255,5,183,0,3,37,254,255,5,88,0,4,18,249,255,5,89,0,4,186,255,5,194,0,4,107,255,5,241,9,0,4,81,255,6,44,0,3,201,255,5,157,0,5,3,227,255,5,150,0,2,39,255,6,63,0,6,125,255,5,242,13,0,1,133,255,5,223,1,0,6,24,250,255,5,105,1,225,255,5,131,0,8,169,255,5,210,66,255,6,37,0,8,64,255,6,210,255,5,199,0,10,213,255,11,105,0,10,108,255,10,249,17,0,10,14,243,255,9,173,0,12,152,255,9,79,0,12,47,255,8,235,5,0,13,197,255,7,147,0,14,91,255,7,53,0,14,6,234,255,5,214,0,15,3,231,255,5,121,0,15,77,255,5,253,29,0,14,2,193,255,5,183,0,13,14,51,169,255,6,72,0,12,228,255,8,194,0,13,228,255,7,244,39,0,13,228,255,6,238,65,0,14,228,255,3,243,207,133,23,0,35,
43,21,0,255,0,41,255,18,0,3,255,18,0,3,255,18,0,3,255,18,0,3,255,17,209,0,11,12,203,255,6,232,30,0,10,5,184,255,6,247,55,0,10,1,163,255,7,88,0,11,140,255,7,128,0,11,115,255,7,167,0,11,91,254,255,6,200,8,0,10,70,250,255,6,225,23,0,10,51,243,255,6,243,46,0,10,36,233,255,6,253,77,0,10,23,221,255,7,115,0,11,206,255,17,0,3,255,18,0,3,255,18,0,3,255,18,0,3,255,18,0,190,
43,26,0,194,13,104,179,225,244,255,4,0,16,54,227,255,8,0,15,20,234,255,9,0,15,127,255,10,0,15,199,255,5,228,85,23,4,0,16,238,255,5,89,0,19,253,255,5,27,0,19,255,6,5,0,19,255,6,0,20,255,6,0,19,3,255,6,0,19,18,255,5,251,0,19,61,255,5,237,0,19,155,255,5,200,0,16,7,42,143,255,6,128,0,15,255,9,217,17,0,15,255,7,234,136,17,0,16,255,7,254,193,52,0,16,255,9,245,41,0,16,8,40,133,252,255,5,156,0,19,138,255,5,217,0,19,47,255,5,245,0,19,11,255,5,254,0,20,255,6,0,20,255,6,0,20,255,6,3,0,19,254,255,5,21,0,19,240,255,5,77,0,19,206,255,5,221,77,21,3,0,16,136,255,10,0,15,25,240,255,9,0,16,62,232,255,8,0,17,16,110,182,226,245,255,4,0,83,
43,13,0,83,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,9,255,4,0,17,
43,26,0,187,255,4,244,224,179,104,13,0,17,255,8,226,54,0,16,255,9,234,19,0,15,255,10,126,0,16,4,23,85,229,255,5,199,0,19,90,255,5,237,0,19,28,255,5,253,0,19,7,255,6,0,20,255,6,0,20,255,6,0,20,255,6,3,0,19,252,255,5,17,0,19,238,255,5,59,0,19,201,255,5,151,0,19,129,255,5,254,141,42,7,0,16,17,218,255,9,0,16,17,135,234,255,7,0,16,51,192,254,255,7,0,15,41,244,255,9,0,15,157,255,5,252,132,39,8,0,16,218,255,5,135,0,19,246,255,5,46,0,19,255,6,9,0,19,255,6,0,20,255,6,0,19,3,255,6,0,19,22,255,5,254,0,19,78,255,5,239,0,16,3,21,77,221,255,5,206,0,15,255,10,135,0,15,255,9,240,25,0,15,255,8,232,62,0,16,255,4,245,226,182,110,16,0,90,
43,30,0,255,0,255,0,56,33,0,9,20,113,181,228,248,238,212,162,95,25,0,8,10,121,246,0,7,21,150,248,255,8,253,198,124,65,21,4,21,63,136,235,255,2,0,7,241,255,22,0,7,255,22,241,0,7,255,2,249,157,72,25,5,23,65,126,199,253,255,8,249,152,22,0,7,255,1,179,34,0,8,25,96,163,212,239,249,229,184,117,22,0,9,113,0,255,0,255,0,25,
43,22,0,200,255,18,0,4,255,18,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,2,0,14,255,2,0,4,255,18,0,4,255,18,0,46,
43,18,0,131,9,116,204,243,243,198,106,6,0,9,31,212,255,6,207,28,0,7,10,213,255,8,208,7,0,6,117,255,2,249,117,24,24,119,250,255,2,108,0,6,205,255,2,117,0,4,120,255,2,199,0,6,245,255,2,24,0,4,25,255,2,243,0,6,245,255,2,22,0,4,23,255,2,243,0,6,208,255,2,111,0,4,114,255,2,201,0,6,123,255,2,247,112,23,23,114,248,255,2,112,0,6,12,219,255,8,213,9,0,7,37,219,255,6,212,32,0,9,12,123,208,245,244,201,111,7,0,255,0,182,
43,24,0,134,95,255,4,136,0,17,34,242,255,3,153,0,17,4,199,255,3,168,2,0,17,130,255,3,182,5,0,17,60,252,255,2,194,9,0,17,15,223,255,2,206,15,0,18,165,255,2,217,21,0,64,9,92,155,209,231,248,242,221,179,112,24,0,11,3,122,236,255,9,246,140,8,0,8,22,195,255,13,202,25,0,6,10,201,255,15,202,8,0,5,145,255,5,246,129,41,7,14,74,206,255,5,140,0,4,31,249,255,4,252,67,0,5,9,203,255,4,246,24,0,3,118,255,5,146,0,7,69,255,5,112,0,3,188,255,5,60,0,7,11,255,5,179,0,3,225,255,19,221,0,3,246,255,19,242,0,3,246,255,20,0,3,225,255,20,0,3,190,255,5,25,0,17,120,255,5,109,0,17,34,251,255,4,238,45,0,9,15,103,205,0,5,150,255,5,245,137,55,15,3,15,33,75,123,191,251,255,2,0,5,13,206,255,17,0,6,24,198,255,16,0,7,3,120,233,255,14,0,9,6,85,148,202,228,245,253,246,236,216,192,159,122,76,25,0,218,
43,24,0,133,95,255,4,136,0,17,34,242,255,3,153,0,17,4,199,255,3,168,2,0,17,130,255,3,182,5,0,17,60,252,255,2,194,9,0,17,15,223,255,2,206,15,0,18,165,255,2,217,21,0,62,23,71,119,167,202,228,243,252,247,234,216,173,122,36,0,10,255,14,180,31,0,8,255,15,235,39,0,7,255,16,211,2,0,6,255,2,201,117,64,26,10,4,19,57,138,249,255,5,84,0,6,185,47,0,9,90,255,5,161,0,17,8,255,5,216,0,7,10,80,150,192,226,240,251,255,9,238,0,6,107,234,255,15,253,0,5,147,255,18,0,4,71,255,19,0,4,173,255,5,241,119,44,11,0,3,1,255,6,0,4,228,255,5,72,0,6,25,255,6,0,4,249,255,5,9,0,6,108,255,6,0,4,236,255,5,67,0,5,46,239,255,6,0,4,185,255,5,231,93,25,5,35,122,244,255,7,0,4,85,255,19,0,5,174,255,11,124,255,6,0,5,8,160,255,8,238,99,0,1,255,6,0,7,57,158,219,245,247,226,189,115,21,0,2,255,6,0,218,
};
unsigned char *ftv = fonttable;
/* fonttable is obtained with:
if(ch >= 32) {
printf("%d,%d,", txt->h, txt->w);
int rle_last = -1, rle_q = 0;
for(int j=0; j <txt->h;j++) {
for(int i=0; i < txt->w; i++) {
int c = (unsigned char) (qpixel(txt, i, j) >> 24);
if(c == rle_last && rle_q < 255) rle_q++;
else {
if(rle_last != -1) printf("%d,%d,", rle_last, rle_q);
if(c == 0 || c == 255) rle_last = c, rle_q = 1;
else { rle_last = -1; printf("%d,", c); }
}
}
}
if(rle_last != -1) printf("%d,%d,", rle_last, rle_q);
printf("\n");
}
*/
void loadCompressedChar(int &otwidth, int &otheight, int *tpix) {
otheight = *(ftv++);
otwidth = *(ftv++);
int left = otwidth * otheight;
while(left) {
int x = *(ftv++);
if(x == 0 || x == 255) {
x = x * 0x1010101;
int q = *(ftv++);
left -= q;
while(q--) *(tpix++) = x;
}
else {
*(tpix++) = (x << 24) | 0xFFFFFF;
left--;
}
}
}

541
init.cpp Normal file
View File

@ -0,0 +1,541 @@
#define VER "9.4c"
#define VERNUM 9403
#define VERNUM_HEX 0x9403
#define GEN_M 0
#define GEN_F 1
#define GEN_N 2
#define GEN_O 3
#ifdef MOBILE
#define MOBWEB
#endif
#ifdef WEB
#define MOBWEB
#define ONEGRAPH
#endif
#ifdef IOS
#define ONEGRAPH
#endif
#ifdef MOBWEB
#define NORUG
#define NOEDIT
#define NOMODEL
#endif
#ifdef MINI
#define NORUG
#define NOEDIT
#define NOMODEL
#define NOSAVE
#define NOCONFIG
#define NOTRANS
#endif
#ifdef MOBILE
#define EXTRALICENSE "\n\nHyperRogue soundtrack by Shawn Parrotte (http://www.shawnparrotte.com), under the Creative Commons BY-SA 3.0 license, http://creativecommons.org/licenses/by-sa/3.0/"
#undef XEXTRALICENSE
#ifndef FAKEMOBILE
#define SDLK_F1 (123001)
#define SDLK_F2 (123002)
#define SDLK_F3 (123003)
#define SDLK_F4 (123004)
#define SDLK_F5 (123005)
#define SDLK_F6 (123006)
#define SDLK_F7 (123007)
#define SDLK_F10 (123010)
#define SDLK_ESCAPE (123099)
#define SDLK_F12 (123012)
#define SDLK_HOME (123013)
#define SDLK_LEFT (123014)
#define SDLK_RIGHT (123015)
#define MIX_MAX_VOLUME 128
#define SDLK_UP (123021)
#define SDLK_DOWN (123022)
#define SDLK_PAGEUP (123023)
#define SDLK_PAGEDOWN (123024)
#define SDLK_RETURN (123025)
#define SDLK_KP1 (123031)
#define SDLK_KP2 (123032)
#define SDLK_KP3 (123033)
#define SDLK_KP4 (123034)
#define SDLK_KP5 (123035)
#define SDLK_KP6 (123036)
#define SDLK_KP7 (123037)
#define SDLK_KP8 (123038)
#define SDLK_KP9 (123039)
#define SDLK_KP_PERIOD (123051)
#define SDLK_DELETE (123052)
#define SDLK_DELETE (123052)
#endif
int fontscale = 100;
bool buttonclicked;
void gdpush(int t);
#endif
// desktop
#include <stdio.h>
#ifdef USE_SDL
#include <SDL/SDL.h>
#ifndef MAC
#undef main
#endif
#include <SDL/SDL_ttf.h>
#endif
#include <cmath>
#include <time.h>
#include <vector>
#include <string>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <map>
#ifdef USE_UNORDERED_MAP
#include <unordered_map>
#else
#define unordered_map map
#endif
using namespace std;
string s0;
void addMessage(string s, char spamtype = 0);
#ifdef ANDROID
FILE *debfile;
#endif
FILE *debugfile;
int debugflags;
#ifdef USE_COMMANDLINE
const char *scorefile = "hyperrogue.log";
const char *conffile = "hyperrogue.ini";
string levelfile = "hyperrogue.lev";
string picfile = "hyperrogue.pic";
const char *musicfile = "";
const char *loadlevel = NULL;
#endif
#define S7 (sphere?5:7)
#define S42 (S7*6)
#define S14 (S7*2)
#define S21 (S7*3)
#define S28 (S7*4)
#define S84 (S7*12)
#include "util.cpp"
#include "hyperpoint.cpp"
#include "patterns.cpp"
#include "classes.cpp"
#include "fieldpattern.cpp"
#include "heptagon.cpp"
#include "language.cpp"
#include "hyper.h"
#include "cell.cpp"
#include "flags.cpp"
#include "yendor.cpp"
#include "complex.cpp"
#include "game.cpp"
#include "landgen.cpp"
#include "orbs.cpp"
#include "system.cpp"
#include "geometry.cpp"
#include "polygons.cpp"
#include "mapeditor.cpp"
#ifndef MOBILE
#include "netgen.cpp"
#endif
#include "graph.cpp"
#include "sound.cpp"
#include "achievement.cpp"
#ifndef MOBILE
#include <unistd.h>
#endif
bool fixseed = false;
void initAll() {
ca::init();
arg::read(1);
srand(time(NULL));
shrand(fixseed ? 0 : time(NULL));
achievement_init(); // not in ANDROID
eLand f = firstland;
// initlanguage();
initgraph();
#ifndef NOSAVE
loadsave();
#endif
resetGeometry();
initcells();
shmup::safety = safety;
initgame();
restartGraph();
if(!shmup::on) {
restoreGolems(items[itOrbLife], moGolem); items[itOrbLife] = 0;
restoreGolems(items[itOrbFriend], moTameBomberbird); items[itOrbFriend] = 0;
restoreGolems(kills[moPrincessMoved], moPrincess, princess::saveHP); kills[moPrincessMoved] = 0;
restoreGolems(kills[moPrincessArmedMoved], moPrincessArmed, princess::saveArmedHP); kills[moPrincessArmedMoved] = 0;
}
firstland = f;
}
void finishAll() {
achievement_final(!items[itOrbSafety]);
#ifndef NOSAVE
saveStats();
#endif
offscreen.clear();
clearMemory();
#ifndef MOBILE
cleargraph();
#endif
achievement_close();
}
#ifdef ANDROID
string buildScoreDescription() {
string s;
time_t timer;
timer = time(NULL);
char buf[128]; strftime(buf, 128, "%c", localtime(&timer));
char buf2[128];
s += XLAT("HyperRogue for Android");
s += " ("VER"), http://www.roguetemple.com/z/hyper.php\n";
s += XLAT("Date: %1 time: %2 s ", buf, its(savetime + time(NULL) - timerstart));
s += XLAT("distance: %1\n", its(celldist(cwt.c)));
// s += buf2;
if(cheater) s += XLAT("Cheats: ") + its(cheater) + "\n";
s += XLAT("Score: ") + its(gold());
for(int i=0; i<ittypes; i++) if(items[i]) {
string t = XLATN(iinf[i].name);
sprintf(buf2, " %s (%d)", t.c_str(), items[i]);
s += buf2;
}
s += "\n";
s += XLAT("Kills: ") + its(tkills());
for(int i=1; i<motypes; i++) if(kills[i]) {
string t = XLATN(minf[i].name);
sprintf(buf2, " %s (%d)", t.c_str(), kills[i]);
s += buf2;
}
s += "\n";
for(int i=0; i<gamelog.size(); i++) if(gamelog[i].msg != "") s += gamelog[i].msg + "\n";
return s;
}
#endif
#ifdef MOBILE
bool lclicked = false, clicked = false;
string lmouseovers;
bool inmenu = false;
bool longclick;
void handleScoreClick();
void openURL();
void displayTexts();
void controlMusic(int ticks);
void showHelp(MOBPAR_FORMAL, string nhelp) {
help = nhelp;
// helptext = help;
lastmode = cmode;
cmode = emHelp;
}
bool useRangedOrb;
void handleclick(MOBPAR_FORMAL) {
if(!shmup::on && andmode == 0 && cmode == emNormal && canmove && !useRangedOrb && vid.mobilecompasssize > 0) {
using namespace shmupballs;
int dx = mousex - xmove;
int dy = mousey - yb;
int h = hypot(dx, dy);
if(h < rad) {
if(h < rad*SKIPFAC) movepcto(MD_WAIT);
else {
double d = revcontrol ? -1 : 1;
mouseh = hpxy(dx * d / rad, dy * d / rad);
mousemovement();
}
getcstat = 0;
return;
}
}
if(buttonclicked || outofmap(mouseh)) {
if(andmode == 0 && getcstat == 'g' && !shmup::on) {
movepcto(MD_DROP);
getcstat = 0;
}
else if(getcstat != SDLK_F1) {
int px = mousex < vid.xcenter ? 0 : 1;
int py = mousey < vid.ycenter ? 0 : 1;
if(cmode == (canmove ? emNormal : emQuit)) {
if(px == 0 && py == 1) {
if(andmode == 0 && shmup::on) ;
else andmode = 10;
}
if(px == 1 && py == 1) {
if(andmode == 0 && shmup::on) ; // just fire, do not change modes
else {
if(andmode == 1) {
centerpc(INF);
View = Id;
viewctr.h = cwt.c->master;
}
andmode = 11;
}
}
if(px == 0 && py == 0) andmode = 22;
if(px == 1 && py == 0) andmode = 13;
}
}
else {
if(andmode == 0 && help != "@") {
addMessage(mouseovers);
showHelp(MOBPAR_ACTUAL, help);
andmode = 10;
getcstat = 0;
return;
}
}
}
if(andmode == 0 && cmode == (canmove ? emNormal : emQuit) && !outofmap(mouseh)) {
bool forcetarget = longclick;
if(mouseover && targetclick && targetRangedOrb(mouseover, forcetarget ? roMouseForce : roMouse)) {
;
}
else if(!forcetarget) movepcto(mousedest);
}
if(andmode == 10) {
if(!playerfound) {
centerpc(INF);
View = Id;
viewctr.h = cwt.c->master;
}
playermoved = true;
}
if(andmode >= 10) andmode -= 10;
if(andmode == 3) cmode = emMenu, andmode = 0;
}
int touchedAt;
int getticks();
void mobile_draw(MOBPAR_FORMAL) {
optimizeview();
int lastt = ticks; ticks = getticks();
if(lastt > ticks) lastt = ticks;
int tdiff = ticks - lastt;
if(playermoved && vid.sspeed > -4.99)
centerpc(tdiff / 1000.0 * exp(vid.sspeed));
if(shmup::on && (andmode == 0 || andmode == 10) && cmode == emNormal)
shmup::turn(tdiff);
safety = false;
vid.fsize = (min(vid.xres, vid.yres) * fontscale + 50) / 3200;
hyperpoint mouseoh = mouseh;
gtouched = mousepressed = clicked;
longclick = lclicked && ticks > touchedAt + 500;
useRangedOrb =
longclick || (!(vid.shifttarget & 2) && haveRangedOrb() && lmouseover && lmouseover->cpdist > 1);
targetclick = ((vid.shifttarget & 2) && !shmup::on) ? longclick : true;
if(shmup::on) {
using namespace shmupballs;
if(hypot(mousex - xfire, mousey - yb) < rad) targetclick = false;
if(hypot(mousex - xmove, mousey - yb) < rad) targetclick = false;
}
if(cmode == emNormal) {
lmouseover = (gtouched && lclicked) ? mouseover : NULL;
if(!shmup::on && !useRangedOrb && vid.mobilecompasssize) {
using namespace shmupballs;
int dx = mousex - xmove;
int dy = mousey - yb;
int h = hypot(dx, dy);
if(h < rad) {
if(h < rad*SKIPFAC) { lmouseover = cwt.c; mousedest.d = -1; }
else {
double d = revcontrol ? -1 : 1;
mouseh = hpxy(dx * d / rad, dy * d / rad);
calcMousedest();
}
}
}
if(andmode == 0 && !useRangedOrb && gtouched && lclicked) {
lmouseover = mousedest.d >= 0 ? cwt.c->mov[(cwt.spin + mousedest.d) % cwt.c->type] : cwt.c;
}
}
mouseh = gethyper(mousex, mousey);
// if(debfile) fprintf(debfile, "d1\n"), fflush(debfile);
frames++;
if(conformal::on) conformal::apply();
if(ticks > lastt) tortoise::updateVals(ticks - lastt);
if(clicked && !lclicked) touchedAt = ticks;
graphdata.clear();
getcstat = 0; shiftmul = 1; getcshift = 1;
drawscreen();
shiftmul = getcshift;
calcMousedest();
if(lclicked && !clicked && !inmenu) handleclick(MOBPAR_ACTUAL);
if(inmenu && !clicked && !lclicked) inmenu = false;
bool keyreact = lclicked && !clicked;
if(cmode == emOverview || cmode == emTactic) {
using namespace dialog::zoom;
if(zoomoff || (cmode != emOverview && cmode != emTactic)) {
zoomf = 1; shiftx = shifty = 0; zoomoff = false; return;
}
if(clicked && !lclicked) {
zoomf = 3;
}
if(zoomf == 3) {
shiftx = -2*mousex;
shifty = -2*mousey;
}
if(!clicked && zoomf > 1) { zoomoff = true; }
}
if(inslider) keyreact = true;
#ifdef ANDROID
if(getcstat == 's'-96) {
cmode = canmove ? emQuit : emNormal;
shareScore(MOBPAR_ACTUAL);
cmode = emNormal;
}
#endif
if(andmode == 2 && cmode != emNormal) andmode = 12;
if((cmode == emQuit && !canmove && keyreact && lclicked && !clicked) && !buttonclicked) {
cmode = emNormal; printf("back to quit\n");
}
else if(cmode == emScores) handleScoreKeys(0);
else if(getcstat && keyreact) {
if(cmode == (canmove ? emQuit : emNormal))
handleQuit(getcstat, getcstat);
else {
if(cmode != emNormal && cmode != emQuit) inmenu = true;
if(cmode == emMenu && getcstat == 'q') openURL();
else { extra ex; handlekey(getcstat, getcstat, ex); }
}
}
#ifdef IOS
displayTexts();
#endif
if((cmode != emVisual1 && cmode != emScores)) {
if(clicked && lclicked && andmode == 1 && !inmenu) {
if(!outofmap(mouseoh) && !outofmap(mouseh) && mouseoh[2] < 50 && mouseh[2] < 50) {
panning(mouseoh, mouseh);
}
}
if(andmode == 1 && lclicked && !clicked && !inmenu && mouseover)
performMarkCommand(mouseover);
if(clicked && andmode == 2 && (mouseover != lmouseover || mouseovers != lmouseovers) && cmode == emNormal) {
addMessage(mouseovers);
lmouseovers = mouseovers;
}
if(andmode == 10 && clicked != lclicked) andmode = 0;
if(andmode == 20 && clicked != lclicked) andmode = 10;
if(andmode == 2 && lclicked && !clicked) {
if(cmode == emNormal)
showHelp(MOBPAR_ACTUAL, help);
else if(cmode != emScores && cmode != emPickScores)
cmode = emNormal;
}
else if(andmode == 4) {
achievement_final(false);
}
if(clicked && andmode == 2 && (mouseover != lmouseover || mouseovers != lmouseovers)) {
addMessage(mouseovers);
showHelp(MOBPAR_ACTUAL, help);
lmouseovers = mouseovers;
}
}
if(clicked != lclicked)
flashMessages();
// END
lclicked = clicked;
#ifdef IOS
controlMusic(ticks - lastt);
#endif
}
#ifdef MOBILE
#include "google-games.cpp"
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -136,10 +136,13 @@ void setstats(set<string>& s, const char* bn) {
int main() {
nothe.insert("R'Lyeh");
nothe.insert("Camelot");
plural.insert("Crossroads");
plural.insert("Crossroads II");
plural.insert("Crossroads III");
plural.insert("Elemental Planes");
plural.insert("Crossroads IV");
plural.insert("Kraken Depths");
#define S(a,b) d[1].add(a,b);
#define N(a,b,c,d,e,f) \
@ -188,8 +191,8 @@ int main() {
string mis = "";
for(int i=1; i<NUMLAN; i++) if(d[i].count(*x) == 0)
mis += d[i]["EN"];
if(mis != "" && mis != "TR" && mis != "TRDE" && mis != "DE")
printf("#warning Missing [%s]: %s\n", mis.c_str(), escape(*x, "?"));
if(mis != "")
printf("// #warning Missing [%s]: %s\n", mis.c_str(), escape(*x, "?"));
}
s.clear();
@ -202,8 +205,8 @@ int main() {
string mis = "";
for(int i=1; i<NUMLAN; i++) if(nouns[i].count(*x) == 0)
mis += d[i]["EN"];
if(mis != "" && mis != "TR" && mis != "TRDE" && mis != "DE")
printf("#warning Missing [%s]: %s\n", mis.c_str(), escape(*x, "?"));
if(mis != "")
printf("// #warning Missing [%s]: %s\n", mis.c_str(), escape(*x, "?"));
}
#ifdef CHECKALL

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -52,6 +52,12 @@ struct fullnoun {
noun n[NUMLAN-1];
};
#ifdef NOTRANS
#define NUMEXTRA 3
const char* natchars[NUMEXTRA] = {"°","é","á"};
#endif
#ifndef NOTRANS
#include "language-data.cpp"
hashcode langhash(const string& s) {
@ -75,6 +81,7 @@ template<class T> const T* findInHashTableS(string s, const T *table, int size)
}
#define findInHashTable(s,t) findInHashTableS(s, t, sizeof(t) / sizeof(t[0]))
#endif
string choose3(int g, string a, string b, string c) {
if(g == GEN_M || g == GEN_O) return a;
@ -100,11 +107,12 @@ set<string> warnshown;
void basicrep(string& x) {
const sentence *s = findInHashTable(x, all_sentences);
if(!s && !warnshown.count(x)) {
printf("WARNING: no translations for '%s'\n", x.c_str());
warnshown.insert(x);
}
#ifndef NOTRANS
const sentence *s = findInHashTable(x, all_sentences);
if(!s && !warnshown.count(x)) {
printf("WARNING: no translations for '%s'\n", x.c_str());
warnshown.insert(x);
}
int l = lang();
if(l) {
@ -122,9 +130,11 @@ void basicrep(string& x) {
rep(x, "%l0", choose3(playergender(), "l", "la", "lo"));
rep(x, "%d0", choose3(playergender(), "", "a", "o"));
}
#endif
}
void parrep(string& x, string w, stringpar p) {
#ifndef NOTRANS
int l = lang();
const fullnoun *N = findInHashTable(p.v, all_nouns);
if(l == 1) {
@ -176,6 +186,7 @@ void parrep(string& x, string w, stringpar p) {
rep(x, "%ůj"+w, choose4(N->n[2].genus, "ého", "ou", "é", "ůj"));
rep(x, "%ým"+w, choose3(N->n[2].genus, "ým", "ou", "ým"));
rep(x, "%ho"+w, choose3(N->n[2].genus, "ho", "ji", "ho"));
rep(x, "%ého"+w, choose3(N->n[2].genus, "ého", "ou", "ého"));
if(p.v == "Mirror Image")
rep(x, "%s"+w, "se");
@ -198,6 +209,7 @@ void parrep(string& x, string w, stringpar p) {
rep(x, "%E"+w, choose3(N->n[3].genus, "", "а", "о"));
rep(x, "%A"+w, choose3(N->n[3].genus, "ый", "ая", "ое"));
rep(x, "%c"+w, choose3(N->n[3].genus, "ся", "ась", ""));
rep(x, "%y"+w, choose3(N->n[3].genus, "ый", "ая", "ое"));
}
else {
rep(x,"%"+w,p.v);
@ -228,10 +240,21 @@ void parrep(string& x, string w, stringpar p) {
rep(x, "%den"+w, "the");
}
}
#endif
if(true) {
// proper names (R'Lyeh)
rep(x,"%"+w,p.v);
if(N && (N->english_grammar_flags & 1)) {
#ifdef NOTRANS
int flags = 0;
if(p.v == "R'Lyeh" || p.v == "Camelot") flags = 1;
if(p.v == "Crossroads" || p.v == "Crossroads II" ||
p.v == "Crossroads III" || p.v == "Crossroads IV" ||
p.v == "Kraken Depths" || p.v == "Elemental Planes")
flags = 2;
#else
int flags = N ? N->english_grammar_flags : 0;
#endif
if(flags & 1) {
rep(x,"%the"+w, p.v);
rep(x,"%The"+w, p.v);
}
@ -242,7 +265,7 @@ void parrep(string& x, string w, stringpar p) {
rep(x,"%his"+w, princessgender() ? "her" : "his");
}
// plural names (Crossroads)
if(N && (N->english_grammar_flags & 2))
if(flags & 2)
rep(x,"%s"+w, "");
else
rep(x,"%s"+w, "s");
@ -288,20 +311,35 @@ string XLAT(string x, stringpar p1, stringpar p2, stringpar p3, stringpar p4) {
postrep(x);
return x;
}
string XLAT(string x, stringpar p1, stringpar p2, stringpar p3, stringpar p4, stringpar p5) {
basicrep(x);
parrep(x,"1",p1.v);
parrep(x,"2",p2.v);
parrep(x,"3",p3.v);
parrep(x,"4",p4.v);
parrep(x,"5",p5.v);
postrep(x);
return x;
}
string XLATN(string x) {
#ifndef NOTRANS
if(lang()) {
const fullnoun *N = findInHashTable(x, all_nouns);
if(N) return N->n[lang()-1].nomp;
}
#endif
return x;
}
string XLAT1(string x) {
#ifndef NOTRANS
if(lang()) {
const fullnoun *N = findInHashTable(x, all_nouns);
if(N) return N->n[lang()-1].nom;
}
#endif
return x;
}

View File

@ -12,14 +12,16 @@
#endif
namespace mapeditor {
#ifndef MOBILE
cell *modelcell[200];
int subcanvas;
#ifndef NOEDIT
map<int, cell*> modelcell;
void clearModelCells() {
for(int i=0; i<200; i++) modelcell[i] = NULL;
modelcell.clear();
}
void applyModelcell(cell *c) {
if(mapeditor::whichPattern == 'H') return;
if(mapeditor::whichPattern == 'H') return;
int i = realpattern(c);
cell *c2 = modelcell[i];
@ -38,7 +40,7 @@ namespace mapeditor {
#endif
}
#ifndef MOBILE
#ifndef NOEDIT
namespace mapstream {
std::map<cell*, int> cellids;
vector<cell*> cellbyid;
@ -83,7 +85,7 @@ namespace mapstream {
cellids[c->mov[j]] < i) {
int32_t i = cellids[c->mov[j]];
save(i);
saveChar(c->spn[j]);
saveChar(c->spn(j));
saveChar(j);
break;
}
@ -167,7 +169,7 @@ namespace mapstream {
// printf("%p:%d,%d -> %p\n", c2, dir, c);
// spinval becomes xspinval
rspin = (c2->spn[dir] - loadChar() + 42) % c->type;
rspin = (c2->spn(dir) - loadChar() + 42) % c->type;
}
cellbyid.push_back(c);
@ -251,7 +253,7 @@ namespace mapstream {
ds.list.push_back(loadPoint());
}
saveImages();
buildpolys();
bfs();
restartGraph();
return true;
@ -302,6 +304,8 @@ namespace mapeditor {
case 'z': {
int t = zebra40(c);
if(euclid) return (t*4) % 6;
int t4 = t>>2, tcdir = 0;
if(purehepta) tcdir = t^1;
@ -321,6 +325,7 @@ namespace mapeditor {
case 'f': {
int t = emeraldval(c);
if(euclid) return 0;
int tcdir = 0, tbest = (t&3);
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
@ -386,7 +391,31 @@ namespace mapeditor {
return 0;
}
#ifndef MOBILE
string infix;
bool hasInfix(const string &s) {
if(infix == "") return true;
string t = "";
for(int i=0; i<size(s); i++) {
char c = s[i];
char tt = 0;
if(c >= 'a' && c <= 'z') tt += c - 32;
else if(c >= 'A' && c <= 'Z') tt += c;
if(tt) t += tt;
}
return t.find(infix) != string::npos;
}
bool editInfix(int uni) {
if(uni >= 'A' && uni <= 'Z') infix += uni;
else if(uni >= 'a' && uni <= 'z') infix += uni-32;
else if(infix != "" && uni == 8) infix = infix.substr(0, size(infix)-1);
else if(infix != "" && uni != 0) infix = "";
else return false;
return true;
}
#ifndef NOEDIT
int paintwhat = 0;
int painttype = 0;
int radius = 0;
@ -397,11 +426,8 @@ namespace mapeditor {
bool symRotation, sym01, sym02, sym03;
int displaycodes;
int subcanvas;
int whichpart;
string infix;
cell *drawcell;
const char *mapeditorhelp =
@ -508,12 +534,6 @@ namespace mapeditor {
return 0x20;
}
int colorhistory[10] = {
0x202020, 0x800000, 0x008000, 0x000080,
0x404040, 0xC0C0C0, 0x804000, 0xC0C000,
0x408040, 0xFFD500
}, lch;
bool choosefile = false;
bool editext = false;
@ -581,89 +601,23 @@ namespace mapeditor {
}
}
void drawColorDialog(int color) {
for(int j=0; j<10; j++) {
int x = vid.xres / 2 + vid.fsize * 2 * (j-5);
int y = vid.yres / 2- 5 * vid.fsize;
string s0 = ""; s0 += ('q'+j);
displayColorButton(x, y, s0, 'q'+j, 0, 0, colorhistory[j]);
}
for(int i=0; i<3; i++) for(int j=0; j<16; j++) {
int x = vid.xres / 2 + vid.fsize * 2 * (j-8);
int y = vid.yres / 2 + (i-1) * vid.fsize * 2;
int p = color;
p &= ~ (0xFF << (8*i));
p |= (17*j) << (8*i);
char c0 = "0aA" [i]+j;
string s0 = ""; s0 += c0;
displayColorButton(x, y, s0, c0, 0, 0, p);
}
displayColorButton(vid.xres/2, vid.yres/2+vid.fsize * 4, "select this color", ' ', 8, 0, color);
}
// 0: nothing happened, 1: color accepted, 2: break
int handleKeyColor(int uni, int& color) {
int i = 3;
int j = 0;
if(uni >= '0' && uni <= '0'+15)
i=0, j=uni-'0';
if(uni >= 'a' && uni <= 'a'+15)
i=1, j=uni-'a';
if(uni >= 'A' && uni <= 'A'+15)
i=2, j=uni-'A';
if(i<3) {
color &= ~ (0xFF << (8*i));
color |= (17*j) << (8*i);
}
else if(uni == ' ') {
bool inHistory = false;
for(int i=0; i<10; i++) if(colorhistory[i] == paintwhat)
inHistory = true;
if(!inHistory) { colorhistory[lch] = paintwhat; lch++; lch %= 10; }
return 1;
}
else if(uni >= 'q' && uni <= 'z') {
color = colorhistory[uni - 'q'];
return 1;
}
else if(uni) return 2;
return 0;
}
void displayFunctionKeys() {
int fs = vid.fsize + 5;
displayButton(8, vid.yres-8-fs*10, XLAT("F1 = help"), SDLK_F1, 0);
displayButton(8, vid.yres-8-fs*9, XLAT("F2 = save"), SDLK_F2, 0);
displayButton(8, vid.yres-8-fs*8, XLAT("F3 = load"), SDLK_F3, 0);
displayButton(8, vid.yres-8-fs*7, XLAT("F4 = file"), SDLK_F3, 0);
displayButton(8, vid.yres-8-fs*6, XLAT("F5 = restart"), SDLK_F5, 0);
displayButton(8, vid.yres-8-fs*5, XLAT("F6 = HQ shot"), SDLK_F6, 0);
displayButton(8, vid.yres-8-fs*4, XLAT("F7 = player on/off"), SDLK_F7, 0);
displayButton(8, vid.yres-8-fs*11, XLAT("F1 = help"), SDLK_F1, 0);
displayButton(8, vid.yres-8-fs*10, XLAT("F2 = save"), SDLK_F2, 0);
displayButton(8, vid.yres-8-fs*9, XLAT("F3 = load"), SDLK_F3, 0);
displayButton(8, vid.yres-8-fs*8, XLAT("F4 = file"), SDLK_F3, 0);
displayButton(8, vid.yres-8-fs*7, XLAT("F5 = restart"), SDLK_F5, 0);
displayButton(8, vid.yres-8-fs*6, XLAT("F6 = HQ shot"), SDLK_F6, 0);
displayButton(8, vid.yres-8-fs*5, XLAT("F7 = player on/off"), SDLK_F7, 0);
displayButton(8, vid.yres-8-fs*4, XLAT("F8 = SVG shot"), SDLK_F8, 0);
displayButton(8, vid.yres-8-fs*3, XLAT("SPACE = map/graphics"), ' ', 0);
displayButton(8, vid.yres-8-fs*2, XLAT("ESC = return to the game"), SDLK_ESCAPE, 0);
}
void vpush(int i, const char *name) {
string s = XLATN(name);
if(infix != "") {
string t = "";
for(int i=0; i<size(s); i++) {
char c = s[i];
char tt = 0;
if(c >= 'a' && c <= 'z') tt += c - 32;
else if(c >= 'A' && c <= 'Z') tt += c;
if(tt) t += tt;
}
if(t.find(infix) == string::npos) return;
}
if(!hasInfix(s)) return;
v.push_back(make_pair(s, i));
}
@ -672,42 +626,59 @@ namespace mapeditor {
if(choosefile) { drawFileDialog(); return; }
if(subscreen == 2) {
displayStat(2, XLAT("Emerald Pattern"), ONOFF(whichPattern == 'f'), 'f');
displayStat(3, XLAT("Palace Pattern"), ONOFF(whichPattern == 'p'), 'p');
displayStat(4, XLAT("Zebra Pattern"), ONOFF(whichPattern == 'z'), 'z');
dialog::init();
dialog::addBoolItem(XLAT(euclid ? "three colors" : "Emerald Pattern"), (whichPattern == 'f'), 'f');
dialog::addBoolItem(XLAT("Palace Pattern"), (whichPattern == 'p'), 'p');
dialog::addBoolItem(XLAT(euclid ? "three colors rotated" : "Zebra Pattern"), (whichPattern == 'z'), 'z');
dialog::addBoolItem(XLAT("field pattern"), (whichPattern == 'F'), 'F');
if(whichPattern == 'f') symRotation = true;
displayStat(6, XLAT("rotational symmetry"), ONOFF(symRotation), '0');
displayStat(7, XLAT("symmetry 0-1"), ONOFF(sym01), '1');
displayStat(8, XLAT("symmetry 0-2"), ONOFF(sym02), '2');
displayStat(9, XLAT("symmetry 0-3"), ONOFF(sym03), '3');
if(whichPattern == 'F') ;
else if(!euclid) {
dialog::addBoolItem(XLAT("rotational symmetry"), (symRotation), '0');
dialog::addBoolItem(XLAT("symmetry 0-1"), (sym01), '1');
dialog::addBoolItem(XLAT("symmetry 0-2"), (sym02), '2');
dialog::addBoolItem(XLAT("symmetry 0-3"), (sym03), '3');
}
else
dialog::addBoolItem(XLAT("edit all three colors"), (symRotation), '0');
displayStat(11, XLAT("display pattern codes (full)"), ONOFF(displaycodes), 'd');
displayStat(12, XLAT("display pattern codes (simplified)"), ONOFF(displaycodes), 's');
dialog::addBoolItem(XLAT("display pattern codes (full)"), (displaycodes == 1), 'd');
dialog::addBoolItem(XLAT("display pattern codes (simplified)"), (displaycodes == 2), 's');
displayStat(14, XLAT("display only hexagons"), ONOFF(whichShape == '6'), '6');
displayStat(15, XLAT("display only heptagons"), ONOFF(whichShape == '7'), '7');
displayStat(16, XLAT("display the triheptagonal grid"), ONOFF(whichShape == '8'), '8');
dialog::addBoolItem(XLAT("display only hexagons"), (whichShape == '6'), '6');
dialog::addBoolItem(XLAT("display only heptagons"), (whichShape == '7'), '7');
dialog::addBoolItem(XLAT("display the triheptagonal grid"), (whichShape == '8'), '8');
displayStat(18, XLAT("predesigned patterns"), "", 'r');
dialog::addItem(XLAT("predesigned patterns"), 'r');
dialog::display();
}
else if(subscreen == 3) {
displayStat(2, XLAT("Gameboard"), "", 'g');
displayStat(3, XLAT("random colors"), "", 'r');
displayStat(4, XLAT("rainbow landscape"), "", 'l');
dialog::init("predesigned patterns");
dialog::addItem(XLAT("Gameboard"), 'g');
dialog::addItem(XLAT("random colors"), 'r');
dialog::addItem(XLAT("rainbow landscape"), 'l');
displayStat(6, XLAT("emerald pattern"), "emerald", 'e');
dialog::addSelItem(XLAT("emerald pattern"), "emerald", 'e');
displayStat(8, XLAT("four elements"), "palace", 'b');
displayStat(9, XLAT("eight domains"), "palace", 'a');
dialog::addSelItem(XLAT("four elements"), "palace", 'b');
dialog::addSelItem(XLAT("eight domains"), "palace", 'a');
displayStat(11, XLAT("zebra pattern"), "zebra", 'z');
displayStat(12, XLAT("three stripes"), "zebra", 'x');
dialog::addSelItem(XLAT("zebra pattern"), "zebra", 'z');
dialog::addSelItem(XLAT("four triangles"), "zebra", 't');
dialog::addSelItem(XLAT("three stripes"), "zebra", 'x');
displayStat(15, XLAT("random black-and-white"), "current", 'w');
dialog::addSelItem(XLAT("random black-and-white"), "current", 'w');
dialog::addSelItem(XLAT("field pattern C"), "field", 'C');
dialog::addSelItem(XLAT("field pattern D"), "field", 'D');
dialog::addSelItem(XLAT("field pattern N"), "field", 'N');
dialog::addSelItem(XLAT("field pattern S"), "field", 'S');
dialog::display();
}
else if(subscreen == 1 && painttype == 6)
drawColorDialog(paintwhat);
dialog::drawColorDialog(paintwhat);
else if(subscreen == 1) {
v.clear();
if(painttype == 4) painttype = 0;
@ -810,11 +781,12 @@ namespace mapeditor {
createMov(c1, i);
int i0 = (42+cf*i+d1) % c1->type;
int i1 = (i + d2) % c2->type;
spillCopy(c1->mov[i0], c1->spn[i0], c2->mov[i1], c2->spn[i1], r-1);
spillCopy(c1->mov[i0], c1->spn(i0), c2->mov[i1], c2->spn(i1), r-1);
}
}
int subpatternEmerald(int i) {
if(euclid) return (symRotation && (i<3)) ? 0 : i;
if((sym01?1:0)+(sym02?1:0)+(sym03?1:0) >= 2) i &= ~3;
if(sym01 && (i&1)) i ^= 1;
if(sym02 && (i&2)) i ^= 2;
@ -823,6 +795,7 @@ namespace mapeditor {
}
int subpatternZebra(int i) {
if(euclid) return (symRotation && (i<3)) ? 0 : i;
i = subpatternEmerald(i);
if(symRotation) {
if(i >= 8 && i < 12) i -= 4;
@ -836,6 +809,7 @@ namespace mapeditor {
}
int subpatternPalace(int i) {
if(euclid) return i;
i = subpatternEmerald(i);
if(symRotation && i >= 3) i -= ((i/4-1) % 7) * 4;
return i;
@ -854,8 +828,12 @@ namespace mapeditor {
if(polarb50(c)) i|=2;
return subpatternPalace(i);
}
case 'P':
return fiftyval(c);
case 'H':
return realpattern(c);
case 'F':
return realpattern(c);
}
return nopattern(c);
}
@ -875,10 +853,19 @@ namespace mapeditor {
}
case 'H':
return towerval(c);
case 'F': {
pair<int, bool> p = fieldpattern::fieldval(c);
return 10*p.first + (p.second?6:7);
}
}
return nopattern(c);
}
int realpatternsh(cell *c) {
if(whichPattern == 'F') return nopattern(c);
else return realpattern(c);
}
int cellShapeGroup() {
if(whichPattern == 'f') return 4;
if(whichPattern == 'p') return 5;
@ -939,6 +926,8 @@ namespace mapeditor {
break;
case 1:
c->item = eItem(paintwhat);
if(c->item == itBabyTortoise)
tortoise::babymap[c] = getBits(c) ^ tortoise::getRandomBits();
break;
case 2: {
eLand last = c->land;
@ -980,7 +969,7 @@ namespace mapeditor {
case 6:
c->land = laCanvas;
c->wall = waNone;
c->landparam = paintwhat;
c->landparam = paintwhat >> 8;
break;
case 4:
c->wall = copywhat->wall;
@ -995,7 +984,7 @@ namespace mapeditor {
break;
}
checkUndo();
if(r) for(int i=0; i<c->type; i++) spill(createMov(c, i), r-1, c->spn[i]);
if(r) for(int i=0; i<c->type; i++) spill(createMov(c, i), r-1, c->spn(i));
}
void allInPattern(cell *c, int r, int cdir) {
@ -1073,11 +1062,11 @@ namespace mapeditor {
return true;
}
void handleKey(int uni, int sym) {
if(choosefile && handleKeyFile(uni, sym)) ;
void handleKey(int sym, int uni) {
if(choosefile && handleKeyFile(sym, uni)) ;
else if(subscreen == 1 && painttype == 6) {
paintwhat_str = "paint";
int v = handleKeyColor(uni, paintwhat);
int v = dialog::handleKeyColor(sym, uni, paintwhat);
if(v == 1) subscreen = 0;
if(v == 2) cmode = emNormal;
}
@ -1090,14 +1079,12 @@ namespace mapeditor {
subscreen = 0;
mousepressed = false;
}
if(uni >= 'A' && uni <= 'Z') infix += uni;
else if(uni >= 'a' && uni <= 'z') infix += uni-32;
else if(infix != "" && uni == 8) infix = infix.substr(0, size(infix)-1);
else if(infix != "" && uni != 0) infix = "";
else if(subscreen == 1 && uni != 0) cmode = emNormal;
if(editInfix(uni)) ;
else if(subscreen == 1 && uni != 0) cmode = emNormal;
}
else if(subscreen == 3) {
if(uni >= 'a' && uni <= 'z') {
dialog::handleNavigation(sym, uni);
if((uni >= 'a' && uni <= 'z') || (uni >= 'A' && uni <= 'Z')) {
whichCanvas = uni;
subcanvas = rand();
firstland = laCanvas; randomPatternsMode = false;
@ -1106,7 +1093,8 @@ namespace mapeditor {
else if(uni != 0) subscreen = 0;
}
else if(subscreen == 2) {
if(uni == 'f' || uni == 'p' || uni == 'z' || uni == 'H') {
dialog::handleNavigation(sym, uni);
if(uni == 'f' || uni == 'p' || uni == 'z' || uni == 'H' || uni == 'F') {
if(whichPattern == uni) whichPattern = 0;
else whichPattern = uni;
clearModelCells();
@ -1171,6 +1159,9 @@ namespace mapeditor {
else if(sym == SDLK_F6) {
saveHighQualityShot();
}
else if(sym == SDLK_F8) {
svg::render();
}
else if(sym == SDLK_F7) {
drawplayer = !drawplayer;
}
@ -1214,7 +1205,7 @@ namespace mapeditor {
int dslayer;
bool coloring;
int colortouse = 0xC0C0C0;
int colortouse = 0xC0C0C0FF;
transmatrix drawtrans;
@ -1255,27 +1246,27 @@ namespace mapeditor {
void drawGrid() {
if(cmode == emDraw && !inHighQual) {
lalpha = 0x20;
for(int d=0; d<84; d++) {
transmatrix d2 = drawtrans * rgpushxto0(ccenter);
int lalpha;
if(d % (84/drawcell->type) == 0)
lalpha = 0x40;
else
lalpha = 0x20;
queueline(d2 * C0, d2 * spin(M_PI*d/42)* xpush(1) * C0, 0xC0C0C0);
int col = darkena(0xC0C0C0, 0, lalpha);
queueline(d2 * C0, d2 * spin(M_PI*d/42)* xpush(1) * C0, col);
for(int u=2; u<=20; u++) {
if(u % 5 == 0) lalpha = 0x40;
else lalpha = 0x20;
queueline(
d2 * spin(M_PI*d/42)* xpush(u/20.) * C0,
d2 * spin(M_PI*(d+1)/42)* xpush(u/20.) * C0,
0xC0C0C0);
darkena(0xC0C0C0, 0, lalpha));
}
}
queueline(drawtrans*ccenter, drawtrans*coldcenter, 0xC0C0C0);
queueline(drawtrans*ccenter, drawtrans*coldcenter, darkena(0xC0C0C0, 0, 0x20));
lalpha = 0xFF;
int sg = drawcellShapeGroup();
for(int i=0; i<USERSHAPEIDS; i++) if(editingShape(sg, i) && usershapes[sg][i]) {
@ -1285,12 +1276,10 @@ namespace mapeditor {
for(int a=0; a<size(ds.list); a++) {
hyperpoint P2 = drawtrans * ds.list[a];
int xc, yc, sc;
getcoord(P2, xc, yc, sc);
queuechr(xc, yc, sc, 10, 'x',
a == 0 ? 0x00FF00 :
queuechr(P2, 10, 'x',
darkena(a == 0 ? 0x00FF00 :
a == size(ds.list)-1 ? 0xFF0000 :
0xFFFF00);
0xFFFF00, 0, 0xFF));
}
}
}
@ -1299,7 +1288,7 @@ namespace mapeditor {
void showDrawEditor() {
if(coloring) {
drawColorDialog(colortouse);
dialog::drawColorDialog(colortouse);
return;
}
@ -1376,14 +1365,15 @@ namespace mapeditor {
displayfr(vid.xres-8, vid.yres-8-fs*6, 2, vid.fsize, XLAT("x: %1", fts4(mh[0])), 0xC0C0C0, 16);
displayfr(vid.xres-8, vid.yres-8-fs*5, 2, vid.fsize, XLAT("y: %1", fts4(mh[1])), 0xC0C0C0, 16);
displayfr(vid.xres-8, vid.yres-8-fs*4, 2, vid.fsize, XLAT("z: %1", fts4(mh[2])), 0xC0C0C0, 16);
displayfr(vid.xres-8, vid.yres-8-fs*2, 2, vid.fsize, XLAT("r: %1", fts4(inverse_sinh(sqrt(mh[0]*mh[0]+mh[1]*mh[1])))), 0xC0C0C0, 16);
displayfr(vid.xres-8, vid.yres-8-fs*2, 2, vid.fsize, XLAT("r: %1", fts4(hdist0(mh))), 0xC0C0C0, 16);
displayfr(vid.xres-8, vid.yres-8-fs, 2, vid.fsize, XLAT("ϕ: %1°", fts4(-atan2(mh[1], mh[0]) * 360 / 2 / M_PI)), 0xC0C0C0, 16);
}
displayFunctionKeys();
}
bool rebuildPolys = false;
void applyToShape(int sg, int id, int uni, hyperpoint mh) {
bool haveshape = usershapes[sg][id];
bool xnew = false;
@ -1402,12 +1392,12 @@ namespace mapeditor {
if(uni == 'n' || xnew) {
dsCur->list.clear();
dsCur->list.push_back(mh);
saveImages();
rebuildPolys = true;
}
if(uni == 'a' && haveshape) {
dsCur->list.push_back(mh);
saveImages();
rebuildPolys = true;
}
if(uni == 'D') {
@ -1434,7 +1424,7 @@ namespace mapeditor {
i--;
}
}
saveImages();
rebuildPolys = true;
}
if(uni == 'T') {
@ -1446,6 +1436,22 @@ namespace mapeditor {
loadShape(sg, id, shPFace, 1, 6);
loadShape(sg, id, shFlowerHair, 1, 7); */
// loadShape(sg, id, shPBody, 1, 0);
// loadShape(sg, id, shPHead, 1, 1);
/* loadShape(sg, id, shReptileFrontFoot, 1, 0);
loadShape(sg, id, shReptileRearFoot, 1, 1);
loadShape(sg, id, shReptileFrontLeg, 1, 2);
loadShape(sg, id, shReptileRearLeg, 1, 3);
loadShape(sg, id, shReptileBody, 2, 4);
loadShape(sg, id, shReptileHead, 2, 5);
loadShape(sg, id, shReptileTail, 2, 6); */
loadShape(sg, id, shTrylobite, 2, 0);
/* loadShape(sg, id, shYeti, 2, 0);
loadShape(sg, id, shHumanFoot, 1, 1); */
/* loadShape(sg, id, shYeti, 1, 2);
loadShape(sg, id, shRatHead, 1, 3);
loadShape(sg, id, shRatTail, 1, 1);
@ -1463,10 +1469,10 @@ namespace mapeditor {
loadShape(3, 1, shTurtleFloor[1], 14, 0); */
// loadShape(sg, id, shDragonSegment, 2, 0);
loadShape(sg, id, shDragonSegment, 2, 1);
// loadShape(sg, id, shEyes, 2, 2);
saveImages();
// loadShape(sg, id, shFamiliarHead, 2, 0);
rebuildPolys = true;
}
if(uni == 'K') {
@ -1483,10 +1489,12 @@ namespace mapeditor {
if(vid.cs.charid&1)
loadShape(sg, id, shFemaleDress, 2, 2);
if(vid.cs.charid&1)
/* if(vid.cs.charid&1)
loadShape(sg, id, shPrincessDress, 1, 3);
else
loadShape(sg, id, shPrinceDress, 2, 3);
loadShape(sg, id, shPrinceDress, 2, 3); */
loadShape(sg, id, shRatCape2, 1, 3);
if(vid.cs.charid&1)
loadShape(sg, id, shFemaleHair, 2, 4);
@ -1498,7 +1506,7 @@ namespace mapeditor {
// loadShape(sg, id, shWolf, 2, dslayer);
saveImages();
rebuildPolys = true;
}
if(uni == '+') dsCur->rots++;
@ -1506,20 +1514,20 @@ namespace mapeditor {
if(uni >= '1' && uni <= '9') {
dsCur->rots = uni - '0';
if(dsCur->rots == 9) dsCur->rots = 21;
saveImages();
rebuildPolys = true;
}
if(uni == '0') {
dsCur->sym = !dsCur->sym;
saveImages();
rebuildPolys = true;
}
if(uni == 't') {
dsCur->shift = mh;
saveImages();
rebuildPolys = true;
}
if(uni == 'y') {
dsCur->spin = mh;
saveImages();
rebuildPolys = true;
}
#define COLORKEY (-10000)
@ -1546,12 +1554,12 @@ namespace mapeditor {
return XLAT("vector graphics editor");
}
void drawHandleKey(int uni, int sym, double shiftmul) {
void drawHandleKey(int sym, int uni) {
if(choosefile && handleKeyFile(uni, sym)) return;
if(choosefile && handleKeyFile(sym, uni)) return;
if(coloring) {
int v = handleKeyColor(uni, colortouse);
int v = dialog::handleKeyColor(sym, uni, colortouse);
if(v == 2) { coloring = false; return; }
else if(v == 1) { coloring = false; uni = COLORKEY; }
else return;
@ -1581,9 +1589,9 @@ namespace mapeditor {
for(int l=0; l<USERLAYERS; l++) if(size(us->d[l].list)) {
usershapelayer& ds(us->d[l]);
printf("// %d %d %d [%06X]\n", i, j, l, ds.color);
for(int i=ds.sh.s; i < ds.sh.e; i++) {
printf(" hpcpush(hpxyz(%f,%f,%f));\n", double(hpc[i][0]), double(hpc[i][1]), double(hpc[i][2]));
}
printf(" ID, %d, %d, ", us->d[l].rots, us->d[l].sym?2:1);
for(int i=0; i<size(us->d[l].list); i++)
printf("%lf,%lf, ", double(us->d[l].list[i][0]), double(us->d[l].list[i][1]));
printf("\n");
}
}
@ -1661,7 +1669,7 @@ namespace mapeditor {
fclose(f);
addMessage(XLAT("Pictures loaded from %1", picfile));
saveImages();
buildpolys();
}
if(sym == SDLK_F7) {
@ -1672,6 +1680,10 @@ namespace mapeditor {
saveHighQualityShot();
}
if(sym == SDLK_F8) {
svg::render();
}
if(sym == SDLK_F5) {
for(int i=0; i<USERSHAPEGROUPS; i++)
for(int j=0; j<USERSHAPEIDS; j++)
@ -1688,11 +1700,42 @@ namespace mapeditor {
}
if(sym == SDLK_F10) cmode = emNormal;
if(rebuildPolys)
buildpolys(), rebuildPolys = false;
}
#endif
int canvasback = linf[laCanvas].color >> 2;
int generateCanvas(cell *c) {
if(whichCanvas == 'C') {
using namespace fieldpattern;
int z = fp43.getdist(fieldval(c), make_pair(0,false));
if(z < fp43.circrad) return 0x00C000;
int z2 = fp43.getdist(fieldval(c), make_pair(fp43.otherpole,false));
if(z2 < fp43.disthep[fp43.otherpole] - fp43.circrad)
return 0x3000;
return 0x6000;
}
if(whichCanvas == 'D') {
using namespace fieldpattern;
int z = fp43.getdist(fieldval(c), make_pair(0,false));
return 255 * (fp43.maxdist+1-z) / fp43.maxdist;
}
if(whichCanvas == 'N') {
using namespace fieldpattern;
int z = fp43.getdist(fieldval(c), make_pair(0,false));
int z2 = fp43.getdist(fieldval(c), make_pair(fp43.otherpole,false));
if(z < z2) return 0x00C000;
if(z > z2) return 0xC00000;
return 0xCCCC00;
}
if(whichCanvas == 'S') {
return 0x3F1F0F * fieldpattern::subval(c).second + 0x000080;
}
if(whichCanvas == 'g')
return linf[laCanvas].color >> 2;
return canvasback;
if(whichCanvas == 'r')
return hrand(0xFFFFFF + 1);
if(whichCanvas == 'e') {
@ -1727,6 +1770,12 @@ namespace mapeditor {
int fv = zebra40(c);
return fcol[fv&3];
}
if(whichCanvas == 't') {
static unsigned int fcol[4] = { 0x804040, 0x408040, 0x404080, 0x808040 };
int fv = zebra40(c);
if(fv/4 == 4 || fv/4 == 6 || fv/4 == 5 || fv/4 == 10) fv ^= 2;
return fcol[fv&3];
}
if(whichCanvas == 'x') {
static unsigned int fcol[4] = { 0xC0C0C0, 0x800000, 0x008000, 0x000080 };
return fcol[zebra3(c)];
@ -1736,20 +1785,17 @@ namespace mapeditor {
return fcol[randpattern(c, subcanvas) ? 1 : 0];
}
if(whichCanvas == 'l') {
#ifdef CDATA
int col[4];
bool err = false;
for(int j=0; j<4; j++) {
col[j] = getCdata(c, j);
col[j] *= 3;
col[j] %= 240;
if(col[j] > 120) col[j] = 240 - col[j];
if(col[j] < -120) col[j] = -240 - col[j];
}
return (0x808080 + col[0] + (col[1] << 8) + (col[2] << 16)) >> (err?2:0);
#endif
int col[4];
bool err = false;
for(int j=0; j<4; j++) {
col[j] = getCdata(c, j);
col[j] *= 3;
col[j] %= 240;
if(col[j] > 120) col[j] = 240 - col[j];
if(col[j] < -120) col[j] = -240 - col[j];
}
return (0x808080 + col[0] + (col[1] << 8) + (col[2] << 16)) >> (err?2:0);
}
return linf[laCanvas].color >> 2;
return canvasback;
}
#endif
}

1445
menus.cpp

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
// HyperRogue paper model generator
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#ifndef MOBILE
#ifndef NOMODEL
namespace netgen {
// We need a two-dimensional vector class for this.
@ -143,7 +143,7 @@ namespace netgen {
loaded = true;
if(!created) return;
if(!created) { fclose(f); return; }
for(int i=0; i<CELLS; i++) err = fscanf(f, "%d", &ct[i]);
@ -227,7 +227,7 @@ namespace netgen {
polyy[0] = int(v1.y);
polyy[1] = int(v2.y);
polyy[2] = int(v3.y);
filledPolygonColor(s, polyx, polyy, 3, col);
filledPolygonColorI(s, polyx, polyy, 3, col);
#endif
}
@ -259,7 +259,7 @@ namespace netgen {
int& hqpixel(hyperpoint h) {
int hx, hy, hs;
getcoord(h, hx, hy, hs);
getcoord0(h, hx, hy, hs);
return qpixel(hqsurface, hx, hy);
}
@ -644,20 +644,25 @@ namespace netgen {
nei[i][e] >= 0 ? 0x808080 :
0xC0C0C0;
drawline(hvec(i, (e+ofs)%t), hvec(i, (e+1+ofs)%t), (col << 8) + 0xFF);
prettyline(hvec(i, (e+ofs)%t), hvec(i, (e+1+ofs)%t), (col << 8) + 0xFF, 3);
}
}
}
if(mode != 2) {
displayStat( 2, XLAT("synchronize net and map"), "", 's');
displayStat( 3, XLAT("display the scope"), "", 't');
displayStat( 5, XLAT("create the model"), "", 'c');
displayStat( 7, XLAT("back to HyperRogue"), "", 'q');
displayStat( 9, XLAT("design the net"), "", 'd');
dialog::init("paper model creator");
dialog::addItem(XLAT("synchronize net and map"), 's');
dialog::addItem(XLAT("display the scope"), 't');
dialog::addItem(XLAT("create the model"), 'c');
dialog::addItem(XLAT("back to HyperRogue"), 'q');
dialog::addItem(XLAT("design the net"), 'd');
dialog::display();
}
}
void handleKey(int uni, int sym) {
void handleKey(int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(!loaded) {
loadData();
@ -668,7 +673,7 @@ namespace netgen {
}
if(!created) {
View = Id;
if(lcenterover) viewctr.h = lcenterover->master;
if(centerover) viewctr.h = centerover->master;
else viewctr.h = cwt.c->master;
playermoved = false;
dataFromHR();
@ -684,17 +689,17 @@ namespace netgen {
}
if(uni == 's') {
View = Id;
if(lcenterover) viewctr.h = lcenterover->master;
if(centerover) viewctr.h = centerover->master;
else viewctr.h = cwt.c->master;
playermoved = false;
}
if(uni == 'c') {
else if(uni == 'c') {
createPapermodel();
addMessage(XLAT("The paper model created as papermodel-*.bmp"));
}
if(uni == 'd') designNet();
if(uni == 't') mode = 2;
if(uni == 'q' || sym == SDLK_ESCAPE)
else if(uni == 'd') designNet();
else if(uni == 't') mode = 2;
else if(uni || sym == SDLK_F10)
cmode = emNormal;
}

467
orbs.cpp
View File

@ -17,10 +17,18 @@ bool markEmpathy(eItem it) {
return true;
}
bool markEmpathy2(eItem it) {
if(items[itOrbEmpathy] < 2) return false;
if(!markOrb2(it)) return false;
markOrb2(itOrbEmpathy);
return true;
}
bool markOrb2(eItem it) {
if(!items[it]) return false;
return markOrb(it);
/* if(!items[it]) return false;
orbused[it] = true;
return items[it] > 1;
return items[it] > 1; */
}
int fixpower(int qty) {
@ -58,32 +66,32 @@ void empathyMove(cell *c, cell *cto, int dir) {
}
bool reduceOrbPower(eItem it, int cap) {
if(items[it] && (lastorbused[it] || (it == itOrbShield && items[it]>3) || !markOrb(itOrbPreserve))) {
items[it] -= numplayers();
if(items[it] && (lastorbused[it] || (it == itOrbShield && items[it]>3) || !markOrb(itOrbTime))) {
items[it] -= multi::activePlayers();
if(isHaunted(cwt.c->land)) survivalist = false;
if(items[it] < 0) items[it] = 0;
if(items[it] > cap) items[it] = cap;
if(items[it] > cap && timerghost) items[it] = cap;
if(items[it] == 0 && it == itOrbLove)
princess::bringBack();
return true;
}
if(items[it] > cap) items[it] = cap;
if(items[it] > cap && timerghost) items[it] = cap;
return false;
}
void reduceOrbPowerAlways(eItem it) {
if(items[it]) {
items[it] -= numplayers();
items[it] -= multi::activePlayers();
if(items[it] < 0) items[it] = 0;
}
}
void reduceOrbPowers() {
if(getMount()) markOrb(itOrbDomination);
if(haveMount()) markOrb(itOrbDomination);
for(int i=0; i<ittypes; i++)
lastorbused[i] = orbused[i], orbused[i] = false;
if(items[itOrbShield]) orbused[itOrbShield] = lastorbused[itOrbShield];
reduceOrbPower(itOrbPreserve, cwt.c->land == laCaribbean ? 777 : 150);
reduceOrbPower(itOrbTime, cwt.c->land == laCaribbean ? 777 : 150);
if(invismove && !invisfish) markOrb(itOrbInvis);
reduceOrbPower(itOrbLightning, 777);
reduceOrbPower(itOrbSpeed, 67);
@ -96,19 +104,20 @@ void reduceOrbPowers() {
reduceOrbPower(itOrbDragon, 111);
reduceOrbPower(itOrbPsi, 111);
reduceOrbPower(itOrbInvis, 77);
reduceOrbPower(itOrbGhost, 77);
reduceOrbPower(itOrbAether, 77);
reduceOrbPower(itOrbDigging, 100);
reduceOrbPower(itOrbTeleport, 200);
reduceOrbPower(itOrbTelekinesis, 150);
reduceOrbPower(itOrbSpace, 150);
reduceOrbPowerAlways(itOrbSafety);
reduceOrbPower(itOrbThorns, 150);
reduceOrbPower(itOrbWater, 150);
reduceOrbPower(itOrbAir, 150);
reduceOrbPower(itOrbFrog, 77);
reduceOrbPower(itOrbDash, 77);
reduceOrbPower(itOrbDiscord, 67);
reduceOrbPower(itOrbSummon, 333);
reduceOrbPower(itOrbMatter, 333);
reduceOrbPower(itOrbFish, 77);
reduceOrbPower(itOrbFish, 57 + 20 * multi::activePlayers());
if(!items[itSavedPrincess]) items[itOrbLove] = 0;
reduceOrbPower(itOrbLove, 777);
reduceOrbPower(itOrbStunning, 100);
@ -117,13 +126,21 @@ void reduceOrbPowers() {
reduceOrbPower(itOrbFreedom, 77);
reduceOrbPower(itOrbEmpathy, 77);
markOrb(itOrb37); reduceOrbPower(itOrb37, 333);
reduceOrbPower(itOrbSkunk, 77);
reduceOrbPower(itOrbBeauty, 77);
reduceOrbPower(itOrbEnergy, 77);
reduceOrbPower(itOrbDomination, 120);
reduceOrbPower(itOrbSword, 100 + 20 * multi::activePlayers());
reduceOrbPower(itOrbSword2, 100 + 20 * multi::activePlayers());
reduceOrbPower(itOrbStone, 120);
reduceOrbPower(itOrbNature, 120);
reduceOrbPower(itOrbRecall, 77);
reduceOrbPower(itOrbBull, 120);
reduceOrbPower(itOrbHorns, 77);
if(cwt.c->land != laWildWest)
reduceOrbPower(itRevolver, 6);
whirlwind::calcdirs(cwt.c);
items[itStrongWind] = !items[itOrbGhost] && whirlwind::qdirs == 1 && !euclid;
items[itStrongWind] = !items[itOrbAether] && whirlwind::qdirs == 1 && !euclid;
items[itWarning] = 0;
}
void flashAlchemist(cell *c) {
@ -135,11 +152,12 @@ void flashAlchemist(cell *c) {
}
}
void flashCell(cell *c, bool msg) {
void flashCell(cell *c, eMonster killer, flagtype flags) {
eWall ow = c->wall;
flashAlchemist(c);
if(msg && c->monst && !isWorm(c) && c->monst != moShadow)
if((flags & AF_MSG) && c->monst && !isWorm(c) && c->monst != moShadow)
addMessage(XLAT("%The1 is destroyed by the Flash.", c->monst));
killWithMessage(c, false);
if(c->monst || isPlayerOn(c)) attackMonster(c, flags, killer);
if(isIcyLand(c))
HEAT(c) += 2;
if(c->land == laDryForest)
@ -147,6 +165,7 @@ void flashCell(cell *c, bool msg) {
if(c->wall == waCavewall) c->wall = waCavefloor;
if(c->wall == waDeadTroll) c->wall = waCavefloor;
if(c->wall == waDeadTroll2) c->wall = waNone;
if(c->wall == waPetrified) c->wall = waNone;
if(c->wall == waDeadfloor2) c->wall = waDeadfloor;
if(c->wall == waGargoyleFloor) c->wall = waChasm;
if(c->wall == waGargoyleBridge) placeWater(c, c);
@ -174,26 +193,35 @@ void flashCell(cell *c, bool msg) {
eWall w = c->wall;
c->wall = waNone;
for(int i=0; i<c->type; i++) if(c->mov[i] && c->mov[i]->wall == w)
flashCell(c->mov[i], msg);
flashCell(c->mov[i], killer, flags);
}
if(c->wall == waRed1) c->wall = waNone;
else if(c->wall == waRed2) c->wall = waRed1;
else if(c->wall == waRed3) c->wall = waRed2;
if(c->wall == waBarrowWall) c->wall = waBarrowDig;
else if(c->wall == waBarrowDig) c->wall = waNone;
if(c->wall != ow && ow) drawParticles(c, winf[ow].color, 16);
if(hasTimeout(c) && c->wparam < 77) c->wparam = 77;
if(isActivable(c))
activateActiv(c, false);
}
void activateFlashFrom(cell *cf) {
void activateFlashFrom(cell *cf, eMonster who, flagtype flags) {
drawFlash(cf);
playSound(cf, "storm");
for(int i=0; i<size(dcal); i++) {
cell *c = dcal[i];
if(c == cf) continue;
for(int t=0; t<c->type; t++)
for(int u=0; u<cf->type; u++)
if(c->mov[t] == cf->mov[u] && c->mov[t] != NULL) {
flashCell(c, true);
flashCell(c, who, flags);
goto nexti;
}
nexti: ;
}
}
@ -233,50 +261,75 @@ void checkFreedom(cell *cf) {
}
addMessage(XLAT("Your %1 activates!", itOrbFreedom));
drainOrb(itOrbFreedom);
drawBigFlash(cf);
for(int i=0; i<numplayers(); i++)
drawBigFlash(playerpos(i));
for(int i=0; i<size(dcal); i++) {
cell *c = dcal[i];
if(c == cf) continue;
if(c == cf && !shmup::on) continue;
if(c->cpdist > 5) break;
flashCell(c, true);
flashCell(c, moPlayer, AF_MAGIC);
}
}
void activateFlash() {
int tk = tkills();
drawFlash(cwt.c);
for(int i=0; i<numplayers(); i++)
drawFlash(playerpos(i));
addMessage(XLAT("You activate the Flash spell!"));
playSound(cwt.c, "storm");
drainOrb(itOrbFlash);
for(int i=0; i<size(dcal); i++) {
cell *c = dcal[i];
if(c->cpdist > 2) break;
flashCell(c, false);
flashCell(c, moPlayer, AF_MAGIC);
}
achievement_count("FLASH", tkills(), tk);
}
bool barrierAt(cellwalker& c, int d) {
if(d >= 7) return true;
if(d <= -7) return true;
bool reflectingBarrierAt(cell *c) {
return
c->wall == waBarrier || c->wall == waCamelot ||
c->wall == waPalace || c->wall == waPlatform ||
c->wall == waTempWall || c->wall == waWarpGate || c->wall == waBarrowDig || c->wall == waBarrowWall;
}
bool reflectingBarrierAt(cellwalker& c, int d) {
if(d >= 3) return true;
if(d <= -3) return true;
d = c.spin + d + 42;
d%=c.c->type;
if(!c.c->mov[d]) return true;
return reflectingBarrierAt(c.c->mov[d]);
// WAS:
// if(c.c->mov[d]->wall == waBarrier) return true;
if(c.c->mov[d]->land == laBarrier || c.c->mov[d]->land == laOceanWall ||
c.c->mov[d]->land == laHauntedWall ||
c.c->mov[d]->land == laElementalWall) return true;
return false;
// THEN:
// if(c.c->mov[d]->land == laBarrier || c.c->mov[d]->land == laOceanWall ||
// c.c->mov[d]->land == laHauntedWall ||
// c.c->mov[d]->land == laElementalWall) ;
// return false;
}
void killAdjacentSharks(cell *c) {
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
if(c2 && isShark(c2->monst)) {
if(!c2) continue;
if(isShark(c2->monst)) {
c2->ligon = true;
killMonster(c2);
killMonster(c2, moLightningBolt);
killAdjacentSharks(c2);
}
if(isKraken(c2->monst) && isWatery(c2)) {
cell *c3 = kraken::head(c2);
c3->ligon = true;
forCellEx(c4, c3) killMonster(c4, moLightningBolt); // kill-all
forCellEx(c4, c3) if(isWatery(c4)) {
c4->ligon = true;
killAdjacentSharks(c4);
}
}
}
}
@ -293,20 +346,23 @@ void castLightningBolt(cellwalker lig) {
cell *c = lig.c;
eWall ow = c->wall;
flashAlchemist(c);
if(c->monst == moMetalBeast2 && !c->item) c->item = itFulgurite;
killWithMessage(c, false);
if(c->monst) attackMonster(c, AF_MAGIC, moLightningBolt);
if(isIcyLand(c)) HEAT(c) += 2;
if(c->land == laDryForest) c->landparam += 2;
bool first = !c->ligon;
c->ligon = 1;
bool brk = false, spin = false;
if(c->wall == waGargoyle) brk = true;
if(c->wall == waCavewall) c->wall = waCavefloor, brk = true;
if(c->wall == waDeadTroll) c->wall = waCavefloor, brk = true;
if(c->wall == waDeadTroll2)c->wall = waNone, brk = true;
if(c->wall == waPetrified) c->wall = waNone, brk = true;
if(c->wall == waDeadfloor2)c->wall = waDeadfloor;
if(c->wall == waRubble) c->wall = waNone;
if(c->wall == waDeadwall) c->wall = waDeadfloor2, brk = true;
@ -315,10 +371,17 @@ void castLightningBolt(cellwalker lig) {
if(c->wall == waIcewall) c->wall = waNone, brk = true;
if(c->wall == waAncientGrave) c->wall = waNone, spin = true;
if(c->wall == waFreshGrave) c->wall = waNone, spin = true;
if(c->wall == waFreshGrave) c->wall = waNone, spin = true;
if(c->wall == waBigStatue) c->wall = waNone, spin = true;
if(c->wall == waColumn) c->wall = waNone, spin = true;
if(c->wall == waStone) c->wall = waNone, brk = true;
if(c->wall == waCanopy || c->wall == waTrunk || c->wall == waBigBush || c->wall == waSmallBush) {
makeflame(c, 12, false); brk = true;
}
if(c->wall == waGrounded) brk = true;
if(c->wall == waFan) spin = true;
if(c->wall == waMetal) c->wall = waCharged, brk = true;
@ -357,18 +420,22 @@ void castLightningBolt(cellwalker lig) {
brk = true;
}
if(c->wall != ow && ow)
drawParticles(c, winf[ow].color, 16);
if(c == cwt.c) {bnc++; if(bnc > 10) break; }
if(spin) cwspin(lig, hrand(lig.c->type));
if(brk) break;
if(c->wall == waBarrier || c->wall == waCamelot || c->wall == waPalace || c->wall == waPlatform ||
c->wall == waTempWall || c->wall == waWarpGate) {
if(reflectingBarrierAt(c)) {
int left = -1;
int right = 1;
while(barrierAt(lig, left)) left--;
while(barrierAt(lig, right)) right++;
cwspin(lig, -(right + left));
while(!reflectingBarrierAt(lig, left)) left--;
while(!reflectingBarrierAt(lig, right)) right++;
cwspin(lig, right + left);
if(c->wall == waBarrowWall) c->wall = waBarrowDig;
else if(c->wall == waBarrowDig) c->wall = waNone;
bnc++; if(bnc > 10) break;
}
else {
@ -377,11 +444,13 @@ void castLightningBolt(cellwalker lig) {
}
if(c->wall == waCloud) {
drawParticles(c, winf[ow].color, 16);
c->wall = waNone;
mirror::createMirages(c, lig.spin, moLightningBolt);
}
if(c->wall == waMirror) {
drawParticles(c, winf[ow].color, 16);
c->wall = waNone;
mirror::createMirrors(c, lig.spin, moLightningBolt);
break;
@ -389,6 +458,10 @@ void castLightningBolt(cellwalker lig) {
}
}
void castLightningBoltFrom(cell *c) {
for(int i=0; i<c->type; i++) castLightningBolt(cellwalker(c, i));
}
void activateLightning() {
int tk = tkills();
drawLightning();
@ -397,14 +470,16 @@ void activateLightning() {
for(int i=0; i<size(dcal); i++) if(dcal[i]) dcal[i]->ligon = 0;
drainOrb(itOrbLightning);
for(int i=0; i<cwt.c->type; i++)
castLightningBolt(cellwalker(cwt.c, i));
for(int i=0; i<numplayers(); i++)
castLightningBoltFrom(playerpos(i));
elec::afterOrb = true;
elec::act();
elec::afterOrb = false;
achievement_count("LIGHTNING", tkills(), tk);
playSound(cwt.c, "storm");
}
// roCheck: return orb type if successful, 0 otherwise
@ -423,11 +498,43 @@ bool haveRangedTarget() {
return false;
}
void checkmoveO() {
if(multi::players > 1 && multi::activePlayers() == 1)
multi::checklastmove();
if(multi::players == 1) checkmove();
}
int teleportAction() {
// normal teleport
if(shmup::on || numplayers() == 1) return 1;
// multi-player, but all in -- do nothing
else if(numplayers() == multi::activePlayers()) return 0;
// otherwise teleport to the game
else return 2;
}
void teleportTo(cell *dest) {
playSound(dest, "other-teleport");
if(dest->monst) {
cwt.c->monst = dest->monst;
dest->monst = moNone;
}
if(teleportAction() == 2) {
bool b = multiRevival(dest, NULL);
if(b) {
killFriendlyIvy();
drainOrb(itOrbTeleport);
movecost(cwt.c, dest);
playerMoveEffects(cwt.c, dest);
for(int i=9; i>=0; i--)
setdist(dest, i, NULL);
bfs();
}
return;
}
killFriendlyIvy();
movecost(cwt.c, dest);
playerMoveEffects(cwt.c, dest);
cwt.c = dest; cwt.spin = hrand(dest->type); flipplayer = !!(hrand(2));
@ -441,19 +548,32 @@ void teleportTo(cell *dest) {
bfs();
items[itOrbSword] = 0;
items[itOrbSword2] = 0;
if(shmup::on)
shmup::teleported();
else
checkmove();
checkmoveO();
}
void jumpTo(cell *dest, eItem byWhat) {
void jumpTo(cell *dest, eItem byWhat, int bonuskill = 0, eMonster dashmon = moNone) {
if(byWhat != itStrongWind) playSound(dest, "orb-frog");
movecost(cwt.c, dest);
killFriendlyIvy();
cell *c1 = cwt.c;
animateMovement(cwt.c, dest, LAYER_SMALL);
cwt.c = dest;
forCellIdEx(c2, i, dest) if(c2->cpdist < dest->cpdist) {
cwt.spin = i;
flipplayer = true;
}
countLocalTreasure();
items[itOrbSword] = 0;
items[itOrbSword2] = 0;
stabbingAttack(c1, dest, moPlayer, bonuskill);
playerMoveEffects(c1, dest);
if(cwt.c->item != itOrbYendor && cwt.c->item != itHolyGrail)
collectItem(cwt.c, true);
@ -463,6 +583,11 @@ void jumpTo(cell *dest, eItem byWhat) {
addMessage(XLAT("You jump!"));
}
if(byWhat == itOrbDash) {
useupOrb(itOrbDash, 5);
addMessage(XLAT("You vault over %the1!", dashmon));
}
mirror::destroy();
for(int i=9; i>=0; i--)
@ -476,6 +601,19 @@ void jumpTo(cell *dest, eItem byWhat) {
monstersTurn();
}
void growIvyTo(cell *dest, cell *src) {
if(dest->monst)
attackMonster(dest, AF_MSG | AF_ORSTUN, moFriendlyIvy);
else {
dest->monst = moFriendlyIvy;
dest->mondir = neighborId(dest, src);
moveEffect(dest, src, moFriendlyIvy);
empathyMove(src, dest, neighborId(src, dest));
}
createNoise(1);
monstersTurn();
}
void telekinesis(cell *dest) {
int cost = dest->cpdist * dest->cpdist;
@ -493,17 +631,17 @@ void telekinesis(cell *dest) {
}
if(dest->wall == waGlass) {
drainOrb(itOrbTelekinesis);
drainOrb(itOrbSpace);
addMessage(XLAT("Your power is drained by %the1!", dest->wall));
}
moveItem(dest, cwt.c, true);
collectItem(cwt.c, true);
useupOrb(itOrbTelekinesis, cost);
useupOrb(itOrbSpace, cost);
createNoise(3);
bfs();
if(!shmup::on) checkmove();
if(!shmup::on) checkmoveO();
}
eMonster summonedAt(cell *dest) {
@ -517,7 +655,7 @@ eMonster summonedAt(cell *dest) {
if(dest->wall == waAncientGrave || dest->wall == waFreshGrave)
return moGhost;
if(dest->wall == waClosePlate || dest->wall == waOpenPlate)
return moPalace;
return dest->land == laPalace ? moPalace : moBat;
if(dest->wall == waFloorA || dest->wall == waFloorB)
return moSlime;
if(dest->wall == waFloorC || dest->wall == waFloorD)
@ -536,8 +674,11 @@ eMonster summonedAt(cell *dest) {
return
isElemental(dest->land) ? moWaterElemental :
dest->land == laLivefjord ? moViking :
dest->land == laGridCoast ? moRatling :
dest->land == laKraken ? moViking :
dest->land == laWarpCoast ? moRatling :
moPirate;
if(isReptile(dest->wall))
return moReptile;
if(dest->wall == waChasm)
return moAirElemental;
if(isFire(dest))
@ -561,6 +702,12 @@ eMonster summonedAt(cell *dest) {
if(dest->wall == waGiantRug)
return moVizier;
if(dest->wall == waNone) {
if(dest->land == laBull) return moRagingBull;
if(dest->land == laPrairie) return moAirElemental;
if(dest->land == laZebra) return moAirElemental;
if(dest->land == laMirror) return moAirElemental;
if(dest->land == laMountain) return moAirElemental; // unfortunately Ivies are too large
if(dest->land == laDungeon) return moBat;
if(dest->land == laIce) return moFireElemental;
if(dest->land == laDesert) return moEarthElemental;
if(dest->land == laJungle) return moWaterElemental;
@ -587,20 +734,24 @@ eMonster summonedAt(cell *dest) {
if(dest->land == laWildWest) return moOutlaw;
if(dest->land == laClearing) return moForestTroll;
if(dest->land == laWhirlwind) return moAirElemental;
if(dest->land == laGridCoast) return moRatling;
if(dest->land == laWarpCoast) return moRatling;
if(dest->land == laRose) return moRoseLady;
if(dest->land == laDragon) return moFireElemental;
if(dest->land == laTortoise) return moTortoise;
if(dest->land == laBurial) return moEarthElemental;
if(isHaunted(dest->land)) return moGhost;
}
return moNone;
}
void summonAt(cell *dest) {
playSound(dest, "orb-ranged");
dest->monst = summonedAt(dest);
dest->stuntime = 3;
if(dest->monst == moPirate || dest->monst == moViking || (dest->monst == moRatling && dest->wall == waSea))
dest->wall = waBoat;
dest->wall = waBoat, dest->item = itNone;
if(dest->monst == moViking && dest->land == laKraken)
dest->item = itOrbFish;
if(dest->wall == waStrandedBoat)
dest->wall = waBoat;
else if(dest->monst == moWaterElemental)
@ -620,7 +771,7 @@ void summonAt(cell *dest) {
useupOrb(itOrbSummon, 20);
createNoise(2);
bfs();
checkmove();
checkmoveO();
}
bool tempWallPossibleAt(cell *dest) {
@ -641,46 +792,61 @@ void tempWallAt(cell *dest) {
dest->item = itNone; // underwater items are destroyed by this
createNoise(2);
bfs();
checkmove();
checkmoveO();
}
void psi_attack(cell *dest) {
playSound(dest, "other-mind");
if(isNonliving(dest->monst))
addMessage(XLAT("You destroy %the1 with a mental blast!", dest->monst));
else if(isDragon(dest->monst))
else if(isDragon(dest->monst) || isKraken(dest->monst))
addMessage(XLAT("You damage %the1 with a mental blast!", dest->monst));
else
addMessage(XLAT("You kill %the1 with a mental blast!", dest->monst));
killWithMessage(dest, false);
// note: psi attack works with Petrify!
attackMonster(dest, AF_MAGIC, moPlayer);
useupOrb(itOrbPsi, 30);
createNoise(2);
bfs();
checkmove();
checkmoveO();
}
void gun_attack(cell *dest) {
playSound(dest, "orb-ranged");
addMessage(XLAT("You shoot %the1!", dest->monst));
killWithMessage(dest, false);
attackMonster(dest, AF_GUN, moNone);
items[itRevolver] --;
bfs();
checkmove();
checkmoveO();
createNoise(5);
monstersTurn();
}
void stun_attack(cell *dest) {
addMessage(XLAT("You stun %the1!", dest->monst));
dest->stuntime += 5;
void checkStunKill(cell *dest) {
if(isBird(dest->monst)) {
moveEffect(dest, dest, moDeadBird);
if(cellUnstableOrChasm(dest)) dest->wall = waChasm;
if(isWatery(dest) || dest->wall == waChasm || isFire(dest))
killMonster(dest);
doesFall(dest);
if(isWatery(dest) || dest->wall == waChasm || isFire(dest)) {
addMessage(XLAT("%The1 falls!", dest->monst));
fallMonster(dest);
return;
}
}
/* if(!isPermanentFlying(dest->monst) && cellEdgeUnstable(dest)) {
addMessage(XLAT("%The1 falls!", dest->monst));
fallMonster(dest);
} */
}
void stun_attack(cell *dest) {
playSound(dest, "orb-ranged");
addMessage(XLAT("You stun %the1!", dest->monst));
dest->stuntime += 5;
checkStunKill(dest);
useupOrb(itOrbStunning, 10);
createNoise(3);
bfs();
checkmove();
checkmoveO();
}
void placeIllusion(cell *c) {
@ -688,38 +854,48 @@ void placeIllusion(cell *c) {
useupOrb(itOrbIllusion, 5);
addMessage(XLAT("You create an Illusion!"));
bfs();
checkmove();
checkmoveO();
}
void blowoff(cell *cf, cell *ct) {
playSound(ct, "orb-ranged");
addMessage(XLAT("You blow %the1 away!", cf->monst));
pushMonster(ct, cf);
if(cf->item == itBabyTortoise) {
if(!ct->item) {
ct->item = itBabyTortoise;
tortoise::babymap[ct] = tortoise::babymap[cf];
}
tortoise::babymap.erase(cf);
cf->item = itNone;
}
if(cf->item == itBabyTortoise && !ct->item)
moveItem(cf, ct, true);
items[itOrbAir]--;
createNoise(2);
bfs();
checkmove();
checkmoveO();
}
void useOrbOfDragon(cell *c) {
makeflame(c, 20, false);
playSound(c, "fire");
addMessage(XLAT("You throw fire!"));
useupOrb(itOrbDragon, 5);
createNoise(3);
bfs();
checkmove();
checkmoveO();
}
int monstersnearO(orbAction a, cell *c, cell *nocount, eMonster who, cell *pushto, cell *comefrom) {
// printf("[a = %d] ", a);
if(shmup::on) return 0;
if(a == roCheck && multi::players > 1)
return 1;
else if(a == roMultiCheck) return 0;
else return monstersnear(c, nocount, who, pushto, comefrom);
}
bool isCheck(orbAction a) { return a == roCheck || a == roMultiCheck; }
bool isWeakCheck(orbAction a) { return a == roCheck || a == roMultiCheck || a == roMouse; }
eItem targetRangedOrb(cell *c, orbAction a) {
if(!haveRangedOrb()) return itNone;
if(!haveRangedOrb()) {
return itNone;
}
if(rosedist(cwt.c) == 1) {
int r = rosemap[cwt.c];
@ -739,37 +915,38 @@ eItem targetRangedOrb(cell *c, orbAction a) {
// (-1) distance
if(c == cwt.c || isNeighbor(cwt.c, c)) {
if(a == roKeyboard || a == roMouseForce )
if(!isWeakCheck(a))
addMessage(XLAT("You cannot target that close!"));
return itNone;
}
if(c->cpdist > 7) {
if(a != roCheck)
if(!isWeakCheck(a))
addMessage(XLAT("You cannot target that far away!"));
return itNone;
}
// (0-) strong wind
if(items[itStrongWind] && c->cpdist == 2 && cwt.c == whirlwind::jumpFromWhereTo(c, true) && !monstersnear(c, NULL, moPlayer, NULL, cwt.c)) {
if(a != roCheck) jumpTo(c, itStrongWind);
if(items[itStrongWind] && c->cpdist == 2 && cwt.c == whirlwind::jumpFromWhereTo(c, true) && !monstersnearO(a, c, NULL, moPlayer, NULL, cwt.c)) {
if(!isCheck(a)) jumpTo(c, itStrongWind);
return itStrongWind;
}
// (0x) control
if(getMount() && items[itOrbDomination] && dragon::whichturn != turncount) {
if(a != roCheck) {
if(haveMount() && items[itOrbDomination] && dragon::whichturn != turncount) {
if(!isCheck(a)) {
dragon::target = c;
dragon::whichturn = turncount;
addMessage(XLAT("Commanded %the1!", getMount()));
checkmove();
addMessage(XLAT("Commanded %the1!", haveMount()));
checkmoveO();
}
return itOrbDomination;
}
// (0) telekinesis
if(c->item && !itemHidden(c) && !cwt.c->item && items[itOrbTelekinesis] >= fixpower(c->cpdist * c->cpdist) && !cantGetGrimoire(c, a != roCheck)) {
if(a != roCheck) telekinesis(c);
return itOrbTelekinesis;
if(c->item && !itemHiddenFromSight(c) && !cwt.c->item && items[itOrbSpace] >= fixpower(c->cpdist * c->cpdist) && !cantGetGrimoire(c, !isCheck(a))
&& c->item != itBarrow) {
if(!isCheck(a)) telekinesis(c);
return itOrbSpace;
}
// (0') air blow
@ -781,20 +958,77 @@ eItem targetRangedOrb(cell *c, orbAction a) {
nowhereToBlow = true;
cell *c2 = c->mov[e % c->type];
if(c2 && c2->cpdist > c->cpdist && passable(c2, c, P_BLOW)) {
if(a != roCheck) blowoff(c, c2);
if(!isCheck(a)) blowoff(c, c2);
return itOrbAir;
}
}
}
// nature
if(items[itOrbNature] && numplayers() == 1 && c->monst != moFriendlyIvy) {
cell *sides[8];
int qsides = 0;
forCellCM(cf, c)
if(cf->monst == moFriendlyIvy) {
if(c->monst) {
if(!canAttack(c, c->monst, cf, moFriendlyIvy, 0)) continue;
if(monstersnear(cwt.c, c, moPlayer, NULL, cwt.c)) continue;
}
else {
if(!passable(c, cf, P_ISPLAYER | P_MONSTER)) continue;
if(strictlyAgainstGravity(c, cf, false, MF_IVY)) continue;
if(monstersnear(cwt.c, NULL, moPlayer, c, cwt.c)) continue;
}
sides[qsides++] = cf;
}
if(qsides > 0) {
if(!isCheck(a)) growIvyTo(c, sides[hrand(qsides)]);
return itOrbNature;
}
}
// (0'') jump
int jumpstate = 0;
cell *jumpthru = NULL;
// orb of vaulting
if(!shmup::on && items[itOrbDash] && c->cpdist == 2) {
int i = items[itOrbAether];
if(i) items[itOrbAether] = i-1;
cell *c2 = NULL, *c3 = NULL;
for(int i=0; i<cwt.c->type; i++) {
cell *cc = cwt.c->mov[i];
if(isNeighbor(cc, c)) c3 = c2, c2 = cc;
}
jumpthru = c2;
jumpstate = 10;
if(jumpstate == 10 && c2) jumpstate = 11;
if(jumpstate == 11 && c2->monst) jumpstate = 12;
if(jumpstate == 12 && !c3) jumpstate = 13;
if(jumpstate == 13 && passable(c2, cwt.c, P_ISPLAYER | P_JUMP1 | P_MONSTER)) jumpstate = 14;
if(jumpstate == 14 && passable(c, c2, P_ISPLAYER | P_JUMP2)) jumpstate = 15;
if(jumpstate == 15 && canAttack(cwt.c, moPlayer, c2, c2->monst, 0)) jumpstate = 16;
if(jumpstate == 16 && !monstersnearO(a, c, c2, moPlayer, NULL, cwt.c)) jumpstate = 17;
items[itOrbAether] = i;
if(jumpstate == 17) {
if(!isCheck(a)) {
int k = tkills();
eMonster m = c2->monst;
attackMonster(c2, AF_ORSTUN | AF_MSG, moPlayer);
k = tkills() - k;
jumpTo(c, itOrbDash, k, m);
}
return itOrbDash;
}
}
if(items[itOrbFrog] && c->cpdist == 2) {
jumpstate = 1;
int i = items[itOrbGhost];
if(i) items[itOrbGhost] = i-1;
int i = items[itOrbAether];
if(i) items[itOrbAether] = i-1;
for(int i=0; i<cwt.c->type; i++) {
cell *c2 = cwt.c->mov[i];
if(isNeighbor(c2, c)) {
@ -808,36 +1042,36 @@ eItem targetRangedOrb(cell *c, orbAction a) {
}
}
}
items[itOrbGhost] = i;
if(jumpstate == 3 && !monstersnear(c, NULL, moPlayer, NULL, c)) {
items[itOrbAether] = i;
if(jumpstate == 3 && !monstersnearO(a, c, NULL, moPlayer, NULL, cwt.c)) {
jumpstate = 4;
if(a != roCheck) jumpTo(c, itOrbFrog);
if(!isCheck(a)) jumpTo(c, itOrbFrog);
return itOrbFrog;
}
}
// (1) switch with an illusion
if(items[itOrbTeleport] && c->monst == moIllusion && !cwt.c->monst) {
if(a != roCheck) teleportTo(c);
if(items[itOrbTeleport] && c->monst == moIllusion && !cwt.c->monst && teleportAction() == 1) {
if(!isCheck(a)) teleportTo(c);
return itOrbTeleport;
}
// (2) place illusion
if(!shmup::on && items[itOrbIllusion] && c->monst == moNone && c->item == itNone && passable(c, NULL, P_MIRROR)) {
if(a != roCheck) placeIllusion(c);
if(!isCheck(a)) placeIllusion(c);
return itOrbIllusion;
}
// (3) teleport
if(items[itOrbTeleport] && c->monst == moNone && (c->item == itNone || itemHidden(c)) &&
passable(c, NULL, P_ISPLAYER | P_TELE) && shmup::verifyTeleport()) {
if(a != roCheck) teleportTo(c);
passable(c, NULL, P_ISPLAYER | P_TELE) && teleportAction() && shmup::verifyTeleport()) {
if(!isCheck(a)) teleportTo(c);
return itOrbTeleport;
}
// (4) remove an illusion
if(!shmup::on && items[itOrbIllusion] && c->monst == moIllusion) {
if(a != roCheck) {
if(!isCheck(a)) {
addMessage(XLAT("You take the Illusion away."));
items[itOrbIllusion] += 3; // 100% effective with the Orb of Energy!
c->monst = moNone;
@ -846,43 +1080,43 @@ eItem targetRangedOrb(cell *c, orbAction a) {
}
// (4a) colt
if(!shmup::on && items[itRevolver] && c->monst && canAttack(cwt.c, moPlayer, c, c->monst, AF_GUN) && !monstersnear(cwt.c, c, moPlayer, NULL, cwt.c)
&& c->pathdist <= GUNRANGE) {
if(a != roCheck) gun_attack(c);
if(!shmup::on && items[itRevolver] && c->monst && canAttack(cwt.c, moPlayer, c, c->monst, AF_GUN)
&& c->pathdist <= GUNRANGE && !monstersnearO(a, cwt.c, c, moPlayer, NULL, cwt.c)) {
if(!isCheck(a)) gun_attack(c);
return itRevolver;
}
// (5) psi blast (non-shmup variant)
if(!shmup::on && items[itOrbPsi] && c->monst && (isDragon(c->monst) || !isWorm(c)) && c->monst != moShadow) {
if(a != roCheck) psi_attack(c);
if(!shmup::on && items[itOrbPsi] && c->monst && (isDragon(c->monst) || !isWorm(c)) && c->monst != moShadow && c->monst != moKrakenH) {
if(!isCheck(a)) psi_attack(c);
return itOrbPsi;
}
// (5a) summoning
if(items[itOrbSummon] && summonedAt(c)) {
if(a != roCheck) summonAt(c);
if(!isCheck(a)) summonAt(c);
return itOrbSummon;
}
// (5b) matter
if(items[itOrbMatter] && tempWallPossibleAt(c)) {
if(a != roCheck) tempWallAt(c);
if(!isCheck(a)) tempWallAt(c);
return itOrbMatter;
}
// (5c) stun
if(items[itOrbStunning] && c->monst && !isMultitile(c->monst) && c->stuntime < 3 && !shmup::on) {
if(a != roCheck) stun_attack(c);
if(!isCheck(a)) stun_attack(c);
return itOrbStunning;
}
// (6) place fire (non-shmup variant)
if(!shmup::on && items[itOrbDragon] && makeflame(c, 20, true)) {
if(a != roCheck) useOrbOfDragon(c);
if(!isCheck(a)) useOrbOfDragon(c);
return itOrbDragon;
}
if(a == roCheck) return itNone;
if(isWeakCheck(a)) return itNone;
if(nowhereToBlow) {
addMessage(XLAT("Nowhere to blow %the1!", c->monst));
@ -911,7 +1145,11 @@ eItem targetRangedOrb(cell *c, orbAction a) {
else if(items[itOrbTeleport] && c->monst) {
addMessage(XLAT("Cannot teleport on a monster!"));
}
else if(c->item && items[itOrbTelekinesis]) {
else if(items[itOrbSpace] && c->item == itBarrow)
addMessage(XLAT("%The1 is protected from this kind of magic!", c->item));
else if(c->item && items[itOrbSpace] && !itemHiddenFromSight(c)) {
if(cwt.c->item)
addMessage(XLAT("Cannot use %the1 here!", itOrbSpace));
addMessage(XLAT("Not enough power for telekinesis!"));
}
else if(items[itOrbIllusion] && c->item)
@ -920,6 +1158,9 @@ eItem targetRangedOrb(cell *c, orbAction a) {
addMessage(XLAT("Cannot cast illusion on a monster!"));
else if(items[itOrbIllusion] && !passable(c, NULL, P_MIRROR))
addMessage(XLAT("Cannot cast illusion here!"));
else if(items[itOrbTeleport] && teleportAction() == 0) {
addMessage(XLAT("All players are in the game!"));
}
else if(items[itOrbTeleport] && !passable(c, NULL, P_TELE | P_ISPLAYER)) {
addMessage(XLAT("Cannot teleport here!"));
}

11854
polygons.cpp

File diff suppressed because it is too large Load Diff

1260
rogueviz.cpp Normal file

File diff suppressed because it is too large Load Diff

74
rug.cpp
View File

@ -3,7 +3,7 @@
// implementation of the Hypersian Rug mode
#ifndef MOBILE
#ifndef NORUG
#define TEXTURESIZE (texturesize)
#define HTEXTURESIZE (texturesize/2)
@ -54,6 +54,7 @@ struct rugpoint {
double x1, y1;
bool valid;
bool inqueue;
double dist;
hyperpoint h;
hyperpoint flat;
vector<edge> edges;
@ -72,13 +73,9 @@ vector<triangle> triangles;
// construct the graph
//---------------------
map<cell*, hyperpoint> gmatrix;
void buildVertexInfo(cell *c, transmatrix V) { if(genrug) gmatrix[c] = V*C0; }
int hyprand;
rugpoint *addRugpoint(hyperpoint h) {
rugpoint *addRugpoint(hyperpoint h, double dist) {
rugpoint *m = new rugpoint;
m->h = h;
@ -89,14 +86,15 @@ rugpoint *addRugpoint(hyperpoint h) {
hpxyz(h[0], h[1], (h[2]-1) * (rand() % 1000 - rand() % 1000) / 1000);
m->valid = false;
m->inqueue = false;
m->dist = dist;
points.push_back(m);
return m;
}
rugpoint *findRugpoint(hyperpoint h) {
rugpoint *findRugpoint(hyperpoint h, double dist) {
for(int i=0; i<size(points); i++)
if(intval(points[i]->h, h) < 1e-5) return points[i];
return addRugpoint(h);
return addRugpoint(h, dist);
}
void addNewEdge(rugpoint *e1, rugpoint *e2) {
@ -116,9 +114,9 @@ void addTriangle(rugpoint *t1, rugpoint *t2, rugpoint *t3) {
}
void addTriangle1(rugpoint *t1, rugpoint *t2, rugpoint *t3) {
rugpoint *t12 = findRugpoint(mid(t1->h, t2->h));
rugpoint *t23 = findRugpoint(mid(t2->h, t3->h));
rugpoint *t31 = findRugpoint(mid(t3->h, t1->h));
rugpoint *t12 = findRugpoint(mid(t1->h, t2->h), (t1->dist+ t2->dist)/2);
rugpoint *t23 = findRugpoint(mid(t2->h, t3->h), (t1->dist+ t2->dist)/2);
rugpoint *t31 = findRugpoint(mid(t3->h, t1->h), (t1->dist+ t2->dist)/2);
addTriangle(t1, t12, t31);
addTriangle(t12, t2, t23);
addTriangle(t23, t3, t31);
@ -140,7 +138,7 @@ void buildRug() {
for(int i=0; i<size(dcal); i++)
if(gmatrix.count(dcal[i]))
vptr[dcal[i]] = addRugpoint(gmatrix[dcal[i]]);
vptr[dcal[i]] = addRugpoint(gmatrix[dcal[i]]*C0, dcal[i]->cpdist);
for(int i=0; i<size(dcal); i++) {
cell *c = dcal[i];
@ -232,7 +230,9 @@ void preset(rugpoint *m) {
// m->h = a->h + (b->h-a->h) * az - ah * ort
hyperpoint res = a->flat + (b->flat-a->flat) * az - ah * ort;
for(int i=0; i<3; i++) h[i] += res[i]; q++;
for(int i=0; i<3; i++) h[i] += res[i];
q++;
}
}
@ -255,7 +255,7 @@ void subdivide() {
rugpoint *m = points[i];
for(int j=0; j<size(m->edges); j++) {
rugpoint *m2 = m->edges[j].target;
rugpoint *mm = addRugpoint(mid(m->h, m2->h));
rugpoint *mm = addRugpoint(mid(m->h, m2->h), (m->dist+m2->dist)/2);
using namespace hyperpoint_vec;
mm->flat = (m->flat + m2->flat) / 2;
mm->valid = true; qvalid++;
@ -334,6 +334,8 @@ void drawTriangle(triangle& t) {
rugpoint& m2 = *t.m[1];
rugpoint& m3 = *t.m[2];
if(!m1.valid || !m2.valid || !m3.valid) return;
if(m1.dist >= sightrange+.51 || m2.dist >= sightrange+.51 || m3.dist >= sightrange+.51)
return;
dt++;
double x1, y1, z1;
double x2, y2, z2;
@ -376,6 +378,7 @@ Uint32 *expanded_data;
void initTexture() {
if(!rendernogl) {
#ifndef PANDORA
FramebufferName = 0;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
@ -400,6 +403,7 @@ void initTexture() {
addMessage("Failed to initialize the framebuffer");
rugged = false;
}
#endif
}
else {
texture = SDL_CreateRGBSurface(SDL_SWSURFACE,TEXTURESIZE,TEXTURESIZE,32,0,0,0,0);
@ -430,6 +434,7 @@ void prepareTexture() {
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, TEXTURESIZE, TEXTURESIZE, 0, GL_BGRA, GL_UNSIGNED_BYTE, expanded_data );
}
else {
#ifndef PANDORA
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
glViewport(0,0,TEXTURESIZE,TEXTURESIZE);
@ -438,7 +443,8 @@ void prepareTexture() {
drawthemap();
if(!renderonce) queueline(C0, mouseh, 0xFF00FF);
drawqueue();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
#endif
}
vid = svid;
if(!rendernogl) glViewport(0,0,vid.xres,vid.yres);
@ -451,9 +457,11 @@ void closeTexture() {
delete[] expanded_data;
}
else {
#ifndef PANDORA
glDeleteTextures(1, &renderedTexture);
glDeleteRenderbuffers(1, &depth_stencil_rb);
glDeleteFramebuffers(1, &FramebufferName);
#endif
}
}
@ -489,6 +497,7 @@ void drawRugScene() {
glDisable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
xview = vid.xres/(vid.radius*scale);
yview = vid.yres/(vid.radius*scale);
@ -563,7 +572,6 @@ void init() {
if(renderonce) prepareTexture();
if(!rugged) return;
gmatrix.clear();
genrug = true;
drawthemap();
genrug = false;
@ -581,7 +589,6 @@ void close() {
triangles.clear();
for(int i=0; i<size(points); i++) delete points[i];
points.clear();
gmatrix.clear();
pqueue = queue<rugpoint*> ();
}
@ -637,16 +644,22 @@ hyperpoint gethyper(ld x, ld y) {
}
void show() {
displayStat( 0, XLAT("hypersian rug mode"), "", ' ');
displayStat( 2, XLAT("what's this?"), "", 'h');
displayStat( 3, XLAT("take me back"), "", 'q');
displayStat( 4, XLAT("enable the Hypersian Rug mode"), "", 'u');
displayStat( 6, XLAT("render the texture only once"), ONOFF(renderonce), 'o');
displayStat( 7, XLAT("render texture without OpenGL"), ONOFF(rendernogl), 'g');
displayStat( 8, XLAT("texture size"), its(texturesize)+"x"+its(texturesize), 's');
dialog::init(XLAT("hypersian rug mode"), iinf[itPalace].color, 150, 100);
dialog::addItem(XLAT("what's this?"), 'h');
dialog::addItem(XLAT("take me back"), 'q');
dialog::addItem(XLAT("enable the Hypersian Rug mode"), 'u');
dialog::addBoolItem(XLAT("render the texture only once"), (renderonce), 'o');
dialog::addBoolItem(XLAT("render texture without OpenGL"), (rendernogl), 'g');
dialog::addSelItem(XLAT("texture size"), its(texturesize)+"x"+its(texturesize), 's');
dialog::display();
}
void handleKey(int uni, int sym) {
void handleKey(int sym, int uni) {
#ifdef PANDORA
rendernogl = true;
#endif
dialog::handleNavigation(sym, uni);
if(uni == 'h') {
lastmode = cmode;
@ -661,19 +674,24 @@ void handleKey(int uni, int sym) {
"Use arrow keys to rotate, Page Up/Down to zoom.";
}
else if(uni == 'u') {
if(sphere) restartGame('E');
if(euclid) restartGame('e');
rug::init();
cmode = emNormal;
}
else if(uni == 'q')
cmode = emChangeMode;
else if(uni == 'o')
renderonce = !renderonce;
#ifndef PANDORA
else if(uni == 'g')
rendernogl = !rendernogl;
#endif
else if(uni == 's') {
texturesize = 2*texturesize;
texturesize *= 2;
if(texturesize == 8192) texturesize = 128;
dialog::scaleLog();
}
else if(uni || sym == SDLK_F10)
cmode = emChangeMode;
}
void select() {

2146
shmup.cpp

File diff suppressed because it is too large Load Diff

207
sound.cpp Normal file
View File

@ -0,0 +1,207 @@
bool audio;
string musiclicense;
string musfname[landtypes];
int musicvolume = 60, effvolume = 60;
eLand getCurrentLandForMusic() {
eLand id = cwt.c->land;
if(isHaunted(id)) id = laHaunted;
if(id == laWarpSea) id = laWarpCoast;
return id;
}
void playSeenSound(cell *c) {
if(!c->monst) return;
bool nearme = c->cpdist <= 7;
forCellEx(c2, c) if(c2->cpdist <= 7) nearme = true;
if(!nearme) return;
if(c->monst == moEagle)
playSound(c, "seen-eagle");
else if(c->monst == moEarthElemental)
playSound(c, "seen-earth");
else if(c->monst == moAirElemental)
playSound(c, "seen-air");
else if(c->monst == moWaterElemental)
playSound(c, "seen-water");
else if(c->monst == moFireElemental)
playSound(c, "fire");
else if(c->monst == moDragonHead)
playSound(c, "seen-dragon");
else if(c->monst == moWorm)
playSound(c, "seen-sandworm");
else if(c->monst == moSkeleton && c->land != laDungeon)
playSound(c, "seen-skeleton");
else if(c->monst == moHexSnake)
playSound(c, "seen-snake");
else if(c->monst == moWolf || c->monst == moRedFox)
playSound(c, "seen-woof");
else if(isTroll(c->monst))
playSound(c, "seen-troll");
else if(c->monst == moNecromancer)
playSound(c, "seen-necromancer");
else if(c->monst == moGhost)
playSound(c, "seen-ghost");
else if(c->monst == moRoseBeauty)
playSound(c, princessgender() ? "seen-rosebeauty" : "seen-gardener");
else if(c->monst == moVizier)
playSound(c, "seen-vizier");
else if(c->monst == moFireFairy)
playSound(c, "seen-fairy");
else if(c->monst == moCultist)
playSound(c, "seen-cultist");
else if(c->monst == moPyroCultist)
playSound(c, "seen-cultistfire");
else if(c->monst == moCultistLeader)
playSound(c, "seen-cultistleader");
}
#ifdef SDLAUDIO
bool loaded[landtypes];
Mix_Music* music[landtypes];
int musicpos[landtypes];
int musstart;
int musfadeval = 2000;
eLand cid = laNone;
void handlemusic() {
DEBB(DF_GRAPH, (debugfile,"handle music\n"));
if(audio && musicvolume) {
eLand id = getCurrentLandForMusic();
#ifdef LOCAL
extern bool local_changemusic(eLand& id);
if(local_changemusic(id)) return;
#endif
if(outoffocus) id = eLand(0);
if(musfname[id] == "LAST") id = cid;
if(!loaded[id]) {
loaded[id] = true;
// printf("loading (%d)> %s\n", id, musfname[id].c_str());
if(musfname[id] != "") {
music[id] = Mix_LoadMUS(musfname[id].c_str());
if(!music[id]) {
printf("Mix_LoadMUS: %s\n", Mix_GetError());
}
}
}
if(cid != id && !musfadeval) {
musicpos[cid] = SDL_GetTicks() - musstart;
musfadeval = musicpos[id] ? 500 : 2000;
Mix_FadeOutMusic(musfadeval);
// printf("fadeout %d, pos %d\n", musfadeval, musicpos[cid]);
}
if(music[id] && !Mix_PlayingMusic()) {
cid = id;
Mix_VolumeMusic(musicvolume);
Mix_FadeInMusicPos(music[id], -1, musfadeval, musicpos[id] / 1000.0);
// printf("fadein %d, pos %d\n", musfadeval, musicpos[cid]);
musstart = SDL_GetTicks() - musicpos[id];
musicpos[id] = 0;
musfadeval = 0;
}
}
}
void resetmusic() {
if(audio && musicvolume) {
Mix_FadeOutMusic(3000);
cid = laNone;
for(int i=0; i<landtypes; i++) musicpos[i] = 0;
musfadeval = 2000;
}
}
bool loadMusicInfo(string dir) {
DEBB(DF_INIT, (debugfile,"load music info\n"));
if(dir == "") return false;
FILE *f = fopen(dir.c_str(), "rt");
if(f) {
string dir2;
for(int i=0; i<size(dir); i++) if(dir[i] == '/' || dir[i] == '\\')
dir2 = dir.substr(0, i+1);
char buf[1000];
while(fgets(buf, 800, f) > 0) {
for(int i=0; buf[i]; i++) if(buf[i] == 10 || buf[i] == 13) buf[i] = 0;
if(buf[0] == '[' && buf[3] == ']') {
int id = (buf[1] - '0') * 10 + buf[2] - '0';
if(id >= 0 && id < landtypes) {
if(buf[5] == '*' && buf[6] == '/') musfname[id] = dir2 + (buf+7);
else musfname[id] = buf+5;
}
else {
fprintf(stderr, "warning: bad soundtrack id, use the following format:\n");
fprintf(stderr, "[##] */filename\n");
fprintf(stderr, "where ## are two digits, and */ is optional and replaced by path to the music\n");
fprintf(stderr, "alternatively LAST = reuse the last track instead of having a special one");
}
// printf("[%2d] %s\n", id, buf);
}
else if(buf[0] == '#') {
}
else {
musiclicense += buf;
musiclicense += "\n";
}
}
fclose(f);
return true;
}
return false;
}
bool loadMusicInfo() {
return
loadMusicInfo(musicfile)
|| loadMusicInfo("./hyperrogue-music.txt")
|| loadMusicInfo("music/hyperrogue-music.txt")
// Destination set by ./configure (in the GitHub repository)
#ifdef MUSICDESTDIR
|| loadMusicInfo(MUSICDESTDIR)
#endif
#ifdef FHS
|| loadMusicInfo("/usr/share/hyperrogue/hyperrogue-music.txt")
|| loadMusicInfo(s0 + getenv("HOME") + "/.hyperrogue-music.txt")
#endif
;
}
void initAudio() {
audio = loadMusicInfo();
if(audio) {
if(Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 2048) != 0) {
fprintf(stderr, "Unable to initialize audio: %s\n", Mix_GetError());
audio = false;
}
else {
audio = true;
Mix_AllocateChannels(16);
}
}
}
map<string, Mix_Chunk*> chunks;
string wheresounds = "sounds/";
void playSound(cell *c, const string& fname, int vol) {
if(effvolume == 0) return;
// printf("Play sound: %s\n", fname.c_str());
if(!chunks.count(fname)) {
string s = wheresounds+fname+".ogg";
chunks[fname] = Mix_LoadWAV(s.c_str());
// printf("Loading, as %p\n", chunks[fname]);
}
Mix_Chunk *chunk = chunks[fname];
if(chunk) {
Mix_VolumeChunk(chunk, effvolume * vol / 100);
Mix_PlayChannel(-1, chunk, 0);
}
}
#else
void resetmusic() {}
#endif

View File

@ -8,6 +8,7 @@ bool cblind;
bool autocheat;
int truelotus;
int gamecount;
time_t timerstart, savetime;
bool timerstopped;
@ -25,26 +26,28 @@ void initgame() {
firstland = safetyland;
}
if(tactic::on && euclid) euclidland = firstland;
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 = origin.c7; cwt.spin = 0;
cwt.c->land = euclid ? euclidland : firstland;
cwt.c = origin.c7; 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))
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)) {
@ -64,9 +67,9 @@ void initgame() {
}
if(tactic::on && firstland == laCaribbean) {
if(hiitemsMax(itRedGem) >= 25) items[itRedGem] = 25;
if(hiitemsMax(itFernFlower) >= 25) items[itFernFlower] = 25;
if(hiitemsMax(itWine) >= 25) items[itWine] = 25;
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)
@ -90,29 +93,69 @@ void initgame() {
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) safety = true;
if(tactic::trailer && cwt.c->land != laClearing) safety = true;
setdist(cwt.c, i, NULL);
if(tactic::trailer) safety = false;
verifycells(&origin);
if(sphere) verifyDodecahedron();
else verifycells(&origin);
}
if(quotient && generateAll(firstland)) {
for(int i=0; i<size(quotientspace::allcells); i++)
setdist(quotientspace::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);
makeEmpty(cwt.c);
multi::revive_queue.clear();
if(shmup::on) shmup::init();
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;
mapeditor::whichPattern = 0;
if(!quotient) mapeditor::whichPattern = 0;
mapeditor::whichShape = 0;
noiseuntil = 0;
sagephase = 0; hardcoreAt = 0;
@ -127,7 +170,8 @@ void initgame() {
if(!randomPatternsMode && !tactic::on && !yendor::on) {
if(firstland != (princess::challenge ? laPalace : laIce)) cheater++;
}
if(princess::challenge) {
if(tactic::trailer) ;
else if(princess::challenge) {
kills[moVizier] = 1;
princess::forceMouse = true;
if(yendor::everwon)
@ -141,25 +185,55 @@ void initgame() {
items[i] = 10;
kills[moYeti] = 1000;
} */
if(ISANDROID && yendor::on && modecode() != 0)
addMessage(XLAT("Note: currently scores are saved only in the normal mode on Android"));
if(ISANDROID && tactic::on)
addMessage(XLAT("Note: you can play, but scores won't be saved on Android"));
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;
}
haverose = false; hadrose = false; rosemap.clear();
havewhat = hadwhat = 0; rosemap.clear();
elec::lightningfast = 0;
lastsafety = gold();
bfs();
}
#define MAXBOX 250
#define POSSCORE 210 // update this when new boxes are added!
bool havesave = true;
#ifndef NOSAVE
#define MAXBOX 300
#define POSSCORE 258 // update this when new boxes are added!
struct score {
string ver;
@ -171,6 +245,7 @@ bool saving, loading, loadingHi;
string boxname[MAXBOX];
bool fakebox[MAXBOX];
bool monsbox[MAXBOX];
void applyBox(int& t) {
if(saving) savebox[boxid++] = t;
@ -181,12 +256,14 @@ void applyBox(int& t) {
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;
}
@ -207,6 +284,7 @@ int applyBoxLoad(string name = "") {
void applyBoxI(eItem it, bool f = false) {
boxname[boxid] = iinf[it].name;
fakebox[boxid] = f;
monsbox[boxid] = false;
if(loadingHi) {
updateHi(it, savebox[boxid++]);
}
@ -216,6 +294,7 @@ void applyBoxI(eItem it, bool f = false) {
void applyBoxM(eMonster m, bool f = false) {
fakebox[boxid] = f;
boxname[boxid] = minf[m].name;
monsbox[boxid] = true;
applyBox(kills[m]);
}
@ -231,7 +310,7 @@ void applyBoxes() {
applyBoxNum(turncount, "turn count");
applyBoxNum(cellcount, "cells generated");
if(!saving) timerstart = time(NULL);
if(loading) timerstart = time(NULL);
for(int i=0; i<itOrbLightning; i++)
if(i == 0) items[i] = 0, applyBoxI(itFernFlower);
@ -240,7 +319,8 @@ void applyBoxes() {
for(int i=0; i<43; i++) {
if(loading) kills[i] = 0;
bool fake =
i == moLesserM || i == moNone || i == moWolfMoved || i == moTentacletail;
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);
@ -252,17 +332,20 @@ void applyBoxes() {
}
if(saving) {
applyBoxSave((int) (savetime + timer - timerstart), "time played");
int totaltime = savetime;
if(!timerstopped) totaltime += timer - timerstart;
applyBoxSave((int) totaltime, "time played");
}
else if(loading) savetime = applyBoxLoad("time played");
else boxid++;
else boxname[boxid] = "time played", boxid++;
if(saving) savecount++;
applyBoxNum(savecount, "number of saves");
if(saving) savecount--;
applyBoxNum(cheater, "number of cheats");
if(saving) applyBoxSave(items[itOrbSafety] ? safetyland : cwt.c->land, "current land");
fakebox[boxid] = true;
if(saving) applyBoxSave(items[itOrbSafety] ? safetyland : cwt.c->land, "");
else if(loading) firstland = safetyland = eLand(applyBoxLoad());
else lostin = eLand(savebox[boxid++]);
@ -275,7 +358,7 @@ void applyBoxes() {
applyBoxI(itPower);
applyBoxI(itOrbFire, true);
applyBoxI(itOrbInvis, true);
applyBoxI(itOrbGhost, true);
applyBoxI(itOrbAether, true);
applyBoxI(itOrbPsi, true);
applyBoxM(moBug0);
applyBoxM(moBug1);
@ -302,20 +385,21 @@ void applyBoxes() {
applyBoxM(moCShark);
applyBoxM(moParrot);
applyBoxI(itPirate);
applyBoxI(itOrbPreserve, true);
applyBoxI(itOrbTime, true);
applyBoxM(moHexSnake);
applyBoxM(moRedTroll);
applyBoxI(itRedGem);
applyBoxI(itOrbTelekinesis, true);
applyBoxI(itOrbSpace, true);
applyBoxBool(euclid, "Euclidean");
int geo = geometry;
applyBoxNum(geo, ""); geometry = eGeometry(geo);
applyBoxBool(hardcore, "hardcore");
applyBoxNum(hardcoreAt, "hardcoreAt");
applyBoxNum(hardcoreAt, "");
applyBoxBool(shmup::on, "shmup");
if(saving) applyBoxSave(euclidland, "euclid land");
else if(loading) euclidland = eLand(applyBoxLoad("euclid land"));
else boxid++;
else fakebox[boxid++] = true;
applyBoxI(itCoast);
applyBoxI(itWhirlpool);
@ -329,8 +413,8 @@ void applyBoxes() {
applyBoxI(itPalace);
applyBoxI(itFjord);
applyBoxI(itOrbFrog);
applyBoxI(itOrbDiscord);
applyBoxI(itOrbFrog, true);
applyBoxI(itOrbDiscord, true);
applyBoxM(moPalace);
applyBoxM(moFatGuard);
applyBoxM(moSkeleton);
@ -340,15 +424,15 @@ void applyBoxes() {
applyBoxM(moWaterElemental);
applyBoxI(itSavedPrincess);
applyBoxI(itOrbLove);
applyBoxI(itOrbLove, true);
applyBoxM(moPrincess);
applyBoxM(moPrincessMoved); // live Princess for Safety
applyBoxM(moPrincessArmedMoved); // live Princess for Safety
applyBoxM(moPrincessMoved, false); // live Princess for Safety
applyBoxM(moPrincessArmedMoved, false); // live Princess for Safety
applyBoxM(moMouse);
applyBoxNum(princess::saveArmedHP, "save armed HP");
applyBoxNum(princess::saveHP, "save HP");
applyBoxNum(princess::saveArmedHP, "");
applyBoxNum(princess::saveHP, "");
applyBoxI(itEdge);
applyBoxI(itIvory);
applyBoxI(itElemental);
applyBoxI(itZebra);
applyBoxI(itFireShard);
@ -358,11 +442,11 @@ void applyBoxes() {
applyBoxM(moAirElemental);
applyBoxM(moFireElemental);
applyBoxM(moEdgeMonkey);
applyBoxM(moFamiliar);
applyBoxM(moGargoyle);
applyBoxM(moOrangeDog);
applyBoxI(itOrbSummon);
applyBoxI(itOrbMatter);
applyBoxI(itOrbSummon, true);
applyBoxI(itOrbMatter, true);
applyBoxM(moForestTroll);
applyBoxM(moStormTroll);
@ -373,11 +457,11 @@ void applyBoxes() {
applyBoxI(itMutant);
applyBoxI(itFulgurite);
applyBoxI(itBounty);
applyBoxI(itOrbLuck);
applyBoxI(itOrbLuck, true);
applyBoxI(itOrbStunning, true);
applyBoxBool(tactic::on, "tactic ON");
applyBoxNum(elec::lightningfast, "lightningfast");
applyBoxBool(tactic::on, "");
applyBoxNum(elec::lightningfast, "");
// if(savebox[boxid]) printf("lotus = %d (lost = %d)\n", savebox[boxid], isHaunted(lostin));
if(loadingHi && isHaunted(lostin)) boxid++;
@ -394,7 +478,7 @@ void applyBoxes() {
else applyBoxNum(truelotus, "lotus/escape");
applyBoxBool(purehepta, "heptagons only");
applyBoxI(itRose);
applyBoxI(itOrbSkunk, true);
applyBoxI(itOrbBeauty, true);
applyBoxI(itCoral);
applyBoxI(itOrb37, true);
applyBoxI(itOrbEnergy, true);
@ -403,22 +487,69 @@ void applyBoxes() {
applyBoxM(moRoseLady);
applyBoxM(moRoseBeauty);
applyBoxBool(chaosmode, "Chaos mode");
applyBoxNum(shmup::players, "shmup players");
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(moKestrel);
applyBoxM(moLemur);
applyBoxM(moSparrowhawk);
applyBoxM(moResearcher);
applyBoxI(itDragon);
applyBoxM(moDragonHead);
applyBoxI(itOrbDomination, true);
applyBoxI(itBabyTortoise);
applyBoxNum(tortoise::seekbits, "tortoise bits");
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() {
@ -432,26 +563,22 @@ void loadBox() {
void loadBoxHigh() {
int sp = shmup::players;
int son = shmup::on;
bool euc = euclid;
bool cha = chaosmode;
bool ph = purehepta;
euclid = savebox[116];
shmup::on = savebox[119];
purehepta = savebox[186];
chaosmode = savebox[196];
shmup::players = savebox[197];
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(shmup::on && !shmup::players) ;
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;
}
purehepta = ph; chaosmode = cha; euclid = euc; shmup::on = son; shmup::players = sp;
}
// certify that saves and achievements were received
@ -473,11 +600,27 @@ namespace anticheat {
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));
#ifndef ANDROID
if(autocheat) return;
if(randomPatternsMode) return;
remove_emergency_save();
FILE *f = fopen(scorefile, "at");
@ -487,16 +630,12 @@ void saveStats(bool emergency = false) {
return;
}
if(saveposition >= 0) {
// addMessage(XLAT("Emergency seek to ") + its(saveposition));
fseek(f, saveposition, SEEK_SET); saveposition = -1;
}
if(emergency) {
saveposition = ftell(f);
// addMessage(XLAT("Emergency save at ") + its(saveposition));
// if(!timerghost) addMessage(XLAT("Emergency save at ") + its(saveposition));
}
if(showoff) return;
if(showoff) { fclose(f); return; }
time_t timer;
timer = time(NULL);
@ -558,11 +697,13 @@ void saveStats(bool emergency = false) {
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 (%d players)\n", shmup::players);
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 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++)
@ -590,15 +731,12 @@ void saveStats(bool emergency = false) {
#ifndef MOBILE
DEBB(DF_INIT, (debugfile, "Game statistics saved to %s\n", scorefile));
addMessage(XLAT("Game statistics saved to %1", scorefile));
if(!tactic::trailer)
addMessage(XLAT("Game statistics saved to %1", scorefile));
#endif
fclose(f);
#endif
}
bool havesave = true;
#ifndef ANDROID
// load the save
void loadsave() {
DEBB(DF_INIT, (debugfile,"loadSave\n"));
@ -606,6 +744,8 @@ void loadsave() {
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;
@ -617,7 +757,9 @@ void loadsave() {
char buf[120];
if(fgets(buf, 120, f) == NULL) break;
if(buf[0] == 'H' && buf[1] == 'y') {
if(fscanf(f, "%s", buf) <= 0) break; sc.ver = buf;
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++) {
@ -661,16 +803,20 @@ void loadsave() {
if(buf[0] == 'Y' && buf[1] == 'E' && buf[2] == 'N') {
char buf1[80], ver[10];
int cid, oy, won, tc, t, ts, cert;
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("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;
}
}
@ -701,14 +847,25 @@ void loadsave() {
void restartGame(char switchWhat) {
DEBB(DF_INIT, (debugfile,"restartGame\n"));
achievement_final(true);
#ifndef NOSAVE
saveStats();
#endif
for(int i=0; i<ittypes; i++) items[i] = 0;
for(int i=0; i<motypes; i++) kills[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;
anticheat::tampered = false; achievementsReceived.clear();
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;
@ -716,20 +873,22 @@ void restartGame(char switchWhat) {
cellcount = 0;
clearMemory();
if(switchWhat == 'C') {
euclid = yendor::on = tactic::on = princess::challenge = false;
geometry = gNormal;
yendor::on = tactic::on = princess::challenge = false;
resetGeometry();
chaosmode = !chaosmode;
}
if(switchWhat == '7') {
if(euclid) euclid = false;
if(euclid) geometry = gNormal;
purehepta = !purehepta;
extern void precalc(); extern void resetGL();
precalc(); resetGL();
resetGeometry();
}
if(switchWhat == 'e') {
extern void precalc(); extern void resetGL();
if(chaosmode) chaosmode = false;
if(purehepta) { purehepta = false; precalc(); resetGL(); }
euclid = !euclid;
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;
@ -794,11 +953,12 @@ void clearGameMemory() {
DEBB(DF_INIT, (debugfile,"clearGameMemory\n"));
pathq.clear();
dcal.clear();
yendor::yii = 0; yendor::yi.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();
@ -809,6 +969,15 @@ void clearGameMemory() {
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;
@ -835,7 +1004,12 @@ eItem randomTreasure2(int cv) {
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 -= 5;
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; }
}
@ -844,10 +1018,20 @@ eItem randomTreasure2(int cv) {
bool isTechnicalLand(eLand l) {
return l == laNone || l == laOceanWall || l == laBarrier || l == laCanvas ||
l == laHauntedWall || l == laHauntedBorder;
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++;
@ -877,7 +1061,7 @@ bool applyCheat(char u, cell *c = NULL) {
}
if(u == 'C') {
cheater++;
activateSafety(laCrossroads);
cheatMoveTo(laCrossroads);
addMessage(XLAT("Activated the Hyperstone Quest!"));
for(int t=1; t<ittypes; t++)
@ -932,11 +1116,7 @@ bool applyCheat(char u, cell *c = NULL) {
return true;
}
if(u == 'R'-64) buildRosemap();
if(u == 'D'-64) {
mapeditor::drawplayer = !mapeditor::drawplayer;
return true;
}
#ifndef MOBILE
#ifndef NOEDIT
if(u == 'A') {
lastexplore = turncount;
cmode = emMapEditor;
@ -986,7 +1166,7 @@ bool applyCheat(char u, cell *c = NULL) {
addMessage(XLAT("You summon an Ivy!"));
cheater++;
int i = cwt.spin;
int j = cwt.c->spn[i];
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;
@ -1037,8 +1217,9 @@ bool applyCheat(char u, cell *c = NULL) {
return true;
}
if(u == 'Z') {
cwt.spin++; flipplayer = false;
cwt.spin %= cwt.c->type;
flipplayer = false;
mirror::spin(1);
cwspin(cwt, 1);
return true;
}
if(u == 'J') {
@ -1058,14 +1239,14 @@ bool applyCheat(char u, cell *c = NULL) {
}
if(u == 'S') {
canmove = true;
activateSafety(cwt.c->land);
cheatMoveTo(cwt.c->land);
items[itOrbSafety] += 3;
cheater++; addMessage(XLAT("Activated Orb of Safety!"));
return true;
}
if(u == 'U') {
canmove = true;
activateSafety(firstland);
cheatMoveTo(firstland);
cheater++; addMessage(XLAT("Teleported to %1!", firstland));
return true;
}
@ -1090,15 +1271,39 @@ bool applyCheat(char u, cell *c = NULL) {
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[itEdge] = 12345;
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 == 'D'-64) {
cheater = 0; autocheat = 0;
return true;
}
#endif
return false;
}

92
util.cpp Normal file
View File

@ -0,0 +1,92 @@
// Hyperbolic Rogue
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
// basic utility functions
#ifdef MOBWEB
typedef double ld;
#define LDF "%lf"
#define PLDF "lf"
#define ASINH asinh
#else
typedef long double ld;
#define LDF "%Lf"
#define PLDF "Lf"
#define ASINH asinhl
#endif
template<class T> int size(const T& x) {return int(x.size()); }
string its(int i) { char buf[64]; sprintf(buf, "%d", i); return buf; }
string fts(float x) { char buf[64]; sprintf(buf, "%4.2f", x); return buf; }
string fts3(float x) { char buf[64]; sprintf(buf, "%5.3f", x); return buf; }
string fts4(float x) { char buf[64]; sprintf(buf, "%6.4f", x); return buf; }
string cts(char c) { char buf[8]; buf[0] = c; buf[1] = 0; return buf; }
string llts(long long i) {
// sprintf does not work on Windows IIRC
if(i < 0) return "-" + llts(-i);
if(i < 10) return its((int) i);
return llts(i/10) + its(i%10);
}
string itsh(int i) {static char buf[16]; sprintf(buf, "%03X", i); return buf; }
// debug utilities
extern FILE *debugfile;
extern int debugflags;
#ifdef ANDROID
#define DEBB(r,x)
#else
#define DEBB(r,x) { if(debugfile && (!(r) || (debugflags & (r)))) { fprintf x; fflush(debugfile); } }
#endif
#define DF_INIT 0 // always display these
#define DF_MSG 0 // always display these
#define DF_STEAM 1
#define DF_GRAPH 2
#define DF_TURN 4
#define DF_FIELD 8
#ifdef PROFILING
#include <sys/time.h>
long long getms() {
struct timeval tval;
gettimeofday(&tval, NULL);
return tval.tv_sec * 1000000 + tval.tv_usec;
}
#define FRAMES 64
#define CATS 16
long long proftable[16][FRAMES];
int pframeid;
void profile_frame() {
pframeid++; pframeid %= FRAMES;
for(int t=0; t<16; t++) proftable[t][pframeid] = 0;
}
void profile_start(int t) { proftable[t][pframeid] -= getms(); }
void profile_stop(int t) { proftable[t][pframeid] += getms(); }
void profile_info() {
for(int t=0; t<16; t++) {
sort(proftable[t], proftable[t]+FRAMES);
if(proftable[t][FRAMES-1] == 0) continue;
long long sum = 0;
for(int f=0; f<FRAMES; f++) sum += proftable[t][f];
printf("Category %d: avg = %Ld, %Ld..%Ld..%Ld..%Ld..%Ld\n",
t, sum / FRAMES, proftable[t][0], proftable[t][16], proftable[t][32],
proftable[t][48], proftable[t][63]);
}
}
#else
#define profile_frame()
#define profile_start(t)
#define profile_stop(t)
#define profile_info()
#endif

View File

@ -5,7 +5,7 @@
// Yendor Quest, together with the Yendor Challenge
// also, the Pure Tactics Mode
#define MODECODES 38
#define MODECODES 254
int hiitemsMax(eItem it) {
int mx = 0;
@ -50,7 +50,7 @@ namespace yendor {
int challenge; // id of the challenge
int lastchallenge;
#define YENDORLEVELS 27
#define YENDORLEVELS 29
int bestscore[MODECODES][YENDORLEVELS];
@ -69,6 +69,7 @@ namespace yendor {
#define YF_START_AL 2048
#define YF_START_CR 4096
#define YF_CHAOS 8192
#define YF_RECALL 16384
#define YF_START_ANY (YF_START_AL|YF_START_CR)
@ -103,14 +104,26 @@ namespace yendor {
{laWildWest, 0},
{laWhirlwind, YF_NEAR_TENT},
{laHell, YF_CHAOS | YF_DEAD},
{laDragon, YF_DEAD}
{laDragon, YF_DEAD},
{laReptile, 0},
{laTortoise, YF_RECALL},
};
int tscorelast;
void uploadScore() {
int tscore = 0;
for(int i=1; i<YENDORLEVELS; i++)
if(bestscore[0][i]) tscore += 999 + bestscore[0][i];
// printf("Yendor score = %d\n", tscore);
if(tscore > tscorelast) {
tscorelast = tscore;
if(tscore >= 1000) achievement_gain("YENDC1", 'x');
if(tscore >= 5000) achievement_gain("YENDC2", 'x');
if(tscore >= 15000) achievement_gain("YENDC3", 'x');
}
achievement_score(LB_YENDOR_CHALLENGE, tscore);
}
@ -134,7 +147,8 @@ namespace yendor {
vector<yendorinfo> yi;
int yii = 0;
#define NOYENDOR 999999
int yii = NOYENDOR;
int hardness() {
int thf = 0;
@ -156,11 +170,10 @@ namespace yendor {
return ysUntouched;
}
bool check(cell *yendor, bool checkonly) {
bool check(cell *yendor) {
int byi = size(yi);
for(int i=0; i<size(yi); i++) if(yi[i].path[0] == yendor) byi = i;
if(byi < size(yi) && yi[byi].found) return true;
if(checkonly) return false;
if(byi < size(yi) && yi[byi].found) return false;
if(byi == size(yi)) {
yendorinfo nyi;
nyi.path[0] = yendor;
@ -241,7 +254,7 @@ namespace yendor {
generating = false;
for(int b=10; b>=5; b--) setdist(key, b, nyi.path[YDIST-2]);
for(int i=-1; i<key->type; i++) {
cell *c2 = i >= 0 ? key->mov[i] : key;
checkTide(c2);
@ -257,6 +270,7 @@ namespace yendor {
c2->wall = waBoat, c2->monst = moPirate, c2->item = itOrbWater;
else c2->wall = waNone;
}
if(c2->wall == waReptile) c2->wall = waNone;
if(c2->wall == waMineMine || c2->wall == waMineUnknown)
c2->wall = waMineOpen;
if(c2->wall == waTrapdoor && i == -1)
@ -269,14 +283,20 @@ namespace yendor {
if(isGravityLand(c2->land) && key->land == c2->land &&
c2->landparam < key->landparam && c2->wall != waTrunk)
c2->wall = waPlatform;
if(c2->land == laReptile && i >= 0)
c2->wall = waChasm;
}
key->item = itKey;
yi.push_back(nyi);
}
yii = byi;
addMessage(XLAT("You need to find the right Key to unlock this Orb of Yendor!"));
achievement_gain("YENDOR1");
if(yii != byi) {
yii = byi;
achievement_gain("YENDOR1");
playSound(yendor, "pickup-yendor");
return true;
}
return false;
}
@ -314,6 +334,16 @@ namespace yendor {
if(clev().flags & YF_DEAD) items[itGreenStone] = 100;
if(clev().flags & YF_DEAD5) items[itGreenStone] = 5;
}
if(clev().flags & YF_RECALL) {
int yq = items[itOrbYendor];
items[itOrbRecall] = 60 - yq;
items[itOrbTime] = 60 - yq;
items[itOrbEnergy] = 60 - yq;
items[itOrbTeleport] = 60 - yq;
items[itOrbSpace] = 60 - yq;
items[itOrbDash] = 60 - yq;
items[itOrbFrog] = 60 - yq;
}
nexttostart = laNone;
}
@ -321,7 +351,7 @@ namespace yendor {
cell *c2 = cwt.c->mov[0];
c2->land = firstland;
if(firstland == laRlyeh) c2->wall = waNone;
yendor::check(c2, false);
yendor::check(c2);
if(clev().flags & YF_NEAR_IVY)
nexttostart = laJungle;
if(clev().flags & YF_NEAR_TENT)
@ -354,6 +384,7 @@ namespace yendor {
makeEmpty(c2);
c2->item = itOrbYendor;
nexttostart = laNone;
if(clev().flags & YF_RECALL) recallCell = cwt.c;
}
}
@ -368,6 +399,7 @@ namespace yendor {
if((ylev.flags & YF_NEAR_TENT) && hiitemsMax(itStatue) < 10) return false;
if((ylev.flags & YF_CHAOS) && !chaosUnlocked) return false;
if((ylev.flags & (YF_DEAD|YF_DEAD5)) && hiitemsMax(itBone) < 10) return false;
if((ylev.flags & YF_RECALL) && hiitemsMax(itSlime) < 10) return false;
return true;
}
@ -380,13 +412,13 @@ namespace yendor {
void showMenu() {
int s = vid.fsize;
vid.fsize = vid.fsize * 4/5;
displayStatHelp(-8, XLAT("Yendor Challenge"));
dialog::init(XLAT("Yendor Challenge"), iinf[itOrbYendor].color, 150, 100);
for(int i=1; i<YENDORLEVELS; i++) {
string s;
yendorlevel& ylev(levels[i]);
if(levelUnlocked(i)) {
if(autocheat || levelUnlocked(i)) {
s = XLATT1(ylev.l);
@ -399,6 +431,7 @@ namespace yendor {
if(ylev.flags & YF_NEAR_RED) { s += "+"; s += XLATT1(laRedRock); }
if(ylev.flags & YF_START_AL) { s += "+"; s += XLATT1(laAlchemist); }
if(ylev.flags & YF_DEAD) { s += "+"; s += XLATT1(itGreenStone); }
if(ylev.flags & YF_RECALL) { s += "+"; s += XLATT1(itOrbRecall); }
}
}
@ -412,14 +445,17 @@ namespace yendor {
else if(bestscore[modecode()][i])
v = XLAT(" (won at level %1!)", its(bestscore[modecode()][i]));
displayStat(i-6, s, v, 'a' + i-1);
dialog::addSelItem(s, v, 'a' + i-1);
}
displayStat(YENDORLEVELS+1-6, XLAT("Return to the normal game"), "", '0');
displayStat(YENDORLEVELS+1-5, XLAT(
dialog::addBreak(60);
dialog::addItem(XLAT("Return to the normal game"), '0');
dialog::addSelItem(XLAT(
easy ? "Challenges do not get harder" : "Each challenge gets harder after each victory"),
" " + XLAT(easy ? "easy" : "challenge"), '1');
dialog::display();
int yc = getcstat - 'a' + 1;
if(yc > 0 && yc < YENDORLEVELS) {
subscoreboard scorehere;
@ -463,10 +499,11 @@ namespace yendor {
"You get 1000 points for each challenge won, and 1 extra point for "
"each extra difficulty level.";
void handleKey(int uni, int sym) {
void handleKey(int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni >= 'a' && uni < 'a'+YENDORLEVELS-1) {
challenge = uni-'a' + 1;
if(levelUnlocked(challenge)) {
if(levelUnlocked(challenge) || autocheat) {
restartGame(yendor::on ? 0 : 'y');
cmode = emNormal;
}
@ -522,6 +559,7 @@ namespace tactic {
bool tacticUnlocked(int i) {
eLand l = land_tac[i].l;
if(autocheat) return true;
if(l == laWildWest) return true;
return hiitemsMax(treasureType(l)) * landMultiplier(l) >= 20;
}
@ -552,11 +590,20 @@ namespace tactic {
unrecord(lasttactic);
}
int tscorelast;
void uploadScoreCode(int code, int lb) {
int tscore = 0;
for(int i=0; i<landtypes; i++)
tscore += recordsum[code][i] * tacmultiplier(eLand(i));
// printf("PTM score = %d\n", tscore);
if(code == 0 && tscore > tscorelast) {
tscorelast = tscore;
if(tscore >= 1000) achievement_gain("PTM1", 'x');
if(tscore >= 5000) achievement_gain("PTM2", 'x');
if(tscore >= 15000) achievement_gain("PTM3", 'x');
}
achievement_score(lb, tscore);
}
@ -565,22 +612,41 @@ namespace tactic {
uploadScoreCode(2, LB_PURE_TACTICS_SHMUP);
uploadScoreCode(4, LB_PURE_TACTICS_COOP);
}
int nl;
eLand getLandById(int i) {
return
sphere ? land_sph[i] :
euclid ? land_euc[i] :
land_tac[i].l;
}
void showMenu() {
mouseovers = XLAT("pure tactics mode") + " - " + mouseovers;
int nl = LAND_TAC;
nl = LAND_TAC;
int vf = min((vid.yres-64) / nl, vid.xres/40);
if(euclid) nl = LAND_EUC;
if(sphere) nl = LAND_SPH;
int nlm;
int ofs = dialog::handlePage(nl, nlm, nl/2);
int vf = min((vid.yres-64-vid.fsize) / nlm, vid.xres/40);
int xr = vid.xres / 64;
if(on) record(firstland, items[treasureType(firstland)]);
int xc = modecode();
getcstat = SDLK_ESCAPE;
for(int i=0; i<nl; i++) {
eLand l = land_tac[i].l;
int i1 = i + ofs;
eLand l = getLandById(i1);
int i0 = 56 + i * vf;
int col;
@ -588,36 +654,32 @@ namespace tactic {
if(!ch) continue;
bool unlocked = tacticUnlocked(i);
bool unlocked = tacticUnlocked(i1);
if(unlocked) col = linf[l].color; else col = 0x202020;
if(displayfr(xr*1, i0, 1, vf-4, XLAT1(linf[l].name), col, 0) && unlocked) {
getcstat = 1000 + i;
if(displayfrZ(xr*1, i0, 1, vf-4, XLAT1(linf[l].name), col, 0) && unlocked) {
getcstat = 1000 + i1;
}
if(unlocked) {
if(unlocked || autocheat) {
for(int ii=0; ii<ch; ii++)
if(displayfr(xr*(24+2*ii), i0, 1, (vf-4)*4/5, lsc[xc][l][ii] >= 0 ? its(lsc[xc][l][ii]) : "-", col, 16))
getcstat = 1000 + i;
if(displayfrZ(xr*(24+2*ii), i0, 1, (vf-4)*4/5, lsc[xc][l][ii] >= 0 ? its(lsc[xc][l][ii]) : "-", col, 16))
getcstat = 1000 + i1;
if(displayfr(xr*(24+2*10), i0, 1, (vf-4)*4/5,
if(displayfrZ(xr*(24+2*10), i0, 1, (vf-4)*4/5,
its(recordsum[xc][l]) + " x" + its(tacmultiplier(l)), col, 0))
getcstat = 1000 + i;
getcstat = 1000 + i1;
}
else {
int m = landMultiplier(l);
displayfr(xr*26, i0, 1, (vf-4)*4/5,
displayfrZ(xr*26, i0, 1, (vf-4)*4/5,
XLAT("Collect %1x %2 to unlock", its((20+m-1)/m), treasureType(l)),
col, 0);
}
}
if(on || ISIOS) {
int i0 = 56 + nl * vf;
if(displayfr(xr*24, i0, 1, vf-4, "press 0 to leave this mode", 0xFFD500, 8))
getcstat = '0';
}
dialog::displayPageButtons(3, true);
uploadScore();
if(on) unrecord(firstland);
@ -635,9 +697,9 @@ namespace tactic {
}
}
void handleKey(int uni, int sym) {
void handleKey(int sym, int uni) {
if(uni >= 1000 && uni < 1000 + LAND_TAC) {
firstland = land_tac[uni - 1000].l;
firstland = euclidland = getLandById(uni - 1000);
restartGame(tactic::on ? 0 : 't');
cmode = emNormal;
}
@ -646,7 +708,7 @@ namespace tactic {
firstland = laIce;
if(tactic::on) restartGame('t');
}
else if(uni == '2' || sym == SDLK_F1) {
else if(sym == SDLK_F1) {
lastmode = cmode;
cmode = emHelp;
help =
@ -673,27 +735,124 @@ namespace tactic {
"Good luck, and have fun!";
}
else if(uni) cmode = emNormal;
else if(dialog::handlePageButtons(uni)) ;
else if(uni || sym == SDLK_F10) cmode = emNormal;
}
};
int modecodetable[42][6] = {
{ 0, 38, 39, 40, 41, 42}, // softcore hyperbolic
{ 7, 43, 44, 45, 46, 47}, // hardcore hyperbolic
{ 2, 4, 9, 11, 48, 49}, // shmup hyperbolic
{ 13, 50, 51, 52, 53, 54}, // softcore heptagonal hyperbolic
{ 16, 55, 56, 57, 58, 59}, // hardcore heptagonal hyperbolic
{ 14, 15, 17, 18, 60, 61}, // shmup heptagonal hyperbolic
{ 1, 62, 63, 64, 65, 66}, // softcore euclidean
{ 8, 67, 68, 69, 70, 71}, // hardcore euclidean
{ 3, 5, 10, 12, 72, 73}, // shmup euclidean
{110,111,112,113,114,115}, // softcore spherical
{116,117,118,119,120,121}, // hardcore spherical
{122,123,124,125,126,127}, // shmup spherical
{128,129,130,131,132,133}, // softcore heptagonal spherical
{134,135,136,137,138,139}, // hardcore heptagonal spherical
{140,141,142,143,144,145}, // shmup heptagonal spherical
{146,147,148,149,150,151}, // softcore elliptic
{152,153,154,155,156,157}, // hardcore elliptic
{158,159,160,161,162,163}, // shmup elliptic
{164,165,166,167,168,169}, // softcore heptagonal elliptic
{170,171,172,173,174,175}, // hardcore heptagonal elliptic
{176,177,178,179,180,181}, // shmup heptagonal elliptic
{ 19, 74, 75, 76, 77, 78}, // softcore hyperbolic chaosmode
{ 26, 79, 80, 81, 82, 83}, // hardcore hyperbolic chaosmode
{ 21, 23, 28, 30, 84, 85}, // shmup hyperbolic chaosmode
{ 32, 86, 87, 88, 89, 90}, // softcore heptagonal hyperbolic chaosmode
{ 35, 91, 92, 93, 94, 95}, // hardcore heptagonal hyperbolic chaosmode
{ 33, 34, 36, 37, 96, 97}, // shmup heptagonal hyperbolic chaosmode
{ 20, 98, 99,100,101,102}, // softcore euclidean chaosmode
{ 27,103,104,105,106,107}, // hardcore euclidean chaosmode
{ 22, 24, 29, 31,108,109}, // shmup euclidean chaosmode
{182,183,184,185,186,187}, // softcore spherical chaosmode
{188,189,190,191,192,193}, // hardcore spherical chaosmode
{194,195,196,197,198,199}, // shmup spherical chaosmode
{200,201,202,203,204,205}, // softcore heptagonal spherical chaosmode
{206,207,208,209,210,211}, // hardcore heptagonal spherical chaosmode
{212,213,214,215,216,217}, // shmup heptagonal spherical chaosmode
{218,219,220,221,222,223}, // softcore elliptic chaosmode
{224,225,226,227,228,229}, // hardcore elliptic chaosmode
{230,231,232,233,234,235}, // shmup elliptic chaosmode
{236,237,238,239,240,241}, // softcore heptagonal elliptic chaosmode
{242,243,244,245,246,247}, // hardcore heptagonal elliptic chaosmode
{248,249,250,251,252,253}, // shmup heptagonal elliptic chaosmode
};
// unused code: 25
int newmodecode = 254;
int modecode() {
int xcode = 0;
if(euclid) xcode += 1;
if(shmup::on) {
if(numplayers() == 1) xcode += 2;
if(numplayers() == 2) xcode += 4;
if(numplayers() == 3) xcode += 9;
if(numplayers() == 4) xcode += 11;
}
if(pureHardcore() && !shmup::on) xcode += 7;
#ifndef NOSAVE
if(anticheat::tampered || cheater) return 6;
if(purehepta) {
if(xcode > 6) xcode--;
xcode /= 2;
xcode += 13;
if(quotient) return 6;
#endif
int xcode = 0;
if(shmup::on) xcode += 2;
else if(pureHardcore()) xcode ++;
if(euclid) xcode += 6;
else if(purehepta) xcode += 3;
if(sphere) {
xcode += 9;
if(elliptic) xcode += 6;
if(purehepta) xcode += 3;
}
if(chaosmode && !yendor::on && cmode != emYendor) xcode += 19;
return xcode;
if(chaosmode) xcode += 21;
int np = numplayers()-1; if(np<0 || np>5) np=5;
return modecodetable[xcode][np];
}
void buildmodetable() {
bool codeused[600];
for(int q=0; q<600; q++) codeused[q] = 0;
codeused[6] = true; // cheater
printf("int modecodetable[42][6] = {\n");
for(int b=0; b<42; b++) {
extern bool hardcore;
hardcore = (b%3 == 1);
shmup::on = (b%3 == 2);
purehepta = (b/3)%7 == 1 || (b/3)%7 == 4 || (b/3)%7 == 6;
geometry = gNormal;
if((b/3)%7 == 2) geometry = gEuclid;
if((b/3)%7 >= 3) geometry = gSphere;
if((b/3)%7 >= 5) geometry = gElliptic;
chaosmode = b >= 21;
printf(" {");
for(int p=0; p<6; p++) {
multi::players = p+1;
if(p) printf(",");
int mc = modecode();
if(codeused[mc]) mc = newmodecode++;
codeused[mc] = true;
printf("%3d", mc);
}
printf("}, //");
if(hardcore) printf(" hardcore");
else if(shmup::on) printf(" shmup");
else printf(" softcore");
if(purehepta) printf(" heptagonal");
if(euclid) printf(" euclidean");
else if(elliptic) printf(" elliptic");
else if(sphere) printf(" spherical");
else printf(" hyperbolic");
if(chaosmode) printf(" chaosmode");
printf("\n");
}
printf(" }\n");
for(int i=0; i<newmodecode; i++) if(!codeused[i]) printf("// unused code: %d\n", i);
printf("int newmodecode = %d;\n", newmodecode);
}