1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-12-23 16:50:27 +00:00

Updated to 8.3j

This commit is contained in:
Eryk Kopczyński 2016-08-26 11:58:03 +02:00
parent da74e6e976
commit 3237ff455e
43 changed files with 54438 additions and 24893 deletions

View File

@ -1,5 +1,10 @@
all: hyper.exe
# for simplicity we use NOPNG here
# you can also include savepng.c and remove -DNOPNG
hyper.exe: hyper.cpp graph.cpp hyperpoint.cpp geometry.cpp cell.cpp heptagon.cpp game.cpp polygons.cpp classes.cpp hyper.res language-data.cpp
g++ -mwindows hyper.cpp hyper.res -o hyper.exe -lSDL -lSDL_mixer -lopengl32 SDL_ttf.dll SDL_gfx.dll -O3
g++ -mwindows hyper.cpp hyper.res -o hyper.exe -lSDL -lSDL_mixer -lopengl32 SDL_ttf.dll SDL_gfx.dll -O3 -DNOPNG
hyper.res: hyper.rc hr-icon.ico
windres hyper.rc -O coff -o hyper.res

View File

@ -1,15 +1,22 @@
#define NUMLEADER 40
// Hyperbolic Rogue -- achievements
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#define NUMLEADER 57
#define SCORE_UNKNOWN (-1)
#define NO_SCORE_YET (-2)
bool offlineMode = false;
int syncstate = 0;
int currentscore[NUMLEADER];
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-73", "Turns to 10 Hyperstones-73", "Orbs of Yendor",
"Time to 10 Hyperstones-83", "Turns to 10 Hyperstones-83", "Orbs of Yendor",
"Fern Flowers",
"Royal Jellies", "Powerstones", "Silver", "Wine", "Emeralds", "Grimoires",
"Holy Grails", "Red Gems", "Pirate Treasures",
@ -22,7 +29,24 @@ const char* leadernames[NUMLEADER] = {
"Princess Challenge", // 36
"Ivory Figurines", // 37
"Elemental Gems", // 38
"Onyxes" // 39
"Onyxes", // 39
"Yendor Challenge", // 40
"Pure Tactics Mode", // 41
"Mutant Saplings", // 42
"Fulgurites", // 43
"Shmup Score 2p", // 44
"Coop Shmup Time to Win", // 45
"Black Lotuses", // 46
"Mutant Fruits", // 47
"White Dove Feathers", // 48
"Pure Tactics Mode (shmup)", // 49
"Pure Tactics Mode (2p)", // 50
"Corals", // 51
"Thornless Roses", // 52
"Chaos Mode", // 53
"Tortoise points", // 54
"Dragon Scales", // 55
"Apples", // 56
};
bool haveLeaderboard(int id);
@ -33,11 +57,25 @@ string achievementMessage[3];
int achievementTimer;
// vector<string> achievementsReceived;
void achievement_log(const char* s, bool euclideanAchievement, bool shmupAchievement) {
if(cheater) return;
if(euclid != euclideanAchievement) return;
if(shmup::on != shmupAchievement) return;
if(randomPatternsMode) return;
bool wrongMode(char flags) {
if(cheater) return true;
if(purehepta != (flags == '7')) return true;
if(euclid != (flags == 'e')) 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;
return false;
}
void achievement_log(const char* s, char flags) {
#ifdef LOCAL
printf("achievement = %s [%d]\n", s, wrongMode(flags));
#endif
if(wrongMode(flags)) return;
for(int i=0; i<size(achievementsReceived); i++)
if(achievementsReceived[i] == s) return;
@ -46,13 +84,13 @@ void achievement_log(const char* s, bool euclideanAchievement, bool shmupAchieve
#ifndef ANDROID
FILE *f = fopen(scorefile, "at");
if(!f) return;
int t = time(NULL) - timerstart;
int t = (int) (time(NULL) - timerstart);
time_t timer = time(NULL);
char buf[128]; strftime(buf, 128, "%c", localtime(&timer));
fprintf(f, "ACHIEVEMENT %s turns: %d time: %d at: %d c: %d date: %s\n",
s, turncount, t, int(timerstart), achievement_certify(s, turncount, t, timerstart), buf);
fprintf(f, "ACHIEVEMENT %s turns: %d time: %d at: %d ver: %s c: %d date: %s\n",
s, turncount, t, int(timerstart), VER, anticheat::certify(s, turncount, t, (int) timerstart, 0), buf);
fclose(f);
#endif
@ -65,8 +103,8 @@ void improveItemScores();
#ifndef ANDROID
void achievement_init() {}
void achievement_close() {}
void achievement_gain(const char* s, bool euclideanAchievement, bool shmupAchievement) {
achievement_log(s, euclideanAchievement, shmupAchievement);
void achievement_gain(const char* s, char flags) {
achievement_log(s, flags);
}
#endif
#endif
@ -99,7 +137,7 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itRedGem) achievement_gain("REDGEM1");
if(it == itPirate) achievement_gain("PIRATE1");
if(it == itCoast) achievement_gain("COAST1");
// if(it == itWhirlpool) achievement_gain("WHIRL1");
// if(it == itWhirlpool) achievement_gain("WHIRL1"); // requires escape
if(it == itBombEgg) achievement_gain("MINE1");
if(it == itPalace) achievement_gain("RUG1");
if(it == itFjord) achievement_gain("GARNET1");
@ -107,11 +145,23 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itEdge) achievement_gain("TOWER1");
if(it == itElemental) achievement_gain("ELEMENT1");
if(it == itZebra) achievement_gain("ZEBRA1");
if(it == itMutant) achievement_gain("MUTANT1");
if(it == itFulgurite) achievement_gain("FULGUR1");
if(it == itMutant2) achievement_gain("FRUIT1");
if(it == itWindstone) achievement_gain("DOVE1");
if(it == itCoral) achievement_gain("CORAL1");
if(it == itRose) achievement_gain("ROSE1");
if(it == itBabyTortoise) achievement_gain("TORTOISE1");
if(it == itDragon) achievement_gain("DRAGON1");
if(it == itApple) achievement_gain("APPLE1");
}
// 32
if(it == itHolyGrail) {
if(q == 1) achievement_gain("GRAIL2");
if(q == 1) achievement_gain("GRAIL2"), achievement_gain("GRAILH", '7');
if(q == 3) achievement_gain("GRAIL3");
if(q == 8) achievement_gain("GRAIL4");
}
@ -147,6 +197,18 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itEdge) achievement_gain("TOWER2");
if(it == itElemental) achievement_gain("ELEMENT2");
if(it == itZebra) achievement_gain("ZEBRA2");
if(it == itMutant) achievement_gain("MUTANT2");
if(it == itFulgurite) achievement_gain("FULGUR2");
if(it == itMutant2) achievement_gain("FRUIT2");
if(it == itWindstone) achievement_gain("DOVE2");
if(it == itCoral) achievement_gain("CORAL2");
if(it == itRose) achievement_gain("ROSE2");
if(it == itBabyTortoise) achievement_gain("TORTOISE2");
if(it == itDragon) achievement_gain("DRAGON2");
if(it == itApple) achievement_gain("APPLE2");
}
if(q == 25) {
@ -180,6 +242,21 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itEdge) achievement_gain("TOWER3");
if(it == itElemental) achievement_gain("ELEMENT3");
if(it == itZebra) achievement_gain("ZEBRA3");
if(it == itMutant) achievement_gain("MUTANT3");
if(it == itFulgurite) achievement_gain("FULGUR3");
if(it == itMutant2) achievement_gain("FRUIT3");
if(it == itWindstone) achievement_gain("DOVE3");
if(it == itCoral) achievement_gain("CORAL3");
if(it == itRose) achievement_gain("ROSE3");
if(it == itFulgurite && pureHardcore() && elec::lightningfast == 0)
achievement_gain("HARDMETAL");
if(it == itBabyTortoise) achievement_gain("TORTOISE3");
if(it == itDragon) achievement_gain("DRAGON3");
if(it == itApple) achievement_gain("APPLE3");
}
if(q == 50) {
@ -213,12 +290,24 @@ void achievement_collection(eItem it, int prevgold, int newgold) {
if(it == itEdge) achievement_gain("TOWER4");
if(it == itElemental) achievement_gain("ELEMENT4");
if(it == itZebra) achievement_gain("ZEBRA4");
if(it == itMutant) achievement_gain("MUTANT4");
if(it == itFulgurite) achievement_gain("FULGUR4");
if(it == itMutant2) achievement_gain("FRUIT4");
if(it == itWindstone) achievement_gain("DOVE4");
if(it == itCoral) achievement_gain("CORAL4");
if(it == itRose) achievement_gain("ROSE4");
if(it == itBabyTortoise) achievement_gain("TORTOISE4");
if(it == itDragon) achievement_gain("DRAGON4");
if(it == itApple) achievement_gain("APPLE4");
}
if(it == itOrbYendor) {
achievement_gain("YENDOR2");
if(pureHardcore()) achievement_gain("HARDCORE");
if(shmup::on) achievement_gain("SHMUP", false, true);
if(shmup::on) achievement_gain("SHMUP", 's');
}
}
@ -255,35 +344,42 @@ void achievement_count(const string& s, int current, int prev) {
if(s == "LIGHTNING" && current-prev >= 10)
achievement_gain("LIGHTNING3");
if(s == "MIRAGE" && current >= 35)
achievement_gain("MIRAGE", true);
achievement_gain("MIRAGE", 'e');
if(s == "ORB" && current >= 10)
achievement_gain("ORB3");
if(s == "BUG" && current >= 1000)
achievement_gain("BUG3");
if(s == "ELEC" && current >= 10)
achievement_gain("ELEC3");
}
int specific_improved = 0;
int specific_what = 0;
void improve_score(int i, eItem what) {
if(offlineMode) return;
#ifdef HAVE_ACHIEVEMENTS
if(haveLeaderboard(i)) updateHi(what, currentscore[i]);
if(items[what] && haveLeaderboard(i)) {
if(items[what] > currentscore[i] && currentscore[i] != SCORE_UNKNOWN) {
specific_improved++; specific_what = what;
currentscore[i] = items[what];
}
upload_score(i, items[what]);
}
#endif
}
void achievement_score(int cat, int number) {
if(offlineMode) return;
#ifdef HAVE_ACHIEVEMENTS
if(cheater) return;
if(euclid) return;
if(purehepta) return;
if(randomPatternsMode) return;
if(shmup::on && cat != LB_PURE_TACTICS_SHMUP && cat != LB_PURE_TACTICS_COOP) return;
if(yendor::on && cat != LB_YENDOR_CHALLENGE) return;
if(tactic::on && cat != LB_PURE_TACTICS && cat != LB_PURE_TACTICS_SHMUP && cat != LB_PURE_TACTICS_COOP)
return;
upload_score(cat, number);
#endif
}
@ -310,57 +406,98 @@ void improveItemScores() {
improve_score(37, itEdge);
improve_score(38, itElemental);
improve_score(39, itZebra);
improve_score(42, itMutant);
improve_score(43, itFulgurite);
if(!isHaunted(cwt.c->land)) improve_score(46, itLotus);
improve_score(47, itMutant2);
improve_score(48, itWindstone);
improve_score(51, itCoral);
improve_score(52, itRose);
improve_score(54, itBabyTortoise);
improve_score(55, itDragon);
improve_score(56, itApple);
}
void achievement_final(bool really_final) {
if(offlineMode) return;
#ifdef HAVE_ACHIEVEMENTS
if(cheater) return;
if(euclid) return;
if(purehepta) return;
if(randomPatternsMode) return;
if(tactic::on) {
tactic::record();
tactic::unrecord();
tactic::uploadScore();
return;
}
if(yendor::on) return;
if(shmup::on && chaosmode) return;
int total_improved = 0;
specific_improved = 0;
specific_what = 0;
if(!shmup::on) improveItemScores();
if(!shmup::on && !chaosmode) improveItemScores();
int sid = shmup::on ? 28 : 0;
int sid = chaosmode ? 53 : shmup::on ? (numplayers() > 1 ? 44 : 28) : 0;
int tg = gold();
if(tg && haveLeaderboard(sid)) {
if(tg > currentscore[sid] && currentscore[sid] != SCORE_UNKNOWN) {
if(currentscore[sid] < 0) total_improved += 2;
total_improved++; currentscore[sid] = tg;
total_improved++; // currentscore[sid] = tg;
}
upload_score(sid, tg);
}
if(total_improved >= 2) {
addMessage(XLAT("Your total treasure has been recorded in the "LEADERFULL"."));
#ifndef ANDROID
addMessage(XLAT("Your total treasure has been recorded in the " LEADERFULL "."));
addMessage(XLAT("Congratulations!"));
#endif
}
else if(total_improved && specific_improved >= 2)
addMessage(XLAT("You have improved your total high score and %1 specific high scores!", its(specific_improved)));
else if(total_improved && specific_improved)
addMessage(XLAT("You have improved your total and '%1' high score!", iinf[specific_what].name));
else if(total_improved)
addMessage(XLAT("You have improved your total high score on "LEADER". Congratulations!"));
else if(total_improved) {
#ifndef ANDROID
addMessage(XLAT("You have improved your total high score on " LEADER ". Congratulations!"));
#endif
}
else if(specific_improved >= 2)
addMessage(XLAT("You have improved %1 of your specific high scores!", its(specific_improved)));
else if(specific_improved)
addMessage(XLAT("You have improved your '%1' high score on "LEADER"!", iinf[specific_what].name));
else if(specific_improved) {
#ifndef ANDROID
addMessage(XLAT("You have improved your '%1' high score on " LEADER "!", iinf[specific_what].name));
#endif
}
#endif
}
void achievement_victory(bool hyper) {
if(offlineMode) return;
#ifdef HAVE_ACHIEVEMENTS
if(cheater) return;
if(euclid) return;
if(purehepta) return;
if(randomPatternsMode) return;
if(hyper && shmup::on) return;
if(yendor::on) return;
if(tactic::on) return;
if(chaosmode) return;
int t = savetime + time(NULL) - timerstart;
int ih1 = hyper ? 15 : shmup::on ? 29 : 13;
int ih1 = hyper ? 15 : shmup::on ? (numplayers() > 1 ? 45 : 29) : 13;
int ih2 = hyper ? 16 : shmup::on ? 30 : 14;
int improved = 0;
@ -368,11 +505,11 @@ void achievement_victory(bool hyper) {
improved += 4;
if(currentscore[ih1] < 0 || currentscore[ih1] > t) {
improved++; currentscore[ih1] = t;
improved++; // currentscore[ih1] = t;
}
if(currentscore[ih2] < 0 || currentscore[ih2] > turncount) {
improved+=2; currentscore[ih2] = turncount;
improved+=2; // currentscore[ih2] = turncount;
}
if(hyper)
@ -381,7 +518,9 @@ void achievement_victory(bool hyper) {
if(improved) {
if(improved >= 4) {
if(!hyper) addMessage(XLAT("This is your first victory!"));
#ifndef ANDROID
addMessage(XLAT("This has been recorded in the " LEADERFULL "."));
#endif
addMessage(XLAT("The faster you get here, the better you are!"));
}
else if(improved >= 3) {
@ -407,18 +546,29 @@ void achievement_victory(bool hyper) {
void achievement_pump();
#ifndef HAVE_ACHIEVEMENTS
void achievement_pump() {}
#endif
void achievement_display() {
#ifdef HAVE_ACHIEVEMENTS
achievement_pump();
#ifdef HAVE_ACHIEVEMENTS
if(achievementTimer) {
int col = (ticks - achievementTimer);
if(col > 5000) { achievementTimer = 0; return; }
if(col > 2500) col = 5000 - col;
col /= 10; col *= 0x10101;
displayfr(vid.xres/2, vid.yres/4, 2, vid.fsize * 2, achievementMessage[0], col & 0xFFFF00, 8);
displayfr(vid.xres/2, vid.yres/4 + vid.fsize*2, 2, vid.fsize * 2, achievementMessage[1], col, 8);
displayfr(vid.xres/2, vid.yres/4 + vid.fsize*4, 2, vid.fsize, achievementMessage[2], col, 8);
}
int w = 2 * vid.fsize;
#ifndef MOBILE
while(w>3 && textwidth(w, achievementMessage[1]) > vid.xres) w--;
#endif
displayfr(vid.xres/2, vid.yres/4 + vid.fsize*2, 2, w, achievementMessage[1], col, 8);
w = vid.fsize;
#ifndef MOBILE
while(w>3 && textwidth(w, achievementMessage[2]) > vid.xres) w--;
#endif
displayfr(vid.xres/2, vid.yres/4 + vid.fsize*4, 2, w, achievementMessage[2], col, 8);
}
#endif
}

View File

@ -1,30 +0,0 @@
// initialize the achievement system.
void achievement_init();
// close the achievement system.
void achievement_close();
// gain the achievement with the given name.
// Only awarded if euclid equals euclideanAchievement.
void achievement_gain(const char*, bool euclideanAchievement = false);
// gain the achievement for collecting a number of 'it'.
void achievement_collection(eItem it, int prevgold, int newgold);
// this is used for 'counting' achievements, such as kill 10
// monsters at the same time.
void achievement_count(const string& s, int current, int prev);
// gain the victory achievements. Set 'hyper' to true for
// the Hyperstone victory, and false for the Orb of Yendor victory.
void achievement_victory(bool hyper);
// gain the final achievements. Called with really=false whenever the user
// looks at their score, and really=true when the game really ends.
void achievement_final(bool really);
// display the last achievement gained.
void achievement_display();
// achievements received this game
vector<string> achievementsReceived;

372
cell.cpp
View File

@ -1,11 +1,18 @@
// Hyperbolic Rogue
// Copyright (C) 2011-2012 Zeno Rogue, see 'hyper.cpp' for details
// 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 dirdiff(int dd, int t) {
dd %= t;
if(dd<0) dd += t;
if(t-dd < dd) dd = t-dd;
return dd;
}
struct cell : gcell {
char type; // 6 for hexagons, 7 for heptagons
unsigned char spn[7];
@ -38,6 +45,8 @@ void merge(cell *c, int d, cell *c2, int d2) {
typedef unsigned short eucoord;
#include <map>
cell*& euclideanAtCreate(eucoord x, eucoord y);
union heptacoder {
@ -69,6 +78,13 @@ 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;
}
else if(c->type == 7) {
cell *n = newCell(6, c->master);
@ -203,11 +219,17 @@ cell*& euclideanAtCreate(eucoord x, eucoord y) {
// 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;
@ -265,7 +287,7 @@ void clearfrom(heptagon *at) {
}
DEBMEM ( printf("at %p\n", at); )
if(at->c7) {
for(int i=0; i<7; i++)
if(!purehepta) for(int i=0; i<7; i++)
clearcell(at->c7->mov[i]);
clearcell(at->c7);
}
@ -280,7 +302,7 @@ void verifycell(cell *c) {
for(int i=0; i<t; i++) {
cell *c2 = c->mov[i];
if(c2) {
if(t == 7) verifycell(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);
}
@ -318,31 +340,6 @@ bool ishex1(cell *c) {
else return c->type == 7;
}
void clearMemory() {
extern void clearGameMemory();
clearGameMemory();
if(shmup::on) shmup::clearMemory();
cleargraphmemory();
#ifndef MOBILE
mapeditor::clearModelCells();
#endif
// EUCLIDEAN
if(euclid) {
for(int y=0; y<256; y++) for(int x=0; x<256; x++)
if(euclidean[y][x]) {
delete euclidean[y][x];
euclidean[y][x] = NULL;
}
}
else {
DEBMEM ( verifycells(&origin); )
clearfrom(&origin);
for(int i=0; i<size(allAlts); i++) clearfrom(allAlts[i]);
allAlts.clear();
}
DEBMEM ( printf("ok\n"); )
}
int emeraldval(cell *c) {
if(euclid) return 0;
if(c->type == 7)
@ -441,6 +438,7 @@ int cdist50(cell *c) {
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",
@ -450,6 +448,12 @@ int cdist50(cell *c) {
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->type == 7) return cdist50(fiftyval(c));
int a0 = cdist50(createMov(c,0));
int a1 = cdist50(createMov(c,2));
@ -566,11 +570,33 @@ int zebra3(cell *c) {
}
}
#define RPV_MODULO 5
#define RPV_RAND 0
#define RPV_ZEBRA 1
#define RPV_EMERALD 2
#define RPV_PALACE 3
#define RPV_CYCLE 4
int getCdata(cell *c, int j);
// x mod 5 = pattern type
// x mod (powers of 2) = pattern type specific
// (x/5) mod 15 = picture for drawing floors
// x mod 7 = chance of pattern-specific pic
// whole = randomization
bool randpattern(cell *c, int rval) {
int i, sw=0;
switch(rval & 3) {
switch(rval%5) {
case 0:
return rand() < rval;
if(rval&1) {
return hrandpos() < rval;
}
else {
int cd = getCdata(c, 0);
return !((cd/(((rval/2)&15)+1))&1);
}
case 1:
i = zebra40(c);
if(i&1) { if(rval&4) sw^=1; i &= ~1; }
@ -583,26 +609,58 @@ bool randpattern(cell *c, int rval) {
i = emeraldval(c);
if(i&1) { if(rval&4) sw^=1; i &= ~1; }
if(i&2) { if(rval&8) sw^=1; i &= ~2; }
i >>= 2; i--;
if(rval & (16<<i)) sw^=1;
return sw;
case 3:
if(polara50(c)) { if(rval&4) sw^=1; }
if(polarb50(c)) { if(rval&8) sw^=1; }
int i = fiftyval049(c); i += 6; i /= 7;
i = fiftyval049(c); i += 6; i /= 7;
if(rval & (16<<i)) sw^=1;
return sw;
case 4:
i = (rval&3);
if(i == 1 && (celldist(c)&1)) sw ^= 1;
if(i == 2 && (celldist(c)&2)) sw ^= 1;
if(i == 3 && ((celldist(c)/3)&1)) sw ^= 1;
if(rval & (4<<towerval(c, celldist))) sw ^= 1;
return sw;
}
return 0;
}
extern int randompattern[landtypes];
string describeRPM(eLand l) {
int rval = randompattern[l];
switch(rval%5) {
case 0:
if(rval&1)
return "R:"+its(rval/(HRANDMAX/100))+"%";
else
return "Landscape/"+its(((rval/2)&15)+1);
case 1:
return "Z/"+its((rval>>2)&3)+"/"+its((rval>>4)&15);
case 2:
return "E/"+its((rval>>2)&3)+"/"+its((rval>>4)&2047);
case 3:
return "P/"+its((rval>>2)&3)+"/"+its((rval>>4)&255);
case 4:
return "C/"+its(rval&3)+"/"+its((rval>>2)&65535);
}
return "?";
}
int randpatternCode(cell *c, int rval) {
switch(rval & 3) {
switch(rval % RPV_MODULO) {
case 1:
return zebra40(c);
case 2:
return emeraldval(c);
case 3:
return fiftyval049(c) + (polara50(c)?50:0) + (polarb50(c)?1000:0);
case 4:
return towerval(c, celldist) * 6 + celldist(c) % 6;
}
return 0;
}
@ -616,14 +674,12 @@ void clearMemoRPM() {
rpm_memoize[a][b][i] = 2;
}
extern int randompattern[landtypes];
bool randpatternMajority(cell *c, int ival, int iterations) {
int rval = 0;
if(ival == 0) rval = randompattern[laCaves];
if(ival == 1) rval = randompattern[laLivefjord];
if(ival == 2) rval = randompattern[laEmerald];
if((rval&3) == 0) return randpattern(c, rval);
if(rval%RPV_MODULO == RPV_RAND) return randpattern(c, rval);
int code = randpatternCode(c, rval);
char& memo(rpm_memoize[ival][code][iterations]);
if(memo < 2) return memo;
@ -639,3 +695,247 @@ bool randpatternMajority(cell *c, int ival, int iterations) {
// printf("%p] rval = %X code = %d iterations = %d result = %d\n", c, rval, code, iterations, memo);
return memo;
}
#ifdef CDATA
#include <map>
map<heptagon*, int> spins;
#define RVAL_MASK 0x10000000
#define DATA_MASK 0x20000000
struct cdata {
int val[4];
int bits;
};
map<heptagon*, struct cdata> eucdata;
cdata orig_cdata;
void affect(cdata& d, short rv, signed char signum) {
if(rv&1) d.val[0]+=signum; else d.val[0]-=signum;
if(rv&2) d.val[1]+=signum; else d.val[1]-=signum;
if(rv&4) d.val[2]+=signum; else d.val[2]-=signum;
if(rv&8) d.val[3]+=signum; else d.val[3]-=signum;
int id = (rv>>4) & 63;
if(id < 32)
d.bits ^= (1 << id);
}
void setHeptagonRval(heptagon *h) {
if(!(h->rval0 || h->rval1)) {
h->rval0 = hrand(0x10000);
h->rval1 = hrand(0x10000);
}
}
cdata *getHeptagonCdata(heptagon *h) {
if(h->cdata) return h->cdata;
if(h == &origin) {
return h->cdata = new cdata(orig_cdata);
}
cdata mydata = *getHeptagonCdata(h->move[0]);
for(int di=3; di<5; di++) {
heptspin hs; hs.h = h; hs.spin = di;
int signum = +1;
while(true) {
heptspin hstab[15];
hstab[7] = hs;
for(int i=8; i<12; i++) {
hstab[i] = hsspin(hstab[i-1], (i&1) ? 4 : 3);
hstab[i] = hsstep(hstab[i], 0);
hstab[i] = hsspin(hstab[i], (i&1) ? 3 : 4);
}
for(int i=6; i>=3; i--) {
hstab[i] = hsspin(hstab[i+1], (i&1) ? 3 : 4);
hstab[i] = hsstep(hstab[i], 0);
hstab[i] = hsspin(hstab[i], (i&1) ? 4 : 3);
}
if(hstab[3].h->distance < hstab[7].h->distance) {
hs = hstab[3]; continue;
}
if(hstab[11].h->distance < hstab[7].h->distance) {
hs = hstab[11]; continue;
}
int jj = 7;
for(int k=3; k<12; k++) if(hstab[k].h->distance < hstab[jj].h->distance) jj = k;
int ties = 0, tiespos = 0;
for(int k=3; k<12; k++) if(hstab[k].h->distance == hstab[jj].h->distance)
ties++, tiespos += (k-jj);
// printf("ties=%d tiespos=%d jj=%d\n", ties, tiespos, jj);
if(ties == 2) jj += tiespos/2;
if(jj&1) signum = -1;
hs = hstab[jj];
break;
}
hs = hsstep(hsspin(hs, 3), 0);
setHeptagonRval(hs.h);
affect(mydata, hs.spin ? hs.h->rval0 : hs.h->rval1, signum);
/* if(!(spins[hs.h] & hs.spin)) {
spins[hs.h] |= (1<<hs.spin);
int t = 0;
for(int k=0; k<7; k++) if(spins[hs.h] & (1<<k)) t++;
static bool wast[256];
if(!wast[spins[hs.h]]) {
printf("%p %4x\n", hs.h, spins[hs.h]);
wast[spins[hs.h]] = true;
}
} */
}
return h->cdata = new cdata(mydata);
}
cdata *getEuclidCdata(heptagon *h) {
eucoord x, y;
if(eucdata.count(h)) return &(eucdata[h]);
decodeMaster(h, x, y);
if(x == 0 && y == 0) {
cdata xx;
for(int i=0; i<4; i++) xx.val[i] = 0;
xx.bits = 0;
return &(eucdata[h] = xx);
}
int ord = 1, bid = 0;
while(!((x|y)&ord)) ord <<= 1, bid++;
for(int k=0; k<3; k++) {
eucoord x1 = x + (k<2 ? ord : 0);
eucoord y1 = y - (k>0 ? ord : 0);
if((x1&ord) || (y1&ord)) continue;
eucoord x2 = x - (k<2 ? ord : 0);
eucoord y2 = y + (k>0 ? ord : 0);
cdata *d1 = getEuclidCdata(encodeMaster(x1,y1));
cdata *d2 = getEuclidCdata(encodeMaster(x2,y2));
cdata xx;
double disp = pow(2, bid/2.) * 6;
for(int i=0; i<4; i++) {
double dv = (d1->val[i] + d2->val[i])/2 + (hrand(1000) - hrand(1000))/1000. * disp;
xx.val[i] = floor(dv);
if(hrand(1000) / 1000. < dv - floor(dv)) xx.val[i]++;
}
xx.bits = 0;
for(int b=0; b<32; b++) {
bool gbit = ((hrand(2)?d1:d2)->bits >> b) & 1;
int flipchance = (1<<bid);
if(flipchance > 512) flipchance = 512;
if(hrand(1024) < flipchance) gbit = !gbit;
if(gbit) xx.bits |= (1<<b);
}
return &(eucdata[h] = xx);
}
// impossible!
return NULL;
}
int getCdata(cell *c, int j) {
if(euclid) return getEuclidCdata(c->master)->val[j];
else if(c->type == 7) return getHeptagonCdata(c->master)->val[j]*3;
else {
int jj = 0;
for(int k=0; k<6; k++) if(c->mov[k] && c->mov[k]->type == 7)
jj += getHeptagonCdata(c->mov[k]->master)->val[j];
return jj;
}
}
int getBits(cell *c) {
if(euclid) return getEuclidCdata(c->master)->bits;
else if(c->type == 7) return getHeptagonCdata(c->master)->bits;
else {
int b0 = getHeptagonCdata(createMov(c, 0)->master)->bits;
int b1 = getHeptagonCdata(createMov(c, 2)->master)->bits;
int b2 = getHeptagonCdata(createMov(c, 4)->master)->bits;
return (b0 & b1) | (b1 & b2) | (b2 & b0);
}
}
eLand getCLand(cell *c) {
int b = getBits(c);
b = (b&31) ^ (b>>5);
return land_scape[b & 31];
}
int celldistance(cell *c1, cell *c2) {
int d = 0;
cell *cl1=c1, *cr1=c1, *cl2=c2, *cr2=c2;
while(true) {
if(cl1 == cl2) return d;
if(cl1 == cr2) return d;
if(cr1 == cl2) return d;
if(cr1 == cr2) return d;
if(isNeighbor(cl1, cl2)) return d+1;
if(isNeighbor(cl1, cr2)) return d+1;
if(isNeighbor(cr1, cl2)) return d+1;
if(isNeighbor(cr1, cr2)) return d+1;
forCellEx(c, cl2) if(isNeighbor(c, cr1)) return d+2;
forCellEx(c, cl1) if(isNeighbor(c, cr2)) return d+2;
int d1 = celldist(cl1), d2 = celldist(cl2);
if(d1 >= d2) {
cl1 = chosenDown(cl1, -1, 0, celldist);
// cl1->item = eItem(rand() % 10);
cr1 = chosenDown(cr1, 1, 0, celldist);
// cr1->item = eItem(rand() % 10);
d++;
}
if(d1 <= d2) {
cl2 = chosenDown(cl2, -1, 0, celldist);
// cl2->item = eItem(rand() % 10);
cr2 = chosenDown(cr2, 1, 0, celldist);
// cr2->item = eItem(rand() % 10);
d++;
}
}
}
void clearMemory() {
extern void clearGameMemory();
clearGameMemory();
if(shmup::on) shmup::clearMemory();
cleargraphmemory();
#ifndef MOBILE
mapeditor::clearModelCells();
#endif
// EUCLIDEAN
if(euclid) {
for(int y=0; y<256; y++) for(int x=0; x<256; x++)
if(euclidean[y][x]) {
delete euclidean[y][x];
euclidean[y][x] = NULL;
}
eucdata.clear();
}
else {
DEBMEM ( verifycells(&origin); )
clearfrom(&origin);
for(int i=0; i<size(allAlts); i++) clearfrom(allAlts[i]);
allAlts.clear();
}
DEBMEM ( printf("ok\n"); )
}
#endif

View File

@ -1,3 +1,10 @@
// 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 ---
@ -53,11 +60,15 @@ const char *trollhelp =
const char *trollhelp2 =
" Additionally, all items around the killed Troll will be destroyed.";
const char *trollhelpX =
"There are several species of trolls living in the hyperbolic world. "
"Some of them leave this wall behind them when they die.";
const char *camelothelp =
"The Knights of the Round Table are the greatest warriors of these lands. "
"They are not very inventive with names though, as they call each of their "
"castles Camelot. "
"You are probably worth of joining them, but they will surely give you "
"You are probably worthy of joining them, but they will surely give you "
"some quest to prove yourself...\n\n"
"Each castle contains a single treasure, the Holy Grail, in the center. "
"The radius of the Round Table is usually 28, but after you find a Holy Grail "
@ -158,10 +169,114 @@ const char *elemdesc =
"You need to collect a Shard from each Plane to construct an Elemental Gem. "
"It is dangerous to collect too many Shards of the same type without constructing a Gem.";
const char *wildwestdesc =
"Take a revolver, kill outlaws, collect bounties.\n\n"
"Note: since this land is anachronistic, it is not available in normal game. "
"It is only available in special modes.";
const char *elecdesc =
"Whenever after your move there is a connection between a charged and a "
"grounded cell, there is a short circuit. All cells on any "
"path connecting a charged and a grounded cell (without repeated cells, "
"or two consecutive grounded/charged cells) become damaged.\n\n"
"Sandstone walls and most creatures are conductive. Great Walls are "
"isolators, but lands beyond them count as grounded.\n\n"
"Fulgurite, the treasure, is created when you manage to short circuit "
"a sandstone wall, or a Rich Metal Beast.\n\n"
"Trolls leave conductive rocks when killed, and Metal Beasts can only "
"be killed by electricity -- your attacks only stun them, or push "
"them away if already stunned.";
const char *overdesc =
"The Overgrown Woods are filled with mutant ivies! These plants "
"grow very fast. Each leaf, after being grown, can grow itself "
"on the next turn. However, each part is only able to grow "
"once in 16 turns. Outside of the Overgrown Woods, the Mutant Ivy "
"may grow only on hexagonal cells.\n\n"
"Maybe such fast growing plants could help you solve the problem "
"of hunger in your world? Kill the Mutant Ivies to collect Mutant Saplings.";
const char *cleardesc =
"A clearing in the Overgrown Woods. Obviously, this gives "
"the Mutant Ivies an infinite space to grow...\n\n"
"Mutant Fruits rot if they are not adjacent to a Mutant Ivy.";
const char *winddesc =
"Someone has put air fans in these plains, causing strong winds everywhere. "
"You think that the purpose is to harness the magical power of Air Elementals, but "
"you are not sure.\n\n"
"All cells except fans are grouped into three colors according to a pattern. "
"Wind blows counterclockwise around each group of cells of a single color. "
"Cells which are blocked by walls, or at distance at most 2 from an Air Elemental, "
"do not count for this.\n\n"
"It is illegal to move in a direction which is closer to incoming wind than to "
"outcoming wind. However, you can move two cells with the wind in a single turn, "
"and so can the birds.";
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 *NODESC = "No description yet.";
const char *GENDERSWITCH = NODESC;
// --- monsters ---
const int motypes = 102;
const char *rosedesc =
"Each eight turns, each rosebush at distance at most 5 from you will "
"release a wave of alluring scent. Creatures on the frontwave "
"will move towards where the scent came from. Even if it causes them "
"to attack their friends or beautiful creatures, or move into water, fire, chasm, or thorns of the rosebush. "
"Ivies, Ghosts, Rock Snakes, Rose Ladies and Lords, and monsters restricted to a specific "
"terrain are immune to scents.";
const char *warpdesc =
"This part of the world is warped, restricting the movement somewhat. "
"\"Diagonal\" movement and attacking between triangular cells is not allowed. "
"Flash, Storms, and Freedom spells ignore this, and Ghosts can move, attack, and "
"be attacked diagonally.";
const char *warplanddesc =
"This land is warped. Ironically, the coast is completely straight...";
const char *roselanddesc =
"This land is filled with beautiful, but dangerous, creatures and plants.";
const char *dragondesc =
"Dragons are powerful monsters. They are slow, but evil, "
"and love to pick on creatures who are even slower than "
"them. They must be stopped!\n\n"
"A Dragon moves each two turns. It may attack with all its segments, "
"or move its whole body forwards or "
"backwards, it may also move a frontal part backwards. To kill a Dragon, "
"you need to hit each of its segments. "
"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.";
const char *tortoisedesc =
"Galápagos is the land of Tortoises. "
"They are very slow, which allows the Dragons to pick on them by "
"stealing and eating their young. Bring the Baby Tortoises back, "
"but, there is a catch: the Tortoises come in many varieties, depending "
"on the part of Galápagos they live in -- there are 21 binary environmental "
"factors, and thus "
"2097152 varieties. You'll have to find a "
"Tortoise which matches the baby exactly!\n\n"
"Tortoises move each 3 turns, and attacks only stun them.\n\n"
"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 int motypes = 125;
struct monstertype {
char glyph;
@ -181,7 +296,7 @@ const char *gargdesc =
"killed, but only if next to something stable -- otherwise it falls.";
monstertype minf[motypes] = {
{ 0, 0, "none" , NULL},
{ 0, 0, "no monster" , NULL},
{ 'Y', 0x4040FF, "Yeti" ,
"A big and quite intelligent monster living in the Icy Land."
},
@ -420,13 +535,64 @@ monstertype minf[motypes] = {
"This also affects most monsters."},
{ 'D', 0xC06000, "Striped Dog", "A predator native to the Zebra."},
{ 'G', 0xFFFFFF, "Tentacle+Ghost", tentdes },
{ 'B', 0x8080C0, "Metal Beast", elecdesc },
{ 'B', 0xC060C0, "Rich Metal Beast", elecdesc },
{ 'O', 0xA06020, "Outlaw", wildwestdesc },
{ 'C', 0xC0C060, "Mutant Ivy", overdesc },
{ 'T', 0x0080FF, "Storm Troll", elecdesc },
{ 'T', 0x00C080, "Forest Troll",
"Forest Trolls create an impassable wall when they die."
},
{ 'F', 0xC35817, "Giant Fox",
"What is freedom for you? A situation when you can walk wherever you want? "
"Or a situation when you do not have to work, since you have as much tasty food "
"as you want?\n\n"
"Well, this creature has chosen the second option. It won't be happy "
"if you destroy its prison.\n"
},
{ 'C', 0x8080FF, "Wind Crow",
"A large bird who likes strong winds. Just as you, it can fly quickly in the wind."
},
{ 'G', 0xC0FFC0, "Friendly Ghost",
"Friendly ghosts are friendly beings who can go through any obstacles. However, "
"unlike most friends, they tend to fly away from you."
},
{ 'R', 0x906030, "Ratling",
"These warped humanoids are skilled warriors and sailors, and they "
"feel at home at the Warped Coast. Their battle experience has taught them "
"that enemies who wait without moving or attacking anything are the most deadly. "
"If they see such an enemy, they become extremely suspicious, and they also wait."
},
{ 'F', 0xC00000, "False Princess", GENDERSWITCH },
{ 'R', 0x500050, "Rose Lady", GENDERSWITCH },
{ 'R', 0xF0A0D0, "Rose Beauty", GENDERSWITCH },
{ 'R', 0x806040, "Ratling Avenger",
"So, you have killed a Ratling on the unwarped sea? You will be punished for this! "
"Luckily, if you run away from the Warped Sea quickly, the Ratling Avengers will lose track of you."
},
{ 'T', 0x487830, "Tortoise", tortoisedesc},
{ 'D', 0xC03000, "Dragon", dragondesc},
{ 'd', 0xC03000, "Dragon", dragondesc},
{ 'N', 0x303030, "Nighthawk", NODESC},
{ 'Y', 0xFF8000, "Yendorian Researcher",
"These people study gravity and infinite trees. "
"They have no special features, other than wearing a strange hat."
},
{ 'K', 0xA8A8A8, "Sparrowhawk",
"A bird who hunts in the treetops of Yendorian Forest."
},
// shmup specials
{ '@', 0xC0C0C0, "Rogue", "In the Shoot'em Up mode, you are armed with thrown Knives."},
{ '*', 0xC0C0C0, "Knife", "A simple, but effective, missile, used by rogues."},
{ '*', 0xFF0000, "Flail", "This attack is likely to hit the attacker."},
{ '*', 0xFFFF00, "Fireball", "This magical missile burns whatever it hits."},
{ '*', 0xFFFF00, "Tongue", "Some monsters have long tongues, which allow them to attack enemies in nearby cells."},
{ '*', 0xFFFFFF, "Airball", "This magical missile pushes back whatever it hits."}
{ '*', 0xFFFFFF, "Airball", "This magical missile pushes back whatever it hits."},
// technical
{ '?', 0x00C000, "dead bug", NODESC},
{ '?', 0xFFFF00, "electric discharge", NODESC}, // appears as 'killed by electrocution'
{ '?', 0xE06000, "dead bird", NODESC},
};
enum eMonster {
@ -462,15 +628,46 @@ enum eMonster {
moPrincessArmed, moPrincessArmedMoved,
moEdgeMonkey, 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,
// shmup specials
moPlayer, moBullet, moFlailBullet, moFireball, moTongue, moAirball,
// temporary
moDeadBug, moLightningBolt, moDeadBird
};
// --- items ----
struct genderswitch_t {
int gender;
eMonster m;
const char *name;
const char *desc;
};
const int ittypes = 67;
#define NUM_GS 6
genderswitch_t genderswitch[NUM_GS] = {
{ GEN_F, moFalsePrincess, "False Princess",
"Don't be fooled by this red-haired girl, or you will be stabbed if you come too close!"},
{ GEN_M, moFalsePrincess, "False Prince",
"Don't be fooled by this red-haired boy, or you will be stabbed if you come too close!"},
{ GEN_F, moRoseLady, "Rose Lady",
"This false princess is immune to the alluring scent of roses."},
{ GEN_M, moRoseLady, "Rose Lord",
"This false prince is immune to the alluring scent of roses."},
{ GEN_F, moRoseBeauty, "Rose Beauty",
"She has flowers in her long fair hair. You could not bring yourself to attack such a beautiful woman."},
{ GEN_M, moRoseBeauty, "Handsome Gardener",
"Tall, strong, and holding a flower in his hand. You could "
"not bring yourself to attack such a handsome man."}
};
// --- items ---
const int ittypes = 92;
struct itemtype {
char glyph;
@ -480,7 +677,7 @@ struct itemtype {
};
itemtype iinf[ittypes] = {
{ 0, 0, "none", NULL},
{ 0, 0, "no item", NULL},
{ '*', 0xFFFFFF, "Ice Diamond",
"Cold white gems, found in the Icy Land."
},
@ -653,7 +850,7 @@ itemtype iinf[ittypes] = {
"This Orb allows your boat to go against the current, "
"and also to go into the land, creating water on the way."
},
{ 'o', 0xC00060, "Orb of Air",
{ 'o', 0xC0C0FF, "Orb of Air",
"This Orb allows you to blow your enemies away.\n\n"
"Click a monster to blow it one cell away. It cannot be used against mimics, ghosts, sharks and other monsters restricted to a specific terrain, and multi-tile monsters."
},
@ -692,7 +889,109 @@ itemtype iinf[ittypes] = {
},
{ 'o', 0x306090, "Orb of Matter",
"This Orb allows to temporarily create impassable matter, either to block paths or "
"to build bridges across chasms and waters."}
"to build bridges across chasms and waters."},
{ '*', 0xF0F000, "Bounty", wildwestdesc},
{ '[', 0xC0C0C0, "Revolver", wildwestdesc},
{ '*', 0xF0F080, "Fulgurite", elecdesc},
{ '%', 0xFFFFFF, "Mutant Sapling", overdesc},
{ 'o', 0xA08000, "Orb of Stunning",
"This Orb allows you to target monsters to stun them. "
"10 charges are used to stun for 5 turns. Does not "
"work against multi-tile monsters."},
{ 'o', 0xC00000, "Orb of Luck",
"This Orb allows you to find new lands more easily. "
"Lands where you have already collected less treasure, "
"and especially the Crossroads, are more likely to "
"spawn while you have this. Additionally, Orbs of Safety "
"are more likely to spawn in the Whirlpool."
},
{ '%', 0xD03030, "Mutant Fruit", cleardesc},
{ 'o', 0xC00000, "Orb of Freedom",
"This orb is activated if you are unable to escape (radius 4) "
"without making illegal moves or "
"going through cells which are currently adjacent to enemy monsters. "
"Most game over situations are covered by this, but generally, "
"this orb is oversensitive...\n\n"
"When activated, it creates a Flash effect of radius 5."
},
{ '%', 0x606060, "Black Lotus",
"This beautiful flower is greatly prized by wizards, as it allows them to cast powerful magical spells "
"without preparation.\n"
},
{ 'o', 0x505050, "Orb of Undeath",
"Monsters slain by you in melee are turned into friendly ghosts, "
"Does not affect plants and friends."
},
{ '*', 0x8080FF, "White Dove Feather",
"This feather is truly beautiful and strong."
},
{ 'o', 0xC00060, "Orb of Empathy",
"This Orb lets your allies to share your Orb powers.\n\n"
"The following Orbs are affected:"
},
{ '>', 0x0000FF, "strong wind",
"In the Windy Plains, you can let the wind carry you, "
"causing you to move two cells with the wind in a single turn. "
"This cannot be done if you are standing at distance at most 2 "
"from the Air Elemental, or if any of the three cells on the way "
"has two wind directions.\n\n"
"Press 't' or click the destination to activate."
},
{ 'x', 0xFF00FF, "buggy item",
"Please report this as a bug."
},
{ 'x', 0xFFFF00, "buggy item",
"Please report this as a bug."
},
{ '%', 0x744c7c / 4 + 0x800000, "Thornless Rose",
"A big, beautiful, magical flower."
},
{ '*', 0xFF40A0, "Coral",
"Corals have a somewhat hyperbolic structure even in your home world, "
"but natural corals from the Warped Sea have truly beautiful shapes. "
"Ratlings know the value of corals, and thus keep them in boats for safety."
},
{ 'o', 0x764e7c*2, "Orb of Beauty",
"This Orb makes you stunningly beautiful. "
"Monsters which come next to you will be stunned for one turn. "
"Multi-tile monsters are not affected. Additionally, it makes you immune to "
"beauty."
},
{ 'o', 0xFFFF80, "Orb of the Warp",
"This Orb creates a warped zone of radius 5 around you, "
"and also allows you to move diagonally in warped zones."
},
{ 'o', 0xFFFF80, "Orb of Energy",
"This Orb halves the power usage of orbs which cost some "
"charges with each activation. It even affects the "
"one-shot orbs such as Flash or Teleport. If such an activation "
"normally costs x charges, it costs only x/2 (rounded up) "
"if you have an Orb of Energy."
},
{ 't', 0x487830, "Baby Tortoise", tortoisedesc},
{ 'o', 0x487830, "Orb of the Shell",
"This Orb protects you from physical attacks. "
"It lasts for more turns than the Orb of Shielding, but "
"10 charges are lost whenever you are attacked. "
"It also does not protect you from fires, scents, and being eaten."
},
{ '!', 0xc00000, "Apple", "A fruit from the Yendorian Forest."},
{ '!', 0xFF6000, "Dragon Scale",
"Dragon Scales are a prized material for armors. "
"They are also prized by collectors, who would like to boast "
"about how they have killed a Dragon.\n\n"
"Dragon Scales disappear after 500 turns."
},
{ 'o', 0x900000, "Orb of Domination",
"This Orb lets you ride Dragons and other worm-like creatures. "
"Simply move onto such a creature to ride them; while riding, you are protected from dangerous terrains "
"and partially from attacks (they cause you to lose half of your Domination power), "
"but you cannot collect items. When only one charge is left, "
"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.",
}
};
enum eItem { itNone, itDiamond, itGold, itSpice, itRuby, itElixir, itShard, itBone, itHell, itStatue,
@ -715,12 +1014,18 @@ enum eItem { itNone, itDiamond, itGold, itSpice, itRuby, itElixir, itShard, itBo
itSavedPrincess, itOrbLove,
itEdge, itZebra,
itFireShard, itAirShard, itEarthShard, itWaterShard,
itElemental, itOrbSummon, itOrbMatter
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
};
// --- wall types ---
const int walltypes = 69;
const int walltypes = 88;
struct walltype {
char glyph;
@ -736,7 +1041,7 @@ const char *thumpdesc = "A device that attracts sandworms and other enemies. You
const char *twdesc = "This structure will disappear after some time.";
walltype winf[walltypes] = {
{ '.', 0xFF00FF, "none", NULL},
{ '.', 0xFF00FF, "no wall", NULL},
{ '#', 0x8080FF, "ice wall",
"Ice Walls melt after some time has passed."
},
@ -745,7 +1050,7 @@ walltype winf[walltypes] = {
{ '+', 0x300090, "blue slime", slimehelp },
{ '#', 0xA0D0A0, "living wall", cavehelp},
{ '.', 0x306060, "living floor",cavehelp},
{ '#', 0xD03030, "dead troll", trollhelp},
{ '#', 0xD03030, "dead rock troll", trollhelp},
{ '#', 0xCDA98F, "sand dune",
"A natural terrain feature of the Desert."
},
@ -816,7 +1121,7 @@ walltype winf[walltypes] = {
"a water cell (and the boat will come with you)."
},
{ '.', 0x00FF00, "island", cislandhelp},
{ '.', 0x80C000, "island", cislandhelp},
{ '.', 0x80C060, "island", cislandhelp},
{ '#', 0x006000, "tree",
"The forests of Caribbean are too dense to be traversed by humans, "
"and they are hard to burn. Many colorful parrots can be found there."
@ -835,14 +1140,14 @@ walltype winf[walltypes] = {
{ '+', 0xFFFFFF, "closed gate", gatedesc },
{ '-', 0x404040, "open gate", gatedesc },
{ '_', 0xC00000, "closing plate", gatedesc },
{ '_', 0x00C000, "opening plate", gatedesc },
{ '_', 0x00C050, "opening plate", gatedesc },
{ '_', 0x202020, "trapdoor", "This floor will fall after someone goes there. Go quickly!" },
{ '+', 0xFF0000, "giant rug",
"This is the biggest Hypersian Rug you have ever seen! "
"Unfortunately, it is too large to take it as a trophy." },
{ '#', 0xfffff0, "platform", "You can stand here."},
{ '#', 0x909090, "stone gargoyle", gargdesc},
{ '.', 0x909090, "stone gargoyle floor", gargdesc},
{ '.', 0xB0B0B0, "stone gargoyle floor", gargdesc},
{ '.', 0x909090, "rubble", "Some rubble."},
{ '+', 0x804000, "ladder",
"You can use this ladder to climb the Tower."
@ -859,12 +1164,36 @@ walltype winf[walltypes] = {
{ '.', 0x909090, "stone gargoyle bridge", gargdesc},
{ '#', 0x309060, "temporary wall", twdesc},
{ '.', 0x309060, "temporary floor", twdesc},
{ '.', 0x309060, "temporary bridge", twdesc}
{ '.', 0x309060, "temporary bridge", twdesc},
{ '#', 0x3030FF, "charged wall", elecdesc},
{ '#', 0xFF3030, "grounded wall", elecdesc},
{ '#', 0xA0A060, "sandstone wall", elecdesc},
{ '+', 0x704000, "saloon wall", wildwestdesc},
{ '#', 0x90C0C0, "metal wall", elecdesc},
{ '#', 0x607030, "dead troll", trollhelpX},
{ '+', 0xC0C0FF, "fan", winddesc},
{ '?', 0xFF00FF, "<temporary>", NODESC},
{ '?', 0xFF00FF, "<earth d", NODESC},
{ '?', 0xFF00FF, "<elemental tmp>", NODESC},
{ '?', 0xFF00FF, "<elemental d>", NODESC},
{ '+', 0x607030, "unnamed floor C", NODESC},
{ '+', 0xC0C0FF, "unnamed floor D", NODESC},
{ '#', 0x764e7c, "rosebush", roselanddesc},
{ '#', 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",
"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."
}
};
enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCavefloor, waDeadTroll, waDune,
waMirror, waCloud, waThumperOff, waFire, waAncientGrave, waFreshGrave, waColumn, waSulphurC, waSulphur,
waLake, waFrozenLake, waChasm, waChasmD, waDryTree, waWetTree,
waLake, waFrozenLake, waChasm, waChasmD, waBigTree, waSmallTree,
waVinePlant, waVineHalfA, waVineHalfB, waPartialFire,
waDeadwall, waDeadfloor, waDeadfloor2, waWaxWall, waGlass, waCamelot, waRoundTable,
waCamelotMoat,
@ -879,14 +1208,16 @@ enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCav
waBonfireOff, waThumperOn, waEternalFire,
waGargoyleBridge,
waTempWall, waTempFloor, waTempBridge,
// temporary walls for various purposes
waTemporary, waEarthD, waElementalTmp, waElementalD
waCharged, waGrounded, waSandstone, waSaloon, waMetal,
waDeadTroll2, waFan,
waTemporary, waEarthD, waElementalTmp, waElementalD,
waFloorC, waFloorD, waRose, waWarpGate,
waTrunk, waSolidBranch, waWeakBranch, waCanopy
};
// --- land types ---
const int numLands = 35;
const int landtypes = numLands + 6;
const int landtypes = 56;
struct landtype {
int color;
@ -940,12 +1271,12 @@ landtype linf[landtypes] = {
"the floor.\n"
},
{ 0x008000, "Dry Forest", foresthelp},
{ 0x0000C0, "Emerald Mine",
{ 0x60C060, "Emerald Mine",
"Evil people are mining for emeralds in this living cave. "
"It does not grow naturally, but it is dug out in a regular "
"pattern, which is optimal according to the evil engineers."
},
{ 0x421C52, "Vineyard", foresthelp},
{ 0x421C52, "Vineyard", vinehelp},
{ 0x104040, "Dead Cave", deadcavehelp},
{ 0x705020, "Hive", hivehelp},
{ 0xFFFF00, "Land of Power",
@ -1004,6 +1335,26 @@ landtype linf[landtypes] = {
{ 0x4040C0, "Elemental Planes", elemdesc},
{ 0xE08020, "Canvas", "A fake Land with colored floors."},
{ 0x00C000, "Palace Quest", princessdesc}, // this is fake
{ 0xD0D060, "Wild West", wildwestdesc},
{ 0x80A080, "Land of Storms", elecdesc},
{ 0x20A050, "Overgrown Woods", overdesc},
{ 0x20D050, "Clearing", cleardesc},
{ 0x303030, "Haunted Woods", hauntdesc},
{ 0x303030, "Haunted Woods", hauntdesc},
{ 0x303030, "Haunted Woods", hauntdesc},
{ 0xC0C0FF, "Windy Plains", winddesc},
{ 0x764e7c*2, "Rose Garden", roselanddesc},
{ 0xFFD580, "Warped Coast", warplanddesc},
{ 0xFFD580, "Warped Sea", warplanddesc},
{ 0xC08080, "Crossroads IV",
"An alternate layout of the Crossroads, without walls."
},
{ 0xFFD580, "Yendorian Forest",
"This forest was planted by one of the wizards from the Ivory Tower "
"to conduct experiments with gravity."
},
{ 0x487830, "Galápagos", tortoisedesc},
{ 0xD04000, "Dragon Chasms", dragondesc},
};
enum eLand { laNone, laBarrier, laCrossroads, laDesert, laIce, laCaves, laJungle, laAlchemist, laMirror, laGraveyard,
@ -1011,8 +1362,14 @@ enum eLand { laNone, laBarrier, laCrossroads, laDesert, laIce, laCaves, laJungle
laHive, laPower, laCamelot, laTemple,
laCrossroads2, laCaribbean, laRedRock, laMinefield, laOcean, laWhirlpool,
laPalace, laLivefjord,
laEdge, laZebra, laEFire, laEAir, laEEarth, laEWater, laCrossroads3,
laOceanWall, laElementalWall, laCanvas, laPrincessQuest };
laIvoryTower, laZebra, laEFire, laEAir, laEEarth, laEWater, laCrossroads3,
laOceanWall, laElementalWall,
laCanvas, laPrincessQuest,
laWildWest, laStorms, laOvergrown, laClearing,
laHaunted, laHauntedWall, laHauntedBorder,
laWhirlwind, laRose, laGridCoast, laGridSea, laCrossroads4,
laEndorian, laTortoise, laDragon
};
// cell information for the game
@ -1052,10 +1409,103 @@ struct gcell {
// CR2 structure;
// hive Weird Rock color / pheromones;
// Ocean/coast depth
union { int32_t landpar; float heat; } LHU;
union { int32_t landpar; float heat; char bytes[4]; } LHU;
};
#define landparam LHU.landpar
#define NODIR 7
#define NOBARRIERS 8
#define LAND_OVER 44
eLand land_over[LAND_OVER] = {
laIce, laCaves, laDesert, laMotion, laJungle, laAlchemist,
laCrossroads,
laMirror, laMinefield, laZebra, laPalace, laPrincessQuest,
laOcean, laLivefjord, laGridCoast, laCaribbean, laWhirlpool, laRlyeh, laTemple,
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
};
#define LAND_EUC 42
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,
laDryForest, laWineyard, laDeadCaves, laGraveyard, laHive, laRedRock, laIvoryTower,
laOvergrown, laClearing, laStorms, laWhirlwind, laRose,
laEmerald, laCamelot, laDragon, laTortoise,
laHell, laCrossroads3, laCocytus, laPower,
laCrossroads4,
laWildWest
};
// MISSING: laCrossroads2
#define LAND_HYP 39
eLand land_hyp[LAND_HYP] = {
laHell, laCocytus, laGraveyard,
laWineyard, laDryForest, laCaves,
laPalace, laEmerald, laHive, laDeadCaves, laPower,
laOcean, laLivefjord, laRlyeh, laTemple, laIce,
laDesert, laRedRock,
laWhirlpool, laOvergrown, laClearing, laStorms,
laCaribbean, laJungle, laAlchemist, laMotion, laMirror, laMinefield,
laZebra, laElementalWall, laIvoryTower, laHaunted, laWhirlwind, laCrossroads,
laGridCoast, laRose, laDragon, laEndorian, laTortoise
};
#define LAND_SCAPE 32
eLand land_scape[LAND_SCAPE] = {
laHell, laCocytus, laGraveyard,
laWineyard, laDryForest, laCaves,
laPalace, laEmerald, laDeadCaves, laPower,
laOcean, laLivefjord, laRlyeh, laTemple, laIce,
laDesert, laRedRock,
laOvergrown, laStorms,
laJungle, laAlchemist, laMotion, laMirror, laMinefield,
laZebra, laWhirlwind, laCrossroads,
laGridCoast, laRose,
laCrossroads, laCrossroads2, laCrossroads3
};
#define LAND_TAC 44
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},
{laDeadCaves, 10, 1}, {laGraveyard, 10, 1},
{laHaunted, 10, 1},
{laIvoryTower, 10, 1}, {laEndorian, 10, 1},
{laEmerald, 10, 1},
{laCocytus, 10, 1},
{laCaribbean, 5, 2}, {laWhirlpool, 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},
{laCrossroads, 10, 1}, {laCrossroads2, 10, 1}, {laCrossroads3, 10, 1}, {laCrossroads4, 10, 1},
{laCamelot, 1, 100},
{laWildWest, 10, 1}
};
#define RANDLANDS 17
eLand randlands[RANDLANDS] = {
laIce, laDesert, laCaves, laAlchemist, laGraveyard, laPower, laLivefjord, laZebra,
laRlyeh, laDryForest, laEmerald, laWineyard, laDeadCaves, laRedRock,
laOvergrown, laWildWest, laGridCoast
};

2102
complex.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.68])
AC_INIT([hyperrogue], [7.4h])
AC_INIT([hyperrogue], [8.3j])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
${CXXFLAGS=""}
AC_CONFIG_SRCDIR([hyperpoint.cpp])
@ -43,7 +43,7 @@ AC_CHECK_HEADERS([SDL/SDL_mixer.h], [], AC_MSG_ERROR([SDL/SDL_mixer.h header was
AC_CHECK_HEADERS([SDL/SDL_ttf.h], [], AS_IF( test ["${host_os#*mingw}" != "$host_os"], [] ,AC_MSG_ERROR([SDL/SDL_ttf.h header was not found])))
# Checks for typedefs, structures, and compiler characteristics.
AC_CHECK_HEADER_STDBOOL
# AC_CHECK_HEADER_STDBOOL
AC_C_INLINE
AC_TYPE_SIZE_T

660
conformal.cpp Normal file
View File

@ -0,0 +1,660 @@
// Hyperbolic Rogue -- the conformal/history mode
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#include <complex>
namespace polygonal {
typedef long double ld;
typedef complex<long double> cld;
int SI = 4;
double STAR = 0;
int deg = 20;
#define MSI 120
ld matrix[MSI][MSI];
ld ans[MSI];
cld coef[MSI];
int maxcoef, coefid;
void solve() {
for(int i=0; i<MSI; i++) ans[i] = cos(M_PI / SI);
for(int i=0; i<MSI; i++)
for(int j=0; j<MSI; j++) {
double i0 = (i+0.) / (MSI-1);
// i0 *= i0;
// i0 = 1 - i0;
i0 *= M_PI;
matrix[i][j] =
cos(i0 * (j + 1./SI)) * (STAR > 0 ? (1+STAR) : 1)
- sin(i0 * (j + 1./SI)) * (STAR > 0 ? STAR : STAR/(1+STAR));
}
for(int i=0; i<MSI; i++) {
ld dby = matrix[i][i];
for(int k=0; k<MSI; k++) matrix[i][k] /= dby;
ans[i] /= dby;
for(int j=i+1; j<MSI; j++) {
ld sub = matrix[j][i];
ans[j] -= ans[i] * sub;
for(int k=0; k<MSI; k++)
matrix[j][k] -= sub * matrix[i][k];
}
}
for(int i=MSI-1; i>=0; i--) {
for(int j=0; j<i; j++) {
ld sub = matrix[j][i];
ans[j] -= ans[i] * sub;
for(int k=0; k<MSI; k++)
matrix[j][k] -= sub * matrix[i][k];
}
}
}
pair<ld, ld> compute(ld x, ld y, int prec) {
if(pmodel == 4) {
cld z(x,y);
cld res (0,0);
for(int i=maxcoef; i>=0; i--) { res += coef[i]; if(i) res *= z; }
return make_pair(real(res), imag(res));
}
cld z(x, y);
cld res (0,0);
cld zp = 1; for(int i=0; i<SI; i++) zp *= z;
for(int i=prec; i>0; i--) {
res += ans[i];
res *= zp;
}
res += ans[0]; res *= z;
return make_pair(real(res), imag(res));
}
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;
}
#endif
}
}
#ifndef MOBILE
namespace spiral {
typedef long double ld;
typedef complex<long double> cld;
int shiftx, shifty, velx, vely;
vector<pair<short, short> > quickmap;
int CX, CY, SX, SY, Yshift;
SDL_Surface *band, *out;
bool displayhelp = true;
void precompute() {
CX = band->w;
CY = band->h;
SX = out->w;
SY = out->h;
ld k = -2*M_PI*M_PI / log(2.6180339);
// cld mnoznik = cld(0, M_PI) / cld(k, M_PI);
cld factor = cld(0, -CY/2/M_PI/M_PI) * cld(k, M_PI);
Yshift = CY * k / M_PI;
quickmap.clear();
double xc = ((SX | 1) - 2) / 2.;
double yc = ((SY | 1) - 2) / 2.;
for(int y=0; y<SY; y++)
for(int x=0; x<SX; x++) {
cld z(x-xc, y-yc);
cld z1 = log(z);
z1 = z1 * factor;
quickmap.push_back(make_pair(int(real(z1)) % CX, int(imag(z1))));
}
}
void draw() {
int c = 0;
for(int y=0; y<SY; y++) for(int x=0; x<SX; x++) {
pair<short,short> p = quickmap[c++];
int cx = p.first + shiftx;
int cy = p.second + + shifty;
int d = cy / CY;
cy -= d * CY; cx -= d * Yshift;
if(cy<0) cy += CY, cx += Yshift;
cx %= CX; if(cx<0) cx += CX;
qpixel(out, x, y) = qpixel(band, cx, cy);
}
}
void loop(SDL_Surface *_band) {
bool saveGL = vid.usingGL;
if(saveGL) { vid.usingGL = false; setvideomode(); }
band = _band;
out = s;
precompute();
shiftx = shifty = 0;
velx=1; vely=1;
bool dosave = false;
while(true) {
time_t timer;
timer = time(NULL);
char buf[128];
strftime(buf, 128, "spiral-%y%m%d-%H%M%S" IMAGEEXT, localtime(&timer));
SDL_LockSurface(s);
draw();
if(dosave) { dosave = false; IMAGESAVE(s, buf); }
SDL_UnlockSurface(s);
if(displayhelp) {
displaystr(SX/2, vid.fsize*2, 0, vid.fsize, "arrows = navigate, ESC = return, h = hide help", 0xFFFFFF, 8);
displaystr(SX/2, SY - vid.fsize*2, 0, vid.fsize, XLAT("s = save to " IMAGEEXT, buf), 0xFFFFFF, 8);
}
SDL_UpdateRect(s, 0, 0, 0, 0);
shiftx += velx; shifty += vely;
SDL_Event event;
while(SDL_PollEvent(&event)) switch (event.type) {
case SDL_QUIT: case SDL_MOUSEBUTTONDOWN:
goto breakloop;
case SDL_KEYDOWN: {
int key = event.key.keysym.sym;
// int uni = event.key.keysym.unicode;
if(key == SDLK_RIGHT) velx++;
if(key == SDLK_LEFT) velx--;
if(key == SDLK_UP) vely++;
if(key == SDLK_DOWN) vely--;
if(key == SDLK_ESCAPE) goto breakloop;
if(key == 'h') displayhelp = !displayhelp;
if(key == 's') dosave = true;
}
}
}
breakloop:
quickmap.clear();
if(saveGL) { vid.usingGL = true; setvideomode(); }
}
}
#endif
bool isbad(ld z) { return !isfinite(z) || fabs(z) > 1e6; }
namespace conformal {
int lastprogress;
void progress(string str) {
#ifndef MOBILE
int tick = SDL_GetTicks();
if(tick > lastprogress + 250) {
lastprogress = tick;
msgs.clear();
addMessage(str);
drawscreen();
}
#endif
}
bool on;
vector<shmup::monster*> v;
int llv;
double phase;
vector<pair<cell*, eMonster> > killhistory;
vector<pair<cell*, eItem> > findhistory;
vector<cell*> movehistory;
bool includeHistory;
double lvspeed = 1;
int bandhalf = 200;
int bandsegment = 16000;
int rotation = 0;
bool autoband = false;
bool autobandhistory = false;
bool dospiral = true;
void clear() {
on = false;
int N = size(v);
for(int i=0; i<N; i++) delete v[i];
v.resize(0);
}
void create() {
if(celldist(cwt.c) <= 7) {
addMessage("Must go a distance from the starting point");
return;
}
on = true;
cell *c = cwt.c;
for(int q=0; q<5; q++) {
for(int i=0; i<c->type; i++)
if(celldist(c->mov[i]) > celldist(c)) {
c = c->mov[i];
break;
}
}
while(true) {
shmup::monster *m = new shmup::monster;
m->at = Id;
m->base = c;
v.push_back(m);
if(c == origin.c7) break;
for(int i=0; i<c->type; i++)
if(celldist(c->mov[i]) < celldist(c)) {
c = c->mov[i];
break;
}
}
reverse(v.begin(), v.end());
int Q = size(v)-1;
for(int i=0; i<1000; i++) {
progress(XLAT("Preparing the line (%1/1000)...", its(i+1)));
/*for(int j=1; j<Q; j++) {
hyperpoint cur = v[j]->at * C0;
printf("%4d/%3d. %p [%3d] %Lf %Lf %Lf\n", i, j, v[j]->base, celldist(v[j]->base), cur[0], cur[1], cur[2]);
} */
for(int j=1; j<Q; j++) if((j^i)&1) {
virtualRebase(v[j], false);
hyperpoint prev = shmup::calc_relative_matrix(v[j-1]->base, v[j]->base->master) *
v[j-1]->at * C0;
hyperpoint next = shmup::calc_relative_matrix(v[j+1]->base, v[j]->base->master) *
v[j+1]->at * C0;
hyperpoint hmid = mid(prev, next);
v[j]->at = rgpushxto0(hmid);
v[j]->at = v[j]->at * rspintox(inverse(v[j]->at) * next);
fixmatrix(v[j]->at);
}
}
llv = ticks;
phase = 0;
}
void apply() {
int t = ticks;
phase += (t-llv) * lvspeed / 400.;
llv = t;
int siz = size(v);
while(phase < 1) phase += siz - 2;
while(phase >= siz-1) phase -= siz - 2;
int ph = int(phase);
if(ph<1 || ph >= siz-1) return;
viewctr.h = v[ph]->base->master;
viewctr.spin = 0;
View = inverse(v[ph]->at);
int j = ph;
hyperpoint now = v[j]->at * C0;
hyperpoint next = shmup::calc_relative_matrix(v[j+1]->base, v[j]->base->master) *
v[j+1]->at * C0;
View = spin(M_PI/2 * rotation) * xpush(-(phase-ph) * hdist(now, next)) * View;
playermoved = false;
}
int measureLength() {
int rad = vid.radius;
vid.radius = bandhalf;
int tpixels = 0;
int siz = size(v);
for(int j=1; j<siz-1; j++) {
hyperpoint next =
inverse(v[j]->at) *
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;
}
vid.radius = rad;
return tpixels;
}
void restore();
void restoreBack();
#ifndef MOBILE
void createImage(bool dospiral) {
int segid = 1;
inHighQual = true;
if(includeHistory) restore();
int bandfull = 2*bandhalf;
int len = measureLength();
time_t timer;
timer = time(NULL);
char timebuf[128];
strftime(timebuf, 128, "%y%m%d-%H%M%S", localtime(&timer));
rotation = 0;
SDL_Surface *sav = s;
SDL_Surface *bbuf = SDL_CreateRGBSurface(SDL_SWSURFACE,bandfull,bandfull,32,0,0,0,0);
s = bbuf;
int ssr = sightrange; sightrange = 10; int sch = cheater; cheater = 0;
videopar vid2 = vid; vid.xres = vid.yres = bandfull; vid.scale = 1;
calcparam();
vid.radius = bandhalf;
int xpos = 0;
SDL_Surface *band = SDL_CreateRGBSurface(SDL_SWSURFACE, min(len, bandsegment), bandfull,32,0,0,0,0);
if(!band) {
addMessage("Could not create an image of that size.");
}
else {
int siz = size(v);
for(int j=1; j<siz-1; j++) {
SDL_Surface *buffer = s;
emtype cm = cmode;
s = sav;
cmode = emProgress;
char buf[128];
sprintf(buf, "#%03d", segid);
progress(s0 + buf + " ("+its(j)+"/"+its(siz-2)+")");
calcparam();
vid.radius = bandhalf;
cmode = cm;
s = buffer;
viewctr.h = v[j]->base->master;
viewctr.spin = 0;
View = inverse(v[j]->at);
SDL_FillRect(s, NULL, 0);
bool ugl = vid.usingGL;
vid.usingGL = false;
drawfullmap();
vid.usingGL = ugl;
hyperpoint next =
inverse(v[j]->at) *
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);
int bwidth = x-bandhalf;
drawsegment:
for(int cy=0; cy<bandfull; cy++) for(int cx=0; cx<bwidth; cx++)
qpixel(band, xpos+cx, cy) = qpixel(s, bandhalf+cx, cy);
if(xpos+bwidth > bandsegment) {
char buf[128];
sprintf(buf, "bandmodel-%s-%03d" IMAGEEXT, timebuf, segid++);
if(dospiral) {
swap(vid.xres, vid2.xres); swap(vid.yres, vid2.yres); s = sav;
spiral::loop(band);
swap(vid.xres, vid2.xres); swap(vid.yres, vid2.yres); s = bbuf;
}
IMAGESAVE(band, buf);
SDL_FreeSurface(band);
len -= bandsegment; xpos -= bandsegment;
band = SDL_CreateRGBSurface(SDL_SWSURFACE, min(len, bandsegment), bandfull,32,0,0,0,0);
goto drawsegment;
}
xpos += bwidth;
}
}
char buf[128];
sprintf(buf, "bandmodel-%s-%03d" IMAGEEXT, timebuf, segid++);
IMAGESAVE(band, buf);
SDL_FreeSurface(sav);
s = sav; vid = vid2; sightrange = ssr; cheater = sch;
if(includeHistory) restoreBack();
if(dospiral) spiral::loop(band);
addMessage(XLAT("Saved the band image as: ") + buf);
SDL_FreeSurface(band);
inHighQual = false;
}
#endif
const char* directions[5][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" }
};
const char *modelnames[5] = {
"disk", "half-plane", "band", "polygonal", "polynomial"
};
void show() {
displayStat( 0, XLAT("conformal/history mode"), "", ' ');
displayStat( 2, XLAT("include history"), ONOFF(includeHistory), 'i');
displayStat( 4, XLAT("model used"), modelnames[pmodel], 'm');
displayStat( 5, 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');
}
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');
}
displayStat(10, XLAT("prepare the line animation"), ONOFF(on), 'e');
if(on) displayStat(11, XLAT("animation speed"), fts(lvspeed), 'a');
#ifndef MOBILE
displayStat(13, XLAT("render bands automatically"), ONOFF(autoband), 'o');
if(autoband)
displayStat(14, XLAT("include history when auto-rendering"), ONOFF(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');
if(renderable)
displayStat(18, XLAT("render now (length: %1)", its(measureLength())), "", 'f');
}
#endif
displayStat(20, XLAT("exit this menu"), "", 'q');
mouseovers = XLAT("see http://www.roguetemple.com/z/hyper/conformal.php");
}
void handleKey(int uni, int sym) {
if(uni == 'e') {
if(on) clear();
else {
if(canmove && !cheater) {
addMessage("Enable cheat mode or GAME OVER to use this");
return;
}
if(canmove && cheater) cheater++;
create();
}
}
else if(uni == 'o')
autoband = !autoband;
else if(uni == 'm') {
pmodel++;
pmodel %= 5;
if(pmodel == 3) 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 == 4) {
int ci = polygonal::coefid;
polygonal::coef[polygonal::coefid] += polygonal::cld(shiftmul/100/ci/ci, 0);
}
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 == '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 == 'g') { dospiral = !dospiral; }
#ifndef MOBILE
else if(uni == 'f' && pmodel == 2 && on) createImage(dospiral);
#endif
else if(sym == 'q' || sym == SDLK_ESCAPE || sym == '0') { cmode = emNormal; }
else if(sym == 'i') {
if(canmove && !cheater) {
addMessage("Enable cheat mode or GAME OVER to use this");
return;
}
if(canmove && cheater) cheater++;
includeHistory = !includeHistory;
}
else if(sym == 'j') {
autobandhistory = !autobandhistory;
}
}
void restore() {
sval++;
for(int i=0; i<size(movehistory); i++)
movehistory[i]->aitmp = sval;
sval++;
int sk = size(killhistory);
for(int i=0; i<sk; i++) {
eMonster m = killhistory[i].second;
killhistory[i].second = killhistory[i].first->monst;
killhistory[i].first->monst = m;
killhistory[i].first->aitmp = sval;
}
int si = size(findhistory);
for(int i=0; i<si; i++) {
eItem m = findhistory[i].second;
findhistory[i].second = findhistory[i].first->item;
findhistory[i].first->item = m;
findhistory[i].first->aitmp = sval;
}
}
void restoreBack() {
int sk = size(killhistory);
for(int i=sk-1; i>=0; i--) {
eMonster m = killhistory[i].second;
killhistory[i].second = killhistory[i].first->monst;
killhistory[i].first->monst = m;
}
int si = size(findhistory);
for(int i=si-1; i>=0; i--) {
eItem m = findhistory[i].second;
findhistory[i].second = findhistory[i].first->item;
findhistory[i].first->item = m;
}
}
void renderAutoband() {
#ifndef MOBILE
if(celldist(cwt.c) <= 7) return;
if(!autoband) return;
int spm = pmodel;
bool ih = includeHistory;
includeHistory = autobandhistory;
pmodel = 2;
create();
createImage(dospiral);
clear();
pmodel = spm;
includeHistory = ih;
#endif
}
}

555
flags.cpp Normal file
View File

@ -0,0 +1,555 @@
// Hyperbolic Rogue
// implementation of various simple flags for lands, items, monsters, and walls
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
bool isIcyLand(eLand l) {
return l == laIce || l == laCocytus;
}
bool isIcyLand(cell *c) {
return isIcyLand(c->land);
}
bool isGravityLand(eLand l) {
return l == laIvoryTower || l == laEndorian;
}
// watery
bool isWatery(cell *c) {
return c->wall == waCamelotMoat || c->wall == waSea || c->wall == waLake;
}
bool isChasmy(cell *c) {
return c->wall == waChasm || c->wall == waSulphur || c->wall == waSulphurC;
}
bool isWateryOrBoat(cell *c) {
return isWatery(c) || c->wall == waBoat;
}
bool isNoFlight(cell *c) {
return
c->wall == waBoat || c->wall == waVineHalfA || c->wall == waVineHalfB ||
c->wall == waStrandedBoat || c->wall == waTrunk;
}
bool boatStrandable(cell *c) {
return c->wall == waNone && (c->land == laLivefjord || c->land == laOcean);
}
// monster/wall types
bool isFire(cell *w) {
return w->wall == waFire || w->wall == waPartialFire || w->wall == waEternalFire;
}
bool isThumper(eWall w) {
return w == waThumperOff || w == waThumperOn;
}
bool isThumper(cell *c) {
return isThumper(c->wall);
}
bool isActivable(cell *c) {
return c->wall == waThumperOff || c->wall == waBonfireOff;
}
bool hasTimeout(cell *c) {
return c->wall == waThumperOn || c->wall == waFire || c->wall == waPartialFire ||
c->wall == waTempWall || c->wall == waTempFloor || c->wall == waTempBridge;
}
bool isMimic(eMonster m) {
return m == moMirror || m == moMirage;
}
bool isMimic(cell *c) {
return isMimic(c->monst);
}
bool isPrincess(eMonster m) {
return
m == moPrincess || m == moPrincessMoved ||
m == moPrincessArmed || m == moPrincessArmedMoved;
}
bool isGolemOrKnight(eMonster m) {
return
m == moGolem || m == moGolemMoved ||
m == moKnight || m == moKnightMoved ||
m == moTameBomberbird || m == moTameBomberbirdMoved ||
m == moMouse || m == moMouseMoved ||
m == moFriendlyGhost ||
isPrincess(m);
}
bool isGolemOrKnight(cell *c) { return isGolemOrKnight(c->monst); }
bool isNonliving(eMonster m) {
return
m == moMirage || m == moMirror || m == moGolem || m == moGolemMoved ||
m == moZombie || m == moGhost || m == moShadow || m == moSkeleton ||
m == moEvilGolem || m == moIllusion || m == moEarthElemental ||
m == moWaterElemental;
}
bool isMetalBeast(eMonster m) {
return m == moMetalBeast || m == moMetalBeast2;
}
bool isStunnable(eMonster m) {
return m == moPalace || m == moFatGuard || m == moSkeleton || isPrincess(m) ||
isMetalBeast(m) || m == moTortoise || isDragon(m);
}
bool hasHitpoints(eMonster m) {
return m == moPalace || m == moFatGuard || m == moVizier || isPrincess(m);
}
bool isMountable(eMonster m) {
return isWorm(m);
}
bool isFriendly(eMonster m) {
return isMimic(m) || isGolemOrKnight(m) || m == moIllusion;
}
bool isFriendly(cell *c) {
if(items[itOrbDomination] && c->monst && sameMonster(c, cwt.c)) return true;
return isFriendly(c->monst);
}
bool isBug(eMonster m) {
return m >= moBug0 && m < moBug0+BUGCOLORS;
}
bool isBug(cell *c) {
return isBug(c->monst);
}
bool isFriendlyOrBug(cell *c) { // or killable discord!
// do not attack the stunned Princess
if(isPrincess(c->monst) && c->stuntime) return false;
return isFriendly(c) || isBug(c) || (c->monst && markOrb(itOrbDiscord) && !c->stuntime);
}
bool isIvy(eMonster m) {
return m == moIvyRoot || m == moIvyHead || m == moIvyBranch || m == moIvyWait ||
m == moIvyNext || m == moIvyDead;
}
bool isIvy(cell *c) {
return isIvy(c->monst);
}
bool isMonsterPart(eMonster m) {
return m == moMutant || (isIvy(m) && m != moIvyRoot);
}
bool isMutantIvy(eMonster m) {
return m == moMutant;
}
bool isBulletType(eMonster m) {
return
m == moBullet || m == moFlailBullet ||
m == moFireball || m == moTongue || m == moAirball;
}
bool isMutantIvy(cell *c) { return isMutantIvy(c->monst); }
bool isDemon(eMonster m) {
return m == moLesser || m == moLesserM || m == moGreater || m == moGreaterM;
}
bool isDemon(cell *c) {
return isDemon(c->monst);
}
bool isWormHead(eMonster m) {
return m == moWorm || m == moTentacle || m == moHexSnake || m == moDragonHead;
}
bool isWorm(eMonster m) {
return m == moWorm || m == moWormtail || m == moWormwait ||
m == moTentacle || m == moTentacletail || m == moTentaclewait ||
m == moTentacleEscaping || m == moTentacleGhost ||
m == moHexSnake || m == moHexSnakeTail ||
m == moDragonHead || m == moDragonTail;
}
bool isWorm(cell *c) {
return isWorm(c->monst);
}
bool isWitch(eMonster m) {
// evil golems don't count
return m >= moWitch && m < moWitch+NUMWITCH-1;
}
bool isOnCIsland(cell *c) {
return (c->wall == waCIsland || c->wall == waCTree || c->wall == waCIsland2);
}
bool isGhostable(eMonster m) {
return m && !isFriendly(m) && !isIvy(m) && !isMultitile(m) && !isMutantIvy(m);
}
bool cellUnstable(cell *c) {
return (c->land == laMotion && c->wall == waNone) || c->wall == waTrapdoor;
}
bool cellUnstableOrChasm(cell *c) {
return
(c->land == laMotion && c->wall == waNone) ||
c->wall == waChasm || c->wall == waTrapdoor;
}
bool cellHalfvine(cell *c) {
return c->wall == waVineHalfA || c->wall == waVineHalfB;
}
bool isWarped(eLand l) {
return l == laGridCoast || l == laGridSea;
}
bool isElementalShard(eItem i) {
return
i == itFireShard || i == itAirShard || i == itEarthShard || i == itWaterShard;
}
eMonster elementalOf(eLand l) {
if(l == laEFire) return moFireElemental;
if(l == laEWater) return moWaterElemental;
if(l == laEAir) return moAirElemental;
if(l == laEEarth) return moEarthElemental;
return moNone;
}
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 == itBounty || i == itFulgurite || i == itMutant || i == itLotus || i == itMutant2 ||
i == itWindstone || i == itCoral || i == itRose ||
i == itBabyTortoise || i == itDragon || i == itApple)
return IC_TREASURE;
if(i == itKey || i == itOrbYendor || i == itGreenStone || i == itHolyGrail || i == itCompass ||
i == itSavedPrincess || isElementalShard(i) || i == itRevolver || i == itStrongWind)
return IC_OTHER;
return IC_ORB;
}
bool isAlch(eWall w) {
return w == waFloorA || w == waFloorB;
}
bool isAlch(cell *c) { return isAlch(c->wall); }
bool isAlchAny(eWall w) {
return w == waFloorA || w == waFloorB || w == waFloorC || w == waFloorD;
}
bool isAlchAny(cell *c) { return isAlchAny(c->wall); }
bool realred(eWall w) {
return w == waRed1 || w == waRed2 || w == waRed3;
}
int snakelevel(eWall w) {
if(w == waRed1 || w == waDeadfloor2 || w == waRubble || w == waGargoyleFloor ||
w == waGargoyleBridge || w == waTempFloor || w == waTempBridge)
return 1;
if(w == waRed2) return 2;
if(w == waRed3) return 3;
return 0;
}
int snakelevel(cell *c) { return snakelevel(c->wall); }
bool isWall(cell *w) {
if(w->wall == waNone || isAlchAny(w) ||
w->wall == waCavefloor || w->wall == waFrozenLake || w->wall == waVineHalfA ||
w->wall == waVineHalfB || w->wall == waDeadfloor || w->wall == waDeadfloor2 ||
w->wall == waRubble || w->wall == waGargoyleFloor || w->wall == waGargoyleBridge ||
w->wall == waTempFloor || w->wall == waTempBridge ||
w->wall == waBoat || w->wall == waCIsland || w->wall == waCIsland2 ||
w->wall == waRed1 || w->wall == waRed2 || w->wall == waRed3 ||
w->wall == waMineUnknown || w->wall == waMineMine || w->wall == waMineOpen ||
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)
return false;
if(isWatery(w) || isChasmy(w) || isFire(w)) return false;
return true;
}
bool isAngryBird(eMonster m) {
return m == moEagle || m == moAlbatross || m == moBomberbird || m == moGargoyle ||
m == moWindCrow || m == moKestrel || m == moNighthawk;
}
bool isBird(eMonster m) {
return isAngryBird(m) || m == moTameBomberbird || m == moTameBomberbirdMoved;
}
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 == moFireFairy || m == moCrystalSage || m == moHedge ||
m == moVineBeast || m == moLancer || m == moFlailer ||
m == moMiner || m == moDarkTroll ||
(playerInPower() && (
(isWitch(m) && m != moWitchGhost && m != moWitchWinter) || m == moEvilGolem
)) ||
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 == moOutlaw || m == moRedFox || m == moFalsePrincess || m == moRoseLady ||
m == moRoseBeauty || m == moWolf ||
m == moTortoise || m == moLemur;
}
// from-to
bool isGhost(eMonster m) {
return m == moGhost || m == moTentacleGhost || m == moFriendlyGhost;
}
bool isGhostMover(eMonster m) {
return m == moGhost || m == moGreaterShark || m == moTentacleGhost ||
(playerInPower() && (m == moWitchGhost || m == moWitchWinter));
}
bool isShark(eMonster m) {
return m == moShark || m == moCShark || m == moGreaterShark;
}
bool isSlimeMover(eMonster m) {
return
m == moSlime || m == moSeep || m == moShark ||
m == moVineSpirit || m == moCShark || m == moParrot;
}
bool isBlowableMonster(eMonster m) {
return m && !(
isWorm(m) || isIvy(m) || isMutantIvy(m) || isSlimeMover(m) ||
m == moGhost || m == moGreaterShark ||
m == moWaterElemental || m == moWitchGhost || isMimic(m)
);
}
bool isMultitile(eMonster m) {
return isWorm(m) || isIvy(m) || isMutantIvy(m);
}
bool isSlimeMover(cell *c) {
return isSlimeMover(c->monst);
}
int slimegroup(cell *c) {
if(c->wall == waCavewall || c->wall == waDeadwall)
return 1;
if(isWatery(c))
return 2;
if(c->wall == waFloorA)
return 3;
if(c->wall == waFloorB)
return 4;
if(c->wall == waVinePlant || cellHalfvine(c))
return 5;
if(c->wall == waCTree)
return 6;
return 0;
}
bool isLeader(eMonster m) {
return m == moPirate || m == moCultistLeader || m == moViking || m == moRatling || m == moRatlingAvenger;
}
bool isFlying(eMonster m) {
return isBird(m) || isGhost(m) || m == moAirElemental || isDragon(m) ||
(isFriendly(m) && checkOrb(m, itOrbGhost));
}
bool survivesChasm(eMonster m) {
return isFlying(m);
}
bool ignoresPlates(eMonster m) {
return m == moMouse || isFlying(m);
}
bool itemBurns(eItem it) {
return it && it != itOrbDragon && it != itOrbFire && it != itDragon;
}
bool attackThruVine(eMonster m) {
return m == moGhost || m == moVineSpirit || m == moFriendlyGhost || m == moTentacleGhost;
}
bool attackNonAdjacent(eMonster m) {
return m == moGhost || m == moFriendlyGhost || m == moTentacleGhost;
}
bool isInactiveEnemy(cell *w, eMonster forwho) {
if(w->monst == moWormtail || w->monst == moWormwait || w->monst == moTentacletail || w->monst == moTentaclewait || w->monst == moHexSnakeTail)
return true;
if(w->monst == moLesserM || w->monst == moGreaterM)
return true;
if(w->monst == moIvyRoot || w->monst == moIvyWait || w->monst == moIvyNext || w->monst == moIvyDead)
return true;
if(w->monst && ((forwho == moPlayer) ? realstuntime(w) : realstuntime(w) > 1) && !isFriendly(w))
return true;
return false;
}
// forpc = true (for PC), false (for golems)
bool isActiveEnemy(cell *w, eMonster forwho) {
if(((forwho == moPlayer) ? realstuntime(w) : realstuntime(w) > 1))
return false;
if(w->monst == moNone) return false;
if(isFriendly(w)) return false;
if(isInactiveEnemy(w, forwho)) return false;
return true;
}
bool isUnarmed(eMonster m) {
return
m == moMouse || m == moMouseMoved || m == moPrincess || m == moPrincessMoved ||
m == moCrystalSage;
}
bool isArmedEnemy(cell *w, eMonster forwho) {
return w->monst != moCrystalSage && isActiveEnemy(w, forwho);
}
bool isHive(eLand l) {
return l == laHive;
}
bool isIcyWall(cell *c) {
return c->wall == waNone || c->wall == waIcewall || c->wall == waFrozenLake || c->wall == waLake;
}
bool eternalFire(cell *c) {
return c->land == laDryForest || c->land == laPower || c->land == laMinefield ||
c->land == laEFire || c->land == laElementalWall;
}
bool isCyclic(eLand l) {
return
l == laWhirlpool || l == laTemple || l == laCamelot || l == laClearing;
}
bool haveRangedOrb() {
return
items[itOrbPsi] || items[itOrbDragon] || items[itOrbTeleport] ||
items[itOrbIllusion] || items[itOrbTelekinesis] || items[itOrbAir] ||
items[itOrbFrog] || items[itOrbSummon] || items[itOrbMatter] ||
items[itRevolver] || items[itOrbStunning] || items[itStrongWind] ||
items[itOrbDomination];
}
bool isOffensiveOrb(eItem it) {
return it == itOrbLightning || it == itOrbFlash || it == itOrbThorns ||
it == itOrbDragon || it == itOrbStunning ||
it == itOrbFreedom || it == itOrbPsi;
}
bool isRangedOrb(eItem i) {
return i == itOrbPsi || i == itOrbDragon || i == itOrbTeleport || i == itOrbIllusion ||
i == itOrbTelekinesis || i == itOrbAir || i == itOrbFrog ||
i == itOrbSummon || i == itOrbMatter || i == itRevolver || i == itOrbStunning ||
i == itOrbDomination;
}
bool isProtectionOrb(eItem i) {
return i == itOrbWinter || i == itOrbShield || i == itOrbInvis || i == itOrbShell;
}
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;
}
bool isUtilityOrb(eItem i) {
return i == itOrbSpeed || i == itOrbDigging ||
i == itOrbSafety || i == itOrbTeleport || i == itOrbGhost ||
i == itOrbPreserve || i == itOrbTelekinesis ||
i == itOrbSummon || i == itOrbLuck || i == itOrbEnergy;
}
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));
}
bool isDragon(eMonster m) { return m == moDragonHead || m == moDragonTail; }
bool survivesWater(eMonster m) {
return
m == moShark || m == moGreaterShark || m == moCShark ||
isGhost(m) || m == moWitchGhost ||
isBird(m) || m == moWaterElemental || m == moAirElemental ||
isWorm(m) || isIvy(m) || isDragon(m) ||
m == moTortoise; // Tortoises and Ivies survive, but don't go through water
}
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);
}
bool survivesMine(eMonster m) {
return isFlying(m);
}
bool survivesWall(eMonster m) {
return isGhost(m);
}
bool survivesThorns(eMonster m) {
return isGhost(m) || m == moSkeleton;
}
bool survivesFall(eMonster m) {
return isBird(m) || m == moAirElemental || m == moSkeleton || isDragon(m);
}
bool isThorny(eWall w) { return w == waRose; }
bool checkOrb(eMonster m1, eItem orb) {
if(m1 == moPlayer) return markOrb(orb);
if(isFriendly(m1)) return markEmpathy(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);
}

9022
game.cpp

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,31 @@
// Hyperbolic Rogue
// Copyright (C) 2011-2012 Zeno Rogue, see 'hyper.cpp' for details
// geometrical constants
ld tessf, crossf, hexf;
// Copyright (C) 2011-2012 Zeno Rogue, see 'hyper.cpp' for details
ld tessf, crossf, hexf, hcrossf;
// 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
#define ALPHA (M_PI*2/7)
hyperpoint Crad[42];
transmatrix heptmove[7], hexmove[7];
transmatrix invheptmove[7], invhexmove[7];
// the results are:
// hexf = 0.378077 hcrossf = 0.620672 tessf = 1.090550
void precalc() {
DEBB(DF_INIT, (debugfile,"precalc\n"));
if(euclid) return;
ld fmin = 1, fmax = 2;
for(int p=0; p<100; p++) {
@ -30,7 +43,8 @@ void precalc() {
ld v1 = intval(H, C0), v2 = intval(H, xpush(tessf) * C0);
if(v1 < v2) fmin = f; else fmax = f;
}
crossf = fmin;
hcrossf = fmin;
crossf = purehepta ? tessf : hcrossf;
fmin = 0, fmax = tessf;
for(int p=0; p<100; p++) {
@ -43,6 +57,8 @@ void precalc() {
}
hexf = fmin;
// 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++)
@ -50,6 +66,8 @@ void precalc() {
for(int d=0; d<7; 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]);
}
transmatrix ddi(ld dir, ld dist) {

2862
graph.cpp

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,8 @@ struct heptagon;
struct cell;
cell *newCell(int type, heptagon *master);
#define CDATA
struct heptagon {
// automaton state
hstate s : 8;
@ -31,6 +33,11 @@ struct heptagon {
short fiftyval;
// zebra generator (1B actually)
short zebraval;
#ifdef CDATA
// evolution data
short rval0, rval1;
struct cdata *cdata;
#endif
// central cell
cell *c7;
// associated generator of alternate structure, for Camelot and horocycles
@ -73,6 +80,9 @@ heptagon *buildHeptagon(heptagon *parent, int d, hstate s, int pard = 0) {
h->c7 = newCell(7, h);
h->emeraldval = emerald_heptagon(parent->emeraldval, d);
h->zebraval = zebra_heptagon(parent->zebraval, d);
#ifdef CDATA
h->rval0 = h->rval1 = 0; h->cdata = NULL;
#endif
if(parent == &origin)
h->fiftyval = fiftytable[0][d];
else
@ -86,14 +96,15 @@ heptagon *buildHeptagon(heptagon *parent, int d, hstate s, int pard = 0) {
//generateEmeraldval(parent);
//generateEmeraldval(h);
if(pard == 0) {
if(parent->s == hsOrigin) h->distance = 2;
if(purehepta) h->distance = parent->distance + 1;
else if(parent->s == hsOrigin) h->distance = 2;
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 h->distance = parent->distance + 2;
}
else h->distance = parent->distance - 2;
else h->distance = parent->distance - (purehepta?1:2);
return h;
}
@ -107,10 +118,12 @@ void addSpin(heptagon *h, int d, heptagon *from, int rot, int spin) {
//generateEmeraldval(h->move[d]); generateEmeraldval(h);
}
extern int hrand(int);
heptagon *createStep(heptagon *h, int d) {
d = fixrot(d);
if(h->s != hsOrigin && !h->move[0]) {
buildHeptagon(h, 0, hsA, 4);
buildHeptagon(h, 0, hsA, 3 + hrand(2));
}
if(h->move[d]) return h->move[d];
if(h->s == hsOrigin) {

139
hyper.cpp
View File

@ -15,13 +15,19 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// disable for the Android version
#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 VER "7.4h"
#define VERNUM 7480
#define VERNUM_HEX 0x7480
#include <stdio.h>
#include <SDL/SDL.h>
@ -38,6 +44,9 @@
using namespace std;
FILE *debugfile;
int debugflags;
const char *scorefile = "hyperrogue.log";
const char *conffile = "hyperrogue.ini";
@ -47,25 +56,26 @@ string picfile = "hyperrogue.pic";
const char *loadlevel = NULL;
const char *musicfile = "";
typedef long double ld;
#ifdef LINUX
#include <sys/resource.h>
template<class T> int size(T& x) {return x.size(); }
string its(int i) { char buf[64]; sprintf(buf, "%d", i); 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(i);
return llts(i/10) + its(i%10);
}
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 itsh(int i) {static char buf[16]; sprintf(buf, "%03X", i); return buf; }
void moreStack() {
const rlim_t kStackSize = 1 << 28; // 28;
struct rlimit rl;
int result;
#undef DEBT
void DEBT(const char *buf) {
printf("%4d %s\n", SDL_GetTicks(), buf);
result = getrlimit(RLIMIT_STACK, &rl);
if(result == 0) {
if(rl.rlim_cur < kStackSize) {
// rl.rlim_cur = 1 << 19; // kStackSize;
result = setrlimit(RLIMIT_STACK, &rl);
if (result != 0) {
fprintf(stderr, "setrlimit returned result = %d\n", result);
}
}
}
}
#endif
string s0;
void addMessage(string s, char spamtype = 0);
@ -86,7 +96,14 @@ string commandline;
#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"
@ -98,6 +115,8 @@ string commandline;
#include "mapeditor.cpp"
#endif
#include "netgen.cpp"
#include "graph.cpp"
#include "achievement.cpp"
@ -106,15 +125,17 @@ string commandline;
int main(int argc, char **argv) {
printf("HyperRogue by Zeno Rogue <zeno@attnam.com>, version "VER"\n");
#ifdef LINUX
moreStack();
#endif
printf("HyperRogue by Zeno Rogue <zeno@attnam.com>, version " VER "\n");
#ifndef NOLICENSE
printf("released under GNU General Public License version 2 and thus\n");
printf("comes with absolutely no warranty; see COPYING for details\n");
#endif
achievement_init();
// printf("cell size = %d\n", int(sizeof(cell)));
srand(time(NULL));
shrand(time(NULL));
@ -177,11 +198,43 @@ int main(int argc, char **argv) {
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], "-P") == 0) { commandline += "P"; }
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;
#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);
@ -203,13 +256,25 @@ int main(int argc, char **argv) {
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 on or off\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(" -P - switch Shmup number of players\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 {
@ -218,30 +283,22 @@ int main(int argc, char **argv) {
}
}
/* transmatrix T;
for(int a=0; a<3; a++) for(int b=0; b<3; b++)
T[a][b] = (10 + a*a + b*4 + (b==1?a:0)) / 20.;
display(T);
transmatrix T2 = inverse(T);
display(T2);
display(T*T2); */
achievement_init();
eLand f = firstland;
// initlanguage();
initgraph();
loadsave();
precalc();
resetGL();
initcells();
/* for(int uu=9; uu >= 0; uu--) {
printf("uu=%d\n", uu);
initgame(uu);
restartGame();
} */
#ifdef BUILDZEBRA
firstland = laCanvas;
shmup::on = false;
#endif
shmup::safety = safety;
initgame();
#ifdef BUILDZEBRA
zebraPattern();
@ -265,13 +322,17 @@ int main(int argc, char **argv) {
if(loadlevel) mapstream::loadMap(loadlevel);
#ifdef LOCAL
// river();
autoplay();
#endif
mainloop();
achievement_final(!items[itOrbSafety]);
saveStats();
int msec = SDL_GetTicks() - t1;
printf("frame : %f ms (%f fps)\n", 1.*msec/frames, 1000.*frames/msec);
DEBB(DF_INIT, (debugfile, "frame : %f ms (%f fps)\n", 1.*msec/frames, 1000.*frames/msec));
offscreen.clear();
clearMemory();
cleargraph();

440
hyper.h
View File

@ -11,6 +11,10 @@
#undef GFX
#endif
#ifdef MAC
#define NOPNG
#endif
// scale the Euclidean
#define EUCSCALE 2.3
@ -23,6 +27,15 @@
// achievements
#define LB_YENDOR_CHALLENGE 40
#define LB_PURE_TACTICS 41
#define NUMLEADER 57
#define LB_PURE_TACTICS_SHMUP 49
#define LB_PURE_TACTICS_COOP 50
extern int currentscore[NUMLEADER];
extern int syncstate;
// initialize the achievement system.
void achievement_init();
@ -30,8 +43,9 @@ void achievement_init();
void achievement_close();
// gain the achievement with the given name.
// Only awarded if euclid equals euclideanAchievement.
void achievement_gain(const char*, bool euclideanAchievement = false, bool shmupAchievement = false);
// flags: 'e' - for Euclidean, 's' - for Shmup, '7' - for heptagonal
// Only awarded if special modes are matched exactly.
void achievement_gain(const char*, char flags = 0);
// gain the achievement for collecting a number of 'it'.
void achievement_collection(eItem it, int prevgold, int newgold);
@ -54,11 +68,33 @@ void achievement_final(bool really);
// display the last achievement gained.
void achievement_display();
// call the achievement callbacks
void achievement_pump();
// achievements received this game
vector<string> achievementsReceived;
// game forward declarations
bool mirrorkill(cell *c);
bool isNeighbor(cell *c1, cell *c2);
void checkTide(cell *c);
namespace anticheat { extern bool tampered; }
int numplayers();
void removeIvy(cell *c);
bool cellEdgeUnstable(cell *c);
int coastvalEdge(cell *c);
typedef int cellfunction(cell*);
int towerval(cell *c, cellfunction* cf = &coastvalEdge);
#define HRANDMAX 0x7FFFFFFF
int hrandpos(); // 0 to HRANDMAX
void restartGame(char switchWhat = 0);
int landMultiplier(eLand l);
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 isCrossroads(eLand l);
enum orbAction { roMouse, roKeyboard, roCheck, roMouseForce };
void moveItem (cell *from, cell *to, bool activateYendor);
@ -68,24 +104,36 @@ void killMonster(cell *c);
void toggleGates(cell *ct, eWall type, int rad);
bool destroyHalfvine(cell *c, eWall newwall = waNone, int tval = 6);
void buildCrossroads2(cell *c);
void createBugArmy(cell *c);
bool isHaunted(eLand l);
heptagon *createAlternateMap(cell *c, int rad, hstate firststate, int special=0);
void generateAlts(heptagon *h);
void whirlGenerate(cell *wto);
void setdist(cell *c, int d, cell *from);
void checkOnYendorPath();
void killThePlayerAt(eMonster m, cell *c);
void killThePlayerAt(eMonster m, cell *c, int 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 = moNone);
void stabbingAttack(cell *mf, cell *mt, eMonster who);
bool earthMove(cell *from, int dir);
void messageKill(eMonster killer, eMonster victim);
void moveMonster(cell *ct, cell *cf);
int palaceHP();
void placeLocalOrbs(cell *c);
int elementalKills();
bool elementalUnlocked();
bool isMultitile(eMonster m);
void checkFreedom(cell *cf);
int rosedist(cell *c);
bool canPushStatueOn(cell *c);
void createMirrors(cell *c, int dir, eMonster type);
void createMirages(cell *c, int dir, eMonster type);
namespace hive { void createBugArmy(cell *c); }
namespace whirlpool { void generate(cell *wto); }
namespace whirlwind { void generate(cell *wto); }
namespace mirror {
void createMirrors(cell *c, int dir, eMonster type);
void createMirages(cell *c, int dir, eMonster type);
}
int neighborId(cell *c1, cell *c2);
@ -96,6 +144,15 @@ void activateActiv(cell *c, bool msg);
// shmup
struct charstyle {
int charid, skincolor, haircolor, dresscolor, swordcolor, dresscolor2;
};
string csname(charstyle& cs);
void initcs(charstyle& cs);
void savecs(FILE *f, charstyle& cs);
void loadcs(FILE *f, charstyle& cs);
namespace shmup {
extern bool on;
extern bool safety;
@ -110,6 +167,8 @@ namespace shmup {
void killThePlayer(eMonster m);
void killThePlayer(eMonster m, int i);
void visibleFor(int t);
bool verifyTeleport();
bool dragonbreath(cell *dragon);
void shmupDrownPlayers(cell *c);
@ -130,10 +189,14 @@ namespace shmup {
char axeaction[8][MAXAXE];
char hataction[8][MAXHAT][4];
};
charstyle scs[4];
}
// graph
extern int msgscroll;
void showMissionScreen();
void restartGraph();
@ -142,6 +205,7 @@ void resetmusic();
void cleargraphmemory();
void drawFlash(cell* c);
void drawBigFlash(cell* c);
void drawLightning();
void drawSafety();
@ -150,6 +214,7 @@ 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);
@ -164,6 +229,21 @@ 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 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;
#ifndef MOBILE
int& qpixel(SDL_Surface *surf, int x, int y);
#endif
void drawthemap();
void drawfullmap();
bool displaystr(int x, int y, int shift, int size, const char *str, int color, int align);
extern int darken;
void setvideomode();
void calcparam();
string ifMousing(string key, string s);
@ -185,7 +265,8 @@ extern int lalpha;
struct videopar {
ld scale, eye, alpha, aspeed;
bool full;
bool goteyes;
bool goteyes; // for rendering
bool goteyes2; // for choosing colors
bool quick;
bool darkhepta;
bool shifttarget;
@ -213,14 +294,16 @@ struct videopar {
int joyvalue, joyvalue2, joypanthreshold;
float joypanspeed;
bool female;
charstyle cs;
bool samegender; // same gender for the Princess?
int language;
int skincolor, haircolor, dresscolor, swordcolor;
int killreduction;
int killreduction, itemreduction, portreduction;
shmup::config scfg;
bool steamscore;
};
extern videopar vid;
@ -234,7 +317,12 @@ enum emtype {emNormal, emHelp,
emShmupConfig,
emMapEditor,
emPatternPicker,
emOverview
emOverview,
emNetgen,
emYendor, emTactic, emRugConfig,
emConformal,
emProgress,
emCheatMenu
};
extern emtype cmode, lastmode;
@ -243,18 +331,342 @@ extern transmatrix View; // current rotation, relative to viewctr
extern transmatrix cwtV; // player-relative view
extern cell *mouseover, *mouseover2;
extern string mouseovers;
extern struct SDL_Surface *s;
namespace mapeditor {
extern bool drawplayer;
extern char whichPattern;
extern char whichPattern, whichShape;
int generateCanvas(cell *c);
void clearModelCells();
void applyModelcell(cell *c);
int realpattern(cell *c);
int patterndir(cell *c, char w = whichPattern);
extern cell *drawcell;
}
namespace rug {
extern bool rugged;
void init();
void close();
void actDraw();
void buildVertexInfo(cell *c, transmatrix V);
}
#define HASLINEVIEW
namespace conformal {
extern bool on;
extern vector<pair<cell*, eMonster> > killhistory;
extern vector<pair<cell*, eItem> > findhistory;
extern vector<cell*> movehistory;
extern bool includeHistory;
void create();
void clear();
void handleKey();
void show();
void apply();
void renderAutoband();
}
namespace polygonal {
extern int SI;
extern double STAR;
void solve();
typedef long double ld;
pair<ld, ld> compute(ld x, ld y);
}
void selectEyeGL(int ed);
void selectEyeMask(int ed);
extern int ticks;
void setGLProjection();
#ifdef LOCAL
extern void process_local_stats();
bool localDescribe();
void localDrawMap();
extern bool localKill(shmup::monster *m);
#endif
// passable flags
#define P_MONSTER (1<<0) // can move through monsters
#define P_MIRROR (1<<1) // can move through mirrors
#define P_REVDIR (1<<2) // reverse direction movement
#define P_WIND (1<<3) // can move against the wind
#define P_GRAVITY (1<<4) // can move against the gravity
#define P_ISPLAYER (1<<5) // player-only moves (like the Round Table jump)
#define P_ONPLAYER (1<<6) // always can step on the player
#define P_FLYING (1<<7) // is flying
#define P_BULLET (1<<8) // bullet can fly through more things
#define P_JUMP1 (1<<10) // first part of a jump
#define P_JUMP2 (1<<11) // second part of a jump
#define P_TELE (1<<12) // teleport onto
#define P_BLOW (1<<13) // Orb of Air -- blow, or push
#define P_AETHER (1<<14) // aethereal
#define P_FISH (1<<15) // swimming
#define P_WINTER (1<<16) // fire resistant
#define P_USEBOAT (1<<17) // can use boat
#define P_NOAETHER (1<<18) // disable AETHER
#define P_FRIENDSWAP (1<<19) // can move on friends (to swap with tem)
#define P_ISFRIEND (1<<20) // is a friend (can use Empathy + Winter/Aether/Fish combo)
#define P_LEADER (1<<21) // can push statues and use boats
#define P_MARKWATER (1<<22) // mark Orb of Water as used
#define P_EARTHELEM (1<<23) // Earth Elemental
#define P_WATERELEM (1<<24) // Water Elemental
#define P_IGNORE37 (1<<25) // ignore the triheptagonal board
#define P_CHAIN (1<<26) // for chaining moves with boats
#define P_DEADLY (1<<27) // suicide moves allowed
#define P_ROSE (1<<28) // rose smell
#define P_CLIMBUP (1<<29) // allow climbing up
#define P_CLIMBDOWN (1<<30) // allow climbing down
bool passable(cell *w, cell *from, int flags);
bool isElemental(eLand l);
int coastval(cell *c, eLand base);
int getHauntedDepth(cell *c);
eLand randomElementalLand();
extern eLand euland[65536];
bool notDippingForExtra(eItem i, eItem x);
void placePrizeOrb(cell *c);
int hivehard();
eMonster randomHyperbug();
void wandering();
bool isSealand(eLand l);
int newRoundTableRadius();
bool grailWasFound(cell *c);
extern bool buggyGeneration;
int buildIvy(cell *c, int children, int minleaf);
int celldistAltRelative(cell *c);
int roundTableRadius(cell *c);
cell *chosenDown(cell *c, int which, int bonus, cellfunction* cf = &coastvalEdge);
eLand pickLandRPM(eLand old);
bool bearsCamelot(eLand l);
extern bool safety;
#define SAGEMELT .1
#define TEMPLE_EACH 6
#define PT(x, y) (tactic::on ? (y) : (x))
#define ROCKSNAKELENGTH 50
#define PUREHARDCORE_LEVEL 10
#define PRIZEMUL 7
#define INF 9999
#define INFD 20
#define BARLEV ((ISANDROID||ISIOS||purehepta)?9:10)
#define BUGLEV 15
// #define BARLEV 9
bool isKillable(cell *c);
bool isKillableSomehow(cell *c);
extern vector<cell*> mirrors, mirrors2;
bool isAlchAny(eWall w);
bool isAlchAny(cell *c);
#define YDIST 101
#define MODECODES 38
extern cellwalker cwt; // player character position
extern int sval;
extern int items[ittypes], hiitems[MODECODES][ittypes], kills[motypes], explore[10], exploreland[10][landtypes], landcount[landtypes];
extern eLand firstland, euclidland;
bool pseudohept(cell *c);
bool pureHardcore();
extern int cheater;
int airdist(cell *c);
bool eq(short a, short b);
extern vector<cell*> dcal; // queue for cpdist
bool isPlayerOn(cell *c);
bool isFriendly(eMonster m);
bool isFriendly(cell *c);
bool isChild(cell *w, cell *killed); // is w killed if killed is killed?
int gold();
int tkills();
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 isMimic(eMonster m);
bool isMimic(cell *c);
void killWithMessage(cell *c, bool orStun = false, eMonster killer = moNone);
bool isWorm(eMonster m);
bool isWorm(cell *c);
void empathyMove(cell *c, cell *cto, int dir);
bool isIvy(eMonster m);
bool isIvy(cell *c);
#define GUNRANGE 3
// 0 = basic treasure, 1 = something else, 2 = power orb
#define IC_TREASURE 0
#define IC_OTHER 1
#define IC_ORB 2
bool playerInPower();
void activateFlash();
void activateLightning();
bool markOrb(eItem it);
bool markOrb2(eItem it);
void drainOrb(eItem it, int target = 0);
void useupOrb(eItem it, int qty);
void initgame();
bool haveRangedTarget();
eItem targetRangedOrb(cell *c, orbAction a);
void reduceOrbPowers();
int realstuntime(cell *c);
extern bool invismove, invisfish;
bool attackingForbidden(cell *c, cell *c2);
void killOrStunMonster(cell *c2);
extern vector<cell*> offscreen; // offscreen cells to take care off
void useup(cell *c); // useup thumpers/bonfires
cell *playerpos(int i);
bool makeflame(cell *c, int timeout, bool checkonly);
void bfs();
bool isPlayerInBoatOn(cell *c);
extern bool showoff;
extern int lastexplore;
extern int truelotus;
extern eLand lastland;
extern time_t timerstart;
extern bool timerstopped;
bool againstRose(cell *cfrom, cell *cto);
bool withRose(cell *cfrom, cell *cto);
// loops
#define fakecellloop(ct) for(cell *ct = (cell*)1; ct; ct=NULL)
#define forCellIdAll(ct, i, cf) fakecellloop(ct) for(int i=0; i<(cf)->type && (ct=(cf)->mov[i],true); i++)
#define forCellIdCM(ct, i, cf) fakecellloop(ct) for(int i=0; i<(cf)->type && (ct=createMov((cf),i),true); i++)
#define forCellIdEx(ct, i, cf) forCellIdAll(ct,i,cf) if(ct)
#define forCellEx(ct, cf) forCellIdEx(ct,forCellEx ## __LINE__,cf)
#define forCellCM(ct, cf) forCellIdCM(ct,forCellCM ## __LINE__,cf)
#define forCellAll(ct, cf) forCellIdCM(ct,forCellAll ## __LINE__,cf)
// canAttack/moveval flags
#define AF_TOUGH (1<<0) // tough attacks: Hyperbugs
#define AF_MAGIC (1<<1) // magical attacks: Flash
#define AF_STAB (1<<2) // stabbing attacks (usually ignored except Hedgehogs)
#define AF_LANCE (1<<3) // lance attacks (used by Lancers)
#define AF_ONLY_ENEMY (1<<4) // only say YES if it is an enemy
#define AF_ONLY_FRIEND (1<<5) // only say YES if it is a friend
#define AF_ONLY_FBUG (1<<6) // only say YES if it is a bug_or friend
#define AF_BACK (1<<7) // backward attacks (ignored except Viziers and Flailers)
#define AF_APPROACH (1<<8) // approach attacks (ignored except Lancers)
#define AF_IGNORE_UNARMED (1<<9) // ignore the UNARMED flag
#define AF_NOSHIELD (1<<10) // ignore the shielded status
#define AF_GETPLAYER (1<<11) // check for player (replace m2 with moPlayer for player position)
#define AF_GUN (1<<12) // revolver attack
#define AF_FAST (1<<13) // fast attack
#define AF_EAT (1<<17) // eating attacks from Worm-likes
#define MF_NOATTACKS (1<<14) // don't do any attacks
#define MF_PATHDIST (1<<15) // consider pathdist for moveval
#define MF_ONLYEAGLE (1<<16) // do this only for Eagles
#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);
extern bool chaosmode;
extern bool chaosUnlocked;
extern bool chaosAchieved;
bool isTechnicalLand(eLand l);
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
// #define NOPNG
#ifdef NOPNG
#define IMAGEEXT ".bmp"
#define IMAGESAVE SDL_SaveBMP
#else
#include "savepng.h"
#define IMAGEEXT ".png"
void IMAGESAVE(SDL_Surface *s, const char *fname);
#endif
void drawscreen();
void buildAirmap();
// currently works for worms only
bool sameMonster(cell *c1, cell *c2);
cell *wormhead(cell *c);
eMonster getMount();
bool isDragon(eMonster m);
// for some reason I need this to compile under OSX
#ifdef MAC
extern "C" { void *_Unwind_Resume = 0; }
#endif
extern bool autocheat;
extern bool inHighQual;
void mountmove(cell *c, int spin, bool fp);
template<class T> struct dynamicval {
T& where;
T backup;
dynamicval(T& wh, T val) : where(wh) { backup = wh; wh = val; }
~dynamicval() { where = backup; }
};
namespace stalemate {
eMonster who;
cell *moveto;
cell *killed;
cell *pushto;
cell *comefrom;
bool nextturn;
bool isKilled(cell *c);
};
extern int turncount;
bool reduceOrbPower(eItem it, int cap);
bool checkOrb(eMonster m1, eItem orb);

View File

@ -1,8 +1,8 @@
id ICON "hr-icon.ico"
1 VERSIONINFO
FILEVERSION 8,1,7,0
PRODUCTVERSION 8,1,7,0
FILEVERSION 8,3,0,10
PRODUCTVERSION 8,3,0,10
BEGIN
BLOCK "StringFileInfo"
BEGIN
@ -10,12 +10,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Zeno Rogue"
VALUE "FileDescription", "A roguelike in non-euclidean space"
VALUE "FileVersion", "81g"
VALUE "FileVersion", "83"
VALUE "InternalName", "hyper"
VALUE "LegalCopyright", "Zeno Rogue"
VALUE "OriginalFilename", "hyper.exe"
VALUE "ProductName", "HyperRogue"
VALUE "ProductVersion", "8.1g"
VALUE "ProductVersion", "8.3j"
END
END

View File

@ -1,16 +1,42 @@
// 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;
// for the pure heptagonal grid
bool purehepta = false;
// hyperbolic points and matrices
// basic functions and types
//===========================
#ifdef SINHCOSH
ld sinh(ld alpha) { return (exp(alpha) - exp(-alpha)) / 2; }
ld cosh(ld alpha) { return (exp(alpha) + exp(-alpha)) / 2; }
#endif
ld squar(ld x) { return x*x; }
@ -36,7 +62,7 @@ hyperpoint hpxyz(ld x, ld y, ld z) {
hyperpoint hpxy(ld x, ld y) {
// EUCLIDEAN
return hpxyz(x,y, euclid ? 1 : 1+x*x+y*y);
return hpxyz(x,y, euclid ? 1 : sqrt(1+x*x+y*y));
}
// center of the pseudosphere
@ -84,6 +110,44 @@ hyperpoint mid(const hyperpoint& H1, const hyperpoint& H2) {
return H3;
}
hyperpoint mid3(const hyperpoint& H1, const hyperpoint& H2, const hyperpoint& H3) {
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];
ld Z = 2;
if(!euclid) {
Z = intval(Hx, Hypc);
Z = sqrt(-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;
}
// matrices
//==========
@ -268,3 +332,32 @@ transmatrix inverse(transmatrix T) {
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]));
}
namespace hyperpoint_vec {
hyperpoint operator * (double d, hyperpoint h) {
return hpxyz(h[0]*d, h[1]*d, h[2]*d);
}
hyperpoint operator * (hyperpoint h, double d) {
return hpxyz(h[0]*d, h[1]*d, h[2]*d);
}
hyperpoint operator / (hyperpoint h, double d) {
return hpxyz(h[0]/d, h[1]/d, h[2]/d);
}
hyperpoint operator + (hyperpoint h, hyperpoint h2) {
return hpxyz(h[0]+h2[0], h[1]+h2[1], h[2]+h2[2]);
}
hyperpoint operator - (hyperpoint h, hyperpoint h2) {
return hpxyz(h[0]-h2[0], h[1]-h2[1], h[2]-h2[2]);
}
}

View File

@ -1,77 +1,101 @@
# Crossroads [02]
[02] music/hr3-crossroads.ogg
[02] */hr3-crossroads.ogg
# Desert [03]
[03] music/hr3-desert.ogg
[03] */hr3-desert.ogg
# Icy Lands [04]
[04] music/hr3-icyland.ogg
[04] */hr3-icyland.ogg
# Living Caves [05]
[05] music/hr3-caves.ogg
[05] */hr3-caves.ogg
# Jungle [06]
[06] music/hr3-jungle.ogg
[06] */hr3-jungle.ogg
# Alchemist's Lab [07]
[07] music/hr3-laboratory.ogg
[07] */hr3-laboratory.ogg
# Mirror [08]
[08] music/hr3-mirror.ogg
[08] */hr3-mirror.ogg
# Graveyard [09]
[09] music/hr3-graveyard.ogg
[09] */hr3-graveyard.ogg
# R'Lyeh [10]
[10] music/hr3-rlyeh.ogg
[10] */hr3-rlyeh.ogg
# Hell [11]
[11] music/hr3-hell.ogg
[11] */hr3-hell.ogg
# Cocytus [12]
[12] music/hr3-icyland.ogg
[12] */hr3-icyland.ogg
# Motion [13]
[13] music/hr3-motion.ogg
[13] */hr3-motion.ogg
# Dry Forest [14]
[14] music/hr3-jungle.ogg
[14] */hr3-jungle.ogg
# Emerald Mines [15]
[15] music/hr3-caves.ogg
[15] */hr3-caves.ogg
# Vineyard [16]
[16] music/hr3-laboratory.ogg
[16] */hr3-laboratory.ogg
# Dead Caves [17]
[17] music/hr3-graveyard.ogg
[17] */hr3-graveyard.ogg
# Hive [18]
[18] music/hr3-motion.ogg
[18] */hr3-motion.ogg
# Land of Power [19]
[19] music/hr3-mirror.ogg
[19] */hr3-mirror.ogg
# Camelot [20]
[20] music/hr3-hell.ogg
[20] */hr3-hell.ogg
# Temple [21]
[21] music/hr3-rlyeh.ogg
[21] */hr3-rlyeh.ogg
# CR2 [22]
[22] music/hr3-crossroads.ogg
[22] */hr3-crossroads.ogg
# Caribbean [23]
[23] music/hr3-crossroads.ogg
[23] */hr3-crossroads.ogg
# Red Rock Valley [24]
[24] music/hr3-desert.ogg
[24] */hr3-desert.ogg
# Minefield [25]
[25] music/hr3-hell.ogg
[25] */hr3-hell.ogg
# Ocean [26]
[26] music/hr3-caves.ogg
[26] */hr3-caves.ogg
# Whirlpool [27]
[27] music/hr3-rlyeh.ogg
[27] */hr3-rlyeh.ogg
# Palace [28]
[28] music/hr3-crossroads.ogg
[28] */hr3-crossroads.ogg
# Living Fjord [39]
[29] music/hr3-caves.ogg
[29] */hr3-caves.ogg
# Ivory Tower [30]
[30] music/hr3-laboratory.ogg
[30] */hr3-laboratory.ogg
# Zebra [31]
[31] music/hr3-motion.ogg
[31] */hr3-motion.ogg
# Elemental Fire [32]
[32] music/hr3-hell.ogg
[32] */hr3-hell.ogg
# Elemental Air [33]
[33] music/hr3-motion.ogg
[33] */hr3-motion.ogg
# Elemental Earth [34]
[34] music/hr3-caves.ogg
[34] */hr3-caves.ogg
# Elemental Water [35]
[35] music/hr3-crossroads.ogg
[35] */hr3-crossroads.ogg
# Crossroads III [36]
[36] music/hr3-crossroads.ogg
[36] */hr3-crossroads.ogg
# Seawall [37] (useless)
# Elemental Wall [38] (useless)
# Canvas [39]
[39] music/hr3-laboratory.ogg
[39] */hr3-laboratory.ogg
# Wild West [41]
[41] */hr3-caves.ogg
# Storms [42]
[42] */hr3-laboratory.ogg
# Overgrown [43]
[43] */hr3-jungle.ogg
# Clearing [44]
[44] */hr3-jungle.ogg
# Haunted [45]
[45] */hr3-graveyard.ogg
# Windy Plains [48]
[48] */hr3-laboratory.ogg
# Rose Garden [49]
[49] */hr3-hell.ogg
# Warped Coast/Sea [50, 51 -> 50]
[50] */hr3-caves.ogg
# Crossroads IV [52]
[52] */hr3-crossroads.ogg
# Yendorian [53]
[53] */hr3-laboratory.ogg
# Galapagos [54]
[54] */hr3-crossroads.ogg
# Dragon Chasms [55]
[55] */hr3-caves.ogg
# None [00] (used when the window is out of focus)

4427
landgen.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,7 @@
// Hyperbolic Rogue language file generator
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#define GEN_M 0
#define GEN_F 1
#define GEN_N 2
@ -84,7 +88,7 @@ typedef unsigned hashcode;
hashcode hashval;
hashcode hash(const string& s) {
hashcode langhash(const string& s) {
hashcode r = 0;
for(int i=0; i<size(s); i++) r = hashval * r + s[i];
return r;
@ -93,7 +97,7 @@ hashcode hash(const string& s) {
map<hashcode, string> buildHashTable(set<string>& s) {
map<hashcode, string> res;
for(set<string>::iterator it = s.begin(); it != s.end(); it++)
res[hash(*it)] = *it;
res[langhash(*it)] = *it;
return res;
}
@ -122,6 +126,13 @@ const char* allstr[] = {
};
#endif
void setstats(set<string>& s, const char* bn) {
int tlen=0, tc = 0;
for(set<string>::iterator it = s.begin(); it != s.end(); it++)
tc++, tlen += it->size();
printf("// %-10s %5d %5d\n", bn, tc, tlen);
}
int main() {
nothe.insert("R'Lyeh");
@ -177,7 +188,7 @@ int main() {
string mis = "";
for(int i=1; i<NUMLAN; i++) if(d[i].count(*x) == 0)
mis += d[i]["EN"];
if(mis != "" && mis != "DE")
if(mis != "" && mis != "TR" && mis != "TRDE" && mis != "DE")
printf("#warning Missing [%s]: %s\n", mis.c_str(), escape(*x, "?"));
}
@ -191,7 +202,7 @@ int main() {
string mis = "";
for(int i=1; i<NUMLAN; i++) if(nouns[i].count(*x) == 0)
mis += d[i]["EN"];
if(mis != "" && mis != "DE")
if(mis != "" && mis != "TR" && mis != "TRDE" && mis != "DE")
printf("#warning Missing [%s]: %s\n", mis.c_str(), escape(*x, "?"));
}

File diff suppressed because it is too large Load Diff

10242
language-data.cpp Normal file

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

@ -1,8 +1,11 @@
// HyperRogue Turkish translation, by zulmetefza & Seyacim
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
// This translation file is encoded with UTF-8
// Nouns.
#define Orb(X,Y) N("Orb of "X, GEN_F, Y" Küresi", Y" Küreleri", Y" Küresini", Y" Küresiyle")
#define Orb(X,Y) N("Orb of " X, GEN_F, Y " Küresi", Y " Küreleri", Y " Küresini", Y " Küresiyle")
// For each noun, provide the type of the noun, and all the grammatical forms
@ -682,7 +685,7 @@ S("A beautiful gem from the Jungle.", "Vahşi Ormandan elde edilmiş bir mücevh
S(
"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.",
"Şahane bir içki, görünen o ki kırmızı ve mavi balçığın karıştırılmasıyla elde edilmiş. İçince daha sağlıklı hissediyorsun ama bir canavarın tek vuruşunun seni öldüreceğine de eminsin.")
S("A piece of a magic mirror, or a mirage cloud, that can be used for magical purposes. Only mirrors and clouds "
@ -1191,7 +1194,7 @@ S(
"The Knights of the Round Table are the greatest warriors of these lands. "
"They are not very inventive with names though, as they call each of their "
"castles Camelot. "
"You are probably worth of joining them, but they will surely give you "
"You are probably worthy of joining them, but they will surely give you "
"some quest to prove yourself...\n\n"
"Each castle contains a single treasure, the Holy Grail, in the center. "
"The radius of the Round Table is usually 28, but after you find a Holy Grail "
@ -2639,8 +2642,313 @@ S("A coastal area, from where you can get both to the inland worlds and to the O
"Ancak Yaşayan Fiyortların dışındaki hücreler, Unsurlar ve Ölü Trollerin etkisi çok güçlüdür."
)
// S("TRANSLATIONWARNING", "Uyarı: Türkçe çeviri henüz Sürüm 8.0' daki yeni özellikleri kapsamıyor.")
// S("TRANSLATIONWARNING", "")
// Also, as the game is constantly updated, the Steam version includes the newest features, such as new lands with new mechanics, or new game modes -- see [ http://www.roguetemple.com/z/hyper/gallery.php]Gallery[/url] for the current differences.
//CORRECTION:
N("great wall", GEN_F, "Yüksek Duvar", "Yüksek Duvarlar", "Yüksek Duvarı", "Yüksek Duvarda")
// VERSION 8.0
//=============
S("The Air Elemental blows you away!", "Hava Unsuru seni uzağa uçuruyor!")
// appended to cave troll description
S(" Additionally, all items around the killed Troll will be destroyed.",
" Ayrıca, Trolün etrafındaki bütün eşyalar yok olur.")
// 'dead cave troll' and 'dead troll' are separated now
N("dead rock troll", GEN_M, "ölü mağara trolü" ,"ölü mağara trolleri", "ölü mağara trolünü", "ölü mağara trolüyle")
S("There are several species of trolls living in the hyperbolic world. "
"Some of them leave this wall behind them when they die.",
"Hiperbolik dünyada pek çok tür trol yaşar."
"Bazıları öldüğünde bu duvarı geride bırakır.")
// paper model creator
//---------------------
S("paper model creator", "kâğıt modeli üreticisi")
S("synchronize net and map", "haritayı ve ağı eşle")
S("display the scope", "odağı göster")
S("create the model", "modeli üret")
S("back to HyperRogue", "HyperRogue'a geri dön")
S("design the net", "ı tasarla")
S("The paper model created as papermodel-*.bmp", "Kâğıt model papermodel-*.bmp şeklinde üretildi")
S("Failed to load the file 'papermodeldata.txt'", "papermodeldata.txt dosyası yüklenemedi")
S("Could not save the paper model data", "Kâğıt modeli verisi kaydedilemedi")
// pure tactics mode
//-------------------
S("pure tactics mode", "saf taktik modu")
S("Not available in the pure tactics mode!", "Taktik modunda kullanılamaz!")
S("\"The Knights of the Horocyclic Table salute you!\"", "\"Horosikıl Masa Şövalyeleri seni selamlıyor!\"")
S("press 0 to leave this mode", "Bu moddan çıkmak için 0'a bas")
S("Collect %1x %2 to unlock", " %1 tane %2 toplayarak aktifleştir")
S(
"In the pure tactics mode, you concentrate on a specific land. "
"Your goal to obtain as high score as possible, without using "
"features of the other lands. You can then compare your score "
"with your friends!\n\n"
"You need to be somewhat proficient in the normal game to "
"unlock the given land in this challenge "
"(collect 20 treasure in the given land, or 2 in case of Camelot).\n\n"
"Since getting high scores in some lands is somewhat luck dependent, "
"you play each land N times, and your score is based on N consecutive "
"plays. The value of N depends on how 'fast' the land is to play, and "
"how random it is.\n\n"
"In the Caribbean, you can access Orbs of Thorns, Aether, and "
"Space if you have ever collected 25 treasure in their native lands.\n\n"
"The rate of treasure spawn is static in this mode. It is not "
"increased by killing monsters.\n\n"
"Good luck, and have fun!",
"Saf taktik modunda sadece tek bir diyara odaklanırsın. Amacın diğer diyarların özelliklerini hiç kullanmadan mümkün olduğunca yüksek puan yapmak. Sonra skorunu arkadaşlarınınkiyle karşılaştırabilirsin!\n\n "
"Normal oyunda maharetini ispatlayarak belli bir diyarı bu modda açabilirsin. "
"(Belli bir diyarda 20 hazine toplamak, Kamelot için 2 hazine toplamak gibi).\n\n"
"Yüksek skor almak şöyle böyle şansa bağlı olduğundan, her diyarı N defa oynarsın ve skorun bu ardarda N oyuna göre belirlenir. N'in değeri diyarın ne kadar hızlı oynandığına ve ne derece rassal olduğuna göre değişir.\n\n"
"Karayiplerde, Diken Küresine, Ether'e ve Uzay'a eğer daha önce ilgili diyarlarda 25'er hazine topladıysan erişebilirsin.\n\n"
"Hazine üretimi bu modda statiktir ve canavar öldürmekle artmaz.\n\n"
"İyi şanslar ve iyi eğlenceler!")
// Yendor Challenge
//------------------
S("Yendor Challenge", "Yendor Ek Görevi")
//Görev?
S("Collect 10 treasures in various lands to unlock the challenges there",
"Çeşitli diyarlarda 10 hazine toplayarak o diyarın özel görevini aktifleştirebilirsin")
S("easy", "kolay")
S("challenge", " ek görev")
S("Challenges do not get harder", "Ek görevler zorlaşmaz.")
S("Each challenge gets harder after each victory", "Her zaferinden sonra ek görevler zorlaşır.")
S(" (won!)", " (kazandın!)")
S(" (won at level %1!)", " (%1. seviyede kazandın!)")
S("(locked)", "(aktif değil)")
S(
"There are many possible solutions to the Yendor Quest. In the Yendor "
"Challenge, you will try many of them!\n\n"
"Each challenge takes part in a specific land, and you have to use what "
"you have available.\n\n"
"You need to obtain an Orb of Yendor in the normal game to activate "
"this challenge, and (ever) collect 10 treasures in one or two lands "
"to activate a specific level.\n\n"
"After you complete each challenge, you can try it again, on a harder "
"difficulty level.\n\n"
"All the solutions showcased in the Yendor Challenge work in the normal "
"play too. However, passages to other lands, and (sometimes) some land features "
"are disabled in the Yendor "
"Challenge, so that you have to use the expected method. Also, "
"the generation rules are changed slightly for the Palace "
"and Minefield while you are looking for the Orb of Yendor, "
"to make the challenge more balanced "
"(but these changes are also active during the normal Yendor Quest).\n\n"
"You get 1000 points for each challenge won, and 1 extra point for "
"each extra difficulty level.",
"Yendor Görevinin bir sürü çözümü var. Yendor Ek Görevi'nde bunların pek çoğunu deneyeceksin!\n\n"
"Her görev belirli bir diyarda geçiyor ve elindekileri bütün verimliliğiyle kullanman gerekecek.\n\n"
"Bu görevi aktifleştirmek için normal oyunda bir kez Yendor'un Küresi'ni ele geçirmen gerekiyor. Ayrıca her hangi bir seviyeyi aktifleştirmek için ilgili diyar(lar)da 10 hazine toplamış olman gerekiyor.\n\n"
"Ek görevi tamamladıktan sonra aynı görevi bir üst zorluk seviyesinde yeniden deneyebilirsin.\n\n"
"Yendor Ek Görevi'ndeki bütün çözümler normal oyunda da işe yarar. Ancak, Yendor Ek görevinde, diğer diyarlara geçiş ve bazen diyarın bazı özellikleri devredışıdır, bu yüzden beklenen çözümü bulman beklenir. "
"Ayrıca, Yendor'un Küresi'ni ararken görevin daha dengeli olması için Saray ve Mayın Tarlasındaki harita üretimi biraz değiştirilmiştir. (bu değişiklikler normal Yendor Görevi'nde de mevcuttur).\n\n"
"Her kazanılan ek görev için 1000 puan ve her fazladan zorluk seviyesi için 1 fazladan puan kazanırsın.\n\n"
)
S("Unlock this challenge by getting the Orb of Yendor!",
"Yendor'un Küresi'ni alarak bu ek görevi aktif et!")
S("Collect 10 treasures in various lands to unlock the challenges there",
"Diyarlardaki ek görevleri aktif etmek için oralarda 10 hazine topla.")
// Wild West
//-----------
N("Wild West", GEN_O, "Vahşi Batı", "Vahşi Batılar", "Vahşi Batıyı", "Vahşi Batıda")
N("Outlaw", GEN_M, "Haydut" ,"Haydutlar", "Haydutu", "Haydutla")
N("Bounty", GEN_F, "Ödül", "Ödüller", "Ödülü", "Ödülle")
N("saloon wall", GEN_F, "bar duvarı", "bar duvarları", "bar duvarını", "bar duvarıyla")
N("Revolver", GEN_O, "Altıpatlar", "Altıpatlarlar", "Altıpatları", "Altıpatlarla")
S("%The1 shoots you!", "%1 sana ateş etti!")
S("You shoot %the1!", "%1 düşmanına ateş ettin!")
S(
"Take a revolver, kill outlaws, collect bounties.\n\n"
"Note: since this land is anachronistic, it is not available in normal game. "
"It is only available in special modes.",
"Altıpatları al, haydutları öldür, başlarına konan ödülleri topla!\n\n"
"Not: Bu diyar anakronistik olduğu için, normal oyunda mevcut değil, sadece özel modlarda oynanabilir.")
// Land of Storms
//----------------
S(
"Whenever after your move there is a connection between a charged and a "
"grounded cell, there is a short circuit. All cells on any "
"path connecting a charged and a grounded cell (without repeated cells, "
"or two consecutive grounded/charged cells) become damaged.\n\n"
"Sandstone walls and most creatures are conductive. Great Walls are "
"isolators, but lands beyond them count as grounded.\n\n"
"Fulgurite, the treasure, is created when you manage to short circuit "
"a sandstone wall, or a Rich Metal Beast.\n\n"
"Trolls leave conductive rocks when killed, and Metal Beasts can only "
"be killed by electricity -- your attacks only stun them, or push "
"them away if already stunned.",
"Hareketinden sonra, eğer yüklü ve topraklı bir hücre arasında bağlantı varsa, kısa devre olur."
"Yüklü ve topraklı hücrenin arasındaki bütün hücreler (tekrarlayan hücreler, yahut iki ardışık topraklı yahut yüklü hücre hariç) hasar görür.\n\n"
"Kumtaşı duvarları ve çoğu yaratık iletkendir. Büyük Duvarlar yalıtkandır, ancak onlardan sonraki hücreler topraklanmış sayılır. \n\n"
"Fulgurit, buradaki hazine, bir kumtaşı duvarını veya Zengin Metal Canavarını kısa devreye uğratmanla ortaya çıkar.\n\n"
"Troller öldüklerinde artlarında iletken kayalar bırakırlar ve Metal Canavarlar sadece elektrik ile öldürülebilirler. Saldırıların metal canavarları sersemletir, yahut zaten sersemlemişlerse ittirir. ")
N("Land of Storms", GEN_F, "Fırtına Diyarı", "Fırtına Diyarları", "Fırtına Diyarını", "Fırtına Diyarında")
N("charged wall", GEN_F, "yüklü duvar", "yüklü duvarlar", "yüklü duvarı", "yüklü duvarla")
N("grounded wall", GEN_F, "topraklı duvar", "topraklı duvarlar", "topraklı duvarı", "topraklı duvarla")
N("metal wall", GEN_F, "metal duvar", "metal duvarlar", "metal duvarı", "metal duvarla")
N("sandstone wall", GEN_F, "kumtaşı duvarı", "kumtaşı duvarları", "kumtaşı duvarını", "kumtaşı duvarla")
N("Storm Troll", GEN_M, "Fırtına Trolü", "Fırtına Trolleri", "Fırtına Trolünü", "Fırtına Trolüyle")
N("Metal Beast", GEN_O, "Metal Canavar", "Metal Canavarları", "Metal Canavarını", "Metal Canavarıyla")
N("Rich Metal Beast", GEN_O, "Zengin Metal Canavar", "Zengin Metal Canavarları", "Zengin Metal Canavarını", "Zengin Metal Canavarıyla")
N("electric discharge", GEN_N, "elektrik boşalımı", "elektrik boşalımları", "elektrik boşalımını", "elektrik boşalımıyla")
S("There is a flash of thunder!", "Bir şimşek çaktı!")
Orb("Stunning", "Sersemletme")
S("This Orb allows you to target monsters to stun them. "
"10 charges are used to stun for 5 turns. Does not "
"work against multi-tile monsters.",
"Bu Küre canavarları hedefleyerek sersemletmeni sağlar. 5 tur sersemletmek için 10 güç kullanır. Çokluhücre canavarlara karşı çalışmaz."
)
S("You stun %the1!", "%a1! sersemlettin!")
// Overgrown Woods
//-----------------
Orb("Luck", "Şans")
S("This Orb allows you to find new lands more easily. "
"Lands where you have already collected less treasure, "
"and especially the Crossroads, are more likely to "
"spawn while you have this. Additionally, Orbs of Safety "
"are more likely to spawn in the Whirlpool.",
"Bu küre yeni diyarlar bulmanı kolaylaştırır. Daha az hazine topladığın diyarlar ve özellikle Arayollar, bu küreyi elinde tutarken daha yüksek ihtimalle karşına çıkar. "
"Ayrıca, Güvenlik Kürelerinin Tayfun'da ortaya çıkma ihtimalini artırır."
)
N("Overgrown Woods", GEN_O, "Azman Orman", "Azman Ormanlar", "Azman Ormanı", "Azman Ormanda")
N("Mutant Ivy", GEN_O, "Mutant Sarmaşık", "Mutant Sarmaşıklar", "Mutant Sarmaşığı", "Mutant Sarmaşıkla")
N("Mutant Sapling", GEN_F, "Mutant Fidan", "Mutant Fidanlar", "Mutant Fidanı", "Mutant Fidanla")
N("Forest Troll", GEN_M, "Orman Trolü", "Orman Trolleri", "Orman Trolünü", "Orman Trolüyle")
S("Forest Trolls create an impassable wall when they die.",
"Orman Trolleri öldüklerinde artlarında aşılmaz bir duvar bırakır.")
S(
"The Overgrown Woods are filled with mutant ivies! These plants "
"grow very fast. Each leaf, after being grown, can grow itself "
"on the next turn. However, each part is only able to grow "
"once in 16 turns. Outside of the Overgrown Woods, the Mutant Ivy "
"may grow only on hexagonal cells.\n\n"
"Maybe such fast growing plants could help you solve the problem "
"of hunger in your world? Kill the Mutant Ivies to collect Mutant Saplings.",
"Azman Orman mutant sarmaşıklarla dolu! Bu bitkiler çok hızlı büyür. Her yaprak, büyüdükten sonra sıradaki tur kendi başına büyüyebilir."
" Ancak, her parça ancak 16 tur içinde bir kez büyüyebilir. Azman Orman dışındaki diyarlarda, Mutant Sarmaşık sadece altıgen hücrelerde büyüyebilir.\n\n"
"Belki bu kadar hızlı büyüyen bitkiler dünyandaki açlık sorununu çözebilir? Mutant Fidanları toplamak için Mutant Sarmaşıkları öldür.")
S("Trees in this forest can be cut down. Big trees take two turns to cut down.",
"Bu ormandaki ağaçlar kesilebilir. Büyük ağaçların kesilmesi iki tur alır."
)
/* ACHIEVEMENTS
"NEW_ACHIEVEMENT_5_24_NAME" "Mutant Fidan"
"NEW_ACHIEVEMENT_5_24_DESC" "Bir Mutant Fidan bul ve topla."
"NEW_ACHIEVEMENT_5_25_NAME" "Orman Trolü"
"NEW_ACHIEVEMENT_5_25_DESC" "10 Mutant Fidan topla."
"NEW_ACHIEVEMENT_5_26_NAME" "Mutant Sarmaşık"
"NEW_ACHIEVEMENT_5_26_DESC" "25 Mutant Fidan topla."
"NEW_ACHIEVEMENT_5_27_NAME" "Sarmaşık Efendisi"
"NEW_ACHIEVEMENT_5_27_DESC" "50 Mutant Fidan topla."
"NEW_ACHIEVEMENT_5_28_NAME" "Şimşek Çakması"
"NEW_ACHIEVEMENT_5_28_DESC" "Bir Fulgurit bul ve topla."
"NEW_ACHIEVEMENT_5_29_NAME" "Fırtına Trolü"
"NEW_ACHIEVEMENT_5_29_DESC" "10 Fulgurit topla."
"NEW_ACHIEVEMENT_5_30_NAME" "Metal Canavarı"
"NEW_ACHIEVEMENT_5_30_DESC" "25 Fulgurit topla."
"NEW_ACHIEVEMENT_5_31_NAME" "Zengin Metal Canavarı"
"NEW_ACHIEVEMENT_5_31_DESC" "50 Fulgurit topla."
*/
#undef Orb
// S("TRANSLATIONWARNING", "Uyarı: Türkçe çeviri henüz Sürüm 7.4' daki yeni özellikleri kapsamıyor.")
S("TRANSLATIONWARNING", "")
S("TRANSLATIONWARNING",
"ÇEVİRİ UYARISI: Türkçe çevirisi güncel değil -- 8.1'den sonra eklenen özellikleri")
S("TRANSLATIONWARNING2",
"çevirmek isterseniz lütfen zeno@attnam.com adresinden benimle iletişime geçin.")
S("summon Bonfire", "Kampateşi çıkar")
S("Hyperstone Quest", "Aşkıntaş Görevi")
S("summon dead orbs", "ölü küre çıkar")
S("summon a Monster", "Bir canavar çıkar")
S("gain orb powers", "küre güçleri kazan")
S("summon a Golem", "Golem çıkar")
S("summon Thumpers", "Gümleyen çıkar")
S("summon Ivy", "Sarmaşık çıkar")
S("lose all treasure", "tüm hazineyi kaybet")
S("gain kills", "leşler kazan.")
S("Select the land ---", "Diyar seç ---")
S("summon Mimics", "Taklitçiler çıkar")
S("summon orbs", "küre çıkar")
S("deplete orb powers", "küre güçlerini tüket")
S("Safety (quick save)", "Güvenlik (hızlı kayıt)")
S("summon treasure", "hazine çıkar")
S("summon lots of treasure", "çok hazine çıkar")
S("--- and teleport there", "--- ve oraya ışınlan.")
S("summon Sand Worm", "Kumkurdu çıkar")
S("summon Orb of Yendor", "Yendorun Küresini çıkar")
S("rotate the character", "karakteri döndür")

View File

@ -1,12 +1,10 @@
// Hyperbolic Rogue language support
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
// #define CHECKTRANS
#define NUMLAN 6
#define GEN_M 0
#define GEN_F 1
#define GEN_N 2
#define GEN_O 3
struct stringpar {
string v;
stringpar(string s) : v(s) { }
@ -30,13 +28,16 @@ void rep(string& pattern, string what, string to) {
}
}
void reponce(string& pattern, string what, string to) {
size_t at = pattern.find(what);
if(at != string::npos)
pattern = pattern.replace(at, what.size(), to);
}
typedef unsigned hashcode;
// a quick hack to make this unambiguous on Android
#define hash my_hash
struct sentence {
hashcode hash;
hashcode langhash;
const char* xlat[NUMLAN-1];
};
@ -46,14 +47,14 @@ struct noun {
};
struct fullnoun {
hashcode hash;
hashcode langhash;
int english_grammar_flags;
noun n[NUMLAN-1];
};
#include "language-data.cpp"
hashcode hash(const string& s) {
hashcode langhash(const string& s) {
hashcode r = 0;
for(int i=0; i<size(s); i++) r = hashval * r + s[i];
return r;
@ -61,14 +62,14 @@ hashcode hash(const string& s) {
template<class T> const T* findInHashTableS(string s, const T *table, int size) {
int b = 0, e = size;
hashcode h = hash(s);
hashcode h = langhash(s);
while(b!=e) {
int m = (b+e)>>1;
// printf("b=%d e=%d m=%d h=%x s=%x\n", b, e, m, table[m].hash, h);
if(table[m].hash >= h) e = m;
// printf("b=%d e=%d m=%d h=%x s=%x\n", b, e, m, table[m].langhash, h);
if(table[m].langhash >= h) e = m;
else b = m+1;
}
if(e != size && table[e].hash == h)
if(e != size && table[e].langhash == h)
return &table[e];
return NULL;
}
@ -211,9 +212,11 @@ void parrep(string& x, string w, stringpar p) {
rep(x, "%P"+w, N->n[4].nomp);
rep(x, "%a"+w, N->n[4].acc);
rep(x, "%abl"+w, N->n[4].abl);
rep(x, "%d"+w, N->n[4].abl); // Dativ (which equals Ablative in German)
rep(x, "%Der"+w, choose3(N->n[4].genus, "Der", "Die", "Das"));
rep(x, "%der"+w, choose3(N->n[4].genus, "der", "die", "das"));
rep(x, "%den"+w, choose3(N->n[4].genus, "den", "die", "das"));
rep(x, "%dem"+w, choose3(N->n[4].genus, "dem", "der", "dem"));
}
else {
rep(x,"%"+w,p.v);
@ -272,7 +275,7 @@ string XLAT(string x, stringpar p1, stringpar p2, stringpar p3) {
basicrep(x);
parrep(x,"1",p1.v);
parrep(x,"2",p2.v);
parrep(x,"3",p2.v);
parrep(x,"3",p3.v);
postrep(x);
return x;
}
@ -280,8 +283,8 @@ string XLAT(string x, stringpar p1, stringpar p2, stringpar p3, stringpar p4) {
basicrep(x);
parrep(x,"1",p1.v);
parrep(x,"2",p2.v);
parrep(x,"3",p2.v);
parrep(x,"4",p2.v);
parrep(x,"3",p3.v);
parrep(x,"4",p4.v);
postrep(x);
return x;
}
@ -302,3 +305,5 @@ string XLAT1(string x) {
return x;
}
string XLATT1(stringpar p) { return XLAT1(p.v); }

View File

@ -1,3 +1,6 @@
// HyperRogue map editor
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#include <map>
#include <stdint.h>
@ -9,6 +12,7 @@
#endif
namespace mapeditor {
#ifndef MOBILE
cell *modelcell[200];
void clearModelCells() {
@ -31,8 +35,10 @@ namespace mapeditor {
c->mondir = (c2->mondir - patterndir(c2) + patterndir(c) + 7*6*5) % c->type;
}
}
#endif
}
#ifndef MOBILE
namespace mapstream {
std::map<cell*, int> cellids;
vector<cell*> cellbyid;
@ -199,7 +205,7 @@ namespace mapstream {
for(int i=0; i<size(cellbyid); i++) {
cell *c = cellbyid[i];
if(c->bardir != NODIR && c->bardir != NOBARRIERS)
buildBarrier(c);
extendBarrier(c);
}
for(int d=BARLEV-1; d>=0; d--)
@ -252,8 +258,135 @@ namespace mapstream {
}
}
#endif
namespace mapeditor {
bool drawplayer = true;
char whichPattern = 0;
char whichShape = 0;
char whichCanvas = 0;
int nopattern(cell *c) {
if(isWarped(c) && !euclid) {
int u = ishept(c)?1:0;
int qhex = 0;
for(int v=0; v<c->type; v++) if(c->mov[v] && !isWarped(c->mov[v])) {
u += 2;
if(!ishept(c->mov[v])) qhex++;
}
if(u == 2 && qhex == 1) return 8;
if(u == 6 && qhex == 2) return 10;
return u;
}
return ishept(c) ? 1 : ishex1(c) ? 2 : 0; // 0 to 1
}
bool reflectPatternAt(cell *c, char p = whichPattern) {
if(p == 'p' && polarb50(c)) return true;
if(p == 0 && nopattern(c) == 4) {
int d = patterndir(c);
return !isWarped(createMov(c, (d+1)%6));
}
return false;
}
int downdir(cell *c, cellfunction *cf = coastvalEdge) {
cell *c2 = chosenDown(c, 1, 1, cf);
if(!c2) return 0;
return neighborId(c, c2);
}
int patterndir(cell *c, char w) {
switch(w) {
case 'z': {
int t = zebra40(c);
int t4 = t>>2, tcdir = 0;
if(purehepta) tcdir = t^1;
else if(t4 == 10) tcdir = t-20;
else if(t4 >= 4 && t4 < 7) tcdir = 40 + (t&3);
else if(t4 >= 1 && t4 < 4) tcdir = t+12;
else if(t4 >= 7 && t4 < 10) tcdir = t-24;
for(int i=0; i<c->type; i++) if(c->mov[i] && zebra40(c->mov[i]) == tcdir)
return i;
// printf("fail to fintd %d -> %d\n", t, tcdir);
return 0;
}
case 'f': {
int t = emeraldval(c);
int tcdir = 0, tbest = (t&3);
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
if(c2) {
int t2 = emeraldval(c2);
if((t&3) == (t2&3) && t2 > tbest)
tbest = t2, tcdir = i;
}
}
return tcdir;
}
case 'p': {
int tcdir = -1, tbest = -1;
int pa = polara50(c);
int pb = polarb50(c);
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
if(c2 && polara50(c2) == pa && polarb50(c2) == pb) {
int t2 = fiftyval049(c2);
if(t2 > tbest) tbest = t2, tcdir = i;
}
}
return tcdir;
}
case 'H':
return downdir(c);
case 0: {
if(euclid) return 0;
int u = nopattern(c);
if(u == 6)
for(int i=1; i<c->type; i+=2) if(!isWarped(createMov(c,i)))
return i;
if(u == 2 || u == 3 || u == 8)
for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,i)))
return i;
if(u == 4 || u == 10)
for(int i=0; i<c->type; i+=2) if(!isWarped(createMov(c,i)))
return i;
if(u == 6)
for(int i=1; i<c->type; i+=2) if(!isWarped(createMov(c,i)))
return i;
if(u == 5)
for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,(i+3)%7)) && !isWarped(createMov(c,(i+4)%7)))
return i;
if(u == 9)
for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,(i+2)%7)) && !isWarped(createMov(c,(i+5)%7)))
return i;
if(u == 7)
for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,(i+1)%7)) && !isWarped(createMov(c,(i+6)%7)))
return i;
}
}
return 0;
}
#ifndef MOBILE
int paintwhat = 0;
int painttype = 0;
int radius = 0;
@ -262,14 +395,13 @@ namespace mapeditor {
int subscreen; //0=normal, 1=config, 2=patterns, 3=predesigned
char whichPattern;
bool symRotation, sym01, sym02, sym03;
int displaycodes;
char whichShape, whichCanvas;
int subcanvas;
int whichpart;
bool drawplayer = true;
string infix;
cell *drawcell;
const char *mapeditorhelp =
@ -368,11 +500,6 @@ namespace mapeditor {
if(checkEq(undo[size(undo)-1])) undo.pop_back();
}
string actkeys = "-"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789!@#$%^&*()_+=[{]}\\|;:'\",<.>/?`~";
int itc(int k) {
if(k == 0) return 0;
if(k == 1) return 0x40;
@ -524,6 +651,22 @@ namespace mapeditor {
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;
}
v.push_back(make_pair(s, i));
}
void showMapEditor() {
if(choosefile) { drawFileDialog(); return; }
@ -544,22 +687,24 @@ namespace mapeditor {
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');
displayStat(17, XLAT("predesigned patterns"), "", 'r');
displayStat(18, XLAT("predesigned patterns"), "", 'r');
}
else if(subscreen == 3) {
displayStat(2, XLAT("Gameboard"), "", 'g');
displayStat(3, XLAT("random colors"), "", 'r');
displayStat(4, XLAT("rainbow landscape"), "", 'l');
displayStat(5, XLAT("emerald pattern"), "emerald", 'e');
displayStat(6, XLAT("emerald pattern"), "emerald", 'e');
displayStat(7, XLAT("four elements"), "palace", 'b');
displayStat(8, XLAT("eight domains"), "palace", 'a');
displayStat(8, XLAT("four elements"), "palace", 'b');
displayStat(9, XLAT("eight domains"), "palace", 'a');
displayStat(10, XLAT("zebra pattern"), "zebra", 'z');
displayStat(11, XLAT("three stripes"), "zebra", 'x');
displayStat(11, XLAT("zebra pattern"), "zebra", 'z');
displayStat(12, XLAT("three stripes"), "zebra", 'x');
displayStat(13, XLAT("random black-and-white"), "current", 'w');
displayStat(15, XLAT("random black-and-white"), "current", 'w');
}
else if(subscreen == 1 && painttype == 6)
drawColorDialog(paintwhat);
@ -575,30 +720,25 @@ namespace mapeditor {
m == moFlailBullet || m == moShadow || m == moAirball ||
m == moWolfMoved || m == moGolemMoved ||
m == moTameBomberbirdMoved || m == moKnightMoved ||
m == moDeadBug || m == moLightningBolt || m == moDeadBird ||
m == moMouseMoved || m == moPrincessMoved || m == moPrincessArmedMoved) ;
else
v.push_back(make_pair(XLATN(minf[i].name), i));
else vpush(i, minf[i].name);
}
break;
case 1:
for(int i=0; i<ittypes; i++) {
v.push_back(make_pair(XLATN(iinf[i].name), i));
}
for(int i=0; i<ittypes; i++) vpush(i, iinf[i].name);
break;
case 2:
for(int i=0; i<landtypes; i++) {
v.push_back(make_pair(XLATN(linf[i].name), i));
}
for(int i=0; i<landtypes; i++) vpush(i, linf[i].name);
break;
case 3:
for(int i=0; i<walltypes; i++) {
if(i != waChasmD)
v.push_back(make_pair(XLATN(winf[i].name), i));
}
for(int i=0; i<walltypes; i++) if(i != waChasmD) vpush(i, winf[i].name);
break;
}
// sort(v.begin(), v.end());
if(infix != "") mouseovers = infix;
int q = v.size();
int percolumn = vid.yres / (vid.fsize+5) - 4;
int columns = 1 + (q-1) / percolumn;
@ -607,7 +747,11 @@ namespace mapeditor {
int x = 16 + (vid.xres * (i/percolumn)) / columns;
int y = (vid.fsize+5) * (i % percolumn) + vid.fsize*2;
displayButton(x, y, v[i].first + ": " + actkeys[i], actkeys[i], 0);
int actkey = 1000 + i;
string vv = v[i].first;
if(i < 9) { vv += ": "; vv += ('1' + i); }
displayButton(x, y, vv, actkey, 0);
}
}
else {
@ -713,7 +857,7 @@ namespace mapeditor {
case 'H':
return realpattern(c);
}
return c->type-6; // 0 to 1
return nopattern(c);
}
int realpattern(cell *c) {
@ -732,7 +876,7 @@ namespace mapeditor {
case 'H':
return towerval(c);
}
return c->type-6; // 0 to 2
return nopattern(c);
}
int cellShapeGroup() {
@ -751,7 +895,7 @@ namespace mapeditor {
}
int drawcellShapeID() {
if(drawcell == cwt.c) return vid.female;
if(drawcell == cwt.c) return vid.cs.charid;
if(drawcell->monst) return drawcell->monst;
if(drawcell->item) return drawcell->item;
return subpattern(drawcell);
@ -770,63 +914,6 @@ namespace mapeditor {
return subpatternShape(id) == subpattern(drawcell);
}
bool reflectPatternAt(cell *c) {
return whichPattern == 'p' && polarb50(c);
}
int patterndir(cell *c, char w) {
switch(w) {
case 'z': {
int t = zebra40(c);
int t4 = t>>2, tcdir;
if(t4 == 10) tcdir = 0;
else if(t4 >= 4 && t4 < 7) tcdir = 40 + (t&3);
else if(t4 >= 1 && t4 < 4) tcdir = t+12;
else if(t4 >= 7 && t4 < 10) tcdir = t-24;
for(int i=0; i<c->type; i++) if(c->mov[i] && zebra40(c->mov[i]) == tcdir)
return i;
}
case 'f': {
int t = emeraldval(c);
int tcdir, tbest = (t&3);
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
if(c2) {
int t2 = emeraldval(c2);
if((t&3) == (t2&3) && t2 > tbest)
tbest = t2, tcdir = i;
}
}
return tcdir;
}
case 'p': {
int tcdir = -1, tbest = -1;
int pa = polara50(c);
int pb = polarb50(c);
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
if(c2 && polara50(c2) == pa && polarb50(c2) == pb) {
int t2 = fiftyval049(c2);
if(t2 > tbest) tbest = t2, tcdir = i;
}
}
return tcdir;
}
case 'H': {
cell *c2 = chosenDown(c, 1, 1);
if(!c2) return 0;
return neighborId(c, c2);
}
}
return 0;
}
void spill(cell *c, int r, int cdir) {
if(painttype == 4 && radius) {
@ -846,7 +933,7 @@ namespace mapeditor {
c->stuntime = 0;
c->mondir = cdir;
if((isWorm(c) || isIvy(c)) && c->mov[cdir] &&
if((isWorm(c) || isIvy(c) || isMutantIvy(c)) && c->mov[cdir] &&
!isWorm(c->mov[cdir]) && !isIvy(c->mov[cdir]))
c->mondir = NODIR;
break;
@ -876,7 +963,7 @@ namespace mapeditor {
if(hasTimeout(c))
c->wparam = 10;
else if(c->wall == waWaxWall)
c->landparam = rand() & 0xFFFFFF;
c->landparam = hrand(0xFFFFFF + 1);
}
else if(hasTimeout(c))
c->wparam += spillinc();
@ -937,7 +1024,7 @@ namespace mapeditor {
}
for(int i=0; i<c2->type; i++) {
cell *c3 = c2->mov[i];
if(c3 && c3->aitmp != sval)
if(c3 && !eq(c3->aitmp, sval))
c3->aitmp = sval, v.push_back(c3);
}
}
@ -995,13 +1082,19 @@ namespace mapeditor {
if(v == 2) cmode = emNormal;
}
else if(subscreen == 1) {
for(int z=0; z<size(v); z++) if(actkeys[z] == uni) {
if(uni >= '1' && uni <= '9') uni = 1000 + uni - '1';
if(sym == SDLK_RETURN || sym == '-') uni = 1000;
for(int z=0; z<size(v); z++) if(1000 + z == uni) {
paintwhat = v[z].second;
paintwhat_str = v[z].first;
subscreen = 0;
mousepressed = false;
}
if(subscreen == 1 && uni != 0) cmode = emNormal;
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;
}
else if(subscreen == 3) {
if(uni >= 'a' && uni <= 'z') {
@ -1023,7 +1116,7 @@ namespace mapeditor {
else if(uni == '1') sym01 = !sym01;
else if(uni == '2') sym02 = !sym02;
else if(uni == '3') sym03 = !sym03;
else if(uni == '6' || uni == '7') {
else if(uni == '6' || uni == '7' || uni == '8') {
if(whichShape == uni) whichShape = 0;
else whichShape = uni;
}
@ -1046,10 +1139,10 @@ namespace mapeditor {
if(uni == 'u') applyUndo();
else if(uni == 'v' || sym == SDLK_F10 || sym == SDLK_ESCAPE) cmode = emNormal;
else if(uni >= '0' && uni <= '9') radius = uni - '0';
else if(uni == 'm') subscreen = 1, painttype = 0;
else if(uni == 'i') subscreen = 1, painttype = 1;
else if(uni == 'l') subscreen = 1, painttype = 2;
else if(uni == 'w') subscreen = 1, painttype = 3;
else if(uni == 'm') subscreen = 1, painttype = 0, infix = "";
else if(uni == 'i') subscreen = 1, painttype = 1, infix = "";
else if(uni == 'l') subscreen = 1, painttype = 2, infix = "";
else if(uni == 'w') subscreen = 1, painttype = 3, infix = "";
else if(uni == 'r') subscreen = 2;
else if(uni == 't' && mouseover) {
cwt.c = mouseover; playermoved = true;
@ -1161,7 +1254,7 @@ namespace mapeditor {
hyperpoint coldcenter = C0;
void drawGrid() {
if(cmode == emDraw) {
if(cmode == emDraw && !inHighQual) {
lalpha = 0x20;
for(int d=0; d<84; d++) {
transmatrix d2 = drawtrans * rgpushxto0(ccenter);
@ -1169,19 +1262,37 @@ namespace mapeditor {
lalpha = 0x40;
else
lalpha = 0x20;
drawline(d2 * C0, d2 * spin(M_PI*d/42)* xpush(1) * C0, 0xC0C0C0);
queueline(d2 * C0, d2 * spin(M_PI*d/42)* xpush(1) * C0, 0xC0C0C0);
for(int u=2; u<=20; u++) {
if(u % 5 == 0) lalpha = 0x40;
else lalpha = 0x20;
drawline(
queueline(
d2 * spin(M_PI*d/42)* xpush(u/20.) * C0,
d2 * spin(M_PI*(d+1)/42)* xpush(u/20.) * C0,
0xC0C0C0);
}
}
drawline(drawtrans*ccenter, drawtrans*coldcenter, 0xC0C0C0);
queueline(drawtrans*ccenter, drawtrans*coldcenter, 0xC0C0C0);
lalpha = 0xFF;
int sg = drawcellShapeGroup();
for(int i=0; i<USERSHAPEIDS; i++) if(editingShape(sg, i) && usershapes[sg][i]) {
usershapelayer &ds(usershapes[sg][i]->d[mapeditor::dslayer]);
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 :
a == size(ds.list)-1 ? 0xFF0000 :
0xFFFF00);
}
}
}
}
@ -1201,7 +1312,7 @@ namespace mapeditor {
switch(sg) {
case 0:
line1 = XLAT("character");
line2 = XLAT(vid.female ? "female" : "male");
line2 = csname(vid.cs);
break;
case 1:
@ -1216,7 +1327,8 @@ namespace mapeditor {
case 3:
line1 = XLAT("floor");
line2 = XLAT(drawcell->type == 6 ? "hexagonal" : "heptagonal");
line2 = XLAT(ishept(drawcell) ? "heptagonal" :
ishex1(drawcell) ? "hexagonal #1" : "hexagonal");
break;
default:
@ -1274,14 +1386,20 @@ namespace mapeditor {
void applyToShape(int sg, int id, int uni, hyperpoint mh) {
bool haveshape = usershapes[sg][id];
bool xnew = false;
if(!haveshape) {
if(uni == 'n' || uni == 'u')
initShape(sg, id);
else return;
else if(uni >= '0' && uni <= '9') {
initShape(sg, id);
xnew = true;
}
else
return;
}
usershapelayer *dsCur = &usershapes[sg][id]->d[dslayer];
if(uni == 'n') {
if(uni == 'n' || xnew) {
dsCur->list.clear();
dsCur->list.push_back(mh);
saveImages();
@ -1319,27 +1437,72 @@ namespace mapeditor {
saveImages();
}
if(uni == 'L') {
if(!vid.female) loadShape(sg, id, shPBody, 2, 0);
if(uni == 'T') {
/* loadShape(sg, id, shFemaleBody, 1, 1);
loadShape(sg, id, shPKnife, 1, 2);
loadShape(sg, id, shFemaleDress, 1, 3);
loadShape(sg, id, shPrincessDress, 1, 4);
loadShape(sg, id, shBeautyHair, 1, 5);
loadShape(sg, id, shPFace, 1, 6);
loadShape(sg, id, shFlowerHair, 1, 7); */
/* loadShape(sg, id, shYeti, 1, 2);
loadShape(sg, id, shRatHead, 1, 3);
loadShape(sg, id, shRatTail, 1, 1);
loadShape(sg, id, shWolf1, 1, 4);
loadShape(sg, id, shWolf2, 1, 5);
loadShape(sg, id, shRatCape1, 1, 7);
loadShape(sg, id, shRatCape2, 1, 6); */
// loadShape(sg, id, shTortoise[0][0], 1, 0);
/* loadShape(sg, id, shTentacleX, 1, 0);
loadShape(sg, id, shTentacle, 1, 1);
loadShape(sg, id, shJoint, 1, 2); */
/* loadShape(3, 0, shTurtleFloor[0], 12, 0);
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();
}
if(uni == 'K') {
if(vid.cs.charid >= 4) {
loadShape(sg, id, shCatBody, 2, 0);
loadShape(sg, id, shCatHead, 2, 1);
}
else {
if(!(vid.cs.charid&1)) loadShape(sg, id, shPBody, 2, 0);
else loadShape(sg, id, shFemaleBody, 2, 0);
loadShape(sg, id, shPSword, 1, 1);
if(vid.female)
if(vid.cs.charid&1)
loadShape(sg, id, shFemaleDress, 2, 2);
if(vid.female)
loadShape(sg, id, shFemaleHair, 2, 3);
if(vid.cs.charid&1)
loadShape(sg, id, shPrincessDress, 1, 3);
else
loadShape(sg, id, shPHead, 2, 3);
loadShape(sg, id, shPrinceDress, 2, 3);
loadShape(sg, id, shPFace, 2, 4);
if(vid.cs.charid&1)
loadShape(sg, id, shFemaleHair, 2, 4);
else
loadShape(sg, id, shPHead, 2, 4);
loadShape(sg, id, shPFace, 2, 5);
}
// loadShape(sg, id, shWolf, 2, dslayer);
saveImages();
}
if(uni == '+') dsCur->rots++;
if(uni >= '1' && uni <= '9') {
dsCur->rots = uni - '0';
if(dsCur->rots == 9) dsCur->rots = 21;
@ -1372,7 +1535,8 @@ namespace mapeditor {
hyperpoint h;
for(int i=0; i<3; i++) {
double d;
fscanf(f, "%lf", &d);
int err = fscanf(f, "%lf", &d);
if(err) printf("Warning: read error\n");
h[i] = d;
}
return h;
@ -1393,7 +1557,7 @@ namespace mapeditor {
else return;
}
dslayer &= 7;
dslayer %= USERLAYERS;
hyperpoint mh = inverse(drawtrans) * mouseh;
int sg = drawcellShapeGroup();
@ -1404,7 +1568,8 @@ namespace mapeditor {
if(uni == 'e') {
drawcell = mouseover ? mouseover : cwt.c;
}
if(uni == 'l') dslayer++;
if(uni == 'l') { dslayer++; dslayer %= USERLAYERS; }
if(uni == 'L') { dslayer--; if(dslayer < 0) dslayer += USERLAYERS; }
if(uni == 'g') coldcenter = ccenter, ccenter = mh;
@ -1467,12 +1632,17 @@ namespace mapeditor {
addMessage(XLAT("Failed to load pictures from %1", picfile));
return;
}
char buf[200]; fgets(buf, 200, f);
int vernum; fscanf(f, "%x", &vernum);
int err;
char buf[200];
if(!fgets(buf, 200, f)) {
addMessage(XLAT("Failed to load pictures from %1", picfile));
fclose(f); return;
}
int vernum; err = fscanf(f, "%x", &vernum);
printf("vernum = %x\n", vernum);
while(true) {
int i, j, l, sym, rots, color, siz;
int err = fscanf(f, "%d%d%d%d%d%x%d", &i, &j, &l, &sym, &rots, &color, &siz);
err = fscanf(f, "%d%d%d%d%d%x%d", &i, &j, &l, &sym, &rots, &color, &siz);
if(i == -1 || err < 6) break;
if(siz < 0 || siz > 1000) break;
initShape(i, j);
@ -1524,7 +1694,7 @@ namespace mapeditor {
if(whichCanvas == 'g')
return linf[laCanvas].color >> 2;
if(whichCanvas == 'r')
return rand() & 0xFFFFFF;
return hrand(0xFFFFFF + 1);
if(whichCanvas == 'e') {
static unsigned int fcol[4] = { 0x404040, 0x800000, 0x008000, 0x000080 };
int fv = emeraldval(c);
@ -1565,6 +1735,21 @@ namespace mapeditor {
static unsigned int fcol[2] = { 0x303030, 0xC0C0C0 };
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
}
return linf[laCanvas].color >> 2;
}
#endif
}

796
menus.cpp Normal file
View File

@ -0,0 +1,796 @@
// HyperRogue menus
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
void showOverview() {
DEBB(DF_GRAPH, (debugfile,"show overview\n"));
mouseovers = XLAT("world overview");
mouseovers += " ";
mouseovers += XLAT(" kills: %1/%2", its(tkills()), its(killtypes()));
mouseovers += XLAT(" $$$: %1", its(gold()));
if(hellUnlocked()) {
int i1, i2; countHyperstoneQuest(i1, i2);
mouseovers += XLAT(" Hyperstone: %1/%2", its(i1), its(i2));
}
else
mouseovers += XLAT(" Hell: %1/9", its(orbsUnlocked()));
int nl = LAND_OVER; eLand *landtab = land_over;
if(randomPatternsMode) { nl = RANDLANDS; landtab = randlands; }
int vf = min((vid.yres-64) / nl, vid.xres/40);
eLand curland = cwt.c->land;
if(curland == laPalace && princess::dist(cwt.c) < OUT_OF_PRISON)
curland = laPrincessQuest;
if(isElemental(curland)) curland = laElementalWall;
getcstat = '0';
for(int i=0; i<nl; i++) {
eLand l = landtab[i];
int xr = vid.xres / 64;
int i0 = 56 + i * vf;
int col;
if(landUnlocked(l)) col = linf[l].color; else col = 0x202020;
if(chaosmode && noChaos(l)) col = 0x200000;
if(l == curland)
displayfr(1, i0, 1, vf-4, "*", 0xFFFFFF, 0);
if(displayfr(xr*1, i0, 1, vf-4, XLAT1(linf[l].name), col, 0))
getcstat = 1000 + l;
eItem it = treasureType(l);
int lv = items[it] * landMultiplier(l);
if(lv >= 25) col = 0xFFD500;
else if(lv >= 10) col = 0x00D500;
else if(items[it]) col = 0xC0C0C0;
else col = 0x202020;
if(chaosmode && noChaos(l)) col = 0x200000;
if(displayfr(xr*24-48, i0, 1, vf-4, its(items[it]), col, 16))
getcstat = 2000+it;
if(!cheater)
if(displayfr(xr*24, i0, 1, vf-4, its(hiitems[modecode()][it]), col, 16))
getcstat = 2000+it;
if(items[it]) col = iinf[it].color; else col = 0x202020;
if(chaosmode && noChaos(l)) col = 0x200000;
if(displayfr(xr*24+32, i0, 1, vf-4, s0 + iinf[it].glyph, col, 16))
getcstat = 2000+it;
if(displayfr(xr*24+40, i0, 1, vf-4, XLAT1(iinf[it].name), col, 0))
getcstat = 2000+it;
eItem io = orbType(l);
if(io == itShard) {
if(items[it] >= 10) col = winf[waMirror].color; else col = 0x202020;
if(chaosmode && noChaos(l)) col = 0x200000;
if(displayfr(xr*46, i0, 1, vf-4, XLAT1(winf[waMirror].name), col, 0))
getcstat = 3000+waMirror;
if(getcstat == 3000+waMirror)
mouseovers = XLAT(
olrDescriptions[getOLR(io, cwt.c->land)], cwt.c->land, it, treasureType(cwt.c->land));
}
else if(io) {
if(lv >= 25) col = 0xFFD500;
else if(lv >= 10) col = 0xC0C0C0;
else col = 0x202020;
if(chaosmode && noChaos(l)) col = 0x200000;
if(displayfr(xr*46-32, i0, 1, vf-4, its(items[io]), col, 16))
getcstat = 2000+io;
if(lv >= 10) col = iinf[io].color; else col = 0x202020;
if(chaosmode && noChaos(l)) col = 0x200000;
if(displayfr(xr*46-8, i0, 1, vf-4, s0 + iinf[io].glyph, col, 16))
getcstat = 2000+io;
if(displayfr(xr*46, i0, 1, vf-4, XLAT1(iinf[io].name), col, 0))
getcstat = 2000+io;
if(getcstat == 2000+io)
mouseovers = XLAT(
olrDescriptions[getOLR(io, curland)], curland, it, treasureType(curland));
}
}
}
void handleOverview(int uni) {
int umod = uni % 1000;
int udiv = uni / 1000;
if(udiv == 1 && umod < landtypes) {
if(cheater) {
eLand l = eLand(umod);
cheater++;
if(l == laPrincessQuest) {
if(kills[moVizier] == 0) kills[moVizier] = 1;
princess::forceMouse = true;
princess::gotoPrincess = true;
l = laPalace;
}
activateSafety(l);
cmode = emNormal;
canmove = true;
}
else {
lastmode = cmode;
cmode = emHelp; help = generateHelpForLand(eLand(umod));
}
}
else if(udiv == 2 && umod < ittypes) {
if(cheater) {
cheater++;
double shiftmul = 1.001;
if(anyshiftclick) shiftmul *= -1;
if(rightclick) shiftmul /= 10;
int ic = itemclass(eItem(umod));
if(ic == IC_TREASURE) items[umod] += int(10*shiftmul);
if(ic == IC_ORB) items[umod] += int(60*shiftmul);
if(umod == itGreenStone) items[umod] += int(100*shiftmul);
else if(ic == IC_OTHER) items[umod] += (shiftmul>0?1:-1);
if(items[umod] < 0) items[umod] = 0;
if(hardcore) canmove = true;
else checkmove();
}
else {
lastmode = cmode;
cmode = emHelp; help = generateHelpForItem(eItem(umod));
if(hardcore) canmove = true;
else checkmove();
}
}
else if(udiv == 3 && umod < walltypes) {
lastmode = cmode;
cmode = emHelp; help = generateHelpForWall(eWall(umod));
}
else if(uni) cmode = emNormal;
}
void showMainMenu() {
int y = vid.yres * .5 - vid.fsize * 10.5;
displayfr(vid.xres/2, y-vid.fsize * 2, 4, vid.fsize*2,
XLAT("HyperRogue %1", VER), 0xC00000, 8
);
displayButton(vid.xres/2, y + vid.fsize*2, ifMousing("b", "basic configuration"), 'b', 8, 2);
displayButton(vid.xres/2, y + vid.fsize*4, ifMousing("a", "advanced configuration"), 'a', 8, 2);
#ifndef ANDROID
displayButton(vid.xres/2, y + vid.fsize*6, ifMousing("t", "local highscores"), 't', 8, 2);
#endif
displayButton(vid.xres/2, y + vid.fsize*8, ifMousing("h, F1", "help"), 'h', 8, 2);
if(cheater)
displayButton(vid.xres/2, y + vid.fsize*10, ifMousing("c", "cheats"), 'c', 8, 2);
displayButton(vid.xres/2, y + vid.fsize*12, ifMousing("r, F5", "restart game"), 'r', 8, 2);
displayButton(vid.xres/2, y + vid.fsize*14, ifMousing("m", "special game modes"), 'm', 8, 2);
string q;
#ifndef ANDROID
q = (items[itOrbSafety] && havesave) ? "save" : "quit";
#ifdef IOS
q = q + " and visit the website";
#else
q = q + " the game";
#endif
displayButton(vid.xres/2, y + vid.fsize*17, ifMousing("q, F10", q), 'q', 8, 2);
#endif
if(canmove)
q = "review your quest";
else
q = "review the scene";
displayButton(vid.xres/2, y + vid.fsize*20, ifMousing("ESC", q), SDLK_ESCAPE, 8, 2);
displayButton(vid.xres/2, y + vid.fsize*22, ifMousing("o", "world overview"), 'o', 8, 2);
if(!canmove) q = "game over screen";
else if(turncount > 0) q = "continue game";
else q = "play the game!";
displayButton(vid.xres/2, y + vid.fsize*25, ifMousing(XLAT("other"), q), ' ', 8, 2);
}
void loadScores();
bool handleMenuKey(int sym, bool mdown) {
if(sym == SDLK_F1 || sym == 'h') {
lastmode = cmode;
cmode = emHelp;
}
else if(sym == 'c' && cheater)
cmode = emCheatMenu;
else if(sym == 'b') cmode = emVisual1;
else if(sym == 'a') cmode = emVisual2;
else if(sym == 'm') cmode = emChangeMode;
#ifndef ANDROID
else if(sym == 't') loadScores();
#endif
else if(sym == 'r' || sym == SDLK_F5) {
restartGame();
cmode = emNormal;
}
else if(sym == 'q' || sym == SDLK_F10) return true;
else if(sym == 'o') cmode = emOverview;
else if(sym == SDLK_ESCAPE) cmode = emQuit;
else if((sym != 0 && sym != SDLK_F12) || mdown) {
cmode = emNormal;
msgs.clear();
}
return false;
}
void showVisual1() {
#ifndef MOBILE
displayStat(2, XLAT("video resolution"), its(vid.xres) + "x"+its(vid.yres), 'r');
displayStat(3, XLAT("fullscreen mode"), ONOFF(vid.full), 'f');
#endif
displayStat(4, XLAT("animation speed"), fts(vid.aspeed), 'a');
displayStat(5, XLAT("dist from hyperboloid ctr"), fts(vid.alpha), 'p');
displayStat(6, XLAT("scale factor"), fts(vid.scale), 'z');
const char *wdmodes[4] = {"ASCII", "black", "plain", "Escher"};
const char *mdmodes[4] = {"ASCII", "items only", "items and monsters", "high contrast"};
displayStat(7, XLAT("draw the heptagons darker"), ONOFF(vid.darkhepta), '7');
displayStat(8, XLAT("wall display mode"),
XLAT(wdmodes[vid.wallmode]), 'w');
displayStat(9, XLAT("monster display mode"),
XLAT(mdmodes[vid.monmode]), 'm');
#ifndef MOBILE
const char *axmodes[4] = {"no axes", "auto", "light", "heavy"};
displayStat(10, XLAT("cross display mode"),
XLAT(axmodes[vid.axes]), 'c');
#endif
#ifndef MOBILE
displayStat(11, XLAT("background music volume"),
its(audiovolume), 'b');
#endif
if(lang() != 0) {
string s = XLAT("TRANSLATIONWARNING");
if(s != "" && s != "TRANSLATIONWARNING") {
int dy = vid.fsize * 23 + vid.yres/4;
int dx = vid.xres/2;
displaystr(dx, dy, 0, vid.fsize, s, 0xFF0000, 8);
}
s = XLAT("TRANSLATIONWARNING2");
if(s != "" && s != "TRANSLATIONWARNING2") {
int dy = vid.fsize * 24 + vid.yres/4;
int dx = vid.xres/2;
displaystr(dx, dy, 0, vid.fsize, s, 0xFF0000, 8);
}
}
displayStat(13, XLAT("language"), XLAT("EN"), 'l');
displayStat(14, XLAT("player character"),
numplayers() > 1 ? "" : csname(vid.cs), 'g');
}
void handleVisual1(int sym, int uni) {
char xuni = uni | 96;
if(uni >= 32 && uni < 64) xuni = uni;
if(xuni == 'p') vid.alpha += shiftmul * 0.1;
if(xuni == 'z') vid.scale += shiftmul * 0.1;
if(xuni == 'i') {
double d = exp(shiftmul/10);
vid.alpha *= d;
vid.scale *= d;
}
if(xuni == 'a') vid.aspeed += shiftmul;
#ifndef MOBILE
if(xuni == 'f') {
vid.full = !vid.full;
if(shiftmul > 0) {
vid.xres = vid.full ? vid.xscr : 9999;
vid.yres = vid.full ? vid.yscr : 9999;
extern bool setfsize;
setfsize = true;
}
setvideomode();
}
#endif
if(xuni == 'v' || sym == SDLK_F2) cmode = emNormal;
#ifndef ANDROID
if(xuni == 's') saveConfig();
#endif
if(xuni == '7') { vid.darkhepta = !vid.darkhepta; }
if(xuni == 'w') { vid.wallmode += 60 + (shiftmul > 0 ? 1 : -1); vid.wallmode %= 4; }
if(xuni == 'm') { vid.monmode += 60 + (shiftmul > 0 ? 1 : -1); vid.monmode %= 4; }
if(xuni == 'c') { vid.axes += 60 + (shiftmul > 0 ? 1 : -1); vid.axes %= 4; }
#ifndef MOBILE
if(xuni == 'b') {
audiovolume += int(10.5 * shiftmul);
if(audiovolume < 0) audiovolume = 0;
if(audiovolume > MIX_MAX_VOLUME) audiovolume = MIX_MAX_VOLUME;
Mix_VolumeMusic(audiovolume);
}
#endif
if(sym == SDLK_ESCAPE) cmode = emNormal;
if(xuni == 'l') {
vid.language += (shiftmul>0?1:-1);
vid.language %= NUMLAN;
if(vid.language < 0) vid.language += NUMLAN;
}
if(xuni == 'g') cmode = emCustomizeChar;
}
void showVisual2() {
#ifndef MOBILE
#ifdef GL
displayStat(2, XLAT("openGL & antialiasing mode"), vid.usingGL ? "OpenGL" : vid.usingAA ? "AA" : "OFF", 'o');
#endif
#endif
displayStat(3, XLAT("distance between eyes"), fts(vid.eye * 10), 'e');
#ifndef MOBILE
displayStat(4, XLAT("framerate limit"), its(vid.framelimit), 'f');
#endif
#ifndef MOBILE
displayStat(6, XLAT("joystick mode"), XLAT(autojoy ? "automatic" : "manual"), 'p');
displayStat(7, XLAT("first joystick: movement threshold"), its(vid.joyvalue), 'a');
displayStat(8, XLAT("first joystick: execute movement threshold"), its(vid.joyvalue2), 'b');
displayStat(9, XLAT("second joystick: pan threshold"), its(vid.joypanthreshold), 'c');
displayStat(10, XLAT("second joystick: panning speed"), fts(vid.joypanspeed * 1000), 'd');
#endif
#ifdef MOBILE
displayStat(6, XLAT("sight range"), its(sightrange), 'a');
extern int fontscale;
displayStat(7, XLAT("font scale"), its(fontscale), 'b');
#endif
displayStat(12, XLAT("message flash time"), its(vid.flashtime), 't');
#ifndef MOBILE
displayStat(13, XLAT("targetting ranged Orbs Shift+click only"), ONOFF(vid.shifttarget), 'i');
#endif
#ifdef STEAM
displayStat(14, XLAT("send scores to Steam leaderboards"), ONOFF(vid.steamscore), 'l');
#endif
}
void handleVisual2(int sym, int uni) {
char xuni = uni | 96;
if(xuni == 'v' || sym == SDLK_F2 || sym == SDLK_ESCAPE) cmode = emNormal;
#ifndef ANDROID
if(xuni == 's') saveConfig();
#endif
if(sym == SDLK_F1 || sym == 'h')
lastmode = cmode, cmode = emHelp;
#ifdef GL
#ifndef MOBILE
if(xuni == 'o' && shiftmul > 0) {
vid.usingGL = !vid.usingGL;
if(vid.usingGL) addMessage(XLAT("openGL mode enabled"));
if(!vid.usingGL) addMessage(XLAT("openGL mode disabled"));
setvideomode();
}
#endif
if(xuni == 'o' && shiftmul < 0 && !vid.usingGL) {
vid.usingAA = !vid.usingAA;
if(vid.usingAA) addMessage(XLAT("anti-aliasing enabled"));
if(!vid.usingAA) addMessage(XLAT("anti-aliasing disabled"));
}
#endif
if(xuni == 'f') {
vid.framelimit += int(10.5 * shiftmul);
if(vid.framelimit < 5) vid.framelimit = 5;
}
#ifdef MOBILE
if(xuni == 'a') {
sightrange += shiftmul>0?1:-1;
if(sightrange < 4) sightrange = 4;
if(sightrange > 7) sightrange = 7;
}
if(xuni =='b') {
extern int fontscale;
fontscale += int(shiftmul * 10);
}
#endif
if(xuni == 'a') vid.joyvalue += int(shiftmul * 100);
if(xuni == 'b') vid.joyvalue2 += int(shiftmul * 100);
if(xuni == 'c') vid.joypanthreshold += int(shiftmul * 100);
if(xuni == 'd') vid.joypanspeed += shiftmul / 50000;
if(xuni == 'e') vid.eye += shiftmul * 0.01;
#ifdef STEAM
if(xuni == 'l') vid.steamscore = !vid.steamscore;
#endif
if(xuni == 't') vid.flashtime += shiftmul>0?1:-1;
if(xuni == 'p') autojoy = !autojoy;
if(xuni == 'i') { vid.shifttarget = !vid.shifttarget; }
}
void showChangeMode() {
#ifndef MOBILE
displayStat(2, XLAT("vector graphics editor"), "", 'g');
displayStat(3, XLAT("map editor"), ONOFF(false), 'm');
#endif
displayStat(4, XLAT("cheat mode"), ONOFF(cheater), 'c');
displayStat(6, XLAT("Euclidean mode"), ONOFF(euclid), 'e');
#ifndef MOBILE
displayStat(7,
XLAT("shoot'em up mode") + " " + XLAT("(includes co-op)"),
ONOFF(shmup::on), 's');
#endif
if(!shmup::on) displayStat(8, XLAT("hardcore mode"),
hardcore && !pureHardcore() ? XLAT("PARTIAL") : ONOFF(hardcore), 'h');
displayStat(9, XLAT("%1 Challenge", moPrincess), ONOFF(princess::challenge), 'p');
displayStat(10, XLAT("random pattern mode"), ONOFF(randomPatternsMode), 'r');
displayStat(11, XLAT("Yendor Challenge"), ONOFF(yendor::on), 'y');
#ifndef ANDROID
displayStat(12, XLAT("pure tactics mode"), ONOFF(tactic::on), 't');
#endif
#ifndef MOBILE
displayStat(13, XLAT("hypersian rug mode"), ONOFF(rug::rugged), 'u');
#endif
displayStat(14, XLAT("heptagonal mode"), ONOFF(purehepta), '7');
displayStat(15, XLAT("Chaos mode"), ONOFF(chaosmode), 'C');
#ifndef MOBILE
displayStat(16, XLAT("paper model creator"), ONOFF(false), 'n');
#endif
displayStat(17, XLAT("conformal/history mode"), ONOFF(conformal::on), 'a');
displayStat(19, XLAT("return to the game"), "", 'v');
}
void handleChangeMode(int sym, int uni) {
char xuni = uni;
if((uni >= 'A' && uni <= 'Z') || (uni >= 1 && uni <= 26)) xuni |= 96;
if(xuni == 'v' || sym == SDLK_F2 || sym == SDLK_ESCAPE) cmode = emNormal;
if(uni == 'c') {
if(tactic::on && gold()) {
addMessage(XLAT("Not available in the pure tactics mode!"));
}
else if(!cheater) {
cheater++;
addMessage(XLAT("You activate your demonic powers!"));
#ifndef MOBILE
addMessage(XLAT("Shift+F, Shift+O, Shift+T, Shift+L, Shift+U, etc."));
#endif
cmode = emNormal;
}
else {
cmode = emNormal;
firstland = princess::challenge ? laPalace : laIce;
restartGame();
}
}
#ifndef MOBILE
if(xuni == 'g') {
cmode = emDraw;
mapeditor::drawcell = cwt.c;
}
#endif
if(xuni == 'e') {
cmode = emPickEuclidean;
}
if(xuni == 't') {
cmode = emTactic;
}
#ifndef MOBILE
if(xuni == 'u')
rug::select();
#endif
if(xuni == 'y') {
if(yendor::everwon)
cmode = emYendor;
else {
cmode = emHelp;
help = yendor::chelp;
lastmode = emChangeMode;
}
}
if(xuni == '7')
restartGame('7');
if(uni == 'a')
cmode = emConformal;
if(uni == 'C') {
if(!chaosmode) {
cmode = emHelp;
help =
"In the Chaos mode, lands change very often, and "
"there are no walls between them. "
"Some lands are incompatible with this."
"\n\nYou need to reach Crossroads IV to unlock the Chaos mode.";
lastmode = chaosUnlocked ? emNormal : emChangeMode;
}
if(chaosUnlocked) restartGame('C');
}
if(xuni == 'p') {
if(!princess::everSaved)
addMessage(XLAT("Save %the1 first to unlock this challenge!", moPrincess));
else {
restartGame('p');
cmode = emNormal;
}
}
#ifndef MOBILE
if(xuni == 'm') {
if(tactic::on)
addMessage(XLAT("Not available in the pure tactics mode!"));
else {
cheater++;
cmode = emMapEditor;
lastexplore = turncount;
addMessage(XLAT("You activate your terraforming powers!"));
}
}
#endif
if(xuni == 's')
cmode = emShmupConfig;
if(xuni == 'n')
cmode = emNetgen;
if(xuni == 'h' && !shmup::on) {
if(hardcore && !canmove) { }
else if(hardcore && canmove) { hardcore = false; }
else { hardcore = true; canmove = true; hardcoreAt = turncount; }
if(hardcore)
addMessage("One wrong move, and it is game over!");
else
addMessage("Not so hardcore?");
if(pureHardcore()) cmode = emNormal;
}
if(xuni == 'r') {
firstland = laIce;
restartGame('r');
cmode = emNormal;
}
}
void showCheatMenu() {
displayStat(0, XLAT("gain orb powers"), "", 'F');
displayStat(1, XLAT("summon treasure"), "", 'T');
displayStat(2, XLAT("summon dead orbs"), "", 'D');
displayStat(3, XLAT("lose all treasure"), "", 'J');
displayStat(4, XLAT("gain kills"), "", 'K');
displayStat(5, XLAT("Hyperstone Quest"), "", 'C');
displayStat(6, XLAT("summon orbs"), "", 'O');
displayStat(7, XLAT("gain Orb of Yendor"), "", 'Y');
displayStat(8, XLAT("summon lots of treasure"), "", 'T'-64);
displayStat(9, XLAT("Safety (quick save)"), "", 'S');
displayStat(10, XLAT("Select the land ---"), "", 'L');
displayStat(11, XLAT("--- and teleport there"), "", 'U');
displayStat(12, XLAT("rotate the character"), "", 'Z');
displayStat(13, XLAT("summon a Golem"), "", 'G');
displayStat(14, XLAT("summon Sandworm"), "", 'W');
displayStat(15, XLAT("summon Ivy"), "", 'I');
displayStat(16, XLAT("summon a Monster"), "", 'E');
displayStat(17, XLAT("summon Thumpers"), "", 'H');
displayStat(18, XLAT("summon Bonfire"), "", 'B');
displayStat(19, XLAT("summon Mimics"), "", 'M');
displayStat(20, XLAT("deplete orb powers"), "", 'P');
displayStat(21, XLAT("summon Orb of Yendor"), "", 'Y'-64);
displayStat(22, XLAT("switch ghost timer"), "", 'G'-64);
displayStat(23, XLAT("switch web display"), "", 'W'-64);
displayStat(-2, XLAT("return to the game"), "", ' ');
}
void handleCheatMenu(int uni) {
if(uni != 0) {
applyCheat(uni);
if(uni == 'F' || uni == 'C' || uni == 'O' ||
uni == 'S' || uni == 'U' || uni == 'G' ||
uni == 'W' || uni == 'I' || uni == 'E' ||
uni == 'H' || uni == 'B' || uni == 'M' ||
uni == 'P' || uni == 'Y'-64 || uni == 'G'-64 ||
uni == ' ')
cmode = emNormal;
}
}
void showCustomizeChar() {
displayStatHelp(0, XLAT("Customize character"));
if(shmup::on) shmup::cpid = shmup::cpid_edit % shmup::players;
charstyle& cs = getcs();
displayStat(2, XLAT("character"), csname(cs), 'g');
displayStat(3, XLAT("skin color"), "?", 's');
displayStat(4, XLAT("weapon color"), "?", 'w');
displayStat(5, XLAT("hair color"), "?", 'h');
if(cs.charid >= 1) displayStat(6, XLAT("dress color"), "?", 'd');
if(cs.charid == 3) displayStat(7, XLAT("dress color II"), "?", 'f');
if(!shmup::on) displayStat(8, XLAT("save whom"), XLAT1(minf[moPrincess].name), 'p');
if(numplayers() > 1) displayStat(8, XLAT("player"), its(shmup::cpid+1), 'a');
displayStatHelp(16, XLAT("Shift=random, Ctrl=mix"));
displayStat(19, XLAT("return to the game"), "", 'v');
}
void switchcolor(int& c, unsigned int* cs, int mod) {
int id = 0;
int q = cs[0]; cs++;
for(int i=0; i<q; i++) if(c == (int) cs[i]) id = i;
if(mod == 1)
c = ((rand() % 0x1000000) << 8) | 0xFF;
else if(mod == 2)
c = (gradient(cs[rand() % q] >> 8, cs[rand() % q] >> 8, 0, rand() % 101, 100) << 8) + 0xFF;
else
c = cs[(id+1) % q];
}
void handleCustomizeChar(int sym, int uni, int mod) {
char xuni = uni | 96;
if(shiftmul < -.5) mod = 1;
else if(shiftmul > -.2 && shiftmul < .2) mod = 2;
else mod = 0;
if(shmup::on) shmup::cpid = shmup::cpid_edit % shmup::players;
charstyle& cs = getcs();
if(xuni == 'a') { shmup::cpid_edit++; shmup::cpid_edit %= 60; }
if(xuni == 'g') {
cs.charid++;
if(cs.charid == 2 && !princess::everSaved) cs.charid = 4;
cs.charid %= 8;
}
if(xuni == 'p') vid.samegender = !vid.samegender;
bool cat = cs.charid >= 4;
if(xuni == 's') switchcolor(cs.skincolor, cat ? haircolors : skincolors, mod);
if(xuni == 'h') switchcolor(cs.haircolor, haircolors, mod);
if(xuni == 'w') switchcolor(cs.swordcolor, cat ? eyecolors : swordcolors, mod);
if(xuni == 'd') switchcolor(cs.dresscolor, cat ? haircolors : dresscolors, mod);
if(xuni == 'f') switchcolor(cs.dresscolor2, dresscolors2, mod);
if(xuni == 'v' || sym == SDLK_F2 || sym == SDLK_ESCAPE) cmode = emNormal;
}
int eupage = 0;
int euperpage = 21;
void showEuclideanMenu() {
int s = vid.fsize;
vid.fsize = vid.fsize * 4/5;
displayStatHelp(-8, XLAT("Euclidean mode"));
if(cheater) for(int i=0; i<landtypes; i++) landvisited[i] = true;
for(int i=0; i<landtypes; i++)
if(hiitemsMax(treasureType(eLand(i))) >= 25) landvisited[i] = true;
landvisited[laCrossroads] = true;
landvisited[laIce] = true;
landvisited[laMirror] = true;
landvisited[laPrincessQuest] = princess::everSaved;
landvisited[laWildWest] = true;
// for(int i=2; i<lt; i++) landvisited[i] = true;
for(int i=0; i<euperpage; i++) {
if(euperpage * eupage + i >= LAND_EUC) break;
eLand l = land_euc[euperpage * eupage + i];
if(landvisited[l]) {
char ch;
if(i < 26) ch = 'a' + i;
else ch = 'A' + (i-26);
displayStat(i-6, XLAT1(linf[l].name), "", ch);
}
}
displayStat(euperpage+1-6, XLAT("Return to the hyperbolic world"), "", '0');
displayStat(euperpage+2-6, XLAT("Next page"), "", '-');
displayStatHelp(euperpage+4-6, XLAT("Choose from the lands visited this game."));
#ifdef HAVE_ACHIEVEMENTS
displayStatHelp(euperpage+6-6, XLAT("Scores and achievements are not"));
displayStatHelp(euperpage+7-6, XLAT("saved in the Euclidean mode!"));
#endif
vid.fsize = s;
}
void handleEuclidean(int sym, int uni) {
int lid;
if(uni >= 'a' && uni <= 'z') lid = uni - 'a';
else if(uni >= 'A' && uni <= 'Z') lid = 26 + uni - 'A';
else lid = -1;
if(lid >= 0) lid += euperpage * eupage;
if(uni == '0') {
if(euclid) restartGame('e');
cmode = emNormal;
}
else if(uni == '-') {
eupage++;
if(eupage * euperpage >= LAND_EUC) eupage = 0;
}
else if(lid >= 0 && lid < LAND_EUC) {
euclidland = land_euc[lid];
if(landvisited[euclidland] && euclidland != laOceanWall) {
if(euclid) restartGame();
else restartGame('e');
cmode = emNormal;
}
else euclidland = laIce;
}
else if(uni == '2' || sym == SDLK_F1) {
help =
"If you want to know how much the gameplay is affected by the "
"hyperbolic geometry in HyperRogue, this mode is for you!\n\n"
"You can play an Euclidean version of each of the lands in "
"HyperRogue. Lands which include horocycles (Temple, Caribbean, "
"Whirlpool), infinite trees (Zebra, Emerald), or networks of "
"ultraparallel lines (Crossroads, Vineyard, Palace) cannot be "
"faithfully represented in Euclidean, so yo get more "
"or less simplified versions of them. Choose Crossroads to play a game "
"where many different lands appear.";
cmode = emHelp;
lastmode = emPickEuclidean;
}
else if(uni) cmode = emNormal;
}
void showConfig() {
displayStatHelp(0, XLAT("Configuration:"));
if(cmode == emVisual1) showVisual1(); else showVisual2();
#ifndef MOBILE
displayStatHelp(16, XLAT("use Shift to decrease and Ctrl to fine tune "));
displayStatHelp(17, XLAT("(e.g. Shift+Ctrl+Z)"));
#endif
displayStat(19, XLAT("exit configuration"), "", 'v');
#ifdef ANDROID
displayStat(16, XLAT("settings set here won't be saved"), "", 's');
displayStat(17, XLAT("-- use the Android menu instead"), "", 's');
#else
displayStat(21, XLAT("save the current config"), "", 's');
#endif
}
void handleMenus(int sym, int uni, int mod) {
if(cmode == emOverview) handleOverview(uni);
else if(cmode == emYendor) yendor::handleKey(uni, sym);
else if(cmode == emChangeMode) handleChangeMode(uni, sym);
else if(cmode == emVisual1) handleVisual1(uni, sym);
else if(cmode == emMenu) handleMenuKey(uni, false);
else if(cmode == emCheatMenu) handleCheatMenu(uni);
else if(cmode == emVisual2) handleVisual2(uni, sym);
else if(cmode == emPickEuclidean) handleEuclidean(uni, sym);
else if(cmode == emCustomizeChar) handleCustomizeChar(uni, sym, 0);
else if(cmode == emTactic) tactic::handleKey(uni, sym);
else if(cmode == emConformal) conformal::handleKey(uni, sym);
}
void displayMenus() {
if(cmode == emOverview) showOverview();
if(cmode == emYendor) yendor::showMenu();
if(cmode == emChangeMode) showChangeMode();
if(cmode == emCustomizeChar) showCustomizeChar();
if(cmode == emShmupConfig) shmup::showShmupConfig();
if(cmode == emConformal) conformal::show();
if(cmode == emTactic) tactic::showMenu();
if(cmode == emPickEuclidean) showEuclideanMenu();
if(cmode == emMenu) showMainMenu();
if(cmode == emCheatMenu) showCheatMenu();
if(cmode == emVisual1 || cmode == emVisual2) showConfig();
#ifndef MOBILE
if(cmode == emNetgen) netgen::show();
if(cmode == emRugConfig) rug::show();
if(cmode == emMapEditor) mapeditor::showMapEditor();
if(cmode == emDraw) mapeditor::showDrawEditor();
#endif
#ifndef ANDROID
if(cmode == emScores) showScores();
if(cmode == emPickScores) showPickScores();
#endif
}

View File

@ -1,5 +1,3 @@
// mtrand.cpp, see include file mtrand.h for information
#include "mtrand.h"

702
netgen.cpp Normal file
View File

@ -0,0 +1,702 @@
// HyperRogue paper model generator
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#ifndef MOBILE
namespace netgen {
// We need a two-dimensional vector class for this.
struct vec {
double x, y;
vec(double _x, double _y) : x(_x), y(_y) { }
vec() : x(0), y(0) {}
};
vec& operator += (vec& a, const vec b) { a.x += b.x; a.y += b.y; return a; }
vec& operator -= (vec& a, const vec b) { a.x -= b.x; a.y -= b.y; return a; }
// coordinatewise multiplication and division
vec& operator *= (vec& a, const vec b) { a.x *= b.x; a.y *= b.y; return a; }
vec& operator *= (vec& a, double scalar) { a.x *= scalar; a.y *= scalar; return a; }
vec& operator /= (vec& a, const vec b) { a.x /= b.x; a.y /= b.y; return a; }
vec& operator /= (vec& a, double scalar) { a.x /= scalar; a.y /= scalar; return a; }
vec operator + (vec a, const vec b) { return a+=b; }
vec operator - (vec a, const vec b) { return a-=b; }
vec operator * (vec a, const vec b) { return a*=b; }
vec operator / (vec a, const vec b) { return a/=b; }
vec operator * (vec a, double scalar) { return a*=scalar; }
vec operator * (double scalar, vec a) { return a*=scalar; }
vec operator / (vec a, double scalar) { return a/=scalar; }
vec operator / (double scalar, vec a) { return a/=scalar; }
vec ang(double f) { return vec(cos(f), sin(f)); }
double norm(vec v) { return v.x*v.x+v.y*v.y; }
// the parameters.
bool loaded;
int SCALE, PX, PY, BASE, SX, SY, CELLS, fontsize, created;
double el;
#define MAXCELLS 1000
// All the datatables stored in the net files.
int ct[MAXCELLS];
double vx[MAXCELLS][16];
vec center[MAXCELLS];
double rot[MAXCELLS];
int glued[MAXCELLS];
int nei[MAXCELLS][7];
// auxiliary data
double raylen[MAXCELLS];
double edgist[MAXCELLS];
char patek[MAXCELLS][7];
// data generated by HyperRogue
hyperpoint hcenter[MAXCELLS][8];
// Functions handling the data.
//==============================
// Use HyperRogue to generate the data (ct, vx, nei).
int mode = 0;
void buildVertexInfo(cell *c, transmatrix V) {
if(mode == 1)
for(int ii=0; ii<CELLS; ii++) if(dcal[ii] == c) {
hcenter[ii][7] = V * C0;
if(c->type == 7) {
for(int i=0; i<c->type; i++) {
int hdir = displaydir(c, i) + 6;
transmatrix V2 = V * spin(hdir * M_PI / 42) * xpush(hexf);
hcenter[ii][i] = V2 * C0;
}
}
if(c->type == 6) {
for(int i=0; i<c->type; i++) {
int hdir = displaydir(c, i);
transmatrix V2 =
V * spin(hdir * M_PI / 42) * xpush(crossf) * spin(M_PI*8/7) * xpush(hexf);
hcenter[ii][i] = V2 * C0;
}
}
}
}
void dataFromHR() {
mode = 1;
drawthemap();
mode = 0;
for(int i=0; i<CELLS; i++) {
ct[i] = dcal[i]->type;
for(int k=0; k<8; k++)
vx[i][2*k] = hcenter[i][k][0],
vx[i][2*k+1] = hcenter[i][k][1];
for(int k=0; k<ct[i]; k++) nei[i][k] = -1;
for(int j=0; j<CELLS; j++) {
cell *c1 = dcal[i];
cell *c2 = dcal[j];
for(int k=0; k<c1->type; k++) if(c1->mov[k] == c2)
nei[i][k] = j;
}
}
for(int i=0; i<CELLS; i++) {
center[i] = vec(SX/2, SY/2);
rot[i] = 0;
glued[i] = -1;
for(int e=0; e<ct[i]; e++)
if(nei[i][e] < i && nei[i][e] != -1 && (glued[i] == -1 || nei[i][e] < glued[i])) {
glued[i] = nei[i][e];
}
}
}
void loadData() {
FILE *f = fopen("papermodeldata.txt", "rt");
if(!f) return;
int err = fscanf(f, "%d %d %d %d %d %d %d %lf %d\n\n",
&CELLS, &SX, &SY, &PX, &PY, &SCALE, &BASE, &el, &created);
if(err != 9) { fclose(f); return; }
loaded = true;
if(!created) return;
for(int i=0; i<CELLS; i++) err = fscanf(f, "%d", &ct[i]);
for(int i=0; i<CELLS; i++) for(int j=0; j<16; j++)
err = fscanf(f, "%lf" ,&vx[i][j]);
for(int i=0; i<CELLS; i++)
for(int j=0; j<7; j++) nei[i][j] = -1;
while(true) {
int a, b, c;
err = fscanf(f, "%d%d%d", &a, &b, &c);
if(a < 0) break;
else nei[a][c] = b;
}
for(int i=0; i<CELLS; i++) {
double dx, dy, dr;
int g;
err = fscanf(f, "%lf%lf%lf%d\n", &dx, &dy, &dr, &g);
center[i] = vec(dx, dy);
rot[i] = dr;
glued[i] = g;
}
fclose(f);
}
void saveData() {
// global parameters
FILE *f = fopen("papermodeldata2.txt", "wt");
if(!f) {
addMessage("Could not save the paper model data");
return;
}
fprintf(f, "%d %d %d %d %d %d %d %lf %d\n\n", CELLS, SX, SY, PX, PY, SCALE, BASE, el, created);
// net parameters: cell types
for(int i=0; i<CELLS; i++)
fprintf(f, "%d ", ct[i]);
fprintf(f, "\n");
// net parameters: hcenters
for(int i=0; i<CELLS; i++) {
for(int k=0; k<16; k++)
fprintf(f, "%9.6lf ", vx[i][k]);
fprintf(f, "\n");
}
fprintf(f, "\n\n");
// create netgen
for(int i=0; i<CELLS; i++) for(int j=0; j<CELLS; j++) {
for(int k=0; k<ct[i]; k++) if(nei[i][k] == j)
fprintf(f, "%d %d %d ", i, j, k);
}
fprintf(f, "-1 -1 -1\n\n");
// graphics
for(int i=0; i<CELLS; i++)
fprintf(f, "%12.7lf %12.7lf %10.7lf %d\n",
center[i].x, center[i].y, rot[i], glued[i]
);
fclose(f);
}
// Simple graphical functions
//============================
void blackline(vec v1, vec v2, int col = 0x000000FF) {
#ifdef GFX
aalineColor(s, int(v1.x), int(v1.y), int(v2.x), int(v2.y), col);
#endif
}
void drawtriangle(vec v1, vec v2, vec v3, int col) {
#ifdef GFX
polyx[0] = int(v1.x);
polyx[1] = int(v2.x);
polyx[2] = int(v3.x);
polyy[0] = int(v1.y);
polyy[1] = int(v2.y);
polyy[2] = int(v3.y);
filledPolygonColor(s, polyx, polyy, 3, col);
#endif
}
void blackcircle(vec v, int r, int col = 0x000000FF) {
#ifdef GFX
aacircleColor(s, int(v.x), int(v.y), r, col);
#endif
}
void blacktext(vec v, char c) {
char str[2]; str[0] = c; str[1] = 0;
int tsize = int(el * 12/27);
displaystr(int(v.x), int(v.y), 0, tsize, str, 0, 8);
}
hyperpoint hvec(int i, int e) {
return hpxy(vx[i][2*e], vx[i][2*e+1]);
}
bool wellspread(double d1, double d2, double d3, int &co) {
int id1 = int(d1);
int id2 = int(d2);
int id3 = int(d3);
co = min(min(id1,id2),id3);
return (id1 <= co+1 && id2 <= co+1 && id3 <= co+1);
}
SDL_Surface *net, *hqsurface;
int& hqpixel(hyperpoint h) {
int hx, hy, hs;
getcoord(h, hx, hy, hs);
return qpixel(hqsurface, hx, hy);
}
void copyhypertriangle(
vec g1, vec g2, vec g3,
hyperpoint h1, hyperpoint h2, hyperpoint h3) {
int ix, iy;
if(wellspread(g1.x,g2.x,g3.x,ix) && wellspread(g1.y,g2.y,g3.y,iy))
qpixel(net,ix,iy) = hqpixel(h1);
else {
vec g4 = (g2+g3)/2;
vec g5 = (g3+g1)/2;
vec g6 = (g1+g2)/2;
hyperpoint h4 = mid(h2,h3);
hyperpoint h5 = mid(h3,h1);
hyperpoint h6 = mid(h1,h2);
copyhypertriangle(g1,g5,g6, h1,h5,h6);
copyhypertriangle(g5,g3,g4, h5,h3,h4);
copyhypertriangle(g6,g4,g2, h6,h4,h2);
copyhypertriangle(g4,g6,g5, h4,h6,h5);
}
}
void setRaylen() {
for(int i=0; i<CELLS; i++) {
raylen[i] = el / sin(M_PI / ct[i]);
edgist[i] = raylen[i] * cos(M_PI / ct[i]);
}
}
// draw the model
void createPapermodel() {
#ifndef GFX
addMessage(XLAT("High quality shots not available on this platform"));
return;
#endif
loadData();
SDL_Surface *sav = s;
s = hqsurface = SDL_CreateRGBSurface(SDL_SWSURFACE,BASE,BASE,32,0,0,0,0);
videopar vid2 = vid;
vid.xres = vid.yres = 2000; vid.scale = 0.99; vid.usingGL = false;
int sch = cheater; cheater = 0;
calcparam();
mode = 2;
darken = 0;
SDL_FillRect(s, NULL, 0);
drawfullmap();
mode = 0;
/* for(int i=0; i<CELLS; i++) {
int t = ct[i];
for(int e=0; e<t; e++)
drawline(hvec(i,e), hvec(i,(e+1)%t), 0x80808080);
for(int e=0; e<7; e++)
drawline(hvec(i,e), hvec(i,7), 0x80808080);
} */
s = net = SDL_CreateRGBSurface(SDL_SWSURFACE,SX*SCALE,SY*SCALE,32,0,0,0,0);
SDL_FillRect(net, NULL, 0xFFFFFF);
int pateks = 0;
int zeroi = nei[0][0];
int zeroe = 0;
for(int e=0; e<6; e++) if(nei[zeroi][e] == 0) zeroe = e;
el *= SCALE;
setRaylen();
for(int faza=0; faza<2; faza++) for(int i=0; i<CELLS; i++) {
int t = ct[i];
printf("faza %d cell %d\n", faza, i);
for(int e=0; e<t; e++) {
vec v1 = center[i] * SCALE + raylen[i] * ang(rot[i] + 2*M_PI*e/t);
vec v2 = center[i] * SCALE + raylen[i] * ang(rot[i] + 2*M_PI*(e+1)/t);
vec v3 = (v1+v2)/2;
if(faza == 1) blackline(v1, v2);
int ofs = t == 7 ? 0 : 5;
// 0,2,0 ~ 2,0,0
if(0) if((i==0 && e == 0) || (i == zeroi && e == zeroe)) {
for(int ofs=0; ofs<t; ofs++) {
printf("OFS %d: %s", ofs, display(hvec(i, (e+ofs)%t)));
printf(" %s\n", display(hvec(i, (e+1+ofs)%t)));
}
}
if(faza == 0) copyhypertriangle(
center[i] * SCALE, v1, v2,
hvec(i,7), hvec(i, (e+ofs)%t), hvec(i, (e+1+ofs)%t)
);
if(faza == 1)
if(nei[i][e] != -1 && nei[i][e] != glued[i] && glued[nei[i][e]] != i) {
vec vd = v2-v1;
swap(vd.x, vd.y); vd.x = -vd.x;
double factor = -sqrt(3)/6;
vd.x *= factor;
vd.y *= factor;
vec v4 = v3 + vd;
vec v5 = v3 + vd/2;
if(!patek[i][e]) {
int i2 = nei[i][e];
for(int e2=0; e2<ct[nei[i][e]]; e2++) if(nei[i2][e2] == i)
patek[i][e] = patek[i2][e2] =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
"!@#$%^&*+=~:;<>?/|\"., [{(\\]})" [(pateks++) % 85];
}
int col = 0xFFFFFFFF;
int p = patek[i][e];
col -= 0x8000 * (p&1); p /= 2;
col -= 0x800000 * (p&1); p /= 2;
col -= 0x80000000 * (p&1); p /= 2;
col -= 0x4000 * (p&1); p /= 2;
col -= 0x400000 * (p&1); p /= 2;
col -= 0x40000000 * (p&1); p /= 2;
col -= 0x2000 * (p&1); p /= 2;
col -= 0x200000 * (p&1); p /= 2;
col -= 0x20000000 * (p&1); p /= 2;
drawtriangle(v1,v2,v4, col);
blacktext(v5, patek[i][e]);
blackline(v1, v4);
blackline(v2, v4);
}
}
}
printf("pateks = %d\n", pateks);
IMAGESAVE(net, "papermodel-all" IMAGEEXT);
IMAGESAVE(hqsurface, "papermodel-source" IMAGEEXT);
int qx = SX*SCALE/PX;
int qy = SY*SCALE/PY;
SDL_Surface *quarter = SDL_CreateRGBSurface(SDL_SWSURFACE,qx,qy,32,0,0,0,0);
for(int iy=0; iy<PY; iy++)
for(int ix=0; ix<PX; ix++) {
for(int y=0; y<qy; y++) for(int x=0; x<qx; x++)
qpixel(quarter,x,y) = qpixel(net, x+qx*ix, y+qy*iy);
char buf[64];
sprintf(buf, "papermodel-page%d%d" IMAGEEXT, iy, ix);
IMAGESAVE(quarter, buf);
}
SDL_FreeSurface(net);
SDL_FreeSurface(hqsurface);
SDL_FreeSurface(quarter);
s = sav; vid = vid2; cheater = sch;
}
vec mousepos, rel;
int bei = 0, bee = 0, whichcell = 0;
double cedist;
bool dragging = false;
int glueroot(int i) {
if(glued[i] == -1) return i;
return glueroot(glued[i]);
}
void clicked(int x, int y, int b) {
mousepos = vec(x, y);
if(b == 1)
rel = center[glueroot(whichcell)] - mousepos,
dragging = true;
if(b == 17)
dragging = false;
if(b == 32 && dragging)
center[glueroot(whichcell)] = rel + mousepos;
}
void applyGlue(int i) {
int j = glued[i];
int it = ct[i];
int jt = ct[j];
int ie = 0, je = 0;
for(int e=0; e<it; e++) if(nei[i][e] == j) ie = e;
for(int e=0; e<jt; e++) if(nei[j][e] == i) je = e;
rot[i] = rot[j] + 2*M_PI*(je+.5)/jt - 2*M_PI*(ie+.5)/it + M_PI;
center[i] =
center[j] +
(edgist[i]+edgist[j]) * ang(rot[j] + 2*M_PI*(je+.5)/jt);
}
void displaynets() {
SDL_LockSurface(s);
setRaylen();
for(int uy=SY-1; uy>=0; uy--)
for(int ux=SX-1; ux>=0; ux--) {
qpixel(s, ux, uy) = 0;
}
for(int y=1; y<PY; y++)
blackline(vec(0,SY*y/PY), vec(SX,SY*y/PY), 0x404080FF);
for(int x=1; x<PX; x++)
blackline(vec(SX*x/PX,0), vec(SX*x/PX,SY), 0x404080FF);
for(int i=0; i<CELLS; i++) {
if(norm(center[i]-mousepos) < norm(center[whichcell]-mousepos))
whichcell = i;
int t = ct[i];
if(i == whichcell)
blackcircle(center[i], 10, 0x40FF40FF);
if(i == bei || i == nei[bei][bee])
blackcircle(center[i], 5, 0x40FF40FF);
if(glued[i] == -1)
blackcircle(center[i], 7, 0xFF4040FF);
if(glued[i] != -1)
applyGlue(i);
for(int e=0; e<t; e++) {
vec v1 = center[i] + raylen[i] * ang(rot[i] + 2*M_PI*e/t);
vec v2 = center[i] + raylen[i] * ang(rot[i] + 2*M_PI*(e+1)/t);
vec v3 = (v1+v2)/2;
if(nei[i][e] >= 0 && !dragging) {
if(norm(v3-mousepos) < cedist) bei = i, bee = e;
if(i == bei && e == bee) cedist = norm(v3-mousepos);
}
int col =
i == bei && e == bee ? 0x40FF40FF:
i == nei[bei][bee] && nei[i][e] == bei ? 0x40FF40FF :
nei[i][e] == glued[i] ? 0x303030FF :
glued[nei[i][e]] == i ? 0x303030FF :
nei[i][e] >= 0 ? 0xC0C0C0FF :
0x808080FF;
blackline(v1, v2, col);
if(nei[i][e] != -1 && nei[i][e] != glued[i] && glued[nei[i][e]] != i) {
vec vd = v2-v1;
swap(vd.x, vd.y); vd.x = -vd.x;
double factor = -sqrt(3)/6;
vd.x *= factor; vd.y *= factor;
vec v4 = v3 + vd;
blackline(v1, v4, 0xFFC0C0C0);
blackline(v2, v4, 0xFFC0C0C0);
}
}
}
SDL_UnlockSurface(s);
SDL_UpdateRect(s, 0, 0, 0, 0);
}
double rs, rz;
void addglue() {
int i = bei;
int j = nei[bei][bee];
if(glued[i] == j)
glued[i] = -1;
else if(glued[j] == i)
glued[j] = -1;
else if(glueroot(i) == glueroot(j))
;
else if(glued[j] == -1)
glued[j] = i;
}
int nti;
void smooth() {
int ti = SDL_GetTicks();
rot[whichcell] += rs * (nti - ti) / 1000.0;
el += rz * (nti - ti) / 1000.0;
nti = ti;
}
void netgen_loop() {
nti = SDL_GetTicks();
while(true) {
smooth();
displaynets();
SDL_Event event;
while(SDL_PollEvent(&event)) switch (event.type) {
case SDL_QUIT:
exit(1);
return;
case SDL_MOUSEBUTTONDOWN: {
clicked(event.button.x, event.button.y, event.button.button);
break;
}
case SDL_MOUSEBUTTONUP: {
clicked(event.button.x, event.button.y, 16+event.button.button);
break;
}
case SDL_MOUSEMOTION: {
clicked(event.motion.x, event.motion.y, 32);
break;
}
case SDL_KEYDOWN: {
int key = event.key.keysym.sym;
int uni = event.key.keysym.unicode;
if(uni == 'q' || key == SDLK_ESCAPE || key == SDLK_F10)
return;
if(key == SDLK_PAGEUP) rs = 3;
if(key == SDLK_PAGEDOWN) rs = -3;
if(uni == 'z') rz = 1;
if(uni == 'x') rz = -1;
if(uni == 'g') addglue();
break;
}
case SDL_KEYUP: {
rs = 0;
rz = 0;
break;
}
}
}
}
void designNet() {
s = SDL_SetVideoMode(SX, SY, 32, 0);
netgen_loop();
saveData();
setvideomode();
}
void show() {
if(true) {
for(int i=0; i<CELLS; i++) {
int t = ct[i];
int ofs = t == 7 ? 0 : 5;
for(int e=0; e<t; e++) {
int col =
nei[i][e] == glued[i] && glued[i] >= 0 ? 0x303030 :
nei[i][e] >= 0 && glued[nei[i][e]] == i ? 0x303030 :
nei[i][e] >= 0 ? 0x808080 :
0xC0C0C0;
drawline(hvec(i, (e+ofs)%t), hvec(i, (e+1+ofs)%t), (col << 8) + 0xFF);
}
}
}
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');
}
}
void handleKey(int uni, int sym) {
if(!loaded) {
loadData();
if(!loaded) {
addMessage(XLAT("Failed to load the file 'papermodeldata.txt'"));
cmode = emNormal;
return;
}
if(!created) {
View = Id;
if(lcenterover) viewctr.h = lcenterover->master;
else viewctr.h = cwt.c->master;
playermoved = false;
dataFromHR();
designNet();
created = 1;
return;
}
}
if(mode == 2 && uni != 0) {
mode = 0;
return;
}
if(uni == 's') {
View = Id;
if(lcenterover) viewctr.h = lcenterover->master;
else viewctr.h = cwt.c->master;
playermoved = false;
}
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)
cmode = emNormal;
}
}
#endif

949
orbs.cpp Normal file
View File

@ -0,0 +1,949 @@
// Hyperbolic Rogue
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
// Orb-related routines
bool markOrb(eItem it) {
if(!items[it]) return false;
orbused[it] = true;
return true;
}
bool markEmpathy(eItem it) {
if(!items[itOrbEmpathy]) return false;
if(!markOrb(it)) return false;
markOrb(itOrbEmpathy);
return true;
}
bool markOrb2(eItem it) {
if(!items[it]) return false;
orbused[it] = true;
return items[it] > 1;
}
int fixpower(int qty) {
if(markOrb(itOrbEnergy)) qty = (qty+1)/2;
return qty;
}
void useupOrb(eItem it, int qty) {
items[it] -= fixpower(qty);
if(items[it] < 0) items[it] = 0;
}
void drainOrb(eItem it, int target) {
if(items[it] > target) useupOrb(it, items[it] - target);
}
void empathyMove(cell *c, cell *cto, int dir) {
if(!items[itOrbEmpathy]) return;
if(items[itOrbFire]) {
invismove = false;
if(makeflame(c, 10, false)) markEmpathy(itOrbFire);
}
if(items[itOrbDigging]) {
if(dir != STRONGWIND && earthMove(c, dir))
markEmpathy(itOrbDigging), invismove = false;
}
if(items[itOrbWinter] && isIcyLand(c) && c->wall == waNone) {
invismove = false;
c->wall = waIcewall;
markEmpathy(itOrbWinter);
}
}
bool reduceOrbPower(eItem it, int cap) {
if(items[it] && (lastorbused[it] || (it == itOrbShield && items[it]>3) || !markOrb(itOrbPreserve))) {
items[it] -= numplayers();
if(isHaunted(cwt.c->land)) survivalist = false;
if(items[it] < 0) items[it] = 0;
if(items[it] > cap) items[it] = cap;
if(items[it] == 0 && it == itOrbLove)
princess::bringBack();
return true;
}
if(items[it] > cap) items[it] = cap;
return false;
}
void reduceOrbPowerAlways(eItem it) {
if(items[it]) {
items[it] -= numplayers();
if(items[it] < 0) items[it] = 0;
}
}
void reduceOrbPowers() {
if(getMount()) 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);
if(invismove && !invisfish) markOrb(itOrbInvis);
reduceOrbPower(itOrbLightning, 777);
reduceOrbPower(itOrbSpeed, 67);
reduceOrbPower(itOrbShield, 77);
reduceOrbPower(itOrbShell, 150);
reduceOrbPower(itOrbFlash, 777);
reduceOrbPower(itOrbWinter, 77);
reduceOrbPower(itOrbFire, 77);
reduceOrbPower(itOrbIllusion, 111);
reduceOrbPower(itOrbDragon, 111);
reduceOrbPower(itOrbPsi, 111);
reduceOrbPower(itOrbInvis, 77);
reduceOrbPower(itOrbGhost, 77);
reduceOrbPower(itOrbDigging, 100);
reduceOrbPower(itOrbTeleport, 200);
reduceOrbPower(itOrbTelekinesis, 150);
reduceOrbPowerAlways(itOrbSafety);
reduceOrbPower(itOrbThorns, 150);
reduceOrbPower(itOrbWater, 150);
reduceOrbPower(itOrbAir, 150);
reduceOrbPower(itOrbFrog, 77);
reduceOrbPower(itOrbDiscord, 67);
reduceOrbPower(itOrbSummon, 333);
reduceOrbPower(itOrbMatter, 333);
reduceOrbPower(itOrbFish, 77);
if(!items[itSavedPrincess]) items[itOrbLove] = 0;
reduceOrbPower(itOrbLove, 777);
reduceOrbPower(itOrbStunning, 100);
reduceOrbPower(itOrbLuck, 333);
reduceOrbPower(itOrbUndeath, 77);
reduceOrbPower(itOrbFreedom, 77);
reduceOrbPower(itOrbEmpathy, 77);
markOrb(itOrb37); reduceOrbPower(itOrb37, 333);
reduceOrbPower(itOrbSkunk, 77);
reduceOrbPower(itOrbEnergy, 77);
reduceOrbPower(itOrbDomination, 120);
if(cwt.c->land != laWildWest)
reduceOrbPower(itRevolver, 6);
whirlwind::calcdirs(cwt.c);
items[itStrongWind] = !items[itOrbGhost] && whirlwind::qdirs == 1 && !euclid;
}
void flashAlchemist(cell *c) {
if(isAlch(c)) {
if(isAlch(cwt.c))
c->wall = cwt.c->wall;
else
c->wall = eWall(c->wall ^ waFloorB ^ waFloorA);
}
}
void flashCell(cell *c, bool msg) {
flashAlchemist(c);
if(msg && c->monst && !isWorm(c) && c->monst != moShadow)
addMessage(XLAT("%The1 is destroyed by the Flash.", c->monst));
killWithMessage(c, false);
if(isIcyLand(c))
HEAT(c) += 2;
if(c->land == laDryForest)
c->landparam += 2;
if(c->wall == waCavewall) c->wall = waCavefloor;
if(c->wall == waDeadTroll) c->wall = waCavefloor;
if(c->wall == waDeadTroll2) c->wall = waNone;
if(c->wall == waDeadfloor2) c->wall = waDeadfloor;
if(c->wall == waGargoyleFloor) c->wall = waChasm;
if(c->wall == waGargoyleBridge) placeWater(c, c);
if(c->wall == waGargoyle) c->wall = waNone;
if(c->wall == waPlatform) c->wall = waNone;
if(c->wall == waStone) c->wall = waNone;
if(c->wall == waRubble) c->wall = waNone;
if(c->wall == waDeadwall) c->wall = waDeadfloor2;
if(c->wall == waGiantRug) c->wall = waNone;
if(c->wall == waMirror) c->wall = waNone;
if(c->wall == waCloud) c->wall = waNone;
if(c->wall == waDune) c->wall = waNone;
if(c->wall == waSaloon) c->wall = waNone;
if(c->wall == waSandstone) c->wall = waNone;
if(c->wall == waAncientGrave) c->wall = waNone;
if(c->wall == waFreshGrave) c->wall = waNone;
if(c->wall == waColumn) c->wall = waNone;
if(c->wall == waGlass) c->wall = waNone;
if(c->wall == waBigTree || c->wall == waSmallTree) c->wall = waNone;
if(c->wall == waBigStatue) c->wall = waNone;
if(c->wall == waCTree) c->wall = waCIsland2;
if(c->wall == waPalace) c->wall = waRubble;
if(c->wall == waRose) c->wall = waNone;
if(c->wall == waOpenGate || c->wall == waClosedGate) {
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);
}
if(c->wall == waRed1) c->wall = waNone;
else if(c->wall == waRed2) c->wall = waRed1;
else if(c->wall == waRed3) c->wall = waRed2;
if(hasTimeout(c) && c->wparam < 77) c->wparam = 77;
if(isActivable(c))
activateActiv(c, false);
}
void activateFlashFrom(cell *cf) {
drawFlash(cf);
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);
}
}
}
bool distanceBound(cell *c1, cell *c2, int d) {
if(!c1 || !c2) return false;
if(d == 0) return c1 == c2;
for(int i=0; i<c2->type; i++)
if(distanceBound(c1, c2->mov[i], d-1)) return true;
return false;
}
void checkFreedom(cell *cf) {
sval++;
static vector<cell*> avcells;
avcells.clear();
avcells.push_back(cf);
cf->aitmp = sval;
for(int i=0; i<size(avcells); i++) {
cell *c = avcells[i];
if(c->cpdist >= 5) return;
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
// todo leader
if(!passable(c2, c, P_ISPLAYER | P_MIRROR | P_LEADER)) continue;
if(eq(c2->aitmp, sval)) continue;
bool monsterhere = false;
for(int j=0; j<c2->type; j++) {
cell *c3 = c2->mov[j];
if(c3 && c3->monst && !isFriendly(c3))
monsterhere = true;
}
if(!monsterhere) {
c2->aitmp = sval;
avcells.push_back(c2);
}
}
}
addMessage(XLAT("Your %1 activates!", itOrbFreedom));
drainOrb(itOrbFreedom);
drawBigFlash(cf);
for(int i=0; i<size(dcal); i++) {
cell *c = dcal[i];
if(c == cf) continue;
if(c->cpdist > 5) break;
flashCell(c, true);
}
}
void activateFlash() {
int tk = tkills();
drawFlash(cwt.c);
addMessage(XLAT("You activate the Flash spell!"));
drainOrb(itOrbFlash);
for(int i=0; i<size(dcal); i++) {
cell *c = dcal[i];
if(c->cpdist > 2) break;
flashCell(c, false);
}
achievement_count("FLASH", tkills(), tk);
}
bool barrierAt(cellwalker& c, int d) {
if(d >= 7) return true;
if(d <= -7) return true;
d = c.spin + d + 42;
d%=c.c->type;
if(!c.c->mov[d]) return true;
// 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;
}
void killAdjacentSharks(cell *c) {
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
if(c2 && isShark(c2->monst)) {
c2->ligon = true;
killMonster(c2);
killAdjacentSharks(c2);
}
}
}
void castLightningBolt(cellwalker lig) {
int bnc = 0;
while(true) {
// printf("at: %p i=%d d=%d\n", lig.c, i, lig.spin);
killAdjacentSharks(lig.c);
if(lig.c->mov[lig.spin] == NULL) break;
cwstep(lig);
cell *c = lig.c;
flashAlchemist(c);
if(c->monst == moMetalBeast2 && !c->item) c->item = itFulgurite;
killWithMessage(c, false);
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 == waDeadfloor2)c->wall = waDeadfloor;
if(c->wall == waRubble) c->wall = waNone;
if(c->wall == waDeadwall) c->wall = waDeadfloor2, brk = true;
if(c->wall == waGlass) c->wall = waNone, spin = true;
if(c->wall == waDune) c->wall = waNone, brk = true;
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 == 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 == waGrounded) brk = true;
if(c->wall == waFan) spin = true;
if(c->wall == waMetal) c->wall = waCharged, brk = true;
if(c->wall == waSandstone) c->wall = waNone, c->item = itFulgurite, brk = true;
if(c->wall == waCharged && first) {
for(int i=0; i<c->type; i++)
// do not do strange things in horocyclic spires
if(c->mov[i] && c->mov[i]->wall != waCharged) {
cellwalker lig2(c, i);
castLightningBolt(lig2);
}
brk = true;
}
if(c->wall == waBoat && c != cwt.c) c->wall = waSea, spin = true;
if(c->wall == waStrandedBoat && c !=cwt.c) c->wall = waNone, spin = true;
if((c->wall == waNone || c->wall == waSea) && c->land == laLivefjord)
c->wall = eWall(c->wall ^ waSea ^ waNone);
if(c->wall == waRed1) c->wall = waNone;
if(c->wall == waRed2) c->wall = waRed1;
if(c->wall == waRed3) c->wall = waRed2, brk = true;
if(isActivable(c)) activateActiv(c, false);
if(c->wall == waBigTree || c->wall == waSmallTree || c->wall == waVinePlant ||
c->wall == waSaloon) {
makeflame(c, 4, false);
brk = true;
}
if(c->wall == waCTree) makeflame(c, 12, false);
if(c->wall == waRose) makeflame(c, 60, false);
if(cellHalfvine(c) && c->mov[lig.spin] && c->wall == c->mov[lig.spin]->wall) {
destroyHalfvine(c, waPartialFire, 4);
brk = true;
}
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) {
int left = -1;
int right = 1;
while(barrierAt(lig, left)) left--;
while(barrierAt(lig, right)) right++;
cwspin(lig, -(right + left));
bnc++; if(bnc > 10) break;
}
else {
cwspin(lig, 3);
if(c->type == 7) cwspin(lig, hrand(2));
}
if(c->wall == waCloud) {
c->wall = waNone;
mirror::createMirages(c, lig.spin, moLightningBolt);
}
if(c->wall == waMirror) {
c->wall = waNone;
mirror::createMirrors(c, lig.spin, moLightningBolt);
break;
}
}
}
void activateLightning() {
int tk = tkills();
drawLightning();
addMessage(XLAT("You activate the Lightning spell!"));
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));
elec::afterOrb = true;
elec::act();
elec::afterOrb = false;
achievement_count("LIGHTNING", tkills(), tk);
}
// roCheck: return orb type if successful, 0 otherwise
// roMouse/roKeyboard:
// return orb type if successful, eItem(-1) if do nothing, 0 otherwise
bool haveRangedTarget() {
if(!haveRangedOrb())
return false;
for(int i=0; i<size(dcal); i++) {
cell *c = dcal[i];
if(targetRangedOrb(c, roCheck)) {
return true;
}
}
return false;
}
void teleportTo(cell *dest) {
if(dest->monst) {
cwt.c->monst = dest->monst;
dest->monst = moNone;
}
movecost(cwt.c, dest);
playerMoveEffects(cwt.c, dest);
cwt.c = dest; cwt.spin = hrand(dest->type); flipplayer = !!(hrand(2));
drainOrb(itOrbTeleport);
addMessage(XLAT("You teleport to a new location!"));
mirror::destroy();
for(int i=9; i>=0; i--)
setdist(cwt.c, i, NULL);
bfs();
if(shmup::on)
shmup::teleported();
else
checkmove();
}
void jumpTo(cell *dest, eItem byWhat) {
movecost(cwt.c, dest);
cell *c1 = cwt.c;
cwt.c = dest;
countLocalTreasure();
playerMoveEffects(c1, dest);
if(cwt.c->item != itOrbYendor && cwt.c->item != itHolyGrail)
collectItem(cwt.c, true);
if(byWhat == itOrbFrog) {
useupOrb(itOrbFrog, 5);
addMessage(XLAT("You jump!"));
}
mirror::destroy();
for(int i=9; i>=0; i--)
setdist(cwt.c, i, NULL);
createNoise(1);
if(shmup::on)
shmup::teleported();
else
monstersTurn();
}
void telekinesis(cell *dest) {
int cost = dest->cpdist * dest->cpdist;
if(dest->land == laAlchemist && isAlchAny(dest) && isAlchAny(cwt.c))
dest->wall = cwt.c->wall;
if(dest->land == laPower && cwt.c->land != laPower && dest->item != itOrbFire && dest->item != itOrbLife) {
if(itemclass(dest->item) != IC_ORB)
items[dest->item] ++;
else
items[dest->item] += 2;
addMessage(XLAT("The Orb loses its power as it leaves the Land of Power!"));
dest->item = itNone;
}
if(dest->wall == waGlass) {
drainOrb(itOrbTelekinesis);
addMessage(XLAT("Your power is drained by %the1!", dest->wall));
}
moveItem(dest, cwt.c, true);
collectItem(cwt.c, true);
useupOrb(itOrbTelekinesis, cost);
createNoise(3);
bfs();
if(!shmup::on) checkmove();
}
eMonster summonedAt(cell *dest) {
if(dest->monst) return moNone;
if(dest->wall == waVineHalfA || dest->wall == waVineHalfB || dest->wall == waVinePlant)
return moVineSpirit;
if(dest->wall == waCTree)
return moParrot;
if(dest->wall == waLake)
return moGreaterShark;
if(dest->wall == waAncientGrave || dest->wall == waFreshGrave)
return moGhost;
if(dest->wall == waClosePlate || dest->wall == waOpenPlate)
return moPalace;
if(dest->wall == waFloorA || dest->wall == waFloorB)
return moSlime;
if(dest->wall == waFloorC || dest->wall == waFloorD)
return moRatling;
if(dest->wall == waCavefloor)
return moTroll;
if(dest->wall == waDeadfloor)
return moEarthElemental;
if(dest->wall == waDeadfloor2)
return moMiner;
if(dest->wall == waMineOpen || dest->wall == waMineMine || dest->wall == waMineUnknown)
return moBomberbird;
if(dest->wall == waTrapdoor)
return dest->land == laPalace ? moFatGuard : moOrangeDog;
if(dest->wall == waSea)
return
isElemental(dest->land) ? moWaterElemental :
dest->land == laLivefjord ? moViking :
dest->land == laGridCoast ? moRatling :
moPirate;
if(dest->wall == waChasm)
return moAirElemental;
if(isFire(dest))
return moFireElemental;
if(dest->wall == waCavewall || dest->wall == waDeadwall)
return moSeep;
if(dest->wall == waRed1 || dest->wall == waRed2 || dest->wall == waRed3)
return moRedTroll;
if(dest->wall == waFrozenLake)
return moFireElemental;
if(dest->wall == waCIsland || dest->wall == waCIsland2)
return moWaterElemental;
if(dest->wall == waRubble || dest->wall == waGargoyleFloor || dest->wall == waGargoyleBridge || dest->wall == waLadder)
return moGargoyle;
if(dest->wall == waStrandedBoat)
return moWaterElemental;
if(dest->wall == waBoat)
return moAirElemental;
if(dest->wall == waStone)
return moEarthElemental;
if(dest->wall == waGiantRug)
return moVizier;
if(dest->wall == waNone) {
if(dest->land == laIce) return moFireElemental;
if(dest->land == laDesert) return moEarthElemental;
if(dest->land == laJungle) return moWaterElemental;
if(dest->land == laGraveyard) return moZombie;
if(dest->land == laRlyeh || dest->land == laTemple) return moPyroCultist;
if(dest->land == laHell) return moWaterElemental;
if(dest->land == laPower) return moWitchFire;
if(dest->land == laWineyard) return moVineBeast;
if(dest->land == laEmerald) return moMiner;
if(dest->land == laHive) return dest->type == 7 ? moBug1 : moBug0;
if(dest->land == laRedRock) return moRedTroll;
if(dest->land == laOcean) return moEarthElemental;
if(dest->land == laDryForest) return moFireFairy;
if(dest->land == laLivefjord) return moFjordTroll;
if(dest->land == laStorms) return moStormTroll;
if(dest->land == laOvergrown) return moForestTroll;
if(dest->land == laIvoryTower) return moAirElemental;
if(dest->land == laEndorian) return moAirElemental;
if(dest->land == laEAir) return moAirElemental;
if(dest->land == laEWater) return moWaterElemental;
if(dest->land == laEEarth) return moEarthElemental;
if(dest->land == laEFire) return moFireElemental;
if(dest->land == laMotion) return moRunDog;
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 == laRose) return moRoseLady;
if(dest->land == laDragon) return moFireElemental;
if(dest->land == laTortoise) return moTortoise;
if(isHaunted(dest->land)) return moGhost;
}
return moNone;
}
void summonAt(cell *dest) {
dest->monst = summonedAt(dest);
dest->stuntime = 3;
if(dest->monst == moPirate || dest->monst == moViking || (dest->monst == moRatling && dest->wall == waSea))
dest->wall = waBoat;
if(dest->wall == waStrandedBoat)
dest->wall = waBoat;
else if(dest->monst == moWaterElemental)
placeWater(dest, dest);
if(dest->wall == waStone)
dest->wall = waNone;
if(dest->monst == moFireElemental && isFire(dest))
dest->wall = waNone;
if(dest->monst == moTortoise)
tortoise::emap[dest] = dest;
addMessage(XLAT("You summon %the1!", dest->monst));
moveEffect(dest, dest, dest->monst);
if(hasHitpoints(dest->monst))
dest->hitpoints = palaceHP();
useupOrb(itOrbSummon, 20);
createNoise(2);
bfs();
checkmove();
}
bool tempWallPossibleAt(cell *dest) {
if(dest->monst || (dest->item && !itemHidden(dest))) return false;
return dest->wall == waChasm || isWatery(dest) || dest->wall == waNone;
}
void tempWallAt(cell *dest) {
if(dest->wall == waChasm)
dest->wall = waTempFloor;
else if(dest->wall == waNone)
dest->wall = waTempWall;
else if(isWatery(dest))
dest->wall = waTempBridge;
int len = (items[itOrbMatter]+1) / 2;
dest->wparam = len;
useupOrb(itOrbMatter, len);
dest->item = itNone; // underwater items are destroyed by this
createNoise(2);
bfs();
checkmove();
}
void psi_attack(cell *dest) {
if(isNonliving(dest->monst))
addMessage(XLAT("You destroy %the1 with a mental blast!", dest->monst));
else if(isDragon(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);
useupOrb(itOrbPsi, 30);
createNoise(2);
bfs();
checkmove();
}
void gun_attack(cell *dest) {
addMessage(XLAT("You shoot %the1!", dest->monst));
killWithMessage(dest, false);
items[itRevolver] --;
bfs();
checkmove();
createNoise(5);
monstersTurn();
}
void stun_attack(cell *dest) {
addMessage(XLAT("You stun %the1!", dest->monst));
dest->stuntime += 5;
if(isBird(dest->monst)) {
moveEffect(dest, dest, moDeadBird);
if(cellUnstableOrChasm(dest)) dest->wall = waChasm;
if(isWatery(dest) || dest->wall == waChasm || isFire(dest))
killMonster(dest);
}
useupOrb(itOrbStunning, 10);
createNoise(3);
bfs();
checkmove();
}
void placeIllusion(cell *c) {
c->monst = moIllusion;
useupOrb(itOrbIllusion, 5);
addMessage(XLAT("You create an Illusion!"));
bfs();
checkmove();
}
void blowoff(cell *cf, cell *ct) {
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;
}
items[itOrbAir]--;
createNoise(2);
bfs();
checkmove();
}
void useOrbOfDragon(cell *c) {
makeflame(c, 20, false);
addMessage(XLAT("You throw fire!"));
useupOrb(itOrbDragon, 5);
createNoise(3);
bfs();
checkmove();
}
eItem targetRangedOrb(cell *c, orbAction a) {
if(!haveRangedOrb()) return itNone;
if(rosedist(cwt.c) == 1) {
int r = rosemap[cwt.c];
int r2 = rosemap[c];
if(r2 <= r) {
if(a == roKeyboard || a == roMouseForce )
addMessage(XLAT("Those roses smell too nicely. You can only target cells closer to them!"));
return itNone;
}
}
// (-2) shmup variants
eItem shmupEffect = shmup::targetRangedOrb(a);
if(shmupEffect) return shmupEffect;
// (-1) distance
if(c == cwt.c || isNeighbor(cwt.c, c)) {
if(a == roKeyboard || a == roMouseForce )
addMessage(XLAT("You cannot target that close!"));
return itNone;
}
if(c->cpdist > 7) {
if(a != roCheck)
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);
return itStrongWind;
}
// (0x) control
if(getMount() && items[itOrbDomination] && dragon::whichturn != turncount) {
if(a != roCheck) {
dragon::target = c;
dragon::whichturn = turncount;
addMessage(XLAT("Commanded %the1!", getMount()));
checkmove();
}
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;
}
// (0') air blow
bool nowhereToBlow = false;
if(items[itOrbAir] && isBlowableMonster(c->monst)) {
int d = 0;
for(; d<c->type; d++) if(c->mov[d] && c->mov[d]->cpdist < c->cpdist) break;
if(d<c->type) for(int e=d; e<d+c->type; e++) {
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);
return itOrbAir;
}
}
}
// (0'') jump
int jumpstate = 0;
cell *jumpthru = NULL;
if(items[itOrbFrog] && c->cpdist == 2) {
jumpstate = 1;
int i = items[itOrbGhost];
if(i) items[itOrbGhost] = i-1;
for(int i=0; i<cwt.c->type; i++) {
cell *c2 = cwt.c->mov[i];
if(isNeighbor(c2, c)) {
jumpthru = c2;
if(passable(c2, cwt.c, P_ISPLAYER | P_JUMP1)) {
jumpstate = 2;
if(passable(c, c2, P_ISPLAYER | P_JUMP2)) {
jumpstate = 3;
break;
}
}
}
}
items[itOrbGhost] = i;
if(jumpstate == 3 && !monstersnear(c, NULL, moPlayer, NULL, c)) {
jumpstate = 4;
if(a != roCheck) jumpTo(c, itOrbFrog);
return itOrbFrog;
}
}
// (1) switch with an illusion
if(items[itOrbTeleport] && c->monst == moIllusion && !cwt.c->monst) {
if(a != roCheck) 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);
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);
return itOrbTeleport;
}
// (4) remove an illusion
if(!shmup::on && items[itOrbIllusion] && c->monst == moIllusion) {
if(a != roCheck) {
addMessage(XLAT("You take the Illusion away."));
items[itOrbIllusion] += 3; // 100% effective with the Orb of Energy!
c->monst = moNone;
}
return itOrbIllusion;
}
// (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);
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);
return itOrbPsi;
}
// (5a) summoning
if(items[itOrbSummon] && summonedAt(c)) {
if(a != roCheck) summonAt(c);
return itOrbSummon;
}
// (5b) matter
if(items[itOrbMatter] && tempWallPossibleAt(c)) {
if(a != roCheck) 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);
return itOrbStunning;
}
// (6) place fire (non-shmup variant)
if(!shmup::on && items[itOrbDragon] && makeflame(c, 20, true)) {
if(a != roCheck) useOrbOfDragon(c);
return itOrbDragon;
}
if(a == roCheck) return itNone;
if(nowhereToBlow) {
addMessage(XLAT("Nowhere to blow %the1!", c->monst));
}
else if(jumpstate == 1 && jumpthru && jumpthru->monst) {
addMessage(XLAT("Cannot jump through %the1!", jumpthru->monst));
}
else if(jumpstate == 1 && jumpthru) {
addMessage(XLAT("Cannot jump through %the1!", jumpthru->wall));
}
else if(jumpstate == 2 && c->monst) {
addMessage(XLAT("Cannot jump on %the1!", c->monst));
}
else if(jumpstate == 2 && c->wall) {
addMessage(XLAT("Cannot jump on %the1!", c->wall));
}
else if(jumpstate == 3) {
addMessage(XLAT("%The1 would get you there!", which));
}
else if(items[itOrbAir] && c->monst) {
addMessage(XLAT("%The1 is immune to wind!", c->monst));
}
else if(items[itOrbPsi] && c->monst) {
addMessage(XLAT("%The1 is immune to mental blasts!", c->monst));
}
else if(items[itOrbTeleport] && c->monst) {
addMessage(XLAT("Cannot teleport on a monster!"));
}
else if(c->item && items[itOrbTelekinesis]) {
addMessage(XLAT("Not enough power for telekinesis!"));
}
else if(items[itOrbIllusion] && c->item)
addMessage(XLAT("Cannot cast illusion on an item!"));
else if(items[itOrbIllusion] && c->monst)
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] && !passable(c, NULL, P_TELE | P_ISPLAYER)) {
addMessage(XLAT("Cannot teleport here!"));
}
else if(items[itOrbMatter] && !tempWallPossibleAt(c)) {
if(c->monst)
addMessage(XLAT("Cannot create temporary matter on a monster!"));
else if(c->item)
addMessage(XLAT("Cannot create temporary matter on an item!"));
else addMessage(XLAT("Cannot create temporary matter here!"));
}
else if(items[itOrbSummon] && !summonedAt(c)) {
if(c->monst)
addMessage(XLAT("Cannot summon on a monster!"));
else
addMessage(XLAT("No summoning possible here!"));
}
else if(items[itOrbTeleport] && c->item) {
addMessage(XLAT("Cannot teleport on an item!"));
}
else if(items[itOrbDragon] && !makeflame(c, 20, true)) {
addMessage(XLAT("Cannot throw fire there!"));
}
else return eItem(0);
return eItem(-1);
}

View File

@ -1,3 +1,7 @@
// Hyperbolic Rogue pattern generator
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
void spill50(cell *c, eWall w, int r) {
c->wall = w;
if(r) for(int i=0; i<c->type; i++) spill50(createMov(c, i), w, r-1);

View File

@ -1,3 +1,7 @@
// HyperRogue patterns
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
// === EMERALD PATTERN ===
// rules for the emeraldvalues of heptagons.

13976
polygons.cpp

File diff suppressed because it is too large Load Diff

697
rug.cpp Normal file
View File

@ -0,0 +1,697 @@
// Hyperbolic Rogue
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
// implementation of the Hypersian Rug mode
#ifndef MOBILE
#define TEXTURESIZE (texturesize)
#define HTEXTURESIZE (texturesize/2)
#ifdef LINUX
extern "C" {
GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers);
GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer);
GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level);
GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target);
GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs);
GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers);
GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer);
GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers);
GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers);
}
#endif
#ifdef MAC
#define glFramebufferTexture glFramebufferTextureEXT
#endif
namespace rug {
// hypersian rug datatypes and globals
//-------------------------------------
bool rugged = false;
bool genrug = false;
bool glew = false;
bool renderonce = false;
bool rendernogl = false;
int texturesize = 1024;
double scale = 1;
int queueiter, qvalid, dt;
double err;
struct edge {
struct rugpoint *target;
double len;
};
struct rugpoint {
double x1, y1;
bool valid;
bool inqueue;
hyperpoint h;
hyperpoint flat;
vector<edge> edges;
};
struct triangle {
rugpoint *m[3];
triangle(rugpoint *m1, rugpoint *m2, rugpoint *m3) {
m[0] = m1; m[1] = m2; m[2] = m3;
}
};
vector<rugpoint*> points;
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 *m = new rugpoint;
m->h = h;
ld tz = vid.alphax+h[2];
m->x1 = (1 + h[0] / tz) / 2;
m->y1 = (1 + h[1] / tz) / 2;
m->flat = // hpxyz(h[0], h[1], sin(atan2(h[0], h[1]) * 3 + hyprand) * (h[2]-1) / 1000);
hpxyz(h[0], h[1], (h[2]-1) * (rand() % 1000 - rand() % 1000) / 1000);
m->valid = false;
m->inqueue = false;
points.push_back(m);
return m;
}
rugpoint *findRugpoint(hyperpoint h) {
for(int i=0; i<size(points); i++)
if(intval(points[i]->h, h) < 1e-5) return points[i];
return addRugpoint(h);
}
void addNewEdge(rugpoint *e1, rugpoint *e2) {
edge e; e.target = e2; e1->edges.push_back(e);
e.target = e1; e2->edges.push_back(e);
}
void addEdge(rugpoint *e1, rugpoint *e2) {
for(int i=0; i<size(e1->edges); i++)
if(e1->edges[i].target == e2) return;
addNewEdge(e1, e2);
}
void addTriangle(rugpoint *t1, rugpoint *t2, rugpoint *t3) {
addEdge(t1, t2); addEdge(t2, t3); addEdge(t3, t1);
triangles.push_back(triangle(t1,t2,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));
addTriangle(t1, t12, t31);
addTriangle(t12, t2, t23);
addTriangle(t23, t3, t31);
addTriangle(t23, t31, t12);
}
bool psort(rugpoint *a, rugpoint *b) {
return a->h[2] < b->h[2];
}
void calcLengths() {
for(int i=0; i<size(points); i++) for(int j=0; j<size(points[i]->edges); j++)
points[i]->edges[j].len = hdist(points[i]->h, points[i]->edges[j].target->h);
}
void buildRug() {
map<cell*, rugpoint *> vptr;
for(int i=0; i<size(dcal); i++)
if(gmatrix.count(dcal[i]))
vptr[dcal[i]] = addRugpoint(gmatrix[dcal[i]]);
for(int i=0; i<size(dcal); i++) {
cell *c = dcal[i];
rugpoint *v = vptr[c];
if(!v) continue;
for(int j=0; j<c->type; j++) {
cell *c2 = c->mov[j];
rugpoint *w = vptr[c2];
if(!w) continue;
// if(v<w) addEdge(v, w);
cell *c3 = c->mov[(j+1) % c->type];
rugpoint *w2 = vptr[c3];
if(!w2) continue;
if(c->type == 7) addTriangle(v, w, w2);
}
}
printf("vertices = %d triangles= %d\n", size(points), size(triangles));
calcLengths();
sort(points.begin(), points.end(), psort);
}
// rug physics
queue<rugpoint*> pqueue;
void enqueue(rugpoint *m) {
if(m->inqueue) return;
pqueue.push(m);
m->inqueue = true;
}
void force(rugpoint& m1, rugpoint& m2, double rd, double d1=1, double d2=1) {
if(!m1.valid || !m2.valid) return;
// double rd = hdist(m1.h, m2.h) * xd;
// if(rd > rdz +1e-6 || rd< rdz-1e-6) printf("%lf %lf\n", rd, rdz);
double t = 0;
for(int i=0; i<3; i++) t += (m1.flat[i] - m2.flat[i]) * (m1.flat[i] - m2.flat[i]);
t = sqrt(t);
// printf("%lf %lf\n", t, rd);
err += (t-rd) * (t-rd);
bool nonzero = t < rd-1e-9 || t > rd+1e-9;
double force = (t - rd) / t / 2; // 20.0;
for(int i=0; i<3; i++) {
double di = (m2.flat[i] - m1.flat[i]) * force;
m1.flat[i] += di * d1;
m2.flat[i] -= di * d2;
if(nonzero && d2>0) enqueue(&m2);
}
}
void preset(rugpoint *m) {
int q = 0;
hyperpoint h;
for(int i=0; i<3; i++) h[i] = 0;
using namespace hyperpoint_vec;
for(int j=0; j<size(m->edges); j++)
for(int k=0; k<j; k++) {
rugpoint *a = m->edges[j].target;
rugpoint *b = m->edges[k].target;
if(!a->valid) continue;
if(!b->valid) continue;
double blen = -1;
for(int j2=0; j2<size(a->edges); j2++)
if(a->edges[j2].target == b) blen = a->edges[j2].len;
if(blen <= 0) continue;
for(int j2=0; j2<size(a->edges); j2++)
for(int k2=0; k2<size(b->edges); k2++)
if(a->edges[j2].target == b->edges[k2].target && a->edges[j2].target != m) {
rugpoint *c = a->edges[j2].target;
if(!c->valid) continue;
double a1 = m->edges[j].len/blen;
double a2 = m->edges[k].len/blen;
double c1 = a->edges[j2].len/blen;
double c2 = b->edges[k2].len/blen;
double cz = (c1*c1-c2*c2+1) / 2;
double ch = sqrt(c2*c2 - cz*cz);
double az = (a1*a1-a2*a2+1) / 2;
double ah = sqrt(a2*a2 - az*az);
// c->h = a->h + (b->h-a->h) * cz + ch * ort
hyperpoint ort = (c->flat - a->flat - cz * (b->flat-a->flat)) / ch;
// 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++;
}
}
if(q>0) for(int i=0; i<3; i++) m->flat[i] = h[i]/q;
}
int divides = 0;
bool stop = false;
void subdivide() {
int N = size(points);
if(divides > 4) {stop = true; return; }
printf("subdivide (%d,%d)\n", N, size(triangles));
divides++;
vector<triangle> otriangles = triangles;
triangles.clear();
// subdivide edges
for(int i=0; i<N; i++) {
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));
using namespace hyperpoint_vec;
mm->flat = (m->flat + m2->flat) / 2;
mm->valid = true; qvalid++;
mm->inqueue = false; enqueue(mm);
}
m->edges.clear();
}
for(int i=0; i<size(otriangles); i++)
addTriangle1(otriangles[i].m[0], otriangles[i].m[1], otriangles[i].m[2]);
calcLengths();
printf("result (%d,%d)\n", size(points), size(triangles));
}
void addNewPoints() {
if(qvalid == size(points)) {
subdivide();
return;
}
double dist = points[qvalid]->h[2] + .1e-6;
int oqvalid = qvalid;
for(int i=0; i<size(points); i++) {
rugpoint& m = *points[i];
bool wasvalid = m.valid;
m.valid = wasvalid || (m.h[2] >= .5 && m.h[2] < dist);
if(m.valid && !wasvalid) {
qvalid++;
if(i > 7) preset(&m);
for(int it=0; it<50; it++)
for(int j=0; j<size(m.edges); j++)
force(m, *m.edges[j].target, m.edges[j].len, 1, 0);
enqueue(&m);
}
}
if(qvalid != oqvalid) { printf("%4d %4d %4d %.9lf %9d %9d\n", oqvalid, qvalid, size(points), dist, dt, queueiter); }
}
void physics() {
for(int it=0; it<10000 && !stop; it++)
if(pqueue.empty()) addNewPoints();
else {
queueiter++;
rugpoint *m = pqueue.front();
pqueue.pop();
m->inqueue = false;
for(int j=0; j<size(m->edges); j++)
force(*m, *m->edges[j].target, m->edges[j].len);
}
}
// drawing the Rug
//-----------------
int eyemod;
void getco(rugpoint& m, double& x, double& y, double& z) {
x = m.flat[0];
y = m.flat[1];
z = m.flat[2];
if(eyemod) x += eyemod * z * vid.eye;
}
extern int besti;
void drawTriangle(triangle& t) {
rugpoint& m1 = *t.m[0];
rugpoint& m2 = *t.m[1];
rugpoint& m3 = *t.m[2];
if(!m1.valid || !m2.valid || !m3.valid) return;
dt++;
double x1, y1, z1;
double x2, y2, z2;
double x3, y3, z3;
getco(m1,x1,y1,z1);
getco(m2,x2,y2,z2);
getco(m3,x3,y3,z3);
double xa = x2-x1, ya = y2-y1, za = z2-z1;
double xb = x3-x1, yb = y3-y1, zb = z3-z1;
double xn = ya * zb - za * yb;
double yn = za * xb - xa * zb;
double zn = xa * yb - ya * xb;
double h = sqrt(xn*xn+yn*yn+zn*zn);
glNormal3f(xn/h,yn/h,zn/h);
glTexCoord2f(m1.x1, m1.y1);
glVertex3f(x1, y1, z1);
glTexCoord2f(m2.x1, m2.y1);
glVertex3f(x2, y2, z2);
glTexCoord2f(m3.x1, m3.y1);
glVertex3f(x3, y3, z3);
}
void setVidParam() {
vid.xres = vid.yres = TEXTURESIZE; vid.scale = 1;
vid.radius = HTEXTURESIZE; vid.xcenter = HTEXTURESIZE; vid.ycenter = HTEXTURESIZE;
vid.beta = 2; vid.alphax = 1; vid.eye = 0; vid.goteyes = false;
}
GLuint FramebufferName = 0;
GLuint renderedTexture = 0;
GLuint depth_stencil_rb = 0;
SDL_Surface *texture;
Uint32 *expanded_data;
void initTexture() {
if(!rendernogl) {
FramebufferName = 0;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
glGenTextures(1, &renderedTexture);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, TEXTURESIZE, TEXTURESIZE, 0,GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, DrawBuffers);
glGenRenderbuffers(1, &depth_stencil_rb);
glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_rb);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, TEXTURESIZE, TEXTURESIZE);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_stencil_rb);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depth_stencil_rb);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
addMessage("Failed to initialize the framebuffer");
rugged = false;
}
}
else {
texture = SDL_CreateRGBSurface(SDL_SWSURFACE,TEXTURESIZE,TEXTURESIZE,32,0,0,0,0);
glGenTextures( 1, &renderedTexture );
glBindTexture( GL_TEXTURE_2D, renderedTexture);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
expanded_data = new Uint32[TEXTURESIZE * TEXTURESIZE];
}
}
void prepareTexture() {
videopar svid = vid;
setVidParam();
if(rendernogl) {
vid.usingGL = false;
SDL_Surface *sav = s;
s = texture;
SDL_FillRect(s, NULL, 0);
drawfullmap();
s = sav;
for(int y=0; y<TEXTURESIZE; y++) for(int x=0; x<TEXTURESIZE; x++)
expanded_data[y*TEXTURESIZE + x] = qpixel(texture, x, TEXTURESIZE-1-y) | 0xFF000000;
glBindTexture( GL_TEXTURE_2D, renderedTexture);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, TEXTURESIZE, TEXTURESIZE, 0, GL_BGRA, GL_UNSIGNED_BYTE, expanded_data );
}
else {
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
glViewport(0,0,TEXTURESIZE,TEXTURESIZE);
setGLProjection();
ptds.clear();
drawthemap();
if(!renderonce) queueline(C0, mouseh, 0xFF00FF);
drawqueue();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
vid = svid;
if(!rendernogl) glViewport(0,0,vid.xres,vid.yres);
}
void closeTexture() {
if(rendernogl) {
SDL_FreeSurface(texture);
glDeleteTextures(1, &renderedTexture);
delete[] expanded_data;
}
else {
glDeleteTextures(1, &renderedTexture);
glDeleteRenderbuffers(1, &depth_stencil_rb);
glDeleteFramebuffers(1, &FramebufferName);
}
}
double xview, yview;
void drawRugScene() {
GLfloat light_ambient[] = { 3.5, 3.5, 3.5, 1.0 };
GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_position[] = { 0.0, 0.0, 0.0, 1.0 };
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
GLERR("lighting");
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glClearColor(0.05,0.05,0.05,1);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
xview = vid.xres/(vid.radius*scale);
yview = vid.yres/(vid.radius*scale);
glOrtho(-xview, xview, -yview, yview, -1000, 1000);
glColor4f(1,1,1,1);
if(vid.eye > .001 || vid.eye < -.001) {
selectEyeMask(1);
glClear(GL_DEPTH_BUFFER_BIT);
glBegin(GL_TRIANGLES);
eyemod = 1;
for(int t=0; t<size(triangles); t++)
drawTriangle(triangles[t]);
glEnd();
selectEyeMask(-1);
eyemod = -1;
glClear(GL_DEPTH_BUFFER_BIT);
glBegin(GL_TRIANGLES);
for(int t=0; t<size(triangles); t++)
drawTriangle(triangles[t]);
glEnd();
selectEyeMask(0);
}
else {
glBegin(GL_TRIANGLES);
for(int t=0; t<size(triangles); t++)
drawTriangle(triangles[t]);
glEnd();
}
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
selectEyeGL(0);
}
// organization
//--------------
transmatrix rotmatrix(double rotation, int c0, int c1) {
transmatrix t = Id;
t[c0][c0] = cos(rotation);
t[c1][c1] = cos(rotation);
t[c0][c1] = sin(rotation);
t[c1][c0] = -sin(rotation);
return t;
}
transmatrix currentrot;
void init() {
#ifdef WINDOWS
if(!glew) {
glew = true;
GLenum err = glewInit();
if (GLEW_OK != err) {
addMessage("Failed to initialize GLEW");
return;
}
}
#endif
if(rugged) return;
rugged = true;
if(scale < .01 || scale > 100) scale = 1;
initTexture();
if(renderonce) prepareTexture();
if(!rugged) return;
gmatrix.clear();
genrug = true;
drawthemap();
genrug = false;
buildRug();
qvalid = 0; dt = 0; queueiter = 0;
currentrot = Id;
}
void close() {
if(!rugged) return;
rugged = false;
closeTexture();
triangles.clear();
for(int i=0; i<size(points); i++) delete points[i];
points.clear();
gmatrix.clear();
pqueue = queue<rugpoint*> ();
}
int lastticks;
void actDraw() {
if(!renderonce) prepareTexture();
physics();
drawRugScene();
Uint8 *keystate = SDL_GetKeyState(NULL);
int qm = 0;
transmatrix t = Id;
double alpha = (ticks - lastticks) / 1000.0;
lastticks = ticks;
if(keystate[SDLK_HOME]) qm++, t = inverse(currentrot);
if(keystate[SDLK_END]) qm++, t = currentrot * rotmatrix(alpha, 0, 1) * inverse(currentrot);
if(keystate[SDLK_DOWN]) qm++, t = t * rotmatrix(alpha, 1, 2);
if(keystate[SDLK_UP]) qm++, t = t * rotmatrix(alpha, 2, 1);
if(keystate[SDLK_LEFT]) qm++, t = t * rotmatrix(alpha, 0, 2);
if(keystate[SDLK_RIGHT]) qm++, t = t * rotmatrix(alpha, 2, 0);
if(keystate[SDLK_PAGEUP]) scale *= exp(alpha);
if(keystate[SDLK_PAGEDOWN]) scale /= exp(alpha);
if(qm) {
currentrot = t * currentrot;
for(int i=0; i<size(points); i++) points[i]->flat = t * points[i]->flat;
}
}
int besti;
hyperpoint gethyper(ld x, ld y) {
double mx = ((x*2 / vid.xres)-1) * xview;
double my = (1-(y*2 / vid.yres)) * yview;
double bdist = 1e12;
besti = 0;
for(int i=0; i<size(points); i++) {
rugpoint& m = *points[i];
double dist = hypot(m.flat[0]-mx, m.flat[1]-my);
if(dist < bdist) bdist = dist, besti = i;
}
double px = points[besti]->x1 * TEXTURESIZE, py = (1-points[besti]->y1) * TEXTURESIZE;
videopar svid = vid;
setVidParam();
hyperpoint h = ::gethyper(px, py);
vid = svid;
return h;
}
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');
}
void handleKey(int uni, int sym) {
if(uni == 'h') {
lastmode = cmode;
cmode = emHelp;
help =
"In this mode, HyperRogue is played on a 3D model of a part of the hyperbolic plane, "
"similar to one you get from the 'paper model creator' or by hyperbolic crocheting.\n\n"
"This requires some OpenGL extensions and may crash or not work correctly -- enabling "
"the 'render texture without OpenGL' options may be helpful in this case. Also the 'render once' option "
"will make the rendering faster, but the surface will be rendered only once, so "
"you won't be able to play a game on it.\n\n"
"Use arrow keys to rotate, Page Up/Down to zoom.";
}
else if(uni == 'u') {
rug::init();
cmode = emNormal;
}
else if(uni == 'q')
cmode = emChangeMode;
else if(uni == 'o')
renderonce = !renderonce;
else if(uni == 'g')
rendernogl = !rendernogl;
else if(uni == 's') {
texturesize = 2*texturesize;
if(texturesize == 8192) texturesize = 128;
}
}
void select() {
if(rug::rugged) rug::close();
else cmode = emRugConfig;
}
}
#else
// fake for mobile
namespace rug {
bool rugged = false;
bool renderonce = false;
bool rendernogl = true;
int texturesize = 512;
double scale = 1.0f;
}
#endif

45
savepng.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef _SDL_SAVEPNG
#define _SDL_SAVEPNG
/*
* SDL_SavePNG -- libpng-based SDL_Surface writer.
*
* This code is free software, available under zlib/libpng license.
* http://www.libpng.org/pub/png/src/libpng-LICENSE.txt
*/
// #include <SDL_video.h>
#ifdef __cplusplus
extern "C" { /* This helps CPP projects that include this header */
#endif
/*
* Save an SDL_Surface as a PNG file.
*
* Returns 0 success or -1 on failure, the error message is then retrievable
* via SDL_GetError().
*/
#define SDL_SavePNG(surface, file) \
SDL_SavePNG_RW(surface, SDL_RWFromFile(file, "wb"), 1)
/*
* Save an SDL_Surface as a PNG file, using writable RWops.
*
* surface - the SDL_Surface structure containing the image to be saved
* dst - a data stream to save to
* freedst - non-zero to close the stream after being written
*
* Returns 0 success or -1 on failure, the error message is then retrievable
* via SDL_GetError().
*/
extern int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *rw, int freedst);
/*
* Return new SDL_Surface with a format suitable for PNG output.
*/
extern SDL_Surface *SDL_PNGFormatAlpha(SDL_Surface *src);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

793
shmup.cpp

File diff suppressed because it is too large Load Diff

1104
system.cpp Normal file

File diff suppressed because it is too large Load Diff

15
tools.cpp Normal file
View File

@ -0,0 +1,15 @@
#include <math.h>
#include <time.h>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
#include "hyper.h"
#include "hyperpoint.cpp"
#include "fjordgen.cpp"
#include "heptagon.cpp"
#include "language.cpp"
#include "achievement.cpp"

699
yendor.cpp Normal file
View File

@ -0,0 +1,699 @@
// Hyperbolic Rogue
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
// Yendor Quest, together with the Yendor Challenge
// also, the Pure Tactics Mode
#define MODECODES 38
int hiitemsMax(eItem it) {
int mx = 0;
for(int i=0; i<MODECODES; i++) if(hiitems[i][it] > mx) mx = hiitems[i][it];
return mx;
}
int modecode();
typedef vector<pair<int, string> > subscoreboard;
void displayScore(subscoreboard& s, int x) {
int vf = min((vid.yres-64) / 70, vid.xres/80);
if(syncstate == 1) {
displayfr(x, 56, 1, vf, "(syncing)", 0xC0C0C0, 0);
}
else {
sort(s.begin(), s.end());
for(int i=0; i<size(s); i++) {
int i0 = 56 + i * vf;
displayfr(x, i0, 1, vf, its(-s[i].first), 0xC0C0C0, 16);
displayfr(x+8, i0, 1, vf, s[i].second, 0xC0C0C0, 0);
}
}
}
namespace yendor {
bool on = false;
bool generating = false;
bool path = false;
bool everwon = false;
bool won = false;
bool easy = false;
struct yendorlevel {
eLand l;
int flags;
};
int challenge; // id of the challenge
int lastchallenge;
#define YENDORLEVELS 27
int bestscore[MODECODES][YENDORLEVELS];
#define YF_DEAD 1
#define YF_WALLS 2
#define YF_END 4
#define YF_DEAD5 8
#define YF_NEAR_IVY 16
#define YF_NEAR_ELEM 32
#define YF_NEAR_OVER 64
#define YF_NEAR_RED 128
#define YF_REPEAT 512
#define YF_NEAR_TENT 1024
#define YF_START_AL 2048
#define YF_START_CR 4096
#define YF_CHAOS 8192
#define YF_START_ANY (YF_START_AL|YF_START_CR)
eLand nexttostart;
#define LAND_YENDOR_CHAOS 41
yendorlevel levels[YENDORLEVELS] = {
{laNone, 0},
{laHell, YF_DEAD}, // FORCE BARRIERS?
{laGraveyard, YF_DEAD5},
{laDesert, YF_NEAR_IVY}, // IVY OR TENTACLE?
{laMinefield, YF_END}, // NOT WON, SEEMS OKAY
{laEmerald, 0}, // WON FINE
{laOvergrown, 0}, // WON, TOO EASY?
{laMotion, YF_START_AL | YF_END}, // NOT WON, SEEMS OKAY
{laAlchemist, 0}, // ALMOST WON
{laIvoryTower,YF_START_CR | YF_NEAR_ELEM | YF_REPEAT}, // won cool
{laMirror, YF_NEAR_OVER}, // OK
{laWhirlpool, 0}, // cool
{laIce, YF_NEAR_ELEM}, // OK
{laHive, YF_NEAR_RED}, // OK
{laCaribbean, 0}, // seems OK
{laOcean, YF_WALLS}, // well... stupid, add Caribbean/Fjord
{laPalace, 0}, // more trapdoors!
{laZebra, 0}, // TOO HARD?
{laWineyard, 0}, // hard-ish
{laStorms, 0}, // ?
{laLivefjord, 0},
{laJungle, 0},
{laPower, YF_START_CR},
{laWildWest, 0},
{laWhirlwind, YF_NEAR_TENT},
{laHell, YF_CHAOS | YF_DEAD},
{laDragon, YF_DEAD}
};
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);
achievement_score(LB_YENDOR_CHALLENGE, tscore);
}
yendorlevel& clev() { return levels[challenge]; }
eLand changeland(int i, eLand l) {
if((clev().flags & YF_START_ANY) && i < 20 && l != clev().l) return clev().l;
if((clev().flags & YF_END) && i > 80 && l == clev().l) return laIce;
return laNone;
}
string name;
eLand first, second, last;
struct yendorinfo {
cell *path[YDIST];
bool found;
bool foundOrb;
int howfar;
};
vector<yendorinfo> yi;
int yii = 0;
int hardness() {
int thf = 0;
for(int i=0; i<size(yi); i++) {
yendorinfo& ye ( yi[i] );
if(!ye.foundOrb && ye.howfar > 25)
thf += (ye.howfar - 25);
}
thf -= 2 * (YDIST - 25);
if(thf<0) thf = 0;
return items[itOrbYendor] * 5 + (thf * 5) / (YDIST-25);
}
enum eState { ysUntouched, ysLocked, ysUnlocked };
eState state(cell *yendor) {
for(int i=0; i<size(yi); i++) if(yi[i].path[0] == yendor)
return yi[i].found ? ysUnlocked : ysLocked;
return ysUntouched;
}
bool check(cell *yendor, bool checkonly) {
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)) {
yendorinfo nyi;
nyi.path[0] = yendor;
nyi.howfar = 0;
if(euclid) {
int di = hrand(6);
int dj = (di+1) % 6;
int qty = hrand(YDIST-1);
int tot = 0;
for(int i=0; i<YDIST-1; i++) {
tot += qty;
if(tot >= YDIST-1) {
tot -= YDIST-1;
nyi.path[i+1] = createMov(nyi.path[i], di);
}
else
nyi.path[i+1] = createMov(nyi.path[i], dj);
}
}
else {
bool endorian_change = true;
bool in_endorian = false;
cellwalker lig(yendor, hrand(yendor->type));
for(int i=0; i<YDIST-1; i++) {
if(lig.c->land == laEndorian)
in_endorian = true;
else if(!isTechnicalLand(lig.c->land))
in_endorian = false;
nyi.path[i] = lig.c;
cwstep(lig);
cwspin(lig, 3);
if(lig.c->type == 7) {
if(in_endorian && endorian_change && i >= YDIST - 20) {
// make the last leg a bit more difficult
cwspin(lig, hrand(2)*3-1);
endorian_change = false;
}
else
cwspin(lig, hrand(2));
}
}
nyi.path[YDIST-1] = lig.c;
}
generating = true;
for(int i=1; i<YDIST-1; i++) {
cell *c = nyi.path[i];
cell *prev = nyi.path[i-1];
setdist(c, 10, prev);
setdist(c, 9, prev);
setdist(c, 8, prev);
setdist(c, 7, prev);
if(challenge && i+BARLEV-7 < YDIST-5 && !euclid) {
cell *c2 = nyi.path[i+BARLEV-7];
if(c2->land == laIvoryTower) continue;
eLand ycl = changeland(i, c2->land);
if(ishept(c2) && ycl) {
int bd = 2 + hrand(2) * 3;
// printf("barrier at %d\n", i);
buildBarrier(c2, bd, ycl);
if(c2->bardir != NODIR && c2->bardir != NOBARRIERS)
extendBarrier(c2);
}
}
}
nyi.found = false;
nyi.foundOrb = false;
cell *key = nyi.path[YDIST-1];
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);
c2->monst = moNone; c2->item = itNone;
if(!passable(c2, NULL, P_MIRROR | P_MONSTER)) {
if(c2->wall == waCavewall) c2->wall = waCavefloor;
else if(c2->wall == waDeadwall) c2->wall = waDeadfloor2;
else if(c2->wall == waLake) c2->wall = waFrozenLake;
else if(c2->land == laCaribbean) c2->wall = waCIsland;
else if(c2->land == laOcean) c2->wall = waCIsland;
else if(c2->land == laRedRock) c2->wall = waRed3;
else if(c2->land == laWhirlpool)
c2->wall = waBoat, c2->monst = moPirate, c2->item = itOrbWater;
else c2->wall = waNone;
}
if(c2->wall == waMineMine || c2->wall == waMineUnknown)
c2->wall = waMineOpen;
if(c2->wall == waTrapdoor && i == -1)
c2->wall = waGargoyleFloor;
if(c2->land == laLivefjord) {
c2->wall = waSea;
for(int i=0; i<c2->type; i++)
c2->mov[i]->wall = waSea;
}
if(isGravityLand(c2->land) && key->land == c2->land &&
c2->landparam < key->landparam && c2->wall != waTrunk)
c2->wall = waPlatform;
}
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");
return false;
}
void onpath() {
path = false;
if(yii < size(yi)) {
for(int i=0; i<YDIST; i++) if(yi[yii].path[i]->cpdist <= 7) {
if(i > yi[yii].howfar) yi[yii].howfar = i;
path = true;
}
}
}
void init(int phase) {
if(!on) return;
if(phase == 1) {
won = false;
if(!easy) items[itOrbYendor] = bestscore[modecode()][challenge];
chaosmode = (clev().flags & YF_CHAOS);
euclidland = firstland = clev().l;
if(clev().flags & YF_START_AL) {
firstland = laAlchemist;
items[itElixir] = 50;
items[itFeather] = 50;
}
if(firstland == laPower)
items[itOrbSpeed] = 60, items[itOrbWinter] = 60;
if(clev().flags & YF_START_CR) {
firstland = laCrossroads;
}
if(firstland == laGraveyard) items[itBone] = 10;
if(firstland == laEmerald) items[itEmerald] = 10;
if(!euclid) {
if(clev().flags & YF_DEAD) items[itGreenStone] = 100;
if(clev().flags & YF_DEAD5) items[itGreenStone] = 5;
}
nexttostart = laNone;
}
if(phase == 2) {
cell *c2 = cwt.c->mov[0];
c2->land = firstland;
if(firstland == laRlyeh) c2->wall = waNone;
yendor::check(c2, false);
if(clev().flags & YF_NEAR_IVY)
nexttostart = laJungle;
if(clev().flags & YF_NEAR_TENT)
nexttostart = laRlyeh;
if(clev().flags & YF_NEAR_ELEM) {
if(firstland == laIce) {
nexttostart = laEWater;
items[itWaterShard] = 10;
}
else nexttostart = laEAir;
}
if(clev().flags & YF_NEAR_OVER)
nexttostart = laOvergrown;
if(clev().flags & YF_NEAR_RED) {
nexttostart = laRedRock;
items[itRedGem] = 25;
}
if(clev().flags & YF_WALLS) {
items[itPirate] += 25;
items[itFjord] += 25;
}
if(clev().l == laWhirlpool) {
items[itWhirlpool] += 10;
items[itOrbWater] += 150;
}
}
if(phase == 3) {
cell *c2 = cwt.c->mov[0];
makeEmpty(c2);
c2->item = itOrbYendor;
nexttostart = laNone;
}
}
bool levelUnlocked(int i) {
yendorlevel& ylev(levels[i]);
eItem t = treasureType(ylev.l);
if(ylev.l != laWildWest && hiitemsMax(t) < 10) return false;
if((ylev.flags & YF_NEAR_ELEM) && hiitemsMax(itElemental) < 10) return false;
if((ylev.flags & YF_NEAR_RED) && hiitemsMax(itRedGem) < 10) return false;
if((ylev.flags & YF_NEAR_OVER) && hiitemsMax(itMutant) < 10) return false;
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;
return true;
}
struct scoredata {
string username;
int scores[landtypes];
};
vector<scoredata> scoreboard;
void showMenu() {
int s = vid.fsize;
vid.fsize = vid.fsize * 4/5;
displayStatHelp(-8, XLAT("Yendor Challenge"));
for(int i=1; i<YENDORLEVELS; i++) {
string s;
yendorlevel& ylev(levels[i]);
if(levelUnlocked(i)) {
s = XLATT1(ylev.l);
if(!euclid) {
if(ylev.flags & YF_CHAOS) { s = "Chaos mode"; }
if(ylev.flags & YF_NEAR_IVY) { s += "+"; s += XLATT1(laJungle); }
if(ylev.flags & YF_NEAR_TENT) { s += "+"; s += XLATT1(laRlyeh); }
if(ylev.flags & YF_NEAR_ELEM) { s += "+"; s += XLATT1(laElementalWall); }
if(ylev.flags & YF_NEAR_OVER) { s += "+"; s += XLATT1(laOvergrown); }
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); }
}
}
else {
s = "(locked)";
}
string v;
if(bestscore[modecode()][i] == 1)
v = XLAT(" (won!)");
else if(bestscore[modecode()][i])
v = XLAT(" (won at level %1!)", its(bestscore[modecode()][i]));
displayStat(i-6, s, v, 'a' + i-1);
}
displayStat(YENDORLEVELS+1-6, XLAT("Return to the normal game"), "", '0');
displayStat(YENDORLEVELS+1-5, XLAT(
easy ? "Challenges do not get harder" : "Each challenge gets harder after each victory"),
" " + XLAT(easy ? "easy" : "challenge"), '1');
int yc = getcstat - 'a' + 1;
if(yc > 0 && yc < YENDORLEVELS) {
subscoreboard scorehere;
for(int i=0; i<size(scoreboard); i++) {
int sc = scoreboard[i].scores[yc];
if(sc > 0)
scorehere.push_back(
make_pair(-sc, scoreboard[i].username));
}
displayScore(scorehere, vid.xres / 4);
}
yendor::uploadScore();
vid.fsize = s;
}
const char *chelp =
"There are many possible solutions to the Yendor Quest. In the Yendor "
"Challenge, you will try many of them!\n\n"
"Each challenge takes part in a specific land, and you have to use what "
"you have available.\n\n"
"You need to obtain an Orb of Yendor in the normal game to activate "
"this challenge, and (ever) collect 10 treasures in one or two lands "
"to activate a specific level.\n\n"
"After you complete each challenge, you can try it again, on a harder "
"difficulty level.\n\n"
"All the solutions showcased in the Yendor Challenge work in the normal "
"play too. However, passages to other lands, and (sometimes) some land features "
"are disabled in the Yendor "
"Challenge, so that you have to use the expected method. Also, "
"the generation rules are changed slightly for the Palace "
"and Minefield while you are looking for the Orb of Yendor, "
"to make the challenge more balanced "
"(but these changes are also active during the normal Yendor Quest).\n\n"
"You get 1000 points for each challenge won, and 1 extra point for "
"each extra difficulty level.";
void handleKey(int uni, int sym) {
if(uni >= 'a' && uni < 'a'+YENDORLEVELS-1) {
challenge = uni-'a' + 1;
if(levelUnlocked(challenge)) {
restartGame(yendor::on ? 0 : 'y');
cmode = emNormal;
}
else
addMessage("Collect 10 treasures in various lands to unlock the challenges there");
}
else if(uni == '0') {
if(yendor::on) restartGame('y');
cmode = emNormal;
}
else if(uni == '1') easy = !easy;
else if(uni == '2' || sym == SDLK_F1) {
lastmode = cmode;
cmode = emHelp;
help = chelp;
}
else if(uni) cmode = emNormal;
}
};
#define MAXTAC 20
namespace tactic {
bool trailer = false;
bool on = false;
int id;
int recordsum[MODECODES][landtypes];
int lsc[MODECODES][landtypes][MAXTAC];
eLand lasttactic;
struct scoredata {
string username;
int scores[landtypes];
};
vector<scoredata> scoreboard[MODECODES];
int chances(eLand l) {
if(modecode() != 0 && l != laCamelot) return 3;
for(int i=0; i<LAND_TAC; i++)
if(land_tac[i].l == l) {
return land_tac[i].tries;
}
return 0;
}
int tacmultiplier(eLand l) {
if(modecode() != 0 && l != laCamelot) return 1;
if(modecode() != 0 && l == laCamelot) return 3;
for(int i=0; i<LAND_TAC; i++)
if(land_tac[i].l == l) return land_tac[i].multiplier;
return 0;
}
bool tacticUnlocked(int i) {
eLand l = land_tac[i].l;
if(l == laWildWest) return true;
return hiitemsMax(treasureType(l)) * landMultiplier(l) >= 20;
}
void record(eLand land, int score, int xc = modecode()) {
if(land >=0 && land < landtypes) {
for(int i=MAXTAC-1; i; i--) lsc[xc][land][i] = lsc[xc][land][i-1];
tactic::lsc[xc][land][0] = score;
}
int t = chances(land);
int csum = 0;
for(int i=0; i<t; i++) if(lsc[xc][land][i] > 0) csum += lsc[xc][land][i];
if(csum > recordsum[xc][land]) recordsum[xc][land] = csum;
}
void record() {
record(lasttactic, items[treasureType(lasttactic)]);
}
void unrecord(eLand land, int xc = modecode()) {
if(land >=0 && land < landtypes) {
for(int i=0; i<MAXTAC-1; i++) lsc[xc][land][i] = lsc[xc][land][i+1];
lsc[xc][land][MAXTAC-1] = -1;
}
}
void unrecord() {
unrecord(lasttactic);
}
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);
achievement_score(lb, tscore);
}
void uploadScore() {
uploadScoreCode(0, LB_PURE_TACTICS);
uploadScoreCode(2, LB_PURE_TACTICS_SHMUP);
uploadScoreCode(4, LB_PURE_TACTICS_COOP);
}
void showMenu() {
mouseovers = XLAT("pure tactics mode") + " - " + mouseovers;
int nl = LAND_TAC;
int vf = min((vid.yres-64) / nl, vid.xres/40);
int xr = vid.xres / 64;
if(on) record(firstland, items[treasureType(firstland)]);
int xc = modecode();
for(int i=0; i<nl; i++) {
eLand l = land_tac[i].l;
int i0 = 56 + i * vf;
int col;
int ch = chances(l);
if(!ch) continue;
bool unlocked = tacticUnlocked(i);
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(unlocked) {
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(displayfr(xr*(24+2*10), i0, 1, (vf-4)*4/5,
its(recordsum[xc][l]) + " x" + its(tacmultiplier(l)), col, 0))
getcstat = 1000 + i;
}
else {
int m = landMultiplier(l);
displayfr(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';
}
uploadScore();
if(on) unrecord(firstland);
if(getcstat >= 1000) {
int ld = land_tac[getcstat-1000].l;
subscoreboard scorehere;
for(int i=0; i<size(scoreboard[xc]); i++) {
int sc = scoreboard[xc][i].scores[ld];
if(sc > 0)
scorehere.push_back(
make_pair(-sc, scoreboard[xc][i].username));
}
displayScore(scorehere, xr * 50);
}
}
void handleKey(int uni, int sym) {
if(uni >= 1000 && uni < 1000 + LAND_TAC) {
firstland = land_tac[uni - 1000].l;
restartGame(tactic::on ? 0 : 't');
cmode = emNormal;
}
else if(uni == '0') {
cmode = emNormal;
firstland = laIce;
if(tactic::on) restartGame('t');
}
else if(uni == '2' || sym == SDLK_F1) {
lastmode = cmode;
cmode = emHelp;
help =
"In the pure tactics mode, you concentrate on a specific land. "
"Your goal to obtain as high score as possible, without using "
"features of the other lands. You can then compare your score "
"with your friends!\n\n"
"You need to be somewhat proficient in the normal game to "
"unlock the given land in this challenge "
"(collect 20 treasure in the given land, or 2 in case of Camelot).\n\n"
"Since getting high scores in some lands is somewhat luck dependent, "
"you play each land N times, and your score is based on N consecutive "
"plays. The value of N depends on how 'fast' the land is to play, and "
"how random it is.\n\n"
"In the Caribbean, you can access Orbs of Thorns, Aether, and "
"Space if you have ever collected 25 treasure in their native lands.\n\n"
"The rate of treasure spawn is static in this mode. It is not "
"increased by killing monsters.\n\n"
"Good luck, and have fun!";
}
else if(uni) cmode = emNormal;
}
};
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;
if(anticheat::tampered || cheater) return 6;
if(purehepta) {
if(xcode > 6) xcode--;
xcode /= 2;
xcode += 13;
}
if(chaosmode && !yendor::on && cmode != emYendor) xcode += 19;
return xcode;
}