1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-01-26 00:47:00 +00:00
hyperrogue/mapeditor.cpp
Arthur O'Dwyer 43cb9c0bf4 Change every "pop this menu" option text to the two words "go back".
And add translations for "go back", copied from the existing translations
for the word "return".
2017-10-30 18:48:14 -07:00

2266 lines
68 KiB
C++

// HyperRogue map editor
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#include <map>
#include <stdint.h>
#if ISWINDOWS
#include "direntx.h"
#include "direntx.c"
#else
#include <dirent.h>
#endif
namespace mapeditor {
int subcanvas;
int displaycodes;
struct editwhat {
double dist;
int rotid, symid, pointid;
bool side;
cell *c;
} ew, ewsearch;
bool autochoose = ISMOBILE;
#if CAP_EDIT
map<int, cell*> modelcell;
void handleKeyMap(int sym, int uni);
bool handleKeyFile(int sym, int uni);
void applyModelcell(cell *c) {
if(mapeditor::whichPattern == 'H') return;
if(mapeditor::whichPattern == 'H') return;
int i = realpattern(c);
cell *c2 = modelcell[i];
if(c2) {
c->wall = c2->wall;
c->land = c2->land;
c->monst = c2->monst;
c->item = c2->item;
c->landparam = c2->landparam;
c->wparam = c2->wparam;
c->mondir = c2->mondir;
if(c2->mondir != NODIR)
c->mondir = (c2->mondir - patterndir(c2) + patterndir(c) + 7*6*5) % c->type;
}
}
#endif
}
#if CAP_EDIT
namespace mapstream {
std::map<cell*, int> cellids;
vector<cell*> cellbyid;
vector<char> relspin;
FILE *f;
void addToQueue(cell* c) {
if(cellids.count(c)) return;
int numcells = size(cellbyid);
cellbyid.push_back(c);
cellids[c] = numcells;
}
void saveChar(char c) { fwrite(&c, 1, 1, f); }
template<class T> void save(T& c) { fwrite(&c, sizeof(T), 1, f); }
char loadChar() { char c; int i=fread(&c, 1, 1, f); if(i!=1) return 0; else return c; }
template<class T> int load(T& c) { return fread(&c, sizeof(T), 1, f); }
int32_t loadInt() { int i; if(load(i) < 1) return -1; else return i; }
void savePoint(const hyperpoint& h) {
for(int i=0; i<3; i++) { double x = h[i]; save(x); }
}
hyperpoint loadPoint() {
hyperpoint h;
for(int i=0; i<3; i++) { double x; load(x); h[i] = x; }
return h;
}
bool saveMap(const char *fname) {
f = fopen(fname, "wb");
if(!f) return false;
int32_t i = VERNUM; save(i);
save(mapeditor::whichPattern);
addToQueue(cwt.c->master->c7);
for(int i=0; i<size(cellbyid); i++) {
cell *c = cellbyid[i];
if(i) {
for(int j=0; j<c->type; j++) if(c->mov[j] && cellids.count(c->mov[j]) &&
cellids[c->mov[j]] < i) {
int32_t i = cellids[c->mov[j]];
save(i);
saveChar(c->spn(j));
saveChar(j);
break;
}
}
saveChar(c->land);
saveChar(c->mondir);
saveChar(c->monst);
saveChar(c->wall);
// saveChar(c->barleft);
// saveChar(c->barright);
saveChar(c->item);
saveChar(c->mpdist);
// saveChar(c->bardir);
save(c->wparam); save(c->landparam);
saveChar(c->stuntime); saveChar(c->hitpoints);
for(int j=0; j<c->type; j++) {
cell *c2 = c->mov[j];
if(c2 && c2->land != laNone) addToQueue(c2);
}
}
int32_t n = -1; save(n);
int32_t id = cellids.count(cwt.c) ? cellids[cwt.c] : -1;
save(id);
for(int i=0; i<USERSHAPEGROUPS; i++) for(int j=0; j<USERSHAPEIDS; j++) {
usershape *us = usershapes[i][j];
if(!us) continue;
for(int l=0; l<USERLAYERS; l++) if(size(us->d[l].list)) {
usershapelayer& ds(us->d[l]);
save(i); save(j); save(l); save(ds.sym); save(ds.rots); save(ds.color);
n = size(ds.list); save(n);
savePoint(ds.shift);
savePoint(ds.spin);
for(int i=0; i<size(ds.list); i++) savePoint(ds.list[i]);
}
}
n = -1; save(n);
fclose(f);
cellids.clear();
cellbyid.clear();
return true;
}
int fixspin(int rspin, int dir, int t) {
if(dir >= 0 && dir < t)
return (dir + rspin) % t;
else
return dir;
}
bool loadMap(const char *fname) {
f = fopen(fname, "rb");
if(!f) return false;
int vernum = loadInt();
printf("vernum = %d\n", vernum);
if(vernum >= 7400) load(mapeditor::whichPattern);
clearMemory();
initcells();
if(shmup::on) shmup::init();
while(true) {
cell *c;
int rspin;
if(size(cellbyid) == 0) {
c = currentmap->gamestart();
rspin = 0;
}
else {
int32_t parent = loadInt();
if(parent<0 || parent >= size(cellbyid)) break;
int dir = loadChar();
cell *c2 = cellbyid[parent];
dir = fixspin(dir, relspin[parent], c2->type);
c = createMov(c2, dir);
// printf("%p:%d,%d -> %p\n", c2, dir, c);
// spinval becomes xspinval
rspin = (c2->spn(dir) - loadChar() + 42) % c->type;
}
cellbyid.push_back(c);
relspin.push_back(rspin);
c->land = (eLand) loadChar();
c->mondir = fixspin(rspin, loadChar(), c->type);
c->monst = (eMonster) loadChar();
c->wall = (eWall) loadChar();
// c->barleft = (eLand) loadChar();
// c->barright = (eLand) loadChar();
c->item = (eItem) loadChar();
c->mpdist = loadChar();
c->bardir = NOBARRIERS;
// fixspin(rspin, loadChar(), c->type);
if(vernum < 7400) {
load(c->aitmp);
c->wparam = c->aitmp;
}
else load(c->wparam);
load(c->landparam);
// backward compatibility
if(vernum < 7400 && !isIcyLand(c->land)) c->landparam = HEAT(c);
c->stuntime = loadChar();
c->hitpoints = loadChar();
if(mapeditor::whichPattern)
mapeditor::modelcell[mapeditor::realpattern(c)] = c;
}
int32_t whereami = loadInt();
if(whereami >= 0 && whereami < size(cellbyid))
cwt.c = cellbyid[whereami];
else cwt.c = currentmap->gamestart();
for(int i=0; i<size(cellbyid); i++) {
cell *c = cellbyid[i];
if(c->bardir != NODIR && c->bardir != NOBARRIERS)
extendBarrier(c);
}
for(int d=BARLEV-1; d>=0; d--)
for(int i=0; i<size(cellbyid); i++) {
cell *c = cellbyid[i];
if(c->mpdist <= d)
for(int j=0; j<c->type; j++) {
cell *c2 = createMov(c, j);
setdist(c2, d+1, c);
}
}
cellbyid.clear();
relspin.clear();
if(shmup::on) shmup::init();
timerstart = time(NULL); turncount = 0;
sagephase = 0; hardcoreAt = 0;
timerstopped = false;
savecount = 0; savetime = 0;
cheater = 1;
if(vernum >= 7400) while(true) {
int i = loadInt();
if(i == -1) break;
int j = loadInt(), l = loadInt();
if(i<0 || i >= USERSHAPEGROUPS) break;
if(j<0 || j >= USERSHAPEIDS) break;
if(l<0 || l >= USERLAYERS) break;
initShape(i, j);
usershapelayer& ds(usershapes[i][j]->d[l]);
load(ds.sym); load(ds.rots); load(ds.color);
ds.list.clear();
int siz = loadInt();
ds.shift = loadPoint();
ds.spin = loadPoint();
for(int i=0; i<siz; i++)
ds.list.push_back(loadPoint());
}
buildpolys();
bfs();
restartGraph();
return true;
}
}
#endif
namespace mapeditor {
bool drawplayer = true;
char whichPattern = 0;
char whichShape = 0;
char whichCanvas = 0;
int nopattern(cell *c) {
if(isWarped(c) && !euclid) {
int u = ishept(c)?1:0;
int qhex = 0;
for(int v=0; v<c->type; v++) if(c->mov[v] && !isWarped(c->mov[v])) {
u += 2;
if(!ishept(c->mov[v])) qhex++;
}
if(u == 2 && qhex == 1) return 8;
if(u == 6 && qhex == 2) return 10;
return u;
}
return ishept(c) ? 1 : ishex1(c) ? 2 : 0; // 0 to 1
}
bool reflectPatternAt(cell *c, char p = whichPattern) {
if(p == 'p' && polarb50(c)) return true;
if(p == 0 && nopattern(c) == 4) {
int d = patterndir(c);
return !isWarped(createMov(c, (d+1)%6));
}
return false;
}
int downdir(cell *c, cellfunction *cf = coastvalEdge) {
cell *c2 = chosenDown(c, 1, 1, cf);
if(!c2) return 0;
return neighborId(c, c2);
}
int patterndir(cell *c, char w) {
switch(w) {
case 'z': {
int t = zebra40(c);
if(euclid) return (t*4) % 6;
int t4 = t>>2, tcdir = 0;
if(purehepta) tcdir = t^1;
else if(t4 == 10) tcdir = t-20;
else if(t4 >= 4 && t4 < 7) tcdir = 40 + (t&3);
else if(t4 >= 1 && t4 < 4) tcdir = t+12;
else if(t4 >= 7 && t4 < 10) tcdir = t-24;
for(int i=0; i<c->type; i++) if(c->mov[i] && zebra40(c->mov[i]) == tcdir)
return i;
// printf("fail to fintd %d -> %d\n", t, tcdir);
return 0;
}
case 'f': {
int t = emeraldval(c);
if(euclid) return 0;
int tcdir = 0, tbest = (t&3);
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
if(c2) {
int t2 = emeraldval(c2);
if((t&3) == (t2&3) && t2 > tbest)
tbest = t2, tcdir = i;
}
}
return tcdir;
}
case 'p': {
int tcdir = -1, tbest = -1;
int pa = polara50(c);
int pb = polarb50(c);
for(int i=0; i<c->type; i++) {
cell *c2 = c->mov[i];
if(c2 && polara50(c2) == pa && polarb50(c2) == pb) {
int t2 = fiftyval049(c2);
if(t2 > tbest) tbest = t2, tcdir = i;
}
}
return tcdir;
}
case 'H':
return downdir(c);
case 0: {
if(euclid) return 0;
int u = nopattern(c);
if(u == 6)
for(int i=1; i<c->type; i+=2) if(!isWarped(createMov(c,i)))
return i;
if(u == 2 || u == 3 || u == 8)
for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,i)))
return i;
if(u == 4 || u == 10)
for(int i=0; i<c->type; i+=2) if(!isWarped(createMov(c,i)))
return i;
if(u == 6)
for(int i=1; i<c->type; i+=2) if(!isWarped(createMov(c,i)))
return i;
if(u == 5)
for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,(i+3)%7)) && !isWarped(createMov(c,(i+4)%7)))
return i;
if(u == 9)
for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,(i+2)%7)) && !isWarped(createMov(c,(i+5)%7)))
return i;
if(u == 7)
for(int i=0; i<c->type; i++) if(!isWarped(createMov(c,(i+1)%7)) && !isWarped(createMov(c,(i+6)%7)))
return i;
}
}
return 0;
}
string infix;
bool hasInfix(const string &s) {
if(infix == "") return true;
string t = "";
for(int i=0; i<size(s); i++) {
char c = s[i];
char tt = 0;
if(c >= 'a' && c <= 'z') tt += c - 32;
else if(c >= 'A' && c <= 'Z') tt += c;
else if(c == '@') tt += c;
if(tt) t += tt;
}
return t.find(infix) != string::npos;
}
bool editInfix(int uni) {
if(uni >= 'A' && uni <= 'Z') infix += uni;
else if(uni >= 'a' && uni <= 'z') infix += uni-32;
else if(infix != "" && uni == 8) infix = infix.substr(0, size(infix)-1);
else if(infix != "" && uni != 0) infix = "";
else return false;
return true;
}
cell *drawcell;
#if CAP_EDIT
int paintwhat = 0;
int painttype = 0;
int radius = 0;
string paintwhat_str = "clear monster";
cell *copywhat = NULL; int copydir; bool copyflip;
bool symRotation, sym01, sym02, sym03;
int whichpart;
const char *mapeditorhelp =
"This mode allows you to edit the map.\n\n"
"NOTE: Use at your own risk. Combinations which never "
"appear in the real game may work in an undefined way "
"(do not work, look strangely, give strange messages, or crash the game).\n\n"
"To get the most of this editor, "
"some knowledge of inner workings of HyperRogue is required. "
"Each cell has four main fields: land type, wall type, monster type, item type. "
"The same wall type (especially \"none\", \"sea\", or \"bonfire\") may look or "
"work a bit differently, based on the land it is in. Sometimes an object may "
"appear twice on the list due to subtle differences (for example, Demons could "
"move next turn or not).\n\n"
"Press w, i, l, or m to choose which aspect of cells to change, "
"then just click on the cells and they will change. Press 'c' while "
"hovering over a cell to copy that cell, this copies all information about it. "
"When copying large areas or placing multi-tile monsters, it might be important where "
"on the cell you are clicking.\n\n"
"You can also press 0-9 to apply your changes to a greater radius. "
"This also affects the copy/paste feature, allowing to copy a larger area.\n\n"
"Press F2 to save the current map (and F3 to load it). If you try this after "
"a long game of HyperRogue (without using Orbs of Safety), the filesize will "
"be very large! "
"Note however that large structures, such as "
"Great Walls, large circles and horocycles, are destroyed by this.\n\n"
"Press 'b' to mark cells as boundaries. Such cells, and cells beyond "
"them, are not copied by the copy/paste feature, nor saved by the "
"save feature.\n\n";
const char* patthelp =
"Press 'r' to choose a regular pattern. When a pattern is on, "
"editing a cell automatically edits all cells which are "
"equivalent according to this pattern. You can choose from "
"several patterns, and choose which symmetries matter "
"for equivalence. Also, you can press Space to switch between "
"the map and graphics editor quickly -- note that editing floors "
"with the graphics editor also adheres to the pattern.";
string mehelptext() {
return XLAT(mapeditorhelp) + XLAT(patthelp);
}
vector<pair<string, int> > v;
struct undo_info {
cell *c;
eWall w;
eItem i;
eMonster m;
eLand l;
char wparam;
int32_t lparam;
char dir;
};
vector<undo_info> undo;
bool checkEq(undo_info& u) {
return u.w == u.c->wall && u.i == u.c->item && u.m == u.c->monst && u.l == u.c->land &&
u.dir == u.c->mondir && u.lparam == u.c->landparam && u.wparam == u.c->wparam;
}
void saveUndo(cell *c) {
undo_info u;
u.c=c; u.w = c->wall; u.i = c->item; u.m = c->monst; u.l = c->land; u.dir = c->mondir;
u.wparam = c->wparam; u.lparam = c->landparam;
undo.push_back(u);
}
undo_info& lastUndo() { return undo[size(undo)-1]; }
void undoLock() {
if(!size(undo) || lastUndo().c) {
undo_info i; i.c = NULL; undo.push_back(i);
}
}
void applyUndo() {
while(size(undo) && !lastUndo().c) undo.pop_back();
while(size(undo)) {
undo_info& i(lastUndo());
if(!i.c) break;
i.c->wall = i.w;
i.c->item = i.i;
i.c->monst = i.m;
i.c->land = i.l;
i.c->mondir = i.dir;
i.c->wparam = i.wparam;
i.c->landparam = i.lparam;
undo.pop_back();
}
}
void checkUndo() {
if(checkEq(undo[size(undo)-1])) undo.pop_back();
}
int itc(int k) {
if(k == 0) return 0;
if(k == 1) return 0x40;
if(k == 2) return 0x80;
if(k == 3) return 0xFF;
return 0x20;
}
bool choosefile = false;
bool editext = false;
#define CDIR 0xC0C0C0
#define CFILE forecolor
bool filecmp(const pair<string,int> &f1, const pair<string,int> &f2) {
if(f1.first == "../") return true;
if(f2.first == "../") return false;
if(f1.second != f2.second)
return f1.second == CDIR;
return f1.first < f2.first;
}
string filecaption, cfileext;
string *cfileptr;
void drawFileDialog() {
displayfr(vid.xres/2, 30 + vid.fsize, 2, vid.fsize,
filecaption, forecolor, 8);
string& cfile = *cfileptr;
displayfr(vid.xres/2, 34 + vid.fsize * 2, 2, vid.fsize,
cfile, editext ? 0xFF00FF : 0xFFFF00, 8);
displayButton(vid.xres*1/5, 38+vid.fsize * 3,
"F2 = save", 2000+SDLK_F2, 8);
displayButton(vid.xres*2/5, 38+vid.fsize * 3,
"F3 = load", 2000+SDLK_F3, 8);
displayButton(vid.xres*3/5, 38+vid.fsize * 3,
"F4 = extension", 2000+SDLK_F4, 8);
displayButton(vid.xres*4/5, 38+vid.fsize * 3,
"Enter = back", 2000+SDLK_RETURN, 8);
v.clear();
DIR *d;
struct dirent *dir;
string where = ".";
for(int i=0; i<size(cfile); i++)
if(cfile[i] == '/' || cfile[i] == '\\')
where = cfile.substr(0, i+1);
d = opendir(where.c_str());
if (d) {
while ((dir = readdir(d)) != NULL) {
string s = dir->d_name;
if(s != ".." && s[0] == '.') ;
else if(size(s) > 4 && s.substr(size(s)-4) == cfileext)
v.push_back(make_pair(s, CFILE));
else if(dir->d_type & DT_DIR)
v.push_back(make_pair(s+"/", CDIR));
}
closedir(d);
}
sort(v.begin(), v.end(), filecmp);
int q = v.size();
int percolumn = (vid.yres-38) / (vid.fsize+5) - 4;
int columns = 1 + (q-1) / percolumn;
for(int i=0; i<q; i++) {
int x = 16 + (vid.xres * (i/percolumn)) / columns;
int y = 42 + vid.fsize * 4 + (vid.fsize+5) * (i % percolumn);
displayColorButton(x, y, v[i].first, 1000 + i, 0, 0, v[i].second, 0xFFFF00);
}
keyhandler = handleKeyFile;
}
void displayFunctionKeys() {
int fs = min(vid.fsize + 5, vid.yres/26);
displayButton(8, vid.yres-8-fs*11, XLAT("F1 = help"), SDLK_F1, 0);
displayButton(8, vid.yres-8-fs*10, XLAT("F2 = save"), SDLK_F2, 0);
displayButton(8, vid.yres-8-fs*9, XLAT("F3 = load"), SDLK_F3, 0);
displayButton(8, vid.yres-8-fs*8, XLAT("F4 = file"), SDLK_F3, 0);
displayButton(8, vid.yres-8-fs*7, XLAT("F5 = restart"), SDLK_F5, 0);
displayButton(8, vid.yres-8-fs*6, XLAT("F6 = HQ shot"), SDLK_F6, 0);
displayButton(8, vid.yres-8-fs*5, XLAT("F7 = player on/off"), SDLK_F7, 0);
#if CAP_SVG
displayButton(8, vid.yres-8-fs*4, XLAT("F8 = SVG shot"), SDLK_F8, 0);
#endif
displayButton(8, vid.yres-8-fs*3, XLAT("SPACE = map/graphics"), ' ', 0);
displayButton(8, vid.yres-8-fs*2, XLAT("ESC = return to the game"), SDLK_ESCAPE, 0);
}
void vpush(int i, const char *name) {
string s = XLATN(name);
if(!hasInfix(s)) return;
v.push_back(make_pair(s, i));
}
void showPrePattern() {
dialog::init("predesigned patterns");
dialog::addItem(XLAT("Gameboard"), 'g');
dialog::addItem(XLAT("random colors"), 'r');
dialog::addItem(XLAT("rainbow landscape"), 'l');
dialog::addItem(XLAT("dark rainbow landscape"), 'd');
dialog::addItem(XLAT("football"), 'F');
dialog::addSelItem(XLAT("emerald pattern"), "emerald", 'e');
dialog::addSelItem(XLAT("four elements"), "palace", 'b');
dialog::addSelItem(XLAT("eight domains"), "palace", 'a');
dialog::addSelItem(XLAT("zebra pattern"), "zebra", 'z');
dialog::addSelItem(XLAT("four triangles"), "zebra", 't');
dialog::addSelItem(XLAT("three stripes"), "zebra", 'x');
dialog::addSelItem(XLAT("random black-and-white"), "current", 'w');
dialog::addSelItem(XLAT("field pattern C"), "field", 'C');
dialog::addSelItem(XLAT("field pattern D"), "field", 'D');
dialog::addSelItem(XLAT("field pattern N"), "field", 'N');
dialog::addSelItem(XLAT("field pattern S"), "field", 'S');
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if((uni >= 'a' && uni <= 'z') || (uni >= 'A' && uni <= 'Z')) {
whichCanvas = uni;
subcanvas = rand();
firstland = laCanvas; randomPatternsMode = false;
restartGame();
}
else if(doexiton(sym, uni)) popScreen();
};
}
void showPattern() {
dialog::init();
dialog::addBoolItem(XLAT(euclid ? "three colors" : "Emerald Pattern"), (whichPattern == 'f'), 'f');
dialog::addBoolItem(XLAT("Palace Pattern"), (whichPattern == 'p'), 'p');
dialog::addBoolItem(XLAT(euclid ? "three colors rotated" : "Zebra Pattern"), (whichPattern == 'z'), 'z');
dialog::addBoolItem(XLAT("field pattern"), (whichPattern == 'F'), 'F');
if(whichPattern == 'f') symRotation = true;
if(whichPattern == 'F') ;
else if(!euclid) {
dialog::addBoolItem(XLAT("rotational symmetry"), (symRotation), '0');
dialog::addBoolItem(XLAT("symmetry 0-1"), (sym01), '1');
dialog::addBoolItem(XLAT("symmetry 0-2"), (sym02), '2');
dialog::addBoolItem(XLAT("symmetry 0-3"), (sym03), '3');
}
else
dialog::addBoolItem(XLAT("edit all three colors"), (symRotation), '0');
dialog::addBoolItem(XLAT("display pattern codes (full)"), (displaycodes == 1), 'd');
dialog::addBoolItem(XLAT("display pattern codes (simplified)"), (displaycodes == 2), 's');
dialog::addBoolItem(XLAT("display only hexagons"), (whichShape == '6'), '6');
dialog::addBoolItem(XLAT("display only heptagons"), (whichShape == '7'), '7');
dialog::addBoolItem(XLAT("display the triheptagonal grid"), (whichShape == '8'), '8');
dialog::addItem(XLAT("line patterns"), 'l');
dialog::addItem(XLAT("predesigned patterns"), 'r');
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni == 'f' || uni == 'p' || uni == 'z' || uni == 'H' || uni == 'F') {
if(whichPattern == uni) whichPattern = 0;
else whichPattern = uni;
modelcell.clear();
}
else if(uni == '0') symRotation = !symRotation;
else if(uni == '1') sym01 = !sym01;
else if(uni == '2') sym02 = !sym02;
else if(uni == '3') sym03 = !sym03;
else if(uni == '6' || uni == '7' || uni == '8') {
if(whichShape == uni) whichShape = 0;
else whichShape = uni;
}
else if(uni == '3') sym03 = !sym03;
else if(uni == 'd') displaycodes = displaycodes == 1 ? 0 : 1;
else if(uni == 's') displaycodes = displaycodes == 2 ? 0 : 2;
else if(uni == 'l')
pushScreen(linepatterns::showMenu);
else if(uni == 'r') pushScreen(showPrePattern);
else if(doexiton(sym, uni)) popScreen();
};
}
void showList() {
v.clear();
if(painttype == 4) painttype = 0;
switch(painttype) {
case 0:
for(int i=0; i<motypes; i++) {
eMonster m = eMonster(i);
if(
m == moTongue || m == moPlayer || m == moFireball || m == moBullet ||
m == moFlailBullet || m == moShadow || m == moAirball ||
m == moWolfMoved || m == moGolemMoved ||
m == moTameBomberbirdMoved || m == moKnightMoved ||
m == moDeadBug || m == moLightningBolt || m == moDeadBird ||
m == moMouseMoved || m == moPrincessMoved || m == moPrincessArmedMoved) ;
else vpush(i, minf[i].name);
}
break;
case 1:
for(int i=0; i<ittypes; i++) vpush(i, iinf[i].name);
break;
case 2:
for(int i=0; i<landtypes; i++) vpush(i, linf[i].name);
break;
case 3:
for(int i=0; i<walltypes; i++) if(i != waChasmD) vpush(i, winf[i].name);
break;
}
// sort(v.begin(), v.end());
if(infix != "") mouseovers = infix;
int q = v.size();
int percolumn = vid.yres / (vid.fsize+5) - 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+5) * (i % percolumn) + vid.fsize*2;
int actkey = 1000 + i;
string vv = v[i].first;
if(i < 9) { vv += ": "; vv += ('1' + i); }
displayButton(x, y, vv, actkey, 0);
}
keyhandler = [] (int sym, int uni) {
if(uni >= '1' && uni <= '9') uni = 1000 + uni - '1';
if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER || sym == '-' || sym == SDLK_KP_MINUS) uni = 1000;
for(int z=0; z<size(v); z++) if(1000 + z == uni) {
paintwhat = v[z].second;
paintwhat_str = v[z].first;
mousepressed = false;
popScreen();
return;
}
if(editInfix(uni)) ;
else if(doexiton(sym, uni)) popScreen();
};
}
void showMapEditor() {
cmode = sm::MAP;
gamescreen(0);
int fs = min(vid.fsize + 5, vid.yres/26);
getcstat = '-';
displayfr(8, 8 + fs, 2, vid.fsize, paintwhat_str, forecolor, 0);
displayfr(8, 8+fs*2, 2, vid.fsize, XLAT("use at your own risk!"), 0x800000, 0);
displayButton(8, 8+fs*4, XLAT("0-9 = radius (%1)", its(radius)), ('0' + (radius+1)%10), 0);
displayButton(8, 8+fs*5, XLAT("b = boundary"), 'b', 0);
displayButton(8, 8+fs*6, XLAT("m = monsters"), 'm', 0);
displayButton(8, 8+fs*7, XLAT("w = walls"), 'w', 0);
displayButton(8, 8+fs*8, XLAT("i = items"), 'i', 0);
displayButton(8, 8+fs*9, XLAT("l = lands"), 'l', 0);
displayfr(8, 8+fs*10, 2, vid.fsize, XLAT("c = copy"), 0xC0C0C0, 0);
displayButton(8, 8+fs*11, XLAT("u = undo"), 'u', 0);
if(painttype == 4)
displayButton(8, 8+fs*12, XLAT("f = flip %1", ONOFF(copyflip)), 'u', 0);
displayButton(8, 8+fs*13, XLAT("r = regular"), 'r', 0);
displayButton(8, 8+fs*14, XLAT("p = paint"), 'p', 0);
displayFunctionKeys();
keyhandler = handleKeyMap;
}
int spillinc() {
if(radius>=2) return 0;
if(anyshiftclick) return -1;
return 1;
}
void spillCopy(cell *c1, int d1, cell *c2, int d2, int r) {
saveUndo(c1);
int cf = copyflip ? -1 : 1;
if(c2->land == laNone) return;
c1->wall = c2->wall;
c1->item = c2->item;
c1->land = c2->land;
c1->monst = c2->monst;
c1->landparam = c2->landparam;
c1->wparam = c2->wparam;
// c1->tmp = c2->tmp;
if(c2->mondir == NODIR) c1->mondir = NODIR;
else c1->mondir = (cf * (c2->mondir - d2) + d1 + 42) % c1->type;
checkUndo();
if(r) for(int i=0; i<c1->type; i++) {
if(c1 != mouseover && (i == 0 || i == 1 || i == c1->type-1)) continue;
createMov(c1, i);
int i0 = (42+cf*i+d1) % c1->type;
int i1 = (i + d2) % c2->type;
spillCopy(c1->mov[i0], c1->spn(i0), c2->mov[i1], c2->spn(i1), r-1);
}
}
int subpatternEmerald(int i) {
if(euclid) return (symRotation && (i<3)) ? 0 : i;
if((sym01?1:0)+(sym02?1:0)+(sym03?1:0) >= 2) i &= ~3;
if(sym01 && (i&1)) i ^= 1;
if(sym02 && (i&2)) i ^= 2;
if(sym03 && (i&2)) i ^= 3;
return i;
}
int subpatternZebra(int i) {
if(euclid) return (symRotation && (i<3)) ? 0 : i;
i = subpatternEmerald(i);
if(symRotation) {
if(i >= 8 && i < 12) i -= 4;
if(i >= 12 && i < 16) i -= 8;
if(i >= 20 && i < 24) i -= 4;
if(i >= 24 && i < 28) i -= 8;
if(i >= 32 && i < 36) i -= 4;
if(i >= 36 && i < 40) i -= 8;
}
return i;
}
int subpatternPalace(int i) {
if(euclid) return i;
i = subpatternEmerald(i);
if(symRotation && i >= 3) i -= ((i/4-1) % 7) * 4;
return i;
}
int subpattern(cell *c) {
switch(whichPattern) {
case 'z':
return subpatternZebra(zebra40(c)); // 4 to 43
case 'f':
return subpatternEmerald(emeraldval(c)); // 44 to 99
case 'p': {
int i = fiftyval049(c);
i *= 4;
if(polara50(c)) i|=1;
if(polarb50(c)) i|=2;
return subpatternPalace(i);
}
case 'P':
return fiftyval(c);
case 'H':
return realpattern(c);
case 'F':
return realpattern(c);
}
return nopattern(c);
}
int realpattern(cell *c) {
switch(whichPattern) {
case 'z':
return zebra40(c); // 4 to 43
case 'f':
return emeraldval(c); // 44 to 99
case 'p': {
int i = fiftyval049(c);
i *= 4;
if(polara50(c)) i|=1;
if(polarb50(c)) i|=2;
return i;
}
case 'H':
return towerval(c);
case 'F': {
pair<int, bool> p = fieldpattern::fieldval(c);
return 10*p.first + (p.second?6:7);
}
}
return nopattern(c);
}
int realpatternsh(cell *c) {
if(whichPattern == 'F') return nopattern(c);
else return realpattern(c);
}
int cellShapeGroup() {
if(whichPattern == 'f') return 4;
if(whichPattern == 'p') return 5;
if(whichPattern == 'z') return 6;
if(whichPattern == 'H') return 7;
return 3;
}
int drawcellShapeGroup() {
if(drawcell == cwt.c) return 0;
if(drawcell->monst) return 1;
if(drawcell->item) return 2;
return cellShapeGroup();
}
int drawcellShapeID() {
if(drawcell == cwt.c) return vid.cs.charid;
if(drawcell->monst) return drawcell->monst;
if(drawcell->item) return drawcell->item;
return subpattern(drawcell);
}
int subpatternShape(int i) {
if(whichPattern == 'z') return subpatternZebra(i);
if(whichPattern == 'f') return subpatternEmerald(i);
if(whichPattern == 'p') return subpatternPalace(i);
return i;
}
bool editingShape(int group, int id) {
if(group != mapeditor::drawcellShapeGroup()) return false;
if(group < 3) return id == drawcellShapeID();
return subpatternShape(id) == subpattern(drawcell);
}
void spill(cell *c, int r, int cdir) {
if(painttype == 4 && radius) {
if(mouseover->type != copywhat->type) return;
if(cdir<0) cdir=0;
if(mouseover->type == 6 && ((cdir^copydir)&1)) {
cdir++; cdir %= 6;
}
spillCopy(mouseover, cdir, copywhat, copydir, radius);
}
saveUndo(c);
switch(painttype) {
case 0:
c->monst = eMonster(paintwhat);
c->hitpoints = 3;
c->stuntime = 0;
c->mondir = cdir;
if((isWorm(c) || isIvy(c) || isMutantIvy(c)) && c->mov[cdir] &&
!isWorm(c->mov[cdir]) && !isIvy(c->mov[cdir]))
c->mondir = NODIR;
break;
case 1:
c->item = eItem(paintwhat);
if(c->item == itBabyTortoise)
tortoise::babymap[c] = getBits(c) ^ tortoise::getRandomBits();
break;
case 2: {
eLand last = c->land;
c->land = eLand(paintwhat);
if(isIcyLand(c) && isIcyLand(last))
HEAT(c) += spillinc() / 100.;
else if(last == laDryForest && c->land == laDryForest)
c->landparam += spillinc();
else if(last == laOcean && c->land == laOcean)
c->landparam += spillinc();
else if(last == laHive && c->land == laHive)
c->landparam += spillinc();
else
c->landparam = 0;
break;
}
case 3: {
eWall last = c->wall;
c->wall = eWall(paintwhat);
if(last != c->wall) {
if(hasTimeout(c))
c->wparam = 10;
else if(c->wall == waWaxWall)
c->landparam = hrand(0xFFFFFF + 1);
}
else if(hasTimeout(c))
c->wparam += spillinc();
break;
}
case 5:
c->land = laNone;
c->wall = waNone;
c->item = itNone;
c->monst = moNone;
c->landparam = 0;
// c->tmp = -1;
break;
case 6:
c->land = laCanvas;
c->wall = waNone;
c->landparam = paintwhat >> 8;
break;
case 4:
c->wall = copywhat->wall;
c->item = copywhat->item;
c->land = copywhat->land;
c->monst = copywhat->monst;
c->landparam = copywhat->landparam;
c->wparam = copywhat->wparam;
if(copywhat->mondir == NODIR) c->mondir = NODIR;
else if(c->mondir == c->type-1) c->mondir = 0;
else c->mondir++;
break;
}
checkUndo();
if(r) for(int i=0; i<c->type; i++) spill(createMov(c, i), r-1, c->spn(i));
}
void allInPattern(cell *c, int r, int cdir) {
if(!whichPattern) {
spill(c, r, cdir);
return;
}
vector<cell*> v;
v.push_back(c);
sval++;
c->aitmp = sval;
cdir = cdir - patterndir(c);
int sp = subpattern(c);
int at = 0;
while(at < size(v)) {
cell *c2 = v[at];
at++;
if(subpattern(c2) == sp) {
spill(c2, r, fixdir(cdir + patterndir(c2), c));
modelcell[realpattern(c2)] = c2;
}
for(int i=0; i<c2->type; i++) {
cell *c3 = c2->mov[i];
if(c3 && !eq(c3->aitmp, sval))
c3->aitmp = sval, v.push_back(c3);
}
}
}
bool handleKeyFile(int uni, int sym) {
string& s(*cfileptr);
int i = size(s) - (editext?0:4);
if(uni > 2000) sym = uni - 2000;
if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER || sym == SDLK_ESCAPE) {
popScreen();
return true;
}
else if(sym == SDLK_F2 || sym == SDLK_F3) {
popScreen();
return false;
}
else if(sym == SDLK_F4) {
editext = !editext;
}
else if(sym == SDLK_BACKSPACE && i) {
s.erase(i-1, 1);
}
else if(uni >= 32 && uni < 127) {
s.insert(i, s0 + char(uni));
}
else if(uni >= 1000 && uni <= 1000+size(v)) {
string where = "", what = s, whereparent = "../";
for(int i=0; i<size(s); i++)
if(s[i] == '/') {
if(i >= 2 && s.substr(i-2,3) == "../")
whereparent = s.substr(0, i+1) + "../";
else
whereparent = where;
where = s.substr(0, i+1), what = s.substr(i+1);
}
int i = uni - 1000;
if(v[i].first == "../") {
s = whereparent + what;
}
else if(v[i].second == CDIR)
s = where + v[i].first + what;
else
s = where + v[i].first;
}
return true;
}
void handleKeyMap(int sym, int uni) {
handlePanning(sym, uni);
// left-clicks are coded with '-', and right-clicks are coded with sym F1
if(uni == '-') undoLock();
if(uni == '-' && mouseover)
allInPattern(mouseover, radius, neighborId(mouseover, mouseover2));
if(mouseover) for(int i=0; i<mouseover->type; i++) createMov(mouseover, i);
if(uni == 'u') applyUndo();
else if(uni == 'v' || sym == SDLK_F10 || sym == SDLK_ESCAPE) popScreen();
else if(uni >= '0' && uni <= '9') radius = uni - '0';
else if(uni == 'm') pushScreen(showList), painttype = 0, infix = "";
else if(uni == 'i') pushScreen(showList), painttype = 1, infix = "";
else if(uni == 'l') pushScreen(showList), painttype = 2, infix = "";
else if(uni == 'w') pushScreen(showList), painttype = 3, infix = "";
else if(uni == 'r') pushScreen(showPattern);
else if(uni == 't' && mouseover) {
cwt.c = mouseover; playermoved = true;
cwt.spin = neighborId(mouseover, mouseover2);
}
else if(uni == 'b') painttype = 5, paintwhat_str = XLAT("boundary");
else if(uni == 'p') {
painttype = 6;
paintwhat_str = "paint";
dialog::openColorDialog((unsigned&)(paintwhat = (painttype ==6 ? paintwhat : 0x808080)));
}
else if(sym == SDLK_F2) {
if(mapstream::saveMap(levelfile.c_str()))
addMessage(XLAT("Map saved to %1", levelfile));
else
addMessage(XLAT("Failed to save map to %1", levelfile));
}
else if(sym == SDLK_F5) {
restartGame();
}
else if(sym == SDLK_F3) {
if(mapstream::loadMap(levelfile.c_str()))
addMessage(XLAT("Map loaded from %1", levelfile));
else
addMessage(XLAT("Failed to load map from %1", levelfile));
}
else if(sym == SDLK_F4) {
cfileptr = &levelfile;
filecaption = XLAT("level to save/load:");
cfileext = ".lev";
pushScreen(drawFileDialog);
}
#if CAP_SDL
else if(sym == SDLK_F6) {
saveHighQualityShot();
}
#endif
#if CAP_SVG
else if(sym == SDLK_F8) {
svg::render();
}
#endif
else if(sym == SDLK_F7) {
drawplayer = !drawplayer;
}
else if(uni == 'c') {
if(mouseover && mouseover2)
copydir = neighborId(mouseover, mouseover2);
if(copydir<0) copydir = 0;
copyflip = (uni == 'f');
copywhat = mouseover, painttype = 4;
paintwhat_str = XLAT("copying");
}
else if(uni == 'f') {
copyflip = !copyflip;
}
else if(uni == 'h' || sym == SDLK_F1)
gotoHelp(mehelptext());
else if(uni == ' ') {
popScreen();
pushScreen(showDrawEditor);
initdraw(mouseover ? mouseover : cwt.c);
}
}
// VECTOR GRAPHICS EDITOR
const char* drawhelp =
"In this mode you can draw your own player characters, "
"floors, monsters, and items. Press 'e' while hovering over "
"an object to edit it. Start drawing shapes with 'n', and "
"add extra vertices with 'a'. Press 0-9 to draw symmetric "
"pictures easily. More complex pictures can "
"be created by using several layers ('l'). See the edges of "
"the screen for more keys.";
string drawhelptext() {
return XLAT(drawhelp);
}
int dslayer;
bool coloring;
unsigned int colortouse = 0xC0C0C0FFu;
transmatrix drawtrans, drawtransnew;
void loadShape(int sg, int id, hpcshape& sh, int d, int layer) {
initShape(sg, id);
usershapelayer *dsCur = &usershapes[sg][id]->d[layer];
dsCur->list.clear();
dsCur->sym = d==2;
for(int i=sh.s; i < sh.s + (sh.e-sh.s)/d; i++)
dsCur->list.push_back(hpc[i]);
}
void drawGhosts(cell *c, const transmatrix& V, int ct) {
}
hyperpoint ccenter = C0;
hyperpoint coldcenter = C0;
void drawGrid() {
for(int d=0; d<84; d++) {
transmatrix d2 = drawtrans * rgpushxto0(ccenter);
int lalpha;
if(d % (84/drawcell->type) == 0)
lalpha = 0x40;
else
lalpha = 0x20;
int col = darkena(0xC0C0C0, 0, lalpha);
queueline(d2 * C0, d2 * spin(M_PI*d/42)* xpush(1) * C0, col);
for(int u=2; u<=20; u++) {
if(u % 5 == 0) lalpha = 0x40;
else lalpha = 0x20;
queueline(
d2 * spin(M_PI*d/42)* xpush(u/20.) * C0,
d2 * spin(M_PI*(d+1)/42)* xpush(u/20.) * C0,
darkena(0xC0C0C0, 0, lalpha));
}
}
queueline(drawtrans*ccenter, drawtrans*coldcenter, darkena(0xC0C0C0, 0, 0x20));
}
void drawHandleKey(int sym, int uni);
void showDrawEditor() {
cmode = sm::DRAW;
gamescreen(0);
drawGrid();
if(!mouseout()) getcstat = '-';
int sg = drawcellShapeGroup();
string line1, line2;
switch(sg) {
case 0:
line1 = XLAT("character");
line2 = csname(vid.cs);
break;
case 1:
line1 = XLAT("monster");
line2 = XLAT1(minf[drawcell->monst].name);
break;
case 2:
line1 = XLAT("item");
line2 = XLAT1(iinf[drawcell->item].name);
break;
case 3:
line1 = XLAT("floor");
line2 = XLAT(ishept(drawcell) ? "heptagonal" :
ishex1(drawcell) ? "hexagonal #1" : "hexagonal");
break;
default:
line1 = XLAT("floor/pattern");
line2 = "#" + its(subpattern(cwt.c));
break;
}
usershape *us =usershapes[drawcellShapeGroup()][drawcellShapeID()];
int fs = min(vid.fsize + 5, vid.yres/28);
// displayButton(8, 8+fs*9, XLAT("l = lands"), 'l', 0);
displayfr(8, 8+fs, 2, vid.fsize, line1, 0xC0C0C0, 0);
displayfr(8, 8+fs*2, 2, vid.fsize, line2, 0xC0C0C0, 0);
displayButton(8, 8+fs*3, XLAT("l = layers: %1", its(dslayer)), 'l', 0);
if(us && size(us->d[dslayer].list)) {
usershapelayer& ds(us->d[dslayer]);
displayButton(8, 8+fs*4, XLAT("1-9 = rotations: %1", its(ds.rots)), '1' + (ds.rots % 9), 0);
displayButton(8, 8+fs*5, XLAT(ds.sym ? "0 = symmetry" : "0 = asymmetry"), '0', 0);
displayfr(8, 8+fs*7, 2, vid.fsize, XLAT("%1 vertices", its(size(ds.list))), 0xC0C0C0, 0);
displaymm('a', 8, 8+fs*8, 2, vid.fsize, XLAT("a = add v"), 0);
if(autochoose) {
displaymm('m', 8, 8+fs*9, 2, vid.fsize, XLAT("m = move v"), 0);
displaymm('d', 8, 8+fs*10, 2, vid.fsize, XLAT("d = delete v"), 0);
}
else {
displayButton(8, 8+fs*9, XLAT("m = move v"), 'm', 0);
displayButton(8, 8+fs*10, XLAT("d = delete v"), 'd', 0);
}
displaymm('c', 8, 8+fs*11, 2, vid.fsize, XLAT(autochoose ? "autochoose" : "c = choose"), 0);
displayButton(8, 8+fs*12, XLAT("b = switch auto"), 'b', 0);
displayfr(8, 8+fs*14, 2, vid.fsize, XLAT("t = shift"), 0xC0C0C0, 0);
displayfr(8, 8+fs*15, 2, vid.fsize, XLAT("y = spin"), 0xC0C0C0, 0);
displayButton(8, 8+fs*16, XLAT("p = paint"), 'p', 0);
}
else {
displaymm('n', 8, 8+fs*5, 2, vid.fsize, XLAT("'n' to start"), 0);
if(mousekey == 'a' || mousekey == 'd' || mousekey == 'd' ||
mousekey == 'c') mousekey = 'n';
}
displaymm('g', vid.xres-8, 8+fs*4, 2, vid.fsize, XLAT("g = grid"), 16);
displayButton(vid.xres-8, 8+fs*3, XLAT("z = zoom in"), 'z', 16);
displayButton(vid.xres-8, 8+fs*2, XLAT("o = zoom out"), 'o', 16);
displaymm('e', vid.xres-8, 8+fs, 2, vid.fsize, XLAT("e = edit this"), 16);
if(!mouseout()) {
hyperpoint mh = inverse(drawtrans * rgpushxto0(ccenter)) * mouseh;
displayfr(vid.xres-8, vid.yres-8-fs*6, 2, vid.fsize, XLAT("x: %1", fts4(mh[0])), 0xC0C0C0, 16);
displayfr(vid.xres-8, vid.yres-8-fs*5, 2, vid.fsize, XLAT("y: %1", fts4(mh[1])), 0xC0C0C0, 16);
displayfr(vid.xres-8, vid.yres-8-fs*4, 2, vid.fsize, XLAT("z: %1", fts4(mh[2])), 0xC0C0C0, 16);
displayfr(vid.xres-8, vid.yres-8-fs*2, 2, vid.fsize, XLAT("r: %1", fts4(hdist0(mh))), 0xC0C0C0, 16);
displayfr(vid.xres-8, vid.yres-8-fs, 2, vid.fsize, XLAT("ϕ: %1°", fts4(-atan2(mh[1], mh[0]) * 360 / 2 / M_PI)), 0xC0C0C0, 16);
}
displayFunctionKeys();
keyhandler = drawHandleKey;
}
bool rebuildPolys = false;
void applyToShape(int sg, int id, int uni, hyperpoint mh) {
bool haveshape = usershapes[sg][id];
bool xnew = false;
if(uni == '-') uni = mousekey;
if(!haveshape) {
if(uni == 'n' || uni == 'u')
initShape(sg, id);
else if(uni >= '0' && uni <= '9') {
initShape(sg, id);
xnew = true;
}
else
return;
}
usershapelayer *dsCur = &usershapes[sg][id]->d[dslayer];
if(uni == 'n' || xnew) {
dsCur->list.clear();
dsCur->list.push_back(mh);
rebuildPolys = true;
}
if(uni == 'a' && haveshape) {
mh = spin(2*M_PI*-ew.rotid/dsCur->rots) * mh;
if(ew.symid) mh = Mirror * mh;
if(ew.pointid < 0 || ew.pointid >= size(dsCur->list))
ew.pointid = size(dsCur->list)-1, ew.side = 1;
dsCur->list.insert(dsCur->list.begin()+ew.pointid+(ew.side?1:0), mh);
if(ew.side) ew.pointid++;
rebuildPolys = true;
}
if(uni == 'D') {
delete usershapes[sg][id];
usershapes[sg][id] = NULL;
}
if(uni == 'm' || uni == 'd') {
int i = ew.pointid;
if(i < 0 || i >= size(dsCur->list)) return;
mh = spin(2*M_PI*-ew.rotid/dsCur->rots) * mh;
if(ew.symid) mh = Mirror * mh;
if(uni == 'm' || uni == 'M')
dsCur->list[i] = mh;
if(uni == 'd' || uni == 'b') {
dsCur->list.erase(dsCur->list.begin() + i);
if(ew.side == 1 && ew.pointid >= i) ew.pointid--;
if(ew.side == 0 && ew.pointid > i) ew.pointid--;
}
rebuildPolys = true;
}
if(uni == 'T') {
/* loadShape(sg, id, shFemaleBody, 1, 1);
loadShape(sg, id, shPKnife, 1, 2);
loadShape(sg, id, shFemaleDress, 1, 3);
loadShape(sg, id, shPrincessDress, 1, 4);
loadShape(sg, id, shBeautyHair, 1, 5);
loadShape(sg, id, shPFace, 1, 6);
loadShape(sg, id, shFlowerHair, 1, 7); */
// loadShape(sg, id, shPBody, 1, 0);
// loadShape(sg, id, shPHead, 1, 1);
/* loadShape(sg, id, shReptileFrontFoot, 1, 0);
loadShape(sg, id, shReptileRearFoot, 1, 1);
loadShape(sg, id, shReptileFrontLeg, 1, 2);
loadShape(sg, id, shReptileRearLeg, 1, 3);
loadShape(sg, id, shReptileBody, 2, 4);
loadShape(sg, id, shReptileHead, 2, 5);
loadShape(sg, id, shReptileTail, 2, 6); */
// loadShape(sg, id, shTrylobite, 2, 0);
for(int i=0; i<8; i++)
loadShape(sg, id, shWave[i][0], 1, i);
/* loadShape(sg, id, shYeti, 2, 0);
loadShape(sg, id, shHumanFoot, 1, 1); */
/* loadShape(sg, id, shYeti, 1, 2);
loadShape(sg, id, shRatHead, 1, 3);
loadShape(sg, id, shRatTail, 1, 1);
loadShape(sg, id, shWolf1, 1, 4);
loadShape(sg, id, shWolf2, 1, 5);
loadShape(sg, id, shRatCape1, 1, 7);
loadShape(sg, id, shRatCape2, 1, 6); */
// loadShape(sg, id, shTortoise[0][0], 1, 0);
/* loadShape(sg, id, shTentacleX, 1, 0);
loadShape(sg, id, shTentacle, 1, 1);
loadShape(sg, id, shJoint, 1, 2); */
/* loadShape(3, 0, shTurtleFloor[0], 12, 0);
loadShape(3, 1, shTurtleFloor[1], 14, 0); */
// loadShape(sg, id, shDragonSegment, 2, 0);
// loadShape(sg, id, shEyes, 2, 2);
// loadShape(sg, id, shFamiliarHead, 2, 0);
rebuildPolys = true;
}
if(uni == 'K') {
if(vid.cs.charid >= 4) {
loadShape(sg, id, shCatBody, 2, 0);
loadShape(sg, id, shCatHead, 2, 1);
}
else {
if(!(vid.cs.charid&1)) loadShape(sg, id, shPBody, 2, 0);
else loadShape(sg, id, shFemaleBody, 2, 0);
loadShape(sg, id, shPSword, 1, 1);
if(vid.cs.charid&1)
loadShape(sg, id, shFemaleDress, 2, 2);
/* if(vid.cs.charid&1)
loadShape(sg, id, shPrincessDress, 1, 3);
else
loadShape(sg, id, shPrinceDress, 2, 3); */
loadShape(sg, id, shRatCape2, 1, 3);
if(vid.cs.charid&1)
loadShape(sg, id, shFemaleHair, 2, 4);
else
loadShape(sg, id, shPHead, 2, 4);
loadShape(sg, id, shPFace, 2, 5);
}
// loadShape(sg, id, shWolf, 2, dslayer);
rebuildPolys = true;
}
if(uni == '+') dsCur->rots++;
if(uni >= '1' && uni <= '9') {
dsCur->rots = uni - '0';
if(dsCur->rots == 9) dsCur->rots = 21;
rebuildPolys = true;
}
if(uni == '0') {
dsCur->sym = !dsCur->sym;
rebuildPolys = true;
}
if(uni == 't') {
dsCur->shift = mh;
rebuildPolys = true;
}
if(uni == 'y') {
dsCur->spin = mh;
rebuildPolys = true;
}
#define COLORKEY (-10000)
if(uni == COLORKEY) dsCur->color = colortouse;
}
void writeHyperpoint(FILE *f, hyperpoint h) {
for(int i=0; i<3; i++) fprintf(f, "%lf ", double(h[i]));
fprintf(f, "\n");
}
hyperpoint readHyperpoint(FILE *f) {
hyperpoint h;
for(int i=0; i<3; i++) {
double d;
int err = fscanf(f, "%lf", &d);
if(err) printf("Warning: read error\n");
h[i] = d;
}
return h;
}
string drawHelpLine() {
return XLAT("vector graphics editor");
}
bool onelayeronly;
void drawHandleKey(int sym, int uni) {
handlePanning(sym, uni);
if(uni == SETMOUSEKEY) mousekey = newmousekey;
dslayer %= USERLAYERS;
hyperpoint mh = inverse(drawtrans) * mouseh;
int sg = drawcellShapeGroup();
for(int i=0; i<USERSHAPEIDS; i++) if(editingShape(sg, i))
applyToShape(sg, i, uni, mh);
if(uni == 'e' || (uni == '-' && mousekey == 'e')) {
initdraw(mouseover ? mouseover : cwt.c);
}
if(uni == 'l') { dslayer++; dslayer %= USERLAYERS; }
if(uni == 'L') { dslayer--; if(dslayer < 0) dslayer += USERLAYERS; }
if(uni == 'l' - 96) onelayeronly = !onelayeronly;
if(uni == 'g') coldcenter = ccenter, ccenter = mh;
if(uni == 'c') ew = ewsearch;
if(uni == 'b') autochoose = !autochoose;
if(uni == 'S') {
for(int i=0; i<USERSHAPEGROUPS; i++) for(int j=0; j<USERSHAPEIDS; j++) {
usershape *us = usershapes[i][j];
if(!us) continue;
for(int l=0; l<USERLAYERS; l++) if(size(us->d[l].list)) {
usershapelayer& ds(us->d[l]);
printf("// %d %d %d [%06X]\n", i, j, l, ds.color);
printf(" ID, %d, %d, ", us->d[l].rots, us->d[l].sym?2:1);
for(int i=0; i<size(us->d[l].list); i++)
printf("%lf,%lf, ", double(us->d[l].list[i][0]), double(us->d[l].list[i][1]));
printf("\n");
}
}
}
if(uni == 'z') vid.scale *= 2;
if(uni == 'o') vid.scale /= 2;
if(uni == ' ' && cheater) {
popScreen();
pushScreen(showMapEditor);
}
if(uni == 'p')
dialog::openColorDialog(colortouse);
if(sym == SDLK_F4) {
filecaption = XLAT("pics to save/load:");
cfileptr = &picfile;
cfileext = ".pic";
pushScreen(drawFileDialog);
return;
}
if(sym == SDLK_F2) {
FILE *f = fopen(picfile.c_str(), "wt");
if(!f) {
addMessage(XLAT("Failed to save pictures to %1", picfile));
return;
}
fprintf(f, "HyperRogue saved picture\n");
fprintf(f, "%x\n", VERNUM_HEX);
for(int i=0; i<USERSHAPEGROUPS; i++) for(int j=0; j<USERSHAPEIDS; j++) {
usershape *us = usershapes[i][j];
if(!us) continue;
for(int l=0; l<USERLAYERS; l++) if(size(us->d[l].list)) {
usershapelayer& ds(us->d[l]);
fprintf(f, "\n%d %d %d %d %d %6x %d\n",
i, j, l, ds.sym, ds.rots, ds.color, int(size(ds.list)));
writeHyperpoint(f, ds.shift);
writeHyperpoint(f, ds.spin);
fprintf(f,"\n");
for(int i=0; i<size(ds.list); i++)
writeHyperpoint(f, ds.list[i]);
}
}
fprintf(f, "\n-1\n");
fclose(f);
addMessage(XLAT("Pictures saved to %1", picfile));
}
if(sym == SDLK_F3) {
FILE *f = fopen(picfile.c_str(), "rt");
if(!f) {
addMessage(XLAT("Failed to load pictures from %1", picfile));
return;
}
int err;
char buf[200];
if(!fgets(buf, 200, f)) {
addMessage(XLAT("Failed to load pictures from %1", picfile));
fclose(f); return;
}
int vernum; err = fscanf(f, "%x", &vernum);
printf("vernum = %x\n", vernum);
while(true) {
int i, j, l, sym, rots, color, siz;
err = fscanf(f, "%d%d%d%d%d%x%d", &i, &j, &l, &sym, &rots, &color, &siz);
if(i == -1 || err < 6) break;
if(siz < 0 || siz > 1000) break;
initShape(i, j);
usershapelayer& ds(usershapes[i][j]->d[l]);
ds.shift = readHyperpoint(f);
ds.spin = readHyperpoint(f);
ds.list.clear();
for(int i=0; i<siz; i++) {
ds.list.push_back(readHyperpoint(f));
writeHyperpoint(stdout, ds.list[i]);
}
ds.sym = sym;
ds.rots = rots;
ds.color = color;
}
fclose(f);
addMessage(XLAT("Pictures loaded from %1", picfile));
buildpolys();
}
if(sym == SDLK_F7) {
drawplayer = !drawplayer;
}
#if CAP_SDL
if(sym == SDLK_F6) {
saveHighQualityShot();
}
#endif
#if CAP_SVG
if(sym == SDLK_F8) {
svg::render();
}
#endif
if(sym == SDLK_F5) {
for(int i=0; i<USERSHAPEGROUPS; i++)
for(int j=0; j<USERSHAPEIDS; j++)
if(usershapes[i][j]) delete usershapes[i][j];
}
if(sym == SDLK_ESCAPE) popScreen();
if(sym == SDLK_F1) {
gotoHelp(drawhelptext());
}
if(sym == SDLK_F10) popScreen();
if(rebuildPolys)
buildpolys(), rebuildPolys = false;
}
auto hooks = addHook(clearmemory, 0, [] () {
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;
modelcell.clear();
});
#endif
int canvasback = linf[laCanvas].color >> 2;
int generateCanvas(cell *c) {
if(whichCanvas == 'C') {
using namespace fieldpattern;
int z = fp43.getdist(fieldval(c), make_pair(0,false));
if(z < fp43.circrad) return 0x00C000;
int z2 = fp43.getdist(fieldval(c), make_pair(fp43.otherpole,false));
if(z2 < fp43.disthep[fp43.otherpole] - fp43.circrad)
return 0x3000;
return 0x6000;
}
if(whichCanvas == 'D') {
using namespace fieldpattern;
int z = fp43.getdist(fieldval(c), make_pair(0,false));
return 255 * (fp43.maxdist+1-z) / fp43.maxdist;
}
if(whichCanvas == 'N') {
using namespace fieldpattern;
int z = fp43.getdist(fieldval(c), make_pair(0,false));
int z2 = fp43.getdist(fieldval(c), make_pair(fp43.otherpole,false));
if(z < z2) return 0x00C000;
if(z > z2) return 0xC00000;
return 0xCCCC00;
}
if(whichCanvas == 'S') {
return 0x3F1F0F * fieldpattern::subval(c).second + 0x000080;
}
if(whichCanvas == 'g')
return canvasback;
if(whichCanvas == 'r')
return hrand(0xFFFFFF + 1);
if(whichCanvas == 'e') {
static unsigned int fcol[4] = { 0x404040, 0x800000, 0x008000, 0x000080 };
int fv = emeraldval(c);
return fcol[fv&3];
}
if(whichCanvas == 'a') {
static unsigned int fcol8[8] = {
0x800000,
0x503000,
0x206000,
0x007010,
0x004040,
0x001070,
0x200060,
0x500030
};
if(c->wall == waNone) {
int col = fcol8[land50(c)];
if(polara50(c)) col += 0x181818;
return col;
}
}
if(whichCanvas == 'b') {
static unsigned int fcol[4] = { 0x404040, 0x800000, 0x008000, 0x000080 };
return fcol[polara50(c) + 2 * polarb50(c)];
}
if(whichCanvas == 'z') {
static unsigned int fcol[4] = { 0xC0C0C0, 0xE0E0E0, 0x404040, 0x606060 };
int fv = zebra40(c);
return fcol[fv&3];
}
if(whichCanvas == 't') {
static unsigned int fcol[4] = { 0x804040, 0x408040, 0x404080, 0x808040 };
int fv = zebra40(c);
if(fv/4 == 4 || fv/4 == 6 || fv/4 == 5 || fv/4 == 10) fv ^= 2;
return fcol[fv&3];
}
if(whichCanvas == 'x') {
static unsigned int fcol[4] = { 0xC0C0C0, 0x800000, 0x008000, 0x000080 };
return fcol[zebra3(c)];
}
if(whichCanvas == 'w') {
static unsigned int fcol[2] = { 0x303030, 0xC0C0C0 };
return fcol[randpattern(c, subcanvas) ? 1 : 0];
}
if(whichCanvas == 'l') {
int col[4];
bool err = false;
for(int j=0; j<4; j++) {
col[j] = getCdata(c, j);
col[j] *= 3;
col[j] %= 240;
if(col[j] > 120) col[j] = 240 - col[j];
if(col[j] < -120) col[j] = -240 - col[j];
}
return (0x808080 + col[0] + (col[1] << 8) + (col[2] << 16)) >> (err?2:0);
}
if(whichCanvas == 'd') {
int col[4];
bool err = false;
for(int j=0; j<4; j++) {
col[j] = getCdata(c, j);
col[j] *= 6;
col[j] %= 240;
if(col[j] > 120) col[j] = 240 - col[j];
if(col[j] < -120) col[j] = -240 - col[j];
}
col[0] /= 8;
col[1] /= 8;
col[2] /= 8;
return (0x101010 + col[0] + (col[1] << 8) + (col[2] << 16)) >> (err?2:0);
}
if(whichCanvas == 'h') {
int col[4];
bool err = false;
for(int j=0; j<4; j++) {
col[j] = getCdata(c, j);
col[j] *= 6;
col[j] %= 240;
if(col[j] > 120) col[j] = 240 - col[j];
if(col[j] < -120) col[j] = -240 - col[j];
}
col[0] /= 4;
col[1] /= 4;
col[2] /= 4;
return (0x202020 + col[0] + (col[1] << 8) + (col[2] << 16)) >> (err?2:0);
}
if(whichCanvas == 'F') {
return ishept(c) ? 0x202020 : 0xC0C0C0;
}
return canvasback;
}
void initdraw(cell *c) {
mapeditor::drawcell = c;
ew.c = c;
ew.rotid = 0;
ew.symid = 0;
ew.pointid = -1;
ew.side = 0;
ewsearch = ew;
}
bool drawUserShape(transmatrix V, int group, int id, int color, cell *c) {
#if !CAP_EDIT
return false;
#else
usershape *us = usershapes[group][id];
if(us) {
for(int i=0; i<USERLAYERS; i++) {
if(i != dslayer && onelayeronly) continue;
usershapelayer& ds(us->d[i]);
hpcshape& sh(ds.sh);
if(sh.s != sh.e)
queuepoly(V, sh, ds.color ? ds.color : color);
}
}
if((cmode & sm::DRAW) && mapeditor::editingShape(group, id)) {
/* 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);
} */
if(c == ew.c) mapeditor::drawtrans = V;
if(!us) return false;
usershapelayer &ds(us->d[mapeditor::dslayer]);
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(mouseout()) break;
hyperpoint P2 = V * spin(2*M_PI*a/ds.rots) * (b?Mirror*mh:mh);
queuechr(P2, 10, 'x', 0xFF00FF);
}
if(size(ds.list) == 0) return us;
hyperpoint Plast = V * spin(-2*M_PI/ds.rots) * (ds.sym?Mirror*ds.list[0]:ds.list[size(ds.list)-1]);
int state = 0;
int gstate = 0;
double dist2 = 0;
hyperpoint lpsm;
for(int a=0; a<ds.rots; a++)
for(int b=0; b<(ds.sym?2:1); b++) {
hyperpoint mh2 = spin(2*M_PI*-ew.rotid/ds.rots) * mh;
if(ew.symid) mh2 = Mirror * mh2;
hyperpoint pseudomouse = V * spin(2*M_PI*a/ds.rots) * mirrorif(mh2, b);
for(int t=0; t<size(ds.list); t++) {
int ti = b ? size(ds.list)-1-t : t;
hyperpoint P2 = V * spin(2*M_PI*a/ds.rots) * mirrorif(ds.list[ti], b);
if(!mouseout()) {
double d = hdist(mouseh, P2);
if(d < ewsearch.dist)
ewsearch.dist = d,
ewsearch.rotid = a,
ewsearch.symid = b,
ewsearch.pointid = ti,
ewsearch.c = c,
ewsearch.side = b,
state = 1,
dist2 = d + hdist(mouseh, Plast) - hdist(P2, Plast);
else if(state == 1) {
double dist3 = d + hdist(mouseh, Plast) - hdist(P2, Plast);
if(dist3 < dist2)
ewsearch.side = !ewsearch.side;
state = 2;
}
}
queuechr(P2, 10, 'o',
0xC000C0);
if(!mouseout()) {
if(gstate == 1) queueline(lpsm, P2, 0x90000080), gstate = 0;
if(ti == ew.pointid) {
queueline(pseudomouse, P2, 0xF0000080);
if(ew.side == b) queueline(pseudomouse, Plast, 0x90000080);
else gstate = 1, lpsm = pseudomouse;
}
}
Plast = P2;
}
}
if(gstate == 1) queueline(lpsm, V * ds.list[0], 0x90000080), gstate = 0;
if(state == 1) {
hyperpoint P2 = V * ds.list[0];
if(hdist(mouseh, P2) + hdist(mouseh, Plast) - hdist(P2, Plast) < dist2)
ewsearch.side = 1;
}
}
return us;
#endif
}
}
namespace linepatterns {
int lessalpha(int col, int m) {
part(col, 0) /= m;
return col;
}
int lessalphaif(int col, bool b) {
return b?lessalpha(col, 4):col;
}
int lessalphaif(int col, bool b1, bool b2) {
if(b1) col = lessalpha(col, 2);
if(b2) col = lessalpha(col, 2);
return col;
}
struct {
int id;
const char *lpname;
unsigned int color;
} patterns[] = {
{patTriNet, "triangle grid: not rings", 0xFFFFFF00},
{patTriRings, "triangle grid: rings", 0xFFFFFF00},
{patHepta, "heptagonal grid", 0x0000C000},
{patRhomb, "rhombic tesselation", 0x0000C000},
{patTrihepta, "triheptagonal tesselation", 0x0000C000},
{patNormal, "normal tesselation", 0x0000C000},
{patBigTriangles, "big triangular grid", 0x00606000},
{patBigRings, "big triangles: rings", 0x0000C000},
{patTree, "underlying tree", 0x00d0d000},
{patAltTree, "circle/horocycle tree", 0xd000d000},
{patZebraTriangles, "zebra triangles", 0x40FF4000},
{patZebraLines, "zebra lines", 0xFF000000},
{patVine, "vineyard pattern", 0x8438A400},
{patPalacelike, "firewall lines", 0xFF400000},
{patPalace, "firewall lines: Palace", 0xFFD50000},
{patPower, "firewall lines: Power", 0xFFFF0000},
{0, NULL, 0}
};
void clearAll() {
for(int k=0; patterns[k].lpname; k++) patterns[k].color &= ~255;
}
bool any() {
for(int k=0; patterns[k].lpname; k++) if(patterns[k].color & 255) return true;
return false;
}
void setColor(ePattern id, int col) {
for(int k=0; patterns[k].lpname; k++)
if(patterns[k].id == id) patterns[k].color = col;
}
void switchAlpha(ePattern id, int col) {
for(int k=0; patterns[k].lpname; k++)
if(patterns[k].id == id) patterns[k].color ^= col;
}
void drawPattern(int id, int col, cell *c, const transmatrix& V) {
switch(id) {
#define col1 \
lessalphaif(col, behindsphere(V))
#define col2 \
lessalphaif(col, behindsphere(V), behindsphere(gmatrix[c2]))
case patZebraTriangles:
if(zebra40(c) / 4 == 10) {
bool all = true;
hyperpoint tri[3];
for(int i=0; i<3; i++) {
cell *c2 = createMov(c, i*2);
if(!gmatrix.count(c2)) all = false;
else tri[i] = tC0(gmatrix[c2]);
}
if(all) for(int i=0; i<3; i++)
queueline(tri[i], tri[(i+1)%3], col, 3);
}
break;
case patZebraLines:
if(!pseudohept(c)) for(int i=0; i<c->type; i+=2) {
cell *c2 = createMov(c, i);
int fv1 = zebra40(c);
if(fv1/4 == 4 || fv1/4 == 6 || fv1/4 == 5 || fv1/4 == 10) fv1 ^= 2;
int fv2 = zebra40(c2);
if(fv2/4 == 4 || fv2/4 == 6 || fv2/4 == 5 || fv2/4 == 10) fv2 ^= 2;
if((fv1&1) == (fv2&1)) continue;
double x = sphere?.3651:euclid?.2611:.2849;
queueline(V * ddspin(c,i,-S14) * xpush0(x),
V * ddspin(c,i,+S14) * xpush0(x),
col, 1);
}
break;
case patNormal: {
double x = sphere?.401:euclid?.3 : .328;
if(euclid || !pseudohept(c)) for(int t=0; t<c->type; t++)
if(euclid ? c->mov[t]<c : (((t^1)&1) || c->mov[t] < c))
queueline(V * ddspin(c,t,-S7) * xpush0(x),
V * ddspin(c,t,+S7) * xpush0(x),
col1, 1);
break;
}
case patTrihepta:
if(!pseudohept(c)) for(int i=0; i<6; i++) {
cell *c2 = c->mov[i];
if(!c2 || !pseudohept(c2)) continue;
double x = sphere?.3651:euclid?.2611:.2849;
queueline(V * ddspin(c,i,-S14) * xpush0(x),
V * ddspin(c,i,+S14) * xpush0(x),
col2, 1);
}
break;
case patTriNet:
forCellEx(c2, c) if(c2 > c) if(gmatrix.count(c2)) if(celldist(c) != celldist(c2)) {
queueline(tC0(V), gmatrix[c2]*C0,
darkena(backcolor ^ 0xFFFFFF, 0, col2),
2);
}
break;
case patTriRings:
forCellEx(c2, c) if(c2 > c) if(gmatrix.count(c2) && celldist(c) == celldist(c2))
queueline(tC0(V), gmatrix[c2]*C0,
darkena(backcolor ^ 0xFFFFFF, 0, col2),
2);
break;
case patHepta:
forCellEx(c2, c) if(c2 > c) if(gmatrix.count(c2) && pseudohept(c) == pseudohept(c2))
queueline(tC0(V), gmatrix[c2]*C0,
darkena(backcolor ^ 0xFFFFFF, 0, col2),
2);
break;
case patRhomb:
forCellEx(c2, c) if(c2 > c) if(gmatrix.count(c2) && pseudohept(c) != pseudohept(c2))
queueline(tC0(V), gmatrix[c2]*C0,
darkena(backcolor ^ 0xFFFFFF, 0, col2),
2);
break;
case patPalace: {
int a = polarb50(c);
if(pseudohept(c)) for(int i=0; i<7; i++) {
cell *c1 = createMov(c, (i+3) % 7);
cell *c2 = createMov(c, (i+4) % 7);
if(polarb50(c1) != a && polarb50(c2) != a)
queueline(V * ddspin(c,i,84*5/14) * xpush0(tessf/2),
V * ddspin(c,i,84*9/14) * xpush0(tessf/2),
col, 1);
}
break;
}
case patPalacelike:
if(pseudohept(c)) for(int i=0; i<7; i++)
queueline(V * ddspin(c,i,84*5/14) * xpush0(tessf/2),
V * ddspin(c,i,84*9/14) * xpush0(tessf/2),
col1, 1);
break;
case patBigTriangles: {
if(pseudohept(c) && !euclid) for(int i=0; i<S7; i++)
if(c->master->move[i] < c->master) {
queueline(tC0(V), V*xspinpush0((purehepta?M_PI:0) -2*M_PI*i/S7, tessf), col1, 2);
}
break;
}
case patBigRings: {
if(pseudohept(c) && !euclid) for(int i=0; i<S7; i++)
if(c->master->move[i] && c->master->move[i] < c->master && c->master->move[i]->dm4 == c->master->dm4) {
cell *c2 = c->master->move[i]->c7;
queueline(tC0(V), V*xspinpush0((purehepta?M_PI:0) -2*M_PI*i/S7, tessf), col2, 2);
}
break;
}
case patTree:
if(c->type != 6 && !euclid)
queueline(tC0(V), V*ddi0(purehepta?S42:0, tessf), col1, 2);
break;
case patAltTree:
if(c->type != 6 && !euclid && c->master->alt) {
for(int i=0; i<S7; i++)
if(c->master->move[i] && c->master->move[i]->alt == c->master->alt->move[0])
queueline(tC0(V), V*xspinpush0((purehepta?M_PI:0) -2*M_PI*i/S7, tessf), col, 2);
}
break;
case patVine: {
int p = emeraldval(c);
double hdist = hdist0(heptmove[0] * heptmove[2] * C0);
if(pseudohept(c) && (p/4 == 10 || p/4 == 8))
for(int i=0; i<S7; i++) if(c->mov[i] && emeraldval(c->mov[i]) == p-4) {
queueline(tC0(V), V*tC0(heptmove[i]), col, 2);
queueline(tC0(V), V*tC0(spin(-i * ALPHA) * xpush(-hdist/2)), col, 2);
}
break;
}
case patPower: {
int a = emeraldval(c);
if(pseudohept(c) && a/4 == 8) for(int i=0; i<7; i++) {
heptagon *h1 = c->master->move[(i+1)%7];
heptagon *h2 = c->master->move[(i+6)%7];
if(!h1 || !h2) continue;
if(emeraldval(h1->c7)/4 == 8 && emeraldval(h2->c7)/4 == 8)
queueline(V * ddspin(c,i,84*5/14) * xpush0(tessf/2),
V * ddspin(c,i,84*9/14) * xpush0(tessf/2),
col, 1);
}
break;
}
}
}
void drawAll() {
if(any()) for(map<cell*, transmatrix>::iterator it = gmatrix.begin(); it != gmatrix.end(); it++) {
cell *c = it->first;
transmatrix& V = it->second;
for(int k=0; patterns[k].lpname; k++) {
int col = patterns[k].color;
if(!(col & 255)) continue;
int id = patterns[k].id;
drawPattern(id, col, c, V);
}
}
}
#undef col1
#undef col2
int numpat = 0;
void showMenu() {
dialog::init(XLAT("line patterns"));
for(numpat=0; patterns[numpat].lpname; numpat++)
dialog::addColorItem(XLAT(patterns[numpat].lpname), patterns[numpat].color, 'a'+numpat);
dialog::addBreak(50);
dialog::addItem(XLAT("go back"), ' ');
dialog::addBreak(50);
dialog::addInfo("change the alpha parameter to show the lines");
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni >= 'a' && uni < 'a' + numpat)
dialog::openColorDialog(patterns[uni - 'a'].color, NULL);
else if(doexiton(sym,uni)) popScreen();
};
}
};