mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-27 14:37:16 +00:00
8830 lines
270 KiB
C++
8830 lines
270 KiB
C++
// Hyperbolic Rogue -- graphics
|
|
|
|
// Copyright (C) 2011-2012 Zeno Rogue, see 'hyper.cpp' for details
|
|
|
|
// basic graphics:
|
|
|
|
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;
|
|
|
|
#define WOLNIEJ 1
|
|
#define BTOFF 0x404040
|
|
#define BTON 0xC0C000
|
|
|
|
#define K(c) (c->type==6?0:1)
|
|
|
|
#ifdef ROGUEVIZ
|
|
#define DOSHMUP (shmup::on || rogueviz::on)
|
|
#else
|
|
#define DOSHMUP shmup::on
|
|
#endif
|
|
|
|
// #define PANDORA
|
|
|
|
#ifndef MOBILE
|
|
#include <SDL/SDL.h>
|
|
|
|
#ifdef SDLAUDIO
|
|
#include <SDL/SDL_mixer.h>
|
|
#endif
|
|
|
|
#ifndef MAC
|
|
#undef main
|
|
#endif
|
|
|
|
#include <SDL/SDL_ttf.h>
|
|
#endif
|
|
|
|
int colorbar;
|
|
#define COLORBAR "###"
|
|
|
|
#ifndef MOBILE
|
|
SDL_Surface *s;
|
|
TTF_Font *font[256];
|
|
SDL_Joystick* sticks[8];
|
|
int numsticks;
|
|
#endif
|
|
|
|
ld shiftmul = 1;
|
|
|
|
bool inHighQual; // taking high quality screenshot
|
|
int webdisplay = 0;
|
|
|
|
// R:239, G:208, B:207
|
|
|
|
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 };
|
|
unsigned int dresscolors2[] = { 7, 0x8080FFC0, 0x80FF80C0, 0xFF8080C0, 0xFFFF80C0, 0xFF80FFC0, 0x80FFFFC0, 0xFFFFFF80 };
|
|
unsigned int swordcolors[] = { 6, 0xC0C0C0FF, 0xFFFFFFFF, 0xFFC0C0FF, 0xC0C0FFFF, 0x808080FF, 0x202020FF };
|
|
unsigned int eyecolors[] = { 4, 0x00C000FF, 0x0000C0FF, 0xC00000FF, 0xC0C000FF };
|
|
|
|
// is the player using mouse? (used for auto-cross)
|
|
bool mousing = true;
|
|
|
|
// is the mouse button pressed?
|
|
bool mousepressed = false;
|
|
bool mousemoved = false;
|
|
bool actonrelease = false;
|
|
|
|
emtype cmode = emNormal, lastmode = emNormal; // last mode in Help
|
|
|
|
//
|
|
int axestate;
|
|
|
|
int ticks;
|
|
int frameid;
|
|
|
|
videopar vid;
|
|
int default_language;
|
|
|
|
charstyle& getcs() {
|
|
if(multi::players>1 && multi::cpid >= 0 && multi::cpid < multi::players)
|
|
return multi::scs[multi::cpid];
|
|
else
|
|
return vid.cs;
|
|
}
|
|
|
|
int playergender() {
|
|
return (getcs().charid&1) ? GEN_F : GEN_M;
|
|
}
|
|
int princessgender() {
|
|
int g = playergender();
|
|
if(vid.samegender) return g;
|
|
return g == GEN_M ? GEN_F : GEN_M;
|
|
}
|
|
|
|
int lang() {
|
|
if(vid.language >= 0)
|
|
return vid.language;
|
|
return default_language;
|
|
}
|
|
|
|
eItem orbToTarget;
|
|
eMonster monsterToSummon;
|
|
|
|
int sightrange = 7;
|
|
bool overgenerate = false; // generate a bigger area with high sightrange
|
|
|
|
cell *mouseover, *mouseover2, *lmouseover, *centerover;
|
|
ld modist, modist2, centdist;
|
|
string mouseovers;
|
|
|
|
movedir mousedest, joydir;
|
|
|
|
int mousex, mousey, joyx, joyy, panjoyx, panjoyy;
|
|
bool autojoy = true;
|
|
hyperpoint mouseh;
|
|
|
|
bool leftclick, rightclick, targetclick, hiliteclick, anyshiftclick, wheelclick,
|
|
forcetarget, lshiftclick, lctrlclick;
|
|
bool gtouched;
|
|
bool revcontrol;
|
|
|
|
int getcstat; ld getcshift; bool inslider;
|
|
|
|
int ZZ;
|
|
|
|
string help;
|
|
|
|
int andmode = 0;
|
|
|
|
int darken = 0;
|
|
|
|
struct fallanim {
|
|
int t_mon, t_floor;
|
|
eWall walltype;
|
|
eMonster m;
|
|
fallanim() { t_floor = 0; t_mon = 0; walltype = waNone; }
|
|
};
|
|
|
|
map<cell*, fallanim> fallanims;
|
|
|
|
bool doHighlight() {
|
|
return (hiliteclick && darken < 2) ? !mmhigh : mmhigh;
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
int& qpixel(SDL_Surface *surf, int x, int y) {
|
|
if(x<0 || y<0 || x >= surf->w || y >= surf->h) return ZZ;
|
|
char *p = (char*) surf->pixels;
|
|
p += y * surf->pitch;
|
|
int *pi = (int*) (p);
|
|
return pi[x];
|
|
}
|
|
|
|
int qpixel3(SDL_Surface *surf, int x, int y) {
|
|
if(x<0 || y<0 || x >= surf->w || y >= surf->h) return ZZ;
|
|
char *p = (char*) surf->pixels;
|
|
p += y * surf->pitch;
|
|
p += x;
|
|
int *pi = (int*) (p);
|
|
return pi[0];
|
|
}
|
|
|
|
void loadfont(int siz) {
|
|
if(!font[siz]) {
|
|
#ifdef WEB
|
|
font[siz] = TTF_OpenFont("sans-serif", siz);
|
|
#else
|
|
font[siz] = TTF_OpenFont("DejaVuSans-Bold.ttf", siz);
|
|
#endif
|
|
// Destination set by ./configure (in the GitHub repository)
|
|
#ifdef FONTDESTDIR
|
|
if (font[siz] == NULL) {
|
|
font[siz] = TTF_OpenFont(FONTDESTDIR, siz);
|
|
}
|
|
#endif
|
|
if (font[siz] == NULL) {
|
|
printf("error: Font file not found\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
int gl_width(int size, const char *s);
|
|
|
|
int textwidth(int siz, const string &str) {
|
|
if(size(str) == 0) return 0;
|
|
|
|
#ifdef WEB
|
|
return gl_width(siz, str.c_str());
|
|
|
|
#else
|
|
|
|
loadfont(siz);
|
|
|
|
int w, h;
|
|
TTF_SizeUTF8(font[siz], str.c_str(), &w, &h);
|
|
// printf("width = %d [%d]\n", w, size(str));
|
|
return w;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
int textwidth(int siz, const string &str);
|
|
|
|
#ifdef IOS
|
|
|
|
int textwidth(int siz, const string &str) {
|
|
return mainfont->getSize(str, siz / 36.0).width;
|
|
}
|
|
|
|
#endif
|
|
|
|
int gradient(int c0, int c1, ld v0, ld v, ld v1);
|
|
|
|
#ifdef LOCAL
|
|
double fadeout = 1;
|
|
#endif
|
|
|
|
int darkened(int c) {
|
|
#ifdef LOCAL
|
|
c = gradient(0, c, 0, fadeout, 1);
|
|
#endif
|
|
// c = ((c & 0xFFFF) << 8) | ((c & 0xFF0000) >> 16);
|
|
// c = ((c & 0xFFFF) << 8) | ((c & 0xFF0000) >> 16);
|
|
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
|
|
|
|
bool cameraangle_on;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void selectEyeGL(int ed) {
|
|
DEBB(DF_GRAPH, (debugfile,"selectEyeGL\n"));
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
glTranslatef((vid.xcenter*2.)/vid.xres - 1, 1 - (vid.ycenter*2.)/vid.yres, 0);
|
|
|
|
if(pmodel) {
|
|
vid.scrdist = 4 * vid.radius;
|
|
|
|
// 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);
|
|
|
|
float lowdepth = .1;
|
|
float hidepth = 1e9;
|
|
|
|
// 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);
|
|
|
|
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;
|
|
}
|
|
|
|
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() {
|
|
DEBB(DF_GRAPH, (debugfile,"setGLProjection\n"));
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
unsigned char *c = (unsigned char*) (&backcolor);
|
|
glClearColor(c[2] / 255.0, c[1] / 255.0, c[0]/255.0, 1);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
glEnable(GL_BLEND);
|
|
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);
|
|
|
|
selectEyeGL(0);
|
|
}
|
|
|
|
void buildpolys();
|
|
|
|
#ifndef MOBILE
|
|
|
|
struct glfont_t {
|
|
GLuint * textures; // Holds The Texture Id's
|
|
//GLuint list_base; // Holds The First Display List ID
|
|
int widths[128+NUMEXTRA];
|
|
int heights[128+NUMEXTRA];
|
|
float tx[128+NUMEXTRA];
|
|
float ty[128+NUMEXTRA];
|
|
};
|
|
|
|
glfont_t *glfont[256];
|
|
|
|
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) {
|
|
// fprintf(stderr, "OPENGL ERROR #%i: in file %s on line %i :: %s\n",errCode,file, line, GLcall);
|
|
}
|
|
}
|
|
#define GLERR(call) glError(call, __FILE__, __LINE__)
|
|
|
|
void init_glfont(int size) {
|
|
if(glfont[size]) return;
|
|
DEBB(DF_INIT, (debugfile,"init GL font: %d\n", size));
|
|
|
|
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 WEB
|
|
loadfont(size);
|
|
if(!font[size]) return;
|
|
|
|
char str[2]; str[1] = 0;
|
|
|
|
SDL_Color white;
|
|
white.r = white.g = white.b = 255;
|
|
#endif
|
|
|
|
// glListBase(0);
|
|
|
|
for(int ch=1;ch<128+NUMEXTRA;ch++) {
|
|
|
|
#ifdef WEB
|
|
if(ch<32) continue;
|
|
int otwidth, otheight, tpix[3000], tpixindex = 0;
|
|
loadCompressedChar(otwidth, otheight, tpix);
|
|
|
|
#else
|
|
|
|
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;
|
|
|
|
int otwidth = txt->w;
|
|
int otheight = txt->h;
|
|
#endif
|
|
|
|
int twidth = next_p2( otwidth );
|
|
int theight = next_p2( otheight );
|
|
|
|
#ifdef WEB
|
|
int expanded_data[twidth * theight];
|
|
#else
|
|
Uint16 expanded_data[twidth * theight];
|
|
#endif
|
|
|
|
for(int j=0; j <theight;j++) for(int i=0; i < twidth; i++) {
|
|
#ifdef WEB
|
|
expanded_data[(i+j*twidth)] = (i>=otwidth || j>=otheight) ? 0 : tpix[tpixindex++];
|
|
#else
|
|
expanded_data[(i+j*twidth)] =
|
|
(i>=txt->w || j>=txt->h) ? 0 : ((qpixel(txt, i, j)>>24)&0xFF) * 0x101;
|
|
#endif
|
|
}
|
|
|
|
/* 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");
|
|
}
|
|
} */
|
|
|
|
// printf("b\n");
|
|
f.widths[ch] = otwidth;
|
|
f.heights[ch] = otheight;
|
|
|
|
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 WEB
|
|
GL_RGBA, GL_UNSIGNED_BYTE,
|
|
#else
|
|
GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
|
|
#endif
|
|
expanded_data );
|
|
|
|
// printf("c\n");
|
|
|
|
float x=(float)otwidth / (float)twidth;
|
|
float y=(float)otheight / (float)theight;
|
|
f.tx[ch] = x;
|
|
f.ty[ch] = y;
|
|
|
|
/* glNewList(f.list_base+ch,GL_COMPILE);
|
|
glBindTexture(GL_TEXTURE_2D, f.textures[ch]);
|
|
|
|
//glPushMatrix();
|
|
|
|
// glTranslatef(bitmap_glyph->left,0,0);
|
|
// glTranslatef(0,bitmap_glyph->top-bitmap.rows,0);
|
|
|
|
// printf("d\n");
|
|
|
|
glBegin(GL_QUADS);
|
|
float eps=0;
|
|
glTexCoord2d(eps,eps); glVertex2f(0, txt->h);
|
|
glTexCoord2d(eps,y-0); glVertex2f(0, 0);
|
|
glTexCoord2d(x-eps,y-0); glVertex2f(txt->w, 0);
|
|
glTexCoord2d(x-eps,0); glVertex2f(txt->w, txt->h);
|
|
glEnd();
|
|
glEndList(); */
|
|
//glPopMatrix();
|
|
|
|
#ifndef WEB
|
|
SDL_FreeSurface(txt);
|
|
#endif
|
|
}
|
|
|
|
//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++];
|
|
}
|
|
|
|
GLfloat tver[24];
|
|
|
|
int gl_width(int size, const char *s) {
|
|
int gsiz = size;
|
|
if(size > vid.fsize || size > 72) gsiz = 72;
|
|
|
|
#ifdef WEB
|
|
gsiz = 36;
|
|
#endif
|
|
|
|
init_glfont(gsiz);
|
|
|
|
#ifndef WEB
|
|
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;
|
|
}
|
|
|
|
bool gl_print(int x, int y, int shift, int size, const char *s, int color, int align) {
|
|
|
|
// printf("gl_print: %s\n", s.c_str());
|
|
|
|
// We Want A Coordinate System Where Distance Is Measured In Window Pixels.
|
|
|
|
/*
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
gluOrtho2D(0, 0, vid.xscr, vid.yscr); */
|
|
|
|
int gsiz = size;
|
|
if(size > vid.fsize || size > 72) gsiz = 72;
|
|
// if(size >= 36) gsiz = 72;
|
|
|
|
#ifdef WEB
|
|
gsiz = 36;
|
|
#endif
|
|
|
|
init_glfont(gsiz);
|
|
|
|
#ifndef WEB
|
|
if(!font[gsiz]) return false;
|
|
#endif
|
|
|
|
glfont_t& f(*glfont[gsiz]);
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glcolor2((color << 8) | 0xFF);
|
|
|
|
int tsize = 0;
|
|
for(int i=0; s[i];) tsize += f.widths[getnext(s,i)] * size/gsiz;
|
|
x -= tsize * align / 16;
|
|
y += f.heights[32] * size / (gsiz*2);
|
|
|
|
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;
|
|
} */
|
|
|
|
for(int i=0; s[i];) {
|
|
|
|
// glListBase(f.list_base);
|
|
// glCallList(s[i]); // s[i]);
|
|
|
|
int tabid = getnext(s,i);
|
|
float fx=f.tx[tabid];
|
|
float fy=f.ty[tabid];
|
|
int wi = f.widths[tabid] * size/gsiz;
|
|
int hi = f.heights[tabid] * size/gsiz;
|
|
|
|
GLERR("pre-print");
|
|
|
|
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]);
|
|
|
|
#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
|
|
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();
|
|
#endif
|
|
glPopMatrix();
|
|
}
|
|
|
|
if(vid.goteyes) selectEyeMask(0);
|
|
|
|
GLERR("print");
|
|
|
|
// int tabid = s[i];
|
|
x += wi;
|
|
|
|
/*
|
|
printf("point %d,%d\n", x, y);
|
|
glBegin(GL_POINTS);
|
|
glVertex3f(rand() % 100 - rand() % 100, rand() % 100 - rand() % 100, 100);
|
|
glEnd(); */
|
|
|
|
}
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
return clicked;
|
|
// printf("gl_print ok\n");
|
|
}
|
|
#endif
|
|
|
|
void resetGL() {
|
|
DEBB(DF_INIT, (debugfile,"reset GL\n"));
|
|
#ifndef MOBILE
|
|
for(int i=0; i<128; i++) if(glfont[i]) {
|
|
delete glfont[i];
|
|
glfont[i] = NULL;
|
|
}
|
|
#endif
|
|
buildpolys();
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
bool displaystr(int x, int y, int shift, int size, const char *str, int color, int align) {
|
|
if(strlen(str) == 0) return false;
|
|
|
|
if(size < 4 || size > 255) {
|
|
// printf("size = %d\n", size);
|
|
return false;
|
|
}
|
|
|
|
#ifdef WEB
|
|
return gl_print(x, y, shift, size, str, color, align);
|
|
#endif
|
|
|
|
#ifdef GL
|
|
if(vid.usingGL) return gl_print(x, y, shift, size, str, color, align);
|
|
#endif
|
|
|
|
SDL_Color col;
|
|
col.r = (color >> 16) & 255;
|
|
col.g = (color >> 8 ) & 255;
|
|
col.b = (color >> 0 ) & 255;
|
|
|
|
col.r >>= darken; col.g >>= darken; col.b >>= darken;
|
|
|
|
loadfont(size);
|
|
|
|
SDL_Surface *txt = (vid.usingAA?TTF_RenderUTF8_Blended:TTF_RenderUTF8_Solid)(font[size], str, col);
|
|
|
|
if(txt == NULL) return false;
|
|
|
|
SDL_Rect rect;
|
|
|
|
rect.w = txt->w;
|
|
rect.h = txt->h;
|
|
|
|
rect.x = x - rect.w * align / 16;
|
|
rect.y = y - rect.h/2;
|
|
|
|
bool clicked = (mousex >= rect.x && mousey >= rect.y && mousex <= rect.x+rect.w && mousey <= rect.y+rect.h);
|
|
|
|
if(shift) {
|
|
SDL_Surface* txt2 = SDL_DisplayFormat(txt);
|
|
SDL_LockSurface(txt2);
|
|
SDL_LockSurface(s);
|
|
int c0 = qpixel(txt2, 0, 0);
|
|
for(int yy=0; yy<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;
|
|
}
|
|
|
|
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) {
|
|
int g = (int) graphdata.size(), q = 0;
|
|
gdpush((int) s.size()); for(int i=0; i<s.size(); i++) {
|
|
#ifdef ANDROID
|
|
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++;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
gdpush(s[i]); q++;
|
|
}
|
|
}
|
|
graphdata[g] = q;
|
|
}
|
|
|
|
bool displayfr(int x, int y, int b, int size, const string &s, int color, int align) {
|
|
gdpush(2); gdpush(x); gdpush(y); gdpush(align);
|
|
gdpush(color); gdpush(size); gdpush(b);
|
|
gdpush_utf8(s);
|
|
int mx = mousex - x;
|
|
int my = mousey - y;
|
|
int len = textwidth(size, s);
|
|
return
|
|
mx >= -len*align/32 && mx <= +len*(16-align)/32 &&
|
|
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) {
|
|
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);
|
|
}
|
|
|
|
#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;
|
|
int quantity;
|
|
string msg;
|
|
};
|
|
|
|
vector<msginfo> msgs;
|
|
|
|
vector<msginfo> gamelog;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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(); }
|
|
|
|
void addMessage(string s, char spamtype) {
|
|
DEBB(DF_MSG, (debugfile,"addMessage: %s\n", s.c_str()));
|
|
|
|
msginfo m;
|
|
m.msg = s; m.spamtype = spamtype; m.flashout = false; m.stamp = ticks;
|
|
m.quantity = 1;
|
|
|
|
addMessageToLog(m, gamelog);
|
|
addMessageToLog(m, msgs);
|
|
}
|
|
|
|
void drawmessages() {
|
|
DEBB(DF_GRAPH, (debugfile,"draw messages\n"));
|
|
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);
|
|
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);
|
|
msgs[i++] = msgs[j];
|
|
}
|
|
}
|
|
msgs.resize(i);
|
|
}
|
|
|
|
eModel pmodel = mdDisk;
|
|
|
|
ld ghx, ghy, ghgx, ghgy;
|
|
hyperpoint ghpm = C0;
|
|
|
|
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];
|
|
}
|
|
}
|
|
|
|
hyperpoint gethyper(ld x, ld y) {
|
|
|
|
if(pmodel) {
|
|
ghx = x, ghy = y;
|
|
return ghpm;
|
|
}
|
|
|
|
ld hx = (x - vid.xcenter) / vid.radius;
|
|
ld hy = (y - vid.ycenter) / vid.radius;
|
|
|
|
if(euclid)
|
|
return hpxy(hx * (EUCSCALE + vid.alphax), hy * (EUCSCALE + vid.alphax));
|
|
|
|
ld hr = hx*hx+hy*hy;
|
|
|
|
if(hr > .9999 && !sphere) return Hypc;
|
|
|
|
// hz*hz-(hx/(hz+alpha))^2 - (hy/(hz+alpha))^2 =
|
|
|
|
// hz*hz-hr*(hz+alpha)^2 == 1
|
|
// hz*hz - hr*hr*hz*Hz
|
|
|
|
|
|
ld A, 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;
|
|
|
|
// Az^2 - Bz = C
|
|
B /= A; C /= A;
|
|
|
|
// z^2 - Bz = C
|
|
// z^2 - Bz + (B^2/4) = C + (B^2/4)
|
|
// z = (B/2) + sqrt(C + B^2/4)
|
|
|
|
ld rootsign = 1;
|
|
if(sphere && vid.alphax > 1) rootsign = -1;
|
|
|
|
ld hz = B / 2 + rootsign * sqrt(C + B*B/4);
|
|
|
|
hyperpoint H;
|
|
H[0] = hx * (hz+vid.alphax);
|
|
H[1] = hy * (hz+vid.alphax);
|
|
H[2] = hz;
|
|
|
|
return H;
|
|
}
|
|
|
|
void 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) {
|
|
|
|
ld tz = euclid ? (EUCSCALE+vid.alphax) : vid.alphax+H[2];
|
|
if(tz < 1e-3 && tz > -1e-3) tz = 1000;
|
|
|
|
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);
|
|
|
|
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;
|
|
return;
|
|
}
|
|
|
|
if(pmodel == mdDisk) {
|
|
ret[0] = H[0] / tz;
|
|
ret[1] = H[1] / tz;
|
|
ret[2] = (1 - vid.beta / tz);
|
|
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);
|
|
return;
|
|
}
|
|
|
|
tz = H[2]+vid.alphax;
|
|
|
|
if(pmodel == mdPolygonal || pmodel == mdPolynomial) {
|
|
pair<long double, long double> p = polygonal::compute(H[0]/tz, H[1]/tz);
|
|
ret[0] = p.first;
|
|
ret[1] = p.second;
|
|
ret[2] = 0;
|
|
ghcheck(ret,H);
|
|
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;
|
|
|
|
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);
|
|
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));
|
|
double yv = 2*y0 / u;
|
|
double sigma = 2 * atan(yv * zlev) - M_PI/2;
|
|
|
|
x0 = tau; y0 = sigma;
|
|
|
|
/* 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;
|
|
} */
|
|
|
|
ret[0] = x0/M_PI*2;
|
|
ret[1] = y0/M_PI*2;
|
|
ret[2] = 0;
|
|
|
|
if(zlev != 1 && vid.goteyes)
|
|
ret[2] = geom3::factor_to_lev(zlev) / (1 + yv * yv);
|
|
|
|
ghcheck(ret,H);
|
|
}
|
|
|
|
int dlit;
|
|
|
|
// game-related graphics
|
|
|
|
transmatrix View; // current rotation, relative to viewctr
|
|
transmatrix cwtV; // player-relative view
|
|
transmatrix sphereflip; // on the sphere, flip
|
|
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];
|
|
}
|
|
|
|
int firegradient(double p) {
|
|
return gradient(0xFFFF00, 0xFF0000, 0, p, 1);
|
|
}
|
|
|
|
int firecolor(int phase) {
|
|
return gradient(0xFFFF00, 0xFF0000, -1, sin((phase + ticks)/100.0), 1);
|
|
}
|
|
|
|
int watercolor(int phase) {
|
|
return 0x0080C0FF + 256 * int(63 * sin((ticks + phase) / 50.));
|
|
}
|
|
|
|
int aircolor(int phase) {
|
|
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;
|
|
}
|
|
|
|
int weakfirecolor(int phase) {
|
|
return gradient(0xFF8000, 0xFF0000, -1, sin((phase + ticks)/500.0), 1);
|
|
}
|
|
|
|
int fc(int ph, int col, int z) {
|
|
if(items[itOrbFire]) col = darkena(firecolor(ph), 0, 0xFF);
|
|
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);
|
|
if(invismove)
|
|
col =
|
|
shmup::on ?
|
|
(col &~0XFF) | (int((col&0xFF) * .25))
|
|
: (col &~0XFF) | (int((col&0xFF) * (100+100*sin(ticks / 500.)))/200);
|
|
return col;
|
|
}
|
|
|
|
int lightat, safetyat;
|
|
void drawLightning() { lightat = ticks; }
|
|
void drawSafety() { safetyat = ticks; }
|
|
|
|
double eurad = 0.52;
|
|
double q3 = sqrt(double(3));
|
|
|
|
bool outofmap(hyperpoint h) {
|
|
if(euclid)
|
|
return h[0] * h[0] + h[1] * h[1] > 15 * eurad;
|
|
else if(sphere)
|
|
return h[2] < .1 && h[2] > -.1 && h[1] > -.1 && h[1] < .1 && h[0] > -.1 && h[0] < .1;
|
|
else
|
|
return h[2] < .5;
|
|
}
|
|
|
|
void drawShield(const transmatrix& V, eItem it) {
|
|
float ds = ticks / 300.;
|
|
int col = iinf[it].color;
|
|
if(it == itOrbShield && items[itOrbTime] && !orbused[it])
|
|
col = (col & 0xFEFEFE) / 2;
|
|
if(sphere && cwt.c->land == laHalloween && !wmblack && !wmascii)
|
|
col = 0;
|
|
double d = it == itOrbShield ? hexf : hexf - .1;
|
|
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);
|
|
}
|
|
|
|
void drawSpeed(const transmatrix& V) {
|
|
ld ds = ticks / 10.;
|
|
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);
|
|
}
|
|
}
|
|
|
|
void drawSafety(const transmatrix& V, int ct) {
|
|
ld ds = ticks / 50.;
|
|
int col = darkena(iinf[itOrbSafety].color, 0, 0xFF);
|
|
for(int a=0; a<ct; a++)
|
|
queueline(V*ddi0(ds+a*S84/ct, 2*hexf), V*ddi0(ds+(a+(ct-1)/2)*S84/ct, 2*hexf), col);
|
|
}
|
|
|
|
void drawFlash(const transmatrix& V) {
|
|
float ds = ticks / 300.;
|
|
int col = darkena(iinf[itOrbFlash].color, 0, 0xFF);
|
|
col &= ~1;
|
|
for(int u=0; u<5; u++) {
|
|
ld rad = hexf * (2.5 + .5 * sin(ds+u*.3));
|
|
for(int a=0; a<=S84; a++) curvepoint(V*ddi0(a, rad));
|
|
queuecurve(col, 0x8080808, PPR_LINE);
|
|
}
|
|
}
|
|
|
|
void drawLove(const transmatrix& V, int hdir) {
|
|
float ds = ticks / 300.;
|
|
int col = darkena(iinf[itOrbLove].color, 0, 0xFF);
|
|
col &= ~1;
|
|
for(int u=0; u<5; u++) {
|
|
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;
|
|
if(z <= 10) d += (10-z) * (10-z) * (10-z) / 3000.;
|
|
|
|
ld rad = hexf * (2.5 + .5 * sin(ds+u*.3)) * d;
|
|
curvepoint(V*ddi0(S42+hdir+a-1, rad));
|
|
}
|
|
queuecurve(col, 0x8080808, PPR_LINE);
|
|
}
|
|
}
|
|
|
|
void drawWinter(const transmatrix& V, int hdir) {
|
|
float ds = ticks / 300.;
|
|
int col = darkena(iinf[itOrbWinter].color, 0, 0xFF);
|
|
for(int u=0; u<20; u++) {
|
|
ld rad = 6 * sin(ds+u * 2 * M_PI / 20);
|
|
queueline(V*ddi0(S42+hdir+rad, hexf*.5), V*ddi0(S42+hdir+rad, hexf*3), col, 2);
|
|
}
|
|
}
|
|
|
|
void drawLightning(const transmatrix& V) {
|
|
int col = darkena(iinf[itOrbLightning].color, 0, 0xFF);
|
|
for(int u=0; u<20; u++) {
|
|
ld leng = 0.5 / (0.1 + (rand() % 100) / 100.0);
|
|
ld rad = rand() % S84;
|
|
queueline(V*ddi0(rad, hexf*0.3), V*ddi0(rad, hexf*leng), col, 2);
|
|
}
|
|
}
|
|
|
|
int displaydir(cell *c, int d) {
|
|
if(euclid)
|
|
return - d * S84 / c->type;
|
|
else
|
|
return S42 - d * S84 / c->type;
|
|
}
|
|
|
|
transmatrix ddspin(cell *c, int d, int bonus = 0) {
|
|
int hdir = displaydir(c, d) + bonus;
|
|
return getspinmatrix(hdir);
|
|
}
|
|
|
|
#ifdef WEB
|
|
Uint8 *SDL_GetKeyState(void *v) { static Uint8 tab[1024]; return tab; }
|
|
#endif
|
|
|
|
#include "shmup.cpp"
|
|
#include "conformal.cpp"
|
|
#include "rug.cpp"
|
|
|
|
void drawPlayerEffects(const transmatrix& V, cell *c, bool onplayer) {
|
|
if(!onplayer && !items[itOrbEmpathy]) return;
|
|
if(items[itOrbShield] > (shmup::on ? 0 : ORBBASE)) drawShield(V, itOrbShield);
|
|
if(items[itOrbShell] > (shmup::on ? 0 : ORBBASE)) drawShield(V, itOrbShell);
|
|
|
|
if(items[itOrbSpeed]) drawSpeed(V);
|
|
|
|
int ct = c->type;
|
|
|
|
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)));
|
|
}
|
|
}
|
|
|
|
if(onplayer && items[itOrbSafety]) drawSafety(V, ct);
|
|
|
|
if(onplayer && items[itOrbFlash]) drawFlash(V);
|
|
if(onplayer && items[itOrbLove]) drawLove(V, 0); // displaydir(c, cwt.spin));
|
|
|
|
if(items[itOrbWinter])
|
|
drawWinter(V, 0); // displaydir(c, cwt.spin));
|
|
|
|
if(onplayer && items[itOrbLightning]) drawLightning(V);
|
|
|
|
if(safetyat > 0) {
|
|
int tim = ticks - safetyat;
|
|
if(tim > 2500) safetyat = 0;
|
|
for(int u=tim; u<=2500; u++) {
|
|
if((u-tim)%250) continue;
|
|
ld rad = hexf * u / 250;
|
|
int col = 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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.);
|
|
queuepolyat(V2, shFlailBall, 0xFFFFFFFF, PPR_STUNSTARS);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
if(sh.s != sh.e)
|
|
queuepoly(V, sh, ds.color ? ds.color : color);
|
|
}
|
|
|
|
#ifndef NOEDIT
|
|
if(cmode == emDraw && mapeditor::editingShape(group, id)) {
|
|
|
|
usershapelayer &ds(usershapes[group][id]->d[mapeditor::dslayer]);
|
|
|
|
/* 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);
|
|
} */
|
|
|
|
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);
|
|
|
|
queuechr(P2, 10, 'x', 0xFF00FF);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
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");
|
|
if(id == 8 || id == 9) return XLATN("Familiar");
|
|
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));
|
|
/* poly_outline = OUTLINE_TRANS;
|
|
queuepoly(V, shHeptaMarker, darkena(mcol, 0, dd));
|
|
poly_outline = OUTLINE_NONE; */
|
|
return gradient(0x487830, mcol, 0, dd, 0xFF);
|
|
}
|
|
};
|
|
|
|
double footfun(double d) {
|
|
d -= floor(d);
|
|
return
|
|
d < .25 ? d :
|
|
d < .75 ? .5-d :
|
|
d-1;
|
|
}
|
|
|
|
bool ivoryz;
|
|
|
|
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)
|
|
mapeditor::drawtrans = V;
|
|
#endif
|
|
|
|
if(m == moTortoise && where && where->stuntime >= 3)
|
|
drawStunStars(V, where->stuntime-2);
|
|
else if (m == moTortoise || m == moPlayer || (where && !where->stuntime)) ;
|
|
else if(where && !(isMetalBeast(m) && where->stuntime == 1))
|
|
drawStunStars(V, where->stuntime);
|
|
|
|
if(m == moTortoise) {
|
|
int bits = where ? tortoise::getb(where) : 0;
|
|
tortoise::draw(V, bits, 0, where ? where->stuntime : 0);
|
|
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();
|
|
|
|
bool havus = drawUserShape(V, 0, cs.charid, cs.skincolor);
|
|
|
|
if(mapeditor::drawplayer && !havus) {
|
|
|
|
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));
|
|
|
|
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
|
|
int col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.swordcolor, 3);
|
|
queuepoly(VAHEAD, shWolf1, col);
|
|
queuepoly(VAHEAD, shWolf2, col);
|
|
queuepoly(VAHEAD, shWolf3, col);
|
|
}
|
|
}
|
|
else if(cs.charid >= 4) {
|
|
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);
|
|
}
|
|
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
|
|
int col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.swordcolor, 3);
|
|
queuepoly(VAHEAD * xpush(.04), shWolf1, col);
|
|
queuepoly(VAHEAD * xpush(.04), shWolf2, col);
|
|
}
|
|
}
|
|
else {
|
|
|
|
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);
|
|
|
|
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);
|
|
}
|
|
if(items[itOrbThorns])
|
|
queuepoly(VBODY, shHedgehogBladePlayer, items[itOrbDiscord] ? watercolor(0) : 0x00FF00FF);
|
|
else if(!shmup::on && items[itOrbDiscord])
|
|
queuepoly(VBODY, cs.charid >= 2 ? shSabre : shPSword, watercolor(0));
|
|
else if(items[itRevolver])
|
|
queuepoly(VBODY, shGunInHand, fc(314, cs.swordcolor, 3)); // 3 not colored
|
|
else if(!shmup::on)
|
|
queuepoly(VBODY, cs.charid >= 2 ? shSabre : shPSword, fc(314, cs.swordcolor, 3)); // 3 not colored
|
|
else if(shmup::curtime >= shmup::getPlayer()->nextshot)
|
|
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));
|
|
}
|
|
|
|
if(cheater && !autocheat) {
|
|
queuepoly(VHEAD, (cs.charid&1) ? shGoatHead : shDemon, darkena(0xFFFF00, 0, 0xFF));
|
|
// queuepoly(V, shHood, darkena(0xFF00, 1, 0xFF));
|
|
}
|
|
else {
|
|
queuepoly(VHEAD, shPFace, fc(500, cs.skincolor, 1));
|
|
queuepoly(VHEAD, (cs.charid&1) ? shFemaleHair : shPHead, fc(150, cs.haircolor, 2));
|
|
}
|
|
if(cs.charid&1)
|
|
queuepoly(VBODY, shFemaleDress, fc(500, cs.dresscolor, 4));
|
|
|
|
if(cs.charid == 2)
|
|
queuepoly(VBODY, shPrinceDress, fc(400, cs.dresscolor, 5));
|
|
if(cs.charid == 3)
|
|
queuepoly(VBODY, shPrincessDress, fc(400, cs.dresscolor2, 5));
|
|
|
|
}
|
|
|
|
if(knighted)
|
|
queuepoly(VBODY, shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
|
|
|
|
if(tortoise::seek())
|
|
tortoise::draw(VBODY * ypush(-crossf*.25), tortoise::seekbits, 4, 0);
|
|
|
|
}
|
|
}
|
|
|
|
else if(drawUserShape(V, 1, m, darkena(col, 0, 0xFF))) return false;
|
|
|
|
else if(isMimic(m) || m == moShadow || m == moIllusion) {
|
|
charstyle& cs = getcs();
|
|
|
|
if(drawUserShape(V, 0, (cs.charid&1)?1:0, darkena(col, 0, 0x80))) return false;
|
|
|
|
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));
|
|
}
|
|
else if(cs.charid >= 4) {
|
|
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));
|
|
}
|
|
else {
|
|
otherbodyparts(V, darkena(col, 0, 0x40), m, footphase);
|
|
queuepoly(VBODY, (cs.charid&1) ? shFemaleBody : shPBody, darkena(col, 0, 0X80));
|
|
|
|
if(!shmup::on)
|
|
queuepoly(VBODY, (cs.charid >= 2 ? shSabre : shPSword), darkena(col, 0, 0XC0));
|
|
else if(shmup::curtime >= shmup::getPlayer()->nextshot)
|
|
queuepoly(VBODY, shPKnife, darkena(col, 0, 0XC0));
|
|
|
|
queuepoly(VHEAD, (cs.charid&1) ? shFemaleHair : shPHead, darkena(col, 1, 0XC0));
|
|
queuepoly(VHEAD, shPFace, darkena(col, 0, 0XC0));
|
|
if(cs.charid&1)
|
|
queuepoly(VBODY, shFemaleDress, darkena(col, 1, 0XC0));
|
|
if(cs.charid == 2)
|
|
queuepoly(VBODY, shPrinceDress, darkena(col, 1, 0XC0));
|
|
if(cs.charid == 3)
|
|
queuepoly(VBODY, shPrincessDress, darkena(col, 1, 0XC0));
|
|
}
|
|
}
|
|
|
|
else if(m == moBullet) {
|
|
ShadowV(V, shKnife);
|
|
queuepoly(VBODY * spin(-M_PI/4), shKnife, getcs().swordcolor);
|
|
}
|
|
|
|
else if(m == moKnight || m == moKnightMoved) {
|
|
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));
|
|
int col;
|
|
if(!euclid && where && where->master->alt)
|
|
col = cloakcolor(roundTableRadius(where));
|
|
else
|
|
col = cloakcolor(newRoundTableRadius());
|
|
queuepoly(VBODY, shKnightCloak, darkena(col, 1, 0xFF));
|
|
queuepoly(VHEAD, shPHead, darkena(0x703800, 1, 0XFF));
|
|
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
|
|
return false;
|
|
}
|
|
|
|
else if(m == moGolem || m == moGolemMoved) {
|
|
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));
|
|
}
|
|
|
|
else if(isPrincess(m) || m == moFalsePrincess || m == moRoseLady || m == moRoseBeauty) {
|
|
|
|
bool girl = princessgender() == GEN_F;
|
|
bool evil = !isPrincess(m);
|
|
|
|
int facecolor = evil ? 0xC0B090FF : 0xD0C080FF;
|
|
|
|
ShadowV(V, girl ? shFemaleBody : shPBody);
|
|
otherbodyparts(V, facecolor, m, footphase);
|
|
queuepoly(VBODY, girl ? shFemaleBody : shPBody, facecolor);
|
|
|
|
if(m == moPrincessArmed)
|
|
queuepoly(VBODY, vid.cs.charid < 2 ? shSabre : shPSword, 0xFFFFFFFF);
|
|
|
|
if((m == moFalsePrincess || m == moRoseBeauty) && where && where->cpdist == 1)
|
|
queuepoly(VBODY, shPKnife, 0xFFFFFFFF);
|
|
|
|
if(m == moRoseLady) {
|
|
queuepoly(VBODY, shPKnife, 0xFFFFFFFF);
|
|
queuepoly(VBODY * Mirror, shPKnife, 0xFFFFFFFF);
|
|
}
|
|
|
|
if(m == moRoseLady) {
|
|
// queuepoly(V, girl ? shGoatHead : shDemon, 0x800000FF);
|
|
queuepoly(VHEAD, girl ? shFemaleHair : shPHead, evil ? 0x500050FF : 0x332A22FF);
|
|
}
|
|
else if(m == moRoseBeauty) {
|
|
if(girl) {
|
|
queuepoly(VHEAD, shBeautyHair, 0xF0A0D0FF);
|
|
queuepoly(VHEAD, shFlowerHair, 0xC00000FF);
|
|
}
|
|
else {
|
|
queuepoly(VHEAD, shPHead, 0xF0A0D0FF);
|
|
queuepoly(VHEAD, shFlowerHand, 0xC00000FF);
|
|
queuepoly(VBODY, shSuspenders, 0xC00000FF);
|
|
}
|
|
}
|
|
else {
|
|
queuepoly(VHEAD, girl ? shFemaleHair : shPHead,
|
|
evil ? 0xC00000FF : 0x332A22FF);
|
|
}
|
|
queuepoly(VHEAD, shPFace, facecolor);
|
|
|
|
if(girl) {
|
|
queuepoly(VBODY, shFemaleDress, evil ? 0xC000C0FF : 0x00C000FF);
|
|
if(vid.cs.charid < 2)
|
|
queuepoly(VBODY, shPrincessDress, evil ? 0xC040C0C0 : 0x8080FFC0);
|
|
}
|
|
else {
|
|
if(vid.cs.charid < 2)
|
|
queuepoly(VBODY, shPrinceDress, evil ? 0x802080FF : 0x404040FF);
|
|
}
|
|
}
|
|
|
|
else if(m == moWolf || m == moRedFox || m == moWolfMoved) {
|
|
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));
|
|
}
|
|
else if(m == moVineBeast) {
|
|
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);
|
|
}
|
|
else if(m == moMouse || m == moMouseMoved) {
|
|
queuepoly(VALEGS, shMouse, darkena(col, 0, 0xFF));
|
|
queuepoly(VALEGS, shMouseLegs, darkena(col, 1, 0xFF));
|
|
queuepoly(VALEGS, shMouseEyes, 0xFF);
|
|
}
|
|
else if(isBug(m)) {
|
|
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));
|
|
}
|
|
else if(m == moRunDog) {
|
|
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));
|
|
}
|
|
else if(m == moOrangeDog) {
|
|
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));
|
|
}
|
|
else if(m == moShark || m == moGreaterShark || m == moCShark)
|
|
queuepoly(VFISH, shShark, darkena(col, 0, 0xFF));
|
|
else if(m == moEagle || m == moParrot || m == moBomberbird || m == moAlbatross ||
|
|
m == moTameBomberbird || m == moWindCrow || m == moTameBomberbirdMoved) {
|
|
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)); */
|
|
}
|
|
else if(m == moGargoyle) {
|
|
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);
|
|
}
|
|
else if(m == moDesertman) {
|
|
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);
|
|
}
|
|
else if(m == moPalace || m == moFatGuard || m == moVizier || m == moSkeleton) {
|
|
queuepoly(VBODY, shSabre, 0xFFFFFFFF);
|
|
if(m == moSkeleton) {
|
|
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);
|
|
}
|
|
else {
|
|
ShadowV(V, shPBody);
|
|
otherbodyparts(V, darkena(0xFFD500, 0, 0xFF), m, footphase);
|
|
if(m == moFatGuard) {
|
|
queuepoly(VBODY, shFatBody, darkena(0xC06000, 0, 0xFF));
|
|
col = 0xFFFFFF;
|
|
if(where && where->hitpoints >= 3)
|
|
queuepoly(VBODY, shKnightCloak, darkena(0xFFC0C0, 1, 0xFF));
|
|
}
|
|
else {
|
|
queuepoly(VBODY, shPBody, darkena(0xFFD500, 0, 0xFF));
|
|
queuepoly(VBODY, shKnightArmor, m == moVizier ? 0xC000C0FF :
|
|
darkena(0x00C000, 1, 0xFF));
|
|
if(where && where->hitpoints >= 3)
|
|
queuepoly(VBODY, shKnightCloak, m == moVizier ? 0x800080Ff :
|
|
darkena(0x00FF00, 1, 0xFF));
|
|
}
|
|
queuepoly(VHEAD, shTurban1, darkena(col, 1, 0xFF));
|
|
if(where && where->hitpoints >= 2)
|
|
queuepoly(VHEAD, shTurban2, darkena(col, 0, 0xFF));
|
|
}
|
|
|
|
drawStunStars(V, where ? where->stuntime : 0);
|
|
}
|
|
else if(m == moCrystalSage) {
|
|
otherbodyparts(V, 0xFFFFFFFF, m, footphase);
|
|
ShadowV(V, shPBody);
|
|
queuepoly(VBODY, shPBody, 0xFFFFFFFF);
|
|
queuepoly(VHEAD, shPHead, 0xFFFFFFFF);
|
|
queuepoly(VHEAD, shPFace, 0xFFFFFFFF);
|
|
}
|
|
else if(m == moHedge) {
|
|
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);
|
|
}
|
|
else if(m == moYeti || m == moMonkey) {
|
|
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));
|
|
}
|
|
}
|
|
else if(m == moRanger) {
|
|
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));
|
|
}
|
|
else if(m == moGhost || m == moSeep || m == moFriendlyGhost) {
|
|
if(m == moFriendlyGhost) col = fghostcolor(ticks, where);
|
|
queuepoly(VGHOST, shGhost, darkena(col, 0, m == moFriendlyGhost ? 0xC0 : 0x80));
|
|
queuepoly(VGHOST, shEyes, 0xFF);
|
|
}
|
|
else if(m == moVineSpirit) {
|
|
queuepoly(VGHOST, shGhost, 0xD0D0D0C0);
|
|
queuepoly(VGHOST, shEyes, 0xFF0000FF);
|
|
}
|
|
else if(m == moFireFairy) {
|
|
col = firecolor(0);
|
|
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));
|
|
}
|
|
else if(m == moSlime) {
|
|
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));
|
|
}
|
|
else if(m == moCultist || m == moPyroCultist || m == moCultistLeader) {
|
|
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));
|
|
}
|
|
else if(m == moPirate) {
|
|
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));
|
|
}
|
|
else if(m == moRatling || m == moRatlingAvenger) {
|
|
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));
|
|
|
|
float t = sin(ticks / 1000.0 + (where ? where->cpdist : 0));
|
|
int eyecol = t > 0.92 ? 0xFF0000 : 0;
|
|
|
|
queuepoly(VHEAD, shWolf1, darkena(eyecol, 0, 0xFF));
|
|
queuepoly(VHEAD, shWolf2, darkena(eyecol, 0, 0xFF));
|
|
queuepoly(VHEAD, shWolf3, darkena(0x202020, 0, 0xFF));
|
|
|
|
if(m == moRatlingAvenger) {
|
|
queuepoly(VBODY, shRatCape1, 0x303030FF);
|
|
queuepoly(VHEAD, shRatCape2, 0x484848FF);
|
|
}
|
|
}
|
|
else if(m == moViking) {
|
|
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));
|
|
}
|
|
else if(m == moOutlaw) {
|
|
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));
|
|
}
|
|
else if(m == moNecromancer) {
|
|
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);
|
|
}
|
|
else if(m == moGoblin) {
|
|
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));
|
|
}
|
|
else if(m == moLancer || m == moFlailer || m == moMiner) {
|
|
transmatrix V2 = V;
|
|
if(m == moLancer)
|
|
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));
|
|
if(m == moMiner)
|
|
queuepoly(Vb, shPickAxe, darkena(0xC0C0C0, 0, 0XFF));
|
|
if(m == moLancer)
|
|
queuepoly(Vb, shPike, darkena(col, 0, 0XFF));
|
|
if(m == moFlailer) {
|
|
queuepoly(Vb, shFlailBall, darkena(col, 0, 0XFF));
|
|
queuepoly(Vb, shFlailChain, darkena(col, 1, 0XFF));
|
|
queuepoly(Vb, shFlailTrunk, darkena(col, 0, 0XFF));
|
|
}
|
|
}
|
|
else if(m == moTroll) {
|
|
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));
|
|
}
|
|
else if(m == moFjordTroll || m == moForestTroll || m == moStormTroll) {
|
|
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));
|
|
}
|
|
else if(m == moDarkTroll) {
|
|
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);
|
|
}
|
|
else if(m == moRedTroll) {
|
|
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);
|
|
}
|
|
else if(m == moEarthElemental) {
|
|
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);
|
|
}
|
|
else if(m == moWaterElemental) {
|
|
otherbodyparts(V, watercolor(50), m, footphase);
|
|
ShadowV(V, shWaterElemental);
|
|
queuepoly(VBODY, shWaterElemental, watercolor(0));
|
|
queuepoly(VHEAD, shFemaleHair, watercolor(100));
|
|
queuepoly(VHEAD, shPFace, watercolor(200));
|
|
}
|
|
else if(m == moFireElemental) {
|
|
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));
|
|
}
|
|
else if(m == moAirElemental) {
|
|
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));
|
|
}
|
|
else if(xch == 'd' || xch == 'D') {
|
|
otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
|
|
queuepoly(VBODY, shPBody, darkena(col, 1, 0xC0));
|
|
ShadowV(V, shPBody);
|
|
int acol = col;
|
|
if(xch == 'D') acol = 0xD0D0D0;
|
|
queuepoly(VHEAD, shDemon, darkena(acol, 0, 0xFF));
|
|
}
|
|
else if(isMetalBeast(m)) {
|
|
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);
|
|
}
|
|
int acol = col;
|
|
queuepoly(VAHEAD, shTrylobiteHead, darkena(acol, 0, 0xFF));
|
|
}
|
|
else if(m == moEvilGolem) {
|
|
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));
|
|
}
|
|
else if(isWitch(m)) {
|
|
otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
|
|
int c = 0xFF;
|
|
if(m == moWitchGhost) c = 0x85 + 120 * sin(ticks / 160.0);
|
|
if(m == moWitchWinter) drawWinter(V, 0);
|
|
if(m == moWitchFlash) drawFlash(V);
|
|
if(m == moWitchSpeed) drawSpeed(V);
|
|
if(m == moWitchFire) col = firecolor(0);
|
|
ShadowV(V, shFemaleBody);
|
|
queuepoly(VBODY, shFemaleBody, darkena(col, 0, c));
|
|
// queuepoly(cV2, ct, shPSword, darkena(col, 0, 0XFF));
|
|
// queuepoly(V, shHood, darkena(col, 0, 0XC0));
|
|
if(m == moWitchFire) col = firecolor(100);
|
|
queuepoly(VHEAD, shWitchHair, darkena(col, 1, c));
|
|
if(m == moWitchFire) col = firecolor(200);
|
|
queuepoly(VHEAD, shPFace, darkena(col, 0, c));
|
|
if(m == moWitchFire) col = firecolor(300);
|
|
queuepoly(VBODY, shWitchDress, darkena(col, 1, 0XC0));
|
|
}
|
|
|
|
else return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool drawMonsterTypeDH(eMonster m, cell *where, const transmatrix& V, int col, bool dh, ld footphase) {
|
|
if(dh) {
|
|
poly_outline = OUTLINE_DEAD;
|
|
darken++;
|
|
}
|
|
bool b = drawMonsterType(m,where,V,col, footphase);
|
|
if(dh) {
|
|
poly_outline = OUTLINE_NONE;
|
|
darken--;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
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 || isnan(R) || isnan(aspd) || R > 10) {
|
|
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?
|
|
|
|
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;
|
|
|
|
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) {
|
|
|
|
bool darkhistory = conformal::includeHistory && eq(c->aitmp, sval);
|
|
|
|
if(doHighlight())
|
|
poly_outline =
|
|
(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;
|
|
|
|
eMonster m = c->monst;
|
|
|
|
if(isIvy(c) || isWorm(c) || isMutantIvy(c) || c->monst == moFriendlyIvy) {
|
|
|
|
if(isDragon(c->monst) && c->stuntime == 0) col = 0xFF6000;
|
|
|
|
transmatrix Vb0 = Vb;
|
|
if(c->mondir != NODIR) {
|
|
|
|
if(mmmon) {
|
|
if(nospinb)
|
|
chainAnimation(c, Vb, c->mov[c->mondir], c->mondir, 0);
|
|
else
|
|
Vb = Vb * ddspin(c, c->mondir);
|
|
|
|
#ifndef NOEDIT
|
|
if(c == mapeditor::drawcell) mapeditor::drawtrans = Vb;
|
|
#endif
|
|
|
|
if(drawUserShape(Vb, 1, c->monst, (col << 8) + 0xFF)) return false;
|
|
|
|
if(isIvy(c) || isMutantIvy(c) || c->monst == moFriendlyIvy)
|
|
queuepoly(Vb, shIBranch, (col << 8) + 0xFF);
|
|
else if(c->monst < moTentacle) {
|
|
ShadowV(Vb, shTentacleX, PPR_GIANTSHADOW);
|
|
queuepoly(mmscale(Vb, geom3::ABODY), shTentacleX, 0xFF);
|
|
queuepoly(mmscale(Vb, geom3::ABODY), shTentacle, (col << 8) + 0xFF);
|
|
}
|
|
else if(c->monst == moDragonHead || c->monst == moDragonTail) {
|
|
char part = dragon::bodypart(c, dragon::findhead(c));
|
|
if(part != '2') {
|
|
queuepoly(mmscale(Vb, geom3::ABODY), shDragonSegment, darkena(col, 0, 0xFF));
|
|
ShadowV(Vb, shDragonSegment, PPR_GIANTSHADOW);
|
|
}
|
|
}
|
|
else {
|
|
if(c->monst == moTentacleGhost) {
|
|
hyperpoint V0 = conformal::on ? tC0(Vs) : inverse(cwtV) * tC0(Vs);
|
|
hyperpoint V1 = spintox(V0) * V0;
|
|
Vs = cwtV * rspintox(V0) * rpushxto0(V1) * pispin;
|
|
drawMonsterType(moGhost, c, Vs, darkena(col, 0, 0xFF), footphase);
|
|
col = minf[moTentacletail].color;
|
|
}
|
|
queuepoly(mmscale(Vb, geom3::ABODY), shTentacleX, 0xFFFFFFFF);
|
|
queuepoly(mmscale(Vb, geom3::ABODY), shTentacle, (col << 8) + 0xFF);
|
|
ShadowV(Vb, shTentacleX, PPR_GIANTSHADOW);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
else if(m == moWorm || m == moWormwait || m == moHexSnake) {
|
|
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);
|
|
}
|
|
else if(m == moDragonHead) {
|
|
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);
|
|
|
|
int noscolor = (c->hitpoints == 1 && c->stuntime ==1) ? 0xFF0000FF : 0xFF;
|
|
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);
|
|
}
|
|
else if(m == moDragonTail) {
|
|
cell *c2 = NULL;
|
|
for(int i=0; i<c->type; i++)
|
|
if(c->mov[i] && isDragon(c->mov[i]->monst) && c->mov[i]->mondir == c->spn(i))
|
|
c2 = c->mov[i];
|
|
int nd = neighborId(c, c2);
|
|
char part = dragon::bodypart(c, dragon::findhead(c));
|
|
if(part == 't') {
|
|
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);
|
|
}
|
|
else if(true) {
|
|
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));
|
|
}
|
|
}
|
|
else if(!(c->mondir == NODIR && (c->monst == moTentacletail || (c->monst == moWormtail && wormpos(c) < WORMLENGTH))))
|
|
queuepoly(Vb, shJoint, darkena(col, 0, 0xFF));
|
|
}
|
|
|
|
if(!mmmon) return true;
|
|
}
|
|
|
|
else if(isMimic(c)) {
|
|
|
|
if(!nospins)
|
|
Vs = Vs * ddspin(c, c->mondir, flipplayer ? S42 : 0);
|
|
|
|
if(c->monst == moMirror) Vs = Vs * Mirror;
|
|
|
|
multi::cpid = c->hitpoints;
|
|
|
|
if(mmmon) {
|
|
drawMonsterType(c->monst, c, Vs, col, footphase);
|
|
drawPlayerEffects(Vs, c, false);
|
|
}
|
|
|
|
if(flipplayer) Vs = Vs * pispin;
|
|
|
|
if(!outofmap(mouseh) && !nospins) {
|
|
// transmatrix invxy = Id; invxy[0][0] = invxy[1][1] = -1;
|
|
|
|
hyperpoint P2 = Vs * inverse(cwtV) * mouseh;
|
|
queuechr(P2, 10, 'x', 0xFF00);
|
|
}
|
|
|
|
return !mmmon;
|
|
}
|
|
|
|
else if(c->monst && !mmmon) return true;
|
|
|
|
// illusions face randomly
|
|
|
|
else if(c->monst == moIllusion) {
|
|
multi::cpid = 0;
|
|
drawMonsterType(c->monst, c, Vs, col, footphase);
|
|
drawPlayerEffects(Vs, c, false);
|
|
}
|
|
|
|
// wolves face the heat
|
|
|
|
else if(c->monst == moWolf && c->cpdist > 1) {
|
|
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);
|
|
}
|
|
return drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase);
|
|
}
|
|
|
|
// golems, knights, and hyperbugs don't face the player (mondir-controlled)
|
|
// also whatever in the lineview mode
|
|
|
|
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);
|
|
}
|
|
|
|
else if(c->monst) {
|
|
// other monsters face the player
|
|
|
|
if(!nospins) {
|
|
hyperpoint V0 = inverse(cwtV) * tC0(Vs);
|
|
hyperpoint V1 = spintox(V0) * V0;
|
|
|
|
Vs = cwtV * rspintox(V0) * rpushxto0(V1) * pispin;
|
|
}
|
|
|
|
if(c->monst == moShadow)
|
|
multi::cpid = c->hitpoints;
|
|
|
|
return drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool showPirateX;
|
|
cell *keycell, *pirateTreasureSeek, *pirateTreasureFound;
|
|
hyperpoint pirateCoords;
|
|
|
|
double downspin;
|
|
cell *straightDownSeek;
|
|
|
|
int keycelldist;
|
|
|
|
void drawCircle(int x, int y, int size, int color) {
|
|
if(size < 0) size = -size;
|
|
#ifdef GL
|
|
if(vid.usingGL) {
|
|
qglcoords = 0;
|
|
glcolor2(color);
|
|
x -= vid.xcenter; y -= vid.ycenter;
|
|
int pts = size * 4;
|
|
if(pts > 1500) pts = 1500;
|
|
if(ISMOBILE && pts > 72) pts = 72;
|
|
for(int r=0; r<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;
|
|
}
|
|
|
|
qglcoords = pts;
|
|
activateGlcoords();
|
|
glDrawArrays(GL_LINE_LOOP, 0, pts);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MOBILE
|
|
gdpush(4); gdpush(color); gdpush(x); gdpush(y); gdpush(size);
|
|
#else
|
|
#ifdef GFX
|
|
(vid.usingAA?aacircleColor:circleColor) (s, x, y, size, color);
|
|
#else
|
|
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
|
|
}
|
|
|
|
int fnt[100][7];
|
|
|
|
bool bugsNearby(cell *c, int dist = 2) {
|
|
if(!(havewhat&HF_BUG)) return false;
|
|
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;
|
|
}
|
|
|
|
int minecolors[8] = {
|
|
0xFFFFFF, 0xF0, 0xF060, 0xF00000,
|
|
0x60, 0x600000, 0x00C0C0, 0x000000
|
|
};
|
|
|
|
int distcolors[8] = {
|
|
0xFFFFFF, 0xF0, 0xF060, 0xF00000,
|
|
0xA0A000, 0xA000A0, 0x00A0A0, 0xFFD500
|
|
};
|
|
|
|
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) {
|
|
transmatrix V = ddspin(c, mapeditor::patterndir(c, patt), S42);
|
|
|
|
if(mapeditor::reflectPatternAt(c, patt))
|
|
return V * Mirror;
|
|
|
|
return V;
|
|
}
|
|
|
|
transmatrix applyDowndir(cell *c, cellfunction *cf) {
|
|
return ddspin(c, mapeditor::downdir(c, cf), S42);
|
|
}
|
|
|
|
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)
|
|
qfloor(c, V, applyDowndir(c, cf), shTower[j], col);
|
|
else if(c->wall != waLadder)
|
|
qfloor(c, V, shMFloor[K(c)], col);
|
|
}
|
|
|
|
void drawZebraFloor(const transmatrix& V, cell *c, int col) {
|
|
|
|
if(euclid) { qfloor(c, V, shTower[10], col); return; }
|
|
|
|
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;
|
|
|
|
qfloor(c, V, applyPatterndir(c, 'z'), shZebra[j], col);
|
|
}
|
|
|
|
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)
|
|
|
|
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)
|
|
qfloor(c, V, applyPatterndir(c, 'f'), shEmeraldFloor[j], col);
|
|
else
|
|
qfloor(c, V, shCaveFloor[euclid?2:K(c)], col);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
transmatrix pushone() { return euclid ? eupush(1, 0) : xpush(sphere?.5 : 1); }
|
|
|
|
void drawMovementArrows(cell *c, transmatrix V) {
|
|
|
|
if(viewdists) return;
|
|
|
|
for(int d=0; d<8; d++) {
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#define SKIPFAC .4
|
|
|
|
void drawMobileArrow(cell *c, transmatrix V) {
|
|
|
|
// int col = getcs().uicolor;
|
|
// col -= (col & 0xFF) >> 1;
|
|
|
|
int dir = neighborId(cwt.c, c);
|
|
bool invalid = !legalmoves[dir];
|
|
|
|
int col = cellcolor(c);
|
|
if(col == OUTLINE_NONE) col = 0xC0C0C0FF;
|
|
col -= (col & 0xFF) >> 1;
|
|
if(invalid) col -= (col & 0xFF) >> 1;
|
|
if(invalid) col -= (col & 0xFF) >> 1;
|
|
|
|
poly_outline = OUTLINE_NONE;
|
|
transmatrix m2 = Id;
|
|
ld scale = vid.mobilecompasssize / 15.;
|
|
m2[0][0] = scale; m2[1][1] = scale; m2[2][2] = 1;
|
|
|
|
transmatrix Centered = rgpushxto0(tC0(cwtV));
|
|
transmatrix t = inverse(Centered) * V;
|
|
double alpha = atan2(tC0(t)[1], tC0(t)[0]);
|
|
|
|
using namespace shmupballs;
|
|
|
|
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;
|
|
} */
|
|
}
|
|
|
|
int celldistAltPlus(cell *c) { return 1000000 + celldistAlt(c); }
|
|
|
|
bool drawstaratvec(double dx, double dy) {
|
|
return dx*dx+dy*dy > .05;
|
|
}
|
|
|
|
int reptilecolor(cell *c) {
|
|
int i = zebra40(c);
|
|
|
|
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) {
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
forCellEx(c2, c) if(c2->monst == moFriendlyGhost)
|
|
fcol = gradient(fcol, fghostcolor(ticks, c2), 0, .25, 1);
|
|
|
|
if(c->monst == moFriendlyGhost)
|
|
fcol = gradient(fcol, fghostcolor(ticks, c), 0, .5, 1);
|
|
|
|
if(c->wall == waSmallTree) wcol = 0x004000;
|
|
else if(c->wall == waBigTree) wcol = 0x008000;
|
|
}
|
|
|
|
if(c->land == laCamelot) {
|
|
int d = showoff ? 0 : ((euclid||c->master->alt) ? celldistAltRelative(c) : 0);
|
|
if(d < 0)
|
|
fcol = 0xA0A0A0;
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
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);
|
|
else
|
|
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);
|
|
else
|
|
qfloor(c, V, applyPatterndir(c), shTriheptaEuc[ishept(c)?1:0], col);
|
|
}
|
|
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) {
|
|
|
|
double z2;
|
|
|
|
double z = zlevel(tC0(ptd.u.poly.V));
|
|
double lev = geom3::factor_to_lev(z);
|
|
double nlev = lev - down;
|
|
|
|
double xyscale = rezoom ? geom3::scale_at_lev(lev) / geom3::scale_at_lev(nlev) : 1;
|
|
z2 = geom3::lev_to_factor(nlev);
|
|
double zscale = z2 / z;
|
|
|
|
// 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!
|
|
}
|
|
else if(nlev < -geom3::lake_top-1e-3)
|
|
ptd.prio = PPR_INLAKEWALL;
|
|
else if(nlev < 0)
|
|
ptd.prio = PPR_LAKEWALL;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// 1 : (floor, water); 2 : (water, bottom); 4 : (bottom, inf)
|
|
|
|
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;
|
|
}
|
|
|
|
bool viewdists = false;
|
|
|
|
bool allemptynear(cell *c) {
|
|
if(c->wall) return false;
|
|
forCellEx(c2, c) if(c2->wall) return false;
|
|
return true;
|
|
}
|
|
|
|
void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
|
|
|
|
qfi.shape = NULL; qfi.special = false;
|
|
ivoryz = isGravityLand(c->land);
|
|
|
|
transmatrix& gm = gmatrix[c];
|
|
bool orig = (gm[2][2] == 0 || fabs(gm[2][2]-1) >= fabs(V[2][2]-1) - 1e-8);
|
|
|
|
if(sphere && vid.alpha > 1) {
|
|
long double d = V[2][2];
|
|
if(d > -1/vid.alpha) return;
|
|
}
|
|
|
|
if(sphere && vid.alpha <= 1) {
|
|
if(V[2][2] < -.8) return;
|
|
}
|
|
|
|
if(orig) gm = V;
|
|
|
|
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);
|
|
|
|
if(!euclid) {
|
|
// draw a web-like map
|
|
if(webdisplay & 1) {
|
|
if(c->type == 6) {
|
|
for(int a=0; a<3; a++)
|
|
queueline(V*Crad[a*S7], V*Crad[a*S7+S42/2], darkena(0xd0d0, 0, 0xFF), 2);
|
|
}
|
|
else {
|
|
for(int a=0; a<S7; a++)
|
|
queueline(tC0(V), V*Crad[(3*S7+a*6)%S42], darkena(0xd0d0, 0, 0xFF), 2);
|
|
}
|
|
}
|
|
|
|
if(webdisplay & 2) if(c->type != 6) {
|
|
queueline(tC0(V), V*ddi0(purehepta?S42:0, tessf), darkena(0xd0d0, 0, 0xFF), 2);
|
|
}
|
|
|
|
if(webdisplay & 4) if(c->type != 6 && !euclid && c->master->alt) {
|
|
for(int i=0; i<S7; i++)
|
|
if(c->master->move[i] && c->master->move[i]->alt == c->master->alt->move[0])
|
|
queueline(tC0(V), V*xspinpush0((purehepta?M_PI:0) -2*M_PI*i/S7, tessf), darkena(0xd000d0, 0, 0xFF), 2);
|
|
}
|
|
}
|
|
|
|
// save the player's view center
|
|
if(isPlayerOn(c) && !shmup::on) {
|
|
playerfound = true;
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
} */
|
|
|
|
if(1) {
|
|
|
|
hyperpoint VC0 = tC0(V);
|
|
|
|
if(intval(mouseh, VC0) < modist) {
|
|
modist2 = modist; mouseover2 = mouseover;
|
|
modist = intval(mouseh, VC0);
|
|
mouseover = c;
|
|
}
|
|
else if(intval(mouseh, VC0) < modist2) {
|
|
modist2 = intval(mouseh, VC0);
|
|
mouseover2 = c;
|
|
}
|
|
|
|
double dfc = euclid ? intval(VC0, C0) : VC0[2];
|
|
|
|
if(dfc < centdist) {
|
|
centdist = dfc;
|
|
centerover = c;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
char ch = winf[c->wall].glyph;
|
|
int wcol, fcol, asciicol;
|
|
|
|
setcolors(c, wcol, 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);
|
|
}
|
|
|
|
asciicol = wcol;
|
|
|
|
if(c->land == laNone && c->wall == waNone)
|
|
queuepoly(V, shTriangle, 0xFFFF0000);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// bool dothept = false;
|
|
|
|
/* if(pseudohept(c) && vid.darkhepta) {
|
|
col = gradient(0, col, 0, 0.75, 1);
|
|
} */
|
|
|
|
eItem it = c->item;
|
|
|
|
bool hidden = itemHidden(c);
|
|
bool hiddens = itemHiddenFromSight(c);
|
|
|
|
if(conformal::includeHistory && eq(c->aitmp, sval)) {
|
|
hidden = true;
|
|
hiddens = false;
|
|
}
|
|
|
|
if(hiddens && cmode != emMapEditor)
|
|
it = itNone;
|
|
|
|
int icol = 0, moncol = 0xFF00FF;
|
|
|
|
if(it)
|
|
ch = iinf[it].glyph, asciicol = icol = iinf[it].color;
|
|
|
|
if(c->monst) {
|
|
ch = minf[c->monst].glyph, moncol = minf[c->monst].color;
|
|
if(c->monst == moMutant) {
|
|
// root coloring
|
|
if(c->stuntime != mutantphase)
|
|
moncol =
|
|
gradient(0xC00030, 0x008000, 0, (c->stuntime-mutantphase) & 15, 15);
|
|
}
|
|
if(isMetalBeast(c->monst) && c->stuntime)
|
|
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;
|
|
}
|
|
|
|
if(c->cpdist == 0 && mapeditor::drawplayer) {
|
|
ch = '@';
|
|
if(!mmitem) asciicol = moncol = cheater ? 0xFF3030 : 0xD0D0D0;
|
|
}
|
|
|
|
if(c->ligon) {
|
|
int tim = ticks - lightat;
|
|
if(tim > 1000) tim = 800;
|
|
if(elec::havecharge && tim > 400) tim = 400;
|
|
for(int t=0; t<c->type; t++) if(c->mov[t] && c->mov[t]->ligon) {
|
|
int hdir = displaydir(c, t);
|
|
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);
|
|
}
|
|
}
|
|
|
|
int ct = c->type;
|
|
int ct6 = K(c);
|
|
|
|
bool error = false;
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
poly_outline = (backcolor << 8) + 0xFF;
|
|
|
|
if(!wmascii) {
|
|
|
|
// floor
|
|
|
|
#ifndef NOEDIT
|
|
transmatrix Vpdir = V * applyPatterndir(c);
|
|
#endif
|
|
|
|
bool eoh = euclid || purehepta;
|
|
|
|
#ifndef NOEDIT
|
|
if(c == mapeditor::drawcell && c != cwt.c && !c->monst && !c->item) {
|
|
mapeditor::drawtrans = Vpdir;
|
|
}
|
|
#endif
|
|
|
|
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)));
|
|
|
|
else if(mapeditor::whichShape == '7') {
|
|
if(ishept(c))
|
|
qfloor(c, Vf, wmblack ? shBFloor[ct6] :
|
|
euclid ? shBigHex :
|
|
shBigHepta, darkena(fcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(mapeditor::whichShape == '8') {
|
|
if(euclid)
|
|
qfloor(c, Vf, shTriheptaEuc[ishept(c) ? 1 : ishex1(c) ? 0 : 2], darkena(fcol, fd, 0xFF));
|
|
else
|
|
qfloor(c, Vf, shTriheptaFloor[ishept(c) ? 1 : 0], darkena(fcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(mapeditor::whichShape == '6') {
|
|
if(!ishept(c))
|
|
qfloor(c, Vf,
|
|
wmblack ? shBFloor[ct6] :
|
|
euclid ? (ishex1(c) ? shBigHexTriangle : shBigHexTriangleRev) :
|
|
shBigTriangle, darkena(fcol, fd, 0xFF));
|
|
}
|
|
#endif
|
|
|
|
else if(c->land == laWineyard && cellHalfvine(c)) {
|
|
|
|
int i =-1;
|
|
for(int t=0;t<6; t++) if(c->mov[t] && c->mov[t]->wall == c->wall)
|
|
i = t;
|
|
|
|
qfi.spin = ddspin(c, i, S14);
|
|
transmatrix V2 = V * qfi.spin;
|
|
|
|
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;
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
else if(c->land == laReptile || c->wall == waReptile)
|
|
drawReptileFloor(Vf, c, fcol, true);
|
|
|
|
else if(wmblack == 1 && c->wall == waMineOpen && vid.grid)
|
|
;
|
|
|
|
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));
|
|
}
|
|
|
|
else if(isWarped(c) && euclid)
|
|
qfloor(c, Vf, shTriheptaEuc[ishept(c)?1:ishex1(c)?0:2], darkena(fcol, fd, 0xFF));
|
|
|
|
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)
|
|
qfloor(c, Vf, applyPatterndir(c), shTriheptaFloor[np], darkena(fcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(wmplain) {
|
|
if(wmspatial && highwall(c)) ;
|
|
else qfloor(c, Vf, shFloor[ct6], darkena(fcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(randomPatternsMode && c->land != laBarrier && !isWarped(c->land)) {
|
|
int j = (randompattern[c->land]/5) % 15;
|
|
int dfcol = darkena(fcol, fd, 0xFF);
|
|
int k = randompattern[c->land] % RPV_MODULO;
|
|
int k7 = randompattern[c->land] % 7;
|
|
|
|
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);
|
|
|
|
else switch(j) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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));
|
|
|
|
else if(c->land == laWineyard) {
|
|
qfloor(c, Vf, shFeatherFloor[euclid?2:ct6], darkena(fcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(c->land == laZebra)
|
|
drawZebraFloor(Vf, c, darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->wall == waTrunk)
|
|
qfloor(c, Vf, shFloor[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->wall == waCanopy || c->wall == waSolidBranch || c->wall == waWeakBranch)
|
|
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);
|
|
|
|
else if(isGravityLand(c->land))
|
|
drawTowerFloor(Vf, c, darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laEmerald)
|
|
drawEmeraldFloor(Vf, c, darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laRlyeh)
|
|
qfloor(c, Vf, (eoh ? shFloor: shTriFloor)[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laTemple)
|
|
qfloor(c, Vf, (eoh ? shFloor: shTriFloor)[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laAlchemist)
|
|
qfloor(c, Vf, shCloudFloor[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laRose)
|
|
qfloor(c, Vf, shRoseFloor[purehepta ? 2 : ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laTortoise)
|
|
qfloor(c, Vf, shTurtleFloor[purehepta ? 2 : ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
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)); */
|
|
}
|
|
|
|
else if((isElemental(c->land) || c->land == laElementalWall) && !eoh)
|
|
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));
|
|
|
|
else if(c->land == laJungle)
|
|
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));
|
|
|
|
else if(c->land == laGraveyard)
|
|
qfloor(c, Vf, (eoh ? shFloor : shCrossFloor)[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laDeadCaves) {
|
|
qfloor(c, Vf, shCaveFloor[euclid?2:ct6], darkena(fcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(c->land == laMotion)
|
|
qfloor(c, Vf, shMFloor[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laWhirlwind)
|
|
// drawZebraFloor(V, c, darkena(fcol, fd, 0xFF));
|
|
qfloor(c, Vf, (eoh ? shCloudFloor : shNewFloor)[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laHell)
|
|
qfloor(c, Vf, (euclid ? shStarFloor : shDemonFloor)[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laIce)
|
|
// qfloor(c, V, shFloor[ct6], darkena(fcol, 2, 0xFF));
|
|
qfloor(c, Vf, shStarFloor[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laCocytus)
|
|
qfloor(c, Vf, (eoh ? shCloudFloor : shDesertFloor)[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laStorms) {
|
|
if(euclid)
|
|
qfloor(c, ishex1(c) ? V*pispin : Vf,
|
|
ishept(c) ? shFloor[0] : shChargedFloor[2], darkena(fcol, fd, 0xFF));
|
|
else
|
|
qfloor(c, Vf, (purehepta ? shChargedFloor[3] : ct==6 ? shChargedFloor[2] : shFloor[1]), darkena(fcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(c->land == laWildWest)
|
|
qfloor(c, Vf, (eoh ? shCloudFloor : shSStarFloor)[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laPower)
|
|
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));
|
|
}
|
|
|
|
else if(c->land == laCaves)
|
|
qfloor(c, Vf, shCaveFloor[ECT], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laDesert)
|
|
qfloor(c, Vf, (eoh ? shCloudFloor : shDesertFloor)[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laOvergrown || c->land == laClearing || isHaunted(c->land))
|
|
qfloor(c, Vf, shOverFloor[ECT], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laRose)
|
|
qfloor(c, Vf, shOverFloor[ECT], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laDryForest)
|
|
qfloor(c, Vf, (eoh ? shStarFloor : shDesertFloor)[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laCaribbean || c->land == laOcean || c->land == laOceanWall || c->land == laWhirlpool)
|
|
qfloor(c, Vf, shCloudFloor[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laLivefjord)
|
|
qfloor(c, Vf, shCaveFloor[ECT], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laRedRock)
|
|
qfloor(c, Vf, eoh ? shFloor[ct6] : shDesertFloor[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else if(c->land == laPalace)
|
|
qfloor(c, Vf, (eoh?shFloor:shPalaceFloor)[ct6], darkena(fcol, fd, 0xFF));
|
|
|
|
else {
|
|
qfloor(c, Vf, shFloor[ct6], darkena(fcol, fd, 0xFF));
|
|
}
|
|
// walls
|
|
|
|
#ifndef NOEDIT
|
|
|
|
if(cmode == emMapEditor && mapeditor::displaycodes) {
|
|
|
|
int labeli = mapeditor::displaycodes == 1 ? mapeditor::realpattern(c) : mapeditor::subpattern(c);
|
|
|
|
string label = its(labeli);
|
|
if(svg::in)
|
|
queuestr(V, .5, label, 0xFF000000);
|
|
else
|
|
queuestr(V, .2, label, 0xFFFFFFFF);
|
|
|
|
/* transmatrix V2 = V * applyPatterndir(c);
|
|
qfloor(c, V2, shNecro, 0x80808080);
|
|
qfloor(c, V2, shStatue, 0x80808080); */
|
|
}
|
|
#endif
|
|
|
|
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
|
|
}
|
|
|
|
if(realred(c->wall) && !wmspatial) {
|
|
int s = snakelevel(c);
|
|
if(s >= 1)
|
|
qfloor(c, V, shRedRockFloor[0][ct6], darkena(winf[waRed1].color, 0, 0xFF));
|
|
if(s >= 2)
|
|
queuepoly(V, shRedRockFloor[1][ct6], darkena(winf[waRed2].color, 0, 0xFF));
|
|
if(s >= 3)
|
|
queuepoly(V, shRedRockFloor[2][ct6], darkena(winf[waRed3].color, 0, 0xFF));
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if(conformal::includeHistory && eq(c->aitmp, sval-1))
|
|
queuepoly((*Vdp), shHeptaMarker, 0x000000C0);
|
|
|
|
char xch = winf[c->wall].glyph;
|
|
|
|
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);
|
|
}
|
|
|
|
else if(c->wall == waSolidBranch) {
|
|
queuepoly(V, shSolidBranch, darkena(wcol, 0, 0xFF));
|
|
}
|
|
|
|
else if(c->wall == waWeakBranch) {
|
|
queuepoly(V, shWeakBranch, darkena(wcol, 0, 0xFF));
|
|
}
|
|
|
|
else if(c->wall == waLadder) {
|
|
if(euclid) {
|
|
queuepoly(V, shMFloor[ct6], 0x804000FF);
|
|
queuepoly(V, shMFloor2[ct6], 0x000000FF);
|
|
}
|
|
else {
|
|
queuepolyat(V, shFloor[ct6], 0x804000FF, PPR_FLOOR+1);
|
|
queuepolyat(V, shMFloor[ct6], 0x000000FF, PPR_FLOOR+2);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
if(!nospin)
|
|
Vboat0 = Vboat0 * ddspin(c, c->mondir, S42);
|
|
else {
|
|
transmatrix Vx;
|
|
if(applyAnimation(c, Vx, footphase, LAYER_SMALL))
|
|
animations[LAYER_SMALL][c].footphase = 0;
|
|
}
|
|
if(wmspatial)
|
|
queuepolyat(mscale(Vboat0, (geom3::LAKE+1)/2), shBoatOuter, outcol, PPR_BOATLEV2);
|
|
queuepoly(Vboat0, shBoatOuter, outcol);
|
|
queuepoly(Vboat0, shBoatInner, incol);
|
|
}
|
|
|
|
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
|
|
);
|
|
}
|
|
|
|
else if(c->wall == waSulphurC) {
|
|
if(drawstar(c)) {
|
|
if(wmspatial)
|
|
queuepolyat(mscale(V, geom3::HELLSPIKE), shGiantStar[ct6], darkena(wcol, 0, 0x40), PPR_HELLSPIKE);
|
|
else
|
|
queuepoly(V, shGiantStar[ct6], darkena(wcol, 0, 0xFF));
|
|
}
|
|
}
|
|
|
|
else if(c->wall == waClosePlate || c->wall == waOpenPlate || (c->wall == waTrapdoor && c->land != laZebra)) {
|
|
transmatrix V2 = V;
|
|
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));
|
|
}
|
|
|
|
else if(c->wall == waFrozenLake || c->wall == waLake || c->wall == waCamelotMoat ||
|
|
c->wall == waSea || c->wall == waClosePlate || c->wall == waOpenPlate ||
|
|
c->wall == waOpenGate || c->wall == waTrapdoor)
|
|
;
|
|
|
|
else if(c->wall == waRose) {
|
|
wcol <<= 1;
|
|
if(c->cpdist > 5)
|
|
wcol = 0xC0C0C0;
|
|
else if(rosephase == 7)
|
|
wcol = 0xFF0000;
|
|
else
|
|
wcol = gradient(wcol, 0xC00000, 0, rosephase, 6);
|
|
queuepoly(V, shThorns, 0xC080C0FF);
|
|
|
|
for(int u=0; u<4; u+=2)
|
|
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);
|
|
}
|
|
|
|
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)) {
|
|
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)); }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if(c->wall == waFan) {
|
|
queuepoly(V * spin(M_PI/6 - fanframe * M_PI / 3), shFan, darkena(wcol, 0, 0xFF));
|
|
}
|
|
|
|
else if(xch == '%') {
|
|
if(doHighlight())
|
|
poly_outline = (c->land == laMirror) ? OUTLINE_TREASURE : OUTLINE_ORB;
|
|
|
|
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));
|
|
}
|
|
poly_outline = OUTLINE_NONE;
|
|
}
|
|
|
|
else if(isFire(c) || isThumper(c) || c->wall == waBonfireOff) {
|
|
ld sp = 0;
|
|
if(hasTimeout(c)) sp = ticks / (c->land == laPower ? 5000. : 500.);
|
|
queuepoly(V * spin(sp), shStar, darkena(wcol, 0, 0xF0));
|
|
if(isFire(c) && rand() % 300 < ticks - lastt)
|
|
drawParticle(c, wcol, 75);
|
|
}
|
|
|
|
else if(c->wall == waFreshGrave || c->wall == waAncientGrave)
|
|
queuepoly(V, shCross, darkena(wcol, 0, 0xFF));
|
|
|
|
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));
|
|
}
|
|
|
|
else if(xch != '.' && xch != '+' && xch != '>' && xch != ':'&& xch != '-' && xch != ';' && c->wall != waSulphur && xch != ',')
|
|
error = true;
|
|
}
|
|
else if(!(it || c->monst || c->cpdist == 0)) error = true;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if(c->wall == waMineOpen) {
|
|
int mines = countMinesAround(c);
|
|
|
|
if(wmascii) {
|
|
if(ch == '.') {
|
|
if(mines == 0) ch = ' ';
|
|
else ch = '0' + mines, asciicol = minecolors[mines];
|
|
}
|
|
else if(ch == '@') asciicol = minecolors[mines];
|
|
}
|
|
else if(mines > 0)
|
|
queuepoly(V, shMineMark[ct6], (minecolors[mines] << 8) | 0xFF);
|
|
}
|
|
|
|
// treasure
|
|
|
|
char xch = iinf[it].glyph;
|
|
hpcshape *xsh =
|
|
(it == itPirate || it == itKraken) ? &shPirateX :
|
|
(it == itBuggy || it == itBuggy2) ? &shPirateX :
|
|
it == itHolyGrail ? &shGrail :
|
|
isElementalShard(it) ? &shElementalShard :
|
|
(it == itBombEgg || it == itTrollEgg) ? &shEgg :
|
|
it == itDodeca ? &shDodeca :
|
|
xch == '*' ? &shGem[ct6] :
|
|
it == itTreat ? &shTreat :
|
|
it == itSlime ? &shEgg :
|
|
xch == '%' ? &shDaisy : xch == '$' ? &shStar : xch == ';' ? &shTriangle :
|
|
xch == '!' ? &shTriangle : it == itBone ? &shNecro : it == itStatue ? &shStatue :
|
|
it == itIvory ? &shFigurine :
|
|
xch == '?' ? &shBookCover :
|
|
it == itKey ? &shKey :
|
|
it == itRevolver ? &shGun :
|
|
NULL;
|
|
|
|
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));
|
|
}
|
|
|
|
if(doHighlight()) {
|
|
int k = itemclass(it);
|
|
if(k == IC_TREASURE)
|
|
poly_outline = OUTLINE_TREASURE;
|
|
else if(k == IC_ORB)
|
|
poly_outline = OUTLINE_ORB;
|
|
else
|
|
poly_outline = OUTLINE_OTHER;
|
|
}
|
|
|
|
if(conformal::includeHistory && eq(c->aitmp, sval)) poly_outline = OUTLINE_DEAD;
|
|
|
|
#ifndef NOEDIT
|
|
if(c == mapeditor::drawcell && mapeditor::drawcellShapeGroup() == 2)
|
|
mapeditor::drawtrans = V;
|
|
#endif
|
|
|
|
if(!mmitem && it)
|
|
error = true;
|
|
|
|
else if(it == itBabyTortoise) {
|
|
int bits = tortoise::babymap[c];
|
|
int over = c->monst == moTortoise;
|
|
tortoise::draw(*Vboat * spin(ticks / 5000.) * ypush(crossf*.15), bits, over ? 4 : 2, 0);
|
|
// queuepoly(V, shHeptaMarker, darkena(tortoise::getMatchColor(bits), 0, 0xC0));
|
|
}
|
|
|
|
else if(it == itCompass) {
|
|
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);
|
|
xsh = NULL;
|
|
}
|
|
|
|
else if(it == itPalace) {
|
|
Vboat0 = *Vboat * spin(ticks / 1500.);
|
|
queuepoly(Vboat0, shMFloor3[ct6], 0xFFD500FF);
|
|
queuepoly(Vboat0, shMFloor4[ct6], darkena(icol, 0, 0xFF));
|
|
queuepoly(Vboat0, shGem[ct6], 0xFFD500FF);
|
|
xsh = NULL;
|
|
}
|
|
|
|
else if(drawUserShape(*Vboat, 2, it, darkena(icol, 0, 0xFF))) ;
|
|
|
|
else if(it == itRose) {
|
|
for(int u=0; u<4; u++)
|
|
queuepoly(*Vboat * spin(ticks / 1500.) * spin(2*M_PI / 3 / 4 * u), shRose, darkena(icol, 0, hidden ? 0x30 : 0xA0));
|
|
}
|
|
|
|
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)));
|
|
}
|
|
|
|
else if(xsh) {
|
|
if(it == itFireShard) icol = firecolor(100);
|
|
if(it == itWaterShard) icol = watercolor(100) >> 8;
|
|
|
|
if(it == itZebra) icol = 0xFFFFFF;
|
|
if(it == itLotus) icol = 0x101010;
|
|
|
|
queuepoly(*Vboat * spin(ticks / 1500.), *xsh, darkena(icol, 0, hidden ? (it == itKraken ? 0xC0 : 0x40) : 0xF0));
|
|
|
|
if(xsh == &shBookCover && mmitem)
|
|
queuepoly(*Vboat * spin(ticks / 1500.), shBook, 0x805020FF);
|
|
if(it == itZebra)
|
|
queuepolyat(*Vboat * spin(ticks / 1500. + M_PI/c->type), *xsh, darkena(0x202020, 0, hidden ? 0x40 : 0xF0), PPR_ITEMb);
|
|
}
|
|
|
|
else if(xch == 'o') {
|
|
if(it == itOrbFire) icol = firecolor(100);
|
|
queuepoly(*Vboat, shDisk, darkena(icol, 0, hidden ? 0x20 : 0xC0));
|
|
if(it == itOrbFire) icol = firecolor(200);
|
|
if(it == itOrbFriend || it == itOrbDiscord) icol = 0xC0C0C0;
|
|
if(it == itOrbFrog) icol = 0xFF0000;
|
|
if(it == itOrbDash) icol = 0xFF0000;
|
|
if(it == itOrbFreedom) icol = 0xC0FF00;
|
|
if(it == itOrbAir) icol = 0xFFFFFF;
|
|
if(it == itOrbUndeath) icol = minf[moFriendlyGhost].color;
|
|
if(it == itOrbRecall) icol = 0x101010;
|
|
hpcshape& sh =
|
|
isRangedOrb(it) ? shTargetRing :
|
|
isOffensiveOrb(it) ? shSawRing :
|
|
isFriendOrb(it) ? shPeaceRing :
|
|
isUtilityOrb(it) ? shGearRing :
|
|
isDirectionalOrb(it) ? shSpearRing :
|
|
it == itOrb37 ? shHeptaRing :
|
|
shRing;
|
|
queuepoly(*Vboat * spin(ticks / 1500.), sh, darkena(icol, 0, int(0x80 + 0x70 * sin(ticks / 300.))));
|
|
}
|
|
|
|
else if(it) error = true;
|
|
|
|
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);
|
|
}
|
|
|
|
int ad = airdist(c);
|
|
if(ad == 1 || ad == 2) {
|
|
|
|
for(int i=0; i<c->type; i++) {
|
|
cell *c2 = c->mov[i];
|
|
if(airdist(c2) < airdist(c)) {
|
|
calcAirdir(c2); // printf("airdir = %d\n", airdir);
|
|
transmatrix V0 = ddspin(c, i, S42);
|
|
|
|
double ph = ticks / (purehepta?150:75.0) + airdir * M_PI / (S21+.0);
|
|
|
|
int aircol = 0x8080FF00 | int(32 + 32 * -cos(ph));
|
|
|
|
double ph0 = ph/2;
|
|
ph0 -= floor(ph0/M_PI)*M_PI;
|
|
|
|
poly_outline = OUTLINE_TRANS;
|
|
queuepoly((*Vdp)*V0*xpush(hexf*-cos(ph0)), shDisk, aircol);
|
|
poly_outline = OUTLINE_NONE;
|
|
}
|
|
}
|
|
|
|
// queuepoly(V*ddi(rand() % S84, hexf*(rand()%100)/100), shDisk, aircolor(airdir));
|
|
}
|
|
|
|
/* 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);
|
|
transmatrix V0 = spin((S42+hdir) * M_PI / S42);
|
|
|
|
double ph = ticks / 75.0; // + airdir * M_PI / (S21+.0);
|
|
|
|
int rosecol = 0x764e7c00 | int(32 + 32 * -cos(ph));
|
|
|
|
double ph0 = ph/2;
|
|
ph0 -= floor(ph0/M_PI)*M_PI;
|
|
|
|
poly_outline = OUTLINE_TRANS;
|
|
queuepoly(V*V0*ddi(0, hexf*-cos(ph0)), shDisk, rosecol);
|
|
poly_outline = OUTLINE_NONE;
|
|
}
|
|
}
|
|
} */
|
|
|
|
if(c->land == laWhirlwind) {
|
|
whirlwind::calcdirs(c);
|
|
|
|
for(int i=0; i<whirlwind::qdirs; i++) {
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
if(error) {
|
|
queuechr(V, 1, ch, asciicol, 2);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if(!euclid && (!pirateTreasureSeek || compassDist(c) < compassDist(pirateTreasureSeek)))
|
|
pirateTreasureSeek = c;
|
|
|
|
if(!euclid) {
|
|
bool usethis = false;
|
|
double spd = 1;
|
|
bool rev = false;
|
|
|
|
if(isGravityLand(cwt.c->land)) {
|
|
if(cwt.c->land == laDungeon) rev = true;
|
|
if(!straightDownSeek || edgeDepth(c) < edgeDepth(straightDownSeek)) {
|
|
usethis = true;
|
|
spd = cwt.c->landparam / 10.;
|
|
}
|
|
}
|
|
|
|
if(c->master->alt && cwt.c->master->alt &&
|
|
(cwt.c->land == laMountain ||
|
|
(pmodel &&
|
|
(cwt.c->land == laTemple || cwt.c->land == laWhirlpool ||
|
|
(cheater && (cwt.c->land == laClearing || cwt.c->land == laCaribbean ||
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
if(usethis) {
|
|
straightDownSeek = c;
|
|
downspin = atan2(VC0[1], VC0[0]);
|
|
downspin -= M_PI/2;
|
|
if(rev) downspin += M_PI;
|
|
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);
|
|
}
|
|
}
|
|
|
|
if(!inHighQual) {
|
|
|
|
#ifndef NOEDIT
|
|
if(cmode == emMapEditor && !mapeditor::subscreen && lmouseover && darken == 0 &&
|
|
(mapeditor::whichPattern ? mapeditor::subpattern(c) == mapeditor::subpattern(lmouseover) : c == lmouseover)) {
|
|
queuecircle(V, .78, 0x00FFFFFF);
|
|
}
|
|
#endif
|
|
|
|
#ifndef NOEDIT
|
|
mapeditor::drawGhosts(c, V, ct);
|
|
#endif
|
|
}
|
|
|
|
if(c->bardir != NODIR && c->bardir != NOBARRIERS && c->land != laHauntedWall &&
|
|
c->barleft != NOWALLSEP_USED) {
|
|
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);
|
|
}
|
|
|
|
#ifndef NOMODEL
|
|
netgen::buildVertexInfo(c, V);
|
|
#endif
|
|
|
|
#ifdef LOCAL
|
|
extern void localdraw (const transmatrix& V, cell *c);
|
|
localdraw(V, c);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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);
|
|
queuecircle(xmove, yb, rad*SKIPFAC,
|
|
legalmoves[7] ? 0xFF0000FF : 0xFF000080
|
|
);
|
|
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--;
|
|
}
|
|
}
|
|
}
|
|
|
|
string buildCredits();
|
|
|
|
string buildHelpText() {
|
|
DEBB(DF_GRAPH, (debugfile,"buildHelpText\n"));
|
|
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(
|
|
"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!"
|
|
);
|
|
h += XLAT(" (press ESC for some hints about it).");
|
|
h += "\n\n";
|
|
|
|
h += XLAT(
|
|
"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"
|
|
);
|
|
|
|
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";
|
|
|
|
#ifdef MOBILE
|
|
h += XLAT(
|
|
"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"
|
|
);
|
|
#else
|
|
h += XLAT(
|
|
"Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Home/Space. "
|
|
"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"
|
|
);
|
|
h += XLAT(
|
|
"You can right click any element to get more information about it.\n\n"
|
|
);
|
|
h += XLAT("(You can also use right Shift)\n\n");
|
|
#endif
|
|
h += XLAT("See more on the website: ")
|
|
+ "http//roguetemple.com/z/hyper/\n\n";
|
|
|
|
|
|
h += XLAT("Still confused? Read the FAQ on the HyperRogue website!\n\n");
|
|
|
|
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(
|
|
"released under GNU General Public License version 2 and thus "
|
|
"comes with absolutely no warranty; see COPYING for details\n\n"
|
|
);
|
|
#endif
|
|
h += XLAT(
|
|
"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, "
|
|
"wonderfullizardofoz, Piotr Migdał, tehora, Michael Heerdegen, Sprite Guard, zelda0x181e, Vipul, snowyowl0, Patashu, phenomist, Alan Malloy, Tom Fryers, Sinquetica, _monad, CtrlAltDestroy, jruderman"
|
|
);
|
|
#ifdef EXTRALICENSE
|
|
h += EXTRALICENSE;
|
|
#endif
|
|
#ifndef MOBILE
|
|
h += XLAT(
|
|
"\n\nSee sounds/credits.txt for credits for sound effects"
|
|
);
|
|
#endif
|
|
if(musiclicense != "") h += musiclicense;
|
|
return h;
|
|
}
|
|
|
|
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() {
|
|
if(princessgender() == GEN_M)
|
|
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. ");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
string generateHelpForItem(eItem it) {
|
|
|
|
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
|
|
if(it == itOrbSafety)
|
|
help += XLAT("This might be very useful for devices with limited memory.");
|
|
#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");
|
|
#endif
|
|
|
|
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'.");
|
|
#endif
|
|
help += XLAT("\nYou can never target cells which are adjacent to the player character, or ones out of the sight range.");
|
|
}
|
|
|
|
#ifdef MOBILE
|
|
if(it == itGreenStone)
|
|
help += XLAT("You can touch the Dead Orb in your inventory to drop it.");
|
|
#else
|
|
if(it == itGreenStone)
|
|
help += XLAT("You can press 'g' or click them in the list to drop a Dead Orb.");
|
|
#endif
|
|
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.");
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return help;
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
string generateHelpForWall(eWall w) {
|
|
|
|
string s = helptitle(XLATN(winf[w].name), winf[w].color);
|
|
|
|
s += XLAT(winf[w].help);
|
|
if(w == waMineMine || w == waMineUnknown || w == waMineOpen)
|
|
addMinefieldExplanation(s);
|
|
if(isThumper(w)) s += pushtext(w);
|
|
if((w == waClosePlate || w == waOpenPlate) && purehepta)
|
|
s += "\n\n(For the heptagonal mode, the radius has been reduced to 2 for closing plates.)";
|
|
return s;
|
|
}
|
|
|
|
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";
|
|
}
|
|
|
|
string generateHelpForMonster(eMonster m) {
|
|
string s = helptitle(XLATN(minf[m].name), minf[m].color);
|
|
|
|
s += XLAT(minf[m].help);
|
|
if(m == moPalace || m == moSkeleton)
|
|
s += pushtext(m);
|
|
if(m == moTroll) s += XLAT(trollhelp2);
|
|
|
|
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);
|
|
|
|
if(m == moBat || m == moEagle)
|
|
s += XLAT("\n\nFast flying creatures may attack or go against gravity only in their first move.", m);
|
|
|
|
return s;
|
|
}
|
|
|
|
string generateHelpForLand(eLand l) {
|
|
string s = helptitle(XLATN(linf[l].name), linf[l].color);
|
|
|
|
if(l == laPalace) s += princedesc();
|
|
|
|
s += XLAT(linf[l].help);
|
|
|
|
if(l == laMinefield) addMinefieldExplanation(s);
|
|
|
|
s += "\n\n";
|
|
if(l == laIce || l == laCaves || l == laDesert || l == laMotion || l == laJungle ||
|
|
l == laCrossroads || l == laAlchemist)
|
|
s += XLAT("Always available.\n");
|
|
|
|
#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);
|
|
#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); }
|
|
|
|
if(l == laMirror || l == laMinefield || l == laPalace ||
|
|
l == laOcean || l == laLivefjord || l == laZebra || l == laWarpCoast || l == laWarpSea ||
|
|
l == laReptile || l == laIvoryTower)
|
|
TREQ(30)
|
|
|
|
|
|
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)
|
|
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)
|
|
if(l == laMountain) ACCONLY(laJungle)
|
|
if(l == laCamelot) ACCONLY2(laCrossroads, laCrossroads3)
|
|
|
|
if(l == laDryForest || l == laWineyard || l == laDeadCaves || l == laHive || l == laRedRock ||
|
|
l == laOvergrown || l == laStorms || l == laWhirlwind || l == laRose ||
|
|
l == laCrossroads2 || l == laRlyeh)
|
|
TREQ(60)
|
|
|
|
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)
|
|
|
|
if(l == laPrairie) TREQ(90)
|
|
if(l == laBull) TREQ(90)
|
|
if(l == laCrossroads4) TREQ(200)
|
|
if(l == laCrossroads5) TREQ(300)
|
|
|
|
if(l == laGraveyard || l == laHive) {
|
|
s += XLAT("Kills required: %1.\n", "100");
|
|
buteol(s, tkills(), 100);
|
|
}
|
|
|
|
if(l == laDragon) {
|
|
s += XLAT("Different kills required: %1.\n", "20");
|
|
buteol(s, killtypes(), 20);
|
|
}
|
|
|
|
if(l == laTortoise) ACCONLY(laDragon)
|
|
if(l == laTortoise) s += XLAT("Find a %1 in %the2.", itBabyTortoise, laDragon);
|
|
|
|
if(l == laHell || l == laCrossroads3) {
|
|
s += XLAT("Finished lands required: %1 (collect 10 treasure)\n", "9");
|
|
buteol(s, orbsUnlocked(), 9);
|
|
}
|
|
|
|
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)
|
|
if(l == laEmerald) {
|
|
TREQ2(5, itFernFlower) TREQ2(5, itGold)
|
|
s += XLAT("Alternatively: kill a %1 in %the2.\n", moVizier, laPalace);
|
|
buteol(s, kills[moVizier], 1);
|
|
}
|
|
|
|
#define KILLREQ(who, where) { s += XLAT("Kills required: %1 (%2).\n", who, where); buteol(s, kills[who], 1); }
|
|
|
|
if(l == laPrincessQuest)
|
|
KILLREQ(moVizier, laPalace);
|
|
|
|
if(l == laElementalWall) {
|
|
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);
|
|
}
|
|
|
|
if(l == laZebra) TREQ2(10, itFeather)
|
|
|
|
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");
|
|
|
|
s += XLAT(
|
|
"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
|
|
|
|
return s;
|
|
}
|
|
|
|
bool instat;
|
|
|
|
void describeMouseover() {
|
|
DEBB(DF_GRAPH, (debugfile,"describeMouseover\n"));
|
|
|
|
cell *c = mousing ? mouseover : playermoved ? NULL : centerover;
|
|
string out = mouseovers;
|
|
if(!c || instat || getcstat) { }
|
|
else if(cmode == emNormal || cmode == emQuit || cmode == emMapEditor) {
|
|
out = XLAT1(linf[c->land].name);
|
|
help = generateHelpForLand(c->land);
|
|
|
|
// Celsius
|
|
|
|
// if(c->land == laIce) out = "Icy Lands (" + fts(60 * (c->heat - .4)) + " C)";
|
|
|
|
if(c->land == laIce || c->land == laCocytus)
|
|
out += " (" + fts(heat::celsius(c)) + " °C)";
|
|
if(c->land == laDryForest && c->landparam)
|
|
out += " (" + its(c->landparam)+"/10)";
|
|
if(c->land == laOcean && chaosmode)
|
|
out += " (" + its(c->CHAOSPARAM)+"S"+its(c->SEADIST)+"L"+its(c->LANDDIST)+")";
|
|
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 += " (" + its(t) + " turns to surface)";
|
|
else
|
|
out += " (" + its(t) + " turns to submerge)";
|
|
}
|
|
}
|
|
|
|
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)+")"; */
|
|
|
|
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) + ")";
|
|
|
|
// out += "(" + its(c->bardir) + ":" + string(linf[c->barleft].name) + " / " + string(linf[c->barright].name) + ")";
|
|
|
|
// out += " MD"+its(c->mpdist);
|
|
|
|
// 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(webdisplay & 8) {
|
|
|
|
out += " LP:" + itsh(c->landparam)+"/"+its(turncount);
|
|
|
|
out += " CD:" + its(celldist(c));
|
|
|
|
out += " D:" + its(c->mpdist);
|
|
|
|
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);
|
|
}
|
|
|
|
// char zz[64]; sprintf(zz, " P%p", c); out += zz;
|
|
|
|
/* whirlwind::calcdirs(c);
|
|
for(int i=0; i<whirlwind::qdirs; i++)
|
|
out += " " + its(whirlwind::dfrom[i]) + ":" + its(whirlwind::dto[i]); */
|
|
// out += " : " + its(whirlwinddir(c));
|
|
|
|
|
|
|
|
if(randomPatternsMode)
|
|
out += " " + describeRPM(c->land);
|
|
|
|
if(euclid && cheater) {
|
|
eucoord x, y;
|
|
decodeMaster(c->master, x, y);
|
|
out += " ("+its(short(x))+","+its(short(y))+")";
|
|
}
|
|
|
|
// char zz[64]; sprintf(zz, " P%d", princess::dist(c)); out += zz;
|
|
// out += " MD"+its(c->mpdist);
|
|
// out += " H "+its(c->heat);
|
|
// if(c->type != 6) out += " Z"+its(c->master->zebraval);
|
|
// out += " H"+its(c->heat);
|
|
|
|
/* // 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 += "]";
|
|
} */
|
|
|
|
if(c->wall &&
|
|
!((c->wall == waFloorA || c->wall == waFloorB || c->wall == waFloorC || c->wall == waFloorD) && c->item)) {
|
|
out += ", "; out += XLAT1(winf[c->wall].name);
|
|
|
|
if(c->wall == waRose) out += " (" + its(7-rosephase) + ")";
|
|
|
|
if((c->wall == waBigTree || c->wall == waSmallTree) && c->land != laDryForest)
|
|
help =
|
|
"Trees in this forest can be chopped down. Big trees take two turns to chop down.";
|
|
else if(c->wall != waSea && c->wall != waPalace)
|
|
if(!((c->wall == waCavefloor || c->wall == waCavewall) && c->land == laEmerald))
|
|
help = generateHelpForWall(c->wall);
|
|
}
|
|
|
|
if(isActivable(c)) out += XLAT(" (touch to activate)");
|
|
|
|
if(hasTimeout(c)) out += XLAT(" [%1 turns]", its(c->wparam));
|
|
|
|
if(isReptile(c->wall))
|
|
out += XLAT(" [%1 turns]", its((unsigned char) c->wparam));
|
|
|
|
if(c->monst) {
|
|
out += ", "; out += XLAT1(minf[c->monst].name);
|
|
if(hasHitpoints(c->monst))
|
|
out += " (" + its(c->hitpoints)+" HP)";
|
|
if(isMutantIvy(c))
|
|
out += " (" + its((c->stuntime - mutantphase) & 15) + "*)";
|
|
else if(c->stuntime)
|
|
out += " (" + its(c->stuntime) + "*)";
|
|
|
|
if(c->monst == moTortoise && tortoise::seek())
|
|
out += " " + tortoise::measure(tortoise::getb(c));
|
|
|
|
help = generateHelpForMonster(c->monst);
|
|
}
|
|
|
|
if(c->item && !itemHiddenFromSight(c)) {
|
|
out += ", ";
|
|
out += XLAT1(iinf[c->item].name);
|
|
if(c->item == itBarrow) out += " (x" + its(c->landparam) + ")";
|
|
if(c->item == itBabyTortoise && tortoise::seek())
|
|
out += " " + tortoise::measure(tortoise::babymap[c]);
|
|
if(!c->monst) help = generateHelpForItem(c->item);
|
|
}
|
|
|
|
if(isPlayerOn(c) && !shmup::on) out += XLAT(", you");
|
|
|
|
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 = XLAT(minf[shmup::mousetarget->type].help);
|
|
}
|
|
/* char buf[64];
|
|
sprintf(buf, "%Lf", intval(mouseh, shmup::mousetarget->pat*C0));
|
|
mouseovers = mouseovers + " D: " + buf;
|
|
printf("ms = %s\n", mouseovers.c_str());*/
|
|
}
|
|
|
|
if(rosedist(c) == 1)
|
|
out += ", wave of scent (front)";
|
|
|
|
if(rosedist(c) == 2)
|
|
out += ", wave of scent (back)";
|
|
|
|
if(sword::at(c)) out += ", Energy Sword";
|
|
|
|
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;
|
|
|
|
}
|
|
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!");
|
|
if(vid.alpha > 5)
|
|
out = XLAT("(press 'i' to approach infinity (Gans model)");
|
|
}
|
|
else if(getcstat == 'r') {
|
|
out = XLAT("simply resize the window to change resolution");
|
|
}
|
|
/* else if(getcstat == 'f') {
|
|
out = XLAT("[+] keep the window size, [-] use the screen resolution");
|
|
} */
|
|
else if(getcstat == 'a' && vid.sspeed > -4.99)
|
|
out = XLAT("+5 = center instantly, -5 = do not center the map");
|
|
else if(getcstat == 'a')
|
|
out = XLAT("press Space or Home to center on the PC");
|
|
else if(getcstat == 'w')
|
|
out = XLAT("also hold Alt during the game to toggle high contrast");
|
|
else if(getcstat == 'w' || getcstat == 'm')
|
|
out = XLAT("You can choose one of the several modes");
|
|
else if(getcstat == 'c')
|
|
out = XLAT("The axes help with keyboard movement");
|
|
else if(getcstat == 'g')
|
|
out = XLAT("Affects looks and grammar");
|
|
#ifndef MOBILE
|
|
else if(getcstat == 's')
|
|
out = XLAT("Config file: %1", conffile);
|
|
#endif
|
|
else out = "";
|
|
}
|
|
else if(cmode == emVisual2) {
|
|
if(getcstat == 'p') {
|
|
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");
|
|
}
|
|
else if(cmode == emChangeMode) {
|
|
if(getcstat == 'h')
|
|
out = XLAT("One wrong move and it is game over!");
|
|
}
|
|
|
|
mouseovers = out;
|
|
|
|
int col = linf[cwt.c->land].color;
|
|
if(cwt.c->land == laRedRock) col = 0xC00000;
|
|
|
|
#ifndef MOBILE
|
|
displayfr(vid.xres/2, vid.fsize, 2, vid.fsize, out, col, 8);
|
|
#endif
|
|
|
|
if(mousey < vid.fsize * 3/2) getcstat = SDLK_F1;
|
|
|
|
if(false && shmup::mousetarget) {
|
|
char buf[64];
|
|
sprintf(buf, "%Lf", (long double) intval(mouseh, tC0(shmup::mousetarget->pat)));
|
|
mouseovers = mouseovers + " D: " + buf;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void drawrec(const heptspin& hs, int lev, hstate s, const transmatrix& V) {
|
|
|
|
// shmup::calc_relative_matrix(cwt.c, hs.h);
|
|
|
|
cell *c = hs.h->c7;
|
|
|
|
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);
|
|
}
|
|
|
|
if(lev <= 0) return;
|
|
|
|
if(!purehepta) for(int d=0; d<S7; d++) {
|
|
int ds = fixrot(hs.spin + d);
|
|
reclevel = maxreclevel - lev + 1;
|
|
// createMov(c, ds);
|
|
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));
|
|
}
|
|
}
|
|
|
|
if(lev <= 1) return;
|
|
|
|
for(int d=0; d<S7; d++) {
|
|
hstate s2 = transition(s, d);
|
|
if(s2 == hsError) continue;
|
|
heptspin hs2 = hsstep(hsspin(hs, d), 0);
|
|
drawrec(hs2, lev-2, s2, V * heptmove[d]);
|
|
}
|
|
|
|
}
|
|
|
|
int mindx=-7, mindy=-7, maxdx=7, maxdy=7;
|
|
|
|
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;
|
|
}
|
|
|
|
void drawEuclidean() {
|
|
DEBB(DF_GRAPH, (debugfile,"drawEuclidean\n"));
|
|
eucoord px, py;
|
|
if(!centerover) centerover = cwt.c;
|
|
// printf("centerover = %p player = %p [%d,%d]-[%d,%d]\n", lcenterover, cwt.c,
|
|
// mindx, mindy, maxdx, maxdy);
|
|
decodeMaster(centerover->master, px, py);
|
|
|
|
int minsx = mindx-1, maxsx=maxdx+1, minsy=mindy-1, maxsy=maxdy+1;
|
|
mindx=maxdx=mindy=maxdy=0;
|
|
|
|
for(int dx=minsx; dx<=maxsx; dx++)
|
|
for(int dy=minsy; dy<=maxsy; dy++) {
|
|
eucoord x = dx+px;
|
|
eucoord y = dy+py;
|
|
reclevel = eudist(dx, dy);
|
|
cell *c = euclideanAt(x,y);
|
|
if(!c) continue;
|
|
transmatrix Mat = eumove(x, y);
|
|
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;
|
|
getcoord0(tC0(Mat), cx, cy, shift);
|
|
if(cx >= 0 && cy >= 0 && cx < vid.xres && cy < vid.yres) {
|
|
if(dx < mindx) mindx = dx;
|
|
if(dy < mindy) mindy = dy;
|
|
if(dx > maxdx) maxdx = dx;
|
|
if(dy > maxdy) maxdy = dy;
|
|
}
|
|
|
|
if(dodrawcell(c)) {
|
|
drawcell(c, Mat, 0, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void drawthemap() {
|
|
|
|
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;
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"draw the map\n"));
|
|
fanframe = ticks / (purehepta ? 300 : 150.0) / M_PI;
|
|
|
|
for(int m=0; m<motypes; m++) if(isPrincess(eMonster(m)))
|
|
minf[m].name = princessgender() ? "Princess" : "Prince";
|
|
|
|
iinf[itSavedPrincess].name = minf[moPrincess].name;
|
|
|
|
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;
|
|
}
|
|
|
|
keycell = NULL;
|
|
|
|
pirateTreasureFound = pirateTreasureSeek;
|
|
pirateTreasureSeek = NULL;
|
|
straightDownSeek = NULL; downspin = 0;
|
|
shmup::mousetarget = NULL;
|
|
showPirateX = false;
|
|
for(int i=0; i<numplayers(); i++) if(multi::playerActive(i))
|
|
if(playerpos(i)->item == itCompass) showPirateX = true;
|
|
|
|
using namespace yendor;
|
|
|
|
if(yii < size(yi)) {
|
|
if(!yi[yii].found)
|
|
for(int i=0; i<YDIST; i++)
|
|
if(yi[yii].path[i]->cpdist <= sightrange) {
|
|
keycell = yi[yii].path[i];
|
|
keycelldist = YDIST - i;
|
|
}
|
|
}
|
|
|
|
modist = 1e20; mouseover = NULL;
|
|
modist2 = 1e20; mouseover2 = NULL;
|
|
mouseovers = XLAT("Press F1 or right click for help");
|
|
#ifdef ROGUEVIZ
|
|
if(rogueviz::on) mouseovers = " ";
|
|
#endif
|
|
centdist = 1e20; centerover = NULL;
|
|
|
|
for(int i=0; i<multi::players; i++) {
|
|
multi::ccdist[i] = 1e20; multi::ccat[i] = NULL;
|
|
}
|
|
|
|
#ifdef MOBILE
|
|
mouseovers = XLAT("No info about this...");
|
|
#endif
|
|
if(outofmap(mouseh))
|
|
modist = -5;
|
|
playerfound = false;
|
|
// playerfoundL = false;
|
|
// playerfoundR = false;
|
|
|
|
sphereflip = Id;
|
|
profile_start(1);
|
|
if(euclid)
|
|
drawEuclidean();
|
|
else {
|
|
if(sphere && vid.alpha > 1) sphereflip[2][2] = -1;
|
|
maxreclevel =
|
|
conformal::on ? sightrange + 2:
|
|
(!playermoved) ? sightrange+1 : sightrange + 4;
|
|
|
|
drawrec(viewctr,
|
|
maxreclevel,
|
|
hsOrigin, ypush(vid.yshift) * sphereflip * View);
|
|
}
|
|
|
|
#ifdef ROGUEVIZ
|
|
rogueviz::drawExtra();
|
|
#endif
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if(shmup::on) {
|
|
if(shmup::players == 1)
|
|
cwtV = shmup::pc[0]->pat;
|
|
else if(shmup::centerplayer != -1)
|
|
cwtV = shmup::pc[shmup::centerplayer]->pat;
|
|
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 MOBILE
|
|
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;
|
|
}
|
|
#endif
|
|
profile_stop(0);
|
|
}
|
|
|
|
void spinEdge(ld aspd) {
|
|
if(downspin > aspd) downspin = aspd;
|
|
if(downspin < -aspd) downspin = -aspd;
|
|
View = spin(downspin) * View;
|
|
}
|
|
|
|
void centerpc(ld aspd) {
|
|
if(vid.sspeed >= 4.99) aspd = 1000;
|
|
DEBB(DF_GRAPH, (debugfile,"center pc\n"));
|
|
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]);
|
|
if(R < 1e-9) {
|
|
/* if(playerfoundL && playerfoundR) {
|
|
|
|
} */
|
|
spinEdge(aspd);
|
|
fixmatrix(View);
|
|
return;
|
|
}
|
|
|
|
if(euclid) {
|
|
// Euclidean
|
|
aspd *= (2+3*R*R);
|
|
if(aspd > R) aspd = R;
|
|
|
|
View[0][2] -= cwtV[0][2] * aspd / R;
|
|
View[1][2] -= cwtV[1][2] * aspd / R;
|
|
}
|
|
|
|
else {
|
|
aspd *= (1+R+(shmup::on?1:0));
|
|
|
|
if(R < aspd) {
|
|
View = gpushxto0(H) * View;
|
|
}
|
|
else
|
|
View = rspintox(H) * xpush(-aspd) * spintox(H) * View;
|
|
|
|
fixmatrix(View);
|
|
spinEdge(aspd);
|
|
}
|
|
}
|
|
|
|
void drawmovestar(double dx, double dy) {
|
|
|
|
if(viewdists) return;
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"draw movestar\n"));
|
|
if(!playerfound) return;
|
|
|
|
if(shmup::on) return;
|
|
#ifndef NORUG
|
|
if(rug::rugged && multi::players == 1 && !multi::alwaysuse) return;
|
|
#endif
|
|
|
|
hyperpoint H = tC0(cwtV);
|
|
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);
|
|
|
|
Centered = Centered * rgpushxto0(hpxy(dx*5, dy*5));
|
|
if(multi::cpid >= 0) multi::crosscenter[multi::cpid] = Centered;
|
|
|
|
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);
|
|
|
|
else for(int d=0; d<8; d++) {
|
|
int col = starcol;
|
|
#ifdef PANDORA
|
|
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;
|
|
#endif
|
|
// EUCLIDEAN
|
|
if(euclid)
|
|
queueline(tC0(Centered), Centered * ddi0(d * 10.5, 0.5) , col, 0);
|
|
else
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
void optimizeview() {
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"optimize view\n"));
|
|
int turn = 0;
|
|
ld best = INF;
|
|
|
|
transmatrix TB = Id;
|
|
|
|
for(int i=-1; i<S7; i++) {
|
|
|
|
ld trot = -i * M_PI * 2 / (S7+.0);
|
|
transmatrix T = i < 0 ? Id : spin(trot) * xpush(tessf) * pispin;
|
|
hyperpoint H = View * tC0(T);
|
|
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);
|
|
}
|
|
}
|
|
|
|
movedir vectodir(const hyperpoint& P) {
|
|
|
|
hyperpoint H = sphereflip * tC0(cwtV);
|
|
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
|
|
transmatrix Centered = sphereflip * cwtV;
|
|
if(!euclid)
|
|
Centered = gpushxto0(H) * Centered;
|
|
else if(R > 1e-9)
|
|
Centered = eupush(-H[0], -H[1]) * Centered;
|
|
ld binv = 99;
|
|
|
|
ld dirdist[7];
|
|
for(int i=0; i<cwt.c->type; i++) {
|
|
dirdist[i] = intval(Centered * xspinpush0(-i * 2 * M_PI /cwt.c->type, .5), P);
|
|
}
|
|
|
|
movedir res;
|
|
res.d = -1;
|
|
|
|
for(int i=0; i<cwt.c->type; i++) {
|
|
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;
|
|
if(sphere) res.subdir = -res.subdir;
|
|
}
|
|
}
|
|
|
|
// if(euclid) bdir = (bdir + 3) % 6;
|
|
return res;
|
|
}
|
|
|
|
void movepckeydir(int d) {
|
|
DEBB(DF_GRAPH, (debugfile,"movepckeydir\n"));
|
|
// EUCLIDEAN
|
|
|
|
movedir md =
|
|
vectodir(spin(-d * M_PI/4) * tC0(pushone()));
|
|
|
|
movepcto(md);
|
|
}
|
|
|
|
void calcMousedest() {
|
|
if(outofmap(mouseh)) return;
|
|
if(revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
|
|
ld mousedist = intval(mouseh, tC0(shmup::ggmatrix(cwt.c)));
|
|
mousedest.d = -1;
|
|
|
|
cellwalker bcwt = cwt;
|
|
|
|
ld dists[7];
|
|
|
|
for(int i=0; i<cwt.c->type; i++)
|
|
dists[i] = intval(mouseh, tC0(shmup::ggmatrix(cwt.c->mov[i])));
|
|
|
|
/* 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;
|
|
|
|
if(cwt.mirrored)
|
|
mousedest.d = fixdir(-mousedest.d, cwt.c),
|
|
mousedest.subdir = -mousedest.subdir;
|
|
|
|
if(sphere) mousedest.subdir = -mousedest.subdir;
|
|
}
|
|
|
|
if(revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
|
|
cwt = bcwt;
|
|
}
|
|
|
|
void mousemovement() {
|
|
calcMousedest();
|
|
movepcto(mousedest);
|
|
lmouseover = NULL;
|
|
}
|
|
|
|
long double sqr(long double x) { return x*x; }
|
|
|
|
// old style joystick control
|
|
|
|
void checkjoy() {
|
|
DEBB(DF_GRAPH, (debugfile,"check joy\n"));
|
|
if(!DEFAULTCONTROL) return;
|
|
ld joyvalue1 = sqr(vid.joyvalue);
|
|
ld joyvalue2 = sqr(vid.joyvalue2);
|
|
|
|
ld jx = joyx;
|
|
ld jy = joyy;
|
|
ld sq = jx*jx+jy*jy;
|
|
|
|
static int laststate = 0;
|
|
int curstate = sq < joyvalue1 ? 0 : sq < joyvalue2 ? 1 : 2;
|
|
if(curstate != laststate) flashMessages(), laststate = curstate;
|
|
|
|
if(autojoy) {
|
|
if(sq < joyvalue1) { if(joydir.d >= 0) movepcto(joydir); joydir.d = -1; return; }
|
|
if(sq < joyvalue2 && joydir.d == -1) return;
|
|
}
|
|
else {
|
|
if(sq < joyvalue1) { joydir.d = -1; return; }
|
|
}
|
|
|
|
joydir = vectodir(hpxy(jx, jy));
|
|
}
|
|
|
|
void checkpanjoy(double t) {
|
|
|
|
if(shmup::on) return;
|
|
|
|
if(vid.joypanspeed < 1e-7) return;
|
|
|
|
if(sqr(panjoyx) + sqr(panjoyy) < sqr(vid.joypanthreshold))
|
|
return;
|
|
|
|
ld jx = panjoyx * t * vid.joypanspeed;
|
|
ld jy = panjoyy * t * vid.joypanspeed;
|
|
|
|
playermoved = false;
|
|
View = gpushxto0(hpxy(jx, jy)) * View;
|
|
}
|
|
|
|
int realradius;
|
|
|
|
bool sidescreen;
|
|
|
|
void calcparam() {
|
|
DEBB(DF_GRAPH, (debugfile,"calc param\n"));
|
|
vid.xcenter = vid.xres / 2;
|
|
vid.ycenter = vid.yres / 2;
|
|
|
|
realradius = min(vid.xcenter, vid.ycenter);
|
|
|
|
vid.radius = int(vid.scale * vid.ycenter) - (ISANDROID ? 2 : ISIOS ? 40 : 40);
|
|
|
|
realradius = min(realradius, vid.radius);
|
|
|
|
sidescreen = false;
|
|
|
|
if(vid.xres < vid.yres) {
|
|
vid.radius = int(vid.scale * vid.xcenter) - (ISIOS ? 10 : 2);
|
|
vid.ycenter = vid.yres - realradius - vid.fsize - (ISIOS ? 10 : 0);
|
|
}
|
|
else {
|
|
if(vid.xres >= vid.yres * 4/3 && dialog::sidedialog && cmode == emNumber)
|
|
sidescreen = true;
|
|
if(viewdists && cmode == emNormal && vid.xres >= vid.yres * 4/3) sidescreen = true;
|
|
if(sidescreen) vid.xcenter = vid.yres/2;
|
|
}
|
|
|
|
ld eye = vid.eye; if(pmodel || rug::rugged) eye = 0;
|
|
vid.beta = 1 + vid.alpha + eye;
|
|
vid.alphax = vid.alpha + eye;
|
|
vid.goteyes = vid.eye > 0.001 || vid.eye < -0.001;
|
|
vid.goteyes2 = vid.goteyes;
|
|
}
|
|
|
|
void displayButton(int x, int y, const string& name, int key, int align, int rad) {
|
|
if(displayfr(x, y, rad, vid.fsize, name, 0x808080, align)) {
|
|
displayfr(x, y, rad, vid.fsize, name, 0xFFFF00, align);
|
|
getcstat = key;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
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);
|
|
displayButton(vid.xres/2, y + vid.fsize*10/2, XLAT("or 'v' to see the main menu"), 'v', 8, 2);
|
|
displayButton(vid.xres/2, y + vid.fsize*13/2, XLAT("or 'o' to see the world overview"), 'o', 8, 2);
|
|
}
|
|
#endif
|
|
|
|
int calcfps() {
|
|
#define CFPS 30
|
|
static int last[CFPS], lidx = 0;
|
|
int ct = ticks;
|
|
int ret = ct - last[lidx];
|
|
last[lidx] = ct;
|
|
lidx++; lidx %= CFPS;
|
|
if(ret == 0) return 0;
|
|
return (1000 * CFPS) / ret;
|
|
}
|
|
|
|
int msgscroll = 0;
|
|
|
|
string timeline() {
|
|
int timespent = (int) (savetime + (timerstopped ? 0 : (time(NULL) - timerstart)));
|
|
char buf[20];
|
|
sprintf(buf, "%d:%02d", timespent/60, timespent % 60);
|
|
return
|
|
shmup::on ?
|
|
XLAT("%1 knives (%2)", its(turncount), buf)
|
|
:
|
|
XLAT("%1 turns (%2)", its(turncount), buf);
|
|
}
|
|
|
|
void showGameover() {
|
|
|
|
dialog::init(
|
|
cheater ? XLAT("It is a shame to cheat!") :
|
|
showoff ? XLAT("Showoff mode") :
|
|
canmove && princess::challenge ? XLAT("%1 Challenge", moPrincess) :
|
|
canmove ? XLAT("Quest status") :
|
|
XLAT("GAME OVER"),
|
|
0xC00000, 200, 100
|
|
);
|
|
dialog::addInfo(XLAT("Your score: %1", its(gold())));
|
|
dialog::addInfo(XLAT("Enemies killed: %1", its(tkills())));
|
|
|
|
if(items[itOrbYendor]) {
|
|
dialog::addInfo(XLAT("Orbs of Yendor found: %1", its(items[itOrbYendor])), iinf[itOrbYendor].color);
|
|
dialog::addInfo(XLAT("CONGRATULATIONS!"), iinf[itOrbYendor].color);
|
|
}
|
|
else {
|
|
if(princess::challenge)
|
|
dialog::addInfo(XLAT("Follow the Mouse and escape with %the1!", moPrincess));
|
|
else if(gold() < 30)
|
|
dialog::addInfo(XLAT("Collect 30 $$$ to access more worlds"));
|
|
else if(gold() < 60)
|
|
dialog::addInfo(XLAT("Collect 60 $$$ to access even more lands"));
|
|
else if(!hellUnlocked())
|
|
dialog::addInfo(XLAT("Collect at least 10 treasures in each of 9 types to access Hell"));
|
|
else if(items[itHell] < 10)
|
|
dialog::addInfo(XLAT("Collect at least 10 Demon Daisies to find the Orbs of Yendor"));
|
|
else if(size(yendor::yi) == 0)
|
|
dialog::addInfo(XLAT("Look for the Orbs of Yendor in Hell or in the Crossroads!"));
|
|
else
|
|
dialog::addInfo(XLAT("Unlock the Orb of Yendor!"));
|
|
}
|
|
|
|
if(!timerstopped && !canmove) {
|
|
savetime += time(NULL) - timerstart;
|
|
timerstopped = true;
|
|
}
|
|
if(canmove && !timerstart)
|
|
timerstart = time(NULL);
|
|
|
|
if(princess::challenge) ;
|
|
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"));
|
|
else if(items[itEmerald] < 5)
|
|
dialog::addInfo(XLAT("Collect 5 Emeralds to access Camelot"));
|
|
else if(hellUnlocked() && !chaosmode) {
|
|
bool b = true;
|
|
for(int i=0; i<LAND_HYP; i++)
|
|
if(b && items[treasureType(land_hyp[i])] < 10) {
|
|
dialog::addInfo(
|
|
XLAT(
|
|
land_hyp[i] == laTortoise ? "Hyperstone Quest: collect at least 10 points in %the2" :
|
|
"Hyperstone Quest: collect at least 10 %1 in %the2",
|
|
treasureType(land_hyp[i]), land_hyp[i]));
|
|
b = false;
|
|
}
|
|
if(b)
|
|
dialog::addInfo(XLAT("Hyperstone Quest completed!"), iinf[itHyperstone].color);
|
|
}
|
|
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);
|
|
}
|
|
if(!cheater) {
|
|
dialog::addInfo(timeline(), 0xC0C0C0);
|
|
}
|
|
|
|
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);
|
|
m.msg = gamelog[i].msg;
|
|
m.quantity = gamelog[i].quantity;
|
|
mnum++,
|
|
msgs.push_back(m);
|
|
}
|
|
|
|
dialog::addBreak(100);
|
|
dialog::addItem(canmove ? "continue" : "see how it ended", SDLK_ESCAPE);
|
|
dialog::addItem("main menu", 'v');
|
|
dialog::addItem("restart", SDLK_F5);
|
|
#ifndef MOBILE
|
|
dialog::addItem(quitsaves() ? "save" : "quit", SDLK_F10);
|
|
#endif
|
|
#ifdef ANDROIDSHARE
|
|
dialog::addItem("SHARE", 's'-96);
|
|
#endif
|
|
|
|
dialog::display();
|
|
|
|
if(mnum)
|
|
displayfr(vid.xres/2, vid.yres-vid.fsize*(mnum+1), 2, vid.fsize/2, XLAT("last messages:"), 0xC0C0C0, 8);
|
|
}
|
|
|
|
#ifdef MOBILE
|
|
void displayabutton(int px, int py, string s, int col) {
|
|
// TMP
|
|
int siz = vid.yres > vid.xres ? vid.fsize*2 : vid.fsize * 3/2;
|
|
int 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.));
|
|
if(gtouched && !mouseover
|
|
&& abs(mousex - vid.xcenter) < vrr
|
|
&& abs(mousey - vid.ycenter) < vrr
|
|
&& hypot(mousex-vid.xcenter, mousey-vid.ycenter) > vrr
|
|
&& px == (mousex > vid.xcenter ? 1 : -1)
|
|
&& py == (mousey > vid.ycenter ? 1 : -1)
|
|
) col = 0xFF0000;
|
|
if(displayfr(x, y, 0, siz, s, col, 8+8*px))
|
|
buttonclicked = true;
|
|
}
|
|
#endif
|
|
|
|
#ifndef NOSAVE
|
|
vector<score> scores;
|
|
|
|
int scoresort = 2;
|
|
int scoredisplay = 1;
|
|
int scorefrom = 0;
|
|
int scoremode = 0;
|
|
bool scorerev = false;
|
|
|
|
bool scorecompare(const score& s1, const score &s2) {
|
|
return s1.box[scoresort] > s2.box[scoresort];
|
|
}
|
|
|
|
bool fakescore() {
|
|
return fakebox[scoredisplay];
|
|
}
|
|
|
|
string displayfor(score* S) {
|
|
// printf("S=%p, scoredisplay = %d\n", S, scoredisplay);
|
|
if(S == NULL) {
|
|
return XLATN(boxname[scoredisplay]);
|
|
}
|
|
if(scoredisplay == 0) {
|
|
char buf[10];
|
|
snprintf(buf, 10, "%d:%02d", S->box[0]/60, S->box[0]%60);
|
|
return buf;
|
|
}
|
|
if(scoredisplay == 1) {
|
|
time_t tim = S->box[1];
|
|
char buf[128]; strftime(buf, 128, "%c", localtime(&tim));
|
|
return buf;
|
|
}
|
|
return its(S->box[scoredisplay]);
|
|
}
|
|
|
|
void loadScores() {
|
|
scores.clear();
|
|
FILE *f = fopen(scorefile, "rt");
|
|
if(!f) {
|
|
printf("Could not open the score file '%s'!\n", scorefile);
|
|
addMessage(s0 + "Could not open the score file: " + scorefile);
|
|
return;
|
|
}
|
|
while(!feof(f)) {
|
|
char buf[120];
|
|
if(fgets(buf, 120, f) == NULL) break;
|
|
if(buf[0] == 'H' && buf[1] == 'y') {
|
|
score sc; bool ok = true;
|
|
{if(fscanf(f, "%s", buf) <= 0) break;} sc.ver = buf;
|
|
|
|
for(int i=0; i<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;
|
|
boxid = 0; applyBoxes();
|
|
scoresort = 2; reverse(scores.begin(), scores.end());
|
|
scoremode = 0;
|
|
if(shmup::on) scoremode = 1;
|
|
else if(hardcore) scoremode = 2;
|
|
scorefrom = 0;
|
|
stable_sort(scores.begin(), scores.end(), scorecompare);
|
|
#ifdef MOBILE
|
|
extern int andmode;
|
|
andmode = 2;
|
|
#endif
|
|
}
|
|
|
|
vector<pair<string, int> > pickscore_options;
|
|
|
|
bool notgl = false;
|
|
#endif
|
|
|
|
void setAppropriateOverview() {
|
|
clearMessages();
|
|
if(tactic::on)
|
|
cmode = emTactic;
|
|
else if(yendor::on)
|
|
cmode = emYendor;
|
|
else if(geometry != gNormal)
|
|
cmode = emPickEuclidean;
|
|
else
|
|
cmode = emOverview;
|
|
}
|
|
|
|
ld textscale() {
|
|
return vid.fsize / (vid.radius * crossf) * (1+vid.alphax) * 2;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int monsterclass(eMonster m) {
|
|
if(isFriendly(m) || m == moTortoise) return 1;
|
|
else if(isMonsterPart(m)) return 2;
|
|
else return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
else {
|
|
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;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
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;
|
|
|
|
int glsize = buttonsize;
|
|
if(glyph == '%' || glyph == 'M' || glyph == 'W') glsize = glsize*4/5;
|
|
|
|
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));
|
|
|
|
string fl = "";
|
|
string str = its(qty);
|
|
|
|
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 += "!";
|
|
|
|
if(fl != "")
|
|
displaystr(cx + buttonsize, cy-buttonsize/2 + buttonsize/4, 0, buttonsize/2, fl, darkenedby(color, 0), 16);
|
|
|
|
if(flags & GLYPH_NONUMBER) str = "";
|
|
|
|
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);
|
|
dialog::addInfo("a(d+4) = a(d+3) + a(d+2) + a(d+1) - a(d)", 0xFFFFFF);
|
|
dialog::addInfo("a(d) ~ 1.72208^d", 0xFFFFFF);
|
|
}
|
|
if(geometry == gNormal && purehepta) {
|
|
dialog::addBreak(200);
|
|
dialog::addInfo("a(d+2) = 3a(d+1) - a(d+2)", 0xFFFFFF);
|
|
dialog::addInfo("a(d) ~ 2.61803^d", 0xFFFFFF);
|
|
}
|
|
if(geometry == gEuclid) {
|
|
dialog::addBreak(300);
|
|
dialog::addInfo("a(n) = 6n", 0xFFFFFF);
|
|
}
|
|
dialog::display();
|
|
}
|
|
if(sidescreen) return;
|
|
instat = false;
|
|
bool portrait = vid.xres < vid.yres;
|
|
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;
|
|
}
|
|
|
|
if(buttonsize <= vid.fsize*3/4) {
|
|
imponly = true; buttonsize = minsize;
|
|
rows = rowspace / buttonsize; if(!rows) return;
|
|
colid[0] = 0; colid[2] = portrait ? 1 : 0;
|
|
}
|
|
|
|
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; }
|
|
|
|
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];
|
|
|
|
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);
|
|
if(t == IC_TREASURE)
|
|
mouseovers = XLAT("treasure collected: %1", it);
|
|
if(t == IC_OTHER)
|
|
mouseovers = XLAT("objects found: %1", it);
|
|
if(t == IC_NAI)
|
|
mouseovers = XLAT("%1", it);
|
|
if(t == IC_ORB)
|
|
mouseovers = XLAT("orb power: %1", eItem(i));
|
|
if(it == itGreenStone) {
|
|
mouseovers += XLAT(" (click to drop)");
|
|
getcstat = 'g';
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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"
|
|
);
|
|
}
|
|
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.");
|
|
}
|
|
if(displayButtonS(4, vid.yres - 4 - vid.fsize/2, s0+VER+ " fps: " + its(calcfps()), 0x202020, 0, vid.fsize/2)) {
|
|
mouseovers = XLAT("frames per second"),
|
|
getcstat = SDLK_F1,
|
|
instat = true,
|
|
help =
|
|
helptitle(XLAT("frames per second"), 0xFF4040) +
|
|
XLAT(
|
|
"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 "
|
|
) +
|
|
#ifdef IOS
|
|
XLAT(
|
|
"(in the MENU). You can reduce the sight range, this should make "
|
|
"the animations smoother.");
|
|
#else
|
|
XLAT(
|
|
"(press v) and change the wall/monster mode to ASCII, or change "
|
|
"the resolution.");
|
|
#endif
|
|
}
|
|
|
|
achievement_display();
|
|
#ifdef LOCAL
|
|
process_local_stats();
|
|
#endif
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
|
|
#ifndef NOPNG
|
|
void IMAGESAVE(SDL_Surface *s, const char *fname) {
|
|
SDL_Surface *s2 = SDL_PNGFormatAlpha(s);
|
|
SDL_SavePNG(s2, fname);
|
|
SDL_FreeSurface(s2);
|
|
}
|
|
#endif
|
|
|
|
int pngres = 2000;
|
|
|
|
void saveHighQualityShot(const char *fname) {
|
|
|
|
#ifndef GFX
|
|
addMessage(XLAT("High quality shots not available on this platform"));
|
|
return;
|
|
#endif
|
|
|
|
dynamicval<int> v3(sightrange, (cheater && sightrange < 10) ? 10 : sightrange);
|
|
|
|
if(cheater) doOvergenerate();
|
|
|
|
time_t timer;
|
|
timer = time(NULL);
|
|
|
|
dynamicval<videopar> v(vid, vid);
|
|
dynamicval<bool> v2(inHighQual, true);
|
|
dynamicval<int> v4(cheater, 0);
|
|
vid.xres = vid.yres = pngres;
|
|
vid.usingGL = false;
|
|
// if(vid.pmodel == 0) vid.scale = 0.99;
|
|
calcparam();
|
|
#ifdef ROGUEVIZ
|
|
rogueviz::fixparam();
|
|
#endif
|
|
|
|
dynamicval<SDL_Surface*> v5(s, SDL_CreateRGBSurface(SDL_SWSURFACE,vid.xres,vid.yres,32,0,0,0,0));
|
|
|
|
darken = 0;
|
|
|
|
for(int i=0; i<(fname?1:2); i++) {
|
|
SDL_FillRect(s, NULL, fname ? backcolor : i ? 0xFFFFFF : 0);
|
|
drawfullmap();
|
|
|
|
char buf[128]; strftime(buf, 128, "bigshota-%y%m%d-%H%M%S" IMAGEEXT, localtime(&timer));
|
|
buf[7] += i;
|
|
if(!fname) fname = buf;
|
|
IMAGESAVE(s, fname);
|
|
|
|
if(i == 0) addMessage(XLAT("Saved the high quality shot to %1", fname));
|
|
}
|
|
|
|
SDL_FreeSurface(s);
|
|
}
|
|
#endif
|
|
|
|
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);
|
|
}
|
|
|
|
void drawfullmap() {
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"draw full map\n"));
|
|
|
|
ptds.clear();
|
|
|
|
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);
|
|
}
|
|
queuecircle(vid.xcenter, vid.ycenter, rad,
|
|
svg::in ? 0x808080FF : darkena(0xFF, 0, 0xFF),
|
|
vid.usingGL ? PPR_CIRCLE : PPR_OUTCIRCLE);
|
|
if(pmodel == mdBall) ballgeometry();
|
|
}
|
|
|
|
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) {
|
|
int ls = size(lines);
|
|
if(ISMOBILE) ls /= 10;
|
|
for(int t=0; t<ls; t++) queueline(View * lines[t].P1, View * lines[t].P2, lines[t].col >> (darken+1));
|
|
} */
|
|
|
|
drawthemap();
|
|
#ifndef NORUG
|
|
if(!inHighQual) {
|
|
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);
|
|
mapeditor::drawGrid();
|
|
}
|
|
#endif
|
|
profile_start(2);
|
|
drawqueue();
|
|
profile_stop(2);
|
|
}
|
|
|
|
#include "menus.cpp"
|
|
|
|
void drawscreen() {
|
|
|
|
if(vid.xres == 0 || vid.yres == 0) return;
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"drawscreen\n"));
|
|
|
|
calcparam();
|
|
#ifdef ROGUEVIZ
|
|
rogueviz::fixparam();
|
|
#endif
|
|
#ifdef GL
|
|
if(vid.usingGL) setGLProjection();
|
|
#endif
|
|
if(cmode != emHelp) help = "@";
|
|
|
|
#ifndef MOBILE
|
|
// SDL_LockSurface(s);
|
|
// unsigned char *b = (unsigned char*) s->pixels;
|
|
// int n = vid.xres * vid.yres * 4;
|
|
// while(n) *b >>= 1, b++, n--;
|
|
// memset(s->pixels, 0, vid.xres * vid.yres * 4);
|
|
if(!vid.usingGL) SDL_FillRect(s, NULL, backcolor);
|
|
#endif
|
|
|
|
if(!canmove) darken = 1;
|
|
if(cmode != emNormal && cmode != emDraw && cmode != emCustomizeChar) darken = 2;
|
|
if(cmode == emQuit && !canmove) darken = 0;
|
|
if(cmode == emOverview) darken = 16;
|
|
|
|
if(sidescreen) darken = 0;
|
|
|
|
#ifndef NOEDIT
|
|
if(cmode == emMapEditor && !mapeditor::subscreen && !mapeditor::choosefile) darken = 0;
|
|
if(cmode == emDraw && mapeditor::choosefile) darken = 2;
|
|
#endif
|
|
if(hiliteclick && darken == 0 && mmmon) darken = 1;
|
|
if(cmode == emProgress) darken = 0;
|
|
|
|
if(conformal::includeHistory && cmode != emProgress) conformal::restore();
|
|
|
|
if(darken >= 8) ;
|
|
#ifndef NORUG
|
|
else if(rug::rugged) {
|
|
rug::actDraw();
|
|
}
|
|
#endif
|
|
else drawfullmap();
|
|
|
|
if(conformal::includeHistory && cmode != emProgress) conformal::restoreBack();
|
|
|
|
getcstat = 0; inslider = false;
|
|
|
|
if(cmode == emNormal || cmode == emQuit) drawStats();
|
|
|
|
#ifdef MOBILE
|
|
|
|
buttonclicked = false;
|
|
|
|
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);
|
|
}
|
|
|
|
#endif
|
|
|
|
// displaynum(vx,100, 0, 24, 0xc0c0c0, celldist(cwt.c), ":");
|
|
|
|
darken = 0;
|
|
drawmessages();
|
|
|
|
if(cmode == emNormal) {
|
|
if(!canmove) showGameover();
|
|
}
|
|
|
|
if(cmode == emProgress) mouseovers = "";
|
|
|
|
displayMenus();
|
|
|
|
describeMouseover();
|
|
|
|
if((havewhat&HF_BUG) && darken == 0 && (cmode == emNormal || cmode == emQuit)) for(int k=0; k<3; k++)
|
|
displayfr(vid.xres/2 + vid.fsize * 5 * (k-1), vid.fsize*2, 2, vid.fsize,
|
|
its(hive::bugcount[k]), minf[moBug0+k].color, 8);
|
|
|
|
bool minefieldNearby = false;
|
|
int mines[4], tmines=0;
|
|
for(int p=0; p<numplayers(); p++) {
|
|
mines[p] = 0;
|
|
cell *c = playerpos(p);
|
|
if(!c) continue;
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if((minefieldNearby || tmines) && canmove && !items[itOrbAether] && darken == 0 && cmode == emNormal) {
|
|
string s;
|
|
if(tmines > 7) tmines = 7;
|
|
int col = minecolors[tmines];
|
|
|
|
if(tmines == 7) seenSevenMines = true;
|
|
|
|
for(int p=0; p<numplayers(); p++) if(multi::playerActive(p))
|
|
displayfr(vid.xres * (p+.5) / numplayers(),
|
|
vid.ycenter - vid.radius * 3/4, 2,
|
|
vid.fsize,
|
|
XLAT(minetexts[mines[p]]), minecolors[mines[p]], 8);
|
|
|
|
if(minefieldNearby && !shmup::on && cwt.c->land != laMinefield && cwt.c->mov[cwt.spin]->land != laMinefield) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
if(cmode == emNormal || cmode == emVisual1 || cmode == emVisual2 || cmode == emChangeMode )
|
|
displayButton(vid.xres-8, vid.yres-vid.fsize, XLAT("(v) menu"), 'v', 16);
|
|
#endif
|
|
|
|
if(cmode == emQuit) {
|
|
if(canmove) showGameover();
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
// SDL_UnlockSurface(s);
|
|
|
|
DEBT("swapbuffers");
|
|
#ifdef GL
|
|
if(vid.usingGL) SDL_GL_SwapBuffers(); else
|
|
#endif
|
|
SDL_UpdateRect(s, 0, 0, vid.xres, vid.yres);
|
|
|
|
//printf("\ec");
|
|
|
|
#endif
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
bool setfsize = true;
|
|
|
|
void setvideomode() {
|
|
|
|
DEBB(DF_INIT, (debugfile,"setvideomode\n"));
|
|
|
|
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
|
|
if(vid.usingGL) {
|
|
glViewport(0, 0, vid.xres, vid.yres);
|
|
resetGL();
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
void restartGraph() {
|
|
DEBB(DF_INIT, (debugfile,"restartGraph\n"));
|
|
|
|
View = Id;
|
|
webdisplay = 0;
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
void initcs(charstyle &cs) {
|
|
cs.charid = 0;
|
|
cs.skincolor = 0xD0D0D0FF;
|
|
cs.haircolor = 0x686868FF;
|
|
cs.dresscolor = 0xC00000FF;
|
|
cs.swordcolor = 0xD0D0D0FF;
|
|
cs.dresscolor2= 0x8080FFC0;
|
|
cs.uicolor = 0xFF0000FF;
|
|
}
|
|
|
|
#ifndef NOCONFIG
|
|
void savecs(FILE *f, charstyle& cs, int xvernum) {
|
|
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);
|
|
if(xvernum >= 8990) fprintf(f, " %x", cs.uicolor);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
void loadcs(FILE *f, charstyle& cs, int xvernum) {
|
|
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))
|
|
;
|
|
if(xvernum >= 8990) if(fscanf(f, "%x", &cs.uicolor))
|
|
;
|
|
}
|
|
|
|
void saveConfig() {
|
|
DEBB(DF_INIT, (debugfile,"save config\n"));
|
|
FILE *f = fopen(conffile, "wt");
|
|
if(!f) {
|
|
addMessage(s0 + "Could not open the config file: " + conffile);
|
|
return;
|
|
}
|
|
fprintf(f, "%d %d %d %d\n", vid.xres, vid.yres, vid.full, vid.fsize);
|
|
fprintf(f, "%f %f %f %f\n", float(vid.scale), float(vid.eye), float(vid.alpha), float(vid.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);
|
|
|
|
savecs(f, vid.cs, 0);
|
|
|
|
fprintf(f, "%d %d\n", vid.darkhepta, vid.shifttarget);
|
|
|
|
fprintf(f, "%d %d %d %d\n", euclid, euclidland, shmup::on, hardcore);
|
|
|
|
shmup::saveConfig(f);
|
|
|
|
fprintf(f, "%d %d %d %d %f %d %d\n", rug::renderonce, rug::rendernogl, rug::texturesize, purehepta, rug::scale, vid.steamscore, chaosmode);
|
|
|
|
fprintf(f, "%d %d %f %d %d %f\n",
|
|
pmodel, polygonal::SI, float(polygonal::STAR), polygonal::deg,
|
|
conformal::includeHistory, float(conformal::lvspeed));
|
|
|
|
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]));
|
|
|
|
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\n", vid.mobilecompasssize);
|
|
|
|
}
|
|
|
|
fprintf(f, "\n\nThis is a configuration file for HyperRogue (version " VER ")\n");
|
|
fprintf(f, "\n\nThe numbers are:\n");
|
|
fprintf(f, "screen width & height, fullscreen mode (0=windowed, 1=fullscreen), font size\n");
|
|
fprintf(f, "scale, eye distance, parameter, scrolling speed\n");
|
|
fprintf(f, "wallmode, monster mode, cross mode, music volume, framerate limit, usingGL, usingAA\n");
|
|
fprintf(f, "calibrate first joystick (threshold A, threshold B), calibrate second joystick (pan threshold, pan speed), joy mode\n");
|
|
fprintf(f, "gender (1=female, 16=same gender prince), language, skin color, hair color, sword color, dress color\n");
|
|
fprintf(f, "darken hepta, shift target\n");
|
|
fprintf(f, "euclid, euclid land, shmup, hardcore\n");
|
|
fprintf(f, "version number, shmup players, alwaysuse, shmup keyboard/joystick config\n");
|
|
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");
|
|
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");
|
|
|
|
fclose(f);
|
|
#ifndef MOBILE
|
|
addMessage(s0 + "Configuration saved to: " + conffile);
|
|
#else
|
|
addMessage(s0 + "Configuration saved");
|
|
#endif
|
|
}
|
|
|
|
void readf(FILE *f, ld& x) {
|
|
double fl = x;
|
|
if(fscanf(f, "%lf", &fl))
|
|
;
|
|
x = fl;
|
|
}
|
|
|
|
void loadConfig() {
|
|
|
|
DEBB(DF_INIT, (debugfile,"load config\n"));
|
|
vid.xres = 9999; vid.yres = 9999; vid.framelimit = 300;
|
|
FILE *f = fopen(conffile, "rt");
|
|
if(f) {
|
|
int fs, gl=1, aa=1, bb=1, cc, dd, ee;
|
|
int err;
|
|
err=fscanf(f, "%d%d%d%d", &vid.xres, &vid.yres, &fs, &vid.fsize);
|
|
vid.full = fs;
|
|
float a, b, c, d;
|
|
err=fscanf(f, "%f%f%f%f\n", &a, &b, &c, &d);
|
|
if(err == 4) {
|
|
vid.scale = a; vid.eye = b; vid.alpha = c; vid.sspeed = d;
|
|
}
|
|
err=fscanf(f, "%d%d%d%d%d%d%d", &vid.wallmode, &vid.monmode, &vid.axes, &musicvolume, &vid.framelimit, &gl, &aa);
|
|
vid.usingGL = gl; vid.usingAA = aa;
|
|
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;
|
|
autojoy = aa; aa = 0;
|
|
|
|
loadcs(f, vid.cs, 0);
|
|
|
|
aa=0; bb=0;
|
|
err=fscanf(f, "%d%d", &aa, &bb);
|
|
vid.darkhepta = aa; vid.shifttarget = bb;
|
|
|
|
aa = geometry; bb = euclidland; cc = shmup::on; dd = hardcore;
|
|
err=fscanf(f, "%d%d%d%d", &aa, &bb, &cc, &dd);
|
|
geometry = eGeometry(aa); euclidland = eLand(bb); shmup::on = cc; hardcore = dd;
|
|
|
|
shmup::loadConfig(f);
|
|
|
|
aa = rug::renderonce; bb = rug::rendernogl; cc = purehepta; dd = chaosmode; ee = vid.steamscore;
|
|
double rs = rug::scale;
|
|
err=fscanf(f, "%d%d%d%d%lf%d%d", &aa, &bb, &rug::texturesize, &cc, &rs, &ee, &dd);
|
|
rug::renderonce = aa; rug::rendernogl = bb; purehepta = cc; chaosmode = dd; vid.steamscore = ee;
|
|
rug::scale = rs;
|
|
|
|
aa=conformal::includeHistory;
|
|
double ps = polygonal::STAR, lv = conformal::lvspeed;
|
|
int pmb = pmodel;
|
|
err=fscanf(f, "%d%d%lf%d%d%lf",
|
|
&pmb, &polygonal::SI, &ps, &polygonal::deg,
|
|
&aa, &lv);
|
|
pmodel = eModel(pmb);
|
|
conformal::includeHistory = aa; polygonal::STAR = ps; conformal::lvspeed = lv;
|
|
|
|
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;
|
|
|
|
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);
|
|
}
|
|
|
|
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\n", &vid.mobilecompasssize);
|
|
|
|
fclose(f);
|
|
DEBB(DF_INIT, (debugfile,"Loaded configuration: %s\n", conffile));
|
|
|
|
if(err)
|
|
;
|
|
}
|
|
|
|
precalc();
|
|
}
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
void initJoysticks() {
|
|
DEBB(DF_INIT, (debugfile,"init joysticks\n"));
|
|
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() {
|
|
DEBB(DF_INIT, (debugfile,"close joysticks\n"));
|
|
for(int i=0; i<numsticks; i++) {
|
|
SDL_JoystickClose(sticks[i]), sticks[i] = NULL;
|
|
}
|
|
numsticks = 0;
|
|
}
|
|
#endif
|
|
|
|
void initgraph() {
|
|
|
|
DEBB(DF_INIT, (debugfile,"initgraph\n"));
|
|
|
|
vid.usingGL = true;
|
|
vid.usingAA = true;
|
|
vid.flashtime = 8;
|
|
vid.scale = 1;
|
|
vid.alpha = 1;
|
|
vid.sspeed = 0;
|
|
vid.mspeed = 1;
|
|
vid.eye = 0;
|
|
vid.full = false;
|
|
vid.ballangle = 20;
|
|
vid.yshift = 0;
|
|
vid.camera_angle = 0;
|
|
vid.ballproj = 1;
|
|
|
|
#ifdef ANDROID
|
|
vid.monmode = 2;
|
|
vid.wallmode = 3;
|
|
#else
|
|
#ifdef PANDORA
|
|
vid.monmode = 2;
|
|
vid.wallmode = 3;
|
|
#else
|
|
vid.monmode = 4;
|
|
vid.wallmode = 5;
|
|
#endif
|
|
#endif
|
|
|
|
vid.particles = 1;
|
|
vid.mobilecompasssize = 30;
|
|
|
|
vid.joyvalue = 4800;
|
|
vid.joyvalue2 = 5600;
|
|
vid.joypanthreshold = 2500;
|
|
#ifdef PANDORA
|
|
vid.joypanspeed = 0.0001;
|
|
#else
|
|
vid.joypanspeed = 0;
|
|
#endif
|
|
|
|
vid.framelimit = 75;
|
|
vid.axes = 1;
|
|
vid.shifttarget = 2;
|
|
vid.steamscore = 1;
|
|
|
|
initcs(vid.cs);
|
|
|
|
vid.killreduction = 0;
|
|
|
|
vid.samegender = false;
|
|
vid.language = -1;
|
|
|
|
joyx = joyy = 0; joydir.d = -1;
|
|
|
|
vid.drawmousecircle = false;
|
|
revcontrol = false;
|
|
#ifdef MOBILE
|
|
vid.drawmousecircle = true;
|
|
#endif
|
|
#ifdef PANDORA
|
|
vid.drawmousecircle = true;
|
|
#endif
|
|
|
|
shmup::initConfig();
|
|
|
|
restartGraph();
|
|
|
|
initgeo();
|
|
|
|
buildpolys();
|
|
|
|
#ifndef MOBILE
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1)
|
|
{
|
|
printf("Failed to initialize video.\n");
|
|
exit(2);
|
|
}
|
|
|
|
#ifdef WEB
|
|
vid.xscr = vid.xres = 1200;
|
|
vid.yscr = vid.yres = 900;
|
|
#else
|
|
const SDL_VideoInfo *inf = SDL_GetVideoInfo();
|
|
vid.xscr = vid.xres = inf->current_w;
|
|
vid.yscr = vid.yres = inf->current_h;
|
|
#endif
|
|
|
|
SDL_WM_SetCaption("HyperRogue " VER, "HyperRogue " VER);
|
|
#endif
|
|
|
|
preparesort();
|
|
#ifndef NOCONFIG
|
|
loadConfig();
|
|
#endif
|
|
|
|
#ifdef USE_COMMANDLINE
|
|
arg::read(2);
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
setvideomode();
|
|
if(!s) {
|
|
printf("Failed to initialize graphics.\n");
|
|
exit(2);
|
|
}
|
|
|
|
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
|
|
SDL_EnableUNICODE(1);
|
|
|
|
if(TTF_Init() != 0) {
|
|
printf("Failed to initialize TTF.\n");
|
|
exit(2);
|
|
}
|
|
|
|
initJoysticks();
|
|
|
|
#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;
|
|
|
|
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_F10) quitmainloop = true;
|
|
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((sym != 0 && sym != SDLK_F12) && !didsomething) {
|
|
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) quitmainloop = true;
|
|
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;
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
#ifdef PANDORA
|
|
if(ev.type == SDL_MOUSEBUTTONUP && sym == 0 && !rightclick)
|
|
#else
|
|
if(ev.type == SDL_MOUSEBUTTONDOWN && sym == 0 && !rightclick)
|
|
#endif
|
|
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)) {
|
|
}
|
|
else if(forcetarget)
|
|
;
|
|
else if(!DEFAULTCONTROL) {
|
|
if(!shmup::on)
|
|
multi::mousemovement(mouseover);
|
|
}
|
|
else mousemovement();
|
|
}
|
|
#endif
|
|
|
|
if(sym == SDLK_F1) {
|
|
lastmode = cmode;
|
|
cmode = emHelp;
|
|
}
|
|
|
|
#ifdef ROGUEVIZ
|
|
rogueviz::processKey(sym, uni);
|
|
#endif
|
|
|
|
#ifdef LOCAL
|
|
process_local0(sym);
|
|
#endif
|
|
}
|
|
|
|
void handlekey(int sym, int uni, extra& ev) {
|
|
|
|
if(((cmode == emNormal && canmove) || (cmode == emQuit && !canmove) || cmode == emDraw || cmode == emMapEditor) && DEFAULTCONTROL && !rug::rugged) {
|
|
#ifndef PANDORA
|
|
if(sym == SDLK_RIGHT)
|
|
View = xpush(-0.2*shiftmul) * View, playermoved = false, didsomething = true;
|
|
if(sym == SDLK_LEFT)
|
|
View = xpush(+0.2*shiftmul) * View, playermoved = false, didsomething = true;
|
|
if(sym == SDLK_UP)
|
|
View = ypush(+0.2*shiftmul) * View, playermoved = false, didsomething = true;
|
|
if(sym == SDLK_DOWN)
|
|
View = ypush(-0.2*shiftmul) * View, playermoved = false, didsomething = true;
|
|
#endif
|
|
if(sym == SDLK_PAGEUP) {
|
|
View = spin(M_PI/S21*shiftmul) * View, didsomething = true;
|
|
}
|
|
if(sym == SDLK_PAGEDOWN)
|
|
View = spin(-M_PI/S21*shiftmul) * View, didsomething = true;
|
|
|
|
if(sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN)
|
|
if(isGravityLand(cwt.c->land)) playermoved = false;
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
if(sym == SDLK_F7 && !vid.usingGL) {
|
|
|
|
time_t timer;
|
|
timer = time(NULL);
|
|
char buf[128]; strftime(buf, 128, "shot-%y%m%d-%H%M%S" IMAGEEXT, localtime(&timer));
|
|
|
|
IMAGESAVE(s, buf);
|
|
addMessage(XLAT("Screenshot saved to %1", buf));
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEMO
|
|
if(cmode == emOverview || cmode == emMenu) handleDemoKey(sym, uni); else
|
|
#endif
|
|
|
|
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);
|
|
#endif
|
|
else if(cmode == emPickScores) handlePickScoreKeys(uni);
|
|
#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
|
|
#ifdef HAVE_ACHIEVEMENTS
|
|
else if(cmode == emLeader) leader::handleKey(sym, uni);
|
|
#endif
|
|
#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);
|
|
#endif
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
|
|
// Warning: a very long function! todo: refactor
|
|
|
|
|
|
void mainloopiter() {
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"main loop\n"));
|
|
|
|
#ifndef GFX
|
|
#ifndef GL
|
|
vid.wallmode = 0;
|
|
vid.monmode = 0;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef LOCAL
|
|
process_local_extra();
|
|
#endif
|
|
|
|
optimizeview();
|
|
|
|
if(conformal::on) conformal::apply();
|
|
|
|
ticks = SDL_GetTicks();
|
|
|
|
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);
|
|
|
|
if(!DOSHMUP && (multi::alwaysuse || multi::players > 1) && cmode == emNormal)
|
|
timetowait = 0, multi::handleMulti(ticks - lastt);
|
|
|
|
if(vid.sspeed >= 5 && gmatrix.count(cwt.c) && !elliptic) {
|
|
cwtV = gmatrix[cwt.c] * ddspin(cwt.c, cwt.spin);
|
|
if(cwt.mirrored) playerV = playerV * Mirror;
|
|
}
|
|
|
|
#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));
|
|
if(panjoyx || panjoyy)
|
|
checkpanjoy((ticks - lastt) / 1000.0);
|
|
}
|
|
tortoise::updateVals(ticks - lastt);
|
|
frames++;
|
|
if(!outoffocus) {
|
|
drawscreen();
|
|
}
|
|
lastt = ticks;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
|
|
/* if(ev.type == SDL_JOYDEVICEADDED || ev.type == SDL_JOYDEVICEREMOVED) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ev.type == SDL_VIDEORESIZE) {
|
|
vid.xres = ev.resize.w;
|
|
vid.yres = ev.resize.h;
|
|
vid.killreduction = 0;
|
|
extern bool setfsize;
|
|
setfsize = true;
|
|
setvideomode();
|
|
#ifdef GL
|
|
if(vid.usingGL) glViewport(0, 0, vid.xres, vid.yres);
|
|
#endif
|
|
}
|
|
|
|
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);
|
|
}
|
|
else {
|
|
if(ev.jaxis.axis == 0)
|
|
panjoyx = ev.jaxis.value;
|
|
else
|
|
panjoyy = ev.jaxis.value;
|
|
}
|
|
}
|
|
|
|
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;
|
|
vid.scfg.setwhat = 0;
|
|
}
|
|
}
|
|
|
|
else if(ev.type == SDL_JOYBUTTONDOWN && DEFAULTCONTROL) {
|
|
flashMessages();
|
|
movepcto(joydir);
|
|
checkjoy();
|
|
}
|
|
|
|
if(ev.type == SDL_KEYDOWN) {
|
|
flashMessages();
|
|
mousing = false;
|
|
sym = ev.key.keysym.sym;
|
|
uni = ev.key.keysym.unicode;
|
|
if(ev.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) shiftmul = -1;
|
|
if(ev.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) shiftmul /= 10;
|
|
}
|
|
|
|
dialog::handleZooming(ev);
|
|
|
|
if(sym == SDLK_F1 && cmode == emNormal && playermoved)
|
|
help = "@";
|
|
|
|
bool rollchange =
|
|
cmode == emOverview && getcstat >= 2000 && cheater;
|
|
|
|
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)) {
|
|
sym = 1; msgscroll--; didsomething = true;
|
|
}
|
|
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) {
|
|
ld jx = (mousex - vid.xcenter - .0) / vid.radius / 10;
|
|
ld jy = (mousey - vid.ycenter - .0) / vid.radius / 10;
|
|
playermoved = false;
|
|
View = gpushxto0(hpxy(jx, jy)) * View;
|
|
sym = 1;
|
|
}
|
|
else
|
|
if(cmode == emTactic || cmode == emYendor || cmode == emPickEuclidean ||
|
|
cmode == emLeader || cmode == emScores || cmode == emOverview)
|
|
if(getcstat < 2000 || !cheater)
|
|
sym = uni = PSEUDOKEY_WHEELUP;
|
|
}
|
|
else if(ev.button.button == SDL_BUTTON_RIGHT) {
|
|
sym = 1; didsomething = true;
|
|
}
|
|
else if(ev.button.button == SDL_BUTTON_MIDDLE) {
|
|
sym = 2; didsomething = true;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
if(ev.type == SDL_MOUSEMOTION) {
|
|
hyperpoint mouseoh = mouseh;
|
|
|
|
mousing = true;
|
|
mousemoved = true;
|
|
mousex = ev.motion.x;
|
|
mousey = ev.motion.y;
|
|
|
|
#ifndef NORUG
|
|
if(rug::rugged)
|
|
mouseh = rug::gethyper(mousex, mousey);
|
|
else
|
|
#endif
|
|
mouseh = gethyper(mousex, mousey);
|
|
|
|
if((rightclick || (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) &&
|
|
!outofmap(mouseh) && !outofmap(mouseoh) &&
|
|
mouseh[2] < 50 && mouseoh[2] < 50) {
|
|
panning(mouseoh, mouseh);
|
|
}
|
|
|
|
#ifdef SIMULATE_JOYSTICK
|
|
// pretend that both joysticks are present
|
|
stick = panstick = (SDL_Joystick*) (&vid);
|
|
panjoyx = 20 * (mousex - vid.xcenter);
|
|
panjoyy = 20 * (mousey - vid.ycenter);
|
|
checkjoy();
|
|
#endif
|
|
|
|
if(mousepressed && inslider) {
|
|
sym = getcstat, uni = getcstat, shiftmul = getcshift;
|
|
}
|
|
}
|
|
|
|
handlekey(sym, uni, ev);
|
|
|
|
if(ev.type == SDL_QUIT) {
|
|
if(needConfirmation() && cmode !=emQuit) cmode = emQuit;
|
|
else quitmainloop = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void mainloop() {
|
|
lastt = 0;
|
|
cmode = emNormal;
|
|
#ifdef WEB
|
|
initweb();
|
|
emscripten_set_main_loop(mainloopiter, 0, true);
|
|
#else
|
|
while(!quitmainloop) mainloopiter();
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
void cleargraph() {
|
|
DEBB(DF_INIT, (debugfile,"clear graph\n"));
|
|
for(int i=0; i<256; i++) if(font[i]) TTF_CloseFont(font[i]);
|
|
for(int i=0; i<128; i++) if(glfont[i]) delete glfont[i];
|
|
#ifndef SIMULATE_JOYSTICK
|
|
closeJoysticks();
|
|
#endif
|
|
SDL_Quit();
|
|
}
|
|
#endif
|
|
|
|
void cleargraphmemory() {
|
|
DEBB(DF_INIT, (debugfile,"clear graph memory\n"));
|
|
mouseover = centerover = lmouseover = NULL;
|
|
#ifndef NOEDIT
|
|
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
|
|
for(int i=0; i<ANIMLAYERS; i++) animations[i].clear();
|
|
gmatrix.clear(); gmatrix0.clear();
|
|
}
|
|
|
|
void showMissionScreen() {
|
|
cmode = emQuit;
|
|
msgscroll = 0;
|
|
}
|
|
|
|
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);
|
|
}
|