1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-11-17 18:54:48 +00:00
hyperrogue/graph.cpp

9214 lines
280 KiB
C++
Raw Normal View History

2016-08-26 09:58:03 +00:00
// Hyperbolic Rogue -- graphics
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
// Copyright (C) 2011-2012 Zeno Rogue, see 'hyper.cpp' for details
// basic graphics:
2017-03-23 10:53:57 +00:00
bool quitsaves() { return (items[itOrbSafety] && havesave); }
bool wmspatial, wmescher, wmplain, wmblack, wmascii;
bool mmspatial, mmhigh, mmmon, mmitem;
int maxreclevel, reclevel;
int lastt;
int backcolor = 0;
int detaillevel = 0;
2016-08-26 09:58:03 +00:00
#define WOLNIEJ 1
2016-01-02 10:09:13 +00:00
#define BTOFF 0x404040
#define BTON 0xC0C000
2017-03-23 10:53:57 +00:00
#define K(c) (c->type==6?0:1)
#ifdef ROGUEVIZ
#define DOSHMUP (shmup::on || rogueviz::on)
#else
#define DOSHMUP shmup::on
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
// #define PANDORA
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int colorbar;
#define COLORBAR "###"
2015-08-08 13:57:52 +00:00
#ifndef NOSDL
2015-08-08 13:57:52 +00:00
SDL_Surface *s;
2016-01-02 10:09:13 +00:00
SDL_Joystick* sticks[8];
int numsticks;
#ifndef NOTTF
TTF_Font *font[256];
#endif
2015-08-08 13:57:52 +00:00
#endif
2016-08-26 09:58:03 +00:00
ld shiftmul = 1;
bool inHighQual; // taking high quality screenshot
2015-08-08 13:57:52 +00:00
// R:239, G:208, B:207
2016-01-02 10:09:13 +00:00
unsigned int skincolors[] = { 7, 0xD0D0D0FF, 0xEFD0C9FF, 0xC77A58FF, 0xA58869FF, 0x602010FF, 0xFFDCB1FF, 0xEDE4C8FF };
unsigned int haircolors[] = { 8, 0x686868FF, 0x8C684AFF, 0xF2E1AEFF, 0xB55239FF, 0xFFFFFFFF, 0x804000FF, 0x502810FF, 0x301800FF };
unsigned int dresscolors[] = { 6, 0xC00000FF, 0x00C000FF, 0x0000C0FF, 0xC0C000FF, 0xC0C0C0FF, 0x202020FF };
2016-08-26 09:58:03 +00:00
unsigned int dresscolors2[] = { 7, 0x8080FFC0, 0x80FF80C0, 0xFF8080C0, 0xFFFF80C0, 0xFF80FFC0, 0x80FFFFC0, 0xFFFFFF80 };
2016-01-02 10:09:13 +00:00
unsigned int swordcolors[] = { 6, 0xC0C0C0FF, 0xFFFFFFFF, 0xFFC0C0FF, 0xC0C0FFFF, 0x808080FF, 0x202020FF };
2016-08-26 09:58:03 +00:00
unsigned int eyecolors[] = { 4, 0x00C000FF, 0x0000C0FF, 0xC00000FF, 0xC0C000FF };
2015-08-08 13:57:52 +00:00
// is the player using mouse? (used for auto-cross)
bool mousing = true;
// is the mouse button pressed?
bool mousepressed = false;
2017-03-23 10:53:57 +00:00
bool mousemoved = false;
bool actonrelease = false;
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
emtype cmode = emNormal, lastmode = emNormal; // last mode in Help
2015-08-08 13:57:52 +00:00
//
int axestate;
int ticks;
2017-03-23 10:53:57 +00:00
int frameid;
2015-08-08 13:57:52 +00:00
videopar vid;
int default_language;
2016-08-26 09:58:03 +00:00
charstyle& getcs() {
2017-03-23 10:53:57 +00:00
if(multi::players>1 && multi::cpid >= 0 && multi::cpid < multi::players)
return multi::scs[multi::cpid];
2016-08-26 09:58:03 +00:00
else
return vid.cs;
}
2017-04-08 15:18:29 +00:00
bool camelotcheat;
2016-08-26 09:58:03 +00:00
int playergender() {
return (getcs().charid&1) ? GEN_F : GEN_M;
}
2016-01-02 10:09:13 +00:00
int princessgender() {
2016-08-26 09:58:03 +00:00
int g = playergender();
if(vid.samegender) return g;
return g == GEN_M ? GEN_F : GEN_M;
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
int lang() {
if(vid.language >= 0)
return vid.language;
return default_language;
}
2017-03-23 10:53:57 +00:00
eItem orbToTarget;
eMonster monsterToSummon;
2015-08-08 13:57:52 +00:00
int sightrange = 7;
2017-03-23 10:53:57 +00:00
bool overgenerate = false; // generate a bigger area with high sightrange
cell *mouseover, *mouseover2, *lmouseover, *centerover;
2016-01-02 10:09:13 +00:00
ld modist, modist2, centdist;
2015-08-08 13:57:52 +00:00
string mouseovers;
2016-01-02 10:09:13 +00:00
movedir mousedest, joydir;
int mousex, mousey, joyx, joyy, panjoyx, panjoyy;
2015-08-08 13:57:52 +00:00
bool autojoy = true;
hyperpoint mouseh;
2017-03-23 10:53:57 +00:00
bool leftclick, rightclick, targetclick, hiliteclick, anyshiftclick, wheelclick,
forcetarget, lshiftclick, lctrlclick;
2015-08-08 13:57:52 +00:00
bool gtouched;
bool revcontrol;
2017-03-23 10:53:57 +00:00
int getcstat; ld getcshift; bool inslider;
2015-08-08 13:57:52 +00:00
int ZZ;
string help;
#ifndef OLDCOMPILE
function<void()> help_delegate;
#endif
2015-08-08 13:57:52 +00:00
int andmode = 0;
int darken = 0;
2017-03-23 10:53:57 +00:00
struct fallanim {
int t_mon, t_floor;
eWall walltype;
eMonster m;
fallanim() { t_floor = 0; t_mon = 0; walltype = waNone; }
};
map<cell*, fallanim> fallanims;
2016-01-02 10:09:13 +00:00
bool doHighlight() {
2017-03-23 10:53:57 +00:00
return (hiliteclick && darken < 2) ? !mmhigh : mmhigh;
2016-01-02 10:09:13 +00:00
}
#ifndef NOSDL
2015-08-08 13:57:52 +00:00
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];
}
#endif
2015-08-08 13:57:52 +00:00
#ifndef EXTERNALFONT
#ifndef NOTTF
2015-08-08 13:57:52 +00:00
void loadfont(int siz) {
if(!font[siz]) {
2017-03-23 10:53:57 +00:00
#ifdef WEB
font[siz] = TTF_OpenFont("sans-serif", siz);
#else
2015-08-08 13:57:52 +00:00
font[siz] = TTF_OpenFont("DejaVuSans-Bold.ttf", siz);
2017-03-23 10:53:57 +00:00
#endif
2016-08-26 09:58:03 +00:00
// Destination set by ./configure (in the GitHub repository)
2016-01-02 10:09:13 +00:00
#ifdef FONTDESTDIR
2015-08-08 13:57:52 +00:00
if (font[siz] == NULL) {
font[siz] = TTF_OpenFont(FONTDESTDIR, siz);
2016-01-02 10:09:13 +00:00
}
#endif
if (font[siz] == NULL) {
printf("error: Font file not found\n");
exit(1);
2015-08-08 13:57:52 +00:00
}
}
}
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int gl_width(int size, const char *s);
2015-08-08 13:57:52 +00:00
int textwidth(int siz, const string &str) {
if(size(str) == 0) return 0;
2017-03-23 10:53:57 +00:00
#ifdef NOTTF
2017-03-23 10:53:57 +00:00
return gl_width(siz, str.c_str());
#else
2015-08-08 13:57:52 +00:00
loadfont(siz);
int w, h;
TTF_SizeUTF8(font[siz], str.c_str(), &w, &h);
// printf("width = %d [%d]\n", w, size(str));
return w;
2017-03-23 10:53:57 +00:00
#endif
2015-08-08 13:57:52 +00:00
}
#endif
2017-03-23 10:53:57 +00:00
int textwidth(int siz, const string &str);
2015-08-08 13:57:52 +00:00
#ifdef IOS
int textwidth(int siz, const string &str) {
return mainfont->getSize(str, siz / 36.0).width;
}
#endif
2016-08-26 09:58:03 +00:00
int gradient(int c0, int c1, ld v0, ld v, ld v1);
#ifdef LOCAL
double fadeout = 1;
#endif
2015-08-08 13:57:52 +00:00
int darkened(int c) {
2016-08-26 09:58:03 +00:00
#ifdef LOCAL
c = gradient(0, c, 0, fadeout, 1);
#endif
// c = ((c & 0xFFFF) << 8) | ((c & 0xFF0000) >> 16);
// c = ((c & 0xFFFF) << 8) | ((c & 0xFF0000) >> 16);
2015-08-08 13:57:52 +00:00
for(int i=0; i<darken; i++) c &= 0xFEFEFE, c >>= 1;
return c;
}
int darkenedby(int c, int lev) {
for(int i=0; i<lev; i++) c &= 0xFEFEFE, c >>= 1;
return c;
}
int darkena(int c, int lev, int a) {
for(int i=0; i<lev; i++) c &= 0xFEFEFE, c >>= 1;
return (c << 8) + a;
}
#ifdef GL
2017-03-23 10:53:57 +00:00
bool cameraangle_on;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
void setcameraangle(bool b) {
if(cameraangle_on != b) {
glMatrixMode(GL_PROJECTION);
cameraangle_on = b;
ld cam = vid.camera_angle * M_PI / 180;
GLfloat cc = cos(cam);
GLfloat ss = sin(cam * (b?1:-1));
GLfloat yzspin[16] = {
1, 0, 0, 0,
0, cc, ss, 0,
0, -ss, cc, 0,
0, 0, 0, 1
};
glMultMatrixf(yzspin);
}
2015-08-08 13:57:52 +00:00
}
void selectEyeGL(int ed) {
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"selectEyeGL\n"));
2015-08-08 13:57:52 +00:00
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glTranslatef((vid.xcenter*2.)/vid.xres - 1, 1 - (vid.ycenter*2.)/vid.yres, 0);
2017-03-23 10:53:57 +00:00
if(pmodel) {
vid.scrdist = 4 * vid.radius;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
// simulate glOrtho
GLfloat ortho[16] = {
GLfloat(2. / vid.xres), 0, 0, 0,
0, GLfloat(-2. / vid.yres), 0, 0,
0, 0, GLfloat(.4 / vid.scrdist), 0,
0, 0, 0, 1};
vid.scrdist = -vid.scrdist;
glMultMatrixf(ortho);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
else {
float ve = ed*vid.eye;
ve *= 2; // vid.xres; ve /= vid.radius;
if(ve)
glTranslatef(-(ve * vid.radius) * (vid.alpha - (vid.radius*1./vid.xres) * vid.eye) / vid.xres, 0, 0);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
float lowdepth = .1;
float hidepth = 1e9;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
// simulate glFrustum
GLfloat frustum[16] = {
GLfloat(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};
glMultMatrixf(frustum);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLfloat sc = vid.radius / (vid.yres/2.);
GLfloat mat[16] = {sc,0,0,0, 0,-sc,0,0, 0,0,-1,0, 0,0, 0,1};
glMultMatrixf(mat);
if(ve) glTranslatef(ve, 0, vid.eye);
vid.scrdist = vid.yres * sc / 2;
}
cameraangle_on = false;
2015-08-08 13:57:52 +00:00
}
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() {
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"setGLProjection\n"));
2015-08-08 13:57:52 +00:00
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
2017-03-23 10:53:57 +00:00
unsigned char *c = (unsigned char*) (&backcolor);
glClearColor(c[2] / 255.0, c[1] / 255.0, c[0]/255.0, 1);
2015-08-08 13:57:52 +00:00
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_BLEND);
2017-03-23 10:53:57 +00:00
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if(pmodel == mdBall || pmodel == mdHyperboloid) {
#ifdef GL_ES
glClearDepthf(1.0f);
#else
glClearDepth(1.0f);
#endif
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
}
else
glDisable(GL_DEPTH_TEST);
2015-08-08 13:57:52 +00:00
selectEyeGL(0);
}
void buildpolys();
#ifdef MOBILE
#define EXTERNALFONT
#endif
#ifndef EXTERNALFONT
2015-08-08 13:57:52 +00:00
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];
inline int next_p2 (int a )
{
int rval=1;
// rval<<=1 Is A Prettier Way Of Writing rval*=2;
while(rval<a) rval<<=1;
return rval;
}
void glError(const char* GLcall, const char* file, const int line) {
GLenum errCode = glGetError();
if(errCode!=GL_NO_ERROR) {
2017-03-23 10:53:57 +00:00
// fprintf(stderr, "OPENGL ERROR #%i: in file %s on line %i :: %s\n",errCode,file, line, GLcall);
2015-08-08 13:57:52 +00:00
}
}
#define GLERR(call) glError(call, __FILE__, __LINE__)
#ifdef NOTTF
#define FIXEDSIZE
#endif
#ifdef CREATEFONT
#define FIXEDSIZE
#endif
#ifdef FIXEDSIZE
#include "nofont.cpp"
#endif
2015-08-08 13:57:52 +00:00
void init_glfont(int size) {
if(glfont[size]) return;
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"init GL font: %d\n", size));
2015-08-08 13:57:52 +00:00
glfont[size] = new glfont_t;
glfont_t& f(*(glfont[size]));
f.textures = new GLuint[128+NUMEXTRA];
//f.list_base = glGenLists(128);
glGenTextures( 128+NUMEXTRA, f.textures );
#ifndef NOTTF
2015-08-08 13:57:52 +00:00
loadfont(size);
if(!font[size]) return;
char str[2]; str[1] = 0;
SDL_Color white;
white.r = white.g = white.b = 255;
2017-03-23 10:53:57 +00:00
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
// glListBase(0);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
for(int ch=1;ch<128+NUMEXTRA;ch++) {
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(ch<32) continue;
#ifdef NOTTF
int otwidth, otheight, tpix[3000], tpixindex = 0;
2017-03-23 10:53:57 +00:00
loadCompressedChar(otwidth, otheight, tpix);
#else
2015-08-08 13:57:52 +00:00
SDL_Surface *txt;
if(ch < 128) {
str[0] = ch;
txt = TTF_RenderText_Blended(font[size], str, white);
}
else {
txt = TTF_RenderUTF8_Blended(font[size], natchars[ch-128], white);
}
if(txt == NULL) continue;
#ifdef CREATEFONT
generateFont(ch, txt);
#endif
2017-03-23 10:53:57 +00:00
int otwidth = txt->w;
int otheight = txt->h;
#endif
int twidth = next_p2( otwidth );
int theight = next_p2( otheight );
#ifdef NOTTF
2017-03-23 10:53:57 +00:00
int expanded_data[twidth * theight];
#else
2015-08-08 13:57:52 +00:00
Uint16 expanded_data[twidth * theight];
2017-03-23 10:53:57 +00:00
#endif
2015-08-08 13:57:52 +00:00
for(int j=0; j <theight;j++) for(int i=0; i < twidth; i++) {
#ifdef NOTTF
2017-03-23 10:53:57 +00:00
expanded_data[(i+j*twidth)] = (i>=otwidth || j>=otheight) ? 0 : tpix[tpixindex++];
#else
2015-08-08 13:57:52 +00:00
expanded_data[(i+j*twidth)] =
(i>=txt->w || j>=txt->h) ? 0 : ((qpixel(txt, i, j)>>24)&0xFF) * 0x101;
2017-03-23 10:53:57 +00:00
#endif
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
/* if(ch == '@') {
for(int j=0; j <theight;j++) {
for(int i=0; i < twidth; i++) printf("%4x ", expanded_data[(i+j*twidth)]);
printf("\n");
}
} */
2015-08-08 13:57:52 +00:00
// printf("b\n");
2017-03-23 10:53:57 +00:00
f.widths[ch] = otwidth;
f.heights[ch] = otheight;
2015-08-08 13:57:52 +00:00
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,
#ifdef NOTTF
2017-03-23 10:53:57 +00:00
GL_RGBA, GL_UNSIGNED_BYTE,
#else
GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
#endif
expanded_data );
2015-08-08 13:57:52 +00:00
// printf("c\n");
2017-03-23 10:53:57 +00:00
float x=(float)otwidth / (float)twidth;
float y=(float)otheight / (float)theight;
2015-08-08 13:57:52 +00:00
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();
#ifndef NOTTF
2015-08-08 13:57:52 +00:00
SDL_FreeSurface(txt);
2017-03-23 10:53:57 +00:00
#endif
2015-08-08 13:57:52 +00:00
}
//printf("init size=%d ok\n", size);
GLERR("initfont");
}
int getnext(const char* s, int& i) {
for(int k=0; k<NUMEXTRA; k++)
if(s[i] == natchars[k][0] && s[i+1] == natchars[k][1]) {
i += 2; return 128+k;
}
if(s[i] < 0 && s[i+1] < 0) {
printf("Unknown character: '%c%c'\n", s[i], s[i+1]);
i += 2; return '?';
}
return s[i++];
}
2017-03-23 10:53:57 +00:00
GLfloat tver[24];
int gl_width(int size, const char *s) {
int gsiz = size;
if(size > vid.fsize || size > 72) gsiz = 72;
#ifdef FIXEDSIZE
2017-03-23 10:53:57 +00:00
gsiz = 36;
#endif
init_glfont(gsiz);
#ifndef NOTTF
2017-03-23 10:53:57 +00:00
if(!font[gsiz]) return 0;
#endif
glfont_t& f(*glfont[gsiz]);
int x = 0;
for(int i=0; s[i];) {
int tabid = getnext(s,i);
x += f.widths[tabid] * size/gsiz;
}
return x;
}
2015-08-08 13:57:52 +00:00
bool gl_print(int x, int y, int shift, int size, const char *s, int color, int align) {
2017-03-23 10:53:57 +00:00
int gsiz = size;
if(size > vid.fsize || size > 72) gsiz = 72;
#ifdef FIXEDSIZE
2017-03-23 10:53:57 +00:00
gsiz = 36;
#endif
init_glfont(gsiz);
#ifndef NOTTF
2017-03-23 10:53:57 +00:00
if(!font[gsiz]) return false;
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
glfont_t& f(*glfont[gsiz]);
2015-08-08 13:57:52 +00:00
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
2017-03-23 10:53:57 +00:00
glcolor2((color << 8) | 0xFF);
2015-08-08 13:57:52 +00:00
int tsize = 0;
2017-03-23 10:53:57 +00:00
for(int i=0; s[i];) tsize += f.widths[getnext(s,i)] * size/gsiz;
2015-08-08 13:57:52 +00:00
x -= tsize * align / 16;
2017-03-23 10:53:57 +00:00
y += f.heights[32] * size / (gsiz*2);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int ysiz = f.heights[32] * size / gsiz;
bool clicked = (mousex >= x && mousey <= y && mousex <= x+tsize && mousey >= y-ysiz);
/* extern bool markcorner;
if(clicked && markcorner) {
markcorner = false;
int w = tsize, h = -ysiz;
displaystr(x, y, 1, 10, "X", 0xFFFFFF, 8);
displaystr(x+w, y, 1, 10, "X", 0xFFFFFF, 8);
displaystr(x, y+h, 1, 10, "X", 0xFFFFFF, 8);
displaystr(x+w, y+h, 1, 10, "X", 0xFFFFFF, 8);
markcorner = true;
} */
2015-08-08 13:57:52 +00:00
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];
2017-03-23 10:53:57 +00:00
int wi = f.widths[tabid] * size/gsiz;
int hi = f.heights[tabid] * size/gsiz;
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
GLERR("pre-print");
2015-08-08 13:57:52 +00:00
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]);
2017-03-23 10:53:57 +00:00
#if 1
tver[1] = tver[10] = -hi;
tver[6] = tver[9] = wi;
tver[12+4] = tver[12+7] = fy;
tver[12+6] = tver[12+9] = fx;
activateVertexArray(tver, 8);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(3, GL_FLOAT, 0, &tver[12]);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
#else
2015-08-08 13:57:52 +00:00
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();
2017-03-23 10:53:57 +00:00
#endif
2015-08-08 13:57:52 +00:00
glPopMatrix();
}
if(vid.goteyes) selectEyeMask(0);
GLERR("print");
// int tabid = s[i];
2017-03-23 10:53:57 +00:00
x += wi;
2015-08-08 13:57:52 +00:00
/*
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;
}
#endif
void resetGL() {
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"reset GL\n"));
#ifndef EXTERNALFONT
2015-08-08 13:57:52 +00:00
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) {
2015-08-08 13:57:52 +00:00
if(strlen(str) == 0) return false;
2017-03-23 10:53:57 +00:00
if(size < 4 || size > 255) {
2015-08-08 13:57:52 +00:00
return false;
}
#ifdef GL
if(vid.usingGL) return gl_print(x, y, shift, size, str, color, align);
#endif
#ifdef NOTTF
static bool towarn = true;
if(towarn) towarn = false, printf("WARNING: NOTTF works only with OpenGL!\n");
return false;
#else
2015-08-08 13:57:52 +00:00
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);
2016-01-02 10:09:13 +00:00
SDL_Surface *txt = (vid.usingAA?TTF_RenderUTF8_Blended:TTF_RenderUTF8_Solid)(font[size], str, col);
2015-08-08 13:57:52 +00:00
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<rect.h; yy++)
for(int xx=0; xx<rect.w; xx++) if(qpixel(txt2, xx, yy) != c0)
qpixel(s, rect.x+xx-shift, rect.y+yy) |= color & 0xFF0000,
qpixel(s, rect.x+xx+shift, rect.y+yy) |= color & 0x00FFFF;
SDL_UnlockSurface(s);
SDL_UnlockSurface(txt2);
SDL_FreeSurface(txt2);
}
else {
SDL_BlitSurface(txt, NULL, s,&rect);
}
SDL_FreeSurface(txt);
return clicked;
#endif
2015-08-08 13:57:52 +00:00
}
bool displaystr(int x, int y, int shift, int size, const string &s, int color, int align) {
return displaystr(x, y, shift, size, s.c_str(), color, align);
}
bool displayfr(int x, int y, int b, int size, const string &s, int color, int align) {
displaystr(x-b, y, 0, size, s, 0, align);
displaystr(x+b, y, 0, size, s, 0, align);
displaystr(x, y-b, 0, size, s, 0, align);
displaystr(x, y+b, 0, size, s, 0, align);
return displaystr(x, y, 0, size, s, color, align);
}
bool displaychr(int x, int y, int shift, int size, char chr, int col) {
char buf[2];
buf[0] = chr; buf[1] = 0;
return displaystr(x, y, shift, size, buf, col, 8);
}
#else
vector<int> 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) {
2016-08-26 09:58:03 +00:00
int g = (int) graphdata.size(), q = 0;
2015-08-08 13:57:52 +00:00
gdpush((int) s.size()); for(int i=0; i<s.size(); i++) {
2016-08-26 09:58:03 +00:00
#ifdef ANDROID
2015-08-08 13:57:52 +00:00
unsigned char uch = (unsigned char) s[i];
if(uch >= 192 && uch < 224) {
int u = ((s[i] - 192)&31) << 6;
i++;
u += (s[i] - 128) & 63;
gdpush(u); q++;
}
2016-08-26 09:58:03 +00:00
else
#endif
{
2015-08-08 13:57:52 +00:00
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);
2016-08-26 09:58:03 +00:00
int mx = mousex - x;
int my = mousey - y;
2017-03-23 10:53:57 +00:00
int len = textwidth(size, s);
2015-08-08 13:57:52 +00:00
return
2017-03-23 10:53:57 +00:00
mx >= -len*align/32 && mx <= +len*(16-align)/32 &&
2015-08-08 13:57:52 +00:00
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) {
2016-08-26 09:58:03 +00:00
return displayfr(x,y,0,size,s,color,align);
}
bool displaystr(int x, int y, int shift, int size, char const *s, int color, int align) {
return displayfr(x,y,0,size,s,color,align);
2015-08-08 13:57:52 +00:00
}
#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;
2017-03-23 10:53:57 +00:00
int quantity;
2015-08-08 13:57:52 +00:00
string msg;
};
vector<msginfo> msgs;
2017-03-23 10:53:57 +00:00
vector<msginfo> gamelog;
2015-08-08 13:57:52 +00:00
void flashMessages() {
for(int i=0; i<size(msgs); i++)
if(msgs[i].stamp < ticks - 1000 && !msgs[i].flashout) {
msgs[i].flashout = true;
msgs[i].stamp = ticks;
}
}
2017-03-23 10:53:57 +00:00
void addMessageToLog(msginfo& m, vector<msginfo>& log) {
if(size(log) != 0) {
msginfo& last = log[size(log)-1];
if(last.msg == m.msg) {
int q = m.quantity + last.quantity;
last = m; last.quantity = q;
return;
}
}
if(size(log) < 200)
log.push_back(m);
else {
for(int i=0; i<size(log)-1; i++) swap(log[i], log[i+1]);
log[size(log)-1] = m;
}
}
void clearMessages() { msgs.clear(); }
2015-08-08 13:57:52 +00:00
void addMessage(string s, char spamtype) {
2016-08-26 09:58:03 +00:00
DEBB(DF_MSG, (debugfile,"addMessage: %s\n", s.c_str()));
2017-03-23 10:53:57 +00:00
2015-08-08 13:57:52 +00:00
msginfo m;
m.msg = s; m.spamtype = spamtype; m.flashout = false; m.stamp = ticks;
2017-03-23 10:53:57 +00:00
m.quantity = 1;
addMessageToLog(m, gamelog);
addMessageToLog(m, msgs);
2015-08-08 13:57:52 +00:00
}
void drawmessages() {
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"draw messages\n"));
2015-08-08 13:57:52 +00:00
int i = 0;
int t = ticks;
for(int j=0; j<size(msgs); j++) {
int age = msgs[j].flashout * (t - msgs[j].stamp);
if(msgs[j].spamtype) {
for(int i=j+1; i<size(msgs); i++) if(msgs[i].spamtype == msgs[j].spamtype)
msgs[j].flashout = 2;
}
if(age < 256*vid.flashtime) {
int x = vid.xres / 2;
int y = vid.yres - vid.fsize * (size(msgs) - j) - (ISIOS ? 4 : 0);
2017-03-23 10:53:57 +00:00
string s = msgs[j].msg;
if(msgs[j].quantity > 1) s += " (x" + its(msgs[j].quantity) + ")";
displayfr(x, y, 1, vid.fsize, s, 0x10101 * (255 - age/vid.flashtime), 8);
2015-08-08 13:57:52 +00:00
msgs[i++] = msgs[j];
}
}
msgs.resize(i);
}
2017-03-23 10:53:57 +00:00
eModel pmodel = mdDisk;
2016-08-26 09:58:03 +00:00
ld ghx, ghy, ghgx, ghgy;
hyperpoint ghpm = C0;
2017-03-23 10:53:57 +00:00
void ghcheck(hyperpoint &ret, const hyperpoint &H) {
if(hypot(ret[0]-ghx, ret[1]-ghy) < hypot(ghgx-ghx, ghgy-ghy)) {
ghpm = H; ghgx = ret[0]; ghgy = ret[1];
2016-08-26 09:58:03 +00:00
}
}
void camrotate(ld& hx, ld& hy) {
ld cam = vid.camera_angle * M_PI / 180;
GLfloat cc = cos(cam);
GLfloat ss = sin(cam);
ld ux = hx, uy = hy * cc + ss, uz = cc - ss * hy;
hx = ux / uz, hy = uy / uz;
}
2015-08-08 13:57:52 +00:00
hyperpoint gethyper(ld x, ld y) {
2016-08-26 09:58:03 +00:00
ld hx = (x - vid.xcenter) / vid.radius;
ld hy = (y - vid.ycenter) / vid.radius;
2016-08-26 09:58:03 +00:00
if(pmodel) {
ghx = hx, ghy = hy;
2016-08-26 09:58:03 +00:00
return ghpm;
}
2015-08-08 13:57:52 +00:00
if(euclid)
return hpxy(hx * (EUCSCALE + vid.alphax), hy * (EUCSCALE + vid.alphax));
if(vid.camera_angle) camrotate(hx, hy);
2017-03-23 10:53:57 +00:00
2015-08-08 13:57:52 +00:00
ld hr = hx*hx+hy*hy;
2017-03-23 10:53:57 +00:00
if(hr > .9999 && !sphere) return Hypc;
2015-08-08 13:57:52 +00:00
// hz*hz-(hx/(hz+alpha))^2 - (hy/(hz+alpha))^2 =
// hz*hz-hr*(hz+alpha)^2 == 1
// hz*hz - hr*hr*hz*Hz
2017-03-23 10:53:57 +00:00
ld A, B, C;
ld curv = sphere ? 1 : -1;
A = 1+curv*hr;
B = 2*hr*vid.alphax*-curv;
C = 1 - curv*hr*vid.alphax*vid.alphax;
2015-08-08 13:57:52 +00:00
// 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)
2017-03-23 10:53:57 +00:00
ld rootsign = 1;
if(sphere && vid.alphax > 1) rootsign = -1;
ld hz = B / 2 + rootsign * sqrt(C + B*B/4);
2015-08-08 13:57:52 +00:00
hyperpoint H;
H[0] = hx * (hz+vid.alphax);
H[1] = hy * (hz+vid.alphax);
H[2] = hz;
return H;
}
2017-03-23 10:53:57 +00:00
void ballmodel(hyperpoint& ret, double alpha, double d, double zl) {
hyperpoint H = ypush(geom3::camera) * xpush(d) * ypush(zl) * C0;
ld tzh = vid.ballproj + H[2];
ld ax = H[0] / tzh;
ld ay = H[1] / tzh;
ld ball = vid.ballangle * M_PI / 180;
ld ca = cos(alpha), sa = sin(alpha);
ld cb = cos(ball), sb = sin(ball);
ret[0] = ax * ca;
ret[1] = ay * cb + ax * sa * sb;
ret[2] = - ax * sa * cb - ay * sb;
}
void applymodel(hyperpoint H, hyperpoint& ret) {
2015-08-08 13:57:52 +00:00
ld tz = euclid ? (EUCSCALE+vid.alphax) : vid.alphax+H[2];
if(tz < 1e-3 && tz > -1e-3) tz = 1000;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(pmodel == mdUnchanged) {
for(int i=0; i<3; i++) ret[i] = H[i] / vid.radius;
return;
}
if(pmodel == mdBall) {
ld zlev = zlevel(H);
using namespace hyperpoint_vec;
H = H / zlev;
ld zl = geom3::depth-geom3::factor_to_lev(zlev);
double alpha = atan2(H[1], H[0]);
double d = hdist0(H);
ballmodel(ret, alpha, d, zl);
ghcheck(ret,H);
2017-03-23 10:53:57 +00:00
return;
}
if(pmodel == mdHyperboloid) {
ld ball = vid.ballangle * M_PI / 180;
ld cb = cos(ball), sb = sin(ball);
ret[0] = H[0] / 3;
ret[1] = (1 - H[2]) / 3 * cb + H[1] / 3 * sb;
ret[2] = H[1] / 3 * cb - (1 - H[2]) / 3 * sb;
ghcheck(ret,H);
2017-03-23 10:53:57 +00:00
return;
}
if(pmodel == mdDisk) {
if(!vid.camera_angle) {
ret[0] = H[0] / tz;
ret[1] = H[1] / tz;
ret[2] = (1 - vid.beta / tz);
}
else {
ld tx = H[0];
ld ty = H[1];
ld cam = vid.camera_angle * M_PI / 180;
GLfloat cc = cos(cam);
GLfloat ss = sin(cam);
ld ux = tx, uy = ty * cc - ss * tz, uz = tz * cc + ss * ty;
ret[0] = ux / uz;
ret[1] = uy / uz;
ret[2] = 1 - vid.beta / uz;
}
2017-03-23 10:53:57 +00:00
return;
}
ld zlev = 1;
if(wmspatial || mmspatial) {
zlev = zlevel(H);
using namespace hyperpoint_vec;
H = H / zlev;
}
if(pmodel == mdEquidistant || pmodel == mdEquiarea) {
ld rad = sqrt(H[0] * H[0] + H[1] * H[1]);
ld d = hdist0(H);
if(pmodel == 6 && sphere)
d = sqrt(2*(1 - cos(d))) * 1.25; // /1.5 to make it fit on the screen better
else if(pmodel == 6)
d = sqrt(2*(cosh(d) - 1)) / 1.5;
ret[0] = d * H[0] / rad / 4;
ret[1] = d * H[1] / rad / 4;
ret[2] = 0;
if(zlev != 1 && vid.goteyes)
ret[2] = geom3::factor_to_lev(zlev);
ghcheck(ret,H);
2016-08-26 09:58:03 +00:00
return;
}
2017-03-23 10:53:57 +00:00
tz = H[2]+vid.alphax;
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(pmodel == mdPolygonal || pmodel == mdPolynomial) {
2016-08-26 09:58:03 +00:00
pair<long double, long double> p = polygonal::compute(H[0]/tz, H[1]/tz);
2017-03-23 10:53:57 +00:00
ret[0] = p.first;
ret[1] = p.second;
ret[2] = 0;
ghcheck(ret,H);
2016-08-26 09:58:03 +00:00
return;
}
// Poincare to half-plane
ld x0, y0;
x0 = H[0] / tz;
y0 = H[1] / tz;
y0 += 1;
double rad = x0*x0 + y0*y0;
y0 /= rad;
x0 /= rad;
y0 -= .5;
2017-03-23 10:53:57 +00:00
if(pmodel == mdHalfplane) {
ret[0] = x0;
if(wmspatial || mmspatial) y0 *= zlev;
ret[1] = 1 - y0;
ret[2] = 0;
if(zlev != 1 && vid.goteyes)
ret[2] = y0 * geom3::factor_to_lev(zlev);
ghcheck(ret,H);
2016-08-26 09:58:03 +00:00
return;
}
// center
x0 *= 2; y0 *= 2;
// half-plane to band
double tau = (log((x0+1)*(x0+1) + y0*y0) - log((x0-1)*(x0-1) + y0*y0)) / 2;
double u=(1-x0*x0-y0*y0);
u = (1 - x0*x0 - y0*y0 + sqrt(u*u+4*y0*y0));
2017-03-23 10:53:57 +00:00
double yv = 2*y0 / u;
double sigma = 2 * atan(yv * zlev) - M_PI/2;
2016-08-26 09:58:03 +00:00
x0 = tau; y0 = sigma;
2017-03-23 10:53:57 +00:00
/* if(zlev != 1) {
double alp = (y0 * y0) / (1-y0*y0);
double gx = alp + sqrt(alp*alp-1);
double gy = y0 * (gx+1);
double yr = zlev * gy / (zlev * gx + 1);
printf("zlev = %10.5lf y0 = %20.10lf yr = %20.10lf\n", double(zlev), (double)y0, yr);
y0 = yr;
} */
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
ret[0] = x0/M_PI*2;
ret[1] = -y0/M_PI*2;
2017-03-23 10:53:57 +00:00
ret[2] = 0;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(zlev != 1 && vid.goteyes)
ret[2] = geom3::factor_to_lev(zlev) / (1 + yv * yv);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
ghcheck(ret,H);
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
int dlit;
2015-08-08 13:57:52 +00:00
// game-related graphics
transmatrix View; // current rotation, relative to viewctr
transmatrix cwtV; // player-relative view
2017-03-23 10:53:57 +00:00
transmatrix sphereflip; // on the sphere, flip
2015-08-08 13:57:52 +00:00
heptspin viewctr; // heptagon and rotation where the view is centered at
bool playerfound; // has player been found in the last drawing?
ld spina(cell *c, int dir) {
return 2 * M_PI * dir / c->type;
}
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];
}
2017-03-23 10:53:57 +00:00
int firegradient(double p) {
return gradient(0xFFFF00, 0xFF0000, 0, p, 1);
}
2015-08-08 13:57:52 +00:00
int firecolor(int phase) {
return gradient(0xFFFF00, 0xFF0000, -1, sin((phase + ticks)/100.0), 1);
}
2016-01-02 10:09:13 +00:00
int watercolor(int phase) {
return 0x0080C0FF + 256 * int(63 * sin((ticks + phase) / 50.));
}
int aircolor(int phase) {
2017-03-23 10:53:57 +00:00
return 0x8080FF00 | int(32 + 32 * sin(ticks/200.0 + 2 * phase * M_PI / (S21+.0)));
}
int fghostcolor(int phase, cell *c) {
phase += (int)(size_t)c;
phase %= 4000;
if(phase<0) phase+=4000;
if(phase < 1000) return gradient(0xFFFF80, 0xA0C0FF, 0, phase, 1000);
else if(phase < 2000) return gradient(0xA0C0FF, 0xFF80FF, 1000, phase, 2000);
else if(phase < 3000) return gradient(0xFF80FF, 0xFF8080, 2000, phase, 3000);
else if(phase < 4000) return gradient(0xFF8080, 0xFFFF80, 3000, phase, 4000);
return 0xFFD500;
2016-01-02 10:09:13 +00:00
}
int weakfirecolor(int phase) {
return gradient(0xFF8000, 0xFF0000, -1, sin((phase + ticks)/500.0), 1);
}
int fc(int ph, int col, int z) {
2015-08-08 13:57:52 +00:00
if(items[itOrbFire]) col = darkena(firecolor(ph), 0, 0xFF);
2017-03-23 10:53:57 +00:00
if(items[itOrbAether]) col = (col &~0XFF) | (col&0xFF) / 2;
for(int i=0; i<numplayers(); i++) if(multi::playerActive(i))
if(items[itOrbFish] && isWatery(playerpos(i)) && z != 3) return watercolor(ph);
2016-01-02 10:09:13 +00:00
if(invismove)
col =
shmup::on ?
(col &~0XFF) | (int((col&0xFF) * .25))
: (col &~0XFF) | (int((col&0xFF) * (100+100*sin(ticks / 500.)))/200);
2015-08-08 13:57:52 +00:00
return col;
}
2017-03-23 10:53:57 +00:00
int lightat, safetyat;
2015-08-08 13:57:52 +00:00
void drawLightning() { lightat = ticks; }
void drawSafety() { safetyat = ticks; }
double eurad = 0.52;
2017-03-23 10:53:57 +00:00
double q3 = sqrt(double(3));
2015-08-08 13:57:52 +00:00
bool outofmap(hyperpoint h) {
if(euclid)
return h[0] * h[0] + h[1] * h[1] > 15 * eurad;
2017-03-23 10:53:57 +00:00
else if(sphere)
return h[2] < .1 && h[2] > -.1 && h[1] > -.1 && h[1] < .1 && h[0] > -.1 && h[0] < .1;
2015-08-08 13:57:52 +00:00
else
return h[2] < .5;
}
2016-08-26 09:58:03 +00:00
void drawShield(const transmatrix& V, eItem it) {
2015-08-08 13:57:52 +00:00
float ds = ticks / 300.;
2017-03-23 10:53:57 +00:00
int col = iinf[it].color;
if(it == itOrbShield && items[itOrbTime] && !orbused[it])
2016-01-02 10:09:13 +00:00
col = (col & 0xFEFEFE) / 2;
2017-03-23 10:53:57 +00:00
if(sphere && cwt.c->land == laHalloween && !wmblack && !wmascii)
col = 0;
2016-08-26 09:58:03 +00:00
double d = it == itOrbShield ? hexf : hexf - .1;
2017-03-23 10:53:57 +00:00
int mt = sphere ? 7 : 5;
for(int a=0; a<=S84*mt; a++)
curvepoint(V*ddi0(a, d + sin(ds + M_PI*2*a/4/mt)*.1));
queuecurve(darkena(col, 0, 0xFF), 0x8080808, PPR_LINE);
2015-08-08 13:57:52 +00:00
}
void drawSpeed(const transmatrix& V) {
2016-01-02 10:09:13 +00:00
ld ds = ticks / 10.;
2017-03-23 10:53:57 +00:00
int col = darkena(iinf[itOrbSpeed].color, 0, 0xFF);
for(int b=0; b<S84; b+=S14) {
for(int a=0; a<=S84; a++)
curvepoint(V*ddi0(ds+b+a, hexf*a/S84));
queuecurve(col, 0x8080808, PPR_LINE);
}
2015-08-08 13:57:52 +00:00
}
void drawSafety(const transmatrix& V, int ct) {
ld ds = ticks / 50.;
2017-03-23 10:53:57 +00:00
int col = darkena(iinf[itOrbSafety].color, 0, 0xFF);
2015-08-08 13:57:52 +00:00
for(int a=0; a<ct; a++)
2017-03-23 10:53:57 +00:00
queueline(V*ddi0(ds+a*S84/ct, 2*hexf), V*ddi0(ds+(a+(ct-1)/2)*S84/ct, 2*hexf), col);
2015-08-08 13:57:52 +00:00
}
void drawFlash(const transmatrix& V) {
float ds = ticks / 300.;
2017-03-23 10:53:57 +00:00
int col = darkena(iinf[itOrbFlash].color, 0, 0xFF);
2015-08-08 13:57:52 +00:00
col &= ~1;
for(int u=0; u<5; u++) {
ld rad = hexf * (2.5 + .5 * sin(ds+u*.3));
2017-03-23 10:53:57 +00:00
for(int a=0; a<=S84; a++) curvepoint(V*ddi0(a, rad));
queuecurve(col, 0x8080808, PPR_LINE);
2015-08-08 13:57:52 +00:00
}
}
2016-01-02 10:09:13 +00:00
void drawLove(const transmatrix& V, int hdir) {
float ds = ticks / 300.;
2017-03-23 10:53:57 +00:00
int col = darkena(iinf[itOrbLove].color, 0, 0xFF);
2016-01-02 10:09:13 +00:00
col &= ~1;
for(int u=0; u<5; u++) {
2017-03-23 10:53:57 +00:00
for(int a=0; a<=S84; a++) {
double d = (1 + cos(a * M_PI/S42)) / 2;
int z = a; if(z>S42) z = S84-z;
2016-01-02 10:09:13 +00:00
if(z <= 10) d += (10-z) * (10-z) * (10-z) / 3000.;
ld rad = hexf * (2.5 + .5 * sin(ds+u*.3)) * d;
2017-03-23 10:53:57 +00:00
curvepoint(V*ddi0(S42+hdir+a-1, rad));
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
queuecurve(col, 0x8080808, PPR_LINE);
2016-01-02 10:09:13 +00:00
}
}
2015-08-08 13:57:52 +00:00
void drawWinter(const transmatrix& V, int hdir) {
float ds = ticks / 300.;
2017-03-23 10:53:57 +00:00
int col = darkena(iinf[itOrbWinter].color, 0, 0xFF);
2015-08-08 13:57:52 +00:00
for(int u=0; u<20; u++) {
ld rad = 6 * sin(ds+u * 2 * M_PI / 20);
2017-03-23 10:53:57 +00:00
queueline(V*ddi0(S42+hdir+rad, hexf*.5), V*ddi0(S42+hdir+rad, hexf*3), col, 2);
2015-08-08 13:57:52 +00:00
}
}
void drawLightning(const transmatrix& V) {
2017-03-23 10:53:57 +00:00
int col = darkena(iinf[itOrbLightning].color, 0, 0xFF);
2015-08-08 13:57:52 +00:00
for(int u=0; u<20; u++) {
ld leng = 0.5 / (0.1 + (rand() % 100) / 100.0);
2017-03-23 10:53:57 +00:00
ld rad = rand() % S84;
queueline(V*ddi0(rad, hexf*0.3), V*ddi0(rad, hexf*leng), col, 2);
2015-08-08 13:57:52 +00:00
}
}
int displaydir(cell *c, int d) {
if(euclid)
2017-03-23 10:53:57 +00:00
return - d * S84 / c->type;
2015-08-08 13:57:52 +00:00
else
2017-03-23 10:53:57 +00:00
return S42 - d * S84 / c->type;
}
transmatrix ddspin(cell *c, int d, int bonus) {
2017-03-23 10:53:57 +00:00
int hdir = displaydir(c, d) + bonus;
return getspinmatrix(hdir);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
#ifdef WEB
Uint8 *SDL_GetKeyState(void *v) { static Uint8 tab[1024]; return tab; }
#endif
2016-01-02 10:09:13 +00:00
#include "shmup.cpp"
2016-08-26 09:58:03 +00:00
#include "conformal.cpp"
2017-03-23 10:53:57 +00:00
#include "rug.cpp"
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
void drawPlayerEffects(const transmatrix& V, cell *c, bool onplayer) {
if(!onplayer && !items[itOrbEmpathy]) return;
2017-03-23 10:53:57 +00:00
if(items[itOrbShield] > (shmup::on ? 0 : ORBBASE)) drawShield(V, itOrbShield);
if(items[itOrbShell] > (shmup::on ? 0 : ORBBASE)) drawShield(V, itOrbShell);
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
if(items[itOrbSpeed]) drawSpeed(V);
int ct = c->type;
2017-03-23 10:53:57 +00:00
if(onplayer && (items[itOrbSword] || items[itOrbSword2])) {
using namespace sword;
double esh = euclid ? M_PI - M_PI*3/S84 + 2.5 * M_PI/S42: 0;
if(shmup::on) {
if(items[itOrbSword])
queuepoly(V*spin(esh+shmup::pc[multi::cpid]->swordangle), shMagicSword, darkena(iinf[itOrbSword].color, 0, 0xC0 + 0x30 * sin(ticks / 200.0)));
if(items[itOrbSword2])
queuepoly(V*spin(esh+shmup::pc[multi::cpid]->swordangle+M_PI), shMagicSword, darkena(iinf[itOrbSword2].color, 0, 0xC0 + 0x30 * sin(ticks / 200.0)));
}
else {
int& ang = angle[multi::cpid];
ang %= S42;
transmatrix Vnow = gmatrix[c] * rgpushxto0(inverse(gmatrix[c]) * tC0(V));
if(!euclid) for(int a=0; a<S42; a++) {
int dda = S42 + (-1-2*a);
if(a == ang && items[itOrbSword]) continue;
if(purehepta && a%3 != ang%3) continue;
if((a+S21)%S42 == ang && items[itOrbSword2]) continue;
bool longer = sword::pos(cwt.c, a-1) != sword::pos(cwt.c, a+1);
int col = darkena(0xC0C0C0, 0, 0xFF);
queueline(Vnow*ddi0(dda, purehepta ? 0.6 : longer ? 0.36 : 0.4), Vnow*ddi0(dda, purehepta ? 0.7 : longer ? 0.44 : 0.42), col, 1);
}
if(items[itOrbSword])
queuepoly(Vnow*spin(esh+M_PI+(-1-2*ang)*2*M_PI/S84), shMagicSword, darkena(iinf[itOrbSword].color, 0, 0x80 + 0x70 * sin(ticks / 200.0)));
if(items[itOrbSword2])
queuepoly(Vnow*spin(esh+(-1-2*ang)*2*M_PI/S84), shMagicSword, darkena(iinf[itOrbSword2].color, 0, 0x80 + 0x70 * sin(ticks / 200.0)));
}
}
2016-08-26 09:58:03 +00:00
if(onplayer && items[itOrbSafety]) drawSafety(V, ct);
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
if(onplayer && items[itOrbFlash]) drawFlash(V);
2017-03-23 10:53:57 +00:00
if(onplayer && items[itOrbLove]) drawLove(V, 0); // displaydir(c, cwt.spin));
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
if(items[itOrbWinter])
2017-03-23 10:53:57 +00:00
drawWinter(V, 0); // displaydir(c, cwt.spin));
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(onplayer && items[itOrbLightning]) drawLightning(V);
2016-01-02 10:09:13 +00:00
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;
2017-03-23 10:53:57 +00:00
int col = darkena(iinf[itOrbSafety].color, 0, 0xFF);
for(int a=0; a<S84; a++)
queueline(V*ddi0(a, rad), V*ddi0(a+1, rad), col, 0);
2016-01-02 10:09:13 +00:00
}
}
}
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
void drawStunStars(const transmatrix& V, int t) {
for(int i=0; i<3*t; i++) {
transmatrix V2 = V * spin(M_PI * 2 * i / (3*t) + M_PI * ticks/600.);
2017-03-23 10:53:57 +00:00
queuepolyat(V2, shFlailBall, 0xFFFFFFFF, PPR_STUNSTARS);
2016-01-02 10:09:13 +00:00
}
}
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
bool drawUserShape(transmatrix V, int group, int id, int color) {
#ifdef MOBILE
return false;
#else
usershape *us = usershapes[group][id];
if(!us) return false;
for(int i=0; i<USERLAYERS; i++) {
usershapelayer& ds(us->d[i]);
hpcshape& sh(ds.sh);
2017-03-23 10:53:57 +00:00
2016-01-02 10:09:13 +00:00
if(sh.s != sh.e)
2017-03-23 10:53:57 +00:00
queuepoly(V, sh, ds.color ? ds.color : color);
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
#ifndef NOEDIT
2016-01-02 10:09:13 +00:00
if(cmode == emDraw && mapeditor::editingShape(group, id)) {
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
usershapelayer &ds(usershapes[group][id]->d[mapeditor::dslayer]);
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
/* for(int a=0; a<size(ds.list); a++) {
hyperpoint P2 = V * ds.list[a];
int xc, yc, sc;
getcoord(P2, xc, yc, sc);
queuechr(xc, yc, sc, 10, 'x',
a == 0 ? 0x00FF00 :
a == size(ds.list)-1 ? 0xFF0000 :
0xFFFF00);
} */
2016-01-02 10:09:13 +00:00
hyperpoint mh = inverse(mapeditor::drawtrans) * mouseh;
for(int a=0; a<ds.rots; a++)
for(int b=0; b<(ds.sym?2:1); b++) {
if(outofmap(mouseh)) break;
hyperpoint P2 = V * spin(2*M_PI*a/ds.rots) * (b?Mirror*mh:mh);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
queuechr(P2, 10, 'x', 0xFF00FF);
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
#endif
2016-01-02 10:09:13 +00:00
return true;
#endif
}
2016-08-26 09:58:03 +00:00
string csnameid(int id) {
if(id == 0) return XLAT("male");
if(id == 1) return XLAT("female");
if(id == 2) return XLAT("Prince");
if(id == 3) return XLAT("Princess");
if(id == 4 || id == 5) return XLAT("cat");
if(id == 6 || id == 7) return XLAT("dog");
2017-03-23 10:53:57 +00:00
if(id == 8 || id == 9) return XLATN("Familiar");
2016-08-26 09:58:03 +00:00
return XLAT("none");
}
string csname(charstyle& cs) {
return csnameid(cs.charid);
}
namespace tortoise {
// small is 0 or 2
void draw(const transmatrix& V, int bits, int small, int stuntime) {
int eyecolor = getBit(bits, tfEyeHue) ? 0xFF0000 : 0xC0C0C0;
int shellcolor = getBit(bits, tfShellHue) ? 0x00C040 : 0xA06000;
int scutecolor = getBit(bits, tfScuteHue) ? 0x00C040 : 0xA06000;
int skincolor = getBit(bits, tfSkinHue) ? 0x00C040 : 0xA06000;
if(getBit(bits, tfShellSat)) shellcolor = gradient(shellcolor, 0xB0B0B0, 0, .5, 1);
if(getBit(bits, tfScuteSat)) scutecolor = gradient(scutecolor, 0xB0B0B0, 0, .5, 1);
if(getBit(bits, tfSkinSat)) skincolor = gradient(skincolor, 0xB0B0B0, 0, .5, 1);
if(getBit(bits, tfShellDark)) shellcolor = gradient(shellcolor, 0, 0, .5, 1);
if(getBit(bits, tfSkinDark)) skincolor = gradient(skincolor, 0, 0, .5, 1);
for(int i=0; i<12; i++) {
int col =
i == 0 ? shellcolor:
i < 8 ? scutecolor :
skincolor;
int b = getBit(bits, i);
int d = darkena(col, 0, 0xFF);
if(i >= 1 && i <= 7) if(b) { d = darkena(col, 1, 0xFF); b = 0; }
if(i >= 8 && i <= 11 && stuntime >= 3) continue;
queuepoly(V, shTortoise[i][b+small], d);
if((i >= 5 && i <= 7) || (i >= 9 && i <= 10))
queuepoly(V * Mirror, shTortoise[i][b+small], d);
if(i == 8) {
for(int k=0; k<stuntime; k++) {
eyecolor &= 0xFEFEFE;
eyecolor >>= 1;
}
queuepoly(V, shTortoise[12][b+small], darkena(eyecolor, 0, 0xFF));
queuepoly(V * Mirror, shTortoise[12][b+small], darkena(eyecolor, 0, 0xFF));
}
}
}
int getMatchColor(int bits) {
int mcol = 1;
double d = tortoise::getScent(bits);
if(d > 0) mcol = 0xFFFFFF;
else if(d < 0) mcol = 0;
int dd = 0xFF * (atan(fabs(d)/2) / (M_PI/2));
2017-03-23 10:53:57 +00:00
/* poly_outline = OUTLINE_TRANS;
2016-08-26 09:58:03 +00:00
queuepoly(V, shHeptaMarker, darkena(mcol, 0, dd));
poly_outline = OUTLINE_NONE; */
return gradient(0x487830, mcol, 0, dd, 0xFF);
}
};
2017-03-23 10:53:57 +00:00
double footfun(double d) {
d -= floor(d);
return
d < .25 ? d :
d < .75 ? .5-d :
d-1;
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
bool ivoryz;
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
void animallegs(const transmatrix& V, eMonster mo, int col, double footphase) {
footphase /= SCALE;
bool dog = mo == moRunDog;
bool bug = mo == moBug0 || mo == moMetalBeast;
if(bug) footphase *= 2.5;
double rightfoot = footfun(footphase / .4 / 2) / 4 * 2;
double leftfoot = footfun(footphase / .4 / 2 - (bug ? .5 : dog ? .1 : .25)) / 4 * 2;
if(bug) rightfoot /= 2.5, leftfoot /= 2.5;
if(!footphase) rightfoot = leftfoot = 0;
transmatrix VAML = mmscale(V, 1.04);
hpcshape* sh[6][4] = {
{&shDogFrontPaw, &shDogRearPaw, &shDogFrontLeg, &shDogRearLeg},
{&shWolfFrontPaw, &shWolfRearPaw, &shWolfFrontLeg, &shWolfRearLeg},
{&shReptileFrontFoot, &shReptileRearFoot, &shReptileFrontLeg, &shReptileRearLeg},
{&shBugLeg, NULL, NULL, NULL},
{&shTrylobiteFrontClaw, &shTrylobiteRearClaw, &shTrylobiteFrontLeg, &shTrylobiteRearLeg},
{&shBullFrontHoof, &shBullRearHoof, &shBullFrontHoof, &shBullRearHoof},
};
hpcshape **x = sh[mo == moRagingBull ? 5 : mo == moBug0 ? 3 : mo == moMetalBeast ? 4 : mo == moRunDog ? 0 : mo == moReptile ? 2 : 1];
if(x[0]) queuepolyat(V * xpush(rightfoot), *x[0], col, PPR_MONSTER_FOOT);
if(x[0]) queuepolyat(V * Mirror * xpush(leftfoot), *x[0], col, PPR_MONSTER_FOOT);
if(x[1]) queuepolyat(V * xpush(-rightfoot), *x[1], col, PPR_MONSTER_FOOT);
if(x[1]) queuepolyat(V * Mirror * xpush(-leftfoot), *x[1], col, PPR_MONSTER_FOOT);
if(x[2]) queuepolyat(VAML * xpush(rightfoot/2), *x[2], col, PPR_MONSTER_FOOT);
if(x[2]) queuepolyat(VAML * Mirror * xpush(leftfoot/2), *x[2], col, PPR_MONSTER_FOOT);
if(x[3]) queuepolyat(VAML * xpush(-rightfoot/2), *x[3], col, PPR_MONSTER_FOOT);
if(x[3]) queuepolyat(VAML * Mirror * xpush(-leftfoot/2), *x[3], col, PPR_MONSTER_FOOT);
}
void ShadowV(const transmatrix& V, const hpcshape& bp, int prio) {
if(mmspatial) {
if(pmodel == mdHyperboloid || pmodel == mdBall)
return; // shadows break the depth testing
int p = poly_outline; poly_outline = OUTLINE_TRANS;
queuepolyat(V, bp, SHADOW_MON, prio);
poly_outline = p;
}
}
void otherbodyparts(const transmatrix& V, int col, eMonster who, double footphase) {
#define VFOOT V
#define VLEG mmscale(V, geom3::LEG)
#define VGROIN mmscale(V, geom3::GROIN)
#define VBODY mmscale(V, geom3::BODY)
#define VNECK mmscale(V, geom3::NECK)
#define VHEAD mmscale(V, geom3::HEAD)
#define VALEGS V
#define VABODY mmscale(V, geom3::ABODY)
#define VAHEAD mmscale(V, geom3::AHEAD)
#define VFISH V
#define VBIRD mmscale(V, geom3::BIRD + .05 * sin((int) (size_t(where)) + ticks / 1000.))
#define VGHOST mmscale(V, geom3::GHOST)
#define VSLIMEEYE mscale(V, geom3::FLATEYE)
// if(!mmspatial && !footphase && who != moSkeleton) return;
footphase /= SCALE;
double rightfoot = footfun(footphase / .4 / 2.5) / 4 * 2.5;
// todo
if(detaillevel >= 2) {
transmatrix VL = mmscale(V, geom3::LEG1);
queuepoly(VL * xpush(rightfoot*3/4), shHumanLeg, col);
queuepoly(VL * Mirror * xpush(-rightfoot*3/4), shHumanLeg, col);
}
if(true) {
transmatrix VL = mmscale(V, geom3::LEG);
queuepoly(VL * xpush(rightfoot/2), shHumanLeg, col);
queuepoly(VL * Mirror * xpush(-rightfoot/2), shHumanLeg, col);
}
if(detaillevel >= 2) {
transmatrix VL = mmscale(V, geom3::LEG3);
queuepoly(VL * xpush(rightfoot/4), shHumanLeg, col);
queuepoly(VL * Mirror * xpush(-rightfoot/4), shHumanLeg, col);
}
if(who == moWaterElemental) {
double fishtail = footfun(footphase / .4) / 4 * 1.5;
queuepoly(VFOOT * xpush(fishtail), shFishTail, watercolor(100));
}
else if(who == moSkeleton) {
queuepoly(VFOOT * xpush(rightfoot), shSkeletalFoot, col);
queuepoly(VFOOT * Mirror * xpush(-rightfoot), shSkeletalFoot, col);
return;
}
else if(isTroll(who) || who == moMonkey || who == moYeti || who == moRatling || who == moRatlingAvenger || who == moGoblin) {
queuepoly(VFOOT * xpush(rightfoot), shYetiFoot, col);
queuepoly(VFOOT * Mirror * xpush(-rightfoot), shYetiFoot, col);
}
else {
queuepoly(VFOOT * xpush(rightfoot), shHumanFoot, col);
queuepoly(VFOOT * Mirror * xpush(-rightfoot), shHumanFoot, col);
}
if(!mmspatial) return;
if(detaillevel >= 2 && who != moZombie)
queuepoly(mmscale(V, geom3::NECK1), shHumanNeck, col);
if(detaillevel >= 1) {
queuepoly(VGROIN, shHumanGroin, col);
if(who != moZombie) queuepoly(VNECK, shHumanNeck, col);
}
if(detaillevel >= 2) {
queuepoly(mmscale(V, geom3::GROIN1), shHumanGroin, col);
if(who != moZombie) queuepoly(mmscale(V, geom3::NECK3), shHumanNeck, col);
}
}
bool drawstar(cell *c) {
for(int t=0; t<c->type; t++)
if(c->mov[t] && c->mov[t]->wall != waSulphur && c->mov[t]->wall != waSulphurC &&
c->mov[t]->wall != waBarrier)
return false;
return true;
}
bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, double footphase) {
char xch = minf[m].glyph;
#ifndef NOEDIT
if(where == mapeditor::drawcell)
2016-01-02 10:09:13 +00:00
mapeditor::drawtrans = V;
#endif
2017-03-23 10:53:57 +00:00
if(m == moTortoise && where && where->stuntime >= 3)
2016-08-26 09:58:03 +00:00
drawStunStars(V, where->stuntime-2);
2017-03-23 10:53:57 +00:00
else if (m == moTortoise || m == moPlayer || (where && !where->stuntime)) ;
else if(where && !(isMetalBeast(m) && where->stuntime == 1))
2016-01-02 10:09:13 +00:00
drawStunStars(V, where->stuntime);
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
if(m == moTortoise) {
2017-03-23 10:53:57 +00:00
int bits = where ? tortoise::getb(where) : 0;
tortoise::draw(V, bits, 0, where ? where->stuntime : 0);
2016-08-26 09:58:03 +00:00
if(tortoise::seek() && !tortoise::diff(bits))
queuepoly(V, shRing, darkena(0xFFFFFF, 0, 0x80 + 0x70 * sin(ticks / 200.0)));
}
else if(m == moPlayer) {
charstyle& cs = getcs();
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
bool havus = drawUserShape(V, 0, cs.charid, cs.skincolor);
2016-01-02 10:09:13 +00:00
if(mapeditor::drawplayer && !havus) {
2017-03-23 10:53:57 +00:00
if(cs.charid >= 8) {
queuepoly(VABODY, shWolfBody, fc(0, cs.skincolor, 0));
if(!mmspatial && !footphase)
queuepoly(VALEGS, shWolfLegs, fc(150, cs.dresscolor, 4));
else {
ShadowV(V, shWolfBody);
animallegs(VALEGS, moWolf, fc(500, cs.dresscolor, 4), footphase);
}
queuepoly(VAHEAD, shFamiliarHead, fc(500, cs.haircolor, 2));
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
int col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.swordcolor, 3);
queuepoly(VAHEAD, shFamiliarEye, col);
queuepoly(VAHEAD * Mirror, shFamiliarEye, col);
}
}
else if(cs.charid >= 6) {
if(!mmspatial && !footphase)
queuepoly(VABODY, shDogBody, fc(0, cs.skincolor, 0));
else {
ShadowV(V, shDogTorso);
queuepoly(VABODY, shDogTorso, fc(0, cs.skincolor, 0));
animallegs(VALEGS, moRunDog, fc(500, cs.dresscolor, 4), footphase);
}
queuepoly(VAHEAD, shDogHead, fc(150, cs.haircolor, 2));
2016-08-26 09:58:03 +00:00
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
2017-03-23 10:53:57 +00:00
int col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.swordcolor, 3);
queuepoly(VAHEAD, shWolf1, col);
queuepoly(VAHEAD, shWolf2, col);
queuepoly(VAHEAD, shWolf3, col);
2016-08-26 09:58:03 +00:00
}
}
else if(cs.charid >= 4) {
2017-03-23 10:53:57 +00:00
queuepoly(VABODY, shCatBody, fc(0, cs.skincolor, 0));
queuepoly(VAHEAD, shCatHead, fc(150, cs.haircolor, 2));
if(!mmspatial && !footphase)
queuepoly(VALEGS, shCatLegs, fc(500, cs.dresscolor, 4));
else {
ShadowV(V, shCatBody);
animallegs(VALEGS, moRunDog, fc(500, cs.dresscolor, 4), footphase);
}
2016-08-26 09:58:03 +00:00
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
2017-03-23 10:53:57 +00:00
int col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.swordcolor, 3);
queuepoly(VAHEAD * xpush(.04), shWolf1, col);
queuepoly(VAHEAD * xpush(.04), shWolf2, col);
2016-08-26 09:58:03 +00:00
}
}
else {
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
otherbodyparts(V, fc(0, cs.skincolor, 0), items[itOrbFish] ? moWaterElemental : moPlayer, footphase);
queuepoly(VBODY, (cs.charid&1) ? shFemaleBody : shPBody, fc(0, cs.skincolor, 0));
ShadowV(V, (cs.charid&1) ? shFemaleBody : shPBody);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(items[itOrbHorns]) {
queuepoly(VBODY, shBullHead, items[itOrbDiscord] ? watercolor(0) : 0xFF000030);
queuepoly(VBODY, shBullHorn, items[itOrbDiscord] ? watercolor(0) : 0xFF000040);
queuepoly(VBODY * Mirror, shBullHorn, items[itOrbDiscord] ? watercolor(0) : 0xFF000040);
}
2016-01-02 10:09:13 +00:00
if(items[itOrbThorns])
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shHedgehogBladePlayer, items[itOrbDiscord] ? watercolor(0) : 0x00FF00FF);
2016-01-02 10:09:13 +00:00
else if(!shmup::on && items[itOrbDiscord])
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, cs.charid >= 2 ? shSabre : shPSword, watercolor(0));
2016-08-26 09:58:03 +00:00
else if(items[itRevolver])
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shGunInHand, fc(314, cs.swordcolor, 3)); // 3 not colored
2016-01-02 10:09:13 +00:00
else if(!shmup::on)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, cs.charid >= 2 ? shSabre : shPSword, fc(314, cs.swordcolor, 3)); // 3 not colored
2016-01-02 10:09:13 +00:00
else if(shmup::curtime >= shmup::getPlayer()->nextshot)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shPKnife, fc(314, cs.swordcolor, 3)); // 3 not colored
if(items[itOrbBeauty]) {
if(cs.charid&1)
queuepoly(VHEAD, shFlowerHair, darkena(iinf[itOrbBeauty].color, 0, 0xFF));
else
queuepoly(VBODY, shFlowerHand, darkena(iinf[itOrbBeauty].color, 0, 0xFF));
}
if(where && where->land == laWildWest) {
queuepoly(VHEAD, shWestHat1, darkena(cs.swordcolor, 1, 0XFF));
queuepoly(VHEAD, shWestHat2, darkena(cs.swordcolor, 0, 0XFF));
2016-08-26 09:58:03 +00:00
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(cheater && !autocheat) {
queuepoly(VHEAD, (cs.charid&1) ? shGoatHead : shDemon, darkena(0xFFFF00, 0, 0xFF));
2016-01-02 10:09:13 +00:00
// queuepoly(V, shHood, darkena(0xFF00, 1, 0xFF));
}
else {
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, shPFace, fc(500, cs.skincolor, 1));
queuepoly(VHEAD, (cs.charid&1) ? shFemaleHair : shPHead, fc(150, cs.haircolor, 2));
2015-08-08 13:57:52 +00:00
}
2016-08-26 09:58:03 +00:00
if(cs.charid&1)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shFemaleDress, fc(500, cs.dresscolor, 4));
2016-08-26 09:58:03 +00:00
if(cs.charid == 2)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shPrinceDress, fc(400, cs.dresscolor, 5));
2016-08-26 09:58:03 +00:00
if(cs.charid == 3)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shPrincessDress, fc(400, cs.dresscolor2, 5));
2016-08-26 09:58:03 +00:00
}
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
if(knighted)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
if(tortoise::seek())
2017-03-23 10:53:57 +00:00
tortoise::draw(VBODY * ypush(-crossf*.25), tortoise::seekbits, 4, 0);
2016-08-26 09:58:03 +00:00
2016-01-02 10:09:13 +00:00
}
}
else if(drawUserShape(V, 1, m, darkena(col, 0, 0xFF))) return false;
2016-08-26 09:58:03 +00:00
else if(isMimic(m) || m == moShadow || m == moIllusion) {
charstyle& cs = getcs();
2017-03-23 10:53:57 +00:00
2016-08-26 09:58:03 +00:00
if(drawUserShape(V, 0, (cs.charid&1)?1:0, darkena(col, 0, 0x80))) return false;
2017-03-23 10:53:57 +00:00
if(cs.charid >= 8) {
queuepoly(VABODY, shWolfBody, darkena(col, 0, 0xC0));
ShadowV(V, shWolfBody);
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, darkena(col, 0, 0xC0), footphase);
else
queuepoly(VABODY, shWolfLegs, darkena(col, 0, 0xC0));
queuepoly(VABODY, shFamiliarHead, darkena(col, 0, 0xC0));
queuepoly(VAHEAD, shFamiliarEye, darkena(col, 0, 0xC0));
queuepoly(VAHEAD * Mirror, shFamiliarEye, darkena(col, 0, 0xC0));
}
else if(cs.charid >= 6) {
ShadowV(V, shDogBody);
queuepoly(VAHEAD, shDogHead, darkena(col, 0, 0xC0));
if(mmspatial || footphase) {
animallegs(VALEGS, moRunDog, darkena(col, 0, 0xC0), footphase);
queuepoly(VABODY, shDogTorso, darkena(col, 0, 0xC0));
}
else
queuepoly(VABODY, shDogBody, darkena(col, 0, 0xC0));
queuepoly(VABODY, shWolf1, darkena(col, 1, 0xC0));
queuepoly(VABODY, shWolf2, darkena(col, 1, 0xC0));
queuepoly(VABODY, shWolf3, darkena(col, 1, 0xC0));
2016-08-26 09:58:03 +00:00
}
else if(cs.charid >= 4) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shCatBody);
queuepoly(VABODY, shCatBody, darkena(col, 0, 0xC0));
queuepoly(VAHEAD, shCatHead, darkena(col, 0, 0xC0));
if(mmspatial || footphase)
animallegs(VALEGS, moRunDog, darkena(col, 0, 0xC0), footphase);
else
queuepoly(VALEGS, shCatLegs, darkena(col, 0, 0xC0));
queuepoly(VAHEAD * xpush(.04), shWolf1, darkena(col, 1, 0xC0));
queuepoly(VAHEAD * xpush(.04), shWolf2, darkena(col, 1, 0xC0));
2016-08-26 09:58:03 +00:00
}
else {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0x40), m, footphase);
queuepoly(VBODY, (cs.charid&1) ? shFemaleBody : shPBody, darkena(col, 0, 0X80));
2016-08-26 09:58:03 +00:00
if(!shmup::on)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, (cs.charid >= 2 ? shSabre : shPSword), darkena(col, 0, 0XC0));
2016-08-26 09:58:03 +00:00
else if(shmup::curtime >= shmup::getPlayer()->nextshot)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shPKnife, darkena(col, 0, 0XC0));
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, (cs.charid&1) ? shFemaleHair : shPHead, darkena(col, 1, 0XC0));
queuepoly(VHEAD, shPFace, darkena(col, 0, 0XC0));
2016-08-26 09:58:03 +00:00
if(cs.charid&1)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shFemaleDress, darkena(col, 1, 0XC0));
2016-08-26 09:58:03 +00:00
if(cs.charid == 2)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shPrinceDress, darkena(col, 1, 0XC0));
2016-08-26 09:58:03 +00:00
if(cs.charid == 3)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shPrincessDress, darkena(col, 1, 0XC0));
2016-08-26 09:58:03 +00:00
}
2016-01-02 10:09:13 +00:00
}
else if(m == moBullet) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shKnife);
queuepoly(VBODY * spin(-M_PI/4), shKnife, getcs().swordcolor);
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(m == moKnight || m == moKnightMoved) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shPBody);
otherbodyparts(V, darkena(0xC0C0A0, 0, 0xC0), m, footphase);
queuepoly(VBODY, shPBody, darkena(0xC0C0A0, 0, 0xC0));
queuepoly(VBODY, shPSword, darkena(0xFFFF00, 0, 0xFF));
queuepoly(VBODY, shKnightArmor, darkena(0xD0D0D0, 1, 0xFF));
2016-01-02 10:09:13 +00:00
int col;
2017-03-23 10:53:57 +00:00
if(!euclid && where && where->master->alt)
2016-01-02 10:09:13 +00:00
col = cloakcolor(roundTableRadius(where));
else
col = cloakcolor(newRoundTableRadius());
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shKnightCloak, darkena(col, 1, 0xFF));
queuepoly(VHEAD, shPHead, darkena(0x703800, 1, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
2016-01-02 10:09:13 +00:00
return false;
}
2017-03-31 19:41:09 +00:00
else if(m == moGolem || m == moGolemMoved) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shPBody);
otherbodyparts(V, darkena(col, 1, 0XC0), m, footphase);
queuepoly(VBODY, shPBody, darkena(col, 0, 0XC0));
queuepoly(VHEAD, shGolemhead, darkena(col, 1, 0XFF));
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(isPrincess(m) || m == moFalsePrincess || m == moRoseLady || m == moRoseBeauty) {
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
bool girl = princessgender() == GEN_F;
bool evil = !isPrincess(m);
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
int facecolor = evil ? 0xC0B090FF : 0xD0C080FF;
2017-03-23 10:53:57 +00:00
ShadowV(V, girl ? shFemaleBody : shPBody);
otherbodyparts(V, facecolor, m, footphase);
queuepoly(VBODY, girl ? shFemaleBody : shPBody, facecolor);
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
if(m == moPrincessArmed)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, vid.cs.charid < 2 ? shSabre : shPSword, 0xFFFFFFFF);
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if((m == moFalsePrincess || m == moRoseBeauty) && where && where->cpdist == 1)
queuepoly(VBODY, shPKnife, 0xFFFFFFFF);
2016-08-26 09:58:03 +00:00
if(m == moRoseLady) {
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shPKnife, 0xFFFFFFFF);
queuepoly(VBODY * Mirror, shPKnife, 0xFFFFFFFF);
2016-08-26 09:58:03 +00:00
}
if(m == moRoseLady) {
// queuepoly(V, girl ? shGoatHead : shDemon, 0x800000FF);
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, girl ? shFemaleHair : shPHead, evil ? 0x500050FF : 0x332A22FF);
2016-08-26 09:58:03 +00:00
}
else if(m == moRoseBeauty) {
if(girl) {
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, shBeautyHair, 0xF0A0D0FF);
queuepoly(VHEAD, shFlowerHair, 0xC00000FF);
2016-08-26 09:58:03 +00:00
}
else {
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, shPHead, 0xF0A0D0FF);
queuepoly(VHEAD, shFlowerHand, 0xC00000FF);
queuepoly(VBODY, shSuspenders, 0xC00000FF);
2016-08-26 09:58:03 +00:00
}
}
else {
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, girl ? shFemaleHair : shPHead,
2016-08-26 09:58:03 +00:00
evil ? 0xC00000FF : 0x332A22FF);
}
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, shPFace, facecolor);
2016-01-02 10:09:13 +00:00
if(girl) {
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shFemaleDress, evil ? 0xC000C0FF : 0x00C000FF);
2016-08-26 09:58:03 +00:00
if(vid.cs.charid < 2)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shPrincessDress, evil ? 0xC040C0C0 : 0x8080FFC0);
2016-01-02 10:09:13 +00:00
}
else {
2016-08-26 09:58:03 +00:00
if(vid.cs.charid < 2)
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shPrinceDress, evil ? 0x802080FF : 0x404040FF);
2016-01-02 10:09:13 +00:00
}
}
2017-03-31 19:41:09 +00:00
else if(m == moWolf || m == moRedFox || m == moWolfMoved) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shWolfBody);
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, darkena(col, 0, 0xFF), footphase);
else
queuepoly(VALEGS, shWolfLegs, darkena(col, 0, 0xFF));
queuepoly(VABODY, shWolfBody, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shWolfHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shWolfEyes, darkena(col, 3, 0xFF));
}
else if(isBull(m)) {
ShadowV(V, shBullBody);
int hoofcol = darkena(gradient(0, col, 0, .65, 1), 0, 0xFF);
if(mmspatial || footphase)
animallegs(VALEGS, moRagingBull, hoofcol, footphase);
queuepoly(VABODY, shBullBody, darkena(gradient(0, col, 0, .80, 1), 0, 0xFF));
queuepoly(VAHEAD, shBullHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shBullHorn, darkena(0xFFFFFF, 0, 0xFF));
queuepoly(VAHEAD * Mirror, shBullHorn, darkena(0xFFFFFF, 0, 0xFF));
}
else if(m == moReptile) {
ShadowV(V, shReptileBody);
animallegs(VALEGS, moReptile, darkena(col, 0, 0xFF), footphase);
queuepoly(VABODY, shReptileBody, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shReptileHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shReptileEye, darkena(col, 3, 0xFF));
queuepoly(VAHEAD * Mirror, shReptileEye, darkena(col, 3, 0xFF));
queuepoly(VABODY, shReptileTail, darkena(col, 2, 0xFF));
2016-01-02 10:09:13 +00:00
}
else if(m == moVineBeast) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shWolfBody);
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, 0x00FF00FF, footphase);
else
queuepoly(VALEGS, shWolfLegs, 0x00FF00FF);
queuepoly(VABODY, shWolfBody, darkena(col, 1, 0xFF));
queuepoly(VAHEAD, shWolfHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shWolfEyes, 0xFF0000FF);
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(m == moMouse || m == moMouseMoved) {
2017-03-23 10:53:57 +00:00
queuepoly(VALEGS, shMouse, darkena(col, 0, 0xFF));
queuepoly(VALEGS, shMouseLegs, darkena(col, 1, 0xFF));
queuepoly(VALEGS, shMouseEyes, 0xFF);
2016-01-02 10:09:13 +00:00
}
else if(isBug(m)) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shBugBody);
if(!mmspatial && !footphase)
queuepoly(VABODY, shBugBody, darkena(col, 0, 0xFF));
else {
animallegs(VALEGS, moBug0, darkena(col, 0, 0xFF), footphase);
queuepoly(VABODY, shBugAntenna, darkena(col, 1, 0xFF));
}
queuepoly(VABODY, shBugArmor, darkena(col, 1, 0xFF));
2016-01-02 10:09:13 +00:00
}
else if(m == moRunDog) {
2017-03-23 10:53:57 +00:00
if(!mmspatial && !footphase)
queuepoly(VABODY, shDogBody, darkena(col, 0, 0xFF));
else {
ShadowV(V, shDogTorso);
queuepoly(VABODY, shDogTorso, darkena(col, 0, 0xFF));
animallegs(VALEGS, moRunDog, darkena(col, 0, 0xFF), footphase);
}
queuepoly(VAHEAD, shDogHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shWolf1, darkena(0x202020, 0, 0xFF));
queuepoly(VAHEAD, shWolf2, darkena(0x202020, 0, 0xFF));
queuepoly(VAHEAD, shWolf3, darkena(0x202020, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
else if(m == moOrangeDog) {
2017-03-23 10:53:57 +00:00
if(!mmspatial && !footphase)
queuepoly(VABODY, shDogBody, darkena(0xFFFFFF, 0, 0xFF));
else {
ShadowV(V, shDogTorso);
queuepoly(VABODY, shDogTorso, darkena(0xFFFFFF, 0, 0xFF));
animallegs(VALEGS, moRunDog, darkena(0xFFFFFF, 0, 0xFF), footphase);
}
queuepoly(VAHEAD, shDogHead, darkena(0xFFFFFF, 0, 0xFF));
queuepoly(VABODY, shDogStripes, darkena(0x303030, 0, 0xFF));
queuepoly(VAHEAD, shWolf1, darkena(0x202020, 0, 0xFF));
queuepoly(VAHEAD, shWolf2, darkena(0x202020, 0, 0xFF));
queuepoly(VAHEAD, shWolf3, darkena(0x202020, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
else if(m == moShark || m == moGreaterShark || m == moCShark)
2017-03-23 10:53:57 +00:00
queuepoly(VFISH, shShark, darkena(col, 0, 0xFF));
2016-01-02 10:09:13 +00:00
else if(m == moEagle || m == moParrot || m == moBomberbird || m == moAlbatross ||
2017-03-31 19:41:09 +00:00
m == moTameBomberbird || m == moWindCrow || m == moTameBomberbirdMoved) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shEagle);
queuepoly(VBIRD, shEagle, darkena(col, 0, 0xFF));
}
else if(m == moSparrowhawk) {
ShadowV(V, shHawk);
queuepoly(VBIRD, shHawk, darkena(col, 0, 0xFF));
}
else if(m == moButterfly) {
transmatrix Vwing = Id;
Vwing[1][1] = .85 + .15 * sin(ticks / 100.0);
ShadowV(V * Vwing, shButterflyWing);
queuepoly(VBIRD * Vwing, shButterflyWing, darkena(col, 0, 0xFF));
queuepoly(VBIRD, shButterflyBody, darkena(col, 2, 0xFF));
}
else if(m == moGadfly) {
transmatrix Vwing = Id;
Vwing[1][1] = .85 + .15 * sin(ticks / 100.0);
ShadowV(V * Vwing, shGadflyWing);
queuepoly(VBIRD * Vwing, shGadflyWing, darkena(col, 0, 0xFF));
queuepoly(VBIRD, shGadflyBody, darkena(col, 1, 0xFF));
queuepoly(VBIRD, shGadflyEye, darkena(col, 2, 0xFF));
queuepoly(VBIRD * Mirror, shGadflyEye, darkena(col, 2, 0xFF));
}
else if(m == moVampire || m == moBat) {
if(m == moBat) ShadowV(V, shBatWings); // but vampires have no shadow
queuepoly(VBIRD, shBatWings, darkena(0x303030, 0, 0xFF));
queuepoly(VBIRD, shBatBody, darkena(0x606060, 0, 0xFF));
/* queuepoly(V, shBatMouth, darkena(0xC00000, 0, 0xFF));
queuepoly(V, shBatFang, darkena(0xFFC0C0, 0, 0xFF));
queuepoly(V*Mirror, shBatFang, darkena(0xFFC0C0, 0, 0xFF));
queuepoly(V, shBatEye, darkena(00000000, 0, 0xFF));
queuepoly(V*Mirror, shBatEye, darkena(00000000, 0, 0xFF)); */
}
2016-01-02 10:09:13 +00:00
else if(m == moGargoyle) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shGargoyleWings);
queuepoly(VBIRD, shGargoyleWings, darkena(col, 0, 0xD0));
queuepoly(VBIRD, shGargoyleBody, darkena(col, 0, 0xFF));
}
else if(m == moZombie) {
int c = darkena(col, where->land == laHalloween ? 1 : 0, 0xFF);
otherbodyparts(V, c, m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, c);
2016-01-02 10:09:13 +00:00
}
else if(m == moDesertman) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(col, 0, 0xC0));
queuepoly(VBODY, shPSword, 0xFFFF00FF);
queuepoly(VHEAD, shHood, 0xD0D000C0);
2016-01-02 10:09:13 +00:00
}
else if(m == moPalace || m == moFatGuard || m == moVizier || m == moSkeleton) {
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shSabre, 0xFFFFFFFF);
2016-01-02 10:09:13 +00:00
if(m == moSkeleton) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(0xFFFFFF, 0, 0xFF), moSkeleton, footphase);
queuepoly(VBODY, shSkeletonBody, darkena(0xFFFFFF, 0, 0xFF));
queuepoly(VHEAD, shSkull, darkena(0xFFFFFF, 0, 0xFF));
queuepoly(VHEAD, shSkullEyes, darkena(0, 0, 0xFF));
ShadowV(V, shSkeletonBody);
2016-01-02 10:09:13 +00:00
}
else {
2017-03-23 10:53:57 +00:00
ShadowV(V, shPBody);
otherbodyparts(V, darkena(0xFFD500, 0, 0xFF), m, footphase);
2016-01-02 10:09:13 +00:00
if(m == moFatGuard) {
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shFatBody, darkena(0xC06000, 0, 0xFF));
2016-01-02 10:09:13 +00:00
col = 0xFFFFFF;
2017-03-23 10:53:57 +00:00
if(where && where->hitpoints >= 3)
queuepoly(VBODY, shKnightCloak, darkena(0xFFC0C0, 1, 0xFF));
2016-01-02 10:09:13 +00:00
}
else {
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shPBody, darkena(0xFFD500, 0, 0xFF));
queuepoly(VBODY, shKnightArmor, m == moVizier ? 0xC000C0FF :
2016-01-02 10:09:13 +00:00
darkena(0x00C000, 1, 0xFF));
2017-03-23 10:53:57 +00:00
if(where && where->hitpoints >= 3)
queuepoly(VBODY, shKnightCloak, m == moVizier ? 0x800080Ff :
2016-01-02 10:09:13 +00:00
darkena(0x00FF00, 1, 0xFF));
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, shTurban1, darkena(col, 1, 0xFF));
if(where && where->hitpoints >= 2)
queuepoly(VHEAD, shTurban2, darkena(col, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
drawStunStars(V, where ? where->stuntime : 0);
2016-01-02 10:09:13 +00:00
}
else if(m == moCrystalSage) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, 0xFFFFFFFF, m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, 0xFFFFFFFF);
queuepoly(VHEAD, shPHead, 0xFFFFFFFF);
queuepoly(VHEAD, shPFace, 0xFFFFFFFF);
2016-01-02 10:09:13 +00:00
}
else if(m == moHedge) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shPBody);
otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
queuepoly(VBODY, shPBody, darkena(col, 0, 0xFF));
queuepoly(VBODY, shHedgehogBlade, 0xC0C0C0FF);
queuepoly(VHEAD, shPHead, 0x804000FF);
queuepoly(VHEAD, shPFace, 0xF09000FF);
2016-01-02 10:09:13 +00:00
}
else if(m == moYeti || m == moMonkey) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD, shPHead, darkena(col, 0, 0xFF));
}
else if(m == moResearcher) {
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(0xFFFF00, 0, 0xC0));
queuepoly(VHEAD, shAztecHead, darkena(col, 0, 0xFF));
queuepoly(VHEAD, shAztecCap, darkena(0xC000C0, 0, 0xFF));
}
else if(m == moFamiliar) {
/* queuepoly(V, shFemaleBody, darkena(0xC0B070, 0, 0xFF));
queuepoly(V, shPFace, darkena(0xC0B070, 0, 0XFF));
queuepoly(V, shWizardCape1, 0x601000FF);
queuepoly(V, shWizardCape2, 0x781800FF);
queuepoly(V, shWizardHat1, 0x902000FF);
queuepoly(V, shWizardHat2, 0xA82800FF); */
// queuepoly(V, shCatBody, darkena(0x601000, 0, 0xFF));
// queuepoly(V, shGargoyleBody, darkena(0x903000, 0, 0xFF));
ShadowV(V, shWolfBody);
queuepoly(VABODY, shWolfBody, darkena(0xA03000, 0, 0xFF));
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, darkena(0xC04000, 0, 0xFF), footphase);
else
queuepoly(VALEGS, shWolfLegs, darkena(0xC04000, 0, 0xFF));
queuepoly(VAHEAD, shFamiliarHead, darkena(0xC04000, 0, 0xFF));
// queuepoly(V, shCatLegs, darkena(0x902000, 0, 0xFF));
if(true) {
queuepoly(VAHEAD, shFamiliarEye, darkena(0xFFFF000, 0, 0xFF));
queuepoly(VAHEAD * Mirror, shFamiliarEye, darkena(0xFFFF000, 0, 0xFF));
}
2016-01-02 10:09:13 +00:00
}
else if(m == moRanger) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shPBody);
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
queuepoly(VBODY, shPBody, darkena(col, 0, 0xC0));
queuepoly(VBODY, shPSword, darkena(col, 0, 0xFF));
queuepoly(VHEAD, shArmor, darkena(col, 1, 0xFF));
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(m == moGhost || m == moSeep || m == moFriendlyGhost) {
2017-03-23 10:53:57 +00:00
if(m == moFriendlyGhost) col = fghostcolor(ticks, where);
queuepoly(VGHOST, shGhost, darkena(col, 0, m == moFriendlyGhost ? 0xC0 : 0x80));
queuepoly(VGHOST, shEyes, 0xFF);
2016-01-02 10:09:13 +00:00
}
else if(m == moVineSpirit) {
2017-03-23 10:53:57 +00:00
queuepoly(VGHOST, shGhost, 0xD0D0D0C0);
queuepoly(VGHOST, shEyes, 0xFF0000FF);
2016-01-02 10:09:13 +00:00
}
else if(m == moFireFairy) {
col = firecolor(0);
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, shFemaleBody);
queuepoly(VBODY, shFemaleBody, darkena(col, 0, 0XC0));
queuepoly(VHEAD, shWitchHair, darkena(col, 1, 0xFF));
queuepoly(VHEAD, shPFace, darkena(col, 0, 0XFF));
2016-01-02 10:09:13 +00:00
}
else if(m == moSlime) {
2017-03-23 10:53:57 +00:00
queuepoly(VFISH, shSlime, darkena(col, 0, 0x80));
queuepoly(VSLIMEEYE, shEyes, 0xFF);
}
else if(m == moKrakenH) {
queuepoly(VFISH, shKrakenHead, darkena(col, 0, 0xD0));
queuepoly(VFISH, shKrakenEye, 0xFFFFFFC0);
queuepoly(VFISH, shKrakenEye2, 0xC0);
queuepoly(VFISH * Mirror, shKrakenEye, 0xFFFFFFC0);
queuepoly(VFISH * Mirror, shKrakenEye2, 0xC0);
}
else if(m == moKrakenT) {
queuepoly(VFISH, shSeaTentacle, darkena(col, 0, 0xD0));
2016-01-02 10:09:13 +00:00
}
else if(m == moCultist || m == moPyroCultist || m == moCultistLeader) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(col, 0, 0xC0));
queuepoly(VBODY, shPSword, darkena(col, 2, 0xFF));
queuepoly(VHEAD, shHood, darkena(col, 1, 0xFF));
2016-01-02 10:09:13 +00:00
}
else if(m == moPirate) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(0x404040, 0, 0xFF));
queuepoly(VBODY, shPirateHook, darkena(0xD0D0D0, 0, 0xFF));
queuepoly(VHEAD, shPFace, darkena(0xFFFF80, 0, 0xFF));
queuepoly(VHEAD, shEyepatch, darkena(0, 0, 0xC0));
queuepoly(VHEAD, shPirateHood, darkena(col, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(m == moRatling || m == moRatlingAvenger) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, shYeti);
queuepoly(VBODY, shYeti, darkena(col, 1, 0xFF));
queuepoly(VHEAD, shRatHead, darkena(col, 0, 0xFF));
queuepoly(VLEG, shRatTail, darkena(col, 0, 0xFF));
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
float t = sin(ticks / 1000.0 + (where ? where->cpdist : 0));
2016-08-26 09:58:03 +00:00
int eyecol = t > 0.92 ? 0xFF0000 : 0;
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, shWolf1, darkena(eyecol, 0, 0xFF));
queuepoly(VHEAD, shWolf2, darkena(eyecol, 0, 0xFF));
queuepoly(VHEAD, shWolf3, darkena(0x202020, 0, 0xFF));
2016-08-26 09:58:03 +00:00
if(m == moRatlingAvenger) {
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shRatCape1, 0x303030FF);
queuepoly(VHEAD, shRatCape2, 0x484848FF);
2016-08-26 09:58:03 +00:00
}
}
2016-01-02 10:09:13 +00:00
else if(m == moViking) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(0xE00000, 0, 0xFF));
queuepoly(VBODY, shPSword, darkena(0xD0D0D0, 0, 0xFF));
queuepoly(VBODY, shKnightCloak, darkena(0x404040, 0, 0xFF));
queuepoly(VHEAD, shVikingHelmet, darkena(0xC0C0C0, 0, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xFFFF80, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(m == moOutlaw) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(col, 0, 0xFF));
queuepoly(VBODY, shKnightCloak, darkena(col, 1, 0xFF));
queuepoly(VHEAD, shWestHat1, darkena(col, 2, 0XFF));
queuepoly(VHEAD, shWestHat2, darkena(col, 1, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xFFFF80, 0, 0xFF));
queuepoly(VBODY, shGunInHand, darkena(col, 1, 0XFF));
2016-08-26 09:58:03 +00:00
}
2016-01-02 10:09:13 +00:00
else if(m == moNecromancer) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, 0xC00000C0);
queuepoly(VHEAD, shHood, darkena(col, 1, 0xFF));
}
else if(m == moDraugr) {
otherbodyparts(V, 0x483828D0, m, footphase);
queuepoly(VBODY, shPBody, 0x483828D0);
queuepoly(VBODY, shPSword, 0xFFFFD0A0);
queuepoly(VHEAD, shPHead, 0x483828D0);
// queuepoly(V, shSkull, 0xC06020D0);
//queuepoly(V, shSkullEyes, 0x000000D0);
// queuepoly(V, shWightCloak, 0xC0A080A0);
int b = where->cpdist;
b--;
if(b < 0) b = 0;
if(b > 6) b = 6;
queuepoly(VHEAD, shWightCloak, 0x605040A0 + 0x10101000 * b);
2016-01-02 10:09:13 +00:00
}
else if(m == moGoblin) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, shYeti);
queuepoly(VBODY, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD, shArmor, darkena(col, 1, 0XFF));
2016-01-02 10:09:13 +00:00
}
else if(m == moLancer || m == moFlailer || m == moMiner) {
transmatrix V2 = V;
if(m == moLancer)
2017-03-23 10:53:57 +00:00
V2 = V * spin((where && where->type == 6) ? -M_PI/3 : -M_PI/2 );
transmatrix Vh = mmscale(V2, geom3::HEAD);
transmatrix Vb = mmscale(V2, geom3::BODY);
otherbodyparts(V2, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V2, shPBody);
queuepoly(Vb, shPBody, darkena(col, 0, 0xC0));
queuepoly(Vh, m == moFlailer ? shArmor : shHood, darkena(col, 1, 0XFF));
2016-01-02 10:09:13 +00:00
if(m == moMiner)
2017-03-23 10:53:57 +00:00
queuepoly(Vb, shPickAxe, darkena(0xC0C0C0, 0, 0XFF));
2016-01-02 10:09:13 +00:00
if(m == moLancer)
2017-03-23 10:53:57 +00:00
queuepoly(Vb, shPike, darkena(col, 0, 0XFF));
2016-01-02 10:09:13 +00:00
if(m == moFlailer) {
2017-03-23 10:53:57 +00:00
queuepoly(Vb, shFlailBall, darkena(col, 0, 0XFF));
queuepoly(Vb, shFlailChain, darkena(col, 1, 0XFF));
queuepoly(Vb, shFlailTrunk, darkena(col, 0, 0XFF));
2015-08-08 13:57:52 +00:00
}
}
2016-01-02 10:09:13 +00:00
else if(m == moTroll) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, shYeti);
queuepoly(VBODY, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD, shPHead, darkena(col, 1, 0XFF));
queuepoly(VHEAD, shPFace, darkena(col, 2, 0XFF));
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(m == moFjordTroll || m == moForestTroll || m == moStormTroll) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, shYeti);
queuepoly(VBODY, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD, shPHead, darkena(col, 1, 0XFF));
queuepoly(VHEAD, shPFace, darkena(col, 2, 0XFF));
2016-01-02 10:09:13 +00:00
}
else if(m == moDarkTroll) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, shYeti);
queuepoly(VBODY, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD, shPHead, darkena(col, 1, 0XFF));
queuepoly(VHEAD, shPFace, 0xFFFFFF80);
2016-01-02 10:09:13 +00:00
}
else if(m == moRedTroll) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, shYeti);
queuepoly(VBODY, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD, shPHead, darkena(0xFF8000, 0, 0XFF));
queuepoly(VHEAD, shPFace, 0xFFFFFF80);
2016-01-02 10:09:13 +00:00
}
else if(m == moEarthElemental) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, shYeti);
queuepoly(VBODY, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD, shPHead, darkena(col, 0, 0XFF));
queuepoly(VHEAD, shPFace, 0xF0000080);
2016-01-02 10:09:13 +00:00
}
else if(m == moWaterElemental) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, watercolor(50), m, footphase);
ShadowV(V, shWaterElemental);
queuepoly(VBODY, shWaterElemental, watercolor(0));
queuepoly(VHEAD, shFemaleHair, watercolor(100));
queuepoly(VHEAD, shPFace, watercolor(200));
2016-01-02 10:09:13 +00:00
}
else if(m == moFireElemental) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(firecolor(50), 0, 0xFF), m, footphase);
ShadowV(V, shWaterElemental);
queuepoly(VBODY, shWaterElemental, darkena(firecolor(0), 0, 0xFF));
queuepoly(VHEAD, shFemaleHair, darkena(firecolor(100), 0, 0xFF));
queuepoly(VHEAD, shPFace, darkena(firecolor(200), 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
else if(m == moAirElemental) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0x40), m, footphase);
ShadowV(V, shWaterElemental);
queuepoly(VBODY, shWaterElemental, darkena(col, 0, 0x80));
queuepoly(VHEAD, shFemaleHair, darkena(col, 0, 0x80));
queuepoly(VHEAD, shPFace, darkena(col, 0, 0x80));
2016-01-02 10:09:13 +00:00
}
else if(xch == 'd' || xch == 'D') {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
queuepoly(VBODY, shPBody, darkena(col, 1, 0xC0));
ShadowV(V, shPBody);
2016-01-02 10:09:13 +00:00
int acol = col;
if(xch == 'D') acol = 0xD0D0D0;
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, shDemon, darkena(acol, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(isMetalBeast(m)) {
2017-03-23 10:53:57 +00:00
ShadowV(V, shTrylobite);
if(!mmspatial)
queuepoly(VABODY, shTrylobite, darkena(col, 1, 0xC0));
else {
queuepoly(VABODY, shTrylobiteBody, darkena(col, 1, 0xFF));
animallegs(VALEGS, moMetalBeast, darkena(col, 1, 0xFF), footphase);
}
2016-08-26 09:58:03 +00:00
int acol = col;
2017-03-23 10:53:57 +00:00
queuepoly(VAHEAD, shTrylobiteHead, darkena(acol, 0, 0xFF));
2016-08-26 09:58:03 +00:00
}
2016-01-02 10:09:13 +00:00
else if(m == moEvilGolem) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 2, 0xC0), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(col, 0, 0XC0));
queuepoly(VHEAD, shGolemhead, darkena(col, 1, 0XFF));
2016-01-02 10:09:13 +00:00
}
else if(isWitch(m)) {
2017-03-23 10:53:57 +00:00
otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
2016-01-02 10:09:13 +00:00
int c = 0xFF;
if(m == moWitchGhost) c = 0x85 + 120 * sin(ticks / 160.0);
2017-03-23 10:53:57 +00:00
if(m == moWitchWinter) drawWinter(V, 0);
2016-01-02 10:09:13 +00:00
if(m == moWitchFlash) drawFlash(V);
if(m == moWitchSpeed) drawSpeed(V);
if(m == moWitchFire) col = firecolor(0);
2017-03-23 10:53:57 +00:00
ShadowV(V, shFemaleBody);
queuepoly(VBODY, shFemaleBody, darkena(col, 0, c));
2016-01-02 10:09:13 +00:00
// queuepoly(cV2, ct, shPSword, darkena(col, 0, 0XFF));
// queuepoly(V, shHood, darkena(col, 0, 0XC0));
if(m == moWitchFire) col = firecolor(100);
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, shWitchHair, darkena(col, 1, c));
2016-01-02 10:09:13 +00:00
if(m == moWitchFire) col = firecolor(200);
2017-03-23 10:53:57 +00:00
queuepoly(VHEAD, shPFace, darkena(col, 0, c));
2016-01-02 10:09:13 +00:00
if(m == moWitchFire) col = firecolor(300);
2017-03-23 10:53:57 +00:00
queuepoly(VBODY, shWitchDress, darkena(col, 1, 0XC0));
2016-01-02 10:09:13 +00:00
}
else return true;
return false;
}
2017-03-23 10:53:57 +00:00
bool drawMonsterTypeDH(eMonster m, cell *where, const transmatrix& V, int col, bool dh, ld footphase) {
2016-08-26 09:58:03 +00:00
if(dh) {
poly_outline = OUTLINE_DEAD;
darken++;
}
2017-03-23 10:53:57 +00:00
bool b = drawMonsterType(m,where,V,col, footphase);
2016-08-26 09:58:03 +00:00
if(dh) {
poly_outline = OUTLINE_NONE;
darken--;
}
return b;
}
2017-03-23 10:53:57 +00:00
transmatrix playerV;
bool applyAnimation(cell *c, transmatrix& V, double& footphase, int layer) {
if(!animations[layer].count(c)) return false;
animation& a = animations[layer][c];
int td = ticks - a.ltick;
ld aspd = td / 1000.0 * exp(vid.mspeed);
ld R = hdist0(tC0(a.wherenow));
aspd *= (1+R+(shmup::on?1:0));
if(R < aspd || std::isnan(R) || std::isnan(aspd) || R > 10) {
2017-03-23 10:53:57 +00:00
animations[layer].erase(c);
return false;
}
else {
a.wherenow = a.wherenow * rspintox(tC0(inverse(a.wherenow)));
a.wherenow = a.wherenow * xpush(aspd);
fixmatrix(a.wherenow);
a.footphase += aspd;
footphase = a.footphase;
V = V * a.wherenow;
a.ltick = ticks;
return true;
}
}
double chainAngle(cell *c, transmatrix& V, cell *c2, double dft) {
if(!gmatrix0.count(c2)) return dft;
hyperpoint h = C0;
if(animations[LAYER_BIG].count(c2)) h = animations[LAYER_BIG][c2].wherenow * h;
h = inverse(V) * gmatrix0[c2] * h;
return atan2(h[1], h[0]);
}
// equivalent to V = V * spin(-chainAngle(c,V,c2,dft));
bool chainAnimation(cell *c, transmatrix& V, cell *c2, int i, int b) {
if(!gmatrix0.count(c2)) {
V = V * ddspin(c,i,b);
return false;
}
hyperpoint h = C0;
if(animations[LAYER_BIG].count(c2)) h = animations[LAYER_BIG][c2].wherenow * h;
h = inverse(V) * gmatrix0[c2] * h;
V = V * rspintox(h);
return true;
}
// push down the queue after q-th element, `down` absolute units down,
// based on cell c and transmatrix V
// do change the zoom factor? do change the priorities?
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
int cellcolor(cell *c) {
if(isPlayerOn(c) || isFriendly(c)) return OUTLINE_FRIEND;
if(noHighlight(c->monst)) return OUTLINE_NONE;
if(c->monst) return OUTLINE_ENEMY;
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(c->wall == waMirror) return c->land == laMirror ? OUTLINE_TREASURE : OUTLINE_ORB;
if(c->item) {
int k = itemclass(c->item);
if(k == IC_TREASURE)
return OUTLINE_TREASURE;
else if(k == IC_ORB)
return OUTLINE_ORB;
else
return OUTLINE_OTHER;
}
return OUTLINE_NONE;
}
bool drawMonster(const transmatrix& Vparam, int ct, cell *c, int col) {
2016-08-26 09:58:03 +00:00
bool darkhistory = conformal::includeHistory && eq(c->aitmp, sval);
2016-01-02 10:09:13 +00:00
if(doHighlight())
poly_outline =
2017-03-23 10:53:57 +00:00
(isPlayerOn(c) || isFriendly(c)) ? OUTLINE_FRIEND :
noHighlight(c->monst) ? OUTLINE_NONE :
OUTLINE_ENEMY;
bool nospins = false, nospinb = false;
double footphaseb = 0, footphase = 0;
transmatrix Vs = Vparam; nospins = applyAnimation(c, Vs, footphase, LAYER_SMALL);
transmatrix Vb = Vparam; nospinb = applyAnimation(c, Vb, footphaseb, LAYER_BIG);
// nospin = true;
2016-01-02 10:09:13 +00:00
eMonster m = c->monst;
2017-03-23 10:53:57 +00:00
if(isIvy(c) || isWorm(c) || isMutantIvy(c) || c->monst == moFriendlyIvy) {
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
if(isDragon(c->monst) && c->stuntime == 0) col = 0xFF6000;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
transmatrix Vb0 = Vb;
2015-08-08 13:57:52 +00:00
if(c->mondir != NODIR) {
2017-03-23 10:53:57 +00:00
if(mmmon) {
if(nospinb)
chainAnimation(c, Vb, c->mov[c->mondir], c->mondir, 0);
else
Vb = Vb * ddspin(c, c->mondir);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
#ifndef NOEDIT
if(c == mapeditor::drawcell) mapeditor::drawtrans = Vb;
2016-01-02 10:09:13 +00:00
#endif
2017-03-23 10:53:57 +00:00
if(drawUserShape(Vb, 1, c->monst, (col << 8) + 0xFF)) return false;
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(isIvy(c) || isMutantIvy(c) || c->monst == moFriendlyIvy)
queuepoly(Vb, shIBranch, (col << 8) + 0xFF);
2015-08-08 13:57:52 +00:00
else if(c->monst < moTentacle) {
2017-03-23 10:53:57 +00:00
ShadowV(Vb, shTentacleX, PPR_GIANTSHADOW);
queuepoly(mmscale(Vb, geom3::ABODY), shTentacleX, 0xFF);
queuepoly(mmscale(Vb, geom3::ABODY), shTentacle, (col << 8) + 0xFF);
2015-08-08 13:57:52 +00:00
}
2016-08-26 09:58:03 +00:00
else if(c->monst == moDragonHead || c->monst == moDragonTail) {
char part = dragon::bodypart(c, dragon::findhead(c));
2017-03-23 10:53:57 +00:00
if(part != '2') {
queuepoly(mmscale(Vb, geom3::ABODY), shDragonSegment, darkena(col, 0, 0xFF));
ShadowV(Vb, shDragonSegment, PPR_GIANTSHADOW);
}
2016-08-26 09:58:03 +00:00
}
2015-08-08 13:57:52 +00:00
else {
2016-01-02 10:09:13 +00:00
if(c->monst == moTentacleGhost) {
2017-03-23 10:53:57 +00:00
hyperpoint V0 = conformal::on ? tC0(Vs) : inverse(cwtV) * tC0(Vs);
2016-01-02 10:09:13 +00:00
hyperpoint V1 = spintox(V0) * V0;
2017-03-23 10:53:57 +00:00
Vs = cwtV * rspintox(V0) * rpushxto0(V1) * pispin;
drawMonsterType(moGhost, c, Vs, darkena(col, 0, 0xFF), footphase);
2016-01-02 10:09:13 +00:00
col = minf[moTentacletail].color;
}
2017-03-23 10:53:57 +00:00
queuepoly(mmscale(Vb, geom3::ABODY), shTentacleX, 0xFFFFFFFF);
queuepoly(mmscale(Vb, geom3::ABODY), shTentacle, (col << 8) + 0xFF);
ShadowV(Vb, shTentacleX, PPR_GIANTSHADOW);
2015-08-08 13:57:52 +00:00
}
}
2017-03-23 10:53:57 +00:00
else {
int hdir = displaydir(c, c->mondir);
int col = darkena(0x606020, 0, 0xFF);
for(int u=-1; u<=1; u++)
queueline(Vparam*ddi0(hdir+S21, u*crossf/5), Vparam*ddi(hdir, crossf)*ddi0(hdir+S21, u*crossf/5), col, 2);
}
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
if(mmmon) {
if(isIvy(c) || isMutantIvy(c) || c->monst == moFriendlyIvy) {
queuepoly(mmscale(Vb, geom3::ABODY), shILeaf[K(c)], darkena(col, 0, 0xFF));
ShadowV(Vb, shILeaf[K(c)], PPR_GIANTSHADOW);
}
2016-01-02 10:09:13 +00:00
else if(m == moWorm || m == moWormwait || m == moHexSnake) {
2017-03-23 10:53:57 +00:00
Vb = Vb * pispin;
transmatrix Vbh = mmscale(Vb, geom3::AHEAD);
queuepoly(Vbh, shWormHead, darkena(col, 0, 0xFF));
queuepolyat(Vbh, shEyes, 0xFF, PPR_ONTENTACLE_EYES);
ShadowV(Vb, shWormHead, PPR_GIANTSHADOW);
2016-08-26 09:58:03 +00:00
}
else if(m == moDragonHead) {
2017-03-23 10:53:57 +00:00
transmatrix Vbh = mmscale(Vb, geom3::AHEAD);
ShadowV(Vb, shDragonHead, PPR_GIANTSHADOW);
queuepoly(Vbh, shDragonHead, darkena(col, c->hitpoints?0:1, 0xFF));
queuepolyat(Vbh/* * pispin */, shEyes, 0xFF, PPR_ONTENTACLE_EYES);
2016-08-26 09:58:03 +00:00
int noscolor = (c->hitpoints == 1 && c->stuntime ==1) ? 0xFF0000FF : 0xFF;
2017-03-23 10:53:57 +00:00
queuepoly(Vbh, shDragonNostril, noscolor);
queuepoly(Vbh * Mirror, shDragonNostril, noscolor);
}
else if(m == moTentacle || m == moTentaclewait || m == moTentacleEscaping) {
Vb = Vb * pispin;
transmatrix Vbh = mmscale(Vb, geom3::AHEAD);
queuepoly(Vbh, shTentHead, darkena(col, 0, 0xFF));
ShadowV(Vb, shTentHead, PPR_GIANTSHADOW);
2015-08-08 13:57:52 +00:00
}
2016-08-26 09:58:03 +00:00
else if(m == moDragonTail) {
cell *c2 = NULL;
for(int i=0; i<c->type; i++)
2017-03-23 10:53:57 +00:00
if(c->mov[i] && isDragon(c->mov[i]->monst) && c->mov[i]->mondir == c->spn(i))
2016-08-26 09:58:03 +00:00
c2 = c->mov[i];
int nd = neighborId(c, c2);
char part = dragon::bodypart(c, dragon::findhead(c));
if(part == 't') {
2017-03-23 10:53:57 +00:00
if(nospinb) {
chainAnimation(c, Vb, c2, nd, 0);
Vb = Vb * pispin;
}
else {
Vb = Vb0 * ddspin(c, nd, S42);
}
transmatrix Vbb = mmscale(Vb, geom3::ABODY);
queuepoly(Vbb, shDragonTail, darkena(col, c->hitpoints?0:1, 0xFF));
ShadowV(Vb, shDragonTail, PPR_GIANTSHADOW);
2016-08-26 09:58:03 +00:00
}
else if(true) {
2017-03-23 10:53:57 +00:00
if(nospinb) {
chainAnimation(c, Vb, c2, nd, 0);
Vb = Vb * pispin;
double ang = chainAngle(c, Vb, c->mov[c->mondir], (displaydir(c, c->mondir) - displaydir(c, nd)) * M_PI / S42);
ang /= 2;
Vb = Vb * spin(M_PI-ang);
}
else {
int hdir0 = displaydir(c, nd) + S42;
int hdir1 = displaydir(c, c->mondir);
while(hdir1 > hdir0 + S42) hdir1 -= S84;
while(hdir1 < hdir0 - S42) hdir1 += S84;
Vb = Vb0 * spin((hdir0 + hdir1)/2 * M_PI / S42 + M_PI);
}
transmatrix Vbb = mmscale(Vb, geom3::ABODY);
if(part == 'l' || part == '2') {
queuepoly(Vbb, shDragonLegs, darkena(col, c->hitpoints?0:1, 0xFF));
}
queuepoly(Vbb, shDragonWings, darkena(col, c->hitpoints?0:1, 0xFF));
2016-08-26 09:58:03 +00:00
}
}
2017-03-23 10:53:57 +00:00
else if(!(c->mondir == NODIR && (c->monst == moTentacletail || (c->monst == moWormtail && wormpos(c) < WORMLENGTH))))
queuepoly(Vb, shJoint, darkena(col, 0, 0xFF));
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
if(!mmmon) return true;
2015-08-08 13:57:52 +00:00
}
else if(isMimic(c)) {
2017-03-23 10:53:57 +00:00
if(!nospins)
Vs = Vs * ddspin(c, c->mondir, flipplayer ? S42 : 0);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(c->monst == moMirror) Vs = Vs * Mirror;
multi::cpid = c->hitpoints;
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(mmmon) {
drawMonsterType(c->monst, c, Vs, col, footphase);
drawPlayerEffects(Vs, c, false);
2016-08-26 09:58:03 +00:00
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(flipplayer) Vs = Vs * pispin;
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(!outofmap(mouseh) && !nospins) {
2016-01-02 10:09:13 +00:00
// transmatrix invxy = Id; invxy[0][0] = invxy[1][1] = -1;
2017-03-23 10:53:57 +00:00
hyperpoint P2 = Vs * inverse(cwtV) * mouseh;
queuechr(P2, 10, 'x', 0xFF00);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
return !mmmon;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else if(c->monst && !mmmon) return true;
2016-01-02 10:09:13 +00:00
// illusions face randomly
2015-08-08 13:57:52 +00:00
else if(c->monst == moIllusion) {
2017-03-23 10:53:57 +00:00
multi::cpid = 0;
drawMonsterType(c->monst, c, Vs, col, footphase);
drawPlayerEffects(Vs, c, false);
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
// wolves face the heat
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
else if(c->monst == moWolf && c->cpdist > 1) {
2017-03-23 10:53:57 +00:00
if(!nospins) {
int d = 0;
double bheat = -999;
for(int i=0; i<c->type; i++) if(c->mov[i] && HEAT(c->mov[i]) > bheat) {
bheat = HEAT(c->mov[i]);
d = i;
}
Vs = Vs * ddspin(c, d);
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
return drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase);
2016-08-26 09:58:03 +00:00
}
// golems, knights, and hyperbugs don't face the player (mondir-controlled)
// also whatever in the lineview mode
2017-03-23 10:53:57 +00:00
else if(isFriendly(c) || isBug(c) || (c->monst && conformal::on) || c->monst == moKrakenH || (isBull(c->monst) && c->mondir != NODIR) || c->monst == moButterfly) {
if(c->monst == moKrakenH) Vs = Vb, nospins = nospinb;
if(!nospins) Vs = Vs * ddspin(c, c->mondir, S42);
if(isFriendly(c)) drawPlayerEffects(Vs, c, false);
return drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase);
}
else if(c->monst == moKrakenT) {
if(c->hitpoints == 0) col = 0x404040;
if(nospinb) {
chainAnimation(c, Vb, c->mov[c->mondir], c->mondir, 0);
Vb = Vb * pispin;
}
else Vb = Vb * ddspin(c, c->mondir, S42);
if(c->type != 6) Vb = Vb * xpush(hexhexdist - hcrossf);
return drawMonsterTypeDH(m, c, Vb, col, darkhistory, footphase);
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(c->monst) {
2016-01-02 10:09:13 +00:00
// other monsters face the player
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(!nospins) {
hyperpoint V0 = inverse(cwtV) * tC0(Vs);
2015-08-08 13:57:52 +00:00
hyperpoint V1 = spintox(V0) * V0;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
Vs = cwtV * rspintox(V0) * rpushxto0(V1) * pispin;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
if(c->monst == moShadow)
multi::cpid = c->hitpoints;
return drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
for(int i=0; i<numplayers(); i++) if(c == playerpos(i) && !shmup::on && mapeditor::drawplayer) {
if(!nospins) {
Vs = playerV;
if(multi::players > 1 ? multi::flipped[i] : flipplayer) Vs = Vs * pispin;
}
shmup::cpid = i;
drawPlayerEffects(Vs, c, true);
if(!mmmon) return true;
if(isWorm(m)) {
ld depth = geom3::factor_to_lev(wormhead(c) == c ? geom3::AHEAD : geom3::ABODY);
footphase = 0;
int q = ptds.size();
drawMonsterType(moPlayer, c, Vs, col, footphase);
pushdown(c, q, Vs, -depth, true, false);
}
else if(mmmon)
drawMonsterType(moPlayer, c, Vs, col, footphase);
}
2015-08-08 13:57:52 +00:00
return false;
}
2016-01-02 10:09:13 +00:00
bool showPirateX;
cell *keycell, *pirateTreasureSeek, *pirateTreasureFound;
2017-03-23 10:53:57 +00:00
hyperpoint pirateCoords;
2016-01-02 10:09:13 +00:00
double downspin;
cell *straightDownSeek;
2015-08-08 13:57:52 +00:00
int keycelldist;
#define AURA 180
int aurac[AURA+1][4];
bool haveaura() {
return pmodel == mdDisk && !sphere && !euclid && vid.aurastr>0 &&
!svg::in && (inHighQual || vid.usingGL);
}
void clearaura() {
if(!haveaura()) return;
for(int a=0; a<AURA; a++) for(int b=0; b<4; b++)
aurac[a][b] = 0;
}
void addaura(const hyperpoint& h, int col, int fd) {
if(!haveaura()) return;
int r = int(2*AURA + atan2(h[0], h[1]) * AURA / 2 / M_PI) % AURA;
aurac[r][3] += ((128 * 128 / vid.aurastr) << (fd + darken));
aurac[r][0] += (col>>16)&255;
aurac[r][1] += (col>>8)&255;
aurac[r][2] += (col>>0)&255;
}
void sumaura(int v) {
int auc[AURA];
for(int t=0; t<AURA; t++) auc[t] = aurac[t][v];
int val = 0;
if(vid.aurasmoothen < 1) vid.aurasmoothen = 1;
if(vid.aurasmoothen > AURA) vid.aurasmoothen = AURA;
int SMO = vid.aurasmoothen;
for(int t=0; t<SMO; t++) val += auc[t];
for(int t=0; t<AURA; t++) {
int tt = (t + SMO/2) % AURA;
aurac[tt][v] = val;
val -= auc[t];
val += auc[(t+SMO) % AURA];
}
aurac[AURA][v] = aurac[0][v];
}
void drawaura() {
if(!haveaura()) return;
for(int v=0; v<4; v++) sumaura(v);
#ifndef NOSDL
if(!vid.usingGL) {
SDL_LockSurface(s);
for(int y=0; y<vid.yres; y++)
for(int x=0; x<vid.xres; x++) {
ld hx = (x * 1. - vid.xcenter) / vid.radius;
ld hy = (y * 1. - vid.ycenter) / vid.radius;
if(vid.camera_angle) camrotate(hx, hy);
ld fac = sqrt(hx*hx+hy*hy);
if(fac < 1) continue;
ld dd = log((fac - .99999) / .00001);
ld cmul = 1 - dd/10.;
if(cmul>1) cmul=1;
if(cmul<0) cmul=0;
ld alpha = AURA * atan2(hx,hy) / (2 * M_PI);
if(alpha<0) alpha += AURA;
if(alpha >= AURA) alpha -= AURA;
int rm = int(alpha);
double fr = alpha-rm;
if(rm<0 || rm >= AURA) continue;
int& p = qpixel(s, x, y);
for(int c=0; c<3; c++) {
double c1 = aurac[rm][2-c] / (aurac[rm][3]+.1);
double c2 = aurac[rm+1][2-c] / (aurac[rm+1][3]+.1);
const ld one = 1;
part(p, c) = int(255 * min(one, cmul * (c1 + fr * (c2-c1))));
}
}
SDL_UnlockSurface(s);
return;
}
#endif
#ifdef GL
setcameraangle(true);
glEnableClientState(GL_COLOR_ARRAY);
float coltab[4][4];
glColorPointer(4, GL_FLOAT, 0, coltab);
activateGlcoords();
float cx[AURA+1][11][5];
double facs[11] = {1, 1.01, 1.02, 1.04, 1.08, 1.70, 1.95, 1.5, 2, 6, 10};
double cmul[11] = {1, .8, .7, .6, .5, .16, .12, .08, .07, .06, 0};
double d2[11] = {0, 2, 4, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10};
for(int d=0; d<11; d++) {
double dd = d2[d];
cmul[d] = (1- dd/10.);
facs[d] = .99999 + .00001 * exp(dd);
}
facs[10] = 10;
for(int r=0; r<=AURA; r++) for(int z=0; z<11; z++) {
float rr = (M_PI * 2 * r) / AURA;
float rad = vid.radius * facs[z];
int rm = r % AURA;
cx[r][z][0] = rad * sin(rr);
cx[r][z][1] = rad * cos(rr);
cx[r][z][2] = cmul[z] * aurac[rm][0] / (aurac[rm][3]+.1);
cx[r][z][3] = cmul[z] * aurac[rm][1] / (aurac[rm][3]+.1);
cx[r][z][4] = cmul[z] * aurac[rm][2] / (aurac[rm][3]+.1);
}
for(int u=0; u<4; u++) glcoords[u][2] = vid.scrdist;
for(int u=0; u<4; u++) coltab[u][3] = 1;
for(int r=0; r<AURA; r++) for(int z=0;z<10;z++) {
for(int c=0; c<4; c++) {
int br = (c == 1 || c == 2) ? r+1 : r;
int bz = (c == 3 || c == 2) ? z+1 : z;
glcoords[c][0] = cx[br][bz][0];
glcoords[c][1] = cx[br][bz][1];
coltab[c][0] = cx[br][bz][2];
coltab[c][1] = cx[br][bz][3];
coltab[c][2] = cx[br][bz][4];
}
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
glDisableClientState(GL_COLOR_ARRAY);
setcameraangle(false);
#endif
}
2015-08-08 13:57:52 +00:00
void drawCircle(int x, int y, int size, int color) {
2017-03-23 10:53:57 +00:00
if(size < 0) size = -size;
2015-08-08 13:57:52 +00:00
#ifdef GL
if(vid.usingGL) {
qglcoords = 0;
2017-03-23 10:53:57 +00:00
glcolor2(color);
2015-08-08 13:57:52 +00:00
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<pts; r++) {
float rr = (M_PI * 2 * r) / pts;
glcoords[r][0] = x + size * sin(rr);
glcoords[r][1] = y + size * cos(rr);
glcoords[r][2] = vid.scrdist;
}
2017-03-23 10:53:57 +00:00
qglcoords = pts;
activateGlcoords();
2015-08-08 13:57:52 +00:00
glDrawArrays(GL_LINE_LOOP, 0, pts);
return;
}
#endif
#ifdef MOBILE
gdpush(4); gdpush(color); gdpush(x); gdpush(y); gdpush(size);
#else
#ifdef SDLGFX
2017-03-23 10:53:57 +00:00
(vid.usingAA?aacircleColor:circleColor) (s, x, y, size, color);
2015-08-08 13:57:52 +00:00
#else
#ifndef NOSDL
2015-08-08 13:57:52 +00:00
int pts = size * 4;
if(pts > 1500) pts = 1500;
for(int r=0; r<pts; r++)
qpixel(s, x + int(size * sin(r)), y + int(size * cos(r))) = color;
#endif
#endif
2015-08-08 13:57:52 +00:00
#endif
}
2016-01-02 10:09:13 +00:00
int fnt[100][7];
2015-08-08 13:57:52 +00:00
bool bugsNearby(cell *c, int dist = 2) {
2017-03-23 10:53:57 +00:00
if(!(havewhat&HF_BUG)) return false;
2015-08-08 13:57:52 +00:00
if(isBug(c)) return true;
if(dist) for(int t=0; t<c->type; t++) if(c->mov[t] && bugsNearby(c->mov[t], dist-1)) return true;
return false;
}
2016-01-02 10:09:13 +00:00
int minecolors[8] = {
2016-08-26 09:58:03 +00:00
0xFFFFFF, 0xF0, 0xF060, 0xF00000,
0x60, 0x600000, 0x00C0C0, 0x000000
};
int distcolors[8] = {
0xFFFFFF, 0xF0, 0xF060, 0xF00000,
0xA0A000, 0xA000A0, 0x00A0A0, 0xFFD500
2016-01-02 10:09:13 +00:00
};
const char* minetexts[8] = {
"No mines next to you.",
"A mine is next to you!",
"Two mines next to you!",
"Three mines next to you!",
"Four mines next to you!",
"Five mines next to you!",
"Six mines next to you!",
"Seven mines next to you!"
};
int countMinesAround(cell *c) {
int mines = 0;
for(int i=0; i<c->type; i++)
if(c->mov[i] && c->mov[i]->wall == waMineMine)
mines++;
return mines;
}
transmatrix applyPatterndir(cell *c, char patt = mapeditor::whichPattern) {
2017-03-23 10:53:57 +00:00
transmatrix V = ddspin(c, mapeditor::patterndir(c, patt), S42);
2016-08-26 09:58:03 +00:00
if(mapeditor::reflectPatternAt(c, patt))
2016-01-02 10:09:13 +00:00
return V * Mirror;
return V;
}
2016-08-26 09:58:03 +00:00
transmatrix applyDowndir(cell *c, cellfunction *cf) {
2017-03-23 10:53:57 +00:00
return ddspin(c, mapeditor::downdir(c, cf), S42);
2016-08-26 09:58:03 +00:00
}
void drawTowerFloor(const transmatrix& V, cell *c, int col, cellfunction *cf = coastvalEdge) {
int j = -1;
if(euclid) j = 10;
else if((*cf)(c) > 1) {
int i = towerval(c, cf);
if(i == 4) j = 0;
if(i == 5) j = 1;
if(i == 6) j = 2;
if(i == 8) j = 3;
if(i == 9) j = 4;
if(i == 10) j = 5;
if(i == 13) j = 6;
if(purehepta) {
if(i == 7) j = 7;
if(i == 11) j = 8;
if(i == 15) j = 9;
}
}
if(j >= 0)
2017-03-23 10:53:57 +00:00
qfloor(c, V, applyDowndir(c, cf), shTower[j], col);
2016-08-26 09:58:03 +00:00
else if(c->wall != waLadder)
2017-03-23 10:53:57 +00:00
qfloor(c, V, shMFloor[K(c)], col);
2016-08-26 09:58:03 +00:00
}
void drawZebraFloor(const transmatrix& V, cell *c, int col) {
2017-03-23 10:53:57 +00:00
if(euclid) { qfloor(c, V, shTower[10], col); return; }
2016-08-26 09:58:03 +00:00
int i = zebra40(c);
i &= ~3;
int j;
if(purehepta) j = 4;
else if(i >=4 && i < 16) j = 2;
else if(i >= 16 && i < 28) j = 1;
else if(i >= 28 && i < 40) j = 3;
else j = 0;
2017-03-23 10:53:57 +00:00
qfloor(c, V, applyPatterndir(c, 'z'), shZebra[j], col);
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
void qplainfloor(cell *c, bool warp, const transmatrix &V, int col);
void drawReptileFloor(const transmatrix& V, cell *c, int col, bool usefloor) {
int i = zebra40(c);
i &= ~3;
int j;
if(!wmescher) j = 4;
else if(purehepta) j = 0;
else if(i < 4) j = 0;
else if(i >=4 && i < 16) j = 1;
else if(i >= 16 && i < 28) j = 2;
else if(i >= 28 && i < 40) j = 3;
else j = 4;
transmatrix V2 = V * applyPatterndir(c, 'z');
if(wmescher) {
if(usefloor)
qfloor(c, V, applyPatterndir(c, 'z'), shReptile[j][0], darkena(col, 0, 0xFF));
else
queuepoly(V2, shReptile[j][0], darkena(col, 0, 0xFF));
}
else
qplainfloor(c, isWarped(c), V, darkena(col, 0, 0xFF));
if(usefloor && chasmg == 2) return;
int dcol = 0;
int ecol = -1;
if(isReptile(c->wall)) {
unsigned char wp = c->wparam;
if(wp == 1)
ecol = 0xFFFF00;
else if(wp <= 5)
ecol = 0xFF0000;
else
ecol = 0;
if(ecol) ecol = gradient(0, ecol, -1, sin(M_PI / 100 * ticks), 1);
}
if(ecol == -1 || ecol == 0) dcol = darkena(col, 1, 0xFF);
else dcol = darkena(ecol, 0, 0x80);
dynamicval<int> p(poly_outline,
doHighlight() && ecol != -1 && ecol != 0 ? OUTLINE_ENEMY : OUTLINE_NONE);
if(!chasmg) {
if(wmescher)
queuepoly(V2, shReptile[j][1], dcol);
else
queuepoly(V2, shMFloor[c->type!=6], dcol);
}
if(ecol != -1) {
queuepoly(V2, shReptile[j][2], (ecol << 8) + 0xFF);
queuepoly(V2, shReptile[j][3], (ecol << 8) + 0xFF);
}
}
#define ECT (euclid?2:ct6)
2016-08-26 09:58:03 +00:00
void drawEmeraldFloor(const transmatrix& V, cell *c, int col) {
int j = -1;
if(!euclid && !purehepta) {
int i = emeraldval(c) & ~3;
if(i == 8) j = 0;
else if(i == 12) j = 1;
else if(i == 16) j = 2;
else if(i == 20) j = 3;
else if(i == 28) j = 4;
else if(i == 36) j = 5;
}
if(j >= 0)
2017-03-23 10:53:57 +00:00
qfloor(c, V, applyPatterndir(c, 'f'), shEmeraldFloor[j], col);
2016-08-26 09:58:03 +00:00
else
2017-03-23 10:53:57 +00:00
qfloor(c, V, shCaveFloor[euclid?2:K(c)], col);
2016-08-26 09:58:03 +00:00
}
double fanframe;
void viewBuggyCells(cell *c, transmatrix V) {
for(int i=0; i<size(buggycells); i++)
if(c == buggycells[i]) {
queuepoly(V, shPirateX, 0xC000C080);
return;
}
for(int i=0; i<size(buggycells); i++) {
cell *c1 = buggycells[i];
cell *cf = cwt.c;
while(cf != c1) {
cf = pathTowards(cf, c1);
if(cf == c) {
queuepoly(V, shMineMark[1], 0xC000C0D0);
return;
}
}
}
}
2017-03-23 10:53:57 +00:00
transmatrix pushone() { return euclid ? eupush(1, 0) : xpush(sphere?.5 : 1); }
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
void drawMovementArrows(cell *c, transmatrix V) {
2016-08-26 09:58:03 +00:00
if(viewdists) return;
2017-03-23 10:53:57 +00:00
for(int d=0; d<8; d++) {
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
movedir md = vectodir(spin(-d * M_PI/4) * tC0(pushone()));
int u = md.d;
cellwalker xc = cwt; cwspin(xc, u); cwstep(xc);
if(xc.c == c) {
transmatrix fixrot = rgpushxto0(tC0(V));
// make it more transparent
int col = getcs().uicolor;
col -= (col & 0xFF) >> 1;
poly_outline = OUTLINE_NONE;
queuepoly(fixrot * spin(-d * M_PI/4 + (sphere && vid.alpha>1?M_PI:0))/* * eupush(1,0)*/, shArrow, col);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(c->type != 6 && (isStunnable(c->monst) || c->wall == waThumperOn)) {
transmatrix Centered = rgpushxto0(tC0(cwtV));
int sd = md.subdir;
if(sphere) sd = -sd;
queuepoly(inverse(Centered) * rgpushxto0(Centered * tC0(V)) * rspintox(Centered*tC0(V)) * spin(-sd * M_PI/S7) * xpush(0.2), shArrow, col);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else break;
2015-08-08 13:57:52 +00:00
}
}
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
transmatrix screenpos(ld x, ld y) {
transmatrix V = Id;
V[0][2] += (x - vid.xcenter) / vid.radius * (1+vid.alphax);
V[1][2] += (y - vid.ycenter) / vid.radius * (1+vid.alphax);
// V[2][0] -= (x - vid.xcenter) / vid.radius * (1+vid.alphax);
// V[2][1] -= (y - vid.ycenter) / vid.radius * (1+vid.alphax);
return V;
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
#define SKIPFAC .4
void drawMobileArrow(cell *c, transmatrix V) {
// int col = getcs().uicolor;
// col -= (col & 0xFF) >> 1;
2016-01-02 10:09:13 +00:00
2017-03-31 19:41:09 +00:00
int dir = neighborId(cwt.c, c);
bool invalid = !legalmoves[dir];
2017-03-23 10:53:57 +00:00
int col = cellcolor(c);
if(col == OUTLINE_NONE) col = 0xC0C0C0FF;
col -= (col & 0xFF) >> 1;
2017-03-31 19:41:09 +00:00
if(invalid) col -= (col & 0xFF) >> 1;
if(invalid) col -= (col & 0xFF) >> 1;
2017-03-23 10:53:57 +00:00
poly_outline = OUTLINE_NONE;
transmatrix m2 = Id;
ld scale = vid.mobilecompasssize / 15.;
m2[0][0] = scale; m2[1][1] = scale; m2[2][2] = 1;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
transmatrix Centered = rgpushxto0(tC0(cwtV));
transmatrix t = inverse(Centered) * V;
double alpha = atan2(tC0(t)[1], tC0(t)[0]);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
using namespace shmupballs;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
double dx = xmove + rad*(1+SKIPFAC-.2)/2 * cos(alpha);
double dy = yb + rad*(1+SKIPFAC-.2)/2 * sin(alpha);
queuepoly(
screenpos(dx, dy) * spin(-alpha) * m2, shArrow, col);
/*
if(c->type != 6 && (isStunnable(c->monst) || c->wall == waThumperOn)) {
transmatrix Centered = rgpushxto0(tC0(cwtV));
int sd = md.subdir;
if(sphere) sd = -sd;
queuepoly(inverse(Centered) * rgpushxto0(Centered * tC0(V)) * rspintox(Centered*tC0(V)) * spin(-sd * M_PI/S7) * xpush(0.2), shArrow, col);
}
else break;
} */
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int celldistAltPlus(cell *c) { return 1000000 + celldistAlt(c); }
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
bool drawstaratvec(double dx, double dy) {
return dx*dx+dy*dy > .05;
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int reptilecolor(cell *c) {
int i = zebra40(c);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(!euclid) {
if(i >= 4 && i < 16) i = 0;
else if(i >= 16 && i < 28) i = 1;
else if(i >= 28 && i < 40) i = 2;
else i = 3;
}
int fcoltab[4] = {0xe3bb97, 0xc2d1b0, 0xebe5cb, 0xA0A0A0};
return fcoltab[i];
}
ld wavefun(ld x) {
return sin(x);
/* x /= (2*M_PI);
x -= (int) x;
if(x > .5) return (x-.5) * 2;
else return 0; */
}
void setcolors(cell *c, int& wcol, int &fcol) {
2017-03-23 10:53:57 +00:00
wcol = fcol = winf[c->wall].color;
// floor colors for all the lands
if(c->land == laKraken) fcol = 0x20A020;
if(c->land == laBurial) fcol = linf[laBurial].color;
if(c->land == laTrollheim) fcol = linf[c->land].color;
if(c->land == laBarrier) fcol = linf[c->land].color;
if(c->land == laOceanWall) fcol = linf[c->land].color;
if(c->land == laAlchemist) {
fcol = 0x202020;
if(c->item && !(conformal::includeHistory && eq(c->aitmp, sval)))
fcol = wcol = iinf[c->item].color;
}
if(c->land == laBull)
fcol = 0x800080;
if(c->land == laCA)
fcol = 0x404040;
if(c->land == laReptile) {
fcol = reptilecolor(c);
}
if(c->land == laCrossroads) fcol = (vid.goteyes2 ? 0xFF3030 : 0xFF0000);
if(c->land == laCrossroads2) fcol = linf[laCrossroads2].color;
if(c->land == laCrossroads3) fcol = linf[laCrossroads3].color;
if(c->land == laCrossroads4) fcol = linf[laCrossroads4].color;
if(c->land == laCrossroads5) fcol = linf[laCrossroads5].color;
if(isElemental(c->land)) fcol = linf[c->land].color;
if(c->land == laDesert) fcol = 0xEDC9AF;
if(c->land == laCaves) fcol = 0x202020;
if(c->land == laEmerald) fcol = 0x202020;
if(c->land == laDeadCaves) fcol = 0x202020;
if(c->land == laJungle) fcol = (vid.goteyes2 ? 0x408040 : 0x008000);
if(c->land == laMountain) {
if(euclid || c->master->alt)
fcol = celldistAlt(c) & 1 ? 0x604020 : 0x302010;
else fcol = 0;
if(c->wall == waPlatform) wcol = 0xF0F0A0;
}
if(c->land == laWineyard) fcol = 0x006000;
if(c->land == laMirror) fcol = 0x808080;
if(c->land == laMotion) fcol = 0xF0F000;
if(c->land == laGraveyard) fcol = 0x107010;
if(c->land == laDryForest) fcol = gradient(0x008000, 0x800000, 0, c->landparam, 10);
if(c->land == laRlyeh) fcol = (vid.goteyes2 ? 0x4080C0 : 0x004080);
if(c->land == laPower) fcol = linf[c->land].color;
if(c->land == laHell) fcol = (vid.goteyes2 ? 0xC03030 : 0xC00000);
if(c->land == laLivefjord) fcol = 0x306030;
if(c->land == laWildWest) fcol = linf[c->land].color;
if(c->land == laHalloween) fcol = linf[c->land].color;
if(c->land == laMinefield) fcol = 0x80A080;
if(c->land == laCaribbean) fcol = 0x006000;
if(c->land == laRose) fcol = linf[c->land].color;
if(c->land == laCanvas) fcol = c->landparam;
if(c->land == laRedRock) fcol = linf[c->land].color;
if(c->land == laDragon) fcol = linf[c->land].color;
if(c->land == laStorms) fcol = linf[c->land].color;
if(c->land == laPalace) {
fcol = 0x806020;
if(c->wall == waClosedGate || c->wall == waOpenGate)
fcol = wcol;
}
if(c->land == laElementalWall)
fcol = (linf[c->barleft].color>>1) + (linf[c->barright].color>>1);
if(c->land == laZebra) {
fcol = 0xE0E0E0;
if(c->wall == waTrapdoor) fcol = 0x808080;
}
if(c->land == laCaribbean && (c->wall == waCIsland || c->wall == waCIsland2))
fcol = wcol = winf[c->wall].color;
if(isHive(c->land)) {
fcol = linf[c->land].color;
if(c->wall == waWaxWall) wcol = c->landparam;
if(items[itOrbInvis] && c->wall == waNone && c->landparam)
fcol = gradient(fcol, 0xFF0000, 0, c->landparam, 100);
if(c->bardir == NOBARRIERS && c->barleft)
fcol = minf[moBug0+c->barright].color;
}
if(isWarped(c->land)) {
fcol = pseudohept(c) ? 0x80C080 : 0xA06020;
if(c->wall == waSmallTree) wcol = 0x608000;
}
if(c->land == laTortoise) {
fcol = tortoise::getMatchColor(getBits(c));
if(c->wall == waBigTree) wcol = 0x709000;
else if(c->wall == waSmallTree) wcol = 0x905000;
}
if(c->land == laOvergrown || c->land == laClearing) {
fcol = (c->land == laOvergrown/* || (celldistAlt(c)&1)*/) ? 0x00C020 : 0x60E080;
if(c->wall == waSmallTree) wcol = 0x008060;
else if(c->wall == waBigTree) wcol = 0x0080C0;
}
if(c->land == laTemple) {
int d = showoff ? 0 : (euclid||c->master->alt) ? celldistAlt(c) : 99;
if(chaosmode)
fcol = 0x405090;
else if(d % TEMPLE_EACH == 0)
fcol = gradient(0x304080, winf[waColumn].color, 0, 0.5, 1);
// else if(c->type == 7)
// wcol = 0x707070;
else if(d% 2 == -1)
fcol = 0x304080;
else
fcol = 0x405090;
}
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(isHaunted(c->land)) {
int itcolor = 0;
for(int i=0; i<c->type; i++) if(c->mov[i] && c->mov[i]->item)
itcolor = 1;
if(c->item) itcolor |= 2;
fcol = 0x609F60 + 0x202020 * itcolor;
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
forCellEx(c2, c) if(c2->monst == moFriendlyGhost)
fcol = gradient(fcol, fghostcolor(ticks, c2), 0, .25, 1);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(c->monst == moFriendlyGhost)
fcol = gradient(fcol, fghostcolor(ticks, c), 0, .5, 1);
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(c->wall == waSmallTree) wcol = 0x004000;
else if(c->wall == waBigTree) wcol = 0x008000;
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(c->land == laCamelot) {
int d = showoff ? 0 : ((euclid||c->master->alt) ? celldistAltRelative(c) : 0);
2017-04-08 15:18:29 +00:00
#ifdef TOUR
if(!tour::on) camelotcheat = false;
if(camelotcheat)
fcol = (d&1) ? 0xC0C0C0 : 0x606060;
else
#endif
if(d < 0) {
2017-03-23 10:53:57 +00:00
fcol = 0xA0A0A0;
2017-04-08 15:18:29 +00:00
}
2017-03-23 10:53:57 +00:00
else {
// a nice floor pattern
int v = emeraldval(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)
fcol = 0xC0C0C0;
else
fcol = 0xA0A0A0;
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(c->land == laPrairie) {
/* if(isWateryOrBoat(c)) {
if(prairie::isriver(c))
fcol = ((c->LHU.fi.rval & 1) ? 0x000090 : 0x0000E0)
+ int(16 * wavefun(ticks / 200. + (c->wparam)*1.5))
+ ((prairie::next(c) ? 0 : 0xC00000));
else
fcol = 0x000080;
} */
if(prairie::isriver(c)) {
fcol = ((c->LHU.fi.rval & 1) ? 0x402000: 0x503000);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else {
fcol = 0x004000 + 0x001000 * c->LHU.fi.walldist;
fcol += 0x10000 * (255 - 511 / (1 + max((int) c->LHU.fi.flowerdist, 1)));
// fcol += 0x1 * (511 / (1 + max((int) c->LHU.fi.walldist2, 1)));
}
}
else if(isIcyLand(c) && isIcyWall(c)) {
float h = HEAT(c);
bool showcoc = c->land == laCocytus && chaosmode && !wmescher;
if(h < -0.4)
wcol = gradient(showcoc ? 0x4080FF : 0x4040FF, 0x0000FF, -0.4, h, -1);
else if(h < 0)
wcol = gradient(showcoc ? 0x80C0FF : 0x8080FF, showcoc ? 0x4080FF : 0x4040FF, 0, h, -0.4);
else if(h < 0.2)
wcol = gradient(showcoc ? 0x80C0FF : 0x8080FF, 0xFFFFFF, 0, h, 0.2);
// else if(h < 0.4)
// wcol = gradient(0xFFFFFF, 0xFFFF00, 0.2, h, 0.4);
else if(h < 0.6)
wcol = gradient(0xFFFFFF, 0xFF0000, 0.2, h, 0.6);
else if(h < 0.8)
wcol = gradient(0xFF0000, 0xFFFF00, 0.6, h, 0.8);
else
wcol = 0xFFFF00;
if(c->wall == waFrozenLake)
fcol = wcol;
else
fcol = (wcol & 0xFEFEFE) >> 1;
if(c->wall == waLake)
fcol = wcol = (wcol & 0xFCFCFC) >> 2;
}
else if(isWateryOrBoat(c) || c->wall == waReptileBridge) {
if(c->land == laOcean)
fcol = (c->landparam > 25 && !chaosmode) ? 0x000090 :
0x1010C0 + int(32 * sin(ticks / 500. + (chaosmode ? c->CHAOSPARAM : c->landparam)*1.5));
else if(c->land == laOceanWall)
fcol = 0x2020FF;
else if(c->land == laKraken) {
fcol = 0x0000A0;
int mafcol = (pseudohept(c) ? 64 : 8);
/* bool nearshore = false;
for(int i=0; i<c->type; i++)
if(c->mov[i]->wall != waSea && c->mov[i]->wall != waBoat)
nearshore = true;
if(nearshore) mafcol += 30; */
fcol = fcol + mafcol * (4+sin(ticks / 500. + ((euclid||c->master->alt) ? celldistAlt(c) : 0)*1.5))/5;
}
else if(c->land == laAlchemist)
fcol = 0x900090;
else if(c->land == laWhirlpool)
fcol = 0x0000C0 + int(32 * sin(ticks / 200. + ((euclid||c->master->alt) ? celldistAlt(c) : 0)*1.5));
else if(c->land == laLivefjord)
fcol = 0x000080;
else if(isWarped(c->land))
fcol = 0x0000C0 + int((pseudohept(c)?30:-30) * sin(ticks / 600.));
else
fcol = wcol;
}
else if(c->land == laOcean) {
if(chaosmode)
fcol = gradient(0xD0A090, 0xD0D020, 0, c->CHAOSPARAM, 30);
else
fcol = gradient(0xD0D090, 0xD0D020, -1, sin((double) c->landparam), 1);
}
if(c->land == laEmerald) {
if(c->wall == waCavefloor || c->wall == waCavewall) {
fcol = wcol = gradient(winf[waCavefloor].color, 0xFF00, 0, 0.5, 1);
if(c->wall == waCavewall) wcol = 0xC0FFC0;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
}
if(c->land == laWhirlwind) {
int wcol[4] = {0x404040, 0x404080, 0x2050A0, 0x5050C0};
fcol = wcol[whirlwind::fzebra3(c)];
}
if(c->land == laIvoryTower)
fcol = 0x10101 * (32 + (c->landparam&1) * 32) - 0x000010;
if(c->land == laDungeon) {
int lp = c->landparam % 5;
// xcol = (c->landparam&1) ? 0xD00000 : 0x00D000;
int lps[5] = { 0x402000, 0x302000, 0x202000, 0x282000, 0x382000 };
fcol = lps[lp];
if(c->wall == waClosedGate)
fcol = wcol = 0xC0C0C0;
if(c->wall == waOpenGate)
fcol = wcol = 0x404040;
if(c->wall == waPlatform)
fcol = wcol = 0xDFB520;
}
if(c->land == laEndorian) {
int clev = cwt.c->land == laEndorian ? edgeDepth(cwt.c) : 0;
// xcol = (c->landparam&1) ? 0xD00000 : 0x00D000;
fcol = 0x10101 * (32 + (c->landparam&1) * 32) - 0x000010;
fcol = gradient(fcol, 0x0000D0, clev-10, edgeDepth(c), clev+10);
if(c->wall == waTrunk) fcol = winf[waTrunk].color;
if(c->wall == waCanopy || c->wall == waSolidBranch || c->wall == waWeakBranch) {
fcol = winf[waCanopy].color;
if(c->landparam & 1) fcol = gradient(0, fcol, 0, .75, 1);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
}
// floors become fcol
if(c->wall == waSulphur || c->wall == waSulphurC || isAlch(c) || c->wall == waPlatform)
fcol = wcol;
if(c->wall == waDeadTroll2 || c->wall == waPetrified) {
eMonster m = eMonster(c->wparam);
if(c->wall == waPetrified)
wcol = gradient(wcol, minf[m].color, 0, .2, 1);
if(c->wall == waPetrified || isTroll(m)) if(!(m == moForestTroll && c->land == laOvergrown))
wcol = gradient(wcol, minf[m].color, 0, .4, 1);
}
if(c->land == laNone && c->wall == waNone)
wcol = fcol = 0x101010;
if(isFire(c))
fcol = wcol = c->wall == waEternalFire ? weakfirecolor(1500) : firecolor(100);
if(c->wall == waBoat && wmascii) {
wcol = 0xC06000;
}
if(mightBeMine(c) || c->wall == waMineOpen) {
fcol = wcol;
if(wmblack || wmascii) fcol >>= 1, wcol >>= 1;
}
if(c->wall == waAncientGrave || c->wall == waFreshGrave || c->wall == waThumperOn || c->wall == waThumperOff || c->wall == waBonfireOff)
fcol = wcol;
if(c->land == laMinefield && c->wall == waMineMine && (cmode == emMapEditor || !canmove))
fcol = wcol = 0xFF4040;
if(mightBeMine(c) && mineMarkedSafe(c))
fcol = wcol = gradient(wcol, 0x40FF40, 0, 0.2, 1);
if(mightBeMine(c) && mineMarked(c))
fcol = wcol = gradient(wcol, 0xFF4040, -1, sin(ticks/100.0), 1);
int rd = rosedist(c);
if(rd == 1)
wcol = gradient(0x804060, wcol, 0,1,3),
fcol = gradient(0x804060, fcol, 0,1,3);
if(rd == 2)
wcol = gradient(0x804060, wcol, 0,2,3),
fcol = gradient(0x804060, fcol, 0,2,3);
if(items[itRevolver] && c->pathdist > GUNRANGE && !shmup::on)
fcol = gradient(fcol, 0, 0, 25, 100),
wcol = gradient(wcol, 0, 0, 25, 100);
if(c->wall == waDeadfloor || c->wall == waCavefloor) fcol = wcol;
if(c->wall == waDeadwall) fcol = winf[waDeadfloor].color;
if(c->wall == waCavewall && c->land != laEmerald) fcol = winf[waCavefloor].color;
if(highwall(c) && !wmspatial)
fcol = wcol;
if(wmascii && (c->wall == waNone || isWatery(c))) wcol = fcol;
if(c->wall == waNone && c->land == laHive) wcol = fcol;
if(!wmspatial && snakelevel(c) && !realred(c->wall)) fcol = wcol;
if(c->wall == waGlass && !wmspatial) fcol = wcol;
if(c->wall == waRoundTable) fcol = wcol;
}
bool noAdjacentChasms(cell *c) {
forCellEx(c2, c) if(c2->wall == waChasm) return false;
return true;
}
// -1 if away, 0 if not away
int away(const transmatrix& V2) {
return intval(C0, V2 * xpush0(1)) > intval(C0, tC0(V2));
}
void floorShadow(cell *c, const transmatrix& V, int col, bool warp) {
if(pmodel == mdHyperboloid || pmodel == mdBall)
return; // shadows break the depth testing
if(shmup::on || purehepta) warp = false;
dynamicval<int> p(poly_outline, OUTLINE_TRANS);
if(wmescher && qfi.special) {
queuepolyat(V * qfi.spin * shadowmulmatrix, *qfi.shape, col, PPR_WALLSHADOW);
}
else if(warp) {
if(euclid) {
if(ishex1(c))
queuepolyat(V * pispin * applyPatterndir(c), shTriheptaEucShadow[0], col, PPR_WALLSHADOW);
else
queuepolyat(V * applyPatterndir(c), shTriheptaEucShadow[ishept(c)?1:0], col, PPR_WALLSHADOW);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else
queuepolyat(V * applyPatterndir(c), shTriheptaFloorShadow[ishept(c)?1:0], col, PPR_WALLSHADOW);
}
else {
queuepolyat(V, shFloorShadow[c->type==6?0:1], col, PPR_WALLSHADOW);
}
}
void plainfloor(cell *c, bool warp, const transmatrix &V, int col, int prio) {
if(warp) {
if(euclid) {
if(ishex1(c))
queuepolyat(V * pispin * applyPatterndir(c), shTriheptaEuc[0], col, prio);
2015-08-08 13:57:52 +00:00
else
2017-03-23 10:53:57 +00:00
queuepolyat(V * applyPatterndir(c), shTriheptaEuc[ishept(c)?1:0], col, prio);
}
else
queuepolyat(V * applyPatterndir(c), shTriheptaFloor[sphere ? 6-c->type : mapeditor::nopattern(c)], col, prio);
}
else {
queuepolyat(V, shFloor[c->type==6?0:1], col, prio);
}
}
void qplainfloor(cell *c, bool warp, const transmatrix &V, int col) {
if(warp) {
if(euclid) {
if(ishex1(c))
qfloor(c, V, pispin * applyPatterndir(c), shTriheptaEuc[0], col);
2015-08-08 13:57:52 +00:00
else
2017-03-23 10:53:57 +00:00
qfloor(c, V, applyPatterndir(c), shTriheptaEuc[ishept(c)?1:0], col);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else
qfloor(c, V, applyPatterndir(c), shTriheptaFloor[sphere ? 6-c->type : mapeditor::nopattern(c)], col);
}
else {
qfloor(c, V, shFloor[c->type==6?0:1], col);
}
}
void warpfloor(cell *c, const transmatrix& V, int col, int prio, bool warp) {
if(shmup::on || purehepta) warp = false;
if(wmescher && qfi.special)
queuepolyat(V*qfi.spin, *qfi.shape, col, prio);
else plainfloor(c, warp, V, col, prio);
}
#define placeSidewallX(a,b,c,d,e,f,g) \
{ if((wmescher && qfi.special) || !validsidepar[c]) { \
escherSidewall(a,c,d,g); break; } \
else placeSidewall(a,b,c,d,e,f,g); }
#define placeSidewallXB(a,b,c,d,e,f,g, Break) \
{ if((wmescher && qfi.shape) || !validsidepar[c]) { \
escherSidewall(a,c,d,g); Break; break; } \
else placeSidewall(a,b,c,d,e,f,g); }
/* double zgrad(double f1, double f2, int nom, int den) {
using namespace geom3;
ld fo1 = factor_to_lev(f1);
ld fo2 = factor_to_lev(f2);
return lev_to_factor(fo1 + (fo2-fo1) * nom / den);
} */
double zgrad0(double l1, double l2, int nom, int den) {
using namespace geom3;
return lev_to_factor(l1 + (l2-l1) * nom / den);
}
void escherSidewall(cell *c, int sidepar, const transmatrix& V, int col) {
if(sidepar >= SIDE_SLEV && sidepar <= SIDE_SLEV+2) {
int sl = sidepar - SIDE_SLEV;
for(int z=1; z<=4; z++) if(z == 1 || (z == 4 && detaillevel == 2))
warpfloor(c, mscale(V, zgrad0(geom3::slev * sl, geom3::slev * (sl+1), z, 4)), col, PPR_REDWALL-4+z+4*sl, false);
}
else if(sidepar == SIDE_WALL) {
const int layers = 2 << detaillevel;
for(int z=1; z<layers; z++)
warpfloor(c, mscale(V, zgrad0(0, geom3::wall_height, z, layers)), col, PPR_WALL3+z-layers, false);
}
else if(sidepar == SIDE_LAKE) {
const int layers = 1 << (detaillevel-1);
if(detaillevel) for(int z=0; z<layers; z++)
warpfloor(c, mscale(V, zgrad0(-geom3::lake_top, 0, z, layers)), col, PPR_FLOOR+z-layers, false);
}
else if(sidepar == SIDE_LTOB) {
const int layers = 1 << (detaillevel-1);
if(detaillevel) for(int z=0; z<layers; z++)
warpfloor(c, mscale(V, zgrad0(-geom3::lake_bottom, -geom3::lake_top, z, layers)), col, PPR_INLAKEWALL+z-layers, false);
}
else if(sidepar == SIDE_BTOI) {
const int layers = 1 << detaillevel;
warpfloor(c, mscale(V, geom3::INFDEEP), col, PPR_MINUSINF, false);
for(int z=1; z<layers; z++)
warpfloor(c, mscale(V, zgrad0(-geom3::lake_bottom, -geom3::lake_top, -z, 1)), col, PPR_LAKEBOTTOM+z-layers, false);
}
}
void placeSidewall(cell *c, int i, int sidepar, const transmatrix& V, bool warp, bool mirr, int col) {
if(shmup::on || purehepta) warp = false;
if(warp && !ishept(c) && (!c->mov[i] || !ishept(c->mov[i]))) return;
int prio;
if(mirr) prio = PPR_GLASS - 2;
else if(sidepar == SIDE_WALL) prio = PPR_WALL3 - 2;
else if(sidepar == SIDE_WTS3) prio = PPR_WALL3 - 2;
else if(sidepar == SIDE_LAKE) prio = PPR_LAKEWALL;
else if(sidepar == SIDE_LTOB) prio = PPR_INLAKEWALL;
else if(sidepar == SIDE_BTOI) prio = PPR_BELOWBOTTOM;
else prio = PPR_REDWALL-2+4*(sidepar-SIDE_SLEV);
transmatrix V2 = V * ddspin(c, i);
int aw = away(V2); prio += aw;
if(!detaillevel && aw < 0) return;
// prio += c->cpdist - c->mov[i]->cpdist;
queuepolyat(V2,
(mirr?shMFloorSide:warp?shTriheptaSide:shFloorSide)[sidepar][c->type==6?0:1], col, prio);
}
bool openorsafe(cell *c) {
return c->wall == waMineOpen || mineMarkedSafe(c);
}
#define Dark(x) darkena(x,0,0xFF)
int gridcolor(cell *c1, cell *c2) {
if(cmode == emDraw) return Dark(0xFFFFFF);
if(!c2)
return 0x202020 >> darken;
int rd1 = rosedist(c1), rd2 = rosedist(c2);
if(rd1 != rd2) {
int r = rd1+rd2;
if(r == 1) return Dark(0x802020);
if(r == 3) return Dark(0xC02020);
if(r == 2) return Dark(0xF02020);
}
if(chasmgraph(c1) != chasmgraph(c2))
return Dark(0x808080);
if(c1->land == laAlchemist && c2->land == laAlchemist && c1->wall != c2->wall)
return Dark(0xC020C0);
if((c1->land == laWhirlpool || c2->land == laWhirlpool) && (celldistAlt(c1) != celldistAlt(c2)))
return Dark(0x2020A0);
if(c1->land == laMinefield && c2->land == laMinefield && (openorsafe(c1) != openorsafe(c2)))
return Dark(0xA0A0A0);
return Dark(0x202020);
}
void pushdown(cell *c, int& q, const transmatrix &V, double down, bool rezoom, bool repriority) {
// since we might be changing priorities, we have to make sure that we are sorting correctly
if(down > 0 && repriority) {
int qq = q+1;
while(qq < size(ptds))
if(qq > q && ptds[qq].prio < ptds[qq-1].prio) {
swap(ptds[qq], ptds[qq-1]);
qq--;
}
else qq++;
}
while(q < size(ptds)) {
polytodraw& ptd = ptds[q++];
if(ptd.kind == pkPoly) {
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
double z2;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
double z = zlevel(tC0(ptd.u.poly.V));
double lev = geom3::factor_to_lev(z);
double nlev = lev - down;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
double xyscale = rezoom ? geom3::scale_at_lev(lev) / geom3::scale_at_lev(nlev) : 1;
z2 = geom3::lev_to_factor(nlev);
double zscale = z2 / z;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
// xyscale = xyscale + (zscale-xyscale) * (1+sin(ticks / 1000.0)) / 2;
ptd.u.poly.V = xyzscale( V, xyscale*zscale, zscale)
* inverse(V) * ptd.u.poly.V;
if(!repriority) ;
else if(nlev < -geom3::lake_bottom-1e-3) {
ptd.prio = PPR_BELOWBOTTOM;
if(c->wall != waChasm)
ptd.col = 0; // disappear!
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else if(nlev < -geom3::lake_top-1e-3)
ptd.prio = PPR_INLAKEWALL;
else if(nlev < 0)
ptd.prio = PPR_LAKEWALL;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
}
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
bool dodrawcell(cell *c) {
// todo: fix when scrolling
if(!buggyGeneration && c->land != laCanvas && sightrange < 10) {
// not yet created
if(c->mpdist > 7 && !cheater) return false;
// in the Yendor Challenge, scrolling back is forbidden
if(c->cpdist > 7 && (yendor::on && !cheater)) return false;
// (incorrect comment) too far, no bugs nearby
if(playermoved && sightrange <= 7 && c->cpdist > sightrange) return false;
}
return true;
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
// 1 : (floor, water); 2 : (water, bottom); 4 : (bottom, inf)
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
int shallow(cell *c) {
if(cellUnstable(c)) return 0;
else if(
c->wall == waReptile) return 1;
else if(c->wall == waReptileBridge ||
c->wall == waGargoyleFloor ||
c->wall == waGargoyleBridge ||
c->wall == waTempFloor ||
c->wall == waTempBridge ||
c->wall == waFrozenLake)
return 5;
return 7;
}
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
bool viewdists = false;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
bool allemptynear(cell *c) {
if(c->wall) return false;
forCellEx(c2, c) if(c2->wall) return false;
return true;
}
2016-08-26 09:58:03 +00:00
bool behindsphere(const transmatrix& V) {
if(!sphere) return false;
if(vid.alpha > 1) {
if(V[2][2] > -1/vid.alpha) return true;
}
if(vid.alpha <= 1) {
if(V[2][2] < -.8) return true;
}
return false;
}
2017-03-23 10:53:57 +00:00
void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
qfi.shape = NULL; qfi.special = false;
ivoryz = isGravityLand(c->land);
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
transmatrix& gm = gmatrix[c];
bool orig = (gm[2][2] == 0 || fabs(gm[2][2]-1) >= fabs(V[2][2]-1) - 1e-8);
2017-03-23 10:53:57 +00:00
if(orig) gm = V;
2016-01-02 10:09:13 +00:00
if(behindsphere(V)) return;
2017-03-23 10:53:57 +00:00
ld dist0 = hdist0(tC0(V)) - 1e-6;
if(dist0 < geom3::highdetail) detaillevel = 2;
else if(dist0 < geom3::middetail) detaillevel = 1;
else detaillevel = 0;
#ifdef BUILDZEBRA
if(c->type == 6 && c->tmp > 0) {
int i = c->tmp;
zebra(cellwalker(c, i&15), 1, i>>4, "", 0);
}
c->item = eItem(c->heat / 4);
buildAutomatonRule(c);
#endif
viewBuggyCells(c,V);
if(conformal::on || inHighQual) checkTide(c);
// save the player's view center
if(isPlayerOn(c) && !shmup::on) {
playerfound = true;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
/* if(euclid)
return d * S84 / c->type;
else
return S42 - d * S84 / c->type;
cwtV = V * spin(-cwt.spin * 2*M_PI/c->type) * pispin; */
if(multi::players > 1) {
for(int i=0; i<numplayers(); i++)
if(playerpos(i) == c) {
playerV = V * ddspin(c, multi::player[i].spin);
if(multi::player[i].mirrored) playerV = playerV * Mirror;
if(multi::player[i].mirrored == mirrored)
multi::whereis[i] = playerV;
}
}
else {
playerV = V * ddspin(c, cwt.spin);
// playerV = V * spin(displaydir(c, cwt.spin) * M_PI/S42);
if(cwt.mirrored) playerV = playerV * Mirror;
if(orig) cwtV = playerV;
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
}
/* if(cwt.c->land == laEdge) {
if(c == chosenDown(cwt.c, 1, 0))
playerfoundL = c, cwtVL = V;
if(c == chosenDown(cwt.c, -1, 0))
playerfoundR = c, cwtVR = V;
} */
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(1) {
hyperpoint VC0 = tC0(V);
if(intval(mouseh, VC0) < modist) {
modist2 = modist; mouseover2 = mouseover;
modist = intval(mouseh, VC0);
mouseover = c;
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
else if(intval(mouseh, VC0) < modist2) {
modist2 = intval(mouseh, VC0);
mouseover2 = c;
}
double dfc = euclid ? intval(VC0, C0) : VC0[2];
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(dfc < centdist) {
centdist = dfc;
centerover = c;
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
int orbrange = (items[itRevolver] ? 3 : 2);
if(c->cpdist <= orbrange) if(multi::players > 1 || multi::alwaysuse)
for(int i=0; i<multi::players; i++) if(multi::playerActive(i)) {
double dfc = intval(VC0, tC0(multi::crosscenter[i]));
if(dfc < multi::ccdist[i] && celldistance(playerpos(i), c) <= orbrange) {
multi::ccdist[i] = dfc;
multi::ccat[i] = c;
}
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
// int col = 0xFFFFFF - 0x20 * c->maxdist - 0x2000 * c->cpdist;
if(!buggyGeneration && c->mpdist > 8 && !cheater) return; // not yet generated
if(c->land == laNone && cmode == emMapEditor) {
queuepoly(V, shTriangle, 0xFF0000FF);
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
char ch = winf[c->wall].glyph;
int wcol, fcol, asciicol;
setcolors(c, wcol, fcol);
// addaura(tC0(V), wcol);
int zcol = fcol;
if(viewdists) {
int cd = celldistance(c, cwt.c);
string label = its(cd);
// string label = its(fieldpattern::getriverdistleft(c)) + its(fieldpattern::getriverdistright(c));
int dc = distcolors[cd&7];
wcol = gradient(wcol, dc, 0, .4, 1);
fcol = gradient(fcol, dc, 0, .4, 1);
/* queuepolyat(V, shFloor[ct6], darkena(gradient(0, distcolors[cd&7], 0, .25, 1), fd, 0xC0),
PPR_TEXT); */
queuestr(V, (cd > 9 ? .6 : 1) * .2, label, 0xFF000000 + distcolors[cd&7], 1);
}
2017-03-23 10:53:57 +00:00
asciicol = wcol;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(c->land == laNone && c->wall == waNone)
queuepoly(V, shTriangle, 0xFFFF0000);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(c->wall == waThumperOn) {
int ds = ticks;
for(int u=0; u<5; u++) {
ld rad = hexf * (.3 * u + (ds%1000) * .0003);
int tcol = darkena(gradient(0xFFFFFF, 0, 0, rad, 1.5 * hexf), 0, 0xFF);
for(int a=0; a<S84; a++)
queueline(V*ddi0(a, rad), V*ddi0(a+1, rad), tcol, 0);
}
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
// bool dothept = false;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
/* if(pseudohept(c) && vid.darkhepta) {
col = gradient(0, col, 0, 0.75, 1);
} */
2016-01-02 10:09:13 +00:00
eItem it = c->item;
bool hidden = itemHidden(c);
bool hiddens = itemHiddenFromSight(c);
2016-08-26 09:58:03 +00:00
if(conformal::includeHistory && eq(c->aitmp, sval)) {
hidden = true;
hiddens = false;
}
2016-01-02 10:09:13 +00:00
if(hiddens && cmode != emMapEditor)
it = itNone;
2017-03-23 10:53:57 +00:00
int icol = 0, moncol = 0xFF00FF;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(it)
ch = iinf[it].glyph, asciicol = icol = iinf[it].color;
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
if(c->monst) {
2017-03-23 10:53:57 +00:00
ch = minf[c->monst].glyph, moncol = minf[c->monst].color;
2016-08-26 09:58:03 +00:00
if(c->monst == moMutant) {
// root coloring
if(c->stuntime != mutantphase)
2017-03-23 10:53:57 +00:00
moncol =
gradient(0xC00030, 0x008000, 0, (c->stuntime-mutantphase) & 15, 15);
2016-08-26 09:58:03 +00:00
}
if(isMetalBeast(c->monst) && c->stuntime)
2017-03-23 10:53:57 +00:00
moncol >>= 1;
if(c->monst == moSlime) {
moncol = winf[c->wall].color;
moncol |= (moncol>>1);
}
asciicol = moncol;
if(isDragon(c->monst) || isKraken(c->monst)) if(!c->hitpoints)
asciicol = 0x505050;
if(c->monst == moTortoise)
asciicol = tortoise::getMatchColor(tortoise::getb(c));
if(c->monst != moMutant) for(int k=0; k<c->stuntime; k++)
asciicol = ((asciicol & 0xFEFEFE) >> 1) + 0x101010;
2016-08-26 09:58:03 +00:00
}
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
if(c->cpdist == 0 && mapeditor::drawplayer) {
ch = '@';
2017-03-23 10:53:57 +00:00
if(!mmitem) asciicol = moncol = cheater ? 0xFF3030 : 0xD0D0D0;
2015-08-08 13:57:52 +00:00
}
if(c->ligon) {
int tim = ticks - lightat;
if(tim > 1000) tim = 800;
2016-08-26 09:58:03 +00:00
if(elec::havecharge && tim > 400) tim = 400;
2017-03-23 10:53:57 +00:00
for(int t=0; t<c->type; t++) if(c->mov[t] && c->mov[t]->ligon) {
2015-08-08 13:57:52 +00:00
int hdir = displaydir(c, t);
2017-03-23 10:53:57 +00:00
int lcol = darkena(gradient(iinf[itOrbLightning].color, 0, 0, tim, 1100), 0, 0xFF);
queueline(V*ddi0(ticks, hexf/2), V*ddi0(hdir, crossf), lcol, 2);
2015-08-08 13:57:52 +00:00
}
}
int ct = c->type;
2017-03-23 10:53:57 +00:00
int ct6 = K(c);
2015-08-08 13:57:52 +00:00
bool error = false;
2017-03-23 10:53:57 +00:00
chasmg = chasmgraph(c);
int fd =
c->land == laRedRock ? 0 :
(c->land == laOcean || c->land == laLivefjord || c->land == laWhirlpool) ? 1 :
c->land == laAlchemist || c->land == laIce || c->land == laGraveyard ||
c->land == laRlyeh || c->land == laTemple || c->land == laWineyard ||
c->land == laDeadCaves || c->land == laPalace || c->land == laCA ? 1 :
c->land == laCanvas ? 0 :
c->land == laKraken ? 1 :
c->land == laBurial ? 1 :
c->land == laIvoryTower ? 1 :
c->land == laDungeon ? 1 :
c->land == laMountain ? 1 :
c->land == laEndorian ? 1 :
c->land == laCaribbean ? 1 :
c->land == laWhirlwind ? 1 :
c->land == laRose ? 1 :
c->land == laWarpSea ? 1 :
c->land == laTortoise ? 1 :
c->land == laDragon ? 1 :
c->land == laHalloween ? 1 :
c->land == laTrollheim ? 2 :
c->land == laReptile ? 0 :
2;
poly_outline = OUTLINE_NONE;
int sl = snakelevel(c);
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
transmatrix Vd0, Vf0, Vboat0;
const transmatrix *Vdp =
(!wmspatial) ? &V :
sl ? &(Vd0= mscale(V, geom3::SLEV[sl])) :
highwall(c) ? &(Vd0= mscale(V, (1+geom3::WALL)/2)) :
(chasmg==1) ? &(Vd0 = mscale(V, geom3::LAKE)) :
&V;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
const transmatrix& Vf = (chasmg && wmspatial) ? (Vf0=mscale(V, geom3::BOTTOM)) : V;
const transmatrix *Vboat = &(*Vdp);
if(DOSHMUP) {
ld zlev = -geom3::factor_to_lev(zlevel(tC0((*Vdp))));
shmup::drawMonster(V, c, Vboat, Vboat0, zlev);
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
poly_outline = (backcolor << 8) + 0xFF;
if(!wmascii) {
2015-08-08 13:57:52 +00:00
// floor
2017-03-23 10:53:57 +00:00
#ifndef NOEDIT
2016-01-02 10:09:13 +00:00
transmatrix Vpdir = V * applyPatterndir(c);
2016-08-26 09:58:03 +00:00
#endif
bool eoh = euclid || purehepta;
2017-03-23 10:53:57 +00:00
#ifndef NOEDIT
2016-01-02 10:09:13 +00:00
if(c == mapeditor::drawcell && c != cwt.c && !c->monst && !c->item) {
mapeditor::drawtrans = Vpdir;
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
#endif
2017-03-23 10:53:57 +00:00
if(c->wall == waChasm) {
if(c->land == laZebra) fd++;
if(c->land == laHalloween && !wmblack) {
transmatrix Vdepth = mscale(V, geom3::BOTTOM);
queuepolyat(Vdepth, shFloor[ct6], darkena(firecolor(ticks / 10), 0, 0xDF),
PPR_LAKEBOTTOM);
}
}
#ifndef NOEDIT
if(drawUserShape(Vpdir, mapeditor::cellShapeGroup(), mapeditor::realpatternsh(c),
darkena(fcol, fd, cmode == emDraw ? 0xC0 : 0xFF)));
2016-01-02 10:09:13 +00:00
else if(mapeditor::whichShape == '7') {
if(ishept(c))
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, wmblack ? shBFloor[ct6] :
2016-01-02 10:09:13 +00:00
euclid ? shBigHex :
2017-03-23 10:53:57 +00:00
shBigHepta, darkena(fcol, fd, 0xFF));
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(mapeditor::whichShape == '8') {
if(euclid)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shTriheptaEuc[ishept(c) ? 1 : ishex1(c) ? 0 : 2], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
else
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shTriheptaFloor[ishept(c) ? 1 : 0], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
}
2016-01-02 10:09:13 +00:00
else if(mapeditor::whichShape == '6') {
if(!ishept(c))
2017-03-23 10:53:57 +00:00
qfloor(c, Vf,
wmblack ? shBFloor[ct6] :
2016-01-02 10:09:13 +00:00
euclid ? (ishex1(c) ? shBigHexTriangle : shBigHexTriangleRev) :
2017-03-23 10:53:57 +00:00
shBigTriangle, darkena(fcol, fd, 0xFF));
2016-01-02 10:09:13 +00:00
}
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
else if(c->land == laWineyard && cellHalfvine(c)) {
2015-08-08 13:57:52 +00:00
int i =-1;
for(int t=0;t<6; t++) if(c->mov[t] && c->mov[t]->wall == c->wall)
i = t;
2017-03-23 10:53:57 +00:00
qfi.spin = ddspin(c, i, S14);
transmatrix V2 = V * qfi.spin;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(wmspatial && wmescher) {
qfi.shape = &shSemiFeatherFloor[0]; qfi.special = true;
int dk = 1;
int vcol = winf[waVinePlant].color;
warpfloor(c, mscale(V, geom3::WALL), darkena(vcol, dk, 0xFF), PPR_WALL3A, false);
escherSidewall(c, SIDE_WALL, V, darkena(gradient(0, vcol, 0, .8, 1), dk, 0xFF));
qfloor(c, V2, shSemiFeatherFloor[1], darkena(fcol, dk, 0xFF));
qfi.shape = &shFeatherFloor[0]; qfi.special = true;
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
else if(wmspatial) {
hpcshape *shar = wmplain ? shFloor : shFeatherFloor;
int dk = 1;
qfloor(c, V, shar[0], darkena(fcol, dk, 0xFF));
int vcol = winf[waVinePlant].color;
int vcol2 = gradient(0, vcol, 0, .8, 1);
transmatrix Vdepth = mscale(V2, geom3::WALL);
queuepolyat(Vdepth, shSemiFloor[0], darkena(vcol, dk, 0xFF), PPR_WALL3A);
{dynamicval<int> p(poly_outline, OUTLINE_TRANS); queuepolyat(V2 * spin(M_PI*2/3), shSemiFloorShadow, SHADOW_WALL, PPR_WALLSHADOW); }
queuepolyat(V2, shSemiFloorSide[SIDE_WALL], darkena(vcol, dk, 0xFF), PPR_WALL3A-2+away(V2));
if(validsidepar[SIDE_WALL]) forCellIdEx(c2, j, c) {
int dis = i-j;
dis %= 6;
if(dis<0) dis += 6;
if(dis != 1 && dis != 5) continue;
placeSidewall(c, j, SIDE_WALL, V, false, false, darkena(vcol2, fd, 0xFF));
}
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
else {
hpcshape *shar = shSemiFeatherFloor;
if(wmblack) shar = shSemiBFloor;
if(wmplain) shar = shSemiFloor;
int dk = wmblack ? 0 : wmplain ? 1 : 1;
qfloor(c, V2, shar[0], darkena(winf[waVinePlant].color, dk, 0xFF));
qfloor(c, V2, shar[1], darkena(fcol, dk, 0xFF));
}
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else if(c->land == laReptile || c->wall == waReptile)
drawReptileFloor(Vf, c, fcol, true);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
else if(wmblack == 1 && c->wall == waMineOpen && vid.grid)
2016-01-02 10:09:13 +00:00
;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
else if(wmblack) {
qfloor(c, Vf, shBFloor[ct6], darkena(fcol, 0, 0xFF));
int rd = rosedist(c);
if(rd == 1)
qfloor(c, Vf, shHeptaMarker, darkena(fcol, 0, 0x80));
else if(rd == 2)
qfloor(c, Vf, shHeptaMarker, darkena(fcol, 0, 0x40));
}
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
else if(isWarped(c) && euclid)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shTriheptaEuc[ishept(c)?1:ishex1(c)?0:2], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
else if(isWarped(c) && !purehepta && !shmup::on) {
int np = mapeditor::nopattern(c);
if(c->landparam == 1337) np = 0; // for the achievement screenshot
if(np < 11)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, applyPatterndir(c), shTriheptaFloor[np], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
else if(wmplain) {
if(wmspatial && highwall(c)) ;
else qfloor(c, Vf, shFloor[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
else if(randomPatternsMode && c->land != laBarrier && !isWarped(c->land)) {
int j = (randompattern[c->land]/5) % 15;
2017-03-23 10:53:57 +00:00
int dfcol = darkena(fcol, fd, 0xFF);
2016-08-26 09:58:03 +00:00
int k = randompattern[c->land] % RPV_MODULO;
int k7 = randompattern[c->land] % 7;
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(k == RPV_ZEBRA && k7 < 2) drawZebraFloor(Vf, c, dfcol);
else if(k == RPV_EMERALD && k7 == 0) drawEmeraldFloor(Vf, c, dfcol);
else if(k == RPV_CYCLE && k7 < 4) drawTowerFloor(Vf, c, dfcol, celldist);
2016-08-26 09:58:03 +00:00
else switch(j) {
2017-03-23 10:53:57 +00:00
case 0: qfloor(c, Vf, shCloudFloor[ct6], dfcol); break;
case 1: qfloor(c, Vf, shFeatherFloor[ECT], dfcol); break;
case 2: qfloor(c, Vf, shStarFloor[ct6], dfcol); break;
case 3: qfloor(c, Vf, shTriFloor[ct6], dfcol); break;
case 4: qfloor(c, Vf, shSStarFloor[ct6], dfcol); break;
case 5: qfloor(c, Vf, shOverFloor[ECT], dfcol); break;
case 6: qfloor(c, Vf, shFeatherFloor[ECT], dfcol); break;
case 7: qfloor(c, Vf, shDemonFloor[ct6], dfcol); break;
case 8: qfloor(c, Vf, shCrossFloor[ct6], dfcol); break;
case 9: qfloor(c, Vf, shMFloor[ct6], dfcol); break;
case 10: qfloor(c, Vf, shCaveFloor[ECT], dfcol); break;
case 11: qfloor(c, Vf, shPowerFloor[ct6], dfcol); break;
case 12: qfloor(c, Vf, shDesertFloor[ct6], dfcol); break;
case 13: qfloor(c, Vf, purehepta ? shChargedFloor[3] : shChargedFloor[ct6], dfcol); break;
case 14: qfloor(c, Vf, ct==6?shChargedFloor[2]:shFloor[1], dfcol); break;
2016-08-26 09:58:03 +00:00
}
}
2017-03-23 10:53:57 +00:00
// else if(c->land == laPrairie && !eoh && allemptynear(c) && fieldpattern::getflowerdist(c) <= 1)
// queuepoly(Vf, shLeafFloor[ct6], darkena(fcol, fd, 0xFF));
/* else if(c->land == laPrairie && prairie::isriver(c))
drawTowerFloor(Vf, c, darkena(fcol, fd, 0xFF),
prairie::isleft(c) ? river::towerleft : river::towerright); */
else if(c->land == laPrairie)
qfloor(c, Vf, shCloudFloor[ct6], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
else if(c->land == laWineyard) {
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shFeatherFloor[euclid?2:ct6], darkena(fcol, fd, 0xFF));
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
else if(c->land == laZebra)
2017-03-23 10:53:57 +00:00
drawZebraFloor(Vf, c, darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
else if(c->wall == waTrunk)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shFloor[ct6], darkena(fcol, fd, 0xFF));
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
else if(c->wall == waCanopy || c->wall == waSolidBranch || c->wall == waWeakBranch)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shFeatherFloor[ct6], darkena(fcol, fd, 0xFF));
else if(c->land == laMountain)
drawTowerFloor(Vf, c, darkena(fcol, fd, 0xFF),
euclid ? celldist : c->master->alt ? celldistAltPlus : celldist);
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
else if(isGravityLand(c->land))
2017-03-23 10:53:57 +00:00
drawTowerFloor(Vf, c, darkena(fcol, fd, 0xFF));
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
else if(c->land == laEmerald)
2017-03-23 10:53:57 +00:00
drawEmeraldFloor(Vf, c, darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laRlyeh)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (eoh ? shFloor: shTriFloor)[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laTemple)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (eoh ? shFloor: shTriFloor)[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laAlchemist)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shCloudFloor[ct6], darkena(fcol, fd, 0xFF));
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
else if(c->land == laRose)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shRoseFloor[purehepta ? 2 : ct6], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
else if(c->land == laTortoise)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shTurtleFloor[purehepta ? 2 : ct6], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
else if(c->land == laDragon && !purehepta) {
/* if(!wmspatial || noAdjacentChasms(c)) */
qfloor(c, Vf, shDragonFloor[euclid?2:ct6], darkena(fcol, fd, 0xFF));
/* if(wmspatial)
qfloor(c, Vf, shFloor[euclid?2:ct6], darkena(fcol, fd, 0xFF)); */
}
2016-08-26 09:58:03 +00:00
else if((isElemental(c->land) || c->land == laElementalWall) && !eoh)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shNewFloor[ct6], darkena(fcol, fd, 0xFF));
else if(c->land == laBurial)
qfloor(c, Vf, shBarrowFloor[euclid?0:purehepta?2:ct6], darkena(fcol, fd, 0xFF));
else if(c->land == laTrollheim && !eoh)
qfloor(c, Vf, shTrollFloor[ct6], darkena(fcol, fd, 0xFF));
else if(c->land == laTrollheim)
qfloor(c, Vf, shCaveFloor[euclid?2:1], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laJungle)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shFeatherFloor[euclid?2:ct6], darkena(fcol, fd, 0xFF));
else if(c->land == laMountain)
qfloor(c, Vf, shFeatherFloor[euclid?2:ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laGraveyard)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (eoh ? shFloor : shCrossFloor)[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laDeadCaves) {
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shCaveFloor[euclid?2:ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
}
else if(c->land == laMotion)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shMFloor[ct6], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
else if(c->land == laWhirlwind)
2017-03-23 10:53:57 +00:00
// drawZebraFloor(V, c, darkena(fcol, fd, 0xFF));
qfloor(c, Vf, (eoh ? shCloudFloor : shNewFloor)[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laHell)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (euclid ? shStarFloor : shDemonFloor)[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laIce)
2017-03-23 10:53:57 +00:00
// qfloor(c, V, shFloor[ct6], darkena(fcol, 2, 0xFF));
qfloor(c, Vf, shStarFloor[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laCocytus)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (eoh ? shCloudFloor : shDesertFloor)[ct6], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
else if(c->land == laStorms) {
if(euclid)
2017-03-23 10:53:57 +00:00
qfloor(c, ishex1(c) ? V*pispin : Vf,
ishept(c) ? shFloor[0] : shChargedFloor[2], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
else
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (purehepta ? shChargedFloor[3] : ct==6 ? shChargedFloor[2] : shFloor[1]), darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
}
else if(c->land == laWildWest)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (eoh ? shCloudFloor : shSStarFloor)[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laPower)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (eoh ? shStarFloor : shPowerFloor)[ct6], darkena(fcol, fd, 0xFF));
else if(c->land == laHive && c->wall != waFloorB && c->wall != waFloorA && c->wall != waMirror && c->wall != waCloud) {
qfloor(c, Vf, shFloor[ct6], darkena(fcol, 1, 0xFF));
if(c->wall != waMirror && c->wall != waCloud)
qfloor(c, Vf, shMFloor[ct6], darkena(fcol, 2, 0xFF));
if(c->wall != waMirror && c->wall != waCloud)
qfloor(c, Vf, shMFloor2[ct6], darkena(fcol, fcol==wcol ? 1 : 2, 0xFF));
2015-08-08 13:57:52 +00:00
}
else if(c->land == laCaves)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shCaveFloor[ECT], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
else if(c->land == laDesert)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (eoh ? shCloudFloor : shDesertFloor)[ct6], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
else if(c->land == laOvergrown || c->land == laClearing || isHaunted(c->land))
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shOverFloor[ECT], darkena(fcol, fd, 0xFF));
2016-08-26 09:58:03 +00:00
else if(c->land == laRose)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shOverFloor[ECT], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
else if(c->land == laBull)
qfloor(c, Vf, (eoh ? shFloor : shButterflyFloor)[ct6], darkena(fcol, fd, 0xFF));
2016-01-02 10:09:13 +00:00
else if(c->land == laDryForest)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (eoh ? shStarFloor : shDesertFloor)[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
else if(c->land == laCaribbean || c->land == laOcean || c->land == laOceanWall || c->land == laWhirlpool)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shCloudFloor[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
else if(c->land == laLivefjord)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shCaveFloor[ECT], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
else if(c->land == laRedRock)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, eoh ? shFloor[ct6] : shDesertFloor[ct6], darkena(fcol, fd, 0xFF));
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
else if(c->land == laPalace)
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, (eoh?shFloor:shPalaceFloor)[ct6], darkena(fcol, fd, 0xFF));
2016-01-02 10:09:13 +00:00
else {
2017-03-23 10:53:57 +00:00
qfloor(c, Vf, shFloor[ct6], darkena(fcol, fd, 0xFF));
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
// walls
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
#ifndef NOEDIT
2017-04-08 15:18:29 +00:00
if(mapeditor::displaycodes) {
2016-01-02 10:09:13 +00:00
int labeli = mapeditor::displaycodes == 1 ? mapeditor::realpattern(c) : mapeditor::subpattern(c);
string label = its(labeli);
2017-03-23 10:53:57 +00:00
if(svg::in)
queuestr(V, .5, label, 0xFF000000);
else
queuestr(V, .2, label, 0xFFFFFFFF);
2016-01-02 10:09:13 +00:00
/* transmatrix V2 = V * applyPatterndir(c);
2017-03-23 10:53:57 +00:00
qfloor(c, V2, shNecro, 0x80808080);
qfloor(c, V2, shStatue, 0x80808080); */
2016-01-02 10:09:13 +00:00
}
#endif
2017-03-23 10:53:57 +00:00
if(cmode == emNumber && (dialog::editingDetail())) {
int col =
dist0 < geom3::highdetail ? 0xFF80FF80 :
dist0 >= geom3::middetail ? 0xFFFF8080 :
0XFFFFFF80;
#if 1
queuepoly(V, shHeptaMarker, darkena(col & 0xFFFFFF, 0, 0xFF));
#else
char buf[64];
sprintf(buf, "%3.1f", float(dist0));
queuestr(V, .6, buf, col);
#endif
}
2017-03-23 10:53:57 +00:00
if(realred(c->wall) && !wmspatial) {
2016-01-02 10:09:13 +00:00
int s = snakelevel(c);
if(s >= 1)
2017-03-23 10:53:57 +00:00
qfloor(c, V, shRedRockFloor[0][ct6], darkena(winf[waRed1].color, 0, 0xFF));
2016-01-02 10:09:13 +00:00
if(s >= 2)
2017-03-23 10:53:57 +00:00
queuepoly(V, shRedRockFloor[1][ct6], darkena(winf[waRed2].color, 0, 0xFF));
2016-01-02 10:09:13 +00:00
if(s >= 3)
2017-03-23 10:53:57 +00:00
queuepoly(V, shRedRockFloor[2][ct6], darkena(winf[waRed3].color, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(c->wall == waTower && !wmspatial) {
qfloor(c, V, shMFloor[ct6], darkena(0xE8E8E8, fd, 0xFF));
}
if(pseudohept(c) && (
c->land == laRedRock ||
vid.darkhepta ||
(purehepta && (c->land == laClearing || isWarped(c))))) {
queuepoly((*Vdp), shHeptaMarker, wmblack ? 0x80808080 : 0x00000080);
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
if(conformal::includeHistory && eq(c->aitmp, sval-1))
2017-03-23 10:53:57 +00:00
queuepoly((*Vdp), shHeptaMarker, 0x000000C0);
2016-08-26 09:58:03 +00:00
2016-01-02 10:09:13 +00:00
char xch = winf[c->wall].glyph;
2017-03-23 10:53:57 +00:00
if(c->wall == waBigBush) {
if(detaillevel >= 2)
queuepolyat(mmscale(V, zgrad0(0, geom3::slev, 1, 2)), shHeptaMarker, darkena(wcol, 0, 0xFF), PPR_REDWALL);
if(detaillevel >= 1)
queuepolyat(mmscale(V, geom3::SLEV[1]) * pispin, shWeakBranch, darkena(wcol, 0, 0xFF), PPR_REDWALL+1);
if(detaillevel >= 2)
queuepolyat(mmscale(V, zgrad0(0, geom3::slev, 3, 2)), shHeptaMarker, darkena(wcol, 0, 0xFF), PPR_REDWALL+2);
queuepolyat(mmscale(V, geom3::SLEV[2]), shSolidBranch, darkena(wcol, 0, 0xFF), PPR_REDWALL+3);
}
else if(c->wall == waSmallBush) {
if(detaillevel >= 2)
queuepolyat(mmscale(V, zgrad0(0, geom3::slev, 1, 2)), shHeptaMarker, darkena(wcol, 0, 0xFF), PPR_REDWALL);
if(detaillevel >= 1)
queuepolyat(mmscale(V, geom3::SLEV[1]) * pispin, shWeakBranch, darkena(wcol, 0, 0xFF), PPR_REDWALL+1);
if(detaillevel >= 2)
queuepolyat(mmscale(V, zgrad0(0, geom3::slev, 3, 2)), shHeptaMarker, darkena(wcol, 0, 0xFF), PPR_REDWALL+2);
queuepolyat(mmscale(V, geom3::SLEV[2]), shWeakBranch, darkena(wcol, 0, 0xFF), PPR_REDWALL+3);
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
else if(c->wall == waSolidBranch) {
queuepoly(V, shSolidBranch, darkena(wcol, 0, 0xFF));
2016-08-26 09:58:03 +00:00
}
else if(c->wall == waWeakBranch) {
2017-03-23 10:53:57 +00:00
queuepoly(V, shWeakBranch, darkena(wcol, 0, 0xFF));
2016-08-26 09:58:03 +00:00
}
else if(c->wall == waLadder) {
2016-01-02 10:09:13 +00:00
if(euclid) {
2017-03-23 10:53:57 +00:00
queuepoly(V, shMFloor[ct6], 0x804000FF);
queuepoly(V, shMFloor2[ct6], 0x000000FF);
2016-01-02 10:09:13 +00:00
}
else {
2017-03-23 10:53:57 +00:00
queuepolyat(V, shFloor[ct6], 0x804000FF, PPR_FLOOR+1);
queuepolyat(V, shMFloor[ct6], 0x000000FF, PPR_FLOOR+2);
2016-01-02 10:09:13 +00:00
}
}
2017-03-23 10:53:57 +00:00
if(c->wall == waReptileBridge) {
Vboat = &(Vboat0 = V);
dynamicval<qfloorinfo> qfi2(qfi, qfi);
int col = reptilecolor(c);
chasmg = 0;
drawReptileFloor(V, c, col, true);
forCellIdEx(c2, i, c) if(chasmgraph(c2))
placeSidewallX(c, i, SIDE_LAKE, V, isWarped(c), false, darkena(gradient(0, col, 0, .8, 1), fd, 0xFF));
chasmg = 1;
}
if(c->wall == waBoat || c->wall == waStrandedBoat) {
double footphase;
bool magical = items[itOrbWater] && (isPlayerOn(c) || (isFriendly(c) && items[itOrbEmpathy]));
int outcol = magical ? watercolor(0) : 0xC06000FF;
int incol = magical ? 0x0060C0FF : 0x804000FF;
bool nospin = false;
Vboat = &(Vboat0 = *Vboat);
if(wmspatial && c->wall == waBoat) {
nospin = c->wall == waBoat && applyAnimation(c, Vboat0, footphase, LAYER_BOAT);
if(!nospin) Vboat0 = Vboat0 * ddspin(c, c->mondir, S42);
queuepolyat(Vboat0, shBoatOuter, outcol, PPR_BOATLEV);
Vboat = &(Vboat0 = V);
}
if(c->wall == waBoat) {
nospin = applyAnimation(c, Vboat0, footphase, LAYER_BOAT);
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
if(!nospin)
Vboat0 = Vboat0 * ddspin(c, c->mondir, S42);
2016-01-02 10:09:13 +00:00
else {
2017-03-23 10:53:57 +00:00
transmatrix Vx;
if(applyAnimation(c, Vx, footphase, LAYER_SMALL))
animations[LAYER_SMALL][c].footphase = 0;
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
if(wmspatial)
queuepolyat(mscale(Vboat0, (geom3::LAKE+1)/2), shBoatOuter, outcol, PPR_BOATLEV2);
queuepoly(Vboat0, shBoatOuter, outcol);
queuepoly(Vboat0, shBoatInner, incol);
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
else if(c->wall == waBigStatue) {
transmatrix V2 = V;
double footphase;
applyAnimation(c, V2, footphase, LAYER_BOAT);
queuepolyat(V2, shStatue,
darkena(winf[c->wall].color, 0, 0xFF),
PPR_BIGSTATUE
2015-08-08 13:57:52 +00:00
);
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
else if(c->wall == waSulphurC) {
2017-03-23 10:53:57 +00:00
if(drawstar(c)) {
zcol = wcol;
2017-03-23 10:53:57 +00:00
if(wmspatial)
queuepolyat(mscale(V, geom3::HELLSPIKE), shGiantStar[ct6], darkena(wcol, 0, 0x40), PPR_HELLSPIKE);
else
queuepoly(V, shGiantStar[ct6], darkena(wcol, 0, 0xFF));
}
2016-01-02 10:09:13 +00:00
}
else if(c->wall == waClosePlate || c->wall == waOpenPlate || (c->wall == waTrapdoor && c->land != laZebra)) {
transmatrix V2 = V;
2017-03-23 10:53:57 +00:00
if(ct != 6 && wmescher) V2 = V * pispin;
queuepoly(V2, shMFloor[ct6], darkena(winf[c->wall].color, 0, 0xFF));
queuepoly(V2, shMFloor2[ct6], (!wmblack) ? darkena(fcol, 1, 0xFF) : darkena(0,1,0xFF));
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
else if(c->wall == waFrozenLake || c->wall == waLake || c->wall == waCamelotMoat ||
2017-03-23 10:53:57 +00:00
c->wall == waSea || c->wall == waClosePlate || c->wall == waOpenPlate ||
2016-01-02 10:09:13 +00:00
c->wall == waOpenGate || c->wall == waTrapdoor)
2015-08-08 13:57:52 +00:00
;
2016-08-26 09:58:03 +00:00
else if(c->wall == waRose) {
zcol = wcol;
2017-03-23 10:53:57 +00:00
wcol <<= 1;
2016-08-26 09:58:03 +00:00
if(c->cpdist > 5)
2017-03-23 10:53:57 +00:00
wcol = 0xC0C0C0;
2016-08-26 09:58:03 +00:00
else if(rosephase == 7)
2017-03-23 10:53:57 +00:00
wcol = 0xFF0000;
2016-08-26 09:58:03 +00:00
else
2017-03-23 10:53:57 +00:00
wcol = gradient(wcol, 0xC00000, 0, rosephase, 6);
2016-08-26 09:58:03 +00:00
queuepoly(V, shThorns, 0xC080C0FF);
for(int u=0; u<4; u+=2)
2017-03-23 10:53:57 +00:00
queuepoly(V * spin(2*M_PI / 3 / 4 * u), shRose, darkena(wcol, 0, 0xC0));
}
else if(sl && wmspatial) {
bool w = isWarped(c);
warpfloor(c, (*Vdp), darkena(wcol, fd, 0xFF), PPR_REDWALL-4+4*sl, w);
floorShadow(c, V, SHADOW_SL * sl, w);
bool tower = c->wall == waTower;
for(int s=0; s<sl; s++)
forCellIdEx(c2, i, c) {
int sl2 = snakelevel(c2);
if(s >= sl2)
placeSidewallX(c, i, SIDE_SLEV+s, V, w, false,
darkena(tower?0xD0D0D0-i*0x101010 : s==sl-1?wcol:winf[waRed1+s].color, fd, 0xFF));
}
}
else if(c->wall == waRoundTable) ;
else if(c->wall == waGlass && wmspatial) {
int col = winf[waGlass].color;
int dcol = darkena(col, 0, 0x80);
transmatrix Vdepth = mscale((*Vdp), geom3::WALL);
queuepolyat(Vdepth, shMFloor[ct6], dcol, PPR_GLASS);
if(validsidepar[SIDE_WALL]) forCellIdEx(c2, i, c)
placeSidewall(c, i, SIDE_WALL, (*Vdp), false, true, dcol);
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
else if(c->wall == waGlass && !wmspatial) ;
else if(wmescher && wmspatial && c->wall == waBarrier && c->land == laOceanWall) {
const int layers = 2 << detaillevel;
dynamicval<const hpcshape*> ds(qfi.shape, &shCircleFloor);
dynamicval<bool> db(qfi.special, true);
for(int z=1; z<layers; z++) {
double zg = zgrad0(-geom3::lake_top, geom3::wall_height, z, layers);
warpfloor(c, xyzscale(V, zg*(layers-z)/layers, zg),
darkena(gradient(0, wcol, -layers, z, layers), 0, 0xFF), PPR_WALL3+z-layers+2, isWarped(c));
}
}
else if(highwall(c)) {
zcol = wcol;
2017-03-23 10:53:57 +00:00
int wcol0 = wcol;
int starcol = wcol;
if(c->wall == waWarpGate) starcol = 0;
if(c->wall == waVinePlant) starcol = 0x60C000;
int wcol2 = gradient(0, wcol0, 0, .8, 1);
if(c->wall == waClosedGate) {
int hdir = 0;
for(int i=0; i<c->type; i++) if(c->mov[i]->wall == waClosedGate)
hdir = i;
transmatrix V2 = mscale(V, wmspatial?geom3::WALL:1) * ddspin(c, hdir, S42);
queuepolyat(V2, shPalaceGate, darkena(wcol, 0, 0xFF), wmspatial?PPR_WALL3A:PPR_WALL);
starcol = 0;
}
hpcshape& shThisWall = isGrave(c->wall) ? shCross : shWall[ct6];
if(conegraph(c)) {
const int layers = 2 << detaillevel;
for(int z=1; z<layers; z++) {
double zg = zgrad0(0, geom3::wall_height, z, layers);
warpfloor(c, xyzscale(V, zg*(layers-z)/layers, zg),
darkena(gradient(0, wcol, -layers, z, layers), 0, 0xFF), PPR_WALL3+z-layers+2, isWarped(c));
}
floorShadow(c, V, SHADOW_WALL, isWarped(c));
}
else if(true) {
if(!wmspatial) {
if(starcol) queuepoly(V, shThisWall, darkena(starcol, 0, 0xFF));
}
else {
transmatrix Vdepth = mscale(V, geom3::WALL);
bool warp = isWarped(c);
if(starcol && !(wmescher && c->wall == waPlatform))
queuepolyat(Vdepth, shThisWall, darkena(starcol, 0, 0xFF), PPR_WALL3A);
warpfloor(c, Vdepth, darkena(wcol0, fd, 0xFF), PPR_WALL3, warp);
floorShadow(c, V, SHADOW_WALL, warp);
if(c->wall == waCamelot) {
forCellIdEx(c2, i, c) {
placeSidewallX(c, i, SIDE_SLEV, V, warp, false, darkena(wcol2, fd, 0xFF));
}
forCellIdEx(c2, i, c) {
placeSidewallX(c, i, SIDE_SLEV+1, V, warp, false, darkena(wcol2, fd, 0xFF));
}
forCellIdEx(c2, i, c) {
placeSidewallX(c, i, SIDE_SLEV+2, V, warp, false, darkena(wcol2, fd, 0xFF));
}
forCellIdEx(c2, i, c) {
placeSidewallX(c, i, SIDE_WTS3, V, warp, false, darkena(wcol2, fd, 0xFF));
}
}
else forCellIdEx(c2, i, c) {
if(!highwall(c2) || conegraph(c2))
{ placeSidewallX(c, i, SIDE_WALL, V, warp, false, darkena(wcol2, fd, 0xFF)); }
}
}
}
2015-08-08 13:57:52 +00:00
}
2016-08-26 09:58:03 +00:00
else if(c->wall == waFan) {
2017-03-23 10:53:57 +00:00
queuepoly(V * spin(M_PI/6 - fanframe * M_PI / 3), shFan, darkena(wcol, 0, 0xFF));
2016-08-26 09:58:03 +00:00
}
else if(xch == '%') {
if(doHighlight())
poly_outline = (c->land == laMirror) ? OUTLINE_TREASURE : OUTLINE_ORB;
2017-03-23 10:53:57 +00:00
if(wmspatial) {
int col = winf[c->wall].color;
int dcol = darkena(col, 0, 0xC0);
transmatrix Vdepth = mscale((*Vdp), geom3::WALL);
queuepolyat(Vdepth, shMFloor[ct6], dcol, PPR_GLASS);
if(validsidepar[SIDE_WALL]) forCellIdEx(c2, i, c)
placeSidewall(c, i, SIDE_WALL, (*Vdp), false, true, dcol);
}
else {
queuepoly(V, shMirror, darkena(wcol, 0, 0xC0));
}
2016-08-26 09:58:03 +00:00
poly_outline = OUTLINE_NONE;
}
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
else if(isFire(c) || isThumper(c) || c->wall == waBonfireOff) {
ld sp = 0;
if(hasTimeout(c)) sp = ticks / (c->land == laPower ? 5000. : 500.);
2017-03-23 10:53:57 +00:00
queuepoly(V * spin(sp), shStar, darkena(wcol, 0, 0xF0));
if(isFire(c) && rand() % 300 < ticks - lastt)
drawParticle(c, wcol, 75);
2015-08-08 13:57:52 +00:00
}
else if(c->wall == waFreshGrave || c->wall == waAncientGrave) {
zcol = wcol;
2017-03-23 10:53:57 +00:00
queuepoly(V, shCross, darkena(wcol, 0, 0xFF));
}
2016-01-02 10:09:13 +00:00
else if(xch == '+' && c->wall == waGiantRug) {
queuepoly(V, shBigCarpet1, darkena(0xC09F00, 0, 0xFF));
queuepoly(V, shBigCarpet2, darkena(0x600000, 0, 0xFF));
queuepoly(V, shBigCarpet3, darkena(0xC09F00, 0, 0xFF));
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
else if(xch != '.' && xch != '+' && xch != '>' && xch != ':'&& xch != '-' && xch != ';' && c->wall != waSulphur && xch != ',')
2015-08-08 13:57:52 +00:00
error = true;
}
2016-01-02 10:09:13 +00:00
else if(!(it || c->monst || c->cpdist == 0)) error = true;
2017-03-23 10:53:57 +00:00
int sha = shallow(c);
if(wmspatial && sha) {
bool w = isWarped(c);
int col = (highwall(c) || c->wall == waTower) ? wcol : fcol;
if(!chasmg) {
if(sha & 1) {
forCellIdEx(c2, i, c) if(chasmgraph(c2))
placeSidewallX(c, i, SIDE_LAKE, V, w, false, darkena(gradient(0, col, 0, .8, 1), fd, 0xFF));
}
if(sha & 2) {
forCellIdEx(c2, i, c) if(chasmgraph(c2))
placeSidewallX(c, i, SIDE_LTOB, V, w, false, darkena(gradient(0, col, 0, .7, 1), fd, 0xFF));
}
if(sha & 4) {
bool dbot = true;
forCellIdEx(c2, i, c) if(chasmgraph(c2) == 2) {
if(dbot) dbot = false,
warpfloor(c, mscale(V, geom3::BOTTOM), 0x080808FF, PPR_LAKEBOTTOM, isWarped(c));
placeSidewallX(c, i, SIDE_BTOI, V, w, false, darkena(gradient(0, col, 0, .6, 1), fd, 0xFF));
}
}
}
// wall between lake and chasm -- no Escher here
if(chasmg == 1) forCellIdEx(c2, i, c) if(chasmgraph(c2) == 2) {
placeSidewall(c, i, SIDE_LAKE, V, w, false, 0x202030FF);
placeSidewall(c, i, SIDE_LTOB, V, w, false, 0x181820FF);
placeSidewall(c, i, SIDE_BTOI, V, w, false, 0x101010FF);
}
}
if(chasmg == 1 && wmspatial) {
int fd0 = fd ? fd-1 : 0;
warpfloor(c, (*Vdp), darkena(fcol, fd0, 0x80), PPR_LAKELEV, isWarped(c));
}
if(chasmg) {
int q = size(ptds);
if(fallanims.count(c)) {
fallanim& fa = fallanims[c];
bool erase = true;
if(fa.t_floor) {
int t = (ticks - fa.t_floor);
if(t <= 1500) {
erase = false;
if(fa.walltype == waNone)
warpfloor(c, V, darkena(fcol, fd, 0xFF), PPR_FLOOR, isWarped(c));
else {
int wcol2, fcol2;
eWall w = c->wall; int p = c->wparam;
c->wall = fa.walltype; c->wparam = fa.m;
setcolors(c, wcol2, fcol2);
int starcol = c->wall == waVinePlant ? 0x60C000 : wcol2;
c->wall = w; c->wparam = p;
bool warp = isWarped(c);
warpfloor(c, mscale(V, geom3::WALL), darkena(starcol, fd, 0xFF), PPR_WALL3, warp);
queuepolyat(mscale(V, geom3::WALL), shWall[ct6], darkena(wcol2, 0, 0xFF), PPR_WALL3A);
forCellIdEx(c2, i, c)
placeSidewallX(c, i, SIDE_WALL, V, warp, false, darkena(wcol2, 1, 0xFF));
}
pushdown(c, q, V, t*t / 1000000. + t / 1000., true, true);
}
}
if(fa.t_mon) {
int t = (ticks - fa.t_mon);
if(t <= 1500) {
erase = false;
c->stuntime = 0;
transmatrix V2 = V;
double footphase = t / 200.0;
applyAnimation(c, V2, footphase, LAYER_SMALL);
drawMonsterType(fa.m, c, V2, minf[fa.m].color, footphase);
pushdown(c, q, V2, t*t / 1000000. + t / 1000., true, true);
}
}
if(erase) fallanims.erase(c);
}
}
2016-01-02 10:09:13 +00:00
if(c->wall == waMineOpen) {
int mines = countMinesAround(c);
2017-03-23 10:53:57 +00:00
if(wmascii) {
2016-01-02 10:09:13 +00:00
if(ch == '.') {
if(mines == 0) ch = ' ';
2017-03-23 10:53:57 +00:00
else ch = '0' + mines, asciicol = minecolors[mines];
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
else if(ch == '@') asciicol = minecolors[mines];
2016-01-02 10:09:13 +00:00
}
else if(mines > 0)
2017-03-23 10:53:57 +00:00
queuepoly(V, shMineMark[ct6], (minecolors[mines] << 8) | 0xFF);
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
// treasure
2016-01-02 10:09:13 +00:00
char xch = iinf[it].glyph;
2015-08-08 13:57:52 +00:00
hpcshape *xsh =
2017-03-23 10:53:57 +00:00
(it == itPirate || it == itKraken) ? &shPirateX :
2016-08-26 09:58:03 +00:00
(it == itBuggy || it == itBuggy2) ? &shPirateX :
2016-01-02 10:09:13 +00:00
it == itHolyGrail ? &shGrail :
isElementalShard(it) ? &shElementalShard :
2017-03-23 10:53:57 +00:00
(it == itBombEgg || it == itTrollEgg) ? &shEgg :
it == itDodeca ? &shDodeca :
xch == '*' ? &shGem[ct6] :
it == itTreat ? &shTreat :
it == itSlime ? &shEgg :
xch == '%' ? &shDaisy : xch == '$' ? &shStar : xch == ';' ? &shTriangle :
2016-01-02 10:09:13 +00:00
xch == '!' ? &shTriangle : it == itBone ? &shNecro : it == itStatue ? &shStatue :
2017-03-23 10:53:57 +00:00
it == itIvory ? &shFigurine :
2015-08-08 13:57:52 +00:00
xch == '?' ? &shBookCover :
2016-08-26 09:58:03 +00:00
it == itKey ? &shKey :
it == itRevolver ? &shGun :
NULL;
2017-03-23 10:53:57 +00:00
if(c->land == laWhirlwind && c->wall != waBoat) {
double footphase = 0;
Vboat = &(Vboat0 = *Vboat);
applyAnimation(c, Vboat0, footphase, LAYER_BOAT);
}
if(it && cellHalfvine(c)) {
int i =-1;
for(int t=0;t<6; t++) if(c->mov[t] && c->mov[t]->wall == c->wall)
i = t;
Vboat = &(Vboat0 = *Vboat * ddspin(c, i) * xpush(-.13));
}
2015-08-08 13:57:52 +00:00
if(doHighlight()) {
2016-01-02 10:09:13 +00:00
int k = itemclass(it);
2015-08-08 13:57:52 +00:00
if(k == IC_TREASURE)
2016-08-26 09:58:03 +00:00
poly_outline = OUTLINE_TREASURE;
2015-08-08 13:57:52 +00:00
else if(k == IC_ORB)
2016-08-26 09:58:03 +00:00
poly_outline = OUTLINE_ORB;
2015-08-08 13:57:52 +00:00
else
2016-08-26 09:58:03 +00:00
poly_outline = OUTLINE_OTHER;
2015-08-08 13:57:52 +00:00
}
2016-08-26 09:58:03 +00:00
if(conformal::includeHistory && eq(c->aitmp, sval)) poly_outline = OUTLINE_DEAD;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
#ifndef NOEDIT
2016-01-02 10:09:13 +00:00
if(c == mapeditor::drawcell && mapeditor::drawcellShapeGroup() == 2)
mapeditor::drawtrans = V;
#endif
2017-03-23 10:53:57 +00:00
if(!mmitem && it)
2015-08-08 13:57:52 +00:00
error = true;
2016-08-26 09:58:03 +00:00
else if(it == itBabyTortoise) {
int bits = tortoise::babymap[c];
2017-03-23 10:53:57 +00:00
int over = c->monst == moTortoise;
tortoise::draw(*Vboat * spin(ticks / 5000.) * ypush(crossf*.15), bits, over ? 4 : 2, 0);
2016-08-26 09:58:03 +00:00
// queuepoly(V, shHeptaMarker, darkena(tortoise::getMatchColor(bits), 0, 0xC0));
}
2016-01-02 10:09:13 +00:00
else if(it == itCompass) {
2017-03-23 10:53:57 +00:00
if(euclid) Vboat0 = (*Vdp) * spin(M_PI/2); // todo incorrect
else Vboat0 = *Vboat * rspintox(inverse(*Vboat) * pirateCoords);
Vboat0 = Vboat0 * spin(M_PI * sin(ticks/100.) / 30);
queuepoly(Vboat0, shCompass1, 0xFF8080FF);
queuepoly(Vboat0, shCompass2, 0xFFFFFFFF);
queuepoly(Vboat0, shCompass3, 0xFF0000FF);
queuepoly(Vboat0 * pispin, shCompass3, 0x000000FF);
2016-01-02 10:09:13 +00:00
xsh = NULL;
}
else if(it == itPalace) {
2017-03-23 10:53:57 +00:00
Vboat0 = *Vboat * spin(ticks / 1500.);
queuepoly(Vboat0, shMFloor3[ct6], 0xFFD500FF);
queuepoly(Vboat0, shMFloor4[ct6], darkena(icol, 0, 0xFF));
queuepoly(Vboat0, shGem[ct6], 0xFFD500FF);
2016-01-02 10:09:13 +00:00
xsh = NULL;
}
2017-03-23 10:53:57 +00:00
else if(drawUserShape(*Vboat, 2, it, darkena(icol, 0, 0xFF))) ;
2016-08-26 09:58:03 +00:00
else if(it == itRose) {
for(int u=0; u<4; u++)
2017-03-23 10:53:57 +00:00
queuepoly(*Vboat * spin(ticks / 1500.) * spin(2*M_PI / 3 / 4 * u), shRose, darkena(icol, 0, hidden ? 0x30 : 0xA0));
2016-08-26 09:58:03 +00:00
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
else if(it == itBarrow) {
for(int i = 0; i<c->landparam; i++)
queuepolyat(*Vboat * spin(2 * M_PI * i / c->landparam) * xpush(.15) * spin(ticks / 1500.), *xsh, darkena(icol, 0, hidden ? 0x40 :
(highwall(c) && wmspatial) ? 0x60 : 0xFF),
PPR_HIDDEN);
// queuepoly(V*spin(M_PI+(1-2*ang)*2*M_PI/S84), shMagicSword, darkena(0xC00000, 0, 0x80 + 0x70 * sin(ticks / 200.0)));
}
2016-01-02 10:09:13 +00:00
else if(xsh) {
if(it == itFireShard) icol = firecolor(100);
if(it == itWaterShard) icol = watercolor(100) >> 8;
2017-03-23 10:53:57 +00:00
if(it == itZebra) icol = 0xFFFFFF;
2016-08-26 09:58:03 +00:00
if(it == itLotus) icol = 0x101010;
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
queuepoly(*Vboat * spin(ticks / 1500.), *xsh, darkena(icol, 0, hidden ? (it == itKraken ? 0xC0 : 0x40) : 0xF0));
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(xsh == &shBookCover && mmitem)
queuepoly(*Vboat * spin(ticks / 1500.), shBook, 0x805020FF);
2016-01-02 10:09:13 +00:00
if(it == itZebra)
2017-03-23 10:53:57 +00:00
queuepolyat(*Vboat * spin(ticks / 1500. + M_PI/c->type), *xsh, darkena(0x202020, 0, hidden ? 0x40 : 0xF0), PPR_ITEMb);
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
else if(xch == 'o') {
2016-01-02 10:09:13 +00:00
if(it == itOrbFire) icol = firecolor(100);
2017-03-23 10:53:57 +00:00
queuepoly(*Vboat, shDisk, darkena(icol, 0, hidden ? 0x20 : 0xC0));
2016-01-02 10:09:13 +00:00
if(it == itOrbFire) icol = firecolor(200);
if(it == itOrbFriend || it == itOrbDiscord) icol = 0xC0C0C0;
if(it == itOrbFrog) icol = 0xFF0000;
2017-03-23 10:53:57 +00:00
if(it == itOrbDash) icol = 0xFF0000;
2016-08-26 09:58:03 +00:00
if(it == itOrbFreedom) icol = 0xC0FF00;
if(it == itOrbAir) icol = 0xFFFFFF;
if(it == itOrbUndeath) icol = minf[moFriendlyGhost].color;
2017-03-23 10:53:57 +00:00
if(it == itOrbRecall) icol = 0x101010;
2016-08-26 09:58:03 +00:00
hpcshape& sh =
isRangedOrb(it) ? shTargetRing :
isOffensiveOrb(it) ? shSawRing :
isFriendOrb(it) ? shPeaceRing :
isUtilityOrb(it) ? shGearRing :
2017-03-23 10:53:57 +00:00
isDirectionalOrb(it) ? shSpearRing :
2016-08-26 09:58:03 +00:00
it == itOrb37 ? shHeptaRing :
shRing;
2017-03-23 10:53:57 +00:00
queuepoly(*Vboat * spin(ticks / 1500.), sh, darkena(icol, 0, int(0x80 + 0x70 * sin(ticks / 300.))));
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
else if(it) error = true;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(true) {
int q = ptds.size();
error |= drawMonster(V, ct, c, moncol);
if(Vboat != &V && Vboat != &Vboat0 && q != size(ptds))
pushdown(c, q, V, -geom3::factor_to_lev(zlevel(tC0((*Vboat)))),
!isMultitile(c->monst), false);
}
if(!shmup::on && sword::at(c)) {
queuepolyat(V, shDisk, 0xC0404040, PPR_SWORDMARK);
2016-08-26 09:58:03 +00:00
}
2016-01-02 10:09:13 +00:00
if(c->wall == waChasm) zcol = 0;
addaura(tC0(V), zcol, fd);
2016-01-02 10:09:13 +00:00
int ad = airdist(c);
if(ad == 1 || ad == 2) {
for(int i=0; i<c->type; i++) {
2016-08-26 09:58:03 +00:00
cell *c2 = c->mov[i];
2016-01-02 10:09:13 +00:00
if(airdist(c2) < airdist(c)) {
2016-08-26 09:58:03 +00:00
calcAirdir(c2); // printf("airdir = %d\n", airdir);
2017-03-23 10:53:57 +00:00
transmatrix V0 = ddspin(c, i, S42);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
double ph = ticks / (purehepta?150:75.0) + airdir * M_PI / (S21+.0);
2016-01-02 10:09:13 +00:00
int aircol = 0x8080FF00 | int(32 + 32 * -cos(ph));
double ph0 = ph/2;
ph0 -= floor(ph0/M_PI)*M_PI;
2017-03-23 10:53:57 +00:00
poly_outline = OUTLINE_TRANS;
queuepoly((*Vdp)*V0*xpush(hexf*-cos(ph0)), shDisk, aircol);
2016-08-26 09:58:03 +00:00
poly_outline = OUTLINE_NONE;
}
}
2017-03-23 10:53:57 +00:00
// queuepoly(V*ddi(rand() % S84, hexf*(rand()%100)/100), shDisk, aircolor(airdir));
2016-08-26 09:58:03 +00:00
}
/* int rd = rosedist(c);
if(rd > 0 && ((rd&7) == (turncount&7))) {
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
if(rosedist(c2) == rosedist(c)-1) {
int hdir = displaydir(c, i);
2017-03-23 10:53:57 +00:00
transmatrix V0 = spin((S42+hdir) * M_PI / S42);
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
double ph = ticks / 75.0; // + airdir * M_PI / (S21+.0);
2016-08-26 09:58:03 +00:00
int rosecol = 0x764e7c00 | int(32 + 32 * -cos(ph));
double ph0 = ph/2;
ph0 -= floor(ph0/M_PI)*M_PI;
2017-03-23 10:53:57 +00:00
poly_outline = OUTLINE_TRANS;
2016-08-26 09:58:03 +00:00
queuepoly(V*V0*ddi(0, hexf*-cos(ph0)), shDisk, rosecol);
2017-03-23 10:53:57 +00:00
poly_outline = OUTLINE_NONE;
2016-01-02 10:09:13 +00:00
}
}
2016-08-26 09:58:03 +00:00
} */
if(c->land == laWhirlwind) {
whirlwind::calcdirs(c);
for(int i=0; i<whirlwind::qdirs; i++) {
2017-03-23 10:53:57 +00:00
int hdir0 = displaydir(c, whirlwind::dfrom[i]) + S42;
int hdir1 = displaydir(c, whirlwind::dto[i]);
double ph1 = fanframe;
int aircol = 0xC0C0FF40;
ph1 -= floor(ph1);
if(hdir1 < hdir0-S42) hdir1 += S84;
if(hdir1 >= hdir0+S42) hdir1 -= S84;
int hdir = (hdir1*ph1+hdir0*(1-ph1));
transmatrix V0 = spin((hdir) * M_PI / S42);
double ldist = purehepta ? crossf : c->type == 6 ? .2840 : 0.3399;
poly_outline = OUTLINE_TRANS;
queuepoly((*Vdp)*V0*xpush(ldist*(2*ph1-1)), shDisk, aircol);
poly_outline = OUTLINE_NONE;
}
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
if(error) {
2017-03-23 10:53:57 +00:00
queuechr(V, 1, ch, asciicol, 2);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
if(vid.grid) {
// sphere: 0.3948
// sphere heptagonal: 0.5739
// sphere trihepta: 0.3467
// hyper trihepta: 0.2849
// hyper heptagonal: 0.6150
// hyper: 0.3798
if(purehepta) {
double x = sphere?.645:.6150;
for(int t=0; t<S7; t++)
if(c->mov[t] && c->mov[t] < c)
queueline(V * ddspin(c,t,-6) * xpush0(x),
V * ddspin(c,t,6) * xpush0(x),
gridcolor(c, c->mov[t]), 1);
}
else if(isWarped(c)) {
double x = sphere?.3651:euclid?.2611:.2849;
if(!ishept(c)) for(int t=0; t<6; t++) if(c->mov[t] && ishept(c->mov[t]))
queueline(V * ddspin(c,t,-S14) * xpush0(x),
V * ddspin(c,t,+S14) * xpush0(x),
gridcolor(c, c->mov[t]), 1);
}
else if(ishept(c) && !euclid) ;
else {
double x = sphere?.401:euclid?.3 : .328;
for(int t=0; t<6; t++)
if(euclid ? c->mov[t]<c : (((t^1)&1) || c->mov[t] < c))
queueline(V * ddspin(c,t,-S7) * xpush0(x),
V * ddspin(c,t,+S7) * xpush0(x),
gridcolor(c, c->mov[t]), 1);
2016-01-02 10:09:13 +00:00
}
}
2017-03-23 10:53:57 +00:00
2016-08-26 09:58:03 +00:00
if(!euclid && (!pirateTreasureSeek || compassDist(c) < compassDist(pirateTreasureSeek)))
2016-01-02 10:09:13 +00:00
pirateTreasureSeek = c;
2016-08-26 09:58:03 +00:00
if(!euclid) {
bool usethis = false;
double spd = 1;
2017-03-23 10:53:57 +00:00
bool rev = false;
2016-08-26 09:58:03 +00:00
if(isGravityLand(cwt.c->land)) {
2017-03-23 10:53:57 +00:00
if(cwt.c->land == laDungeon) rev = true;
2016-08-26 09:58:03 +00:00
if(!straightDownSeek || edgeDepth(c) < edgeDepth(straightDownSeek)) {
usethis = true;
spd = cwt.c->landparam / 10.;
}
}
2017-03-23 10:53:57 +00:00
if(c->master->alt && cwt.c->master->alt &&
(cwt.c->land == laMountain ||
(pmodel &&
2016-08-26 09:58:03 +00:00
(cwt.c->land == laTemple || cwt.c->land == laWhirlpool ||
(cheater && (cwt.c->land == laClearing || cwt.c->land == laCaribbean ||
2017-03-23 10:53:57 +00:00
cwt.c->land == laCamelot || cwt.c->land == laPalace)))
))
&& c->land == cwt.c->land && c->master->alt->alt == cwt.c->master->alt->alt) {
if(!straightDownSeek || !straightDownSeek->master->alt || celldistAlt(c) < celldistAlt(straightDownSeek)) {
usethis = true;
spd = .5;
if(cwt.c->land == laMountain) rev = true;
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
}
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(pmodel && cwt.c->land == laOcean && cwt.c->landparam < 25) {
if(!straightDownSeek || coastval(c, laOcean) < coastval(straightDownSeek, laOcean)) {
usethis = true;
spd = cwt.c->landparam / 10;
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
2016-08-26 09:58:03 +00:00
}
if(usethis) {
straightDownSeek = c;
2017-03-23 10:53:57 +00:00
downspin = atan2(VC0[1], VC0[0]);
downspin -= M_PI/2;
if(rev) downspin += M_PI;
2016-08-26 09:58:03 +00:00
downspin += M_PI/2 * (conformal::rotation%4);
while(downspin < -M_PI) downspin += 2*M_PI;
while(downspin > +M_PI) downspin -= 2*M_PI;
downspin = downspin * min(spd, (double)1);
2016-01-02 10:09:13 +00:00
}
}
2017-03-23 10:53:57 +00:00
if(!inHighQual) {
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
#ifndef NOEDIT
if(cmode == emMapEditor && !mapeditor::subscreen && lmouseover && darken == 0 &&
2016-01-02 10:09:13 +00:00
(mapeditor::whichPattern ? mapeditor::subpattern(c) == mapeditor::subpattern(lmouseover) : c == lmouseover)) {
2017-03-23 10:53:57 +00:00
queuecircle(V, .78, 0x00FFFFFF);
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
#ifndef NOEDIT
2016-01-02 10:09:13 +00:00
mapeditor::drawGhosts(c, V, ct);
#endif
2016-08-26 09:58:03 +00:00
}
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
if(c->bardir != NODIR && c->bardir != NOBARRIERS && c->land != laHauntedWall &&
c->barleft != NOWALLSEP_USED) {
2017-03-23 10:53:57 +00:00
int col = darkena(0x505050, 0, 0xFF);
queueline(tC0(V), V*tC0(heptmove[c->bardir]), col, 2);
queueline(tC0(V), V*tC0(hexmove[c->bardir]), col, 2);
2015-08-08 13:57:52 +00:00
}
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
#ifndef NOMODEL
2016-08-26 09:58:03 +00:00
netgen::buildVertexInfo(c, V);
#endif
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
#ifdef LOCAL
extern void localdraw (const transmatrix& V, cell *c);
localdraw(V, c);
#endif
2015-08-08 13:57:52 +00:00
}
}
2017-03-23 10:53:57 +00:00
bool confusingGeometry() {
return elliptic || quotient == 1;
}
struct flashdata {
int t;
int size;
cell *where;
double angle;
int spd; // 0 for flashes, >0 for particles
int color;
flashdata(int _t, int _s, cell *_w, int col, int sped) {
t=_t; size=_s; where=_w; color = col;
angle = rand() % 1000; spd = sped;
}
};
vector<flashdata> flashes;
void drawFlash(cell *c) {
flashes.push_back(flashdata(ticks, 1000, c, iinf[itOrbFlash].color, 0));
}
void drawBigFlash(cell *c) {
flashes.push_back(flashdata(ticks, 2000, c, 0xC0FF00, 0));
}
void drawParticle(cell *c, int col, int maxspeed) {
if(vid.particles && !confusingGeometry())
flashes.push_back(flashdata(ticks, rand() % 16, c, col, 1+rand() % maxspeed));
}
void drawParticles(cell *c, int col, int qty, int maxspeed) {
if(vid.particles)
while(qty--) drawParticle(c,col, maxspeed);
}
void drawFireParticles(cell *c, int qty, int maxspeed) {
if(vid.particles)
for(int i=0; i<qty; i++)
drawParticle(c, firegradient(i / (qty-1.)), maxspeed);
}
void fallingFloorAnimation(cell *c, eWall w, eMonster m) {
if(!wmspatial) return;
fallanim& fa = fallanims[c];
fa.t_floor = ticks;
fa.walltype = w; fa.m = m;
// drawParticles(c, darkenedby(linf[c->land].color, 1), 4, 50);
}
void fallingMonsterAnimation(cell *c, eMonster m) {
if(!mmspatial) return;
fallanim& fa = fallanims[c];
fa.t_mon = ticks;
fa.m = m;
// drawParticles(c, darkenedby(linf[c->land].color, 1), 4, 50);
}
void queuecircleat(cell *c, double rad, int col) {
if(!c) return;
if(!gmatrix.count(c)) return;
queuecircle(gmatrix[c], rad, col);
if(!wmspatial) return;
if(highwall(c))
queuecircle(mscale(gmatrix[c], geom3::WALL), rad, col);
int sl;
if((sl = snakelevel(c))) {
queuecircle(mscale(gmatrix[c], geom3::SLEV[sl]), rad, col);
}
if(chasmgraph(c))
queuecircle(mscale(gmatrix[c], geom3::LAKE), rad, col);
}
#define G(x) x && gmatrix.count(x)
#define IG(x) if(G(x))
#define Gm(x) gmatrix[x]
#define Gm0(x) tC0(gmatrix[x])
#ifdef MOBILE
#define MOBON (clicked)
#else
#define MOBON true
#endif
void drawMarkers() {
if(darken || cmode == emNumber) return;
if(!inHighQual) {
#ifdef PANDORA
bool ok = mousepressed;
#else
bool ok = true;
#endif
if(G(dragon::target) && haveMount()) {
queuechr(Gm0(dragon::target), 2*vid.fsize, 'X',
gradient(0, iinf[itOrbDomination].color, -1, sin(ticks/(dragon::whichturn == turncount ? 75. : 150.)), 1));
}
/* for(int i=0; i<12; i++) if(c->type == 5 && c->master == &dodecahedron[i])
queuechr(xc, yc, sc, 4*vid.fsize, 'A'+i, iinf[itOrbDomination].color); */
IG(keycell) {
queuechr(Gm0(keycell), 2*vid.fsize, 'X', 0x10101 * int(128 + 100 * sin(ticks / 150.)));
queuestr(Gm0(keycell), vid.fsize, its(keycelldist), 0x10101 * int(128 - 100 * sin(ticks / 150.)));
}
IG(pirateTreasureFound) {
pirateCoords = Gm0(pirateTreasureFound);
if(showPirateX) {
queuechr(pirateCoords, 2*vid.fsize, 'X', 0x10100 * int(128 + 100 * sin(ticks / 150.)));
if(numplayers() == 1 && cwt.c->master->alt)
queuestr(pirateCoords, vid.fsize, its(-celldistAlt(cwt.c)), 0x10101 * int(128 - 100 * sin(ticks / 150.)));
}
}
if(lmouseover && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON) {
queuecircleat(lmouseover, .8, darkena(lmouseover->cpdist > 1 ? 0x00FFFF : 0xFF0000, 0, 0xFF));
}
if(global_pushto && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON) {
queuecircleat(global_pushto, .6, darkena(0xFFD500, 0, 0xFF));
}
if(joydir.d >= 0)
queuecircleat(cwt.c->mov[(joydir.d+cwt.spin) % cwt.c->type], .78 - .02 * sin(ticks/199.0),
darkena(0x00FF00, 0, 0xFF));
#ifndef NOMODEL
if(centerover && !playermoved && netgen::mode == 0 && !conformal::on)
queuecircleat(centerover, .70 - .06 * sin(ticks/200.0),
darkena(int(175 + 25 * sin(ticks / 200.0)), 0, 0xFF));
#endif
if(multi::players > 1 || multi::alwaysuse) for(int i=0; i<numplayers(); i++) {
multi::cpid = i;
if(multi::players == 1) multi::player[i] = cwt;
cell *ctgt = multi::multiPlayerTarget(i);
queuecircleat(ctgt, .40 - .06 * sin(ticks/200.0 + i * 2 * M_PI / numplayers()), getcs().uicolor);
}
// process mouse
#ifdef MOBILE
extern bool useRangedOrb;
if(canmove && !shmup::on && andmode == 0 && !useRangedOrb && vid.mobilecompasssize > 0) {
using namespace shmupballs;
calc();
queuecircle(xmove, yb, rad, 0xFF0000FF);
2017-03-31 19:41:09 +00:00
queuecircle(xmove, yb, rad*SKIPFAC,
legalmoves[7] ? 0xFF0000FF : 0xFF000080
);
2017-03-23 10:53:57 +00:00
forCellAll(c2, cwt.c) IG(c2) drawMobileArrow(c2, Gm(c2));
}
#endif
if((vid.axes == 4 || (vid.axes == 1 && !mousing)) && !shmup::on) {
if(multi::players == 1) {
forCellAll(c2, cwt.c) IG(c2) drawMovementArrows(c2, Gm(c2));
}
else if(multi::players > 1) for(int p=0; p<multi::players; p++) {
if(multi::playerActive(p) && (vid.axes == 4 || !drawstaratvec(multi::mdx[p], multi::mdy[p])))
forCellAll(c2, multi::player[p].c) IG(c2) {
multi::cpid = p;
dynamicval<transmatrix> ttm(cwtV, multi::whereis[p]);
dynamicval<cellwalker> tcw(cwt, multi::player[p]);
drawMovementArrows(c2, Gm(c2));
}
}
}
}
monsterToSummon = moNone;
orbToTarget = itNone;
if(mouseover && targetclick && cmode == emNormal) {
shmup::cpid = 0;
orbToTarget = targetRangedOrb(mouseover, roCheck);
if(orbToTarget == itOrbSummon) {
monsterToSummon = summonedAt(mouseover);
queuechr(mousex, mousey, 0, vid.fsize, minf[monsterToSummon].glyph, minf[monsterToSummon].color);
queuecircleat(mouseover, 0.6, darkena(minf[monsterToSummon].color, 0, 0xFF));
}
else if(orbToTarget) {
queuechr(mousex, mousey, 0, vid.fsize, '@', iinf[orbToTarget].color);
queuecircleat(mouseover, 0.6, darkena(iinf[orbToTarget].color, 0, 0xFF));
}
if(orbToTarget && rand() % 200 < ticks - lastt) {
if(orbToTarget == itOrbDragon)
drawFireParticles(mouseover, 2);
else if(orbToTarget == itOrbSummon) {
drawParticles(mouseover, iinf[orbToTarget].color, 1);
drawParticles(mouseover, minf[monsterToSummon].color, 1);
}
else {
drawParticles(mouseover, iinf[orbToTarget].color, 2);
}
}
}
}
void drawFlashes() {
for(int k=0; k<size(flashes); k++) {
flashdata& f = flashes[k];
transmatrix V = gmatrix[f.where];
int tim = ticks - f.t;
bool kill = tim > f.size;
if(f.spd) {
kill = tim > 300;
int partcol = darkena(f.color, 0, max(255 - kill/2, 0));
poly_outline = OUTLINE_NONE;
queuepoly(V * spin(f.angle) * xpush(f.spd * tim / 50000.), shParticle[f.size], partcol);
}
else if(f.size == 1000) {
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 flashcol = f.color;
if(u > 500) flashcol = gradient(flashcol, 0, 500, u, 1100);
flashcol = darkena(flashcol, 0, 0xFF);
for(int a=0; a<=S84; a++) curvepoint(V*ddi0(a, rad));
queuecurve(flashcol, 0x8080808, PPR_LINE);
}
}
else if(f.size == 2000) {
for(int u=0; u<=tim; u++) {
if((u-tim)%50) continue;
if(u < tim-250) continue;
ld rad = u * 3 / 2000.;
rad = rad * (5-rad) * 1.25;
rad *= hexf;
int flashcol = f.color;
if(u > 1000) flashcol = gradient(flashcol, 0, 1000, u, 2200);
flashcol = darkena(flashcol, 0, 0xFF);
for(int a=0; a<=S84; a++) curvepoint(V*ddi0(a, rad));
queuecurve(flashcol, 0x8080808, PPR_LINE);
}
}
if(kill) {
f = flashes[size(flashes)-1];
flashes.pop_back(); k--;
}
}
}
2015-08-08 13:57:52 +00:00
string buildCredits();
string buildHelpText() {
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"buildHelpText\n"));
#ifdef ROGUEVIZ
if(rogueviz::on) return rogueviz::help();
#endif
2015-08-08 13:57:52 +00:00
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(
2016-01-02 10:09:13 +00:00
"You have been trapped in a strange, non-Euclidean world. Collect as much treasure as possible "
"before being caught by monsters. The more treasure you collect, the more "
"monsters come to hunt you, as long as you are in the same land type. The "
"Orbs of Yendor are the ultimate treasure; get at least one of them to win the game!"
2015-08-08 13:57:52 +00:00
);
h += XLAT(" (press ESC for some hints about it).");
h += "\n\n";
h += XLAT(
2016-01-02 10:09:13 +00:00
"You can fight most monsters by moving into their location. "
"The monster could also kill you by moving into your location, but the game "
"automatically cancels all moves which result in that.\n\n"
2015-08-08 13:57:52 +00:00
);
2017-03-23 10:53:57 +00:00
h += XLAT(
"There are many lands in HyperRogue. Collect 10 treasure "
"in the given land type to complete it; this enables you to "
"find the magical Orbs of this land, and in some cases "
"get access to new lands. At 25 treasures "
"this type of Orbs starts appearing in other lands as well. Press 'o' to "
"get the details of all the Lands.\n\n");
h += "\n\n";
2015-08-08 13:57:52 +00:00
#ifdef MOBILE
h += XLAT(
2016-01-02 10:09:13 +00:00
"Usually, you move by touching somewhere on the map; you can also touch one "
"of the four buttons on the map corners to change this (to scroll the map "
"or get information about map objects). You can also touch the "
"numbers displayed to get their meanings.\n"
2015-08-08 13:57:52 +00:00
);
#else
h += XLAT(
2016-01-02 10:09:13 +00:00
"Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Home/Space. "
2017-03-23 10:53:57 +00:00
"To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n"
2015-08-08 13:57:52 +00:00
);
h += XLAT(
"You can right click any element to get more information about it.\n\n"
);
2017-03-23 10:53:57 +00:00
h += XLAT("(You can also use right Shift)\n\n");
2015-08-08 13:57:52 +00:00
#endif
h += XLAT("See more on the website: ")
2017-03-31 19:41:09 +00:00
+ "http//roguetemple.com/z/hyper/\n\n";
2017-04-08 15:18:29 +00:00
#ifdef TOUR
h += XLAT("Try the Tutorial to help with understanding the "
"geometry of HyperRogue (menu -> special modes).\n\n");
#endif
2016-01-02 10:09:13 +00:00
h += XLAT("Still confused? Read the FAQ on the HyperRogue website!\n\n");
2015-08-08 13:57:52 +00:00
return h;
}
string buildCredits() {
string h;
h += XLAT("game design, programming, texts and graphics by Zeno Rogue <zeno@attnam.com>\n\n");
if(lang() != 0)
h += XLAT("add credits for your translation here");
#ifndef NOLICENSE
h += XLAT(
2016-01-02 10:09:13 +00:00
"released under GNU General Public License version 2 and thus "
"comes with absolutely no warranty; see COPYING for details\n\n"
2015-08-08 13:57:52 +00:00
);
#endif
h += XLAT(
2016-01-02 10:09:13 +00:00
"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, "
2017-03-23 10:53:57 +00:00
"wonderfullizardofoz, Piotr Migdał, tehora, Michael Heerdegen, Sprite Guard, zelda0x181e, Vipul, snowyowl0, Patashu, phenomist, Alan Malloy, Tom Fryers, Sinquetica, _monad, CtrlAltDestroy, jruderman"
2015-08-08 13:57:52 +00:00
);
#ifdef EXTRALICENSE
h += EXTRALICENSE;
#endif
2017-03-23 10:53:57 +00:00
#ifndef MOBILE
h += XLAT(
"\n\nSee sounds/credits.txt for credits for sound effects"
);
#endif
2015-08-08 13:57:52 +00:00
if(musiclicense != "") h += musiclicense;
return h;
}
2016-01-02 10:09:13 +00:00
string pushtext(stringpar p) {
string s = XLAT(
"\n\nNote: when pushing %the1 off a heptagonal cell, you can control the pushing direction "
"by clicking left or right half of the heptagon.", p);
#ifndef MOBILE
s += XLAT(" With the keyboard, you can rotate the view for a similar effect (Page Up/Down).");
#endif
return s;
}
string princedesc() {
2016-08-26 09:58:03 +00:00
if(princessgender() == GEN_M)
2016-01-02 10:09:13 +00:00
return XLAT("Apparently a prince is kept locked somewhere, but you won't ever find him in this hyperbolic palace. ");
else
return XLAT("Apparently a princess is kept locked somewhere, but you won't ever find her in this hyperbolic palace. ");
}
2017-03-23 10:53:57 +00:00
string helptitle(string s, int col) {
return "@" + its(col) + "\t" + s + "\n";
}
string princessReviveHelp() {
string h = "\n\n" +
XLAT("Killed %1 can be revived with Orb of the Love, after you collect 20 more $$$.", moPrincess);
if(princess::reviveAt)
h += "\n\n" +
XLAT("%The1 will be revivable at %2 $$$", moPrincess, its(princess::reviveAt));
return h;
}
2015-08-08 13:57:52 +00:00
string generateHelpForItem(eItem it) {
2017-03-23 10:53:57 +00:00
string help = helptitle(XLATN(iinf[it].name), iinf[it].color);
help += XLAT(iinf[it].help);
if(it == itSavedPrincess || it == itOrbLove)
help += princessReviveHelp();
if(it == itTrollEgg)
help += XLAT("\n\nAfter the Trolls leave, you have 750 turns to collect %the1, or it gets stolen.", it);
if(it == itIvory || it == itAmethyst || it == itLotus || it == itMutant) {
help += XLAT(
"\n\nEasy %1 might disappear when you collect more of its kind.", it);
if(it != itMutant) help += XLAT(
" You need to go deep to collect lots of them.");
}
#ifdef MOBILE
2015-08-08 13:57:52 +00:00
if(it == itOrbSafety)
2017-03-23 10:53:57 +00:00
help += XLAT("This might be very useful for devices with limited memory.");
2015-08-08 13:57:52 +00:00
#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");
2016-08-26 09:58:03 +00:00
#endif
2017-03-23 10:53:57 +00:00
if(isRangedOrb(it)) {
help += XLAT("\nThis is a ranged Orb. ");
#ifdef ISMOBILE
if(vid.shifttarget&2)
help += XLAT("\nRanged Orbs can be targeted by long touching the desired location.");
else
help += XLAT("\nRanged Orbs can be targeted by touching the desired location.");
#else
if(vid.shifttarget&1)
help += XLAT("\nRanged Orbs can be targeted by shift-clicking the desired location. "
else
help += XLAT("\nRanged Orbs can be targeted by clicking the desired location. ");
help += "You can also scroll to the desired location and then press 't'.");
2016-08-26 09:58:03 +00:00
#endif
2017-03-23 10:53:57 +00:00
help += XLAT("\nYou can never target cells which are adjacent to the player character, or ones out of the sight range.");
}
2016-08-26 09:58:03 +00:00
#ifdef MOBILE
if(it == itGreenStone)
help += XLAT("You can touch the Dead Orb in your inventory to drop it.");
#else
2015-08-08 13:57:52 +00:00
if(it == itGreenStone)
help += XLAT("You can press 'g' or click them in the list to drop a Dead Orb.");
#endif
2017-03-23 10:53:57 +00:00
if(it == itOrbLightning || it == itOrbFlash)
help += XLAT("\n\nThis Orb is triggered on your first attack or illegal move.");
if(it == itOrbShield)
help += XLAT("\n\nThis Orb protects you from attacks, scents, and insulates you "
"from electricity. It does not let you go through deadly terrain, but "
"if you are attacked with fire, it lets you stay in place in it.");
2016-08-26 09:58:03 +00:00
if(it == itOrbEmpathy) {
int cnt = 0;
for(int i=0; i<ittypes; i++) {
eItem it2 = eItem(i);
if(isEmpathyOrb(it2)) {
help += XLAT(cnt ? ", %1" : " %1", it2);
cnt++;
}
}
}
2017-03-23 10:53:57 +00:00
if(itemclass(it) == IC_ORB || it == itGreenStone || it == itOrbYendor) {
eOrbLandRelation olr = getOLR(it, cwt.c->land);
for(int i=0; i<ORBLINES; i++) {
orbinfo& oi(orbinfos[i]);
if(oi.orb == it) {
eItem tr = treasureType(oi.l);
help += "\n\n" + XLAT(olrDescriptions[olr], cwt.c->land, tr, treasureType(cwt.c->land));
int t = items[tr] * landMultiplier(oi.l);
if(t >= 25)
if(olr == olrPrize25 || olr == olrPrize3 || olr == olrGuest || olr == olrMonster || olr == olrAlways) {
help += XLAT("\nSpawn rate (as prize Orb): %1%/%2\n",
its(int(.5 + 100 * orbprizefun(t))),
its(oi.gchance));
}
if(t >= 10)
if(olr == olrHub) {
help += XLAT("\nSpawn rate (in Hubs): %1%/%2\n",
its(int(.5 + 100 * orbcrossfun(t))),
its(oi.gchance));
}
}
}
}
2015-08-08 13:57:52 +00:00
return help;
}
2017-03-23 10:53:57 +00:00
void addMinefieldExplanation(string& s) {
s += XLAT(
"\n\nOnce you collect 10 Bomberbird Eggs, "
"stepping on a cell with no adjacent mines also reveals the adjacent cells. "
"Collecting even more Eggs will increase the radius. Additionally, collecting "
"25 Bomberbird Eggs will reveal adjacent cells even in your future games."
);
s += "\n\n";
#ifdef MOBILE
s += XLAT("Known mines may be marked by pressing 'm'. Your allies won't step on marked mines.");
#else
s += XLAT("Known mines may be marked by touching while in drag mode. Your allies won't step on marked mines.");
#endif
}
2016-01-02 10:09:13 +00:00
string generateHelpForWall(eWall w) {
2017-03-23 10:53:57 +00:00
string s = helptitle(XLATN(winf[w].name), winf[w].color);
s += XLAT(winf[w].help);
if(w == waMineMine || w == waMineUnknown || w == waMineOpen)
addMinefieldExplanation(s);
2016-01-02 10:09:13 +00:00
if(isThumper(w)) s += pushtext(w);
2016-08-26 09:58:03 +00:00
if((w == waClosePlate || w == waOpenPlate) && purehepta)
s += "\n\n(For the heptagonal mode, the radius has been reduced to 2 for closing plates.)";
2016-01-02 10:09:13 +00:00
return s;
}
2017-03-23 10:53:57 +00:00
void buteol(string& s, int current, int req) {
int siz = size(s);
if(s[siz-1] == '\n') s.resize(siz-1);
char buf[100]; sprintf(buf, " (%d/%d)", current, req);
s += buf; s += "\n";
}
2016-01-02 10:09:13 +00:00
string generateHelpForMonster(eMonster m) {
2017-03-23 10:53:57 +00:00
string s = helptitle(XLATN(minf[m].name), minf[m].color);
if(m == moPlayer) {
#ifdef TOUR
if(tour::on)
return s+XLAT(
"A tourist from another world. They mutter something about the 'tutorial', "
"and claim that they are here just to learn, and to leave without any treasures. "
"Do not kill them!"
);
#endif
s += XLAT(
"This monster has come from another world, presumably to steal our treasures. "
"Not as fast as an Eagle, not as resilient as the guards from the Palace, "
"and not as huge as the Mutant Ivy from the Clearing; however, "
"they are very dangerous because of their intelligence, "
"and access to magical powers.\n\n");
if(cheater)
s += XLAT("Actually, their powers appear god-like...\n\n");
else if(!hardcore)
s += XLAT(
"Rogues will never make moves which result in their immediate death. "
"Even when cornered, they are able to instantly teleport back to their "
"home world at any moment, taking the treasures forever... but "
"at least they will not steal anything further!\n\n"
);
if(!euclid)
s += XLAT(
"Despite this intelligence, Rogues appear extremely surprised "
"by the most basic facts about geometry. They must come from "
"some really strange world.\n\n"
);
if(shmup::on)
s += XLAT("In the Shoot'em Up mode, you are armed with thrown Knives.");
return s;
}
2017-03-23 10:53:57 +00:00
s += XLAT(minf[m].help);
2016-01-02 10:09:13 +00:00
if(m == moPalace || m == moSkeleton)
s += pushtext(m);
if(m == moTroll) s += XLAT(trollhelp2);
2016-08-26 09:58:03 +00:00
if(isMonsterPart(m))
s += XLAT("\n\nThis is a part of a monster. It does not count for your total kills.", m);
if(isFriendly(m))
s += XLAT("\n\nThis is a friendly being. It does not count for your total kills.", m);
if(m == moTortoise)
s += XLAT("\n\nTortoises are not monsters! They are just annoyed. They do not count for your total kills.", m);
if(isGhost(m))
s += XLAT("\n\nA Ghost never moves to a cell which is adjacent to another Ghost of the same kind.", m);
2017-03-23 10:53:57 +00:00
if(m == moBat || m == moEagle)
s += XLAT("\n\nFast flying creatures may attack or go against gravity only in their first move.", m);
2016-01-02 10:09:13 +00:00
return s;
}
string generateHelpForLand(eLand l) {
2017-03-23 10:53:57 +00:00
string s = helptitle(XLATN(linf[l].name), linf[l].color);
if(l == laPalace) s += princedesc();
s += XLAT(linf[l].help);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(l == laMinefield) addMinefieldExplanation(s);
2016-01-02 10:09:13 +00:00
s += "\n\n";
if(l == laIce || l == laCaves || l == laDesert || l == laMotion || l == laJungle ||
2016-08-26 09:58:03 +00:00
l == laCrossroads || l == laAlchemist)
2016-01-02 10:09:13 +00:00
s += XLAT("Always available.\n");
2016-08-26 09:58:03 +00:00
#define ACCONLY(z) s += XLAT("Accessible only from %the1.\n", z);
#define ACCONLY2(z,x) s += XLAT("Accessible only from %the1 or %the2.\n", z, x);
#define ACCONLYF(z) s += XLAT("Accessible only from %the1 (until finished).\n", z);
2017-03-23 10:53:57 +00:00
#define TREQ(z) { s += XLAT("Treasure required: %1 $$$.\n", #z); buteol(s, gold(), z); }
#define TREQ2(z,x) { s += XLAT("Treasure required: %1 x %2.\n", #z, x); buteol(s, items[x], z); }
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
if(l == laMirror || l == laMinefield || l == laPalace ||
2017-03-23 10:53:57 +00:00
l == laOcean || l == laLivefjord || l == laZebra || l == laWarpCoast || l == laWarpSea ||
l == laReptile || l == laIvoryTower)
2016-08-26 09:58:03 +00:00
TREQ(30)
2017-03-23 10:53:57 +00:00
if(isCoastal(l))
s += XLAT("Coastal region -- connects inland and aquatic regions.\n");
if(isPureSealand(l))
s += XLAT("Aquatic region -- accessible only from coastal regions and other aquatic regions.\n");
if(l == laWhirlpool) ACCONLY(laOcean)
2016-08-26 09:58:03 +00:00
if(l == laRlyeh) ACCONLYF(laOcean)
if(l == laTemple) ACCONLY(laRlyeh)
if(l == laClearing) ACCONLY(laOvergrown)
if(l == laHaunted) ACCONLY(laGraveyard)
if(l == laPrincessQuest) ACCONLY(laPalace)
2017-03-23 10:53:57 +00:00
if(l == laMountain) ACCONLY(laJungle)
2016-08-26 09:58:03 +00:00
if(l == laCamelot) ACCONLY2(laCrossroads, laCrossroads3)
2016-01-02 10:09:13 +00:00
if(l == laDryForest || l == laWineyard || l == laDeadCaves || l == laHive || l == laRedRock ||
2017-03-23 10:53:57 +00:00
l == laOvergrown || l == laStorms || l == laWhirlwind || l == laRose ||
l == laCrossroads2 || l == laRlyeh)
2016-08-26 09:58:03 +00:00
TREQ(60)
2017-03-23 10:53:57 +00:00
if(l == laReptile) TREQ2(10, itElixir)
if(l == laEndorian) TREQ2(10, itIvory)
if(l == laKraken) TREQ2(10, itFjord)
if(l == laBurial) TREQ2(10, itKraken)
if(l == laDungeon) TREQ2(5, itIvory)
if(l == laDungeon) TREQ2(5, itPalace)
if(l == laMountain) TREQ2(5, itIvory)
if(l == laMountain) TREQ2(5, itRuby)
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(l == laPrairie) TREQ(90)
if(l == laBull) TREQ(90)
2016-08-26 09:58:03 +00:00
if(l == laCrossroads4) TREQ(200)
2017-03-23 10:53:57 +00:00
if(l == laCrossroads5) TREQ(300)
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(l == laGraveyard || l == laHive) {
2016-01-02 10:09:13 +00:00
s += XLAT("Kills required: %1.\n", "100");
2017-03-23 10:53:57 +00:00
buteol(s, tkills(), 100);
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(l == laDragon) {
2016-08-26 09:58:03 +00:00
s += XLAT("Different kills required: %1.\n", "20");
2017-03-23 10:53:57 +00:00
buteol(s, killtypes(), 20);
}
2016-08-26 09:58:03 +00:00
if(l == laTortoise) ACCONLY(laDragon)
if(l == laTortoise) s += XLAT("Find a %1 in %the2.", itBabyTortoise, laDragon);
2017-03-23 10:53:57 +00:00
if(l == laHell || l == laCrossroads3) {
2016-01-02 10:09:13 +00:00
s += XLAT("Finished lands required: %1 (collect 10 treasure)\n", "9");
2017-03-23 10:53:57 +00:00
buteol(s, orbsUnlocked(), 9);
}
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
if(l == laCocytus || l == laPower) TREQ2(10, itHell)
if(l == laRedRock) TREQ2(10, itSpice)
if(l == laOvergrown) TREQ2(10, itRuby)
if(l == laClearing) TREQ2(5, itMutant)
if(l == laCocytus) TREQ2(10, itDiamond)
if(l == laDeadCaves) TREQ2(10, itGold)
if(l == laTemple) TREQ2(5, itStatue)
if(l == laHaunted) TREQ2(10, itBone)
if(l == laCamelot) TREQ2(5, itEmerald)
2016-01-02 10:09:13 +00:00
if(l == laEmerald) {
2016-08-26 09:58:03 +00:00
TREQ2(5, itFernFlower) TREQ2(5, itGold)
2016-01-02 10:09:13 +00:00
s += XLAT("Alternatively: kill a %1 in %the2.\n", moVizier, laPalace);
2017-03-23 10:53:57 +00:00
buteol(s, kills[moVizier], 1);
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
#define KILLREQ(who, where) { s += XLAT("Kills required: %1 (%2).\n", who, where); buteol(s, kills[who], 1); }
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(l == laPrincessQuest)
KILLREQ(moVizier, laPalace);
2016-08-26 09:58:03 +00:00
if(l == laElementalWall) {
2017-03-23 10:53:57 +00:00
KILLREQ(moFireElemental, laDragon);
KILLREQ(moEarthElemental, laDeadCaves);
KILLREQ(moWaterElemental, laLivefjord);
KILLREQ(moAirElemental, laWhirlwind);
}
if(l == laTrollheim) {
KILLREQ(moTroll, laCaves);
KILLREQ(moFjordTroll, laLivefjord);
KILLREQ(moDarkTroll, laDeadCaves);
KILLREQ(moStormTroll, laStorms);
KILLREQ(moForestTroll, laOvergrown);
KILLREQ(moRedTroll, laRedRock);
2016-08-26 09:58:03 +00:00
}
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
if(l == laZebra) TREQ2(10, itFeather)
2017-03-23 10:53:57 +00:00
if(l == laCamelot || l == laPrincessQuest)
s += XLAT("Completing the quest in this land is not necessary for the Hyperstone Quest.");
int rl = isRandland(l);
if(rl == 2)
s += XLAT("Variants of %the1 are always available in the Random Pattern Mode.", l);
else if(rl == 1)
s += XLAT(
"Variants of %the1 are available in the Random Pattern Mode after "
"getting a highscore of at least 10 %2.", l, treasureType(l));
if(l == laPrincessQuest) {
s += XLAT("Unavailable in the shmup mode.\n");
s += XLAT("Unavailable in the multiplayer mode.\n");
}
if(noChaos(l))
s += XLAT("Unavailable in the Chaos mode.\n");
if(l == laWildWest)
s += XLAT("Bonus land, available only in some special modes.\n");
if(l == laWhirlpool)
s += XLAT("Orbs of Safety always appear here, and may be used to escape.\n");
/* if(isHaunted(l) || l == laDungeon)
s += XLAT("You may be unable to leave %the1 if you are not careful!\n", l); */
if(l == laStorms) {
if(elec::lightningfast == 0) s += XLAT("\nSpecial conduct (still valid)\n");
else s += XLAT("\nSpecial conduct failed:\n");
s += XLAT(
"Avoid escaping from a discharge (\"That was close\").");
}
if(isHaunted(l)) {
if(survivalist) s += XLAT("\nSpecial conduct (still valid)\n");
else s += XLAT("\nSpecial conduct failed:\n");
2016-08-26 09:58:03 +00:00
2016-01-02 10:09:13 +00:00
s += XLAT(
2017-03-23 10:53:57 +00:00
"Avoid chopping trees, using Orbs, and non-graveyard monsters in the Haunted Woods."
);
}
#ifndef ISMOBILE
if(l == laCA)
s += XLAT("\n\nHint: use 'm' to toggle cells quickly");
#endif
2016-01-02 10:09:13 +00:00
return s;
}
2017-03-23 10:53:57 +00:00
bool instat;
string turnstring(int i) {
if(i == 1) return XLAT("1 turn");
else return XLAT("%1 turns", its(i));
}
2015-08-08 13:57:52 +00:00
void describeMouseover() {
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"describeMouseover\n"));
2015-08-08 13:57:52 +00:00
cell *c = mousing ? mouseover : playermoved ? NULL : centerover;
string out = mouseovers;
2017-03-23 10:53:57 +00:00
if(!c || instat || getcstat) { }
2016-01-02 10:09:13 +00:00
else if(cmode == emNormal || cmode == emQuit || cmode == emMapEditor) {
2015-08-08 13:57:52 +00:00
out = XLAT1(linf[c->land].name);
2016-01-02 10:09:13 +00:00
help = generateHelpForLand(c->land);
// Celsius
2015-08-08 13:57:52 +00:00
// if(c->land == laIce) out = "Icy Lands (" + fts(60 * (c->heat - .4)) + " C)";
2017-03-23 10:53:57 +00:00
2016-01-02 10:09:13 +00:00
if(c->land == laIce || c->land == laCocytus)
2016-08-26 09:58:03 +00:00
out += " (" + fts(heat::celsius(c)) + " °C)";
2016-01-02 10:09:13 +00:00
if(c->land == laDryForest && c->landparam)
out += " (" + its(c->landparam)+"/10)";
2016-08-26 09:58:03 +00:00
if(c->land == laOcean && chaosmode)
out += " (" + its(c->CHAOSPARAM)+"S"+its(c->SEADIST)+"L"+its(c->LANDDIST)+")";
2017-03-23 10:53:57 +00:00
else if(c->land == laOcean && c->landparam <= 25) {
if(shmup::on)
out += " (" + its(c->landparam)+")";
else {
bool b = c->landparam >= tide[(turncount-1) % tidalsize];
int t = 1;
for(; t < 1000 && b == (c->landparam >= tide[(turncount+t-1) % tidalsize]); t++) ;
if(b)
out += " (" + turnstring(t) + XLAT(" to surface") + ")";
2017-03-23 10:53:57 +00:00
else
out += " (" + turnstring(t) + XLAT(" to submerge") + ")";
2017-03-23 10:53:57 +00:00
}
}
2016-08-26 09:58:03 +00:00
if(c->land == laTortoise && tortoise::seek()) out += " " + tortoise::measure(getBits(c));
/* if(c->land == laGraveyard || c->land == laHauntedBorder || c->land == laHaunted)
out += " (" + its(c->landparam)+")"; */
2016-01-02 10:09:13 +00:00
if(buggyGeneration) {
char buf[20]; sprintf(buf, " H=%d M=%d", c->landparam, c->mpdist); out += buf;
}
// if(c->land == laBarrier)
// out += "(" + string(linf[c->barleft].name) + " / " + string(linf[c->barright].name) + ")";
2016-08-26 09:58:03 +00:00
// out += "(" + its(c->bardir) + ":" + string(linf[c->barleft].name) + " / " + string(linf[c->barright].name) + ")";
2016-01-02 10:09:13 +00:00
// out += " MD"+its(c->mpdist);
2016-08-26 09:58:03 +00:00
// out += " WP:" + its(c->wparam);
// out += " rose:" + its(rosemap[c]/4) + "." + its(rosemap[c]%4);
// out += " MP:" + its(c->mpdist);
// out += " cda:" + its(celldistAlt(c));
/* out += " DP=" + its(celldistance(c, cwt.c));
out += " DO=" + its(celldist(c));
out += " PD=" + its(c->pathdist); */
if(false) {
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
out += " LP:" + itsh(c->landparam)+"/"+its(turncount);
out += " CD:" + its(celldist(c));
2017-03-23 10:53:57 +00:00
out += " D:" + its(c->mpdist);
2016-08-26 09:58:03 +00:00
char zz[64]; sprintf(zz, " P%p", c); out += zz;
// out += " rv" + its(rosedist(c));
// if(rosemap.count(c))
// out += " rv " + its(rosemap[c]/8) + "." + its(rosemap[c]%8);
// out += " ai" + its(c->aitmp);
if(euclid) {
for(int i=0; i<4; i++) out += " " + its(getEuclidCdata(c->master)->val[i]);
out += " " + itsh(getBits(c));
}
else {
for(int i=0; i<4; i++) out += " " + its(getHeptagonCdata(c->master)->val[i]);
// out += " " + itsh(getHeptagonCdata(c->master)->bits);
out += " " + fts(tortoise::getScent(getBits(c)));
}
// itsh(getHeptagonCdata(c->master)->bits);
// out += " barleft: " + s0 + dnameof(c->barleft);
// out += " barright: " + s0 + dnameof(c->barright);
}
2016-01-02 10:09:13 +00:00
// char zz[64]; sprintf(zz, " P%p", c); out += zz;
2016-08-26 09:58:03 +00:00
/* whirlwind::calcdirs(c);
for(int i=0; i<whirlwind::qdirs; i++)
out += " " + its(whirlwind::dfrom[i]) + ":" + its(whirlwind::dto[i]); */
// out += " : " + its(whirlwinddir(c));
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
if(randomPatternsMode)
out += " " + describeRPM(c->land);
if(euclid && cheater) {
eucoord x, y;
decodeMaster(c->master, x, y);
out += " ("+its(short(x))+","+its(short(y))+")";
}
2016-01-02 10:09:13 +00:00
// char zz[64]; sprintf(zz, " P%d", princess::dist(c)); out += zz;
// out += " MD"+its(c->mpdist);
// out += " H "+its(c->heat);
2017-03-23 10:53:57 +00:00
// if(c->type != 6) out += " Z"+its(c->master->zebraval);
2016-01-02 10:09:13 +00:00
// out += " H"+its(c->heat);
2015-08-08 13:57:52 +00:00
/* // 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; i<size(bugqueue[k]); i++)
if(bugqueue[k][i] == c->tmp)
out += " B"+its(k)+":"+its(i);
}
out += "]";
} */
2016-01-02 10:09:13 +00:00
if(c->wall &&
2016-08-26 09:58:03 +00:00
!((c->wall == waFloorA || c->wall == waFloorB || c->wall == waFloorC || c->wall == waFloorD) && c->item)) {
2015-08-08 13:57:52 +00:00
out += ", "; out += XLAT1(winf[c->wall].name);
2016-08-26 09:58:03 +00:00
if(c->wall == waRose) out += " (" + its(7-rosephase) + ")";
if((c->wall == waBigTree || c->wall == waSmallTree) && c->land != laDryForest)
help =
2017-03-23 10:53:57 +00:00
"Trees in this forest can be chopped down. Big trees take two turns to chop down.";
2016-08-26 09:58:03 +00:00
else if(c->wall != waSea && c->wall != waPalace)
2016-01-02 10:09:13 +00:00
if(!((c->wall == waCavefloor || c->wall == waCavewall) && c->land == laEmerald))
help = generateHelpForWall(c->wall);
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
if(isActivable(c)) out += XLAT(" (touch to activate)");
if(hasTimeout(c)) out += XLAT(" [" + turnstring(c->wparam) + "]");
2017-03-23 10:53:57 +00:00
if(isReptile(c->wall))
out += XLAT(" [" + turnstring((unsigned char) c->wparam) + "]");
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
if(c->monst) {
out += ", "; out += XLAT1(minf[c->monst].name);
if(hasHitpoints(c->monst))
out += " (" + its(c->hitpoints)+" HP)";
2016-08-26 09:58:03 +00:00
if(isMutantIvy(c))
out += " (" + its((c->stuntime - mutantphase) & 15) + "*)";
else if(c->stuntime)
2016-01-02 10:09:13 +00:00
out += " (" + its(c->stuntime) + "*)";
2016-08-26 09:58:03 +00:00
if(c->monst == moTortoise && tortoise::seek())
out += " " + tortoise::measure(tortoise::getb(c));
2016-01-02 10:09:13 +00:00
help = generateHelpForMonster(c->monst);
}
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
if(c->item && !itemHiddenFromSight(c)) {
2015-08-08 13:57:52 +00:00
out += ", ";
out += XLAT1(iinf[c->item].name);
2017-03-23 10:53:57 +00:00
if(c->item == itBarrow) out += " (x" + its(c->landparam) + ")";
2016-08-26 09:58:03 +00:00
if(c->item == itBabyTortoise && tortoise::seek())
out += " " + tortoise::measure(tortoise::babymap[c]);
2015-08-08 13:57:52 +00:00
if(!c->monst) help = generateHelpForItem(c->item);
}
if(isPlayerOn(c) && !shmup::on) out += XLAT(", you"), help = generateHelpForMonster(moPlayer);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(shmup::mousetarget && intval(mouseh, tC0(shmup::mousetarget->pat)) < .1) {
out += ", ";
#ifdef ROGUEVIZ
if(shmup::mousetarget->type == moRogueviz) {
help = XLAT(minf[shmup::mousetarget->type].help);
out += rogueviz::describe(shmup::mousetarget);
}
else
#endif
{
out += XLAT1(minf[shmup::mousetarget->type].name);
help = generateHelpForMonster(shmup::mousetarget->type);
2017-03-23 10:53:57 +00:00
}
2016-01-02 10:09:13 +00:00
/* char buf[64];
sprintf(buf, "%Lf", intval(mouseh, shmup::mousetarget->pat*C0));
mouseovers = mouseovers + " D: " + buf;
printf("ms = %s\n", mouseovers.c_str());*/
2016-08-26 09:58:03 +00:00
}
if(rosedist(c) == 1)
out += ", wave of scent (front)";
if(rosedist(c) == 2)
out += ", wave of scent (back)";
2017-03-23 10:53:57 +00:00
if(sword::at(c)) out += ", Energy Sword";
2016-08-26 09:58:03 +00:00
if(rosedist(c) || c->land == laRose || c->wall == waRose)
help += s0 + "\n\n" + rosedesc;
if(isWarped(c) && !isWarped(c->land))
out += ", warped";
if(isWarped(c))
help += s0 + "\n\n" + warpdesc;
2016-01-02 10:09:13 +00:00
2015-08-08 13:57:52 +00:00
}
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!");
2016-08-26 09:58:03 +00:00
if(vid.alpha > 5)
out = XLAT("(press 'i' to approach infinity (Gans model)");
2015-08-08 13:57:52 +00:00
}
else if(getcstat == 'r') {
out = XLAT("simply resize the window to change resolution");
}
2017-03-23 10:53:57 +00:00
/* else if(getcstat == 'f') {
2015-08-08 13:57:52 +00:00
out = XLAT("[+] keep the window size, [-] use the screen resolution");
2017-03-23 10:53:57 +00:00
} */
else if(getcstat == 'a' && vid.sspeed > -4.99)
2015-08-08 13:57:52 +00:00
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) {
2016-01-02 10:09:13 +00:00
if(getcstat == 'p') {
2015-08-08 13:57:52 +00:00
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");
}
2016-01-02 10:09:13 +00:00
else if(cmode == emChangeMode) {
if(getcstat == 'h')
out = XLAT("One wrong move and it is game over!");
}
2015-08-08 13:57:52 +00:00
mouseovers = out;
2016-01-02 10:09:13 +00:00
int col = linf[cwt.c->land].color;
if(cwt.c->land == laRedRock) col = 0xC00000;
2017-03-23 10:53:57 +00:00
#ifndef MOBILE
displayfr(vid.xres/2, vid.fsize, 2, vid.fsize, out, col, 8);
2016-08-26 09:58:03 +00:00
#endif
2017-03-23 10:53:57 +00:00
2015-08-08 13:57:52 +00:00
if(mousey < vid.fsize * 3/2) getcstat = SDLK_F1;
2016-01-02 10:09:13 +00:00
if(false && shmup::mousetarget) {
char buf[64];
2017-03-23 10:53:57 +00:00
sprintf(buf, "%Lf", (long double) intval(mouseh, tC0(shmup::mousetarget->pat)));
2016-01-02 10:09:13 +00:00
mouseovers = mouseovers + " D: " + buf;
return;
}
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
void drawrec(const heptspin& hs, int lev, hstate s, const transmatrix& V) {
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
// shmup::calc_relative_matrix(cwt.c, hs.h);
2016-08-26 09:58:03 +00:00
2015-08-08 13:57:52 +00:00
cell *c = hs.h->c7;
2017-03-23 10:53:57 +00:00
transmatrix V10;
const transmatrix& V1 = hs.mirrored ? (V10 = V * Mirror) : V;
if(dodrawcell(c)) {
reclevel = maxreclevel - lev;
drawcell(c, (hs.spin || purehepta) ? V1 * spin(hs.spin*2*M_PI/S7 + (purehepta ? M_PI:0)) : V1, hs.spin,
hs.mirrored);
}
2015-08-08 13:57:52 +00:00
if(lev <= 0) return;
2017-03-23 10:53:57 +00:00
if(!purehepta) for(int d=0; d<S7; d++) {
2015-08-08 13:57:52 +00:00
int ds = fixrot(hs.spin + d);
2017-03-23 10:53:57 +00:00
reclevel = maxreclevel - lev + 1;
2015-08-08 13:57:52 +00:00
// createMov(c, ds);
2017-03-23 10:53:57 +00:00
if(c->mov[ds] && c->spn(ds) == 0 && dodrawcell(c->mov[ds])) {
drawcell(c->mov[ds], V1 * hexmove[d], 0, hs.mirrored ^ c->mirror(ds));
}
2015-08-08 13:57:52 +00:00
}
if(lev <= 1) return;
2017-03-23 10:53:57 +00:00
for(int d=0; d<S7; d++) {
2015-08-08 13:57:52 +00:00
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;
2017-03-23 10:53:57 +00:00
transmatrix eumove(int x, int y) {
transmatrix Mat = Id;
Mat[2][2] = 1;
Mat[0][2] += (x + y * .5) * eurad;
// Mat[2][0] += (x + y * .5) * eurad;
Mat[1][2] += y * q3 /2 * eurad;
// Mat[2][1] += 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;
return Mat;
}
2015-08-08 13:57:52 +00:00
void drawEuclidean() {
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"drawEuclidean\n"));
2015-08-08 13:57:52 +00:00
eucoord px, py;
2017-03-23 10:53:57 +00:00
if(!centerover) centerover = cwt.c;
2016-01-02 10:09:13 +00:00
// printf("centerover = %p player = %p [%d,%d]-[%d,%d]\n", lcenterover, cwt.c,
// mindx, mindy, maxdx, maxdy);
2017-03-23 10:53:57 +00:00
decodeMaster(centerover->master, px, py);
2015-08-08 13:57:52 +00:00
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;
2017-03-23 10:53:57 +00:00
reclevel = eudist(dx, dy);
2015-08-08 13:57:52 +00:00
cell *c = euclideanAt(x,y);
if(!c) continue;
2017-03-23 10:53:57 +00:00
transmatrix Mat = eumove(x, y);
2015-08-08 13:57:52 +00:00
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;
2017-03-23 10:53:57 +00:00
getcoord0(tC0(Mat), cx, cy, shift);
2015-08-08 13:57:52 +00:00
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;
}
2017-03-23 10:53:57 +00:00
if(dodrawcell(c)) {
drawcell(c, Mat, 0, false);
}
2015-08-08 13:57:52 +00:00
}
}
void drawthemap() {
2017-03-23 10:53:57 +00:00
frameid++;
if(!cheater && !svg::in && !inHighQual) {
if(sightrange > 7) sightrange = 7;
overgenerate = false;
}
profile_frame();
profile_start(0);
swap(gmatrix0, gmatrix);
gmatrix.clear();
wmspatial = vid.wallmode == 4 || vid.wallmode == 5;
wmescher = vid.wallmode == 3 || vid.wallmode == 5;
wmplain = vid.wallmode == 2 || vid.wallmode == 4;
wmascii = vid.wallmode == 0;
wmblack = vid.wallmode == 1;
mmitem = vid.monmode >= 1;
mmmon = vid.monmode >= 2;
mmhigh = vid.monmode == 3 || vid.monmode == 5;
mmspatial = vid.monmode == 4 || vid.monmode == 5;
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"draw the map\n"));
2017-03-23 10:53:57 +00:00
fanframe = ticks / (purehepta ? 300 : 150.0) / M_PI;
2016-08-26 09:58:03 +00:00
2016-01-02 10:09:13 +00:00
for(int m=0; m<motypes; m++) if(isPrincess(eMonster(m)))
minf[m].name = princessgender() ? "Princess" : "Prince";
2016-08-26 09:58:03 +00:00
2016-01-02 10:09:13 +00:00
iinf[itSavedPrincess].name = minf[moPrincess].name;
2016-08-26 09:58:03 +00:00
for(int i=0; i<NUM_GS; i++) {
genderswitch_t& g = genderswitch[i];
if(g.gender != princessgender()) continue;
minf[g.m].help = g.desc;
minf[g.m].name = g.name;
}
2015-08-08 13:57:52 +00:00
keycell = NULL;
2016-01-02 10:09:13 +00:00
pirateTreasureFound = pirateTreasureSeek;
pirateTreasureSeek = NULL;
straightDownSeek = NULL; downspin = 0;
shmup::mousetarget = NULL;
2017-03-23 10:53:57 +00:00
showPirateX = false;
for(int i=0; i<numplayers(); i++) if(multi::playerActive(i))
if(playerpos(i)->item == itCompass) showPirateX = true;
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
using namespace yendor;
2015-08-08 13:57:52 +00:00
if(yii < size(yi)) {
2016-08-26 09:58:03 +00:00
if(!yi[yii].found)
for(int i=0; i<YDIST; i++)
if(yi[yii].path[i]->cpdist <= sightrange) {
2015-08-08 13:57:52 +00:00
keycell = yi[yii].path[i];
keycelldist = YDIST - i;
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
modist = 1e20; mouseover = NULL;
modist2 = 1e20; mouseover2 = NULL;
mouseovers = XLAT("Press F1 or right click for help");
2017-03-31 19:41:09 +00:00
#ifdef ROGUEVIZ
if(rogueviz::on) mouseovers = " ";
2017-04-08 15:18:29 +00:00
#endif
#ifdef TOUR
if(tour::on) mouseovers = tour::tourhelp;
2017-03-31 19:41:09 +00:00
#endif
2017-03-23 10:53:57 +00:00
centdist = 1e20; centerover = NULL;
for(int i=0; i<multi::players; i++) {
multi::ccdist[i] = 1e20; multi::ccat[i] = NULL;
}
2015-08-08 13:57:52 +00:00
#ifdef MOBILE
mouseovers = XLAT("No info about this...");
#endif
if(outofmap(mouseh))
modist = -5;
playerfound = false;
2016-01-02 10:09:13 +00:00
// playerfoundL = false;
// playerfoundR = false;
2017-03-23 10:53:57 +00:00
sphereflip = Id;
profile_start(1);
2015-08-08 13:57:52 +00:00
if(euclid)
drawEuclidean();
2017-03-23 10:53:57 +00:00
else {
if(sphere && vid.alpha > 1) sphereflip[2][2] = -1;
maxreclevel =
2016-08-26 09:58:03 +00:00
conformal::on ? sightrange + 2:
2017-03-23 10:53:57 +00:00
(!playermoved) ? sightrange+1 : sightrange + 4;
drawrec(viewctr,
maxreclevel,
hsOrigin, ypush(vid.yshift) * sphereflip * View);
}
linepatterns::drawAll();
2017-03-23 10:53:57 +00:00
#ifdef ROGUEVIZ
rogueviz::drawExtra();
#endif
2017-04-08 15:18:29 +00:00
#ifdef TOUR
if(tour::on) tour::presentation(tour::pmFrame);
2017-04-08 15:18:29 +00:00
#endif
2017-03-23 10:53:57 +00:00
profile_stop(1);
profile_start(4);
drawMarkers();
profile_stop(4);
drawFlashes();
if(multi::players > 1 && !shmup::on) {
if(shmup::centerplayer != -1)
cwtV = multi::whereis[shmup::centerplayer];
else {
hyperpoint h;
for(int i=0; i<3; i++) h[i] = 0;
for(int p=0; p<multi::players; p++) if(multi::playerActive(p)) {
hyperpoint h1 = tC0(multi::whereis[p]);
for(int i=0; i<3; i++) h[i] += h1[i];
}
h = mid(h, h);
cwtV = rgpushxto0(h);
}
}
2016-01-02 10:09:13 +00:00
if(shmup::on) {
if(shmup::players == 1)
cwtV = shmup::pc[0]->pat;
2016-08-26 09:58:03 +00:00
else if(shmup::centerplayer != -1)
cwtV = shmup::pc[shmup::centerplayer]->pat;
2017-03-23 10:53:57 +00:00
else {
hyperpoint h;
for(int i=0; i<3; i++) h[i] = 0;
for(int p=0; p<multi::players; p++) {
hyperpoint h1 = tC0(shmup::pc[p]->pat);
for(int i=0; i<3; i++) h[i] += h1[i];
}
h = mid(h, h);
cwtV = rgpushxto0(h);
}
}
#ifndef NOSDL
2017-03-23 10:53:57 +00:00
Uint8 *keystate = SDL_GetKeyState(NULL);
lmouseover = mouseover;
bool useRangedOrb = (!(vid.shifttarget & 1) && haveRangedOrb() && lmouseover && lmouseover->cpdist > 1) || (keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]);
if(!useRangedOrb && cmode != emMapEditor && DEFAULTCONTROL && !outofmap(mouseh)) {
void calcMousedest();
calcMousedest();
cellwalker cw = cwt; bool f = flipplayer;
items[itWarning]+=2;
bool recorduse[ittypes];
for(int i=0; i<ittypes; i++) recorduse[i] = orbused[i];
movepcto(mousedest.d, mousedest.subdir, true);
for(int i=0; i<ittypes; i++) orbused[i] = recorduse[i];
items[itWarning] -= 2;
if(multi::players == 1 && cw.spin != cwt.spin) mirror::spin(-mousedest.d);
cwt = cw; flipplayer = f;
lmouseover = mousedest.d >= 0 ? cwt.c->mov[(cwt.spin + mousedest.d) % cwt.c->type] : cwt.c;
}
2016-08-26 09:58:03 +00:00
#endif
2017-03-23 10:53:57 +00:00
profile_stop(0);
2016-01-02 10:09:13 +00:00
}
void spinEdge(ld aspd) {
if(downspin > aspd) downspin = aspd;
if(downspin < -aspd) downspin = -aspd;
View = spin(downspin) * View;
2015-08-08 13:57:52 +00:00
}
void centerpc(ld aspd) {
2017-03-23 10:53:57 +00:00
if(vid.sspeed >= 4.99) aspd = 1000;
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"center pc\n"));
2017-03-23 10:53:57 +00:00
hyperpoint H = ypush(-vid.yshift) * sphereflip * tC0(cwtV);
if(H[0] == 0 && H[1] == 0) return; // either already centered or direction unknown
ld R = hdist0(H); // = sqrt(H[0] * H[0] + H[1] * H[1]);
2015-08-08 13:57:52 +00:00
if(R < 1e-9) {
2016-01-02 10:09:13 +00:00
/* if(playerfoundL && playerfoundR) {
} */
spinEdge(aspd);
2017-03-23 10:53:57 +00:00
fixmatrix(View);
2015-08-08 13:57:52 +00:00
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 {
2016-01-02 10:09:13 +00:00
aspd *= (1+R+(shmup::on?1:0));
2015-08-08 13:57:52 +00:00
if(R < aspd) {
View = gpushxto0(H) * View;
}
else
View = rspintox(H) * xpush(-aspd) * spintox(H) * View;
2016-01-02 10:09:13 +00:00
fixmatrix(View);
spinEdge(aspd);
2015-08-08 13:57:52 +00:00
}
}
2017-03-23 10:53:57 +00:00
void drawmovestar(double dx, double dy) {
2015-08-08 13:57:52 +00:00
if(viewdists) return;
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"draw movestar\n"));
2015-08-08 13:57:52 +00:00
if(!playerfound) return;
2016-01-02 10:09:13 +00:00
if(shmup::on) return;
2017-03-23 10:53:57 +00:00
#ifndef NORUG
if(rug::rugged && multi::players == 1 && !multi::alwaysuse) return;
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
hyperpoint H = tC0(cwtV);
2015-08-08 13:57:52 +00:00
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);
2017-03-23 10:53:57 +00:00
Centered = Centered * rgpushxto0(hpxy(dx*5, dy*5));
if(multi::cpid >= 0) multi::crosscenter[multi::cpid] = Centered;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int rax = vid.axes;
if(rax == 1) rax = drawstaratvec(dx, dy) ? 2 : 0;
if(rax == 0 || vid.axes == 4) return;
int starcol = getcs().uicolor;
if(vid.axes == 3)
queuepoly(Centered, shMovestar, starcol);
2015-08-08 13:57:52 +00:00
else for(int d=0; d<8; d++) {
int col = starcol;
#ifdef PANDORA
2017-03-23 10:53:57 +00:00
if(leftclick && (d == 2 || d == 6 || d == 1 || d == 7)) col &= 0xFFFFFF3F;
if(rightclick && (d == 2 || d == 6 || d == 3 || d == 5)) col &= 0xFFFFFF3F;
if(!leftclick && !rightclick && (d&1)) col &= 0xFFFFFF3F;
2015-08-08 13:57:52 +00:00
#endif
// EUCLIDEAN
if(euclid)
2017-03-23 10:53:57 +00:00
queueline(tC0(Centered), Centered * ddi0(d * 10.5, 0.5) , col, 0);
2015-08-08 13:57:52 +00:00
else
2017-03-23 10:53:57 +00:00
// queueline(tC0(Centered), Centered * spin(M_PI*d/4)* xpush(d==0?.7:d==2?.6:.5) * C0, col >> darken);
queueline(tC0(Centered), Centered * xspinpush0(M_PI*d/4, d==0?.7:d==2?.5:.2), col, 3);
2015-08-08 13:57:52 +00:00
}
}
void optimizeview() {
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"optimize view\n"));
2015-08-08 13:57:52 +00:00
int turn = 0;
ld best = INF;
2016-08-26 09:58:03 +00:00
transmatrix TB = Id;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
for(int i=-1; i<S7; i++) {
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
ld trot = -i * M_PI * 2 / (S7+.0);
transmatrix T = i < 0 ? Id : spin(trot) * xpush(tessf) * pispin;
hyperpoint H = View * tC0(T);
2015-08-08 13:57:52 +00:00
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);
}
}
2016-01-02 10:09:13 +00:00
movedir vectodir(const hyperpoint& P) {
2017-03-23 10:53:57 +00:00
hyperpoint H = sphereflip * tC0(cwtV);
2015-08-08 13:57:52 +00:00
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
2017-03-23 10:53:57 +00:00
transmatrix Centered = sphereflip * cwtV;
2015-08-08 13:57:52 +00:00
if(!euclid)
2017-03-23 10:53:57 +00:00
Centered = gpushxto0(H) * Centered;
2015-08-08 13:57:52 +00:00
else if(R > 1e-9)
Centered = eupush(-H[0], -H[1]) * Centered;
ld binv = 99;
2016-01-02 10:09:13 +00:00
ld dirdist[7];
2017-03-23 10:53:57 +00:00
for(int i=0; i<cwt.c->type; i++) {
dirdist[i] = intval(Centered * xspinpush0(-i * 2 * M_PI /cwt.c->type, .5), P);
}
2016-01-02 10:09:13 +00:00
movedir res;
res.d = -1;
2015-08-08 13:57:52 +00:00
for(int i=0; i<cwt.c->type; i++) {
2016-01-02 10:09:13 +00:00
if(dirdist[i] < binv) {
binv = dirdist[i];
res.d = i;
res.subdir = dirdist[(i+1)%cwt.c->type] < dirdist[(i+cwt.c->type-1)%cwt.c->type] ? 1 : -1;
2017-03-23 10:53:57 +00:00
if(sphere) res.subdir = -res.subdir;
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
2015-08-08 13:57:52 +00:00
// if(euclid) bdir = (bdir + 3) % 6;
2016-01-02 10:09:13 +00:00
return res;
2015-08-08 13:57:52 +00:00
}
void movepckeydir(int d) {
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"movepckeydir\n"));
2015-08-08 13:57:52 +00:00
// EUCLIDEAN
2017-03-23 10:53:57 +00:00
movedir md =
vectodir(spin(-d * M_PI/4) * tC0(pushone()));
movepcto(md);
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
void calcMousedest() {
if(outofmap(mouseh)) return;
2016-08-26 09:58:03 +00:00
if(revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
2017-03-23 10:53:57 +00:00
ld mousedist = intval(mouseh, tC0(shmup::ggmatrix(cwt.c)));
2016-01-02 10:09:13 +00:00
mousedest.d = -1;
2017-03-23 10:53:57 +00:00
cellwalker bcwt = cwt;
2016-01-02 10:09:13 +00:00
ld dists[7];
2017-03-23 10:53:57 +00:00
for(int i=0; i<cwt.c->type; i++)
dists[i] = intval(mouseh, tC0(shmup::ggmatrix(cwt.c->mov[i])));
2016-01-02 10:09:13 +00:00
/* printf("curcell = %Lf\n", mousedist);
for(int i=0; i<cwt.c->type; i++)
printf("d%d = %Lf\n", i, dists[i]); */
for(int i=0; i<cwt.c->type; i++) if(dists[i] < mousedist) {
mousedist = dists[i];
mousedest.d = fixdir(i - cwt.spin, cwt.c);
mousedest.subdir =
dists[(i+1)%cwt.c->type] < dists[(i+cwt.c->type-1)%cwt.c->type] ? 1 : -1;
2017-03-23 10:53:57 +00:00
if(cwt.mirrored)
mousedest.d = fixdir(-mousedest.d, cwt.c),
mousedest.subdir = -mousedest.subdir;
if(sphere) mousedest.subdir = -mousedest.subdir;
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
if(revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
cwt = bcwt;
2016-01-02 10:09:13 +00:00
}
void mousemovement() {
calcMousedest();
movepcto(mousedest);
2017-03-23 10:53:57 +00:00
lmouseover = NULL;
2016-01-02 10:09:13 +00:00
}
long double sqr(long double x) { return x*x; }
2017-03-23 10:53:57 +00:00
// old style joystick control
2015-08-08 13:57:52 +00:00
void checkjoy() {
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"check joy\n"));
2017-03-23 10:53:57 +00:00
if(!DEFAULTCONTROL) return;
2016-01-02 10:09:13 +00:00
ld joyvalue1 = sqr(vid.joyvalue);
ld joyvalue2 = sqr(vid.joyvalue2);
2015-08-08 13:57:52 +00:00
ld jx = joyx;
ld jy = joyy;
ld sq = jx*jx+jy*jy;
2017-03-23 10:53:57 +00:00
static int laststate = 0;
int curstate = sq < joyvalue1 ? 0 : sq < joyvalue2 ? 1 : 2;
if(curstate != laststate) flashMessages(), laststate = curstate;
2015-08-08 13:57:52 +00:00
if(autojoy) {
2016-01-02 10:09:13 +00:00
if(sq < joyvalue1) { if(joydir.d >= 0) movepcto(joydir); joydir.d = -1; return; }
if(sq < joyvalue2 && joydir.d == -1) return;
2015-08-08 13:57:52 +00:00
}
else {
2016-01-02 10:09:13 +00:00
if(sq < joyvalue1) { joydir.d = -1; return; }
2015-08-08 13:57:52 +00:00
}
joydir = vectodir(hpxy(jx, jy));
}
void checkpanjoy(double t) {
2016-01-02 10:09:13 +00:00
if(shmup::on) return;
if(vid.joypanspeed < 1e-7) return;
if(sqr(panjoyx) + sqr(panjoyy) < sqr(vid.joypanthreshold))
2015-08-08 13:57:52 +00:00
return;
ld jx = panjoyx * t * vid.joypanspeed;
ld jy = panjoyy * t * vid.joypanspeed;
playermoved = false;
View = gpushxto0(hpxy(jx, jy)) * View;
}
2017-03-23 10:53:57 +00:00
int realradius;
bool sidescreen;
bool dronemode;
2015-08-08 13:57:52 +00:00
void calcparam() {
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"calc param\n"));
2015-08-08 13:57:52 +00:00
vid.xcenter = vid.xres / 2;
vid.ycenter = vid.yres / 2;
2017-03-23 10:53:57 +00:00
realradius = min(vid.xcenter, vid.ycenter);
2015-08-08 13:57:52 +00:00
vid.radius = int(vid.scale * vid.ycenter) - (ISANDROID ? 2 : ISIOS ? 40 : 40);
2017-03-23 10:53:57 +00:00
realradius = min(realradius, vid.radius);
sidescreen = false;
2015-08-08 13:57:52 +00:00
if(vid.xres < vid.yres) {
vid.radius = int(vid.scale * vid.xcenter) - (ISIOS ? 10 : 2);
2017-03-23 10:53:57 +00:00
vid.ycenter = vid.yres - realradius - vid.fsize - (ISIOS ? 10 : 0);
2015-08-08 13:57:52 +00:00
}
else {
2017-04-08 15:18:29 +00:00
if(vid.xres >= vid.yres * 5/4-16 && dialog::sidedialog && cmode == emNumber)
sidescreen = true;
2017-04-08 15:18:29 +00:00
if(viewdists && cmode == emNormal && vid.xres > vid.yres) sidescreen = true;
if(sidescreen) vid.xcenter = vid.yres/2;
}
if(dronemode) { vid.ycenter -= vid.radius; vid.ycenter += vid.fsize/2; vid.ycenter += vid.fsize/2; vid.radius *= 2; }
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
ld eye = vid.eye; if(pmodel || rug::rugged) eye = 0;
vid.beta = 1 + vid.alpha + eye;
vid.alphax = vid.alpha + eye;
2015-08-08 13:57:52 +00:00
vid.goteyes = vid.eye > 0.001 || vid.eye < -0.001;
2016-08-26 09:58:03 +00:00
vid.goteyes2 = vid.goteyes;
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
void displayButton(int x, int y, const string& name, int key, int align, int rad) {
2015-08-08 13:57:52 +00:00
if(displayfr(x, y, rad, vid.fsize, name, 0x808080, align)) {
displayfr(x, y, rad, vid.fsize, name, 0xFFFF00, align);
getcstat = key;
}
}
2017-03-23 10:53:57 +00:00
bool displayButtonS(int x, int y, const string& name, int col, int align, int size) {
if(displaystr(x, y, 0, size, name, col, align)) {
displaystr(x, y, 0, size, name, 0xFFFF00, align);
return true;
}
else return false;
}
2016-01-02 10:09:13 +00:00
void displayColorButton(int x, int y, const string& name, int key, int align, int rad, int color, int color2) {
if(displayfr(x, y, rad, vid.fsize, name, color, align)) {
if(color2) displayfr(x, y, rad, vid.fsize, name, color2, align);
getcstat = key;
}
}
2016-08-26 09:58:03 +00:00
#ifndef MOBILE
2015-08-08 13:57:52 +00:00
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);
2016-08-26 09:58:03 +00:00
displayButton(vid.xres/2, y + vid.fsize*10/2, XLAT("or 'v' to see the main menu"), 'v', 8, 2);
2016-01-02 10:09:13 +00:00
displayButton(vid.xres/2, y + vid.fsize*13/2, XLAT("or 'o' to see the world overview"), 'o', 8, 2);
2015-08-08 13:57:52 +00:00
}
#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() {
2016-08-26 09:58:03 +00:00
int timespent = (int) (savetime + (timerstopped ? 0 : (time(NULL) - timerstart)));
2015-08-08 13:57:52 +00:00
char buf[20];
sprintf(buf, "%d:%02d", timespent/60, timespent % 60);
2016-01-02 10:09:13 +00:00
return
shmup::on ?
XLAT("%1 knives (%2)", its(turncount), buf)
:
XLAT("%1 turns (%2)", its(turncount), buf);
}
2015-08-08 13:57:52 +00:00
void showGameover() {
2017-03-23 10:53:57 +00:00
dialog::init(
2017-04-08 15:18:29 +00:00
#ifdef TOUR
tour::on ? (canmove ? XLAT("Tutorial") : XLAT("GAME OVER")) :
#endif
2015-08-08 13:57:52 +00:00
cheater ? XLAT("It is a shame to cheat!") :
showoff ? XLAT("Showoff mode") :
2016-01-02 10:09:13 +00:00
canmove && princess::challenge ? XLAT("%1 Challenge", moPrincess) :
2015-08-08 13:57:52 +00:00
canmove ? XLAT("Quest status") :
2017-03-23 10:53:57 +00:00
XLAT("GAME OVER"),
0xC00000, 200, 100
2015-08-08 13:57:52 +00:00
);
2017-03-23 10:53:57 +00:00
dialog::addInfo(XLAT("Your score: %1", its(gold())));
dialog::addInfo(XLAT("Enemies killed: %1", its(tkills())));
#ifdef TOUR
if(tour::on) ; else
#endif
if(items[itOrbYendor]) {
2017-03-23 10:53:57 +00:00
dialog::addInfo(XLAT("Orbs of Yendor found: %1", its(items[itOrbYendor])), iinf[itOrbYendor].color);
dialog::addInfo(XLAT("CONGRATULATIONS!"), iinf[itOrbYendor].color);
2015-08-08 13:57:52 +00:00
}
else {
2017-03-23 10:53:57 +00:00
if(princess::challenge)
dialog::addInfo(XLAT("Follow the Mouse and escape with %the1!", moPrincess));
2016-01-02 10:09:13 +00:00
else if(gold() < 30)
2017-03-23 10:53:57 +00:00
dialog::addInfo(XLAT("Collect 30 $$$ to access more worlds"));
2015-08-08 13:57:52 +00:00
else if(gold() < 60)
2017-03-23 10:53:57 +00:00
dialog::addInfo(XLAT("Collect 60 $$$ to access even more lands"));
2015-08-08 13:57:52 +00:00
else if(!hellUnlocked())
2017-03-23 10:53:57 +00:00
dialog::addInfo(XLAT("Collect at least 10 treasures in each of 9 types to access Hell"));
2015-08-08 13:57:52 +00:00
else if(items[itHell] < 10)
2017-03-23 10:53:57 +00:00
dialog::addInfo(XLAT("Collect at least 10 Demon Daisies to find the Orbs of Yendor"));
2016-08-26 09:58:03 +00:00
else if(size(yendor::yi) == 0)
2017-03-23 10:53:57 +00:00
dialog::addInfo(XLAT("Look for the Orbs of Yendor in Hell or in the Crossroads!"));
2015-08-08 13:57:52 +00:00
else
2017-03-23 10:53:57 +00:00
dialog::addInfo(XLAT("Unlock the Orb of Yendor!"));
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
if(!timerstopped && !canmove) {
savetime += time(NULL) - timerstart;
timerstopped = true;
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
if(canmove && !timerstart)
timerstart = time(NULL);
if(princess::challenge) ;
2017-04-08 15:18:29 +00:00
#ifdef TOUR
else if(tour::on) ;
#endif
2017-03-23 10:53:57 +00:00
else if(tkills() < 100)
dialog::addInfo(XLAT("Defeat 100 enemies to access the Graveyard"));
else if(kills[moVizier] == 0 && (items[itFernFlower] < 5 || items[itGold] < 5))
dialog::addInfo(XLAT("Kill a Vizier in the Palace to access Emerald Mine"));
2016-01-02 10:09:13 +00:00
else if(items[itEmerald] < 5)
2017-03-23 10:53:57 +00:00
dialog::addInfo(XLAT("Collect 5 Emeralds to access Camelot"));
else if(hellUnlocked() && !chaosmode) {
2015-08-08 13:57:52 +00:00
bool b = true;
2016-08-26 09:58:03 +00:00
for(int i=0; i<LAND_HYP; i++)
if(b && items[treasureType(land_hyp[i])] < 10) {
2017-03-23 10:53:57 +00:00
dialog::addInfo(
2016-08-26 09:58:03 +00:00
XLAT(
land_hyp[i] == laTortoise ? "Hyperstone Quest: collect at least 10 points in %the2" :
"Hyperstone Quest: collect at least 10 %1 in %the2",
2017-03-23 10:53:57 +00:00
treasureType(land_hyp[i]), land_hyp[i]));
2015-08-08 13:57:52 +00:00
b = false;
}
2017-03-23 10:53:57 +00:00
if(b)
dialog::addInfo(XLAT("Hyperstone Quest completed!"), iinf[itHyperstone].color);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else dialog::addInfo(XLAT("Some lands unlock at specific treasures or kills"));
if(cheater) {
dialog::addInfo(XLAT("you have cheated %1 times", its(cheater)), 0xFF2020);
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
if(!cheater) {
2017-03-23 10:53:57 +00:00
dialog::addInfo(timeline(), 0xC0C0C0);
2015-08-08 13:57:52 +00:00
}
msgs.clear();
if(msgscroll < 0) msgscroll = 0;
int gls = size(gamelog) - msgscroll;
int mnum = 0;
for(int i=gls-5; i<gls; i++)
if(i>=0) {
msginfo m;
m.spamtype = 0;
m.flashout = true;
m.stamp = ticks-128*vid.flashtime-128*(gls-i);
2017-03-23 10:53:57 +00:00
m.msg = gamelog[i].msg;
m.quantity = gamelog[i].quantity;
2015-08-08 13:57:52 +00:00
mnum++,
msgs.push_back(m);
}
2017-03-23 10:53:57 +00:00
dialog::addBreak(100);
2017-04-08 15:18:29 +00:00
bool intour = false;
#ifdef TOUR
intour = tour::on;
#endif
if(intour) {
#ifdef TOUR
2017-04-08 15:18:29 +00:00
if(canmove) {
dialog::addItem("spherical geometry", '1');
dialog::addItem("Euclidean geometry", '2');
dialog::addItem("more curved hyperbolic geometry", '3');
}
if(!items[itOrbTeleport])
dialog::addItem("teleport away", '4');
else if(!items[itOrbAether])
dialog::addItem("move through walls", '4');
else
dialog::addItem("flash", '4');
2017-04-08 15:18:29 +00:00
if(canmove) {
if(tour::slidecommand != "")
dialog::addItem(tour::slidecommand, '5');
2017-04-08 15:18:29 +00:00
dialog::addItem("static mode", '6');
dialog::addItem("enable/disable texts", '7');
dialog::addItem("next slide", SDLK_RETURN);
dialog::addItem("previous slide", SDLK_BACKSPACE);
}
else
dialog::addBreak(200);
2017-04-08 15:18:29 +00:00
dialog::addItem("main menu", 'v');
#endif
2017-04-08 15:18:29 +00:00
}
else {
dialog::addItem(canmove ? "continue" : "see how it ended", SDLK_ESCAPE);
dialog::addItem("main menu", 'v');
dialog::addItem("restart", SDLK_F5);
#ifndef MOBILE
dialog::addItem(quitsaves() ? "save" : "quit", SDLK_F10);
#endif
#ifdef ANDROIDSHARE
dialog::addItem("SHARE", 's'-96);
#endif
}
2017-03-23 10:53:57 +00:00
dialog::display();
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(mnum)
displayfr(vid.xres/2, vid.yres-vid.fsize*(mnum+1), 2, vid.fsize/2, XLAT("last messages:"), 0xC0C0C0, 8);
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
#ifdef MOBILE
2015-08-08 13:57:52 +00:00
void displayabutton(int px, int py, string s, int col) {
// TMP
int siz = vid.yres > vid.xres ? vid.fsize*2 : vid.fsize * 3/2;
2017-03-23 10:53:57 +00:00
int vrx = min(vid.radius, vid.xres/2 - 40);
int vry = min(vid.radius, min(vid.ycenter, vid.yres - vid.ycenter) - 20);
int x = vid.xcenter + px * vrx;
int y = vid.ycenter + py * (vry - siz/2);
int vrr = int(hypot(vrx, vry) * sqrt(2.));
2015-08-08 13:57:52 +00:00
if(gtouched && !mouseover
2017-03-23 10:53:57 +00:00
&& abs(mousex - vid.xcenter) < vrr
&& abs(mousey - vid.ycenter) < vrr
&& hypot(mousex-vid.xcenter, mousey-vid.ycenter) > vrr
2015-08-08 13:57:52 +00:00
&& px == (mousex > vid.xcenter ? 1 : -1)
&& py == (mousey > vid.ycenter ? 1 : -1)
) col = 0xFF0000;
2016-08-26 09:58:03 +00:00
if(displayfr(x, y, 0, siz, s, col, 8+8*px))
buttonclicked = true;
2015-08-08 13:57:52 +00:00
}
2016-08-26 09:58:03 +00:00
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
#ifndef NOSAVE
2015-08-08 13:57:52 +00:00
vector<score> scores;
int scoresort = 2;
int scoredisplay = 1;
int scorefrom = 0;
2016-01-02 10:09:13 +00:00
int scoremode = 0;
2015-08-08 13:57:52 +00:00
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]);
}
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;
2017-03-23 10:53:57 +00:00
{if(fscanf(f, "%s", buf) <= 0) break;} sc.ver = buf;
2015-08-08 13:57:52 +00:00
for(int i=0; i<MAXBOX; i++) {
if(fscanf(f, "%d", &sc.box[i]) <= 0) { boxid = i; break; }
}
for(int i=boxid; i<MAXBOX; i++) sc.box[i] = 0;
if(sc.ver >= "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;
2017-03-23 10:53:57 +00:00
boxid = 0; applyBoxes();
2015-08-08 13:57:52 +00:00
scoresort = 2; reverse(scores.begin(), scores.end());
2016-01-02 10:09:13 +00:00
scoremode = 0;
if(shmup::on) scoremode = 1;
else if(hardcore) scoremode = 2;
2015-08-08 13:57:52 +00:00
scorefrom = 0;
stable_sort(scores.begin(), scores.end(), scorecompare);
2017-03-23 10:53:57 +00:00
#ifdef MOBILE
extern int andmode;
andmode = 2;
#endif
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
vector<pair<string, int> > pickscore_options;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
bool notgl = false;
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
void setAppropriateOverview() {
clearMessages();
if(tactic::on)
cmode = emTactic;
else if(yendor::on)
cmode = emYendor;
else if(geometry != gNormal)
cmode = emPickEuclidean;
else
cmode = emOverview;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
ld textscale() {
return vid.fsize / (vid.radius * crossf) * (1+vid.alphax) * 2;
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
transmatrix xymatrix(int x, int y, ld scale) {
transmatrix V;
{for(int i=0; i<3; i++) for(int j=0; j<3; j++) V[i][j] = i==j ? 1 : 0; }
V[0][2] = (x - vid.xcenter + .0) / vid.radius * (1+vid.alphax);
V[1][2] = (y - vid.ycenter + .0) / vid.radius * (1+vid.alphax);
V[0][0] = scale;
V[1][1] = scale;
V[2][2] = 0;
return V;
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int monsterclass(eMonster m) {
if(isFriendly(m) || m == moTortoise) return 1;
else if(isMonsterPart(m)) return 2;
else return 0;
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int glyphclass(int i) {
if(i < ittypes) {
eItem it = eItem(i);
return itemclass(it) == IC_TREASURE ? 0 : 1;
}
else {
eMonster m = eMonster(i-ittypes);
return monsterclass(m) == 0 ? 2 : 3;
2015-08-08 13:57:52 +00:00
}
}
2017-03-23 10:53:57 +00:00
int subclass(int i) {
if(i < ittypes)
return itemclass(eItem(i));
else
return monsterclass(eMonster(i-ittypes));
}
#define GLYPH_MARKTODO 1
#define GLYPH_MARKOVER 2
#define GLYPH_LOCAL 4
#define GLYPH_IMPORTANT 8
#define GLYPH_NONUMBER 16
#define GLYPH_DEMON 32
#define GLYPH_RUNOUT 64
#define GLYPH_INPORTRAIT 128
#define GLYPH_LOCAL2 256
#define GLYPH_TARGET 512
eGlyphsortorder glyphsortorder;
int zero = 0;
int& ikmerge(int i) {
if(i < ittypes) return items[i];
else if(i == ittypes) return zero;
else return kills[i-ittypes];
}
const int glyphs = ittypes + motypes;
int gfirsttime[glyphs], glasttime[glyphs], gcopy[glyphs], ikland[glyphs];
int glyphorder[glyphs];
void updatesort() {
for(int i=0; i<glyphs; i++) {
if(ikmerge(i) && gfirsttime[i] == 0)
gfirsttime[i] = ticks;
if(ikmerge(i) != gcopy[i])
gcopy[i] = items[i], glasttime[i] = ticks;
}
}
void preparesort() {
for(int i=0; i<glyphs; i++) glyphorder[i] = i;
for(int i=0; i<LAND_OVERX; i++) {
eLand l = land_over[i];
ikland[treasureType(l)] = i+1;
for(int mi=0; mi<motypes; mi++)
if(isNative(l, eMonster(mi)))
ikland[mi+ittypes] = i+1;
}
glyphsortorder = gsoLand; updatesort();
glyphsortorder = gsoFirstTop;
}
int glyphsortkey = 0;
bool glyphsort(int i, int j) {
if(subclass(i) != subclass(j))
return subclass(i) < subclass(j);
if(glyphsortorder == gsoFirstTop)
return gfirsttime[i] < gfirsttime[j];
if(glyphsortorder == gsoFirstBottom)
return gfirsttime[i] > gfirsttime[j];
if(glyphsortorder == gsoLastTop)
return glasttime[i] > glasttime[j];
if(glyphsortorder == gsoLastBottom)
return glasttime[i] < glasttime[j];
if(glyphsortorder == gsoValue)
return ikmerge(i) > ikmerge(j);
if(glyphsortorder == gsoLand)
return ikland[i] < ikland[j];
return 0;
}
int glyphflags(int gid) {
int f = 0;
if(gid < ittypes) {
eItem i = eItem(gid);
if(itemclass(i) == IC_NAI) f |= GLYPH_NONUMBER;
if(isElementalShard(i)) {
f |= GLYPH_LOCAL;
if(i == localshardof(cwt.c->land)) f |= GLYPH_LOCAL2;
}
if(i == treasureType(cwt.c->land)) f |= GLYPH_LOCAL | GLYPH_LOCAL2 | GLYPH_IMPORTANT;
if(i == itHolyGrail) {
if(items[i] >= 3) f |= GLYPH_MARKOVER;
}
else if(itemclass(i) == IC_TREASURE) {
if(items[i] >= 25 && items[i] < 100) f |= GLYPH_MARKOVER;
else if(items[i] < 10) f |= GLYPH_MARKTODO;
}
else {
f |= GLYPH_IMPORTANT;
if(itemclass(i) == IC_ORB && items[i] < 10) f |= GLYPH_RUNOUT;
}
if(i == orbToTarget) f |= GLYPH_TARGET;
f |= GLYPH_INPORTRAIT;
}
2015-08-08 13:57:52 +00:00
else {
2017-03-23 10:53:57 +00:00
eMonster m = eMonster(gid-ittypes);
if(m == moLesser) f |= GLYPH_IMPORTANT | GLYPH_DEMON | GLYPH_INPORTRAIT;
int isnat = isNative(cwt.c->land, m);
if(isnat) f |= GLYPH_LOCAL | GLYPH_IMPORTANT | GLYPH_INPORTRAIT;
if(isnat == 2) f |= GLYPH_LOCAL2;
if(m == monsterToSummon) f |= GLYPH_TARGET;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
return f;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
bool displayglyph(int cx, int cy, int buttonsize, char glyph, int color, int qty, int flags) {
bool b =
mousex >= cx && mousex < cx+buttonsize && mousey >= cy-buttonsize/2 && mousey <= cy-buttonsize/2+buttonsize;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int glsize = buttonsize;
if(glyph == '%' || glyph == 'M' || glyph == 'W') glsize = glsize*4/5;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(glyph == '*')
displaychr(cx + buttonsize/2, cy+buttonsize/4, 0, glsize*3/2, glyph, darkenedby(color, b?0:1));
else
displaychr(cx + buttonsize/2, cy, 0, glsize, glyph, darkenedby(color, b?0:1));
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
string fl = "";
string str = its(qty);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(flags & GLYPH_TARGET) fl += "!";
if(flags & GLYPH_LOCAL2) fl += "+";
else if(flags & GLYPH_LOCAL) fl += "-";
if(flags & GLYPH_DEMON) fl += "X";
if(flags & GLYPH_MARKOVER) str += "!";
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(fl != "")
displaystr(cx + buttonsize, cy-buttonsize/2 + buttonsize/4, 0, buttonsize/2, fl, darkenedby(color, 0), 16);
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(flags & GLYPH_NONUMBER) str = "";
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int bsize =
(qty < 10 && (flags & (GLYPH_MARKTODO | GLYPH_RUNOUT))) ? buttonsize*3/4 :
qty < 100 ? buttonsize / 2 :
buttonsize / 3;
if(str != "")
displayfr(cx + buttonsize, cy + buttonsize/2 - bsize/2, 1, bsize, str, color, 16);
return b;
}
void drawStats() {
#ifdef ROGUEVIZ
if(rogueviz::on) return;
#endif
if(viewdists && sidescreen) {
dialog::init("");
int qty[64];
vector<cell*>& ac = currentmap->allcells();
for(int i=0; i<64; i++) qty[i] = 0;
for(int i=0; i<size(ac); i++) {
int d = celldistance(ac[i], cwt.c);
if(d >= 0 && d < 64) qty[d]++;
}
if(geometry == gNormal)
for(int i=purehepta?6:8; i<=15; i++)
qty[i] =
purehepta ?
3*qty[i-1] - qty[i-2]
: qty[i-1] + qty[i-2] + qty[i-3] - qty[i-4];
if(geometry == gEuclid)
for(int i=8; i<=15; i++) qty[i] = 6*i;
for(int i=0; i<64; i++) if(qty[i])
dialog::addInfo(its(qty[i]), distcolors[i&7]);
if(geometry == gNormal && !purehepta) {
dialog::addBreak(200);
2017-04-08 15:18:29 +00:00
dialog::addHelp("a(d+4) = a(d+3) + a(d+2) + a(d+1) - a(d)");
dialog::addInfo("a(d) ~ 1.72208^d", 0xFFFFFF);
}
if(geometry == gNormal && purehepta) {
dialog::addBreak(200);
2017-04-08 15:18:29 +00:00
dialog::addHelp("a(d+2) = 3a(d+1) - a(d+2)");
dialog::addInfo("a(d) ~ 2.61803^d", 0xFFFFFF);
}
if(geometry == gEuclid) {
dialog::addBreak(300);
dialog::addInfo("a(n) = 6n", 0xFFFFFF);
}
dialog::display();
}
if(sidescreen) return;
2017-03-23 10:53:57 +00:00
instat = false;
2016-08-26 09:58:03 +00:00
bool portrait = vid.xres < vid.yres;
2017-03-23 10:53:57 +00:00
int colspace = portrait ? (vid.yres - vid.xres - vid.fsize*3) : (vid.xres - vid.yres - 16) / 2;
int rowspace = portrait ? vid.xres - 16 : vid.yres - vid.fsize * 4;
int colid[4], rowid[4];
int maxbyclass[4];
for(int z=0; z<4; z++) maxbyclass[z] = 0;
for(int i=0; i<glyphs; i++) if(ikmerge(i))
if(!portrait || (glyphflags(i) | GLYPH_INPORTRAIT))
maxbyclass[glyphclass(i)]++;
int buttonsize;
int columns, rows;
bool imponly = false;
int minsize = vid.fsize * (portrait ? 4 : 2);
rows = 0;
while((buttonsize = minsize - vid.killreduction)) {
columns = colspace / buttonsize;
rows = rowspace / buttonsize;
int coltaken = 0;
for(int z=0; z<4; z++) {
if(z == 2 && !portrait) {
if(coltaken > columns) { vid.killreduction++; continue; }
coltaken = 0;
}
colid[z] = coltaken, rowid[z] = 0,
coltaken += (maxbyclass[z] + rows-1) / rows;
}
if(coltaken > columns) { vid.killreduction++; continue; }
break;
2015-08-08 13:57:52 +00:00
}
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(buttonsize <= vid.fsize*3/4) {
imponly = true; buttonsize = minsize;
rows = rowspace / buttonsize; if(!rows) return;
colid[0] = 0; colid[2] = portrait ? 1 : 0;
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
updatesort();
stable_sort(glyphorder, glyphorder+glyphs, glyphsort);
for(int i0=0; i0<glyphs; i0++) {
int i = glyphorder[i0];
if(!ikmerge(i)) continue;
int z = glyphclass(i);
int imp = glyphflags(i);
if(imponly) { z &=~1; if(!(imp & GLYPH_IMPORTANT)) continue; }
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
int cx, cy;
if(portrait)
cx = 8 + buttonsize * rowid[z], cy = vid.fsize*2 + buttonsize * (colid[z]) + buttonsize/2;
else
cx = 8 + buttonsize * (colid[z]), cy = vid.fsize * 3 + buttonsize * rowid[z];
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(!portrait && z < 2) cx = vid.xres - cx - buttonsize;
rowid[z]++; if(rowid[z] >= rows) rowid[z] = 0, colid[z]++;
char glyph = i < ittypes ? iinf[i].glyph : minf[i-ittypes].glyph;
int color = i < ittypes ? iinf[i].color : minf[i-ittypes].color;
if(displayglyph(cx, cy, buttonsize, glyph, color, ikmerge(i), imp)) {
instat = true;
getcstat = SDLK_F1;
if(i < ittypes) {
eItem it = eItem(i);
int t = itemclass(it);
2015-08-08 13:57:52 +00:00
if(t == IC_TREASURE)
2017-03-23 10:53:57 +00:00
mouseovers = XLAT("treasure collected: %1", it);
2015-08-08 13:57:52 +00:00
if(t == IC_OTHER)
2017-03-23 10:53:57 +00:00
mouseovers = XLAT("objects found: %1", it);
if(t == IC_NAI)
mouseovers = XLAT("%1", it);
2015-08-08 13:57:52 +00:00
if(t == IC_ORB)
mouseovers = XLAT("orb power: %1", eItem(i));
2017-03-23 10:53:57 +00:00
if(it == itGreenStone) {
2015-08-08 13:57:52 +00:00
mouseovers += XLAT(" (click to drop)");
getcstat = 'g';
}
2017-03-23 10:53:57 +00:00
if(imp & GLYPH_LOCAL) mouseovers += XLAT(" (local treasure)");
help = generateHelpForItem(it);
}
else {
eMonster m = eMonster(i-ittypes);
if(isMonsterPart(m))
mouseovers = s0 + XLAT("parts destroyed: %1", m);
else if(isFriendly(m) && isNonliving(m))
mouseovers = s0 + XLAT("friends destroyed: %1", m);
else if(isFriendly(m))
mouseovers = s0 + XLAT("friends killed: %1", m);
else if(isNonliving(m))
mouseovers = s0 + XLAT("monsters destroyed: %1", m);
else if(m == moTortoise)
mouseovers = s0 + XLAT("animals killed: %1", m);
else
mouseovers = s0 + XLAT("monsters killed: %1", m);
if(imp & GLYPH_LOCAL2) mouseovers += XLAT(" (killing increases treasure spawn)");
else if(imp & GLYPH_LOCAL) mouseovers += XLAT(" (appears here)");
help = generateHelpForMonster(m);
2015-08-08 13:57:52 +00:00
}
}
}
2017-03-23 10:53:57 +00:00
string s0;
if(displayButtonS(vid.xres - 8, vid.fsize, "score: " + its(gold()), 0xFFFFFFF, 16, vid.fsize)) {
mouseovers = XLAT("Your total wealth"),
instat = true,
getcstat = SDLK_F1,
help = helptitle(XLAT("Your total wealth"), 0xFFD500) +
XLAT(
"The total value of the treasure you have collected.\n\n"
"Every world type contains a specific type of treasure, worth 1 $$$; "
"your goal is to collect as much treasure as possible, but every treasure you find "
"causes more enemies to hunt you in its native land.\n\n"
"Orbs of Yendor are worth 50 $$$ each.\n\n"
);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
if(displayButtonS(8, vid.fsize, "kills: " + its(tkills()), 0xFFFFFFF, 0, vid.fsize)) {
instat = true,
getcstat = SDLK_F1,
mouseovers = XLAT("Your total kills")+": " + its(tkills()),
help = helptitle(XLAT("Your total kills") + ": " + its(tkills()), 0x404040) +
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.");
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
if(displayButtonS(4, vid.yres - 4 - vid.fsize/2, s0+VER+ " fps: " + its(calcfps()), 0x202020, 0, vid.fsize/2)) {
2015-08-08 13:57:52 +00:00
mouseovers = XLAT("frames per second"),
2017-03-23 10:53:57 +00:00
getcstat = SDLK_F1,
instat = true,
2015-08-08 13:57:52 +00:00
help =
2017-03-23 10:53:57 +00:00
helptitle(XLAT("frames per second"), 0xFF4040) +
2015-08-08 13:57:52 +00:00
XLAT(
2016-01-02 10:09:13 +00:00
"The higher the number, the smoother the animations in the game. "
"If you find that animations are not smooth enough, you can try "
"to change the options "
2015-08-08 13:57:52 +00:00
) +
#ifdef IOS
XLAT(
2016-01-02 10:09:13 +00:00
"(in the MENU). You can reduce the sight range, this should make "
"the animations smoother.");
2015-08-08 13:57:52 +00:00
#else
XLAT(
2016-01-02 10:09:13 +00:00
"(press v) and change the wall/monster mode to ASCII, or change "
"the resolution.");
2015-08-08 13:57:52 +00:00
#endif
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
achievement_display();
2016-08-26 09:58:03 +00:00
#ifdef LOCAL
process_local_stats();
#endif
2016-01-02 10:09:13 +00:00
}
#ifndef NOSDL
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
#ifndef NOPNG
void IMAGESAVE(SDL_Surface *s, const char *fname) {
SDL_Surface *s2 = SDL_PNGFormatAlpha(s);
SDL_SavePNG(s2, fname);
SDL_FreeSurface(s2);
}
#endif
2017-03-23 10:53:57 +00:00
int pngres = 2000;
int pngformat = 0;
2017-03-23 10:53:57 +00:00
void saveHighQualityShot(const char *fname, const char *caption, int fade) {
2016-01-02 10:09:13 +00:00
#ifndef SDLGFX
2016-01-02 10:09:13 +00:00
addMessage(XLAT("High quality shots not available on this platform"));
return;
#endif
2017-03-23 10:53:57 +00:00
dynamicval<int> v3(sightrange, (cheater && sightrange < 10) ? 10 : sightrange);
if(cheater) doOvergenerate();
2016-01-02 10:09:13 +00:00
time_t timer;
timer = time(NULL);
2017-03-23 10:53:57 +00:00
dynamicval<videopar> v(vid, vid);
dynamicval<bool> v2(inHighQual, true);
dynamicval<int> v4(cheater, 0);
2017-03-23 10:53:57 +00:00
vid.xres = vid.yres = pngres;
if(pngformat == 1) vid.xres = vid.yres * 4/3;
if(pngformat == 2) vid.xres = vid.yres * 16/9;
if(pngformat == 3) {
vid.xres = vid.yres * 22/16;
while(vid.xres & 15) vid.xres++;
}
printf("format = %d, %d x %d\n", pngformat, vid.xres, vid.yres);
2016-01-02 10:09:13 +00:00
vid.usingGL = false;
2016-08-26 09:58:03 +00:00
// if(vid.pmodel == 0) vid.scale = 0.99;
2016-01-02 10:09:13 +00:00
calcparam();
2017-03-23 10:53:57 +00:00
#ifdef ROGUEVIZ
rogueviz::fixparam();
#endif
printf("format = %d, %d x %d\n", pngformat, vid.xres, vid.yres);
2017-03-23 10:53:57 +00:00
dynamicval<SDL_Surface*> v5(s, SDL_CreateRGBSurface(SDL_SWSURFACE,vid.xres,vid.yres,32,0,0,0,0));
2016-01-02 10:09:13 +00:00
darken = 0;
int numi = (fname?1:2);
2016-01-02 10:09:13 +00:00
for(int i=0; i<numi; i++) {
SDL_FillRect(s, NULL, numi==1 ? backcolor : i ? 0xFFFFFF : 0);
2016-08-26 09:58:03 +00:00
drawfullmap();
if(fade < 255)
for(int y=0; y<vid.yres; y++)
for(int x=0; x<vid.xres; x++) {
int& p = qpixel(s, x, y);
for(int i=0; i<3; i++) {
part(p,i) = (part(p,i) * fade + 127) / 255;
}
}
if(caption)
displayfr(vid.xres/2, vid.fsize+vid.fsize/4, 3, vid.fsize*2, caption, 0xFFFFFF, 8);
2016-08-26 09:58:03 +00:00
char buf[128]; strftime(buf, 128, "bigshota-%y%m%d-%H%M%S" IMAGEEXT, localtime(&timer));
2016-01-02 10:09:13 +00:00
buf[7] += i;
2017-03-23 10:53:57 +00:00
if(!fname) fname = buf;
IMAGESAVE(s, fname);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(i == 0) addMessage(XLAT("Saved the high quality shot to %1", fname));
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
SDL_FreeSurface(s);
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
#endif
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
void addball(ld a, ld b, ld c) {
hyperpoint h;
ballmodel(h, a, b, c);
for(int i=0; i<3; i++) h[i] *= vid.radius;
curvepoint(h);
}
void ballgeometry() {
queuereset(vid.usingGL ? mdDisk : mdUnchanged, PPR_CIRCLE);
for(int i=0; i<60; i++)
addball(i * M_PI/30, 10, 0);
for(double d=10; d>=-10; d-=.2)
addball(0, d, 0);
for(double d=-10; d<=10; d+=.2)
addball(0, d, geom3::depth);
addball(0, 0, -geom3::camera);
addball(0, 0, geom3::depth);
addball(0, 0, -geom3::camera);
addball(0, -10, 0);
addball(0, 0, -geom3::camera);
queuecurve(darkena(0xFF, 0, 0x80), 0, PPR_CIRCLE);
queuereset(pmodel, PPR_CIRCLE);
}
2016-08-26 09:58:03 +00:00
void drawfullmap() {
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"draw full map\n"));
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
ptds.clear();
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(!vid.goteyes && !euclid && (pmodel == mdDisk || pmodel == mdBall)) {
double rad = vid.radius;
if(sphere) {
if(!vid.grid && !elliptic)
rad = 0;
else if(vid.alphax <= 0)
;
else if(vid.alphax <= 1 && (vid.grid || elliptic)) // mark the equator
rad = rad * 1 / vid.alphax;
else if(vid.grid) // mark the edge
rad /= sqrt(vid.alphax*vid.alphax - 1);
}
if(!haveaura()) queuecircle(vid.xcenter, vid.ycenter, rad,
2017-03-23 10:53:57 +00:00
svg::in ? 0x808080FF : darkena(0xFF, 0, 0xFF),
vid.usingGL ? PPR_CIRCLE : PPR_OUTCIRCLE);
if(pmodel == mdBall) ballgeometry();
2016-08-26 09:58:03 +00:00
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(pmodel == mdHyperboloid) {
int col = darkena(0x80, 0, 0x80);
queueline(hpxyz(0,0,1), hpxyz(0,0,-vid.alpha), col, 0, PPR_CIRCLE);
queueline(xpush(+4)*C0, hpxyz(0,0,0), col, 0, PPR_CIRCLE);
queueline(xpush(+4)*C0, hpxyz(0,0,-vid.alpha), col, 0, PPR_CIRCLE);
queueline(xpush(-4)*C0, hpxyz(0,0,0), col, 0, PPR_CIRCLE);
queueline(xpush(-4)*C0, hpxyz(0,0,-vid.alpha), col, 0, PPR_CIRCLE);
queueline(hpxyz(-1,0,0), hpxyz(1,0,0), col, 0, PPR_CIRCLE);
}
if(pmodel == mdPolygonal || pmodel == mdPolynomial)
polygonal::drawBoundary(darkena(0xFF, 0, 0xFF));
/* if(vid.wallmode < 2 && !euclid && !mapeditor::whichShape) {
2016-08-26 09:58:03 +00:00
int ls = size(lines);
if(ISMOBILE) ls /= 10;
2017-03-23 10:53:57 +00:00
for(int t=0; t<ls; t++) queueline(View * lines[t].P1, View * lines[t].P2, lines[t].col >> (darken+1));
} */
2016-01-02 10:09:13 +00:00
clearaura();
2016-08-26 09:58:03 +00:00
drawthemap();
2017-03-23 10:53:57 +00:00
#ifndef NORUG
2016-08-26 09:58:03 +00:00
if(!inHighQual) {
2017-03-23 10:53:57 +00:00
if(cmode == emNormal && !rug::rugged) {
if(multi::players > 1) {
transmatrix bcwtV = cwtV;
for(int i=0; i<multi::players; i++) if(multi::playerActive(i))
cwtV = multi::whereis[i], multi::cpid = i, drawmovestar(multi::mdx[i], multi::mdy[i]);
cwtV = bcwtV;
}
else if(multi::alwaysuse)
drawmovestar(multi::mdx[0], multi::mdy[0]);
else
drawmovestar(0, 0);
}
if(rug::rugged && !rug::renderonce) queueline(C0, mouseh, 0xFF00FFFF, 5);
2016-08-26 09:58:03 +00:00
mapeditor::drawGrid();
}
#endif
2017-03-23 10:53:57 +00:00
profile_start(2);
drawaura();
2016-08-26 09:58:03 +00:00
drawqueue();
2017-03-23 10:53:57 +00:00
profile_stop(2);
2016-01-02 10:09:13 +00:00
}
2016-08-26 09:58:03 +00:00
#include "menus.cpp"
2016-01-02 10:09:13 +00:00
2015-08-08 13:57:52 +00:00
void drawscreen() {
2017-03-23 10:53:57 +00:00
if(vid.xres == 0 || vid.yres == 0) return;
2016-08-26 09:58:03 +00:00
DEBB(DF_GRAPH, (debugfile,"drawscreen\n"));
calcparam();
2017-03-23 10:53:57 +00:00
#ifdef ROGUEVIZ
rogueviz::fixparam();
#endif
2015-08-08 13:57:52 +00:00
#ifdef GL
if(vid.usingGL) setGLProjection();
#endif
if(cmode != emHelp) help = "@";
#ifndef NOSDL
2015-08-08 13:57:52 +00:00
// 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);
2017-03-23 10:53:57 +00:00
if(!vid.usingGL) SDL_FillRect(s, NULL, backcolor);
2015-08-08 13:57:52 +00:00
#endif
if(!canmove) darken = 1;
if(cmode != emNormal && cmode != emDraw && cmode != emCustomizeChar) darken = 2;
if(cmode == emQuit && !canmove) darken = 0;
2017-03-23 10:53:57 +00:00
if(cmode == emOverview) darken = 16;
if(sidescreen) darken = 0;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
#ifndef NOEDIT
2016-01-02 10:09:13 +00:00
if(cmode == emMapEditor && !mapeditor::subscreen && !mapeditor::choosefile) darken = 0;
if(cmode == emDraw && mapeditor::choosefile) darken = 2;
#endif
2017-03-23 10:53:57 +00:00
if(hiliteclick && darken == 0 && mmmon) darken = 1;
2016-08-26 09:58:03 +00:00
if(cmode == emProgress) darken = 0;
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
if(conformal::includeHistory && cmode != emProgress) conformal::restore();
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(darken >= 8) ;
#ifndef NORUG
else if(rug::rugged) {
2016-08-26 09:58:03 +00:00
rug::actDraw();
}
2017-03-23 10:53:57 +00:00
#endif
2016-08-26 09:58:03 +00:00
else drawfullmap();
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
if(conformal::includeHistory && cmode != emProgress) conformal::restoreBack();
2017-03-23 10:53:57 +00:00
getcstat = 0; inslider = false;
2015-08-08 13:57:52 +00:00
if(cmode == emNormal || cmode == emQuit) drawStats();
#ifdef MOBILE
2016-08-26 09:58:03 +00:00
buttonclicked = false;
2017-03-23 10:53:57 +00:00
if(cmode == (canmove ? emNormal : emQuit)) {
if(andmode == 0 && shmup::on) {
using namespace shmupballs;
calc();
drawCircle(xmove, yb, rad, 0xFFFFFFFF);
drawCircle(xmove, yb, rad/2, 0xFFFFFFFF);
drawCircle(xfire, yb, rad, 0xFF0000FF);
drawCircle(xfire, yb, rad/2, 0xFF0000FF);
}
else {
if(andmode != 0) 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 == 12 ? BTON : BTOFF);
displayabutton(+1, -1, XLAT("MENU"), andmode == 3 ? BTON : BTOFF);
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
#endif
2015-08-08 13:57:52 +00:00
// displaynum(vx,100, 0, 24, 0xc0c0c0, celldist(cwt.c), ":");
darken = 0;
drawmessages();
if(cmode == emNormal) {
if(!canmove) showGameover();
}
2016-08-26 09:58:03 +00:00
if(cmode == emProgress) mouseovers = "";
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
displayMenus();
2015-08-08 13:57:52 +00:00
describeMouseover();
2017-03-23 10:53:57 +00:00
if((havewhat&HF_BUG) && darken == 0 && (cmode == emNormal || cmode == emQuit)) for(int k=0; k<3; k++)
2015-08-08 13:57:52 +00:00
displayfr(vid.xres/2 + vid.fsize * 5 * (k-1), vid.fsize*2, 2, vid.fsize,
2016-08-26 09:58:03 +00:00
its(hive::bugcount[k]), minf[moBug0+k].color, 8);
2016-01-02 10:09:13 +00:00
bool minefieldNearby = false;
2016-08-26 09:58:03 +00:00
int mines[4], tmines=0;
for(int p=0; p<numplayers(); p++) {
mines[p] = 0;
cell *c = playerpos(p);
2017-03-23 10:53:57 +00:00
if(!c) continue;
2016-08-26 09:58:03 +00:00
for(int i=0; i<c->type; i++) if(c->mov[i]) {
if(c->mov[i]->land == laMinefield)
minefieldNearby = true;
if(c->mov[i]->wall == waMineMine) {
bool ep = false;
if(!ep) mines[p]++, tmines++;
}
}
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
if((minefieldNearby || tmines) && canmove && !items[itOrbAether] && darken == 0 && cmode == emNormal) {
2016-01-02 10:09:13 +00:00
string s;
2016-08-26 09:58:03 +00:00
if(tmines > 7) tmines = 7;
int col = minecolors[tmines];
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
if(tmines == 7) seenSevenMines = true;
2017-03-23 10:53:57 +00:00
for(int p=0; p<numplayers(); p++) if(multi::playerActive(p))
2016-08-26 09:58:03 +00:00
displayfr(vid.xres * (p+.5) / numplayers(),
vid.ycenter - vid.radius * 3/4, 2,
vid.fsize,
XLAT(minetexts[mines[p]]), minecolors[mines[p]], 8);
2016-01-02 10:09:13 +00:00
2016-08-26 09:58:03 +00:00
if(minefieldNearby && !shmup::on && cwt.c->land != laMinefield && cwt.c->mov[cwt.spin]->land != laMinefield) {
2016-01-02 10:09:13 +00:00
displayfr(vid.xres/2, vid.ycenter - vid.radius * 3/4 - vid.fsize*3/2, 2,
vid.fsize,
XLAT("WARNING: you are entering a minefield!"),
col, 8);
}
}
2015-08-08 13:57:52 +00:00
#ifndef MOBILE
2017-04-08 15:18:29 +00:00
if(cmode == emNormal || cmode == emVisual1 || cmode == emVisual2 || cmode == emChangeMode ) {
#ifdef TOUR
2017-04-08 15:18:29 +00:00
if(tour::on)
displayButton(vid.xres-8, vid.yres-vid.fsize, XLAT("(ESC) tour menu"), SDLK_ESCAPE, 16);
else
#endif
2017-04-08 15:18:29 +00:00
displayButton(vid.xres-8, vid.yres-vid.fsize, XLAT("(v) menu"), 'v', 16);
}
2017-03-23 10:53:57 +00:00
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(cmode == emQuit) {
if(canmove) showGameover();
2015-08-08 13:57:52 +00:00
}
// SDL_UnlockSurface(s);
2017-03-23 10:53:57 +00:00
DEBT("swapbuffers");
#ifndef NOSDL
2015-08-08 13:57:52 +00:00
#ifdef GL
if(vid.usingGL) SDL_GL_SwapBuffers(); else
#endif
SDL_UpdateRect(s, 0, 0, vid.xres, vid.yres);
#endif
2016-01-02 10:09:13 +00:00
//printf("\ec");
2015-08-08 13:57:52 +00:00
}
#ifndef NOSDL
2017-03-23 10:53:57 +00:00
bool setfsize = true;
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
void setvideomode() {
DEBB(DF_INIT, (debugfile,"setvideomode\n"));
2015-08-08 13:57:52 +00:00
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
2016-08-26 09:58:03 +00:00
if(vid.usingGL) {
glViewport(0, 0, vid.xres, vid.yres);
resetGL();
}
2015-08-08 13:57:52 +00:00
#endif
}
#endif
void restartGraph() {
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"restartGraph\n"));
2015-08-08 13:57:52 +00:00
View = Id;
linepatterns::clearAll();
if(currentmap) {
if(euclid) {
centerover = euclideanAtCreate(0,0);
}
else {
viewctr.h = currentmap->getOrigin();
viewctr.spin = 0;
viewctr.mirrored = false;
}
if(sphere) View = spin(-M_PI/2);
}
2017-03-23 10:53:57 +00:00
}
void resetview() {
DEBB(DF_GRAPH, (debugfile,"reset view\n"));
View = Id;
// EUCLIDEAN
if(!euclid)
viewctr.h = cwt.c->master,
viewctr.spin = cwt.spin;
else centerover = cwt.c;
// SDL_LockSurface(s);
// SDL_UnlockSurface(s);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
2016-08-26 09:58:03 +00:00
void initcs(charstyle &cs) {
cs.charid = 0;
cs.skincolor = 0xD0D0D0FF;
cs.haircolor = 0x686868FF;
cs.dresscolor = 0xC00000FF;
cs.swordcolor = 0xD0D0D0FF;
cs.dresscolor2= 0x8080FFC0;
2017-03-23 10:53:57 +00:00
cs.uicolor = 0xFF0000FF;
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
#ifndef NOCONFIG
void savecs(FILE *f, charstyle& cs, int xvernum) {
2016-08-26 09:58:03 +00:00
int gflags = cs.charid;
if(vid.samegender) gflags |= 16;
fprintf(f, "%d %d %08x %08x %08x %08x",
gflags, vid.language, cs.skincolor, cs.haircolor, cs.swordcolor, cs.dresscolor);
if(cs.charid == 3) fprintf(f, " %08x", cs.dresscolor2);
2017-03-23 10:53:57 +00:00
if(xvernum >= 8990) fprintf(f, " %x", cs.uicolor);
2016-08-26 09:58:03 +00:00
fprintf(f, "\n");
}
2017-03-23 10:53:57 +00:00
void loadcs(FILE *f, charstyle& cs, int xvernum) {
2016-08-26 09:58:03 +00:00
int gflags, err =
fscanf(f, "%d%d%x%x%x%x", &gflags, &vid.language, &cs.skincolor, &cs.haircolor, &cs.swordcolor, &cs.dresscolor);
if(err) cs.charid = gflags & 15;
if(err) vid.samegender = (gflags & 16) ? true : false;
if(cs.charid == 3) if(fscanf(f, "%x", &cs.dresscolor2))
;
2017-03-23 10:53:57 +00:00
if(xvernum >= 8990) if(fscanf(f, "%x", &cs.uicolor))
;
2016-08-26 09:58:03 +00:00
}
2015-08-08 13:57:52 +00:00
void saveConfig() {
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"save config\n"));
2015-08-08 13:57:52 +00:00
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);
2017-03-23 10:53:57 +00:00
fprintf(f, "%f %f %f %f\n", float(vid.scale), float(vid.eye), float(vid.alpha), float(vid.sspeed));
fprintf(f, "%d %d %d %d %d %d %d\n", vid.wallmode, vid.monmode, vid.axes, musicvolume, vid.framelimit, vid.usingGL, vid.usingAA);
fprintf(f, "%d %d %d %f %d %d\n", vid.joyvalue, vid.joyvalue2, vid.joypanthreshold, float(vid.joypanspeed), autojoy, vid.flashtime);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
savecs(f, vid.cs, 0);
2016-01-02 10:09:13 +00:00
2015-08-08 13:57:52 +00:00
fprintf(f, "%d %d\n", vid.darkhepta, vid.shifttarget);
2016-01-02 10:09:13 +00:00
fprintf(f, "%d %d %d %d\n", euclid, euclidland, shmup::on, hardcore);
2016-08-26 09:58:03 +00:00
2016-01-02 10:09:13 +00:00
shmup::saveConfig(f);
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
fprintf(f, "%d %d %d %d %f %d %d\n", rug::renderonce, rug::rendernogl, rug::texturesize, purehepta, rug::scale, vid.steamscore, chaosmode);
2017-03-23 10:53:57 +00:00
fprintf(f, "%d %d %f %d %d %f\n",
pmodel, polygonal::SI, float(polygonal::STAR), polygonal::deg,
conformal::includeHistory, float(conformal::lvspeed));
2016-08-26 09:58:03 +00:00
fprintf(f, "%d %d %d %d %d %d\n",
conformal::bandhalf, conformal::bandsegment,
conformal::rotation, conformal::autoband, conformal::autobandhistory,
conformal::dospiral);
fprintf(f, "%d", polygonal::maxcoef);
for(int i=0; i<=polygonal::maxcoef; i++) fprintf(f, " %lf %lf",
(double) real(polygonal::coef[i]), (double) imag(polygonal::coef[i]));
2017-03-23 10:53:57 +00:00
fprintf(f, "\n%d %d %d %f %d %d\n",
revcontrol, vid.drawmousecircle, sightrange, float(vid.mspeed), effvolume, vid.particles);
{
int pt_depth = 0, pt_camera = 0, pt_alpha = 0;
using namespace geom3;
if(tc_depth > tc_camera) pt_depth++;
if(tc_depth < tc_camera) pt_camera++;
if(tc_depth > tc_alpha ) pt_depth++;
if(tc_depth < tc_alpha ) pt_alpha ++;
if(tc_alpha > tc_camera) pt_alpha++;
if(tc_alpha < tc_camera) pt_camera++;
fprintf(f, "%f %f %f %f %f %f %f %d %d %d %f %f %d\n",
float(geom3::depth), float(geom3::camera), float(geom3::wall_height),
float(geom3::rock_wall_ratio),
float(geom3::human_wall_ratio),
float(geom3::lake_top),
float(geom3::lake_bottom),
pt_depth, pt_camera, pt_alpha,
float(geom3::highdetail), float(geom3::middetail),
glyphsortorder);
fprintf(f, "%f %f %f %f\n",
float(vid.yshift), float(vid.camera_angle),
float(vid.ballangle), float(vid.ballproj)
);
fprintf(f, "%d %d %d\n", vid.mobilecompasssize, vid.aurastr, vid.aurasmoothen);
2017-03-23 10:53:57 +00:00
}
2016-08-26 09:58:03 +00:00
fprintf(f, "\n\nThis is a configuration file for HyperRogue (version " VER ")\n");
2015-08-08 13:57:52 +00:00
fprintf(f, "\n\nThe numbers are:\n");
fprintf(f, "screen width & height, fullscreen mode (0=windowed, 1=fullscreen), font size\n");
2017-03-23 10:53:57 +00:00
fprintf(f, "scale, eye distance, parameter, scrolling speed\n");
fprintf(f, "wallmode, monster mode, cross mode, music volume, framerate limit, usingGL, usingAA\n");
2015-08-08 13:57:52 +00:00
fprintf(f, "calibrate first joystick (threshold A, threshold B), calibrate second joystick (pan threshold, pan speed), joy mode\n");
2016-01-02 10:09:13 +00:00
fprintf(f, "gender (1=female, 16=same gender prince), language, skin color, hair color, sword color, dress color\n");
2015-08-08 13:57:52 +00:00
fprintf(f, "darken hepta, shift target\n");
2016-01-02 10:09:13 +00:00
fprintf(f, "euclid, euclid land, shmup, hardcore\n");
2017-03-23 10:53:57 +00:00
fprintf(f, "version number, shmup players, alwaysuse, shmup keyboard/joystick config\n");
2016-08-26 09:58:03 +00:00
fprintf(f, "hypersian rug config: renderonce, rendernogl, texturesize; purehepta; rug scale; share score; chaosmode\n");
fprintf(f, "conformal: model, sides, star, degree, includeHistory, speed\n");
fprintf(f, "conformal: bandwidth, segment, rotation, autoband, autohistory, dospiral\n");
fprintf(f, "conformal: degree, (degree+1) times {real, imag}\n");
2017-03-23 10:53:57 +00:00
fprintf(f, "revcontrol, drawmousecircle, sight range, movement animation speed, sound effect volume, particle effects\n");
fprintf(f, "3D parameters, sort order\n");
fprintf(f, "yhsift, camera angle, ball angle, ball projection\n");
fprintf(f, "compass size\n");
2015-08-08 13:57:52 +00:00
fclose(f);
2016-08-26 09:58:03 +00:00
#ifndef MOBILE
2015-08-08 13:57:52 +00:00
addMessage(s0 + "Configuration saved to: " + conffile);
2016-08-26 09:58:03 +00:00
#else
addMessage(s0 + "Configuration saved");
#endif
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
void readf(FILE *f, ld& x) {
double fl = x;
if(fscanf(f, "%lf", &fl))
;
x = fl;
}
2015-08-08 13:57:52 +00:00
void loadConfig() {
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"load config\n"));
2015-08-08 13:57:52 +00:00
vid.xres = 9999; vid.yres = 9999; vid.framelimit = 300;
FILE *f = fopen(conffile, "rt");
if(f) {
2016-08-26 09:58:03 +00:00
int fs, gl=1, aa=1, bb=1, cc, dd, ee;
2015-08-08 13:57:52 +00:00
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) {
2017-03-23 10:53:57 +00:00
vid.scale = a; vid.eye = b; vid.alpha = c; vid.sspeed = d;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
err=fscanf(f, "%d%d%d%d%d%d%d", &vid.wallmode, &vid.monmode, &vid.axes, &musicvolume, &vid.framelimit, &gl, &aa);
2015-08-08 13:57:52 +00:00
vid.usingGL = gl; vid.usingAA = aa;
2017-03-23 10:53:57 +00:00
double jps = vid.joypanspeed;
err=fscanf(f, "%d%d%d%lf%d%d", &vid.joyvalue, &vid.joyvalue2, &vid.joypanthreshold, &jps, &aa, &vid.flashtime);
vid.joypanspeed = jps;
2015-08-08 13:57:52 +00:00
autojoy = aa; aa = 0;
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
loadcs(f, vid.cs, 0);
2016-08-26 09:58:03 +00:00
2015-08-08 13:57:52 +00:00
aa=0; bb=0;
err=fscanf(f, "%d%d", &aa, &bb);
vid.darkhepta = aa; vid.shifttarget = bb;
2017-03-23 10:53:57 +00:00
aa = geometry; bb = euclidland; cc = shmup::on; dd = hardcore;
2016-01-02 10:09:13 +00:00
err=fscanf(f, "%d%d%d%d", &aa, &bb, &cc, &dd);
2017-03-23 10:53:57 +00:00
geometry = eGeometry(aa); euclidland = eLand(bb); shmup::on = cc; hardcore = dd;
2016-01-02 10:09:13 +00:00
shmup::loadConfig(f);
2016-08-26 09:58:03 +00:00
aa = rug::renderonce; bb = rug::rendernogl; cc = purehepta; dd = chaosmode; ee = vid.steamscore;
2017-03-23 10:53:57 +00:00
double rs = rug::scale;
err=fscanf(f, "%d%d%d%d%lf%d%d", &aa, &bb, &rug::texturesize, &cc, &rs, &ee, &dd);
2016-08-26 09:58:03 +00:00
rug::renderonce = aa; rug::rendernogl = bb; purehepta = cc; chaosmode = dd; vid.steamscore = ee;
2017-03-23 10:53:57 +00:00
rug::scale = rs;
2016-08-26 09:58:03 +00:00
aa=conformal::includeHistory;
2017-03-23 10:53:57 +00:00
double ps = polygonal::STAR, lv = conformal::lvspeed;
int pmb = pmodel;
2016-08-26 09:58:03 +00:00
err=fscanf(f, "%d%d%lf%d%d%lf",
2017-03-23 10:53:57 +00:00
&pmb, &polygonal::SI, &ps, &polygonal::deg,
&aa, &lv);
pmodel = eModel(pmb);
conformal::includeHistory = aa; polygonal::STAR = ps; conformal::lvspeed = lv;
2016-08-26 09:58:03 +00:00
aa=conformal::autoband; bb=conformal::autobandhistory; cc=conformal::dospiral;
err=fscanf(f, "%d%d%d%d%d%d",
&conformal::bandhalf, &conformal::bandsegment, &conformal::rotation,
&aa, &bb, &cc);
conformal::autoband = aa; conformal::autobandhistory = bb; conformal::dospiral = cc;
2017-03-23 10:53:57 +00:00
2016-08-26 09:58:03 +00:00
err=fscanf(f, "%d", &polygonal::maxcoef);
if(polygonal::maxcoef < 0) polygonal::maxcoef = 0;
if(polygonal::maxcoef > MSI) polygonal::maxcoef = MSI;
for(int i=0; i<=polygonal::maxcoef; i++) {
double re=0, im=0;
err=fscanf(f, "%lf%lf", &re, &im);
polygonal::coef[i] = polygonal::cld(re, im);
}
2017-03-23 10:53:57 +00:00
aa=revcontrol; bb=vid.drawmousecircle;
d = vid.mspeed;
err=fscanf(f, "%d%d%d%f%d%d", &aa, &bb, &sightrange, &d, &effvolume, &vid.particles);
vid.mspeed = d;
if(sightrange < 4) sightrange = 4;
if(sightrange > 7) sightrange = 7;
revcontrol = aa; vid.drawmousecircle = bb;
readf(f, geom3::depth); readf(f, geom3::camera); readf(f, geom3::wall_height);
readf(f, geom3::rock_wall_ratio); readf(f, geom3::human_wall_ratio);
readf(f, geom3::lake_top); readf(f, geom3::lake_bottom);
err=fscanf(f, "%d %d %d", &geom3::tc_depth, &geom3::tc_camera, &geom3::tc_alpha);
readf(f, geom3::highdetail);
geom3::middetail = 200; readf(f, geom3::middetail);
if(geom3::middetail == 200) {
if(ISMOBILE)
geom3::highdetail = 0, geom3::middetail = 3;
else
geom3::highdetail = geom3::middetail = 5;
}
int gso = glyphsortorder; err=fscanf(f, "%d", &gso); glyphsortorder = eGlyphsortorder(gso);
readf(f, vid.yshift); readf(f, vid.camera_angle); readf(f, vid.ballangle); readf(f, vid.ballproj);
err=fscanf(f, "%d%d%d\n", &vid.mobilecompasssize, &vid.aurastr, &vid.aurasmoothen);
2016-08-26 09:58:03 +00:00
2015-08-08 13:57:52 +00:00
fclose(f);
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"Loaded configuration: %s\n", conffile));
2017-03-23 10:53:57 +00:00
if(err)
;
2015-08-08 13:57:52 +00:00
}
2016-08-26 09:58:03 +00:00
precalc();
2015-08-08 13:57:52 +00:00
}
#endif
#ifndef NOSDL
2016-01-02 10:09:13 +00:00
void initJoysticks() {
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"init joysticks\n"));
2016-01-02 10:09:13 +00:00
numsticks = SDL_NumJoysticks();
if(numsticks > 8) numsticks = 8;
for(int i=0; i<numsticks; i++) {
sticks[i] = SDL_JoystickOpen(i);
/* printf("axes = %d, balls = %d, buttons = %d, hats = %d\n",
SDL_JoystickNumAxes(sticks[i]),
SDL_JoystickNumBalls(sticks[i]),
SDL_JoystickNumButtons(sticks[i]),
SDL_JoystickNumHats(sticks[i])
); */
}
}
void closeJoysticks() {
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"close joysticks\n"));
2016-01-02 10:09:13 +00:00
for(int i=0; i<numsticks; i++) {
SDL_JoystickClose(sticks[i]), sticks[i] = NULL;
}
numsticks = 0;
}
#endif
bool noGUI = false;
2015-08-08 13:57:52 +00:00
void initgraph() {
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"initgraph\n"));
2015-08-08 13:57:52 +00:00
vid.usingGL = true;
vid.usingAA = true;
vid.flashtime = 8;
vid.scale = 1;
vid.alpha = 1;
2017-03-23 10:53:57 +00:00
vid.sspeed = 0;
vid.mspeed = 1;
2015-08-08 13:57:52 +00:00
vid.eye = 0;
vid.full = false;
2017-03-23 10:53:57 +00:00
vid.ballangle = 20;
vid.yshift = 0;
vid.camera_angle = 0;
vid.ballproj = 1;
vid.aurastr = 128;
vid.aurasmoothen = 5;
2017-03-23 10:53:57 +00:00
#ifdef ANDROID
vid.monmode = 2;
2015-08-08 13:57:52 +00:00
vid.wallmode = 3;
2017-03-25 01:10:15 +00:00
#else
#ifdef PANDORA
vid.monmode = 2;
vid.wallmode = 3;
2017-03-23 10:53:57 +00:00
#else
vid.monmode = 4;
vid.wallmode = 5;
2017-03-25 01:10:15 +00:00
#endif
2017-03-23 10:53:57 +00:00
#endif
vid.particles = 1;
vid.mobilecompasssize = 30;
2015-08-08 13:57:52 +00:00
vid.joyvalue = 4800;
vid.joyvalue2 = 5600;
vid.joypanthreshold = 2500;
2016-01-02 10:09:13 +00:00
#ifdef PANDORA
2015-08-08 13:57:52 +00:00
vid.joypanspeed = 0.0001;
2016-01-02 10:09:13 +00:00
#else
vid.joypanspeed = 0;
#endif
2015-08-08 13:57:52 +00:00
vid.framelimit = 75;
vid.axes = 1;
2017-03-23 10:53:57 +00:00
vid.shifttarget = 2;
vid.steamscore = 1;
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
initcs(vid.cs);
2015-08-08 13:57:52 +00:00
vid.killreduction = 0;
2016-01-02 10:09:13 +00:00
vid.samegender = false;
2015-08-08 13:57:52 +00:00
vid.language = -1;
2016-01-02 10:09:13 +00:00
joyx = joyy = 0; joydir.d = -1;
2017-03-23 10:53:57 +00:00
vid.drawmousecircle = false;
revcontrol = false;
#ifdef MOBILE
vid.drawmousecircle = true;
#endif
#ifdef PANDORA
vid.drawmousecircle = true;
#endif
2016-01-02 10:09:13 +00:00
shmup::initConfig();
2015-08-08 13:57:52 +00:00
restartGraph();
initgeo();
buildpolys();
if(noGUI) {
#ifdef USE_COMMANDLINE
arg::read(2);
#endif
return;
}
2015-08-08 13:57:52 +00:00
#ifndef NOSDL
2015-08-08 13:57:52 +00:00
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1)
{
printf("Failed to initialize video.\n");
exit(2);
}
2017-03-23 10:53:57 +00:00
#ifdef WEB
vid.xscr = vid.xres = 1200;
vid.yscr = vid.yres = 900;
#else
2015-08-08 13:57:52 +00:00
const SDL_VideoInfo *inf = SDL_GetVideoInfo();
vid.xscr = vid.xres = inf->current_w;
vid.yscr = vid.yres = inf->current_h;
2017-03-23 10:53:57 +00:00
#endif
2015-08-08 13:57:52 +00:00
2016-08-26 09:58:03 +00:00
SDL_WM_SetCaption("HyperRogue " VER, "HyperRogue " VER);
2017-03-23 10:53:57 +00:00
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
preparesort();
#ifndef NOCONFIG
2015-08-08 13:57:52 +00:00
loadConfig();
2017-03-23 10:53:57 +00:00
#endif
#ifdef USE_COMMANDLINE
arg::read(2);
#endif
2015-08-08 13:57:52 +00:00
#ifndef NOSDL
2015-08-08 13:57:52 +00:00
setvideomode();
if(!s) {
printf("Failed to initialize graphics.\n");
exit(2);
}
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
SDL_EnableUNICODE(1);
#ifndef NOTTF
2015-08-08 13:57:52 +00:00
if(TTF_Init() != 0) {
printf("Failed to initialize TTF.\n");
exit(2);
}
#endif
2015-08-08 13:57:52 +00:00
2016-01-02 10:09:13 +00:00
initJoysticks();
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
#ifdef SDLAUDIO
initAudio();
#endif
#endif
}
int frames;
bool outoffocus = false;
void panning(hyperpoint hf, hyperpoint ht) {
View =
rgpushxto0(hf) * rgpushxto0(gpushxto0(hf) * ht) * gpushxto0(hf) * View;
playermoved = false;
}
#ifdef LOCAL
#include "local.cpp"
#endif
bool needConfirmation() {
return canmove && (gold() >= 30 || tkills() >= 50) && !cheater && !quitsaves();
}
void fullcenter() {
if(playerfound && false) centerpc(INF);
else {
bfs();
resetview();
drawthemap();
centerpc(INF);
}
playermoved = true;
}
bool didsomething;
bool quitmainloop = false;
bool doexiton(int sym, int uni) {
if(sym == SDLK_ESCAPE) return true;
if(sym == SDLK_F10) return true;
if(uni != 0) return true;
return false;
}
2017-03-23 10:53:57 +00:00
void handleKeyQuit(int sym, int uni) {
dialog::handleNavigation(sym, uni);
// ignore the camera movement keys
#ifndef NORUG
if(rug::rugged && (sym == SDLK_UP || sym == SDLK_DOWN || sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN ||
sym == SDLK_RIGHT || sym == SDLK_LEFT))
sym = 0;
#endif
if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER || sym == SDLK_F10) quitmainloop = true;
2017-03-23 10:53:57 +00:00
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 == 'v') cmode = emMenu;
else if(sym == SDLK_HOME || sym == SDLK_F3 || (sym == ' ' && DEFAULTCONTROL))
fullcenter();
else if(uni == 'o') setAppropriateOverview();
#ifndef NOSAVE
else if(uni == 't') {
if(!canmove) restartGame();
loadScores();
msgs.clear();
}
#endif
else if(doexiton(sym, uni) && !didsomething) {
2017-03-23 10:53:57 +00:00
cmode = emNormal;
msgscroll = 0;
msgs.clear();
}
}
#ifdef MOBILE
#define extra int
#else
#define extra SDL_Event
#endif
void handleKeyNormal(int sym, int uni, extra& ev) {
if(cheater) {
if(applyCheat(uni, mouseover))
sym = 0;
}
if(!(uni >= 'A' && uni <= 'Z') && DEFAULTCONTROL) {
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(DEFAULTCONTROL) {
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(DEFAULTCONTROL) {
if(sym == '.' || sym == 's') movepcto(-1, 1);
if(uni == '%' && sym == '5') {
if(vid.wallmode == 0) vid.wallmode = 6;
vid.wallmode--;
}
if(uni == sym) {
if(uni == '1') {
vid.alpha = 999; vid.scale = 998;
}
if(uni == '2') {
vid.alpha = 1; vid.scale = 0.4;
}
if(uni == '3') {
vid.alpha = 1; vid.scale = 1;
}
if(uni == '4') {
vid.alpha = 0; vid.scale = 1;
}
if(uni == '5') {
vid.wallmode++;
if(vid.wallmode == 6) vid.wallmode = 0;
}
if(uni == '6') {
vid.grid = !vid.grid;
}
if(uni == '7') {
vid.darkhepta = !vid.darkhepta;
}
if(uni == '8') {
backcolor = backcolor ^ 0xFFFFFF;
printf("back = %x\n", backcolor);
}
if(uni == '9') {
pmodel = eModel(8 - pmodel);
// vid.yshift = 1 - vid.yshift;
// vid.drawmousecircle = true;
}
}
if((sym == SDLK_DELETE || sym == SDLK_KP_PERIOD || sym == 'g') && uni != 'G' && uni != 'G'-64)
movepcto(MD_DROP, 1);
if(sym == 'm' && canmove && cmode == emNormal && (centerover == cwt.c ? mouseover : centerover))
performMarkCommand(mouseover);
if(sym == 't' && uni != 'T' && uni != 'T'-64 && canmove && cmode == emNormal) {
if(playermoved && items[itStrongWind]) {
cell *c = whirlwind::jumpDestination(cwt.c);
if(c) centerover = c;
}
targetRangedOrb(centerover, roKeyboard);
sym = 0; uni = 0;
}
}
if(sym == SDLK_KP5 && DEFAULTCONTROL) movepcto(-1, 1);
// if(sym == SDLK_F4) restartGameSwitchEuclid();
if(sym == SDLK_F5) {
if(needConfirmation()) cmode = emQuit;
else restartGame();
}
if(sym == SDLK_ESCAPE) {
cmode = emQuit;
achievement_final(false);
if(!canmove) {
addMessage(XLAT("GAME OVER"));
addMessage(timeline());
}
msgscroll = 0;
}
if(sym == SDLK_F10) {
if(needConfirmation()) cmode = emQuit;
else quitmainloop = true;
}
if(!canmove) {
if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER) quitmainloop = true;
2017-03-23 10:53:57 +00:00
else if(uni == 'r') restartGame();
#ifndef NOSAVE
else if(uni == 't') {
restartGame();
loadScores();
}
#endif
#ifndef NORUG
else if(rug::rugged) ;
#endif
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;
}
if(uni == 'o') setAppropriateOverview();
if(sym == SDLK_HOME || sym == SDLK_F3 || (sym == ' ' && DEFAULTCONTROL))
fullcenter();
/* if(sym == SDLK_F6) {
View = spin(M_PI/2) * inverse(cwtV) * View;
if(flipplayer) View = pispin * View;
cmode = emDraw;
} */
if(sym == 'v') {
cmode = emMenu;
}
if(sym == SDLK_F2) {
cmode = emVisual1;
}
2015-08-08 13:57:52 +00:00
#ifndef NOSDL
2017-03-23 10:53:57 +00:00
#ifdef PANDORA
if(ev.type == SDL_MOUSEBUTTONUP && sym == 0 && !rightclick)
#else
if(ev.type == SDL_MOUSEBUTTONDOWN && sym == 0 && !rightclick)
2015-08-08 13:57:52 +00:00
#endif
2017-03-23 10:53:57 +00:00
if(canmove && getcstat != 'v' && getcstat != 'g' && getcstat != SDLK_F1)
{
actonrelease = false;
shmup::cpid = 0;
if(mouseover &&
targetclick && (!shmup::on || numplayers() == 1) && targetRangedOrb(mouseover, forcetarget ? roMouseForce : roMouse)) {
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else if(forcetarget)
;
else if(!DEFAULTCONTROL) {
if(!shmup::on)
multi::mousemovement(mouseover);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else mousemovement();
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(sym == SDLK_F1) {
lastmode = cmode;
cmode = emHelp;
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
#ifdef ROGUEVIZ
rogueviz::processKey(sym, uni);
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
#ifdef LOCAL
process_local0(sym);
#endif
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
void handlekey(int sym, int uni, extra& ev) {
2015-08-08 13:57:52 +00:00
2017-04-08 15:18:29 +00:00
#ifdef TOUR
if(tour::on && tour::handleKeyTour(sym, uni)) return;
#endif
if(((cmode == emNormal && canmove) || (cmode == emQuit && !canmove) || cmode == emDraw || (cmode == emMapEditor && !mapeditor::subscreen)) && DEFAULTCONTROL && !rug::rugged) {
2017-03-23 10:53:57 +00:00
#ifndef PANDORA
if(sym == SDLK_RIGHT) {
if(conformal::on)
conformal::lvspeed += 0.1 * shiftmul;
else
View = xpush(-0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
if(sym == SDLK_LEFT) {
if(conformal::on)
conformal::lvspeed -= 0.1 * shiftmul;
else
View = xpush(+0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
if(sym == SDLK_UP) {
if(conformal::on)
conformal::lvspeed += 0.1 * shiftmul;
else
View = ypush(+0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
if(sym == SDLK_DOWN) {
if(conformal::on)
conformal::lvspeed -= 0.1 * shiftmul;
else
View = ypush(-0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
2016-08-26 09:58:03 +00:00
#endif
2017-03-23 10:53:57 +00:00
if(sym == SDLK_PAGEUP) {
if(conformal::on)
conformal::rotation++;
else
View = spin(M_PI/S21*shiftmul) * View, didsomething = true;
}
if(sym == SDLK_PAGEDOWN) {
if(conformal::on)
conformal::rotation++;
else
View = spin(-M_PI/S21*shiftmul) * View, didsomething = true;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
if(sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN)
if(isGravityLand(cwt.c->land)) playermoved = false;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
#ifndef NOSDL
2017-03-23 10:53:57 +00:00
if(sym == SDLK_F7 && !vid.usingGL) {
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
time_t timer;
timer = time(NULL);
char buf[128]; strftime(buf, 128, "shot-%y%m%d-%H%M%S" IMAGEEXT, localtime(&timer));
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
IMAGESAVE(s, buf);
addMessage(XLAT("Screenshot saved to %1", buf));
2015-08-08 13:57:52 +00:00
}
#endif
2017-03-23 10:53:57 +00:00
#ifdef DEMO
if(cmode == emOverview || cmode == emMenu) handleDemoKey(sym, uni); else
#endif
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(cmode == emNormal) handleKeyNormal(sym, uni, ev);
else if(cmode == emMenu) handleMenuKey(sym, uni);
else if(cmode == emCheatMenu) handleCheatMenu(sym, uni);
else if(cmode == emVisual1) handleVisual1(sym, uni);
else if(cmode == emJoyConfig) handleJoystickConfig(sym, uni);
else if(cmode == emCustomizeChar) handleCustomizeChar(sym, uni);
else if(cmode == emVisual2) handleVisual2(sym, uni);
else if(cmode == emChangeMode) handleChangeMode(sym, uni);
else if(cmode == emShmupConfig) shmup::handleConfig(sym, uni);
#ifndef NOMODEL
else if(cmode == emNetgen) netgen::handleKey(sym, uni);
#endif
#ifndef NORUG
else if(cmode == emRugConfig) rug::handleKey(sym, uni);
#endif
#ifndef NOEDIT
else if(cmode == emMapEditor) mapeditor::handleKey(sym, uni);
else if(cmode == emDraw) mapeditor::drawHandleKey(sym, uni);
#endif
#ifndef NOSAVE
#ifndef MOBILE
else if(cmode == emScores) handleScoreKeys(sym, uni);
2017-03-23 10:53:57 +00:00
#endif
else if(cmode == emPickScores) handlePickScoreKeys(sym, uni);
2017-03-23 10:53:57 +00:00
#endif
else if(cmode == emConformal) conformal::handleKey(sym, uni);
else if(cmode == emYendor) yendor::handleKey(sym, uni);
else if(cmode == emTactic) tactic::handleKey(sym, uni);
else if(cmode == emOverview) handleOverview(sym, uni);
else if(cmode == emPickEuclidean) handleEuclidean(sym, uni);
#ifdef MOBILE
2017-03-23 13:09:36 +00:00
#ifdef HAVE_ACHIEVEMENTS
2017-03-23 10:53:57 +00:00
else if(cmode == emLeader) leader::handleKey(sym, uni);
2017-03-23 13:09:36 +00:00
#endif
2017-03-23 10:53:57 +00:00
#endif
else if(cmode == emColor) dialog::handleColor(sym, uni);
else if(cmode == emNumber) dialog::handleNumber(sym, uni);
else if(cmode == emHelp) handleHelp(sym, uni);
else if(cmode == em3D) handle3D(sym, uni);
else if(cmode == emQuit) handleKeyQuit(sym, uni);
#ifdef ROGUEVIZ
else if(cmode == emRogueviz) rogueviz::handleMenu(sym, uni);
2015-08-08 13:57:52 +00:00
#endif
else if(cmode == emLinepattern) linepatterns::handleMenu(sym, uni);
2016-01-02 10:09:13 +00:00
}
#ifdef NOSDL
void mainloopiter() { printf("(compiled without SDL -- no action)\n"); quitmainloop = true; }
#else
2015-08-08 13:57:52 +00:00
// Warning: a very long function! todo: refactor
2017-03-23 10:53:57 +00:00
void mainloopiter() {
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
DEBB(DF_GRAPH, (debugfile,"main loop\n"));
#ifndef GFX
#ifndef GL
vid.wallmode = 0;
vid.monmode = 0;
#endif
#endif
2016-08-26 09:58:03 +00:00
#ifdef LOCAL
2017-03-23 10:53:57 +00:00
process_local_extra();
2016-08-26 09:58:03 +00:00
#endif
2017-03-23 10:53:57 +00:00
optimizeview();
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if(conformal::on) conformal::apply();
ticks = SDL_GetTicks();
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
int cframelimit = vid.framelimit;
if((cmode == emVisual1 || cmode == emVisual2 || cmode == emHelp || cmode == emQuit ||
cmode == emCustomizeChar || cmode == emMenu || cmode == emPickEuclidean ||
cmode == emScores || cmode == emPickScores) && cframelimit > 15)
cframelimit = 15;
if(outoffocus && cframelimit > 10) cframelimit = 10;
int timetowait = lastt + 1000 / cframelimit - ticks;
if(DOSHMUP && cmode == emNormal)
timetowait = 0, shmup::turn(ticks - lastt);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(!DOSHMUP && (multi::alwaysuse || multi::players > 1) && cmode == emNormal)
timetowait = 0, multi::handleMulti(ticks - lastt);
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
if(vid.sspeed >= 5 && gmatrix.count(cwt.c) && !elliptic) {
cwtV = gmatrix[cwt.c] * ddspin(cwt.c, cwt.spin);
if(cwt.mirrored) playerV = playerV * Mirror;
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
#ifdef WEB
if(playermoved && vid.sspeed > -4.99 && !outoffocus) {
centerpc((ticks - lastt) / 1000.0 * exp(vid.sspeed));
}
if(!outoffocus) drawscreen();
#else
if(timetowait > 0)
SDL_Delay(timetowait);
else {
if(cmode != emOverview) {
if(playermoved && vid.sspeed > -4.99 && !outoffocus)
centerpc((ticks - lastt) / 1000.0 * exp(vid.sspeed));
2016-01-02 10:09:13 +00:00
if(panjoyx || panjoyy)
2015-08-08 13:57:52 +00:00
checkpanjoy((ticks - lastt) / 1000.0);
}
2017-03-23 10:53:57 +00:00
tortoise::updateVals(ticks - lastt);
frames++;
if(!outoffocus) {
drawscreen();
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
lastt = ticks;
}
2015-08-08 13:57:52 +00:00
#endif
2017-03-23 10:53:57 +00:00
Uint8 *keystate = SDL_GetKeyState(NULL);
rightclick = keystate[SDLK_RCTRL];
leftclick = keystate[SDLK_RSHIFT];
lctrlclick = keystate[SDLK_LCTRL];
lshiftclick = keystate[SDLK_LSHIFT];
forcetarget = (keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]);
hiliteclick = keystate[SDLK_LALT] | keystate[SDLK_RALT];
anyshiftclick = keystate[SDLK_LSHIFT] | keystate[SDLK_RSHIFT];
wheelclick = false;
getcshift = 1;
if(keystate[SDLK_LSHIFT] || keystate[SDLK_RSHIFT]) getcshift = -1;
if(keystate[SDLK_LCTRL] || keystate[SDLK_RCTRL]) getcshift /= 10;
if(keystate[SDLK_LALT] || keystate[SDLK_RALT]) getcshift *= 10;
didsomething = false;
if(vid.shifttarget&1) {
leftclick = false;
targetclick = keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT];
}
else {
leftclick = keystate[SDLK_RSHIFT];
targetclick = true;
}
#ifdef SDLAUDIO
if(audio) handlemusic();
#endif
SDL_Event ev;
DEBB(DF_GRAPH, (debugfile,"polling for events\n"));
achievement_pump();
while(SDL_PollEvent(&ev)) {
DEBB(DF_GRAPH, (debugfile,"got event type #%d\n", ev.type));
int sym = 0;
int uni = 0;
shiftmul = 1;
2016-08-26 09:58:03 +00:00
/* if(ev.type == SDL_JOYDEVICEADDED || ev.type == SDL_JOYDEVICEREMOVED) {
2017-03-23 10:53:57 +00:00
joyx = joyy = 0;
panjoyx = panjoyy = 0;
closeJoysticks();
initJoysticks();
}*/
if(ev.type == SDL_ACTIVEEVENT) {
if(ev.active.state & SDL_APPINPUTFOCUS) {
if(ev.active.gain) {
outoffocus = false;
}
else {
outoffocus = true;
2015-08-08 13:57:52 +00:00
}
}
2017-03-23 10:53:57 +00:00
}
if(ev.type == SDL_VIDEORESIZE) {
vid.xres = ev.resize.w;
vid.yres = ev.resize.h;
vid.killreduction = 0;
extern bool setfsize;
setfsize = true;
setvideomode();
2015-08-08 13:57:52 +00:00
#ifdef GL
2017-03-23 10:53:57 +00:00
if(vid.usingGL) glViewport(0, 0, vid.xres, vid.yres);
2015-08-08 13:57:52 +00:00
#endif
2017-03-23 10:53:57 +00:00
}
if(ev.type == SDL_VIDEOEXPOSE) {
drawscreen();
}
if(ev.type == SDL_JOYAXISMOTION) {
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);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else {
if(ev.jaxis.axis == 0)
panjoyx = ev.jaxis.value;
else
panjoyy = ev.jaxis.value;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(ev.type == SDL_JOYBUTTONDOWN && cmode == emShmupConfig && vid.scfg.setwhat) {
int joyid = ev.jbutton.which;
int button = ev.jbutton.button;
if(joyid < 8 && button < 32)
vid.scfg.joyaction[joyid][button] = vid.scfg.setwhat;
vid.scfg.setwhat = 0;
}
else if(ev.type == SDL_JOYHATMOTION && cmode == emShmupConfig && vid.scfg.setwhat) {
int joyid = ev.jhat.which;
int hat = ev.jhat.hat;
int dir = 4;
if(ev.jhat.value == SDL_HAT_UP) dir = 0;
if(ev.jhat.value == SDL_HAT_RIGHT) dir = 1;
if(ev.jhat.value == SDL_HAT_DOWN) dir = 2;
if(ev.jhat.value == SDL_HAT_LEFT) dir = 3;
if(joyid < 8 && hat < 4 && dir < 4) {
vid.scfg.hataction[joyid][hat][dir] = vid.scfg.setwhat;
2016-01-02 10:09:13 +00:00
vid.scfg.setwhat = 0;
}
2017-03-23 10:53:57 +00:00
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
else if(ev.type == SDL_JOYBUTTONDOWN && DEFAULTCONTROL) {
flashMessages();
movepcto(joydir);
checkjoy();
}
2016-01-02 10:09:13 +00:00
2017-03-23 10:53:57 +00:00
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)) shiftmul = -1;
if(ev.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) shiftmul /= 10;
if(sym == SDLK_RETURN && (ev.key.keysym.mod & (KMOD_LALT | KMOD_RALT))) {
sym = 0; uni = 0;
switchFullscreen();
}
2017-03-23 10:53:57 +00:00
}
dialog::handleZooming(ev);
if(sym == SDLK_F1 && cmode == emNormal && playermoved)
help = "@";
bool rollchange =
cmode == emOverview && getcstat >= 2000 && cheater;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(ev.type == SDL_MOUSEBUTTONDOWN) {
flashMessages();
mousepressed = true;
mousing = true;
actonrelease = true;
if(ev.button.button==SDL_BUTTON_WHEELUP && ((cmode == emQuit) ^ !canmove)) {
}
else if(ev.button.button==SDL_BUTTON_WHEELDOWN) {
if(cmode == (canmove ? emQuit : emNormal)) {
2016-01-02 10:09:13 +00:00
sym = 1; msgscroll--; didsomething = true;
}
2017-03-23 10:53:57 +00:00
else
if(cmode == emTactic || cmode == emYendor || cmode == emPickEuclidean ||
cmode == emLeader || cmode == emScores || cmode == emOverview)
if(!rollchange)
sym = uni = PSEUDOKEY_WHEELDOWN;
}
if(ev.button.button==SDL_BUTTON_WHEELUP) {
if(cmode == (canmove ? emQuit : emNormal)) {
sym = 1; msgscroll++; didsomething = true;
}
else if(cmode == (canmove ? emNormal : emQuit) || cmode == emMapEditor || cmode == emDraw) {
2015-08-08 13:57:52 +00:00
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;
}
2017-03-23 10:53:57 +00:00
else
if(cmode == emTactic || cmode == emYendor || cmode == emPickEuclidean ||
cmode == emLeader || cmode == emScores || cmode == emOverview)
if(getcstat < 2000 || !cheater)
sym = uni = PSEUDOKEY_WHEELUP;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else if(ev.button.button == SDL_BUTTON_RIGHT) {
sym = 1; didsomething = true;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else if(ev.button.button == SDL_BUTTON_MIDDLE) {
sym = 2; didsomething = true;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(ev.type == SDL_MOUSEBUTTONUP) {
mousepressed = false;
mousing = true;
if(ev.button.button==SDL_BUTTON_RIGHT || leftclick)
sym = SDLK_F1;
else if(ev.button.button==SDL_BUTTON_MIDDLE || rightclick)
sym = 1, didsomething = true;
else if(ev.button.button == SDL_BUTTON_LEFT && actonrelease) {
sym = getcstat, uni = getcstat, shiftmul = getcshift;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else if(ev.button.button == SDL_BUTTON_WHEELUP && rollchange) {
sym = getcstat, uni = getcstat, shiftmul = getcshift, wheelclick = true;
}
else if(ev.button.button == SDL_BUTTON_WHEELDOWN && rollchange) {
sym = getcstat, uni = getcstat, shiftmul = -getcshift, wheelclick = true;
}
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(ev.type == SDL_MOUSEMOTION) {
hyperpoint mouseoh = mouseh;
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
mousing = true;
mousemoved = true;
mousex = ev.motion.x;
mousey = ev.motion.y;
#ifndef NORUG
if(rug::rugged)
mouseh = rug::gethyper(mousex, mousey);
else
2015-08-08 13:57:52 +00:00
#endif
2017-03-23 10:53:57 +00:00
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);
}
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
#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();
2015-08-08 13:57:52 +00:00
#endif
2017-03-23 10:53:57 +00:00
if(mousepressed && inslider) {
sym = getcstat, uni = getcstat, shiftmul = getcshift;
2015-08-08 13:57:52 +00:00
}
}
2017-03-23 10:53:57 +00:00
handlekey(sym, uni, ev);
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
if(ev.type == SDL_QUIT) {
if(needConfirmation() && cmode !=emQuit) cmode = emQuit;
else quitmainloop = true;
}
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
}
#endif
2017-03-23 10:53:57 +00:00
void mainloop() {
lastt = 0;
#ifdef WEB
initweb();
emscripten_set_main_loop(mainloopiter, 0, true);
#else
while(!quitmainloop) mainloopiter();
#endif
2015-08-08 13:57:52 +00:00
}
void cleargraph() {
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"clear graph\n"));
#ifndef NOTTF
2015-08-08 13:57:52 +00:00
for(int i=0; i<256; i++) if(font[i]) TTF_CloseFont(font[i]);
#endif
#ifndef EXTERNALFONT
2015-08-08 13:57:52 +00:00
for(int i=0; i<128; i++) if(glfont[i]) delete glfont[i];
#endif
#ifndef NOSDL
2015-08-08 13:57:52 +00:00
#ifndef SIMULATE_JOYSTICK
2016-01-02 10:09:13 +00:00
closeJoysticks();
2015-08-08 13:57:52 +00:00
#endif
SDL_Quit();
#endif
}
2015-08-08 13:57:52 +00:00
void cleargraphmemory() {
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"clear graph memory\n"));
2015-08-08 13:57:52 +00:00
mouseover = centerover = lmouseover = NULL;
2017-03-23 10:53:57 +00:00
#ifndef NOEDIT
2016-01-02 10:09:13 +00:00
if(mapeditor::painttype == 4)
mapeditor::painttype = 0, mapeditor::paintwhat = 0,
mapeditor::paintwhat_str = "clear monster";
mapeditor::copywhat = NULL;
mapeditor::undo.clear();
if(!cheater) mapeditor::displaycodes = 0;
if(!cheater) mapeditor::whichShape = 0;
#endif
2017-03-23 10:53:57 +00:00
for(int i=0; i<ANIMLAYERS; i++) animations[i].clear();
gmatrix.clear(); gmatrix0.clear();
2015-08-08 13:57:52 +00:00
}
2016-01-02 10:09:13 +00:00
void showMissionScreen() {
cmode = emQuit;
2016-08-26 09:58:03 +00:00
msgscroll = 0;
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
void resetGeometry() {
precalc();
fp43.analyze();
resetGL();
}
//=== animation
map<cell*, animation> animations[ANIMLAYERS];
unordered_map<cell*, transmatrix> gmatrix, gmatrix0;
void animateMovement(cell *src, cell *tgt, int layer) {
if(vid.mspeed >= 5) return; // no animations!
if(confusingGeometry()) return;
if(gmatrix.count(src) && gmatrix.count(tgt)) {
animation& a = animations[layer][tgt];
if(animations[layer].count(src)) {
a = animations[layer][src];
a.wherenow = inverse(gmatrix[tgt]) * gmatrix[src] * a.wherenow;
animations[layer].erase(src);
}
else {
a.ltick = ticks;
a.wherenow = inverse(gmatrix[tgt]) * gmatrix[src];
a.footphase = 0;
}
}
}
vector<pair<cell*, animation> > animstack;
void indAnimateMovement(cell *src, cell *tgt, int layer) {
if(vid.mspeed >= 5) return; // no animations!
if(confusingGeometry()) return;
if(animations[layer].count(tgt)) {
animation res = animations[layer][tgt];
animations[layer].erase(tgt);
animateMovement(src, tgt, layer);
if(animations[layer].count(tgt))
animstack.push_back(make_pair(tgt, animations[layer][tgt]));
animations[layer][tgt] = res;
}
else {
animateMovement(src, tgt, layer);
if(animations[layer].count(tgt)) {
animstack.push_back(make_pair(tgt, animations[layer][tgt]));
animations[layer].erase(tgt);
}
}
}
void commitAnimations(int layer) {
for(int i=0; i<size(animstack); i++)
animations[layer][animstack[i].first] = animstack[i].second;
animstack.clear();
}
void animateReplacement(cell *a, cell *b, int layer) {
if(vid.mspeed >= 5) return; // no animations!
static cell c1;
gmatrix[&c1] = gmatrix[b];
if(animations[layer].count(b)) animations[layer][&c1] = animations[layer][b];
animateMovement(a, b, layer);
animateMovement(&c1, a, layer);
}