diff --git a/achievement.cpp b/achievement.cpp index e0b9a34c..73a8cde5 100644 --- a/achievement.cpp +++ b/achievement.cpp @@ -84,6 +84,7 @@ bool wrongMode(char flags) { if(randomPatternsMode) return true; if(yendor::on) return true; if(tactic::on) return true; + if(tour::on) return true; if(chaosmode != (flags == 'C')) return true; if((numplayers() > 1) != (flags == 'm')) return true; return false; @@ -516,8 +517,9 @@ void improveItemScores() { void achievement_final(bool really_final) { if(offlineMode) return; #ifdef HAVE_ACHIEVEMENTS - upload_score(LB_STATISTICS, time(NULL)); + // upload_score(LB_STATISTICS, time(NULL)); if(cheater) return; + if(tour::on) return; if(sphere && euclidland == laHalloween) { if(shmup::on || chaosmode || purehepta || numplayers() > 1 || tactic::on || randomPatternsMode) diff --git a/cell.cpp b/cell.cpp index 77fb76cf..b1d32ae6 100644 --- a/cell.cpp +++ b/cell.cpp @@ -1324,39 +1324,60 @@ int celldistance(cell *c1, cell *c2) { if(quotient == 2) return fp43.getdist(fieldpattern::fieldval(c1), fieldpattern::fieldval(c2)); + int d1 = celldist(c1), d2 = celldist(c2); + cell *cl1=c1, *cr1=c1, *cl2=c2, *cr2=c2; while(true) { - if(cl1 == cl2) return d; + /* if(cl1 == cl2) return d; if(cl1 == cr2) return d; if(cr1 == cl2) return d; - if(cr1 == cr2) 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; + //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; + + if(d1 == d2) for(int u=0; u<2; u++) { + cell *ac0 = u ? cr1 : cr2, *ac = ac0; + cell *tgt = u ? cl2 : cl1; + cell *xtgt = u ? cr2 : cr1; + if(ac == tgt) return d; + ac = chosenDown(ac, 1, 1, celldist); + if(ac == tgt) return d+1; + if(ac == xtgt) return d; + ac = chosenDown(ac, 1, 1, celldist); + if(ac == tgt) return d+2; + if(!purehepta) { + ac = chosenDown(ac, 1, 1, celldist); + if(ac == tgt) { + if(chosenDown(ac0, 1, 0, celldist) == + chosenDown(tgt, -1, 0, celldist)) + return d+2; + return d+3; + } + } + } + + /* forCellEx(c, cl2) if(isNeighbor(c, cr1)) return d+2; forCellEx(c, cl1) if(isNeighbor(c, cr2)) return d+2; forCellEx(ca, cl2) forCellEx(cb, cr1) if(isNeighbor(ca, cb)) return d+3; - forCellEx(ca, cl1) forCellEx(cb, cr2) if(isNeighbor(ca, cb)) return d+3; + forCellEx(ca, cl1) forCellEx(cb, cr2) if(isNeighbor(ca, cb)) return d+3; */ - 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++; + d++; d1--; } - if(d1 <= d2) { + 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++; + d++; d2--; } } } diff --git a/certify.cpp b/certify.cpp new file mode 100644 index 00000000..a92ecb4f --- /dev/null +++ b/certify.cpp @@ -0,0 +1,75 @@ +// PRIVATE + +namespace anticheat { + + bool tampered; + + void save(FILE *f) { + int cert = 0x1234; + for(int i=0; i= "8.0i") tid += 1 + cert + cert * cert / 7; + else tid++; + } + +// 8.0i: ver included in achievements +// 8.0f: fixed in Windows + } + +#ifndef MOBILE +void SDL_Delay_anticheat(int i) { + saveBox(); + int i0 = 0; savebox[1] = savebox[65] = anticheat::tampered; // do not consider Timer + for(int t=0; t band; + SDL_Surface *out; bool displayhelp = true; + int& bandpixel(int x, int y) { + int i = 0; + while(i < size(band) && x >= band[i]->w) + x -= band[i]->w, i++; + return qpixel(band[i], x, y); + } + void precompute() { - CX = band->w; - CY = band->h; + CX = 0; + for(int i=0; iw; + if(CX == 0) { printf("ERROR: no CX\n"); return; } + CY = band[0]->h; SX = out->w; SY = out->h; @@ -151,11 +161,11 @@ namespace spiral { 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); + qpixel(out, x, y) = bandpixel(cx, cy); } } - void loop(SDL_Surface *_band) { + void loop(vector _band) { bool saveGL = vid.usingGL; if(saveGL) { vid.usingGL = false; setvideomode(); } @@ -163,6 +173,7 @@ namespace spiral { band = _band; out = s; precompute(); + if(CX == 0) return; shiftx = shifty = 0; velx=1; vely=1; bool dosave = false; @@ -399,6 +410,8 @@ namespace conformal { int xpos = 0; + vector bands; + SDL_Surface *band = SDL_CreateRGBSurface(SDL_SWSURFACE, min(len, bandsegment), bandfull,32,0,0,0,0); if(!band) { @@ -451,14 +464,13 @@ namespace conformal { 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); + + if(dospiral) + bands.push_back(band); + else + SDL_FreeSurface(band); + len -= bandsegment; xpos -= bandsegment; band = SDL_CreateRGBSurface(SDL_SWSURFACE, min(len, bandsegment), bandfull,32,0,0,0,0); goto drawsegment; @@ -470,12 +482,22 @@ namespace conformal { char buf[128]; sprintf(buf, "bandmodel-%s-%03d" IMAGEEXT, timebuf, segid++); IMAGESAVE(band, buf); + addMessage(XLAT("Saved the band image as: ") + buf); + + if(dospiral) + bands.push_back(band); + else + SDL_FreeSurface(band); + 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); + + if(dospiral) { + spiral::loop(bands); + for(int i=0; i= 1 && k <= 26) { string s = "Ctrl+"; s += (k+64); return s; } if(k < 128) { string s; s += k; return s; } @@ -178,7 +180,7 @@ namespace dialog { int xs, xo; if(sidescreen) - xs = dwidth - vid.fsize, xo = vid.xres + vid.fsize; + xs = dwidth - vid.fsize*2, xo = vid.yres + vid.fsize; else xs = vid.xres * 618/1000, xo = vid.xres * 186/1000; @@ -252,7 +254,7 @@ namespace dialog { if(sidescreen) { dwidth = vid.xres - vid.yres; - dcenter = (vid.xres + dwidth) / 2; + dcenter = vid.xres - dwidth / 2; } while(tothei > vid.yres - 5 * vid.fsize) { diff --git a/google-games.cpp b/google-games.cpp new file mode 100644 index 00000000..8472ddc0 --- /dev/null +++ b/google-games.cpp @@ -0,0 +1,386 @@ +// PRIVATE + +void viewAchievements(); +void viewLeaderboard(string id); +void switchGoogleConnection(); + +namespace leader { + +bool currentlyConnecting(); +bool currentlyConnected(); + +string leaderboardIDs[NUMLEADER] = { + "CgkIspzRq_8NEAIQAg", + "CgkIspzRq_8NEAIQKg", + "CgkIspzRq_8NEAIQLA", + "CgkIspzRq_8NEAIQKw", + "CgkIspzRq_8NEAIQLQ", + + "CgkIspzRq_8NEAIQMA", + "CgkIspzRq_8NEAIQLg", + "CgkIspzRq_8NEAIQNQ", + "CgkIspzRq_8NEAIQNg", + "CgkIspzRq_8NEAIQMg", + + "CgkIspzRq_8NEAIQLw", + "CgkIspzRq_8NEAIQOA", + "CgkIspzRq_8NEAIQOg", + "CgkIspzRq_8NEAIQPA", + "CgkIspzRq_8NEAIQOw", + + "CgkIspzRq_8NEAIQPg", // 15 + "CgkIspzRq_8NEAIQPQ", + "CgkIspzRq_8NEAIQUA", + "CgkIspzRq_8NEAIQMw", + "CgkIspzRq_8NEAIQUQ", + + "CgkIspzRq_8NEAIQVA", // 20 + "CgkIspzRq_8NEAIQUw", + "CgkIspzRq_8NEAIQVQ", + "CgkIspzRq_8NEAIQUg", + "CgkIspzRq_8NEAIQcQ", // Grimories + "CgkIspzRq_8NEAIQcg", // 25 Holy Grails + + "CgkIspzRq_8NEAIQdA", // 26 Red Gems + "CgkIspzRq_8NEAIQcw", // 27 Pirate Treasures + + "CgkIspzRq_8NEAIQ4QE", // 28 Shmup mode + + "CgkIspzRq_8NEAIQ4gE", // 29 Shmup time to win + "CgkIspzRq_8NEAIQ4wE", // 30 Shmup knives to win + + "CgkIspzRq_8NEAIQigE", // 31 bombeggs + "CgkIspzRq_8NEAIQiwE", // 32 ambers + "CgkIspzRq_8NEAIQjAE", // 33 pearls + "CgkIspzRq_8NEAIQjQE", // 34 hyperrugs + "CgkIspzRq_8NEAIQjgE", // 35 garnets + + "CgkIspzRq_8NEAIQ5gE", // 36 princess challenge + + "CgkIspzRq_8NEAIQnAE", // 37 Ivory Figurines + "CgkIspzRq_8NEAIQmgE", // 38 Elemental Gems + "CgkIspzRq_8NEAIQmwE", // 39 Onyxes + + + "CgkIspzRq_8NEAIQwAE", // 40 Yendor Challenge + "CgkIspzRq_8NEAIQ5AE", // 41 Pure Tactics Mode + + "CgkIspzRq_8NEAIQpgE", // 42 Mutant Saplings + "CgkIspzRq_8NEAIQpQE", // 43 Fulgurites + + "", // 44 + "", // 45 + "CgkIspzRq_8NEAIQswE", // 46 Black Lotuses + "CgkIspzRq_8NEAIQsgE", // 47 Mutant Fruits + "CgkIspzRq_8NEAIQsQE", // 48 White Dove + + "CgkIspzRq_8NEAIQ5QE", // 49 PTM shmup + "", // 50 + + + "CgkIspzRq_8NEAIQtQE", // 51 Corals + "CgkIspzRq_8NEAIQtgE", // 52 Thornless Roses + "CgkIspzRq_8NEAIQtwE", // 53 Chaos Mode + + "CgkIspzRq_8NEAIQwwE", // 54 Tortoise Points + "CgkIspzRq_8NEAIQwgE", // 55 Dragon Scales + "CgkIspzRq_8NEAIQwQE", // 56 Apples + "CgkIspzRq_8NEAIQ4AE", // 57 Heptagonal mode + "CgkIspzRq_8NEAIQ3gE", // 58 Sunken treasures + "CgkIspzRq_8NEAIQ3QE", // 59 Ancient Jewelry + "CgkIspzRq_8NEAIQ3wE", // 60 Golden Eggs + + "", // 61 multiplayer + "", // 62 statistics + + "CgkIspzRq_8NEAIQ6gE", // 63 Halloween + + "CgkIspzRq_8NEAIQ9AE", // 64 Amethyst + "CgkIspzRq_8NEAIQ9QE", // 65 Slime Molds + "CgkIspzRq_8NEAIQ9gE", // 66 Dodecahedra + + "CgkIspzRq_8NEAIQ_gE", // 67 Green Grass + "CgkIspzRq_8NEAIQ_QE", // 68 Spinels + }; + +const char* achievementIDs[] = { + "DIAMOND2", "CgkIspzRq_8NEAIQAw", + "SPICE2", "CgkIspzRq_8NEAIQBA", + "GOLD2", "CgkIspzRq_8NEAIQBQ", + "RUBY2", "CgkIspzRq_8NEAIQCQ", + "MIRROR2", "CgkIspzRq_8NEAIQBw", + "FEATHER2", "CgkIspzRq_8NEAIQDA", + "ELIXIR2", "CgkIspzRq_8NEAIQDQ", + "STATUE2", "CgkIspzRq_8NEAIQBg", + "FERN2", "CgkIspzRq_8NEAIQDg", + "TOTEM2", "CgkIspzRq_8NEAIQCA", + "DAISY2", "CgkIspzRq_8NEAIQCw", + "SAPPHIRE2", "CgkIspzRq_8NEAIQDw", + "HYPER2", "CgkIspzRq_8NEAIQCg", + "DIAMOND3", "CgkIspzRq_8NEAIQEA", + "SPICE3", "CgkIspzRq_8NEAIQEQ", + "GOLD3", "CgkIspzRq_8NEAIQEg", + "RUBY3", "CgkIspzRq_8NEAIQEw", + "MIRROR3", "CgkIspzRq_8NEAIQFA", + "FEATHER3", "CgkIspzRq_8NEAIQFQ", + "ELIXIR3", "CgkIspzRq_8NEAIQFg", + "STATUE3", "CgkIspzRq_8NEAIQFw", + "FERN3", "CgkIspzRq_8NEAIQGA", + "TOTEM3", "CgkIspzRq_8NEAIQGQ", + "DAISY3", "CgkIspzRq_8NEAIQGg", + "SAPPHIRE3", "CgkIspzRq_8NEAIQGw", + "HYPER3", "CgkIspzRq_8NEAIQHA", + "DIAMOND4", "CgkIspzRq_8NEAIQHQ", + "SPICE4", "CgkIspzRq_8NEAIQHg", + "GOLD4", "CgkIspzRq_8NEAIQHw", + "RUBY4", "CgkIspzRq_8NEAIQIA", + "MIRROR4", "CgkIspzRq_8NEAIQIQ", + "FEATHER4", "CgkIspzRq_8NEAIQIg", + "ELIXIR4", "CgkIspzRq_8NEAIQIw", + "STATUE4", "CgkIspzRq_8NEAIQJA", + "FERN4", "CgkIspzRq_8NEAIQJQ", + "TOTEM4", "CgkIspzRq_8NEAIQJg", + "DAISY4", "CgkIspzRq_8NEAIQJw", + "SAPPHIRE4", "CgkIspzRq_8NEAIQKA", + "HYPER4", "CgkIspzRq_8NEAIQKQ", + "STABBER2", "CgkIspzRq_8NEAIQPw", + "STABBER3", "CgkIspzRq_8NEAIQQA", + "FLASH2", "CgkIspzRq_8NEAIQQQ", + "FLASH3", "CgkIspzRq_8NEAIQQg", + "LIGHTNING2", "CgkIspzRq_8NEAIQQw", + "LIGHTNING3", "CgkIspzRq_8NEAIQRA", + "FALLDEATH1", "CgkIspzRq_8NEAIQRQ", + "GOLEM2", "CgkIspzRq_8NEAIQRg", + "GOLEM3", "CgkIspzRq_8NEAIQRw", + "MIRAGE", "CgkIspzRq_8NEAIQSA", + "MIRRORKILL2", "CgkIspzRq_8NEAIQSQ", + "MIRRORKILL3", "CgkIspzRq_8NEAIQSg", + "IVYSLAYER", "CgkIspzRq_8NEAIQSw", + "DEMONSLAYER", "CgkIspzRq_8NEAIQTA", + "YENDOR1", "CgkIspzRq_8NEAIQTQ", + "YENDOR2", "CgkIspzRq_8NEAIQTg", + + "WINE2", "CgkIspzRq_8NEAIQVg", + "EMERALD2", "CgkIspzRq_8NEAIQVw", + "SILVER2", "CgkIspzRq_8NEAIQWA", + "JELLY2", "CgkIspzRq_8NEAIQWQ", + "POWER2", "CgkIspzRq_8NEAIQWg", + + "WINE3", "CgkIspzRq_8NEAIQWw", + "EMERALD3", "CgkIspzRq_8NEAIQXQ", + "SILVER3", "CgkIspzRq_8NEAIQXA", + "JELLY3", "CgkIspzRq_8NEAIQXg", + "POWER3 ", "CgkIspzRq_8NEAIQXw", + + "WINE4", "CgkIspzRq_8NEAIQYA", + "EMERALD4", "CgkIspzRq_8NEAIQYQ", + "SILVER4", "CgkIspzRq_8NEAIQYg", + "JELLY4", "CgkIspzRq_8NEAIQYw", + "POWER4", "CgkIspzRq_8NEAIQZA", + + "ORB3", "CgkIspzRq_8NEAIQZQ", + "BUG3", "CgkIspzRq_8NEAIQZg", + "LIFEBRINGER", "CgkIspzRq_8NEAIQZw", + "GARDENER ", "CgkIspzRq_8NEAIQaA", + "MELEE3", "CgkIspzRq_8NEAIQaQ", + "MELEE5", "CgkIspzRq_8NEAIQag", + + "GRIMOIRE2", "CgkIspzRq_8NEAIQaw", + "GRIMOIRE3", "CgkIspzRq_8NEAIQbA", + "GRIMOIRE4", "CgkIspzRq_8NEAIQbQ", + + "GRAIL2", "CgkIspzRq_8NEAIQbg", + "GRAIL3", "CgkIspzRq_8NEAIQbw", + "GRAIL4", "CgkIspzRq_8NEAIQcA", + + "PIRATE2", "CgkIspzRq_8NEAIQdQ", + "PIRATE3", "CgkIspzRq_8NEAIQdg", + "PIRATE4", "CgkIspzRq_8NEAIQdw", + + "REDGEM2", "CgkIspzRq_8NEAIQeA", + "REDGEM3", "CgkIspzRq_8NEAIQeQ", + "REDGEM4", "CgkIspzRq_8NEAIQeg", + + "COAST2", "CgkIspzRq_8NEAIQew", + "COAST3", "CgkIspzRq_8NEAIQfA", + "COAST4", "CgkIspzRq_8NEAIQfQ", + + "WHIRL2", "CgkIspzRq_8NEAIQfg", + "WHIRL3", "CgkIspzRq_8NEAIQfw", + "WHIRL4", "CgkIspzRq_8NEAIQgAE", + + "MINE2", "CgkIspzRq_8NEAIQgQE", + "MINE3", "CgkIspzRq_8NEAIQggE", + "MINE4", "CgkIspzRq_8NEAIQgwE", + + "RUG2", "CgkIspzRq_8NEAIQhAE", + "RUG3", "CgkIspzRq_8NEAIQhQE", + "RUG4", "CgkIspzRq_8NEAIQhgE", + + "GARNET2", "CgkIspzRq_8NEAIQhwE", + "GARNET3", "CgkIspzRq_8NEAIQiAE", + "GARNET4", "CgkIspzRq_8NEAIQiQE", + + "ZEBRA2", "CgkIspzRq_8NEAIQkQE", + "ZEBRA3", "CgkIspzRq_8NEAIQkgE", + "ZEBRA4", "CgkIspzRq_8NEAIQkwE", + "ELEMENT2", "CgkIspzRq_8NEAIQlAE", + "ELEMENT3", "CgkIspzRq_8NEAIQlQE", + "ELEMENT4", "CgkIspzRq_8NEAIQlgE", + "TOWER2", "CgkIspzRq_8NEAIQlwE", + "TOWER3", "CgkIspzRq_8NEAIQmAE", + "TOWER4", "CgkIspzRq_8NEAIQmQE", + + "PRINCESS1", "CgkIspzRq_8NEAIQjwE", + "PRINCESS_PACIFIST", "CgkIspzRq_8NEAIQkAE", + // Achievements.unlock(mGoogleApiClient, "CgkIspzRq_8NEAIQAw"), + + "FULGUR2", "CgkIspzRq_8NEAIQoAE", + "FULGUR3", "CgkIspzRq_8NEAIQoQE", + "FULGUR4", "CgkIspzRq_8NEAIQogE", + "HARDMETAL", "CgkIspzRq_8NEAIQpAE", + "ELEC3", "CgkIspzRq_8NEAIQowE", + "MUTANT2", "CgkIspzRq_8NEAIQnQE", + "MUTANT3", "CgkIspzRq_8NEAIQngE", + "MUTANT4", "CgkIspzRq_8NEAIQnwE", + + "DOVE2", "CgkIspzRq_8NEAIQqwE", + "DOVE3", "CgkIspzRq_8NEAIQrAE", + "DOVE4", "CgkIspzRq_8NEAIQrQE", + "LOTUS2", "CgkIspzRq_8NEAIQpwE", + "LOTUS3", "CgkIspzRq_8NEAIQqAE", + "LOTUS4", "CgkIspzRq_8NEAIQqQE", + "FRUIT2", "CgkIspzRq_8NEAIQrgE", + "FRUIT3", "CgkIspzRq_8NEAIQrwE", + "FRUIT4", "CgkIspzRq_8NEAIQsAE", + "COMEBACK", "CgkIspzRq_8NEAIQtAE", + "SURVIVAL", "CgkIspzRq_8NEAIQqgE", + + "CORAL2", "CgkIspzRq_8NEAIQuAE", + "CORAL3", "CgkIspzRq_8NEAIQuQE", + "CORAL4", "CgkIspzRq_8NEAIQugE", + "ROSE2", "CgkIspzRq_8NEAIQuwE", + "ROSE3", "CgkIspzRq_8NEAIQvAE", + "ROSE4", "CgkIspzRq_8NEAIQvQE", + "CR4", "CgkIspzRq_8NEAIQvgE", + "CHAOS", "CgkIspzRq_8NEAIQvwE", + + "APPLE2", "CgkIspzRq_8NEAIQxAE", + "APPLE3", "CgkIspzRq_8NEAIQxQE", + "APPLE4", "CgkIspzRq_8NEAIQxgE", + "DRAGON2", "CgkIspzRq_8NEAIQxwE", + "DRAGON3", "CgkIspzRq_8NEAIQyAE", + "DRAGON4", "CgkIspzRq_8NEAIQyQE", + "TORTOISE2", "CgkIspzRq_8NEAIQygE", + "TORTOISE3", "CgkIspzRq_8NEAIQywE", + "TORTOISE4", "CgkIspzRq_8NEAIQzAE", + + "KRAKEN2", "CgkIspzRq_8NEAIQ0QE", + "KRAKEN3", "CgkIspzRq_8NEAIQ0gE", + "KRAKEN4", "CgkIspzRq_8NEAIQ0wE", + "BARROW2", "CgkIspzRq_8NEAIQ1AE", + "BARROW3", "CgkIspzRq_8NEAIQ1QE", + "BARROW4", "CgkIspzRq_8NEAIQ1gE", + "TROLL2", "CgkIspzRq_8NEAIQ1wE", + "TROLL3", "CgkIspzRq_8NEAIQ2AE", + "TROLL4", "CgkIspzRq_8NEAIQ2QE", + "GRAILH", "CgkIspzRq_8NEAIQ2gE", + "HARDCORE", "CgkIspzRq_8NEAIQ3AE", + "SHMUP", "CgkIspzRq_8NEAIQ2wE", + "SLASH2", "CgkIspzRq_8NEAIQzwE", + "GOSWORD", "CgkIspzRq_8NEAIQ0AE", + + "HALLOWEEN1", "CgkIspzRq_8NEAIQ5wE", + "HALLOWEEN2", "CgkIspzRq_8NEAIQ6AE", + "GOBLINSWORD", "CgkIspzRq_8NEAIQ6QE", + + "DOD2", "CgkIspzRq_8NEAIQ6wE", + "DOD3", "CgkIspzRq_8NEAIQ7AE", + "DOD4", "CgkIspzRq_8NEAIQ7QE", + "DUNG2", "CgkIspzRq_8NEAIQ7gE", + "DUNG3", "CgkIspzRq_8NEAIQ7wE", + "DUNG4", "CgkIspzRq_8NEAIQ8AE", + "MOUNT2", "CgkIspzRq_8NEAIQ8QE", + "MOUNT3", "CgkIspzRq_8NEAIQ8gE", + "MOUNT4", "CgkIspzRq_8NEAIQ8wE", + + "PRAIR2", "CgkIspzRq_8NEAIQ9wE", + "PRAIR3", "CgkIspzRq_8NEAIQ-AE", + "PRAIR4", "CgkIspzRq_8NEAIQ-QE", + "BULL2", "CgkIspzRq_8NEAIQ-gE", + "BULL3", "CgkIspzRq_8NEAIQ-wE", + "BULL4", "CgkIspzRq_8NEAIQ_AE", + NULL + }; + +const char *getGoogleAchievementID(const string& s) { + const char** a = achievementIDs; + while(*a && **a) { + if(a[0] == s) return a[1]; + a++; + } + return NULL; + } + +int qleader, leaders[NUMLEADER]; + +int pshift; + +void showMenu() { + dialog::init(); + dialog::addItem(XLAT("return to the game"), ' '); + if(!currentlyConnecting()) + dialog::addBoolItem(XLAT("connected to Google Games"), currentlyConnected(), '1'); + else + dialog::addBreak(100); + dialog::addItem(XLAT("view your achievements"), '2'); + dialog::addItem(XLAT("next page"), '3'); + + qleader = 0; + for(int i=0; i= 0 ? its(sco) : SCORE_UNKNOWN ? "?" : "-"; + dialog::addSelItem(leadernames[id], ss, 'a' + i); + } + } + dialog::display(); + } + +void handleKey(int sym, int uni) { + dialog::handleNavigation(sym, uni); + if(uni == '1') + switchGoogleConnection(); + else if(uni == '2') + viewAchievements(); + else if(uni == '3') { + pshift += 10; + if(pshift >= qleader) pshift = 0; + } + else if(uni >= 'a' && uni <= 'a' + 10) { + int id = (uni - 'a') + pshift; + if(id >= qleader) return; + viewLeaderboard(leaderboardIDs[leaders[id]]); + } + else + cmode = emNormal; + } + +bool ccon = false; +bool incon = false; + +bool currentlyConnected() { return ccon; } +bool currentlyConnecting() { return incon; } + +int scale(int i) { if(i == 13 || i == 15 || i == 29 || i == 45) return 1000; else return 1; } + +bool isDescending(int i) { return !isAscending(i); } + +} diff --git a/graph.cpp b/graph.cpp index c52af32d..b67bbd49 100644 --- a/graph.cpp +++ b/graph.cpp @@ -94,6 +94,8 @@ charstyle& getcs() { return vid.cs; } +bool camelotcheat; + int playergender() { return (getcs().charid&1) ? GEN_F : GEN_M; } @@ -3263,8 +3265,15 @@ void setcolors(cell *c, int& wcol, int &fcol) { if(c->land == laCamelot) { int d = showoff ? 0 : ((euclid||c->master->alt) ? celldistAltRelative(c) : 0); - if(d < 0) +#ifdef TOUR + if(!tour::on) camelotcheat = false; + if(camelotcheat) + fcol = (d&1) ? 0xC0C0C0 : 0x606060; + else +#endif + if(d < 0) { fcol = 0xA0A0A0; + } else { // a nice floor pattern int v = emeraldval(c); @@ -4336,7 +4345,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { #ifndef NOEDIT - if(cmode == emMapEditor && mapeditor::displaycodes) { + if(mapeditor::displaycodes) { int labeli = mapeditor::displaycodes == 1 ? mapeditor::realpattern(c) : mapeditor::subpattern(c); @@ -5416,7 +5425,11 @@ string buildHelpText() { #endif h += XLAT("See more on the website: ") + "http//roguetemple.com/z/hyper/\n\n"; - + +#ifdef TOUR + h += XLAT("Try the Tutorial to help with understanding the " + "geometry of HyperRogue (menu -> special modes).\n\n"); +#endif h += XLAT("Still confused? Read the FAQ on the HyperRogue website!\n\n"); @@ -6249,6 +6262,9 @@ void drawthemap() { mouseovers = XLAT("Press F1 or right click for help"); #ifdef ROGUEVIZ if(rogueviz::on) mouseovers = " "; +#endif +#ifdef TOUR + if(tour::on) mouseovers = tour::tourhelp; #endif centdist = 1e20; centerover = NULL; @@ -6283,6 +6299,10 @@ void drawthemap() { #ifdef ROGUEVIZ rogueviz::drawExtra(); #endif + + #ifdef TOUR + if(tour::on) tour::presentation(2); + #endif profile_stop(1); profile_start(4); @@ -6612,9 +6632,9 @@ void calcparam() { vid.ycenter = vid.yres - realradius - vid.fsize - (ISIOS ? 10 : 0); } else { - if(vid.xres >= vid.yres * 4/3 && dialog::sidedialog && cmode == emNumber) + if(vid.xres >= vid.yres * 5/4-16 && dialog::sidedialog && cmode == emNumber) sidescreen = true; - if(viewdists && cmode == emNormal && vid.xres >= vid.yres * 4/3) sidescreen = true; + if(viewdists && cmode == emNormal && vid.xres > vid.yres) sidescreen = true; if(sidescreen) vid.xcenter = vid.yres/2; } @@ -6689,6 +6709,9 @@ string timeline() { void showGameover() { dialog::init( +#ifdef TOUR + tour::on ? (canmove ? XLAT("Tutorial") : XLAT("GAME OVER")) : +#endif cheater ? XLAT("It is a shame to cheat!") : showoff ? XLAT("Showoff mode") : canmove && princess::challenge ? XLAT("%1 Challenge", moPrincess) : @@ -6699,7 +6722,8 @@ void showGameover() { dialog::addInfo(XLAT("Your score: %1", its(gold()))); dialog::addInfo(XLAT("Enemies killed: %1", its(tkills()))); - if(items[itOrbYendor]) { + if(tour::on) ; + else if(items[itOrbYendor]) { dialog::addInfo(XLAT("Orbs of Yendor found: %1", its(items[itOrbYendor])), iinf[itOrbYendor].color); dialog::addInfo(XLAT("CONGRATULATIONS!"), iinf[itOrbYendor].color); } @@ -6728,6 +6752,9 @@ void showGameover() { timerstart = time(NULL); if(princess::challenge) ; +#ifdef TOUR + else if(tour::on) ; +#endif else if(tkills() < 100) dialog::addInfo(XLAT("Defeat 100 enemies to access the Graveyard")); else if(kills[moVizier] == 0 && (items[itFernFlower] < 5 || items[itGold] < 5)) @@ -6773,15 +6800,40 @@ void showGameover() { } dialog::addBreak(100); - dialog::addItem(canmove ? "continue" : "see how it ended", SDLK_ESCAPE); - dialog::addItem("main menu", 'v'); - dialog::addItem("restart", SDLK_F5); - #ifndef MOBILE - dialog::addItem(quitsaves() ? "save" : "quit", SDLK_F10); - #endif - #ifdef ANDROIDSHARE - dialog::addItem("SHARE", 's'-96); - #endif + + bool intour = false; + +#ifdef TOUR + intour = tour::on; +#endif + + if(intour) { + if(canmove) { + dialog::addItem("spherical geometry", '1'); + dialog::addItem("Euclidean geometry", '2'); + dialog::addItem("more curved hyperbolic geometry", '3'); + } + dialog::addItem("teleport away", '4'); + if(canmove) { + dialog::addItem("slide-specific command", '5'); + dialog::addItem("static mode", '6'); + dialog::addItem("enable/disable texts", '7'); + dialog::addItem("next slide", SDLK_RETURN); + dialog::addItem("previous slide", SDLK_BACKSPACE); + } + dialog::addItem("main menu", 'v'); + } + else { + dialog::addItem(canmove ? "continue" : "see how it ended", SDLK_ESCAPE); + dialog::addItem("main menu", 'v'); + dialog::addItem("restart", SDLK_F5); + #ifndef MOBILE + dialog::addItem(quitsaves() ? "save" : "quit", SDLK_F10); + #endif + #ifdef ANDROIDSHARE + dialog::addItem("SHARE", 's'-96); + #endif + } dialog::display(); @@ -7118,12 +7170,12 @@ void drawStats() { dialog::addInfo(its(qty[i]), distcolors[i&7]); if(geometry == gNormal && !purehepta) { dialog::addBreak(200); - dialog::addInfo("a(d+4) = a(d+3) + a(d+2) + a(d+1) - a(d)", 0xFFFFFF); + dialog::addHelp("a(d+4) = a(d+3) + a(d+2) + a(d+1) - a(d)"); dialog::addInfo("a(d) ~ 1.72208^d", 0xFFFFFF); } if(geometry == gNormal && purehepta) { dialog::addBreak(200); - dialog::addInfo("a(d+2) = 3a(d+1) - a(d+2)", 0xFFFFFF); + dialog::addHelp("a(d+2) = 3a(d+1) - a(d+2)"); dialog::addInfo("a(d) ~ 2.61803^d", 0xFFFFFF); } if(geometry == gEuclid) { @@ -7572,8 +7624,12 @@ void drawscreen() { } #ifndef MOBILE - if(cmode == emNormal || cmode == emVisual1 || cmode == emVisual2 || cmode == emChangeMode ) - displayButton(vid.xres-8, vid.yres-vid.fsize, XLAT("(v) menu"), 'v', 16); + if(cmode == emNormal || cmode == emVisual1 || cmode == emVisual2 || cmode == emChangeMode ) { + if(tour::on) + displayButton(vid.xres-8, vid.yres-vid.fsize, XLAT("(ESC) tour menu"), SDLK_ESCAPE, 16); + else + displayButton(vid.xres-8, vid.yres-vid.fsize, XLAT("(v) menu"), 'v', 16); + } #endif if(cmode == emQuit) { @@ -8321,6 +8377,10 @@ void handleKeyNormal(int sym, int uni, extra& ev) { void handlekey(int sym, int uni, extra& ev) { +#ifdef TOUR + if(tour::on && tour::handleKeyTour(sym, uni)) return; +#endif + if(((cmode == emNormal && canmove) || (cmode == emQuit && !canmove) || cmode == emDraw || cmode == emMapEditor) && DEFAULTCONTROL && !rug::rugged) { #ifndef PANDORA if(sym == SDLK_RIGHT) diff --git a/hyper.cpp b/hyper.cpp index 0a39cd36..af190e1a 100644 --- a/hyper.cpp +++ b/hyper.cpp @@ -241,6 +241,15 @@ else if(args()[0] == '-' && args()[1] == x && args()[2] == '0') { PHASE(2); para else if(argis("--run")) { PHASE(3); mainloop(); quitmainloop = false; } +#ifdef TOUR + else if(argis("--tour")) { + PHASE(3); tour::start(); + } + else if(argis("--presentation")) { + PHASE(3); tour::texts = false; + tour::start(); + } +#endif else if(argis("--draw")) { PHASE(3); drawscreen(); } diff --git a/hyper.h b/hyper.h index f44a29fe..9e85226a 100644 --- a/hyper.h +++ b/hyper.h @@ -1127,3 +1127,24 @@ void clearHexes(heptagon *at); void verifycells(heptagon *at); int zebra40(cell *c); cell *createMov(cell *c, int d); + +#ifndef MOBWEB +#ifndef TOUR +#define TOUR +#endif +#endif + +#ifdef TOUR +namespace tour { + extern bool on; + extern string tourhelp; + + bool handleKeyTour(int sym, int uni); + void presentation(int mode); + int getid(); + + eLand getNext(eLand old); + bool quickfind(eLand next); + void start(); + }; +#endif diff --git a/init.cpp b/init.cpp index cb5e78d4..b691a6b3 100644 --- a/init.cpp +++ b/init.cpp @@ -1,6 +1,6 @@ -#define VER "9.4e" -#define VERNUM 9405 -#define VERNUM_HEX 0x9405 +#define VER "9.4f" +#define VERNUM 9406 +#define VERNUM_HEX 0x9406 #define GEN_M 0 #define GEN_F 1 @@ -162,6 +162,9 @@ const char *loadlevel = NULL; #include "graph.cpp" #include "sound.cpp" #include "achievement.cpp" +#ifdef TOUR +#include "tour.cpp" +#endif #ifndef MOBILE #include #endif diff --git a/landgen.cpp b/landgen.cpp index 0dd11416..39db3dcd 100644 --- a/landgen.cpp +++ b/landgen.cpp @@ -1582,6 +1582,13 @@ eLand getNewLand(eLand old) { // return landtab[items[itStrongWind]++ % 10]; // if(old != laPrairie) return laRiver; + #ifdef TOUR + if(tour::on) { + eLand l = tour::getNext(old); + if(l) return l; + } + #endif + #ifdef LOCAL extern bool doAutoplay; if(doAutoplay) @@ -2680,6 +2687,14 @@ void setLandQuotient(cell *c) { setland(c, eLand(laEFire + (fv%4))); } +bool quickfind(eLand l) { + if(l == cheatdest) return true; +#ifdef TOUR + if(tour::on && tour::quickfind(l)) return true; +#endif + return false; + } + void setLandSphere(cell *c) { if(euclidland == laWarpCoast) setland(c, getHemisphere(c, 0) > 0 ? laWarpCoast : laWarpSea); @@ -2908,7 +2923,7 @@ void buildBigStuff(cell *c, cell *from) { } if((!chaosmode) && bearsCamelot(c->land) && c->type == 7 && - (cheatdest == laCamelot || (hrand(2000) < 200 && + (quickfind(laCamelot) || (hrand(2000) < 200 && items[itEmerald] >= 5 && !tactic::on))) { int rtr = newRoundTableRadius(); heptagon *alt = createAlternateMap(c, rtr+14, hsOrigin); @@ -2923,18 +2938,18 @@ void buildBigStuff(cell *c, cell *from) { // buildbigstuff if(c->land == laRlyeh && c->type == 7 && - (cheatdest == laTemple || (hrand(2000) < 100 && + (quickfind(laTemple) || (hrand(2000) < 100 && items[itStatue] >= 5 && !randomPatternsMode && !tactic::on && !yendor::on))) createAlternateMap(c, 2, hsA); if(c->land == laJungle && c->type == 7 && - (cheatdest == laMountain || (hrand(2000) < 100 && + (quickfind(laMountain) || (hrand(2000) < 100 && !randomPatternsMode && !tactic::on && !yendor::on && landUnlocked(laMountain)))) createAlternateMap(c, 2, hsA); if(c->land == laOvergrown && c->type == 7 && - (cheatdest == laClearing || (hrand(2000) < 25 && + (quickfind(laClearing) || (hrand(2000) < 25 && !randomPatternsMode && items[itMutant] >= 5 && !tactic::on && !yendor::on))) { heptagon *h = createAlternateMap(c, 2, hsA); @@ -2947,7 +2962,7 @@ void buildBigStuff(cell *c, cell *from) { } if(c->land == laOcean && c->type == 7 && deepOcean && !generatingEquidistant && - (cheatdest == laWhirlpool || ( + (quickfind(laWhirlpool) || ( hrand(2000) < (purehepta ? 500 : 1000) && !tactic::on && !yendor::on))) createAlternateMap(c, 2, hsA); @@ -3555,6 +3570,7 @@ void setdist(cell *c, int d, cell *from) { } c->wparam = 1 + hrand(reptilemax()); } + // c->wall = waReptile; c->wparam = 100; } if(c->land == laBurial) { @@ -4659,7 +4675,10 @@ void setdist(cell *c, int d, cell *from) { c->monst = moTortoise; c->hitpoints = 3; } - if((tactic::on || euclid) && hrand(4000) < 50 + items[itBabyTortoise]*2 && !safety) { + + int chance = 50 + items[itBabyTortoise]*2; + if(quickfind(laTortoise)) chance += 150; + if((tactic::on || euclid) && hrand(4000) < chance && !safety) { c->item = itBabyTortoise; tortoise::babymap[c] = getBits(c) ^ tortoise::getRandomBits(); } @@ -4885,7 +4904,7 @@ void setdist(cell *c, int d, cell *from) { if(c->land == laBurial && !safety) { if(hrand(15000) < 5 + 3 * items[itBarrow] + 4 * hard) c->monst = moDraugr; - else if(hrand(5000) < 20) + else if(hrand(5000) < 20 + (quickfind(laBurial) ? 40 : 0)) c->item = itOrbSword; } if(c->land == laJungle) { diff --git a/local.cpp b/local.cpp new file mode 100644 index 00000000..cbea3179 --- /dev/null +++ b/local.cpp @@ -0,0 +1,1139 @@ +// PRIVATE + +void saveAchShot(const char *b, double scale) { + + for(int i=0; i<2; i++) { + SDL_Surface *sav = s; + s = SDL_CreateRGBSurface(SDL_SWSURFACE,512,512,32,0,0,0,0); + + videopar vid2 = vid; + vid.xres = vid.yres = 512; vid.scale = scale; + bool bb = vid.usingGL; vid.usingGL = false; + calcparam(); + + darken = 0; + ptds.clear(); + drawthemap(); + + SDL_FillRect(s, NULL, i ? 0xFFFFFF : 0); + + drawqueue(); + ptds.clear(); + drawthemap(); + + char buf[128]; sprintf(buf, "steam/ach/%d%s.bmp", i, b); + SDL_SaveBMP(s, buf); + SDL_FreeSurface(s); s = sav; vid = vid2; + vid.usingGL = bb; + } + } + +void saveFrame(int fr) { + + fanframe = fr / 30.0; + + for(int i=0; i<1; i++) { + SDL_Surface *sav = s; + s = SDL_CreateRGBSurface(SDL_SWSURFACE,2048,2048,32,0,0,0,0); + + videopar vid2 = vid; + vid.xres = vid.yres = 2048; vid.scale = 1; + bool bb = vid.usingGL; vid.usingGL = false; + calcparam(); + + int ssr = sightrange; sightrange = 10; int sch = cheater; cheater = 0; + + darken = 0; + ptds.clear(); + mapeditor::drawplayer = false; + drawthemap(); + + SDL_FillRect(s, NULL, i ? 0xFFFFFF : 0); + + drawqueue(); + + char buf[128]; sprintf(buf, "fanframe/image%02d.bmp", fr); + SDL_SaveBMP(s, buf); + SDL_FreeSurface(s); s = sav; vid = vid2; + vid.usingGL = bb; + + cheater = sch; sightrange = ssr; + } + } + +int fadeoutat = -1; +bool before; +int tv = -1; +int textat; +string landmsg; +string landmsg2; +int landcol; +eLand nextland; + +void process_local_extra() { + if(fadeoutat != -1) { + fadeout = 1 - (SDL_GetTicks() - fadeoutat) / 1000.0; + if(fadeout < -1) { fadeoutat = -1; fadeout = 1; nextland = laNone; return; } + if(fadeout < 0) { + fadeout = -fadeout; + if(before) { + msgs.clear(); + textat = SDL_GetTicks(); + before = false; + firstland = nextland; + landmsg = linf[firstland].name; + landmsg2 = ""; + landcol = linf[firstland].color; + if(shmup::on) restartGame('s'); + if(euclid) restartGame('e'); + tactic::trailer = true; + if(!tactic::on) restartGame('t'); + else restartGame(); + + /* if(firstland == laCamelot) { + firstland = laIce; + landmsg = "Shmup Mode"; + landmsg2 = "you can play with your friends!"; + shmup::players = 2; + restartGame('t'); + restartGame('s'); + } + + if(firstland == laMirror) { + landmsg2 = "featuring the Euclidean mode!"; + restartGame('e'); + } */ + + if(firstland == laWildWest) + landmsg2 = "(bonus land)"; + + // vid.female = hrand(2); + + if(firstland == laRedRock) + landcol = 0xFF0000; + + /* if(firstland == laPalace) + cwt.c->mov[0]->monst = moPrincessArmed, + cwt.c->mov[0]->hitpoints = 7, + landmsg2 = "featuring the Princess!", + vid.female = false, + vid.samegender = false; */ + + if(isCrossroads(firstland)) { + if(firstland == laCrossroads) landmsg2 = "and the Camelot!", + tactic::on = false; + for(int i=0; i= textat && ticks < textat + 2500) { + int dt = SDL_GetTicks()-textat; + int col = gradient(0, landcol, 0, min(dt, 1000), 1000); + displayfr(vid.xres/2, vid.yres/4, 2, vid.fsize * 2, landmsg, col, 8); + displayfr(vid.xres/2, vid.yres/4 + vid.fsize*2, 2, vid.fsize, landmsg2, col, 8); + } + } + +bool nomusic = false; // true; + +bool local_changemusic(eLand& id) { + if(nomusic) return true; + if(nextland) id = nextland; + return false; + } + +int syserr; + +int tab[3000][3000]; + +vector gtab[600][600][20]; + +int gromovprod(int x, int y, int o) { + return tab[o][x] + tab[o][y] - tab[x][y]; + } + +void gromov1() { + int N = size(dcal); + + for(int i=0; i& v) { + int maxd = -1; + for(int i=0; i maxd) maxd = tab[a][v[i]]; + return maxd; + } + +void gromov() { + static bool done = false; + if(done) return; + done = true; + + int N = 120; // size(dcal); + + for(int i=0; i fdelta) { + fdelta = mindelta; + printf("found delta of %d [%d %d %d]\n", fdelta, i, j, k); + fi=i; fj=j; fk=k; + } + } + } + + dcal[fi]->item = itRuby; + dcal[fj]->item = itAmethyst; + dcal[fk]->item = itCompass; + + for(int i=0; iwall = waFloorA; + if(tab[i][fj] + tab[i][fk] == tab[fj][fk]) dcal[i]->wall = waFloorB; + if(tab[i][fk] + tab[i][fi] == tab[fk][fi]) dcal[i]->wall = waFloorC; + } + + gromov1(); + } + +void process_local0(int sym) { + + // gromov(); + + if(sym == SDLK_F4) { + fadeoutat = SDL_GetTicks(); + before = true; + tv++; + tv %= LAND_TAC; + nextland = land_tac[tv].l; + vid.axes = 0; + if(nextland == laCamelot) process_local0(sym); + return; + } + +/* + if(sym == SDLK_F6 && nomusic && !items[itOrbFriend]) { nomusic = false; return; } */ + + /* if(sym == SDLK_F9) { + syserr = system("killall recordmydesktop"); + } + + if(sym == SDLK_F8) { + syserr = system("killall -s SIGUSR1 recordmydesktop"); + } */ + + } + +void process_local(int sym) { + if(sym == SDLK_F9) { + showoff = true; showid = 0; + for(int i=0; iland = laCamelot; + cwt.c->item = itHolyGrail; + for(int t=0; ttype; t++) + cwt.c->mov[t]->wall = waChasm; + saveAchShot("grailh", 5); + return; + } + + if(sphere && cwt.c->land == laHalloween) { + mapeditor::drawplayer = false; + vid.alpha = 999; + View = spin(M_PI); + saveAchShot("halloween", 998); + return; + } + + for(int t=0; ttype; t++) + cwt.c->mov[t]->wall = waChasm; + +/* mapeditor::drawplayer = false; + cwt.c->land = laDryForest; + cwt.c->item = itFernFlower; + for(int t=0; ttype; t++) + cwt.c->mov[t]->wall = waChasm; + saveAchShot("fern", 5); + + cwt.c->land = laPower; + cwt.c->item = itPower; + cwt.c->type = 6; + cwt.c->wall = waGlass; + saveAchShot("power", 5); + + cwt.c->item = itOrbFire; + cwt.c->wall = waNone; + saveAchShot("fireorb", 5); */ + + cwt.c->type = 7; + +/* cwt.c->land = laHive; + cwt.c->item = itRoyalJelly; + saveAchShot("jelly", 5); + + cwt.c->land = laDeadCaves; + cwt.c->item = itSilver; + saveAchShot("silver", 5); + + cwt.c->land = laEmerald; + cwt.c->item = itEmerald; + saveAchShot("emerald", 5); + + cwt.c->land = laWineyard; + cwt.c->item = itWine; + saveAchShot("wine", 5); + + cwt.c->land = laEmerald; + cwt.c->item = itNone; + cwt.c->monst = moFlailer; + saveAchShot("flailer", 5); + + cwt.c->land = laHive; + cwt.c->item = itNone; + cwt.c->monst = moBug2; + saveAchShot("bug", 5); + + cwt.c->land = laCaves; + cwt.c->item = itNone; + cwt.c->monst = moNone; + cwt.c->wall = waCavewall; + saveAchShot("cavea", 5); + + cwt.c->land = laCrossroads; + cwt.c->item = itNone; + cwt.c->monst = moNone; + cwt.c->wall = waCavewall; + saveAchShot("caveb", 5); + + cwt.c->wall = waNone; + cwt.c->item = itNone; + cwt.c->monst = moHedge; + saveAchShot("hedge", 5); + + cwt.c->item = itSapphire; + cwt.c->monst = moNone; + cwt.c->land = laCocytus; + saveAchShot("sapphire", 5); + + cwt.c->item = itGrimoire; + cwt.c->monst = moNone; + cwt.c->land = laTemple; + saveAchShot("grimoire", 5); + + cwt.c->item = itHolyGrail; + cwt.c->monst = moNone; + cwt.c->land = laCamelot; + saveAchShot("grail", 5); + + cwt.c->item = itNone; + cwt.c->monst = moKnight; + cwt.c->land = laCamelot; + cwt.c->wall = waChasm; + saveAchShot("knight", 5); + + cwt.c->monst = moFlailer; + cwt.c->land = laCamelot; + cwt.c->wall = waChasm; + saveAchShot("flailer", 5); + + cwt.c->monst = moNone; + cwt.c->item = itHolyGrail; + cwt.c->land = laCamelot; + cwt.c->wall = waChasm; + saveAchShot("grail-emo", 5); + + cwt.c->monst = moNone; + cwt.c->item = itPirate; + cwt.c->land = laCaribbean; + cwt.c->wall = waCIsland; + saveAchShot("pirate", 5); + + cwt.c->monst = moNone; + cwt.c->item = itRedGem; + cwt.c->land = laRedRock; + cwt.c->wall = waRed3; + saveAchShot("redgem", 5); + + cwt.c->monst = moNone; + cwt.c->item = itWhirlpool; + cwt.c->land = laWhirlpool; + cwt.c->wall = waBoat; + cwt.c->master->alt = cwt.c->master; + cwt.c->master->distance = 0; + saveAchShot("whirlpool", 5); + + cwt.c->monst = moNone; + cwt.c->item = itCoast; + cwt.c->land = laOcean; + cwt.c->wall = waNone; + saveAchShot("amber", 5); + + cwt.c->monst = moNone; + cwt.c->item = itBombEgg; + cwt.c->land = laMinefield; + cwt.c->wall = waMineUnknown; + saveAchShot("bombegg", 5); + + cwt.c->monst = moNone; + cwt.c->item = itPalace; + cwt.c->land = laPalace; + cwt.c->wall = waNone; + saveAchShot("hyperrug", 5); + + cwt.c->monst = moNone; + cwt.c->item = itFjord; + cwt.c->land = laLivefjord; + cwt.c->wall = waNone; + saveAchShot("garnet", 5); + + cwt.c->monst = moPrincess; + cwt.c->item = itNone; + cwt.c->land = laPalace; + cwt.c->wall = waNone; + saveAchShot("princess_palace", 5); + + cwt.c->monst = moPrincess; + cwt.c->item = itNone; + cwt.c->land = laCrossroads; + cwt.c->wall = waNone; + saveAchShot("princess_cross", 5); + + cwt.c->monst = moPrincess; + cwt.c->item = itNone; + cwt.c->land = laEmerald; + cwt.c->wall = waNone; + saveAchShot("princess_emerald", 5); */ + +/* + cwt.c->monst = moNone; + cwt.c->item = itFireShard; + cwt.c->land = laEFire; + saveAchShot("elfire", 5); + + cwt.c->monst = moNone; + cwt.c->item = itWaterShard; + cwt.c->land = laEWater; + saveAchShot("elwater", 5); + + cwt.c->monst = moNone; + cwt.c->item = itAirShard; + cwt.c->land = laEAir; + saveAchShot("elair", 5); + + cwt.c->monst = moNone; + cwt.c->item = itEarthShard; + cwt.c->land = laEEarth; + saveAchShot("elearth", 5); + + cwt.c->monst = moNone; + cwt.c->item = itZebra; + cwt.c->land = laZebra; + saveAchShot("zebra", 5); + + cwt.c->monst = moNone; + cwt.c->item = itFulgurite; + cwt.c->land = laStorms; + saveAchShot("fulgurite", 5); + + cwt.c->monst = moNone; + cwt.c->item = itMutant; + cwt.c->land = laOvergrown; + saveAchShot("mutant", 5); */ + +/* cwt.c->monst = moMetalBeast2; + cwt.c->item = itNone; + cwt.c->land = laStorms; + saveAchShot("metalbeast", 5); */ + +/* + cwt.c->monst = moNone; + cwt.c->item = itLotus; + cwt.c->land = laHaunted; + saveAchShot("lotus", 5); + + cwt.c->monst = moGhost; + cwt.c->item = itLotus; + cwt.c->land = laHaunted; + saveAchShot("lotusghost", 5); + + cwt.c->monst = moNone; + cwt.c->item = itWindstone; + cwt.c->land = laWhirlwind; + saveAchShot("dove", 5); + + cwt.c->monst = moNone; + cwt.c->item = itMutant2; + cwt.c->land = laClearing; + saveAchShot("fruit", 5); */ + +/* mapeditor::drawplayer = true; + cwt.c->item = itNone; + cwt.c->land = laIce; + cwt.c->monst = moNone; + saveAchShot("rogue", 5); */ + +/* cwt.c->monst = moNone; + cwt.c->land = laMinefield; + cwt.c->wall = waMineOpen; + cwt.c->item = itNone; + for(int t=0; ttype; t++) + cwt.c->mov[t]->land = laMinefield, + cwt.c->mov[t]->wall = waMineMine; + saveAchShot("sevenmine", 5); + + cwt.c->monst = moCShark; + cwt.c->land = laCamelot; + cwt.c->item = itNone; + cwt.c->wall = waCamelotMoat; + cwt.c->mov[0]->wall = waCamelotMoat; + cwt.c->mov[1]->wall = waCamelot; + cwt.c->mov[2]->wall = waCamelotMoat; + cwt.c->mov[3]->wall = waNone; + cwt.c->mov[4]->wall = waNone; + cwt.c->mov[5]->wall = waNone; + cwt.c->mov[6]->wall = waNone; + for(int t=0; ttype; t++) + cwt.c->mov[t]->land = t<3? laCamelot : laCrossroads; + saveAchShot("moatshark", 5); + + if(euclid) { + vid.female = true; + cwt.c->item = itNone; + cwt.c->monst = moMirage; + cwt.c->land = laMirror; + cwt.c->wall = waNone; + s veAchShot("mirage", 9); + } */ + + /* + cwt.c->monst = moNone; + cwt.c->item = itCompass; + cwt.c->land = laCaribbean; + cwt.c->wall = waChasm; + saveAchShot("compass-emo", 5); + + cwt.c->monst = moPirate; + cwt.c->item = itNone; + cwt.c->land = laCaribbean; + cwt.c->wall = waChasm; + saveAchShot("pirate-emo", 5); + + cwt.c->monst = moBullet; + cwt.c->item = itOrbYendor; + cwt.c->land = laHell; + cwt.c->wall = waNone; + saveAchShot("knife", 5);*/ + +/* + mapeditor::drawplayer = false; + cwt.c->item = itRose; + cwt.c->land = laRose; + cwt.c->monst = moNone; + cwt.c->wall = waNone; + saveAchShot("rose", 5); */ + +/* + mapeditor::drawplayer = false; + cwt.c->item = itCoral; + cwt.c->land = laWarpSea; + cwt.c->monst = moNone; + for(int i=0; itype; i++) + cwt.c->mov[i]->land = laWarpSea; + cwt.c->landparam = 1337; + cwt.c->wall = waBoat; + saveAchShot("coral", 5); */ +/* + bfs(); + for(int i=0; i<200; i++) { + cell *c = dcal[i]; + c->wall = waChasm, + c->item = itNone; + } + + mapeditor::drawplayer = false; + cwt.c->item = itApple; + cwt.c->land = laEndorian; + cwt.c->monst = moNone; + cwt.c->wall = waCanopy; + saveAchShot("apple", 5); + + mapeditor::drawplayer = false; + cwt.c->item = itDragon; + cwt.c->land = laDragon; + cwt.c->monst = moNone; + cwt.c->wall = waNone; + saveAchShot("dragon", 4); + + mapeditor::drawplayer = false; + cwt.c->item = itBabyTortoise; + cwt.c->land = laTortoise; tortoise::babymap[cwt.c] = 0; + cwt.c->monst = moTortoise; tortoise::emap[cwt.c] = cwt.c; + cwt.c->wall = waNone; + saveAchShot("tortoise", 4); */ + +/* + mapeditor::drawplayer = false; + cwt.c->item = itKraken; + cwt.c->land = laKraken; + cwt.c->monst = moNone; + cwt.c->wall = waSea; + items[itOrbInvis] = 10; + saveAchShot("kraken", 5); + items[itOrbInvis] = 0; + + mapeditor::drawplayer = false; + cwt.c->item = itBarrow; + cwt.c->land = laBurial; + cwt.c->monst = moNone; + cwt.c->wall = waBarrowDig; + cwt.c->landparam = 2; + saveAchShot("burial", 5); + + forCellEx(c2, cwt.c) forCellEx(c3, c2) forCellEx(c4, c3) + c4->wall = waChasm; + + mapeditor::drawplayer = true; + mousing = true; + cwt.c->item = itNone; + cwt.c->land = laBurial; + cwt.c->monst = moNone; + cwt.c->wall = waNone; + items[itOrbSword] = 10; + saveAchShot("swordslash", 3); + items[itOrbSword] = 0; + + mapeditor::drawplayer = false; + cwt.c->item = itTrollEgg; + cwt.c->land = laTrollheim; + cwt.c->monst = moNone; + cwt.c->wall = waNone; + saveAchShot("troll", 5); */ + +/* + cwt.c->land = laCrossroads4; + cwt.c->wall = waNone; + cwt.c->item = itNone; + cwt.c->mov[0]->wall = waCavewall; + cwt.c->mov[0]->land = laCaves; + cwt.c->mov[1]->wall = waColumn; + cwt.c->mov[1]->land = laRlyeh; + cwt.c->mov[3]->wall = waRose; + cwt.c->mov[3]->land = laRose; + cwt.c->mov[2]->wall = waDune; + cwt.c->mov[2]->land = laDesert; + cwt.c->mov[4]->wall = waPalace; + cwt.c->mov[4]->land = laPalace; + cwt.c->mov[5]->wall = waIcewall; + cwt.c->mov[5]->land = laIce; + cwt.c->mov[6]->wall = waVinePlant; + cwt.c->mov[6]->land = laWineyard; + mapeditor::drawplayer = true; + + saveAchShot("cr4", 2.3); */ + +/* mapeditor::drawplayer = false; + cwt.c->land = laCaves; + cwt.c->monst = moGoblin; + cwt.c->item = itNone; + cwt.c->wall = waCavefloor; + saveAchShot("goblin", 5); */ + +/* mapeditor::drawplayer = false; + cwt.c->land = laReptile; + cwt.c->monst = moNone; + cwt.c->item = itDodeca; + cwt.c->wall = waReptile; + cwt.c->wparam = 30; + saveAchShot("dod", 5); + + mapeditor::drawplayer = false; + cwt.c->land = laDungeon; + cwt.c->monst = moNone; + cwt.c->item = itSlime; + cwt.c->wall = waNone; + saveAchShot("slime", 5); + + mapeditor::drawplayer = false; + cwt.c->land = laMountain; + cwt.c->monst = moNone; + cwt.c->item = itAmethyst; + cwt.c->wall = waBigBush; + saveAchShot("amethyst", 5); */ + + mapeditor::drawplayer = false; + cwt.c->land = laDesert; + cwt.c->monst = moWorm; + cwt.c->item = itNone; + cwt.c->wall = waNone; + saveAchShot("wormhead", 5); + + mapeditor::drawplayer = false; + cwt.c->land = laPrairie; + cwt.c->monst = moNone; + cwt.c->item = itGreenGrass; + cwt.c->wall = waNone; + saveAchShot("ggrass", 5); + + mapeditor::drawplayer = false; + cwt.c->land = laBull; + cwt.c->monst = moNone; + cwt.c->item = itBull; + cwt.c->wall = waNone; + saveAchShot("bull", 5); + +/* + forCellEx(c2, cwt.c) forCellEx(c3, c2) forCellEx(c4, c3) + c4->wall = waChasm, c4->item = itNone; + + cwt.c->land = laHell; + cwt.c->wall = waNone; + cwt.c->item = itOrbYendor; + for(int i=0; i<7; i++) cwt.c->mov[i]->item = itOrbYendor; + mapeditor::drawplayer = false; + cwt.c->mov[0]->wall = waBoat; + cwt.c->mov[0]->land = laOcean; + + cwt.c->mov[1]->wall = waNone; + cwt.c->mov[1]->land = laGraveyard; + cwt.c->mov[2]->wall = waNone; + cwt.c->mov[2]->land = laJungle; + cwt.c->mov[3]->wall = waNone; + cwt.c->mov[3]->land = laOvergrown; + cwt.c->mov[4]->wall = waFloorA; + cwt.c->mov[4]->land = laAlchemist; + cwt.c->mov[5]->wall = waNone; + cwt.c->mov[5]->land = laPalace; + cwt.c->mov[6]->wall = waNone; + cwt.c->mov[6]->land = laWineyard; + + saveAchShot("yendc", 2.3); + + cwt.c->land = laIce; + cwt.c->wall = waNone; + cwt.c->item = itDiamond; + mapeditor::drawplayer = false; + cwt.c->mov[0]->wall = waNone; + cwt.c->mov[0]->land = laDesert; + cwt.c->mov[0]->item = itSpice; + + cwt.c->mov[1]->wall = waCavefloor; + cwt.c->mov[1]->land = laCaves; + cwt.c->mov[1]->item = itGold; + + cwt.c->mov[2]->wall = waNone; + cwt.c->mov[2]->land = laJungle; + cwt.c->mov[2]->item = itRuby; + + cwt.c->mov[3]->wall = waNone; + cwt.c->mov[3]->land = laRlyeh; + cwt.c->mov[3]->item = itStatue; + + cwt.c->mov[4]->wall = waFloorA; + cwt.c->mov[4]->land = laAlchemist; + cwt.c->mov[4]->item = itElixir; + + cwt.c->mov[5]->wall = waNone; + cwt.c->mov[5]->land = laMotion; + cwt.c->mov[5]->item = itFeather; + + cwt.c->mov[6]->wall = waNone; + cwt.c->mov[6]->land = laHell; + cwt.c->mov[6]->item = itHell; + + saveAchShot("ptm", 2.3); */ + } + } + +void verifyHell() { + for(int i=0; i<50000; i++) { + activateSafety(laHell); + while(true) { + setdist(cwt.c, 5, NULL); + cwstep(cwt); + if(cwt.c->wall != waNone) break; + cwspin(cwt, 3); + } + cwstep(cwt); + cellwalker z = cwt; + int steps = 0; + do { + setdist(cwt.c, 6, NULL); + cwspin(cwt, 1); + cwstep(cwt); + if(cwt.c->wall != waNone) { + cwt.c->item = itGreenStone; + cwstep(cwt); + } + else { + if(cwt.c->item != itDiamond && cwt.c->item != itGreenStone) { + cwt.c->item = itDiamond; + steps++; + } + cwspin(cwt, 1); + } + } + while(z.c != cwt.c || z.spin != cwt.spin); + if(steps == 0) {i--; continue; } + if(i % 20 == 0) { printf("\n"); fflush(stdout); } + printf("%d,", steps); + if(steps > 100000000 || steps < 2) { fflush(stdout); break; } + } + } + +bool sameland(eLand ll, eLand ln) { + if(ln == laBarrier || ln == laOceanWall) + return true; + if(ln == ll) return true; + if(isElemental(ln) && isElemental(ll)) return true; + if(isHaunted(ln) && isHaunted(ll)) return true; + return false; + } + +bool doAutoplay; + +void autoplay() { + // drawMesh(); + // exit(0); + + doAutoplay = true; + + cheater = 1; + eLand lland = laIce; + eLand lland2 = laIce; + int lcount = 0; + int gcount = 0; + int lastgold = 0; + + int lastdraw = 0; + while(doAutoplay) { + + if(gold() > lastgold) { + lastgold = gold(); + gcount = 0; + } + else gcount++; + + if(sameland(lland, cwt.c->land)) lcount++; + else { + lcount = 0; lland2 = lland; lland = cwt.c->land; + printf("%10dcc %5dt %5de %5d$ %5dK %5dgc %-30s\n", cellcount, turncount, celldist(cwt.c), gold(), tkills(), gcount, dnameof(cwt.c->land)); + if(int(SDL_GetTicks()) > lastdraw + 3000) { + lastdraw = SDL_GetTicks(); + extern void fullcenter(); + extern void mainloop(); + fullcenter(); + msgs.clear(); + drawscreen(); + } + //mainloop(); + } + int i = rand() % cwt.c->type; + cell *c2 = cwt.c->mov[i]; + if(gcount < 500) for(int i=1; i= 50 && !sameland(lland, c2->land) && !sameland(lland2, c2->land)) break; + else if(lcount < 50 && c2->item && c2->item != itOrbSafety) break; + } + items[rand() % ittypes] = 1 << (rand() % 11); + kills[rand() % motypes] = 1 << (rand() % 11); + items[itOrbYendor] &= 15; + again: + if(c2->cpdist > 1) { + for(int j=0; jtype; j++) + if(c2->mov[j] && c2->mov[j]->cpdist < c2->cpdist) { + c2 = c2->mov[j]; + goto again; + } + } + killMonster(c2, moNone); + jumpTo(c2, itNone); + if(false) if(turncount % 5000 == 0) { + printf("cells travelled: %d\n", celldist(cwt.c)); + + printf("\n"); + + for(int i=0; i\n", kills[i], minf[i].name, i); + + printf("\n\n\n"); + } + + if(rand() % 5000 == 0 || (isGravityLand(cwt.c->land) && coastvalEdge(cwt.c) >= 100) || gcount > 2000 || cellcount >= 20000000) { + printf("RESET\n"); + gcount = 0; + cellcount = 0; + activateSafety(land_tac[rand() % LAND_TAC].l); + } + + for(int i=0; iland == laNone) { + printf("no-land found\n"); + kills[moPlayer] = 0; + canmove = true; + doAutoplay = false; + } + } + + if(buggyGeneration) break; + if(size(buggycells)) break; + +// if(turncount == 50000) exit(0); + } + } + +void verifyMutant(cell *c) { + if(!c) return; + if(c->monst == moMutant && c->mondir != NODIR && c->mov[c->mondir]->monst != moMutant) { + raiseBuggyGeneration(c, "mutant failed!"); + childbug = true; + } + } + +void verifyMutantAround(cell *c) { + if(!c) return; + verifyMutant(c); + for(int i=0; itype; i++) verifyMutant(c->mov[i]); + if(childbug) printf("around!\n"); + } + +priority_queue > riverqueue; + +void enriver(cell *c) { + if(c->wall != waNone) return; + c->wall = waBigTree; + int d = -(abs(getCdata(c, 0)-1) + abs(getCdata(c, 1)-1) + abs(getCdata(c, 2)-1)); + riverqueue.push(make_pair(d, c)); + } + +/* void river() { + enriver(cwt.c); + for(int i=0; i<10000; i++) { + cell *c = riverqueue.top().second; + riverqueue.pop(); + c->wall = waFloorA; + forCellCM(c2, c) enriver(c2); + } + } */ + +void bandpicture(); + +int lcol(cell *c1, cell *c2) { + bool h7 = c1->type == 7; + if(!c2) return 0xFF0000FF; + int i0 = zebra40(c1)&2; + int i1 = zebra40(c2)&2; + /* if(i0 == 2 && i1 == 2) return 0xFFFFFFFF; + if(i0 == 0 && i1 == 0) return 0x404040FF; + return 0xA0A0A0FF; */ + if(i0 == i1) return h7 ? 0x206020FF : 0x202020FF; + return h7 ? 0x40F040FF : 0x00F000FF; + } + +void localdraw(const transmatrix &V, cell *c) { + return; + int ct = c->type; + if(ct == 7) for(int i=0; i<7; i++) + queuepoly(V * spin(-2*M_PI*i/7), shBead1, lcol(c, c->mov[i])); + + if(ct == 6) for(int i=1; i<6; i+=2) if(c < c->mov[i]) + queuepoly(V * spin(-2*M_PI*i/6 + M_PI), shBead0, lcol(c, c->mov[i])); + } + +bool doCross; +extern bool fixseed; + +namespace arg { + int readLocal() { + if(argis("-auto")) { PHASE(3); autoplay(); } + else if(argis("-bmt")) buildmodetable(); + else if(argis("-cross")) { PHASE(2); fixseed = doCross = true, firstland = laCrossroads2; } + else if(argis("-musictable")) { + loadMusicInfo(); + for(int i=0; i 1), 's'); if(!shmup::on) dialog::addSelItem(XLAT("hardcore mode"), @@ -863,8 +867,6 @@ void handleChangeMode(int sym, int uni) { dialog::handleNavigation(sym, 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; else if(uni == 'c') { @@ -923,6 +925,12 @@ void handleChangeMode(int sym, int uni) { restartGame('7'); else if(uni == 'a') cmode = emConformal; +#ifdef TOUR + else if(uni == 'T') { + cmode = emNormal; + tour::start(); + } +#endif else if(uni == 'C') { if(!chaosmode) { cmode = emHelp; diff --git a/rogueviz.cpp b/rogueviz.cpp index f6bf409e..d2bb2c89 100644 --- a/rogueviz.cpp +++ b/rogueviz.cpp @@ -1194,6 +1194,7 @@ void readcolor(const char *cfname) { } void init() { + if(on) return; on = autocheat = true; #ifndef WEB mapeditor::drawplayer = false; @@ -1216,6 +1217,7 @@ void close() { vdata.clear(); labeler.clear(); legend.clear(); + on = false; } void turn(int delta) { diff --git a/setversion-sh b/setversion-sh index 9483de7a..d1975605 100755 --- a/setversion-sh +++ b/setversion-sh @@ -2,7 +2,7 @@ # set the version numbers in hyper.rc automatically export VER=`grep "#define VER " hyper.cpp | sed "s/#define VER \"//" | sed "s/\"//"` #export VERNUM=`grep "#define VERNUM " hyper.cpp | sed "s/#define VERNUM //" | sed "s/^\(.\)\(.\)\(.\)\(.\)$/\1.\2.\4.\3/"` -VERNUM=9,4,0,4 +VERNUM=9,4,0,6 #VERNUM=8.1.7.0 #echo $VERNUM sed "s/\"ProductVersion\", \"\(.*\)\"/\"ProductVersion\", \"$VER\"/" -i hyper.rc diff --git a/steamvisualinterface.cpp b/steamvisualinterface.cpp new file mode 100644 index 00000000..2fff575f --- /dev/null +++ b/steamvisualinterface.cpp @@ -0,0 +1,54 @@ +// PRIVATE + +#include "sdk/public/steam/steam_api.h" +#include "steamvisualinterface.h" + +class SteamContact { + public: + + STEAM_CALLBACK( SteamContact, OnUserStatsReceived, UserStatsReceived_t, m_CallbackStatsReceived ); + SteamContact (); + + void OnFindLeaderboard( LeaderboardFindResult_t *pFindLearderboardResult, bool bIOFailure ); + void OnDownloadScores( LeaderboardScoresDownloaded_t *pLeaderboardScoresDownloaded, bool bIOFailure ); + CCallResult m_SteamCallResultCreateLeaderboard; + CCallResult m_SteamCallResultScoresDownloaded; + }; + +SteamContact :: SteamContact() +: m_CallbackStatsReceived(this, &SteamContact::OnUserStatsReceived) + { } + +static SteamContact *steam; + +callback_statsreceived sr; +callback_findleaderboard fl; +callback_scoresdownload sdl; + +void init_steamvisualinterface(callback_statsreceived _sr, callback_findleaderboard _fl, callback_scoresdownload _sdl) { + sr = _sr; + fl = _fl; + sdl = _sdl; + steam = new SteamContact; + } + +void register_callback(SteamAPICall_t handle) { + steam->m_SteamCallResultCreateLeaderboard.Set( handle, steam, &SteamContact::OnFindLeaderboard ); + } + +void register_callback_download(SteamAPICall_t handle) { + steam->m_SteamCallResultScoresDownloaded.Set( handle, steam, &SteamContact::OnDownloadScores ); + } + +void SteamContact::OnFindLeaderboard( LeaderboardFindResult_t *pFindLeaderboardResult, bool bIOFailure ) { + fl(pFindLeaderboardResult, bIOFailure); + } + +void SteamContact::OnDownloadScores( LeaderboardScoresDownloaded_t *pLeaderboardScoresDownloaded, bool bIOFailure ) { + sdl(pLeaderboardScoresDownloaded, bIOFailure); + } + +void SteamContact::OnUserStatsReceived( UserStatsReceived_t *pCallback ) { + sr(pCallback); + } + diff --git a/system.cpp b/system.cpp index 4703cd8e..fbccae8a 100644 --- a/system.cpp +++ b/system.cpp @@ -141,6 +141,9 @@ void initgame() { yendor::init(3); multi::revive_queue.clear(); +#ifdef TOUR + if(tour::on) tour::presentation(5); +#endif if(multi::players > 1 && !shmup::on) { for(int i=0; i gd; @@ -868,6 +882,7 @@ namespace gamestack { gdn.viewctr = viewctr; gdn.View = View; gdn.geometry = geometry; + gdn.hepta = purehepta; gd.push_back(gdn); } @@ -878,6 +893,7 @@ namespace gamestack { viewctr = gdn.viewctr; View = gdn.View; geometry = gdn.geometry; + purehepta = gdn.hepta; resetGeometry(); gd.pop_back(); bfs(); @@ -928,6 +944,16 @@ void restartGame(char switchWhat, bool push) { resetGeometry(); chaosmode = !chaosmode; } +#ifdef TOUR + if(switchWhat == 'T') { + geometry = gNormal; + yendor::on = tactic::on = princess::challenge = false; + chaosmode = purehepta = randomPatternsMode = false; + shmup::on = false; + resetGeometry(); + tour::on = !tour::on; + } +#endif if(switchWhat == '7') { if(euclid) geometry = gNormal; purehepta = !purehepta; diff --git a/tour.cpp b/tour.cpp new file mode 100644 index 00000000..dbe40979 --- /dev/null +++ b/tour.cpp @@ -0,0 +1,571 @@ +// work in progress + +namespace tour { + +bool on; + +bool texts = true; + +string tourhelp; + +int currentslide; + +static struct { const char *name; int id; const char *help; } slides[] = { + {"Introduction", 10, + "This tutorial is mostly aimed to show what is " + "special about the geometry used by HyperRogue. " + "It also shows the basics of gameplay, and " + "how is it affected by geometry.\n\n" + "Press Enter to go to the next slide, or ESC to see a " + "menu with other options." + }, + {"Basics of gameplay", 11, + "The game starts in the Icy Lands. Collect the Ice Diamonds " + "(press F1 if you do not know how to move). " + "After you collect many of them, monsters will start to pose a challenge.\n" + "As is typical in roguelikes and other games based on tactical skill rather " + "than story, if you lose, you have to start a new one from the start. " + "However, in this tutorial, you can simply press '4' " + "to teleport away from a bad situation." + "In general, the tutorial is rigged to show you what it " + "wants -- for example, in this slide, you can press '5' to get " + "lots of Ice Diamonds quickly." + }, + {"Hypersian Rug model", 21, + "New players think that the action of HyperRogue takes place on a sphere. " + "This is not true -- the next slide will show the surface HyperRogue " + "actually takes place on.\n\n" + "Use arrow keys to rotate the model, and Page Up/Down to zoom.\n\n" + "If you do not see anything, press '5' to try a safer renderer." + }, + {"Expansion", 22, + "The next slide shows the number of cells in distance 1, 2, 3, ... from you. " + "It grows exponentially: there are more than 10^100 cells " + "in radius 1000 around you, and you will move further away during the game!\n\n" + "This is extremely important in the design of HyperRogue. " + "HyperRogue has many navigational puzzles -- what would be simple in Euclidean world " + "is extremely tricky " + "in hyperbolic geometry (you want to reach a specific location 20 cells away, " + "which of the thousands of possible directions should you take?); however, other things virtually impossible in Euclidean " + "world become easy in HyperRogue. " + "HyperRogue had to be specially designed so that it is impossible to grind the " + "infinite world. There are almost no permanent upgrades; collecting treasures " + "brings you benefits, but trying to get too many of the same kind is extremely dangerous." + }, + {"Tiling and Tactics", 23, + "The tactics of fighting simple monsters, such as the Yetis from the Icy Lands, " + "might appear shallow, but hyperbolic geometry is essential even there. " + "In the next slide, you are attacked by two monsters at once. " + "You can make them line up simply by " + "running away in a straight line. " + "Press '2' to try the same in the Euclidean world -- it is impossible." + }, + {"Straight Lines", 24, + "Hyperbolic geometry has been created by 19th century mathematicians who " + "wondered about the nature of paralellness. Take a line L and a point A. " + "Can a world exist where there is more than one line passing through A " + "which does not cross L?\n\n" + "Wander further, and you should find Crossroads quickly -- " + "the Great Walls are straight lines, and indeed, they work differently than in " + "Euclidean. On the other side of Great Walls, you see other lands -- " + "there are about 50 lands in HyperRogue, based " + "on different mechanics and aspects of hyperbolic geometry." + }, + {"Running Dogs", 25, + "To learn more about straight lines, " + "wander further, and you should find the Land of Eternal Motion. " + "Try to run in a straight line, with a Running Dog next to you. " + "Even though the Running Dog runs at the same speed as you, " + "it will appear to go slower -- this is because you are running " + "in a straight line, and the Running Dog has to run in a curve " + "called an equidistant." + }, + {"Equidistants", 27, + "Equidistants are curves which are at some fixed distance from a " + "straight line. Some lands in HyperRogue are based on equidistants; " + "you should see them after wandering a bit more.\n\n" + "This tutorial gives you freedom to go wherever you choose, " + "but we do not recommend going deep into the Dungeon or the Ocean -- " + "getting back might be difficult." + }, + {"Circles", 26, + "Circles are strange in hyperbolic geometry too. " + "Look for the Castle of Camelot in the Crossroads; " + "the Round Table inside is a circle of radius 28. " + "Finding its center is a difficult challenge.\n\n" + "Press '5' to cheat by seeing the smaller circles too." + }, + {"Horocycles", 28, + "Horocycles are similar to circles, but you cannot reach their center at all -- " + "they can be understood as limit circles of infinite radius centered in some point " + "in infinity (also called an ideal point).\n\n" + "Go to R'Lyeh, and you should quickly find a Temple of Cthulhu there. " + "Each circle of columns is actually a horocycle. Horocycles in a given " + "temple are concentric, and there is an infinite number of them." + }, + {"Half-plane model", 47, + "The game is normally displayed in the so called Poincaré disk model, " + "which is a kind of a map of the infinite hyperbolic world. " + "There are many projections of Earth, but since Earth is curved, " + "all of them have to distort distances or angles in some way -- " + "the same is true in hyperbolic geometry. " + "The next slide shows another model, called the Poincaré upper half-plane model. In this model, " + "horocycles centered at one specific ideal point are drawn as straight lines." + }, + {"Curvature", 29, + "Now, go to the Burial Grounds and find an Orb of the Sword. The Sword appears to " + "always be facing in the same direction whatever you do, and it appears that " + "you have to rotate the sword to excavate the treasures; " + "yet, it is possible to excavate them! You migth have already noticed " + "that the world rotates after you move around a loop and return to an old " + "place.\n\n" + "This is related to the fact that the world of HyperRogue is curved, and " + "the sum of angles in a triangle is not equal to 180 degrees." + }, + {"Periodic patterns", 30, + "Hyperbolic geometry yields much more interesting periodic patterns " + "than Euclidean." + }, + {"Periodic patterns: application", 31, + "Many lands in HyperRogue are based around periodic patterns. " + "For example, both Zebra and Windy Plains are based on the pattern " + "shown in the previous slide. " + "Such lands often have tree-like nature." + }, + {"Fractal landscapes", 32, + "On the following slide, the colors change smoothly in the whole infinite world. " + "Again, this works better than in Euclidean geometry." + }, + {"Fractal landscapes: application", 33, + "This is applied in HyperRogue to create landscapes, such as the chasms in the " + "land of Reptiles or the Dragon Chasms, which you should find quickly. " + "Also in the Dragon Chasms, you can find a Baby Tortoise, and try to find " + "a matching adult tortoise in the Galápagos. " + "There are over two millions of species, but since there is so much space in " + "hyperbolic geometry, finding a matching tortoise is possible. The brighter " + "the color in Galápagos is, the more aspects of the tortoises in the given " + "area are matching." + }, + {"Poincaré Ball model", 41, + "The Poincaré disk model is a model of a hyperbolic *plane* -- you " + "might wonder why are the walls rendered in 3D then.\n\n" + "HyperRogue actually assumes that the floor level is an equidistant surface " + "in a three-dimensional hyperbolic world, and the camera is placed above the " + "plane that the surface is equidistant to (which boils down to showing " + "the floor level in Poincaré disk model).\n\n" + "This is shown on the next slide, in the Poincaré ball model, which is " + "the 3D analog of the Poincaré disk model." + }, + {"Hyperboloid model", 42, + "Let's see more models of the hyperbolic plane. " + "This model uses a hyperboloid in the Minkowski geometry; " + "it is used internally by HyperRogue." + }, + {"Beltrami-Klein model", 43, + "This model renders straight lines as straight, but it distorts angles." + }, + {"Gans model", 44, + "Yet another model, which corresponds to orthographic projection of the " + "sphere. Poincaré disk model, Beltrami-Klein model, and the Gans " + "model are all obtained by looking at either the hyperboloid model or an " + "equidistant surface from various distances." + }, + {"Band model", 45, + "The band model is the hyperbolic analog of the Mercator projection of the sphere: " + "a given straight line is rendered as a straight line, and the rest of the " + "world is mapped conformally, that is, angles are not distorted. " + "Here, we take the straight line connecting your starting point and your " + "current position -- usually the path taken by the player is surprisingly " + "close to a straight line. Press '8' to see this path.\n\n" + "If you want, press '5' to see it rendered as a spiral, although it takes lots of time and " + "memory." + }, + {"Conformal square model", 46, + "The world can be mapped conformally to a square too." + }, +#ifdef ROGUEVIZ + {"Collatz conjecture", 51, + "Your version of HyperRogue includes RogueViz, which " + "is an adaptation of HyperRogue as a visualization tool " + "rather than a game. Hyperbolic space is great " + "for visualizing some kinds of data because of the vast amount " + "of space.\n\n" + "The following slide is a visualization of the Collatz conjecture. " + "Press '5' for a spiral rendering of the Collatz conjecture visualization."}, +#endif + {"THE END", 99, + "This is not everything you can see in HyperRogue. For example, " + "hyperbolic mazes are much fun than their Euclidean counterparts. " + "Have fun exploring!\n\n" + "Press '5' to leave the tutorial mode." + } + }; + +int getid() { + if(!on) return 0; + return slides[currentslide].id; + } + +// modes: +// 1 - enter the slide +// 2 - each frame +// 3 - leave the slide +// 4 - quicken or modify the slide +// 5 - on initgame + +void setCanvas(int mode, char canv) { + static char wc; + static eLand ld; + if(mode == 1) { + wc = mapeditor::whichCanvas; + mapeditor::whichCanvas = canv; + ld = firstland; + firstland = laCanvas; + restartGame(0, true); + } + if(mode == 3) { + mapeditor::whichCanvas = wc; + firstland = ld; + restartGame(0, false); + } + } + +bool sickmode; + +void presentation(int mode) { + + int id = getid(); + cheater = 0; + + if(id && mode == 1) tourhelp = slides[currentslide].name; + + if(sickmode && !items[itOrbTeleport]) items[itOrbTeleport] = 1; + + if(id == 10 && mode == 1) { + if(tour::texts) addMessage(XLAT("Welcome to the HyperRogue tutorial!")); + else clearMessages(); + } + + if(id == 11 && mode == 4) + forCellEx(c2, cwt.c) + forCellEx(c3, c2) + if(c3->wall == waNone && c3->item == itNone && c3->monst == moNone && c3 != cwt.c) + c3->item = itDiamond; + + // Hypersian Rug + if(id == 21) { + static int wm, mm; + if(mode == 1) { + rug::init(); + wm = vid.wallmode; + mm = vid.monmode; + vid.wallmode = 3; + vid.monmode = 2; + } + if(mode == 3) { + rug::close(); + vid.wallmode = wm; + vid.monmode = mm; + } + if(mode == 4) { + rug::close(); + rug::rendernogl = !rug::rendernogl; + rug::init(); + } + } + + // Expansion + + if(id == 22) { + if(mode == 1) viewdists = true; + if(mode == 3) viewdists = false; + } + + // Tiling and Tactics + + if(id == 23) { + setCanvas(mode, 'F'); + if(mode == 5) { + cwt.c->mov[0]->monst = moRunDog; + cwt.c->mov[1]->monst = moGoblin; + } + } + + if(id == 26 && mode == 4) + camelotcheat = !camelotcheat; + + // Curvature + if(id == 29) { + if(mode == 4) + items[itOrbSword] = 90; + } + + if(id == 30) { + setCanvas(mode, 't'); + if(mode == 1) + mapeditor::displaycodes = 2, + mapeditor::whichPattern = 'z'; + if(mode == 3) + mapeditor::displaycodes = 0, + mapeditor::whichPattern = 0; + } + + if(id == 32) + setCanvas(mode, 'l'); + + if(id == 33) { + if(mode == 4) { + cell *c = cwt.c->mov[0]; + c->item = itBabyTortoise; + tortoise::babymap[c] = getBits(c) ^ tortoise::getRandomBits(); + } + } + + if(id == 41) { + if(mode == 1) pmodel = mdBall; + if(mode == 3) pmodel = mdDisk; + } + + if(id == 42) { + if(mode == 1) pmodel = mdHyperboloid; + if(mode == 3) pmodel = mdDisk; + } + + if(id == 43) { + if(mode == 1) vid.alpha = 0; + if(mode == 3) vid.alpha = 1; + } + + if(id == 44) { + if(mode == 1) vid.alpha = 400, vid.scale = 150; + if(mode == 3) vid.alpha = vid.scale = 1; + } + + if(id == 45) { + if(mode == 1) pmodel = mdBand, conformal::create(), conformal::rotation = 0; + if(mode == 3) { + conformal::clear(), pmodel = mdDisk; + resetview(); + drawthemap(); + centerpc(INF); + } + if(mode == 4) conformal::createImage(true); + } + + if(id == 46) { + if(mode == 1) pmodel = mdPolygonal, polygonal::solve(); + if(mode == 3) pmodel = mdDisk; + } + + if(id == 47) { + if(mode == 1) + pmodel = mdHalfplane; + if(mode == 2) + conformal::rotation = cwt.c->land == laDungeon ? 0 : 2; + if(mode == 3) pmodel = mdDisk, conformal::rotation = 0; + } + +#ifdef ROGUEVIZ + if(id == 51) { + setCanvas(mode, 'd'); + if(mode == 1) { + rogueviz::dftcolor = 0x206020FF; + + rogueviz::collatz::s2 = .3; + rogueviz::collatz::p2 = .5; + rogueviz::collatz::s3 = -.4; + rogueviz::collatz::p3 = .4; + + rogueviz::showlabels = true; + + rogueviz::on = true; + gmatrix.clear(); + drawthemap(); + gmatrix0 = gmatrix; + + rogueviz::collatz::start(); + } + + if(mode == 4) + pmodel = mdBand, conformal::create(), conformal::rotation = 0, + conformal::createImage(true), + conformal::clear(), pmodel = mdDisk; + } + +#endif + + if(id == 99 && mode == 4) + restartGame('T'); + } + +eLand getNext(eLand old) { + + // Straight Lines + + int id = getid(); + if(id == 24) { + if(isCrossroads(old)) + return pick( + pick(laRedRock, laWarpCoast, laMirror), + pick(laLivefjord, laAlchemist, laHell), + pick(laJungle, laDesert, laRose), + pick(laGraveyard, laMotion, laDryForest) + ); + else return laCrossroads; + } + + // Running Dogs + + if(id == 25) { + if(isCrossroads(old)) return pick(laMotion, laNone); + else if(old == laMotion) return laCrossroads; + else return laMotion; + } + + // Circles + + if(id == 26) { + if(!isCrossroads(old)) return laCrossroads; + // Camelot is a circle + } + + // Equidistants + + if(id == 27) { + if(isCrossroads(old)) + return hrand(100) < 20 ? laNone : + pick(laOcean, laIvoryTower, laDungeon, laEndorian); + else return laCrossroads; + } + + // Horocycles + if(id == 28) { + if(isCrossroads(old)) + return pick(laRlyeh, laNone, laNone); + else return pick(laCrossroads, old == laRlyeh ? laNone : laRlyeh); + } + + // Curvature + if(id == 29) { + if(isCrossroads(old)) + return pick(laBurial, laNone, laNone); + else return pick(laCrossroads, old == laBurial ? laNone : laBurial); + } + + // periodic patterns application + if(id == 31) { + if(isCrossroads(old)) + return pick( + pick(laWineyard, laEmerald, laPower), + pick(laZebra, laWhirlwind), + laPalace, laNone + ); + else return laCrossroads; + } + + // fractal landscapes application + if(id == 33) { + if(old == laDragon) return pick(laTortoise, laTortoise, laCrossroads); + else if(isCrossroads(old)) + return pick(laDragon, laReptile, laNone); + } + + return laNone; + + } + +void slidehelp() { + if(texts) { + help = + helptitle(slides[currentslide].name, 0xFF8000) + + slides[currentslide].help; + if(cmode != emHelp) + lastmode = cmode; + cmode = emHelp; + } + } + +bool handleKeyTour(int sym, int uni) { + if(sym == SDLK_RETURN && (cmode != emHelp || getid() == 10)) { + if(geometry) { restartGame(0, false); return true; } + if(getid() == 99) return true; + presentation(3); + currentslide++; + slidehelp(); + presentation(1); + return true; + } + if(sym == SDLK_BACKSPACE) { + if(geometry) { restartGame(0, false); return true; } + if(currentslide == 0) { slidehelp(); return true; } + presentation(3); + currentslide--; + presentation(1); + if(cmode == emHelp) slidehelp(); + return true; + } + if(sym == '1' || sym == '2' || sym == '3') { + if(geometry) { + restartGame(0, false); return true; + } + if(sym == '1') targetgeometry = gSphere; + if(sym == '2') targetgeometry = gEuclid; + firstland = euclidland = cwt.c->land; + restartGame(sym == '3' ? '7' : 'g', true); + return true; + } + if(sym == '4') { + items[itOrbTeleport] = 1; + canmove = true; + return true; + } + if(sym == '5') { + presentation(4); + return true; + } + if(sym == '6') { + sickmode = !sickmode; + static ld spd; + if(sickmode == true) { + spd = vid.sspeed, vid.sspeed = 5; + addMessage("Static mode enabled."); + } + else { + vid.sspeed = spd; + addMessage("Static mode disabled."); + } + return true; + } + if(sym == '7') { + texts = !texts; + if(texts) slidehelp(); + else addMessage("Help texts disabled."); + return true; + } + if(sym == '8') + conformal::includeHistory = !conformal::includeHistory; + return false; + } + +bool quickfind(eLand l) { + int id = getid(); + if(id == 26 && l == laCamelot) return true; + if(id == 28 && l == laTemple) return true; + if(id == 29 && l == laBurial && !items[itOrbSword]) return true; + if(id == 33 && l == laTortoise && !items[itBabyTortoise]) return true; + return false; + } + +void start() { + currentslide = 0; + vid.scale = 1; + vid.alpha = 1; + pmodel = mdDisk; + restartGame('T'); + if(tour::on) { + presentation(1); + slidehelp(); + } + } + +}