mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-27 14:37:16 +00:00
6345 lines
191 KiB
C++
6345 lines
191 KiB
C++
// Hyperbolic Rogue -- graphics
|
|
|
|
// Copyright (C) 2011-2012 Zeno Rogue, see 'hyper.cpp' for details
|
|
|
|
// basic graphics:
|
|
|
|
#define WOLNIEJ 1
|
|
#define BTOFF 0x404040
|
|
#define BTON 0xC0C000
|
|
|
|
// #define PANDORA
|
|
|
|
|
|
#ifndef MOBILE
|
|
#include <SDL/SDL.h>
|
|
|
|
#ifdef AUDIO
|
|
#include <SDL/SDL_mixer.h>
|
|
#endif
|
|
bool audio;
|
|
int audiovolume = 60;
|
|
|
|
#ifndef MAC
|
|
#undef main
|
|
#endif
|
|
|
|
#include <SDL/SDL_ttf.h>
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
|
|
// x resolutions
|
|
|
|
#define NUMMODES 7
|
|
|
|
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;
|
|
|
|
emtype cmode = emNormal, lastmode = emNormal; // last mode in Help
|
|
|
|
//
|
|
int axestate;
|
|
|
|
int ticks;
|
|
|
|
videopar vid;
|
|
int default_language;
|
|
|
|
charstyle& getcs() {
|
|
if(shmup::on && shmup::players>1 && shmup::cpid >= 0 && shmup::cpid < shmup::players)
|
|
return shmup::scs[shmup::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;
|
|
}
|
|
|
|
int sightrange = 7;
|
|
|
|
cell *mouseover, *mouseover2, *lmouseover, *centerover, *lcenterover;
|
|
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;
|
|
bool gtouched;
|
|
bool revcontrol;
|
|
|
|
int getcstat; ld getcshift;
|
|
|
|
int ZZ;
|
|
|
|
string help;
|
|
|
|
int andmode = 0;
|
|
|
|
int darken = 0;
|
|
|
|
bool doHighlight() {
|
|
return (hiliteclick && darken < 2) ? vid.monmode == 2 : vid.monmode == 3;
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
int& qpixel(SDL_Surface *surf, int x, int y) {
|
|
if(x<0 || y<0 || x >= surf->w || y >= surf->h) return ZZ;
|
|
char *p = (char*) surf->pixels;
|
|
p += y * surf->pitch;
|
|
int *pi = (int*) (p);
|
|
return pi[x];
|
|
}
|
|
|
|
int qpixel3(SDL_Surface *surf, int x, int y) {
|
|
if(x<0 || y<0 || x >= surf->w || y >= surf->h) return ZZ;
|
|
char *p = (char*) surf->pixels;
|
|
p += y * surf->pitch;
|
|
p += x;
|
|
int *pi = (int*) (p);
|
|
return pi[0];
|
|
}
|
|
|
|
void loadfont(int siz) {
|
|
if(!font[siz]) {
|
|
font[siz] = TTF_OpenFont("DejaVuSans-Bold.ttf", siz);
|
|
// 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 textwidth(int siz, const string &str) {
|
|
if(size(str) == 0) return 0;
|
|
|
|
loadfont(siz);
|
|
|
|
int w, h;
|
|
TTF_SizeUTF8(font[siz], str.c_str(), &w, &h);
|
|
// printf("width = %d [%d]\n", w, size(str));
|
|
return w;
|
|
}
|
|
#endif
|
|
|
|
#ifdef IOS
|
|
|
|
int textwidth(int siz, const string &str) {
|
|
return mainfont->getSize(str, siz / 36.0).width;
|
|
}
|
|
|
|
#endif
|
|
|
|
int 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
|
|
|
|
int lalpha = 0xFF;
|
|
|
|
void glcolor(int color) {
|
|
unsigned char *c = (unsigned char*) (&color);
|
|
glColor4f(c[2] / 255.0, c[1] / 255.0, c[0]/255.0, lalpha / 255.0); // c[3]/255.0);
|
|
}
|
|
|
|
void selectEyeGL(int ed) {
|
|
DEBB(DF_GRAPH, (debugfile,"selectEyeGL\n"));
|
|
float ve = ed*vid.eye;
|
|
ve *= 2; // vid.xres; ve /= vid.radius;
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
float lowdepth = 0.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};
|
|
|
|
if(ve)
|
|
glTranslatef(-(ve * vid.radius) * (vid.alpha - (vid.radius*1./vid.xres) * vid.eye) / vid.xres, 0, 0);
|
|
|
|
glTranslatef((vid.xcenter*2.)/vid.xres - 1, 1 - (vid.ycenter*2.)/vid.yres, 0);
|
|
|
|
glMultMatrixf(frustum);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
GLfloat sc = vid.radius / (vid.yres/2.);
|
|
GLfloat mat[16] = {sc,0,0,0, 0,-sc,0,0, 0,0,-1,0, 0,0, GLfloat(-vid.alpha),1};
|
|
glMultMatrixf(mat);
|
|
|
|
if(ve) glTranslatef(ve, 0, vid.eye);
|
|
|
|
vid.scrdist = -vid.alpha + vid.yres * sc / 2;
|
|
}
|
|
|
|
void selectEyeMask(int ed) {
|
|
if(ed == 0) {
|
|
glColorMask( GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE );
|
|
}
|
|
else if(ed == 1) {
|
|
glColorMask( GL_TRUE,GL_FALSE,GL_FALSE,GL_TRUE );
|
|
}
|
|
else if(ed == -1) {
|
|
glColorMask( GL_FALSE,GL_TRUE,GL_TRUE,GL_TRUE );
|
|
}
|
|
}
|
|
|
|
void setGLProjection() {
|
|
DEBB(DF_GRAPH, (debugfile,"setGLProjection\n"));
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
glClearColor(0,0,0,1);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
selectEyeGL(0);
|
|
}
|
|
|
|
bool GL_initialized = false;
|
|
void buildpolys();
|
|
|
|
#ifndef MOBILE
|
|
|
|
struct glfont_t {
|
|
GLuint * textures; // Holds The Texture Id's
|
|
//GLuint list_base; // Holds The First Display List ID
|
|
int widths[128+NUMEXTRA];
|
|
int heights[128+NUMEXTRA];
|
|
float tx[128+NUMEXTRA];
|
|
float ty[128+NUMEXTRA];
|
|
};
|
|
|
|
glfont_t *glfont[256];
|
|
|
|
void init(const char * fname, unsigned int h);
|
|
|
|
inline int next_p2 (int a )
|
|
{
|
|
int rval=1;
|
|
// rval<<=1 Is A Prettier Way Of Writing rval*=2;
|
|
while(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 );
|
|
|
|
loadfont(size);
|
|
if(!font[size]) return;
|
|
|
|
char str[2]; str[1] = 0;
|
|
|
|
SDL_Color white;
|
|
white.r = white.g = white.b = 255;
|
|
|
|
glListBase(0);
|
|
|
|
for(unsigned char ch=1;ch<128+NUMEXTRA;ch++) {
|
|
// printf("init size=%d ch=%d\n", size, ch);
|
|
|
|
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;
|
|
// printf("a\n");
|
|
|
|
int twidth = next_p2( txt->w );
|
|
int theight = next_p2( txt->h );
|
|
|
|
Uint16 expanded_data[twidth * theight];
|
|
|
|
for(int j=0; j <theight;j++) for(int i=0; i < twidth; i++) {
|
|
expanded_data[(i+j*twidth)] =
|
|
(i>=txt->w || j>=txt->h) ? 0 : ((qpixel(txt, i, j)>>24)&0xFF) * 0x101;
|
|
}
|
|
|
|
// printf("b\n");
|
|
f.widths[ch] = txt->w;
|
|
f.heights[ch] = txt->h;
|
|
|
|
glBindTexture( GL_TEXTURE_2D, f.textures[ch]);
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
|
|
|
|
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, twidth, theight, 0,
|
|
GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data );
|
|
|
|
// printf("c\n");
|
|
|
|
float x=(float)txt->w / (float)twidth;
|
|
float y=(float)txt->h / (float)theight;
|
|
f.tx[ch] = x;
|
|
f.ty[ch] = y;
|
|
|
|
/* glNewList(f.list_base+ch,GL_COMPILE);
|
|
glBindTexture(GL_TEXTURE_2D, f.textures[ch]);
|
|
|
|
//glPushMatrix();
|
|
|
|
// glTranslatef(bitmap_glyph->left,0,0);
|
|
// glTranslatef(0,bitmap_glyph->top-bitmap.rows,0);
|
|
|
|
// printf("d\n");
|
|
|
|
glBegin(GL_QUADS);
|
|
float eps=0;
|
|
glTexCoord2d(eps,eps); glVertex2f(0, txt->h);
|
|
glTexCoord2d(eps,y-0); glVertex2f(0, 0);
|
|
glTexCoord2d(x-eps,y-0); glVertex2f(txt->w, 0);
|
|
glTexCoord2d(x-eps,0); glVertex2f(txt->w, txt->h);
|
|
glEnd();
|
|
glEndList(); */
|
|
//glPopMatrix();
|
|
|
|
SDL_FreeSurface(txt);
|
|
}
|
|
|
|
//printf("init size=%d ok\n", size);
|
|
GLERR("initfont");
|
|
}
|
|
|
|
int getnext(const char* s, int& i) {
|
|
for(int k=0; k<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++];
|
|
}
|
|
|
|
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); */
|
|
|
|
init_glfont(size);
|
|
if(!font[size]) return false;
|
|
|
|
glfont_t& f(*glfont[size]);
|
|
|
|
glDisable(GL_LIGHTING);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glcolor(color);
|
|
|
|
int tsize = 0;
|
|
for(int i=0; s[i];) tsize += f.widths[getnext(s,i)];
|
|
x -= tsize * align / 16;
|
|
y += f.heights[32]/2;
|
|
|
|
bool clicked = (mousex >= x && mousey <= y && mousex <= x+tsize && mousey >= y-f.heights[32]);
|
|
|
|
for(int i=0; s[i];) {
|
|
|
|
// glListBase(f.list_base);
|
|
// glCallList(s[i]); // s[i]);
|
|
|
|
int tabid = getnext(s,i);
|
|
float fx=f.tx[tabid];
|
|
float fy=f.ty[tabid];
|
|
int wi = f.widths[tabid];
|
|
int hi = f.heights[tabid];
|
|
|
|
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]);
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2d(0,0); glVertex2f(0, -hi);
|
|
glTexCoord2d(0,fy); glVertex2f(0, 0);
|
|
glTexCoord2d(fx,fy); glVertex2f(wi, 0);
|
|
glTexCoord2d(fx,0); glVertex2f(wi, -hi);
|
|
glEnd();
|
|
glPopMatrix();
|
|
}
|
|
|
|
if(vid.goteyes) selectEyeMask(0);
|
|
|
|
GLERR("print");
|
|
|
|
// int tabid = s[i];
|
|
x += f.widths[tabid];
|
|
|
|
/*
|
|
printf("point %d,%d\n", x, y);
|
|
glBegin(GL_POINTS);
|
|
glVertex3f(rand() % 100 - rand() % 100, rand() % 100 - rand() % 100, 100);
|
|
glEnd(); */
|
|
|
|
}
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
return clicked;
|
|
// printf("gl_print ok\n");
|
|
}
|
|
#endif
|
|
|
|
void resetGL() {
|
|
DEBB(DF_INIT, (debugfile,"reset GL\n"));
|
|
GL_initialized = false;
|
|
#ifndef MOBILE
|
|
for(int i=0; i<128; i++) if(glfont[i]) {
|
|
delete glfont[i];
|
|
glfont[i] = NULL;
|
|
}
|
|
#endif
|
|
buildpolys();
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
bool displaystr(int x, int y, int shift, int size, const char *str, int color, int align) {
|
|
|
|
if(strlen(str) == 0) return false;
|
|
|
|
if(size <= 0 || size > 255) {
|
|
// printf("size = %d\n", size);
|
|
return false;
|
|
}
|
|
|
|
#ifdef GL
|
|
if(vid.usingGL) return gl_print(x, y, shift, size, str, color, align);
|
|
#endif
|
|
|
|
SDL_Color col;
|
|
col.r = (color >> 16) & 255;
|
|
col.g = (color >> 8 ) & 255;
|
|
col.b = (color >> 0 ) & 255;
|
|
|
|
col.r >>= darken; col.g >>= darken; col.b >>= darken;
|
|
|
|
loadfont(size);
|
|
|
|
SDL_Surface *txt = (vid.usingAA?TTF_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 = (int) s.size();
|
|
return
|
|
mx >= -len*size*align/32 && mx <= +len*size*(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;
|
|
string msg;
|
|
};
|
|
|
|
vector<msginfo> msgs;
|
|
|
|
vector<string> 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 addMessage(string s, char spamtype) {
|
|
DEBB(DF_MSG, (debugfile,"addMessage: %s\n", s.c_str()));
|
|
if(gamelog.size() == 20) {
|
|
for(int i=0; i<19; i++) gamelog[i] = gamelog[i+1];
|
|
gamelog[19] = s;
|
|
}
|
|
else gamelog.push_back(s);
|
|
msginfo m;
|
|
m.msg = s; m.spamtype = spamtype; m.flashout = false; m.stamp = ticks;
|
|
msgs.push_back(m);
|
|
// printf("%s\n", s.c_str());
|
|
}
|
|
|
|
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);
|
|
displayfr(x, y, 1, vid.fsize, msgs[j].msg, 0x10101 * (255 - age/vid.flashtime), 8);
|
|
msgs[i++] = msgs[j];
|
|
}
|
|
}
|
|
msgs.resize(i);
|
|
}
|
|
|
|
int pmodel = 0;
|
|
|
|
ld ghx, ghy, ghgx, ghgy;
|
|
hyperpoint ghpm = C0;
|
|
|
|
void xybound(int& x, int &y) {
|
|
if(x<-vid.xres) x=-vid.xres; if(x>2*vid.xres) x=2*vid.xres;
|
|
if(y<-vid.yres) y=-vid.yres; if(y>2*vid.yres) y=2*vid.yres;
|
|
}
|
|
|
|
void ghcheck(int& x, int &y, const hyperpoint &H) {
|
|
xybound(x,y);
|
|
if(hypot(x-ghx, y-ghy) < hypot(ghgx-ghx, ghgy-ghy)) {
|
|
ghpm = H; ghgx = x; ghgy = y;
|
|
}
|
|
}
|
|
|
|
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) return Hypc;
|
|
|
|
// hz*hz-(hx/(hz+alpha))^2 - (hy/(hz+alpha))^2 =
|
|
|
|
// hz*hz-hr*(hz+alpha)^2 == 1
|
|
// hz*hz - hr*hr*hz*Hz
|
|
|
|
ld A = 1-hr;
|
|
ld B = 2*hr*vid.alphax;
|
|
ld C = 1 + hr*vid.alphax*vid.alphax;
|
|
|
|
// Az^2 - Bz = C
|
|
B /= A; C /= A;
|
|
|
|
// z^2 - Bz = C
|
|
// z^2 - Bz + (B^2/4) = C + (B^2/4)
|
|
// z = (B/2) + sqrt(C + B^2/4)
|
|
|
|
ld hz = B / 2 + sqrt(C + B*B/4);
|
|
hyperpoint H;
|
|
H[0] = hx * (hz+vid.alphax);
|
|
H[1] = hy * (hz+vid.alphax);
|
|
H[2] = hz;
|
|
|
|
return H;
|
|
}
|
|
|
|
void getcoord(const hyperpoint& H, int& x, int& y, int &shift) {
|
|
|
|
if(H[2] < 0.999) {
|
|
// printf("error: %s\n", display(H));
|
|
// exit(1);
|
|
}
|
|
ld tz = euclid ? (EUCSCALE+vid.alphax) : vid.alphax+H[2];
|
|
if(tz < 1e-3 && tz > -1e-3) tz = 1000;
|
|
|
|
if(pmodel == 0) {
|
|
x = vid.xcenter + int(vid.radius * H[0] / tz);
|
|
y = vid.ycenter + int(vid.radius * H[1] / tz);
|
|
#ifndef MOBILE
|
|
shift = vid.goteyes ? int(vid.eye * vid.radius * (1 - vid.beta / tz)) : 0;
|
|
#endif
|
|
xybound(x,y);
|
|
return;
|
|
}
|
|
|
|
if(pmodel == 3 || pmodel == 4) {
|
|
pair<long double, long double> p = polygonal::compute(H[0]/tz, H[1]/tz);
|
|
x = vid.xcenter + int(vid.radius * p.first);
|
|
y = vid.ycenter + int(vid.radius * p.second);
|
|
shift = 0; ghcheck(x,y,H);
|
|
return;
|
|
}
|
|
|
|
// Poincare to half-plane
|
|
tz = H[2]+vid.alphax;
|
|
|
|
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 == 1) {
|
|
x = vid.xcenter + int(x0 * vid.radius);
|
|
y = vid.ycenter*2 - int(y0 * vid.radius);
|
|
shift = 0; ghcheck(x,y,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 sigma = 2 * atan(2*y0 / u) - M_PI/2;
|
|
|
|
x0 = tau; y0 = sigma;
|
|
|
|
x = vid.xcenter + int(x0 * vid.radius/M_PI*2);
|
|
y = vid.ycenter - int(y0 * vid.radius/M_PI*2);
|
|
shift = 0; ghcheck(x,y,H);
|
|
}
|
|
|
|
int dlit;
|
|
|
|
void drawline(const hyperpoint& H1, int x1, int y1, int s1, const hyperpoint& H2, int x2, int y2, int col) {
|
|
dlit++; if(dlit>500) return;
|
|
|
|
int dst = (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
|
|
|
|
#ifdef GL
|
|
if(vid.usingGL && dst <= (ISMOBILE ? 100 : 20)) {
|
|
if(pmodel) {
|
|
glcoords[qglcoords][0] = x1 - vid.xcenter;
|
|
glcoords[qglcoords][1] = y1 - vid.ycenter;
|
|
glcoords[qglcoords][2] = vid.scrdist;
|
|
}
|
|
else if(euclid) {
|
|
for(int i=0; i<2; i++) glcoords[qglcoords][i] = H1[i]; glcoords[qglcoords][2] = EUCSCALE;
|
|
}
|
|
else {
|
|
for(int i=0; i<3; i++) glcoords[qglcoords][i] = H1[i];
|
|
}
|
|
qglcoords++;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MOBILE
|
|
if(dst <= 400 && !vid.usingGL) {
|
|
if(polyi >= POLYMAX) return;
|
|
polyx[polyi] = x1;
|
|
polyy[polyi] = y1;
|
|
polyi++;
|
|
return;
|
|
}
|
|
#else
|
|
#ifdef GFX
|
|
if(dst <= 20 && col == -1) {
|
|
if(polyi >= POLYMAX) return;
|
|
polyx[polyi] = x1-s1;
|
|
polyxr[polyi] = x1+s1;
|
|
polyy[polyi] = y1;
|
|
polyi++;
|
|
return;
|
|
}
|
|
if(dst <= 20 && !vid.usingGL) {
|
|
(vid.usingAA?aalineColor:lineColor) (s, x1, y1, x2, y2, col);
|
|
return;
|
|
}
|
|
#endif
|
|
if(dst <= 2) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
hyperpoint H3 = mid(H1, H2);
|
|
int x3, y3, s3; getcoord(H3, x3, y3, s3);
|
|
#ifndef GFX
|
|
if(!vid.usingGL) {
|
|
qpixel(s, x3-s3, y3) |= col & 0xFF0000;
|
|
qpixel(s, x3+s3, y3) |= col & 0x00FFFF;
|
|
}
|
|
#endif
|
|
|
|
drawline(H1, x1, y1, s1, H3, x3, y3, col);
|
|
drawline(H3, x3, y3, s3, H2, x2, y2, col);
|
|
}
|
|
|
|
void fixcolor(int& col) {
|
|
if(col != -1) {
|
|
col = (col << 8) + lalpha;
|
|
if(col == -1) col = -2;
|
|
polyi = 0;
|
|
#ifndef GFX
|
|
if(!vid.usingGL) {
|
|
SDL_LockSurface(s);
|
|
col >>= 8;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void drawline(const hyperpoint& H1, const hyperpoint& H2, int col) {
|
|
if(vid.usingGL && !pmodel) {
|
|
qglcoords = 0;
|
|
}
|
|
|
|
dlit = 0;
|
|
int x1, y1, s1; getcoord(H1, x1, y1, s1);
|
|
int x2, y2, s2; getcoord(H2, x2, y2, s2);
|
|
drawline(H1, x1, y1, s1, H2, x2, y2, col);
|
|
|
|
if(vid.usingGL) {
|
|
|
|
// EUCLIDEAN
|
|
if(pmodel) {
|
|
glcoords[qglcoords][0] = x2 - vid.xcenter;
|
|
glcoords[qglcoords][1] = y2 - vid.ycenter;
|
|
glcoords[qglcoords][2] = vid.scrdist;
|
|
}
|
|
else if(euclid) {
|
|
for(int i=0; i<2; i++) glcoords[qglcoords][i] = H2[i]; glcoords[qglcoords][2] = EUCSCALE;
|
|
}
|
|
else {
|
|
for(int i=0; i<3; i++) glcoords[qglcoords][i] = H2[i];
|
|
}
|
|
qglcoords++;
|
|
|
|
if(pmodel) return;
|
|
|
|
glVertexPointer(3, GL_FLOAT, 0, glcoords);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glcolor(col >> 8);
|
|
|
|
if(vid.goteyes) {
|
|
selectEyeGL(-1);
|
|
selectEyeMask(-1);
|
|
glDrawArrays(GL_LINE_STRIP, 0, qglcoords);
|
|
selectEyeGL(+1);
|
|
selectEyeMask(+1);
|
|
glDrawArrays(GL_LINE_STRIP, 0, qglcoords);
|
|
selectEyeGL(0);
|
|
selectEyeMask(0);
|
|
}
|
|
else glDrawArrays(GL_LINE_STRIP, 0, qglcoords);
|
|
}
|
|
|
|
#ifdef MOBILE
|
|
else if(col != -1) {
|
|
gdpush(3); gdpush(col);
|
|
gdpush(polyi+1);
|
|
for(int i=0; i<polyi; i++) gdpush(polyx[i]), gdpush(polyy[i]);
|
|
gdpush(x2), gdpush(y2);
|
|
}
|
|
#endif
|
|
#ifndef GFX
|
|
if(col != -1) {
|
|
SDL_UnlockSurface(s);
|
|
}
|
|
#endif
|
|
// printf("ok\n");
|
|
}
|
|
|
|
// game-related graphics
|
|
|
|
transmatrix View; // current rotation, relative to viewctr
|
|
transmatrix cwtV; // player-relative view
|
|
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 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 / 21.));
|
|
}
|
|
|
|
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[itOrbGhost]) col = (col &~0XFF) | (col&0xFF) / 2;
|
|
if(items[itOrbFish] && isWatery(cwt.c) && 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 flashat, bigflashat, lightat, safetyat;
|
|
cell *flashcell;
|
|
|
|
void drawFlash(cell *c) { flashat = ticks; flashcell = c; }
|
|
void drawBigFlash(cell *c) { bigflashat = ticks; flashcell = c; }
|
|
void drawLightning() { lightat = ticks; }
|
|
void drawSafety() { safetyat = ticks; }
|
|
|
|
double eurad = 0.52;
|
|
|
|
bool outofmap(hyperpoint h) {
|
|
if(euclid)
|
|
return h[0] * h[0] + h[1] * h[1] > 15 * eurad;
|
|
else
|
|
return h[2] < .5;
|
|
}
|
|
|
|
void drawShield(const transmatrix& V, eItem it) {
|
|
float ds = ticks / 300.;
|
|
int col = darkened(iinf[it].color);
|
|
if(it == itOrbShield && items[itOrbPreserve] && !orbused[it])
|
|
col = (col & 0xFEFEFE) / 2;
|
|
double d = it == itOrbShield ? hexf : hexf - .1;
|
|
for(int a=0; a<84*5; a++)
|
|
queueline(V*ddi(a, d + sin(ds + M_PI*2*a/20)*.1)*C0, V*ddi((a+1), d + sin(ds + M_PI*2*(a+1)/20)*.1)*C0, col);
|
|
}
|
|
|
|
void drawShell(const transmatrix& V) {
|
|
float ds = ticks / 300.;
|
|
int col = darkened(iinf[itOrbShell].color);
|
|
for(int a=0; a<84*5; a++)
|
|
queueline(V*ddi(a, hexf + sin(ds + M_PI*2*a/20)*.1)*C0, V*ddi((a+1), hexf + sin(ds + M_PI*2*(a+1)/20)*.1)*C0, col);
|
|
}
|
|
|
|
void drawSpeed(const transmatrix& V) {
|
|
ld ds = ticks / 10.;
|
|
int col = darkened(iinf[itOrbSpeed].color);
|
|
for(int b=0; b<84; b+=14)
|
|
for(int a=0; a<84; a++)
|
|
queueline(V*ddi(ds+b+a, hexf*a/84)*C0, V*ddi(ds+b+(a+1), hexf*(a+1)/84)*C0, col);
|
|
}
|
|
|
|
void drawSafety(const transmatrix& V, int ct) {
|
|
ld ds = ticks / 50.;
|
|
int col = darkened(iinf[itOrbSafety].color);
|
|
for(int a=0; a<ct; a++)
|
|
queueline(V*ddi(ds+a*84/ct, 2*hexf)*C0, V*ddi(ds+(a+(ct-1)/2)*84/ct, 2*hexf)*C0, col);
|
|
}
|
|
|
|
void drawFlash(const transmatrix& V) {
|
|
float ds = ticks / 300.;
|
|
int col = darkened(iinf[itOrbFlash].color);
|
|
col &= ~1;
|
|
for(int u=0; u<5; u++) {
|
|
ld rad = hexf * (2.5 + .5 * sin(ds+u*.3));
|
|
for(int a=0; a<84; a++)
|
|
queueline(V*ddi(a, rad)*C0, V*ddi(a+1, rad)*C0, col);
|
|
}
|
|
}
|
|
|
|
void drawLove(const transmatrix& V, int hdir) {
|
|
float ds = ticks / 300.;
|
|
int col = darkened(iinf[itOrbLove].color);
|
|
col &= ~1;
|
|
for(int u=0; u<5; u++) {
|
|
for(int a=0; a<84; a++) {
|
|
double d = (1 + cos(a * M_PI/42)) / 2;
|
|
int z = a; if(z>42) z = 84-z;
|
|
if(z <= 10) d += (10-z) * (10-z) * (10-z) / 3000.;
|
|
|
|
ld rad = hexf * (2.5 + .5 * sin(ds+u*.3)) * d;
|
|
queueline(V*ddi(hdir+a-1, rad)*C0, V*ddi(hdir+a+1, rad)*C0, col);
|
|
}
|
|
}
|
|
}
|
|
|
|
void drawWinter(const transmatrix& V, int hdir) {
|
|
float ds = ticks / 300.;
|
|
int col = darkened(iinf[itOrbWinter].color);
|
|
for(int u=0; u<20; u++) {
|
|
ld rad = 6 * sin(ds+u * 2 * M_PI / 20);
|
|
queueline(V*ddi(hdir+rad, hexf*.5)*C0, V*ddi(hdir+rad, hexf*3)*C0, col);
|
|
}
|
|
}
|
|
|
|
void drawLightning(const transmatrix& V) {
|
|
int col = iinf[itOrbLightning].color;
|
|
for(int u=0; u<20; u++) {
|
|
ld leng = 0.5 / (0.1 + (rand() % 100) / 100.0);
|
|
ld rad = rand() % 84;
|
|
queueline(V*ddi(rad, hexf*0.3)*C0, V*ddi(rad, hexf*leng)*C0, col);
|
|
}
|
|
}
|
|
|
|
int displaydir(cell *c, int d) {
|
|
if(euclid)
|
|
return - d * 84 / c->type;
|
|
else
|
|
return 42 - d * 84 / c->type;
|
|
}
|
|
|
|
#include "shmup.cpp"
|
|
#include "rug.cpp"
|
|
#include "conformal.cpp"
|
|
|
|
void drawPlayerEffects(const transmatrix& V, cell *c, bool onplayer) {
|
|
if(!onplayer && !items[itOrbEmpathy]) return;
|
|
if(items[itOrbShield] > (shmup::on ? 0 : 1)) drawShield(V, itOrbShield);
|
|
if(items[itOrbShell] > (shmup::on ? 0 : 1)) drawShield(V, itOrbShell);
|
|
|
|
if(items[itOrbSpeed]) drawSpeed(V);
|
|
|
|
int ct = c->type;
|
|
|
|
if(onplayer && items[itOrbSafety]) drawSafety(V, ct);
|
|
|
|
if(onplayer && items[itOrbFlash]) drawFlash(V);
|
|
if(onplayer && items[itOrbLove]) drawLove(V, displaydir(c, cwt.spin));
|
|
|
|
if(items[itOrbWinter])
|
|
drawWinter(V, displaydir(c, cwt.spin));
|
|
|
|
if(onplayer && items[itOrbLightning] > 1) drawLightning(V);
|
|
|
|
if(safetyat > 0) {
|
|
int tim = ticks - safetyat;
|
|
if(tim > 2500) safetyat = 0;
|
|
for(int u=tim; u<=2500; u++) {
|
|
if((u-tim)%250) continue;
|
|
ld rad = hexf * u / 250;
|
|
int col = iinf[itOrbSafety].color;
|
|
for(int a=0; a<84; a++)
|
|
queueline(V*ddi(a, rad)*C0, V*ddi(a+1, rad)*C0, col);
|
|
}
|
|
}
|
|
}
|
|
|
|
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.);
|
|
queuepoly(V2, shFlailBall, 0xFFFFFFFF);
|
|
}
|
|
}
|
|
|
|
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 ? darkena(ds.color, 0, 0xFF) : color);
|
|
}
|
|
|
|
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);
|
|
|
|
int xc, yc, sc;
|
|
getcoord(P2, xc, yc, sc);
|
|
queuechr(xc, yc, sc, 10, 'x', 0xFF00FF);
|
|
|
|
/* if(crad > 0 && c->cpdist <= 3) {
|
|
lalpha = 0x80;
|
|
transmatrix movtocc = V2 * inverse(cwtV) * rgpushxto0(ccenter);
|
|
for(int d=0; d<84; d++)
|
|
queueline(movtocc * ddi(d+1, crad) * C0, movtocc * ddi(d, crad) * C0, 0xC00000);
|
|
lalpha = 0xFF;
|
|
} */
|
|
}
|
|
}
|
|
|
|
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");
|
|
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 = 0;
|
|
queuepoly(V, shHeptaMarker, darkena(mcol, 0, dd));
|
|
poly_outline = OUTLINE_NONE; */
|
|
return gradient(0x487830, mcol, 0, dd, 0xFF);
|
|
}
|
|
};
|
|
|
|
bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col) {
|
|
|
|
char xch = minf[m].glyph;
|
|
|
|
#ifndef MOBILE
|
|
if(where == mapeditor::drawcell)
|
|
mapeditor::drawtrans = V;
|
|
#endif
|
|
|
|
if(m == moTortoise && where->stuntime >= 3)
|
|
drawStunStars(V, where->stuntime-2);
|
|
else if (m == moTortoise || m == moPlayer || !where->stuntime) ;
|
|
else if(!(isMetalBeast(m) && where->stuntime == 1))
|
|
drawStunStars(V, where->stuntime);
|
|
|
|
if(m == moTortoise) {
|
|
int bits = tortoise::getb(where);
|
|
tortoise::draw(V, bits, 0, where->stuntime);
|
|
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 >= 6) {
|
|
queuepoly(V, shWolf, fc(0, cs.skincolor, 0));
|
|
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
|
|
queuepoly(V, shWolf1, fc(314, cs.swordcolor, 3));
|
|
queuepoly(V, shWolf2, fc(314, cs.swordcolor, 3));
|
|
queuepoly(V, shWolf3, fc(314, cs.swordcolor, 3));
|
|
}
|
|
}
|
|
else if(cs.charid >= 4) {
|
|
queuepoly(V, shCatBody, fc(0, cs.skincolor, 0));
|
|
queuepoly(V, shCatHead, fc(150, cs.haircolor, 2));
|
|
queuepoly(V, shCatLegs, fc(500, cs.dresscolor, 4));
|
|
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
|
|
queuepoly(V * xpush(.04), shWolf1, fc(314, cs.swordcolor, 3));
|
|
queuepoly(V * xpush(.04), shWolf2, fc(314, cs.swordcolor, 3));
|
|
}
|
|
}
|
|
else {
|
|
|
|
queuepoly(V, (cs.charid&1) ? shFemaleBody : shPBody, fc(0, cs.skincolor, 0));
|
|
|
|
if(items[itOrbThorns])
|
|
queuepoly(V, shHedgehogBladePlayer, items[itOrbDiscord] ? watercolor(0) : 0x00FF00FF);
|
|
else if(!shmup::on && items[itOrbDiscord])
|
|
queuepoly(V, shPSword, watercolor(0));
|
|
else if(items[itRevolver])
|
|
queuepoly(V, shGunInHand, fc(314, cs.swordcolor, 3)); // 3 not colored
|
|
else if(!shmup::on)
|
|
queuepoly(V, cs.charid >= 2 ? shSabre : shPSword, fc(314, cs.swordcolor, 3)); // 3 not colored
|
|
else if(shmup::curtime >= shmup::getPlayer()->nextshot)
|
|
queuepoly(V, shPKnife, fc(314, cs.swordcolor, 3)); // 3 not colored
|
|
|
|
if(where->land == laWildWest) {
|
|
queuepoly(V, shWestHat1, darkena(cs.swordcolor, 1, 0XFF));
|
|
queuepoly(V, shWestHat2, darkena(cs.swordcolor, 0, 0XFF));
|
|
}
|
|
|
|
if(cheater) {
|
|
queuepoly(V, (cs.charid&1) ? shGoatHead : shDemon, darkena(0xFFFF00, 0, 0xFF));
|
|
// queuepoly(V, shHood, darkena(0xFF00, 1, 0xFF));
|
|
}
|
|
else {
|
|
queuepoly(V, shPFace, fc(500, cs.skincolor, 1));
|
|
queuepoly(V, (cs.charid&1) ? shFemaleHair : shPHead, fc(150, cs.haircolor, 2));
|
|
}
|
|
if(cs.charid&1)
|
|
queuepoly(V, shFemaleDress, fc(500, cs.dresscolor, 4));
|
|
|
|
if(cs.charid == 2)
|
|
queuepoly(V, shPrinceDress, fc(400, cs.dresscolor, 5));
|
|
if(cs.charid == 3)
|
|
queuepoly(V, shPrincessDress, fc(400, cs.dresscolor2, 5));
|
|
|
|
}
|
|
|
|
if(knighted)
|
|
queuepoly(V, shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
|
|
|
|
if(items[itOrbFish])
|
|
queuepoly(V, shFishTail, watercolor(100));
|
|
|
|
if(tortoise::seek())
|
|
tortoise::draw(V * 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 >= 6) {
|
|
queuepoly(V, shWolf, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shWolf1, darkena(col, 1, 0xC0));
|
|
queuepoly(V, shWolf2, darkena(col, 1, 0xC0));
|
|
queuepoly(V, shWolf3, darkena(col, 1, 0xC0));
|
|
}
|
|
else if(cs.charid >= 4) {
|
|
queuepoly(V, shCatBody, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shCatHead, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shCatLegs, darkena(col, 0, 0xC0));
|
|
queuepoly(V * xpush(.04), shWolf1, darkena(col, 1, 0xC0));
|
|
queuepoly(V * xpush(.04), shWolf2, darkena(col, 1, 0xC0));
|
|
}
|
|
else {
|
|
queuepoly(V, (cs.charid&1) ? shFemaleBody : shPBody, darkena(col, 0, 0X80));
|
|
|
|
if(!shmup::on)
|
|
queuepoly(V, (cs.charid >= 2 ? shSabre : shPSword), darkena(col, 0, 0XC0));
|
|
else if(shmup::curtime >= shmup::getPlayer()->nextshot)
|
|
queuepoly(V, shPKnife, darkena(col, 0, 0XC0));
|
|
|
|
queuepoly(V, (cs.charid&1) ? shFemaleHair : shPHead, darkena(col, 1, 0XC0));
|
|
queuepoly(V, shPFace, darkena(col, 0, 0XC0));
|
|
if(cs.charid&1)
|
|
queuepoly(V, shFemaleDress, darkena(col, 1, 0XC0));
|
|
if(cs.charid == 2)
|
|
queuepoly(V, shPrinceDress, darkena(col, 1, 0XC0));
|
|
if(cs.charid == 3)
|
|
queuepoly(V, shPrincessDress, darkena(col, 1, 0XC0));
|
|
}
|
|
}
|
|
|
|
/* else if(m == moShadow) {
|
|
charstyle& cs = getcs();
|
|
queuepoly(V, (cs.charid&1) ? shFemaleBody : shPBody, darkena(col, 0, 0X80));
|
|
queuepoly(V, (cscharid >= 2 ? shSabre : shPSword), darkena(col, 0, 0XC0));
|
|
queuepoly(V, (cs.charid&1) ? shFemaleHair : shPHead, darkena(col, 1, 0XC0));
|
|
queuepoly(V, shPFace, darkena(col, 0, 0XC0));
|
|
|
|
if(cs.charid&1)
|
|
queuepoly(V, shFemaleDress, darkena(col, 1, 0xC0));
|
|
if(cs.charid == 2)
|
|
queuepoly(V, shPrinceDress, darkena(col, 1, 0XC0));
|
|
if(cs.charid == 3)
|
|
queuepoly(V, shPrincessDress, darkena(col, 1, 0XC0));
|
|
}
|
|
|
|
else if(m == moIllusion) {
|
|
charstyle& cs = getcs();
|
|
if(drawUserShape(V, 0, (cs.charid&1)?1:0, darkena(col, 0, 0x80))) return false;
|
|
queuepoly(V, (cs.charid&1) ? shFemaleBody : shPBody, darkena(col, 0, 0X80));
|
|
queuepoly(V, (cscharid >= 2 ? shSabre : shPSword), darkena(col, 0, 0XC0));
|
|
queuepoly(V, (cs.charid&1) ? shFemaleHair : shPHead, darkena(col, 1, 0XC0));
|
|
queuepoly(V, shPFace, darkena(col, 0, 0XC0));
|
|
if(cs.charid&1)
|
|
queuepoly(V, shFemaleDress, darkena(col, 1, 0XC0));
|
|
if(cs.charid == 2)
|
|
queuepoly(V, shPrinceDress, darkena(col, 1, 0XC0));
|
|
if(cs.charid == 3)
|
|
queuepoly(V, shPrincessDress, darkena(col, 1, 0XC0));
|
|
} */
|
|
|
|
else if(m == moBullet) {
|
|
queuepoly(V * spin(-M_PI/4), shKnife, getcs().swordcolor);
|
|
}
|
|
|
|
else if(m == moKnight || m == moKnightMoved) {
|
|
queuepoly(V, shPBody, darkena(0xC0C0A0, 0, 0xC0));
|
|
queuepoly(V, shPSword, darkena(0xFFFF00, 0, 0xFF));
|
|
queuepoly(V, shKnightArmor, darkena(0xD0D0D0, 1, 0xFF));
|
|
int col;
|
|
if(!euclid && where->master->alt)
|
|
col = cloakcolor(roundTableRadius(where));
|
|
else
|
|
col = cloakcolor(newRoundTableRadius());
|
|
queuepoly(V, shKnightCloak, darkena(col, 1, 0xFF));
|
|
queuepoly(V, shPHead, darkena(0x703800, 1, 0XFF));
|
|
queuepoly(V, shPFace, darkena(0xC0C0A0, 0, 0XFF));
|
|
return false;
|
|
}
|
|
|
|
else if(m == moGolem) {
|
|
queuepoly(V, shPBody, darkena(col, 0, 0XC0));
|
|
queuepoly(V, 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;
|
|
|
|
queuepoly(V, girl ? shFemaleBody : shPBody, facecolor);
|
|
|
|
if(m == moPrincessArmed)
|
|
queuepoly(V, vid.cs.charid < 2 ? shSabre : shPSword, 0xFFFFFFFF);
|
|
|
|
if((m == moFalsePrincess || m == moRoseBeauty) && where->cpdist == 1)
|
|
queuepoly(V, shPKnife, 0xFFFFFFFF);
|
|
|
|
if(m == moRoseLady) {
|
|
queuepoly(V, shPKnife, 0xFFFFFFFF);
|
|
queuepoly(V * Mirror, shPKnife, 0xFFFFFFFF);
|
|
}
|
|
|
|
if(m == moRoseLady) {
|
|
// queuepoly(V, girl ? shGoatHead : shDemon, 0x800000FF);
|
|
queuepoly(V, girl ? shFemaleHair : shPHead, evil ? 0x500050FF : 0x332A22FF);
|
|
}
|
|
else if(m == moRoseBeauty) {
|
|
if(girl) {
|
|
queuepoly(V, shBeautyHair, 0xF0A0D0FF);
|
|
queuepoly(V, shFlowerHair, 0xC00000FF);
|
|
}
|
|
else {
|
|
queuepoly(V, shPHead, 0xF0A0D0FF);
|
|
queuepoly(V, shFlowerHand, 0xC00000FF);
|
|
queuepoly(V, shSuspenders, 0xC00000FF);
|
|
}
|
|
}
|
|
else {
|
|
queuepoly(V, girl ? shFemaleHair : shPHead,
|
|
evil ? 0xC00000FF : 0x332A22FF);
|
|
}
|
|
queuepoly(V, shPFace, facecolor);
|
|
|
|
if(girl) {
|
|
queuepoly(V, shFemaleDress, evil ? 0xC000C0FF : 0x00C000FF);
|
|
if(vid.cs.charid < 2)
|
|
queuepoly(V, shPrincessDress, evil ? 0xC040C0C0 : 0x8080FFC0);
|
|
}
|
|
else {
|
|
if(vid.cs.charid < 2)
|
|
queuepoly(V, shPrinceDress, evil ? 0x802080FF : 0x404040FF);
|
|
}
|
|
}
|
|
|
|
else if(m == moWolf || m == moRedFox) {
|
|
queuepoly(V, shWolfLegs, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shWolfBody, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shWolfHead, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shWolfEyes, darkena(col, 3, 0xFF));
|
|
}
|
|
else if(m == moVineBeast) {
|
|
queuepoly(V, shWolfLegs, 0x00FF00FF);
|
|
queuepoly(V, shWolfBody, darkena(col, 1, 0xFF));
|
|
queuepoly(V, shWolfHead, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shWolfEyes, 0xFF0000FF);
|
|
}
|
|
else if(m == moMouse || m == moMouseMoved) {
|
|
queuepoly(V, shMouse, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shMouseLegs, darkena(col, 1, 0xFF));
|
|
queuepoly(V, shMouseEyes, 0xFF);
|
|
}
|
|
else if(isBug(m)) {
|
|
queuepoly(V, shBugBody, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shBugArmor, darkena(col, 1, 0xFF));
|
|
}
|
|
else if(m == moRunDog) {
|
|
queuepoly(V, shWolf, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shWolf1, darkena(0x202020, 0, 0xFF));
|
|
queuepoly(V, shWolf2, darkena(0x202020, 0, 0xFF));
|
|
queuepoly(V, shWolf3, darkena(0x202020, 0, 0xFF));
|
|
}
|
|
else if(m == moOrangeDog) {
|
|
queuepoly(V, shWolf, darkena(0xFFFFFF, 0, 0xFF));
|
|
queuepoly(V, shWolf1, darkena(0x202020, 0, 0xFF));
|
|
queuepoly(V, shWolf2, darkena(0x202020, 0, 0xFF));
|
|
queuepoly(V, shWolf3, darkena(0x202020, 0, 0xFF));
|
|
queuepoly(V, shDogStripes, darkena(0x303030, 0, 0xFF));
|
|
}
|
|
else if(m == moShark || m == moGreaterShark || m == moCShark)
|
|
queuepoly(V, shShark, darkena(col, 0, 0xFF));
|
|
else if(m == moEagle || m == moParrot || m == moBomberbird || m == moAlbatross ||
|
|
m == moTameBomberbird || m == moWindCrow)
|
|
queuepoly(V, shEagle, darkena(col, 0, 0xFF));
|
|
else if(m == moNighthawk || m == moKestrel)
|
|
queuepoly(V, shHawk, darkena(col, 0, 0xFF));
|
|
else if(m == moGargoyle) {
|
|
queuepoly(V, shGargoyleWings, darkena(col, 0, 0xD0));
|
|
queuepoly(V, shGargoyleBody, darkena(col, 0, 0xFF));
|
|
}
|
|
else if(m == moZombie)
|
|
queuepoly(V, shPBody, darkena(col, 0, 0xFF));
|
|
else if(m == moDesertman) {
|
|
queuepoly(V, shPBody, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shPSword, 0xFFFF00FF);
|
|
queuepoly(V, shHood, 0xD0D000C0);
|
|
}
|
|
else if(m == moPalace || m == moFatGuard || m == moVizier || m == moSkeleton) {
|
|
queuepoly(V, shSabre, 0xFFFFFFFF);
|
|
if(m == moSkeleton) {
|
|
queuepoly(V, shSkeletonBody, darkena(0xFFFFFF, 0, 0xFF));
|
|
queuepoly(V, shSkull, darkena(0xFFFFFF, 0, 0xFF));
|
|
queuepoly(V, shSkullEyes, darkena(0, 0, 0xFF));
|
|
}
|
|
else {
|
|
if(m == moFatGuard) {
|
|
queuepoly(V, shFatBody, darkena(0xC06000, 0, 0xFF));
|
|
col = 0xFFFFFF;
|
|
if(where->hitpoints >= 3)
|
|
queuepoly(V, shKnightCloak, darkena(0xFFC0C0, 1, 0xFF));
|
|
}
|
|
else {
|
|
queuepoly(V, shPBody, darkena(0xFFD500, 0, 0xFF));
|
|
queuepoly(V, shKnightArmor, m == moVizier ? 0xC000C0FF :
|
|
darkena(0x00C000, 1, 0xFF));
|
|
if(where->hitpoints >= 3)
|
|
queuepoly(V, shKnightCloak, m == moVizier ? 0x800080Ff :
|
|
darkena(0x00FF00, 1, 0xFF));
|
|
}
|
|
queuepoly(V, shTurban1, darkena(col, 1, 0xFF));
|
|
if(where->hitpoints >= 2)
|
|
queuepoly(V, shTurban2, darkena(col, 0, 0xFF));
|
|
}
|
|
|
|
drawStunStars(V, where->stuntime);
|
|
}
|
|
else if(m == moCrystalSage) {
|
|
queuepoly(V, shPBody, 0xFFFFFFFF);
|
|
queuepoly(V, shPHead, 0xFFFFFFFF);
|
|
queuepoly(V, shPFace, 0xFFFFFFFF);
|
|
}
|
|
else if(m == moHedge) {
|
|
queuepoly(V, shPBody, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shHedgehogBlade, 0xC0C0C0FF);
|
|
queuepoly(V, shPHead, 0x804000FF);
|
|
queuepoly(V, shPFace, 0xF09000FF);
|
|
}
|
|
else if(m == moYeti || m == moMonkey) {
|
|
queuepoly(V, shYeti, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shPHead, darkena(col, 0, 0xFF));
|
|
}
|
|
else if(m == moLemur) {
|
|
queuepoly(V, shPBody, darkena(0xFFFF00, 0, 0xC0));
|
|
queuepoly(V, shAztecHead, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shAztecCap, darkena(0xC000C0, 0, 0xFF));
|
|
}
|
|
else if(m == moEdgeMonkey) {
|
|
queuepoly(V, shYeti, darkena(0xC04040, 0, 0xC0));
|
|
queuepoly(V, shPHead, darkena(col, 0, 0xFF));
|
|
}
|
|
else if(m == moRanger) {
|
|
queuepoly(V, shPBody, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shPSword, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shArmor, darkena(col, 1, 0xFF));
|
|
}
|
|
else if(m == moGhost || m == moSeep || m == moFriendlyGhost) {
|
|
queuepoly(V, shGhost, darkena(col, 0, 0x80));
|
|
queuepoly(V, shEyes, 0xFF);
|
|
}
|
|
else if(m == moVineSpirit) {
|
|
queuepoly(V, shGhost, 0xD0D0D0C0);
|
|
queuepoly(V, shEyes, 0xFF0000FF);
|
|
}
|
|
else if(m == moFireFairy) {
|
|
col = firecolor(0);
|
|
queuepoly(V, shFemaleBody, darkena(col, 0, 0XC0));
|
|
queuepoly(V, shWitchHair, darkena(col, 1, 0xFF));
|
|
queuepoly(V, shPFace, darkena(col, 0, 0XFF));
|
|
}
|
|
else if(m == moSlime) {
|
|
queuepoly(V, shSlime, darkena(col, 0, 0x80));
|
|
queuepoly(V, shEyes, 0xFF);
|
|
}
|
|
else if(m == moCultist || m == moPyroCultist || m == moCultistLeader) {
|
|
queuepoly(V, shPBody, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shPSword, darkena(col, 2, 0xFF));
|
|
queuepoly(V, shHood, darkena(col, 1, 0xFF));
|
|
}
|
|
else if(m == moPirate) {
|
|
queuepoly(V, shPBody, darkena(0x404040, 0, 0xFF));
|
|
queuepoly(V, shPirateHook, darkena(0xD0D0D0, 0, 0xFF));
|
|
queuepoly(V, shPFace, darkena(0xFFFF80, 0, 0xFF));
|
|
queuepoly(V, shEyepatch, darkena(0, 0, 0xC0));
|
|
queuepoly(V, shPirateHood, darkena(col, 0, 0xFF));
|
|
}
|
|
else if(m == moRatling || m == moRatlingAvenger) {
|
|
queuepoly(V, shYeti, darkena(col, 1, 0xFF));
|
|
queuepoly(V, shRatHead, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shRatTail, darkena(col, 0, 0xFF));
|
|
|
|
float t = sin(ticks / 1000.0 + where->cpdist);
|
|
int eyecol = t > 0.92 ? 0xFF0000 : 0;
|
|
|
|
queuepoly(V, shWolf1, darkena(eyecol, 0, 0xFF));
|
|
queuepoly(V, shWolf2, darkena(eyecol, 0, 0xFF));
|
|
queuepoly(V, shWolf3, darkena(0x202020, 0, 0xFF));
|
|
|
|
if(m == moRatlingAvenger) {
|
|
queuepoly(V, shRatCape1, 0x303030FF);
|
|
queuepoly(V, shRatCape2, 0x484848FF);
|
|
}
|
|
}
|
|
else if(m == moViking) {
|
|
queuepoly(V, shPBody, darkena(0xE00000, 0, 0xFF));
|
|
queuepoly(V, shPSword, darkena(0xD0D0D0, 0, 0xFF));
|
|
queuepoly(V, shKnightCloak, darkena(0x404040, 0, 0xFF));
|
|
queuepoly(V, shVikingHelmet, darkena(0xC0C0C0, 0, 0XFF));
|
|
queuepoly(V, shPFace, darkena(0xFFFF80, 0, 0xFF));
|
|
}
|
|
else if(m == moOutlaw) {
|
|
queuepoly(V, shPBody, darkena(col, 0, 0xFF));
|
|
queuepoly(V, shKnightCloak, darkena(col, 1, 0xFF));
|
|
queuepoly(V, shWestHat1, darkena(col, 2, 0XFF));
|
|
queuepoly(V, shWestHat2, darkena(col, 1, 0XFF));
|
|
queuepoly(V, shPFace, darkena(0xFFFF80, 0, 0xFF));
|
|
queuepoly(V, shGunInHand, darkena(col, 1, 0XFF));
|
|
}
|
|
else if(m == moNecromancer) {
|
|
queuepoly(V, shPBody, 0xC00000C0);
|
|
queuepoly(V, shHood, darkena(col, 1, 0xFF));
|
|
}
|
|
else if(m == moGoblin) {
|
|
queuepoly(V, shYeti, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shArmor, darkena(col, 1, 0XFF));
|
|
}
|
|
else if(m == moLancer || m == moFlailer || m == moMiner) {
|
|
transmatrix V2 = V;
|
|
if(m == moLancer)
|
|
V2 = V * spin(where->type == 6 ? -M_PI/3 : -M_PI/2 );
|
|
queuepoly(V2, shPBody, darkena(col, 0, 0xC0));
|
|
queuepoly(V2, m == moFlailer ? shArmor : shHood, darkena(col, 1, 0XFF));
|
|
if(m == moMiner)
|
|
queuepoly(V2, shPickAxe, darkena(0xC0C0C0, 0, 0XFF));
|
|
if(m == moLancer)
|
|
queuepoly(V2, shPike, darkena(col, 0, 0XFF));
|
|
if(m == moFlailer) {
|
|
queuepoly(V2, shFlailBall, darkena(col, 0, 0XFF));
|
|
queuepoly(V2, shFlailChain, darkena(col, 1, 0XFF));
|
|
queuepoly(V2, shFlailTrunk, darkena(col, 0, 0XFF));
|
|
}
|
|
}
|
|
else if(m == moTroll) {
|
|
queuepoly(V, shYeti, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shPHead, darkena(col, 1, 0XFF));
|
|
queuepoly(V, shPFace, darkena(col, 2, 0XFF));
|
|
}
|
|
else if(m == moFjordTroll || m == moForestTroll || m == moStormTroll) {
|
|
queuepoly(V, shYeti, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shPHead, darkena(col, 1, 0XFF));
|
|
queuepoly(V, shPFace, darkena(col, 2, 0XFF));
|
|
}
|
|
else if(m == moDarkTroll) {
|
|
queuepoly(V, shYeti, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shPHead, darkena(col, 1, 0XFF));
|
|
queuepoly(V, shPFace, 0xFFFFFF80);
|
|
}
|
|
else if(m == moRedTroll) {
|
|
queuepoly(V, shYeti, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shPHead, darkena(0xFF8000, 0, 0XFF));
|
|
queuepoly(V, shPFace, 0xFFFFFF80);
|
|
}
|
|
else if(m == moEarthElemental) {
|
|
queuepoly(V, shYeti, darkena(col, 0, 0xC0));
|
|
queuepoly(V, shPHead, darkena(col, 0, 0XFF));
|
|
queuepoly(V, shPFace, 0xF0000080);
|
|
}
|
|
else if(m == moWaterElemental) {
|
|
queuepoly(V, shWaterElemental, watercolor(0));
|
|
queuepoly(V, shFemaleHair, watercolor(100));
|
|
queuepoly(V, shPFace, watercolor(200));
|
|
}
|
|
else if(m == moFireElemental) {
|
|
queuepoly(V, shWaterElemental, darkena(firecolor(0), 0, 0xFF));
|
|
queuepoly(V, shFemaleHair, darkena(firecolor(100), 0, 0xFF));
|
|
queuepoly(V, shPFace, darkena(firecolor(200), 0, 0xFF));
|
|
}
|
|
else if(m == moAirElemental) {
|
|
queuepoly(V, shWaterElemental, darkena(col, 0, 0x80));
|
|
queuepoly(V, shFemaleHair, darkena(col, 0, 0x80));
|
|
queuepoly(V, shPFace, darkena(col, 0, 0x80));
|
|
}
|
|
else if(xch == 'd' || xch == 'D') {
|
|
queuepoly(V, shPBody, darkena(col, 1, 0xC0));
|
|
int acol = col;
|
|
if(xch == 'D') acol = 0xD0D0D0;
|
|
queuepoly(V, shDemon, darkena(acol, 0, 0xFF));
|
|
}
|
|
else if(isMetalBeast(m)) {
|
|
queuepoly(V, shTrylobite, darkena(col, 1, 0xC0));
|
|
int acol = col;
|
|
queuepoly(V, shTrylobiteHead, darkena(acol, 0, 0xFF));
|
|
}
|
|
else if(m == moEvilGolem) {
|
|
queuepoly(V, shPBody, darkena(col, 0, 0XC0));
|
|
queuepoly(V, shGolemhead, darkena(col, 1, 0XFF));
|
|
}
|
|
else if(isWitch(m)) {
|
|
int c = 0xFF;
|
|
if(m == moWitchGhost) c = 0x85 + 120 * sin(ticks / 160.0);
|
|
if(m == moWitchWinter) drawWinter(V, 42);
|
|
if(m == moWitchFlash) drawFlash(V);
|
|
if(m == moWitchSpeed) drawSpeed(V);
|
|
if(m == moWitchFire) col = firecolor(0);
|
|
queuepoly(V, 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(V, shWitchHair, darkena(col, 1, c));
|
|
if(m == moWitchFire) col = firecolor(200);
|
|
queuepoly(V, shPFace, darkena(col, 0, c));
|
|
if(m == moWitchFire) col = firecolor(300);
|
|
queuepoly(V, shWitchDress, darkena(col, 1, 0XC0));
|
|
}
|
|
|
|
else return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
#define OUTLINE_NONE 0x000000FF
|
|
#define OUTLINE_FRIEND 0x00FF00FF
|
|
#define OUTLINE_ENEMY 0xFF0000FF
|
|
#define OUTLINE_TREASURE 0xFFFF00FF
|
|
#define OUTLINE_ORB 0xFF8000FF
|
|
#define OUTLINE_OTHER 0xFFFFFFFF
|
|
#define OUTLINE_DEAD 0x800000FF
|
|
|
|
bool drawMonsterTypeDH(eMonster m, cell *where, const transmatrix& V, int col, bool dh) {
|
|
if(dh) {
|
|
poly_outline = OUTLINE_DEAD;
|
|
darken++;
|
|
}
|
|
bool b = drawMonsterType(m,where,V,col);
|
|
if(dh) {
|
|
poly_outline = OUTLINE_NONE;
|
|
darken--;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
bool drawMonster(const transmatrix& V, int ct, cell *c, int col) {
|
|
|
|
if(shmup::on) shmup::drawMonster(V, c);
|
|
|
|
bool darkhistory = conformal::includeHistory && eq(c->aitmp, sval);
|
|
|
|
if(doHighlight())
|
|
poly_outline =
|
|
(c == cwt.c || isFriendly(c)) ? OUTLINE_FRIEND : OUTLINE_ENEMY;
|
|
|
|
eMonster m = c->monst;
|
|
|
|
if(c == cwt.c && !shmup::on && mapeditor::drawplayer) {
|
|
transmatrix cV2 = cwtV;
|
|
// if(flipplayer) cV2 = cV2 * spin(M_PI);
|
|
if(flipplayer) cV2 = cV2 * spin(M_PI);
|
|
|
|
drawPlayerEffects(V, c, true);
|
|
if(vid.monmode > 1) {
|
|
drawMonsterType(moPlayer, c, cV2, col);
|
|
}
|
|
else return true;
|
|
}
|
|
|
|
if(isIvy(c) || isWorm(c) || isMutantIvy(c)) {
|
|
|
|
transmatrix V2 = V;
|
|
if(isDragon(c->monst) && c->stuntime == 0) col = 0xFF6000;
|
|
|
|
if(c->mondir != NODIR) {
|
|
int hdir = displaydir(c, c->mondir);
|
|
|
|
if(vid.monmode > 1) {
|
|
V2 = V2 * spin(hdir * M_PI / 42);
|
|
|
|
#ifndef MOBILE
|
|
if(c == mapeditor::drawcell) mapeditor::drawtrans = V2;
|
|
#endif
|
|
|
|
if(drawUserShape(V2, 1, c->monst, (col << 8) + 0xFF)) return false;
|
|
|
|
if(isIvy(c) || isMutantIvy(c))
|
|
queuepoly(V2, shIBranch, (col << 8) + 0xFF);
|
|
else if(c->monst < moTentacle) {
|
|
queuepoly(V2, shTentacleX, 0xFF);
|
|
queuepoly(V2, shTentacle, (col << 8) + 0xFF);
|
|
}
|
|
else if(c->monst == moDragonHead || c->monst == moDragonTail) {
|
|
char part = dragon::bodypart(c, dragon::findhead(c));
|
|
if(part != '2') queuepoly(V2, shDragonSegment, darkena(col, 0, 0xFF));
|
|
}
|
|
else {
|
|
if(c->monst == moTentacleGhost) {
|
|
hyperpoint V0 = conformal::on ? V*C0 : inverse(cwtV) * V * C0;
|
|
hyperpoint V1 = spintox(V0) * V0;
|
|
transmatrix VL = cwtV * rspintox(V0) * rpushxto0(V1) * spin(M_PI);
|
|
drawMonsterType(moGhost, c, VL, darkena(col, 0, 0xFF));
|
|
col = minf[moTentacletail].color;
|
|
}
|
|
queuepoly(V2, shTentacleX, 0xFFFFFFFF);
|
|
queuepoly(V2, shTentacle, (col << 8) + 0xFF);
|
|
}
|
|
}
|
|
|
|
else for(int u=-1; u<=1; u++)
|
|
queueline(V*ddi(hdir+21, u*crossf/5)*C0, V*ddi(hdir, crossf)*ddi(hdir+21, u*crossf/5)*C0, 0x606020 >> darken);
|
|
}
|
|
|
|
if(vid.monmode > 1) {
|
|
if(isIvy(c) || isMutantIvy(c))
|
|
queuepoly(V, shILeaf[ct-6], darkena(col, 0, 0xFF));
|
|
else if(m == moWorm || m == moWormwait || m == moHexSnake) {
|
|
queuepoly(V2 * spin(M_PI), shWormHead, darkena(col, 0, 0xFF));
|
|
queuepoly(V2 * spin(M_PI), shDragonEyes, 0xFF);
|
|
}
|
|
else if(m == moDragonHead) {
|
|
queuepoly(V2, shDragonHead, darkena(col, c->hitpoints?0:1, 0xFF));
|
|
queuepoly(V2/* * spin(M_PI) */, shDragonEyes, 0xFF);
|
|
|
|
int noscolor = (c->hitpoints == 1 && c->stuntime ==1) ? 0xFF0000FF : 0xFF;
|
|
queuepoly(V2/* * spin(M_PI) */, shDragonNostril, noscolor);
|
|
queuepoly(V2 * Mirror, shDragonNostril, noscolor);
|
|
}
|
|
else if(m == moTentacle || m == moTentaclewait || m == moTentacleEscaping)
|
|
queuepoly(V2 * spin(M_PI), shTentHead, darkena(col, 0, 0xFF));
|
|
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') {
|
|
int hdir = displaydir(c, nd);
|
|
V2 = V * spin(hdir * M_PI / 42 + M_PI);
|
|
queuepoly(V2, shDragonTail, darkena(col, c->hitpoints?0:1, 0xFF));
|
|
}
|
|
else if(true) {
|
|
int hdir0 = displaydir(c, nd) + 42;
|
|
int hdir1 = displaydir(c, c->mondir);
|
|
while(hdir1 > hdir0 + 42) hdir1 -= 84;
|
|
while(hdir1 < hdir0 - 42) hdir1 += 84;
|
|
V2 = V * spin((hdir0 + hdir1)/2 * M_PI / 42 + M_PI);
|
|
if(part == 'l' || part == '2')
|
|
queuepoly(V2, shDragonLegs, darkena(col, c->hitpoints?0:1, 0xFF));
|
|
queuepoly(V2, shDragonWings, darkena(col, c->hitpoints?0:1, 0xFF));
|
|
}
|
|
}
|
|
else
|
|
queuepoly(V2, shJoint, darkena(col, 0, 0xFF));
|
|
}
|
|
|
|
return vid.monmode < 2;
|
|
}
|
|
|
|
else if(isMimic(c)) {
|
|
|
|
int hdir = displaydir(c, c->mondir);
|
|
|
|
transmatrix V2 = V * spin(M_PI*hdir/42);
|
|
if(c->monst == moMirror) V2 = V2 * Mirror;
|
|
|
|
if(flipplayer) V2 = V2 * spin(M_PI);
|
|
|
|
if(vid.monmode > 1) {
|
|
drawMonsterType(c->monst, c, V2, col);
|
|
drawPlayerEffects(V2, c, false);
|
|
}
|
|
|
|
if(flipplayer) V2 = V2 * spin(M_PI);
|
|
|
|
if(!outofmap(mouseh)) {
|
|
// transmatrix invxy = Id; invxy[0][0] = invxy[1][1] = -1;
|
|
|
|
hyperpoint P2 = V2 * inverse(cwtV) * mouseh;
|
|
int xc, yc, sc;
|
|
getcoord(P2, xc, yc, sc);
|
|
queuechr(xc, yc, sc, 10, 'x', 0xFF00);
|
|
}
|
|
|
|
return vid.monmode < 2;
|
|
}
|
|
|
|
else if(c->monst && vid.monmode < 2) return true;
|
|
|
|
// illusions face randomly
|
|
|
|
else if(c->monst == moIllusion) {
|
|
drawMonsterType(c->monst, c, V, col);
|
|
drawPlayerEffects(V, c, false);
|
|
}
|
|
|
|
// wolves face the heat
|
|
|
|
else if(c->monst == moWolf && c->cpdist > 1) {
|
|
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;
|
|
}
|
|
int hdir = displaydir(c, d);
|
|
transmatrix V2 = V * spin(hdir * M_PI / 42);
|
|
return drawMonsterTypeDH(m, c, V2, col, darkhistory);
|
|
}
|
|
|
|
// 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)) {
|
|
int hdir = displaydir(c, c->mondir) + 42;
|
|
transmatrix V2 = V * spin(hdir * M_PI / 42);
|
|
if(!isBug(c)) drawPlayerEffects(V2, c, false);
|
|
return drawMonsterTypeDH(m, c, V2, col, darkhistory);
|
|
}
|
|
|
|
else if(c->monst) {
|
|
// other monsters face the player
|
|
transmatrix VL;
|
|
|
|
if(false) {
|
|
// hyperpoint V0 = cwtV * C0;
|
|
hyperpoint V1 = V * C0;
|
|
VL = V * spin(hypot(V1[0], V1[1]));
|
|
}
|
|
else {
|
|
hyperpoint V0 = inverse(cwtV) * V * C0;
|
|
hyperpoint V1 = spintox(V0) * V0;
|
|
|
|
VL = cwtV * rspintox(V0) * rpushxto0(V1) * spin(M_PI);
|
|
}
|
|
|
|
return drawMonsterTypeDH(m, c, VL, col, darkhistory);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool showPirateX;
|
|
cell *keycell, *pirateTreasureSeek, *pirateTreasureFound;
|
|
transmatrix pirateCoords;
|
|
|
|
double downspin;
|
|
cell *straightDownSeek;
|
|
|
|
int keycelldist;
|
|
|
|
void drawCircle(int x, int y, int size, int color) {
|
|
#ifdef GL
|
|
if(vid.usingGL) {
|
|
qglcoords = 0;
|
|
glcolor(color);
|
|
x -= vid.xcenter; y -= vid.ycenter;
|
|
int pts = size * 4;
|
|
if(pts > 1500) pts = 1500;
|
|
if(ISMOBILE && pts > 72) pts = 72;
|
|
for(int r=0; r<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;
|
|
}
|
|
|
|
glVertexPointer(3, GL_FLOAT, 0, glcoords);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
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 << 8) | 0x80);
|
|
#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(!havebugs) 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, 0
|
|
};
|
|
|
|
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 movecell[7], curcell;
|
|
|
|
transmatrix applyPatterndir(cell *c, char patt = mapeditor::whichPattern) {
|
|
int hdir = displaydir(c, mapeditor::patterndir(c, patt));
|
|
transmatrix V = spin((42+hdir) * M_PI / 42);
|
|
|
|
if(mapeditor::reflectPatternAt(c, patt))
|
|
return V * Mirror;
|
|
|
|
return V;
|
|
}
|
|
|
|
transmatrix applyDowndir(cell *c, cellfunction *cf) {
|
|
int hdir = displaydir(c, mapeditor::downdir(c, cf));
|
|
return spin((42+hdir) * M_PI / 42);
|
|
}
|
|
|
|
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)
|
|
queuepoly(V * applyDowndir(c, cf), shTower[j], col);
|
|
else if(c->wall != waLadder)
|
|
queuepoly(V, shMFloor[c->type-6], col);
|
|
}
|
|
|
|
void drawZebraFloor(const transmatrix& V, cell *c, int col) {
|
|
|
|
if(euclid) { queuepoly(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;
|
|
|
|
queuepoly(V * applyPatterndir(c, 'z'), shZebra[j], col);
|
|
}
|
|
|
|
#define ECT (euclid?2:ct-6)
|
|
|
|
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)
|
|
queuepoly(V * applyPatterndir(c, 'f'), shEmeraldFloor[j], col);
|
|
else
|
|
queuepoly(V, shCaveFloor[euclid?2:c->type-6], 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void drawcell(cell *c, const transmatrix& V, int spinv) {
|
|
|
|
#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);
|
|
|
|
// todo: fix when scrolling
|
|
if(!buggyGeneration && c->land != laCanvas && sightrange < 10) {
|
|
// not yet created
|
|
if(c->mpdist > 7 && !cheater) return;
|
|
// in the Yendor Challenge, scrolling back is forbidden
|
|
if(c->cpdist > 7 && (yendor::on && !cheater)) return;
|
|
// (incorrect comment) too far, no bugs nearby
|
|
if(playermoved && c->cpdist > sightrange) return;
|
|
}
|
|
|
|
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*7], V*Crad[a*7+21], 0xd0d0 >> darken);
|
|
}
|
|
else {
|
|
for(int a=0; a<7; a++)
|
|
queueline(V*C0, V*Crad[(21+a*6)%42], 0xd0d0 >> darken);
|
|
}
|
|
}
|
|
|
|
if(webdisplay & 2) if(c->type == 7) {
|
|
queueline(V*C0, V*xpush(tessf)*C0, 0xd0d0 >> darken);
|
|
}
|
|
|
|
if(webdisplay & 4) if(c->type == 7 && !euclid && c->master->alt) {
|
|
for(int i=0; i<7; i++)
|
|
if(c->master->move[i] && c->master->move[i]->alt == c->master->alt->move[0])
|
|
queueline(V*C0, V*spin(-2*M_PI*i/7)*xpush(tessf)*C0, 0xd000d0 >> darken);
|
|
}
|
|
}
|
|
|
|
// save the player's view center
|
|
if(c == cwt.c && !shmup::on) {
|
|
playerfound = true;
|
|
|
|
/* if(euclid)
|
|
return d * 84 / c->type;
|
|
else
|
|
return 42 - d * 84 / c->type;
|
|
cwtV = V * spin(-cwt.spin * 2*M_PI/c->type) * spin(M_PI); */
|
|
|
|
cwtV = V * spin(displaydir(c, cwt.spin) * M_PI/42);
|
|
}
|
|
|
|
/* if(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 = V*C0;
|
|
|
|
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 xc, yc, sc, xs, ys, ss;
|
|
getcoord(VC0, xc, yc, sc);
|
|
getcoord(V*xpush(.5)*C0, xs, ys, ss);
|
|
// int col = 0xFFFFFF - 0x20 * c->maxdist - 0x2000 * c->cpdist;
|
|
|
|
if(!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 col = winf[c->wall].color;
|
|
|
|
if(c->land == laAlchemist && c->wall == waNone) col = 0x202020;
|
|
if(c->land == laCrossroads && c->wall == waNone) col = (vid.goteyes2 ? 0xFF3030 : 0xFF0000);
|
|
if(c->land == laCrossroads2 && c->wall == waNone)
|
|
col = linf[laCrossroads2].color;
|
|
|
|
if(c->land == laCrossroads3 && c->wall == waNone)
|
|
col = linf[laCrossroads3].color;
|
|
|
|
if(c->land == laCrossroads4 && c->wall == waNone)
|
|
col = linf[laCrossroads4].color;
|
|
|
|
if(isElemental(c->land) && c->wall == waNone)
|
|
col = linf[c->land].color;
|
|
|
|
if(c->land == laElementalWall && c->wall == waNone)
|
|
col = (linf[c->barleft].color>>1) + (linf[c->barright].color>>1);
|
|
|
|
if(c->land == laZebra && c->wall == waNone) {
|
|
col = 0xE0E0E0;
|
|
}
|
|
|
|
if(c->land == laZebra && c->wall == waTrapdoor)
|
|
col = 0x808080;
|
|
|
|
if(c->land == laDesert && c->wall == waNone) col = 0xEDC9AF;
|
|
if(c->land == laCaves && c->wall == waNone) col = 0x202020;
|
|
if(c->land == laEmerald && c->wall == waNone) col = 0x202020;
|
|
if(c->land == laDeadCaves && c->wall == waNone) col = 0x202020;
|
|
|
|
if(c->land == laNone && c->wall == waNone) {
|
|
col = 0x101010;
|
|
queuepoly(V, shTriangle, 0xFFFF0000);
|
|
}
|
|
|
|
if(isHive(c->land) && !isWateryOrBoat(c) && c->wall != waCloud && c->wall != waMirror && c->wall != waMineMine) {
|
|
col = linf[c->land].color;
|
|
if(c->wall == waWaxWall) col = c->landparam;
|
|
}
|
|
if(c->land == laJungle && c->wall == waNone) col = (vid.goteyes2 ? 0x408040 : 0x008000);
|
|
if(c->land == laPower && c->wall == waNone)
|
|
col = linf[c->land].color;
|
|
/* if(c->land == laEmerald && c->wall == waNone) {
|
|
col = 0x50A020;
|
|
}
|
|
if(c->land == laEmerald && c->wall == waLake) {
|
|
col = 0x202080;
|
|
int i = 0;
|
|
for(int k=0; k<c->type; k++) if(c->mov[k] && c->mov[k]->wall != waLake)
|
|
i++;
|
|
if(i > 0) {
|
|
col = gradient(col, 0xFFFFFF, 0, i-fabs(sin(ticks/1500.0)), 7);
|
|
}
|
|
} */
|
|
if(c->land == laWineyard && c->wall == waNone) {
|
|
col = 0x006000;
|
|
}
|
|
if(c->land == laTortoise && (c->wall == waNone || c->wall == waBigTree || c->wall == waSmallTree)) {
|
|
|
|
if(c->wall == waBigTree) col = 0x709000;
|
|
else if(c->wall == waSmallTree) col = 0x905000;
|
|
else col = tortoise::getMatchColor(getBits(c));
|
|
}
|
|
|
|
if(c->land == laDryForest && c->wall == waNone) {
|
|
/*if(c->wall == waBigTree)
|
|
col = (vid.goteyes ? 0xC0C060 : 0xC0C000);
|
|
else if(c->wall == waSmallTree)
|
|
col = (vid.goteyes ? 0x60C060 : 0x00C000);
|
|
else*/ if(c->wall == waNone) {
|
|
col = gradient(0x008000, 0x800000, 0, c->landparam, 10);
|
|
}
|
|
}
|
|
if(c->land == laMirror && c->wall == waNone) col = 0x808080;
|
|
if(c->land == laMotion && c->wall == waNone) col = 0xF0F000;
|
|
if(c->land == laGraveyard && c->wall == waNone) col = 0x107010;
|
|
if(c->land == laCamelot && c->wall == waNone) {
|
|
int d = showoff ? 0 : ((euclid||c->master->alt) ? celldistAltRelative(c) : 0);
|
|
if(d < 0)
|
|
col = 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)
|
|
col = 0xC0C0C0;
|
|
else
|
|
col = 0xA0A0A0;
|
|
}
|
|
}
|
|
if(c->land == laRlyeh && c->wall == waNone) col = (vid.goteyes2 ? 0x4080C0 : 0x004080);
|
|
if(c->land == laTemple) {
|
|
int d = showoff ? 0 : (euclid||c->master->alt) ? celldistAlt(c) : 99;
|
|
if(chaosmode)
|
|
col = c->wall == waColumn ? winf[waColumn].color : 0x405090;
|
|
else if(d % TEMPLE_EACH == 0)
|
|
col = c->wall == waColumn ? winf[waColumn].color :
|
|
gradient(0x304080, winf[waColumn].color, 0, 0.5, 1);
|
|
// else if(c->type == 7)
|
|
// col = 0x707070;
|
|
else if(d% 2 == -1)
|
|
col = 0x304080;
|
|
else
|
|
col = 0x405090;
|
|
}
|
|
if(c->land == laHell && c->wall == waNone) col = (vid.goteyes2 ? 0xC03030 : 0xC00000);
|
|
|
|
if(c->land == laPalace && (c->wall == waNone || c->wall == waClosePlate || c->wall == waOpenPlate ||
|
|
c->wall == waTrapdoor) && (c->wall == waNone || vid.wallmode != 0))
|
|
col = 0x806020;
|
|
|
|
if(c->land == laPalace && c->wall == waCamelot)
|
|
col = 0xFFD500;
|
|
|
|
if(isIcyLand(c) && isIcyWall(c)) {
|
|
float h = HEAT(c);
|
|
bool showcoc = c->land == laCocytus && chaosmode && vid.wallmode < 3;
|
|
if(h < -0.4)
|
|
col = gradient(showcoc ? 0x4080FF : 0x4040FF, 0x0000FF, -0.4, h, -1);
|
|
else if(h < 0)
|
|
col = gradient(showcoc ? 0x80C0FF : 0x8080FF, showcoc ? 0x4080FF : 0x4040FF, 0, h, -0.4);
|
|
else if(h < 0.2)
|
|
col = gradient(showcoc ? 0x80C0FF : 0x8080FF, 0xFFFFFF, 0, h, 0.2);
|
|
// else if(h < 0.4)
|
|
// col = gradient(0xFFFFFF, 0xFFFF00, 0.2, h, 0.4);
|
|
else if(h < 0.6)
|
|
col = gradient(0xFFFFFF, 0xFF0000, 0.2, h, 0.6);
|
|
else if(h < 0.8)
|
|
col = gradient(0xFF0000, 0xFFFF00, 0.6, h, 0.8);
|
|
else
|
|
col = 0xFFFF00;
|
|
if(c->wall == waNone)
|
|
col = (col & 0xFEFEFE) >> 1;
|
|
if(c->wall == waLake)
|
|
col = (col & 0xFCFCFC) >> 2;
|
|
}
|
|
|
|
/* if(c->wall == waBonfireOff)
|
|
col = 0x404040; */
|
|
|
|
if(isFire(c))
|
|
col = c->wall == waEternalFire ? weakfirecolor(1500) : firecolor(100);
|
|
|
|
/* if(c->wall == waThumperOff)
|
|
col = 0xEDC9AF; */
|
|
|
|
if(c->wall == waThumperOn) {
|
|
int ds = ticks;
|
|
for(int u=0; u<5; u++) {
|
|
ld rad = hexf * (.3 * u + (ds%1000) * .0003);
|
|
int col = gradient(0xFFFFFF, 0, 0, rad, 1.5 * hexf);
|
|
for(int a=0; a<84; a++)
|
|
queueline(V*ddi(a, rad)*C0, V*ddi(a+1, rad)*C0, col);
|
|
}
|
|
}
|
|
|
|
if(c->land == laEmerald && c->wall == waCavefloor) {
|
|
col = gradient(col, 0xFF00, 0, 0.5, 1);
|
|
// col |= 0xFF00; // col += 0x300060; // col += 0x2F18; col -= 0x100000;
|
|
}
|
|
|
|
if(c->land == laOcean && (c->wall == waNone || c->wall == waStrandedBoat)) {
|
|
if(chaosmode)
|
|
col = gradient(0xD0A090, 0xD0D020, 0, c->CHAOSPARAM, 30);
|
|
else
|
|
col = gradient(0xD0D090, 0xD0D020, -1, sin((double) c->landparam), 1);
|
|
}
|
|
|
|
if(c->land == laLivefjord && (c->wall == waNone || c->wall == waStrandedBoat))
|
|
col = 0x306030;
|
|
|
|
if(c->land == laEmerald && c->wall == waCavewall) {
|
|
col = 0xC0FFC0;
|
|
// col |= 0xFF00; // col += 0x300060; // col += 0x2F18; col -= 0x100000;
|
|
}
|
|
|
|
if(c->land == laHive && items[itOrbInvis] && c->wall == waNone && c->landparam)
|
|
col = gradient(col, 0xFF0000, 0, c->landparam, 100);
|
|
|
|
if(c->land == laStorms && (c->wall == waNone))
|
|
col = linf[c->land].color;
|
|
|
|
if(c->land == laWhirlwind && (c->wall == waNone)) {
|
|
int wcol[4] = {0x404040, 0x404080, 0x2050A0, 0x5050C0};
|
|
col = wcol[whirlwind::fzebra3(c)];
|
|
}
|
|
|
|
if(c->land == laOvergrown || c->land == laClearing) {
|
|
if(c->wall == waSmallTree) col = 0x008060;
|
|
else if(c->wall == waBigTree) col = 0x0080C0;
|
|
else if(c->wall == waNone)
|
|
col = (c->land == laOvergrown/* || (celldistAlt(c)&1)*/) ? 0x00C020 : 0x60E080;
|
|
}
|
|
|
|
if(c->land == laGridCoast && c->wall == waSmallTree) col = 0x608000;
|
|
|
|
if(isHaunted(c->land)) {
|
|
if(c->wall == waSmallTree) col = 0x004000;
|
|
else if(c->wall == waBigTree) col = 0x008000;
|
|
else if(c->wall == waNone) {
|
|
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;
|
|
col = 0x609F60 + 0x202020 * itcolor;
|
|
}
|
|
}
|
|
|
|
if(c->land == laWildWest && (c->wall == waNone))
|
|
col = linf[c->land].color;
|
|
|
|
if(c->land == laCaribbean && (c->wall == waCIsland || c->wall == waCIsland2))
|
|
col = winf[c->wall].color;
|
|
|
|
if(c->wall == waBoat && !vid.wallmode) {
|
|
col = 0xC06000;
|
|
}
|
|
|
|
if(c->land == laMinefield && c->wall == waMineMine && (cmode == emMapEditor || !canmove))
|
|
col = 0xFF4040;
|
|
|
|
if(c->wall == waMineMine && c->land != laMinefield)
|
|
col = gradient(col, 0xFF4040, -1, sin(ticks/100.0), 1);
|
|
|
|
if(c->land == laMinefield && c->wall == waNone)
|
|
col = 0x80A080;
|
|
|
|
if(c->land == laCaribbean && c->wall == waNone)
|
|
col = 0x006000;
|
|
|
|
if(c->land == laRose && c->wall == waNone) {
|
|
col = linf[c->land].color;
|
|
}
|
|
|
|
if(isWarped(c->land) && c->wall == waNone)
|
|
col = pseudohept(c) ? 0x80C080 : 0xA06020;
|
|
|
|
if(c->land == laRedRock && (c->wall == waNone || snakelevel(c)) && c->wall != waDeadfloor2) {
|
|
col = linf[c->land].color;
|
|
}
|
|
|
|
if(c->land == laDragon && (c->wall == waNone || snakelevel(c)) && c->wall != waDeadfloor2) {
|
|
col = linf[c->land].color;
|
|
}
|
|
|
|
if(c->land == laCanvas && c->wall == waNone) {
|
|
col = c->landparam;
|
|
}
|
|
|
|
if(pseudohept(c)) {
|
|
if(vid.darkhepta)
|
|
col = gradient(0, col, 0, 0.75, 1);
|
|
}
|
|
|
|
int rd = rosedist(c);
|
|
if(rd == 1) col = gradient(0x804060, col, 0,1,3);
|
|
if(rd == 2) col = gradient(0x804060, col, 0,2,3);
|
|
|
|
int ycol = col;
|
|
|
|
if(c->land == laHive && c->bardir == NOBARRIERS && c->barleft) {
|
|
col = minf[moBug0+c->barright].color;
|
|
}
|
|
|
|
if(items[itRevolver] && c->pathdist > GUNRANGE && !shmup::on)
|
|
col = gradient(col, 0, 0, 25, 100);
|
|
|
|
int xcol = col;
|
|
|
|
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;
|
|
|
|
if(it)
|
|
ch = iinf[it].glyph, col = iinf[it].color;
|
|
|
|
int icol = col;
|
|
|
|
if(it && c->land == laAlchemist)
|
|
if(!(conformal::includeHistory && eq(c->aitmp, sval)))
|
|
xcol = col;
|
|
|
|
if(c->monst) {
|
|
ch = minf[c->monst].glyph, col = minf[c->monst].color;
|
|
if(c->monst == moMutant) {
|
|
// root coloring
|
|
if(c->stuntime != mutantphase)
|
|
col = gradient(0xC00030, 0x008000, 0, (c->stuntime-mutantphase) & 15, 15);
|
|
}
|
|
if(isMetalBeast(c->monst) && c->stuntime)
|
|
col >>= 1;
|
|
}
|
|
|
|
if(c->cpdist == 0 && mapeditor::drawplayer) {
|
|
ch = '@';
|
|
if(vid.monmode == 0) col = cheater ? 0xFF3030 : 0xD0D0D0;
|
|
}
|
|
|
|
if(c->monst == moSlime) {
|
|
col = winf[c->wall].color;
|
|
col |= (col>>1);
|
|
}
|
|
|
|
if(c->ligon) {
|
|
int tim = ticks - lightat;
|
|
if(tim > 1000) tim = 800;
|
|
if(elec::havecharge && tim > 400) tim = 400;
|
|
for(int t=0; t<7; t++) if(c->mov[t] && c->mov[t]->ligon) {
|
|
int hdir = displaydir(c, t);
|
|
int col = gradient(iinf[itOrbLightning].color, 0, 0, tim, 1100);
|
|
queueline(V*ddi(ticks, hexf/2)*C0, V*ddi(hdir, crossf)*C0, col);
|
|
}
|
|
}
|
|
|
|
int ct = c->type;
|
|
|
|
bool error = false;
|
|
|
|
if(c->land == laIvoryTower && (c->wall == waNone || c->wall == waLadder))
|
|
// xcol = (c->landparam&1) ? 0xD00000 : 0x00D000;
|
|
xcol = 0x10101 * (32 + (c->landparam&1) * 32) - 0x000010;
|
|
|
|
if(c->land == laEndorian && c->wall == waNone) {
|
|
int clev = cwt.c->land == laEndorian ? edgeDepth(cwt.c) : 0;
|
|
// xcol = (c->landparam&1) ? 0xD00000 : 0x00D000;
|
|
xcol = 0x10101 * (32 + (c->landparam&1) * 32) - 0x000010;
|
|
xcol = gradient(xcol, 0x0000D0, clev-10, edgeDepth(c), clev+10);
|
|
}
|
|
|
|
if(c->wall == waTrunk) xcol = winf[waTrunk].color;
|
|
|
|
if(c->wall == waCanopy || c->wall == waSolidBranch || c->wall == waWeakBranch) {
|
|
xcol = winf[waCanopy].color;
|
|
if(c->landparam & 1) xcol = gradient(0, xcol, 0, .75, 1);
|
|
}
|
|
|
|
if(c->wall == waSea || c->wall == waBoat) {
|
|
if(c->land == laOcean)
|
|
xcol = (c->landparam > 25 && !chaosmode) ? 0x000090 :
|
|
0x1010C0 + int(32 * sin(ticks / 500. + (chaosmode ? c->CHAOSPARAM : c->landparam)*1.5));
|
|
else if(c->land == laOceanWall)
|
|
xcol = 0x2020FF;
|
|
else if(c->land == laAlchemist)
|
|
xcol = 0x900090;
|
|
else if(c->land == laWhirlpool)
|
|
xcol = 0x0000C0 + int(32 * sin(ticks / 200. + ((euclid||c->master->alt) ? celldistAlt(c) : 0)*1.5));
|
|
else if(c->land == laLivefjord)
|
|
xcol = 0x000080;
|
|
else if(isWarped(c->land))
|
|
xcol = 0x0000C0 + int((pseudohept(c)?30:-30) * sin(ticks / 600.));
|
|
}
|
|
|
|
if(vid.wallmode) {
|
|
|
|
poly_outline = OUTLINE_NONE;
|
|
|
|
// floor
|
|
|
|
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 ? 1 :
|
|
c->land == laCanvas ? 0 :
|
|
c->land == laIvoryTower ? 1 :
|
|
c->land == laEndorian ? 1 :
|
|
c->land == laCaribbean ? 1 :
|
|
c->land == laWhirlwind ? 1 :
|
|
c->land == laRose ? 1 :
|
|
c->land == laGridSea ? 1 :
|
|
c->land == laTortoise ? 1 :
|
|
c->land == laDragon ? 1 :
|
|
2;
|
|
|
|
#ifndef MOBILE
|
|
transmatrix Vpdir = V * applyPatterndir(c);
|
|
#endif
|
|
|
|
// printf("ql\n");
|
|
// queueline(Vpdir * C0, Vpdir * xpush(crossf/3) * C0, 0xFFFFFF);
|
|
|
|
bool eoh = euclid || purehepta;
|
|
|
|
#ifndef MOBILE
|
|
if(c == mapeditor::drawcell && c != cwt.c && !c->monst && !c->item) {
|
|
mapeditor::drawtrans = Vpdir;
|
|
}
|
|
#endif
|
|
|
|
if(c->wall == waChasm) ;
|
|
|
|
/* else if(purehepta)
|
|
queuepoly(V * spin(M_PI), shBigHepta, darkena(xcol, fd, 0xFF)); */
|
|
|
|
|
|
#ifndef MOBILE
|
|
else if(drawUserShape(Vpdir, mapeditor::cellShapeGroup(), mapeditor::realpattern(c),
|
|
darkena(xcol, fd, cmode == emDraw ? 0xC0 : 0xFF)));
|
|
|
|
else if(mapeditor::whichShape == '7') {
|
|
if(ishept(c))
|
|
queuepoly(V, vid.wallmode == 1 ? shBFloor[ct-6] :
|
|
euclid ? shBigHex :
|
|
shBigHepta, darkena(xcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(mapeditor::whichShape == '8') {
|
|
if(euclid)
|
|
queuepoly(V, shTriheptaEuc[ishept(c) ? 1 : ishex1(c) ? 0 : 2], darkena(xcol, fd, 0xFF));
|
|
else
|
|
queuepoly(V, shTriheptaFloor[ishept(c) ? 1 : 0], darkena(xcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(mapeditor::whichShape == '6') {
|
|
if(!ishept(c))
|
|
queuepoly(V,
|
|
vid.wallmode == 1 ? shBFloor[ct-6] :
|
|
euclid ? (ishex1(c) ? shBigHexTriangle : shBigHexTriangleRev) :
|
|
shBigTriangle, darkena(xcol, fd, 0xFF));
|
|
}
|
|
#endif
|
|
|
|
else if(c->land == laWineyard && (c->wall == waVineHalfA || c-> wall == waVineHalfB)) {
|
|
|
|
int i =-1;
|
|
for(int t=0;t<6; t++) if(c->mov[t] && c->mov[t]->wall == c->wall)
|
|
i = t;
|
|
|
|
int hdir = 14 + displaydir(c, i);
|
|
|
|
transmatrix V2 = V * spin(M_PI*hdir/42);
|
|
|
|
hpcshape *shar = shSemiFeatherFloor;
|
|
|
|
if(vid.wallmode == 1) shar = shSemiBFloor;
|
|
if(vid.wallmode == 2) shar = shSemiFloor;
|
|
|
|
int dk = vid.wallmode == 1 ? 0 : vid.wallmode == 2 ? 1 : 1;
|
|
|
|
queuepoly(V2, shar[0], darkena(winf[waVinePlant].color, dk, 0xFF));
|
|
queuepoly(V2, shar[1], darkena(xcol, dk, 0xFF));
|
|
}
|
|
|
|
else if(vid.wallmode == 1 && c->land == laAlchemist)
|
|
queuepoly(V, shFloor[ct-6], darkena(xcol, 1, 0xFF));
|
|
|
|
else if(vid.wallmode == 1 && c->wall == waMineOpen)
|
|
;
|
|
|
|
else if(vid.wallmode == 1)
|
|
queuepoly(V, shBFloor[ct-6], darkena(xcol, 0, 0xFF));
|
|
|
|
else if(isWarped(c) && euclid)
|
|
queuepoly(V, shTriheptaEuc[ishept(c)?1:ishex1(c)?0:2], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(isWarped(c) && !purehepta && !shmup::on) {
|
|
transmatrix V2 = V * applyPatterndir(c);
|
|
int np = mapeditor::nopattern(c);
|
|
if(c->landparam == 1337) np = 0; // for the achievement screenshot
|
|
if(np < 11)
|
|
queuepoly(V2, shTriheptaFloor[np], darkena(xcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(vid.wallmode == 2) {
|
|
queuepoly(V, shFloor[ct-6], darkena(xcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(randomPatternsMode && c->land != laBarrier && !isWarped(c->land)) {
|
|
int j = (randompattern[c->land]/5) % 15;
|
|
int col = darkena(xcol, fd, 0xFF);
|
|
int k = randompattern[c->land] % RPV_MODULO;
|
|
int k7 = randompattern[c->land] % 7;
|
|
|
|
if(k == RPV_ZEBRA && k7 < 2) drawZebraFloor(V, c, col);
|
|
else if(k == RPV_EMERALD && k7 == 0) drawEmeraldFloor(V, c, col);
|
|
else if(k == RPV_CYCLE && k7 < 4) drawTowerFloor(V, c, col, celldist);
|
|
|
|
else switch(j) {
|
|
case 0: queuepoly(V, shCloudFloor[ct-6], col); break;
|
|
case 1: queuepoly(V, shFeatherFloor[ECT], col); break;
|
|
case 2: queuepoly(V, shStarFloor[ct-6], col); break;
|
|
case 3: queuepoly(V, shTriFloor[ct-6], col); break;
|
|
case 4: queuepoly(V, shSStarFloor[ct-6], col); break;
|
|
case 5: queuepoly(V, shOverFloor[ECT], col); break;
|
|
case 6: queuepoly(V, shFeatherFloor[ECT], col); break;
|
|
case 7: queuepoly(V, shDemonFloor[ct-6], col); break;
|
|
case 8: queuepoly(V, shCrossFloor[ct-6], col); break;
|
|
case 9: queuepoly(V, shMFloor[ct-6], col); break;
|
|
case 10: queuepoly(V, shCaveFloor[ECT], col); break;
|
|
case 11: queuepoly(V, shPowerFloor[ct-6], col); break;
|
|
case 12: queuepoly(V, shDesertFloor[ct-6], col); break;
|
|
case 13: queuepoly(V, purehepta ? shChargedFloor[3] : shChargedFloor[ct-6], col); break;
|
|
case 14: queuepoly(V, ct==6?shChargedFloor[2]:shFloor[1], col); break;
|
|
}
|
|
}
|
|
|
|
else if(c->land == laWineyard) {
|
|
queuepoly(V, shFeatherFloor[euclid?2:ct-6], darkena(xcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(c->land == laZebra)
|
|
drawZebraFloor(V, c, darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->wall == waTrunk)
|
|
queuepoly(V, shFloor[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->wall == waCanopy || c->wall == waSolidBranch || c->wall == waWeakBranch)
|
|
queuepoly(V, shFeatherFloor[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(isGravityLand(c->land))
|
|
drawTowerFloor(V, c, darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laEmerald)
|
|
drawEmeraldFloor(V, c, darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laRlyeh)
|
|
queuepoly(V, (eoh ? shFloor: shTriFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laTemple)
|
|
queuepoly(V, (eoh ? shFloor: shTriFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laAlchemist)
|
|
queuepoly(V, shCloudFloor[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laRose)
|
|
queuepoly(V, shRoseFloor[purehepta ? 2 : ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laTortoise)
|
|
queuepoly(V, shTurtleFloor[purehepta ? 2 : ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laDragon && !purehepta)
|
|
queuepoly(V, shDragonFloor[euclid?2:ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if((isElemental(c->land) || c->land == laElementalWall) && !eoh)
|
|
queuepoly(V, shNewFloor[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laJungle)
|
|
queuepoly(V, shFeatherFloor[euclid?2:ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laGraveyard)
|
|
queuepoly(V, (eoh ? shFloor : shCrossFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laDeadCaves) {
|
|
queuepoly(V, shCaveFloor[euclid?2:ct-6], darkena(xcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(c->land == laMotion)
|
|
queuepoly(V, shMFloor[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laWhirlwind)
|
|
// drawZebraFloor(V, c, darkena(xcol, fd, 0xFF));
|
|
queuepoly(V, (eoh ? shCloudFloor : shNewFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laHell)
|
|
queuepoly(V, (euclid ? shStarFloor : shDemonFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laIce)
|
|
// queuepoly(V, shFloor[ct-6], darkena(xcol, 2, 0xFF));
|
|
queuepoly(V, shStarFloor[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laCocytus)
|
|
queuepoly(V, (eoh ? shCloudFloor : shDesertFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laStorms) {
|
|
if(euclid)
|
|
queuepoly(ishex1(c) ? V*spin(M_PI) : V,
|
|
ishept(c) ? shFloor[0] : shChargedFloor[2], darkena(xcol, fd, 0xFF));
|
|
else
|
|
queuepoly(V, (purehepta ? shChargedFloor[3] : ct==6 ? shChargedFloor[2] : shFloor[1]), darkena(xcol, fd, 0xFF));
|
|
}
|
|
|
|
else if(c->land == laWildWest)
|
|
queuepoly(V, (eoh ? shCloudFloor : shSStarFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laPower)
|
|
queuepoly(V, (eoh ? shStarFloor : shPowerFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laHive && !isWateryOrBoat(c) && c->wall != waFloorB && c->wall != waFloorA &&
|
|
c->wall != waMirror && c->wall != waCloud) {
|
|
queuepoly(V, shFloor[ct-6], darkena(xcol, 1, 0xFF));
|
|
if(!snakelevel(c) && c->wall != waMirror && c->wall != waCloud)
|
|
queuepoly(V, shMFloor[ct-6], darkena(xcol, 2, 0xFF));
|
|
if(c->wall != waWaxWall && c->wall != waDeadTroll && c->wall != waDeadTroll2 && c->wall != waVinePlant &&
|
|
!snakelevel(c) && c->wall != waMirror && c->wall != waCloud &&
|
|
c->wall != waStrandedBoat)
|
|
queuepoly(V, shMFloor2[ct-6], darkena(xcol, xcol==ycol ? 1 : 2, 0xFF));
|
|
}
|
|
|
|
else if(c->land == laCaves)
|
|
queuepoly(V, shCaveFloor[ECT], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laDesert)
|
|
queuepoly(V, (eoh ? shCloudFloor : shDesertFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laOvergrown || c->land == laClearing || isHaunted(c->land))
|
|
queuepoly(V, shOverFloor[ECT], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laRose)
|
|
queuepoly(V, shOverFloor[ECT], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laDryForest)
|
|
queuepoly(V, (eoh ? shStarFloor : shDesertFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laCaribbean || c->land == laOcean || c->land == laOceanWall || c->land == laWhirlpool)
|
|
queuepoly(V, shCloudFloor[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laLivefjord)
|
|
queuepoly(V, shCaveFloor[ECT], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laRedRock)
|
|
queuepoly(V, eoh ? shFloor[ct-6] : shDesertFloor[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else if(c->land == laPalace)
|
|
queuepoly(V, (eoh?shFloor:shPalaceFloor)[ct-6], darkena(xcol, fd, 0xFF));
|
|
|
|
else {
|
|
queuepoly(V, shFloor[ct-6], darkena(xcol, fd, 0xFF));
|
|
}
|
|
// walls
|
|
|
|
#ifndef MOBILE
|
|
if(cmode == emMapEditor && mapeditor::displaycodes) {
|
|
|
|
int labeli = mapeditor::displaycodes == 1 ? mapeditor::realpattern(c) : mapeditor::subpattern(c);
|
|
|
|
string label = its(labeli);
|
|
int siz = int(sqrt(squar(xc-xs)+squar(yc-ys))) / 5;
|
|
queuestr(xc, yc, sc, siz, label, 0xFFFFFFFF);
|
|
|
|
/* transmatrix V2 = V * applyPatterndir(c);
|
|
queuepoly(V2, shNecro, 0x80808080);
|
|
queuepoly(V2, shStatue, 0x80808080); */
|
|
}
|
|
#endif
|
|
|
|
if(realred(c->wall)) {
|
|
int s = snakelevel(c);
|
|
if(s >= 1)
|
|
queuepoly(V, shRedRockFloor[0][ct-6], darkena(winf[waRed1].color, 0, 0xFF));
|
|
if(s >= 2)
|
|
queuepoly(V, shRedRockFloor[1][ct-6], darkena(winf[waRed2].color, 0, 0xFF));
|
|
if(s >= 3)
|
|
queuepoly(V, shRedRockFloor[2][ct-6], darkena(winf[waRed3].color, 0, 0xFF));
|
|
}
|
|
|
|
if(pseudohept(c) && (c->land == laRedRock || (purehepta && (c->land == laClearing || isWarped(c))))) {
|
|
queuepoly(V, shHeptaMarker, 0x00000080);
|
|
}
|
|
|
|
if(conformal::includeHistory && eq(c->aitmp, sval-1))
|
|
queuepoly(V, shHeptaMarker, 0x000000C0);
|
|
|
|
/* if(c->land == laBarrier || c->land == laCrossroads2) {
|
|
int siz = int(sqrt(squar(xc-xs)+squar(yc-ys))) / 5;
|
|
displaystr(xc, yc, sc, siz, its(int(c->heat + .5)), 0xFFFFFFFF, 8);
|
|
} */
|
|
|
|
char xch = winf[c->wall].glyph;
|
|
|
|
if(c->wall == waSolidBranch) {
|
|
queuepoly(V, shSolidBranch, 0x804000FF);
|
|
}
|
|
|
|
else if(c->wall == waWeakBranch) {
|
|
queuepoly(V, shWeakBranch, 0x804000FF);
|
|
}
|
|
|
|
else if(c->wall == waLadder) {
|
|
if(euclid) {
|
|
queuepoly(V, shMFloor[ct-6], 0x804000FF);
|
|
queuepoly(V, shMFloor2[ct-6], 0x000000FF);
|
|
}
|
|
else {
|
|
queuepoly(V, shFloor[ct-6], 0x804000FF);
|
|
queuepoly(V, shMFloor[ct-6], 0x000000FF);
|
|
}
|
|
}
|
|
|
|
if(c->wall == waBoat || c->wall == waStrandedBoat) {
|
|
int hdir = displaydir(c, c->mondir);
|
|
transmatrix V2 = V * spin((42+hdir) * M_PI / 42);
|
|
if(items[itOrbWater] && (c == cwt.c || (isFriendly(c) && items[itOrbEmpathy]))) {
|
|
queuepoly(V2, shBoatOuter, watercolor(0));
|
|
queuepoly(V2, shBoatInner, 0x0060C0FF);
|
|
}
|
|
else {
|
|
queuepoly(V2, shBoatOuter, 0xC06000FF);
|
|
queuepoly(V2, shBoatInner, 0x804000FF);
|
|
}
|
|
}
|
|
|
|
else if(c->wall == waBigStatue)
|
|
queuepoly(V, shStatue,
|
|
darkena(winf[c->wall].color, 0, 0xFF)
|
|
);
|
|
|
|
else if(c->wall == waSulphurC) {
|
|
bool drawStar = true;
|
|
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)
|
|
drawStar = false;
|
|
if(drawStar) queuepoly(V, shGiantStar[ct-6], darkena(xcol, 0, 0xFF));
|
|
}
|
|
|
|
else if(c->wall == waClosePlate || c->wall == waOpenPlate || (c->wall == waTrapdoor && c->land != laZebra)) {
|
|
transmatrix V2 = V;
|
|
if(ct == 7 && vid.wallmode == 3) V2 = V * spin(M_PI);
|
|
queuepoly(V2, shMFloor[ct-6], darkena(winf[c->wall].color, 0, 0xFF));
|
|
queuepoly(V2, shMFloor2[ct-6], vid.wallmode >= 2 ? darkena(xcol, 1, 0xFF) : darkena(0,1,0xFF));
|
|
}
|
|
|
|
else if(c->wall == waFrozenLake || c->wall == waLake || c->wall == waCamelotMoat ||
|
|
c->wall == waRoundTable || c->wall == waSea || c->wall == waClosePlate || c->wall == waOpenPlate ||
|
|
c->wall == waOpenGate || c->wall == waTrapdoor)
|
|
;
|
|
|
|
else if(c->wall == waRose) {
|
|
xcol <<= 1;
|
|
if(c->cpdist > 5)
|
|
xcol = 0xC0C0C0;
|
|
else if(rosephase == 7)
|
|
xcol = 0xFF0000;
|
|
else
|
|
xcol = gradient(xcol, 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(xcol, 0, 0xC0));
|
|
}
|
|
|
|
else if(xch == '#') {
|
|
if(c->wall == waVinePlant)
|
|
xcol = 0x60C000;
|
|
if(c->wall != waPlatform && c->wall != waWarpGate)
|
|
queuepoly(V, shWall[ct-6], darkena(xcol, 0, 0xFF));
|
|
}
|
|
|
|
else if(c->wall == waFan) {
|
|
queuepoly(V * spin(M_PI/6 - fanframe * M_PI / 3), shFan, darkena(xcol, 0, 0xFF));
|
|
}
|
|
|
|
else if(xch == '%') {
|
|
if(doHighlight())
|
|
poly_outline = (c->land == laMirror) ? OUTLINE_TREASURE : OUTLINE_ORB;
|
|
queuepoly(V, shMirror, darkena(xcol, 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(col, 0, 0xF0));
|
|
}
|
|
|
|
else if(xch == '+' && (c->land == laGraveyard || isHaunted(c->land)) && c->wall != waFloorB && c->wall != waFloorA &&
|
|
c->wall != waFloorC && c->wall != waFloorD)
|
|
queuepoly(V, shCross, darkena(xcol, 0, 0xFF));
|
|
|
|
else if(xch == '+' && c->wall == waClosedGate) {
|
|
int hdir = 0;
|
|
for(int i=0; i<c->type; i++) if(c->mov[i]->wall == waClosedGate)
|
|
hdir = i;
|
|
hdir = displaydir(c, hdir);
|
|
transmatrix V2 = V * spin((42+hdir) * M_PI / 42);
|
|
queuepoly(V2, shPalaceGate, darkena(xcol, 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 != ';' && c->wall != waSulphur && xch != ',')
|
|
error = true;
|
|
|
|
/* if(c->master->alt) {
|
|
int d = celldistAlt(c);
|
|
int siz = int(sqrt(squar(xc-xs)+squar(yc-ys))) / 5;
|
|
if(d != ALTDIST_UNKNOWN && d != ALTDIST_BOUNDARY)
|
|
displaystr(xc, yc, sc, siz, its(d), 0xFFFFFFFF, 8);
|
|
} */
|
|
}
|
|
else if(!(it || c->monst || c->cpdist == 0)) error = true;
|
|
|
|
if(c->wall == waMineOpen) {
|
|
int mines = countMinesAround(c);
|
|
|
|
if(vid.wallmode == 0) {
|
|
if(ch == '.') {
|
|
if(mines == 0) ch = ' ';
|
|
else ch = '0' + mines, col = minecolors[mines];
|
|
}
|
|
else if(ch == '@') col = minecolors[mines];
|
|
}
|
|
else if(mines > 0)
|
|
queuepoly(V, shMineMark[c->type-6], (minecolors[mines] << 8) | 0xFF);
|
|
}
|
|
|
|
// treasure
|
|
|
|
char xch = iinf[it].glyph;
|
|
hpcshape *xsh =
|
|
it == itPirate ? &shPirateX :
|
|
(it == itBuggy || it == itBuggy2) ? &shPirateX :
|
|
it == itHolyGrail ? &shGrail :
|
|
isElementalShard(it) ? &shElementalShard :
|
|
xch == '*' ? &shGem[ct-6] : xch == '%' ? &shDaisy : xch == '$' ? &shStar : xch == ';' ? &shTriangle :
|
|
xch == '!' ? &shTriangle : it == itBone ? &shNecro : it == itStatue ? &shStatue :
|
|
it == itEdge ? &shFigurine :
|
|
xch == '?' ? &shBookCover :
|
|
it == itKey ? &shKey :
|
|
it == itRevolver ? &shGun :
|
|
NULL;
|
|
|
|
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 MOBILE
|
|
if(c == mapeditor::drawcell && mapeditor::drawcellShapeGroup() == 2)
|
|
mapeditor::drawtrans = V;
|
|
#endif
|
|
|
|
if(vid.monmode == 0 && it)
|
|
error = true;
|
|
|
|
else if(it == itBabyTortoise) {
|
|
int bits = tortoise::babymap[c];
|
|
tortoise::draw(V * spin(ticks / 5000.) * ypush(crossf*.15), bits, 2, 0);
|
|
// queuepoly(V, shHeptaMarker, darkena(tortoise::getMatchColor(bits), 0, 0xC0));
|
|
}
|
|
|
|
else if(it == itCompass) {
|
|
transmatrix V2 = V;
|
|
if(euclid) V2 = V2 * spin(M_PI/2);
|
|
else V2 = V2 * rspintox(inverse(V) * pirateCoords * C0);
|
|
V2 = V2 * spin(M_PI * sin(ticks/100.) / 30);
|
|
queuepoly(V2, shCompass1, 0xFF8080FF);
|
|
queuepoly(V2, shCompass2, 0xFFFFFFFF);
|
|
queuepoly(V2, shCompass3, 0xFF0000FF);
|
|
queuepoly(V2 * spin(M_PI), shCompass3, 0x000000FF);
|
|
xsh = NULL;
|
|
}
|
|
|
|
else if(it == itPalace) {
|
|
transmatrix V2 = V * spin(ticks / 1500.);
|
|
queuepoly(V2, shMFloor3[ct-6], 0xFFD500FF);
|
|
queuepoly(V2, shMFloor4[ct-6], darkena(icol, 0, 0xFF));
|
|
queuepoly(V2, shGem[ct-6], 0xFFD500FF);
|
|
xsh = NULL;
|
|
}
|
|
|
|
else if(drawUserShape(V, 2, it, darkena(icol, 0, 0xFF))) ;
|
|
|
|
else if(it == itRose) {
|
|
for(int u=0; u<4; u++)
|
|
queuepoly(V * spin(ticks / 1500.) * spin(2*M_PI / 3 / 4 * u), shRose, darkena(icol, 0, hidden ? 0x30 : 0xA0));
|
|
}
|
|
|
|
else if(xsh) {
|
|
if(it == itFireShard) icol = firecolor(100);
|
|
if(it == itWaterShard) icol = watercolor(100) >> 8;
|
|
|
|
if(it == itZebra) icol = 0x202020;
|
|
if(it == itLotus) icol = 0x101010;
|
|
|
|
queuepoly(V * spin(ticks / 1500.), *xsh, darkena(icol, 0, hidden ? 0x40 : 0xF0));
|
|
|
|
if(xsh == &shBookCover && vid.monmode)
|
|
queuepoly(V * spin(ticks / 1500.), shBook, 0x805020FF);
|
|
if(it == itZebra)
|
|
queuepoly(V * spin(ticks / 1500. + M_PI/c->type), *xsh, darkena(0xFFFFFF, 0, hidden ? 0x40 : 0xF0));
|
|
}
|
|
|
|
else if(xch == 'o') {
|
|
if(it == itOrbFire) icol = firecolor(100);
|
|
queuepoly(V, 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 == itOrbFreedom) icol = 0xC0FF00;
|
|
if(it == itOrbAir) icol = 0xFFFFFF;
|
|
if(it == itOrbUndeath) icol = minf[moFriendlyGhost].color;
|
|
hpcshape& sh =
|
|
isRangedOrb(it) ? shTargetRing :
|
|
isOffensiveOrb(it) ? shSawRing :
|
|
isFriendOrb(it) ? shPeaceRing :
|
|
isUtilityOrb(it) ? shGearRing :
|
|
it == itOrb37 ? shHeptaRing :
|
|
shRing;
|
|
queuepoly(V * spin(ticks / 1500.), sh, darkena(icol, 0, int(0x80 + 0x70 * sin(ticks / 300.))));
|
|
}
|
|
|
|
else if(it) error = true;
|
|
|
|
|
|
|
|
// monsters
|
|
|
|
if(flashat > 0 && c == flashcell) {
|
|
int tim = ticks - flashat;
|
|
if(tim > 1000) flashat = 0;
|
|
for(int u=0; u<=tim; u++) {
|
|
if((u-tim)%50) continue;
|
|
if(u < tim-150) continue;
|
|
ld rad = u * 3 / 1000.;
|
|
rad = rad * (5-rad) / 2;
|
|
rad *= hexf;
|
|
int col = iinf[itOrbFlash].color;
|
|
if(u > 500) col = gradient(col, 0, 500, u, 1100);
|
|
for(int a=0; a<84; a++)
|
|
queueline(V*ddi(a, rad)*C0, V*ddi(a+1, rad)*C0, col);
|
|
}
|
|
}
|
|
|
|
if(bigflashat > 0 && c == flashcell) {
|
|
int tim = ticks - bigflashat;
|
|
if(tim > 2000) bigflashat = 0;
|
|
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 col = 0xC0FF00;
|
|
if(u > 1000) col = gradient(col, 0, 1000, u, 2200);
|
|
for(int a=0; a<84; a++)
|
|
queueline(V*ddi(a, rad)*C0, V*ddi(a+1, rad)*C0, col);
|
|
}
|
|
}
|
|
|
|
error |= drawMonster(V, ct, c, col);
|
|
|
|
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);
|
|
int hdir = displaydir(c, i);
|
|
transmatrix V0 = spin((42+hdir) * M_PI / 42);
|
|
|
|
double ph = ticks / 75.0 + airdir * M_PI / 21.;
|
|
|
|
int aircol = 0x8080FF00 | int(32 + 32 * -cos(ph));
|
|
|
|
double ph0 = ph/2;
|
|
ph0 -= floor(ph0/M_PI)*M_PI;
|
|
|
|
poly_outline = 0;
|
|
queuepoly(V*V0*ddi(0, hexf*-cos(ph0)), shDisk, aircol);
|
|
poly_outline = OUTLINE_NONE;
|
|
}
|
|
}
|
|
|
|
// queuepoly(V*ddi(rand() % 84, 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((42+hdir) * M_PI / 42);
|
|
|
|
double ph = ticks / 75.0; // + airdir * M_PI / 21.;
|
|
|
|
int rosecol = 0x764e7c00 | int(32 + 32 * -cos(ph));
|
|
|
|
double ph0 = ph/2;
|
|
ph0 -= floor(ph0/M_PI)*M_PI;
|
|
|
|
poly_outline = 0;
|
|
queuepoly(V*V0*ddi(0, hexf*-cos(ph0)), shDisk, rosecol);
|
|
poly_outline = 0xFF;
|
|
}
|
|
}
|
|
} */
|
|
|
|
if(c->land == laWhirlwind) {
|
|
whirlwind::calcdirs(c);
|
|
|
|
for(int i=0; i<whirlwind::qdirs; i++) {
|
|
int hdir0 = displaydir(c, whirlwind::dfrom[i]) + 42;
|
|
int hdir1 = displaydir(c, whirlwind::dto[i]);
|
|
|
|
double ph1 = fanframe;
|
|
|
|
int aircol = 0xC0C0FF40;
|
|
|
|
ph1 -= floor(ph1);
|
|
|
|
if(hdir1 < hdir0-42) hdir1 += 84;
|
|
if(hdir1 >= hdir0+42) hdir1 -= 84;
|
|
|
|
int hdir = (hdir1*ph1+hdir0*(1-ph1));
|
|
|
|
transmatrix V0 = spin((hdir) * M_PI / 42);
|
|
|
|
double ldist = purehepta ? crossf : c->type == 6 ? .2840 : 0.3399;
|
|
|
|
poly_outline = 0;
|
|
queuepoly(V*V0*ddi(0, ldist*(2*ph1-1)), shDisk, aircol);
|
|
poly_outline = OUTLINE_NONE;
|
|
}
|
|
|
|
// queuepoly(V*ddi(rand() % 84, hexf*(rand()%100)/100), shDisk, aircolor(airdir));
|
|
}
|
|
|
|
/* if(ch == '.') {
|
|
col = darkened(col);
|
|
for(int t=0; t<ct; t++)
|
|
queueline(V*ddi(t*84/ct, hexf/3)*C0, V*ddi((t+1)*84/ct, hexf/3)*C0, col);
|
|
}
|
|
|
|
else if(ch == '#') {
|
|
col = darkened(col);
|
|
for(int u=1; u<6; u++)
|
|
for(int t=0; t<ct; t++)
|
|
queueline(V*ddi(0 + t*84/ct, u*hexf/6)*C0, V*ddi(0 + (t+1)*84/ct, u*hexf/6)*C0, col);
|
|
}
|
|
|
|
else */
|
|
|
|
if(error) {
|
|
int siz = int(sqrt(squar(xc-xs)+squar(yc-ys)));
|
|
|
|
if(c->wall == waSea) col = xcol;
|
|
|
|
queuechr(xc, yc, sc, siz, ch, col, 2);
|
|
}
|
|
|
|
if(c == dragon::target && getMount()) {
|
|
queuechr(xc, yc, sc, 2*vid.fsize, 'X',
|
|
gradient(0, iinf[itOrbDomination].color, -1, sin(ticks/(dragon::whichturn == turncount ? 75. : 150.)), 1));
|
|
}
|
|
|
|
if(c == keycell) {
|
|
queuechr(xc, yc, sc, 2*vid.fsize, 'X', 0x10101 * int(128 + 100 * sin(ticks / 150.)));
|
|
queuestr(xc, yc, sc, vid.fsize, its(keycelldist), 0x10101 * int(128 - 100 * sin(ticks / 150.)));
|
|
}
|
|
|
|
if(c == pirateTreasureFound) {
|
|
pirateCoords = V;
|
|
if(showPirateX) {
|
|
queuechr(xc, yc, sc, 2*vid.fsize, 'X', 0x10100 * int(128 + 100 * sin(ticks / 150.)));
|
|
if(cwt.c->master->alt)
|
|
queuestr(xc, yc, sc, vid.fsize, its(-celldistAlt(cwt.c)), 0x10101 * int(128 - 100 * sin(ticks / 150.)));
|
|
}
|
|
}
|
|
|
|
if(!euclid && (!pirateTreasureSeek || compassDist(c) < compassDist(pirateTreasureSeek)))
|
|
pirateTreasureSeek = c;
|
|
|
|
if(!euclid) {
|
|
bool usethis = false;
|
|
double spd = 1;
|
|
|
|
if(isGravityLand(cwt.c->land)) {
|
|
if(!straightDownSeek || edgeDepth(c) < edgeDepth(straightDownSeek)) {
|
|
usethis = true;
|
|
spd = cwt.c->landparam / 10.;
|
|
}
|
|
}
|
|
|
|
if(pmodel) {
|
|
if(c->master->alt && cwt.c->master->alt &&
|
|
(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 || celldistAlt(c) < celldistAlt(straightDownSeek)) {
|
|
usethis = true;
|
|
spd = .5;
|
|
}
|
|
}
|
|
|
|
if(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]) - M_PI/2;
|
|
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);
|
|
// queuechr(xc, yc, sc, 2*vid.fsize, 'X', 0x10100 * int(128 + 100 * sin(ticks / 150.)));
|
|
}
|
|
}
|
|
|
|
|
|
if(!inHighQual) {
|
|
|
|
#if defined(ANDROID) || defined(PANDORA) || defined(IOS)
|
|
if(c == lmouseover && (mousepressed || ISANDROID || ISMOBILE)) {
|
|
queuecircle(xc, yc, int(sqrt(squar(xc-xs)+squar(yc-ys)) * .8), c->cpdist > 1 ? 0x00FFFF : 0xFF0000);
|
|
}
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
if(cmode == emMapEditor && !mapeditor::subscreen && lmouseover &&
|
|
(mapeditor::whichPattern ? mapeditor::subpattern(c) == mapeditor::subpattern(lmouseover) : c == lmouseover)) {
|
|
queuecircle(xc, yc, int(sqrt(squar(xc-xs)+squar(yc-ys)) * .8), 0x00FFFF);
|
|
}
|
|
#endif
|
|
|
|
if(joydir.d >= 0 && c == cwt.c->mov[(joydir.d+cwt.spin) % cwt.c->type])
|
|
queuecircle(xc, yc, int(sqrt(squar(xc-xs)+squar(yc-ys)) * (.78 - .02 * sin(ticks/199.0))), 0x00FF00);
|
|
|
|
#ifndef MOBILE
|
|
if(c == lcenterover && !playermoved && netgen::mode == 0 && !conformal::on)
|
|
queuecircle(xc, yc, int(sqrt(squar(xc-xs)+squar(yc-ys)) * (.70 - .06 * sin(ticks/200.0))), int(175 + 25 * sin(ticks / 200.0)));
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
mapeditor::drawGhosts(c, V, ct);
|
|
#endif
|
|
}
|
|
|
|
// process mouse
|
|
|
|
if(c == cwt.c) curcell = V;
|
|
for(int i=0; i<cwt.c->type; i++)
|
|
if(c == cwt.c->mov[i]) movecell[i] = V;
|
|
|
|
// drawline(V*C0, V*Crad[0], 0xC00000);
|
|
if(c->bardir != NODIR && c->bardir != NOBARRIERS && c->land != laHauntedWall &&
|
|
c->barleft != NOWALLSEP_USED) {
|
|
queueline(V*C0, V*heptmove[c->bardir]*C0, 0x505050 >> darken);
|
|
queueline(V*C0, V*hexmove[c->bardir]*C0, 0x505050 >> darken);
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
netgen::buildVertexInfo(c, V);
|
|
rug::buildVertexInfo(c, V);
|
|
#endif
|
|
|
|
#ifdef LOCAL
|
|
extern void localdraw (const transmatrix& V, cell *c);
|
|
localdraw(V, c);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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"
|
|
);
|
|
#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 config, ESC for the quest status and menu.\n\n"
|
|
);
|
|
h += XLAT(
|
|
"You can right click any element to get more information about it.\n\n"
|
|
);
|
|
#endif
|
|
h += XLAT("See more on the website: ")
|
|
+ "http//roguetemple.com/z/hyper.php\n\n";
|
|
|
|
|
|
h += XLAT("Still confused? Read the FAQ on the HyperRogue website!\n\n");
|
|
|
|
#ifdef MOBILE
|
|
h += buildCredits();
|
|
#else
|
|
h += XLAT("Press 'c' for credits.");
|
|
#endif
|
|
return h;
|
|
}
|
|
|
|
string musiclicense;
|
|
|
|
string buildCredits() {
|
|
string h;
|
|
h += XLAT("game design, programming, texts and graphics by Zeno Rogue <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"
|
|
);
|
|
#ifdef EXTRALICENSE
|
|
h += EXTRALICENSE;
|
|
#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 generateHelpForItem(eItem it) {
|
|
string help = XLAT(iinf[it].help);
|
|
#ifdef ANDROID
|
|
if(it == itOrbSafety)
|
|
help += XLAT("This might be useful for Android devices with limited memory.");
|
|
#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
|
|
#ifndef MOBILE
|
|
if(isRangedOrb(it))
|
|
help += XLAT("You can also scroll to the desired location and then press 't'.");
|
|
#endif
|
|
#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 == 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++;
|
|
}
|
|
}
|
|
}
|
|
return help;
|
|
}
|
|
|
|
string generateHelpForWall(eWall w) {
|
|
string s = XLAT(winf[w].help);
|
|
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;
|
|
}
|
|
|
|
string generateHelpForMonster(eMonster m) {
|
|
string 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);
|
|
|
|
return s;
|
|
}
|
|
|
|
string generateHelpForLand(eLand l) {
|
|
string s = XLAT(linf[l].help);
|
|
|
|
if(l == laPalace) s = princedesc() + 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);
|
|
#define TREQ2(z,x) s += XLAT("Treasure required: %1 x %2.\n", #z, x);
|
|
|
|
if(l == laMirror || l == laMinefield || l == laPalace ||
|
|
l == laOcean || l == laLivefjord || l == laZebra || l == laGridCoast || l == laGridSea)
|
|
TREQ(30)
|
|
|
|
if(l == laCaribbean || 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 == laCamelot) ACCONLY2(laCrossroads, laCrossroads3)
|
|
|
|
if(l == laDryForest || l == laWineyard || l == laDeadCaves || l == laHive || l == laRedRock ||
|
|
l == laOvergrown || l == laStorms || l == laWhirlwind || l == laRose)
|
|
TREQ(60)
|
|
|
|
if(l == laIvoryTower) TREQ(30)
|
|
if(l == laIvoryTower) TREQ2(10, itElixir)
|
|
if(l == laEndorian) TREQ2(10, itEdge)
|
|
|
|
if(l == laCrossroads4) TREQ(200)
|
|
|
|
if(l == laGraveyard || l == laHive)
|
|
s += XLAT("Kills required: %1.\n", "100");
|
|
|
|
if(l == laDragon)
|
|
s += XLAT("Different kills required: %1.\n", "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");
|
|
|
|
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);
|
|
}
|
|
|
|
if(l == laPrincessQuest)
|
|
s += XLAT("Kills required: %1.\n", moVizier);
|
|
|
|
if(l == laElementalWall) {
|
|
s += XLAT("Kills required: %1 (%2).\n", moFireElemental, laDragon);
|
|
s += XLAT("Kills required: %1 (%2).\n", moEarthElemental, laDeadCaves);
|
|
s += XLAT("Kills required: %1 (%2).\n", moWaterElemental, laLivefjord);
|
|
s += XLAT("Kills required: %1 (%2).\n", moAirElemental, laWhirlwind);
|
|
}
|
|
|
|
if(l == laZebra) TREQ2(10, itFeather)
|
|
|
|
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));
|
|
|
|
return s;
|
|
}
|
|
|
|
void describeMouseover() {
|
|
DEBB(DF_GRAPH, (debugfile,"describeMouseover\n"));
|
|
|
|
#ifdef LOCAL
|
|
if(localDescribe()) return;
|
|
#endif
|
|
|
|
cell *c = mousing ? mouseover : playermoved ? NULL : centerover;
|
|
string out = mouseovers;
|
|
if(!c) { }
|
|
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)
|
|
out += " (" + its(c->landparam)+")";
|
|
|
|
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:" + its(c->landparam)+"/"+its(turncount);
|
|
|
|
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 == 7) 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 cut down. Big trees take two turns to cut 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(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 == itBabyTortoise && tortoise::seek())
|
|
out += " " + tortoise::measure(tortoise::babymap[c]);
|
|
if(!c->monst) help = generateHelpForItem(c->item);
|
|
}
|
|
|
|
if(c == cwt.c && !shmup::on) out += XLAT(", you");
|
|
|
|
if(shmup::mousetarget && intval(mouseh, shmup::mousetarget->pat*C0) < .1) {
|
|
out += ", "; 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(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.aspeed > -4.99)
|
|
out = XLAT("+5 = center instantly, -5 = do not center the map");
|
|
else if(getcstat == 'a')
|
|
out = XLAT("press Space or Home to center on the PC");
|
|
else if(getcstat == 'w')
|
|
out = XLAT("also hold Alt during the game to toggle high contrast");
|
|
else if(getcstat == 'w' || getcstat == 'm')
|
|
out = XLAT("You can choose one of the several modes");
|
|
else if(getcstat == 'c')
|
|
out = XLAT("The axes help with keyboard movement");
|
|
else if(getcstat == 'g')
|
|
out = XLAT("Affects looks and grammar");
|
|
#ifndef MOBILE
|
|
else if(getcstat == 's')
|
|
out = XLAT("Config file: %1", conffile);
|
|
#endif
|
|
else out = "";
|
|
}
|
|
else if(cmode == emVisual2) {
|
|
if(getcstat == 'p') {
|
|
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;
|
|
if(cmode != emPickScores)
|
|
#ifdef MOBILE
|
|
if(cmode != emNormal && cmode != emQuit)
|
|
#endif
|
|
displayfr(vid.xres/2, vid.fsize, 2, vid.fsize, out, col, 8);
|
|
if(mousey < vid.fsize * 3/2) getcstat = SDLK_F1;
|
|
|
|
if(false && shmup::mousetarget) {
|
|
char buf[64];
|
|
sprintf(buf, "%Lf", (long double) intval(mouseh, shmup::mousetarget->pat*C0));
|
|
mouseovers = mouseovers + " D: " + buf;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void drawrec(const heptspin& hs, int lev, hstate s, transmatrix V) {
|
|
|
|
shmup::calc_relative_matrix(cwt.c, hs.h);
|
|
|
|
cell *c = hs.h->c7;
|
|
|
|
drawcell(c, V * spin(hs.spin*2*M_PI/7 + (purehepta ? M_PI:0)), hs.spin);
|
|
|
|
if(lev <= 0) return;
|
|
|
|
if(!purehepta) for(int d=0; d<7; d++) {
|
|
int ds = fixrot(hs.spin + d);
|
|
// createMov(c, ds);
|
|
if(c->mov[ds] && c->spn[ds] == 0)
|
|
drawcell(c->mov[ds], V * hexmove[d], 0);
|
|
}
|
|
|
|
if(lev <= 1) return;
|
|
|
|
for(int d=0; d<7; d++) {
|
|
hstate s2 = transition(s, d);
|
|
if(s2 == hsError) continue;
|
|
heptspin hs2 = hsstep(hsspin(hs, d), 0);
|
|
drawrec(hs2, lev-2, s2, V * heptmove[d]);
|
|
}
|
|
|
|
}
|
|
|
|
int mindx=-7, mindy=-7, maxdx=7, maxdy=7;
|
|
|
|
|
|
void drawEuclidean() {
|
|
DEBB(DF_GRAPH, (debugfile,"drawEuclidean\n"));
|
|
eucoord px, py;
|
|
if(!lcenterover) lcenterover = cwt.c;
|
|
// printf("centerover = %p player = %p [%d,%d]-[%d,%d]\n", lcenterover, cwt.c,
|
|
// mindx, mindy, maxdx, maxdy);
|
|
decodeMaster(lcenterover->master, px, py);
|
|
|
|
int minsx = mindx-1, maxsx=maxdx+1, minsy=mindy-1, maxsy=maxdy+1;
|
|
mindx=maxdx=mindy=maxdy=0;
|
|
|
|
for(int dx=minsx; dx<=maxsx; dx++)
|
|
for(int dy=minsy; dy<=maxsy; dy++) {
|
|
eucoord x = dx+px;
|
|
eucoord y = dy+py;
|
|
cell *c = euclideanAt(x,y);
|
|
if(!c) continue;
|
|
transmatrix Mat = Id;
|
|
Mat[2][2] = 1;
|
|
Mat[0][2] += (x + y * .5) * eurad;
|
|
double q3 = sqrt(double(3));
|
|
Mat[1][2] += y * q3 /2 * eurad;
|
|
while(Mat[0][2] <= -16384 * eurad) Mat[0][2] += 32768 * eurad;
|
|
while(Mat[0][2] >= 16384 * eurad) Mat[0][2] -= 32768 * eurad;
|
|
while(Mat[1][2] <= -16384 * q3 * eurad) Mat[1][2] += 32768 * q3 * eurad;
|
|
while(Mat[1][2] >= 16384 * q3 * eurad) Mat[1][2] -= 32768 * q3 * eurad;
|
|
Mat = View * Mat;
|
|
|
|
// Mat[0][0] = -1;
|
|
// Mat[1][1] = -1;
|
|
|
|
// Mat[2][0] = x*x/10;
|
|
// Mat[2][1] = y*y/10;
|
|
// Mat = Mat * xpush(x-30) * ypush(y-30);
|
|
|
|
int cx, cy, shift;
|
|
getcoord(Mat * C0, cx, cy, shift);
|
|
if(cx >= 0 && cy >= 0 && cx < vid.xres && cy < vid.yres) {
|
|
if(dx < mindx) mindx = dx;
|
|
if(dy < mindy) mindy = dy;
|
|
if(dx > maxdx) maxdx = dx;
|
|
if(dy > maxdy) maxdy = dy;
|
|
}
|
|
|
|
drawcell(c, Mat, 0);
|
|
}
|
|
}
|
|
|
|
void drawthemap() {
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"draw the map\n"));
|
|
fanframe = ticks / 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 = cwt.c->item == itCompass;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
lmouseover = mouseover;
|
|
#endif
|
|
modist = 1e20; mouseover = NULL;
|
|
modist2 = 1e20; mouseover2 = NULL;
|
|
mouseovers = XLAT("Press F1 or right click for help");
|
|
centdist = 1e20; lcenterover = centerover; centerover = NULL;
|
|
#ifdef MOBILE
|
|
mouseovers = XLAT("No info about this...");
|
|
#endif
|
|
if(outofmap(mouseh))
|
|
modist = -5;
|
|
playerfound = false;
|
|
// playerfoundL = false;
|
|
// playerfoundR = false;
|
|
|
|
if(euclid)
|
|
drawEuclidean();
|
|
else
|
|
drawrec(viewctr,
|
|
conformal::on ? sightrange + 2:
|
|
(!playermoved) ? sightrange+1 : sightrange + 4,
|
|
hsOrigin, View);
|
|
|
|
if(shmup::on) {
|
|
if(shmup::players == 1)
|
|
cwtV = shmup::pc[0]->pat;
|
|
else if(shmup::centerplayer != -1)
|
|
cwtV = shmup::pc[shmup::centerplayer]->pat;
|
|
else if(shmup::players == 2) {
|
|
hyperpoint h0 = shmup::pc[0]->pat * C0;
|
|
hyperpoint h1 = shmup::pc[1]->pat * C0;
|
|
hyperpoint h2 = mid(h0, h1);
|
|
cwtV = rgpushxto0(h2);
|
|
}
|
|
else if(shmup::players == 3) {
|
|
hyperpoint h0 = shmup::pc[0]->pat * C0;
|
|
hyperpoint h1 = shmup::pc[1]->pat * C0;
|
|
hyperpoint h2 = shmup::pc[2]->pat * C0;
|
|
hyperpoint h3 = mid3(h0, h1, h2);
|
|
cwtV = rgpushxto0(h3);
|
|
}
|
|
else if(shmup::players == 4) {
|
|
hyperpoint h0 = shmup::pc[0]->pat * C0;
|
|
hyperpoint h1 = shmup::pc[1]->pat * C0;
|
|
hyperpoint h2 = shmup::pc[2]->pat * C0;
|
|
hyperpoint h3 = shmup::pc[3]->pat * C0;
|
|
hyperpoint h4 = mid4(h0, h1, h2, h3);
|
|
cwtV = rgpushxto0(h4);
|
|
}
|
|
}
|
|
|
|
#ifdef LOCAL
|
|
localDrawMap();
|
|
#endif
|
|
}
|
|
|
|
void spinEdge(ld aspd) {
|
|
if(downspin > aspd) downspin = aspd;
|
|
if(downspin < -aspd) downspin = -aspd;
|
|
View = spin(downspin) * View;
|
|
}
|
|
|
|
void centerpc(ld aspd) {
|
|
DEBB(DF_GRAPH, (debugfile,"center pc\n"));
|
|
hyperpoint H = cwtV * C0;
|
|
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
|
|
if(R < 1e-9) {
|
|
/* if(playerfoundL && playerfoundR) {
|
|
|
|
} */
|
|
spinEdge(aspd);
|
|
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() {
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"draw movestar\n"));
|
|
if(!playerfound) return;
|
|
|
|
if(shmup::on) return;
|
|
if(rug::rugged) return;
|
|
|
|
if(vid.axes == 0 || (vid.axes == 1 && mousing)) return;
|
|
|
|
hyperpoint H = cwtV * C0;
|
|
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
|
|
transmatrix Centered = Id;
|
|
|
|
if(euclid)
|
|
Centered = eupush(H[0], H[1]);
|
|
else if(R > 1e-9) Centered = rgpushxto0(H);
|
|
|
|
int starcol = (vid.goteyes2 ? 0xE08060 : 0xC00000);
|
|
|
|
if(vid.axes == 3 || (vid.wallmode == 2 && vid.axes == 1))
|
|
queuepoly(Centered, shMovestar, darkena(starcol, 0, 0xFF));
|
|
|
|
else for(int d=0; d<8; d++) {
|
|
int col = starcol;
|
|
#ifdef PANDORA
|
|
if(leftclick && (d == 2 || d == 6 || d == 1 || d == 7)) col >>= 2;
|
|
if(rightclick && (d == 2 || d == 6 || d == 3 || d == 5)) col >>= 2;
|
|
if(!leftclick && !rightclick && (d&1)) col >>= 2;
|
|
#endif
|
|
// EUCLIDEAN
|
|
if(euclid)
|
|
queueline(Centered * C0, Centered * ddi(d * 10.5, 0.5) * C0, col >> darken);
|
|
else
|
|
queueline(Centered * C0, Centered * spin(M_PI*d/4)* xpush(.5) * C0, col >> darken);
|
|
}
|
|
}
|
|
|
|
void optimizeview() {
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"optimize view\n"));
|
|
int turn = 0;
|
|
ld best = INF;
|
|
|
|
transmatrix TB = Id;
|
|
|
|
for(int i=-1; i<7; i++) {
|
|
|
|
ld trot = -i * M_PI * 2 / 7.0;
|
|
transmatrix T = i < 0 ? Id : spin(trot) * xpush(tessf) * spin(M_PI);
|
|
hyperpoint H = View * T * C0;
|
|
if(H[2] < best) best = H[2], turn = i, TB = T;
|
|
}
|
|
|
|
if(turn >= 0) {
|
|
View = View * TB;
|
|
fixmatrix(View);
|
|
viewctr = hsspin(viewctr, turn);
|
|
viewctr = hsstep(viewctr, 0);
|
|
}
|
|
}
|
|
|
|
movedir vectodir(const hyperpoint& P) {
|
|
hyperpoint H = cwtV * C0;
|
|
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
|
|
transmatrix Centered = cwtV;
|
|
if(!euclid)
|
|
Centered = gpushxto0(H) * Centered;
|
|
else if(R > 1e-9)
|
|
Centered = eupush(-H[0], -H[1]) * Centered;
|
|
ld binv = 99;
|
|
|
|
ld dirdist[7];
|
|
for(int i=0; i<cwt.c->type; i++)
|
|
dirdist[i] = intval(Centered * spin(-i * 2 * M_PI /cwt.c->type) * xpush(1) * C0, 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(euclid) bdir = (bdir + 3) % 6;
|
|
return res;
|
|
}
|
|
|
|
void movepckeydir(int d) {
|
|
DEBB(DF_GRAPH, (debugfile,"movepckeydir\n"));
|
|
// EUCLIDEAN
|
|
if(euclid)
|
|
movepcto(vectodir(spin(-d * M_PI/4) * eupush(1, 0) * C0));
|
|
else
|
|
movepcto(vectodir(spin(-d * M_PI/4) * xpush(1) * C0));
|
|
}
|
|
|
|
void calcMousedest() {
|
|
if(outofmap(mouseh)) return;
|
|
if(revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
|
|
ld mousedist = intval(mouseh, curcell * C0);
|
|
mousedest.d = -1;
|
|
|
|
ld dists[7];
|
|
|
|
for(int i=0; i<cwt.c->type; i++)
|
|
dists[i] = intval(mouseh, movecell[i] * C0);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
}
|
|
|
|
void mousemovement() {
|
|
calcMousedest();
|
|
movepcto(mousedest);
|
|
}
|
|
|
|
long double sqr(long double x) { return x*x; }
|
|
|
|
void checkjoy() {
|
|
DEBB(DF_GRAPH, (debugfile,"check joy\n"));
|
|
if(shmup::on) return;
|
|
ld joyvalue1 = sqr(vid.joyvalue);
|
|
ld joyvalue2 = sqr(vid.joyvalue2);
|
|
|
|
ld jx = joyx;
|
|
ld jy = joyy;
|
|
ld sq = jx*jx+jy*jy;
|
|
|
|
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;
|
|
}
|
|
|
|
void calcparam() {
|
|
DEBB(DF_GRAPH, (debugfile,"calc param\n"));
|
|
vid.xcenter = vid.xres / 2;
|
|
vid.ycenter = vid.yres / 2;
|
|
vid.radius = int(vid.scale * vid.ycenter) - (ISANDROID ? 2 : ISIOS ? 40 : 40);
|
|
|
|
if(vid.xres < vid.yres) {
|
|
vid.radius = int(vid.scale * vid.xcenter) - (ISIOS ? 10 : 2);
|
|
vid.ycenter = vid.yres - vid.radius - vid.fsize - (ISIOS ? 10 : 0);
|
|
}
|
|
|
|
vid.beta = 1 + vid.alpha + vid.eye;
|
|
vid.alphax = vid.alpha + vid.eye;
|
|
vid.goteyes = vid.eye > 0.001 || vid.eye < -0.001;
|
|
vid.goteyes2 = vid.goteyes;
|
|
}
|
|
|
|
void displayStat(int y, const string& name, const string& val, char mkey) {
|
|
|
|
int dy = vid.fsize * y + vid.yres/4;
|
|
int dx = vid.xres/2 - 100;
|
|
|
|
bool xthis = (mousey >= dy-vid.fsize/2 && mousey <= dy + vid.fsize/2);
|
|
int xcol = 0x808080;
|
|
|
|
if(xthis) {
|
|
getcstat = mkey; getcshift = 0;
|
|
int mx = mousex - dx;
|
|
if(mx >= 0 && mx <= 100) {
|
|
if(mx < 20) getcshift = -1 , xcol = 0xFF0000;
|
|
else if(mx < 40) getcshift = -0.1 , xcol = 0x0000FF;
|
|
else if(mx < 50) getcshift = -0.01, xcol = 0x00FF00;
|
|
if(mx > 80) getcshift = +1 , xcol = 0xFF0000;
|
|
else if(mx > 60) getcshift = +0.1 , xcol = 0x0000FF;
|
|
else if(mx > 50) getcshift = +0.01, xcol = 0x00FF00;
|
|
}
|
|
}
|
|
|
|
if(val != "") {
|
|
if(val[0] != ' ') {
|
|
displaystr(dx, dy, 0, vid.fsize, val, xthis ? 0xFFFF00 : 0x808080, 16);
|
|
displaystr(dx+25, dy, 0, vid.fsize, "-", xthis && getcshift < 0 ? xcol : 0x808080, 8);
|
|
displaystr(dx+75, dy, 0, vid.fsize, "+", xthis && getcshift > 0 ? xcol : 0x808080, 8);
|
|
}
|
|
else
|
|
displaystr(dx+75, dy, 0, vid.fsize, val, xthis ? 0xFFFF00 : 0x808080, 16);
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
string mk = s0 + mkey;
|
|
int hkx = dx + 100;
|
|
if(mkey >= 1 && mkey <= 26) mk = s0 + "^", mk += (mkey+64), hkx -= vid.fsize;
|
|
// if(mkey >= 64+1 && mkey <= 64+26) mk = s0 + "Shift+", mk += mkey;
|
|
displaystr(hkx, dy, 0, vid.fsize, mk, xthis ? 0xFFFF00 : 0xC0F0C0, 0);
|
|
#endif
|
|
|
|
displaystr(dx+125, dy, 0, vid.fsize, name, xthis ? 0xFFFF00 : 0x808080, 0);
|
|
}
|
|
|
|
void displayStatHelp(int y, string name) {
|
|
|
|
int dy = vid.fsize * y + vid.yres/4;
|
|
int dx = vid.xres/2 - 100;
|
|
|
|
displaystr(dx+100, dy, 0, vid.fsize, name, 0xC0C0C0, 0);
|
|
}
|
|
|
|
void displayButton(int x, int y, const string& name, int key, int align, int rad) {
|
|
if(displayfr(x, y, rad, vid.fsize, name, 0x808080, align)) {
|
|
displayfr(x, y, rad, vid.fsize, name, 0xFFFF00, align);
|
|
getcstat = key;
|
|
}
|
|
}
|
|
|
|
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);
|
|
if(canmove) displayButton(vid.xres/2, y + vid.fsize*16/2, XLAT("or another key to continue"), ' ', 8, 2);
|
|
else displayButton(vid.xres/2, y + vid.fsize*16/2, XLAT("or ESC to see how it ended"), SDLK_ESCAPE, 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() {
|
|
int y = vid.yres * (1000-618) / 1000 - vid.fsize * 7/2;
|
|
displayfr(vid.xres/2, y, 4, vid.fsize*2,
|
|
cheater ? XLAT("It is a shame to cheat!") :
|
|
showoff ? XLAT("Showoff mode") :
|
|
canmove && princess::challenge ? XLAT("%1 Challenge", moPrincess) :
|
|
canmove ? XLAT("Quest status") :
|
|
XLAT("GAME OVER"), 0xC00000, 8
|
|
);
|
|
displayfr(vid.xres/2, y + vid.fsize*2, 2, vid.fsize, XLAT("Your score: %1", its(gold())), 0xD0D0D0, 8);
|
|
displayfr(vid.xres/2, y + vid.fsize*3, 2, vid.fsize, XLAT("Enemies killed: %1", its(tkills())), 0xD0D0D0, 8);
|
|
|
|
if(!timerstopped && !canmove) {
|
|
savetime += time(NULL) - timerstart;
|
|
timerstopped = true;
|
|
}
|
|
if(canmove && !timerstart)
|
|
timerstart = time(NULL);
|
|
|
|
if(items[itOrbYendor]) {
|
|
displayfr(vid.xres/2, y + vid.fsize*4, 2, vid.fsize, XLAT("Orbs of Yendor found: %1", its(items[itOrbYendor])), 0xFF00FF, 8);
|
|
displayfr(vid.xres/2, y + vid.fsize*5, 2, vid.fsize, XLAT("CONGRATULATIONS!"), 0xFFFF00, 8);
|
|
}
|
|
else {
|
|
if(princess::challenge) ;
|
|
else if(gold() < 30)
|
|
displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Collect 30 $$$ to access more worlds"), 0xC0C0C0, 8);
|
|
else if(gold() < 60)
|
|
displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Collect 60 $$$ to access even more lands"), 0xC0C0C0, 8);
|
|
else if(!hellUnlocked())
|
|
displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Collect at least 10 treasures in each of 9 types to access Hell"), 0xC0C0C0, 8);
|
|
else if(items[itHell] < 10)
|
|
displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Collect at least 10 Demon Daisies to find the Orbs of Yendor"), 0xC0C0C0, 8);
|
|
else if(size(yendor::yi) == 0)
|
|
displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Look for the Orbs of Yendor in Hell or in the Crossroads!"), 0xC0C0C0, 8);
|
|
else
|
|
displayfr(vid.xres/2, y+vid.fsize*5, 2, vid.fsize, XLAT("Unlock the Orb of Yendor!"), 0xC0C0C0, 8);
|
|
}
|
|
if(princess::challenge)
|
|
displayfr(vid.xres/2, y+vid.fsize*6, 2, vid.fsize, XLAT("Follow the Mouse and escape with %the1!", moPrincess), 0xC0C0C0, 8);
|
|
else if(tkills() < 100)
|
|
displayfr(vid.xres/2, y+vid.fsize*6, 2, vid.fsize, XLAT("Defeat 100 enemies to access the Graveyard"), 0xC0C0C0, 8);
|
|
else if(kills[moVizier] == 0 && (items[itFernFlower] < 5 || items[itGold] < 5)) {
|
|
displayfr(vid.xres/2, y+vid.fsize*6, 2, vid.fsize, XLAT("Kill a Vizier in the Palace to access Emerald Mine"), 0xC0C0C0, 8);
|
|
}
|
|
else if(items[itEmerald] < 5)
|
|
displayfr(vid.xres/2, y+vid.fsize*6, 2, vid.fsize, XLAT("Collect 5 Emeralds to access Camelot"), 0xC0C0C0, 8);
|
|
else if(hellUnlocked()) {
|
|
bool b = true;
|
|
for(int i=0; i<LAND_HYP; i++)
|
|
if(b && items[treasureType(land_hyp[i])] < 10) {
|
|
displayfr(vid.xres/2, y+vid.fsize*6, 2, vid.fsize,
|
|
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]),
|
|
0xC0C0C0, 8);
|
|
b = false;
|
|
}
|
|
if(b)
|
|
displayfr(vid.xres/2, y+vid.fsize*6, 2, vid.fsize, XLAT("Hyperstone Quest completed!"), 0xC0C0C0, 8);
|
|
}
|
|
else {
|
|
displayfr(vid.xres/2, y+vid.fsize*6, 2, vid.fsize, XLAT("Some lands unlock at specific treasures or kills"), 0xC0C0C0, 8);
|
|
}
|
|
if((!canmove) && (!ISMOBILE))
|
|
displayfr(vid.xres/2, y+vid.fsize*7, 2, vid.fsize, XLAT("(press ESC during the game to review your quest)"), 0xB0B0B0, 8);
|
|
if(cheater)
|
|
displayfr(vid.xres/2, y+vid.fsize*9, 2, vid.fsize, XLAT("you have cheated %1 times", its(cheater)), 0xFF2020, 8);
|
|
if(!cheater) {
|
|
displayfr(vid.xres/2, y + vid.fsize*9, 2, vid.fsize, timeline(), 0xC0C0C0, 8);
|
|
}
|
|
|
|
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];
|
|
mnum++,
|
|
msgs.push_back(m);
|
|
}
|
|
|
|
if(mnum)
|
|
displayfr(vid.xres/2, vid.yres-vid.fsize*(mnum+1), 2, vid.fsize/2, XLAT("last messages:"), 0xC0C0C0, 8);
|
|
|
|
#ifndef MOBILE
|
|
quitOrAgain();
|
|
#endif
|
|
}
|
|
|
|
string ifMousing(string key, string s) {
|
|
if(mousing) return XLAT(s);
|
|
else return key + " - " + XLAT(s);
|
|
}
|
|
|
|
#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 x = vid.xcenter + px * (vid.radius);
|
|
int y = vid.ycenter + py * (vid.radius - siz/2);
|
|
if(gtouched && !mouseover
|
|
&& abs(mousex - vid.xcenter) < vid.radius
|
|
&& abs(mousey - vid.ycenter) < vid.radius
|
|
&& hypot(mousex-vid.xcenter, mousey-vid.ycenter) > vid.radius
|
|
&& px == (mousex > vid.xcenter ? 1 : -1)
|
|
&& py == (mousey > vid.ycenter ? 1 : -1)
|
|
) col = 0xFF0000;
|
|
if(displayfr(x, y, 0, siz, s, col, 8+8*px))
|
|
buttonclicked = true;
|
|
}
|
|
#endif
|
|
|
|
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]);
|
|
}
|
|
|
|
#ifndef ANDROID
|
|
void loadScores() {
|
|
scores.clear();
|
|
FILE *f = fopen(scorefile, "rt");
|
|
if(!f) {
|
|
printf("Could not open the score file '%s'!\n", scorefile);
|
|
addMessage(s0 + "Could not open the score file: " + scorefile);
|
|
return;
|
|
}
|
|
while(!feof(f)) {
|
|
char buf[120];
|
|
if(fgets(buf, 120, f) == NULL) break;
|
|
if(buf[0] == 'H' && buf[1] == 'y') {
|
|
score sc; bool ok = true;
|
|
if(fscanf(f, "%s", buf) <= 0) break; sc.ver = buf;
|
|
|
|
for(int i=0; i<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);
|
|
}
|
|
|
|
bool notgl = false;
|
|
|
|
void showPickScores() {
|
|
|
|
int d = scoredisplay;
|
|
|
|
vector<pair<string, int> > v;
|
|
for(int i=0; i<POSSCORE; i++) {
|
|
scoredisplay = i;
|
|
if(!fakescore())
|
|
v.push_back(make_pair(displayfor(NULL), i));
|
|
}
|
|
sort(v.begin(), v.end());
|
|
|
|
int q = (int) v.size();
|
|
int percolumn = vid.yres / (vid.fsize+3) - 4;
|
|
int columns = 1 + (q-1) / percolumn;
|
|
|
|
for(int i=0; i<q; i++) {
|
|
int x = 16 + (vid.xres * (i/percolumn)) / columns;
|
|
int y = (vid.fsize+3) * (i % percolumn) + vid.fsize*2;
|
|
|
|
scoredisplay = v[i].second;
|
|
if(!fakescore())
|
|
displayButton(x, y, v[i].first, char(33+scoredisplay), 0);
|
|
}
|
|
|
|
scoredisplay = d;
|
|
}
|
|
|
|
void showScores() {
|
|
int y = vid.fsize * 7/2;
|
|
int bx = vid.fsize;
|
|
|
|
string modes =
|
|
scoremode == 0 ? XLAT(", m - mode: normal") :
|
|
scoremode == 1 ? XLAT(", m - mode: shoot'em up") :
|
|
scoremode == 2 ? XLAT(", m - mode: hardcore only") :
|
|
"?";
|
|
|
|
if(euclid) modes += XLAT(" (E:%1)", euclidland);
|
|
|
|
|
|
mouseovers = XLAT("t/left/right - change display, up/down - scroll, s - sort by") + modes;
|
|
|
|
displaystr(bx*4, vid.fsize*2, 0, vid.fsize, "#", 0xFFFFFF, 16);
|
|
displaystr(bx*8, vid.fsize*2, 0, vid.fsize, "$$$", 0xFFFFFF, 16);
|
|
displaystr(bx*12, vid.fsize*2, 0, vid.fsize, XLAT("kills"), 0xFFFFFF, 16);
|
|
displaystr(bx*18, vid.fsize*2, 0, vid.fsize, XLAT("time"), 0xFFFFFF, 16);
|
|
displaystr(bx*22, vid.fsize*2, 0, vid.fsize, XLAT("ver"), 0xFFFFFF, 16);
|
|
displaystr(bx*23, vid.fsize*2, 0, vid.fsize, displayfor(NULL), 0xFFFFFF, 0);
|
|
if(scorefrom < 0) scorefrom = 0;
|
|
int id = scorefrom;
|
|
while(y < (ISIOS ? vid.yres - 3*vid.fsize : vid.yres)) {
|
|
if(id >= size(scores)) break;
|
|
|
|
score& S(scores[id]);
|
|
|
|
bool wrongtype = false;
|
|
|
|
wrongtype |= (euclid && (!S.box[116] || S.box[120] != euclidland));
|
|
wrongtype |= (!euclid && S.box[116]);
|
|
|
|
wrongtype |= (scoremode == 1 && !S.box[119]);
|
|
wrongtype |= (scoremode != 1 && S.box[119]);
|
|
wrongtype |= (scoremode == 2 && (!S.box[117] || S.box[118] >= PUREHARDCORE_LEVEL));
|
|
|
|
if(wrongtype) { id++; continue; }
|
|
|
|
char buf[16];
|
|
|
|
sprintf(buf, "%d", id+1);
|
|
displaystr(bx*4, y, 0, vid.fsize, buf, 0xC0C0C0, 16);
|
|
|
|
sprintf(buf, "%d", S.box[2]);
|
|
displaystr(bx*8, y, 0, vid.fsize, buf, 0xC0C0C0, 16);
|
|
|
|
sprintf(buf, "%d", S.box[3]);
|
|
displaystr(bx*12, y, 0, vid.fsize, buf, 0xC0C0C0, 16);
|
|
|
|
sprintf(buf, "%d:%02d", S.box[0]/60, S.box[0] % 60);
|
|
displaystr(bx*18, y, 0, vid.fsize, buf, 0xC0C0C0, 16);
|
|
|
|
displaystr(bx*22, y, 0, vid.fsize, S.ver, 0xC0C0C0, 16);
|
|
|
|
displaystr(bx*23, y, 0, vid.fsize, displayfor(&S), 0xC0C0C0, 0);
|
|
|
|
y += vid.fsize*5/4; id++;
|
|
}
|
|
|
|
#ifdef IOS
|
|
buttonclicked = false;
|
|
displayabutton(-1, +1, XLAT("SORT"), BTON);
|
|
displayabutton(+1, +1, XLAT("PLAY"), BTON);
|
|
#endif
|
|
}
|
|
|
|
void sortScores() {
|
|
if(scorerev) reverse(scores.begin(), scores.end());
|
|
else {
|
|
scorerev = true;
|
|
scoresort = scoredisplay;
|
|
stable_sort(scores.begin(), scores.end(), scorecompare);
|
|
}
|
|
}
|
|
|
|
void shiftScoreDisplay(int delta) {
|
|
scoredisplay = (scoredisplay + POSSCORE + delta) % POSSCORE, scorerev = false;
|
|
if(fakescore()) shiftScoreDisplay(delta);
|
|
}
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
void handleScoreKeys(int sym, SDL_Event& ev) {
|
|
if(sym == SDLK_LEFT || sym == SDLK_KP4 || sym == 'h' || sym == 'a')
|
|
shiftScoreDisplay(-1);
|
|
else if(sym == SDLK_RIGHT || sym == SDLK_KP6 || sym == 'l' || sym == 'd')
|
|
shiftScoreDisplay(1);
|
|
else if(sym == 't') cmode = emPickScores;
|
|
else if(sym == SDLK_UP || sym == 'k' || sym == 'w')
|
|
scorefrom -= 5;
|
|
else if(sym == SDLK_DOWN || sym == 'j' || sym == 'x')
|
|
scorefrom += 5;
|
|
else if(sym == 's') sortScores();
|
|
else if(sym == 'm') { scoremode++; scoremode %= 3; }
|
|
else if(sym != 0 || ev.type == SDL_MOUSEBUTTONDOWN) cmode = emNormal;
|
|
}
|
|
|
|
void handlePickScoreKeys(int uni, SDL_Event& ev) {
|
|
if(uni != 0) {
|
|
int k = uni - '!';
|
|
if(k >= 0 && k < POSSCORE) scoredisplay = k;
|
|
cmode = emScores;
|
|
scorerev = false;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void setAppropriateOverview() {
|
|
if(tactic::on)
|
|
cmode = emTactic;
|
|
else if(yendor::on)
|
|
cmode = emYendor;
|
|
else if(euclid)
|
|
cmode = emPickEuclidean;
|
|
else
|
|
cmode = emOverview;
|
|
}
|
|
|
|
void drawStats() {
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"drawStats\n"));
|
|
|
|
#ifdef IOS
|
|
if(cmode != emNormal && cmode != emQuit)
|
|
return;
|
|
#endif
|
|
|
|
int vx, vy;
|
|
|
|
int s = vid.fsize;
|
|
|
|
bool portrait = vid.xres < vid.yres;
|
|
|
|
if(portrait) {
|
|
vx = vid.fsize * 3;
|
|
vy = vid.fsize * 2;
|
|
if(havebugs) vy += vid.fsize * 3/2;
|
|
}
|
|
else {
|
|
vx = vid.xres - vid.fsize * 3;
|
|
vy = vid.fsize;
|
|
}
|
|
|
|
#define ADV(z) \
|
|
{if(portrait) { \
|
|
vx += vid.fsize*4; \
|
|
if(vx > vid.xres - vid.fsize*2) vx = vid.fsize * 3, vy += vid.fsize; \
|
|
} \
|
|
else { \
|
|
vy += vid.fsize * z/2; \
|
|
if(vy > vid.yres || (vx > vid.xres/2 && vy > vid.yres - s * 3)) vx += (vx > vid.xres/2 ? -5:5) * vid.fsize, vy = vid.fsize * 5/2; \
|
|
}}
|
|
|
|
if(!portrait) vid.portreduction = 0;
|
|
if(vid.xres >= vid.yres * 5/3 || portrait)
|
|
vid.killreduction = vid.itemreduction = 0;
|
|
|
|
if(portrait) vid.fsize = s - vid.portreduction;
|
|
else vid.fsize = s - vid.itemreduction;
|
|
|
|
if(displaynum(vx, vy, 0, vid.fsize, 0xFFFFFF, gold(), "$$$")) {
|
|
mouseovers = XLAT("Your total wealth"),
|
|
help = 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"
|
|
);
|
|
}
|
|
|
|
ADV(3); if(portrait) {ADV(3);}
|
|
|
|
#ifndef MOBILE
|
|
int oldz = 0;
|
|
#endif
|
|
|
|
for(int z=0; z<3; z++) for(int i=0; i<ittypes; i++) if(itemclass(eItem(i)) == z) {
|
|
#ifndef MOBILE
|
|
if(z != oldz) { ADV(1); oldz = z; }
|
|
#endif
|
|
if(items[i]) {
|
|
bool b = displaynum(vx, vy, 0, vid.fsize, iinf[i].color, items[i], s0 + iinf[i].glyph);
|
|
ADV(2);
|
|
if(b) {
|
|
int t = itemclass(eItem(i));
|
|
if(t == IC_TREASURE)
|
|
mouseovers = XLAT("treasure collected: %1", eItem(i));
|
|
if(t == IC_OTHER)
|
|
mouseovers = XLAT("objects found: %1", eItem(i));
|
|
if(t == IC_ORB)
|
|
mouseovers = XLAT("orb power: %1", eItem(i));
|
|
if(i == itGreenStone) {
|
|
mouseovers += XLAT(" (click to drop)");
|
|
getcstat = 'g';
|
|
}
|
|
help = generateHelpForItem(eItem(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
// printf("vx = %d/%d fsize = %d\n", vx, vid.xres - vid.fsize*4, vid.fsize);
|
|
if(vx < vid.xres - s * 3)
|
|
vid.itemreduction++;
|
|
|
|
if(portrait) {
|
|
vx = vid.fsize * 3;
|
|
vy += vid.fsize * 2; // vy = vid.fsize * 5;
|
|
}
|
|
else {
|
|
vx = vid.fsize * 3;
|
|
vy = vid.fsize;
|
|
vid.fsize = s;
|
|
}
|
|
|
|
if(displaynum(vx, vy, 0, vid.fsize, 0xFFFFFF, calcfps(), "fps"))
|
|
mouseovers = XLAT("frames per second"),
|
|
help =
|
|
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 ANDROID
|
|
XLAT(
|
|
"(Menu button) and select the ASCII mode, which runs much faster. "
|
|
"Depending on your device, turning the OpenGL mode on or off might "
|
|
"make it faster, slower, or cause glitches.");
|
|
#else
|
|
#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
|
|
// todo: iOS
|
|
#endif
|
|
ADV(3);
|
|
|
|
int killtypes = 0;
|
|
for(int i=1; i<motypes; i++) if(kills[i]) killtypes++;
|
|
|
|
if(killtypes >= 3) {
|
|
int kvx, kvy;
|
|
if(!portrait)
|
|
kvx = vid.fsize * 8, kvy = vid.fsize;
|
|
else { ADV(3); kvx=vx, kvy=vy; }
|
|
if(displaynum(kvx, kvy, 0, vid.fsize, 0xFFFFFF, tkills(), "XX"))
|
|
mouseovers = XLAT("Your total kills"),
|
|
help = XLAT(
|
|
"In most lands, more treasures are generated with each enemy native to this land you kill. "
|
|
"Moreover, 100 kills is a requirement to enter the Graveyard and the Hive.\n\n"
|
|
"Friendly creatures and parts of monsters (such as the Ivy) do appear in the list, "
|
|
"but are not counted in the total kill count.");
|
|
if(portrait) ADV(6);
|
|
}
|
|
|
|
if(!portrait) vid.fsize = s - vid.killreduction;
|
|
|
|
for(int i=1; i<motypes; i++) if(kills[i]) {
|
|
eMonster m = eMonster(i);
|
|
|
|
if(displaynum(vx, vy, 0, vid.fsize, minf[i].color, kills[i], s0 + minf[i].glyph)) {
|
|
help = generateHelpForMonster(m);
|
|
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);
|
|
}
|
|
ADV(2);
|
|
}
|
|
|
|
if(!portrait && vx > s * 3) vid.killreduction++;
|
|
if(portrait && vy > vid.ycenter - vid.radius && vid.fsize > 1) vid.portreduction ++;
|
|
vid.fsize = s;
|
|
|
|
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
|
|
|
|
void saveHighQualityShot() {
|
|
|
|
#ifndef GFX
|
|
addMessage(XLAT("High quality shots not available on this platform"));
|
|
return;
|
|
#endif
|
|
|
|
int dcs = size(dcal);
|
|
for(int i=0; i<dcs; i++) {
|
|
cell *c = dcal[i];
|
|
if(c->cpdist <= 4) setdist(c, 1, NULL);
|
|
}
|
|
|
|
time_t timer;
|
|
timer = time(NULL);
|
|
|
|
SDL_Surface *sav = s;
|
|
s = SDL_CreateRGBSurface(SDL_SWSURFACE,2000,2000,32,0,0,0,0);
|
|
|
|
int ssr = sightrange; sightrange = 10; int sch = cheater; cheater = 0;
|
|
|
|
bool b = vid.usingGL;
|
|
vid.usingGL = false;
|
|
videopar vid2 = vid;
|
|
vid.xres = vid.yres = 2000;
|
|
// if(vid.pmodel == 0) vid.scale = 0.99;
|
|
calcparam();
|
|
inHighQual = true;
|
|
|
|
darken = 0;
|
|
|
|
for(int i=0; i<2; i++) {
|
|
SDL_FillRect(s, NULL, i ? 0xFFFFFF : 0);
|
|
drawfullmap();
|
|
|
|
char buf[128]; strftime(buf, 128, "bigshota-%y%m%d-%H%M%S" IMAGEEXT, localtime(&timer));
|
|
buf[7] += i;
|
|
IMAGESAVE(s, buf);
|
|
|
|
if(i == 0) addMessage(XLAT("Saved the high quality shot to %1", buf));
|
|
}
|
|
|
|
inHighQual = false;
|
|
SDL_FreeSurface(s); s = sav; vid = vid2; sightrange = ssr; cheater = sch;
|
|
vid.usingGL = b;
|
|
}
|
|
#endif
|
|
|
|
void drawfullmap() {
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"draw full map\n"));
|
|
|
|
ptds.clear();
|
|
|
|
if(!vid.goteyes && !euclid && pmodel == 0) {
|
|
queuecircle(vid.xcenter, vid.ycenter, vid.radius, 0xFF >> darken);
|
|
}
|
|
|
|
if(pmodel == 3 || pmodel == 4) polygonal::drawBoundary(0xFF >> darken);
|
|
|
|
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);
|
|
}
|
|
|
|
drawthemap();
|
|
#ifndef MOBILE
|
|
if(!inHighQual) {
|
|
if(cmode == emNormal && !rug::rugged) drawmovestar();
|
|
if(rug::rugged && !rug::renderonce) queueline(C0, mouseh, 0xFF00FF);
|
|
mapeditor::drawGrid();
|
|
}
|
|
#endif
|
|
drawqueue();
|
|
}
|
|
|
|
#include "menus.cpp"
|
|
|
|
void drawscreen() {
|
|
|
|
DEBB(DF_GRAPH, (debugfile,"drawscreen\n"));
|
|
|
|
calcparam();
|
|
#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, 0);
|
|
#endif
|
|
|
|
if(!canmove) darken = 1;
|
|
if(cmode != emNormal && cmode != emDraw && cmode != emCustomizeChar) darken = 2;
|
|
if(cmode == emQuit && !canmove) darken = 0;
|
|
if(cmode == emOverview) darken = 4;
|
|
|
|
#ifdef MOBILE
|
|
if(cmode == emQuit) darken = 1;
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
if(cmode == emMapEditor && !mapeditor::subscreen && !mapeditor::choosefile) darken = 0;
|
|
if(cmode == emDraw && mapeditor::choosefile) darken = 2;
|
|
#endif
|
|
if(hiliteclick && darken == 0 && vid.monmode == 2) darken = 1;
|
|
if(cmode == emProgress) darken = 0;
|
|
|
|
if(conformal::includeHistory && cmode != emProgress) conformal::restore();
|
|
|
|
if(rug::rugged) {
|
|
#ifndef MOBILE
|
|
rug::actDraw();
|
|
#endif
|
|
}
|
|
else drawfullmap();
|
|
|
|
if(conformal::includeHistory && cmode != emProgress) conformal::restoreBack();
|
|
|
|
getcstat = 0;
|
|
|
|
if(cmode == emNormal || cmode == emQuit) drawStats();
|
|
|
|
#ifdef MOBILE
|
|
|
|
buttonclicked = false;
|
|
|
|
if(cmode == emNormal) {
|
|
displayabutton(-1, -1, XLAT("MOVE"), andmode == 0 ? BTON : BTOFF);
|
|
displayabutton(+1, -1, XLAT(andmode == 1 ? "BACK" : "DRAG"), andmode == 1 ? BTON : BTOFF);
|
|
displayabutton(-1, +1, XLAT("INFO"), andmode == 2 ? BTON : BTOFF);
|
|
displayabutton(+1, +1, XLAT("MENU"), andmode == 3 ? BTON : BTOFF);
|
|
}
|
|
|
|
if(cmode == emQuit) {
|
|
displayabutton(-1, +1, XLAT("NEW"), BTON);
|
|
displayabutton(+1, -1, XLAT(canmove ? "PLAY" : ISIOS ? " " : "SHARE"), BTON);
|
|
displayabutton(+1, +1, XLAT("MENU"), BTON);
|
|
}
|
|
|
|
#endif
|
|
|
|
// displaynum(vx,100, 0, 24, 0xc0c0c0, celldist(cwt.c), ":");
|
|
|
|
darken = 0;
|
|
drawmessages();
|
|
|
|
if(cmode == emNormal) {
|
|
#ifdef MOBILE
|
|
if(!canmove) cmode = emQuit;
|
|
#endif
|
|
if(!canmove) showGameover();
|
|
#ifndef MOBILE
|
|
// if(!canmove)
|
|
// displayButton(vid.xres-8, vid.yres-vid.fsize*2, XLAT("ESC for menu/quest"), SDLK_ESCAPE, 16);
|
|
#endif
|
|
}
|
|
|
|
#ifndef ANDROID
|
|
if(cmode == emProgress) mouseovers = "";
|
|
#endif
|
|
|
|
displayMenus();
|
|
|
|
describeMouseover();
|
|
|
|
if(havebugs && darken == 0) 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);
|
|
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[itOrbGhost] && darken == 0) {
|
|
string s;
|
|
if(tmines > 7) tmines = 7;
|
|
int col = minecolors[tmines];
|
|
|
|
if(tmines == 7) seenSevenMines = true;
|
|
|
|
for(int p=0; p<numplayers(); 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 || ISMOBILE) showGameover();
|
|
}
|
|
|
|
#ifndef ANDROID
|
|
if(cmode == emHelp) {
|
|
int last = 0;
|
|
int lastspace = 0;
|
|
int siz = vid.fsize;
|
|
|
|
if(help == "@") help = buildHelpText();
|
|
|
|
if(ISIOS && size(help) >= 500) siz = (siz * 9)/10;
|
|
else if(ISIOS) siz = (siz * 3+1)/2;
|
|
else if(size(help) >= 500) siz = siz * 2/3;
|
|
|
|
int vy = vid.fsize * 4;
|
|
|
|
int xs = vid.xres * 618/1000;
|
|
int xo = vid.xres * 186/1000;
|
|
|
|
for(int i=0; i<=size(help); i++) {
|
|
int ls = 0;
|
|
int prev = last;
|
|
if(help[i] == ' ') lastspace = i;
|
|
if(textwidth(siz, help.substr(last, i-last)) > xs) {
|
|
if(lastspace == last) ls = i-1, last = i-1;
|
|
else ls = lastspace, last = ls+1;
|
|
}
|
|
if(help[i] == 10 || i == size(help)) ls = i, last = i+1;
|
|
if(ls) {
|
|
displayfr(xo, vy, 2, siz, help.substr(prev, ls-prev), 0xC0C0C0, 0);
|
|
if(ls == prev) vy += siz/2;
|
|
else vy += siz;
|
|
lastspace = last;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
// DEB
|
|
if(mouseover && targetclick) {
|
|
shmup::cpid = 0;
|
|
eItem i = targetRangedOrb(mouseover, roCheck);
|
|
if(i == itOrbSummon) {
|
|
eMonster m = summonedAt(mouseover);
|
|
displaychr(mousex, mousey, 0, vid.fsize, minf[m].glyph, minf[m].color);
|
|
}
|
|
else if(i)
|
|
displaychr(mousex, mousey, 0, vid.fsize, '@', iinf[i].color);
|
|
}
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
// SDL_UnlockSurface(s);
|
|
|
|
//profile("swapbuffers");
|
|
#ifdef GL
|
|
if(vid.usingGL) SDL_GL_SwapBuffers(); else
|
|
#endif
|
|
SDL_UpdateRect(s, 0, 0, vid.xres, vid.yres);
|
|
|
|
//printf("\ec");
|
|
|
|
#endif
|
|
|
|
//profile("centerpc");
|
|
if(playermoved && vid.aspeed > 4.99 && !shmup::on) {
|
|
centerpc(1000);
|
|
playermoved = false;
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
bool setfsize = false;
|
|
|
|
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"));
|
|
if(euclid) {
|
|
centerover = euclideanAtCreate(0,0);
|
|
}
|
|
else {
|
|
viewctr.h = &origin;
|
|
viewctr.spin = 0;
|
|
}
|
|
View = Id;
|
|
webdisplay = 0;
|
|
}
|
|
|
|
void initcs(charstyle &cs) {
|
|
cs.charid = 0;
|
|
cs.skincolor = 0xD0D0D0FF;
|
|
cs.haircolor = 0x686868FF;
|
|
cs.dresscolor = 0xC00000FF;
|
|
cs.swordcolor = 0xD0D0D0FF;
|
|
cs.dresscolor2= 0x8080FFC0;
|
|
}
|
|
|
|
#ifndef ANDROID
|
|
|
|
void savecs(FILE *f, charstyle& cs) {
|
|
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);
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
void loadcs(FILE *f, charstyle& cs) {
|
|
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))
|
|
;
|
|
}
|
|
|
|
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.aspeed));
|
|
fprintf(f, "%d %d %d %d %d %d %d\n", vid.wallmode, vid.monmode, vid.axes, audiovolume, vid.framelimit, vid.usingGL, vid.usingAA);
|
|
fprintf(f, "%d %d %d %lf %d %d\n", vid.joyvalue, vid.joyvalue2, vid.joypanthreshold, vid.joypanspeed, autojoy, vid.flashtime);
|
|
|
|
savecs(f, vid.cs);
|
|
|
|
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 %lf %d %d %lf\n",
|
|
pmodel, polygonal::SI, polygonal::STAR, polygonal::deg,
|
|
conformal::includeHistory, 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\nThis is a configuration file for HyperRogue (version " VER ")\n");
|
|
fprintf(f, "\n\nThe numbers are:\n");
|
|
fprintf(f, "screen width & height, fullscreen mode (0=windowed, 1=fullscreen), font size\n");
|
|
fprintf(f, "scale, eye distance, parameter, animation speed\n");
|
|
fprintf(f, "wallmode, monster mode, cross mode, audiovolume, framerate limit, usingGL, usingAA\n");
|
|
fprintf(f, "calibrate first joystick (threshold A, threshold B), calibrate second joystick (pan threshold, pan speed), joy mode\n");
|
|
fprintf(f, "gender (1=female, 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, 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");
|
|
|
|
fclose(f);
|
|
#ifndef MOBILE
|
|
addMessage(s0 + "Configuration saved to: " + conffile);
|
|
#else
|
|
addMessage(s0 + "Configuration saved");
|
|
#endif
|
|
}
|
|
|
|
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.aspeed = d;
|
|
}
|
|
err=fscanf(f, "%d%d%d%d%d%d%d", &vid.wallmode, &vid.monmode, &vid.axes, &audiovolume, &vid.framelimit, &gl, &aa);
|
|
vid.usingGL = gl; vid.usingAA = aa;
|
|
err=fscanf(f, "%d%d%d%f%d%d", &vid.joyvalue, &vid.joyvalue2, &vid.joypanthreshold, &vid.joypanspeed, &aa, &vid.flashtime);
|
|
autojoy = aa; aa = 0;
|
|
|
|
loadcs(f, vid.cs);
|
|
|
|
aa=0; bb=0;
|
|
err=fscanf(f, "%d%d", &aa, &bb);
|
|
vid.darkhepta = aa; vid.shifttarget = bb;
|
|
|
|
aa = euclid; bb = euclidland; cc = shmup::on; dd = hardcore;
|
|
err=fscanf(f, "%d%d%d%d", &aa, &bb, &cc, &dd);
|
|
euclid = 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;
|
|
err=fscanf(f, "%d%d%d%d%lf%d%d", &aa, &bb, &rug::texturesize, &cc, &rug::scale, &ee, &dd);
|
|
rug::renderonce = aa; rug::rendernogl = bb; purehepta = cc; chaosmode = dd; vid.steamscore = ee;
|
|
|
|
aa=conformal::includeHistory;
|
|
err=fscanf(f, "%d%d%lf%d%d%lf",
|
|
&pmodel, &polygonal::SI, &polygonal::STAR, &polygonal::deg,
|
|
&aa, &conformal::lvspeed);
|
|
conformal::includeHistory = aa;
|
|
|
|
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);
|
|
}
|
|
|
|
fclose(f);
|
|
DEBB(DF_INIT, (debugfile,"Loaded configuration: %s\n", conffile));
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
if(clWidth) vid.xres = clWidth;
|
|
if(clHeight) vid.yres = clHeight;
|
|
if(clFont) vid.fsize = clFont;
|
|
|
|
for(int k=0; k<int(commandline.size()); k++) switch(commandline[k]) {
|
|
case 'f': vid.full = true; break;
|
|
case 'w': vid.full = false; break;
|
|
case 'e': vid.wallmode = 3; vid.monmode = 2; break;
|
|
case 'p': vid.wallmode = 2; vid.monmode = 2; break;
|
|
case 'a': vid.wallmode = 0; vid.monmode = 0; break;
|
|
case 'o': vid.usingGL = !vid.usingGL; break;
|
|
case char(200): vid.usingGL = false; break;
|
|
case char(201): vid.usingGL = true; break;
|
|
case 'O': vid.usingGL = false; break;
|
|
case 'E': euclid = !euclid; if(euclid) euclidland = firstland; break;
|
|
case 'S': shmup::on = !shmup::on; break;
|
|
case 'P': k++; vid.scfg.players = commandline[k]-'0'; break;
|
|
case 'H': hardcore = !hardcore; break;
|
|
case '7': purehepta = !purehepta; break;
|
|
case 'C': chaosmode = !chaosmode; break;
|
|
case 'T': tactic::on = !tactic::on; break;
|
|
case 'R': randomPatternsMode = !randomPatternsMode; break;
|
|
case 'D':
|
|
randomPatternsMode = false;
|
|
tactic::on = false;
|
|
hardcore = false;
|
|
vid.scfg.players = 1;
|
|
euclid = false;
|
|
shmup::on = false;
|
|
break;
|
|
}
|
|
#endif
|
|
precalc();
|
|
}
|
|
#endif
|
|
|
|
#ifndef MOBILE
|
|
string musfname[landtypes];
|
|
|
|
bool loadMusicInfo(string dir) {
|
|
DEBB(DF_INIT, (debugfile,"load music info\n"));
|
|
if(dir == "") return false;
|
|
FILE *f = fopen(dir.c_str(), "rt");
|
|
if(f) {
|
|
string dir2;
|
|
for(int i=0; i<size(dir); i++) if(dir[i] == '/' || dir[i] == '\\')
|
|
dir2 = dir.substr(0, i+1);
|
|
char buf[1000];
|
|
while(fgets(buf, 800, f) > 0) {
|
|
for(int i=0; buf[i]; i++) if(buf[i] == 10 || buf[i] == 13) buf[i] = 0;
|
|
if(buf[0] == '[' && buf[3] == ']') {
|
|
int id = (buf[1] - '0') * 10 + buf[2] - '0';
|
|
if(id >= 0 && id < landtypes) {
|
|
if(buf[5] == '*' && buf[6] == '/') musfname[id] = dir2 + (buf+7);
|
|
else musfname[id] = buf+5;
|
|
}
|
|
else {
|
|
fprintf(stderr, "warning: bad soundtrack id, use the following format:\n");
|
|
fprintf(stderr, "[##] */filename\n");
|
|
fprintf(stderr, "where ## are two digits, and */ is optional and replaced by path to the music\n");
|
|
fprintf(stderr, "alternatively LAST = reuse the last track instead of having a special one");
|
|
}
|
|
// printf("[%2d] %s\n", id, buf);
|
|
}
|
|
else if(buf[0] == '#') {
|
|
}
|
|
else {
|
|
musiclicense += buf;
|
|
musiclicense += "\n";
|
|
}
|
|
}
|
|
fclose(f);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#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.aspeed = 0;
|
|
vid.eye = 0;
|
|
vid.full = false;
|
|
vid.quick = true;
|
|
vid.wallmode = 3;
|
|
|
|
vid.joyvalue = 4800;
|
|
vid.joyvalue2 = 5600;
|
|
vid.joypanthreshold = 2500;
|
|
#ifdef PANDORA
|
|
vid.joypanspeed = 0.0001;
|
|
#else
|
|
vid.joypanspeed = 0;
|
|
#endif
|
|
|
|
vid.framelimit = 75;
|
|
vid.monmode = 2;
|
|
vid.axes = 1;
|
|
vid.steamscore = true;
|
|
|
|
initcs(vid.cs);
|
|
|
|
vid.killreduction = 0;
|
|
|
|
vid.samegender = false;
|
|
vid.language = -1;
|
|
|
|
joyx = joyy = 0; joydir.d = -1;
|
|
|
|
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);
|
|
}
|
|
const SDL_VideoInfo *inf = SDL_GetVideoInfo();
|
|
vid.xscr = vid.xres = inf->current_w;
|
|
vid.yscr = vid.yres = inf->current_h;
|
|
|
|
SDL_WM_SetCaption("HyperRogue " VER, "HyperRogue " VER);
|
|
|
|
loadConfig();
|
|
|
|
setvideomode();
|
|
if(!s) {
|
|
printf("Failed to initialize graphics.\n");
|
|
exit(2);
|
|
}
|
|
|
|
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
|
|
SDL_EnableUNICODE(1);
|
|
|
|
if(TTF_Init() != 0) {
|
|
printf("Failed to initialize TTF.\n");
|
|
exit(2);
|
|
}
|
|
|
|
initJoysticks();
|
|
|
|
#ifdef AUDIO
|
|
|
|
audio =
|
|
loadMusicInfo(musicfile)
|
|
|| loadMusicInfo("./hyperrogue-music.txt")
|
|
|| loadMusicInfo("music/hyperrogue-music.txt")
|
|
// Destination set by ./configure (in the GitHub repository)
|
|
#ifdef MUSICDESTDIR
|
|
|| loadMusicInfo(MUSICDESTDIR)
|
|
#endif
|
|
#ifdef FHS
|
|
|| loadMusicInfo("/usr/share/hyperrogue/hyperrogue-music.txt")
|
|
|| loadMusicInfo(s0 + getenv("HOME") + "/.hyperrogue-music.txt")
|
|
#endif
|
|
;
|
|
|
|
if(audio) {
|
|
if(Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 4096) != 0) {
|
|
fprintf(stderr, "Unable to initialize audio: %s\n", Mix_GetError());
|
|
audio = false;
|
|
}
|
|
else {
|
|
audio = true;
|
|
Mix_AllocateChannels(4);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
|
|
int frames;
|
|
bool outoffocus = false;
|
|
|
|
#ifdef AUDIO
|
|
|
|
bool loaded[landtypes];
|
|
Mix_Music* music[landtypes];
|
|
int musicpos[landtypes];
|
|
int musstart;
|
|
int musfadeval = 2000;
|
|
|
|
eLand cid = laNone;
|
|
|
|
void handlemusic() {
|
|
DEBB(DF_GRAPH, (debugfile,"handle music\n"));
|
|
if(audio && audiovolume) {
|
|
eLand id = cwt.c->land;
|
|
if(isHaunted(id)) id = laHaunted;
|
|
if(id == laGridSea) id = laGridCoast;
|
|
#ifdef LOCAL
|
|
extern bool local_changemusic(eLand& id);
|
|
if(local_changemusic(id)) return;
|
|
#endif
|
|
if(outoffocus) id = eLand(0);
|
|
if(musfname[id] == "LAST") id = cid;
|
|
if(!loaded[id]) {
|
|
loaded[id] = true;
|
|
// printf("loading (%d)> %s\n", id, musfname[id].c_str());
|
|
if(musfname[id] != "") {
|
|
music[id] = Mix_LoadMUS(musfname[id].c_str());
|
|
if(!music[id]) {
|
|
printf("Mix_LoadMUS: %s\n", Mix_GetError());
|
|
}
|
|
}
|
|
}
|
|
if(cid != id && !musfadeval) {
|
|
musicpos[cid] = SDL_GetTicks() - musstart;
|
|
musfadeval = musicpos[id] ? 500 : 2000;
|
|
Mix_FadeOutMusic(musfadeval);
|
|
// printf("fadeout %d, pos %d\n", musfadeval, musicpos[cid]);
|
|
}
|
|
if(music[id] && !Mix_PlayingMusic()) {
|
|
cid = id;
|
|
Mix_VolumeMusic(audiovolume);
|
|
Mix_FadeInMusicPos(music[id], -1, musfadeval, musicpos[id] / 1000.0);
|
|
// printf("fadein %d, pos %d\n", musfadeval, musicpos[cid]);
|
|
musstart = SDL_GetTicks() - musicpos[id];
|
|
musicpos[id] = 0;
|
|
musfadeval = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void resetmusic() {
|
|
if(audio && audiovolume) {
|
|
Mix_FadeOutMusic(3000);
|
|
cid = laNone;
|
|
for(int i=0; i<landtypes; i++) musicpos[i] = 0;
|
|
musfadeval = 2000;
|
|
}
|
|
}
|
|
|
|
#else
|
|
void resetmusic() {}
|
|
#endif
|
|
|
|
void panning(hyperpoint hf, hyperpoint ht) {
|
|
View =
|
|
rgpushxto0(hf) * rgpushxto0(gpushxto0(hf) * ht) * gpushxto0(hf) * View;
|
|
playermoved = false;
|
|
}
|
|
|
|
#ifdef LOCAL
|
|
#include "local.cpp"
|
|
#endif
|
|
|
|
void resetview() {
|
|
DEBB(DF_GRAPH, (debugfile,"reset view\n"));
|
|
View = Id;
|
|
// EUCLIDEAN
|
|
if(!euclid) viewctr.h = cwt.c->master;
|
|
else centerover = cwt.c;
|
|
// SDL_LockSurface(s);
|
|
// SDL_UnlockSurface(s);
|
|
}
|
|
|
|
void fullcenter() {
|
|
if(playerfound && false) centerpc(INF);
|
|
else {
|
|
resetview();
|
|
drawthemap();
|
|
centerpc(INF);
|
|
}
|
|
playermoved = true;
|
|
}
|
|
|
|
#ifndef MOBILE
|
|
|
|
// Warning: a very long function! todo: refactor
|
|
|
|
void mainloop() {
|
|
int lastt = 0;
|
|
cmode = emNormal;
|
|
while(true) {
|
|
|
|
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(shmup::on && cmode == emNormal)
|
|
timetowait = 0, shmup::turn(ticks - lastt);
|
|
|
|
if(timetowait > 0)
|
|
SDL_Delay(timetowait);
|
|
else {
|
|
if(playermoved && vid.aspeed > -4.99 && !outoffocus)
|
|
centerpc((ticks - lastt) / 1000.0 * exp(vid.aspeed));
|
|
if(panjoyx || panjoyy)
|
|
checkpanjoy((ticks - lastt) / 1000.0);
|
|
tortoise::updateVals(ticks - lastt);
|
|
lastt = ticks;
|
|
frames++;
|
|
if(!outoffocus) {
|
|
drawscreen();
|
|
}
|
|
}
|
|
|
|
Uint8 *keystate = SDL_GetKeyState(NULL);
|
|
rightclick = keystate[SDLK_RCTRL];
|
|
leftclick = keystate[SDLK_RSHIFT];
|
|
hiliteclick = keystate[SDLK_LALT] | keystate[SDLK_RALT];
|
|
anyshiftclick = keystate[SDLK_LSHIFT] | keystate[SDLK_RSHIFT];
|
|
|
|
bool didsomething = false;
|
|
|
|
if(vid.shifttarget) {
|
|
leftclick = false;
|
|
targetclick = keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT];
|
|
}
|
|
else {
|
|
leftclick = keystate[SDLK_RSHIFT];
|
|
targetclick = true;
|
|
}
|
|
|
|
#ifdef AUDIO
|
|
if(audio) handlemusic();
|
|
#endif
|
|
SDL_Event ev;
|
|
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) {
|
|
flashMessages();
|
|
if(ev.jaxis.value != 0)
|
|
printf("which = %d axis = %d value = %d\n",
|
|
ev.jaxis.which,
|
|
ev.jaxis.axis,
|
|
ev.jaxis.value);
|
|
if(ev.jaxis.which == 0) {
|
|
if(ev.jaxis.axis == 0)
|
|
joyx = ev.jaxis.value;
|
|
else if(ev.jaxis.axis == 1)
|
|
joyy = ev.jaxis.value;
|
|
else if(ev.jaxis.axis == 3)
|
|
panjoyx = ev.jaxis.value;
|
|
else if(ev.jaxis.axis == 4)
|
|
panjoyy = ev.jaxis.value;
|
|
checkjoy();
|
|
// printf("panjoy = %d,%d\n", panjoyx, panjoyy);
|
|
}
|
|
else {
|
|
if(ev.jaxis.axis == 0)
|
|
panjoyx = ev.jaxis.value;
|
|
else
|
|
panjoyy = ev.jaxis.value;
|
|
}
|
|
}
|
|
|
|
if(ev.type == SDL_JOYBUTTONDOWN && 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 && !shmup::on) {
|
|
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;
|
|
}
|
|
|
|
if(ev.type == SDL_MOUSEBUTTONDOWN) {
|
|
flashMessages();
|
|
mousepressed = true;
|
|
mousing = true;
|
|
if(ev.button.button==SDL_BUTTON_RIGHT || leftclick)
|
|
sym = SDLK_F1;
|
|
else if(ev.button.button==SDL_BUTTON_MIDDLE || rightclick)
|
|
sym = 1, didsomething = true;
|
|
else if(ev.button.button==SDL_BUTTON_WHEELUP && ((cmode == emQuit) ^ !canmove)) {
|
|
sym = 1; msgscroll++; didsomething = true;
|
|
}
|
|
else if(ev.button.button==SDL_BUTTON_WHEELDOWN && ((cmode == emQuit) ^ !canmove)) {
|
|
sym = 1; msgscroll--; didsomething = true;
|
|
}
|
|
else if(ev.button.button==SDL_BUTTON_WHEELUP) {
|
|
ld jx = (mousex - vid.xcenter - .0) / vid.radius / 10;
|
|
ld jy = (mousey - vid.ycenter - .0) / vid.radius / 10;
|
|
playermoved = false;
|
|
View = gpushxto0(hpxy(jx, jy)) * View;
|
|
sym = 1;
|
|
}
|
|
else {
|
|
sym = getcstat, uni = getcstat, shiftmul = getcshift;
|
|
}
|
|
}
|
|
|
|
if(ev.type == SDL_MOUSEBUTTONUP)
|
|
mousepressed = false;
|
|
|
|
if(((cmode == emNormal && canmove) || (cmode == emQuit && !canmove) || cmode == emDraw || cmode == emMapEditor) && !shmup::on && !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/21*shiftmul) * View, didsomething = true;
|
|
}
|
|
if(sym == SDLK_PAGEDOWN)
|
|
View = spin(-M_PI/21*shiftmul) * View, didsomething = true;
|
|
|
|
if(sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN)
|
|
if(isGravityLand(cwt.c->land)) playermoved = false;
|
|
}
|
|
|
|
if(ev.type == SDL_MOUSEMOTION) {
|
|
hyperpoint mouseoh = mouseh;
|
|
|
|
mousing = true;
|
|
mousex = ev.motion.x;
|
|
mousey = ev.motion.y;
|
|
if(rug::rugged)
|
|
mouseh = rug::gethyper(mousex, mousey);
|
|
else
|
|
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(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));
|
|
}
|
|
|
|
if(cmode == emNormal) {
|
|
|
|
if(cheater) {
|
|
if(applyCheat(uni, mouseover))
|
|
sym = 0;
|
|
}
|
|
|
|
if(!(uni >= 'A' && uni <= 'Z') && !shmup::on) {
|
|
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(!shmup::on) {
|
|
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(!shmup::on) {
|
|
if(sym == '.' || sym == 's') movepcto(-1, 1);
|
|
if((sym == SDLK_DELETE || sym == SDLK_KP_PERIOD || sym == 'g') && uni != 'G' && uni != 'G'-64)
|
|
movepcto(-2, 1);
|
|
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 && !shmup::on) movepcto(-1, 1);
|
|
|
|
// if(sym == SDLK_F4) restartGameSwitchEuclid();
|
|
|
|
if(sym == SDLK_F5) restartGame();
|
|
if(sym == SDLK_ESCAPE) {
|
|
cmode = emQuit;
|
|
achievement_final(false);
|
|
if(!canmove) {
|
|
addMessage(XLAT("GAME OVER"));
|
|
addMessage(timeline());
|
|
}
|
|
msgscroll = 0;
|
|
}
|
|
if(sym == SDLK_F10) return;
|
|
|
|
if(!canmove) {
|
|
if(sym == SDLK_RETURN) return;
|
|
else if(uni == 'r') restartGame();
|
|
else if(uni == 't') {
|
|
restartGame();
|
|
loadScores();
|
|
}
|
|
else if(rug::rugged) ;
|
|
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 == ' ' && !shmup::on))
|
|
fullcenter();
|
|
|
|
/* if(sym == SDLK_F6) {
|
|
View = spin(M_PI/2) * inverse(cwtV) * View;
|
|
if(flipplayer) View = spin(M_PI) * View;
|
|
cmode = emDraw;
|
|
} */
|
|
|
|
if(sym == 'v') {
|
|
cmode = emMenu;
|
|
}
|
|
|
|
if(sym == SDLK_F2) {
|
|
cmode = emVisual1;
|
|
}
|
|
|
|
#ifdef PANDORA
|
|
if(ev.type == SDL_MOUSEBUTTONUP && sym == 0 && !rightclick)
|
|
#else
|
|
if(ev.type == SDL_MOUSEBUTTONDOWN && sym == 0 && !rightclick)
|
|
#endif
|
|
{
|
|
bool forcetarget = (keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]);
|
|
|
|
shmup::cpid = 0;
|
|
if(mouseover && targetclick && (numplayers() == 1) && targetRangedOrb(mouseover, forcetarget ? roMouseForce : roMouse))
|
|
;
|
|
else if(forcetarget)
|
|
;
|
|
else if(shmup::on)
|
|
;
|
|
else mousemovement();
|
|
}
|
|
|
|
if(sym == SDLK_F1) {
|
|
lastmode = cmode;
|
|
cmode = emHelp;
|
|
}
|
|
|
|
#ifdef LOCAL
|
|
process_local0(sym);
|
|
#endif
|
|
}
|
|
|
|
else if(cmode == emMenu) { if(handleMenuKey(sym, ev.type == SDL_MOUSEBUTTONDOWN)) return; }
|
|
else if(cmode == emCheatMenu) handleCheatMenu(uni);
|
|
else if(cmode == emVisual1) handleVisual1(sym, uni);
|
|
else if(cmode == emCustomizeChar) handleCustomizeChar(sym, uni, ev.key.keysym.mod);
|
|
else if(cmode == emVisual2) handleVisual2(sym, uni);
|
|
else if(cmode == emChangeMode) handleChangeMode(sym, uni);
|
|
else if(cmode == emShmupConfig) shmup::handleConfig(uni, sym);
|
|
else if(cmode == emNetgen) netgen::handleKey(uni, sym);
|
|
else if(cmode == emRugConfig) rug::handleKey(uni, sym);
|
|
else if(cmode == emConformal) conformal::handleKey(uni, sym);
|
|
else if(cmode == emYendor) yendor::handleKey(uni, sym);
|
|
else if(cmode == emTactic) tactic::handleKey(uni, sym);
|
|
else if(cmode == emMapEditor) mapeditor::handleKey(uni, sym);
|
|
else if(cmode == emOverview) handleOverview(uni);
|
|
else if(cmode == emDraw) mapeditor::drawHandleKey(uni, sym, shiftmul);
|
|
else if(cmode == emPickEuclidean) handleEuclidean(sym, uni);
|
|
else if(cmode == emScores) handleScoreKeys(sym, ev);
|
|
else if(cmode == emPickScores) handlePickScoreKeys(uni, ev);
|
|
|
|
else if(cmode == emHelp) {
|
|
if(sym == SDLK_F1 && help != "@")
|
|
help = "@";
|
|
else if(uni == 'c')
|
|
help = buildCredits();
|
|
else if((sym != 0 && sym != SDLK_F12) || ev.type == SDL_MOUSEBUTTONDOWN)
|
|
cmode = lastmode;
|
|
}
|
|
|
|
else if(cmode == emQuit) {
|
|
// ignore the camera movement keys
|
|
if(rug::rugged && (sym == SDLK_UP || sym == SDLK_DOWN || sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN ||
|
|
sym == SDLK_RIGHT || sym == SDLK_LEFT))
|
|
sym = 0;
|
|
|
|
if(sym == SDLK_RETURN || sym == SDLK_F10) return;
|
|
else if(uni == 'r' || sym == SDLK_F5) {
|
|
restartGame(), cmode = emNormal;
|
|
msgs.clear();
|
|
}
|
|
else if(sym == SDLK_UP || sym == SDLK_KP8) msgscroll++;
|
|
else if(sym == SDLK_DOWN || sym == SDLK_KP2) msgscroll--;
|
|
else if(sym == SDLK_PAGEUP || sym == SDLK_KP9) msgscroll+=5;
|
|
else if(sym == SDLK_PAGEDOWN || sym == SDLK_KP3) msgscroll-=5;
|
|
else if(uni == 'v') cmode = emMenu;
|
|
else if(sym == SDLK_HOME || sym == SDLK_F3 || (sym == ' ' && !shmup::on))
|
|
fullcenter();
|
|
else if(uni == 'o') setAppropriateOverview();
|
|
else if(uni == 't') {
|
|
if(!canmove) restartGame();
|
|
loadScores();
|
|
msgs.clear();
|
|
}
|
|
|
|
else if(((sym != 0 && sym != SDLK_F12) || ev.type == SDL_MOUSEBUTTONDOWN) && !didsomething) {
|
|
cmode = emNormal;
|
|
msgscroll = 0;
|
|
msgs.clear();
|
|
}
|
|
}
|
|
|
|
if(ev.type == SDL_QUIT)
|
|
return;
|
|
|
|
}
|
|
|
|
if(playerdead) break;
|
|
}
|
|
|
|
}
|
|
#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 MOBILE
|
|
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
|
|
}
|
|
|
|
void showMissionScreen() {
|
|
cmode = emQuit;
|
|
msgscroll = 0;
|
|
}
|