other changes in 9.4n

This commit is contained in:
Zeno Rogue 2017-06-18 18:51:46 +02:00
parent 131dfb2b4b
commit b172d3930d
7 changed files with 759 additions and 227 deletions

208
game.cpp
View File

@ -848,7 +848,7 @@ bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) {
if((flags & AF_ONLY_FBUG) && m2 != moPlayer && !isFriendlyOrBug(c2)) return false;
if((flags & AF_ONLY_ENEMY) && (m2 == moPlayer || isFriendlyOrBug(c2))) return false;
if(m2 == moHedge && !(flags & (AF_STAB | AF_TOUGH | AF_EAT | AF_MAGIC | AF_LANCE | AF_SWORD_INTO | AF_HORNS)))
if(m2 == moHedge && !(flags & (AF_STAB | AF_TOUGH | AF_EAT | AF_MAGIC | AF_LANCE | AF_SWORD_INTO | AF_HORNS | AF_BULL)))
if(!checkOrb(m1, itOrbThorns)) return false;
if(m2 == moDraugr && !(flags & (AF_SWORD | AF_MAGIC | AF_SWORD_INTO | AF_HORNS))) return false;
@ -906,17 +906,17 @@ bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) {
// if(m2 == moTortoise && !(flags & AF_MAGIC)) return false;
if(m2 == moRoseBeauty)
if(!(flags & (AF_MAGIC | AF_LANCE | AF_GUN | AF_SWORD_INTO)))
if(!(flags & (AF_MAGIC | AF_LANCE | AF_GUN | AF_SWORD_INTO | AF_BULL)))
if(!isMimic(m1))
if(!checkOrb(m1, itOrbBeauty) && !checkOrb(m1, itOrbAether) && !checkOrb(m1, itOrbShield))
if(!c1 || !c2 || !withRose(c1,c2))
return false;
if(m2 == moFlailer && !c2->stuntime)
if(!(flags & (AF_MAGIC | AF_TOUGH | AF_EAT | AF_HORNS | AF_LANCE | AF_BACK | AF_SWORD_INTO))) return false;
if(!(flags & (AF_MAGIC | AF_TOUGH | AF_EAT | AF_HORNS | AF_LANCE | AF_BACK | AF_SWORD_INTO | AF_BULL))) return false;
if(m2 == moVizier && c2->hitpoints > 1)
if(!(flags & (AF_MAGIC | AF_TOUGH | AF_EAT | AF_HORNS | AF_LANCE | AF_BACK | AF_FAST))) return false;
if(!(flags & (AF_MAGIC | AF_TOUGH | AF_EAT | AF_HORNS | AF_LANCE | AF_BACK | AF_FAST | AF_BULL))) return false;
return true;
}
@ -1582,8 +1582,8 @@ void stunMonster(cell *c2) {
c2->hitpoints--;
if(c2->monst == moPrincess)
playSound(c2, princessgender() ? "hit-princess" : "hit-prince");
}
c2->stuntime = (
}
int newtime = (
c2->monst == moFatGuard ? 2 :
c2->monst == moSkeleton && c2->land != laPalace && c2->land != laHalloween ? 7 :
isMetalBeast(c2->monst) ? 7 :
@ -1599,6 +1599,7 @@ void stunMonster(cell *c2) {
c2->monst == moHedge ? 1 :
c2->monst == moFlailer ? 1 :
3);
if(c2->stuntime < newtime) c2->stuntime = newtime;
if(isBull(c2->monst)) c2->mondir = NODIR;
checkStunKill(c2);
}
@ -2116,6 +2117,10 @@ bool attackMonster(cell *c, flagtype flags, eMonster killer) {
bool dostun = (flags & AF_ORSTUN) && attackJustStuns(c);
if((flags & AF_HORNS) && hornStuns(c)) dostun = true;
if((flags & AF_BULL) && c->monst == moVizier && c->hitpoints > 1) {
dostun = true;
c->stuntime = 2;
}
if(c->monst == moSkeleton && (flags & AF_SWORD)) dostun = false;
@ -3265,6 +3270,7 @@ int moveval(cell *c1, cell *c2, int d, int mf) {
if(isWorm(m) && !passable_for(c1->monst, c2, c1, P_MONSTER)) return -1700;
if(canAttack(c1, m, c2, c2->monst, AF_GETPLAYER | mf) && !(mf & MF_NOATTACKS)) {
if(m == moRagingBull && c1->mondir != NODIR) return -1700;
if(mf & MF_MOUNT) {
if(c2 == dragon::target) return 3000;
else if(isFriendlyOrBug(c2)) return 500;
@ -3369,6 +3375,43 @@ int stayval(cell *c, flagtype mf) {
return 1000;
}
int totalbulldistance(cell *c, int k) {
int tbd = 0;
for(int p=0; p<numplayers(); p++) {
cell *c2 = shpos[p][(cshpos+SHSIZE-k-1)%SHSIZE];
if(c2) tbd += bulldistance(c, c2);
}
return tbd;
}
void determinizeBull(cell *c, int *posdir, int& nc) {
// determinize the Angry Beast movement:
// use the previous PC's positions as the tiebreaker
for(int k=0; k<SHSIZE && nc>1; k++) {
int pts[10];
for(int d=0; d<nc; d++) pts[d] = totalbulldistance(c->mov[posdir[d]], k);
int bestpts = 1000;
for(int d=0; d<nc; d++) if(pts[d] < bestpts) bestpts = pts[d];
int nc0 = 0;
for(int d=0; d<nc; d++) if(pts[d] == bestpts) posdir[nc0++] = posdir[d];
nc = nc0;
}
}
int determinizeBullPush(cellwalker bull) {
int nc = 2;
int dirs[2], positive;
cwstep(bull);
cell *c2 = bull.c;
if(c2->type == 6) return 1; // irrelevant
cwspin(bull, 3); dirs[0] = positive = bull.spin;
cwspin(bull, -6); dirs[1] = bull.spin;
determinizeBull(c2, dirs, nc);
if(dirs[0] == positive) return -1;
return 1;
}
int pickMoveDirection(cell *c, flagtype mf) {
int posdir[10], nc = 0, bestval = stayval(c, mf);
@ -3381,26 +3424,9 @@ int pickMoveDirection(cell *c, flagtype mf) {
if(val == bestval) posdir[nc++] = d;
}
if(c->monst == moRagingBull) {
// determinize the Angry Beast movement:
// use the previous PC's positions as the tiebreaker
for(int k=0; k<SHSIZE && nc>1; k++) {
int pts[10];
for(int d=0; d<nc; d++) pts[d] = 0;
for(int d=0; d<nc; d++)
for(int p=0; p<numplayers(); p++) {
cell *c2 = shpos[p][(cshpos+SHSIZE-k-1)%SHSIZE];
if(c2) pts[d] += bulldistance(c->mov[posdir[d]], c2);
}
int bestpts = 1000;
for(int d=0; d<nc; d++)
if(pts[d] < bestpts) bestpts = pts[d];
int nc0 = 0;
for(int d=0; d<nc; d++) if(pts[d] == bestpts) posdir[nc0++] = posdir[d];
nc = nc0;
}
}
if(c->monst == moRagingBull)
determinizeBull(c, posdir, nc);
if(!nc) return -1;
nc = hrand(nc);
return posdir[nc];
@ -3427,6 +3453,32 @@ int pickDownDirection(cell *c, flagtype mf) {
return downs[hrand(qdowns)];
}
template<class T>
cell *determinePush(cellwalker who, cell *c2, int subdir, T valid) {
cellwalker push = who;
cwstep(push);
cwspin(push, 3 * -subdir);
cwstep(push);
if(valid(push.c)) return push.c;
if(c2->type == 7) {
cwstep(push);
cwspin(push, 1 * -subdir);
cwstep(push);
if(valid(push.c)) return push.c;
}
if(gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, 1); cwstep(push);
if(gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, -2); cwstep(push);
}
if(gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, 1); cwstep(push);
}
if(valid(push.c)) return push.c;
}
return c2;
}
// Angry Beast attack
// note: this is done both before and after movement
void beastAttack(cell *c, bool player) {
@ -3436,8 +3488,27 @@ void beastAttack(cell *c, bool player) {
int flags = AF_BULL | AF_ORSTUN;
if(player) flags |= AF_GETPLAYER;
if(!opposite) flags |= AF_ONLY_FBUG;
if(canAttack(c, moRagingBull, c2, c2->monst, flags))
if(canAttack(c, moRagingBull, c2, c2->monst, flags)) {
attackMonster(c2, flags | AF_MSG, moRagingBull);
if(c2->monst && c2->stuntime) {
cellwalker bull (c, d);
int subdir = determinizeBullPush(bull);
cell *c3 = determinePush(bull, c2, subdir, [c2] (cell *c) { return passable(c, c2, P_BLOW); });
if(c3 && c3 != c2)
pushMonster(c3, c2);
}
}
if(c2->wall == waThumperOff) {
playSound(c2, "click");
c2->wall = waThumperOn;
}
if(c2->wall == waThumperOn) {
cellwalker bull (c, d);
int subdir = determinizeBullPush(bull);
cell *c3 = determinePush(bull, c2, subdir, [c2] (cell *c) { return canPushThumperOn(c, c2, c); });
if(c3 && c3 != c2)
pushThumper(c2, c3);
}
}
}
@ -6358,6 +6429,10 @@ void killFriendlyIvy() {
killMonster(c2, moPlayer, 0);
}
bool monsterPushable(cell *c2) {
return (c2->monst != moFatGuard && !(isMetalBeast(c2->monst) && c2->stuntime < 2) && c2->monst != moTortoise);
}
bool movepcto(int d, int subdir, bool checkonly) {
global_pushto = NULL;
bool switchplaces = false;
@ -6440,21 +6515,8 @@ bool movepcto(int d, int subdir, bool checkonly) {
}
if(c2->wall == waThumperOn && !c2->monst && !nonAdjacentPlayer(c2, cwt.c)) {
cellwalker push = cwt;
cwstep(push);
cwspin(push, 3 * -subdir);
cwstep(push);
/* if(w == waBigStatue && push.c->type == 7) {
if(checkonly) return false;
addMessage(XLAT("%The1 is too heavy to put it back on the pedestal.", c2->wall));
return false;
} */
if((!canPushThumperOn(push.c, c2, cwt.c) && c2->type == 7)) {
cwstep(push);
cwspin(push, 1 * -subdir);
cwstep(push);
}
if(!canPushThumperOn(push.c, c2, cwt.c)) {
cell *c3 = determinePush(cwt, c2, subdir, [c2] (cell *c) { return canPushThumperOn(c, c2, cwt.c); });
if(c3 == c2) {
if(checkonly) return false;
addMessage(XLAT("No room to push %the1.", c2->wall));
return false;
@ -6463,40 +6525,13 @@ bool movepcto(int d, int subdir, bool checkonly) {
if(!checkonly && errormsgs) wouldkill("%The1 would kill you there!");
return false;
}
global_pushto = push.c;
global_pushto = c3;
if(checkonly) return true;
addMessage(XLAT("You push %the1.", c2->wall));
lastmovetype = lmPush; lastmove = cwt.c;
pushThumper(c2, push.c);
pushThumper(c2, c3);
}
/* if((c2->wall == waBigStatue) && c2->type == 7 && !monstersnear(c2)) {
int q = 0;
for(int i=3; i<=4; i++) {
cellwalker push = cwt;
cwstep(push);
cwspin(push, i);
cwstep(push);
if(passable(push.c, c2, false, true)) q++;
}
if(!q) {
if(checkonly) return false;
addMessage(XLAT("No room to push %the1.", c2->wall));
return false;
}
if(checkonly) return true;
addMessage(XLAT("You push %the1.", c2->wall));
c2->wall = waNone;
for(int i=3; i<=4; i++) {
cellwalker push = cwt;
cwstep(push);
cwspin(push, i);
cwstep(push);
if(passable(push.c, c2, false, true))
push.c->wall = waBigStatue;
}
} */
if(c2->item == itHolyGrail && roundTableRadius(c2) < newRoundTableRadius()) {
if(checkonly) return false;
addMessage(XLAT("That was not a challenge. Find a larger castle!"));
@ -6658,33 +6693,14 @@ bool movepcto(int d, int subdir, bool checkonly) {
return false;
}
// pushto=c2 means that the monster is not killed and thus
// still counts for lightning in monstersnear
cell *pushto = NULL;
if(isStunnable(c2->monst) && c2->hitpoints > 1) {
// pushto=c2 means that the monster is not killed and thus
// still counts for lightning in monstersnear
pushto = c2;
if(c2->monst != moFatGuard && !(isMetalBeast(c2->monst) && c2->stuntime < 2) && c2->monst != moTortoise) {
cellwalker push = cwt;
cwstep(push);
cwspin(push, 3 * -subdir);
cwstep(push);
if(c2->type == 7 && !passable(push.c, c2, P_BLOW)) {
cwstep(push);
cwspin(push, 1 * -subdir);
cwstep(push);
}
if(!passable(push.c, c2, P_BLOW) && gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, 1); cwstep(push);
if(gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, -2); cwstep(push);
}
if(gravityLevel(push.c) < gravityLevel(c2)) {
cwstep(push); cwspin(push, 1); cwstep(push);
}
}
if(passable(push.c, c2, P_BLOW))
pushto = push.c;
}
if(monsterPushable(c2))
pushto = determinePush(cwt, c2, subdir, [c2] (cell *c) { return passable(c, c2, P_BLOW); });
else
pushto = c2;
}
if(c2->monst == moTroll || c2->monst == moFjordTroll ||
c2->monst == moForestTroll || c2->monst == moStormTroll || c2->monst == moVineSpirit)

272
graph.cpp
View File

@ -114,14 +114,14 @@ movedir mousedest, joydir;
int mousex, mousey, joyx, joyy, panjoyx, panjoyy;
bool autojoy = true;
hyperpoint mouseh;
hyperpoint mouseh, mouseoh;
bool leftclick, rightclick, targetclick, hiliteclick, anyshiftclick, wheelclick,
forcetarget, lshiftclick, lctrlclick;
bool gtouched;
bool revcontrol;
int getcstat; ld getcshift; bool inslider;
int getcstat, lgetcstat; ld getcshift; bool inslider;
int ZZ;
@ -1252,13 +1252,23 @@ double q3 = sqrt(double(3));
bool outofmap(hyperpoint h) {
if(euclid)
return h[0] * h[0] + h[1] * h[1] > 15 * eurad;
return false; // h[0] * h[0] + h[1] * h[1] > 15 * eurad;
else if(sphere)
return h[2] < .1 && h[2] > -.1 && h[1] > -.1 && h[1] < .1 && h[0] > -.1 && h[0] < .1;
else
return h[2] < .5;
}
bool mouseout() {
if((getcstat != '-' && getcstat) || (lgetcstat && lgetcstat != '-')) return true;
return outofmap(mouseh);
}
bool mouseout2() {
if((getcstat && getcstat != '-') || (lgetcstat && lgetcstat != '-')) return true;
return outofmap(mouseh) || outofmap(mouseoh);
}
void drawShield(const transmatrix& V, eItem it) {
float ds = ticks / 300.;
int col = iinf[it].color;
@ -1432,53 +1442,9 @@ void drawStunStars(const transmatrix& V, int t) {
}
}
bool drawUserShape(transmatrix V, int group, int id, int color) {
#ifdef MOBILE
return false;
#else
usershape *us = usershapes[group][id];
if(!us) return false;
for(int i=0; i<USERLAYERS; i++) {
usershapelayer& ds(us->d[i]);
hpcshape& sh(ds.sh);
if(sh.s != sh.e)
queuepoly(V, sh, ds.color ? ds.color : color);
}
#ifndef NOEDIT
if(cmode == emDraw && mapeditor::editingShape(group, id)) {
usershapelayer &ds(usershapes[group][id]->d[mapeditor::dslayer]);
/* for(int a=0; a<size(ds.list); a++) {
hyperpoint P2 = V * ds.list[a];
int xc, yc, sc;
getcoord(P2, xc, yc, sc);
queuechr(xc, yc, sc, 10, 'x',
a == 0 ? 0x00FF00 :
a == size(ds.list)-1 ? 0xFF0000 :
0xFFFF00);
} */
hyperpoint mh = inverse(mapeditor::drawtrans) * mouseh;
for(int a=0; a<ds.rots; a++)
for(int b=0; b<(ds.sym?2:1); b++) {
if(outofmap(mouseh)) break;
hyperpoint P2 = V * spin(2*M_PI*a/ds.rots) * (b?Mirror*mh:mh);
queuechr(P2, 10, 'x', 0xFF00FF);
}
}
#endif
return true;
#endif
hyperpoint mirrorif(const hyperpoint& V, bool b) {
if(b) return Mirror*V;
else return V;
}
string csnameid(int id) {
@ -1773,7 +1739,7 @@ bool drawItemType(eItem it, cell *c, const transmatrix& V, int icol, int ticks,
xsh = NULL;
}
else if(drawUserShape(V, 2, it, darkena(icol, 0, 0xFF))) ;
else if(mapeditor::drawUserShape(V, 2, it, darkena(icol, 0, 0xFF), c)) ;
else if(it == itRose) {
for(int u=0; u<4; u++)
@ -1838,11 +1804,6 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou
char xch = minf[m].glyph;
#ifndef NOEDIT
if(where == mapeditor::drawcell)
mapeditor::drawtrans = V;
#endif
if(m == moTortoise && where && where->stuntime >= 3)
drawStunStars(V, where->stuntime-2);
else if (m == moTortoise || m == moPlayer || (where && !where->stuntime)) ;
@ -1860,8 +1821,8 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou
charstyle& cs = getcs();
bool havus = drawUserShape(V, 0, cs.charid, cs.skincolor);
bool havus = mapeditor::drawUserShape(V, 0, cs.charid, cs.skincolor, where);
if(mapeditor::drawplayer && !havus) {
if(cs.charid >= 8) {
@ -1974,12 +1935,12 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou
}
}
else if(drawUserShape(V, 1, m, darkena(col, 0, 0xFF))) return false;
else if(mapeditor::drawUserShape(V, 1, m, darkena(col, 0, 0xFF), where)) 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(mapeditor::drawUserShape(V, 0, (cs.charid&1)?1:0, darkena(col, 0, 0x80), where)) return false;
if(cs.charid >= 8) {
queuepoly(VABODY, shWolfBody, darkena(col, 0, 0xC0));
@ -2729,11 +2690,7 @@ bool drawMonster(const transmatrix& Vparam, int ct, cell *c, int col) {
else
Vb = Vb * ddspin(c, c->mondir);
#ifndef NOEDIT
if(c == mapeditor::drawcell) mapeditor::drawtrans = Vb;
#endif
if(drawUserShape(Vb, 1, c->monst, (col << 8) + 0xFF)) return false;
if(mapeditor::drawUserShape(Vb, 1, c->monst, (col << 8) + 0xFF, c)) return false;
if(isIvy(c) || isMutantIvy(c) || c->monst == moFriendlyIvy)
queuepoly(Vb, shIBranch, (col << 8) + 0xFF);
@ -2863,7 +2820,7 @@ bool drawMonster(const transmatrix& Vparam, int ct, cell *c, int col) {
if(flipplayer) Vs = Vs * pispin;
if(!outofmap(mouseh) && !nospins) {
if(!mouseout() && !nospins) {
// transmatrix invxy = Id; invxy[0][0] = invxy[1][1] = -1;
hyperpoint P2 = Vs * inverse(cwtV) * mouseh;
@ -4091,7 +4048,11 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
ivoryz = isGravityLand(c->land);
transmatrix& gm = gmatrix[c];
bool orig = (gm[2][2] == 0 || fabs(gm[2][2]-1) >= fabs(V[2][2]-1) - 1e-8);
bool orig =
gm[2][2] == 0 ? true :
torus ? hypot(gm[0][2], gm[1][2]) >= hypot(V[0][2], V[1][2]) :
(sphere && vid.alphax >= 1.001) ? fabs(gm[2][2]-1) <= fabs(V[2][2]-1) :
fabs(gm[2][2]-1) >= fabs(V[2][2]-1) - 1e-8;
if(orig) gm = V;
@ -4163,12 +4124,14 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
modist2 = intval(mouseh, VC0);
mouseover2 = c;
}
double dfc = euclid ? intval(VC0, C0) : VC0[2];
if(dfc < centdist) {
centdist = dfc;
centerover = c;
if(!torus) {
double dfc = euclid ? intval(VC0, C0) : VC0[2];
if(dfc < centdist) {
centdist = dfc;
centerover = c;
}
}
int orbrange = (items[itRevolver] ? 3 : 2);
@ -4356,12 +4319,6 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
bool eoh = euclid || purehepta;
#ifndef NOEDIT
if(c == mapeditor::drawcell && c != cwt.c && !c->monst && !c->item) {
mapeditor::drawtrans = Vpdir;
}
#endif
if(c->wall == waChasm) {
if(c->land == laZebra) fd++;
if(c->land == laHalloween && !wmblack) {
@ -4372,8 +4329,8 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
}
#ifndef NOEDIT
if(drawUserShape(Vpdir, mapeditor::cellShapeGroup(), mapeditor::realpatternsh(c),
darkena(fcol, fd, cmode == emDraw ? 0xC0 : 0xFF)));
if(mapeditor::drawUserShape(Vpdir, mapeditor::cellShapeGroup(), mapeditor::realpatternsh(c),
darkena(fcol, fd, cmode == emDraw ? 0xC0 : 0xFF), c));
else if(mapeditor::whichShape == '7') {
if(ishept(c))
@ -5309,6 +5266,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
#ifndef NOEDIT
if(cmode == emMapEditor && !mapeditor::subscreen && lmouseover && darken == 0 &&
!mouseout() &&
(mapeditor::whichPattern ? mapeditor::subpattern(c) == mapeditor::subpattern(lmouseover) : c == lmouseover)) {
queuecircle(V, .78, 0x00FFFFFF);
}
@ -5338,7 +5296,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
}
bool confusingGeometry() {
return elliptic || quotient == 1;
return elliptic || quotient == 1 || torus;
}
struct flashdata {
@ -5717,6 +5675,25 @@ string princessReviveHelp() {
return h;
}
void describeOrb(string& help, const orbinfo& oi) {
eOrbLandRelation olr = getOLR(oi.orb, cwt.c->land);
eItem tr = treasureType(oi.l);
help += "\n\n" + XLAT(olrDescriptions[olr], cwt.c->land, tr, treasureType(cwt.c->land));
int t = items[tr] * landMultiplier(oi.l);
if(t >= 25)
if(olr == olrPrize25 || olr == olrPrize3 || olr == olrGuest || olr == olrMonster || olr == olrAlways) {
help += XLAT("\nSpawn rate (as prize Orb): %1%/%2\n",
its(int(.5 + 100 * orbprizefun(t))),
its(oi.gchance));
}
if(t >= 10)
if(olr == olrHub) {
help += XLAT("\nSpawn rate (in Hubs): %1%/%2\n",
its(int(.5 + 100 * orbcrossfun(t))),
its(oi.gchance));
}
}
string generateHelpForItem(eItem it) {
string help = helptitle(XLATN(iinf[it].name), iinf[it].color);
@ -5786,25 +5763,23 @@ string generateHelpForItem(eItem it) {
}
if(itemclass(it) == IC_ORB || it == itGreenStone || it == itOrbYendor) {
eOrbLandRelation olr = getOLR(it, cwt.c->land);
for(int i=0; i<ORBLINES; i++) {
orbinfo& oi(orbinfos[i]);
if(oi.orb == it) {
eItem tr = treasureType(oi.l);
help += "\n\n" + XLAT(olrDescriptions[olr], cwt.c->land, tr, treasureType(cwt.c->land));
int t = items[tr] * landMultiplier(oi.l);
if(t >= 25)
if(olr == olrPrize25 || olr == olrPrize3 || olr == olrGuest || olr == olrMonster || olr == olrAlways) {
help += XLAT("\nSpawn rate (as prize Orb): %1%/%2\n",
its(int(.5 + 100 * orbprizefun(t))),
its(oi.gchance));
const orbinfo& oi(orbinfos[i]);
if(oi.orb == it) describeOrb(help, oi);
}
}
if(itemclass(it) == IC_TREASURE) {
for(int i=0; i<ORBLINES; i++) {
const orbinfo& oi(orbinfos[i]);
if(treasureType(oi.l) == it) {
if(oi.gchance > 0) {
help += XLAT("\n\nOrb unlocked: %1", oi.orb);
describeOrb(help, oi);
}
if(t >= 10)
if(olr == olrHub) {
help += XLAT("\nSpawn rate (in Hubs): %1%/%2\n",
its(int(.5 + 100 * orbcrossfun(t))),
its(oi.gchance));
else if(oi.l == cwt.c->land) {
help += XLAT("\n\nSecondary orb: %1", oi.orb);
describeOrb(help, oi);
}
}
}
@ -6192,9 +6167,14 @@ void describeMouseover() {
out += " " + describeRPM(c->land);
if(euclid && cheater) {
eucoord x, y;
decodeMaster(c->master, x, y);
out += " ("+its(short(x))+","+its(short(y))+")";
if(torus) {
out += " ("+its(decodeId(c->master))+")";
}
else {
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;
@ -6233,10 +6213,10 @@ void describeMouseover() {
if(isActivable(c)) out += XLAT(" (touch to activate)");
if(hasTimeout(c)) out += XLAT(" [" + turnstring(c->wparam) + "]");
if(hasTimeout(c)) out += " [" + turnstring(c->wparam) + "]";
if(isReptile(c->wall))
out += XLAT(" [" + turnstring((unsigned char) c->wparam) + "]");
out += " [" + turnstring((unsigned char) c->wparam) + "]";
if(c->monst) {
out += ", "; out += XLAT1(minf[c->monst].name);
@ -6299,7 +6279,6 @@ void describeMouseover() {
if(isWarped(c))
help += s0 + "\n\n" + warpdesc;
}
else if(cmode == emVisual1) {
if(getcstat == 'p') {
@ -6351,6 +6330,10 @@ void describeMouseover() {
}
mouseovers = out;
#ifdef ROGUEVIZ
rogueviz::describe(c);
#endif
int col = linf[cwt.c->land].color;
if(cwt.c->land == laRedRock) col = 0xC00000;
@ -6422,25 +6405,52 @@ transmatrix eumove(int x, int y) {
return Mat;
}
transmatrix eumovedir(int d) {
d = fix6(d);
switch(d) {
case 0: return eumove(1,0);
case 1: return eumove(0,1);
case 2: return eumove(-1,1);
case 3: return eumove(-1,0);
case 4: return eumove(0,-1);
case 5: return eumove(1,-1);
}
return eumove(0,0);
}
void drawEuclidean() {
DEBB(DF_GRAPH, (debugfile,"drawEuclidean\n"));
eucoord px, py;
eucoord px=0, py=0;
if(!centerover) centerover = cwt.c;
// printf("centerover = %p player = %p [%d,%d]-[%d,%d]\n", lcenterover, cwt.c,
// mindx, mindy, maxdx, maxdy);
decodeMaster(centerover->master, px, py);
int pid;
const bool b = torus;
if(b)
pid = decodeId(centerover->master);
else
decodeMaster(centerover->master, px, py);
int minsx = mindx-1, maxsx=maxdx+1, minsy=mindy-1, maxsy=maxdy+1;
mindx=maxdx=mindy=maxdy=0;
for(int dx=minsx; dx<=maxsx; dx++)
for(int dy=minsy; dy<=maxsy; dy++) {
eucoord x = dx+px;
eucoord y = dy+py;
reclevel = eudist(dx, dy);
cell *c = euclideanAt(x,y);
cell *c;
transmatrix Mat;
if(b) {
reclevel = eudist(dx, dy);
c = getTorusId(pid+torusconfig::dx*dx+torusconfig::dy*dy);
Mat = eumove(dx,dy);
}
else {
eucoord x = dx+px;
eucoord y = dy+py;
c = euclideanAt(x,y);
Mat = eumove(x, y);
}
if(!c) continue;
transmatrix Mat = eumove(x, y);
Mat = View * Mat;
// Mat[0][0] = -1;
@ -6525,7 +6535,9 @@ void drawthemap() {
keycelldist = YDIST - i;
}
}
if(mapeditor::autochoose) mapeditor::ew = mapeditor::ewsearch;
mapeditor::ewsearch.dist = 1e30;
modist = 1e20; mouseover = NULL;
modist2 = 1e20; mouseover2 = NULL;
mouseovers = XLAT("Press F1 or right click for help");
@ -6535,7 +6547,8 @@ void drawthemap() {
#ifdef TOUR
if(tour::on) mouseovers = tour::tourhelp;
#endif
centdist = 1e20; centerover = NULL;
centdist = 1e20;
if(!torus) centerover = NULL;
for(int i=0; i<multi::players; i++) {
multi::ccdist[i] = 1e20; multi::ccat[i] = NULL;
@ -6544,7 +6557,7 @@ void drawthemap() {
#ifdef MOBILE
mouseovers = XLAT("No info about this...");
#endif
if(outofmap(mouseh))
if(mouseout())
modist = -5;
playerfound = false;
// playerfoundL = false;
@ -6617,7 +6630,7 @@ void drawthemap() {
Uint8 *keystate = SDL_GetKeyState(NULL);
lmouseover = mouseover;
bool useRangedOrb = (!(vid.shifttarget & 1) && haveRangedOrb() && lmouseover && lmouseover->cpdist > 1) || (keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]);
if(!useRangedOrb && cmode != emMapEditor && DEFAULTCONTROL && !outofmap(mouseh)) {
if(!useRangedOrb && cmode != emMapEditor && DEFAULTCONTROL && !mouseout()) {
void calcMousedest();
calcMousedest();
cellwalker cw = cwt; bool f = flipplayer;
@ -6661,7 +6674,7 @@ void centerpc(ld aspd) {
// 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;
}
@ -6744,7 +6757,7 @@ void optimizeview() {
hyperpoint H = View * tC0(T);
if(H[2] < best) best = H[2], turn = i, TB = T;
}
if(turn >= 0) {
View = View * TB;
fixmatrix(View);
@ -6796,7 +6809,7 @@ void movepckeydir(int d) {
}
void calcMousedest() {
if(outofmap(mouseh)) return;
if(mouseout()) return;
if(revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
ld mousedist = intval(mouseh, tC0(shmup::ggmatrix(cwt.c)));
mousedest.d = -1;
@ -6918,6 +6931,7 @@ void calcparam() {
vid.alphax = vid.alpha + eye;
vid.goteyes = vid.eye > 0.001 || vid.eye < -0.001;
vid.goteyes2 = vid.goteyes;
vid.scrdist = vid.radius;
}
void displayButton(int x, int y, const string& name, int key, int align, int rad) {
@ -6927,6 +6941,16 @@ void displayButton(int x, int y, const string& name, int key, int align, int rad
}
}
char mousekey = 'n';
char newmousekey;
void displaymm(char c, int x, int y, int rad, int size, const string& title, int align) {
if(displayfr(x, y, rad, size, title, c == mousekey ? 0xFF8000 : 0xC0C0C0, align)) {
displayfr(x, y, rad, size, title, 0xFFFF00, align);
getcstat = SETMOUSEKEY, newmousekey = c;
}
}
bool displayButtonS(int x, int y, const string& name, int col, int align, int size) {
if(displaystr(x, y, 0, size, name, col, align)) {
displaystr(x, y, 0, size, name, 0xFFFF00, align);
@ -7413,8 +7437,8 @@ transmatrix atscreenpos(ld x, ld y, ld size) {
V[0][2] += (x - vid.xcenter);
V[1][2] += (y - vid.ycenter);
V[0][0] = size * 2 * crossf / hcrossf;
V[1][1] = size * 2 * crossf / hcrossf;
V[0][0] = size * 2 * hcrossf / crossf;
V[1][1] = size * 2 * hcrossf / crossf;
V[2][2] = vid.scrdist;
if(euclid) V[2][2] /= EUCSCALE;
@ -7964,6 +7988,7 @@ void drawscreen() {
if(conformal::includeHistory && cmode != emProgress) conformal::restoreBack();
lgetcstat = getcstat;
getcstat = 0; inslider = false;
if(cmode == emNormal || cmode == emQuit) drawStats();
@ -8132,7 +8157,7 @@ void restartGraph() {
linepatterns::clearAll();
if(currentmap) {
if(euclid) {
centerover = euclideanAtCreate(0,0);
centerover = torus ? getTorusId(0) : euclideanAtCreate(0,0);
}
else {
viewctr.h = currentmap->getOrigin();
@ -9210,7 +9235,7 @@ void mainloopiter() {
}
if(ev.type == SDL_MOUSEMOTION) {
hyperpoint mouseoh = mouseh;
mouseoh = mouseh;
mousing = true;
mousemoved = true;
@ -9223,9 +9248,8 @@ void mainloopiter() {
else
#endif
mouseh = gethyper(mousex, mousey);
if((rightclick || (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) &&
!outofmap(mouseh) && !outofmap(mouseoh) &&
if((rightclick || (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) && !mouseout2() &&
mouseh[2] < 50 && mouseoh[2] < 50) {
panning(mouseoh, mouseh);
}

View File

@ -309,6 +309,11 @@ else if(args()[0] == '-' && args()[1] == x && args()[2] == '0') { if(curphase ==
else if(argis("-svgsize")) {
shift(); sscanf(args(), "%d/%d", &svg::svgsize, &svg::divby);
}
else if(argis("-svgfont")) {
shift(); svg::font = args();
// note: use '-svgfont latex' to produce text output as: \myfont{size}{text}
// (this is helpful with Inkscape's PDF+TeX output feature; define \myfont yourself)
}
#ifndef NOPNG
else if(argis("-pngsize")) {
shift(); sscanf(args(), "%d", &pngres);

43
hyper.h
View File

@ -216,6 +216,7 @@ namespace shmup {
void virtualRebase(cell*& base, transmatrix& at, bool tohex);
void virtualRebase(shmup::monster *m, bool tohex);
void fixStorage();
}
// graph
@ -384,6 +385,7 @@ namespace mapeditor {
int patterndir(cell *c, char w = whichPattern);
int subpattern(cell *c);
extern cell *drawcell;
void initdraw(cell *c);
}
#ifndef NORUG
@ -992,6 +994,7 @@ namespace rogueviz {
void fixparam();
int readArgs();
void close();
void mark(cell *c);
}
#endif
@ -1004,6 +1007,7 @@ void movecost(cell* from, cell *to);
void checkmove();
transmatrix eumove(int x, int y);
transmatrix eumovedir(int d);
#ifndef NOSAVE
void loadScores();
@ -1136,6 +1140,8 @@ namespace tour {
pmGeometry = 11, pmGeometryReset = 13, pmGeometryStart = 15
};
void setCanvas(presmode mode, char canv);
void presentation(presmode mode);
void checkGoodLand(eLand l);
int getid();
@ -1145,12 +1151,34 @@ namespace tour {
extern function<bool(eLand)> showland;
void start();
struct slide {
const char *name; int unused_id; int flags; const char *help;
function<void(presmode mode)> action;
} ;
extern slide *slides;
extern slide default_slides[];
static const int LEGAL_NONE=0;
static const int LEGAL_UNLIMITED=1;
static const int LEGAL_HYPERBOLIC=2;
static const int LEGAL_ANY=3;
static const int LEGAL_NONEUC=4;
static const int QUICKSKIP=8;
static const int FINALSLIDE=16;
extern slide slideHypersian;
extern slide slideExpansion;
};
#endif
namespace rogueviz {
extern bool rog3;
extern bool rvwarp;
namespace rvtour {
extern tour::slide rvslides[];
}
};
extern bool doCross;
@ -1199,3 +1227,18 @@ int celldistance(cell *c1, cell *c2);
bool behindsphere(const transmatrix& V);
extern hyperpoint pirateCoords;
bool mouseout();
bool againstWind(cell *c2, cell *c1); // to, from
transmatrix atscreenpos(ld x, ld y, ld size);
hyperpoint mirrorif(const hyperpoint& V, bool b);
#define SETMOUSEKEY 5000
extern char mousekey;
extern char newmousekey;
void displaymm(char c, int x, int y, int rad, int size, const string& title, int align);
bool canPushThumperOn(cell *tgt, cell *thumper, cell *player);
void pushThumper(cell *th, cell *cto);

View File

@ -1,8 +1,8 @@
id ICON "hr-icon.ico"
1 VERSIONINFO
FILEVERSION 9,4,0,12
PRODUCTVERSION 9,4,0,12
FILEVERSION 9,4,0,14
PRODUCTVERSION 9,4,0,14
BEGIN
BLOCK "StringFileInfo"
BEGIN
@ -10,12 +10,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Zeno Rogue"
VALUE "FileDescription", "A roguelike in non-euclidean space"
VALUE "FileVersion", "94k"
VALUE "FileVersion", "94n"
VALUE "InternalName", "hyper"
VALUE "LegalCopyright", "Zeno Rogue"
VALUE "OriginalFilename", "hyper.exe"
VALUE "ProductName", "HyperRogue"
VALUE "ProductVersion", "9.4l"
VALUE "ProductVersion", "9.4n"
END
END

View File

@ -1,6 +1,6 @@
#define VER "9.4l"
#define VERNUM 9412
#define VERNUM_HEX 0x9412
#define VER "9.4n"
#define VERNUM 9414
#define VERNUM_HEX 0x9414
#define GEN_M 0
#define GEN_F 1

444
kohonen.cpp Normal file
View File

@ -0,0 +1,444 @@
// Hyperbolic Rogue
// Copyright (C) 2011-2017 Zeno and Tehora Rogue, see 'hyper.cpp' for details
// Kohonen's self-organizing networks.
// This is a part of RogueViz, not a part of HyperRogue.
namespace kohonen {
int cols;
typedef vector<double> kohvec;
struct sample {
kohvec val;
string name;
};
vector<sample> data;
int whattodraw[3];
struct neuron {
kohvec net;
cell *where;
double udist;
int lpbak;
int col;
int samples, csample;
};
kohvec weights;
vector<neuron> net;
void alloc(kohvec& k) { k.resize(cols); }
bool neurons_indexed = false;
int samples;
template<class T> T sqr(T x) { return x*x; }
vector<neuron*> whowon;
void normalize() {
alloc(weights);
for(int k=0; k<cols; k++) {
double sum = 0, sqsum = 0;
for(sample& s: data)
sum += s.val[k],
sqsum += s.val[k] * s.val[k];
double variance = sqsum/samples - sqr(sum/samples);
weights[k] = 1 / sqrt(variance);
}
}
double vnorm(kohvec& a, kohvec& b) {
double diff = 0;
for(int k=0; k<cols; k++) diff += sqr((a[k]-b[k]) * weights[k]);
return diff;
}
void loadsamples(const char *fname) {
normalize();
FILE *f = fopen(fname, "rt");
if(!f) return;
if(fscanf(f, "%d", &cols) != 1) { fclose(f); return; }
while(true) {
sample s;
alloc(s.val);
for(int i=0; i<cols; i++)
if(fscanf(f, "%lf", &s.val[i]) != 1) { fclose(f); return; }
if(feof(f)) break;
fgetc(f);
while(true) {
int c = fgetc(f);
if(c == -1 || c == 10 || c == 13) break;
if(c != 32 && c != 9) s.name += c;
}
data.push_back(move(s));
}
fclose(f);
samples = size(data);
normalize();
vdata.resize(samples);
for(int i=0; i<samples; i++) {
vdata[i].name = data[i].name;
vdata[i].cp = dftcolor;
createViz(i, cwt.c, Id);
}
storeall();
}
int t;
int lpct, mul, maxdist, cells, perdist;
double maxfac;
neuron& winner(int id) {
double bdiff = 1e20;
neuron *bcell = NULL;
for(neuron& n: net) {
double diff = vnorm(n.net, data[id].val);
if(diff < bdiff) bdiff = diff, bcell = &n;
}
return *bcell;
}
void setindex(bool b) {
if(b == neurons_indexed) return;
neurons_indexed = b;
if(b) {
for(neuron& n: net) n.lpbak = n.where->landparam, n.where->landparam = (&n - &net[0]);
}
else {
for(neuron& n: net) n.where->landparam = n.lpbak;
}
}
neuron *getNeuron(cell *c) {
if(!c) return NULL;
setindex(true);
if(c->landparam < 0 || c->landparam >= cells) return NULL;
neuron& ret = net[c->landparam];
if(ret.where != c) return NULL;
return &ret;
}
neuron *getNeuronSlow(cell *c) {
if(neurons_indexed) return getNeuron(c);
for(neuron& n: net) if(n.where == c) return &n;
return NULL;
}
double maxudist;
neuron *distfrom;
void coloring() {
setindex(false);
for(int pid=0; pid<3; pid++) {
int c = whattodraw[pid];
vector<double> listing;
for(neuron& n: net) switch(c) {
case -3:
if(distfrom)
listing.push_back(vnorm(n.net, distfrom->net));
else
listing.push_back(0);
break;
case -2:
listing.push_back(n.udist);
break;
case -1:
listing.push_back(-n.udist);
break;
default:
listing.push_back(n.net[c]);
break;
}
double minl = listing[0], maxl = listing[0];
for(double& d: listing) minl = min(minl, d), maxl = max(maxl, d);
if(maxl-minl < 1e-3) maxl = minl+1e-3;
for(int i=0; i<cells; i++)
part(net[i].where->landparam, pid) = (255 * (listing[i] - minl)) / (maxl - minl);
}
}
void analyze() {
setindex(true);
maxudist = 0;
for(neuron& n: net) {
int qty = 0;
double total = 0;
forCellEx(c2, n.where) {
neuron *n2 = getNeuron(c2);
if(!n2) continue;
qty++;
total += sqrt(vnorm(n.net, n2->net));
}
n.udist = total / qty;
maxudist = max(maxudist, n.udist);
}
whowon.resize(samples);
for(neuron& n: net) n.samples = 0;
for(int id=0; id<samples; id++) {
auto& w = winner(id);
whowon[id] = &w;
w.samples++;
}
for(int id=0; id<samples; id++) {
auto& w = *whowon[id];
vdata[id].m->base = w.where;
vdata[id].m->at =
spin(2*M_PI*w.csample / w.samples) * xpush(.25 * (w.samples-1) / w.samples);
w.csample++;
}
shmup::fixStorage();
setindex(false);
coloring();
}
struct cellcrawler {
struct cellcrawlerdata {
cellwalker orig;
int from, spin, dist;
cellwalker target;
cellcrawlerdata(const cellwalker& o, int fr, int sp) : orig(o), from(fr), spin(sp) {}
};
vector<cellcrawlerdata> data;
void store(const cellwalker& o, int from, int spin) {
if(eq(o.c->aitmp, sval)) return;
o.c->aitmp = sval;
data.emplace_back(o, from, spin);
}
void build(const cellwalker& start) {
sval++;
data.clear();
store(start, 0, 0);
for(int i=0; i<size(data); i++) {
cellwalker cw0 = data[i].orig;
for(int j=0; j<cw0.c->type; j++) {
cellwalker cw = cw0;
cwspin(cw, j); cwstep(cw);
if(!getNeuron(cw.c)) continue;
store(cw, i, j);
}
}
for(cellcrawlerdata& s: data)
s.dist = celldistance(s.orig.c, start.c);
}
void sprawl(const cellwalker& start) {
data[0].target = start;
for(int i=1; i<size(data); i++) {
cellcrawlerdata& s = data[i];
s.target = data[s.from].target;
if(!s.target.c) continue;
cwspin(s.target, s.spin);
if(cwstepcreates(s.target)) s.target.c = NULL;
else cwstep(s.target);
}
}
};
cellcrawler s0, s1; // hex and non-hex
void buildcellcrawler(cell *c) {
(c->type == 6 ? s0 : s1).build(cellwalker(c,0));
}
bool finished() { return t == 0; }
void step() {
double sigma = maxdist * t / (perdist*(double) mul);
if(t == 0) return;
// double sigma = maxdist * exp(-t / t1);
int pct = (int) (100 * ((t*(double) mul) / perdist));
if(pct != lpct) {
lpct = pct;
analyze();
printf("t = %6d/%2dx%6d pct = %3d sigma=%10.7lf maxudist=%10.7lf\n", t, mul, perdist, pct, sigma, maxudist);
}
int id = hrand(samples);
neuron& n = winner(id);
/*
for(neuron& n2: net) {
int d = celldistance(n.where, n2.where);
double nu = maxfac;
// nu *= exp(-t*(double)maxdist/perdist);
// nu *= exp(-t/t2);
nu *= exp(-sqr(d/sigma));
for(int k=0; k<cols; k++)
n2.net[k] += nu * (irisdata[id][k] - n2.net[k]);
} */
cellcrawler& s = n.where->type == 6 ? s0 : s1;
s.sprawl(cellwalker(n.where, 0));
for(auto& sd: s.data) {
neuron *n2 = getNeuron(sd.target.c);
if(!n2) continue;
double nu = maxfac;
nu *= exp(-sqr(sd.dist/sigma));
for(int k=0; k<cols; k++)
n2->net[k] += nu * (data[id].val[k] - n2->net[k]);
}
t--;
if(t == 0) analyze();
}
void run(const char *fname, int _perdist, double _maxfac) {
perdist = _perdist;
maxfac = _maxfac;
init(); kind = kKohonen;
loadsamples(fname);
/* if(geometry != gQuotient1) {
targetGeometry = gQuotient1;
restartGame('g');
}
if(!purehepta) restartGame('7'); */
#define Z 1
vector<cell*>& allcells = currentmap->allcells();
cells = size(allcells);
net.resize(cells);
for(int i=0; i<cells; i++) net[i].where = allcells[i], allcells[i]->landparam = i;
for(int i=0; i<cells; i++) {
net[i].where->land = laCanvas;
alloc(net[i].net);
for(int k=0; k<cols; k++)
for(int z=0; z<Z; z++)
net[i].net[k] += data[hrand(samples)].val[k] / Z;
}
for(neuron& n: net) for(int d=BARLEV; d>=7; d--) setdist(n.where, d, NULL);
cell *c1 = net[cells/2].where;
vector<int> mapdist;
for(neuron &n2: net) mapdist.push_back(celldistance(c1,n2.where));
sort(mapdist.begin(), mapdist.end());
maxdist = mapdist[size(mapdist)*5/6];
printf("samples = %d cells = %d maxdist = %d\n", samples, cells, maxdist);
c1 = currentmap->gamestart();
cell *c2 = createMov(c1, 0);
buildcellcrawler(c1);
if(c1->type != c2->type) buildcellcrawler(c2);
lpct = -46130;
mul = 1;
t = perdist*mul;
step();
for(int i=0; i<3; i++) whattodraw[i] = -2;
analyze();
}
void describe(cell *c) {
if(cmode == emHelp) return;
neuron *n = getNeuronSlow(c);
if(!n) return;
help += "cell number: " + its(n - &net[0]) + "\n";
help += "parameters:"; for(int k=0; k<cols; k++) help += " " + fts(n->net[k]);
help += ", u-matrix = " + fts(n->udist);
help += "\n";
for(int s=0; s<samples; s++) if(whowon[s] == n) {
help += "sample "+its(s)+":";
for(int k=0; k<cols; k++) help += " " + fts(data[s].val[k]);
help += " "; help += data[s].name; help += "\n";
}
}
void ksave(const char *fname) {
FILE *f = fopen(fname, "wt");
fprintf(f, "%d %d\n", cells, t);
for(neuron& n: net) {
for(int k=0; k<cols; k++) fprintf(f, "%.4lf ", n.net[k]); fprintf(f, "\n");
}
fclose(f);
}
void kload(const char *fname) {
int xcells;
FILE *f = fopen(fname, "rt");
if(!f) return;
if(fscanf(f, "%d%d\n", &xcells, &t) != 2) return;
if(xcells != cells) {
printf("Error: bad number of cells\n");
exit(1);
}
for(neuron& n: net) {
for(int k=0; k<cols; k++) if(fscanf(f, "%lf", &n.net[k]) != 1) return;
}
fclose(f);
analyze();
}
void steps() {
if(!kohonen::finished()) {
unsigned int t = SDL_GetTicks();
while(SDL_GetTicks() < t+20) kohonen::step();
setindex(false);
}
}
void showMenu() {
string parts[3] = {"red", "green", "blue"};
for(int i=0; i<3; i++) {
string c;
if(whattodraw[i] == -1) c = "u-matrix";
else if(whattodraw[i] == -2) c = "u-matrix reversed";
else if(whattodraw[i] == -3) c = "distance from marked ('m')";
else c = "column " + its(whattodraw[i]);
dialog::addSelItem(XLAT("coloring (%1)", parts[i]), c, '1'+i);
}
}
bool handleMenu(int sym, int uni) {
if(uni >= '1' && uni <= '3') {
int i = uni - '1';
whattodraw[i]++;
if(whattodraw[i] == cols) whattodraw[i] = -3;
coloring();
return true;
}
if(uni == '0') {
for(char x: {'1','2','3'}) handleMenu(x, x);
return true;
}
return false;
}
}
void mark(cell *c) {
using namespace kohonen;
distfrom = getNeuronSlow(c);
coloring();
}