#define GL #define GL_GLEXT_PROTOTYPES // #define PANDORA // Hyperbolic Rogue // Copyright (C) 2011-2012 Zeno Rogue, see 'hyper.cpp' for details // basic graphics: // disable this if you have no access to SDL_gfx #ifndef STEAM #define GFX #endif #ifdef NOGFX #undef GFX #endif // scale the Euclidean #define EUCSCALE 2.3 // disable this if you have no access to SDL_mixer #ifndef MOBILE #define AUDIO #endif #define BTOFF 0x404040 #define BTON 0xC0C000 #ifndef MOBILE #include #ifdef AUDIO #include #endif bool audio; int audiovolume = 60; #ifndef MAC #undef main #endif #include #ifdef GFX #include #endif #endif #ifndef MOBILE #ifdef GL #ifdef MAC #include #include #else #include #include #endif #ifdef MAC #include #else #include #endif #endif #endif #ifdef ANDROID #ifndef FAKE #ifdef GL #include #include #include #endif #endif #endif #ifndef MOBILE // x resolutions #define NUMMODES 7 SDL_Surface *s; TTF_Font *font[256]; SDL_Joystick *stick, *panstick; #endif int webdisplay = 0; // R:239, G:208, B:207 struct videopar { ld scale, eye, alpha, aspeed; bool full; bool goteyes; bool quick; bool darkhepta; bool shifttarget; int xres, yres, framelimit; int xscr, yscr; // paramaters calculated from the above int xcenter, ycenter; int radius; ld alphax, beta; int fsize; int flashtime; int wallmode, monmode, axes; // for OpenGL float scrdist; bool usingGL; bool usingAA; int joyvalue, joyvalue2, joypanthreshold; float joypanspeed; bool female; int language; int boardmode; int skincolor, haircolor, dresscolor, swordcolor; int killreduction; }; int skincolors[] = { 7, 0xD0D0D0FF, 0xEFD0C9FF, 0xC77A58FF, 0xA58869FF, 0x602010FF, 0xFFDCB1FF, 0xEDE4C8FF }; int haircolors[] = { 8, 0x686868FF, 0x8C684AFF, 0xF2E1AEFF, 0xB55239FF, 0xFFFFFFFF, 0x804000FF, 0x502810FF, 0x301800FF }; int dresscolors[] = { 6, 0xC00000FF, 0x00C000FF, 0x0000C0FF, 0xC0C000FF, 0xC0C0C0FF, 0x202020FF }; int swordcolors[] = { 6, 0xC0C0C0FF, 0xFFFFFFFF, 0xFFC0C0FF, 0xC0C0FFFF, 0x808080FF, 0x202020FF }; // is the player using mouse? (used for auto-cross) bool mousing = true; // is the mouse button pressed? bool mousepressed = false; // int axestate; int ticks; hyperpoint ccenter; ld crad; videopar vid; int default_language; int playergender() { return vid.female ? GEN_F : GEN_M; } int lang() { if(vid.language >= 0) return vid.language; return default_language; } int sightrange = 7; cell *mouseover, *lmouseover, *centerover, *lcenterover; ld modist, centdist; string mouseovers; int mousex, mousey, mousedist, mousedest, joyx, joyy, joydir, panjoyx, panjoyy; bool autojoy = true; hyperpoint mouseh; bool leftclick, rightclick, targetclick, hiliteclick; bool gtouched; bool revcontrol; bool doHighlight() { return hiliteclick ? vid.monmode == 2 : vid.monmode == 3; } int getcstat; ld getcshift; int ZZ; string help; enum emtype {emNormal, emHelp, emVisual1, emVisual2, emChangeMode, emCustomizeChar, emQuit, emDraw, emScores, emPickEuclidean, emPickScores} cmode; int andmode = 0; int darken = 0; #ifndef MOBILE int& qpixel(SDL_Surface *surf, int x, int y) { if(x<0 || y<0 || x >= surf->w || y >= surf->h) return ZZ; char *p = (char*) surf->pixels; p += y * surf->pitch; int *pi = (int*) (p); return pi[x]; } int qpixel3(SDL_Surface *surf, int x, int y) { if(x<0 || y<0 || x >= surf->w || y >= surf->h) return ZZ; char *p = (char*) surf->pixels; p += y * surf->pitch; p += x; int *pi = (int*) (p); return pi[0]; } void loadfont(int siz) { if(!font[siz]) { font[siz] = TTF_OpenFont("DejaVuSans-Bold.ttf", siz); if (font[siz] == NULL) { printf("error: Font file not found\n"); exit(1); } } } int textwidth(int siz, const string &str) { if(size(str) == 0) return 0; loadfont(siz); int w, h; TTF_SizeUTF8(font[siz], str.c_str(), &w, &h); // printf("width = %d [%d]\n", w, size(str)); return w; } #endif #ifdef IOS int textwidth(int siz, const string &str) { return mainfont->getSize(str, siz / 36.0).width; } #endif int darkened(int c) { for(int i=0; i>= 1; return c; } int darkenedby(int c, int lev) { for(int i=0; i>= 1; return c; } int darkena(int c, int lev, int a) { for(int i=0; i>= 1; return (c << 8) + a; } #ifdef GL int lalpha = 0xFF; void glcolor(int color) { unsigned char *c = (unsigned char*) (&color); glColor4f(c[2] / 255.0, c[1] / 255.0, c[0]/255.0, lalpha / 255.0); // c[3]/255.0); } void selectEyeGL(int ed) { float ve = ed*vid.eye; ve *= 2; // vid.xres; ve /= vid.radius; glMatrixMode(GL_PROJECTION); glLoadIdentity(); float lowdepth = 0.1; float hidepth = 10000; // simulate glFrustum GLfloat frustum[16] = { vid.yres * 1./vid.xres, 0, 0, 0, 0, 1, 0, 0, 0, 0, -(hidepth+lowdepth)/(hidepth-lowdepth), -1, 0, 0, -2*lowdepth*hidepth/(hidepth-lowdepth), 0}; if(ve) glTranslatef(-(ve * vid.radius) * (vid.alpha - (vid.radius*1./vid.xres) * vid.eye) / vid.xres, 0, 0); glTranslatef((vid.xcenter*2.)/vid.xres - 1, 1 - (vid.ycenter*2.)/vid.yres, 0); glMultMatrixf(frustum); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float sc = vid.radius / (vid.yres/2.); GLfloat mat[16] = {sc,0,0,0, 0,-sc,0,0, 0,0,-1,0, 0,0,-vid.alpha,1}; glMultMatrixf(mat); if(ve) glTranslatef(ve, 0, vid.eye); vid.scrdist = -vid.alpha + vid.yres * sc / 2; } void selectEyeMask(int ed) { if(ed == 0) { glColorMask( GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE ); } else if(ed == 1) { glColorMask( GL_TRUE,GL_FALSE,GL_FALSE,GL_TRUE ); } else if(ed == -1) { glColorMask( GL_FALSE,GL_TRUE,GL_TRUE,GL_TRUE ); } } void setGLProjection() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); selectEyeGL(0); } GLfloat glcoords[1500][3]; int qglcoords; bool GL_initialized = false; void buildpolys(); #ifndef MOBILE struct glfont_t { GLuint * textures; // Holds The Texture Id's //GLuint list_base; // Holds The First Display List ID int widths[128+NUMEXTRA]; int heights[128+NUMEXTRA]; float tx[128+NUMEXTRA]; float ty[128+NUMEXTRA]; }; glfont_t *glfont[256]; void init(const char * fname, unsigned int h); inline int next_p2 (int a ) { int rval=1; // rval<<=1 Is A Prettier Way Of Writing rval*=2; while(rvalw ); int theight = next_p2( txt->h ); Uint16 expanded_data[twidth * theight]; for(int j=0; j =txt->w || j>=txt->h) ? 0 : ((qpixel(txt, i, j)>>24)&0xFF) * 0x101; } // printf("b\n"); f.widths[ch] = txt->w; f.heights[ch] = txt->h; glBindTexture( GL_TEXTURE_2D, f.textures[ch]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, twidth, theight, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data ); // printf("c\n"); float x=(float)txt->w / (float)twidth; float y=(float)txt->h / (float)theight; f.tx[ch] = x; f.ty[ch] = y; /* glNewList(f.list_base+ch,GL_COMPILE); glBindTexture(GL_TEXTURE_2D, f.textures[ch]); //glPushMatrix(); // glTranslatef(bitmap_glyph->left,0,0); // glTranslatef(0,bitmap_glyph->top-bitmap.rows,0); // printf("d\n"); glBegin(GL_QUADS); float eps=0; glTexCoord2d(eps,eps); glVertex2f(0, txt->h); glTexCoord2d(eps,y-0); glVertex2f(0, 0); glTexCoord2d(x-eps,y-0); glVertex2f(txt->w, 0); glTexCoord2d(x-eps,0); glVertex2f(txt->w, txt->h); glEnd(); glEndList(); */ //glPopMatrix(); SDL_FreeSurface(txt); } //printf("init size=%d ok\n", size); GLERR("initfont"); } int getnext(const char* s, int& i) { for(int k=0; k= x && mousey <= y && mousex <= x+tsize && mousey >= y-f.heights[32]); for(int i=0; s[i];) { // glListBase(f.list_base); // glCallList(s[i]); // s[i]); int tabid = getnext(s,i); float fx=f.tx[tabid]; float fy=f.ty[tabid]; int wi = f.widths[tabid]; int hi = f.heights[tabid]; for(int ed = (vid.goteyes && shift)?-1:0; ed<2; ed+=2) { glPushMatrix(); glTranslatef(x-ed*shift-vid.xcenter,y-vid.ycenter, vid.scrdist); selectEyeMask(ed); glBindTexture(GL_TEXTURE_2D, f.textures[tabid]); glBegin(GL_QUADS); glTexCoord2d(0,0); glVertex2f(0, -hi); glTexCoord2d(0,fy); glVertex2f(0, 0); glTexCoord2d(fx,fy); glVertex2f(wi, 0); glTexCoord2d(fx,0); glVertex2f(wi, -hi); glEnd(); glPopMatrix(); } if(vid.goteyes) selectEyeMask(0); GLERR("print"); // int tabid = s[i]; x += f.widths[tabid]; /* printf("point %d,%d\n", x, y); glBegin(GL_POINTS); glVertex3f(rand() % 100 - rand() % 100, rand() % 100 - rand() % 100, 100); glEnd(); */ } glDisable(GL_TEXTURE_2D); return clicked; // printf("gl_print ok\n"); } #endif void resetGL() { GL_initialized = false; #ifndef MOBILE for(int i=0; i<128; i++) if(glfont[i]) { delete glfont[i]; glfont[i] = NULL; } #endif buildpolys(); } #endif #ifndef MOBILE bool displaystr(int x, int y, int shift, int size, const char *str, int color, int align) { if(strlen(str) == 0) return false; if(size <= 0 || size > 255) { // printf("size = %d\n", size); return false; } #ifdef GL if(vid.usingGL) return gl_print(x, y, shift, size, str, color, align); #endif SDL_Color col; col.r = (color >> 16) & 255; col.g = (color >> 8 ) & 255; col.b = (color >> 0 ) & 255; col.r >>= darken; col.g >>= darken; col.b >>= darken; loadfont(size); SDL_Surface *txt = (vid.usingAA?TTF_RenderText_Blended:TTF_RenderText_Solid)(font[size], str, col); if(txt == NULL) return false; SDL_Rect rect; rect.w = txt->w; rect.h = txt->h; rect.x = x - rect.w * align / 16; rect.y = y - rect.h/2; bool clicked = (mousex >= rect.x && mousey >= rect.y && mousex <= rect.x+rect.w && mousey <= rect.y+rect.h); if(shift) { SDL_Surface* txt2 = SDL_DisplayFormat(txt); SDL_LockSurface(txt2); SDL_LockSurface(s); int c0 = qpixel(txt2, 0, 0); for(int yy=0; yy graphdata; void gdpush(int t) { graphdata.push_back(t); } bool displaychr(int x, int y, int shift, int size, char chr, int col) { gdpush(2); gdpush(x); gdpush(y); gdpush(8); gdpush(col); gdpush(size); gdpush(0); gdpush(1); gdpush(chr); return false; } void gdpush_utf8(const string& s) { int g = graphdata.size(), q = 0; gdpush((int) s.size()); for(int i=0; i= 192 && uch < 224) { int u = ((s[i] - 192)&31) << 6; i++; u += (s[i] - 128) & 63; gdpush(u); q++; } else { gdpush(s[i]); q++; } } graphdata[g] = q; } bool displayfr(int x, int y, int b, int size, const string &s, int color, int align) { gdpush(2); gdpush(x); gdpush(y); gdpush(align); gdpush(color); gdpush(size); gdpush(b); gdpush_utf8(s); int mx = x - mousex; int my = y - mousey; return mx >= -3*size && mx <= +3*size && my >= -size*3/4 && my <= +size*3/4; } bool displaystr(int x, int y, int shift, int size, const string &s, int color, int align) { gdpush(2); gdpush(x); gdpush(y); gdpush(align); gdpush(color); gdpush(size); gdpush(0); gdpush_utf8(s); int mx = x - mousex; int my = y - mousey; return mx >= -3*size && mx <= +3*size && my >= -size*3/4 && my <= +size*3/4; } #endif bool displaynum(int x, int y, int shift, int size, int col, int val, string title) { char buf[64]; sprintf(buf, "%d", val); bool b1 = displayfr(x-8, y, 1, size, buf, col, 16); bool b2 = displayfr(x, y, 1, size, title, col, 0); if((b1 || b2) && gtouched) { col ^= 0x00FFFF; displayfr(x-8, y, 1, size, buf, col, 16); displayfr(x, y, 1, size, title, col, 0); } return b1 || b2; } struct msginfo { int stamp; char flashout; char spamtype; string msg; }; vector msgs; vector gamelog; void flashMessages() { for(int i=0; i .9999) return Hypc; // hz*hz-(hx/(hz+alpha))^2 - (hy/(hz+alpha))^2 = // hz*hz-hr*(hz+alpha)^2 == 1 // hz*hz - hr*hr*hz*Hz ld A = 1-hr; ld B = 2*hr*vid.alphax; ld C = 1 + hr*vid.alphax*vid.alphax; // Az^2 - Bz = C B /= A; C /= A; // z^2 - Bz = C // z^2 - Bz + (B^2/4) = C + (B^2/4) // z = (B/2) + sqrt(C + B^2/4) ld hz = B / 2 + sqrt(C + B*B/4); hyperpoint H; H[0] = hx * (hz+vid.alphax); H[1] = hy * (hz+vid.alphax); H[2] = hz; return H; } void getcoord(const hyperpoint& H, int& x, int& y, int &shift) { if(H[2] < 0.999) { printf("error: %s\n", display(H)); // exit(1); } ld tz = euclid ? (EUCSCALE+vid.alphax) : vid.alphax+H[2]; if(tz < 1e-3 && tz > -1e-3) tz = 1000; x = vid.xcenter + int(vid.radius * H[0] / tz); y = vid.ycenter + int(vid.radius * H[1] / tz); #ifndef MOBILE shift = vid.goteyes ? int(vid.eye * vid.radius * (1 - vid.beta / tz)) : 0; #endif } int dlit; int polyi; #define POLYMAX 60000 #ifdef MOBILE short polyx[POLYMAX], polyy[POLYMAX]; #else Sint16 polyx[POLYMAX], polyxr[POLYMAX], polyy[POLYMAX]; #endif void drawline(const hyperpoint& H1, int x1, int y1, int s1, const hyperpoint& H2, int x2, int y2, int col) { dlit++; if(dlit>500) return; int dst = (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2); #ifdef GL if(vid.usingGL && dst <= (ISMOBILE ? 100 : 20)) { if(euclid) { for(int i=0; i<2; i++) glcoords[qglcoords][i] = H1[i]; glcoords[qglcoords][2] = EUCSCALE; } else { for(int i=0; i<3; i++) glcoords[qglcoords][i] = H1[i]; } qglcoords++; return; } #endif #ifdef MOBILE if(dst <= 400 && !vid.usingGL) { if(polyi >= POLYMAX) return; polyx[polyi] = x1; polyy[polyi] = y1; polyi++; return; } #else #ifdef GFX if(dst <= 20 && !vid.usingGL) { if(col == -1) { if(polyi >= POLYMAX) return; polyx[polyi] = x1-s1; polyxr[polyi] = x1+s1; polyy[polyi] = y1; polyi++; } else (vid.usingAA?aalineColor:lineColor) (s, x1, y1, x2, y2, col); return; } #endif if(dst <= 2) { return; } #endif hyperpoint H3 = mid(H1, H2); int x3, y3, s3; getcoord(H3, x3, y3, s3); #ifndef GFX if(!vid.usingGL) { qpixel(s, x3-s3, y3) |= col & 0xFF0000; qpixel(s, x3+s3, y3) |= col & 0x00FFFF; } #endif drawline(H1, x1, y1, s1, H3, x3, y3, col); drawline(H3, x3, y3, s3, H2, x2, y2, col); } void drawline(const hyperpoint& H1, const hyperpoint& H2, int col) { if(vid.usingGL) { qglcoords = 0; } // printf("line\n"); if(col != -1) { col = (col << 8) + lalpha; if(col == -1) col = -2; polyi = 0; #ifndef GFX if(!vid.usingGL) { SDL_LockSurface(s); col >>= 8; } #endif } dlit = 0; int x1, y1, s1; getcoord(H1, x1, y1, s1); int x2, y2, s2; getcoord(H2, x2, y2, s2); drawline(H1, x1, y1, s1, H2, x2, y2, col); if(vid.usingGL) { // EUCLIDEAN if(euclid) { for(int i=0; i<2; i++) glcoords[qglcoords][i] = H2[i]; glcoords[qglcoords][2] = EUCSCALE; } else { for(int i=0; i<3; i++) glcoords[qglcoords][i] = H2[i]; } qglcoords++; glVertexPointer(3, GL_FLOAT, 0, glcoords); glEnableClientState(GL_VERTEX_ARRAY); glcolor(col >> 8); if(vid.goteyes) { selectEyeGL(-1); selectEyeMask(-1); glDrawArrays(GL_LINE_STRIP, 0, qglcoords); selectEyeGL(+1); selectEyeMask(+1); glDrawArrays(GL_LINE_STRIP, 0, qglcoords); selectEyeGL(0); selectEyeMask(0); } else glDrawArrays(GL_LINE_STRIP, 0, qglcoords); } #ifdef MOBILE else if(col != -1) { gdpush(3); gdpush(col); gdpush(polyi+1); for(int i=0; itype; } int gradient(int c0, int c1, ld v0, ld v, ld v1) { int vv = int(256 * ((v-v0) / (v1-v0))); int c = 0; for(int a=0; a<3; a++) { int p0 = (c0 >> (8*a)) & 255; int p1 = (c1 >> (8*a)) & 255; int p = (p0*(256-vv) + p1*vv + 127) >> 8; c += p << (8*a); } return c; } // cloak color int cloakcolor(int rtr) { rtr -= 28; rtr /= 2; rtr %= 10; if(rtr < 0) rtr += 10; // rtr = time(NULL) % 10; int cc[10] = { 0x8080FF, 0x80FFFF, 0x80FF80, 0xFF8080, 0xFF80FF, 0xFFFF80, 0xFFFFC0, 0xFFD500, 0x421C52, 0 }; return cc[rtr]; } int firecolor(int phase) { return gradient(0xFFFF00, 0xFF0000, -1, sin((phase + ticks)/100.0), 1); } int fc(int ph, int col) { if(items[itOrbFire]) col = darkena(firecolor(ph), 0, 0xFF); if(items[itOrbGhost]) col = (col &~0XFF) | (col&0xFF) / 2; if(invismove) col = (col &~0XFF) | (int((col&0xFF) * (100+100*sin(ticks / 500.)))/200); return col; } int flashat, lightat, safetyat; cell *flashcell; void drawFlash(cell *c) { flashat = ticks; flashcell = c; } void drawLightning() { lightat = ticks; } void drawSafety() { safetyat = ticks; } #include "polygons.cpp" bool drawplayer = true; transmatrix playertrans; double eurad = 0.52; bool outofmap(hyperpoint h) { if(euclid) return h[0] * h[0] + h[1] * h[1] > 15 * eurad; else return h[2] < .5; } void drawShield(const transmatrix& V) { float ds = ticks / 300.; int col = darkened(iinf[itOrbShield].color); for(int a=0; a<84*5; a++) drawline(V*ddi(a, hexf + sin(ds + M_PI*2*a/20)*.1)*C0, V*ddi((a+1), hexf + sin(ds + M_PI*2*(a+1)/20)*.1)*C0, col); } void drawSpeed(const transmatrix& V) { ld ds = ticks / 10.; int col = darkened(iinf[itOrbSpeed].color); for(int b=0; b<84; b+=14) for(int a=0; a<84; a++) drawline(V*ddi(ds+b+a, hexf*a/84)*C0, V*ddi(ds+b+(a+1), hexf*(a+1)/84)*C0, col); } void drawSafety(const transmatrix& V, int ct) { ld ds = ticks / 50.; int col = darkened(iinf[itOrbSafety].color); for(int a=0; atype; else return 42 - d * 84 / c->type; } bool drawMonster(const transmatrix& V, int ct, cell *c, int col) { if(doHighlight()) poly_outline = (c->cpdist == 0 || isFriendly(c)) ? 0x00FF00FF : 0xFF0000FF; eMonster m = c->monst; if(c->cpdist == 0) { if(items[itOrbShield] > 1) drawShield(V); if(items[itOrbSpeed]) drawSpeed(V); int ct = c->type; if(items[itOrbSafety]) drawSafety(V, ct); if(items[itOrbFlash]) drawFlash(V); if(items[itOrbWinter]) drawWinter(V, displaydir(c, cwt.spin)); if(items[itOrbLightning] > 1) drawLightning(V); if(safetyat > 0) { int tim = ticks - safetyat; if(tim > 2500) safetyat = 0; for(int u=tim; u<=2500; u++) { if((u-tim)%250) continue; ld rad = hexf * u / 250; int col = iinf[itOrbSafety].color; for(int a=0; a<84; a++) drawline(V*ddi(a, rad)*C0, V*ddi(a+1, rad)*C0, col); } } transmatrix cV2 = c == cwt.c ? cwtV : V; // if(flipplayer) cV2 = cV2 * spin(M_PI); if(flipplayer) cV2 = cV2 * spin(M_PI); if(vid.monmode < 2) return true; bool havus = shUser[0][2].s; playertrans = cV2; for(int i=0; i<8; i++) if(shUser[i][2].s) queuepoly(cV2, ct, shUser[i][2], 0xFFFFFFFF); if(drawplayer && !havus) { if(c->monst == moGolem) { queuepoly(cV2, ct, shPBody, darkena(col, 0, 0XC0)); queuepoly(cV2, ct, shGolemhead, darkena(col, 1, 0XFF)); } else { queuepoly(cV2, ct, vid.female ? shFemaleBody : shPBody, fc(0, vid.skincolor)); if(items[itOrbThorns]) queuepoly(cV2, ct, shHedgehogBladePlayer, 0x00FF00FF); else queuepoly(cV2, ct, shPSword, fc(314, vid.swordcolor)); if(cheater) { queuepoly(cV2, ct, shDemon, darkena(0xFFFF00, 0, 0xFF)); // queuepoly(cV2, ct, shHood, darkena(0xFF00, 1, 0xFF)); } else { queuepoly(cV2, ct, shPFace, fc(500, vid.skincolor)); queuepoly(cV2, ct, vid.female ? shFemaleHair : shPHead, fc(150, vid.haircolor)); } if(vid.female) queuepoly(cV2, ct, shFemaleDress, fc(500, vid.dresscolor)); if(knighted) queuepoly(cV2, ct, shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF)); } } } if(isIvy(c) || isWorm(c)) { transmatrix V2 = V; if(c->mondir != NODIR) { int hdir = displaydir(c, c->mondir); if(vid.monmode > 1) { V2 = V2 * spin(hdir * M_PI / 42); if(isIvy(c)) queuepoly(V2, ct, shIBranch, (col << 8) + 0xFF); else if(c->monst < moTentacle) { queuepoly(V2, ct, shTentacleX, 0xFF); queuepoly(V2, ct, shTentacle, (col << 8) + 0xFF); } else { queuepoly(V2, ct, shTentacleX, 0xFFFFFFFF); queuepoly(V2, ct, shTentacle, (col << 8) + 0xFF); } } else for(int u=-1; u<=1; u++) drawline(V*ddi(hdir+21, u*crossf/5)*C0, V*ddi(hdir, crossf)*ddi(hdir+21, u*crossf/5)*C0, 0x606020 >> darken); } if(vid.monmode > 1) { if(isIvy(c)) queuepoly(V, ct, shILeaf[ct-6], darkena(col, 0, 0xFF)); else if(m == moWorm || m == moWormwait) { queuepoly(V2 * spin(M_PI), ct, shWormHead, darkena(col, 0, 0xFF)); queuepoly(V2 * spin(M_PI), ct, shEyes, 0xFF); } else if(m == moTentacle || m == moTentaclewait || m == moTentacleEscaping) queuepoly(V2 * spin(M_PI), ct, shTentHead, darkena(col, 0, 0xFF)); else queuepoly(V2, ct, shJoint, darkena(col, 0, 0xFF)); } return vid.monmode < 2; } else if(isMimic(c)) { int hdir = displaydir(c, c->mondir); transmatrix mirrortrans = Id; if(c->monst == moMirror) mirrortrans[1][1] = -1; transmatrix V2 = V * spin(M_PI*hdir/42) * mirrortrans; if(vid.monmode > 1) { if(flipplayer) V2 = V2 * spin(M_PI); queuepoly(V2, ct, vid.female ? shFemaleBody : shPBody, darkena(col, 0, 0X80)); queuepoly(V2, ct, shPSword, darkena(col, 0, 0XC0)); queuepoly(V2, ct, vid.female ? shFemaleHair : shPHead, darkena(col, 1, 0XC0)); queuepoly(V2, ct, shPFace, darkena(col, 0, 0XC0)); if(vid.female) queuepoly(V2, ct, shFemaleDress, darkena(col, 1, 0XC0)); if(flipplayer) V2 = V2 * spin(M_PI); } if(!outofmap(mouseh)) { hyperpoint P2 = V2 * inverse(cwtV) * mouseh; int xc, yc, sc; getcoord(P2, xc, yc, sc); displaychr(xc, yc, sc, 10, 'x', 0xFF00); } return vid.monmode < 2; } else if(c->monst == moIllusion) { if(vid.monmode > 1) { queuepoly(V, ct, vid.female ? shFemaleBody : shPBody, darkena(col, 0, 0X80)); queuepoly(V, ct, shPSword, darkena(col, 0, 0XC0)); queuepoly(V, ct, vid.female ? shFemaleHair : shPHead, darkena(col, 1, 0XC0)); queuepoly(V, ct, shPFace, darkena(col, 0, 0XC0)); if(vid.female) queuepoly(V, ct, shFemaleDress, darkena(col, 1, 0XC0)); } return vid.monmode < 2; } else if(c->monst && vid.monmode < 2) return true; else if(isFriendly(c)) { // golems and knights don't face player int hdir = displaydir(c, c->mondir) + 42; transmatrix V2 = V * spin(hdir * M_PI / 42) ; if(m == moKnight) { queuepoly(V2, ct, shPBody, darkena(0xC0C0A0, 0, 0xC0)); queuepoly(V2, ct, shPSword, darkena(0xFFFF00, 0, 0xFF)); queuepoly(V2, ct, shKnightArmor, darkena(0xD0D0D0, 1, 0xFF)); int col; if(c->master->alt) col = cloakcolor(roundTableRadius(c)); else col = cloakcolor(newRoundTableRadius()); queuepoly(V2, ct, shKnightCloak, darkena(col, 1, 0xFF)); queuepoly(V2, ct, shPHead, darkena(0x703800, 1, 0XFF)); queuepoly(V2, ct, shPFace, darkena(0xC0C0A0, 0, 0XFF)); } else { queuepoly(V2, ct, shPBody, darkena(col, 0, 0XC0)); queuepoly(V2, ct, shGolemhead, darkena(col, 1, 0XFF)); } } else if(c->monst) { // face the player transmatrix VL; if(false) { // hyperpoint V0 = cwtV * C0; hyperpoint V1 = V * C0; VL = V * spin(hypot(V1[0], V1[1])); } else { hyperpoint V0 = inverse(cwtV) * V * C0; hyperpoint V1 = spintox(V0) * V0; VL = cwtV * rspintox(V0) * rpushxto0(V1) * spin(M_PI); } char xch = minf[m].glyph; if(m == moWolf) { queuepoly(VL, ct, shWolfLegs, darkena(col, 0, 0xFF)); queuepoly(VL, ct, shWolfBody, darkena(col, 0, 0xFF)); queuepoly(VL, ct, shWolfHead, darkena(col, 0, 0xFF)); queuepoly(VL, ct, shWolfEyes, darkena(col, 3, 0xFF)); } else if(m == moVineBeast) { queuepoly(VL, ct, shWolfLegs, 0x00FF00FF); queuepoly(VL, ct, shWolfBody, darkena(col, 1, 0xFF)); queuepoly(VL, ct, shWolfHead, darkena(col, 0, 0xFF)); queuepoly(VL, ct, shWolfEyes, 0xFF0000FF); } else if(isBug(m)) { int hdir = displaydir(c, c->mondir) + 42; transmatrix V2 = V * spin(hdir * M_PI / 42) ; queuepoly(V2, ct, shBugBody, darkena(col, 0, 0xFF)); queuepoly(V2, ct, shBugArmor, darkena(col, 1, 0xFF)); } else if(m == moRunDog) { queuepoly(VL, ct, shWolf, darkena(col, 0, 0xFF)); } else if(m == moShark || m == moGreaterShark) queuepoly(VL, ct, shShark, darkena(col, 0, 0xFF)); else if(m == moEagle) queuepoly(VL, ct, shEagle, darkena(col, 0, 0xFF)); else if(m == moZombie) queuepoly(VL, ct, shPBody, darkena(col, 0, 0xFF)); else if(m == moDesertman) { queuepoly(VL, ct, shPBody, darkena(col, 0, 0xC0)); queuepoly(VL, ct, shPSword, 0xFFFF00FF); queuepoly(VL, ct, shHood, 0xD0D000C0); } else if(m == moCrystalSage) { queuepoly(VL, ct, shPBody, 0xFFFFFFFF); queuepoly(VL, ct, shPHead, 0xFFFFFFFF); queuepoly(VL, ct, shPFace, 0xFFFFFFFF); } else if(m == moHedge) { queuepoly(VL, ct, shPBody, darkena(col, 0, 0xFF)); queuepoly(VL, ct, shHedgehogBlade, 0xC0C0C0FF); queuepoly(VL, ct, shPHead, 0x804000FF); queuepoly(VL, ct, shPFace, 0xF09000FF); } else if(m == moYeti || m == moMonkey) { queuepoly(VL, ct, shYeti, darkena(col, 0, 0xC0)); queuepoly(VL, ct, shPHead, darkena(col, 0, 0xFF)); } else if(m == moShadow) { queuepoly(VL, ct, shPBody, darkena(col, 0, 0X80)); queuepoly(VL, ct, shPSword, darkena(col, 0, 0XC0)); queuepoly(VL, ct, shPHead, darkena(col, 1, 0XC0)); queuepoly(VL, ct, shPFace, darkena(col, 0, 0XC0)); } else if(m == moRanger) { queuepoly(VL, ct, shPBody, darkena(col, 0, 0xC0)); queuepoly(VL, ct, shPSword, darkena(col, 0, 0xFF)); queuepoly(VL, ct, shArmor, darkena(col, 1, 0xFF)); } else if(m == moGhost || m == moSeep) { queuepoly(VL, ct, shGhost, darkena(col, 0, 0x80)); queuepoly(VL, ct, shEyes, 0xFF); } else if(m == moVineSpirit) { queuepoly(VL, ct, shGhost, 0xD0D0D0C0); queuepoly(VL, ct, shEyes, 0xFF0000FF); } else if(m == moFireFairy) { col = firecolor(0); queuepoly(VL, ct, shFemaleBody, darkena(col, 0, 0XC0)); // queuepoly(cV2, ct, shPSword, darkena(col, 0, 0XFF)); queuepoly(VL, ct, shFemaleHair, darkena(col, 1, 0xFF)); queuepoly(VL, ct, shPFace, darkena(col, 0, 0XFF)); // queuepoly(cV2, ct, shFemaleDress, 0xC00000FF); } else if(m == moSlime) { queuepoly(VL, ct, shSlime, darkena(col, 0, 0x80)); queuepoly(VL, ct, shEyes, 0xFF); } else if(m == moCultist || m == moPyroCultist || m == moCultistLeader) { queuepoly(VL, ct, shPBody, darkena(col, 0, 0xC0)); queuepoly(VL, ct, shPSword, darkena(col, 2, 0xFF)); queuepoly(VL, ct, shHood, darkena(col, 1, 0xFF)); } else if(m == moNecromancer) { queuepoly(VL, ct, shPBody, 0xC00000C0); queuepoly(VL, ct, shHood, darkena(col, 1, 0xFF)); } else if(m == moGoblin) { queuepoly(VL, ct, shYeti, darkena(col, 0, 0xC0)); queuepoly(VL, ct, shArmor, darkena(col, 1, 0XFF)); } else if(m == moLancer || m == moFlailer || m == moMiner) { if(m == moLancer) VL = VL * spin(c->type == 6 ? -M_PI/3 : -M_PI/2 ); queuepoly(VL, ct, shPBody, darkena(col, 0, 0xC0)); queuepoly(VL, ct, m == moFlailer ? shArmor : shHood, darkena(col, 1, 0XFF)); if(m == moMiner) queuepoly(VL, ct, shPickAxe, darkena(0xC0C0C0, 0, 0XFF)); if(m == moLancer) queuepoly(VL, ct, shPike, darkena(col, 0, 0XFF)); if(m == moFlailer) { queuepoly(VL, ct, shFlailBall, darkena(col, 0, 0XFF)); queuepoly(VL, ct, shFlailChain, darkena(col, 1, 0XFF)); queuepoly(VL, ct, shFlailTrunk, darkena(col, 0, 0XFF)); } } else if(m == moTroll) { queuepoly(VL, ct, shYeti, darkena(col, 0, 0xC0)); queuepoly(VL, ct, shPHead, darkena(col, 1, 0XFF)); queuepoly(VL, ct, shPFace, darkena(col, 2, 0XFF)); } else if(m == moDarkTroll) { queuepoly(VL, ct, shYeti, darkena(col, 0, 0xC0)); queuepoly(VL, ct, shPHead, darkena(col, 0, 0XFF)); queuepoly(VL, ct, shPFace, 0xFFFFFF80); } else if(m == moEarthElemental) { queuepoly(VL, ct, shYeti, darkena(col, 0, 0xC0)); queuepoly(VL, ct, shPHead, darkena(col, 0, 0XFF)); queuepoly(VL, ct, shPFace, 0xF0000080); } else if(xch == 'd' || xch == 'D') { queuepoly(VL, ct, shPBody, darkena(col, 1, 0xC0)); int acol = col; if(xch == 'D') acol = 0xD0D0D0; queuepoly(VL, ct, shDemon, darkena(acol, 0, 0xFF)); } else if(m == moEvilGolem) { queuepoly(VL, ct, shPBody, darkena(col, 0, 0XC0)); queuepoly(VL, ct, shGolemhead, darkena(col, 1, 0XFF)); } else if(isWitch(m)) { int c = 0xFF; if(m == moWitchGhost) c = 0x85 + 120 * sin(ticks / 160.0); if(m == moWitchWinter) drawWinter(VL, 42); if(m == moWitchFlash) drawFlash(VL); if(m == moWitchSpeed) drawSpeed(VL); if(m == moWitchFire) col = firecolor(0); queuepoly(VL, ct, shFemaleBody, darkena(col, 0, c)); // queuepoly(cV2, ct, shPSword, darkena(col, 0, 0XFF)); // queuepoly(VL, ct, shHood, darkena(col, 0, 0XC0)); if(m == moWitchFire) col = firecolor(100); queuepoly(VL, ct, shFemaleHair, darkena(col, 1, c)); if(m == moWitchFire) col = firecolor(200); queuepoly(VL, ct, shPFace, darkena(col, 0, c)); if(m == moWitchFire) col = firecolor(300); queuepoly(VL, ct, shWitchDress, darkena(col, 1, 0XC0)); } else return true; } return false; } cell *keycell; int keycelldist; void drawCircle(int x, int y, int size, int color) { #ifdef GL if(vid.usingGL) { qglcoords = 0; glcolor(color); x -= vid.xcenter; y -= vid.ycenter; int pts = size * 4; if(pts > 1500) pts = 1500; if(ISMOBILE && pts > 72) pts = 72; for(int r=0; r 1500) pts = 1500; for(int r=0; rtype; t++) if(c->mov[t] && bugsNearby(c->mov[t], dist-1)) return true; return false; } void drawcell(cell *c, transmatrix V, int spinv) { // todo: fix when scrolling if(c->land != laGameBoard && sightrange < 10) { // not yet created if(c->mpdist > 7) return; // (incorrect comment) too far, no bugs nearby if(playermoved && c->cpdist > sightrange) return; } if(!euclid) { // draw a web-like map if(webdisplay & 1) { if(c->type == 6) { for(int a=0; a<3; a++) drawline(V*Crad[a*7], V*Crad[a*7+21], 0xd0d0 >> darken); } else { for(int a=0; a<7; a++) drawline(V*C0, V*Crad[(21+a*6)%42], 0xd0d0 >> darken); } } if(webdisplay & 2) if(c->type == 7) { drawline(V*C0, V*xpush(tessf)*C0, 0xd0d0 >> darken); } if(webdisplay & 4) if(c->type == 7 && c->master->alt) { for(int i=0; i<7; i++) if(c->master->move[i]->alt == c->master->alt->move[0]) drawline(V*C0, V*spin(-2*M_PI*i/7)*xpush(tessf)*C0, 0xd000d0 >> darken); } } // save the player's view center if(c == cwt.c) { playerfound = true; /* if(euclid) return d * 84 / c->type; else return 42 - d * 84 / c->type; cwtV = V * spin(-cwt.spin * 2*M_PI/c->type) * spin(M_PI); */ cwtV = V * spin(displaydir(c, cwt.spin) * M_PI/42); } if(1) { hyperpoint VC0 = V*C0; if(intval(mouseh, VC0) < modist) { modist = intval(mouseh, VC0); mouseover = c; } double dfc = euclid ? intval(VC0, C0) : VC0[2]; if(dfc < centdist) { centdist = dfc; centerover = c; } int xc, yc, sc, xs, ys, ss; getcoord(VC0, xc, yc, sc); getcoord(V*xpush(.5)*C0, xs, ys, ss); // int col = 0xFFFFFF - 0x20 * c->maxdist - 0x2000 * c->cpdist; if(c->mpdist > 8) return; // not yet generated char ch = winf[c->wall].glyph; int col = winf[c->wall].color; if(c->land == laAlchemist && c->wall == waNone) col = 0x202020; if(c->land == laCrossroads && c->wall == waNone) col = (vid.goteyes ? 0xFF3030 : 0xFF0000); if(c->land == laDesert && c->wall == waNone) col = 0xEDC9AF; if(c->land == laCaves && c->wall == waNone) col = 0x202020; if(c->land == laFjord && c->wall == waNone) col = 0x202020; if(c->land == laDeadCaves && c->wall == waNone) col = 0x202020; if(isHive(c->land)) { col = linf[c->land].color; if(c->wall == waWaxWall) col = int(c->heat) & 0xFFFFFF; } if(c->land == laJungle && c->wall == waNone) col = (vid.goteyes ? 0x408040 : 0x008000); if(c->land == laPower && c->wall == waNone) col = linf[c->land].color; /* if(c->land == laFjord && c->wall == waNone) { col = 0x50A020; } if(c->land == laFjord && c->wall == waLake) { col = 0x202080; int i = 0; for(int k=0; ktype; k++) if(c->mov[k] && c->mov[k]->wall != waLake) i++; if(i > 0) { col = gradient(col, 0xFFFFFF, 0, i-fabs(sin(ticks/1500.0)), 7); } } */ if(c->land == laWineyard && c->wall == waNone) { col = 0x006000; } if(c->land == laDryForest && c->wall == waNone) { if(c->wall == waDryTree) col = (vid.goteyes ? 0xC0C060 : 0xC0C000); else if(c->wall == waWetTree) col = (vid.goteyes ? 0x60C060 : 0x00C000); else if(c->wall == waNone) { col = gradient(0x008000, 0x800000, 0, c->heat, 10); } } if(c->land == laMirror && c->wall == waNone) col = 0x808080; if(c->land == laMotion && c->wall == waNone) col = 0xF0F000; if(c->land == laGraveyard && c->wall == waNone) col = 0x107010; if(c->land == laCamelot && c->wall == waNone) { int d = showoff ? 0 : celldistAltRelative(c); if(d < 0) col = 0xA0A0A0; else { // a nice floor pattern int v = fjordval(c); int v0 = (v&~3); bool sw = (v&1); if(v0 == 8 || v0 == 12 || v0 == 20 || v0 == 40 || v0 == 36 || v0 == 24) sw = !sw; if(sw) col = 0xC0C0C0; else col = 0xA0A0A0; } } if(c->land == laRlyeh && c->wall == waNone) col = (vid.goteyes ? 0x4080C0 : 0x004080); if(c->land == laTemple) { int d = showoff ? 0 : celldistAlt(c); if(d % TEMPLE_EACH == 0) col = c->wall == waColumn ? winf[waColumn].color : gradient(0x304080, winf[waColumn].color, 0, 0.5, 1); // else if(c->type == 7) // col = 0x707070; else if(d% 2 == -1) col = 0x304080; else col = 0x405090; } if(c->land == laHell && c->wall == waNone) col = (vid.goteyes ? 0xC03030 : 0xC00000); if(c->land == laGameBoard) { col = linf[c->land].color; if(c->wall != waNone) col = winf[c->wall].color; if(c->wall == waLake) col = 0x202020; if(c->wall == waFrozenLake) col = 0xC0C0C0; if(c->type == 7 && vid.boardmode == '3') col = 0; if(c->type == 6 && vid.boardmode == '7') col = 0; } if(isIcyLand(c) && isIcyWall(c)) { if(c->heat < -0.4) col = gradient(0x4040FF, 0x0000FF, -0.4, c->heat, -1); else if(c->heat < 0) col = gradient(0x8080FF, 0x4040FF, 0, c->heat, -0.4); else if(c->heat < 0.2) col = gradient(0x8080FF, 0xFFFFFF, 0, c->heat, 0.2); // else if(c->heat < 0.4) // col = gradient(0xFFFFFF, 0xFFFF00, 0.2, c->heat, 0.4); else if(c->heat < 0.6) col = gradient(0xFFFFFF, 0xFF0000, 0.2, c->heat, 0.6); else if(c->heat < 0.8) col = gradient(0xFF0000, 0xFFFF00, 0.6, c->heat, 0.8); else col = 0xFFFF00; if(c->wall == waNone) col = (col & 0xFEFEFE) >> 1; if(c->wall == waLake) col = (col & 0xFCFCFC) >> 2; } if(c->wall == waBonfire && c->tmp == 0) col = 0x404040; if(isFire(c) && c->tmp > 0) col = firecolor(100); if(c->wall == waThumper && c->tmp == 0) col = 0xEDC9AF; if(c->wall == waThumper && c->tmp > 0) { int ds = ticks; for(int u=0; u<5; u++) { ld rad = hexf * (.3 * u + (ds%1000) * .0003); int col = gradient(0xFFFFFF, 0, 0, rad, 1.5 * hexf); for(int a=0; a<84; a++) drawline(V*ddi(a, rad)*C0, V*ddi(a+1, rad)*C0, col); } } if(c->land == laFjord && c->wall == waCavefloor) { col = gradient(col, 0xFF00, 0, 0.5, 1); // col |= 0xFF00; // col += 0x300060; // col += 0x2F18; col -= 0x100000; } if(c->land == laFjord && c->wall == waCavewall) { col = 0xC0FFC0; // col |= 0xFF00; // col += 0x300060; // col += 0x2F18; col -= 0x100000; } if(c->land == laHive && items[itOrbInvis] && c->wall == waNone && c->heat) col = gradient(col, 0xFF0000, 0, c->heat, 100); if(vid.darkhepta && ishept(c)) col = gradient(0, col, 0, 0.75, 1); int ycol = col; if(c->land == laHive && c->bardir == NOBARRIERS && c->barleft) { col = minf[moBug0+c->barright].color; } int xcol = col; if(c->item) ch = iinf[c->item].glyph, col = iinf[c->item].color; int icol = col; if(c->item && c->land == laAlchemist) xcol = col; if(c->monst) ch = minf[c->monst].glyph, col = minf[c->monst].color; if(c->cpdist == 0 && drawplayer) { ch = '@'; col = cheater ? 0xFF3030 : 0xD0D0D0; } if(c->monst == moSlime) { col = winf[c->wall].color; col |= (col>>1); } if(c->ligon) { int tim = ticks - lightat; if(tim > 1000) tim = 800; for(int t=0; t<7; t++) if(c->mov[t] && c->mov[t]->ligon) { int hdir = displaydir(c, t); int col = gradient(iinf[itOrbLightning].color, 0, 0, tim, 1100); drawline(V*ddi(ticks, hexf/2)*C0, V*ddi(hdir, crossf)*C0, col); } } int ct = c->type; bool error = false; if(vid.wallmode) { poly_outline = 0x000000FF; // floor int fd = c->land == laAlchemist || c->land == laIce || c->land == laGraveyard || c->land == laRlyeh || c->land == laTemple ? 1 : 2; if(shUser[0][ct-6].s) { for(int i=0; i<8; i++) if(shUser[i][ct-6].s) queuepoly(V, ct, shUser[i][ct-6], darkena(xcol, 2, 0xC0)); } else if(c->wall == waChasm) ; else if(c->land == laWineyard && (c->wall == waVineHalfA || c-> wall == waVineHalfB)) { int i =-1; for(int t=0;t<6; t++) if(c->mov[t] && c->mov[t]->wall == c->wall) i = t; int hdir = 14 + displaydir(c, i); transmatrix V2 = V * spin(M_PI*hdir/42); hpcshape *shar = shSemiFeatherFloor; if(vid.wallmode == 1) shar = shSemiBFloor; if(vid.wallmode == 2) shar = shSemiFloor; int dk = vid.wallmode == 1 ? 0 : vid.wallmode == 2 ? 2 : 1; queuepoly(V2, ct, shar[0], darkena(winf[waVinePlant].color, dk, 0xFF)); queuepoly(V2, ct, shar[1], darkena(xcol, dk, 0xFF)); } else if(vid.wallmode == 1 && c->land == laAlchemist) queuepoly(V, ct, shFloor[ct-6], darkena(xcol, 1, 0xFF)); else if(vid.wallmode == 1 && c->land != laGameBoard) queuepoly(V, ct, shBFloor[ct-6], darkena(xcol, 0, 0xFF)); else if(vid.wallmode == 2 && c->land != laGameBoard) { queuepoly(V, ct, shFloor[ct-6], darkena(xcol, fd, 0xFF)); } else if(c->land == laWineyard) { queuepoly(V, ct, (euclid ? shStarFloor : shFeatherFloor)[ct-6], darkena(xcol, 1, 0xFF)); } else if(c->land == laRlyeh) queuepoly(V, ct, (euclid ? shFloor: shTriFloor)[ct-6], darkena(xcol, 1, 0xFF)); else if(c->land == laTemple) queuepoly(V, ct, (euclid ? shFloor: shTriFloor)[ct-6], darkena(xcol, 1, 0xFF)); else if(c->land == laAlchemist) queuepoly(V, ct, shCloudFloor[ct-6], darkena(xcol, 1, 0xFF)); else if(c->land == laJungle) queuepoly(V, ct, (euclid ? shStarFloor : shFeatherFloor)[ct-6], darkena(xcol, 2, 0xFF)); else if(c->land == laGraveyard) queuepoly(V, ct, (euclid ? shFloor : shCrossFloor)[ct-6], darkena(xcol, 1, 0xFF)); else if(c->land == laFjord) { queuepoly(V, ct, (euclid ? shFloor : shCaveFloor)[ct-6], darkena(xcol, 2, 0xFF)); } else if(c->land == laDeadCaves) { queuepoly(V, ct, (euclid ? shFloor : shCaveFloor)[ct-6], darkena(xcol, 1, 0xFF)); } else if(c->land == laMotion) queuepoly(V, ct, shMFloor[ct-6], darkena(xcol, 2, 0xFF)); else if(c->land == laHell) queuepoly(V, ct, (euclid ? shStarFloor : shDemonFloor)[ct-6], darkena(xcol, 2, 0xFF)); else if(c->land == laIce) // queuepoly(V, ct, shFloor[ct-6], darkena(xcol, 2, 0xFF)); queuepoly(V, ct, shStarFloor[ct-6], darkena(xcol, 1, 0xFF)); else if(c->land == laCocytus) queuepoly(V, ct, (euclid ? shCloudFloor : shDesertFloor)[ct-6], darkena(xcol, 1, 0xFF)); else if(c->land == laPower) queuepoly(V, ct, (euclid ? shCloudFloor : shPowerFloor)[ct-6], darkena(xcol, 1, 0xFF)); else if(c->land == laHive) { queuepoly(V, ct, shFloor[ct-6], darkena(xcol, 1, 0xFF)); queuepoly(V, ct, shMFloor[ct-6], darkena(xcol, 2, 0xFF)); if(c->wall != waWaxWall && c->wall != waDeadTroll && c->wall != waVinePlant) queuepoly(V, ct, shMFloor2[ct-6], darkena(xcol, xcol==ycol ? 1 : 2, 0xFF)); } else if(c->land == laCaves) queuepoly(V, ct, (euclid ? shCloudFloor : shCaveFloor)[ct-6], darkena(xcol, 2, 0xFF)); else if(c->land == laGameBoard) { if(c->type == 7 && vid.boardmode == '7') queuepoly(V, ct, vid.wallmode == 1 ? shBFloor[ct-6] : shBigHepta, darkena(xcol, 2, 0xFF)); if(c->type == 6 && vid.boardmode == '3') queuepoly(V, ct, vid.wallmode == 1 ? shBFloor[ct-6] : shBigTriangle, darkena(xcol, 2, 0xFF)); if(vid.boardmode == 'h') queuepoly(V, ct, vid.wallmode == 1 ? shBFloor[ct-6] : shFloor[c->type - 6], darkena(xcol, 2, 0xFF)); if(vid.boardmode == 'f') { static int fcol[4] = { 0x404040FF, 0xFF000080, 0x008000FF, 0x000080FF }; int fv = fjordval(c); col = fcol[fv&3]; queuepoly(V, ct, vid.wallmode == 1 ? shBFloor[ct-6] : shFloor[c->type - 6], col); if(fjordwalled[fv]) queuepoly(V, ct, shWall[ct-6], col | 0xFF3F3F); int siz = int(sqrt(squar(xc-xs)+squar(yc-ys))) / 5; displaystr(xc, yc, sc, siz, its(fv), 0xFFFFFFFF, 8); } } else if(c->land == laDesert) queuepoly(V, ct, (euclid ? shCloudFloor : shDesertFloor)[ct-6], darkena(xcol, 2, 0xFF)); else if(c->land == laDryForest) queuepoly(V, ct, (euclid ? shStarFloor : shDesertFloor)[ct-6], darkena(xcol, 2, 0xFF)); else queuepoly(V, ct, shFloor[ct-6], darkena(xcol, 2, 0xFF)); // walls char xch = winf[c->wall].glyph; if(c->wall == waBigStatue) queuepoly(V, ct, shStatue, darkena(winf[c->wall].color, 0, 0xFF) ); else if(c->wall == waSulphurC) queuepoly(V, ct, shGiantStar[ct-6], darkena(xcol, 0, 0xFF)); else if(c->wall == waFrozenLake || c->wall == waLake || c->wall == waCamelotMoat || c->wall == waRoundTable) ; else if(xch == '#') { if(c->wall == waVinePlant) xcol = 0x60C000; queuepoly(V, ct, shWall[ct-6], darkena(xcol, 0, 0xFF)); } else if(xch == '%') queuepoly(V, ct, shMirror, darkena(xcol, 0, 0xC0)); else if(isActiv(c)) { ld sp = c->tmp > 0 ? ticks / 500. : 0; queuepoly(V * spin(sp), ct, shStar, darkena(col, 0, 0xF0)); } else if(xch == '+' && c->land == laGraveyard && c->wall != waFloorB && c->wall != waFloorA) queuepoly(V, ct, shCross, darkena(xcol, 0, 0xFF)); else if(xch != '.' && xch != '+' && xch != '>' && xch != ':' && xch != ';' && c->wall != waSulphur) error = true; /* if(c->master->alt) { int d = celldistAlt(c); int siz = int(sqrt(squar(xc-xs)+squar(yc-ys))) / 5; if(d != ALTDIST_UNKNOWN && d != ALTDIST_BOUNDARY) displaystr(xc, yc, sc, siz, its(d), 0xFFFFFFFF, 8); } */ } else if(!(c->item || c->monst || c->cpdist == 0)) error = true; // treasure char xch = iinf[c->item].glyph; hpcshape *xsh = c->item == itHolyGrail ? &shGrail : xch == '*' ? &shGem[ct-6] : xch == '%' ? &shDaisy : xch == '$' ? &shStar : xch == ';' ? &shTriangle : xch == '!' ? &shTriangle : c->item == itBone ? &shNecro : c->item == itStatue ? &shStatue : xch == '?' ? &shBookCover : c->item == itKey ? &shKey : NULL; if(doHighlight()) { int k = itemclass(c->item); if(k == IC_TREASURE) poly_outline = 0xFFFF00FF; else if(k == IC_ORB) poly_outline = 0xFF8000FF; else poly_outline = 0xFFFFFFFF; } if(vid.monmode == 0 && c->item) error = true; else if(xsh) queuepoly(V * spin(ticks / 1500.), ct, *xsh, darkena(icol, 0, 0xF0)); else if(xch == 'o') { if(c->item == itOrbFire) icol = firecolor(100); queuepoly(V, ct, shDisk, darkena(icol, 0, 0xC0)); if(c->item == itOrbFire) icol = firecolor(200); queuepoly(V, ct, shRing, darkena(icol, 0, int(0x80 + 0x70 * sin(ticks / 300.)))); } else if(c->item) error = true; if(xsh == &shBookCover) queuepoly(V * spin(ticks / 1500.), ct, shBook, 0x805020FF); // monsters if(flashat > 0 && c == flashcell) { int tim = ticks - flashat; if(tim > 1000) flashat = 0; for(int u=0; u<=tim; u++) { if((u-tim)%50) continue; if(u < tim-150) continue; ld rad = u * 3 / 1000.; rad = rad * (5-rad) / 2; rad *= hexf; int col = iinf[itOrbFlash].color; if(u > 500) col = gradient(col, 0, 500, u, 1100); for(int a=0; a<84; a++) drawline(V*ddi(a, rad)*C0, V*ddi(a+1, rad)*C0, col); } } error |= drawMonster(V, ct, c, col); /* if(ch == '.') { col = darkened(col); for(int t=0; t= 2) { displaychr(xc-2, yc, sc, siz, ch, 0); displaychr(xc+2, yc, sc, siz, ch, 0); displaychr(xc, yc-2, sc, siz, ch, 0); displaychr(xc, yc+2, sc, siz, ch, 0); } displaychr(xc, yc, sc, siz, ch, col); } if(c == keycell) { displaychr(xc, yc, sc, 2*vid.fsize, 'X', 0x10101 * int(128 + 100 * sin(ticks / 150.))); displaystr(xc, yc, sc, vid.fsize, its(keycelldist), 0x10101 * int(128 - 100 * sin(ticks / 150.)), 8); } #if defined(ANDROID) || defined(PANDORA) || defined(IOS) if(c == lmouseover && (mousepressed || ISANDROID || ISMOBILE)) { drawCircle(xc, yc, int(sqrt(squar(xc-xs)+squar(yc-ys)) * .8), c->cpdist > 1 ? 0x00FFFF : 0xFF0000); } #endif if(c == lmouseover && c->land == laGameBoard) { drawCircle(xc, yc, int(sqrt(squar(xc-xs)+squar(yc-ys)) * .8), 0x00FFFF); } if(joydir >= 0 && c == cwt.c->mov[(joydir+cwt.spin) % cwt.c->type]) drawCircle(xc, yc, int(sqrt(squar(xc-xs)+squar(yc-ys)) * (.78 - .02 * sin(ticks/199.0))), 0x00FF00); #ifndef MOBILE if(c == lcenterover && !playermoved) drawCircle(xc, yc, int(sqrt(squar(xc-xs)+squar(yc-ys)) * (.70 - .06 * sin(ticks/200.0))), int(175 + 25 * sin(ticks / 200.0))); #endif if(cmode == emDraw && cwt.c->type == 6 && ct == 6) for(int a=0; arots; a++) { transmatrix V2 = V * spin(M_PI + 2*M_PI*a/dsCur->rots); if(outofmap(mouseh)) break; hyperpoint P2 = V2 * inverse(cwtV) * mouseh; int xc, yc, sc; getcoord(P2, xc, yc, sc); displaychr(xc, yc, sc, 10, 'x', 0xFF); if(crad > 0 && c->cpdist <= 3) { lalpha = 0x80; transmatrix movtocc = V2 * inverse(cwtV) * rgpushxto0(ccenter); for(int d=0; d<84; d++) drawline(movtocc * ddi(d+1, crad) * C0, movtocc * ddi(d, crad) * C0, 0xC00000); lalpha = 0xFF; } } // process mouse for(int i=-1; itype; i++) if(i == -1 ? cwt.c == c : cwt.c->mov[i % cwt.c->type] == c) { int mx = mousex, my = mousey; if(revcontrol) mx = vid.xcenter*2-mx, my = vid.ycenter*2-my; int ndist = (xc-mx) * (xc-mx) + (yc-my) * (yc-my); if(ndist < mousedist) mousedist = ndist, mousedest = i; } // drawline(V*C0, V*Crad[0], 0xC00000); if(c->bardir != NODIR && c->bardir != NOBARRIERS) { drawline(V*C0, V*heptmove[c->bardir]*C0, 0x505050 >> darken); drawline(V*C0, V*hexmove[c->bardir]*C0, 0x505050 >> darken); } } } string buildCredits(); string buildHelpText() { string h; h += XLAT("Welcome to HyperRogue"); #ifdef ANDROID h += XLAT(" for Android"); #endif #ifdef IOS h += XLAT(" for iOS"); #endif h += XLAT("! (version %1)\n\n", VER); h += XLAT( MC "You have been trapped in a strange, non-Euclidean world. Collect as much treasure as possible " MC "before being caught by monsters. The more treasure you collect, the more " MC "monsters come to hunt you, as long as you are in the same land type. The " M "Orbs of Yendor are the ultimate treasure; get at least one of them to win the game!" ); h += XLAT(" (press ESC for some hints about it)."); h += "\n\n"; h += XLAT( MC "You can fight most monsters by moving into their location. " MC "The monster could also kill you by moving into your location, but the game " M "automatically cancels all moves which result in that.\n\n" ); #ifdef MOBILE h += XLAT( MC "Usually, you move by touching somewhere on the map; you can also touch one " MC "of the four buttons on the map corners to change this (to scroll the map " MC "or get information about map objects). You can also touch the " M "numbers displayed to get their meanings.\n" ); #else h += XLAT( MC "Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Home/Space. " MC "To save the game you need an Orb of Safety. Press 'v' for config, ESC for the quest status and menu.\n\n" ); h += XLAT( "You can right click any element to get more information about it.\n\n" ); #endif h += XLAT("See more on the website: ") + "http//roguetemple.com/z/hyper.php\n\n"; #ifdef MOBILE h += buildCredits(); #else h += XLAT("Press 'c' for credits."); #endif return h; } string musiclicense; string buildCredits() { string h; h += XLAT("game design, programming, texts and graphics by Zeno Rogue \n\n"); if(lang() != 0) h += XLAT("add credits for your translation here"); #ifndef NOLICENSE h += XLAT( MC "released under GNU General Public License version 2 and thus " M "comes with absolutely no warranty; see COPYING for details\n\n" ); #endif h += XLAT( M "special thanks to the following people for their bug reports, feature requests, porting, and other help:\n\n%1\n\n", "Konstantin Stupnik, ortoslon, chrysn, Adam Borowski, Damyan Ivanov, Ryan Farnsley, mcobit, Darren Grey, tricosahedron, Maciej Chojecki, Marek Čtrnáct" ); #ifdef EXTRALICENSE h += EXTRALICENSE; #endif if(musiclicense != "") h += musiclicense; return h; } string generateHelpForItem(eItem it) { string help = XLAT(iinf[it].help); #ifdef ANDROID if(it == itOrbSafety) help += XLAT("This might be useful for Android devices with limited memory."); if(it == itGreenStone) help += XLAT("You can touch the Dead Orb in your inventory to drop it."); #else if(it == itOrbSafety) help += XLAT("Thus, it is potentially useful for extremely long games, which would eat all the memory on your system otherwise.\n"); if(isRangedOrb(it)) help += XLAT("You can also scroll to the desired location and then press 't'."); if(it == itGreenStone) help += XLAT("You can press 'g' or click them in the list to drop a Dead Orb."); #endif return help; } void describeMouseover() { cell *c = mousing ? mouseover : playermoved ? NULL : centerover; string out = mouseovers; if(!c) { } else if(cmode == emNormal) { out = XLAT1(linf[c->land].name); help = XLAT(linf[c->land].help); // if(c->land == laIce) out = "Icy Lands (" + fts(60 * (c->heat - .4)) + " C)"; if(c->land == laIce) out += " (" + fts(60 * (c->heat-.4)) + " °C)"; if(c->land == laCocytus) out += " (" + fts(60 * (c->heat-.8)) + " °C)"; if(c->land == laDryForest && c->heat > .5) out += " (" + its(int(c->heat+.1))+"/10)"; /* // Hive debug if(c->land == laHive) { out += " [" + its(c->tmp) + " H" + its(int(c->heat)); if(c->tmp >= 0 && c->tmp < size(buginfo) && buginfo[c->tmp].where == c) { buginfo_t b(buginfo[c->tmp]); for(int k=0; k<3; k++) out += ":" + its(b.dist[k]); for(int k=0; k<3; k++) for(int i=0; itmp) out += " B"+its(k)+":"+its(i); } out += "]"; } */ if(c->wall && !((c->wall == waFloorA || c->wall == waFloorB) && c->item)) { out += ", "; out += XLAT1(winf[c->wall].name); if(!((c->wall == waCavefloor || c->wall == waCavewall) && c->land == laFjord)) help = XLAT(winf[c->wall].help); } if(isActiv(c)) { if(c->tmp < 0) out += XLAT(" (touch to activate)"); if(c->tmp == 0) out += XLAT(" (expired)"); if(c->tmp > 0 && !(c->land == laPower && c->wall == waBonfire)) out += XLAT(" [%1 turns]", its(c->tmp)); } if(c->monst) {out += ", "; out += XLAT1(minf[c->monst].name); help = XLAT(minf[c->monst].help);} if(c->item) { out += ", "; out += XLAT1(iinf[c->item].name); if(!c->monst) help = generateHelpForItem(c->item); } if(!c->cpdist) out += XLAT(", you"); } else if(cmode == emVisual1) { if(getcstat == 'p') { out = XLAT("0 = Klein model, 1 = Poincaré model"); if(vid.alpha < -0.5) out = XLAT("you are looking through it!"); } else if(getcstat == 'r') { out = XLAT("simply resize the window to change resolution"); } else if(getcstat == 'f') { out = XLAT("[+] keep the window size, [-] use the screen resolution"); } else if(getcstat == 'a' && vid.aspeed > -4.99) out = XLAT("+5 = center instantly, -5 = do not center the map"); else if(getcstat == 'a') out = XLAT("press Space or Home to center on the PC"); else if(getcstat == 'w') out = XLAT("also hold Alt during the game to toggle high contrast"); else if(getcstat == 'w' || getcstat == 'm') out = XLAT("You can choose one of the several modes"); else if(getcstat == 'c') out = XLAT("The axes help with keyboard movement"); else if(getcstat == 'g') out = XLAT("Affects looks and grammar"); #ifndef MOBILE else if(getcstat == 's') out = XLAT("Config file: %1", conffile); #endif else out = ""; } else if(cmode == emVisual2) { if(getcstat == 'p') { // stick) { if(autojoy) out = XLAT("joystick mode: automatic (release the joystick to move)"); if(!autojoy) out = XLAT("joystick mode: manual (press a button to move)"); } else if(getcstat == 'e') out = XLAT("You need special glasses to view the game in 3D"); else if(getcstat == 'f') out = XLAT("Reduce the framerate limit to conserve CPU energy"); } mouseovers = out; #ifndef MOBILE if(cmode != emPickScores) displayfr(vid.xres/2, vid.fsize, 2, vid.fsize, out, linf[cwt.c->land].color, 8); if(mousey < vid.fsize * 3/2) getcstat = SDLK_F1; #endif } void drawrec(const heptspin& hs, int lev, hstate s, transmatrix V) { cell *c = hs.h->c7; drawcell(c, V * spin(hs.spin*2*M_PI/7), hs.spin); if(lev <= 0) return; for(int d=0; d<7; d++) { int ds = fixrot(hs.spin + d); // createMov(c, ds); if(c->mov[ds] && c->spn[ds] == 0) drawcell(c->mov[ds], V * hexmove[d], 0); } if(lev <= 1) return; for(int d=0; d<7; d++) { hstate s2 = transition(s, d); if(s2 == hsError) continue; heptspin hs2 = hsstep(hsspin(hs, d), 0); drawrec(hs2, lev-2, s2, V * heptmove[d]); } } int mindx=-7, mindy=-7, maxdx=7, maxdy=7; void drawEuclidean() { eucoord px, py; if(!lcenterover) lcenterover = cwt.c; printf("centerover = %p player = %p [%d,%d]-[%d,%d]\n", lcenterover, cwt.c, mindx, mindy, maxdx, maxdy); decodeMaster(lcenterover->master, px, py); int minsx = mindx-1, maxsx=maxdx+1, minsy=mindy-1, maxsy=maxdy+1; mindx=maxdx=mindy=maxdy=0; for(int dx=minsx; dx<=maxsx; dx++) for(int dy=minsy; dy<=maxsy; dy++) { eucoord x = dx+px; eucoord y = dy+py; cell *c = euclideanAt(x,y); if(!c) continue; transmatrix Mat = Id; Mat[2][2] = 1; Mat[0][2] += (x + y * .5) * eurad; double q3 = sqrt(double(3)); Mat[1][2] += y * q3 /2 * eurad; while(Mat[0][2] <= -16384 * eurad) Mat[0][2] += 32768 * eurad; while(Mat[0][2] >= 16384 * eurad) Mat[0][2] -= 32768 * eurad; while(Mat[1][2] <= -16384 * q3 * eurad) Mat[1][2] += 32768 * q3 * eurad; while(Mat[1][2] >= 16384 * q3 * eurad) Mat[1][2] -= 32768 * q3 * eurad; Mat = View * Mat; // Mat[0][0] = -1; // Mat[1][1] = -1; // Mat[2][0] = x*x/10; // Mat[2][1] = y*y/10; // Mat = Mat * xpush(x-30) * ypush(y-30); int cx, cy, shift; getcoord(Mat * C0, cx, cy, shift); if(cx >= 0 && cy >= 0 && cx < vid.xres && cy < vid.yres) { if(dx < mindx) mindx = dx; if(dy < mindy) mindy = dy; if(dx > maxdx) maxdx = dx; if(dy > maxdy) maxdy = dy; } drawcell(c, Mat, 0); } } void drawthemap() { keycell = NULL; if(yii < size(yi)) { if(!yi[yii].found) for(int i=0; icpdist <= sightrange) { keycell = yi[yii].path[i]; keycelldist = YDIST - i; } } #ifndef MOBILE lmouseover = mouseover; #endif mousedist = 1000000; mousedest = -1; modist = 1e20; mouseover = NULL; mouseovers = XLAT("Press F1 or right click for help"); centdist = 1e20; lcenterover = centerover; centerover = NULL; #ifdef MOBILE mouseovers = XLAT("No info about this..."); #endif if(outofmap(mouseh)) modist = -5; playerfound = false; if(euclid) drawEuclidean(); else drawrec(viewctr, (!playermoved) ? sightrange+1 : sightrange + 4, hsOrigin, View); } void centerpc(ld aspd) { hyperpoint H = cwtV * C0; ld R = sqrt(H[0] * H[0] + H[1] * H[1]); if(R < 1e-9) { return; } if(euclid) { // Euclidean aspd *= (2+3*R*R); if(aspd > R) aspd = R; View[0][2] -= cwtV[0][2] * aspd / R; View[1][2] -= cwtV[1][2] * aspd / R; } else { aspd *= (1+R); if(R < aspd) { View = gpushxto0(H) * View; } else View = rspintox(H) * xpush(-aspd) * spintox(H) * View; } } void drawmovestar() { if(!playerfound) return; if(vid.axes == 0 || (vid.axes == 1 && mousing)) return; hyperpoint H = cwtV * C0; ld R = sqrt(H[0] * H[0] + H[1] * H[1]); transmatrix Centered = Id; if(euclid) Centered = eupush(H[0], H[1]); else if(R > 1e-9) Centered = rgpushxto0(H); int starcol = (vid.goteyes? 0xE08060 : 0xC00000); if(vid.axes == 3 || (vid.wallmode == 2 && vid.axes == 1)) queuepoly(Centered, 7, shMovestar, darkena(starcol, 0, 0xFF)); else for(int d=0; d<8; d++) { int col = starcol; #ifdef PANDORA if(leftclick && (d == 2 || d == 6 || d == 1 || d == 7)) col >>= 2; if(rightclick && (d == 2 || d == 6 || d == 3 || d == 5)) col >>= 2; if(!leftclick && !rightclick && (d&1)) col >>= 2; #endif // EUCLIDEAN if(euclid) drawline(Centered * C0, Centered * ddi(d * 10.5, 0.5) * C0, col >> darken); else drawline(Centered * C0, Centered * spin(M_PI*d/4)* xpush(.5) * C0, col >> darken); } } void optimizeview() { int turn = 0; ld best = INF; transmatrix TB; for(int i=-1; i<7; i++) { ld trot = -i * M_PI * 2 / 7.0; transmatrix T = i < 0 ? Id : spin(trot) * xpush(tessf) * spin(M_PI); hyperpoint H = View * T * C0; if(H[2] < best) best = H[2], turn = i, TB = T; } if(turn >= 0) { View = View * TB; fixmatrix(View); viewctr = hsspin(viewctr, turn); viewctr = hsstep(viewctr, 0); } } int vectodir(const hyperpoint& P) { hyperpoint H = cwtV * C0; ld R = sqrt(H[0] * H[0] + H[1] * H[1]); transmatrix Centered = cwtV; if(!euclid) Centered = gpushxto0(H) * Centered; else if(R > 1e-9) Centered = eupush(-H[0], -H[1]) * Centered; int bdir = -1; ld binv = 99; for(int i=0; itype; i++) { ld inv = intval(Centered * spin(-i * 2 * M_PI /cwt.c->type) * xpush(1) * C0, P); if(inv < binv) binv = inv, bdir = i; } // if(euclid) bdir = (bdir + 3) % 6; return bdir; } void movepckeydir(int d) { // EUCLIDEAN if(euclid) movepcto(vectodir(spin(-d * M_PI/4) * eupush(1, 0) * C0)); else movepcto(vectodir(spin(-d * M_PI/4) * xpush(1) * C0)); } void checkjoy() { ld joyvalue1 = vid.joyvalue * vid.joyvalue; ld joyvalue2 = vid.joyvalue2 * vid.joyvalue2; ld jx = joyx; ld jy = joyy; ld sq = jx*jx+jy*jy; if(autojoy) { if(sq < joyvalue1) { if(joydir >= 0) movepcto(joydir); joydir = -1; return; } if(sq < joyvalue2 && joydir == -1) return; } else { if(sq < joyvalue1) { joydir = -1; return; } } joydir = vectodir(hpxy(jx, jy)); } void checkpanjoy(double t) { if(panjoyx * panjoyx + panjoyy * panjoyy < vid.joypanthreshold * vid.joypanthreshold) return; ld jx = panjoyx * t * vid.joypanspeed; ld jy = panjoyy * t * vid.joypanspeed; playermoved = false; View = gpushxto0(hpxy(jx, jy)) * View; } void calcparam() { vid.xcenter = vid.xres / 2; vid.ycenter = vid.yres / 2; vid.radius = int(vid.scale * vid.ycenter) - (ISANDROID ? 2 : ISIOS ? 40 : 40); if(vid.xres < vid.yres) { vid.radius = int(vid.scale * vid.xcenter) - (ISIOS ? 10 : 2); vid.ycenter = vid.yres - vid.radius - vid.fsize - (ISIOS ? 10 : 0); } vid.beta = 1 + vid.alpha + vid.eye; vid.alphax = vid.alpha + vid.eye; vid.goteyes = vid.eye > 0.001 || vid.eye < -0.001; } #ifndef MOBILE void displayStat(int y, const string& name, const string& val, char mkey) { int dy = vid.fsize * y + vid.yres/4; int dx = vid.xres/2 - 100; bool xthis = (mousey >= dy-vid.fsize/2 && mousey <= dy + vid.fsize/2); int xcol = 0x808080; if(xthis) { getcstat = mkey; getcshift = 0; int mx = mousex - dx; if(mx >= 0 && mx <= 100) { if(mx < 20) getcshift = -1 , xcol = 0xFF0000; else if(mx < 40) getcshift = -0.1 , xcol = 0x0000FF; else if(mx < 50) getcshift = -0.01, xcol = 0x00FF00; if(mx > 80) getcshift = +1 , xcol = 0xFF0000; else if(mx > 60) getcshift = +0.1 , xcol = 0x0000FF; else if(mx > 50) getcshift = +0.01, xcol = 0x00FF00; } } if(val != "") { displaystr(dx, dy, 0, vid.fsize, val, xthis ? 0xFFFF00 : 0x808080, 16); displaystr(dx+25, dy, 0, vid.fsize, "-", xthis && getcshift < 0 ? xcol : 0x808080, 8); displaystr(dx+75, dy, 0, vid.fsize, "+", xthis && getcshift > 0 ? xcol : 0x808080, 8); } displaystr(dx+100, dy, 0, vid.fsize, s0 + mkey, xthis ? 0xFFFF00 : 0xC0F0C0, 0); displaystr(dx+125, dy, 0, vid.fsize, name, xthis ? 0xFFFF00 : 0x808080, 0); } void displayStatHelp(int y, string name) { int dy = vid.fsize * y + vid.yres/4; int dx = vid.xres/2 - 100; displaystr(dx+100, dy, 0, vid.fsize, name, 0xC0C0C0, 0); } void displayButton(int x, int y, const string& name, int key, int align, int rad = 0) { if(displayfr(x, y, rad, vid.fsize, name, 0x808080, align)) { displayfr(x, y, rad, vid.fsize, name, 0xFFFF00, align); getcstat = key; } } void quitOrAgain() { int y = vid.yres * (618) / 1000; displayButton(vid.xres/2, y + vid.fsize*1/2, (items[itOrbSafety] && havesave) ? XLAT("Press Enter or F10 to save") : XLAT("Press Enter or F10 to quit"), SDLK_RETURN, 8, 2); displayButton(vid.xres/2, y + vid.fsize*2, XLAT("or 'r' or F5 to restart"), 'r', 8, 2); displayButton(vid.xres/2, y + vid.fsize*7/2, XLAT("or 't' to see the top scores"), 't', 8, 2); if(canmove) displayButton(vid.xres/2, y + vid.fsize*10/2, XLAT("or another key to continue"), ' ', 8, 2); else displayButton(vid.xres/2, y + vid.fsize*10/2, XLAT("or ESC to see how it ended"), ' ', 8, 2); } #endif int calcfps() { #define CFPS 30 static int last[CFPS], lidx = 0; int ct = ticks; int ret = ct - last[lidx]; last[lidx] = ct; lidx++; lidx %= CFPS; if(ret == 0) return 0; return (1000 * CFPS) / ret; } int msgscroll = 0; string timeline() { int timespent = savetime + (timerstopped ? 0 : (time(NULL) - timerstart)); char buf[20]; sprintf(buf, "%d:%02d", timespent/60, timespent % 60); return XLAT("%1 turns (%2)", its(turncount), buf); } void showGameover() { int y = vid.yres * (1000-618) / 1000 - vid.fsize * 7/2; displayfr(vid.xres/2, y, 4, vid.fsize*2, cheater ? XLAT("It is a shame to cheat!") : showoff ? XLAT("Showoff mode") : canmove ? XLAT("Quest status") : XLAT("GAME OVER"), 0xC00000, 8 ); displayfr(vid.xres/2, y + vid.fsize*2, 2, vid.fsize, XLAT("Your score: %1", its(gold())), 0xD0D0D0, 8); displayfr(vid.xres/2, y + vid.fsize*3, 2, vid.fsize, XLAT("Enemies killed: %1", its(tkills())), 0xD0D0D0, 8); if(!timerstopped && !canmove) { savetime += time(NULL) - timerstart; timerstopped = true; } if(canmove && !timerstart) timerstart = time(NULL); if(items[itOrbYendor]) { displayfr(vid.xres/2, y + vid.fsize*4, 2, vid.fsize, XLAT("Orbs of Yendor found: %1", its(items[itOrbYendor])), 0xFF00FF, 8); displayfr(vid.xres/2, y + vid.fsize*5, 2, vid.fsize, XLAT("CONGRATULATIONS!"), 0xFFFF00, 8); } else { if(gold() < 30) displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Collect 30 $$$ to access more worlds"), 0xC0C0C0, 8); else if(gold() < 60) displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Collect 60 $$$ to access even more lands"), 0xC0C0C0, 8); else if(!hellUnlocked()) displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Collect at least 10 treasures in each of 9 types to access Hell"), 0xC0C0C0, 8); else if(items[itHell] < 10) displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Collect at least 10 Demon Daisies to find the Orbs of Yendor"), 0xC0C0C0, 8); else if(size(yi) == 0) displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Look for the Orbs of Yendor in Hell or in the Crossroads!"), 0xC0C0C0, 8); else displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Unlock the Orb of Yendor!"), 0xC0C0C0, 8); } if(tkills() < 100) displayfr(vid.xres/2, y+vid.fsize*6, 2, vid.fsize, XLAT("Defeat 100 enemies to access the Graveyard"), 0xC0C0C0, 8); else if(hellUnlocked()) { #define NUMSEQ 19 eLand seq[NUMSEQ] = { laHell, laCocytus, laGraveyard, laWineyard, laFjord, laHive, laDeadCaves, laPower, laRlyeh, laTemple, laDryForest, laIce, laCaves, laJungle, laDesert, laAlchemist, laMotion, laMirror, laCrossroads }; bool b = true; for(int i=0; i=0) { msginfo m; m.spamtype = 0; m.flashout = true; m.stamp = ticks-128*vid.flashtime-128*(gls-i); m.msg = gamelog[i]; mnum++, msgs.push_back(m); } if(mnum) displayfr(vid.xres/2, vid.yres-vid.fsize*(mnum+1), 2, vid.fsize/2, XLAT("last messages:"), 0xC0C0C0, 8); #ifndef MOBILE quitOrAgain(); #endif } void displayabutton(int px, int py, string s, int col) { // TMP int siz = vid.yres > vid.xres ? vid.fsize*2 : vid.fsize * 3/2; int x = vid.xcenter + px * (vid.radius); int y = vid.ycenter + py * (vid.radius - siz/2); if(gtouched && !mouseover && abs(mousex - vid.xcenter) < vid.radius && abs(mousey - vid.ycenter) < vid.radius && hypot(mousex-vid.xcenter, mousey-vid.ycenter) > vid.radius && px == (mousex > vid.xcenter ? 1 : -1) && py == (mousey > vid.ycenter ? 1 : -1) ) col = 0xFF0000; displayfr(x, y, 0, siz, s, col, 8+8*px); } vector scores; int scoresort = 2; int scoredisplay = 1; int scorefrom = 0; bool scorerev = false; bool scorecompare(const score& s1, const score &s2) { return s1.box[scoresort] > s2.box[scoresort]; } bool fakescore() { return fakebox[scoredisplay]; } string displayfor(score* S) { // printf("S=%p, scoredisplay = %d\n", S, scoredisplay); if(S == NULL) { return XLATN(boxname[scoredisplay]); } if(scoredisplay == 0) { char buf[10]; snprintf(buf, 10, "%d:%02d", S->box[0]/60, S->box[0]%60); return buf; } if(scoredisplay == 1) { time_t tim = S->box[1]; char buf[128]; strftime(buf, 128, "%c", localtime(&tim)); return buf; } return its(S->box[scoredisplay]); } #ifndef ANDROID void loadScores() { scores.clear(); FILE *f = fopen(scorefile, "rt"); if(!f) { printf("Could not open the score file '%s'!\n", scorefile); addMessage(s0 + "Could not open the score file: " + scorefile); return; } while(!feof(f)) { char buf[120]; if(fgets(buf, 120, f) == NULL) break; if(buf[0] == 'H' && buf[1] == 'y') { score sc; bool ok = true; if(fscanf(f, "%s", buf) <= 0) break; sc.ver = buf; for(int i=0; i= "4.4") { sc.box[0] = sc.box[65]; // the first executable on Steam included a corruption if(sc.box[65] > 1420000000 && sc.box[65] < 1430000000) { sc.box[0] = sc.box[65] - sc.box[1]; sc.box[65] = sc.box[0]; } // do not include saves if(sc.box[65 + 4 + itOrbSafety - itOrbLightning]) ok = false; } else sc.box[0] = sc.box[1] - sc.box[0]; // could not save then if(ok && boxid > 20) scores.push_back(sc); } } fclose(f); addMessage(its(size(scores))+" games have been recorded in "+scorefile); cmode = emScores; scoresort = 2; reverse(scores.begin(), scores.end()); scorefrom = 0; stable_sort(scores.begin(), scores.end(), scorecompare); } bool notgl = false; void showPickScores() { int d = scoredisplay; vector > v; for(int i=0; i= size(scores)) break; score& S(scores[id]); char buf[16]; sprintf(buf, "%d", id+1); displaystr(bx*4, y, 0, vid.fsize, buf, 0xC0C0C0, 16); sprintf(buf, "%d", S.box[2]); displaystr(bx*8, y, 0, vid.fsize, buf, 0xC0C0C0, 16); sprintf(buf, "%d", S.box[3]); displaystr(bx*12, y, 0, vid.fsize, buf, 0xC0C0C0, 16); sprintf(buf, "%d:%02d", S.box[0]/60, S.box[0] % 60); displaystr(bx*18, y, 0, vid.fsize, buf, 0xC0C0C0, 16); displaystr(bx*22, y, 0, vid.fsize, S.ver, 0xC0C0C0, 16); displaystr(bx*23, y, 0, vid.fsize, displayfor(&S), 0xC0C0C0, 0); y += vid.fsize*5/4; id++; } #ifdef IOS displayabutton(-1, +1, XLAT("SORT"), BTON); displayabutton(+1, +1, XLAT("PLAY"), BTON); #endif } void sortScores() { if(scorerev) reverse(scores.begin(), scores.end()); else { scorerev = true; scoresort = scoredisplay; stable_sort(scores.begin(), scores.end(), scorecompare); } } void shiftScoreDisplay(int delta) { scoredisplay = (scoredisplay + POSSCORE + delta) % POSSCORE, scorerev = false; if(fakescore()) shiftScoreDisplay(delta); } #endif #ifndef MOBILE void handleScoreKeys(int sym, SDL_Event& ev) { if(sym == SDLK_LEFT || sym == SDLK_KP4 || sym == 'h' || sym == 'a') shiftScoreDisplay(-1); else if(sym == SDLK_RIGHT || sym == SDLK_KP6 || sym == 'l' || sym == 'd') shiftScoreDisplay(1); else if(sym == 't') cmode = emPickScores; else if(sym == SDLK_UP || sym == 'k' || sym == 'w') scorefrom -= 5; else if(sym == SDLK_DOWN || sym == 'j' || sym == 'x') scorefrom += 5; else if(sym == 's') sortScores(); else if(sym != 0 || ev.type == SDL_MOUSEBUTTONDOWN) cmode = emNormal; } void handlePickScoreKeys(int uni, SDL_Event& ev) { if(uni != 0) { int k = uni - '!'; if(k >= 0 && k < POSSCORE) scoredisplay = k; cmode = emScores; scorerev = false; } } #endif string ONOFF(bool b) { return XLAT(b ? "ON" : "OFF"); } void drawStats() { DEB("stats"); #ifdef IOS if(cmode != emNormal && cmode != emQuit) return; #endif int vx, vy; if(vid.xres < vid.yres) { vx = vid.fsize * 3; vy = vid.fsize * 2; } else { vx = vid.xres - vid.fsize * 3; vy = vid.fsize; } #define ADV(z) \ if(vid.xres < vid.yres) { \ vx += vid.fsize*4; \ if(vx > vid.xres - vid.fsize*2) vx = vid.fsize * 3, vy += vid.fsize; \ } \ else { \ vy += vid.fsize * z/2; \ if(vy > vid.yres) vx += (vx > vid.xres/2 ? -5:5) * vid.fsize, vy = vid.fsize * 5/2; \ } DEB("$$$"); if(displaynum(vx, vy, 0, vid.fsize, 0xFFFFFF, gold(), "$$$")) { mouseovers = XLAT("Your total wealth"), help = XLAT( MC "The total value of the treasure you have collected.\n\n" MC "Every world type contains a specific type of treasure, worth 1 $$$; " MC "your goal is to collect as much treasure as possible, but every treasure you find " MC "causes more enemies to hunt you in its native land.\n\n" M "Orbs of Yendor are worth 50 $$$ each.\n\n" ); } DEB("$$$Z"); ADV(3); int oldz = 0; for(int z=0; z<3; z++) for(int i=0; i= 3) { int kvx, kvy; if(vid.xres >= vid.yres) kvx = vid.fsize * 8, kvy = vid.fsize; else { ADV(2); kvx=vx, kvy=vy; } if(displaynum(kvx, kvy, 0, vid.fsize, 0xFFFFFF, tkills(), "XX")) mouseovers = XLAT("Your total kills"), help = XLAT( "In most lands, more treasures are generated with each enemy native to this land you kill. " "Moreover, 100 kills is a requirement to enter the Graveyard and the Hive.\n\n" "Friendly creatures and parts of monsters (such as the Ivy) do appear in the list, " "but are not counted in the total kill count."); } ADV(3); int s = vid.fsize; if(vid.xres >= vid.yres) vid.fsize = vid.fsize - vid.killreduction; for(int i=1; i vid.fsize * 3 && vid.xres >= vid.yres && vid.xres < vid.yres * 5/3) vid.killreduction++; DEB("stats OK"); achievement_display(); } void drawscreen() { #ifdef GL if(vid.usingGL) setGLProjection(); #endif calcparam(); if(cmode != emHelp) help = "@"; #ifndef MOBILE // SDL_LockSurface(s); // unsigned char *b = (unsigned char*) s->pixels; // int n = vid.xres * vid.yres * 4; // while(n) *b >>= 1, b++, n--; // memset(s->pixels, 0, vid.xres * vid.yres * 4); if(!vid.usingGL) SDL_FillRect(s, NULL, 0); #endif if(!canmove) darken = 1; if(cmode != emNormal && cmode != emDraw && cmode != emCustomizeChar) darken = 2; if(cmode == emQuit && !canmove) darken = 0; if(!vid.goteyes && !euclid) drawCircle(vid.xcenter, vid.ycenter, vid.radius, 0xFF); if(vid.wallmode < 2 && !euclid && !(vid.boardmode != 'h' && cwt.c->land == laGameBoard)) { int ls = size(lines); if(ISMOBILE) ls /= 10; for(int t=0; t> darken); } drawqueue(); ptds.clear(); DEB("dmap"); drawthemap(); DEB("mstar"); #ifndef MOBILE if(cmode == emNormal) drawmovestar(); #endif if(cmode == emDraw) { lalpha = 0x20; for(int d=0; d<84; d++) drawline(C0, spin(M_PI*d/42)* xpush(crossf) * C0, 0xC0C0C0); for(int d=0; d<84; d++) for(int u=2; u<=20; u++) drawline(spin(M_PI*d/42)* xpush(crossf*u/20) * C0, spin(M_PI*(d+1)/42)* xpush(crossf*u/20) * C0, 0xC0C0C0); lalpha = 0x80; if(crad > 0) { transmatrix movtocc = rgpushxto0(ccenter); for(int d=0; d<84; d++) drawline(movtocc * ddi(d+1, crad) * C0, movtocc * ddi(d, crad) * C0, 0x00C000); } // spin(M_PI*(d+1)/42) * xpush(crad) * spin(-M_PI*(d+1)/42) * ccenter, spin(M_PI*d/42) * xpush(crad) * spin(-M_PI*d/42) * ccenter, 0xC0C0C0); lalpha = 0xFF; } getcstat = 0; if(cmode == emNormal || cmode == emQuit) drawStats(); #ifdef MOBILE if(cmode == emNormal) { displayabutton(-1, -1, XLAT("MOVE"), andmode == 0 ? BTON : BTOFF); displayabutton(+1, -1, XLAT(andmode == 1 ? "BACK" : "DRAG"), andmode == 1 ? BTON : BTOFF); displayabutton(-1, +1, XLAT("INFO"), andmode == 2 ? BTON : BTOFF); displayabutton(+1, +1, XLAT(ISIOS ? "MENU" : andmode == 3 ? "QUEST" : "HELP"), andmode == 3 ? (ISIOS ? BTON : 0xFF00FF) : BTOFF); } if(cmode == emQuit) { displayabutton(-1, +1, XLAT("NEW"), BTON); displayabutton(+1, +1, XLAT(canmove ? "PLAY" : ISIOS ? " " : "SHARE"), BTON); } #endif if(cmode == emDraw) { mouseovers = floordraw ? (cwt.c->type == 6 ? "hex floor" : "hepta floor") : "character"; mouseovers = XLAT("Drawing %1 (layer %2), F1 for help", XLAT(mouseovers), its(dslayer)); } // displaynum(vx,100, 0, 24, 0xc0c0c0, celldist(cwt.c), ":"); darken = 0; drawmessages(); DEB("msgs1"); if(cmode == emNormal) { #ifdef MOBILE if(!canmove) cmode = emQuit; #endif if(!canmove) showGameover(); #ifndef MOBILE // if(!canmove) // displayButton(vid.xres-8, vid.yres-vid.fsize*2, XLAT("ESC for menu/quest"), SDLK_ESCAPE, 16); #endif } #ifndef ANDROID if(cmode == emScores) showScores(); if(cmode == emPickScores) showPickScores(); #endif #ifndef MOBILE if(cmode == emChangeMode) { displayStat(2, XLAT("vector graphics editor"), "", 'g'); displayStat(3, XLAT("Euclidean mode"), "", 'e'); displayStat(4, XLAT("cheat mode"), "", 'c'); displayStat(6, XLAT("heptagonal game board"), "", '7'); displayStat(7, XLAT("triangular game board"), "", '3'); displayStat(8, XLAT("HyperRogue game board"), "", 'h'); displayStat(9, XLAT("Periodic Editor"), "", 'f'); displayStat(18, XLAT("first page [Space]"), "", ' '); displayStat(19, XLAT("exit configuration"), "", 'v'); } if(cmode == emCustomizeChar) { displayStatHelp(0, XLAT("Customize character")); displayStat(2, XLAT("gender"), XLAT(vid.female ? "female" : "male"), 'g'); displayStat(3, XLAT("skin color"), "?", 's'); displayStat(4, XLAT("weapon color"), "?", 'w'); displayStat(5, XLAT("hair color"), "?", 'h'); if(vid.female) displayStat(6, XLAT("dress color"), "?", 'd'); displayStatHelp(16, XLAT("Shift=random, Ctrl=mix")); displayStat(19, XLAT("exit configuration"), "", 'v'); } if(cmode == emPickEuclidean) { int s = vid.fsize; vid.fsize = vid.fsize * 4/5; displayStatHelp(0, XLAT("Euclidean mode")); int lt = landtypes; landvisited[laCrossroads] = true; landvisited[laIce] = true; landvisited[laMirror] = true; // for(int i=2; i= 500) siz = (siz * 9)/10; else if(ISIOS) siz = (siz * 3+1)/2; else if(size(help) >= 500) siz = siz * 2/3; int vy = vid.fsize * 4; int xs = vid.xres * 618/1000; int xo = vid.xres * 186/1000; for(int i=0; i<=size(help); i++) { int ls = 0; int prev = last; if(help[i] == ' ') lastspace = i; if(textwidth(siz, help.substr(last, i-last)) > xs) { if(lastspace == last) ls = i-1, last = i-1; else ls = lastspace, last = ls+1; } if(help[i] == 10 || i == size(help)) ls = i, last = i+1; if(ls) { displayfr(xo, vy, 2, siz, help.substr(prev, ls-prev), 0xC0C0C0, 0); if(ls == prev) vy += siz/2; else vy += siz; lastspace = last; } } } #endif #ifndef MOBILE if(mouseover && targetclick) { eItem i = targetRangedOrb(mouseover, roCheck); if(i) displaychr(mousex, mousey, 0, vid.fsize, '@', iinf[i].color); } #endif #ifndef MOBILE DEB("msgs3"); // SDL_UnlockSurface(s); #ifdef GL if(vid.usingGL) SDL_GL_SwapBuffers(); else #endif SDL_UpdateRect(s, 0, 0, vid.xres, vid.yres); #endif if(playermoved && vid.aspeed > 4.99) { centerpc(1000); playermoved = false; return; } } #ifndef MOBILE bool setfsize = false; void setvideomode() { if(!vid.full) { if(vid.xres > vid.xscr) vid.xres = vid.xscr * 9/10, setfsize = true; if(vid.yres > vid.yscr) vid.yres = vid.yscr * 9/10, setfsize = true; } if(setfsize) vid.fsize = min(vid.yres / 32, vid.xres / 48), setfsize = false; int flags = 0; #ifdef GL if(vid.usingGL) { flags = SDL_OPENGL | SDL_HWSURFACE | SDL_GL_DOUBLEBUFFER; SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1); } #endif int sizeflag = (vid.full ? SDL_FULLSCREEN : SDL_RESIZABLE); s= SDL_SetVideoMode(vid.xres, vid.yres, 32, flags | sizeflag); if(vid.full && !s) { vid.xres = vid.xscr; vid.yres = vid.yscr; vid.fsize = min(vid.yres / 32, vid.xres / 48); s = SDL_SetVideoMode(vid.xres, vid.yres, 32, flags | SDL_FULLSCREEN); } if(!s) { addMessage("Failed to set the graphical mode: "+its(vid.xres)+"x"+its(vid.yres)+(vid.full ? " fullscreen" : " windowed")); vid.xres = 640; vid.yres = 480; s = SDL_SetVideoMode(vid.xres, vid.yres, 32, flags | SDL_RESIZABLE); } #ifdef GL glViewport(0, 0, vid.xres, vid.yres); resetGL(); #endif } #endif void restartGraph() { if(euclid) { centerover = euclideanAtCreate(0,0); printf("centerover = %p\n", centerover); } else { viewctr.h = &origin; viewctr.spin = 0; } View = Id; webdisplay = 0; } #ifndef ANDROID void saveConfig() { FILE *f = fopen(conffile, "wt"); if(!f) { addMessage(s0 + "Could not open the config file: " + conffile); return; } fprintf(f, "%d %d %d %d\n", vid.xres, vid.yres, vid.full, vid.fsize); fprintf(f, "%f %f %f %f\n", float(vid.scale), float(vid.eye), float(vid.alpha), float(vid.aspeed)); fprintf(f, "%d %d %d %d %d %d %d\n", vid.wallmode, vid.monmode, vid.axes, audiovolume, vid.framelimit, vid.usingGL, vid.usingAA); fprintf(f, "%d %d %d %f %d %d\n", vid.joyvalue, vid.joyvalue2, vid.joypanthreshold, vid.joypanspeed, autojoy, vid.flashtime); fprintf(f, "%d %d %08x %08x %08x %08x\n", vid.female?1:0, vid.language, vid.skincolor, vid.haircolor, vid.swordcolor, vid.dresscolor); fprintf(f, "%d %d\n", vid.darkhepta, vid.shifttarget); fprintf(f, "\n\nThis is a configuration file for HyperRogue (version "VER")\n"); fprintf(f, "\n\nThe numbers are:\n"); fprintf(f, "screen width & height, fullscreen mode (0=windowed, 1=fullscreen), font size\n"); fprintf(f, "scale, eye distance, parameter, animation speed\n"); fprintf(f, "wallmode, monster mode, cross mode, audiovolume, framerate limit, usingGL, usingAA\n"); fprintf(f, "calibrate first joystick (threshold A, threshold B), calibrate second joystick (pan threshold, pan speed), joy mode\n"); fprintf(f, "gender (1=female), language, skin color, hair color, sword color, dress color\n"); fprintf(f, "darken hepta, shift target\n"); fclose(f); addMessage(s0 + "Configuration saved to: " + conffile); } void loadConfig() { vid.xres = 9999; vid.yres = 9999; vid.framelimit = 300; FILE *f = fopen(conffile, "rt"); if(f) { int fs, gl=1, aa=1, bb=1; int err; err=fscanf(f, "%d%d%d%d", &vid.xres, &vid.yres, &fs, &vid.fsize); vid.full = fs; float a, b, c, d; err=fscanf(f, "%f%f%f%f\n", &a, &b, &c, &d); if(err == 4) { vid.scale = a; vid.eye = b; vid.alpha = c; vid.aspeed = d; } err=fscanf(f, "%d%d%d%d%d%d%d", &vid.wallmode, &vid.monmode, &vid.axes, &audiovolume, &vid.framelimit, &gl, &aa); vid.usingGL = gl; vid.usingAA = aa; err=fscanf(f, "%d%d%d%f%d%d", &vid.joyvalue, &vid.joyvalue2, &vid.joypanthreshold, &vid.joypanspeed, &aa, &vid.flashtime); autojoy = aa; aa = 0; err=fscanf(f, "%d%d%x%x%x%x", &aa, &vid.language, &vid.skincolor, &vid.haircolor, &vid.swordcolor, &vid.dresscolor); vid.female = aa; aa=0; bb=0; err=fscanf(f, "%d%d", &aa, &bb); vid.darkhepta = aa; vid.shifttarget = bb; fclose(f); printf("Loaded configuration: %s\n", conffile); } #ifndef MOBILE if(clWidth) vid.xres = clWidth; if(clHeight) vid.yres = clHeight; if(clFont) vid.fsize = clFont; for(int k=0; k 0) { for(int i=0; buf[i]; i++) if(buf[i] == 10 || buf[i] == 13) buf[i] = 0; if(buf[0] == '[' && buf[3] == ']') { int id = (buf[1] - '0') * 10 + buf[2] - '0'; if(id >= 0 && id < landtypes) { if(buf[5] == '*' && buf[6] == '/') musfname[id] = dir2 + (buf+7); else musfname[id] = buf+5; } else { fprintf(stderr, "warning: bad soundtrack id, use the following format:\n"); fprintf(stderr, "[##] */filename\n"); fprintf(stderr, "where ## are two digits, and */ is optional and replaced by path to the music\n"); fprintf(stderr, "alternatively LAST = reuse the last track instead of having a special one"); } // printf("[%2d] %s\n", id, buf); } else if(buf[0] == '#') { } else { musiclicense += buf; musiclicense += "\n"; } } fclose(f); return true; } return false; } #endif void initgraph() { vid.usingGL = true; vid.usingAA = true; vid.flashtime = 8; vid.scale = 1; vid.alpha = 1; vid.aspeed = 0; vid.eye = 0; vid.full = false; vid.quick = true; #ifdef ANDROID vid.wallmode = 2; #else vid.wallmode = 3; #endif vid.joyvalue = 4800; vid.joyvalue2 = 5600; vid.joypanthreshold = 2500; vid.joypanspeed = 0.0001; vid.framelimit = 75; vid.monmode = 2; vid.axes = 1; vid.skincolor = 0xD0D0D0FF; vid.haircolor = 0x686868FF; vid.dresscolor= 0xC00000FF; vid.swordcolor= 0xD0D0D0FF; vid.killreduction = 0; vid.female = false; vid.language = -1; vid.boardmode = 'h'; joyx = joyy = 0; joydir = -1; restartGraph(); initgeo(); buildpolys(); for(int i=0; i<8; i++) dsUser[i][0].rots = 6, dsUser[i][1].rots = 7, dsUser[i][2].rots = 1, dsUser[i][2].sym = true; for(int i=0; i<8; i++) for(int k=0; k<3; k++) dsUser[i][k].shift = C0, dsUser[i][k].spin = Cx1; #ifndef MOBILE if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1) { printf("Failed to initialize video.\n"); exit(2); } const SDL_VideoInfo *inf = SDL_GetVideoInfo(); vid.xscr = vid.xres = inf->current_w; vid.yscr = vid.yres = inf->current_h; SDL_WM_SetCaption("HyperRogue " VER, "HyperRogue "VER); loadConfig(); setvideomode(); if(!s) { printf("Failed to initialize graphics.\n"); exit(2); } SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); SDL_EnableUNICODE(1); if(TTF_Init() != 0) { printf("Failed to initialize TTF.\n"); exit(2); } if(SDL_NumJoysticks()) stick = SDL_JoystickOpen(0); if(SDL_NumJoysticks() >= 2) panstick = SDL_JoystickOpen(1); #ifdef AUDIO audio = loadMusicInfo(musicfile) || loadMusicInfo("./hyperrogue-music.txt") || loadMusicInfo("music/hyperrogue-music.txt") #ifdef FHS || loadMusicInfo("/usr/share/hyperrogue/hyperrogue-music.txt") || loadMusicInfo(s0 + getenv("HOME") + "/.hyperrogue-music.txt") #endif ; if(audio) { if(Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 4096) != 0) { fprintf(stderr, "Unable to initialize audio: %s\n", Mix_GetError()); audio = false; } else { audio = true; Mix_AllocateChannels(4); } } #endif #endif } int frames; bool outoffocus = false; #ifdef AUDIO bool loaded[landtypes]; Mix_Music* music[landtypes]; int musicpos[landtypes]; int musstart; int musfadeval = 2000; eLand cid = laNone; void handlemusic() { if(audio && audiovolume) { eLand id = cwt.c->land; if(outoffocus) id = eLand(0); if(musfname[id] == "LAST") id = cid; if(!loaded[id]) { loaded[id] = true; // printf("loading (%d)> %s\n", id, musfname[id].c_str()); if(musfname[id] != "") { music[id] = Mix_LoadMUS(musfname[id].c_str()); if(!music[id]) { printf("Mix_LoadMUS: %s\n", Mix_GetError()); } } } if(cid != id && !musfadeval) { musicpos[cid] = SDL_GetTicks() - musstart; musfadeval = musicpos[id] ? 500 : 2000; Mix_FadeOutMusic(musfadeval); // printf("fadeout %d, pos %d\n", musfadeval, musicpos[cid]); } if(music[id] && !Mix_PlayingMusic()) { cid = id; Mix_VolumeMusic(audiovolume); Mix_FadeInMusicPos(music[id], -1, musfadeval, musicpos[id] / 1000.0); // printf("fadein %d, pos %d\n", musfadeval, musicpos[cid]); musstart = SDL_GetTicks() - musicpos[id]; musicpos[id] = 0; musfadeval = 0; } } } void resetmusic() { if(audio && audiovolume) { Mix_FadeOutMusic(3000); cid = laNone; for(int i=0; i> 8, cs[rand() % q] >> 8, 0, rand() % 101, 100) << 8) + 0xFF; else c = cs[(id+1) % q]; } const char* drawhelp = "In this mode you can draw your own player character and floors. " "Mostly for the development purposes, but you can have fun too.\n\n" "f - floor, p - player (repeat 'p' for layers)\n\n" "n - new shape, u - copy the 'player body' shape\n\n" "1-9 - rotational symmetries, 0 - toggle axial symmetry\n\n" "point with mouse and: a - add point, m - move nearest point, d - delete nearest point, c - nearest point again, b - add after nearest\n\n" "s - save in C++ format (but cannot be loaded yet without editing source)\n\n" "z - zoom, o - Poincaré model\n"; #ifndef MOBILE // Warning: a very long function! todo: refactor void loadShape(hpcshape& sh, int d, int layer) { dsCur = &(dsUser[layer][2]); dsCur->list.clear(); dsCur->sym = d==2; for(int i=sh.s; i < sh.s + (sh.e-sh.s)/d; i++) dsCur->list.push_back(hpc[i]); } void mainloop() { int lastt = 0; while(true) { #ifndef GFX #ifndef GL vid.wallmode = 0; vid.monmode = 0; #endif #endif DEB("screen"); optimizeview(); ticks = SDL_GetTicks(); dslayer %= 8; dsCur = &(dsUser[dslayer][floordraw ? cwt.c->type-6 : 2]); int cframelimit = vid.framelimit; if((cmode == emVisual1 || cmode == emVisual2 || cmode == emHelp || cmode == emQuit) && cframelimit > 15) cframelimit = 15; if(outoffocus && cframelimit > 10) cframelimit = 10; int timetowait = lastt + 1000 / cframelimit - ticks; if(timetowait > 0) SDL_Delay(timetowait); else { if(playermoved && vid.aspeed > -4.99 && !outoffocus) centerpc((ticks - lastt) / 1000.0 * exp(vid.aspeed)); if(panstick || panjoyx || panjoyy) checkpanjoy((ticks - lastt) / 1000.0); lastt = ticks; frames++; if(!outoffocus) drawscreen(); } Uint8 *keystate = SDL_GetKeyState(NULL); rightclick = keystate[SDLK_RCTRL]; leftclick = keystate[SDLK_RSHIFT]; hiliteclick = keystate[SDLK_LALT] | keystate[SDLK_RALT]; if(vid.shifttarget) { leftclick = false; targetclick = keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]; } else { leftclick = keystate[SDLK_RSHIFT]; targetclick = true; } #ifdef AUDIO if(audio) handlemusic(); #endif SDL_Event ev; DEB("react"); while(SDL_PollEvent(&ev)) { int sym = 0; int uni = 0; ld shift = 1; if(ev.type == SDL_ACTIVEEVENT) { if(ev.active.state & SDL_APPINPUTFOCUS) { if(ev.active.gain) { outoffocus = false; } else { outoffocus = true; } } } if(ev.type == SDL_VIDEORESIZE) { vid.xres = ev.resize.w; vid.yres = ev.resize.h; vid.killreduction = 0; setfsize = true; setvideomode(); #ifdef GL glViewport(0, 0, vid.xres, vid.yres); #endif } if(ev.type == SDL_VIDEOEXPOSE) { drawscreen(); } if(ev.type == SDL_JOYAXISMOTION) { if(ev.jaxis.value != 0 && ev.jaxis.axis >= 2) printf("which = %d axis = %d value = %d\n", ev.jaxis.which, ev.jaxis.axis, ev.jaxis.value); if(ev.jaxis.which == 0) { if(ev.jaxis.axis == 0) joyx = ev.jaxis.value; else if(ev.jaxis.axis == 1) joyy = ev.jaxis.value; else if(ev.jaxis.axis == 3) panjoyx = ev.jaxis.value; else if(ev.jaxis.axis == 4) panjoyy = ev.jaxis.value; checkjoy(); printf("panjoy = %d,%d\n", panjoyx, panjoyy); } else { if(ev.jaxis.axis == 0) panjoyx = ev.jaxis.value; else panjoyy = ev.jaxis.value; } } if(ev.type == SDL_JOYBUTTONDOWN) { movepcto(joydir); checkjoy(); } if(ev.type == SDL_KEYDOWN) { flashMessages(); mousing = false; sym = ev.key.keysym.sym; uni = ev.key.keysym.unicode; if(ev.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) shift = -1; if(ev.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) shift /= 10; } if(ev.type == SDL_MOUSEBUTTONDOWN) { flashMessages(); mousepressed = true; mousing = true; if(ev.button.button==SDL_BUTTON_RIGHT || leftclick) sym = SDLK_F1; else if(ev.button.button==SDL_BUTTON_MIDDLE || rightclick) sym = 1; else if(ev.button.button==SDL_BUTTON_WHEELUP) { ld jx = (mousex - vid.xcenter - .0) / vid.radius / 10; ld jy = (mousey - vid.ycenter - .0) / vid.radius / 10; playermoved = false; View = gpushxto0(hpxy(jx, jy)) * View; sym = 1; } else { sym = getcstat, uni = getcstat, shift = getcshift; } } if(ev.type == SDL_MOUSEBUTTONUP) mousepressed = false; if(cmode != emScores) { #ifndef PANDORA if(sym == SDLK_RIGHT) View = xpush(-0.2*shift) * View, playermoved = false; if(sym == SDLK_LEFT) View = xpush(+0.2*shift) * View, playermoved = false; if(sym == SDLK_UP) View = ypush(+0.2*shift) * View, playermoved = false; if(sym == SDLK_DOWN) View = ypush(-0.2*shift) * View, playermoved = false; #endif if(sym == SDLK_PAGEUP) View = spin(M_PI/21*shift) * View; if(sym == SDLK_PAGEDOWN) View = spin(-M_PI/21*shift) * View; } if(ev.type == SDL_MOUSEMOTION) { hyperpoint mouseoh = mouseh; mousing = true; mousex = ev.motion.x; mousey = ev.motion.y; mouseh = gethyper(mousex, mousey); if((rightclick || (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) && !outofmap(mouseh) && !outofmap(mouseoh) && mouseh[2] < 50 && mouseoh[2] < 50) { panning(mouseoh, mouseh); } #ifdef SIMULATE_JOYSTICK // pretend that both joysticks are present stick = panstick = (SDL_Joystick*) (&vid); panjoyx = 20 * (mousex - vid.xcenter); panjoyy = 20 * (mousey - vid.ycenter); checkjoy(); #endif } DEB("r1"); if(sym == SDLK_F7) { time_t timer; timer = time(NULL); char buf[128]; strftime(buf, 128, "shot-%y%m%d-%H%M%S.bmp", localtime(&timer)); SDL_SaveBMP(s, buf); addMessage(XLAT("Screenshot saved to %1", buf)); } DEB("r2"); if(cmode == emDraw) { hyperpoint mh = inverse(playertrans) * mouseh; // inverse(playertrans) * // mouseh; // spin(-M_PI/2) * mouseh; if(uni == 'n') { dsCur->list.clear(); dsCur->list.push_back(mh); #ifdef GL GL_initialized = false; #endif } if(uni == 'a') { dsCur->list.push_back(mh); #ifdef GL GL_initialized = false; #endif } if(uni == 'u') { if(!vid.female) loadShape(shPBody, 2, 0); else loadShape(shFemaleBody, 2, 0); loadShape(shPSword, 1, 1); if(vid.female) loadShape(shFemaleDress, 2, 2); if(vid.female) loadShape(shFemaleHair, 2, 3); else loadShape(shPHead, 2, 3); loadShape(shPFace, 2, 4); #ifdef GL GL_initialized = false; #endif } if(uni == 'm' || uni == 'd' || uni == 'c' || uni == 'b') { int i = 0; if(size(dsCur->list) < 2) continue; for(int j=1; jlist); j++) if(intval(mh, dsCur->list[j]) < intval(mh, dsCur->list[i])) i = j; if(uni == 'm') dsCur->list[i] = mh; if(uni == 'd') dsCur->list.erase(dsCur->list.begin() + i); if(uni == 'c') dsCur->list.push_back(dsCur->list[i]); if(uni == 'b') { while(i) { dsCur->list.push_back(dsCur->list[0]); dsCur->list.erase(dsCur->list.begin()); i--; } } #ifdef GL GL_initialized = false; #endif } if(uni == 'f') { if(floordraw) dslayer++; else floordraw = true; } if(uni == 'p') { if(!floordraw) dslayer++; else floordraw = false; } if(uni == 'g') ccenter = mouseh, crad += shift / 20; if(uni == '1') dsCur->rots = 1; if(uni == '2') dsCur->rots = 2; if(uni == '3') dsCur->rots = 3; if(uni == '4') dsCur->rots = 4; if(uni == '5') dsCur->rots = 5; if(uni == '6') dsCur->rots = 6; if(uni == '7') dsCur->rots = 7; if(uni == '8') dsCur->rots = 8; if(uni == '9') dsCur->rots = 21; if(uni == '0') dsCur->sym = !dsCur->sym; if(uni == 't') dsCur->shift = mh; if(uni == 'y') dsCur->spin = mh; #ifdef GL if(uni == 't' || uni == 'y') GL_initialized = false; #endif if(uni == 's') { for(int i=prehpc; i < qhpc; i++) { for(int k=0; k<8; k++) for(int j=0; j<3; j++) if(i == shUser[k][j].s) printf("\n // group %d layer %d\n\n", j, k); printf(" hpcpush(hpxyz(%f,%f,%f));\n", double(hpc[i][0]), double(hpc[i][1]), double(hpc[i][2])); } } if(uni == 'z') vid.alpha = -.5; if(uni == 'o') vid.alpha = 1; saveImages(); if(sym == SDLK_F6) { cmode = emNormal; sym = 0; } if(sym == SDLK_ESCAPE) cmode = emNormal; if(sym == SDLK_F1) { cmode = emHelp; sym = 0; help = XLAT(drawhelp); } if(sym == SDLK_F2) { cmode = emVisual1; sym = 0; } if(sym == SDLK_F10) cmode = emNormal; } if(cmode == emNormal) { if(!(uni >= 'A' && uni <= 'Z') && cwt.c->land != laGameBoard) { if(sym == 'l' || sym == 'd' || sym == SDLK_KP6) movepckeydir(0); if(sym == 'n' || sym == 'c' || sym == SDLK_KP3) movepckeydir(1); if(sym == 'j' || sym == 'x' || sym == SDLK_KP2) movepckeydir(2); if(sym == 'b' || sym == 'z' || sym == SDLK_KP1) movepckeydir(3); if(sym == 'h' || sym == 'a' || sym == SDLK_KP4) movepckeydir(4); if(sym == 'y' || sym == 'q' || sym == SDLK_KP7) movepckeydir(5); if(sym == 'k' || sym == 'w' || sym == SDLK_KP8) movepckeydir(6); if(sym == 'u' || sym == 'e' || sym == SDLK_KP9) movepckeydir(7); } #ifdef PANDORA if(sym == SDLK_RIGHT) movepckeydir(0); if(sym == SDLK_LEFT) movepckeydir(4); if(sym == SDLK_DOWN) movepckeydir(2 + (leftclick?1:0) - (rightclick?1:0)); if(sym == SDLK_UP) movepckeydir(6 - (leftclick?1:0) + (rightclick?1:0)); #endif if(cheater) applyCheat(uni, mouseover); if(cwt.c->land == laGameBoard) applyGameBoard(uni, sym, mouseover, vid.boardmode); else { if(sym == '.' || sym == 's') movepcto(-1); if((sym == SDLK_DELETE || sym == SDLK_KP_PERIOD || sym == 'g') && uni != 'G' && uni != 'G'-64) movepcto(-2); if(sym == 't' && uni != 'T' && uni != 'T'-64) { targetRangedOrb(centerover, roKeyboard); } } if(sym == SDLK_KP5) movepcto(-1); // if(sym == SDLK_F4) restartGameSwitchEuclid(); if(sym == SDLK_F5) restartGame(); if(sym == SDLK_ESCAPE) { cmode = emQuit; achievement_final(false); if(!canmove) { addMessage(XLAT("GAME OVER")); addMessage(timeline()); } msgscroll = 0; } if(sym == SDLK_F10) return; if(!canmove) { if(sym == SDLK_RETURN) return; else if(uni == 'r') restartGame(); else if(uni == 't') { restartGame(); loadScores(); } } if(sym == SDLK_HOME || sym == SDLK_F3 || sym == ' ') { if(playerfound) centerpc(INF); else { View = Id; // EUCLIDEAN if(!euclid) viewctr.h = cwt.c->master; else centerover = cwt.c; // SDL_LockSurface(s); drawthemap(); // SDL_UnlockSurface(s); centerpc(INF); } playermoved = true; } if(sym == SDLK_F6) { View = spin(M_PI/2) * inverse(cwtV) * View; if(flipplayer) View = spin(M_PI) * View; cmode = emDraw; } if(sym == 'v' || sym == SDLK_F2) { cmode = emVisual1; } #ifdef PANDORA if(ev.type == SDL_MOUSEBUTTONUP && sym == 0 && !rightclick) { #else if(ev.type == SDL_MOUSEBUTTONDOWN && sym == 0 && !rightclick) { #endif bool forcetarget = (keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]); if(mouseover && targetclick && targetRangedOrb(mouseover, forcetarget ? roMouseForce : roMouse)) ; else if(forcetarget) ; else if(mousedest == -1) movepcto(mousedest); else movepcto((mousedest + 42 - cwt.spin)%42); } if(sym == SDLK_F1) { cmode = emHelp; } } else if(cmode == emVisual1) { char xuni = uni | 96; if(uni >= 32 && uni < 64) xuni = uni; if(uni == ' ') cmode = emVisual2; if(xuni == 'p') vid.alpha += shift * 0.1; if(xuni == 'z') vid.scale += shift * 0.1; if(xuni == 'a') vid.aspeed += shift; if(xuni == 'f') { vid.full = !vid.full; if(shift > 0) { vid.xres = vid.full ? vid.xscr : 9999; vid.yres = vid.full ? vid.yscr : 9999; setfsize = true; } setvideomode(); } if(xuni == 'v' || sym == SDLK_F2) cmode = emNormal; if(xuni == 's') saveConfig(); if(xuni == '7') { vid.darkhepta = !vid.darkhepta; } if(xuni == 'w') { vid.wallmode += 60 + (shift > 0 ? 1 : -1); vid.wallmode %= 4; } if(xuni == 'm') { vid.monmode += 60 + (shift > 0 ? 1 : -1); vid.monmode %= 4; } if(xuni == 'c') { vid.axes += 60 + (shift > 0 ? 1 : -1); vid.axes %= 4; } if(xuni == 'b') { audiovolume += int(10.5 * shift); if(audiovolume < 0) audiovolume = 0; if(audiovolume > MIX_MAX_VOLUME) audiovolume = MIX_MAX_VOLUME; Mix_VolumeMusic(audiovolume); } if(sym == SDLK_ESCAPE) cmode = emNormal; #ifdef SHOWOFF process_showoff(sym); #endif if(sym == SDLK_F1 || sym == 'h') { cmode = emHelp; } if(xuni == 'l') { vid.language++; vid.language %= NUMLAN; printf("lan = %d\n", vid.language); } if(xuni == 'g') { vid.female = !vid.female; switchcolor(vid.skincolor, skincolors, 0); switchcolor(vid.haircolor, haircolors, 0); switchcolor(vid.dresscolor, dresscolors, 0); switchcolor(vid.swordcolor, swordcolors, 0); cmode = emCustomizeChar; } } else if(cmode == emCustomizeChar) { char xuni = uni | 96; int mod = ev.key.keysym.mod; if(shift < -.5) mod = 1; else if(shift > -.2 && shift < .2) mod = 2; else mod = 0; if(xuni == 'g') vid.female = !vid.female; if(xuni == 's') switchcolor(vid.skincolor, skincolors, mod); if(xuni == 'h') switchcolor(vid.haircolor, haircolors, mod); if(xuni == 'w') switchcolor(vid.swordcolor, swordcolors, mod); if(xuni == 'd') switchcolor(vid.dresscolor, dresscolors, mod); if(xuni == 'v' || sym == SDLK_F2 || sym == SDLK_ESCAPE) cmode = emNormal; } else if(cmode == emVisual2) { char xuni = uni | 96; #ifdef GL if(xuni == 'v' || sym == SDLK_F2 || sym == SDLK_ESCAPE) cmode = emNormal; if(xuni == 'o' && shift > 0) { vid.usingGL = !vid.usingGL; if(vid.usingGL) addMessage(XLAT("openGL mode enabled")); if(!vid.usingGL) addMessage(XLAT("openGL mode disabled")); setvideomode(); } if(xuni == 'o' && shift < 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 * shift); if(vid.framelimit < 5) vid.framelimit = 5; } if(xuni == 'a') vid.joyvalue += int(shift * 100); if(xuni == 'b') vid.joyvalue2 += int(shift * 100); if(xuni == 'c') vid.joypanthreshold += int(shift * 100); if(xuni == 'd') vid.joypanspeed += shift / 50000; if(xuni == 'e') vid.eye += shift * 0.01; if(xuni == 't') vid.flashtime += shift>0?1:-1; if(xuni == 'p') autojoy = !autojoy; if(xuni == 's') { vid.shifttarget = !vid.shifttarget; } if(uni == ' ') cmode = emChangeMode; } else if(cmode == emChangeMode) { char xuni = uni | 96; if(xuni == 'v' || sym == SDLK_F2 || sym == SDLK_ESCAPE) cmode = emNormal; if(uni == ' ') cmode = emVisual1; if(sym == 'c') { cheater++; addMessage(XLAT("You activate your demonic powers!")); addMessage(XLAT("Shift+F, Shift+O, Shift+T, Shift+L, Shift+U, etc.")); cmode = emNormal; } if(xuni == 'g') cmode = emDraw; if(xuni == 'e') cmode = emPickEuclidean; if(uni == 'h' || uni == '3' || uni == '7' || uni == 'f') { vid.boardmode = uni; cheater = 1; showid = 0; firstland = laGameBoard; for(int i=0; i= 'a' && xuni < 'a' + landtypes) { euclidland = eLand(2 + (xuni - 'a')); if(landvisited[euclidland]) { if(euclid) restartGame(); else restartGameSwitchEuclid(); cmode = emNormal; } else euclidland = laIce; } } else if(cmode == emHelp) { if(sym == SDLK_F1 && help != "@") help = "@"; else if(uni == 'c') help = buildCredits(); else if((sym != 0 && sym != SDLK_F12) || ev.type == SDL_MOUSEBUTTONDOWN) cmode = (help == XLAT(drawhelp) ? emDraw : emNormal); } else if(cmode == emQuit) { if(sym == SDLK_RETURN || sym == SDLK_F10) return; else if(uni == 'r' || sym == SDLK_F5) { restartGame(), cmode = emNormal; msgs.clear(); } else if(sym == SDLK_UP || sym == SDLK_KP8) msgscroll++; else if(sym == SDLK_DOWN || sym == SDLK_KP2) msgscroll--; else if(sym == SDLK_PAGEUP || sym == SDLK_KP9) msgscroll+=5; else if(sym == SDLK_PAGEDOWN || sym == SDLK_KP3) msgscroll-=5; else if(uni == 't') { if(!canmove) restartGame(); loadScores(); msgs.clear(); } else if((sym != 0 && sym != SDLK_F12) || ev.type == SDL_MOUSEBUTTONDOWN) { cmode = emNormal; msgs.clear(); } } else if(cmode == emScores) handleScoreKeys(sym, ev); else if(cmode == emPickScores) handlePickScoreKeys(uni, ev); if(ev.type == SDL_QUIT) return; DEB("r3"); } if(playerdead) break; } } #endif #ifndef MOBILE void cleargraph() { for(int i=0; i<256; i++) if(font[i]) TTF_CloseFont(font[i]); for(int i=0; i<128; i++) if(glfont[i]) delete glfont[i]; #ifndef SIMULATE_JOYSTICK if(stick) SDL_JoystickClose(stick); if(panstick) SDL_JoystickClose(panstick); #endif SDL_Quit(); } #endif void cleargraphmemory() { mouseover = centerover = lmouseover = NULL; }